@@ -9,6 +9,65 @@ export class Converter
99 run ( need_append_get_suffix = true ) {
1010 let sections = [ ]
1111
12+ // Handle UNION / UNION ALL set operations at top-level
13+ if ( propertyExistsInObjectAndNotNull ( this . ast . body , 'SetOperation' ) || propertyExistsInObjectAndNotNull ( this . ast . body , 'SetExpr' ) ) {
14+ // Support several possible AST shapes (SetOperation or SetExpr)
15+ let set_node = this . ast . body . SetOperation || this . ast . body . SetExpr || this . ast . body ;
16+
17+ // helper to extract left/right and op
18+ let left = set_node . left || set_node . l || null ;
19+ let right = set_node . right || set_node . r || null ;
20+ let op = set_node . op || set_node . operator || set_node . SetOperator || null ;
21+
22+ // if nested structure (e.g. { Unnamed: { SetOperation: { ... } } }) try deeper
23+ if ( ! left && propertyExistsInObjectAndNotNull ( set_node , 'Unnamed' ) && propertyExistsInObjectAndNotNull ( set_node . Unnamed , 'SetOperation' ) ) {
24+ set_node = set_node . Unnamed . SetOperation ;
25+ left = set_node . left ; right = set_node . right ; op = set_node . op ;
26+ }
27+
28+ if ( ! left || ! right ) {
29+ throw 'Logic error, unhandled set operation node structure' ;
30+ }
31+
32+ // convert left and right subqueries to Converter outputs without get();
33+ let left_sql = ( new Converter ( left . Query || left , this ) . run ( false ) ) . replace ( / ^ D B : : t a b l e / , '' ) ;
34+ // create converter for right
35+ let right_converter = new Converter ( right . Query || right , this ) ;
36+ let right_sql = right_converter . run ( false ) ;
37+
38+ // choose union method
39+ let union_method = 'union' ;
40+ // detect union all keywords
41+ if ( op && ( op . toString ( ) . toLowerCase ( ) . includes ( 'all' ) || ( op . type && op . type . toLowerCase && op . type . toLowerCase ( ) . includes ( 'all' ) ) ) ) {
42+ union_method = 'unionAll' ;
43+ } else if ( typeof op === 'string' && op . toLowerCase ( ) . includes ( 'union all' ) ) {
44+ union_method = 'unionAll' ;
45+ }
46+
47+ // assemble: start from left (which is expected to be a DB::table(...) prefix), then chain ->union( DB::table(...) ... )
48+ // left_sql currently may start with "DB::table('...')" or "->from('...')" when nested; ensure proper prefix
49+ let base = ( new Converter ( left . Query || left , this ) . run ( false ) ) ;
50+
51+ // Build union call: pass the right query builder expression as a DB::raw of the SQL from right_converter without trailing get();
52+ // Prefer passing DB::table(...) closure to preserve query builder: use function($query) { $query->from(...)->... }
53+ let right_closure = 'function ($query) {\n\t' + right_sql . replace ( 'DB::table' , '$query->from' ) . split ( '\n' ) . join ( '\n\t' ) + ';\n}' ;
54+
55+ let union_section = base + '\n->' + union_method + '(' + right_closure + ')' ;
56+
57+ // If top-level has order_by / limit / offset, append them
58+ if ( propertyExistsInObjectAndNotNull ( this . ast , 'order_by' ) && this . ast . order_by . length > 0 ) {
59+ union_section = union_section + '\n->' + ( new Converter ( this . ast , this ) . resolveOrderBySection ( ) ) ;
60+ } else if ( propertyExistsInObjectAndNotNull ( this . ast , 'order_by' ) && this . ast . order_by . length === 0 && propertyExistsInObjectAndNotNull ( this . ast . body , 'order_by' ) && this . ast . body . order_by . length > 0 ) {
61+ union_section = union_section + '\n->' + ( new Converter ( this . ast . body , this ) . resolveOrderBySection ( ) ) ;
62+ }
63+
64+ if ( need_append_get_suffix ) {
65+ union_section = union_section + '\n->get();' ;
66+ }
67+
68+ return union_section ;
69+ }
70+
1271 let from_item = this . ast . body . Select . from [ 0 ] ;
1372
1473 if ( propertyExistsInObjectAndNotNull ( from_item . relation , 'Table' ) ) {
0 commit comments