@@ -41,17 +41,6 @@ private enum CollationFlags : ushort
4141 VariationSelectorSensitive = 1 << 10 ,
4242 }
4343
44- /// <summary>
45- /// Per-name override registry. Populated by <see cref="RegisterOverride"/>
46- /// at static-init time. Names in this map bypass <see cref="TryParse"/>
47- /// and return the registered instance directly — the extension point
48- /// for hand-tuned bodies with quirky semantics that the parser's
49- /// generic <see cref="CultureCollation"/> / <see cref="BinaryCollationBody"/>
50- /// constructions don't capture.
51- /// </summary>
52- private static readonly ConcurrentDictionary < string , Collation > overrides =
53- new ( StringComparer . OrdinalIgnoreCase ) ;
54-
5544 /// <summary>
5645 /// Interning cache for parser-derived collation instances. Keyed by the
5746 /// canonical (uppercase) name; <see cref="TryGet"/> inserts on first
@@ -64,41 +53,29 @@ private enum CollationFlags : ushort
6453 /// <summary>
6554 /// Returns the recognized <see cref="Collation"/> for <paramref name="name"/>,
6655 /// or <see langword="null"/> if the name isn't grammatically valid +
67- /// doesn't carry a known prefix. Override-registry entries take
68- /// precedence over the parser; subsequent calls with the same name
69- /// return the same reference (interned).
56+ /// doesn't carry a known prefix. Subsequent calls with the same name
57+ /// return the same reference (interned). The default collation name is
58+ /// special-cased inside <see cref="CreateInstance"/> to wrap its comparer
59+ /// in the byte-exact <see cref="SqlLatin1Cp1CiAsCollation"/>.
7060 /// </summary>
7161 internal static Collation ? TryGet ( string name ) =>
7262 string . IsNullOrEmpty ( name )
7363 ? null
74- : overrides . TryGetValue ( name , out var ov )
75- ? ov
76- : interned . TryGetValue ( name , out var hit )
77- ? hit
78- : TryParse ( name , out var parsed ) ? interned . GetOrAdd ( parsed . Name , parsed ) : null ;
79-
80- /// <summary>
81- /// Adds <paramref name="specialized"/> to the override registry under
82- /// its <see cref="Name"/>. Overrides take precedence over parser-derived
83- /// instances; the slot supports per-name behavior tuning when the
84- /// parser's generic body doesn't match real SQL Server's quirks for
85- /// that specific collation.
86- /// </summary>
87- private static void RegisterOverride ( Collation specialized ) =>
88- overrides [ specialized . Name ] = specialized ;
64+ : interned . TryGetValue ( name , out var hit )
65+ ? hit
66+ : TryParse ( name , out var parsed ) ? interned . GetOrAdd ( parsed . Name , parsed ) : null ;
8967
9068 /// <summary>
9169 /// True if <paramref name="name"/> is a recognized SQL Server collation
92- /// name — either in the override registry or grammatically parseable
93- /// with a known prefix. Replaces the legacy whitelist check.
70+ /// name — grammatically parseable with a known prefix. Replaces the legacy
71+ /// whitelist check.
9472 /// </summary>
9573 internal static bool IsRecognized ( string name ) => TryGet ( name ) is not null ;
9674
9775 /// <summary>
9876 /// Enumerates every recognized collation name with its description
9977 /// (the form <c>sys.fn_helpcollations()</c> exposes). Crosses the
100- /// SQL_* sort-order table, the per-prefix tail-set patterns, and any
101- /// override-registry entries that fall outside those (rare). Ordering
78+ /// SQL_* sort-order table and the per-prefix tail-set patterns. Ordering
10279 /// is the caller's responsibility.
10380 /// </summary>
10481 internal static IEnumerable < ( string Name , string Description ) > EnumerateRecognized ( )
@@ -121,33 +98,6 @@ private static void RegisterOverride(Collation specialized) =>
12198 yield return ( name , c . Description ) ;
12299 }
123100 }
124-
125- // Override entries whose names don't appear in either slice.
126- foreach ( var ( name , c ) in overrides )
127- {
128- if ( ! SqlServerSortOrders . ContainsKey ( name ) && ! IsInPatternCatalog ( name ) )
129- yield return ( name , c . Description ) ;
130- }
131- }
132-
133- /// <summary>
134- /// Tests whether <paramref name="name"/> falls in the per-prefix tail
135- /// catalog — used to avoid double-emitting an override that the
136- /// pattern enumeration would also produce.
137- /// </summary>
138- private static bool IsInPatternCatalog ( string name )
139- {
140- foreach ( var ( prefix , patternIdx ) in PrefixToPattern )
141- {
142- if ( name . StartsWith ( prefix , StringComparison . OrdinalIgnoreCase )
143- && name . Length > prefix . Length
144- && name [ prefix . Length ] == '_'
145- && GetPatternTails ( patternIdx ) . Contains ( name [ prefix . Length ..] ) )
146- {
147- return true ;
148- }
149- }
150- return false ;
151101 }
152102
153103 /// <summary>
@@ -338,7 +288,14 @@ private static Collation CreateInstance(string name, string prefix, PrefixInfo p
338288 // SC behavior is engaged when the _SC_ flag is set explicitly or
339289 // when the version is v140+ (where SC is implicit / default).
340290 var scAware = flags . HasFlag ( CollationFlags . SupplementaryCharacters ) || version is >= 140 ;
341- return new CultureCollation ( name , description , prefixInfo . CultureName , caseSensitive , kanaSensitive , widthSensitive , storageEncoding , scAware ) ;
291+ var cultureBody = new CultureCollation ( name , description , prefixInfo . CultureName , caseSensitive , kanaSensitive , widthSensitive , storageEncoding , scAware ) ;
292+
293+ // The default collation gets a byte-exact sort body wrapping the
294+ // generic culture comparer (which still supplies metadata + the
295+ // non-CP1252 fallback). See Collation.SqlLatin1Sort.cs.
296+ return name . Equals ( SqlLatin1Cp1CiAsCollation . CollationName , StringComparison . OrdinalIgnoreCase )
297+ ? new SqlLatin1Cp1CiAsCollation ( cultureBody )
298+ : cultureBody ;
342299 }
343300
344301 /// <summary>
0 commit comments