From 4842395790a68d451b0ee70daee6bc6f4b0d0890 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Wed, 8 Jan 2025 13:28:51 -0600 Subject: [PATCH 1/4] Prepare for Tables release --- sdk/tables/Azure.Data.Tables/CHANGELOG.md | 6 +----- sdk/tables/Azure.Data.Tables/src/Azure.Data.Tables.csproj | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/sdk/tables/Azure.Data.Tables/CHANGELOG.md b/sdk/tables/Azure.Data.Tables/CHANGELOG.md index a7f7638829dc..ae42e957d1c0 100644 --- a/sdk/tables/Azure.Data.Tables/CHANGELOG.md +++ b/sdk/tables/Azure.Data.Tables/CHANGELOG.md @@ -1,15 +1,11 @@ # Release History -## 12.10.0-beta.1 (Unreleased) - -### Features Added +## 12.10.0 (2025-01-07) ### Breaking Changes - Calling `TableClient.Query`, `TableClient.QueryAsync`, or `TableClient.CreateQueryFilter` with a filter expression that uses `string.Equals` or `string.Compare` with a `StringComparison` parameter will now throw an exception. This is because the Azure Table service does not support these methods in query filters. Previously the `StringComparison` argument was silently ignored, which can lead to subtle bugs in client code. -### Bugs Fixed - ### Other Changes - Improved the performance of `TableServiceClient.GetTableClient()` diff --git a/sdk/tables/Azure.Data.Tables/src/Azure.Data.Tables.csproj b/sdk/tables/Azure.Data.Tables/src/Azure.Data.Tables.csproj index 608c3c3cfcb8..d561f9c1a6ad 100644 --- a/sdk/tables/Azure.Data.Tables/src/Azure.Data.Tables.csproj +++ b/sdk/tables/Azure.Data.Tables/src/Azure.Data.Tables.csproj @@ -2,7 +2,7 @@ This client library enables working with the Microsoft Azure Table service Microsoft Azure.Data.Tables client library - 12.10.0-beta.1 + 12.10.0 12.9.1 TableSDK;$(DefineConstants) From e978aa72f0cbaa48c84398328ef983aafbf770ef Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Wed, 8 Jan 2025 14:36:40 -0600 Subject: [PATCH 2/4] add app compat switch for the breaking change for filter queries --- sdk/tables/Azure.Data.Tables/CHANGELOG.md | 9 +++++- .../src/Queryable/ExpressionNormalizer.cs | 6 ++-- .../Azure.Data.Tables/src/TableConstants.cs | 2 ++ .../src/TablesCompatSwitches.cs | 5 ++++ .../tests/TableClientQueryExpressionTests.cs | 28 +++++++++++++++++++ 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/sdk/tables/Azure.Data.Tables/CHANGELOG.md b/sdk/tables/Azure.Data.Tables/CHANGELOG.md index ae42e957d1c0..fdd569860a0f 100644 --- a/sdk/tables/Azure.Data.Tables/CHANGELOG.md +++ b/sdk/tables/Azure.Data.Tables/CHANGELOG.md @@ -4,7 +4,14 @@ ### Breaking Changes -- Calling `TableClient.Query`, `TableClient.QueryAsync`, or `TableClient.CreateQueryFilter` with a filter expression that uses `string.Equals` or `string.Compare` with a `StringComparison` parameter will now throw an exception. This is because the Azure Table service does not support these methods in query filters. Previously the `StringComparison` argument was silently ignored, which can lead to subtle bugs in client code. +- Calling `TableClient.Query`, `TableClient.QueryAsync`, or `TableClient.CreateQueryFilter` with a filter expression that uses `string.Equals` or `string.Compare` with a `StringComparison` parameter will now throw an exception. This is because the Azure Table service does not support these methods in query filters. Previously the `StringComparison` argument was silently ignored, which can lead to subtle bugs in client code. The new behavior can be overridden by either setting an AppContext switch named "Azure.Data.Tables.DisableThrowOnStringComparisonFilter" to `true` or by setting the environment variable "AZURE_DATA_TABLES_DISABLE_THROWONSTRINGCOMPARISONFILTER" to "true". Note: AppContext switches can also be configured via configuration like below: + +```xml + + + + ``` + ### Other Changes - Improved the performance of `TableServiceClient.GetTableClient()` diff --git a/sdk/tables/Azure.Data.Tables/src/Queryable/ExpressionNormalizer.cs b/sdk/tables/Azure.Data.Tables/src/Queryable/ExpressionNormalizer.cs index 1bfe6237cd08..e9cf61276ae2 100644 --- a/sdk/tables/Azure.Data.Tables/src/Queryable/ExpressionNormalizer.cs +++ b/sdk/tables/Azure.Data.Tables/src/Queryable/ExpressionNormalizer.cs @@ -162,7 +162,7 @@ internal Expression VisitMethodCallNoRewrite(MethodCallExpression call) if (visited.Method.IsStatic && visited.Method.Name == "Equals" && visited.Arguments.Count > 1) { - if (visited.Arguments.Count > 2) + if (visited.Arguments.Count > 2 && !TablesCompatSwitches.DisableThrowOnStringComparisonFilter) { throw new NotSupportedException("string.Equals method with more than two arguments is not supported."); } @@ -171,7 +171,7 @@ internal Expression VisitMethodCallNoRewrite(MethodCallExpression call) if (!visited.Method.IsStatic && visited.Method.Name == "Equals" && visited.Arguments.Count > 0) { - if (visited.Arguments.Count > 1) + if (visited.Arguments.Count > 1 && !TablesCompatSwitches.DisableThrowOnStringComparisonFilter) { throw new NotSupportedException("Equals method with more than two arguments is not supported."); } @@ -190,7 +190,7 @@ internal Expression VisitMethodCallNoRewrite(MethodCallExpression call) if (visited.Method.IsStatic && visited.Method.Name == "Compare" && visited.Arguments.Count > 1 && visited.Method.ReturnType == typeof(int)) { - if (visited.Arguments.Count > 2) + if (visited.Arguments.Count > 2 && !TablesCompatSwitches.DisableThrowOnStringComparisonFilter) { throw new NotSupportedException("string.Compare method with more than two arguments is not supported."); } diff --git a/sdk/tables/Azure.Data.Tables/src/TableConstants.cs b/sdk/tables/Azure.Data.Tables/src/TableConstants.cs index 91f847232b8c..857bfb266efb 100644 --- a/sdk/tables/Azure.Data.Tables/src/TableConstants.cs +++ b/sdk/tables/Azure.Data.Tables/src/TableConstants.cs @@ -17,6 +17,8 @@ internal static class CompatSwitches public const string DisableEscapeSingleQuotesOnGetEntityEnvVar = "AZURE_DATA_TABLES_DISABLE_ESCAPESINGLEQUOTESONGETENTITY"; public const string DisableEscapeSingleQuotesOnDeleteEntitySwitchName = "Azure.Data.Tables.DisableEscapeSingleQuotesOnDeleteEntity"; public const string DisableEscapeSingleQuotesOnDeleteEntityEnvVar = "AZURE_DATA_TABLES_DISABLE_ESCAPESINGLEQUOTESONDELETEENTITY"; + public const string DisableThrowOnStringComparisonFilterSwitchName = "Azure.Data.Tables.DisableThrowOnStringComparisonFilter"; + public const string DisableThrowOnStringComparisonFilterEnvVar = "AZURE_DATA_TABLES_DISABLE_THROWONSTRINGCOMPARISONFILTER"; } internal static class HeaderNames diff --git a/sdk/tables/Azure.Data.Tables/src/TablesCompatSwitches.cs b/sdk/tables/Azure.Data.Tables/src/TablesCompatSwitches.cs index 8994bb8123a3..fb232b7a1ede 100644 --- a/sdk/tables/Azure.Data.Tables/src/TablesCompatSwitches.cs +++ b/sdk/tables/Azure.Data.Tables/src/TablesCompatSwitches.cs @@ -16,5 +16,10 @@ public static bool DisableEscapeSingleQuotesOnDeleteEntity => AppContextSwitchHelper.GetConfigValue( TableConstants.CompatSwitches.DisableEscapeSingleQuotesOnDeleteEntitySwitchName, TableConstants.CompatSwitches.DisableEscapeSingleQuotesOnDeleteEntityEnvVar); + + public static bool DisableThrowOnStringComparisonFilter + => AppContextSwitchHelper.GetConfigValue( + TableConstants.CompatSwitches.DisableThrowOnStringComparisonFilterSwitchName, + TableConstants.CompatSwitches.DisableThrowOnStringComparisonFilterEnvVar); } } diff --git a/sdk/tables/Azure.Data.Tables/tests/TableClientQueryExpressionTests.cs b/sdk/tables/Azure.Data.Tables/tests/TableClientQueryExpressionTests.cs index d3799dc0571e..2629515afeab 100644 --- a/sdk/tables/Azure.Data.Tables/tests/TableClientQueryExpressionTests.cs +++ b/sdk/tables/Azure.Data.Tables/tests/TableClientQueryExpressionTests.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Linq.Expressions; using System.Xml; +using Azure.Core.TestFramework; using Azure.Data.Tables.Models; using NUnit.Framework; using static Azure.Data.Tables.Tests.TableServiceLiveTestsBase; @@ -213,9 +214,36 @@ public void TestDictionaryTableEntityFilterExpressions(string expectedFilter, Ex [TestCaseSource(nameof(UnSupportedTableItemExpressionTestCases))] [Test] + [NonParallelizable] public void TestTableItemFilterExpressionsUnsupported(Expression> expression) { Assert.Throws(() => TableClient.CreateQueryFilter(expression)); } + + [TestCaseSource(nameof(UnSupportedTableItemExpressionTestCases))] + [Test] + [NonParallelizable] + public void TestTableItemFilterExpressionsUnsupportedDoesNotThrowWithCompatSwitch(Expression> expression) + { + if (expression == s_TEequalsStaticUnsupported) + { + Assert.Ignore("Ignore this expression because it was never supported."); + } + using var ctx = new TestAppContextSwitch(TableConstants.CompatSwitches.DisableThrowOnStringComparisonFilterSwitchName, true.ToString()); + TableClient.CreateQueryFilter(expression); + } + + [TestCaseSource(nameof(UnSupportedTableItemExpressionTestCases))] + [Test] + [NonParallelizable] + public void TestTableItemFilterExpressionsUnsupportedDoesNotThrowWithCompatSwitchEnv(Expression> expression) + { + if (expression == s_TEequalsStaticUnsupported) + { + Assert.Ignore("Ignore this expression because it was never supported."); + } + using var env = new TestEnvVar(TableConstants.CompatSwitches.DisableThrowOnStringComparisonFilterEnvVar, true.ToString()); + TableClient.CreateQueryFilter(expression); + } } } From 2ed7b6b597642766a2baea0dd7c9463bf1ba9b62 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Wed, 8 Jan 2025 16:37:32 -0600 Subject: [PATCH 3/4] fb --- .../tests/TableClientQueryExpressionTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/tables/Azure.Data.Tables/tests/TableClientQueryExpressionTests.cs b/sdk/tables/Azure.Data.Tables/tests/TableClientQueryExpressionTests.cs index 2629515afeab..8eb213edd6b1 100644 --- a/sdk/tables/Azure.Data.Tables/tests/TableClientQueryExpressionTests.cs +++ b/sdk/tables/Azure.Data.Tables/tests/TableClientQueryExpressionTests.cs @@ -177,7 +177,7 @@ public class TableClientQueryExpressionTests new object[] { $"PartitionKey eq '{Partition}'", s_tableEntExpEquals }, }; - public static object[] UnSupportedTableItemExpressionTestCases = + public static object[] UnsupportedTableItemExpressionTestCases = { new object[] { s_TEequalsUnsupported }, new object[] { s_TEequalsStaticUnsupported }, @@ -212,7 +212,7 @@ public void TestDictionaryTableEntityFilterExpressions(string expectedFilter, Ex Assert.That(filter, Is.EqualTo(expectedFilter)); } - [TestCaseSource(nameof(UnSupportedTableItemExpressionTestCases))] + [TestCaseSource(nameof(UnsupportedTableItemExpressionTestCases))] [Test] [NonParallelizable] public void TestTableItemFilterExpressionsUnsupported(Expression> expression) @@ -220,7 +220,7 @@ public void TestTableItemFilterExpressionsUnsupported(Expression(() => TableClient.CreateQueryFilter(expression)); } - [TestCaseSource(nameof(UnSupportedTableItemExpressionTestCases))] + [TestCaseSource(nameof(UnsupportedTableItemExpressionTestCases))] [Test] [NonParallelizable] public void TestTableItemFilterExpressionsUnsupportedDoesNotThrowWithCompatSwitch(Expression> expression) @@ -233,7 +233,7 @@ public void TestTableItemFilterExpressionsUnsupportedDoesNotThrowWithCompatSwitc TableClient.CreateQueryFilter(expression); } - [TestCaseSource(nameof(UnSupportedTableItemExpressionTestCases))] + [TestCaseSource(nameof(UnsupportedTableItemExpressionTestCases))] [Test] [NonParallelizable] public void TestTableItemFilterExpressionsUnsupportedDoesNotThrowWithCompatSwitchEnv(Expression> expression) From 72d98d2287c7b8ea02d045804ad56a5bc3036258 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Wed, 8 Jan 2025 16:54:30 -0600 Subject: [PATCH 4/4] Update release date in CHANGELOG.md --- sdk/tables/Azure.Data.Tables/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/tables/Azure.Data.Tables/CHANGELOG.md b/sdk/tables/Azure.Data.Tables/CHANGELOG.md index fdd569860a0f..4e0552458bdf 100644 --- a/sdk/tables/Azure.Data.Tables/CHANGELOG.md +++ b/sdk/tables/Azure.Data.Tables/CHANGELOG.md @@ -1,6 +1,6 @@ # Release History -## 12.10.0 (2025-01-07) +## 12.10.0 (2025-01-14) ### Breaking Changes