@@ -19,6 +19,13 @@ export class TableSchema {
19
19
}
20
20
}
21
21
22
+ export interface SchemaAnalysisResult {
23
+ success : boolean ;
24
+ schemas : TableSchema [ ] ;
25
+ unresolvedColumns : string [ ] ;
26
+ error ?: string ;
27
+ }
28
+
22
29
/**
23
30
* A visitor that collects schema information (table names and column names) from a SQL query structure.
24
31
*/
@@ -29,6 +36,11 @@ export class SchemaCollector implements SqlComponentVisitor<void> {
29
36
private visitedNodes : Set < SqlComponent > = new Set ( ) ;
30
37
private commonTables : CommonTable [ ] = [ ] ;
31
38
private running = false ;
39
+
40
+ // For analyze method
41
+ private unresolvedColumns : string [ ] = [ ] ;
42
+ private analysisError : string | undefined = undefined ;
43
+ private isAnalyzeMode = false ;
32
44
33
45
constructor (
34
46
private tableColumnResolver : TableColumnResolver | null = null ,
@@ -53,6 +65,34 @@ export class SchemaCollector implements SqlComponentVisitor<void> {
53
65
return this . tableSchemas ;
54
66
}
55
67
68
+ /**
69
+ * Analyzes schema information from a SQL query structure without throwing errors.
70
+ * Returns a result object containing successfully resolved schemas, unresolved columns,
71
+ * and error information if any issues were encountered.
72
+ *
73
+ * @param arg The SQL query structure to analyze.
74
+ * @returns Analysis result containing schemas, unresolved columns, and success status.
75
+ */
76
+ public analyze ( arg : SqlComponent ) : SchemaAnalysisResult {
77
+ // Set analyze mode flag
78
+ this . isAnalyzeMode = true ;
79
+
80
+ try {
81
+ this . visit ( arg ) ;
82
+
83
+ // If we got here without errors, it's a success
84
+ return {
85
+ success : this . unresolvedColumns . length === 0 && ! this . analysisError ,
86
+ schemas : this . tableSchemas ,
87
+ unresolvedColumns : this . unresolvedColumns ,
88
+ error : this . analysisError
89
+ } ;
90
+ } finally {
91
+ // Reset analyze mode flag
92
+ this . isAnalyzeMode = false ;
93
+ }
94
+ }
95
+
56
96
/**
57
97
* Main entry point for the visitor pattern.
58
98
* Implements the shallow visit pattern to distinguish between root and recursive visits.
@@ -122,6 +162,8 @@ export class SchemaCollector implements SqlComponentVisitor<void> {
122
162
this . tableSchemas = [ ] ;
123
163
this . visitedNodes = new Set ( ) ;
124
164
this . commonTables = [ ] ;
165
+ this . unresolvedColumns = [ ] ;
166
+ this . analysisError = undefined ;
125
167
}
126
168
127
169
/**
@@ -235,11 +277,18 @@ export class SchemaCollector implements SqlComponentVisitor<void> {
235
277
} ) ) ;
236
278
}
237
279
238
- // Throw an error if there are columns without table names in queries with joins
280
+ // Handle columns without table names in queries with joins
239
281
if ( query . fromClause . joins !== null && query . fromClause . joins . length > 0 ) {
240
282
const columnsWithoutTable = queryColumns . filter ( ( columnRef ) => columnRef . table === "" ) . map ( ( columnRef ) => columnRef . column ) ;
241
283
if ( columnsWithoutTable . length > 0 ) {
242
- throw new Error ( `Column reference(s) without table name found in query: ${ columnsWithoutTable . join ( ', ' ) } ` ) ;
284
+ if ( this . isAnalyzeMode ) {
285
+ // In analyze mode, collect unresolved columns
286
+ this . unresolvedColumns . push ( ...columnsWithoutTable ) ;
287
+ this . analysisError = `Column reference(s) without table name found in query: ${ columnsWithoutTable . join ( ', ' ) } ` ;
288
+ } else {
289
+ // In collect mode, throw error as before
290
+ throw new Error ( `Column reference(s) without table name found in query: ${ columnsWithoutTable . join ( ', ' ) } ` ) ;
291
+ }
243
292
}
244
293
}
245
294
@@ -296,12 +345,25 @@ export class SchemaCollector implements SqlComponentVisitor<void> {
296
345
. filter ( ( columnRef ) => columnRef . column === "*" )
297
346
. length > 0 ;
298
347
299
- // Throw error if wildcard is found and allowWildcardWithoutResolver is false (default behavior)
348
+ // Handle error if wildcard is found and allowWildcardWithoutResolver is false (default behavior)
300
349
if ( hasWildcard && ! this . allowWildcardWithoutResolver ) {
301
350
const errorMessage = tableName
302
351
? `Wildcard (*) is used. A TableColumnResolver is required to resolve wildcards. Target table: ${ tableName } `
303
352
: "Wildcard (*) is used. A TableColumnResolver is required to resolve wildcards." ;
304
- throw new Error ( errorMessage ) ;
353
+
354
+ if ( this . isAnalyzeMode ) {
355
+ // In analyze mode, record the error but continue processing
356
+ this . analysisError = errorMessage ;
357
+ // Add wildcard columns to unresolved list
358
+ const wildcardColumns = queryColumns
359
+ . filter ( ( columnRef ) => columnRef . table === tableAlias || ( includeUnnamed && columnRef . table === "" ) )
360
+ . filter ( ( columnRef ) => columnRef . column === "*" )
361
+ . map ( ( columnRef ) => columnRef . table ? `${ columnRef . table } .*` : "*" ) ;
362
+ this . unresolvedColumns . push ( ...wildcardColumns ) ;
363
+ } else {
364
+ // In collect mode, throw error as before
365
+ throw new Error ( errorMessage ) ;
366
+ }
305
367
}
306
368
}
307
369
0 commit comments