data-finder-ts 0.1.3 → 0.1.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 +17 -55
- package/dist/chunk-BGEX377Q.js +230 -0
- package/dist/chunk-BGEX377Q.js.map +1 -0
- package/dist/chunk-BKPWV3OF.js +962 -0
- package/dist/chunk-BKPWV3OF.js.map +1 -0
- package/dist/{chunk-EYXBYV2X.js → chunk-GPHWC533.js} +2 -2
- package/dist/chunk-KAK75RVS.js +198 -0
- package/dist/chunk-KAK75RVS.js.map +1 -0
- package/dist/chunk-MGEL5RWM.js +628 -0
- package/dist/chunk-MGEL5RWM.js.map +1 -0
- package/dist/chunk-PF6EJZHB.js +1015 -0
- package/dist/chunk-PF6EJZHB.js.map +1 -0
- package/dist/datafinder/attribute.cjs +595 -18
- package/dist/datafinder/attribute.cjs.map +1 -1
- package/dist/datafinder/attribute.d.cts +73 -10
- package/dist/datafinder/attribute.d.ts +73 -10
- package/dist/datafinder/attribute.js +2 -2
- package/dist/datafinder/runner.cjs +1589 -288
- package/dist/datafinder/runner.cjs.map +1 -1
- package/dist/datafinder/runner.d.cts +41 -7
- package/dist/datafinder/runner.d.ts +41 -7
- package/dist/datafinder/runner.js +12 -6
- package/dist/datafinder/sql-generator.cjs +1484 -281
- package/dist/datafinder/sql-generator.cjs.map +1 -1
- package/dist/datafinder/sql-generator.d.cts +19 -14
- package/dist/datafinder/sql-generator.d.ts +19 -14
- package/dist/datafinder/sql-generator.js +9 -7
- package/dist/datafinder/typed-attributes.cjs +856 -93
- package/dist/datafinder/typed-attributes.cjs.map +1 -1
- package/dist/datafinder/typed-attributes.d.cts +3 -135
- package/dist/datafinder/typed-attributes.d.ts +3 -135
- package/dist/datafinder/typed-attributes.js +3 -3
- package/dist/dialect-CogGPm_y.d.cts +38 -0
- package/dist/dialect-aaG1bDi5.d.ts +38 -0
- package/dist/index.cjs +2080 -480
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +119 -14
- package/dist/index.d.ts +119 -14
- package/dist/index.js +362 -12
- package/dist/index.js.map +1 -1
- package/dist/model/milestoning.cjs +529 -0
- package/dist/model/milestoning.cjs.map +1 -1
- package/dist/model/milestoning.js +2 -2
- package/dist/model/relational.cjs +608 -12
- package/dist/model/relational.cjs.map +1 -1
- package/dist/model/relational.d.cts +456 -47
- package/dist/model/relational.d.ts +456 -47
- package/dist/model/relational.js +61 -3
- package/dist/typed-attributes-CUTf_6Ru.d.ts +328 -0
- package/dist/typed-attributes-CdWXq_78.d.cts +328 -0
- package/package.json +5 -2
- package/dist/chunk-3SSWFES4.js +0 -448
- package/dist/chunk-3SSWFES4.js.map +0 -1
- package/dist/chunk-5K2Z3YDY.js +0 -393
- package/dist/chunk-5K2Z3YDY.js.map +0 -1
- package/dist/chunk-6QTCGJWX.js +0 -98
- package/dist/chunk-6QTCGJWX.js.map +0 -1
- package/dist/chunk-DFYZVBII.js +0 -652
- package/dist/chunk-DFYZVBII.js.map +0 -1
- package/dist/chunk-V647VNMY.js +0 -135
- package/dist/chunk-V647VNMY.js.map +0 -1
- /package/dist/{chunk-EYXBYV2X.js.map → chunk-GPHWC533.js.map} +0 -0
|
@@ -0,0 +1,962 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BiTemporalColumns,
|
|
3
|
+
BusinessDateAndProcessingTemporalColumns,
|
|
4
|
+
MilestonedTable,
|
|
5
|
+
ProcessingTemporalColumns,
|
|
6
|
+
SingleBusinessDateColumn
|
|
7
|
+
} from "./chunk-GPHWC533.js";
|
|
8
|
+
import {
|
|
9
|
+
DateAttribute,
|
|
10
|
+
DateTimeAttribute
|
|
11
|
+
} from "./chunk-MGEL5RWM.js";
|
|
12
|
+
import {
|
|
13
|
+
Attribute
|
|
14
|
+
} from "./chunk-BGEX377Q.js";
|
|
15
|
+
import {
|
|
16
|
+
AggregateOperation,
|
|
17
|
+
Alias,
|
|
18
|
+
ArithmeticOperation,
|
|
19
|
+
BinaryOperation,
|
|
20
|
+
BooleanConstantOperation,
|
|
21
|
+
CaseOperation,
|
|
22
|
+
Column,
|
|
23
|
+
ColumnWithJoin,
|
|
24
|
+
ComparisonOperation,
|
|
25
|
+
ConcatOperation,
|
|
26
|
+
ConstantOperation,
|
|
27
|
+
CountAllOperation,
|
|
28
|
+
DateArithmeticOperation,
|
|
29
|
+
DateConstantOperation,
|
|
30
|
+
DateDiffOperation,
|
|
31
|
+
DateExtractOperation,
|
|
32
|
+
DateTimeConstantOperation,
|
|
33
|
+
DateTruncOperation,
|
|
34
|
+
DecimalConstantOperation,
|
|
35
|
+
ExistsOperation,
|
|
36
|
+
FloatConstantOperation,
|
|
37
|
+
InOperation,
|
|
38
|
+
IntegerConstantOperation,
|
|
39
|
+
IsNotNullOperation,
|
|
40
|
+
IsNullOperation,
|
|
41
|
+
LIKE_ESCAPE,
|
|
42
|
+
LogicalOperation,
|
|
43
|
+
NoOperation,
|
|
44
|
+
NotExistsOperation,
|
|
45
|
+
NotInOperation,
|
|
46
|
+
RelationalOperationElement,
|
|
47
|
+
ScalarFunctionOperation,
|
|
48
|
+
SelectOperation,
|
|
49
|
+
SortOperation,
|
|
50
|
+
StringConstantOperation,
|
|
51
|
+
UnaryOperation,
|
|
52
|
+
WindowFunctionOperation,
|
|
53
|
+
WindowSpecification,
|
|
54
|
+
containsAggregate,
|
|
55
|
+
containsAggregateOrWindow
|
|
56
|
+
} from "./chunk-PF6EJZHB.js";
|
|
57
|
+
|
|
58
|
+
// src/datafinder/dialect.ts
|
|
59
|
+
var doubleQuoted = (name) => '"' + name.replaceAll('"', '""') + '"';
|
|
60
|
+
var quoted = (value) => "'" + value.replaceAll("'", "''") + "'";
|
|
61
|
+
var SECONDS_PER = { ["HOUR" /* HOUR */]: 3600, ["MINUTE" /* MINUTE */]: 60, ["SECOND" /* SECOND */]: 1 };
|
|
62
|
+
var POSTGRES = {
|
|
63
|
+
name: "postgres",
|
|
64
|
+
quoteIdentifier: doubleQuoted,
|
|
65
|
+
stringLiteral: quoted,
|
|
66
|
+
orderDirection: (direction) => direction === "ASC" /* ASC */ ? " ASC" : " DESC",
|
|
67
|
+
doubleType: "DOUBLE PRECISION",
|
|
68
|
+
dayOfWeek: (expression) => `EXTRACT(DOW FROM ${expression})`,
|
|
69
|
+
dateDiff(unit, from, to) {
|
|
70
|
+
const start = `CAST(${from} AS TIMESTAMP)`;
|
|
71
|
+
const end = `CAST(${to} AS TIMESTAMP)`;
|
|
72
|
+
switch (unit) {
|
|
73
|
+
case "DAY" /* DAY */:
|
|
74
|
+
return `(CAST(${to} AS DATE) - CAST(${from} AS DATE))`;
|
|
75
|
+
case "MONTH" /* MONTH */:
|
|
76
|
+
return `CAST(EXTRACT(YEAR FROM AGE(${end}, ${start})) * 12 + EXTRACT(MONTH FROM AGE(${end}, ${start})) AS BIGINT)`;
|
|
77
|
+
case "YEAR" /* YEAR */:
|
|
78
|
+
return `CAST(EXTRACT(YEAR FROM AGE(${end}, ${start})) AS BIGINT)`;
|
|
79
|
+
default: {
|
|
80
|
+
const seconds = SECONDS_PER[unit];
|
|
81
|
+
if (seconds === void 0) throw new Error(`Cannot take the difference in ${unit}`);
|
|
82
|
+
return `CAST(FLOOR(EXTRACT(EPOCH FROM (${end} - ${start})) / ${seconds}) AS BIGINT)`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
stringAgg: (expression, separator, orderBy) => `STRING_AGG(${expression}, ${separator}${orderBy.length > 0 ? " ORDER BY " + orderBy.join(", ") : ""})`
|
|
87
|
+
};
|
|
88
|
+
var DUCKDB = {
|
|
89
|
+
...POSTGRES,
|
|
90
|
+
name: "duckdb",
|
|
91
|
+
// DuckDB sorts nulls last whichever way the sort goes
|
|
92
|
+
orderDirection: (direction) => direction === "ASC" /* ASC */ ? " ASC" : " DESC NULLS FIRST",
|
|
93
|
+
doubleType: "DOUBLE",
|
|
94
|
+
dateDiff: (unit, from, to) => `DATE_DIFF('${unit.toLowerCase()}', ${from}, ${to})`
|
|
95
|
+
};
|
|
96
|
+
var DATABRICKS = {
|
|
97
|
+
name: "databricks",
|
|
98
|
+
// a double-quoted token is a string in Databricks; identifiers take backticks
|
|
99
|
+
quoteIdentifier: (name) => "`" + name.replaceAll("`", "``") + "`",
|
|
100
|
+
// a backslash starts an escape in a Databricks string literal, and a doubled quote is not one: Spark reads
|
|
101
|
+
// 'it''s' as the two literals 'it' and 's' and joins them, losing the quote. A quote is escaped with a backslash.
|
|
102
|
+
stringLiteral: (value) => "'" + value.replaceAll("\\", "\\\\").replaceAll("'", "\\'") + "'",
|
|
103
|
+
// Databricks sorts nulls first when ascending, last when descending
|
|
104
|
+
orderDirection: (direction) => direction === "ASC" /* ASC */ ? " ASC NULLS LAST" : " DESC NULLS FIRST",
|
|
105
|
+
doubleType: "DOUBLE",
|
|
106
|
+
// DAYOFWEEK is 1 = Sunday through 7 = Saturday
|
|
107
|
+
dayOfWeek: (expression) => `(DAYOFWEEK(${expression}) - 1)`,
|
|
108
|
+
dateDiff: (unit, from, to) => `DATEDIFF(${unit}, ${from}, ${to})`,
|
|
109
|
+
stringAgg: (expression, separator, orderBy) => `LISTAGG(${expression}, ${separator})${orderBy.length > 0 ? " WITHIN GROUP (ORDER BY " + orderBy.join(", ") + ")" : ""}`
|
|
110
|
+
};
|
|
111
|
+
var DIALECTS = { postgres: POSTGRES, duckdb: DUCKDB, databricks: DATABRICKS };
|
|
112
|
+
function dialectOf(dialect) {
|
|
113
|
+
if (typeof dialect !== "string") return dialect;
|
|
114
|
+
const found = DIALECTS[dialect];
|
|
115
|
+
if (found === void 0) throw new Error(`Unknown SQL dialect '${dialect}': one of ${Object.keys(DIALECTS).join(", ")}`);
|
|
116
|
+
return found;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/datafinder/sql-generator.ts
|
|
120
|
+
var TableAlias = class {
|
|
121
|
+
constructor(table, alias) {
|
|
122
|
+
this.table = table;
|
|
123
|
+
this.alias = alias;
|
|
124
|
+
}
|
|
125
|
+
table;
|
|
126
|
+
alias;
|
|
127
|
+
};
|
|
128
|
+
var TableAliasColumn = class extends RelationalOperationElement {
|
|
129
|
+
constructor(column, tableAlias) {
|
|
130
|
+
super();
|
|
131
|
+
this.column = column;
|
|
132
|
+
this.tableAlias = tableAlias;
|
|
133
|
+
}
|
|
134
|
+
column;
|
|
135
|
+
tableAlias;
|
|
136
|
+
};
|
|
137
|
+
var Join = class {
|
|
138
|
+
constructor(source, target, filterOp) {
|
|
139
|
+
this.source = source;
|
|
140
|
+
this.target = target;
|
|
141
|
+
this.filterOp = filterOp;
|
|
142
|
+
}
|
|
143
|
+
source;
|
|
144
|
+
target;
|
|
145
|
+
filterOp;
|
|
146
|
+
/** `source = target`, written with the key column first as Legend writes a join defined as key = foreign key. */
|
|
147
|
+
condition() {
|
|
148
|
+
let [first, second] = [this.source, this.target];
|
|
149
|
+
if (second.column.primaryKey && !first.column.primaryKey) [first, second] = [second, first];
|
|
150
|
+
return first.tableAlias.alias + "." + first.column.name + " = " + second.tableAlias.alias + "." + second.column.name;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
function sqlFormatDatetime(value) {
|
|
154
|
+
const pad = (n, w = 2) => String(n).padStart(w, "0");
|
|
155
|
+
return `'${value.getUTCFullYear()}-${pad(value.getUTCMonth() + 1)}-${pad(value.getUTCDate())} ${pad(value.getUTCHours())}:${pad(value.getUTCMinutes())}:${pad(value.getUTCSeconds())}'`;
|
|
156
|
+
}
|
|
157
|
+
function sqlFormatDate(value) {
|
|
158
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
159
|
+
return `'${value.getUTCFullYear()}-${pad(value.getUTCMonth() + 1)}-${pad(value.getUTCDate())}'`;
|
|
160
|
+
}
|
|
161
|
+
var LOGICAL_OPERATOR_STR = {
|
|
162
|
+
["AND" /* AND */]: " AND ",
|
|
163
|
+
["OR" /* OR */]: " OR "
|
|
164
|
+
};
|
|
165
|
+
var COMPARISON_OPERATOR_STR = {
|
|
166
|
+
["EQUAL" /* EQUAL */]: " = ",
|
|
167
|
+
["NOT_EQUAL" /* NOT_EQUAL */]: " <> ",
|
|
168
|
+
["LESS_THAN" /* LESS_THAN */]: " < ",
|
|
169
|
+
["GREATER_THAN" /* GREATER_THAN */]: " > ",
|
|
170
|
+
["LESS_THAN_OR_EQUAL_TO" /* LESS_THAN_OR_EQUAL_TO */]: " <= ",
|
|
171
|
+
["GREATER_THAN_OR_EQUAL_TO" /* GREATER_THAN_OR_EQUAL_TO */]: " >= ",
|
|
172
|
+
["LIKE" /* LIKE */]: " LIKE ",
|
|
173
|
+
["NOT_LIKE" /* NOT_LIKE */]: " NOT LIKE "
|
|
174
|
+
};
|
|
175
|
+
var AGGREGATE_SQL_NAMES = {
|
|
176
|
+
["AVERAGE" /* AVERAGE */]: "AVG"
|
|
177
|
+
};
|
|
178
|
+
var GUARDED_COMPARISONS = /* @__PURE__ */ new Set([
|
|
179
|
+
"LESS_THAN" /* LESS_THAN */,
|
|
180
|
+
"GREATER_THAN" /* GREATER_THAN */,
|
|
181
|
+
"LESS_THAN_OR_EQUAL_TO" /* LESS_THAN_OR_EQUAL_TO */,
|
|
182
|
+
"GREATER_THAN_OR_EQUAL_TO" /* GREATER_THAN_OR_EQUAL_TO */,
|
|
183
|
+
"LIKE" /* LIKE */
|
|
184
|
+
]);
|
|
185
|
+
var WINDOW_SQL_NAMES = {
|
|
186
|
+
["ROW_NUMBER" /* ROW_NUMBER */]: "ROW_NUMBER",
|
|
187
|
+
["RANK" /* RANK */]: "RANK",
|
|
188
|
+
["DENSE_RANK" /* DENSE_RANK */]: "DENSE_RANK",
|
|
189
|
+
["NTILE" /* NTILE */]: "NTILE",
|
|
190
|
+
["LAG" /* LAG */]: "LAG",
|
|
191
|
+
["LEAD" /* LEAD */]: "LEAD",
|
|
192
|
+
["FIRST_VALUE" /* FIRST_VALUE */]: "FIRST_VALUE",
|
|
193
|
+
["LAST_VALUE" /* LAST_VALUE */]: "LAST_VALUE",
|
|
194
|
+
["CUME_DIST" /* CUME_DIST */]: "CUME_DIST",
|
|
195
|
+
["PERCENT_RANK" /* PERCENT_RANK */]: "PERCENT_RANK"
|
|
196
|
+
};
|
|
197
|
+
var SCALAR_SQL_NAMES = {
|
|
198
|
+
["ABS" /* ABS */]: "ABS",
|
|
199
|
+
["CEILING" /* CEILING */]: "CEILING",
|
|
200
|
+
["FLOOR" /* FLOOR */]: "FLOOR",
|
|
201
|
+
["MOD" /* MOD */]: "MOD",
|
|
202
|
+
["POWER" /* POWER */]: "POWER",
|
|
203
|
+
["SQRT" /* SQRT */]: "SQRT",
|
|
204
|
+
["ROUND" /* ROUND */]: "ROUND",
|
|
205
|
+
["UPPER" /* UPPER */]: "UPPER",
|
|
206
|
+
["LOWER" /* LOWER */]: "LOWER",
|
|
207
|
+
["TRIM" /* TRIM */]: "TRIM",
|
|
208
|
+
["LTRIM" /* LTRIM */]: "LTRIM",
|
|
209
|
+
["RTRIM" /* RTRIM */]: "RTRIM",
|
|
210
|
+
["LENGTH" /* LENGTH */]: "LENGTH",
|
|
211
|
+
["REVERSE" /* REVERSE */]: "REVERSE",
|
|
212
|
+
["LEFT" /* LEFT */]: "LEFT",
|
|
213
|
+
["RIGHT" /* RIGHT */]: "RIGHT",
|
|
214
|
+
["REPEAT" /* REPEAT */]: "REPEAT",
|
|
215
|
+
["REPLACE" /* REPLACE */]: "REPLACE",
|
|
216
|
+
["SUBSTRING" /* SUBSTRING */]: "SUBSTRING",
|
|
217
|
+
["COALESCE" /* COALESCE */]: "COALESCE"
|
|
218
|
+
};
|
|
219
|
+
function constantValueString(op, d) {
|
|
220
|
+
if (op instanceof StringConstantOperation) return d.stringLiteral(op.value);
|
|
221
|
+
if (op instanceof DateConstantOperation) return sqlFormatDate(op.value);
|
|
222
|
+
if (op instanceof DateTimeConstantOperation) return sqlFormatDatetime(op.value);
|
|
223
|
+
if (op instanceof IntegerConstantOperation) return String(op.value);
|
|
224
|
+
if (op instanceof FloatConstantOperation) return Number.isInteger(op.value) && Math.abs(op.value) < 1e21 ? op.value.toFixed(1) : String(op.value);
|
|
225
|
+
if (op instanceof BooleanConstantOperation) return op.value ? "TRUE" : "FALSE";
|
|
226
|
+
if (op instanceof DecimalConstantOperation) return String(op.value);
|
|
227
|
+
throw new Error("Unknown constant operation");
|
|
228
|
+
}
|
|
229
|
+
function selectedConstantString(op, d) {
|
|
230
|
+
if (op instanceof FloatConstantOperation) return "CAST(" + constantValueString(op, d) + " AS " + d.doubleType + ")";
|
|
231
|
+
if (op instanceof DateTimeConstantOperation) return "TIMESTAMP " + constantValueString(op, d);
|
|
232
|
+
if (op instanceof DateConstantOperation) return "DATE " + constantValueString(op, d);
|
|
233
|
+
return constantValueString(op, d);
|
|
234
|
+
}
|
|
235
|
+
function windowSpecToString(window, d) {
|
|
236
|
+
const parts = [];
|
|
237
|
+
if (window.partitionBy.length > 0) {
|
|
238
|
+
parts.push("PARTITION BY " + window.partitionBy.map((p) => sqlOperationToString(p, d)).join(", "));
|
|
239
|
+
}
|
|
240
|
+
if (window.orderBy.length > 0) {
|
|
241
|
+
parts.push("ORDER BY " + window.orderBy.map(
|
|
242
|
+
(s) => sqlOperationToString(s.column, d) + d.orderDirection(s.direction)
|
|
243
|
+
).join(", "));
|
|
244
|
+
}
|
|
245
|
+
return "OVER (" + parts.join(" ") + ")";
|
|
246
|
+
}
|
|
247
|
+
var ARITHMETIC_PRECEDENCE = {
|
|
248
|
+
["+" /* ADD */]: 1,
|
|
249
|
+
["-" /* SUBTRACT */]: 1,
|
|
250
|
+
["*" /* MULTIPLY */]: 2,
|
|
251
|
+
["/" /* DIVIDE */]: 2
|
|
252
|
+
};
|
|
253
|
+
function arithmeticNeedsParentheses(parent, child, onRight) {
|
|
254
|
+
if (!(child instanceof ArithmeticOperation)) return false;
|
|
255
|
+
const here = ARITHMETIC_PRECEDENCE[parent.operator];
|
|
256
|
+
const there = ARITHMETIC_PRECEDENCE[child.operator];
|
|
257
|
+
return there < here || there === here && onRight && (parent.operator === "-" /* SUBTRACT */ || parent.operator === "/" /* DIVIDE */);
|
|
258
|
+
}
|
|
259
|
+
function operandString(operation, d) {
|
|
260
|
+
return operation instanceof ConstantOperation ? constantValueString(operation, d) : sqlOperationToString(operation, d);
|
|
261
|
+
}
|
|
262
|
+
var RenderedSql = class extends RelationalOperationElement {
|
|
263
|
+
constructor(sql) {
|
|
264
|
+
super();
|
|
265
|
+
this.sql = sql;
|
|
266
|
+
}
|
|
267
|
+
sql;
|
|
268
|
+
};
|
|
269
|
+
function isCondition(op) {
|
|
270
|
+
return op instanceof ComparisonOperation || op instanceof LogicalOperation || op instanceof IsNullOperation || op instanceof IsNotNullOperation || op instanceof InOperation || op instanceof NotInOperation || op instanceof ExistsOperation || op instanceof NotExistsOperation;
|
|
271
|
+
}
|
|
272
|
+
function sqlOperationToString(operation, d) {
|
|
273
|
+
if (operation instanceof TableAliasColumn) {
|
|
274
|
+
return operation.tableAlias.alias + "." + operation.column.name;
|
|
275
|
+
}
|
|
276
|
+
if (operation instanceof WindowFunctionOperation) {
|
|
277
|
+
const fn = WINDOW_SQL_NAMES[operation.func];
|
|
278
|
+
const parts = [];
|
|
279
|
+
if (operation.element !== null && operation.element !== void 0) {
|
|
280
|
+
parts.push(sqlOperationToString(operation.element, d));
|
|
281
|
+
}
|
|
282
|
+
if (operation.secondArg !== void 0) parts.push(String(operation.secondArg));
|
|
283
|
+
for (const arg of operation.extraArgs) {
|
|
284
|
+
parts.push(typeof arg === "string" ? d.stringLiteral(arg) : String(arg));
|
|
285
|
+
}
|
|
286
|
+
const sql = fn + "(" + parts.join(", ") + ")";
|
|
287
|
+
return sql + (operation.window !== void 0 ? " " + windowSpecToString(operation.window, d) : " OVER ()");
|
|
288
|
+
}
|
|
289
|
+
if (operation instanceof AggregateOperation && operation.operator === "COUNT_DISTINCT" /* COUNT_DISTINCT */) {
|
|
290
|
+
return "COUNT(DISTINCT " + sqlOperationToString(operation.element, d) + ")";
|
|
291
|
+
}
|
|
292
|
+
if (operation instanceof AggregateOperation && operation.operator === "STRING_AGG" /* STRING_AGG */) {
|
|
293
|
+
let sql = d.stringAgg(
|
|
294
|
+
sqlOperationToString(operation.element, d),
|
|
295
|
+
d.stringLiteral(operation.separator ?? ""),
|
|
296
|
+
operation.orderBy.map((s) => sqlOperationToString(s.column, d) + d.orderDirection(s.direction))
|
|
297
|
+
);
|
|
298
|
+
if (operation.window !== void 0) sql += " " + windowSpecToString(operation.window, d);
|
|
299
|
+
return sql;
|
|
300
|
+
}
|
|
301
|
+
if (operation instanceof AggregateOperation) {
|
|
302
|
+
const fn = AGGREGATE_SQL_NAMES[operation.operator] ?? operation.operator;
|
|
303
|
+
let sql = fn + "(" + sqlOperationToString(operation.element, d) + ")";
|
|
304
|
+
if (operation.window !== void 0) {
|
|
305
|
+
sql += " " + windowSpecToString(operation.window, d);
|
|
306
|
+
}
|
|
307
|
+
return sql;
|
|
308
|
+
}
|
|
309
|
+
if (operation instanceof ScalarFunctionOperation) {
|
|
310
|
+
const fn = SCALAR_SQL_NAMES[operation.func];
|
|
311
|
+
const parts = [sqlOperationToString(operation.element, d)];
|
|
312
|
+
if (operation.secondArg !== void 0) parts.push(String(operation.secondArg));
|
|
313
|
+
for (const arg of operation.extraArgs) {
|
|
314
|
+
parts.push(typeof arg === "string" ? d.stringLiteral(arg) : String(arg));
|
|
315
|
+
}
|
|
316
|
+
return fn + "(" + parts.join(", ") + ")";
|
|
317
|
+
}
|
|
318
|
+
if (operation instanceof DateExtractOperation) {
|
|
319
|
+
const element = sqlOperationToString(operation.element, d);
|
|
320
|
+
return operation.part === "DOW" /* DOW */ ? d.dayOfWeek(element) : "EXTRACT(" + operation.part + " FROM " + element + ")";
|
|
321
|
+
}
|
|
322
|
+
if (operation instanceof DateTruncOperation) {
|
|
323
|
+
return "CAST(DATE_TRUNC('" + operation.part.toLowerCase() + "', " + sqlOperationToString(operation.element, d) + ") AS DATE)";
|
|
324
|
+
}
|
|
325
|
+
if (operation instanceof DateArithmeticOperation) {
|
|
326
|
+
const op = operation.isAdd ? "+" : "-";
|
|
327
|
+
return sqlOperationToString(operation.element, d) + " " + op + " INTERVAL '" + operation.n + "' " + operation.unit;
|
|
328
|
+
}
|
|
329
|
+
if (operation instanceof DateDiffOperation) {
|
|
330
|
+
return d.dateDiff(operation.unit, sqlOperationToString(operation.element, d), "TIMESTAMP " + sqlFormatDatetime(operation.other));
|
|
331
|
+
}
|
|
332
|
+
if (operation instanceof CountAllOperation) return "COUNT(*)";
|
|
333
|
+
if (operation instanceof ConstantOperation) return selectedConstantString(operation, d);
|
|
334
|
+
if (operation instanceof RenderedSql) return operation.sql;
|
|
335
|
+
if (operation instanceof ConcatOperation) return operation.parts.map((part) => operandString(part, d)).join(" || ");
|
|
336
|
+
if (operation instanceof ArithmeticOperation) {
|
|
337
|
+
let left = operandString(operation.left, d);
|
|
338
|
+
let right = operandString(operation.right, d);
|
|
339
|
+
if (arithmeticNeedsParentheses(operation, operation.left, false)) left = "(" + left + ")";
|
|
340
|
+
if (arithmeticNeedsParentheses(operation, operation.right, true)) right = "(" + right + ")";
|
|
341
|
+
return left + " " + operation.operator + " " + right;
|
|
342
|
+
}
|
|
343
|
+
if (operation instanceof Alias) {
|
|
344
|
+
return sqlOperationToString(operation.element, d) + " AS " + d.quoteIdentifier(operation.name);
|
|
345
|
+
}
|
|
346
|
+
throw new TypeError("Unknown operation in sqlOperationToString: " + operation.constructor.name);
|
|
347
|
+
}
|
|
348
|
+
function embeddedOf(ref) {
|
|
349
|
+
return ref instanceof Attribute ? ref.embedded() : ref.embedded;
|
|
350
|
+
}
|
|
351
|
+
function findColumn(operation) {
|
|
352
|
+
if (operation instanceof UnaryOperation) return findColumn(operation.element);
|
|
353
|
+
if (operation instanceof ColumnWithJoin) return operation;
|
|
354
|
+
throw new TypeError("Cannot find ColumnWithJoin in: " + operation.constructor.name);
|
|
355
|
+
}
|
|
356
|
+
function openEndClause(endAttr, value, infiniteDatetime) {
|
|
357
|
+
const gtOp = endAttr instanceof DateTimeAttribute ? endAttr.gt(value) : endAttr.gt(value);
|
|
358
|
+
if (infiniteDatetime === void 0) {
|
|
359
|
+
const colRef = new ColumnWithJoin(endAttr.column(), endAttr.parent());
|
|
360
|
+
return new LogicalOperation(gtOp, "OR" /* OR */, new IsNullOperation(colRef));
|
|
361
|
+
}
|
|
362
|
+
return gtOp;
|
|
363
|
+
}
|
|
364
|
+
function andAll(ops) {
|
|
365
|
+
const present = ops.filter((op) => op !== null);
|
|
366
|
+
if (present.length === 0) return null;
|
|
367
|
+
return present.slice(1).reduce((left, right) => new LogicalOperation(left, "AND" /* AND */, right), present[0]);
|
|
368
|
+
}
|
|
369
|
+
function processingPredicate(mc, processingDatetime, joinNode) {
|
|
370
|
+
if (!(mc instanceof ProcessingTemporalColumns || mc instanceof BusinessDateAndProcessingTemporalColumns || mc instanceof BiTemporalColumns) || processingDatetime === null) return null;
|
|
371
|
+
const startAt = new DateTimeAttribute("start_at", mc.startAtColumn.name, mc.startAtColumn.type, mc.startAtColumn.table.qualifiedName, joinNode);
|
|
372
|
+
const endAt = new DateTimeAttribute("end_at", mc.endAtColumn.name, mc.endAtColumn.type, mc.endAtColumn.table.qualifiedName, joinNode);
|
|
373
|
+
return new LogicalOperation(startAt.lte(processingDatetime), "AND" /* AND */, openEndClause(endAt, processingDatetime, mc.infiniteDatetime));
|
|
374
|
+
}
|
|
375
|
+
function businessPredicate(mc, businessDate, joinNode) {
|
|
376
|
+
if (businessDate === null) return null;
|
|
377
|
+
if (mc instanceof BiTemporalColumns) {
|
|
378
|
+
const dateFrom = new DateAttribute("business_date_from", mc.businessDateFromColumn.name, mc.businessDateFromColumn.type, mc.businessDateFromColumn.table.qualifiedName, joinNode);
|
|
379
|
+
const dateTo = new DateAttribute("business_date_to", mc.businessDateToColumn.name, mc.businessDateToColumn.type, mc.businessDateToColumn.table.qualifiedName, joinNode);
|
|
380
|
+
return new LogicalOperation(dateFrom.lte(businessDate), "AND" /* AND */, openEndClause(dateTo, businessDate, mc.infiniteDatetime));
|
|
381
|
+
}
|
|
382
|
+
if (mc instanceof BusinessDateAndProcessingTemporalColumns || mc instanceof SingleBusinessDateColumn) {
|
|
383
|
+
const businessAtt = new DateAttribute("business_date", mc.businessDateColumn.name, mc.businessDateColumn.type, mc.businessDateColumn.table.qualifiedName, joinNode);
|
|
384
|
+
return businessAtt.eq(businessDate);
|
|
385
|
+
}
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
function businessRangePredicate(mc, businessDateFrom, businessDateTo, joinNode) {
|
|
389
|
+
if (businessDateFrom === null || businessDateTo === null) return null;
|
|
390
|
+
if (mc instanceof BiTemporalColumns) {
|
|
391
|
+
const dateFrom = new DateAttribute("business_date_from", mc.businessDateFromColumn.name, mc.businessDateFromColumn.type, mc.businessDateFromColumn.table.qualifiedName, joinNode);
|
|
392
|
+
const dateTo = new DateAttribute("business_date_to", mc.businessDateToColumn.name, mc.businessDateToColumn.type, mc.businessDateToColumn.table.qualifiedName, joinNode);
|
|
393
|
+
return new LogicalOperation(dateFrom.lte(businessDateTo), "AND" /* AND */, openEndClause(dateTo, businessDateFrom, mc.infiniteDatetime));
|
|
394
|
+
}
|
|
395
|
+
if (mc instanceof BusinessDateAndProcessingTemporalColumns || mc instanceof SingleBusinessDateColumn) {
|
|
396
|
+
const businessAtt = new DateAttribute("business_date", mc.businessDateColumn.name, mc.businessDateColumn.type, mc.businessDateColumn.table.qualifiedName, joinNode);
|
|
397
|
+
return new LogicalOperation(businessAtt.gte(businessDateFrom), "AND" /* AND */, businessAtt.lte(businessDateTo));
|
|
398
|
+
}
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
function buildMilestoningFilterOperation(businessDate, processingDatetime, table, joinNode) {
|
|
402
|
+
const mc = table.milestoningColumns;
|
|
403
|
+
return andAll([processingPredicate(mc, processingDatetime, joinNode), businessPredicate(mc, businessDate, joinNode)]);
|
|
404
|
+
}
|
|
405
|
+
function buildMilestoningFilterOperationForDateRange(businessDateFrom, businessDateTo, processingDatetime, table, joinNode) {
|
|
406
|
+
const mc = table.milestoningColumns;
|
|
407
|
+
return andAll([
|
|
408
|
+
processingPredicate(mc, processingDatetime, joinNode),
|
|
409
|
+
businessRangePredicate(mc, businessDateFrom, businessDateTo, joinNode)
|
|
410
|
+
]);
|
|
411
|
+
}
|
|
412
|
+
function walkRefs(op, visit, visitExists) {
|
|
413
|
+
if (op === null || op === void 0) return;
|
|
414
|
+
if (op instanceof Attribute || op instanceof ColumnWithJoin) {
|
|
415
|
+
visit(op);
|
|
416
|
+
} else if (op instanceof ExistsOperation || op instanceof NotExistsOperation) {
|
|
417
|
+
visitExists?.(op.node);
|
|
418
|
+
} else if (op instanceof AggregateOperation) {
|
|
419
|
+
walkRefs(op.element, visit, visitExists);
|
|
420
|
+
if (op.window) walkWindowSpec(op.window, visit, visitExists);
|
|
421
|
+
for (const sort of op.orderBy) walkRefs(sort, visit, visitExists);
|
|
422
|
+
} else if (op instanceof WindowFunctionOperation) {
|
|
423
|
+
if (op.element) walkRefs(op.element, visit, visitExists);
|
|
424
|
+
if (op.window) walkWindowSpec(op.window, visit, visitExists);
|
|
425
|
+
} else if (op instanceof ScalarFunctionOperation || op instanceof DateExtractOperation || op instanceof DateTruncOperation || op instanceof DateArithmeticOperation || op instanceof DateDiffOperation || op instanceof IsNullOperation || op instanceof IsNotNullOperation || op instanceof InOperation || op instanceof NotInOperation) {
|
|
426
|
+
walkRefs(op.element, visit, visitExists);
|
|
427
|
+
} else if (op instanceof Alias) {
|
|
428
|
+
walkRefs(op.element, visit, visitExists);
|
|
429
|
+
} else if (op instanceof ConcatOperation) {
|
|
430
|
+
for (const part of op.parts) walkRefs(part, visit, visitExists);
|
|
431
|
+
} else if (op instanceof ArithmeticOperation) {
|
|
432
|
+
walkRefs(op.left, visit, visitExists);
|
|
433
|
+
walkRefs(op.right, visit, visitExists);
|
|
434
|
+
} else if (op instanceof CaseOperation) {
|
|
435
|
+
walkRefs(op.condition, visit, visitExists);
|
|
436
|
+
walkRefs(op.then, visit, visitExists);
|
|
437
|
+
walkRefs(op.fallback, visit, visitExists);
|
|
438
|
+
} else if (op instanceof SortOperation) {
|
|
439
|
+
walkRefs(op.column, visit, visitExists);
|
|
440
|
+
} else if (op instanceof LogicalOperation || op instanceof ComparisonOperation) {
|
|
441
|
+
walkRefs(op.left, visit, visitExists);
|
|
442
|
+
walkRefs(op.right, visit, visitExists);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
function walkWindowSpec(window, visit, visitExists) {
|
|
446
|
+
for (const p of window.partitionBy) walkRefs(p, visit, visitExists);
|
|
447
|
+
for (const s of window.orderBy) walkRefs(s.column, visit, visitExists);
|
|
448
|
+
}
|
|
449
|
+
function buildJoinFilterFromProperties(node, filter, columnMap) {
|
|
450
|
+
const ops = [];
|
|
451
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
452
|
+
const entry = columnMap[key];
|
|
453
|
+
if (entry === void 0) {
|
|
454
|
+
throw new Error(`Unknown property '${key}' \u2014 valid properties are: ${Object.keys(columnMap).sort().join(", ")}`);
|
|
455
|
+
}
|
|
456
|
+
const [name, type, table] = entry;
|
|
457
|
+
const col = new ColumnWithJoin(new Column(name, type, table), node);
|
|
458
|
+
let constant;
|
|
459
|
+
if (typeof value === "boolean") constant = new BooleanConstantOperation(value);
|
|
460
|
+
else if (typeof value === "number") constant = Number.isInteger(value) ? new IntegerConstantOperation(value) : new FloatConstantOperation(value);
|
|
461
|
+
else if (value instanceof Date) constant = type.toUpperCase() === "DATE" ? new DateConstantOperation(value) : new DateTimeConstantOperation(value);
|
|
462
|
+
else constant = new StringConstantOperation(String(value));
|
|
463
|
+
ops.push(new ComparisonOperation(col, "EQUAL" /* EQUAL */, constant));
|
|
464
|
+
}
|
|
465
|
+
return ops.reduce((acc, op) => new LogicalOperation(acc, "AND" /* AND */, op));
|
|
466
|
+
}
|
|
467
|
+
function withAncestors(nodes) {
|
|
468
|
+
const ordered = /* @__PURE__ */ new Set();
|
|
469
|
+
const add = (node) => {
|
|
470
|
+
if (ordered.has(node)) return;
|
|
471
|
+
if (node.parent !== void 0) add(node.parent);
|
|
472
|
+
ordered.add(node);
|
|
473
|
+
};
|
|
474
|
+
for (const node of nodes) add(node);
|
|
475
|
+
return [...ordered];
|
|
476
|
+
}
|
|
477
|
+
function buildQueryOperation(businessDate, processingDatetime, columns, table, op, orderBy = [], groupBy = [], limit, businessDateTo = null, distinct = false, offset, having) {
|
|
478
|
+
let filter = op;
|
|
479
|
+
const milestoningFor = (t, node) => businessDateTo !== null ? buildMilestoningFilterOperationForDateRange(businessDate, businessDateTo, processingDatetime, t, node) : buildMilestoningFilterOperation(businessDate, processingDatetime, t, node);
|
|
480
|
+
if (table instanceof MilestonedTable) {
|
|
481
|
+
const milestonedOp = milestoningFor(table);
|
|
482
|
+
if (milestonedOp !== null) {
|
|
483
|
+
filter = filter instanceof NoOperation ? milestonedOp : new LogicalOperation(filter, "AND" /* AND */, milestonedOp);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
const requiredJoins = /* @__PURE__ */ new Set();
|
|
487
|
+
const collect = (ref) => {
|
|
488
|
+
const parent = ref instanceof Attribute ? ref.parent() : ref.parent;
|
|
489
|
+
if (parent !== void 0) requiredJoins.add(parent);
|
|
490
|
+
};
|
|
491
|
+
const collectNode = (node) => requiredJoins.add(node);
|
|
492
|
+
for (const col of columns) walkRefs(col, collect, collectNode);
|
|
493
|
+
for (const expr of groupBy) walkRefs(expr, collect, collectNode);
|
|
494
|
+
for (const sort of orderBy) walkRefs(sort, collect, collectNode);
|
|
495
|
+
walkRefs(op, collect, collectNode);
|
|
496
|
+
const joinFilters = /* @__PURE__ */ new Map();
|
|
497
|
+
for (const node of withAncestors(requiredJoins)) {
|
|
498
|
+
let combined;
|
|
499
|
+
if (Object.keys(node.join.filterKwargs).length > 0) {
|
|
500
|
+
combined = buildJoinFilterFromProperties(node, node.join.filterKwargs, node.join.filterColumnMap);
|
|
501
|
+
}
|
|
502
|
+
if (node.join.target instanceof MilestonedTable) {
|
|
503
|
+
const milestonedOp = milestoningFor(node.join.target, node);
|
|
504
|
+
if (milestonedOp !== null) {
|
|
505
|
+
combined = combined === void 0 ? milestonedOp : new LogicalOperation(combined, "AND" /* AND */, milestonedOp);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
if (combined !== void 0) joinFilters.set(node, combined);
|
|
509
|
+
}
|
|
510
|
+
return new SelectOperation(columns, filter, orderBy, groupBy, limit, table, joinFilters, distinct, offset, having);
|
|
511
|
+
}
|
|
512
|
+
function needsParentheses(parent, child) {
|
|
513
|
+
return child instanceof LogicalOperation && child.operator !== parent.operator;
|
|
514
|
+
}
|
|
515
|
+
function reachesToMany(op) {
|
|
516
|
+
if (op instanceof ColumnWithJoin) {
|
|
517
|
+
for (let node = op.parent; node !== void 0; node = node.parent) if (node.many) return true;
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
if (op instanceof UnaryOperation || op instanceof Alias) return reachesToMany(op.element);
|
|
521
|
+
if (op instanceof BinaryOperation) return reachesToMany(op.left) || reachesToMany(op.right);
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
function unaliased(op) {
|
|
525
|
+
return op instanceof Alias ? unaliased(op.element) : op;
|
|
526
|
+
}
|
|
527
|
+
var SQLQueryGenerator = class {
|
|
528
|
+
constructor(_dialect) {
|
|
529
|
+
this._dialect = _dialect;
|
|
530
|
+
}
|
|
531
|
+
_dialect;
|
|
532
|
+
_select = [];
|
|
533
|
+
_from = /* @__PURE__ */ new Set();
|
|
534
|
+
_join = [];
|
|
535
|
+
_groupByParts = [];
|
|
536
|
+
_having = "";
|
|
537
|
+
_orderByParts = [];
|
|
538
|
+
_where = "";
|
|
539
|
+
_limit;
|
|
540
|
+
_distinct = false;
|
|
541
|
+
_offset;
|
|
542
|
+
_tableAliasIncr = 0;
|
|
543
|
+
_tableAliasesByTable = /* @__PURE__ */ new Map();
|
|
544
|
+
_addedJoinIds = /* @__PURE__ */ new Set();
|
|
545
|
+
_rootTable;
|
|
546
|
+
_elidableNodes = /* @__PURE__ */ new Set();
|
|
547
|
+
_joinFilters = /* @__PURE__ */ new Map();
|
|
548
|
+
_guardOptional = true;
|
|
549
|
+
generate(select) {
|
|
550
|
+
this._rootTable = select.table;
|
|
551
|
+
this._elidableNodes = this._computeElidableNodes(select);
|
|
552
|
+
this._joinFilters = select.joinFilters;
|
|
553
|
+
if (this._rootTable !== void 0) {
|
|
554
|
+
this._from.add(this._tableAliasForTable(this._rootTable.qualifiedName, this._rootTable.qualifiedName));
|
|
555
|
+
}
|
|
556
|
+
this.processSelect(select.display);
|
|
557
|
+
this._where = this.buildFilter(select.filter);
|
|
558
|
+
const groupByJoins = /* @__PURE__ */ new Map();
|
|
559
|
+
for (const expr of select.groupBy) {
|
|
560
|
+
this._collectRequiredJoins(expr, groupByJoins);
|
|
561
|
+
}
|
|
562
|
+
for (const node of groupByJoins.values()) {
|
|
563
|
+
this._addJoin(node);
|
|
564
|
+
}
|
|
565
|
+
this._groupByParts = this._groupByPartsFor(select);
|
|
566
|
+
this._having = "";
|
|
567
|
+
if (select.having !== void 0) {
|
|
568
|
+
const havingJoins = /* @__PURE__ */ new Map();
|
|
569
|
+
this._collectRequiredJoins(select.having, havingJoins);
|
|
570
|
+
for (const node of havingJoins.values()) this._addJoin(node);
|
|
571
|
+
this._guardOptional = false;
|
|
572
|
+
try {
|
|
573
|
+
this._having = this.buildFilter(select.having);
|
|
574
|
+
} finally {
|
|
575
|
+
this._guardOptional = true;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
this._orderByParts = select.orderBy.map(
|
|
579
|
+
(s) => this._sortExpression(s.column) + this._dialect.orderDirection(s.direction)
|
|
580
|
+
);
|
|
581
|
+
this._limit = select.limit;
|
|
582
|
+
this._distinct = select.distinct;
|
|
583
|
+
this._offset = select.offset;
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* The GROUP BY of a query: the grouping it was given, else the grouping its columns imply.
|
|
587
|
+
*
|
|
588
|
+
* Columns that are not aggregated are what the aggregates are computed for, so a query that selects both is
|
|
589
|
+
* grouped by those columns (as a DataFrame's `groupby(columns).agg(...)` does). An aggregate read through a
|
|
590
|
+
* to-many association is computed for each row of the root table, as Legend does, so the root's primary key
|
|
591
|
+
* is grouped by too. A query that selects only aggregates over the root's own columns is not grouped: the
|
|
592
|
+
* aggregate covers every row. A constant needs no grouping: a bare
|
|
593
|
+
* `GROUP BY 7` would be a column position.
|
|
594
|
+
*/
|
|
595
|
+
_groupByPartsFor(select) {
|
|
596
|
+
if (select.groupBy.length > 0) return select.groupBy.map((a) => this._attrToColString(a));
|
|
597
|
+
const aggregates = select.display.filter(containsAggregate);
|
|
598
|
+
if (aggregates.length === 0) return [];
|
|
599
|
+
const perRoot = select.table !== void 0 && aggregates.some(reachesToMany);
|
|
600
|
+
const keys = perRoot ? select.table.columns.filter((c) => c.primaryKey).map((c) => this._columnToString(c)) : [];
|
|
601
|
+
const grouped = select.display.map((column, i) => ({ column, shown: this._select[i] })).filter(({ column }) => !containsAggregateOrWindow(column) && !(unaliased(column) instanceof ConstantOperation)).map(({ shown }) => sqlOperationToString(shown.element, this._dialect));
|
|
602
|
+
return [.../* @__PURE__ */ new Set([...keys, ...grouped])];
|
|
603
|
+
}
|
|
604
|
+
/** What a sort orders by: a column, or an expression such as an aggregate. */
|
|
605
|
+
_sortExpression(expression) {
|
|
606
|
+
if (expression instanceof ColumnWithJoin || expression instanceof Attribute) return this.buildFilter(expression);
|
|
607
|
+
return sqlOperationToString(this._rewriteOperation(expression), this._dialect);
|
|
608
|
+
}
|
|
609
|
+
_columnToString(column) {
|
|
610
|
+
return this._tableAliasForTable(column.owner, column.owner).alias + "." + column.name;
|
|
611
|
+
}
|
|
612
|
+
_attrToColString(attr) {
|
|
613
|
+
const { column, parent } = this._resolve(attr);
|
|
614
|
+
const ta = this._tableAliasForTable(column.owner, parent ?? column.owner);
|
|
615
|
+
return ta.alias + "." + column.name;
|
|
616
|
+
}
|
|
617
|
+
_collectRequiredJoins(op, out) {
|
|
618
|
+
walkRefs(op, (ref) => {
|
|
619
|
+
const { parent } = this._resolve(ref);
|
|
620
|
+
if (parent !== void 0) out.set(parent, parent);
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Determine upfront which join nodes can be satisfied entirely by embedded (join-less) alternates:
|
|
625
|
+
* a node is elidable when every reference to it has an embedded alternate. A reference without one
|
|
626
|
+
* (e.g. a milestoning column) keeps the join for every reference to that node.
|
|
627
|
+
*/
|
|
628
|
+
_computeElidableNodes(select) {
|
|
629
|
+
const refsByNode = /* @__PURE__ */ new Map();
|
|
630
|
+
const record = (ref) => {
|
|
631
|
+
const parent = ref instanceof Attribute ? ref.parent() : ref.parent;
|
|
632
|
+
if (parent === void 0) return;
|
|
633
|
+
const refs = refsByNode.get(parent);
|
|
634
|
+
if (refs) refs.push(ref);
|
|
635
|
+
else refsByNode.set(parent, [ref]);
|
|
636
|
+
};
|
|
637
|
+
for (const col of select.display) walkRefs(col, record);
|
|
638
|
+
for (const expr of select.groupBy) walkRefs(expr, record);
|
|
639
|
+
for (const sort of select.orderBy) walkRefs(sort, record);
|
|
640
|
+
walkRefs(select.filter, record);
|
|
641
|
+
const elidable = /* @__PURE__ */ new Set();
|
|
642
|
+
for (const [node, refs] of refsByNode) {
|
|
643
|
+
if (refs.every((ref) => embeddedOf(ref) !== void 0)) elidable.add(node);
|
|
644
|
+
}
|
|
645
|
+
return elidable;
|
|
646
|
+
}
|
|
647
|
+
/** The (column, parent) a reference should resolve to, substituting its embedded alternate when its join is elided. */
|
|
648
|
+
_resolve(ref) {
|
|
649
|
+
const column = ref instanceof Attribute ? ref.column() : ref.column;
|
|
650
|
+
const parent = ref instanceof Attribute ? ref.parent() : ref.parent;
|
|
651
|
+
if (parent !== void 0 && this._elidableNodes.has(parent)) {
|
|
652
|
+
const embedded = embeddedOf(ref);
|
|
653
|
+
if (embedded !== void 0) return this._resolve(embedded);
|
|
654
|
+
}
|
|
655
|
+
return { column, parent };
|
|
656
|
+
}
|
|
657
|
+
/** Whether the value `op` reads can be missing, in the join or embedded form that is used for it. */
|
|
658
|
+
_resolveOptional(op) {
|
|
659
|
+
if (op.parent !== void 0 && this._elidableNodes.has(op.parent) && op.embedded !== void 0) {
|
|
660
|
+
return this._resolveOptional(op.embedded);
|
|
661
|
+
}
|
|
662
|
+
return op.optional;
|
|
663
|
+
}
|
|
664
|
+
_rewriteWindowSpec(window) {
|
|
665
|
+
const partitionBy = window.partitionBy.map((p) => this._rewriteOperation(p));
|
|
666
|
+
const orderBy = window.orderBy.map((s) => new SortOperation(this._rewriteOperation(s.column), s.direction));
|
|
667
|
+
return new WindowSpecification(partitionBy, orderBy);
|
|
668
|
+
}
|
|
669
|
+
_rewriteOperation(op) {
|
|
670
|
+
if (op instanceof Attribute || op instanceof ColumnWithJoin) {
|
|
671
|
+
const { column, parent } = this._resolve(op);
|
|
672
|
+
return new TableAliasColumn(column, this._tableAliasForTable(column.owner, parent ?? column.owner));
|
|
673
|
+
}
|
|
674
|
+
if (op instanceof Column) {
|
|
675
|
+
const ta = this._tableAliasForTable(op.owner, op.owner);
|
|
676
|
+
return new TableAliasColumn(op, ta);
|
|
677
|
+
}
|
|
678
|
+
if (op instanceof AggregateOperation) {
|
|
679
|
+
return new AggregateOperation(
|
|
680
|
+
this._rewriteOperation(op.element),
|
|
681
|
+
op.operator,
|
|
682
|
+
op.displayName,
|
|
683
|
+
op.window ? this._rewriteWindowSpec(op.window) : void 0,
|
|
684
|
+
op.separator,
|
|
685
|
+
op.orderBy.map((s) => new SortOperation(this._rewriteOperation(s.column), s.direction))
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
if (op instanceof CaseOperation) return new RenderedSql(this._caseSql(op));
|
|
689
|
+
if (isCondition(op)) return new RenderedSql(this.buildFilter(op));
|
|
690
|
+
if (op instanceof ConcatOperation) return new ConcatOperation(op.parts.map((part) => this._rewriteOperation(part)), op.displayName);
|
|
691
|
+
if (op instanceof ArithmeticOperation) {
|
|
692
|
+
return new ArithmeticOperation(this._rewriteOperation(op.left), op.operator, this._rewriteOperation(op.right), op.displayName);
|
|
693
|
+
}
|
|
694
|
+
if (op instanceof WindowFunctionOperation) {
|
|
695
|
+
const el = op.element ? this._rewriteOperation(op.element) : null;
|
|
696
|
+
return new WindowFunctionOperation(
|
|
697
|
+
el,
|
|
698
|
+
op.func,
|
|
699
|
+
op.displayName,
|
|
700
|
+
op.secondArg,
|
|
701
|
+
[...op.extraArgs],
|
|
702
|
+
op.window ? this._rewriteWindowSpec(op.window) : void 0
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
if (op instanceof ScalarFunctionOperation) {
|
|
706
|
+
return new ScalarFunctionOperation(this._rewriteOperation(op.element), op.func, op.displayName, op.secondArg, op.extraArgs);
|
|
707
|
+
}
|
|
708
|
+
if (op instanceof DateExtractOperation) {
|
|
709
|
+
return new DateExtractOperation(this._rewriteOperation(op.element), op.part, op.displayName);
|
|
710
|
+
}
|
|
711
|
+
if (op instanceof DateTruncOperation) {
|
|
712
|
+
return new DateTruncOperation(this._rewriteOperation(op.element), op.part, op.displayName);
|
|
713
|
+
}
|
|
714
|
+
if (op instanceof DateArithmeticOperation) {
|
|
715
|
+
return new DateArithmeticOperation(this._rewriteOperation(op.element), op.n, op.unit, op.isAdd, op.displayName);
|
|
716
|
+
}
|
|
717
|
+
if (op instanceof DateDiffOperation) {
|
|
718
|
+
return new DateDiffOperation(this._rewriteOperation(op.element), op.other, op.unit, op.displayName);
|
|
719
|
+
}
|
|
720
|
+
return op;
|
|
721
|
+
}
|
|
722
|
+
/** The CASE written out now: its condition can only be rendered while the query is being built. */
|
|
723
|
+
_caseSql(op) {
|
|
724
|
+
const branch = (value) => value instanceof ConstantOperation ? constantValueString(value, this._dialect) : sqlOperationToString(this._rewriteOperation(value), this._dialect);
|
|
725
|
+
let sql = "CASE WHEN " + this.buildFilter(op.condition) + " THEN " + branch(op.then);
|
|
726
|
+
if (op.fallback !== void 0) sql += " ELSE " + branch(op.fallback);
|
|
727
|
+
return sql + " END";
|
|
728
|
+
}
|
|
729
|
+
processSelect(cols) {
|
|
730
|
+
const requiredJoins = /* @__PURE__ */ new Map();
|
|
731
|
+
for (const col of cols) {
|
|
732
|
+
this._collectRequiredJoins(col, requiredJoins);
|
|
733
|
+
}
|
|
734
|
+
for (const node of requiredJoins.values()) {
|
|
735
|
+
this._addJoin(node);
|
|
736
|
+
}
|
|
737
|
+
for (const col of cols) this._selectColumn(col);
|
|
738
|
+
}
|
|
739
|
+
/** Add one column to the select list. An alias reads the column it wraps, then names the entry. */
|
|
740
|
+
_selectColumn(col) {
|
|
741
|
+
if (col instanceof Alias) {
|
|
742
|
+
this._selectColumn(col.element);
|
|
743
|
+
const read = this._select.pop();
|
|
744
|
+
this._select.push(new Alias(read.element, col.name));
|
|
745
|
+
} else if (col instanceof Attribute) {
|
|
746
|
+
const { column, parent: node } = this._resolve(col);
|
|
747
|
+
let ta;
|
|
748
|
+
if (node !== void 0) {
|
|
749
|
+
ta = this._tableAliasForTable(column.owner, node);
|
|
750
|
+
} else {
|
|
751
|
+
ta = this._tableAliasForTable(column.owner, column.owner);
|
|
752
|
+
this._from.add(ta);
|
|
753
|
+
}
|
|
754
|
+
this._select.push(new Alias(new TableAliasColumn(column, ta), col.displayName()));
|
|
755
|
+
} else if (col instanceof WindowFunctionOperation) {
|
|
756
|
+
const rewritten = this._rewriteOperation(col);
|
|
757
|
+
let alias;
|
|
758
|
+
if (col.displayName) {
|
|
759
|
+
alias = col.displayName;
|
|
760
|
+
} else if (["ROW_NUMBER" /* ROW_NUMBER */, "RANK" /* RANK */, "DENSE_RANK" /* DENSE_RANK */].includes(col.func)) {
|
|
761
|
+
alias = col.func.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
762
|
+
} else if (col.element) {
|
|
763
|
+
alias = col.func + " " + findColumn(col).column.name;
|
|
764
|
+
} else {
|
|
765
|
+
alias = col.func;
|
|
766
|
+
}
|
|
767
|
+
this._select.push(new Alias(rewritten, alias));
|
|
768
|
+
} else if (col instanceof AggregateOperation && !(col.element instanceof ColumnWithJoin)) {
|
|
769
|
+
this._select.push(new Alias(this._rewriteOperation(col), col.displayName ?? "Expression"));
|
|
770
|
+
} else if (col instanceof AggregateOperation) {
|
|
771
|
+
const colNested = this._resolve(findColumn(col));
|
|
772
|
+
const node = colNested.parent;
|
|
773
|
+
let ta;
|
|
774
|
+
if (node !== void 0) {
|
|
775
|
+
ta = this._tableAliasForTable(colNested.column.owner, node);
|
|
776
|
+
} else {
|
|
777
|
+
ta = this._tableAliasForTable(colNested.column.owner, colNested.column.owner);
|
|
778
|
+
this._from.add(ta);
|
|
779
|
+
}
|
|
780
|
+
const alias = col.displayName ?? col.operator + " " + findColumn(col).column.name;
|
|
781
|
+
const rewrittenAgg = new AggregateOperation(
|
|
782
|
+
new TableAliasColumn(colNested.column, ta),
|
|
783
|
+
col.operator,
|
|
784
|
+
void 0,
|
|
785
|
+
col.window ? this._rewriteWindowSpec(col.window) : void 0,
|
|
786
|
+
col.separator,
|
|
787
|
+
col.orderBy.map((s) => new SortOperation(this._rewriteOperation(s.column), s.direction))
|
|
788
|
+
);
|
|
789
|
+
this._select.push(new Alias(rewrittenAgg, alias));
|
|
790
|
+
} else if (col instanceof ScalarFunctionOperation) {
|
|
791
|
+
this._select.push(new Alias(this._rewriteOperation(col), col.displayName ?? col.func + " " + findColumn(col).column.name));
|
|
792
|
+
} else if (col instanceof DateExtractOperation || col instanceof DateArithmeticOperation || col instanceof DateDiffOperation) {
|
|
793
|
+
const colNested = this._resolve(findColumn(col));
|
|
794
|
+
const node = colNested.parent;
|
|
795
|
+
let ta;
|
|
796
|
+
if (node !== void 0) {
|
|
797
|
+
ta = this._tableAliasForTable(colNested.column.owner, node);
|
|
798
|
+
} else {
|
|
799
|
+
ta = this._tableAliasForTable(colNested.column.owner, colNested.column.owner);
|
|
800
|
+
this._from.add(ta);
|
|
801
|
+
}
|
|
802
|
+
const alias = col.displayName ?? findColumn(col).column.name;
|
|
803
|
+
let aliasedOp;
|
|
804
|
+
if (col instanceof DateExtractOperation) {
|
|
805
|
+
aliasedOp = new DateExtractOperation(new TableAliasColumn(colNested.column, ta), col.part);
|
|
806
|
+
} else if (col instanceof DateArithmeticOperation) {
|
|
807
|
+
aliasedOp = new DateArithmeticOperation(new TableAliasColumn(colNested.column, ta), col.n, col.unit, col.isAdd);
|
|
808
|
+
} else {
|
|
809
|
+
aliasedOp = new DateDiffOperation(new TableAliasColumn(colNested.column, ta), col.other, col.unit);
|
|
810
|
+
}
|
|
811
|
+
this._select.push(new Alias(aliasedOp, alias));
|
|
812
|
+
} else if (col instanceof CaseOperation) {
|
|
813
|
+
this._select.push(new Alias(this._rewriteOperation(col), col.displayName ?? "Expression"));
|
|
814
|
+
} else if (col instanceof ConcatOperation) {
|
|
815
|
+
this._select.push(new Alias(this._rewriteOperation(col), col.displayName ?? "Expression"));
|
|
816
|
+
} else if (col instanceof ArithmeticOperation) {
|
|
817
|
+
this._select.push(new Alias(this._rewriteOperation(col), col.displayName ?? "Expression"));
|
|
818
|
+
} else if (col instanceof DateTruncOperation) {
|
|
819
|
+
this._select.push(new Alias(this._rewriteOperation(col), col.displayName ?? findColumn(col).column.name));
|
|
820
|
+
} else if (isCondition(col)) {
|
|
821
|
+
this._select.push(new Alias(new RenderedSql(this.buildFilter(col)), "Expression"));
|
|
822
|
+
} else if (col instanceof ConstantOperation) {
|
|
823
|
+
this._select.push(new Alias(col, "Constant"));
|
|
824
|
+
} else if (col instanceof CountAllOperation) {
|
|
825
|
+
if (col.table !== "") this._from.add(this._tableAliasForTable(col.table, col.table));
|
|
826
|
+
this._select.push(new Alias(col, "Count"));
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
_addJoin(node) {
|
|
830
|
+
if (this._addedJoinIds.has(node)) return;
|
|
831
|
+
if (node.parent !== void 0) this._addJoin(node.parent);
|
|
832
|
+
this._addedJoinIds.add(node);
|
|
833
|
+
const join = node.join;
|
|
834
|
+
const left = join.left;
|
|
835
|
+
const srcTa = this._tableAliasForTable(left.owner, node.parent ?? left.owner);
|
|
836
|
+
if (node.parent === void 0) this._from.add(srcTa);
|
|
837
|
+
const sc = new TableAliasColumn(left, srcTa);
|
|
838
|
+
const right = join.right;
|
|
839
|
+
const tc = new TableAliasColumn(right, this._tableAliasForTable(right.owner, node));
|
|
840
|
+
const queryFilter = this._joinFilters.get(node);
|
|
841
|
+
const filterOp = join.filter !== void 0 && queryFilter !== void 0 ? new LogicalOperation(join.filter, "AND" /* AND */, queryFilter) : join.filter ?? queryFilter;
|
|
842
|
+
this._join.push(new Join(sc, tc, filterOp));
|
|
843
|
+
}
|
|
844
|
+
buildFilter(op) {
|
|
845
|
+
if (op instanceof NoOperation) return "";
|
|
846
|
+
if (op instanceof ExistsOperation) {
|
|
847
|
+
const col = op.node.join.right;
|
|
848
|
+
return this.buildFilter(new ColumnWithJoin(col, op.node)) + " IS NOT NULL";
|
|
849
|
+
}
|
|
850
|
+
if (op instanceof NotExistsOperation) {
|
|
851
|
+
const col = op.node.join.right;
|
|
852
|
+
return this.buildFilter(new ColumnWithJoin(col, op.node)) + " IS NULL";
|
|
853
|
+
}
|
|
854
|
+
if (op instanceof InOperation) {
|
|
855
|
+
return this.buildFilter(op.element) + " IN (" + op.values.map((value) => constantValueString(value, this._dialect)).join(", ") + ")";
|
|
856
|
+
}
|
|
857
|
+
if (op instanceof NotInOperation) {
|
|
858
|
+
const column = this.buildFilter(op.element);
|
|
859
|
+
const sql = column + " NOT IN (" + op.values.map((value) => constantValueString(value, this._dialect)).join(", ") + ")";
|
|
860
|
+
if (op.element instanceof ColumnWithJoin && this._resolve(op.element).column.nullable) {
|
|
861
|
+
return "(" + sql + " OR " + column + " IS NULL)";
|
|
862
|
+
}
|
|
863
|
+
return sql;
|
|
864
|
+
}
|
|
865
|
+
if (op instanceof IsNotNullOperation) return this.buildFilter(op.element) + " IS NOT NULL";
|
|
866
|
+
if (op instanceof IsNullOperation) return this.buildFilter(op.element) + " IS NULL";
|
|
867
|
+
if (op instanceof LogicalOperation) {
|
|
868
|
+
let left = this.buildFilter(op.left);
|
|
869
|
+
let right = this.buildFilter(op.right);
|
|
870
|
+
if (needsParentheses(op, op.left)) left = "(" + left + ")";
|
|
871
|
+
if (needsParentheses(op, op.right)) right = "(" + right + ")";
|
|
872
|
+
return left + LOGICAL_OPERATOR_STR[op.operator] + right;
|
|
873
|
+
}
|
|
874
|
+
if (op instanceof ComparisonOperation) {
|
|
875
|
+
let sql = this.buildFilter(op.left) + COMPARISON_OPERATOR_STR[op.operator] + this.buildFilter(op.right);
|
|
876
|
+
if ((op.operator === "LIKE" /* LIKE */ || op.operator === "NOT_LIKE" /* NOT_LIKE */) && op.right instanceof StringConstantOperation && (op.right.value.includes(LIKE_ESCAPE) || op.right.value.includes("\\"))) {
|
|
877
|
+
sql += " ESCAPE '" + LIKE_ESCAPE + "'";
|
|
878
|
+
}
|
|
879
|
+
if (this._guardOptional && GUARDED_COMPARISONS.has(op.operator) && op.left instanceof ColumnWithJoin && this._resolveOptional(op.left)) {
|
|
880
|
+
sql = "(" + this.buildFilter(op.left) + " IS NOT NULL AND " + sql + ")";
|
|
881
|
+
}
|
|
882
|
+
return sql;
|
|
883
|
+
}
|
|
884
|
+
if (op instanceof ConstantOperation) return constantValueString(op, this._dialect);
|
|
885
|
+
if (op instanceof ScalarFunctionOperation || op instanceof ArithmeticOperation || op instanceof ConcatOperation || op instanceof DateTruncOperation || op instanceof AggregateOperation || op instanceof CountAllOperation) {
|
|
886
|
+
const required = /* @__PURE__ */ new Map();
|
|
887
|
+
this._collectRequiredJoins(op, required);
|
|
888
|
+
for (const node of required.values()) this._addJoin(node);
|
|
889
|
+
return sqlOperationToString(this._rewriteOperation(op), this._dialect);
|
|
890
|
+
}
|
|
891
|
+
if (op instanceof ColumnWithJoin) {
|
|
892
|
+
const { column, parent: node } = this._resolve(op);
|
|
893
|
+
let ta;
|
|
894
|
+
if (node !== void 0) {
|
|
895
|
+
ta = this._tableAliasForTable(column.owner, node);
|
|
896
|
+
this._addJoin(node);
|
|
897
|
+
} else {
|
|
898
|
+
ta = this._tableAliasForTable(column.owner, column.owner);
|
|
899
|
+
}
|
|
900
|
+
return ta.alias + "." + column.name;
|
|
901
|
+
}
|
|
902
|
+
if (op instanceof Column) {
|
|
903
|
+
const ta = this._tableAliasForTable(op.owner, op.owner);
|
|
904
|
+
return ta.alias + "." + op.name;
|
|
905
|
+
}
|
|
906
|
+
throw new Error("Unknown operation in buildFilter: " + op.constructor.name);
|
|
907
|
+
}
|
|
908
|
+
_tableAliasForTable(table, key) {
|
|
909
|
+
if (this._tableAliasesByTable.has(key)) {
|
|
910
|
+
return this._tableAliasesByTable.get(key);
|
|
911
|
+
}
|
|
912
|
+
const ta = new TableAlias(table, "t" + this._tableAliasIncr++);
|
|
913
|
+
this._tableAliasesByTable.set(key, ta);
|
|
914
|
+
return ta;
|
|
915
|
+
}
|
|
916
|
+
buildQueryString() {
|
|
917
|
+
const joins = this._join.map(
|
|
918
|
+
(j) => " LEFT OUTER JOIN " + j.target.tableAlias.table + " AS " + j.target.tableAlias.alias + " ON (" + j.condition() + (j.filterOp ? " AND " + this.buildFilter(j.filterOp) : "") + ")"
|
|
919
|
+
);
|
|
920
|
+
return (this._distinct ? "SELECT DISTINCT " : "SELECT ") + this._select.map((ca) => sqlOperationToString(ca, this._dialect)).join(",") + " FROM " + Array.from(this._from).map((ta) => ta.table + " AS " + ta.alias).join(",") + joins.join("") + this._buildWhere() + this._buildGroupBy() + this._buildHaving() + this._buildOrderBy() + this._buildLimit() + this._buildOffset();
|
|
921
|
+
}
|
|
922
|
+
_buildWhere() {
|
|
923
|
+
return this._where.length > 0 ? " WHERE " + this._where : "";
|
|
924
|
+
}
|
|
925
|
+
_buildGroupBy() {
|
|
926
|
+
return this._groupByParts.length > 0 ? " GROUP BY " + this._groupByParts.join(", ") : "";
|
|
927
|
+
}
|
|
928
|
+
_buildHaving() {
|
|
929
|
+
return this._having.length > 0 ? " HAVING " + this._having : "";
|
|
930
|
+
}
|
|
931
|
+
_buildOrderBy() {
|
|
932
|
+
return this._orderByParts.length > 0 ? " ORDER BY " + this._orderByParts.join(", ") : "";
|
|
933
|
+
}
|
|
934
|
+
_buildOffset() {
|
|
935
|
+
return this._offset !== void 0 ? " OFFSET " + this._offset : "";
|
|
936
|
+
}
|
|
937
|
+
_buildLimit() {
|
|
938
|
+
return this._limit !== void 0 ? " LIMIT " + this._limit : "";
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
function selectSqlToString(selectOperation, dialect = "postgres") {
|
|
942
|
+
const qg = new SQLQueryGenerator(dialectOf(dialect));
|
|
943
|
+
qg.generate(selectOperation);
|
|
944
|
+
return qg.buildQueryString();
|
|
945
|
+
}
|
|
946
|
+
function toSql(businessDate, processingDatetime, columns, table, op, orderBy = [], groupBy = [], limit, businessDateTo = null, distinct = false, offset, having, dialect = "postgres") {
|
|
947
|
+
const selectOp = buildQueryOperation(businessDate, processingDatetime, columns, table, op, orderBy, groupBy, limit, businessDateTo, distinct, offset, having);
|
|
948
|
+
return selectSqlToString(selectOp, dialect);
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
export {
|
|
952
|
+
POSTGRES,
|
|
953
|
+
DUCKDB,
|
|
954
|
+
DATABRICKS,
|
|
955
|
+
dialectOf,
|
|
956
|
+
arithmeticNeedsParentheses,
|
|
957
|
+
buildQueryOperation,
|
|
958
|
+
needsParentheses,
|
|
959
|
+
selectSqlToString,
|
|
960
|
+
toSql
|
|
961
|
+
};
|
|
962
|
+
//# sourceMappingURL=chunk-BKPWV3OF.js.map
|