Skip to content

Commit 2915bd3

Browse files
committed
Improve procedure analyzer error reporting
1 parent 4ca0467 commit 2915bd3

2 files changed

Lines changed: 88 additions & 6 deletions

File tree

src/SpocRVNext/SnapshotBuilder/Analyzers/DatabaseProcedureAnalyzer.cs

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ void AddDependency(ProcedureDependencyKind kind, string? schema, string? name)
140140
{
141141
try
142142
{
143-
await EnrichJsonResultSetMetadataAsync(ast, cancellationToken).ConfigureAwait(false);
143+
await EnrichJsonResultSetMetadataAsync(descriptor, ast, cancellationToken).ConfigureAwait(false);
144144
}
145145
catch (Exception ex)
146146
{
@@ -231,11 +231,13 @@ private static string BuildSnapshotFileName(ProcedureDescriptor descriptor)
231231
return $"{schema}.{name}.json";
232232
}
233233

234-
private async Task EnrichJsonResultSetMetadataAsync(StoredProcedureContentModel ast, CancellationToken cancellationToken)
234+
private async Task EnrichJsonResultSetMetadataAsync(ProcedureDescriptor descriptor, StoredProcedureContentModel ast, CancellationToken cancellationToken)
235235
{
236236
if (ast?.ResultSets == null || ast.ResultSets.Count == 0) return;
237237

238238
var tableCache = new Dictionary<string, Dictionary<string, Column>>(StringComparer.OrdinalIgnoreCase);
239+
var unresolvedColumns = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
240+
var descriptorLabel = FormatProcedureLabel(descriptor);
239241

240242
foreach (var resultSet in ast.ResultSets)
241243
{
@@ -247,23 +249,30 @@ private async Task EnrichJsonResultSetMetadataAsync(StoredProcedureContentModel
247249

248250
foreach (var column in resultSet.Columns ?? Array.Empty<StoredProcedureContentModel.ResultColumn>())
249251
{
250-
await EnrichColumnRecursiveAsync(column, tableCache, cancellationToken).ConfigureAwait(false);
252+
var initialPath = column?.Name;
253+
await EnrichColumnRecursiveAsync(column, tableCache, descriptorLabel, initialPath, unresolvedColumns, cancellationToken).ConfigureAwait(false);
251254
}
252255
}
253256
}
254257

255258
private async Task EnrichColumnRecursiveAsync(
256259
StoredProcedureContentModel.ResultColumn? column,
257260
Dictionary<string, Dictionary<string, Column>> tableCache,
261+
string descriptorLabel,
262+
string? path,
263+
ISet<string> unresolvedColumns,
258264
CancellationToken cancellationToken)
259265
{
260266
if (column == null) return;
261267

268+
var currentPath = string.IsNullOrWhiteSpace(path) ? column.Name : path;
269+
262270
if (column.Columns != null && column.Columns.Count > 0)
263271
{
264272
foreach (var child in column.Columns)
265273
{
266-
await EnrichColumnRecursiveAsync(child, tableCache, cancellationToken).ConfigureAwait(false);
274+
var childPath = CombinePath(currentPath, child?.Name);
275+
await EnrichColumnRecursiveAsync(child, tableCache, descriptorLabel, childPath, unresolvedColumns, cancellationToken).ConfigureAwait(false);
267276
}
268277
}
269278

@@ -275,7 +284,20 @@ private async Task EnrichColumnRecursiveAsync(
275284
if (column.ReturnsJson == true) return;
276285

277286
var metadata = await ResolveColumnMetadataAsync(column, tableCache, cancellationToken).ConfigureAwait(false);
278-
if (metadata == null) return;
287+
if (metadata == null)
288+
{
289+
var warningPath = !string.IsNullOrWhiteSpace(currentPath)
290+
? currentPath
291+
: !string.IsNullOrWhiteSpace(column.Name) ? column.Name : "(unnamed)";
292+
293+
if (!string.IsNullOrWhiteSpace(warningPath) && unresolvedColumns.Add(string.Concat(descriptorLabel, "|", warningPath)))
294+
{
295+
var sourceDetails = BuildColumnSourceDetails(column);
296+
_console.Warn($"[snapshot-analyze] Type resolution failed for column '{warningPath}' in {descriptorLabel}{sourceDetails}. Snapshot will omit sqlType metadata.");
297+
}
298+
299+
return;
300+
}
279301

280302
if (!string.IsNullOrWhiteSpace(metadata.SqlType))
281303
{
@@ -358,7 +380,66 @@ private async Task ApplyFunctionJsonMetadataAsync(StoredProcedureContentModel.Re
358380
return heuristic;
359381
}
360382

361-
return new ColumnMetadata("nvarchar(max)", null, null, null, column.IsNullable ?? true, column.UserTypeSchemaName, column.UserTypeName);
383+
return null;
384+
}
385+
386+
private static string? CombinePath(string? parent, string? child)
387+
{
388+
if (string.IsNullOrWhiteSpace(child)) return parent;
389+
if (string.IsNullOrWhiteSpace(parent)) return child;
390+
return string.Concat(parent, ".", child);
391+
}
392+
393+
private static string FormatProcedureLabel(ProcedureDescriptor descriptor)
394+
{
395+
if (descriptor == null)
396+
{
397+
return "(unknown procedure)";
398+
}
399+
400+
var schema = descriptor.Schema?.Trim();
401+
var name = descriptor.Name?.Trim();
402+
403+
if (string.IsNullOrWhiteSpace(schema))
404+
{
405+
return string.IsNullOrWhiteSpace(name) ? "(unknown procedure)" : name;
406+
}
407+
408+
if (string.IsNullOrWhiteSpace(name))
409+
{
410+
return schema;
411+
}
412+
413+
return string.Concat(schema, ".", name);
414+
}
415+
416+
private static string BuildColumnSourceDetails(StoredProcedureContentModel.ResultColumn column)
417+
{
418+
if (column == null) return string.Empty;
419+
420+
var parts = new List<string>();
421+
if (!string.IsNullOrWhiteSpace(column.SourceSchema)) parts.Add(column.SourceSchema);
422+
if (!string.IsNullOrWhiteSpace(column.SourceTable)) parts.Add(column.SourceTable);
423+
424+
var location = parts.Count > 0 ? string.Join('.', parts) : null;
425+
if (!string.IsNullOrWhiteSpace(column.SourceColumn))
426+
{
427+
location = string.IsNullOrWhiteSpace(location)
428+
? column.SourceColumn
429+
: string.Concat(location, '.', column.SourceColumn);
430+
}
431+
432+
if (!string.IsNullOrWhiteSpace(location))
433+
{
434+
return string.Concat(" (source: ", location, ")");
435+
}
436+
437+
if (!string.IsNullOrWhiteSpace(column.SourceAlias))
438+
{
439+
return string.Concat(" (source alias: ", column.SourceAlias, ")");
440+
}
441+
442+
return string.Empty;
362443
}
363444

364445
private async Task<Column?> GetTableColumnAsync(

src/SpocRVNext/SnapshotBuilder/CHECKLIST.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
- [x] debug\.spocr\schema\procedures\workflow-state.TransitionFindAsJson.json `SqlTypeName` scheint redundant zu sein, können wir die Property komplett entfernen, da alles über `TypeRef` ableitbar? [x] leeres `"Json": {},` vermeiden.
9999
- [x] debug\.spocr\schema\procedures\workflow.NodeListAsJson.json: "FunctionRef": "identity.RecordAsJson" dürfte kein Array sein, da die referenzierte Funktion kein Array liefert. Siehe debug\[workflow]_[NodeListAsJson].sql und debug\[identity]_[RecordAsJson].sql (benötigen wir hier die `Json` Property überhaupt, wenn wir `FunctionRef` haben? Oder kann diese Eigenschaft in anderen Fällen abweichen?)
100100
- [x] src\SpocRVNext\SnapshotBuilder\Metadata: TableType- und UDT-Queries als Provider ausgekoppelt (`DatabaseTableTypeMetadataProvider`, `DatabaseUserDefinedTypeMetadataProvider`); StoredProcedures laufen weiterhin über Collector/Analyzer.
101+
- [ ] debug\.spocr\cache\schema hier werden noch alle Daten, die eigentlich aus dem Snapshot hervorgehen redundant gespeichert. Sollten hier nicht nur Metadaten in den Cache?
101102

102103
## Artefakte
103104

0 commit comments

Comments
 (0)