Skip to content

Commit b95b95f

Browse files
Fix SQL Syntax when doing a valid insert with request body empty (#2530)
## Why make this change? Closes #2527 ## What is this change? We introduce new SQL text for PostgreSQL and MSSQL when the collection of insertion columns is empty, when doing an insert. MSSQL and PostgreSQL provide key words DEFAULT VALUES in order to handle this case, and MySQL uses an empty VALUES () text that we already generate. ## How was this tested? We add a new test to MSSQL and PostreSQL called InsertOneWithDefaultValuesAndEmptyRequestBody which makes a simple insertion without a request body, we then validate that the response is as expected. ## Sample Request(s) Any valid POST request to a table that is made up of columns that all have some kind of default value, with an empty request body. ie: {POST}https://localhost:5001/restApi/Book/ Request Body: { } --------- Co-authored-by: Abhishek Kumar <[email protected]>
1 parent 7ff62e7 commit b95b95f

19 files changed

+335
-8
lines changed

config-generators/mssql-commands.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ add Publisher --config "dab-config.MsSql.json" --source publishers --permissions
33
add Publisher_MM --config "dab-config.MsSql.json" --source publishers_mm --graphql "Publisher_MM:Publishers_MM" --permissions "anonymous:*"
44
add Stock --config "dab-config.MsSql.json" --source stocks --permissions "anonymous:create,read,update,delete"
55
add Book --config "dab-config.MsSql.json" --source books --permissions "anonymous:create,read,update,delete" --graphql "book:books"
6+
add Default_Books --config "dab-config.MsSql.json" --source default_books --permissions "anonymous:create,read,update,delete" --graphql "default_book:default_books"
67
add Book_MM --config "dab-config.MsSql.json" --source books_mm --permissions "anonymous:*" --graphql "book_mm:books_mm"
78
add BookWebsitePlacement --config "dab-config.MsSql.json" --source book_website_placements --permissions "anonymous:read"
89
add Author --config "dab-config.MsSql.json" --source authors --permissions "anonymous:read"

config-generators/mysql-commands.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ init --config "dab-config.MySql.json" --database-type mysql --connection-string
22
add Publisher --config "dab-config.MySql.json" --source publishers --permissions "anonymous:read"
33
add Stock --config "dab-config.MySql.json" --source stocks --permissions "anonymous:create,read,update,delete"
44
add Book --config "dab-config.MySql.json" --source books --permissions "anonymous:create,read,update,delete" --graphql "book:books"
5+
add Default_Books --config "dab-config.MySql.json" --source default_books --permissions "anonymous:create,read,update,delete" --graphql "default_book:default_books"
56
add BookNF --config "dab-config.MySql.json" --source books --permissions "anonymous:*" --rest true --graphql "bookNF:booksNF"
67
add BookWebsitePlacement --config "dab-config.MySql.json" --source book_website_placements --permissions "anonymous:read"
78
add Author --config "dab-config.MySql.json" --source authors --permissions "anonymous:read"

config-generators/postgresql-commands.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ init --config "dab-config.PostgreSql.json" --database-type postgresql --connecti
22
add Publisher --config "dab-config.PostgreSql.json" --source publishers --permissions "anonymous:read"
33
add Stock --config "dab-config.PostgreSql.json" --source stocks --permissions "anonymous:create,read,update,delete"
44
add Book --config "dab-config.PostgreSql.json" --source books --permissions "anonymous:create,read,update,delete" --graphql "book:books"
5+
add Default_Books --config "dab-config.PostgreSql.json" --source default_books --permissions "anonymous:create,read,update,delete" --graphql "default_book:default_books"
56
add BookWebsitePlacement --config "dab-config.PostgreSql.json" --source book_website_placements --permissions "anonymous:read"
67
add Author --config "dab-config.PostgreSql.json" --source authors --permissions "anonymous:read"
78
add Review --config "dab-config.PostgreSql.json" --source reviews --permissions "anonymous:create,read,update" --rest false --graphql "review:reviews"

src/Core/Authorization/RestAuthorizationHandler.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,18 @@ public Task HandleAsync(AuthorizationHandlerContext context)
197197

198198
restContext.UpdateReturnFields(fieldsReturnedForFind);
199199
}
200+
else if (columnsToCheck.Count() == 0 && restContext.OperationType is EntityActionOperation.Insert)
201+
{
202+
// It's possible that a INSERT operation has no columns in the request
203+
// body, but the operation is still allowed in cases where the table
204+
// contains default values for all columns. In such cases, we check
205+
// all the columns if the insert operation is allowed.
206+
IEnumerable<string> fieldsForCreate = _authorizationResolver.GetAllowedExposedColumns(entityName, roleName, operation);
207+
if (fieldsForCreate.Count() == 0)
208+
{
209+
context.Fail();
210+
}
211+
}
200212
else
201213
{
202214
context.Fail();

src/Core/Resolvers/MsSqlQueryBuilder.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,18 @@ public string Build(SqlInsertStructure structure)
114114
StringBuilder insertQuery = new();
115115
if (!isInsertDMLTriggerEnabled)
116116
{
117-
// When there is no DML trigger enabled on the table for insert operation, we can use OUTPUT clause to return the data.
118-
insertQuery.Append($"INSERT INTO {tableName} ({insertColumns}) OUTPUT " +
119-
$"{MakeOutputColumns(structure.OutputColumns, OutputQualifier.Inserted.ToString())} ");
120-
insertQuery.Append(values);
117+
if (!string.IsNullOrEmpty(insertColumns))
118+
{
119+
// When there is no DML trigger enabled on the table for insert operation, we can use OUTPUT clause to return the data.
120+
insertQuery.Append($"INSERT INTO {tableName} ({insertColumns}) OUTPUT " +
121+
$"{MakeOutputColumns(structure.OutputColumns, OutputQualifier.Inserted.ToString())} ");
122+
insertQuery.Append(values);
123+
}
124+
else
125+
{
126+
insertQuery.Append($"INSERT INTO {tableName} OUTPUT " +
127+
$"{MakeOutputColumns(structure.OutputColumns, OutputQualifier.Inserted.ToString())} DEFAULT VALUES");
128+
}
121129
}
122130
else
123131
{

src/Core/Resolvers/MySqlQueryBuilder.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,8 @@ private string MakeInsertSelections(SqlInsertStructure structure)
295295
}
296296
else if (columnDef.HasDefault)
297297
{
298-
selections.Add($"{GetMySQLDefaultValue(columnDef)} as {quotedColName}");
298+
string columnSelectionValue = structure.InsertColumns.Any() ? GetMySQLDefaultValue(columnDef) : $"'{GetMySQLDefaultValue(columnDef)}'";
299+
selections.Add($"{columnSelectionValue} as {quotedColName}");
299300
}
300301
}
301302

