sumak 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +255 -5
- package/dist/ast/expression.d.mts +26 -0
- package/dist/ast/expression.mjs +140 -0
- package/dist/ast/nodes.d.mts +298 -0
- package/dist/ast/nodes.mjs +59 -0
- package/dist/ast/transformer.d.mts +10 -0
- package/dist/ast/transformer.mjs +140 -0
- package/dist/ast/typed-expression.d.mts +37 -0
- package/dist/ast/typed-expression.mjs +77 -0
- package/dist/ast/visitor.d.mts +13 -0
- package/dist/ast/visitor.mjs +11 -0
- package/dist/builder/delete.d.mts +18 -0
- package/dist/builder/delete.mjs +94 -0
- package/dist/builder/eb.d.mts +210 -0
- package/dist/builder/eb.mjs +399 -0
- package/dist/builder/expression.d.mts +5 -0
- package/dist/builder/expression.mjs +10 -0
- package/dist/builder/insert.d.mts +40 -0
- package/dist/builder/insert.mjs +146 -0
- package/dist/builder/merge.d.mts +20 -0
- package/dist/builder/merge.mjs +100 -0
- package/dist/builder/raw.d.mts +2 -0
- package/dist/builder/raw.mjs +4 -0
- package/dist/builder/select.d.mts +38 -0
- package/dist/builder/select.mjs +242 -0
- package/dist/builder/typed-delete.d.mts +43 -0
- package/dist/builder/typed-delete.mjs +77 -0
- package/dist/builder/typed-insert.d.mts +74 -0
- package/dist/builder/typed-insert.mjs +136 -0
- package/dist/builder/typed-merge.d.mts +31 -0
- package/dist/builder/typed-merge.mjs +93 -0
- package/dist/builder/typed-select.d.mts +125 -0
- package/dist/builder/typed-select.mjs +217 -0
- package/dist/builder/typed-update.d.mts +55 -0
- package/dist/builder/typed-update.mjs +102 -0
- package/dist/builder/update.d.mts +18 -0
- package/dist/builder/update.mjs +102 -0
- package/dist/dialect/mssql.d.mts +2 -0
- package/dist/dialect/mssql.mjs +9 -0
- package/dist/dialect/mysql.d.mts +2 -0
- package/dist/dialect/mysql.mjs +9 -0
- package/dist/dialect/pg.d.mts +2 -0
- package/dist/dialect/pg.mjs +9 -0
- package/dist/dialect/sqlite.d.mts +2 -0
- package/dist/dialect/sqlite.mjs +9 -0
- package/dist/dialect/types.d.mts +6 -0
- package/dist/dialect/types.mjs +1 -0
- package/dist/errors.d.mts +12 -0
- package/dist/errors.mjs +24 -0
- package/dist/index.d.mts +49 -806
- package/dist/index.mjs +46 -3
- package/dist/mssql.d.mts +2 -2
- package/dist/mssql.mjs +2 -1
- package/dist/mysql.d.mts +2 -2
- package/dist/mysql.mjs +2 -1
- package/dist/pg.d.mts +2 -2
- package/dist/pg.mjs +2 -1
- package/dist/plugin/camel-case.d.mts +11 -0
- package/dist/plugin/camel-case.mjs +16 -0
- package/dist/plugin/hooks.d.mts +72 -0
- package/dist/plugin/hooks.mjs +49 -0
- package/dist/plugin/plugin-manager.d.mts +17 -0
- package/dist/plugin/plugin-manager.mjs +37 -0
- package/dist/plugin/soft-delete.d.mts +27 -0
- package/dist/plugin/soft-delete.mjs +52 -0
- package/dist/plugin/types.d.mts +19 -0
- package/dist/plugin/types.mjs +1 -0
- package/dist/plugin/with-schema.d.mts +21 -0
- package/dist/plugin/with-schema.mjs +53 -0
- package/dist/printer/base.d.mts +48 -0
- package/dist/printer/base.mjs +450 -0
- package/dist/printer/document.d.mts +45 -0
- package/dist/printer/document.mjs +153 -0
- package/dist/printer/formatter.d.mts +5 -0
- package/dist/printer/formatter.mjs +134 -0
- package/dist/printer/mssql.d.mts +10 -0
- package/dist/printer/mssql.mjs +161 -0
- package/dist/printer/mysql.d.mts +8 -0
- package/dist/printer/mysql.mjs +41 -0
- package/dist/printer/pg.d.mts +6 -0
- package/dist/printer/pg.mjs +9 -0
- package/dist/printer/sqlite.d.mts +8 -0
- package/dist/printer/sqlite.mjs +29 -0
- package/dist/printer/types.d.mts +11 -0
- package/dist/printer/types.mjs +1 -0
- package/dist/schema/column.d.mts +52 -0
- package/dist/schema/column.mjs +120 -0
- package/dist/schema/index.d.mts +6 -0
- package/dist/schema/index.mjs +4 -0
- package/dist/schema/table.d.mts +37 -0
- package/dist/schema/table.mjs +7 -0
- package/dist/schema/type-utils.d.mts +46 -0
- package/dist/schema/type-utils.mjs +1 -0
- package/dist/schema/types.d.mts +64 -0
- package/dist/schema/types.mjs +1 -0
- package/dist/schema.d.mts +2 -2
- package/dist/schema.mjs +1 -1
- package/dist/sqlite.d.mts +2 -2
- package/dist/sqlite.mjs +2 -1
- package/dist/sumak.d.mts +98 -0
- package/dist/sumak.mjs +132 -0
- package/dist/types.d.mts +14 -0
- package/dist/types.mjs +1 -0
- package/dist/utils/identifier.d.mts +3 -0
- package/dist/utils/identifier.mjs +14 -0
- package/dist/utils/param.d.mts +2 -0
- package/dist/utils/param.mjs +8 -0
- package/package.json +1 -1
- package/dist/_chunks/base.mjs +0 -1
- package/dist/_chunks/errors.mjs +0 -1
- package/dist/_chunks/index.d.mts +0 -136
- package/dist/_chunks/mssql.d.mts +0 -11
- package/dist/_chunks/mssql.mjs +0 -1
- package/dist/_chunks/mysql.d.mts +0 -9
- package/dist/_chunks/mysql.mjs +0 -1
- package/dist/_chunks/pg.d.mts +0 -7
- package/dist/_chunks/pg.mjs +0 -1
- package/dist/_chunks/schema.mjs +0 -1
- package/dist/_chunks/sqlite.d.mts +0 -9
- package/dist/_chunks/sqlite.mjs +0 -1
- package/dist/_chunks/types.d.mts +0 -338
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
import { quoteIdentifier, quoteTableRef } from "../utils/identifier.mjs";
|
|
2
|
+
import { formatParam } from "../utils/param.mjs";
|
|
3
|
+
export class BasePrinter {
|
|
4
|
+
params = [];
|
|
5
|
+
dialect;
|
|
6
|
+
constructor(dialect) {
|
|
7
|
+
this.dialect = dialect;
|
|
8
|
+
}
|
|
9
|
+
print(node) {
|
|
10
|
+
this.params = [];
|
|
11
|
+
const sql = this.printNode(node);
|
|
12
|
+
return {
|
|
13
|
+
sql,
|
|
14
|
+
params: [...this.params]
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
printNode(node) {
|
|
18
|
+
switch (node.type) {
|
|
19
|
+
case "select": return this.printSelect(node);
|
|
20
|
+
case "insert": return this.printInsert(node);
|
|
21
|
+
case "update": return this.printUpdate(node);
|
|
22
|
+
case "delete": return this.printDelete(node);
|
|
23
|
+
case "merge": return this.printMerge(node);
|
|
24
|
+
case "explain": return this.printExplain(node);
|
|
25
|
+
default: return this.printExpression(node);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
printSelect(node) {
|
|
29
|
+
const parts = [];
|
|
30
|
+
if (node.ctes.length > 0) {
|
|
31
|
+
parts.push(this.printCTEs(node.ctes));
|
|
32
|
+
}
|
|
33
|
+
parts.push("SELECT");
|
|
34
|
+
if (node.distinctOn && node.distinctOn.length > 0) {
|
|
35
|
+
parts.push(`DISTINCT ON (${node.distinctOn.map((e) => this.printExpression(e)).join(", ")})`);
|
|
36
|
+
} else if (node.distinct) {
|
|
37
|
+
parts.push("DISTINCT");
|
|
38
|
+
}
|
|
39
|
+
if (node.columns.length === 0) {
|
|
40
|
+
parts.push("*");
|
|
41
|
+
} else {
|
|
42
|
+
parts.push(node.columns.map((c) => this.printExpression(c)).join(", "));
|
|
43
|
+
}
|
|
44
|
+
if (node.from) {
|
|
45
|
+
parts.push("FROM");
|
|
46
|
+
if (node.from.type === "subquery") {
|
|
47
|
+
parts.push(this.printSubquery(node.from));
|
|
48
|
+
} else {
|
|
49
|
+
parts.push(this.printTableRef(node.from));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
for (const join of node.joins) {
|
|
53
|
+
parts.push(this.printJoin(join));
|
|
54
|
+
}
|
|
55
|
+
if (node.where) {
|
|
56
|
+
parts.push("WHERE", this.printExpression(node.where));
|
|
57
|
+
}
|
|
58
|
+
if (node.groupBy.length > 0) {
|
|
59
|
+
parts.push("GROUP BY", node.groupBy.map((g) => this.printExpression(g)).join(", "));
|
|
60
|
+
}
|
|
61
|
+
if (node.having) {
|
|
62
|
+
parts.push("HAVING", this.printExpression(node.having));
|
|
63
|
+
}
|
|
64
|
+
if (node.orderBy.length > 0) {
|
|
65
|
+
parts.push("ORDER BY", node.orderBy.map((o) => this.printOrderBy(o)).join(", "));
|
|
66
|
+
}
|
|
67
|
+
if (node.limit) {
|
|
68
|
+
parts.push("LIMIT", this.printExpression(node.limit));
|
|
69
|
+
}
|
|
70
|
+
if (node.offset) {
|
|
71
|
+
parts.push("OFFSET", this.printExpression(node.offset));
|
|
72
|
+
}
|
|
73
|
+
if (node.setOp) {
|
|
74
|
+
parts.push(node.setOp.op, this.printSelect(node.setOp.query));
|
|
75
|
+
}
|
|
76
|
+
if (node.lock) {
|
|
77
|
+
parts.push(`FOR ${node.lock.mode}`);
|
|
78
|
+
if (node.lock.noWait) {
|
|
79
|
+
parts.push("NOWAIT");
|
|
80
|
+
} else if (node.lock.skipLocked) {
|
|
81
|
+
parts.push("SKIP LOCKED");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return parts.join(" ");
|
|
85
|
+
}
|
|
86
|
+
printInsert(node) {
|
|
87
|
+
const parts = [];
|
|
88
|
+
if (node.ctes.length > 0) {
|
|
89
|
+
parts.push(this.printCTEs(node.ctes));
|
|
90
|
+
}
|
|
91
|
+
const insertKeyword = node.insertMode ?? "INSERT";
|
|
92
|
+
parts.push(`${insertKeyword} INTO`, this.printTableRef(node.table));
|
|
93
|
+
if (node.columns.length > 0) {
|
|
94
|
+
parts.push(`(${node.columns.map((c) => quoteIdentifier(c, this.dialect)).join(", ")})`);
|
|
95
|
+
}
|
|
96
|
+
if (node.defaultValues) {
|
|
97
|
+
parts.push("DEFAULT VALUES");
|
|
98
|
+
} else if (node.source) {
|
|
99
|
+
parts.push(this.printSelect(node.source));
|
|
100
|
+
} else {
|
|
101
|
+
parts.push("VALUES");
|
|
102
|
+
const rows = node.values.map((row) => `(${row.map((v) => this.printExpression(v)).join(", ")})`);
|
|
103
|
+
parts.push(rows.join(", "));
|
|
104
|
+
}
|
|
105
|
+
if (node.onConflict) {
|
|
106
|
+
parts.push(this.printOnConflict(node.onConflict));
|
|
107
|
+
}
|
|
108
|
+
if (node.returning.length > 0) {
|
|
109
|
+
parts.push("RETURNING", node.returning.map((r) => this.printExpression(r)).join(", "));
|
|
110
|
+
}
|
|
111
|
+
return parts.join(" ");
|
|
112
|
+
}
|
|
113
|
+
printOnConflict(node) {
|
|
114
|
+
const parts = ["ON CONFLICT"];
|
|
115
|
+
if (node.constraint) {
|
|
116
|
+
parts.push(`ON CONSTRAINT ${quoteIdentifier(node.constraint, this.dialect)}`);
|
|
117
|
+
} else if (node.columns.length > 0) {
|
|
118
|
+
parts.push(`(${node.columns.map((c) => quoteIdentifier(c, this.dialect)).join(", ")})`);
|
|
119
|
+
}
|
|
120
|
+
if (node.action === "nothing") {
|
|
121
|
+
parts.push("DO NOTHING");
|
|
122
|
+
} else {
|
|
123
|
+
parts.push("DO UPDATE SET");
|
|
124
|
+
const sets = node.action.set.map((s) => `${quoteIdentifier(s.column, this.dialect)} = ${this.printExpression(s.value)}`);
|
|
125
|
+
parts.push(sets.join(", "));
|
|
126
|
+
}
|
|
127
|
+
if (node.where) {
|
|
128
|
+
parts.push("WHERE", this.printExpression(node.where));
|
|
129
|
+
}
|
|
130
|
+
return parts.join(" ");
|
|
131
|
+
}
|
|
132
|
+
printUpdate(node) {
|
|
133
|
+
const parts = [];
|
|
134
|
+
if (node.ctes.length > 0) {
|
|
135
|
+
parts.push(this.printCTEs(node.ctes));
|
|
136
|
+
}
|
|
137
|
+
parts.push("UPDATE", this.printTableRef(node.table), "SET");
|
|
138
|
+
const sets = node.set.map((s) => `${quoteIdentifier(s.column, this.dialect)} = ${this.printExpression(s.value)}`);
|
|
139
|
+
parts.push(sets.join(", "));
|
|
140
|
+
for (const join of node.joins) {
|
|
141
|
+
parts.push(this.printJoin(join));
|
|
142
|
+
}
|
|
143
|
+
if (node.from) {
|
|
144
|
+
parts.push("FROM", this.printTableRef(node.from));
|
|
145
|
+
}
|
|
146
|
+
if (node.where) {
|
|
147
|
+
parts.push("WHERE", this.printExpression(node.where));
|
|
148
|
+
}
|
|
149
|
+
if (node.returning.length > 0) {
|
|
150
|
+
parts.push("RETURNING", node.returning.map((r) => this.printExpression(r)).join(", "));
|
|
151
|
+
}
|
|
152
|
+
return parts.join(" ");
|
|
153
|
+
}
|
|
154
|
+
printDelete(node) {
|
|
155
|
+
const parts = [];
|
|
156
|
+
if (node.ctes.length > 0) {
|
|
157
|
+
parts.push(this.printCTEs(node.ctes));
|
|
158
|
+
}
|
|
159
|
+
parts.push("DELETE FROM", this.printTableRef(node.table));
|
|
160
|
+
if (node.using) {
|
|
161
|
+
parts.push("USING", this.printTableRef(node.using));
|
|
162
|
+
}
|
|
163
|
+
for (const join of node.joins) {
|
|
164
|
+
parts.push(this.printJoin(join));
|
|
165
|
+
}
|
|
166
|
+
if (node.where) {
|
|
167
|
+
parts.push("WHERE", this.printExpression(node.where));
|
|
168
|
+
}
|
|
169
|
+
if (node.returning.length > 0) {
|
|
170
|
+
parts.push("RETURNING", node.returning.map((r) => this.printExpression(r)).join(", "));
|
|
171
|
+
}
|
|
172
|
+
return parts.join(" ");
|
|
173
|
+
}
|
|
174
|
+
printExpression(node) {
|
|
175
|
+
switch (node.type) {
|
|
176
|
+
case "column_ref": return this.printColumnRef(node);
|
|
177
|
+
case "literal": return this.printLiteral(node);
|
|
178
|
+
case "binary_op": return this.printBinaryOp(node);
|
|
179
|
+
case "unary_op": return this.printUnaryOp(node);
|
|
180
|
+
case "function_call": return this.printFunctionCall(node);
|
|
181
|
+
case "param": return this.printParam(node);
|
|
182
|
+
case "raw": return this.printRaw(node);
|
|
183
|
+
case "subquery": return this.printSubquery(node);
|
|
184
|
+
case "between": return this.printBetween(node);
|
|
185
|
+
case "in": return this.printIn(node);
|
|
186
|
+
case "is_null": return this.printIsNull(node);
|
|
187
|
+
case "cast": return this.printCast(node);
|
|
188
|
+
case "exists": return this.printExists(node);
|
|
189
|
+
case "star": return this.printStar(node);
|
|
190
|
+
case "case": return this.printCase(node);
|
|
191
|
+
case "json_access": return this.printJsonAccess(node);
|
|
192
|
+
case "array_expr": return this.printArrayExpr(node);
|
|
193
|
+
case "window_function": return this.printWindowFunction(node);
|
|
194
|
+
case "aliased_expr": return this.printAliasedExpr(node);
|
|
195
|
+
case "full_text_search": return this.printFullTextSearch(node);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
printColumnRef(node) {
|
|
199
|
+
let result = node.table ? `${quoteIdentifier(node.table, this.dialect)}.${quoteIdentifier(node.column, this.dialect)}` : quoteIdentifier(node.column, this.dialect);
|
|
200
|
+
if (node.alias) {
|
|
201
|
+
result += ` AS ${quoteIdentifier(node.alias, this.dialect)}`;
|
|
202
|
+
}
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
printLiteral(node) {
|
|
206
|
+
if (node.value === null) return "NULL";
|
|
207
|
+
if (typeof node.value === "boolean") return node.value ? "TRUE" : "FALSE";
|
|
208
|
+
if (typeof node.value === "number") return String(node.value);
|
|
209
|
+
return `'${String(node.value).replaceAll("'", "''")}'`;
|
|
210
|
+
}
|
|
211
|
+
printBinaryOp(node) {
|
|
212
|
+
return `(${this.printExpression(node.left)} ${node.op} ${this.printExpression(node.right)})`;
|
|
213
|
+
}
|
|
214
|
+
printUnaryOp(node) {
|
|
215
|
+
if (node.position === "postfix") {
|
|
216
|
+
return `(${this.printExpression(node.operand)} ${node.op})`;
|
|
217
|
+
}
|
|
218
|
+
return `(${node.op} ${this.printExpression(node.operand)})`;
|
|
219
|
+
}
|
|
220
|
+
printFunctionCall(node) {
|
|
221
|
+
const distinctPrefix = node.distinct ? "DISTINCT " : "";
|
|
222
|
+
let result = `${node.name}(${distinctPrefix}${node.args.map((a) => this.printExpression(a)).join(", ")})`;
|
|
223
|
+
if (node.filter) {
|
|
224
|
+
result += ` FILTER (WHERE ${this.printExpression(node.filter)})`;
|
|
225
|
+
}
|
|
226
|
+
if (node.alias) {
|
|
227
|
+
result += ` AS ${quoteIdentifier(node.alias, this.dialect)}`;
|
|
228
|
+
}
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
printParam(node) {
|
|
232
|
+
this.params.push(node.value);
|
|
233
|
+
return formatParam(this.params.length - 1, this.dialect);
|
|
234
|
+
}
|
|
235
|
+
printRaw(node) {
|
|
236
|
+
this.params.push(...node.params);
|
|
237
|
+
return node.sql;
|
|
238
|
+
}
|
|
239
|
+
printSubquery(node) {
|
|
240
|
+
let result = `(${this.printSelect(node.query)})`;
|
|
241
|
+
if (node.alias) {
|
|
242
|
+
result += ` AS ${quoteIdentifier(node.alias, this.dialect)}`;
|
|
243
|
+
}
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
printBetween(node) {
|
|
247
|
+
const neg = node.negated ? "NOT " : "";
|
|
248
|
+
return `(${this.printExpression(node.expr)} ${neg}BETWEEN ${this.printExpression(node.low)} AND ${this.printExpression(node.high)})`;
|
|
249
|
+
}
|
|
250
|
+
printIn(node) {
|
|
251
|
+
const neg = node.negated ? "NOT " : "";
|
|
252
|
+
if (Array.isArray(node.values)) {
|
|
253
|
+
return `(${this.printExpression(node.expr)} ${neg}IN (${node.values.map((v) => this.printExpression(v)).join(", ")}))`;
|
|
254
|
+
}
|
|
255
|
+
return `(${this.printExpression(node.expr)} ${neg}IN (${this.printSelect(node.values)}))`;
|
|
256
|
+
}
|
|
257
|
+
printIsNull(node) {
|
|
258
|
+
const neg = node.negated ? " NOT" : "";
|
|
259
|
+
return `(${this.printExpression(node.expr)} IS${neg} NULL)`;
|
|
260
|
+
}
|
|
261
|
+
printCase(node) {
|
|
262
|
+
const parts = ["CASE"];
|
|
263
|
+
if (node.operand) {
|
|
264
|
+
parts.push(this.printExpression(node.operand));
|
|
265
|
+
}
|
|
266
|
+
for (const when of node.whens) {
|
|
267
|
+
parts.push("WHEN", this.printExpression(when.condition), "THEN", this.printExpression(when.result));
|
|
268
|
+
}
|
|
269
|
+
if (node.else_) {
|
|
270
|
+
parts.push("ELSE", this.printExpression(node.else_));
|
|
271
|
+
}
|
|
272
|
+
parts.push("END");
|
|
273
|
+
return parts.join(" ");
|
|
274
|
+
}
|
|
275
|
+
printCast(node) {
|
|
276
|
+
return `CAST(${this.printExpression(node.expr)} AS ${node.dataType})`;
|
|
277
|
+
}
|
|
278
|
+
printExists(node) {
|
|
279
|
+
const neg = node.negated ? "NOT " : "";
|
|
280
|
+
return `(${neg}EXISTS (${this.printSelect(node.query)}))`;
|
|
281
|
+
}
|
|
282
|
+
printStar(node) {
|
|
283
|
+
return node.table ? `${quoteIdentifier(node.table, this.dialect)}.*` : "*";
|
|
284
|
+
}
|
|
285
|
+
printTableRef(ref) {
|
|
286
|
+
let result = quoteTableRef(ref.name, this.dialect, ref.schema);
|
|
287
|
+
if (ref.temporal) {
|
|
288
|
+
result += ` ${this.printTemporalClause(ref.temporal)}`;
|
|
289
|
+
}
|
|
290
|
+
if (ref.alias) {
|
|
291
|
+
result += ` AS ${quoteIdentifier(ref.alias, this.dialect)}`;
|
|
292
|
+
}
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
printTemporalClause(clause) {
|
|
296
|
+
switch (clause.kind) {
|
|
297
|
+
case "as_of": return `FOR SYSTEM_TIME AS OF ${this.printExpression(clause.timestamp)}`;
|
|
298
|
+
case "from_to": return `FOR SYSTEM_TIME FROM ${this.printExpression(clause.start)} TO ${this.printExpression(clause.end)}`;
|
|
299
|
+
case "between": return `FOR SYSTEM_TIME BETWEEN ${this.printExpression(clause.start)} AND ${this.printExpression(clause.end)}`;
|
|
300
|
+
case "contained_in": return `FOR SYSTEM_TIME CONTAINED IN (${this.printExpression(clause.start)}, ${this.printExpression(clause.end)})`;
|
|
301
|
+
case "all": return "FOR SYSTEM_TIME ALL";
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
printJoin(node) {
|
|
305
|
+
const parts = [];
|
|
306
|
+
parts.push(`${node.joinType} JOIN`);
|
|
307
|
+
if (node.table.type === "subquery") {
|
|
308
|
+
parts.push(this.printSubquery(node.table));
|
|
309
|
+
} else {
|
|
310
|
+
parts.push(this.printTableRef(node.table));
|
|
311
|
+
}
|
|
312
|
+
if (node.on) {
|
|
313
|
+
parts.push("ON", this.printExpression(node.on));
|
|
314
|
+
}
|
|
315
|
+
return parts.join(" ");
|
|
316
|
+
}
|
|
317
|
+
printOrderBy(node) {
|
|
318
|
+
let result = `${this.printExpression(node.expr)} ${node.direction}`;
|
|
319
|
+
if (node.nulls) {
|
|
320
|
+
result += ` NULLS ${node.nulls}`;
|
|
321
|
+
}
|
|
322
|
+
return result;
|
|
323
|
+
}
|
|
324
|
+
printCTEs(ctes) {
|
|
325
|
+
const hasRecursive = ctes.some((c) => c.recursive);
|
|
326
|
+
const prefix = hasRecursive ? "WITH RECURSIVE" : "WITH";
|
|
327
|
+
const cteParts = ctes.map((c) => `${quoteIdentifier(c.name, this.dialect)} AS (${this.printSelect(c.query)})`);
|
|
328
|
+
return `${prefix} ${cteParts.join(", ")}`;
|
|
329
|
+
}
|
|
330
|
+
printJsonAccess(node) {
|
|
331
|
+
let result = `${this.printExpression(node.expr)}${node.operator}${this.printLiteral({
|
|
332
|
+
type: "literal",
|
|
333
|
+
value: node.path
|
|
334
|
+
})}`;
|
|
335
|
+
if (node.alias) {
|
|
336
|
+
result += ` AS ${quoteIdentifier(node.alias, this.dialect)}`;
|
|
337
|
+
}
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
340
|
+
printArrayExpr(node) {
|
|
341
|
+
return `ARRAY[${node.elements.map((e) => this.printExpression(e)).join(", ")}]`;
|
|
342
|
+
}
|
|
343
|
+
printWindowFunction(node) {
|
|
344
|
+
const parts = [];
|
|
345
|
+
parts.push(this.printFunctionCall(node.fn));
|
|
346
|
+
parts.push("OVER");
|
|
347
|
+
const overParts = [];
|
|
348
|
+
if (node.partitionBy.length > 0) {
|
|
349
|
+
overParts.push(`PARTITION BY ${node.partitionBy.map((p) => this.printExpression(p)).join(", ")}`);
|
|
350
|
+
}
|
|
351
|
+
if (node.orderBy.length > 0) {
|
|
352
|
+
overParts.push(`ORDER BY ${node.orderBy.map((o) => this.printOrderBy(o)).join(", ")}`);
|
|
353
|
+
}
|
|
354
|
+
if (node.frame) {
|
|
355
|
+
overParts.push(this.printFrameSpec(node.frame));
|
|
356
|
+
}
|
|
357
|
+
parts.push(`(${overParts.join(" ")})`);
|
|
358
|
+
if (node.alias) {
|
|
359
|
+
parts.push("AS", quoteIdentifier(node.alias, this.dialect));
|
|
360
|
+
}
|
|
361
|
+
return parts.join(" ");
|
|
362
|
+
}
|
|
363
|
+
printFrameSpec(frame) {
|
|
364
|
+
const start = this.printFrameBound(frame.start);
|
|
365
|
+
if (frame.end) {
|
|
366
|
+
return `${frame.kind} BETWEEN ${start} AND ${this.printFrameBound(frame.end)}`;
|
|
367
|
+
}
|
|
368
|
+
return `${frame.kind} ${start}`;
|
|
369
|
+
}
|
|
370
|
+
printFrameBound(bound) {
|
|
371
|
+
switch (bound.type) {
|
|
372
|
+
case "unbounded_preceding": return "UNBOUNDED PRECEDING";
|
|
373
|
+
case "preceding": return `${bound.value} PRECEDING`;
|
|
374
|
+
case "current_row": return "CURRENT ROW";
|
|
375
|
+
case "following": return `${bound.value} FOLLOWING`;
|
|
376
|
+
case "unbounded_following": return "UNBOUNDED FOLLOWING";
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
printMerge(node) {
|
|
380
|
+
const parts = [];
|
|
381
|
+
if (node.ctes.length > 0) {
|
|
382
|
+
parts.push(this.printCTEs(node.ctes));
|
|
383
|
+
}
|
|
384
|
+
parts.push("MERGE INTO", this.printTableRef(node.target));
|
|
385
|
+
parts.push("USING");
|
|
386
|
+
if (node.source.type === "subquery") {
|
|
387
|
+
parts.push(this.printSubquery(node.source));
|
|
388
|
+
} else {
|
|
389
|
+
parts.push(this.printTableRef(node.source));
|
|
390
|
+
}
|
|
391
|
+
parts.push("AS", quoteIdentifier(node.sourceAlias, this.dialect));
|
|
392
|
+
parts.push("ON", this.printExpression(node.on));
|
|
393
|
+
for (const when of node.whens) {
|
|
394
|
+
if (when.type === "matched") {
|
|
395
|
+
parts.push(this.printMergeWhenMatched(when));
|
|
396
|
+
} else {
|
|
397
|
+
parts.push(this.printMergeWhenNotMatched(when));
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return parts.join(" ");
|
|
401
|
+
}
|
|
402
|
+
printMergeWhenMatched(when) {
|
|
403
|
+
const parts = ["WHEN MATCHED"];
|
|
404
|
+
if (when.condition) {
|
|
405
|
+
parts.push("AND", this.printExpression(when.condition));
|
|
406
|
+
}
|
|
407
|
+
if (when.action === "delete") {
|
|
408
|
+
parts.push("THEN DELETE");
|
|
409
|
+
} else {
|
|
410
|
+
parts.push("THEN UPDATE SET");
|
|
411
|
+
const sets = (when.set ?? []).map((s) => `${quoteIdentifier(s.column, this.dialect)} = ${this.printExpression(s.value)}`);
|
|
412
|
+
parts.push(sets.join(", "));
|
|
413
|
+
}
|
|
414
|
+
return parts.join(" ");
|
|
415
|
+
}
|
|
416
|
+
printMergeWhenNotMatched(when) {
|
|
417
|
+
const parts = ["WHEN NOT MATCHED"];
|
|
418
|
+
if (when.condition) {
|
|
419
|
+
parts.push("AND", this.printExpression(when.condition));
|
|
420
|
+
}
|
|
421
|
+
parts.push("THEN INSERT");
|
|
422
|
+
parts.push(`(${when.columns.map((c) => quoteIdentifier(c, this.dialect)).join(", ")})`);
|
|
423
|
+
parts.push(`VALUES (${when.values.map((v) => this.printExpression(v)).join(", ")})`);
|
|
424
|
+
return parts.join(" ");
|
|
425
|
+
}
|
|
426
|
+
printAliasedExpr(node) {
|
|
427
|
+
return `${this.printExpression(node.expr)} AS ${quoteIdentifier(node.alias, this.dialect)}`;
|
|
428
|
+
}
|
|
429
|
+
printExplain(node) {
|
|
430
|
+
const parts = ["EXPLAIN"];
|
|
431
|
+
if (node.analyze) {
|
|
432
|
+
parts.push("ANALYZE");
|
|
433
|
+
}
|
|
434
|
+
if (node.format) {
|
|
435
|
+
parts.push(`(FORMAT ${node.format})`);
|
|
436
|
+
}
|
|
437
|
+
parts.push(this.printNode(node.statement));
|
|
438
|
+
return parts.join(" ");
|
|
439
|
+
}
|
|
440
|
+
printFullTextSearch(node) {
|
|
441
|
+
|
|
442
|
+
const cols = node.columns.map((c) => this.printExpression(c)).join(" || ' ' || ");
|
|
443
|
+
const lang = node.language ? `'${node.language}', ` : "";
|
|
444
|
+
let result = `(to_tsvector(${lang}${cols}) @@ to_tsquery(${lang}${this.printExpression(node.query)}))`;
|
|
445
|
+
if (node.alias) {
|
|
446
|
+
result += ` AS ${quoteIdentifier(node.alias, this.dialect)}`;
|
|
447
|
+
}
|
|
448
|
+
return result;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wadler-style pretty printer document algebra.
|
|
3
|
+
*
|
|
4
|
+
* Based on "A prettier printer" (Wadler 1998) and "Strictly Pretty" (Lindig 2000).
|
|
5
|
+
* Used to format SQL output with width-sensitive line breaking.
|
|
6
|
+
*/
|
|
7
|
+
export type Doc = {
|
|
8
|
+
tag: "empty";
|
|
9
|
+
} | {
|
|
10
|
+
tag: "text";
|
|
11
|
+
text: string;
|
|
12
|
+
} | {
|
|
13
|
+
tag: "line";
|
|
14
|
+
} | {
|
|
15
|
+
tag: "nest";
|
|
16
|
+
indent: number;
|
|
17
|
+
doc: Doc;
|
|
18
|
+
} | {
|
|
19
|
+
tag: "group";
|
|
20
|
+
doc: Doc;
|
|
21
|
+
} | {
|
|
22
|
+
tag: "concat";
|
|
23
|
+
docs: Doc[];
|
|
24
|
+
};
|
|
25
|
+
export declare function empty(): Doc;
|
|
26
|
+
export declare function text(s: string): Doc;
|
|
27
|
+
/** A line break. In flat mode, renders as a single space. */
|
|
28
|
+
export declare function line(): Doc;
|
|
29
|
+
/** Increase indentation for the nested document. */
|
|
30
|
+
export declare function nest(indent: number, doc: Doc): Doc;
|
|
31
|
+
/** Try to render flat; if too wide, break lines. */
|
|
32
|
+
export declare function group(doc: Doc): Doc;
|
|
33
|
+
/** Concatenate documents. */
|
|
34
|
+
export declare function concat(...docs: Doc[]): Doc;
|
|
35
|
+
/** Join documents with a separator. */
|
|
36
|
+
export declare function join(sep: Doc, docs: Doc[]): Doc;
|
|
37
|
+
/** Convenience: text + line */
|
|
38
|
+
export declare function textLine(s: string): Doc;
|
|
39
|
+
/**
|
|
40
|
+
* Render a document to a string.
|
|
41
|
+
*
|
|
42
|
+
* Uses Wadler/Lindig's algorithm: maintain a stack of (indent, mode, doc) triples.
|
|
43
|
+
* mode = "flat" (try single line) or "break" (use line breaks).
|
|
44
|
+
*/
|
|
45
|
+
export declare function render(doc: Doc, width?: number): string;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
export function empty() {
|
|
2
|
+
return { tag: "empty" };
|
|
3
|
+
}
|
|
4
|
+
export function text(s) {
|
|
5
|
+
return {
|
|
6
|
+
tag: "text",
|
|
7
|
+
text: s
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function line() {
|
|
12
|
+
return { tag: "line" };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function nest(indent, doc) {
|
|
16
|
+
return {
|
|
17
|
+
tag: "nest",
|
|
18
|
+
indent,
|
|
19
|
+
doc
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function group(doc) {
|
|
24
|
+
return {
|
|
25
|
+
tag: "group",
|
|
26
|
+
doc
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function concat(...docs) {
|
|
31
|
+
const filtered = docs.filter((d) => d.tag !== "empty");
|
|
32
|
+
if (filtered.length === 0) return empty();
|
|
33
|
+
if (filtered.length === 1) return filtered[0];
|
|
34
|
+
return {
|
|
35
|
+
tag: "concat",
|
|
36
|
+
docs: filtered
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function join(sep, docs) {
|
|
41
|
+
if (docs.length === 0) return empty();
|
|
42
|
+
const result = [];
|
|
43
|
+
for (let i = 0; i < docs.length; i++) {
|
|
44
|
+
if (i > 0) result.push(sep);
|
|
45
|
+
result.push(docs[i]);
|
|
46
|
+
}
|
|
47
|
+
return concat(...result);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function textLine(s) {
|
|
51
|
+
return concat(text(s), line());
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function render(doc, width = 80) {
|
|
55
|
+
let output = "";
|
|
56
|
+
let col = 0;
|
|
57
|
+
const stack = [[
|
|
58
|
+
0,
|
|
59
|
+
"break",
|
|
60
|
+
doc
|
|
61
|
+
]];
|
|
62
|
+
while (stack.length > 0) {
|
|
63
|
+
const [indent, mode, d] = stack.pop();
|
|
64
|
+
switch (d.tag) {
|
|
65
|
+
case "empty": break;
|
|
66
|
+
case "text":
|
|
67
|
+
output += d.text;
|
|
68
|
+
col += d.text.length;
|
|
69
|
+
break;
|
|
70
|
+
case "line":
|
|
71
|
+
if (mode === "flat") {
|
|
72
|
+
output += " ";
|
|
73
|
+
col += 1;
|
|
74
|
+
} else {
|
|
75
|
+
output += "\n" + " ".repeat(indent);
|
|
76
|
+
col = indent;
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
case "nest":
|
|
80
|
+
stack.push([
|
|
81
|
+
indent + d.indent,
|
|
82
|
+
mode,
|
|
83
|
+
d.doc
|
|
84
|
+
]);
|
|
85
|
+
break;
|
|
86
|
+
case "group":
|
|
87
|
+
if (mode === "flat") {
|
|
88
|
+
stack.push([
|
|
89
|
+
indent,
|
|
90
|
+
"flat",
|
|
91
|
+
d.doc
|
|
92
|
+
]);
|
|
93
|
+
} else {
|
|
94
|
+
|
|
95
|
+
const flat = measureFlat(d.doc);
|
|
96
|
+
if (flat !== null && col + flat <= width) {
|
|
97
|
+
stack.push([
|
|
98
|
+
indent,
|
|
99
|
+
"flat",
|
|
100
|
+
d.doc
|
|
101
|
+
]);
|
|
102
|
+
} else {
|
|
103
|
+
stack.push([
|
|
104
|
+
indent,
|
|
105
|
+
"break",
|
|
106
|
+
d.doc
|
|
107
|
+
]);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
case "concat":
|
|
112
|
+
|
|
113
|
+
for (let i = d.docs.length - 1; i >= 0; i--) {
|
|
114
|
+
stack.push([
|
|
115
|
+
indent,
|
|
116
|
+
mode,
|
|
117
|
+
d.docs[i]
|
|
118
|
+
]);
|
|
119
|
+
}
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return output;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function measureFlat(doc) {
|
|
127
|
+
let width = 0;
|
|
128
|
+
const stack = [doc];
|
|
129
|
+
while (stack.length > 0) {
|
|
130
|
+
const d = stack.pop();
|
|
131
|
+
switch (d.tag) {
|
|
132
|
+
case "empty": break;
|
|
133
|
+
case "text":
|
|
134
|
+
width += d.text.length;
|
|
135
|
+
break;
|
|
136
|
+
case "line":
|
|
137
|
+
width += 1;
|
|
138
|
+
break;
|
|
139
|
+
case "nest":
|
|
140
|
+
stack.push(d.doc);
|
|
141
|
+
break;
|
|
142
|
+
case "group":
|
|
143
|
+
stack.push(d.doc);
|
|
144
|
+
break;
|
|
145
|
+
case "concat":
|
|
146
|
+
for (let i = d.docs.length - 1; i >= 0; i--) {
|
|
147
|
+
stack.push(d.docs[i]);
|
|
148
|
+
}
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return width;
|
|
153
|
+
}
|