Skip to content

Commit d7f22ba

Browse files
authored
Fix column name order issue in insert query (#693)
* fix column order in insert query + add test * add suppression * add test for all languages * use select
1 parent 65690a6 commit d7f22ba

File tree

17 files changed

+341
-4
lines changed

17 files changed

+341
-4
lines changed

src/SqlAsyncCollector.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,8 +403,7 @@ private static void GenerateDataQueryForMerge(TableInformation table, IEnumerabl
403403

404404
rowData = JsonConvert.SerializeObject(rowsToUpsert, table.JsonSerializerSettings);
405405
IEnumerable<string> columnNamesFromItem = GetColumnNamesFromItem(rows.First());
406-
IEnumerable<string> bracketColumnDefinitionsFromItem = table.Columns.Where(c => columnNamesFromItem.Contains(c.Key))
407-
.Select(c => $"{c.Key.AsBracketQuotedString()} {c.Value}");
406+
IEnumerable<string> bracketColumnDefinitionsFromItem = columnNamesFromItem.Select(c => $"{c.AsBracketQuotedString()} {table.Columns[c]}");
408407
newDataQuery = $"WITH {CteName} AS ( SELECT * FROM OPENJSON({RowDataParameter}) WITH ({string.Join(",", bracketColumnDefinitionsFromItem)}) )";
409408
}
410409

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using Microsoft.Azure.Functions.Worker;
5+
using Microsoft.Azure.Functions.Worker.Extensions.Sql;
6+
using DotnetIsolatedTests.Common;
7+
using Microsoft.AspNetCore.Http;
8+
9+
namespace DotnetIsolatedTests
10+
{
11+
public static class AddProductDefaultPKAndDifferentColumnOrder
12+
{
13+
/// <summary>
14+
/// This shows an example of a SQL Output binding where the target table has a default primary key
15+
/// of type uniqueidentifier and the column is not included in the output object. The order of the
16+
/// properties in the POCO is different from the order of the columns in the SQL table. A new row will
17+
/// be inserted and the uniqueidentifier will be generated by the engine.
18+
/// </summary>
19+
[Function("AddProductDefaultPKAndDifferentColumnOrder")]
20+
[SqlOutput("dbo.ProductsWithDefaultPK", "SqlConnectionString")]
21+
public static ProductDefaultPKWithDifferentColumnOrder Run(
22+
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "addproductdefaultpkanddifferentcolumnorder")] HttpRequest req)
23+
{
24+
var product = new ProductDefaultPKWithDifferentColumnOrder()
25+
{
26+
Cost = 100,
27+
Name = "test"
28+
};
29+
30+
return product;
31+
}
32+
}
33+
}

test-outofproc/GlobalSuppressions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@
1515
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:DotnetIsolatedTests.AddProductsNoPartialUpsert.Run(Microsoft.AspNetCore.Http.HttpRequest)~System.Collections.Generic.List{DotnetIsolatedTests.Common.Product}")]
1616
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:DotnetIsolatedTests.GetProductsColumnTypesSerialization.Run(Microsoft.AspNetCore.Http.HttpRequest,System.Collections.Generic.IEnumerable{DotnetIsolatedTests.Common.ProductColumnTypes})~System.Collections.Generic.IEnumerable{DotnetIsolatedTests.Common.ProductColumnTypes}")]
1717
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:DotnetIsolatedTests.AddProductIncorrectCasing.Run(Microsoft.Azure.Functions.Worker.Http.HttpRequestData)~DotnetIsolatedTests.Common.ProductIncorrectCasing")]
18-
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:DotnetIsolatedTests.AddProductUnsupportedTypes.Run(Microsoft.AspNetCore.Http.HttpRequest)~DotnetIsolatedTests.Common.ProductUnsupportedTypes")]
18+
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:DotnetIsolatedTests.AddProductUnsupportedTypes.Run(Microsoft.AspNetCore.Http.HttpRequest)~DotnetIsolatedTests.Common.ProductUnsupportedTypes")]
19+
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:DotnetIsolatedTests.AddProductDefaultPKAndDifferentColumnOrder.Run(Microsoft.AspNetCore.Http.HttpRequest)~DotnetIsolatedTests.Common.ProductDefaultPKWithDifferentColumnOrder")]