src/Core/Resolvers/PostgresQueryBuilder.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,18 @@ public string Build(SqlQueryStructure structure)
6767
/// <inheritdoc />
6868
public string Build(SqlInsertStructure structure)
6969
{
70-
return $"INSERT INTO {QuoteIdentifier(structure.DatabaseObject.SchemaName)}.{QuoteIdentifier(structure.DatabaseObject.Name)} ({Build(structure.InsertColumns)}) " +
71-
$"VALUES ({string.Join(", ", (structure.Values))}) " +
72-
$"RETURNING {Build(structure.OutputColumns)};";
70+
string insertQuery = $"INSERT INTO {QuoteIdentifier(structure.DatabaseObject.SchemaName)}.{QuoteIdentifier(structure.DatabaseObject.Name)} ";
71+
if (structure.InsertColumns.Any())
72+
{
73+
insertQuery += $"({Build(structure.InsertColumns)}) " +
74+
$"VALUES ({string.Join(", ", (structure.Values))}) ";
75+
}
76+
else
77+
{
78+
insertQuery += "DEFAULT VALUES ";
79+
}
80+
81+
return $"{insertQuery} RETURNING {Build(structure.OutputColumns)}";
7382
}
7483

7584
/// <inheritdoc />

src/Service.Tests/DatabaseSchema-MsSql.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ DROP TABLE IF EXISTS default_with_function_table;
6161
DROP TABLE IF EXISTS [DimAccount]
6262
DROP TABLE IF EXISTS users;
6363
DROP TABLE IF EXISTS user_profiles;
64+
DROP TABLE IF EXISTS default_books;
6465
DROP SCHEMA IF EXISTS [foo];
6566
DROP SCHEMA IF EXISTS [bar];
6667
COMMIT;
@@ -387,6 +388,11 @@ CREATE TABLE user_profiles (
387388
userid INT
388389
);
389390

