@@ -42,9 +42,7 @@ type AliasTableInfo struct {
42
42
}
43
43
44
44
// Returns table info with just the table name
45
- func newTableInfoAlias (alias string ) * AliasTableInfo {
46
- return & AliasTableInfo {Name : alias }
47
- }
45
+
48
46
func (t * AliasTableInfo ) GetSchema () * string {
49
47
return nil
50
48
}
@@ -108,17 +106,6 @@ func NewQueryBuilder(defaultSchema, driver string, subsetByForeignKeyConstraints
108
106
}
109
107
}
110
108
111
- func (qb * QueryBuilder ) isJsonColumn (schematable , column string ) bool {
112
- tableInfo , ok := qb .columnInfo [schematable ]
113
- if ok {
114
- colInfo , ok := tableInfo [column ]
115
- if ok {
116
- return colInfo .DataType == "json"
117
- }
118
- }
119
- return false
120
- }
121
-
122
109
func (qb * QueryBuilder ) AddTable (table * TableInfo ) {
123
110
key := qb .getTableKey (table .Schema , table .Name )
124
111
qb .tables [key ] = table
@@ -165,7 +152,7 @@ func (qb *QueryBuilder) BuildQuery(schema, tableName string) (sqlstatement strin
165
152
if ! ok {
166
153
return "" , nil , fmt .Errorf ("table not found: %s" , key )
167
154
}
168
- query , err := qb .buildQueryRecursive (schema , tableName , nil , table .Columns , map [string ]int {}, map [string ]bool {})
155
+ query , err := qb .buildQueryRecursive (schema , tableName , table .Columns , map [string ]int {}, map [string ]bool {})
169
156
if err != nil {
170
157
return "" , nil , fmt .Errorf ("unable to build query for %s.%s: %w" , schema , tableName , err )
171
158
}
@@ -189,7 +176,7 @@ func (qb *QueryBuilder) isSelfReferencing(table *TableInfo) bool {
189
176
}
190
177
191
178
func (qb * QueryBuilder ) buildQueryRecursive (
192
- schema , tableName string , parentTable * TableInfo ,
179
+ schema , tableName string ,
193
180
columnsToInclude []string , joinCount map [string ]int ,
194
181
visitedTables map [string ]bool ,
195
182
) (* goqu.SelectDataset , error ) {
@@ -220,85 +207,51 @@ func (qb *QueryBuilder) buildQueryRecursive(
220
207
221
208
isSelfReferencing := qb .isSelfReferencing (table )
222
209
223
- if isSelfReferencing && qb .subsetByForeignKeyConstraints && len (qb .whereConditions ) > 0 && parentTable == nil {
224
- // Handle self-referencing table with possible additional foreign keys
225
- cteQuery , err := qb .buildRecursiveCTE (table , qb .whereConditions [key ])
226
- if err != nil {
227
- return nil , err
210
+ t := table .GetIdentifierExpression ()
211
+ cols := make ([]exp.Expression , len (columnsToInclude ))
212
+ for i := range columnsToInclude {
213
+ cols [i ] = t .Col (columnsToInclude [i ])
214
+ }
215
+ query = dialect .From (t ).Select (toAnySlice (cols )... )
216
+
217
+ // Add WHERE conditions for this table
218
+ if conditions , ok := qb .whereConditions [key ]; ok {
219
+ for _ , cond := range conditions {
220
+ qualifiedCondition , err := qb .qualifyWhereCondition (table , cond .Condition )
221
+ if err != nil {
222
+ return nil , err
223
+ }
224
+ query = query .Where (goqu .L (qualifiedCondition , cond .Args ... ))
228
225
}
229
- query = dialect . From ( cteQuery . As ( "recursive_cte" ))
226
+ }
230
227
231
- // Add joins for other foreign keys
228
+ // Only join and apply subsetting if subsetByForeignKeyConstraints is true
229
+ if qb .subsetByForeignKeyConstraints && len (qb .whereConditions ) > 0 {
230
+ // Recursively build and join queries for related tables
232
231
for _ , fk := range table .ForeignKeys {
233
- if fk .ReferenceSchema == table .Schema && fk .ReferenceTable == table .Name {
234
- continue // Skip the self-referencing foreign key
232
+ if isSelfReferencing && fk .ReferenceSchema == table .Schema && fk .ReferenceTable == table .Name {
233
+ continue // Skip self-referencing foreign keys here
235
234
}
236
- subQuery , err := qb .buildQueryRecursive (fk .ReferenceSchema , fk .ReferenceTable , table , fk .ReferenceColumns , joinCount , visitedTables )
235
+ subQuery , err := qb .buildQueryRecursive (fk .ReferenceSchema , fk .ReferenceTable , fk .ReferenceColumns , joinCount , visitedTables )
237
236
if err != nil {
238
237
return nil , err
239
238
}
239
+
240
240
if subQuery != nil {
241
241
joinCount [fk .ReferenceTable ]++
242
- subqueryAlias := fmt .Sprintf ("%s_%s_%d" , fk .ReferenceSchema , fk .ReferenceTable , joinCount [fk .ReferenceTable ])
242
+ subQueryAlias := fmt .Sprintf ("%s_%s_%d" , fk .ReferenceSchema , fk .ReferenceTable , joinCount [fk .ReferenceTable ])
243
243
conditions := make ([]goqu.Expression , len (fk .Columns ))
244
244
for i := range fk .Columns {
245
- conditions [i ] = goqu . T ( "recursive_cte" ) .Col (fk .Columns [i ]).Eq (
246
- goqu .T (subqueryAlias ).Col (fk .ReferenceColumns [i ]),
245
+ conditions [i ] = t .Col (fk .Columns [i ]).Eq (
246
+ goqu .T (subQueryAlias ).Col (fk .ReferenceColumns [i ]),
247
247
)
248
248
}
249
249
query = query .Join (
250
- goqu .L ("(?)" , subQuery ).As (subqueryAlias ),
250
+ goqu .L ("(?)" , subQuery ).As (subQueryAlias ),
251
251
goqu .On (goqu .And (conditions ... )),
252
252
)
253
253
}
254
254
}
255
- } else {
256
- t := table .GetIdentifierExpression ()
257
- cols := make ([]exp.Expression , len (columnsToInclude ))
258
- for i := range columnsToInclude {
259
- cols [i ] = t .Col (columnsToInclude [i ])
260
- }
261
- query = dialect .From (t ).Select (toAnySlice (cols )... )
262
-
263
- // Add WHERE conditions for this table
264
- if conditions , ok := qb .whereConditions [key ]; ok {
265
- for _ , cond := range conditions {
266
- qualifiedCondition , err := qb .qualifyWhereCondition (table , cond .Condition )
267
- if err != nil {
268
- return nil , err
269
- }
270
- query = query .Where (goqu .L (qualifiedCondition , cond .Args ... ))
271
- }
272
- }
273
-
274
- // Only join and apply subsetting if subsetByForeignKeyConstraints is true
275
- if qb .subsetByForeignKeyConstraints && len (qb .whereConditions ) > 0 {
276
- // Recursively build and join queries for related tables
277
- for _ , fk := range table .ForeignKeys {
278
- if isSelfReferencing && fk .ReferenceSchema == table .Schema && fk .ReferenceTable == table .Name {
279
- continue // Skip self-referencing foreign keys here
280
- }
281
- subQuery , err := qb .buildQueryRecursive (fk .ReferenceSchema , fk .ReferenceTable , table , fk .ReferenceColumns , joinCount , visitedTables )
282
- if err != nil {
283
- return nil , err
284
- }
285
-
286
- if subQuery != nil {
287
- joinCount [fk .ReferenceTable ]++
288
- subQueryAlias := fmt .Sprintf ("%s_%s_%d" , fk .ReferenceSchema , fk .ReferenceTable , joinCount [fk .ReferenceTable ])
289
- conditions := make ([]goqu.Expression , len (fk .Columns ))
290
- for i := range fk .Columns {
291
- conditions [i ] = t .Col (fk .Columns [i ]).Eq (
292
- goqu .T (subQueryAlias ).Col (fk .ReferenceColumns [i ]),
293
- )
294
- }
295
- query = query .Join (
296
- goqu .L ("(?)" , subQuery ).As (subQueryAlias ),
297
- goqu .On (goqu .And (conditions ... )),
298
- )
299
- }
300
- }
301
- }
302
255
}
303
256
304
257
return query , nil
@@ -430,96 +383,13 @@ func qualifyMysqlWhereColumnNames(sql string, schema *string, table string) (str
430
383
return sqlparser .String (stmt ), nil
431
384
}
432
385
433
- func (qb * QueryBuilder ) getQualifiedTableName (table * TableInfo ) exp.IdentifierExpression {
434
- schema := table .Schema
435
- if schema == "" {
436
- schema = qb .defaultSchema
437
- }
438
- return goqu .T (table .Name ).Schema (schema )
439
- }
440
-
441
386
func (qb * QueryBuilder ) getTableKey (schema , tableName string ) string {
442
387
if schema == "" {
443
388
schema = qb .defaultSchema
444
389
}
445
390
return fmt .Sprintf ("%s.%s" , schema , tableName )
446
391
}
447
392
448
- const (
449
- hierarchyTableName = "hierarchy"
450
- )
451
-
452
- func (qb * QueryBuilder ) buildRecursiveCTE (
453
- table * TableInfo ,
454
- whereConditions []WhereCondition ,
455
- ) (* goqu.SelectDataset , error ) {
456
- dialect := qb .getDialect ()
457
-
458
- baseTable := qb .getQualifiedTableName (table ).As ("b" )
459
- baseColumns := make ([]exp.Expression , len (table .Columns ))
460
- tableKey := qb .getTableKey (table .Schema , table .Name )
461
- for i , col := range table .Columns {
462
- if qb .isJsonColumn (tableKey , col ) {
463
- // json the type has no comparison operator and thus can't union but jsonb does
464
- baseColumns [i ] = goqu .L ("to_jsonb(?)" , baseTable .Col (col )).As (col )
465
- } else {
466
- baseColumns [i ] = baseTable .Col (col )
467
- }
468
- }
469
-
470
- // Base case
471
- baseQuery := dialect .
472
- From (baseTable ).
473
- Select (toAnySlice (baseColumns )... )
474
- qualifiedWheres := []goqu.Expression {}
475
- for _ , whereCond := range whereConditions {
476
- qualifiedCondition , err := qb .qualifyWhereCondition (newTableInfoAlias ("b" ), whereCond .Condition )
477
- if err != nil {
478
- return nil , err
479
- }
480
- qualifiedWheres = append (qualifiedWheres , goqu .L (qualifiedCondition , whereCond .Args ... ))
481
- }
482
- if len (qualifiedWheres ) > 0 {
483
- baseQuery = baseQuery .Where (goqu .And (qualifiedWheres ... ))
484
- }
485
-
486
- recursiveTable := qb .getQualifiedTableName (table ).As ("r" )
487
- hierarchicalTable := goqu .T (hierarchyTableName ).As ("h" )
488
-
489
- recursiveColumns := make ([]exp.IdentifierExpression , len (table .Columns ))
490
- for i , col := range table .Columns {
491
- recursiveColumns [i ] = recursiveTable .Col (col )
492
- }
493
-
494
- joinConditions := []exp.Expression {}
495
- for _ , fk := range table .ForeignKeys {
496
- if fk .ReferenceSchema == table .Schema && fk .ReferenceTable == table .Name {
497
- for i , col := range fk .Columns {
498
- joinConditions = append (joinConditions ,
499
- recursiveTable .Col (fk .ReferenceColumns [i ]).Eq (hierarchicalTable .Col (col )),
500
- )
501
- }
502
- }
503
- }
504
-
505
- recursiveQuery := dialect .
506
- From (recursiveTable ).
507
- Select (toAnySlice (recursiveColumns )... ).
508
- Join (
509
- hierarchicalTable ,
510
- goqu .On (
511
- goqu .Or (joinConditions ... ),
512
- ),
513
- )
514
-
515
- cte := dialect .
516
- From (hierarchyTableName ).
517
- Select (goqu .Star ()).
518
- Distinct ().
519
- WithRecursive (hierarchyTableName , baseQuery .Union (recursiveQuery ))
520
- return cte , nil
521
- }
522
-
523
393
func toAnySlice [T any ](input []T ) []any {
524
394
anys := make ([]any , len (input ))
525
395
for i := range input {
0 commit comments