test-outofproc/Product.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,11 @@ public class ProductUnsupportedTypes
199199

200200
public byte[] ImageCol { get; set; }
201201
}
202+
203+
public class ProductDefaultPKWithDifferentColumnOrder
204+
{
205+
public int Cost { get; set; }
206+
207+
public string Name { get; set; }
208+
}
202209
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common
5+
{
6+
public class ProductDefaultPKAndDifferentColumnOrder
7+
{
8+
public int Cost { get; set; }
9+
10+
public string Name { get; set; }
11+
}
12+
}

test/GlobalSuppressions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@
2020
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples.GetProductsColumnTypesSerializationAsyncEnumerable.Run(Microsoft.AspNetCore.Http.HttpRequest,System.Collections.Generic.IAsyncEnumerable{Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common.ProductColumnTypes},Microsoft.Extensions.Logging.ILogger)~System.Threading.Tasks.Task{Microsoft.AspNetCore.Mvc.IActionResult}")]
2121
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples.GetProductsColumnTypesSerialization.Run(Microsoft.AspNetCore.Http.HttpRequest,System.Collections.Generic.IEnumerable{Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common.ProductColumnTypes},Microsoft.Extensions.Logging.ILogger)~Microsoft.AspNetCore.Mvc.IActionResult")]
2222
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration.AddProductIncorrectCasing.Run(Microsoft.AspNetCore.Http.HttpRequest,Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common.ProductIncorrectCasing@)~Microsoft.AspNetCore.Mvc.IActionResult")]
23-
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration.AddProductUnsupportedTypes.Run(Microsoft.AspNetCore.Http.HttpRequest,Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common.ProductUnsupportedTypes@)~Microsoft.AspNetCore.Mvc.IActionResult")]
23+
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration.AddProductUnsupportedTypes.Run(Microsoft.AspNetCore.Http.HttpRequest,Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common.ProductUnsupportedTypes@)~Microsoft.AspNetCore.Mvc.IActionResult")]
24+
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration.AddProductDefaultPKAndDifferentColumnOrder.Run(Microsoft.AspNetCore.Http.HttpRequest,Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common.ProductDefaultPKAndDifferentColumnOrder@)~Microsoft.AspNetCore.Mvc.IActionResult")]

test/Integration/SqlOutputBindingIntegrationTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,5 +523,20 @@ public async Task AddProductUnsupportedTypesTest(SupportedLanguages lang)
523523
Assert.Throws<AggregateException>(() => this.SendOutputGetRequest("addproduct-unsupportedtypes").Wait());
524524
await foundExpectedMessageSource.Task.TimeoutAfter(TimeSpan.FromMilliseconds(2000), $"Timed out waiting for expected error message");
525525
}
526+
527+
/// <summary>
528+
/// Tests that rows are inserted correctly when the table contains default values or identity columns even if the order of
529+
/// the properties in the POCO/JSON object is different from the order of the columns in the table.
530+
/// </summary>
531+
[Theory]
532+
[SqlInlineData()]
533+
public void AddProductDefaultPKAndDifferentColumnOrderTest(SupportedLanguages lang)
534+
{
535+
this.StartFunctionHost(nameof(AddProductDefaultPKAndDifferentColumnOrder), lang, true);
536+
537+
Assert.Equal(0, this.ExecuteScalar("SELECT COUNT(*) FROM dbo.ProductsWithDefaultPK"));
538+
this.SendOutputGetRequest("addproductdefaultpkanddifferentcolumnorder").Wait();
539+
Assert.Equal(1, this.ExecuteScalar("SELECT COUNT(*) FROM dbo.ProductsWithDefaultPK"));
540+
}
526541
}
527542
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using Microsoft.AspNetCore.Http;
5+
using Microsoft.AspNetCore.Mvc;
6+
using Microsoft.Azure.WebJobs.Extensions.Http;
7+
using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common;
8+
9+
namespace Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration
10+
{
11+
12+
public static class AddProductDefaultPKAndDifferentColumnOrder
13+
{
14+
/// <summary>
15+
/// This shows an example of a SQL Output binding where the target table has a default primary key
16+
/// of type uniqueidentifier and the column is not included in the output object. The order of the
17+
/// properties in the POCO is different from the order of the columns in the SQL table. A new row will
18+
/// be inserted and the uniqueidentifier will be generated by the engine.
19+
/// </summary>
20+
/// <param name="req">The original request that triggered the function</param>
21+
/// <param name="product">The created ProductDefaultPKAndDifferentColumnOrder object</param>
22+
/// <returns>The CreatedResult containing the new object that was inserted</returns>
23+
[FunctionName("AddProductDefaultPKAndDifferentColumnOrder")]
24+
public static IActionResult Run(
25+
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "addproductdefaultpkanddifferentcolumnorder")]
26+
HttpRequest req,
27+
[Sql("dbo.ProductsWithDefaultPK", "SqlConnectionString")] out ProductDefaultPKAndDifferentColumnOrder output)
28+
{
29+
output = new ProductDefaultPKAndDifferentColumnOrder
30+
{
31+
Cost = 100,
32+
Name = "test"
33+
};
34+
return new CreatedResult($"/api/addproductdefaultpkanddifferentcolumnorder", output);
35+
}
36+
}
37+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for
4+
* license information.
5+
*/
6+
7+
package com.function;
8+
9+
import com.microsoft.azure.functions.HttpMethod;
10+
import com.microsoft.azure.functions.HttpRequestMessage;
11+
import com.microsoft.azure.functions.HttpResponseMessage;
12+
import com.microsoft.azure.functions.HttpStatus;
13+
import com.microsoft.azure.functions.OutputBinding;
14+
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
15+
import com.microsoft.azure.functions.annotation.FunctionName;
16+
import com.microsoft.azure.functions.annotation.HttpTrigger;
17+
import com.microsoft.azure.functions.sql.annotation.SQLOutput;
18+
import com.function.Common.ProductDefaultPKAndDifferentColumnOrder;
19+
20+
import java.util.Optional;
21+
22+
/**
23+
* This shows an example of a SQL Output binding where the target table has a default primary key
24+
* of type uniqueidentifier and the column is not included in the output object. The order of the
25+
* properties in the POCO is different from the order of the columns in the SQL table. A new row will
26+
* be inserted and the uniqueidentifier will be generated by the engine.
27+
*/
28+
public class AddProductDefaultPKAndDifferentColumnOrder {
29+
@FunctionName("AddProductDefaultPKAndDifferentColumnOrder")
30+
public HttpResponseMessage run(
31+
@HttpTrigger(
32+
name = "req",
33+
methods = {HttpMethod.GET},
34+
authLevel = AuthorizationLevel.ANONYMOUS,
35+
route = "addproductdefaultpkanddifferentcolumnorder")
36+
HttpRequestMessage<Optional<String>> request,
37+
@SQLOutput(
38+
name = "product",
39+
commandText = "dbo.ProductsWithDefaultPK",
40+
connectionStringSetting = "SqlConnectionString")
41+
OutputBinding<ProductDefaultPKAndDifferentColumnOrder> product) {
42+
43+
ProductDefaultPKAndDifferentColumnOrder p = new ProductDefaultPKAndDifferentColumnOrder(
44+
100,
45+
"test");
46+
product.setValue(p);
47+
48+
return request.createResponseBuilder(HttpStatus.OK).header("Content-Type", "application/json").body(product).build();
49+
}
50+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for
4+
* license information.
5+
*/
6+
7+
package com.function.Common;
8+
9+
public class ProductDefaultPKAndDifferentColumnOrder {
10+
private int Cost;
11+
private String Name;
12+
13+
public ProductDefaultPKAndDifferentColumnOrder(int cost,String name) {
14+
Cost = cost;
15+
Name = name;
16+
}
17+
18+
public int getCost() {
19+
return Cost;
20+
}
21+
22+
public String getName() {
23+
return Name;
24+
}
25+
26+
}

0 commit comments

Comments
 (0)