391+
CREATE TABLE default_books(
392+
id int IDENTITY(5001, 1) PRIMARY KEY,
393+
title NVARCHAR(100)
394+
);
395+
390396
ALTER TABLE books
391397
ADD CONSTRAINT book_publisher_fk
392398
FOREIGN KEY (publisher_id)
@@ -444,6 +450,10 @@ ON DELETE CASCADE;
444450
ALTER TABLE sales
445451
ADD total AS (subtotal + tax) PERSISTED;
446452

453+
ALTER TABLE default_books
454+
ADD CONSTRAINT title_constraint
455+
DEFAULT 'Placeholder' FOR title
456+
447457
SET IDENTITY_INSERT publishers ON
448458
INSERT INTO publishers(id, name) VALUES (1234, 'Big Company'), (2345, 'Small Town Publisher'), (2323, 'TBD Publishing One'), (2324, 'TBD Publishing Two Ltd'), (1940, 'Policy Publisher 01'), (1941, 'Policy Publisher 02'), (1156, 'The First Publisher');
449459
SET IDENTITY_INSERT publishers OFF

src/Service.Tests/DatabaseSchema-MySql.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ DROP TABLE IF EXISTS authors;
1212
DROP TABLE IF EXISTS book_website_placements;
1313
DROP TABLE IF EXISTS website_users;
1414
DROP TABLE IF EXISTS books;
15+
DROP TABLE IF EXISTS default_books;
1516
DROP TABLE IF EXISTS players;
1617
DROP TABLE IF EXISTS clubs;
1718
DROP TABLE IF EXISTS publishers;
@@ -50,6 +51,11 @@ CREATE TABLE books(
5051
publisher_id int NOT NULL
5152
);
5253

54+
CREATE TABLE default_books (
55+
id INT AUTO_INCREMENT PRIMARY KEY,
56+
title VARCHAR(255) DEFAULT 'Placeholder'
57+
);
58+
5359
CREATE TABLE players(
5460
id int AUTO_INCREMENT PRIMARY KEY,
5561
name text NOT NULL,

src/Service.Tests/DatabaseSchema-PostgreSql.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ DROP TABLE IF EXISTS authors;
1212
DROP TABLE IF EXISTS book_website_placements;
1313
DROP TABLE IF EXISTS website_users;
1414
DROP TABLE IF EXISTS books;
15+
DROP TABLE IF EXISTS default_books;
1516
DROP TABLE IF EXISTS players;
1617
DROP TABLE IF EXISTS clubs;
1718
DROP TABLE IF EXISTS publishers;
@@ -58,6 +59,11 @@ CREATE TABLE books(
5859
publisher_id int NOT NULL
5960
);
6061

62+
CREATE TABLE default_books(
63+
id SERIAL PRIMARY KEY,
64+
title TEXT DEFAULT 'Placeholder'
65+
);
66+
6167
CREATE TABLE clubs(
6268
id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
6369
name text NOT NULL

0 commit comments

Comments
 (0)