From 3942f7b4460c15865a7df1aad01761b803344cfc Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Sun, 19 Jul 2026 22:19:24 +0100 Subject: [PATCH] Require explicit LCID to be specified in globalization invariant mode --- .../Data/SqlClient/Server/SqlMetaData.cs | 12 ++++++ .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 4 ++ .../src/Resources/Strings.Designer.cs | 9 +++++ .../src/Resources/Strings.resx | 3 ++ .../tests/FunctionalTests/SqlMetaDataTest.cs | 38 +++++++++++++++++++ 5 files changed, 66 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlMetaData.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlMetaData.cs index 2c3c6acaab..98a7bbfcfc 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlMetaData.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlMetaData.cs @@ -536,6 +536,7 @@ internal string UdtTypeName // Construction for all types that do not have variable attributes private void Construct(string name, SqlDbType dbType, bool useServerDefault, bool isUniqueKey, SortOrder columnSortOrder, int sortOrdinal) { + ValidateGlobalizationInvariantLcid(dbType); AssertNameIsValid(name); ValidateSortOrder(columnSortOrder, sortOrdinal); @@ -586,6 +587,7 @@ private void Construct(string name, SqlDbType dbType, bool useServerDefault, boo // Construction for all types that vary by user-specified length (not Udts) private void Construct(string name, SqlDbType dbType, long maxLength, bool useServerDefault, bool isUniqueKey, SortOrder columnSortOrder, int sortOrdinal) { + ValidateGlobalizationInvariantLcid(dbType); AssertNameIsValid(name); ValidateSortOrder(columnSortOrder, sortOrdinal); @@ -929,6 +931,16 @@ private void ValidateSortOrder(SortOrder columnSortOrder, int sortOrdinal) } } + private static void ValidateGlobalizationInvariantLcid(SqlDbType dbType) + { + if (LocalAppContextSwitches.GlobalizationInvariantMode && + (dbType is SqlDbType.Char or SqlDbType.VarChar or SqlDbType.Text + or SqlDbType.NChar or SqlDbType.NVarChar or SqlDbType.NText)) + { + throw SQL.GlobalizationInvariantModeRequiresLcid(); + } + } + /// public short Adjust(short value) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs index c9388a42f1..d420a1ca3c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -204,6 +204,10 @@ internal static Exception ChangePasswordUseOfUnallowedKey(string key) { return ADP.InvalidOperation(StringsHelper.GetString(Strings.SQL_ChangePasswordUseOfUnallowedKey, key)); } + internal static Exception GlobalizationInvariantModeRequiresLcid() + { + return ADP.NotSupported(StringsHelper.GetString(Strings.SQL_GlobalizationInvariantModeRequiresLcid)); + } internal static Exception GlobalizationInvariantModeNotSupported() { return ADP.NotSupported(StringsHelper.GetString(Strings.SQL_GlobalizationInvariantModeNotSupported)); diff --git a/src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs index 7064a6c19c..9e14a4234e 100644 --- a/src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs @@ -3336,6 +3336,15 @@ internal static string SQL_GlobalizationInvariantModeNotSupported { } } + /// + /// Looks up a localized string similar to When Globalization Invariant Mode is enabled, SqlMetaData instances describing character or text types must specify an LCID.. + /// + internal static string SQL_GlobalizationInvariantModeRequiresLcid { + get { + return ResourceManager.GetString("SQL_GlobalizationInvariantModeRequiresLcid", resourceCulture); + } + } + /// /// Looks up a localized string similar to Instance failure.. /// diff --git a/src/Microsoft.Data.SqlClient/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/src/Resources/Strings.resx index e7f438873f..3fcdb6bec6 100644 --- a/src/Microsoft.Data.SqlClient/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/src/Resources/Strings.resx @@ -2091,6 +2091,9 @@ Exception occurred while trying to set the AppContext Switch '{0}'={1}. + + When Globalization Invariant Mode is enabled, SqlMetaData instances describing character or text types must specify an LCID. + Globalization Invariant Mode is not supported. diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlMetaDataTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlMetaDataTest.cs index cd316a9eab..f466338b10 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlMetaDataTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlMetaDataTest.cs @@ -9,6 +9,7 @@ using System.Globalization; using System.Reflection; using Microsoft.Data.SqlClient.Server; +using Microsoft.Data.SqlClient.Tests.Common; using Xunit; namespace Microsoft.Data.SqlClient.Tests @@ -228,6 +229,43 @@ public void ConstructorWithDefaultLocale() Assert.Equal(0, metaData.SortOrdinal); } + #if NET + [Theory] + [MemberData(nameof(ConstructorCharData))] + [MemberData(nameof(ConstructorTextData))] + public void ConstructorWithDefaultLocale_ThrowsInInvariantGlobalizationMode(SqlDbType dbType) + { + using LocalAppContextSwitchesHelper helper = new(); + + helper.GlobalizationInvariantMode = true; + + Assert.Throws(() => new SqlMetaData("col1", dbType)); + Assert.Throws(() => new SqlMetaData("col1", dbType, true, true, SortOrder.Ascending, 0)); + Assert.Throws(() => new SqlMetaData("col1", dbType, 0)); + Assert.Throws(() => new SqlMetaData("col1", dbType, 0, true, true, SortOrder.Ascending, 0)); + } + + [Theory] + [MemberData(nameof(ConstructorCharData))] + public void ConstructorWithMaxLengthAndExplicitLocale_DoesNotThrowInInvariantGlobalizationMode(SqlDbType dbType) + { + using LocalAppContextSwitchesHelper helper = new(); + + helper.GlobalizationInvariantMode = true; + ConstructorWithMaxLengthAndLocale(dbType); + } + + [Theory] + [MemberData(nameof(ConstructorTextData))] + public void ConstructorWithMaxLengthTextExplicitLocale_DoesNotThrowInInvariantGlobalizationMode(SqlDbType dbType) + { + using LocalAppContextSwitchesHelper helper = new(); + + helper.GlobalizationInvariantMode = true; + ConstructorWithMaxLengthTextAndLocale(dbType); + } + #endif + [Fact] public void ConstructorWithDefaultLocaleInvalidType_Throws() {