Skip to content

Commit f54cab2

Browse files
authored
Merge pull request #38 from liquid207/feat/issue-30
Implement feature request #30 — Support UNION
2 parents 94cc6d4 + 7d4cd60 commit f54cab2

File tree

1 file changed

+59
-0
lines changed

1 file changed

+59
-0
lines changed

src/converter.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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(/^DB::table/, '');
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

Comments
 (0)