data-finder-ts 0.1.4 → 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/dist/{chunk-4JVW4TAB.js → chunk-BGEX377Q.js} +58 -8
- 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-PMPLCEWQ.js → chunk-GPHWC533.js} +2 -2
- package/dist/{chunk-FJYA2ASS.js → chunk-KAK75RVS.js} +74 -12
- package/dist/chunk-KAK75RVS.js.map +1 -0
- package/dist/{chunk-4LNZV6EN.js → chunk-MGEL5RWM.js} +206 -78
- 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 +537 -11
- package/dist/datafinder/attribute.cjs.map +1 -1
- package/dist/datafinder/attribute.d.cts +29 -7
- package/dist/datafinder/attribute.d.ts +29 -7
- package/dist/datafinder/attribute.js +2 -2
- package/dist/datafinder/runner.cjs +1267 -215
- package/dist/datafinder/runner.cjs.map +1 -1
- package/dist/datafinder/runner.d.cts +28 -5
- package/dist/datafinder/runner.d.ts +28 -5
- package/dist/datafinder/runner.js +6 -6
- package/dist/datafinder/sql-generator.cjs +1203 -206
- package/dist/datafinder/sql-generator.cjs.map +1 -1
- package/dist/datafinder/sql-generator.d.cts +19 -5
- package/dist/datafinder/sql-generator.d.ts +19 -5
- package/dist/datafinder/sql-generator.js +9 -5
- package/dist/datafinder/typed-attributes.cjs +693 -88
- package/dist/datafinder/typed-attributes.cjs.map +1 -1
- package/dist/datafinder/typed-attributes.d.cts +3 -242
- package/dist/datafinder/typed-attributes.d.ts +3 -242
- 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 +1677 -408
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +109 -5
- package/dist/index.d.ts +109 -5
- package/dist/index.js +321 -11
- 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 +547 -16
- package/dist/model/relational.cjs.map +1 -1
- package/dist/model/relational.d.cts +421 -57
- package/dist/model/relational.d.ts +421 -57
- package/dist/model/relational.js +53 -3
- package/dist/typed-attributes-CUTf_6Ru.d.ts +328 -0
- package/dist/typed-attributes-CdWXq_78.d.cts +328 -0
- package/package.json +3 -2
- package/dist/chunk-4JVW4TAB.js.map +0 -1
- package/dist/chunk-4LNZV6EN.js.map +0 -1
- package/dist/chunk-FJYA2ASS.js.map +0 -1
- package/dist/chunk-MH4XMYVB.js +0 -696
- package/dist/chunk-MH4XMYVB.js.map +0 -1
- package/dist/chunk-UYMOE6YU.js +0 -509
- package/dist/chunk-UYMOE6YU.js.map +0 -1
- /package/dist/{chunk-PMPLCEWQ.js.map → chunk-GPHWC533.js.map} +0 -0
|
@@ -32,28 +32,61 @@ __export(runner_exports, {
|
|
|
32
32
|
module.exports = __toCommonJS(runner_exports);
|
|
33
33
|
|
|
34
34
|
// src/model/relational.ts
|
|
35
|
+
var LIKE_ESCAPE = "!";
|
|
36
|
+
function escapeLike(value) {
|
|
37
|
+
return value.replaceAll(LIKE_ESCAPE, LIKE_ESCAPE + LIKE_ESCAPE).replaceAll("%", LIKE_ESCAPE + "%").replaceAll("_", LIKE_ESCAPE + "_");
|
|
38
|
+
}
|
|
35
39
|
var RelationalOperationElement = class {
|
|
36
40
|
};
|
|
37
41
|
var Operation = class extends RelationalOperationElement {
|
|
38
|
-
|
|
42
|
+
/** Name this column in the result: `f.price().sum().alias('Total')`, `f.age().gt(30).alias('Adult')`. */
|
|
43
|
+
alias(name) {
|
|
44
|
+
return new Alias(this, name);
|
|
45
|
+
}
|
|
46
|
+
and(other) {
|
|
39
47
|
return new LogicalOperation(this, "AND" /* AND */, other);
|
|
40
48
|
}
|
|
49
|
+
/** Combine with `other` using OR. A chain reads left to right: `a.or(b).and(c)` is `(a OR b) AND c`. */
|
|
50
|
+
or(other) {
|
|
51
|
+
return new LogicalOperation(this, "OR" /* OR */, other);
|
|
52
|
+
}
|
|
41
53
|
};
|
|
42
54
|
var NoOperation = class extends Operation {
|
|
43
|
-
|
|
55
|
+
and(other) {
|
|
44
56
|
return other;
|
|
45
57
|
}
|
|
58
|
+
/** No filter matches every row, so OR-ing anything with it still matches every row. */
|
|
59
|
+
or(_other) {
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
46
62
|
};
|
|
47
63
|
var ConstantOperation = class extends RelationalOperationElement {
|
|
64
|
+
/** Name this column in the result. */
|
|
65
|
+
alias(name) {
|
|
66
|
+
return new Alias(this, name);
|
|
67
|
+
}
|
|
68
|
+
/** Sort by this expression, smallest first. */
|
|
69
|
+
ascending() {
|
|
70
|
+
return new SortOperation(this, "ASC" /* ASC */);
|
|
71
|
+
}
|
|
72
|
+
/** Sort by this expression, largest first. */
|
|
73
|
+
descending() {
|
|
74
|
+
return new SortOperation(this, "DESC" /* DESC */);
|
|
75
|
+
}
|
|
48
76
|
};
|
|
49
|
-
var
|
|
77
|
+
var NumericConstantOperation = class extends ConstantOperation {
|
|
78
|
+
arithmeticOperand() {
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
var IntegerConstantOperation = class extends NumericConstantOperation {
|
|
50
83
|
constructor(value) {
|
|
51
84
|
super();
|
|
52
85
|
this.value = value;
|
|
53
86
|
}
|
|
54
87
|
value;
|
|
55
88
|
};
|
|
56
|
-
var FloatConstantOperation = class extends
|
|
89
|
+
var FloatConstantOperation = class extends NumericConstantOperation {
|
|
57
90
|
constructor(value) {
|
|
58
91
|
super();
|
|
59
92
|
this.value = value;
|
|
@@ -67,6 +100,9 @@ var StringConstantOperation = class extends ConstantOperation {
|
|
|
67
100
|
}
|
|
68
101
|
value;
|
|
69
102
|
};
|
|
103
|
+
var DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
|
104
|
+
var DateOnly = class extends Date {
|
|
105
|
+
};
|
|
70
106
|
var DateConstantOperation = class extends ConstantOperation {
|
|
71
107
|
constructor(value) {
|
|
72
108
|
super();
|
|
@@ -81,7 +117,7 @@ var DateTimeConstantOperation = class extends ConstantOperation {
|
|
|
81
117
|
}
|
|
82
118
|
value;
|
|
83
119
|
};
|
|
84
|
-
var DecimalConstantOperation = class extends
|
|
120
|
+
var DecimalConstantOperation = class extends NumericConstantOperation {
|
|
85
121
|
constructor(value) {
|
|
86
122
|
super();
|
|
87
123
|
this.value = value;
|
|
@@ -115,6 +151,20 @@ var IsNullOperation = class extends UnaryOperation {
|
|
|
115
151
|
};
|
|
116
152
|
var IsNotNullOperation = class extends UnaryOperation {
|
|
117
153
|
};
|
|
154
|
+
var InOperation = class extends UnaryOperation {
|
|
155
|
+
constructor(element, values) {
|
|
156
|
+
super(element);
|
|
157
|
+
this.values = values;
|
|
158
|
+
}
|
|
159
|
+
values;
|
|
160
|
+
};
|
|
161
|
+
var NotInOperation = class extends UnaryOperation {
|
|
162
|
+
constructor(element, values) {
|
|
163
|
+
super(element);
|
|
164
|
+
this.values = values;
|
|
165
|
+
}
|
|
166
|
+
values;
|
|
167
|
+
};
|
|
118
168
|
var ComparisonOperation = class extends BinaryOperation {
|
|
119
169
|
constructor(left, operator, right) {
|
|
120
170
|
super(left, right);
|
|
@@ -138,21 +188,41 @@ var WindowSpecification = class {
|
|
|
138
188
|
orderBy;
|
|
139
189
|
};
|
|
140
190
|
var AggregateOperation = class _AggregateOperation extends UnaryOperation {
|
|
141
|
-
constructor(element, operator, displayName, window) {
|
|
191
|
+
constructor(element, operator, displayName, window, separator, orderBy = []) {
|
|
142
192
|
super(element);
|
|
143
193
|
this.operator = operator;
|
|
144
194
|
this.displayName = displayName;
|
|
145
195
|
this.window = window;
|
|
196
|
+
this.separator = separator;
|
|
197
|
+
this.orderBy = orderBy;
|
|
146
198
|
}
|
|
147
199
|
operator;
|
|
148
200
|
displayName;
|
|
149
201
|
window;
|
|
202
|
+
separator;
|
|
203
|
+
orderBy;
|
|
204
|
+
/** Sort by this expression, smallest first. */
|
|
205
|
+
ascending() {
|
|
206
|
+
return new SortOperation(this, "ASC" /* ASC */);
|
|
207
|
+
}
|
|
208
|
+
/** Sort by this expression, largest first. */
|
|
209
|
+
descending() {
|
|
210
|
+
return new SortOperation(this, "DESC" /* DESC */);
|
|
211
|
+
}
|
|
212
|
+
arithmeticOperand() {
|
|
213
|
+
return this;
|
|
214
|
+
}
|
|
215
|
+
comparisonOperand() {
|
|
216
|
+
return this;
|
|
217
|
+
}
|
|
150
218
|
over(partitionBy, orderBy) {
|
|
151
219
|
return new _AggregateOperation(
|
|
152
220
|
this.element,
|
|
153
221
|
this.operator,
|
|
154
222
|
this.displayName,
|
|
155
|
-
new WindowSpecification(partitionBy ?? [], orderBy ?? [])
|
|
223
|
+
new WindowSpecification(partitionBy ?? [], orderBy ?? []),
|
|
224
|
+
this.separator,
|
|
225
|
+
this.orderBy
|
|
156
226
|
);
|
|
157
227
|
}
|
|
158
228
|
};
|
|
@@ -170,6 +240,14 @@ var WindowFunctionOperation = class _WindowFunctionOperation extends UnaryOperat
|
|
|
170
240
|
secondArg;
|
|
171
241
|
extraArgs;
|
|
172
242
|
window;
|
|
243
|
+
/** Sort by this expression, smallest first. */
|
|
244
|
+
ascending() {
|
|
245
|
+
return new SortOperation(this, "ASC" /* ASC */);
|
|
246
|
+
}
|
|
247
|
+
/** Sort by this expression, largest first. */
|
|
248
|
+
descending() {
|
|
249
|
+
return new SortOperation(this, "DESC" /* DESC */);
|
|
250
|
+
}
|
|
173
251
|
over(partitionBy, orderBy) {
|
|
174
252
|
return new _WindowFunctionOperation(
|
|
175
253
|
this.element,
|
|
@@ -193,7 +271,38 @@ var ScalarFunctionOperation = class extends UnaryOperation {
|
|
|
193
271
|
displayName;
|
|
194
272
|
secondArg;
|
|
195
273
|
extraArgs;
|
|
274
|
+
/** Sort by this expression, smallest first. */
|
|
275
|
+
ascending() {
|
|
276
|
+
return new SortOperation(this, "ASC" /* ASC */);
|
|
277
|
+
}
|
|
278
|
+
/** Sort by this expression, largest first. */
|
|
279
|
+
descending() {
|
|
280
|
+
return new SortOperation(this, "DESC" /* DESC */);
|
|
281
|
+
}
|
|
282
|
+
/** This expression as an operand of a comparison. */
|
|
283
|
+
comparisonOperand() {
|
|
284
|
+
return this;
|
|
285
|
+
}
|
|
286
|
+
arithmeticOperand() {
|
|
287
|
+
return this;
|
|
288
|
+
}
|
|
289
|
+
/** Only a function that gives a number can be used in arithmetic. */
|
|
290
|
+
assertNumeric() {
|
|
291
|
+
if (!NUMERIC_FUNCTIONS.has(this.func)) {
|
|
292
|
+
throw new TypeError(`${this.func} does not give a number, so it cannot be used in arithmetic`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
196
295
|
};
|
|
296
|
+
var NUMERIC_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
297
|
+
"ABS" /* ABS */,
|
|
298
|
+
"CEILING" /* CEILING */,
|
|
299
|
+
"FLOOR" /* FLOOR */,
|
|
300
|
+
"MOD" /* MOD */,
|
|
301
|
+
"POWER" /* POWER */,
|
|
302
|
+
"SQRT" /* SQRT */,
|
|
303
|
+
"ROUND" /* ROUND */,
|
|
304
|
+
"LENGTH" /* LENGTH */
|
|
305
|
+
]);
|
|
197
306
|
var DateExtractOperation = class extends UnaryOperation {
|
|
198
307
|
constructor(element, part, displayName) {
|
|
199
308
|
super(element);
|
|
@@ -202,6 +311,34 @@ var DateExtractOperation = class extends UnaryOperation {
|
|
|
202
311
|
}
|
|
203
312
|
part;
|
|
204
313
|
displayName;
|
|
314
|
+
/** Sort by this expression, smallest first. */
|
|
315
|
+
ascending() {
|
|
316
|
+
return new SortOperation(this, "ASC" /* ASC */);
|
|
317
|
+
}
|
|
318
|
+
/** Sort by this expression, largest first. */
|
|
319
|
+
descending() {
|
|
320
|
+
return new SortOperation(this, "DESC" /* DESC */);
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
var DateTruncOperation = class extends UnaryOperation {
|
|
324
|
+
constructor(element, part, displayName) {
|
|
325
|
+
super(element);
|
|
326
|
+
this.part = part;
|
|
327
|
+
this.displayName = displayName;
|
|
328
|
+
}
|
|
329
|
+
part;
|
|
330
|
+
displayName;
|
|
331
|
+
/** Sort by this expression, earliest first. */
|
|
332
|
+
ascending() {
|
|
333
|
+
return new SortOperation(this, "ASC" /* ASC */);
|
|
334
|
+
}
|
|
335
|
+
/** Sort by this expression, latest first. */
|
|
336
|
+
descending() {
|
|
337
|
+
return new SortOperation(this, "DESC" /* DESC */);
|
|
338
|
+
}
|
|
339
|
+
comparisonOperand() {
|
|
340
|
+
return this;
|
|
341
|
+
}
|
|
205
342
|
};
|
|
206
343
|
var DateArithmeticOperation = class extends UnaryOperation {
|
|
207
344
|
constructor(element, n, unit, isAdd = true, displayName) {
|
|
@@ -215,6 +352,14 @@ var DateArithmeticOperation = class extends UnaryOperation {
|
|
|
215
352
|
unit;
|
|
216
353
|
isAdd;
|
|
217
354
|
displayName;
|
|
355
|
+
/** Sort by this expression, smallest first. */
|
|
356
|
+
ascending() {
|
|
357
|
+
return new SortOperation(this, "ASC" /* ASC */);
|
|
358
|
+
}
|
|
359
|
+
/** Sort by this expression, largest first. */
|
|
360
|
+
descending() {
|
|
361
|
+
return new SortOperation(this, "DESC" /* DESC */);
|
|
362
|
+
}
|
|
218
363
|
};
|
|
219
364
|
var DateDiffOperation = class extends UnaryOperation {
|
|
220
365
|
constructor(element, other, unit, displayName) {
|
|
@@ -226,20 +371,31 @@ var DateDiffOperation = class extends UnaryOperation {
|
|
|
226
371
|
other;
|
|
227
372
|
unit;
|
|
228
373
|
displayName;
|
|
374
|
+
/** Sort by this expression, smallest first. */
|
|
375
|
+
ascending() {
|
|
376
|
+
return new SortOperation(this, "ASC" /* ASC */);
|
|
377
|
+
}
|
|
378
|
+
/** Sort by this expression, largest first. */
|
|
379
|
+
descending() {
|
|
380
|
+
return new SortOperation(this, "DESC" /* DESC */);
|
|
381
|
+
}
|
|
229
382
|
};
|
|
230
383
|
var Column = class extends RelationalOperationElement {
|
|
231
|
-
constructor(name, type, owner, primaryKey = false) {
|
|
384
|
+
constructor(name, type, owner, primaryKey = false, nullable = true) {
|
|
232
385
|
super();
|
|
233
386
|
this.name = name;
|
|
234
387
|
this.type = type;
|
|
235
388
|
this.owner = owner;
|
|
236
389
|
this.primaryKey = primaryKey;
|
|
390
|
+
this.nullable = nullable && !primaryKey;
|
|
237
391
|
}
|
|
238
392
|
name;
|
|
239
393
|
type;
|
|
240
394
|
owner;
|
|
241
395
|
primaryKey;
|
|
242
396
|
table;
|
|
397
|
+
/** Whether the column may hold NULL. As in Legend, a primary key is never null. */
|
|
398
|
+
nullable;
|
|
243
399
|
};
|
|
244
400
|
var Table = class {
|
|
245
401
|
constructor(name, columns, schema) {
|
|
@@ -272,15 +428,24 @@ var ColumnWithJoin = class extends RelationalOperationElement {
|
|
|
272
428
|
* `embedded` is an alternate reference to a flat (denormalized) column on the owning table that
|
|
273
429
|
* holds the same value without the join; the SQL generator uses it when the join can be elided.
|
|
274
430
|
*/
|
|
275
|
-
constructor(column, parent, embedded) {
|
|
431
|
+
constructor(column, parent, embedded, _optional = false) {
|
|
276
432
|
super();
|
|
277
433
|
this.column = column;
|
|
278
434
|
this.parent = parent;
|
|
279
435
|
this.embedded = embedded;
|
|
436
|
+
this._optional = _optional;
|
|
280
437
|
}
|
|
281
438
|
column;
|
|
282
439
|
parent;
|
|
283
440
|
embedded;
|
|
441
|
+
_optional;
|
|
442
|
+
/** Whether the value can be missing: its property is optional, or a navigation on the way to it is. */
|
|
443
|
+
get optional() {
|
|
444
|
+
for (let node = this.parent; node !== void 0; node = node.parent) {
|
|
445
|
+
if (node.optional) return true;
|
|
446
|
+
}
|
|
447
|
+
return this._optional;
|
|
448
|
+
}
|
|
284
449
|
};
|
|
285
450
|
var SortOperation = class {
|
|
286
451
|
constructor(column, direction) {
|
|
@@ -296,8 +461,23 @@ var CountAllOperation = class extends RelationalOperationElement {
|
|
|
296
461
|
this.table = table;
|
|
297
462
|
}
|
|
298
463
|
table;
|
|
464
|
+
/** Name this column in the result. */
|
|
465
|
+
alias(name) {
|
|
466
|
+
return new Alias(this, name);
|
|
467
|
+
}
|
|
468
|
+
/** Sort by this expression, smallest first. */
|
|
469
|
+
ascending() {
|
|
470
|
+
return new SortOperation(this, "ASC" /* ASC */);
|
|
471
|
+
}
|
|
472
|
+
/** Sort by this expression, largest first. */
|
|
473
|
+
descending() {
|
|
474
|
+
return new SortOperation(this, "DESC" /* DESC */);
|
|
475
|
+
}
|
|
476
|
+
comparisonOperand() {
|
|
477
|
+
return this;
|
|
478
|
+
}
|
|
299
479
|
};
|
|
300
|
-
var Alias = class extends RelationalOperationElement {
|
|
480
|
+
var Alias = class _Alias extends RelationalOperationElement {
|
|
301
481
|
constructor(element, name) {
|
|
302
482
|
super();
|
|
303
483
|
this.element = element;
|
|
@@ -305,6 +485,30 @@ var Alias = class extends RelationalOperationElement {
|
|
|
305
485
|
}
|
|
306
486
|
element;
|
|
307
487
|
name;
|
|
488
|
+
alias(name) {
|
|
489
|
+
return new _Alias(this.element, name);
|
|
490
|
+
}
|
|
491
|
+
/** Sorts by the column the alias names. */
|
|
492
|
+
ascending() {
|
|
493
|
+
return new SortOperation(this.element, "ASC" /* ASC */);
|
|
494
|
+
}
|
|
495
|
+
/** Sorts by the column the alias names. */
|
|
496
|
+
descending() {
|
|
497
|
+
return new SortOperation(this.element, "DESC" /* DESC */);
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
var CaseOperation = class extends Operation {
|
|
501
|
+
constructor(condition, then, fallback, displayName) {
|
|
502
|
+
super();
|
|
503
|
+
this.condition = condition;
|
|
504
|
+
this.then = then;
|
|
505
|
+
this.fallback = fallback;
|
|
506
|
+
this.displayName = displayName;
|
|
507
|
+
}
|
|
508
|
+
condition;
|
|
509
|
+
then;
|
|
510
|
+
fallback;
|
|
511
|
+
displayName;
|
|
308
512
|
};
|
|
309
513
|
var ExistsOperation = class extends RelationalOperationElement {
|
|
310
514
|
constructor(node) {
|
|
@@ -312,9 +516,16 @@ var ExistsOperation = class extends RelationalOperationElement {
|
|
|
312
516
|
this.node = node;
|
|
313
517
|
}
|
|
314
518
|
node;
|
|
315
|
-
|
|
519
|
+
/** Name this column in the result. */
|
|
520
|
+
alias(name) {
|
|
521
|
+
return new Alias(this, name);
|
|
522
|
+
}
|
|
523
|
+
and(other) {
|
|
316
524
|
return new LogicalOperation(this, "AND" /* AND */, other);
|
|
317
525
|
}
|
|
526
|
+
or(other) {
|
|
527
|
+
return new LogicalOperation(this, "OR" /* OR */, other);
|
|
528
|
+
}
|
|
318
529
|
};
|
|
319
530
|
var NotExistsOperation = class extends RelationalOperationElement {
|
|
320
531
|
constructor(node) {
|
|
@@ -322,12 +533,19 @@ var NotExistsOperation = class extends RelationalOperationElement {
|
|
|
322
533
|
this.node = node;
|
|
323
534
|
}
|
|
324
535
|
node;
|
|
325
|
-
|
|
536
|
+
/** Name this column in the result. */
|
|
537
|
+
alias(name) {
|
|
538
|
+
return new Alias(this, name);
|
|
539
|
+
}
|
|
540
|
+
and(other) {
|
|
326
541
|
return new LogicalOperation(this, "AND" /* AND */, other);
|
|
327
542
|
}
|
|
543
|
+
or(other) {
|
|
544
|
+
return new LogicalOperation(this, "OR" /* OR */, other);
|
|
545
|
+
}
|
|
328
546
|
};
|
|
329
547
|
var SelectOperation = class {
|
|
330
|
-
constructor(display, filter, orderBy = [], groupBy = [], limit, table, joinFilters = /* @__PURE__ */ new Map()) {
|
|
548
|
+
constructor(display, filter, orderBy = [], groupBy = [], limit, table, joinFilters = /* @__PURE__ */ new Map(), distinct = false, offset, having) {
|
|
331
549
|
this.display = display;
|
|
332
550
|
this.filter = filter;
|
|
333
551
|
this.orderBy = orderBy;
|
|
@@ -335,6 +553,9 @@ var SelectOperation = class {
|
|
|
335
553
|
this.limit = limit;
|
|
336
554
|
this.table = table;
|
|
337
555
|
this.joinFilters = joinFilters;
|
|
556
|
+
this.distinct = distinct;
|
|
557
|
+
this.offset = offset;
|
|
558
|
+
this.having = having;
|
|
338
559
|
}
|
|
339
560
|
display;
|
|
340
561
|
filter;
|
|
@@ -343,7 +564,247 @@ var SelectOperation = class {
|
|
|
343
564
|
limit;
|
|
344
565
|
table;
|
|
345
566
|
joinFilters;
|
|
567
|
+
distinct;
|
|
568
|
+
offset;
|
|
569
|
+
having;
|
|
570
|
+
};
|
|
571
|
+
function containsAggregateOrWindow(op) {
|
|
572
|
+
if (op instanceof AggregateOperation || op instanceof WindowFunctionOperation || op instanceof CountAllOperation) return true;
|
|
573
|
+
if (op instanceof BinaryOperation) return containsAggregateOrWindow(op.left) || containsAggregateOrWindow(op.right);
|
|
574
|
+
if (op instanceof UnaryOperation || op instanceof Alias) return containsAggregateOrWindow(op.element);
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
function containsAggregate(op) {
|
|
578
|
+
if (op instanceof AggregateOperation || op instanceof CountAllOperation) return true;
|
|
579
|
+
if (op instanceof BinaryOperation) return containsAggregate(op.left) || containsAggregate(op.right);
|
|
580
|
+
if (op instanceof UnaryOperation || op instanceof Alias) return containsAggregate(op.element);
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
function valueOperand(value) {
|
|
584
|
+
if (typeof value === "boolean") return new BooleanConstantOperation(value);
|
|
585
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
586
|
+
return Number.isInteger(value) ? new IntegerConstantOperation(value) : new FloatConstantOperation(value);
|
|
587
|
+
}
|
|
588
|
+
if (typeof value === "string") return new StringConstantOperation(value);
|
|
589
|
+
if (value instanceof DateOnly) return new DateConstantOperation(value);
|
|
590
|
+
if (value instanceof Date) return new DateTimeConstantOperation(value);
|
|
591
|
+
throw new TypeError(`A value to compare with is a boolean, a finite number, a string or a date, not ${value === null ? "null" : typeof value}`);
|
|
592
|
+
}
|
|
593
|
+
function operandOf(other) {
|
|
594
|
+
if (other instanceof RelationalOperationElement) return other;
|
|
595
|
+
if (typeof other?.cwj === "function") return other.cwj();
|
|
596
|
+
return valueOperand(other);
|
|
597
|
+
}
|
|
598
|
+
var Comparable = class {
|
|
599
|
+
_compared(operator, other) {
|
|
600
|
+
return new ComparisonOperation(this.comparisonOperand(), operator, operandOf(other));
|
|
601
|
+
}
|
|
602
|
+
/** Filter to rows where this expression equals `other`. */
|
|
603
|
+
eq(other) {
|
|
604
|
+
return this._compared("EQUAL" /* EQUAL */, other);
|
|
605
|
+
}
|
|
606
|
+
/** Filter to rows where this expression does not equal `other`. */
|
|
607
|
+
ne(other) {
|
|
608
|
+
return this._compared("NOT_EQUAL" /* NOT_EQUAL */, other);
|
|
609
|
+
}
|
|
610
|
+
/** Filter to rows where this expression is less than `other`. */
|
|
611
|
+
lt(other) {
|
|
612
|
+
return this._compared("LESS_THAN" /* LESS_THAN */, other);
|
|
613
|
+
}
|
|
614
|
+
/** Filter to rows where this expression is at most `other`. */
|
|
615
|
+
lte(other) {
|
|
616
|
+
return this._compared("LESS_THAN_OR_EQUAL_TO" /* LESS_THAN_OR_EQUAL_TO */, other);
|
|
617
|
+
}
|
|
618
|
+
/** Filter to rows where this expression is greater than `other`. */
|
|
619
|
+
gt(other) {
|
|
620
|
+
return this._compared("GREATER_THAN" /* GREATER_THAN */, other);
|
|
621
|
+
}
|
|
622
|
+
/** Filter to rows where this expression is at least `other`. */
|
|
623
|
+
gte(other) {
|
|
624
|
+
return this._compared("GREATER_THAN_OR_EQUAL_TO" /* GREATER_THAN_OR_EQUAL_TO */, other);
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
function sample(ddof) {
|
|
628
|
+
if (ddof !== 0 && ddof !== 1) throw new RangeError(`ddof is 0 (population) or 1 (sample), not ${ddof}`);
|
|
629
|
+
return ddof === 1;
|
|
630
|
+
}
|
|
631
|
+
var StringPredicates = class {
|
|
632
|
+
_like(pattern) {
|
|
633
|
+
return new ComparisonOperation(this.comparisonOperand(), "LIKE" /* LIKE */, new StringConstantOperation(pattern));
|
|
634
|
+
}
|
|
635
|
+
/** Filter to rows where this text contains `value`. */
|
|
636
|
+
contains(value) {
|
|
637
|
+
return this._like(`%${escapeLike(value)}%`);
|
|
638
|
+
}
|
|
639
|
+
/** Filter to rows where this text starts with `prefix`. */
|
|
640
|
+
startsWith(prefix) {
|
|
641
|
+
return this._like(`${escapeLike(prefix)}%`);
|
|
642
|
+
}
|
|
643
|
+
/** Filter to rows where this text ends with `suffix`. */
|
|
644
|
+
endsWith(suffix) {
|
|
645
|
+
return this._like(`%${escapeLike(suffix)}`);
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
var Aggregates = class {
|
|
649
|
+
_aggregate(operator, label) {
|
|
650
|
+
return new AggregateOperation(this, operator, label + " Expression");
|
|
651
|
+
}
|
|
652
|
+
/** Sum of the values in the group. */
|
|
653
|
+
sum() {
|
|
654
|
+
return this._aggregate("SUM" /* SUM */, "Sum");
|
|
655
|
+
}
|
|
656
|
+
/** Smallest value in the group. */
|
|
657
|
+
min() {
|
|
658
|
+
return this._aggregate("MIN" /* MIN */, "Min");
|
|
659
|
+
}
|
|
660
|
+
/** Largest value in the group. */
|
|
661
|
+
max() {
|
|
662
|
+
return this._aggregate("MAX" /* MAX */, "Max");
|
|
663
|
+
}
|
|
664
|
+
/** Arithmetic mean of the values in the group. */
|
|
665
|
+
average() {
|
|
666
|
+
return this._aggregate("AVERAGE" /* AVERAGE */, "Average");
|
|
667
|
+
}
|
|
668
|
+
/** Number of non-null values in the group. */
|
|
669
|
+
count() {
|
|
670
|
+
return this._aggregate("COUNT" /* COUNT */, "Count");
|
|
671
|
+
}
|
|
672
|
+
/** Standard deviation of the values in the group: the sample one by default, `ddof = 0` for the population one. */
|
|
673
|
+
std(ddof = 1) {
|
|
674
|
+
return this._aggregate(sample(ddof) ? "STDDEV_SAMP" /* STDDEV_SAMP */ : "STDDEV_POP" /* STDDEV_POP */, "Std");
|
|
675
|
+
}
|
|
676
|
+
/** Variance of the values in the group: the sample one by default, `ddof = 0` for the population one. */
|
|
677
|
+
var(ddof = 1) {
|
|
678
|
+
return this._aggregate(sample(ddof) ? "VAR_SAMP" /* VAR_SAMP */ : "VAR_POP" /* VAR_POP */, "Var");
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
var BooleanAggregates = class {
|
|
682
|
+
/** True for a group where every row satisfies the condition. */
|
|
683
|
+
all() {
|
|
684
|
+
return new AggregateOperation(this, "BOOL_AND" /* BOOL_AND */, "All Expression");
|
|
685
|
+
}
|
|
686
|
+
/** True for a group where at least one row satisfies the condition. */
|
|
687
|
+
any() {
|
|
688
|
+
return new AggregateOperation(this, "BOOL_OR" /* BOOL_OR */, "Any Expression");
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
function applyMixins(derived, bases) {
|
|
692
|
+
for (const base of bases) {
|
|
693
|
+
for (const name of Object.getOwnPropertyNames(base.prototype)) {
|
|
694
|
+
if (name === "constructor" || Object.prototype.hasOwnProperty.call(derived.prototype, name)) continue;
|
|
695
|
+
Object.defineProperty(derived.prototype, name, Object.getOwnPropertyDescriptor(base.prototype, name));
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
var Arithmetic = class {
|
|
700
|
+
/** Raises if this expression is not a number. */
|
|
701
|
+
assertNumeric() {
|
|
702
|
+
}
|
|
703
|
+
_arithmetic(operator, other) {
|
|
704
|
+
this.assertNumeric();
|
|
705
|
+
let operand;
|
|
706
|
+
if (typeof other === "number") {
|
|
707
|
+
if (!Number.isFinite(other)) throw new TypeError("Arithmetic is between numbers and numeric columns, not a non-finite number");
|
|
708
|
+
operand = valueOperand(other);
|
|
709
|
+
} else if (other !== null && typeof other === "object" && typeof other.arithmeticOperand === "function") {
|
|
710
|
+
other.assertNumeric?.();
|
|
711
|
+
operand = other.arithmeticOperand();
|
|
712
|
+
} else {
|
|
713
|
+
throw new TypeError(`Arithmetic is between numbers and numeric columns, not ${other === null ? "null" : typeof other}`);
|
|
714
|
+
}
|
|
715
|
+
return new ArithmeticOperation(this.arithmeticOperand(), operator, operand);
|
|
716
|
+
}
|
|
717
|
+
/** `this + other`. */
|
|
718
|
+
plus(other) {
|
|
719
|
+
return this._arithmetic("+" /* ADD */, other);
|
|
720
|
+
}
|
|
721
|
+
/** `this - other`. */
|
|
722
|
+
minus(other) {
|
|
723
|
+
return this._arithmetic("-" /* SUBTRACT */, other);
|
|
724
|
+
}
|
|
725
|
+
/** `this * other`. */
|
|
726
|
+
times(other) {
|
|
727
|
+
return this._arithmetic("*" /* MULTIPLY */, other);
|
|
728
|
+
}
|
|
729
|
+
/** `this / other`. */
|
|
730
|
+
dividedBy(other) {
|
|
731
|
+
return this._arithmetic("/" /* DIVIDE */, other);
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
var ArithmeticOperation = class extends BinaryOperation {
|
|
735
|
+
constructor(left, operator, right, displayName) {
|
|
736
|
+
super(left, right);
|
|
737
|
+
this.operator = operator;
|
|
738
|
+
this.displayName = displayName;
|
|
739
|
+
}
|
|
740
|
+
operator;
|
|
741
|
+
displayName;
|
|
742
|
+
/** Sort by this expression, smallest first. */
|
|
743
|
+
ascending() {
|
|
744
|
+
return new SortOperation(this, "ASC" /* ASC */);
|
|
745
|
+
}
|
|
746
|
+
/** Sort by this expression, largest first. */
|
|
747
|
+
descending() {
|
|
748
|
+
return new SortOperation(this, "DESC" /* DESC */);
|
|
749
|
+
}
|
|
750
|
+
arithmeticOperand() {
|
|
751
|
+
return this;
|
|
752
|
+
}
|
|
753
|
+
comparisonOperand() {
|
|
754
|
+
return this;
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
var ConcatOperation = class extends Operation {
|
|
758
|
+
constructor(parts, displayName) {
|
|
759
|
+
super();
|
|
760
|
+
this.parts = parts;
|
|
761
|
+
this.displayName = displayName;
|
|
762
|
+
}
|
|
763
|
+
parts;
|
|
764
|
+
displayName;
|
|
765
|
+
/** Join more text on the end: `f.first().concat(' ', f.last())`. A concatenation joins into one. */
|
|
766
|
+
concat(...more) {
|
|
767
|
+
return concat(this, ...more);
|
|
768
|
+
}
|
|
769
|
+
/** Sort by this expression, smallest first. */
|
|
770
|
+
ascending() {
|
|
771
|
+
return new SortOperation(this, "ASC" /* ASC */);
|
|
772
|
+
}
|
|
773
|
+
/** Sort by this expression, largest first. */
|
|
774
|
+
descending() {
|
|
775
|
+
return new SortOperation(this, "DESC" /* DESC */);
|
|
776
|
+
}
|
|
777
|
+
comparisonOperand() {
|
|
778
|
+
return this;
|
|
779
|
+
}
|
|
346
780
|
};
|
|
781
|
+
function concat(...parts) {
|
|
782
|
+
const partsOf = (part) => {
|
|
783
|
+
if (part instanceof ConcatOperation) return part.parts;
|
|
784
|
+
if (typeof part === "string") return [new StringConstantOperation(part)];
|
|
785
|
+
if (part instanceof RelationalOperationElement) return [part];
|
|
786
|
+
if (part !== null && typeof part === "object" && typeof part.functionInput === "function") return [part.functionInput()];
|
|
787
|
+
throw new TypeError(`Text is joined with text, not ${part === null ? "null" : typeof part}`);
|
|
788
|
+
};
|
|
789
|
+
return new ConcatOperation(parts.flatMap(partsOf));
|
|
790
|
+
}
|
|
791
|
+
applyMixins(ConcatOperation, [Comparable, StringPredicates]);
|
|
792
|
+
applyMixins(DateTruncOperation, [Comparable]);
|
|
793
|
+
applyMixins(ArithmeticOperation, [Arithmetic, Comparable]);
|
|
794
|
+
applyMixins(ScalarFunctionOperation, [Comparable, Arithmetic, StringPredicates]);
|
|
795
|
+
applyMixins(AggregateOperation, [Arithmetic, Comparable, StringPredicates]);
|
|
796
|
+
applyMixins(CountAllOperation, [Comparable]);
|
|
797
|
+
applyMixins(NumericConstantOperation, [Arithmetic]);
|
|
798
|
+
applyMixins(ConstantOperation, [Aggregates]);
|
|
799
|
+
applyMixins(ArithmeticOperation, [Aggregates]);
|
|
800
|
+
applyMixins(ScalarFunctionOperation, [Aggregates]);
|
|
801
|
+
applyMixins(CaseOperation, [Aggregates]);
|
|
802
|
+
applyMixins(IsNullOperation, [BooleanAggregates]);
|
|
803
|
+
applyMixins(IsNotNullOperation, [BooleanAggregates]);
|
|
804
|
+
applyMixins(InOperation, [BooleanAggregates]);
|
|
805
|
+
applyMixins(NotInOperation, [BooleanAggregates]);
|
|
806
|
+
applyMixins(ComparisonOperation, [BooleanAggregates]);
|
|
807
|
+
applyMixins(LogicalOperation, [BooleanAggregates]);
|
|
347
808
|
|
|
348
809
|
// src/datafinder/attribute.ts
|
|
349
810
|
var Attribute = class _Attribute {
|
|
@@ -352,9 +813,11 @@ var Attribute = class _Attribute {
|
|
|
352
813
|
_owner;
|
|
353
814
|
_parent;
|
|
354
815
|
_embedded;
|
|
355
|
-
|
|
816
|
+
_optional;
|
|
817
|
+
constructor(displayName, columnName, columnDbType, owner, parent, embedded, nullable = true, optional = false) {
|
|
818
|
+
this._optional = optional;
|
|
356
819
|
this._displayName = displayName;
|
|
357
|
-
this._column = new Column(columnName, columnDbType, owner);
|
|
820
|
+
this._column = new Column(columnName, columnDbType, owner, false, nullable);
|
|
358
821
|
this._owner = owner;
|
|
359
822
|
this._parent = parent;
|
|
360
823
|
this._embedded = embedded;
|
|
@@ -375,10 +838,22 @@ var Attribute = class _Attribute {
|
|
|
375
838
|
embedded() {
|
|
376
839
|
return this._embedded;
|
|
377
840
|
}
|
|
841
|
+
/** @internal The expression a function of this attribute applies to: its column. */
|
|
842
|
+
functionInput() {
|
|
843
|
+
return this.cwj();
|
|
844
|
+
}
|
|
845
|
+
/** @internal What a function of this attribute is labelled after: its display name. */
|
|
846
|
+
functionLabel() {
|
|
847
|
+
return this._displayName;
|
|
848
|
+
}
|
|
849
|
+
/** @internal This attribute as an operand of arithmetic: its column. */
|
|
850
|
+
arithmeticOperand() {
|
|
851
|
+
return this.cwj();
|
|
852
|
+
}
|
|
378
853
|
cwj() {
|
|
379
|
-
return new ColumnWithJoin(this._column, this._parent, this._embedded?.cwj());
|
|
854
|
+
return new ColumnWithJoin(this._column, this._parent, this._embedded?.cwj(), this._optional);
|
|
380
855
|
}
|
|
381
|
-
/** Count the number of non-null values. Use in a projection with `
|
|
856
|
+
/** Count the number of non-null values. Use in a projection with `groupBy` to aggregate per group. */
|
|
382
857
|
count() {
|
|
383
858
|
return new AggregateOperation(
|
|
384
859
|
this.cwj(),
|
|
@@ -386,11 +861,26 @@ var Attribute = class _Attribute {
|
|
|
386
861
|
this._displayName + " Count"
|
|
387
862
|
);
|
|
388
863
|
}
|
|
389
|
-
/**
|
|
864
|
+
/** Name this column in the result: `f.price().sum().alias('Total')`. */
|
|
865
|
+
alias(name) {
|
|
866
|
+
return new Alias(this, name);
|
|
867
|
+
}
|
|
868
|
+
/** The number of different non-NULL values in the group: `COUNT(DISTINCT column)`. Use with `groupBy` to count per group. */
|
|
869
|
+
countDistinct() {
|
|
870
|
+
return new AggregateOperation(this.cwj(), "COUNT_DISTINCT" /* COUNT_DISTINCT */, this._displayName + " Count Distinct");
|
|
871
|
+
}
|
|
872
|
+
/** The value, or `defaultValue` when it is NULL: `name().orElse('N/A')`. Like `COALESCE` in SQL. */
|
|
873
|
+
orElse(defaultValue) {
|
|
874
|
+
if (!["string", "number", "boolean"].includes(typeof defaultValue)) {
|
|
875
|
+
throw new TypeError(`An orElse default is a string, number or boolean, not ${defaultValue === null ? "null" : typeof defaultValue}`);
|
|
876
|
+
}
|
|
877
|
+
return new ScalarFunctionOperation(this.cwj(), "COALESCE" /* COALESCE */, "Or Else " + this._displayName, void 0, [defaultValue]);
|
|
878
|
+
}
|
|
879
|
+
/** Sort this attribute in ascending order. Pass to `orderBy` or a window function's `orderBy`. */
|
|
390
880
|
ascending() {
|
|
391
881
|
return new SortOperation(this.cwj(), "ASC" /* ASC */);
|
|
392
882
|
}
|
|
393
|
-
/** Sort this attribute in descending order. Pass to `
|
|
883
|
+
/** Sort this attribute in descending order. Pass to `orderBy` or a window function's `orderBy`. */
|
|
394
884
|
descending() {
|
|
395
885
|
return new SortOperation(this.cwj(), "DESC" /* DESC */);
|
|
396
886
|
}
|
|
@@ -402,6 +892,23 @@ var Attribute = class _Attribute {
|
|
|
402
892
|
isNotNull() {
|
|
403
893
|
return new IsNotNullOperation(this.cwj());
|
|
404
894
|
}
|
|
895
|
+
/** Wrap a value as a constant of this attribute's type. Overridden by the typed attributes. */
|
|
896
|
+
toConstant(_value) {
|
|
897
|
+
throw new TypeError(`${this.constructor.name} does not support isIn / isNotIn`);
|
|
898
|
+
}
|
|
899
|
+
_constants(values) {
|
|
900
|
+
const constants = values.map((v) => this.toConstant(v));
|
|
901
|
+
if (constants.length === 0) throw new Error("isIn / isNotIn need at least one value");
|
|
902
|
+
return constants;
|
|
903
|
+
}
|
|
904
|
+
/** Filter to rows where this attribute equals any of `values`. */
|
|
905
|
+
isIn(values) {
|
|
906
|
+
return new InOperation(this.cwj(), this._constants(values));
|
|
907
|
+
}
|
|
908
|
+
/** Filter to rows where this attribute equals none of `values`. Rows with no value are excluded, as in SQL. */
|
|
909
|
+
isNotIn(values) {
|
|
910
|
+
return new NotInOperation(this.cwj(), this._constants(values));
|
|
911
|
+
}
|
|
405
912
|
_windowSpec(partitionBy, orderBy) {
|
|
406
913
|
const pbArr = partitionBy === void 0 ? [] : Array.isArray(partitionBy) ? partitionBy : [partitionBy];
|
|
407
914
|
const pb = pbArr.map((x) => x instanceof _Attribute ? x.cwj() : x);
|
|
@@ -587,16 +1094,215 @@ var MilestonedTable = class extends Table {
|
|
|
587
1094
|
milestoningColumns;
|
|
588
1095
|
};
|
|
589
1096
|
|
|
1097
|
+
// src/datafinder/scalar-functions.ts
|
|
1098
|
+
var StringFunctionOperation = class extends ScalarFunctionOperation {
|
|
1099
|
+
functionInput() {
|
|
1100
|
+
return this;
|
|
1101
|
+
}
|
|
1102
|
+
functionLabel() {
|
|
1103
|
+
return labelOf(this);
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
1106
|
+
var NumericFunctionOperation = class extends ScalarFunctionOperation {
|
|
1107
|
+
functionInput() {
|
|
1108
|
+
return this;
|
|
1109
|
+
}
|
|
1110
|
+
functionLabel() {
|
|
1111
|
+
return labelOf(this);
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1114
|
+
function labelOf(operation) {
|
|
1115
|
+
return operation.displayName ?? operation.func.charAt(0) + operation.func.slice(1).toLowerCase();
|
|
1116
|
+
}
|
|
1117
|
+
function stringFunction(element, func, label, secondArg, extraArgs = []) {
|
|
1118
|
+
return new StringFunctionOperation(element, func, label, secondArg, extraArgs);
|
|
1119
|
+
}
|
|
1120
|
+
function numberFunction(element, func, label, secondArg) {
|
|
1121
|
+
return new NumericFunctionOperation(element, func, label, secondArg);
|
|
1122
|
+
}
|
|
1123
|
+
var StringFunctions = class {
|
|
1124
|
+
/** Join text on the end: `f.first().concat(' ', f.last())`. A NULL part makes the whole NULL. */
|
|
1125
|
+
concat(...more) {
|
|
1126
|
+
return concat(this, ...more);
|
|
1127
|
+
}
|
|
1128
|
+
/** Convert every character to uppercase. */
|
|
1129
|
+
toUpperCase() {
|
|
1130
|
+
return stringFunction(this.functionInput(), "UPPER" /* UPPER */, "Upper " + this.functionLabel());
|
|
1131
|
+
}
|
|
1132
|
+
/** Convert every character to lowercase. */
|
|
1133
|
+
toLowerCase() {
|
|
1134
|
+
return stringFunction(this.functionInput(), "LOWER" /* LOWER */, "Lower " + this.functionLabel());
|
|
1135
|
+
}
|
|
1136
|
+
/** Remove leading and trailing whitespace. */
|
|
1137
|
+
trim() {
|
|
1138
|
+
return stringFunction(this.functionInput(), "TRIM" /* TRIM */, "Trim " + this.functionLabel());
|
|
1139
|
+
}
|
|
1140
|
+
/** Remove leading whitespace. */
|
|
1141
|
+
trimStart() {
|
|
1142
|
+
return stringFunction(this.functionInput(), "LTRIM" /* LTRIM */, "Trim Start " + this.functionLabel());
|
|
1143
|
+
}
|
|
1144
|
+
/** Remove trailing whitespace. */
|
|
1145
|
+
trimEnd() {
|
|
1146
|
+
return stringFunction(this.functionInput(), "RTRIM" /* RTRIM */, "Trim End " + this.functionLabel());
|
|
1147
|
+
}
|
|
1148
|
+
/** Return the number of characters in the string. */
|
|
1149
|
+
length() {
|
|
1150
|
+
return numberFunction(this.functionInput(), "LENGTH" /* LENGTH */, "Length " + this.functionLabel());
|
|
1151
|
+
}
|
|
1152
|
+
/** Return the string with characters in reverse order. */
|
|
1153
|
+
reverse() {
|
|
1154
|
+
return stringFunction(this.functionInput(), "REVERSE" /* REVERSE */, "Reverse " + this.functionLabel());
|
|
1155
|
+
}
|
|
1156
|
+
/** Return the first `n` characters. */
|
|
1157
|
+
left(n) {
|
|
1158
|
+
return stringFunction(this.functionInput(), "LEFT" /* LEFT */, "Left " + this.functionLabel(), n);
|
|
1159
|
+
}
|
|
1160
|
+
/** Return the last `n` characters. */
|
|
1161
|
+
right(n) {
|
|
1162
|
+
return stringFunction(this.functionInput(), "RIGHT" /* RIGHT */, "Right " + this.functionLabel(), n);
|
|
1163
|
+
}
|
|
1164
|
+
/** Return the string repeated `n` times. */
|
|
1165
|
+
repeat(n) {
|
|
1166
|
+
return stringFunction(this.functionInput(), "REPEAT" /* REPEAT */, "Repeat " + this.functionLabel(), n);
|
|
1167
|
+
}
|
|
1168
|
+
/** Replace all occurrences of `fromStr` with `toStr`. */
|
|
1169
|
+
replace(fromStr, toStr) {
|
|
1170
|
+
return stringFunction(this.functionInput(), "REPLACE" /* REPLACE */, "Replace " + this.functionLabel(), void 0, [fromStr, toStr]);
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Extract a portion of the string.
|
|
1174
|
+
* `start` is zero-based (following Python convention); `length` is optional.
|
|
1175
|
+
*/
|
|
1176
|
+
substring(start, length) {
|
|
1177
|
+
const args = length !== void 0 ? [start + 1, length] : [start + 1];
|
|
1178
|
+
return stringFunction(this.functionInput(), "SUBSTRING" /* SUBSTRING */, "Substring " + this.functionLabel(), void 0, args);
|
|
1179
|
+
}
|
|
1180
|
+
};
|
|
1181
|
+
var NumericFunctions = class {
|
|
1182
|
+
/** Absolute value (distance from zero, always non-negative). */
|
|
1183
|
+
abs() {
|
|
1184
|
+
return numberFunction(this.functionInput(), "ABS" /* ABS */, "Abs " + this.functionLabel());
|
|
1185
|
+
}
|
|
1186
|
+
/** Round up to the nearest integer. */
|
|
1187
|
+
ceil() {
|
|
1188
|
+
return numberFunction(this.functionInput(), "CEILING" /* CEILING */, "Ceil " + this.functionLabel());
|
|
1189
|
+
}
|
|
1190
|
+
/** Round down to the nearest integer. */
|
|
1191
|
+
floor() {
|
|
1192
|
+
return numberFunction(this.functionInput(), "FLOOR" /* FLOOR */, "Floor " + this.functionLabel());
|
|
1193
|
+
}
|
|
1194
|
+
/** Square root. */
|
|
1195
|
+
sqrt() {
|
|
1196
|
+
return numberFunction(this.functionInput(), "SQRT" /* SQRT */, "Sqrt " + this.functionLabel());
|
|
1197
|
+
}
|
|
1198
|
+
/** Remainder after dividing by `n`. */
|
|
1199
|
+
mod(n) {
|
|
1200
|
+
return numberFunction(this.functionInput(), "MOD" /* MOD */, "Mod " + this.functionLabel(), n);
|
|
1201
|
+
}
|
|
1202
|
+
/** Raise to the power of `n`. */
|
|
1203
|
+
power(n) {
|
|
1204
|
+
return numberFunction(this.functionInput(), "POWER" /* POWER */, "Power " + this.functionLabel(), n);
|
|
1205
|
+
}
|
|
1206
|
+
/** Round to `d` decimal places, or to the nearest integer when `d` is omitted. */
|
|
1207
|
+
round(d) {
|
|
1208
|
+
return numberFunction(this.functionInput(), "ROUND" /* ROUND */, "Round " + this.functionLabel(), d);
|
|
1209
|
+
}
|
|
1210
|
+
};
|
|
1211
|
+
applyMixins(StringFunctionOperation, [StringFunctions]);
|
|
1212
|
+
applyMixins(NumericFunctionOperation, [NumericFunctions]);
|
|
1213
|
+
|
|
590
1214
|
// src/datafinder/typed-attributes.ts
|
|
1215
|
+
var MinMax = class {
|
|
1216
|
+
/** Smallest value in the group. */
|
|
1217
|
+
min() {
|
|
1218
|
+
return new AggregateOperation(this.cwj(), "MIN" /* MIN */, "Min " + this.displayName());
|
|
1219
|
+
}
|
|
1220
|
+
/** Largest value in the group. */
|
|
1221
|
+
max() {
|
|
1222
|
+
return new AggregateOperation(this.cwj(), "MAX" /* MAX */, "Max " + this.displayName());
|
|
1223
|
+
}
|
|
1224
|
+
};
|
|
1225
|
+
var StringAttribute = class extends Attribute {
|
|
1226
|
+
constructor(displayName, columnName, columnDbType, owner, parent, embedded, nullable = true, optional = false) {
|
|
1227
|
+
super(displayName, columnName, columnDbType, owner, parent, embedded, nullable, optional);
|
|
1228
|
+
}
|
|
1229
|
+
toConstant(value) {
|
|
1230
|
+
return new StringConstantOperation(String(value));
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* Join the values of a group into one text with `separator` between them. `orderBy` is the order they are
|
|
1234
|
+
* joined in (`[f.name().ascending()]`); without it the order is not defined. Missing values are skipped.
|
|
1235
|
+
*/
|
|
1236
|
+
join(separator = "", orderBy = []) {
|
|
1237
|
+
return new AggregateOperation(this.cwj(), "STRING_AGG" /* STRING_AGG */, "Join " + this.displayName(), void 0, separator, orderBy);
|
|
1238
|
+
}
|
|
1239
|
+
/** Filter to rows where this attribute equals `value`. */
|
|
1240
|
+
eq(value) {
|
|
1241
|
+
return new ComparisonOperation(this.cwj(), "EQUAL" /* EQUAL */, new StringConstantOperation(value));
|
|
1242
|
+
}
|
|
1243
|
+
/** Filter to rows where this attribute does not equal `value`. */
|
|
1244
|
+
ne(value) {
|
|
1245
|
+
return new ComparisonOperation(this.cwj(), "NOT_EQUAL" /* NOT_EQUAL */, new StringConstantOperation(value));
|
|
1246
|
+
}
|
|
1247
|
+
/** Filter to rows where this attribute contains `value` as a substring. */
|
|
1248
|
+
contains(value) {
|
|
1249
|
+
return new ComparisonOperation(this.cwj(), "LIKE" /* LIKE */, new StringConstantOperation(`%${escapeLike(value)}%`));
|
|
1250
|
+
}
|
|
1251
|
+
/** Filter to rows where this attribute begins with `prefix`. */
|
|
1252
|
+
startsWith(prefix) {
|
|
1253
|
+
return new ComparisonOperation(this.cwj(), "LIKE" /* LIKE */, new StringConstantOperation(`${escapeLike(prefix)}%`));
|
|
1254
|
+
}
|
|
1255
|
+
/** Filter to rows where this attribute ends with `suffix`. */
|
|
1256
|
+
endsWith(suffix) {
|
|
1257
|
+
return new ComparisonOperation(this.cwj(), "LIKE" /* LIKE */, new StringConstantOperation(`%${escapeLike(suffix)}`));
|
|
1258
|
+
}
|
|
1259
|
+
/** Filter to rows where this attribute does not contain `value` as a substring. */
|
|
1260
|
+
notContains(value) {
|
|
1261
|
+
return new ComparisonOperation(this.cwj(), "NOT_LIKE" /* NOT_LIKE */, new StringConstantOperation(`%${escapeLike(value)}%`));
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
applyMixins(StringAttribute, [MinMax, StringFunctions]);
|
|
1265
|
+
var NumericAttribute = class extends Attribute {
|
|
1266
|
+
constructor(displayName, columnName, columnDbType, owner, parent, embedded, nullable = true, optional = false) {
|
|
1267
|
+
super(displayName, columnName, columnDbType, owner, parent, embedded, nullable, optional);
|
|
1268
|
+
}
|
|
1269
|
+
/** Sum of all values in the group. */
|
|
1270
|
+
sum() {
|
|
1271
|
+
return new AggregateOperation(this.cwj(), "SUM" /* SUM */, "Sum " + this.displayName());
|
|
1272
|
+
}
|
|
1273
|
+
/** Arithmetic mean of all values in the group. */
|
|
1274
|
+
average() {
|
|
1275
|
+
return new AggregateOperation(this.cwj(), "AVERAGE" /* AVERAGE */, "Average " + this.displayName());
|
|
1276
|
+
}
|
|
1277
|
+
/** Standard deviation of the values in the group: the sample one by default, `ddof = 0` for the population one, as `Series.std`. */
|
|
1278
|
+
std(ddof = 1) {
|
|
1279
|
+
const operator = sample(ddof) ? "STDDEV_SAMP" /* STDDEV_SAMP */ : "STDDEV_POP" /* STDDEV_POP */;
|
|
1280
|
+
return new AggregateOperation(this.cwj(), operator, "Std " + this.displayName());
|
|
1281
|
+
}
|
|
1282
|
+
/** Variance of the values in the group: the sample one by default, `ddof = 0` for the population one, as `Series.var`. */
|
|
1283
|
+
var(ddof = 1) {
|
|
1284
|
+
const operator = sample(ddof) ? "VAR_SAMP" /* VAR_SAMP */ : "VAR_POP" /* VAR_POP */;
|
|
1285
|
+
return new AggregateOperation(this.cwj(), operator, "Var " + this.displayName());
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
applyMixins(NumericAttribute, [MinMax, NumericFunctions, Arithmetic]);
|
|
591
1289
|
function parseDate(value) {
|
|
592
1290
|
if (value instanceof Date) return value;
|
|
593
1291
|
const d = new Date(value);
|
|
594
1292
|
if (isNaN(d.getTime())) throw new Error(`Cannot parse date: ${value}`);
|
|
595
1293
|
return d;
|
|
596
1294
|
}
|
|
1295
|
+
function temporalConstant(value) {
|
|
1296
|
+
if (typeof value === "string" && DATE_ONLY_PATTERN.test(value)) return new DateConstantOperation(new DateOnly(value));
|
|
1297
|
+
const date = parseDate(value);
|
|
1298
|
+
return date instanceof DateOnly ? new DateConstantOperation(date) : new DateTimeConstantOperation(date);
|
|
1299
|
+
}
|
|
597
1300
|
var DateAttribute = class extends Attribute {
|
|
598
|
-
constructor(displayName, columnName, columnDbType, owner, parent, embedded) {
|
|
599
|
-
super(displayName, columnName, columnDbType, owner, parent, embedded);
|
|
1301
|
+
constructor(displayName, columnName, columnDbType, owner, parent, embedded, nullable = true, optional = false) {
|
|
1302
|
+
super(displayName, columnName, columnDbType, owner, parent, embedded, nullable, optional);
|
|
1303
|
+
}
|
|
1304
|
+
toConstant(value) {
|
|
1305
|
+
return new DateConstantOperation(parseDate(value));
|
|
600
1306
|
}
|
|
601
1307
|
/** Filter to rows where this date equals `value`. */
|
|
602
1308
|
eq(value) {
|
|
@@ -646,6 +1352,22 @@ var DateAttribute = class extends Attribute {
|
|
|
646
1352
|
dayOfWeek() {
|
|
647
1353
|
return new DateExtractOperation(this.cwj(), "DOW" /* DOW */, "Day Of Week " + this.displayName());
|
|
648
1354
|
}
|
|
1355
|
+
/** The date of the first day of the week the date falls in (a Monday). */
|
|
1356
|
+
firstDayOfWeek() {
|
|
1357
|
+
return new DateTruncOperation(this.cwj(), "WEEK" /* WEEK */, "First Day Of Week " + this.displayName());
|
|
1358
|
+
}
|
|
1359
|
+
/** The date of the first day of the month the date falls in. */
|
|
1360
|
+
firstDayOfMonth() {
|
|
1361
|
+
return new DateTruncOperation(this.cwj(), "MONTH" /* MONTH */, "First Day Of Month " + this.displayName());
|
|
1362
|
+
}
|
|
1363
|
+
/** The date of the first day of the quarter the date falls in. */
|
|
1364
|
+
firstDayOfQuarter() {
|
|
1365
|
+
return new DateTruncOperation(this.cwj(), "QUARTER" /* QUARTER */, "First Day Of Quarter " + this.displayName());
|
|
1366
|
+
}
|
|
1367
|
+
/** The date of the first day of the year the date falls in. */
|
|
1368
|
+
firstDayOfYear() {
|
|
1369
|
+
return new DateTruncOperation(this.cwj(), "YEAR" /* YEAR */, "First Day Of Year " + this.displayName());
|
|
1370
|
+
}
|
|
649
1371
|
/** Add `n` days to this date. */
|
|
650
1372
|
addDays(n) {
|
|
651
1373
|
return new DateArithmeticOperation(this.cwj(), n, "DAY" /* DAY */, true, "Add Days " + this.displayName());
|
|
@@ -684,32 +1406,35 @@ var DateAttribute = class extends Attribute {
|
|
|
684
1406
|
}
|
|
685
1407
|
};
|
|
686
1408
|
var DateTimeAttribute = class extends Attribute {
|
|
687
|
-
constructor(displayName, columnName, columnDbType, owner, parent, embedded) {
|
|
688
|
-
super(displayName, columnName, columnDbType, owner, parent, embedded);
|
|
1409
|
+
constructor(displayName, columnName, columnDbType, owner, parent, embedded, nullable = true, optional = false) {
|
|
1410
|
+
super(displayName, columnName, columnDbType, owner, parent, embedded, nullable, optional);
|
|
1411
|
+
}
|
|
1412
|
+
toConstant(value) {
|
|
1413
|
+
return temporalConstant(value);
|
|
689
1414
|
}
|
|
690
1415
|
/** Filter to rows where this timestamp equals `value`. */
|
|
691
1416
|
eq(value) {
|
|
692
|
-
return new ComparisonOperation(this.cwj(), "EQUAL" /* EQUAL */,
|
|
1417
|
+
return new ComparisonOperation(this.cwj(), "EQUAL" /* EQUAL */, temporalConstant(value));
|
|
693
1418
|
}
|
|
694
1419
|
/** Filter to rows where this timestamp does not equal `value`. */
|
|
695
1420
|
ne(value) {
|
|
696
|
-
return new ComparisonOperation(this.cwj(), "NOT_EQUAL" /* NOT_EQUAL */,
|
|
1421
|
+
return new ComparisonOperation(this.cwj(), "NOT_EQUAL" /* NOT_EQUAL */, temporalConstant(value));
|
|
697
1422
|
}
|
|
698
1423
|
/** Filter to rows where this timestamp is after `value`. */
|
|
699
1424
|
gt(value) {
|
|
700
|
-
return new ComparisonOperation(this.cwj(), "GREATER_THAN" /* GREATER_THAN */,
|
|
1425
|
+
return new ComparisonOperation(this.cwj(), "GREATER_THAN" /* GREATER_THAN */, temporalConstant(value));
|
|
701
1426
|
}
|
|
702
1427
|
/** Filter to rows where this timestamp is before `value`. */
|
|
703
1428
|
lt(value) {
|
|
704
|
-
return new ComparisonOperation(this.cwj(), "LESS_THAN" /* LESS_THAN */,
|
|
1429
|
+
return new ComparisonOperation(this.cwj(), "LESS_THAN" /* LESS_THAN */, temporalConstant(value));
|
|
705
1430
|
}
|
|
706
1431
|
/** Filter to rows where this timestamp is on or after `value`. */
|
|
707
1432
|
gte(value) {
|
|
708
|
-
return new ComparisonOperation(this.cwj(), "GREATER_THAN_OR_EQUAL_TO" /* GREATER_THAN_OR_EQUAL_TO */,
|
|
1433
|
+
return new ComparisonOperation(this.cwj(), "GREATER_THAN_OR_EQUAL_TO" /* GREATER_THAN_OR_EQUAL_TO */, temporalConstant(value));
|
|
709
1434
|
}
|
|
710
1435
|
/** Filter to rows where this timestamp is on or before `value`. */
|
|
711
1436
|
lte(value) {
|
|
712
|
-
return new ComparisonOperation(this.cwj(), "LESS_THAN_OR_EQUAL_TO" /* LESS_THAN_OR_EQUAL_TO */,
|
|
1437
|
+
return new ComparisonOperation(this.cwj(), "LESS_THAN_OR_EQUAL_TO" /* LESS_THAN_OR_EQUAL_TO */, temporalConstant(value));
|
|
713
1438
|
}
|
|
714
1439
|
/** Extract the four-digit calendar year. */
|
|
715
1440
|
year() {
|
|
@@ -747,6 +1472,22 @@ var DateTimeAttribute = class extends Attribute {
|
|
|
747
1472
|
dayOfWeek() {
|
|
748
1473
|
return new DateExtractOperation(this.cwj(), "DOW" /* DOW */, "Day Of Week " + this.displayName());
|
|
749
1474
|
}
|
|
1475
|
+
/** The date of the first day of the week the date falls in (a Monday). */
|
|
1476
|
+
firstDayOfWeek() {
|
|
1477
|
+
return new DateTruncOperation(this.cwj(), "WEEK" /* WEEK */, "First Day Of Week " + this.displayName());
|
|
1478
|
+
}
|
|
1479
|
+
/** The date of the first day of the month the date falls in. */
|
|
1480
|
+
firstDayOfMonth() {
|
|
1481
|
+
return new DateTruncOperation(this.cwj(), "MONTH" /* MONTH */, "First Day Of Month " + this.displayName());
|
|
1482
|
+
}
|
|
1483
|
+
/** The date of the first day of the quarter the date falls in. */
|
|
1484
|
+
firstDayOfQuarter() {
|
|
1485
|
+
return new DateTruncOperation(this.cwj(), "QUARTER" /* QUARTER */, "First Day Of Quarter " + this.displayName());
|
|
1486
|
+
}
|
|
1487
|
+
/** The date of the first day of the year the date falls in. */
|
|
1488
|
+
firstDayOfYear() {
|
|
1489
|
+
return new DateTruncOperation(this.cwj(), "YEAR" /* YEAR */, "First Day Of Year " + this.displayName());
|
|
1490
|
+
}
|
|
750
1491
|
/** Add `n` days to this timestamp. */
|
|
751
1492
|
addDays(n) {
|
|
752
1493
|
return new DateArithmeticOperation(this.cwj(), n, "DAY" /* DAY */, true, "Add Days " + this.displayName());
|
|
@@ -820,6 +1561,69 @@ var DateTimeAttribute = class extends Attribute {
|
|
|
820
1561
|
return new DateDiffOperation(this.cwj(), other, "SECOND" /* SECOND */, "Diff Seconds " + this.displayName());
|
|
821
1562
|
}
|
|
822
1563
|
};
|
|
1564
|
+
applyMixins(DateAttribute, [MinMax]);
|
|
1565
|
+
applyMixins(DateTimeAttribute, [MinMax]);
|
|
1566
|
+
|
|
1567
|
+
// src/datafinder/dialect.ts
|
|
1568
|
+
var doubleQuoted = (name) => '"' + name.replaceAll('"', '""') + '"';
|
|
1569
|
+
var quoted = (value) => "'" + value.replaceAll("'", "''") + "'";
|
|
1570
|
+
var SECONDS_PER = { ["HOUR" /* HOUR */]: 3600, ["MINUTE" /* MINUTE */]: 60, ["SECOND" /* SECOND */]: 1 };
|
|
1571
|
+
var POSTGRES = {
|
|
1572
|
+
name: "postgres",
|
|
1573
|
+
quoteIdentifier: doubleQuoted,
|
|
1574
|
+
stringLiteral: quoted,
|
|
1575
|
+
orderDirection: (direction) => direction === "ASC" /* ASC */ ? " ASC" : " DESC",
|
|
1576
|
+
doubleType: "DOUBLE PRECISION",
|
|
1577
|
+
dayOfWeek: (expression) => `EXTRACT(DOW FROM ${expression})`,
|
|
1578
|
+
dateDiff(unit, from, to) {
|
|
1579
|
+
const start = `CAST(${from} AS TIMESTAMP)`;
|
|
1580
|
+
const end = `CAST(${to} AS TIMESTAMP)`;
|
|
1581
|
+
switch (unit) {
|
|
1582
|
+
case "DAY" /* DAY */:
|
|
1583
|
+
return `(CAST(${to} AS DATE) - CAST(${from} AS DATE))`;
|
|
1584
|
+
case "MONTH" /* MONTH */:
|
|
1585
|
+
return `CAST(EXTRACT(YEAR FROM AGE(${end}, ${start})) * 12 + EXTRACT(MONTH FROM AGE(${end}, ${start})) AS BIGINT)`;
|
|
1586
|
+
case "YEAR" /* YEAR */:
|
|
1587
|
+
return `CAST(EXTRACT(YEAR FROM AGE(${end}, ${start})) AS BIGINT)`;
|
|
1588
|
+
default: {
|
|
1589
|
+
const seconds = SECONDS_PER[unit];
|
|
1590
|
+
if (seconds === void 0) throw new Error(`Cannot take the difference in ${unit}`);
|
|
1591
|
+
return `CAST(FLOOR(EXTRACT(EPOCH FROM (${end} - ${start})) / ${seconds}) AS BIGINT)`;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
},
|
|
1595
|
+
stringAgg: (expression, separator, orderBy) => `STRING_AGG(${expression}, ${separator}${orderBy.length > 0 ? " ORDER BY " + orderBy.join(", ") : ""})`
|
|
1596
|
+
};
|
|
1597
|
+
var DUCKDB = {
|
|
1598
|
+
...POSTGRES,
|
|
1599
|
+
name: "duckdb",
|
|
1600
|
+
// DuckDB sorts nulls last whichever way the sort goes
|
|
1601
|
+
orderDirection: (direction) => direction === "ASC" /* ASC */ ? " ASC" : " DESC NULLS FIRST",
|
|
1602
|
+
doubleType: "DOUBLE",
|
|
1603
|
+
dateDiff: (unit, from, to) => `DATE_DIFF('${unit.toLowerCase()}', ${from}, ${to})`
|
|
1604
|
+
};
|
|
1605
|
+
var DATABRICKS = {
|
|
1606
|
+
name: "databricks",
|
|
1607
|
+
// a double-quoted token is a string in Databricks; identifiers take backticks
|
|
1608
|
+
quoteIdentifier: (name) => "`" + name.replaceAll("`", "``") + "`",
|
|
1609
|
+
// a backslash starts an escape in a Databricks string literal, and a doubled quote is not one: Spark reads
|
|
1610
|
+
// 'it''s' as the two literals 'it' and 's' and joins them, losing the quote. A quote is escaped with a backslash.
|
|
1611
|
+
stringLiteral: (value) => "'" + value.replaceAll("\\", "\\\\").replaceAll("'", "\\'") + "'",
|
|
1612
|
+
// Databricks sorts nulls first when ascending, last when descending
|
|
1613
|
+
orderDirection: (direction) => direction === "ASC" /* ASC */ ? " ASC NULLS LAST" : " DESC NULLS FIRST",
|
|
1614
|
+
doubleType: "DOUBLE",
|
|
1615
|
+
// DAYOFWEEK is 1 = Sunday through 7 = Saturday
|
|
1616
|
+
dayOfWeek: (expression) => `(DAYOFWEEK(${expression}) - 1)`,
|
|
1617
|
+
dateDiff: (unit, from, to) => `DATEDIFF(${unit}, ${from}, ${to})`,
|
|
1618
|
+
stringAgg: (expression, separator, orderBy) => `LISTAGG(${expression}, ${separator})${orderBy.length > 0 ? " WITHIN GROUP (ORDER BY " + orderBy.join(", ") + ")" : ""}`
|
|
1619
|
+
};
|
|
1620
|
+
var DIALECTS = { postgres: POSTGRES, duckdb: DUCKDB, databricks: DATABRICKS };
|
|
1621
|
+
function dialectOf(dialect) {
|
|
1622
|
+
if (typeof dialect !== "string") return dialect;
|
|
1623
|
+
const found = DIALECTS[dialect];
|
|
1624
|
+
if (found === void 0) throw new Error(`Unknown SQL dialect '${dialect}': one of ${Object.keys(DIALECTS).join(", ")}`);
|
|
1625
|
+
return found;
|
|
1626
|
+
}
|
|
823
1627
|
|
|
824
1628
|
// src/datafinder/sql-generator.ts
|
|
825
1629
|
var TableAlias = class {
|
|
@@ -848,6 +1652,12 @@ var Join = class {
|
|
|
848
1652
|
source;
|
|
849
1653
|
target;
|
|
850
1654
|
filterOp;
|
|
1655
|
+
/** `source = target`, written with the key column first as Legend writes a join defined as key = foreign key. */
|
|
1656
|
+
condition() {
|
|
1657
|
+
let [first, second] = [this.source, this.target];
|
|
1658
|
+
if (second.column.primaryKey && !first.column.primaryKey) [first, second] = [second, first];
|
|
1659
|
+
return first.tableAlias.alias + "." + first.column.name + " = " + second.tableAlias.alias + "." + second.column.name;
|
|
1660
|
+
}
|
|
851
1661
|
};
|
|
852
1662
|
function sqlFormatDatetime(value) {
|
|
853
1663
|
const pad = (n, w = 2) => String(n).padStart(w, "0");
|
|
@@ -874,6 +1684,13 @@ var COMPARISON_OPERATOR_STR = {
|
|
|
874
1684
|
var AGGREGATE_SQL_NAMES = {
|
|
875
1685
|
["AVERAGE" /* AVERAGE */]: "AVG"
|
|
876
1686
|
};
|
|
1687
|
+
var GUARDED_COMPARISONS = /* @__PURE__ */ new Set([
|
|
1688
|
+
"LESS_THAN" /* LESS_THAN */,
|
|
1689
|
+
"GREATER_THAN" /* GREATER_THAN */,
|
|
1690
|
+
"LESS_THAN_OR_EQUAL_TO" /* LESS_THAN_OR_EQUAL_TO */,
|
|
1691
|
+
"GREATER_THAN_OR_EQUAL_TO" /* GREATER_THAN_OR_EQUAL_TO */,
|
|
1692
|
+
"LIKE" /* LIKE */
|
|
1693
|
+
]);
|
|
877
1694
|
var WINDOW_SQL_NAMES = {
|
|
878
1695
|
["ROW_NUMBER" /* ROW_NUMBER */]: "ROW_NUMBER",
|
|
879
1696
|
["RANK" /* RANK */]: "RANK",
|
|
@@ -905,31 +1722,63 @@ var SCALAR_SQL_NAMES = {
|
|
|
905
1722
|
["RIGHT" /* RIGHT */]: "RIGHT",
|
|
906
1723
|
["REPEAT" /* REPEAT */]: "REPEAT",
|
|
907
1724
|
["REPLACE" /* REPLACE */]: "REPLACE",
|
|
908
|
-
["SUBSTRING" /* SUBSTRING */]: "SUBSTRING"
|
|
1725
|
+
["SUBSTRING" /* SUBSTRING */]: "SUBSTRING",
|
|
1726
|
+
["COALESCE" /* COALESCE */]: "COALESCE"
|
|
909
1727
|
};
|
|
910
|
-
function constantValueString(op) {
|
|
911
|
-
if (op instanceof StringConstantOperation) return
|
|
1728
|
+
function constantValueString(op, d) {
|
|
1729
|
+
if (op instanceof StringConstantOperation) return d.stringLiteral(op.value);
|
|
912
1730
|
if (op instanceof DateConstantOperation) return sqlFormatDate(op.value);
|
|
913
1731
|
if (op instanceof DateTimeConstantOperation) return sqlFormatDatetime(op.value);
|
|
914
1732
|
if (op instanceof IntegerConstantOperation) return String(op.value);
|
|
915
|
-
if (op instanceof FloatConstantOperation) return String(op.value);
|
|
1733
|
+
if (op instanceof FloatConstantOperation) return Number.isInteger(op.value) && Math.abs(op.value) < 1e21 ? op.value.toFixed(1) : String(op.value);
|
|
916
1734
|
if (op instanceof BooleanConstantOperation) return op.value ? "TRUE" : "FALSE";
|
|
917
1735
|
if (op instanceof DecimalConstantOperation) return String(op.value);
|
|
918
1736
|
throw new Error("Unknown constant operation");
|
|
919
1737
|
}
|
|
920
|
-
function
|
|
1738
|
+
function selectedConstantString(op, d) {
|
|
1739
|
+
if (op instanceof FloatConstantOperation) return "CAST(" + constantValueString(op, d) + " AS " + d.doubleType + ")";
|
|
1740
|
+
if (op instanceof DateTimeConstantOperation) return "TIMESTAMP " + constantValueString(op, d);
|
|
1741
|
+
if (op instanceof DateConstantOperation) return "DATE " + constantValueString(op, d);
|
|
1742
|
+
return constantValueString(op, d);
|
|
1743
|
+
}
|
|
1744
|
+
function windowSpecToString(window, d) {
|
|
921
1745
|
const parts = [];
|
|
922
1746
|
if (window.partitionBy.length > 0) {
|
|
923
|
-
parts.push("PARTITION BY " + window.partitionBy.map((p) => sqlOperationToString(p)).join(", "));
|
|
1747
|
+
parts.push("PARTITION BY " + window.partitionBy.map((p) => sqlOperationToString(p, d)).join(", "));
|
|
924
1748
|
}
|
|
925
1749
|
if (window.orderBy.length > 0) {
|
|
926
1750
|
parts.push("ORDER BY " + window.orderBy.map(
|
|
927
|
-
(s) => sqlOperationToString(s.column) + (s.direction
|
|
1751
|
+
(s) => sqlOperationToString(s.column, d) + d.orderDirection(s.direction)
|
|
928
1752
|
).join(", "));
|
|
929
1753
|
}
|
|
930
1754
|
return "OVER (" + parts.join(" ") + ")";
|
|
931
1755
|
}
|
|
932
|
-
|
|
1756
|
+
var ARITHMETIC_PRECEDENCE = {
|
|
1757
|
+
["+" /* ADD */]: 1,
|
|
1758
|
+
["-" /* SUBTRACT */]: 1,
|
|
1759
|
+
["*" /* MULTIPLY */]: 2,
|
|
1760
|
+
["/" /* DIVIDE */]: 2
|
|
1761
|
+
};
|
|
1762
|
+
function arithmeticNeedsParentheses(parent, child, onRight) {
|
|
1763
|
+
if (!(child instanceof ArithmeticOperation)) return false;
|
|
1764
|
+
const here = ARITHMETIC_PRECEDENCE[parent.operator];
|
|
1765
|
+
const there = ARITHMETIC_PRECEDENCE[child.operator];
|
|
1766
|
+
return there < here || there === here && onRight && (parent.operator === "-" /* SUBTRACT */ || parent.operator === "/" /* DIVIDE */);
|
|
1767
|
+
}
|
|
1768
|
+
function operandString(operation, d) {
|
|
1769
|
+
return operation instanceof ConstantOperation ? constantValueString(operation, d) : sqlOperationToString(operation, d);
|
|
1770
|
+
}
|
|
1771
|
+
var RenderedSql = class extends RelationalOperationElement {
|
|
1772
|
+
constructor(sql) {
|
|
1773
|
+
super();
|
|
1774
|
+
this.sql = sql;
|
|
1775
|
+
}
|
|
1776
|
+
sql;
|
|
1777
|
+
};
|
|
1778
|
+
function isCondition(op) {
|
|
1779
|
+
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;
|
|
1780
|
+
}
|
|
1781
|
+
function sqlOperationToString(operation, d) {
|
|
933
1782
|
if (operation instanceof TableAliasColumn) {
|
|
934
1783
|
return operation.tableAlias.alias + "." + operation.column.name;
|
|
935
1784
|
}
|
|
@@ -937,46 +1786,71 @@ function sqlOperationToString(operation) {
|
|
|
937
1786
|
const fn = WINDOW_SQL_NAMES[operation.func];
|
|
938
1787
|
const parts = [];
|
|
939
1788
|
if (operation.element !== null && operation.element !== void 0) {
|
|
940
|
-
parts.push(sqlOperationToString(operation.element));
|
|
1789
|
+
parts.push(sqlOperationToString(operation.element, d));
|
|
941
1790
|
}
|
|
942
1791
|
if (operation.secondArg !== void 0) parts.push(String(operation.secondArg));
|
|
943
1792
|
for (const arg of operation.extraArgs) {
|
|
944
|
-
parts.push(typeof arg === "string" ?
|
|
1793
|
+
parts.push(typeof arg === "string" ? d.stringLiteral(arg) : String(arg));
|
|
945
1794
|
}
|
|
946
1795
|
const sql = fn + "(" + parts.join(", ") + ")";
|
|
947
|
-
return sql + (operation.window !== void 0 ? " " + windowSpecToString(operation.window) : " OVER ()");
|
|
1796
|
+
return sql + (operation.window !== void 0 ? " " + windowSpecToString(operation.window, d) : " OVER ()");
|
|
1797
|
+
}
|
|
1798
|
+
if (operation instanceof AggregateOperation && operation.operator === "COUNT_DISTINCT" /* COUNT_DISTINCT */) {
|
|
1799
|
+
return "COUNT(DISTINCT " + sqlOperationToString(operation.element, d) + ")";
|
|
1800
|
+
}
|
|
1801
|
+
if (operation instanceof AggregateOperation && operation.operator === "STRING_AGG" /* STRING_AGG */) {
|
|
1802
|
+
let sql = d.stringAgg(
|
|
1803
|
+
sqlOperationToString(operation.element, d),
|
|
1804
|
+
d.stringLiteral(operation.separator ?? ""),
|
|
1805
|
+
operation.orderBy.map((s) => sqlOperationToString(s.column, d) + d.orderDirection(s.direction))
|
|
1806
|
+
);
|
|
1807
|
+
if (operation.window !== void 0) sql += " " + windowSpecToString(operation.window, d);
|
|
1808
|
+
return sql;
|
|
948
1809
|
}
|
|
949
1810
|
if (operation instanceof AggregateOperation) {
|
|
950
1811
|
const fn = AGGREGATE_SQL_NAMES[operation.operator] ?? operation.operator;
|
|
951
|
-
let sql = fn + "(" + sqlOperationToString(operation.element) + ")";
|
|
1812
|
+
let sql = fn + "(" + sqlOperationToString(operation.element, d) + ")";
|
|
952
1813
|
if (operation.window !== void 0) {
|
|
953
|
-
sql += " " + windowSpecToString(operation.window);
|
|
1814
|
+
sql += " " + windowSpecToString(operation.window, d);
|
|
954
1815
|
}
|
|
955
1816
|
return sql;
|
|
956
1817
|
}
|
|
957
1818
|
if (operation instanceof ScalarFunctionOperation) {
|
|
958
1819
|
const fn = SCALAR_SQL_NAMES[operation.func];
|
|
959
|
-
const parts = [sqlOperationToString(operation.element)];
|
|
1820
|
+
const parts = [sqlOperationToString(operation.element, d)];
|
|
960
1821
|
if (operation.secondArg !== void 0) parts.push(String(operation.secondArg));
|
|
961
1822
|
for (const arg of operation.extraArgs) {
|
|
962
|
-
parts.push(typeof arg === "string" ?
|
|
1823
|
+
parts.push(typeof arg === "string" ? d.stringLiteral(arg) : String(arg));
|
|
963
1824
|
}
|
|
964
1825
|
return fn + "(" + parts.join(", ") + ")";
|
|
965
1826
|
}
|
|
966
1827
|
if (operation instanceof DateExtractOperation) {
|
|
967
|
-
|
|
1828
|
+
const element = sqlOperationToString(operation.element, d);
|
|
1829
|
+
return operation.part === "DOW" /* DOW */ ? d.dayOfWeek(element) : "EXTRACT(" + operation.part + " FROM " + element + ")";
|
|
1830
|
+
}
|
|
1831
|
+
if (operation instanceof DateTruncOperation) {
|
|
1832
|
+
return "CAST(DATE_TRUNC('" + operation.part.toLowerCase() + "', " + sqlOperationToString(operation.element, d) + ") AS DATE)";
|
|
968
1833
|
}
|
|
969
1834
|
if (operation instanceof DateArithmeticOperation) {
|
|
970
1835
|
const op = operation.isAdd ? "+" : "-";
|
|
971
|
-
return sqlOperationToString(operation.element) + " " + op + " INTERVAL " + operation.n + " " + operation.unit;
|
|
1836
|
+
return sqlOperationToString(operation.element, d) + " " + op + " INTERVAL '" + operation.n + "' " + operation.unit;
|
|
972
1837
|
}
|
|
973
1838
|
if (operation instanceof DateDiffOperation) {
|
|
974
|
-
|
|
975
|
-
return "DATE_DIFF('" + operation.unit.toLowerCase() + "', " + sqlOperationToString(operation.element) + ", " + otherSql + ")";
|
|
1839
|
+
return d.dateDiff(operation.unit, sqlOperationToString(operation.element, d), "TIMESTAMP " + sqlFormatDatetime(operation.other));
|
|
976
1840
|
}
|
|
977
1841
|
if (operation instanceof CountAllOperation) return "COUNT(*)";
|
|
1842
|
+
if (operation instanceof ConstantOperation) return selectedConstantString(operation, d);
|
|
1843
|
+
if (operation instanceof RenderedSql) return operation.sql;
|
|
1844
|
+
if (operation instanceof ConcatOperation) return operation.parts.map((part) => operandString(part, d)).join(" || ");
|
|
1845
|
+
if (operation instanceof ArithmeticOperation) {
|
|
1846
|
+
let left = operandString(operation.left, d);
|
|
1847
|
+
let right = operandString(operation.right, d);
|
|
1848
|
+
if (arithmeticNeedsParentheses(operation, operation.left, false)) left = "(" + left + ")";
|
|
1849
|
+
if (arithmeticNeedsParentheses(operation, operation.right, true)) right = "(" + right + ")";
|
|
1850
|
+
return left + " " + operation.operator + " " + right;
|
|
1851
|
+
}
|
|
978
1852
|
if (operation instanceof Alias) {
|
|
979
|
-
return sqlOperationToString(operation.element) +
|
|
1853
|
+
return sqlOperationToString(operation.element, d) + " AS " + d.quoteIdentifier(operation.name);
|
|
980
1854
|
}
|
|
981
1855
|
throw new TypeError("Unknown operation in sqlOperationToString: " + operation.constructor.name);
|
|
982
1856
|
}
|
|
@@ -996,73 +1870,53 @@ function openEndClause(endAttr, value, infiniteDatetime) {
|
|
|
996
1870
|
}
|
|
997
1871
|
return gtOp;
|
|
998
1872
|
}
|
|
999
|
-
function
|
|
1000
|
-
const
|
|
1001
|
-
|
|
1873
|
+
function andAll(ops) {
|
|
1874
|
+
const present = ops.filter((op) => op !== null);
|
|
1875
|
+
if (present.length === 0) return null;
|
|
1876
|
+
return present.slice(1).reduce((left, right) => new LogicalOperation(left, "AND" /* AND */, right), present[0]);
|
|
1877
|
+
}
|
|
1878
|
+
function processingPredicate(mc, processingDatetime, joinNode) {
|
|
1879
|
+
if (!(mc instanceof ProcessingTemporalColumns || mc instanceof BusinessDateAndProcessingTemporalColumns || mc instanceof BiTemporalColumns) || processingDatetime === null) return null;
|
|
1880
|
+
const startAt = new DateTimeAttribute("start_at", mc.startAtColumn.name, mc.startAtColumn.type, mc.startAtColumn.table.qualifiedName, joinNode);
|
|
1881
|
+
const endAt = new DateTimeAttribute("end_at", mc.endAtColumn.name, mc.endAtColumn.type, mc.endAtColumn.table.qualifiedName, joinNode);
|
|
1882
|
+
return new LogicalOperation(startAt.lte(processingDatetime), "AND" /* AND */, openEndClause(endAt, processingDatetime, mc.infiniteDatetime));
|
|
1883
|
+
}
|
|
1884
|
+
function businessPredicate(mc, businessDate, joinNode) {
|
|
1885
|
+
if (businessDate === null) return null;
|
|
1002
1886
|
if (mc instanceof BiTemporalColumns) {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
if (processingDatetime !== null) {
|
|
1009
|
-
const startAt = new DateTimeAttribute("start_at", mc.startAtColumn.name, mc.startAtColumn.type, mc.startAtColumn.table.qualifiedName, joinNode);
|
|
1010
|
-
const endAt = new DateTimeAttribute("end_at", mc.endAtColumn.name, mc.endAtColumn.type, mc.endAtColumn.table.qualifiedName, joinNode);
|
|
1011
|
-
ops.push(new LogicalOperation(startAt.lte(processingDatetime), "AND" /* AND */, openEndClause(endAt, processingDatetime, mc.infiniteDatetime)));
|
|
1012
|
-
}
|
|
1013
|
-
} else if (mc instanceof BusinessDateAndProcessingTemporalColumns) {
|
|
1014
|
-
if (businessDate !== null) {
|
|
1015
|
-
const businessAtt = new DateAttribute("business_date", mc.businessDateColumn.name, mc.businessDateColumn.type, mc.businessDateColumn.table.qualifiedName, joinNode);
|
|
1016
|
-
ops.push(businessAtt.eq(businessDate));
|
|
1017
|
-
}
|
|
1018
|
-
if (processingDatetime !== null) {
|
|
1019
|
-
const startAt = new DateTimeAttribute("start_at", mc.startAtColumn.name, mc.startAtColumn.type, mc.startAtColumn.table.qualifiedName, joinNode);
|
|
1020
|
-
const endAt = new DateTimeAttribute("end_at", mc.endAtColumn.name, mc.endAtColumn.type, mc.endAtColumn.table.qualifiedName, joinNode);
|
|
1021
|
-
ops.push(new LogicalOperation(startAt.lte(processingDatetime), "AND" /* AND */, openEndClause(endAt, processingDatetime, mc.infiniteDatetime)));
|
|
1022
|
-
}
|
|
1023
|
-
} else if (mc instanceof ProcessingTemporalColumns && processingDatetime !== null) {
|
|
1024
|
-
const startAt = new DateTimeAttribute("start_at", mc.startAtColumn.name, mc.startAtColumn.type, mc.startAtColumn.table.qualifiedName, joinNode);
|
|
1025
|
-
const endAt = new DateTimeAttribute("end_at", mc.endAtColumn.name, mc.endAtColumn.type, mc.endAtColumn.table.qualifiedName, joinNode);
|
|
1026
|
-
ops.push(new LogicalOperation(startAt.lte(processingDatetime), "AND" /* AND */, openEndClause(endAt, processingDatetime, mc.infiniteDatetime)));
|
|
1027
|
-
} else if (mc instanceof SingleBusinessDateColumn && businessDate !== null) {
|
|
1887
|
+
const dateFrom = new DateAttribute("business_date_from", mc.businessDateFromColumn.name, mc.businessDateFromColumn.type, mc.businessDateFromColumn.table.qualifiedName, joinNode);
|
|
1888
|
+
const dateTo = new DateAttribute("business_date_to", mc.businessDateToColumn.name, mc.businessDateToColumn.type, mc.businessDateToColumn.table.qualifiedName, joinNode);
|
|
1889
|
+
return new LogicalOperation(dateFrom.lte(businessDate), "AND" /* AND */, openEndClause(dateTo, businessDate, mc.infiniteDatetime));
|
|
1890
|
+
}
|
|
1891
|
+
if (mc instanceof BusinessDateAndProcessingTemporalColumns || mc instanceof SingleBusinessDateColumn) {
|
|
1028
1892
|
const businessAtt = new DateAttribute("business_date", mc.businessDateColumn.name, mc.businessDateColumn.type, mc.businessDateColumn.table.qualifiedName, joinNode);
|
|
1029
|
-
|
|
1893
|
+
return businessAtt.eq(businessDate);
|
|
1030
1894
|
}
|
|
1031
|
-
|
|
1032
|
-
if (ops.length === 1) return ops[0];
|
|
1033
|
-
return new LogicalOperation(ops[0], "AND" /* AND */, ops[1]);
|
|
1895
|
+
return null;
|
|
1034
1896
|
}
|
|
1035
|
-
function
|
|
1036
|
-
|
|
1037
|
-
const ops = [];
|
|
1038
|
-
const hasRange = businessDateFrom !== null && businessDateTo !== null;
|
|
1039
|
-
const processingOp = (m) => {
|
|
1040
|
-
const startAt = new DateTimeAttribute("start_at", m.startAtColumn.name, m.startAtColumn.type, m.startAtColumn.table.qualifiedName, joinNode);
|
|
1041
|
-
const endAt = new DateTimeAttribute("end_at", m.endAtColumn.name, m.endAtColumn.type, m.endAtColumn.table.qualifiedName, joinNode);
|
|
1042
|
-
return new LogicalOperation(startAt.lte(processingDatetime), "AND" /* AND */, openEndClause(endAt, processingDatetime, m.infiniteDatetime));
|
|
1043
|
-
};
|
|
1897
|
+
function businessRangePredicate(mc, businessDateFrom, businessDateTo, joinNode) {
|
|
1898
|
+
if (businessDateFrom === null || businessDateTo === null) return null;
|
|
1044
1899
|
if (mc instanceof BiTemporalColumns) {
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
if (processingDatetime !== null) ops.push(processingOp(mc));
|
|
1051
|
-
} else if (mc instanceof BusinessDateAndProcessingTemporalColumns) {
|
|
1052
|
-
if (hasRange) {
|
|
1053
|
-
const businessAtt = new DateAttribute("business_date", mc.businessDateColumn.name, mc.businessDateColumn.type, mc.businessDateColumn.table.qualifiedName, joinNode);
|
|
1054
|
-
ops.push(new LogicalOperation(businessAtt.gte(businessDateFrom), "AND" /* AND */, businessAtt.lte(businessDateTo)));
|
|
1055
|
-
}
|
|
1056
|
-
if (processingDatetime !== null) ops.push(processingOp(mc));
|
|
1057
|
-
} else if (mc instanceof ProcessingTemporalColumns) {
|
|
1058
|
-
if (processingDatetime !== null) ops.push(processingOp(mc));
|
|
1059
|
-
} else if (mc instanceof SingleBusinessDateColumn && hasRange) {
|
|
1900
|
+
const dateFrom = new DateAttribute("business_date_from", mc.businessDateFromColumn.name, mc.businessDateFromColumn.type, mc.businessDateFromColumn.table.qualifiedName, joinNode);
|
|
1901
|
+
const dateTo = new DateAttribute("business_date_to", mc.businessDateToColumn.name, mc.businessDateToColumn.type, mc.businessDateToColumn.table.qualifiedName, joinNode);
|
|
1902
|
+
return new LogicalOperation(dateFrom.lte(businessDateTo), "AND" /* AND */, openEndClause(dateTo, businessDateFrom, mc.infiniteDatetime));
|
|
1903
|
+
}
|
|
1904
|
+
if (mc instanceof BusinessDateAndProcessingTemporalColumns || mc instanceof SingleBusinessDateColumn) {
|
|
1060
1905
|
const businessAtt = new DateAttribute("business_date", mc.businessDateColumn.name, mc.businessDateColumn.type, mc.businessDateColumn.table.qualifiedName, joinNode);
|
|
1061
|
-
|
|
1906
|
+
return new LogicalOperation(businessAtt.gte(businessDateFrom), "AND" /* AND */, businessAtt.lte(businessDateTo));
|
|
1062
1907
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1908
|
+
return null;
|
|
1909
|
+
}
|
|
1910
|
+
function buildMilestoningFilterOperation(businessDate, processingDatetime, table, joinNode) {
|
|
1911
|
+
const mc = table.milestoningColumns;
|
|
1912
|
+
return andAll([processingPredicate(mc, processingDatetime, joinNode), businessPredicate(mc, businessDate, joinNode)]);
|
|
1913
|
+
}
|
|
1914
|
+
function buildMilestoningFilterOperationForDateRange(businessDateFrom, businessDateTo, processingDatetime, table, joinNode) {
|
|
1915
|
+
const mc = table.milestoningColumns;
|
|
1916
|
+
return andAll([
|
|
1917
|
+
processingPredicate(mc, processingDatetime, joinNode),
|
|
1918
|
+
businessRangePredicate(mc, businessDateFrom, businessDateTo, joinNode)
|
|
1919
|
+
]);
|
|
1066
1920
|
}
|
|
1067
1921
|
function walkRefs(op, visit, visitExists) {
|
|
1068
1922
|
if (op === null || op === void 0) return;
|
|
@@ -1073,11 +1927,23 @@ function walkRefs(op, visit, visitExists) {
|
|
|
1073
1927
|
} else if (op instanceof AggregateOperation) {
|
|
1074
1928
|
walkRefs(op.element, visit, visitExists);
|
|
1075
1929
|
if (op.window) walkWindowSpec(op.window, visit, visitExists);
|
|
1930
|
+
for (const sort of op.orderBy) walkRefs(sort, visit, visitExists);
|
|
1076
1931
|
} else if (op instanceof WindowFunctionOperation) {
|
|
1077
1932
|
if (op.element) walkRefs(op.element, visit, visitExists);
|
|
1078
1933
|
if (op.window) walkWindowSpec(op.window, visit, visitExists);
|
|
1079
|
-
} else if (op instanceof ScalarFunctionOperation || op instanceof DateExtractOperation || op instanceof DateArithmeticOperation || op instanceof DateDiffOperation || op instanceof IsNullOperation || op instanceof IsNotNullOperation) {
|
|
1934
|
+
} 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) {
|
|
1935
|
+
walkRefs(op.element, visit, visitExists);
|
|
1936
|
+
} else if (op instanceof Alias) {
|
|
1080
1937
|
walkRefs(op.element, visit, visitExists);
|
|
1938
|
+
} else if (op instanceof ConcatOperation) {
|
|
1939
|
+
for (const part of op.parts) walkRefs(part, visit, visitExists);
|
|
1940
|
+
} else if (op instanceof ArithmeticOperation) {
|
|
1941
|
+
walkRefs(op.left, visit, visitExists);
|
|
1942
|
+
walkRefs(op.right, visit, visitExists);
|
|
1943
|
+
} else if (op instanceof CaseOperation) {
|
|
1944
|
+
walkRefs(op.condition, visit, visitExists);
|
|
1945
|
+
walkRefs(op.then, visit, visitExists);
|
|
1946
|
+
walkRefs(op.fallback, visit, visitExists);
|
|
1081
1947
|
} else if (op instanceof SortOperation) {
|
|
1082
1948
|
walkRefs(op.column, visit, visitExists);
|
|
1083
1949
|
} else if (op instanceof LogicalOperation || op instanceof ComparisonOperation) {
|
|
@@ -1117,7 +1983,7 @@ function withAncestors(nodes) {
|
|
|
1117
1983
|
for (const node of nodes) add(node);
|
|
1118
1984
|
return [...ordered];
|
|
1119
1985
|
}
|
|
1120
|
-
function buildQueryOperation(businessDate, processingDatetime, columns, table, op, orderBy = [], groupBy = [], limit, businessDateTo = null) {
|
|
1986
|
+
function buildQueryOperation(businessDate, processingDatetime, columns, table, op, orderBy = [], groupBy = [], limit, businessDateTo = null, distinct = false, offset, having) {
|
|
1121
1987
|
let filter = op;
|
|
1122
1988
|
const milestoningFor = (t, node) => businessDateTo !== null ? buildMilestoningFilterOperationForDateRange(businessDate, businessDateTo, processingDatetime, t, node) : buildMilestoningFilterOperation(businessDate, processingDatetime, t, node);
|
|
1123
1989
|
if (table instanceof MilestonedTable) {
|
|
@@ -1150,22 +2016,45 @@ function buildQueryOperation(businessDate, processingDatetime, columns, table, o
|
|
|
1150
2016
|
}
|
|
1151
2017
|
if (combined !== void 0) joinFilters.set(node, combined);
|
|
1152
2018
|
}
|
|
1153
|
-
return new SelectOperation(columns, filter, orderBy, groupBy, limit, table, joinFilters);
|
|
2019
|
+
return new SelectOperation(columns, filter, orderBy, groupBy, limit, table, joinFilters, distinct, offset, having);
|
|
2020
|
+
}
|
|
2021
|
+
function needsParentheses(parent, child) {
|
|
2022
|
+
return child instanceof LogicalOperation && child.operator !== parent.operator;
|
|
2023
|
+
}
|
|
2024
|
+
function reachesToMany(op) {
|
|
2025
|
+
if (op instanceof ColumnWithJoin) {
|
|
2026
|
+
for (let node = op.parent; node !== void 0; node = node.parent) if (node.many) return true;
|
|
2027
|
+
return false;
|
|
2028
|
+
}
|
|
2029
|
+
if (op instanceof UnaryOperation || op instanceof Alias) return reachesToMany(op.element);
|
|
2030
|
+
if (op instanceof BinaryOperation) return reachesToMany(op.left) || reachesToMany(op.right);
|
|
2031
|
+
return false;
|
|
2032
|
+
}
|
|
2033
|
+
function unaliased(op) {
|
|
2034
|
+
return op instanceof Alias ? unaliased(op.element) : op;
|
|
1154
2035
|
}
|
|
1155
2036
|
var SQLQueryGenerator = class {
|
|
2037
|
+
constructor(_dialect) {
|
|
2038
|
+
this._dialect = _dialect;
|
|
2039
|
+
}
|
|
2040
|
+
_dialect;
|
|
1156
2041
|
_select = [];
|
|
1157
2042
|
_from = /* @__PURE__ */ new Set();
|
|
1158
2043
|
_join = [];
|
|
1159
2044
|
_groupByParts = [];
|
|
2045
|
+
_having = "";
|
|
1160
2046
|
_orderByParts = [];
|
|
1161
2047
|
_where = "";
|
|
1162
2048
|
_limit;
|
|
2049
|
+
_distinct = false;
|
|
2050
|
+
_offset;
|
|
1163
2051
|
_tableAliasIncr = 0;
|
|
1164
2052
|
_tableAliasesByTable = /* @__PURE__ */ new Map();
|
|
1165
2053
|
_addedJoinIds = /* @__PURE__ */ new Set();
|
|
1166
2054
|
_rootTable;
|
|
1167
2055
|
_elidableNodes = /* @__PURE__ */ new Set();
|
|
1168
2056
|
_joinFilters = /* @__PURE__ */ new Map();
|
|
2057
|
+
_guardOptional = true;
|
|
1169
2058
|
generate(select) {
|
|
1170
2059
|
this._rootTable = select.table;
|
|
1171
2060
|
this._elidableNodes = this._computeElidableNodes(select);
|
|
@@ -1182,11 +2071,52 @@ var SQLQueryGenerator = class {
|
|
|
1182
2071
|
for (const node of groupByJoins.values()) {
|
|
1183
2072
|
this._addJoin(node);
|
|
1184
2073
|
}
|
|
1185
|
-
this._groupByParts =
|
|
2074
|
+
this._groupByParts = this._groupByPartsFor(select);
|
|
2075
|
+
this._having = "";
|
|
2076
|
+
if (select.having !== void 0) {
|
|
2077
|
+
const havingJoins = /* @__PURE__ */ new Map();
|
|
2078
|
+
this._collectRequiredJoins(select.having, havingJoins);
|
|
2079
|
+
for (const node of havingJoins.values()) this._addJoin(node);
|
|
2080
|
+
this._guardOptional = false;
|
|
2081
|
+
try {
|
|
2082
|
+
this._having = this.buildFilter(select.having);
|
|
2083
|
+
} finally {
|
|
2084
|
+
this._guardOptional = true;
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
1186
2087
|
this._orderByParts = select.orderBy.map(
|
|
1187
|
-
(s) => this.
|
|
2088
|
+
(s) => this._sortExpression(s.column) + this._dialect.orderDirection(s.direction)
|
|
1188
2089
|
);
|
|
1189
2090
|
this._limit = select.limit;
|
|
2091
|
+
this._distinct = select.distinct;
|
|
2092
|
+
this._offset = select.offset;
|
|
2093
|
+
}
|
|
2094
|
+
/**
|
|
2095
|
+
* The GROUP BY of a query: the grouping it was given, else the grouping its columns imply.
|
|
2096
|
+
*
|
|
2097
|
+
* Columns that are not aggregated are what the aggregates are computed for, so a query that selects both is
|
|
2098
|
+
* grouped by those columns (as a DataFrame's `groupby(columns).agg(...)` does). An aggregate read through a
|
|
2099
|
+
* to-many association is computed for each row of the root table, as Legend does, so the root's primary key
|
|
2100
|
+
* is grouped by too. A query that selects only aggregates over the root's own columns is not grouped: the
|
|
2101
|
+
* aggregate covers every row. A constant needs no grouping: a bare
|
|
2102
|
+
* `GROUP BY 7` would be a column position.
|
|
2103
|
+
*/
|
|
2104
|
+
_groupByPartsFor(select) {
|
|
2105
|
+
if (select.groupBy.length > 0) return select.groupBy.map((a) => this._attrToColString(a));
|
|
2106
|
+
const aggregates = select.display.filter(containsAggregate);
|
|
2107
|
+
if (aggregates.length === 0) return [];
|
|
2108
|
+
const perRoot = select.table !== void 0 && aggregates.some(reachesToMany);
|
|
2109
|
+
const keys = perRoot ? select.table.columns.filter((c) => c.primaryKey).map((c) => this._columnToString(c)) : [];
|
|
2110
|
+
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));
|
|
2111
|
+
return [.../* @__PURE__ */ new Set([...keys, ...grouped])];
|
|
2112
|
+
}
|
|
2113
|
+
/** What a sort orders by: a column, or an expression such as an aggregate. */
|
|
2114
|
+
_sortExpression(expression) {
|
|
2115
|
+
if (expression instanceof ColumnWithJoin || expression instanceof Attribute) return this.buildFilter(expression);
|
|
2116
|
+
return sqlOperationToString(this._rewriteOperation(expression), this._dialect);
|
|
2117
|
+
}
|
|
2118
|
+
_columnToString(column) {
|
|
2119
|
+
return this._tableAliasForTable(column.owner, column.owner).alias + "." + column.name;
|
|
1190
2120
|
}
|
|
1191
2121
|
_attrToColString(attr) {
|
|
1192
2122
|
const { column, parent } = this._resolve(attr);
|
|
@@ -1233,6 +2163,13 @@ var SQLQueryGenerator = class {
|
|
|
1233
2163
|
}
|
|
1234
2164
|
return { column, parent };
|
|
1235
2165
|
}
|
|
2166
|
+
/** Whether the value `op` reads can be missing, in the join or embedded form that is used for it. */
|
|
2167
|
+
_resolveOptional(op) {
|
|
2168
|
+
if (op.parent !== void 0 && this._elidableNodes.has(op.parent) && op.embedded !== void 0) {
|
|
2169
|
+
return this._resolveOptional(op.embedded);
|
|
2170
|
+
}
|
|
2171
|
+
return op.optional;
|
|
2172
|
+
}
|
|
1236
2173
|
_rewriteWindowSpec(window) {
|
|
1237
2174
|
const partitionBy = window.partitionBy.map((p) => this._rewriteOperation(p));
|
|
1238
2175
|
const orderBy = window.orderBy.map((s) => new SortOperation(this._rewriteOperation(s.column), s.direction));
|
|
@@ -1252,9 +2189,17 @@ var SQLQueryGenerator = class {
|
|
|
1252
2189
|
this._rewriteOperation(op.element),
|
|
1253
2190
|
op.operator,
|
|
1254
2191
|
op.displayName,
|
|
1255
|
-
op.window ? this._rewriteWindowSpec(op.window) : void 0
|
|
2192
|
+
op.window ? this._rewriteWindowSpec(op.window) : void 0,
|
|
2193
|
+
op.separator,
|
|
2194
|
+
op.orderBy.map((s) => new SortOperation(this._rewriteOperation(s.column), s.direction))
|
|
1256
2195
|
);
|
|
1257
2196
|
}
|
|
2197
|
+
if (op instanceof CaseOperation) return new RenderedSql(this._caseSql(op));
|
|
2198
|
+
if (isCondition(op)) return new RenderedSql(this.buildFilter(op));
|
|
2199
|
+
if (op instanceof ConcatOperation) return new ConcatOperation(op.parts.map((part) => this._rewriteOperation(part)), op.displayName);
|
|
2200
|
+
if (op instanceof ArithmeticOperation) {
|
|
2201
|
+
return new ArithmeticOperation(this._rewriteOperation(op.left), op.operator, this._rewriteOperation(op.right), op.displayName);
|
|
2202
|
+
}
|
|
1258
2203
|
if (op instanceof WindowFunctionOperation) {
|
|
1259
2204
|
const el = op.element ? this._rewriteOperation(op.element) : null;
|
|
1260
2205
|
return new WindowFunctionOperation(
|
|
@@ -1272,6 +2217,9 @@ var SQLQueryGenerator = class {
|
|
|
1272
2217
|
if (op instanceof DateExtractOperation) {
|
|
1273
2218
|
return new DateExtractOperation(this._rewriteOperation(op.element), op.part, op.displayName);
|
|
1274
2219
|
}
|
|
2220
|
+
if (op instanceof DateTruncOperation) {
|
|
2221
|
+
return new DateTruncOperation(this._rewriteOperation(op.element), op.part, op.displayName);
|
|
2222
|
+
}
|
|
1275
2223
|
if (op instanceof DateArithmeticOperation) {
|
|
1276
2224
|
return new DateArithmeticOperation(this._rewriteOperation(op.element), op.n, op.unit, op.isAdd, op.displayName);
|
|
1277
2225
|
}
|
|
@@ -1280,6 +2228,13 @@ var SQLQueryGenerator = class {
|
|
|
1280
2228
|
}
|
|
1281
2229
|
return op;
|
|
1282
2230
|
}
|
|
2231
|
+
/** The CASE written out now: its condition can only be rendered while the query is being built. */
|
|
2232
|
+
_caseSql(op) {
|
|
2233
|
+
const branch = (value) => value instanceof ConstantOperation ? constantValueString(value, this._dialect) : sqlOperationToString(this._rewriteOperation(value), this._dialect);
|
|
2234
|
+
let sql = "CASE WHEN " + this.buildFilter(op.condition) + " THEN " + branch(op.then);
|
|
2235
|
+
if (op.fallback !== void 0) sql += " ELSE " + branch(op.fallback);
|
|
2236
|
+
return sql + " END";
|
|
2237
|
+
}
|
|
1283
2238
|
processSelect(cols) {
|
|
1284
2239
|
const requiredJoins = /* @__PURE__ */ new Map();
|
|
1285
2240
|
for (const col of cols) {
|
|
@@ -1288,88 +2243,96 @@ var SQLQueryGenerator = class {
|
|
|
1288
2243
|
for (const node of requiredJoins.values()) {
|
|
1289
2244
|
this._addJoin(node);
|
|
1290
2245
|
}
|
|
1291
|
-
for (const col of cols)
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
);
|
|
1332
|
-
this._select.push(new Alias(rewrittenAgg, alias));
|
|
1333
|
-
} else if (col instanceof ScalarFunctionOperation) {
|
|
1334
|
-
const colNested = this._resolve(findColumn(col));
|
|
1335
|
-
const node = colNested.parent;
|
|
1336
|
-
let ta;
|
|
1337
|
-
if (node !== void 0) {
|
|
1338
|
-
ta = this._tableAliasForTable(colNested.column.owner, node);
|
|
1339
|
-
} else {
|
|
1340
|
-
ta = this._tableAliasForTable(colNested.column.owner, colNested.column.owner);
|
|
1341
|
-
this._from.add(ta);
|
|
1342
|
-
}
|
|
1343
|
-
const alias = col.displayName ?? col.func + " " + findColumn(col).column.name;
|
|
1344
|
-
this._select.push(new Alias(
|
|
1345
|
-
new ScalarFunctionOperation(new TableAliasColumn(colNested.column, ta), col.func, void 0, col.secondArg, col.extraArgs),
|
|
1346
|
-
alias
|
|
1347
|
-
));
|
|
1348
|
-
} else if (col instanceof DateExtractOperation || col instanceof DateArithmeticOperation || col instanceof DateDiffOperation) {
|
|
1349
|
-
const colNested = this._resolve(findColumn(col));
|
|
1350
|
-
const node = colNested.parent;
|
|
1351
|
-
let ta;
|
|
1352
|
-
if (node !== void 0) {
|
|
1353
|
-
ta = this._tableAliasForTable(colNested.column.owner, node);
|
|
1354
|
-
} else {
|
|
1355
|
-
ta = this._tableAliasForTable(colNested.column.owner, colNested.column.owner);
|
|
1356
|
-
this._from.add(ta);
|
|
1357
|
-
}
|
|
1358
|
-
const alias = col.displayName ?? findColumn(col).column.name;
|
|
1359
|
-
let aliasedOp;
|
|
1360
|
-
if (col instanceof DateExtractOperation) {
|
|
1361
|
-
aliasedOp = new DateExtractOperation(new TableAliasColumn(colNested.column, ta), col.part);
|
|
1362
|
-
} else if (col instanceof DateArithmeticOperation) {
|
|
1363
|
-
aliasedOp = new DateArithmeticOperation(new TableAliasColumn(colNested.column, ta), col.n, col.unit, col.isAdd);
|
|
1364
|
-
} else {
|
|
1365
|
-
aliasedOp = new DateDiffOperation(new TableAliasColumn(colNested.column, ta), col.other, col.unit);
|
|
1366
|
-
}
|
|
1367
|
-
this._select.push(new Alias(aliasedOp, alias));
|
|
1368
|
-
} else if (col instanceof CountAllOperation) {
|
|
1369
|
-
const ta = this._tableAliasForTable(col.table, col.table);
|
|
2246
|
+
for (const col of cols) this._selectColumn(col);
|
|
2247
|
+
}
|
|
2248
|
+
/** Add one column to the select list. An alias reads the column it wraps, then names the entry. */
|
|
2249
|
+
_selectColumn(col) {
|
|
2250
|
+
if (col instanceof Alias) {
|
|
2251
|
+
this._selectColumn(col.element);
|
|
2252
|
+
const read = this._select.pop();
|
|
2253
|
+
this._select.push(new Alias(read.element, col.name));
|
|
2254
|
+
} else if (col instanceof Attribute) {
|
|
2255
|
+
const { column, parent: node } = this._resolve(col);
|
|
2256
|
+
let ta;
|
|
2257
|
+
if (node !== void 0) {
|
|
2258
|
+
ta = this._tableAliasForTable(column.owner, node);
|
|
2259
|
+
} else {
|
|
2260
|
+
ta = this._tableAliasForTable(column.owner, column.owner);
|
|
2261
|
+
this._from.add(ta);
|
|
2262
|
+
}
|
|
2263
|
+
this._select.push(new Alias(new TableAliasColumn(column, ta), col.displayName()));
|
|
2264
|
+
} else if (col instanceof WindowFunctionOperation) {
|
|
2265
|
+
const rewritten = this._rewriteOperation(col);
|
|
2266
|
+
let alias;
|
|
2267
|
+
if (col.displayName) {
|
|
2268
|
+
alias = col.displayName;
|
|
2269
|
+
} else if (["ROW_NUMBER" /* ROW_NUMBER */, "RANK" /* RANK */, "DENSE_RANK" /* DENSE_RANK */].includes(col.func)) {
|
|
2270
|
+
alias = col.func.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
2271
|
+
} else if (col.element) {
|
|
2272
|
+
alias = col.func + " " + findColumn(col).column.name;
|
|
2273
|
+
} else {
|
|
2274
|
+
alias = col.func;
|
|
2275
|
+
}
|
|
2276
|
+
this._select.push(new Alias(rewritten, alias));
|
|
2277
|
+
} else if (col instanceof AggregateOperation && !(col.element instanceof ColumnWithJoin)) {
|
|
2278
|
+
this._select.push(new Alias(this._rewriteOperation(col), col.displayName ?? "Expression"));
|
|
2279
|
+
} else if (col instanceof AggregateOperation) {
|
|
2280
|
+
const colNested = this._resolve(findColumn(col));
|
|
2281
|
+
const node = colNested.parent;
|
|
2282
|
+
let ta;
|
|
2283
|
+
if (node !== void 0) {
|
|
2284
|
+
ta = this._tableAliasForTable(colNested.column.owner, node);
|
|
2285
|
+
} else {
|
|
2286
|
+
ta = this._tableAliasForTable(colNested.column.owner, colNested.column.owner);
|
|
1370
2287
|
this._from.add(ta);
|
|
1371
|
-
this._select.push(new Alias(col, "Count"));
|
|
1372
2288
|
}
|
|
2289
|
+
const alias = col.displayName ?? col.operator + " " + findColumn(col).column.name;
|
|
2290
|
+
const rewrittenAgg = new AggregateOperation(
|
|
2291
|
+
new TableAliasColumn(colNested.column, ta),
|
|
2292
|
+
col.operator,
|
|
2293
|
+
void 0,
|
|
2294
|
+
col.window ? this._rewriteWindowSpec(col.window) : void 0,
|
|
2295
|
+
col.separator,
|
|
2296
|
+
col.orderBy.map((s) => new SortOperation(this._rewriteOperation(s.column), s.direction))
|
|
2297
|
+
);
|
|
2298
|
+
this._select.push(new Alias(rewrittenAgg, alias));
|
|
2299
|
+
} else if (col instanceof ScalarFunctionOperation) {
|
|
2300
|
+
this._select.push(new Alias(this._rewriteOperation(col), col.displayName ?? col.func + " " + findColumn(col).column.name));
|
|
2301
|
+
} else if (col instanceof DateExtractOperation || col instanceof DateArithmeticOperation || col instanceof DateDiffOperation) {
|
|
2302
|
+
const colNested = this._resolve(findColumn(col));
|
|
2303
|
+
const node = colNested.parent;
|
|
2304
|
+
let ta;
|
|
2305
|
+
if (node !== void 0) {
|
|
2306
|
+
ta = this._tableAliasForTable(colNested.column.owner, node);
|
|
2307
|
+
} else {
|
|
2308
|
+
ta = this._tableAliasForTable(colNested.column.owner, colNested.column.owner);
|
|
2309
|
+
this._from.add(ta);
|
|
2310
|
+
}
|
|
2311
|
+
const alias = col.displayName ?? findColumn(col).column.name;
|
|
2312
|
+
let aliasedOp;
|
|
2313
|
+
if (col instanceof DateExtractOperation) {
|
|
2314
|
+
aliasedOp = new DateExtractOperation(new TableAliasColumn(colNested.column, ta), col.part);
|
|
2315
|
+
} else if (col instanceof DateArithmeticOperation) {
|
|
2316
|
+
aliasedOp = new DateArithmeticOperation(new TableAliasColumn(colNested.column, ta), col.n, col.unit, col.isAdd);
|
|
2317
|
+
} else {
|
|
2318
|
+
aliasedOp = new DateDiffOperation(new TableAliasColumn(colNested.column, ta), col.other, col.unit);
|
|
2319
|
+
}
|
|
2320
|
+
this._select.push(new Alias(aliasedOp, alias));
|
|
2321
|
+
} else if (col instanceof CaseOperation) {
|
|
2322
|
+
this._select.push(new Alias(this._rewriteOperation(col), col.displayName ?? "Expression"));
|
|
2323
|
+
} else if (col instanceof ConcatOperation) {
|
|
2324
|
+
this._select.push(new Alias(this._rewriteOperation(col), col.displayName ?? "Expression"));
|
|
2325
|
+
} else if (col instanceof ArithmeticOperation) {
|
|
2326
|
+
this._select.push(new Alias(this._rewriteOperation(col), col.displayName ?? "Expression"));
|
|
2327
|
+
} else if (col instanceof DateTruncOperation) {
|
|
2328
|
+
this._select.push(new Alias(this._rewriteOperation(col), col.displayName ?? findColumn(col).column.name));
|
|
2329
|
+
} else if (isCondition(col)) {
|
|
2330
|
+
this._select.push(new Alias(new RenderedSql(this.buildFilter(col)), "Expression"));
|
|
2331
|
+
} else if (col instanceof ConstantOperation) {
|
|
2332
|
+
this._select.push(new Alias(col, "Constant"));
|
|
2333
|
+
} else if (col instanceof CountAllOperation) {
|
|
2334
|
+
if (col.table !== "") this._from.add(this._tableAliasForTable(col.table, col.table));
|
|
2335
|
+
this._select.push(new Alias(col, "Count"));
|
|
1373
2336
|
}
|
|
1374
2337
|
}
|
|
1375
2338
|
_addJoin(node) {
|
|
@@ -1397,19 +2360,43 @@ var SQLQueryGenerator = class {
|
|
|
1397
2360
|
const col = op.node.join.right;
|
|
1398
2361
|
return this.buildFilter(new ColumnWithJoin(col, op.node)) + " IS NULL";
|
|
1399
2362
|
}
|
|
2363
|
+
if (op instanceof InOperation) {
|
|
2364
|
+
return this.buildFilter(op.element) + " IN (" + op.values.map((value) => constantValueString(value, this._dialect)).join(", ") + ")";
|
|
2365
|
+
}
|
|
2366
|
+
if (op instanceof NotInOperation) {
|
|
2367
|
+
const column = this.buildFilter(op.element);
|
|
2368
|
+
const sql = column + " NOT IN (" + op.values.map((value) => constantValueString(value, this._dialect)).join(", ") + ")";
|
|
2369
|
+
if (op.element instanceof ColumnWithJoin && this._resolve(op.element).column.nullable) {
|
|
2370
|
+
return "(" + sql + " OR " + column + " IS NULL)";
|
|
2371
|
+
}
|
|
2372
|
+
return sql;
|
|
2373
|
+
}
|
|
1400
2374
|
if (op instanceof IsNotNullOperation) return this.buildFilter(op.element) + " IS NOT NULL";
|
|
1401
2375
|
if (op instanceof IsNullOperation) return this.buildFilter(op.element) + " IS NULL";
|
|
1402
2376
|
if (op instanceof LogicalOperation) {
|
|
1403
2377
|
let left = this.buildFilter(op.left);
|
|
1404
2378
|
let right = this.buildFilter(op.right);
|
|
1405
|
-
if (op.left
|
|
1406
|
-
if (op.right
|
|
2379
|
+
if (needsParentheses(op, op.left)) left = "(" + left + ")";
|
|
2380
|
+
if (needsParentheses(op, op.right)) right = "(" + right + ")";
|
|
1407
2381
|
return left + LOGICAL_OPERATOR_STR[op.operator] + right;
|
|
1408
2382
|
}
|
|
1409
2383
|
if (op instanceof ComparisonOperation) {
|
|
1410
|
-
|
|
2384
|
+
let sql = this.buildFilter(op.left) + COMPARISON_OPERATOR_STR[op.operator] + this.buildFilter(op.right);
|
|
2385
|
+
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("\\"))) {
|
|
2386
|
+
sql += " ESCAPE '" + LIKE_ESCAPE + "'";
|
|
2387
|
+
}
|
|
2388
|
+
if (this._guardOptional && GUARDED_COMPARISONS.has(op.operator) && op.left instanceof ColumnWithJoin && this._resolveOptional(op.left)) {
|
|
2389
|
+
sql = "(" + this.buildFilter(op.left) + " IS NOT NULL AND " + sql + ")";
|
|
2390
|
+
}
|
|
2391
|
+
return sql;
|
|
2392
|
+
}
|
|
2393
|
+
if (op instanceof ConstantOperation) return constantValueString(op, this._dialect);
|
|
2394
|
+
if (op instanceof ScalarFunctionOperation || op instanceof ArithmeticOperation || op instanceof ConcatOperation || op instanceof DateTruncOperation || op instanceof AggregateOperation || op instanceof CountAllOperation) {
|
|
2395
|
+
const required = /* @__PURE__ */ new Map();
|
|
2396
|
+
this._collectRequiredJoins(op, required);
|
|
2397
|
+
for (const node of required.values()) this._addJoin(node);
|
|
2398
|
+
return sqlOperationToString(this._rewriteOperation(op), this._dialect);
|
|
1411
2399
|
}
|
|
1412
|
-
if (op instanceof ConstantOperation) return constantValueString(op);
|
|
1413
2400
|
if (op instanceof ColumnWithJoin) {
|
|
1414
2401
|
const { column, parent: node } = this._resolve(op);
|
|
1415
2402
|
let ta;
|
|
@@ -1437,9 +2424,9 @@ var SQLQueryGenerator = class {
|
|
|
1437
2424
|
}
|
|
1438
2425
|
buildQueryString() {
|
|
1439
2426
|
const joins = this._join.map(
|
|
1440
|
-
(j) => " LEFT OUTER JOIN " + j.target.tableAlias.table + " AS " + j.target.tableAlias.alias + " ON " + j.
|
|
2427
|
+
(j) => " LEFT OUTER JOIN " + j.target.tableAlias.table + " AS " + j.target.tableAlias.alias + " ON (" + j.condition() + (j.filterOp ? " AND " + this.buildFilter(j.filterOp) : "") + ")"
|
|
1441
2428
|
);
|
|
1442
|
-
return "SELECT " + this._select.map((ca) => sqlOperationToString(ca)).join(",") + " FROM " + Array.from(this._from).map((ta) => ta.table + " AS " + ta.alias).join(",") + joins.join("") + this._buildWhere() + this._buildGroupBy() + this._buildOrderBy() + this._buildLimit();
|
|
2429
|
+
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();
|
|
1443
2430
|
}
|
|
1444
2431
|
_buildWhere() {
|
|
1445
2432
|
return this._where.length > 0 ? " WHERE " + this._where : "";
|
|
@@ -1447,21 +2434,27 @@ var SQLQueryGenerator = class {
|
|
|
1447
2434
|
_buildGroupBy() {
|
|
1448
2435
|
return this._groupByParts.length > 0 ? " GROUP BY " + this._groupByParts.join(", ") : "";
|
|
1449
2436
|
}
|
|
2437
|
+
_buildHaving() {
|
|
2438
|
+
return this._having.length > 0 ? " HAVING " + this._having : "";
|
|
2439
|
+
}
|
|
1450
2440
|
_buildOrderBy() {
|
|
1451
2441
|
return this._orderByParts.length > 0 ? " ORDER BY " + this._orderByParts.join(", ") : "";
|
|
1452
2442
|
}
|
|
2443
|
+
_buildOffset() {
|
|
2444
|
+
return this._offset !== void 0 ? " OFFSET " + this._offset : "";
|
|
2445
|
+
}
|
|
1453
2446
|
_buildLimit() {
|
|
1454
2447
|
return this._limit !== void 0 ? " LIMIT " + this._limit : "";
|
|
1455
2448
|
}
|
|
1456
2449
|
};
|
|
1457
|
-
function selectSqlToString(selectOperation) {
|
|
1458
|
-
const qg = new SQLQueryGenerator();
|
|
2450
|
+
function selectSqlToString(selectOperation, dialect = "postgres") {
|
|
2451
|
+
const qg = new SQLQueryGenerator(dialectOf(dialect));
|
|
1459
2452
|
qg.generate(selectOperation);
|
|
1460
2453
|
return qg.buildQueryString();
|
|
1461
2454
|
}
|
|
1462
|
-
function toSql(businessDate, processingDatetime, columns, table, op, orderBy = [], groupBy = [], limit, businessDateTo = null) {
|
|
1463
|
-
const selectOp = buildQueryOperation(businessDate, processingDatetime, columns, table, op, orderBy, groupBy, limit, businessDateTo);
|
|
1464
|
-
return selectSqlToString(selectOp);
|
|
2455
|
+
function toSql(businessDate, processingDatetime, columns, table, op, orderBy = [], groupBy = [], limit, businessDateTo = null, distinct = false, offset, having, dialect = "postgres") {
|
|
2456
|
+
const selectOp = buildQueryOperation(businessDate, processingDatetime, columns, table, op, orderBy, groupBy, limit, businessDateTo, distinct, offset, having);
|
|
2457
|
+
return selectSqlToString(selectOp, dialect);
|
|
1465
2458
|
}
|
|
1466
2459
|
|
|
1467
2460
|
// src/datafinder/runner.ts
|
|
@@ -1485,6 +2478,18 @@ function getRunner() {
|
|
|
1485
2478
|
if (!_runner) throw new Error("No query runner registered. Call registerRunner() first.");
|
|
1486
2479
|
return _runner;
|
|
1487
2480
|
}
|
|
2481
|
+
var expressionIds = /* @__PURE__ */ new WeakMap();
|
|
2482
|
+
var nextExpressionId = 1;
|
|
2483
|
+
function sortTarget(sort) {
|
|
2484
|
+
const column = sort.column;
|
|
2485
|
+
if (!(column instanceof ColumnWithJoin)) {
|
|
2486
|
+
if (!expressionIds.has(column)) expressionIds.set(column, nextExpressionId++);
|
|
2487
|
+
return JSON.stringify(["expression", expressionIds.get(column)]);
|
|
2488
|
+
}
|
|
2489
|
+
const joins = [];
|
|
2490
|
+
for (let node = column.parent; node !== void 0; node = node.parent) joins.push(node.join.name);
|
|
2491
|
+
return JSON.stringify([column.column.owner, column.column.name, joins]);
|
|
2492
|
+
}
|
|
1488
2493
|
var FinderResult = class {
|
|
1489
2494
|
constructor(_businessDate, _processingDatetime, _columns, _table, _op, _businessDateTo = null) {
|
|
1490
2495
|
this._businessDate = _businessDate;
|
|
@@ -1503,12 +2508,20 @@ var FinderResult = class {
|
|
|
1503
2508
|
_orderBy = [];
|
|
1504
2509
|
_groupBy = [];
|
|
1505
2510
|
_limit;
|
|
2511
|
+
_distinct = false;
|
|
2512
|
+
_offset;
|
|
2513
|
+
_having;
|
|
1506
2514
|
_timeoutMs = DEFAULT_TIMEOUT_MS;
|
|
1507
|
-
|
|
1508
|
-
|
|
2515
|
+
/**
|
|
2516
|
+
* Order the rows. Calls accumulate: the sorts of the latest call take priority and earlier ones order the
|
|
2517
|
+
* rows it leaves tied, as in a chain of `sortBy` calls. Sorting a column again replaces its earlier direction.
|
|
2518
|
+
*/
|
|
2519
|
+
orderBy(...sorts) {
|
|
2520
|
+
const resorted = new Set(sorts.map(sortTarget));
|
|
2521
|
+
this._orderBy = [...sorts, ...this._orderBy.filter((s) => !resorted.has(sortTarget(s)))];
|
|
1509
2522
|
return this;
|
|
1510
2523
|
}
|
|
1511
|
-
|
|
2524
|
+
groupBy(...attrs) {
|
|
1512
2525
|
this._groupBy = attrs;
|
|
1513
2526
|
return this;
|
|
1514
2527
|
}
|
|
@@ -1516,12 +2529,40 @@ var FinderResult = class {
|
|
|
1516
2529
|
this._limit = n;
|
|
1517
2530
|
return this;
|
|
1518
2531
|
}
|
|
2532
|
+
/** Skip the first `n` rows. Sort the rows first: without an order, which rows are first is not defined. */
|
|
2533
|
+
offset(n) {
|
|
2534
|
+
if (!Number.isInteger(n) || n < 0) throw new Error("offset cannot be negative");
|
|
2535
|
+
this._offset = n;
|
|
2536
|
+
return this;
|
|
2537
|
+
}
|
|
2538
|
+
/** Rows `start` up to but not including `stop`, counting from 0: `offset(start).limit(stop - start)`. */
|
|
2539
|
+
slice(start, stop) {
|
|
2540
|
+
if (!Number.isInteger(start) || !Number.isInteger(stop) || start < 0 || stop < start) {
|
|
2541
|
+
throw new Error("slice needs 0 <= start <= stop");
|
|
2542
|
+
}
|
|
2543
|
+
return this.offset(start).limit(stop - start);
|
|
2544
|
+
}
|
|
2545
|
+
/**
|
|
2546
|
+
* Keep the groups for which every condition holds: `HAVING`. A condition tests an aggregate or a grouping column:
|
|
2547
|
+
* `f.findAll(null, null, [f.category(), countAll()]).groupBy(f.category()).having(countAll().gt(1))`.
|
|
2548
|
+
* Calls accumulate, as `and` does.
|
|
2549
|
+
*/
|
|
2550
|
+
having(...conditions) {
|
|
2551
|
+
for (const condition of conditions) this._having = this._having === void 0 ? condition : this._having.and(condition);
|
|
2552
|
+
return this;
|
|
2553
|
+
}
|
|
2554
|
+
/** Return each distinct row once: `SELECT DISTINCT`. A sort on a distinct query has to use a selected column. */
|
|
2555
|
+
distinct() {
|
|
2556
|
+
this._distinct = true;
|
|
2557
|
+
return this;
|
|
2558
|
+
}
|
|
1519
2559
|
/** Abort the query and throw QueryTimeoutError if it runs longer than `milliseconds` (default 60000). */
|
|
1520
2560
|
timeout(milliseconds) {
|
|
1521
2561
|
this._timeoutMs = milliseconds;
|
|
1522
2562
|
return this;
|
|
1523
2563
|
}
|
|
1524
|
-
toSql()
|
|
2564
|
+
/** The query as SQL, in Postgres unless a dialect is given: `toSql('databricks')`. Nothing is run. */
|
|
2565
|
+
toSql(dialect = "postgres") {
|
|
1525
2566
|
return toSql(
|
|
1526
2567
|
this._businessDate,
|
|
1527
2568
|
this._processingDatetime,
|
|
@@ -1531,7 +2572,11 @@ var FinderResult = class {
|
|
|
1531
2572
|
this._orderBy,
|
|
1532
2573
|
this._groupBy,
|
|
1533
2574
|
this._limit,
|
|
1534
|
-
this._businessDateTo
|
|
2575
|
+
this._businessDateTo,
|
|
2576
|
+
this._distinct,
|
|
2577
|
+
this._offset,
|
|
2578
|
+
this._having,
|
|
2579
|
+
dialect
|
|
1535
2580
|
);
|
|
1536
2581
|
}
|
|
1537
2582
|
async toRows() {
|
|
@@ -1545,15 +2590,22 @@ var FinderResult = class {
|
|
|
1545
2590
|
this._groupBy,
|
|
1546
2591
|
this._limit,
|
|
1547
2592
|
this._timeoutMs,
|
|
1548
|
-
this._businessDateTo
|
|
2593
|
+
this._businessDateTo,
|
|
2594
|
+
this._distinct,
|
|
2595
|
+
this._offset,
|
|
2596
|
+
this._having
|
|
1549
2597
|
);
|
|
1550
2598
|
}
|
|
1551
2599
|
};
|
|
2600
|
+
function convertProcessingDate(value) {
|
|
2601
|
+
if (typeof value === "string" && DATE_ONLY_PATTERN.test(value)) return new DateOnly(value + "T00:00:00Z");
|
|
2602
|
+
return convertDate(value);
|
|
2603
|
+
}
|
|
1552
2604
|
function convertDate(value) {
|
|
1553
2605
|
if (value === null) return null;
|
|
1554
2606
|
if (value instanceof Date) return value;
|
|
1555
2607
|
let normalized = value;
|
|
1556
|
-
if (
|
|
2608
|
+
if (DATE_ONLY_PATTERN.test(value)) normalized = value + "T00:00:00Z";
|
|
1557
2609
|
else if (/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}(:\d{2}(\.\d+)?)?$/.test(value)) normalized = value.replace(" ", "T") + "Z";
|
|
1558
2610
|
const d = new Date(normalized);
|
|
1559
2611
|
if (isNaN(d.getTime())) throw new Error(`Cannot parse date: ${value}`);
|
|
@@ -1562,7 +2614,7 @@ function convertDate(value) {
|
|
|
1562
2614
|
function convertInputsAndSelect(businessDate, processingDatetime, columns, table, op = new NoOperation()) {
|
|
1563
2615
|
return new FinderResult(
|
|
1564
2616
|
convertDate(businessDate),
|
|
1565
|
-
|
|
2617
|
+
convertProcessingDate(processingDatetime),
|
|
1566
2618
|
columns,
|
|
1567
2619
|
table,
|
|
1568
2620
|
op
|
|
@@ -1574,7 +2626,7 @@ function convertInputsAndSelectForDateRange(businessDateFrom, businessDateTo, pr
|
|
|
1574
2626
|
if (processingDatetime == null) throw new Error("processingValidAt is required for findForDateRange");
|
|
1575
2627
|
return new FinderResult(
|
|
1576
2628
|
convertDate(businessDateFrom),
|
|
1577
|
-
|
|
2629
|
+
convertProcessingDate(processingDatetime),
|
|
1578
2630
|
columns,
|
|
1579
2631
|
table,
|
|
1580
2632
|
op,
|