@@ -62,6 +62,7 @@ internal sealed class ColumnMetadata
6262 public bool IsColumnSet { get ; init ; }
6363 public string ? GeneratedAlwaysType { get ; init ; }
6464 public bool ? IsIdentity { get ; init ; }
65+ public bool ? IsUniqueKey { get ; init ; }
6566}
6667
6768/// <summary>
@@ -139,7 +140,8 @@ public EnhancedSchemaMetadataProvider(
139140 if ( indexColumn != null )
140141 {
141142 _console . Verbose ( $ "[enhanced-schema] Resolved { schema } .{ tableName } .{ columnName } from snapshot index") ;
142- return MapIndexColumn ( indexColumn , columnName ) ;
143+ var mapped = MapIndexColumn ( indexColumn , columnName ) ;
144+ return await EnrichIndexColumnWithLiveAsync ( mapped , schema , tableName , columnName , catalog , cancellationToken ) . ConfigureAwait ( false ) ;
143145 }
144146
145147 var indexColumns = await _snapshotIndexProvider . GetTableColumnsMetadataAsync ( schema , tableName , cancellationToken ) . ConfigureAwait ( false ) ;
@@ -152,7 +154,8 @@ public EnhancedSchemaMetadataProvider(
152154 if ( fallback != null )
153155 {
154156 _console . Verbose ( $ "[enhanced-schema] Resolved { schema } .{ tableName } .{ columnName } from snapshot index (table scan)") ;
155- return MapIndexColumn ( fallback , columnName ) ;
157+ var mapped = MapIndexColumn ( fallback , columnName ) ;
158+ return await EnrichIndexColumnWithLiveAsync ( mapped , schema , tableName , columnName , catalog , cancellationToken ) . ConfigureAwait ( false ) ;
156159 }
157160 }
158161 catch ( Exception ex )
@@ -197,7 +200,7 @@ public async Task<IReadOnlyList<ColumnMetadata>> GetTableColumnsAsync(string sch
197200 var indexColumns = await LoadColumnsFromSnapshotIndexAsync ( schema , tableName , catalog , cancellationToken ) . ConfigureAwait ( false ) ;
198201 if ( indexColumns . Count > 0 )
199202 {
200- return indexColumns ;
203+ return await EnrichIndexColumnsWithLiveAsync ( indexColumns , schema , tableName , catalog , cancellationToken ) . ConfigureAwait ( false ) ;
201204 }
202205
203206 return await LoadColumnsFromLiveFallbacksAsync ( schema , tableName , catalog , cancellationToken ) . ConfigureAwait ( false ) ;
@@ -244,6 +247,129 @@ private async Task<IReadOnlyList<ColumnMetadata>> LoadColumnsFromSnapshotIndexAs
244247 return Array . Empty < ColumnMetadata > ( ) ;
245248 }
246249
250+ private async Task < ColumnMetadata > EnrichIndexColumnWithLiveAsync (
251+ ColumnMetadata indexColumn ,
252+ string schema ,
253+ string tableName ,
254+ string columnName ,
255+ string ? catalog ,
256+ CancellationToken cancellationToken )
257+ {
258+ if ( _dbContext == null )
259+ {
260+ return indexColumn ;
261+ }
262+
263+ var liveColumn = await ResolveFromDatabaseAsync ( schema , tableName , columnName , catalog , cancellationToken ) . ConfigureAwait ( false ) ;
264+ if ( liveColumn == null )
265+ {
266+ return indexColumn ;
267+ }
268+
269+ return MergeIndexAndLive ( indexColumn , liveColumn ) ;
270+ }
271+
272+ private async Task < IReadOnlyList < ColumnMetadata > > EnrichIndexColumnsWithLiveAsync (
273+ IReadOnlyList < ColumnMetadata > indexColumns ,
274+ string schema ,
275+ string tableName ,
276+ string ? catalog ,
277+ CancellationToken cancellationToken )
278+ {
279+ if ( _dbContext == null )
280+ {
281+ return indexColumns ;
282+ }
283+
284+ var liveColumns = await GetTableColumnsFromLiveSourcesAsync ( schema , tableName , catalog , cancellationToken ) . ConfigureAwait ( false ) ;
285+ if ( liveColumns . Count == 0 )
286+ {
287+ return indexColumns ;
288+ }
289+
290+ var liveMap = new Dictionary < string , ColumnMetadata > ( StringComparer . OrdinalIgnoreCase ) ;
291+ foreach ( var live in liveColumns )
292+ {
293+ if ( live != null && ! string . IsNullOrWhiteSpace ( live . Name ) )
294+ {
295+ liveMap [ live . Name ] = live ;
296+ }
297+ }
298+
299+ var merged = new List < ColumnMetadata > ( Math . Max ( indexColumns . Count , liveMap . Count ) ) ;
300+ var seen = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) ;
301+
302+ foreach ( var indexColumn in indexColumns )
303+ {
304+ if ( indexColumn == null || string . IsNullOrWhiteSpace ( indexColumn . Name ) )
305+ {
306+ continue ;
307+ }
308+
309+ if ( liveMap . TryGetValue ( indexColumn . Name , out var live ) )
310+ {
311+ merged . Add ( MergeIndexAndLive ( indexColumn , live ) ) ;
312+ }
313+ else
314+ {
315+ merged . Add ( indexColumn ) ;
316+ }
317+
318+ seen . Add ( indexColumn . Name ) ;
319+ }
320+
321+ foreach ( var live in liveMap . Values )
322+ {
323+ if ( live == null || string . IsNullOrWhiteSpace ( live . Name ) || seen . Contains ( live . Name ) )
324+ {
325+ continue ;
326+ }
327+
328+ merged . Add ( live ) ;
329+ }
330+
331+ _console . Verbose ( $ "[enhanced-schema] Merged { indexColumns . Count } snapshot index column(s) with { liveMap . Count } live column(s) for { schema } .{ tableName } ") ;
332+ return merged ;
333+ }
334+
335+ private static ColumnMetadata MergeIndexAndLive ( ColumnMetadata indexColumn , ColumnMetadata liveColumn )
336+ {
337+ var name = ! string . IsNullOrWhiteSpace ( indexColumn . Name ) ? indexColumn . Name : liveColumn . Name ;
338+ var sqlTypeName = ! string . IsNullOrWhiteSpace ( indexColumn . SqlTypeName ) ? indexColumn . SqlTypeName : liveColumn . SqlTypeName ;
339+ var maxLength = indexColumn . MaxLength ?? liveColumn . MaxLength ;
340+ var precision = indexColumn . Precision ?? liveColumn . Precision ;
341+ var scale = indexColumn . Scale ?? liveColumn . Scale ;
342+ var userTypeSchema = indexColumn . UserTypeSchema ?? liveColumn . UserTypeSchema ;
343+ var userTypeName = indexColumn . UserTypeName ?? liveColumn . UserTypeName ;
344+
345+ return new ColumnMetadata
346+ {
347+ Name = name ?? string . Empty ,
348+ Catalog = liveColumn . Catalog ?? indexColumn . Catalog ,
349+ SqlTypeName = sqlTypeName ?? string . Empty ,
350+ IsNullable = liveColumn . IsNullable ,
351+ MaxLength = maxLength ,
352+ Precision = precision ,
353+ Scale = scale ,
354+ UserTypeSchema = userTypeSchema ,
355+ UserTypeName = userTypeName ,
356+ IsFromSnapshot = false ,
357+ HasDefaultValue = liveColumn . HasDefaultValue ,
358+ DefaultDefinition = liveColumn . DefaultDefinition ,
359+ DefaultConstraintName = liveColumn . DefaultConstraintName ,
360+ IsComputed = liveColumn . IsComputed ,
361+ ComputedDefinition = liveColumn . ComputedDefinition ,
362+ IsComputedPersisted = liveColumn . IsComputedPersisted ,
363+ IsRowGuid = liveColumn . IsRowGuid ,
364+ IsSparse = liveColumn . IsSparse ,
365+ IsHidden = liveColumn . IsHidden ,
366+ IsColumnSet = liveColumn . IsColumnSet ,
367+ GeneratedAlwaysType = liveColumn . GeneratedAlwaysType ,
368+ IsIdentity = liveColumn . IsIdentity ?? indexColumn . IsIdentity ,
369+ IsUniqueKey = liveColumn . IsUniqueKey ?? indexColumn . IsUniqueKey
370+ } ;
371+ }
372+
247373 private async Task < IReadOnlyList < ColumnMetadata > > LoadColumnsFromLiveFallbacksAsync ( string schema , string tableName , string ? catalog , CancellationToken cancellationToken )
248374 {
249375 if ( _dbContext == null )
@@ -403,7 +529,8 @@ private static ColumnMetadata MapSnapshotColumn(SnapshotTableColumn column, stri
403529 IsHidden = column . IsHidden == true ,
404530 IsColumnSet = column . IsColumnSet == true ,
405531 GeneratedAlwaysType = NormalizeGeneratedAlwaysType ( column . GeneratedAlwaysType ) ,
406- IsIdentity = column . IsIdentity == true
532+ IsIdentity = column . IsIdentity == true ,
533+ IsUniqueKey = column . IsUniqueKey == true
407534 } ;
408535 }
409536
@@ -463,7 +590,8 @@ private static ColumnMetadata MapDatabaseColumn(Column column, string? fallbackC
463590 IsHidden = column . IsHidden ,
464591 IsColumnSet = column . IsColumnSet ,
465592 GeneratedAlwaysType = NormalizeGeneratedAlwaysType ( column . GeneratedAlwaysType ) ,
466- IsIdentity = isIdentity
593+ IsIdentity = isIdentity ,
594+ IsUniqueKey = column . IsUniqueKey
467595 } ;
468596 }
469597
@@ -485,7 +613,8 @@ private static ColumnMetadata MapIndexColumn(IndexColumnEntry column, string fal
485613 UserTypeSchema = userTypeSchema ,
486614 UserTypeName = userTypeName ,
487615 IsFromSnapshot = true ,
488- IsIdentity = column . IsIdentity
616+ IsIdentity = column . IsIdentity ,
617+ IsUniqueKey = column . IsUniqueKey
489618 } ;
490619 }
491620
0 commit comments