@balena/abstract-sql-compiler 8.4.0 → 8.4.1
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/.versionbot/CHANGELOG.yml +19 -4
- package/CHANGELOG.md +4 -0
- package/out/AbstractSQLCompiler.d.ts +15 -7
- package/out/AbstractSQLCompiler.js.map +1 -1
- package/out/AbstractSQLOptimiser.js +35 -30
- package/out/AbstractSQLOptimiser.js.map +1 -1
- package/package.json +2 -2
- package/src/AbstractSQLCompiler.ts +26 -20
- package/src/AbstractSQLOptimiser.ts +453 -268
|
@@ -4,26 +4,106 @@ import { Dictionary } from 'lodash';
|
|
|
4
4
|
import {
|
|
5
5
|
AbstractSqlQuery,
|
|
6
6
|
AbstractSqlType,
|
|
7
|
+
AddDateDurationNode,
|
|
8
|
+
AddDateNumberNode,
|
|
9
|
+
AddNode,
|
|
10
|
+
AggregateJSONNode,
|
|
11
|
+
AliasNode,
|
|
7
12
|
AndNode,
|
|
13
|
+
AnyNode,
|
|
8
14
|
AnyTypeNodes,
|
|
15
|
+
BetweenNode,
|
|
9
16
|
BindNode,
|
|
17
|
+
BitwiseAndNode,
|
|
18
|
+
BitwiseShiftRightNode,
|
|
19
|
+
BooleanNode,
|
|
20
|
+
BooleanTypeNodes,
|
|
10
21
|
CaseNode,
|
|
22
|
+
CastNode,
|
|
23
|
+
CeilingNode,
|
|
24
|
+
CharacterLengthNode,
|
|
25
|
+
CoalesceNode,
|
|
26
|
+
ConcatenateNode,
|
|
27
|
+
ConcatenateWithSeparatorNode,
|
|
28
|
+
CrossJoinNode,
|
|
29
|
+
CurrentDateNode,
|
|
30
|
+
CurrentTimestampNode,
|
|
31
|
+
DateTruncNode,
|
|
32
|
+
DeleteQueryNode,
|
|
33
|
+
DivideNode,
|
|
11
34
|
DurationNode,
|
|
35
|
+
ElseNode,
|
|
36
|
+
EqualsNode,
|
|
37
|
+
ExistsNode,
|
|
38
|
+
ExtractJSONPathAsTextNode,
|
|
39
|
+
ExtractNumericDateTypeNodes,
|
|
40
|
+
FieldNode,
|
|
41
|
+
FieldsNode,
|
|
42
|
+
FloorNode,
|
|
43
|
+
FromNode,
|
|
44
|
+
FullJoinNode,
|
|
45
|
+
GreaterThanNode,
|
|
46
|
+
GreaterThanOrEqualNode,
|
|
47
|
+
GroupByNode,
|
|
48
|
+
HavingNode,
|
|
49
|
+
InNode,
|
|
50
|
+
InnerJoinNode,
|
|
51
|
+
InsertQueryNode,
|
|
12
52
|
IntegerNode,
|
|
53
|
+
IsDistinctFromNode,
|
|
54
|
+
IsNotDistinctFromNode,
|
|
55
|
+
JSONTypeNodes,
|
|
56
|
+
JoinTypeNodes,
|
|
57
|
+
LeftJoinNode,
|
|
58
|
+
LessThanNode,
|
|
59
|
+
LessThanOrEqualNode,
|
|
60
|
+
LikeNode,
|
|
61
|
+
LimitNode,
|
|
62
|
+
LowerNode,
|
|
63
|
+
MultiplyNode,
|
|
64
|
+
NotEqualsNode,
|
|
65
|
+
NotExistsNode,
|
|
66
|
+
NotNode,
|
|
13
67
|
NullNode,
|
|
14
68
|
NumberNode,
|
|
69
|
+
NumberTypeNodes,
|
|
70
|
+
OffsetNode,
|
|
15
71
|
OrNode,
|
|
16
72
|
OrderByNode,
|
|
17
73
|
RealNode,
|
|
74
|
+
ReferencedFieldNode,
|
|
18
75
|
ReplaceNode,
|
|
19
|
-
SelectQueryNode,
|
|
20
76
|
StrictBooleanTypeNodes,
|
|
21
77
|
StrictDateTypeNodes,
|
|
22
78
|
StrictNumberTypeNodes,
|
|
23
79
|
StrictTextTypeNodes,
|
|
80
|
+
RightJoinNode,
|
|
81
|
+
RightNode,
|
|
82
|
+
RoundNode,
|
|
83
|
+
SelectNode,
|
|
84
|
+
SelectQueryNode,
|
|
85
|
+
StrPosNode,
|
|
86
|
+
SubtractDateDateNode,
|
|
87
|
+
SubtractDateDurationNode,
|
|
88
|
+
SubtractDateNumberNode,
|
|
89
|
+
SubtractNode,
|
|
90
|
+
TableNode,
|
|
91
|
+
TextArrayTypeNodes,
|
|
24
92
|
TextNode,
|
|
25
93
|
TextTypeNodes,
|
|
94
|
+
ToDateNode,
|
|
95
|
+
ToJSONNode,
|
|
96
|
+
ToTimeNode,
|
|
97
|
+
TrimNode,
|
|
98
|
+
UnionQueryNode,
|
|
99
|
+
UnknownTypeNodes,
|
|
100
|
+
UpdateQueryNode,
|
|
101
|
+
UpperNode,
|
|
102
|
+
ValuesNode,
|
|
26
103
|
ValuesNodeTypes,
|
|
104
|
+
WhenNode,
|
|
105
|
+
WhereNode,
|
|
106
|
+
FromTypeNode,
|
|
27
107
|
} from './AbstractSQLCompiler';
|
|
28
108
|
import * as AbstractSQLRules2SQL from './AbstractSQLRules2SQL';
|
|
29
109
|
|
|
@@ -35,11 +115,11 @@ const {
|
|
|
35
115
|
isNotNullable,
|
|
36
116
|
} = AbstractSQLRules2SQL;
|
|
37
117
|
|
|
38
|
-
type OptimisationMatchFn =
|
|
39
|
-
args: AbstractSqlType[]
|
|
40
|
-
) =>
|
|
41
|
-
type MetaMatchFn = (args: AbstractSqlQuery) =>
|
|
42
|
-
type MatchFn = (args: AbstractSqlType[]) =>
|
|
118
|
+
type OptimisationMatchFn<T extends AnyTypeNodes> =
|
|
119
|
+
| ((args: AbstractSqlType[]) => T | false)
|
|
120
|
+
| ((args: AbstractSqlType[]) => T);
|
|
121
|
+
type MetaMatchFn<T extends AnyTypeNodes> = (args: AbstractSqlQuery) => T;
|
|
122
|
+
type MatchFn<T extends AnyTypeNodes> = (args: AbstractSqlType[]) => T;
|
|
43
123
|
|
|
44
124
|
const deprecated = (() => {
|
|
45
125
|
const deprecationMessages = {
|
|
@@ -114,28 +194,29 @@ const isEmptySelectQuery = (query: AnyTypeNodes): boolean => {
|
|
|
114
194
|
};
|
|
115
195
|
|
|
116
196
|
const rewriteMatch =
|
|
117
|
-
(
|
|
118
|
-
name:
|
|
197
|
+
<I extends AnyTypeNodes, O extends AnyTypeNodes>(
|
|
198
|
+
name: I[0],
|
|
119
199
|
matchers: Array<(args: AbstractSqlType) => AbstractSqlType>,
|
|
120
|
-
rewriteFn: MatchFn
|
|
121
|
-
): MatchFn =>
|
|
200
|
+
rewriteFn: MatchFn<O>,
|
|
201
|
+
): MatchFn<O> =>
|
|
122
202
|
(args) => {
|
|
123
203
|
checkArgs(name, args, matchers.length);
|
|
124
204
|
return rewriteFn(
|
|
125
205
|
args.map((arg, index) => {
|
|
126
|
-
|
|
127
|
-
return matchers[index](arg as AbstractSqlType);
|
|
206
|
+
return matchers[index](arg);
|
|
128
207
|
}),
|
|
129
208
|
);
|
|
130
209
|
};
|
|
131
210
|
|
|
132
|
-
const matchArgs = (
|
|
133
|
-
name:
|
|
211
|
+
const matchArgs = <T extends AnyTypeNodes>(
|
|
212
|
+
name: T[0],
|
|
134
213
|
...matchers: Array<(args: AbstractSqlType) => AbstractSqlType>
|
|
135
|
-
): MatchFn => rewriteMatch(name, matchers, (args) => [name, ...args]);
|
|
214
|
+
): MatchFn<T> => rewriteMatch(name, matchers, (args) => [name, ...args] as T);
|
|
136
215
|
|
|
137
|
-
const tryMatches =
|
|
138
|
-
|
|
216
|
+
const tryMatches = <T extends AnyTypeNodes>(
|
|
217
|
+
...matchers: Array<OptimisationMatchFn<T>>
|
|
218
|
+
): MatchFn<T> => {
|
|
219
|
+
return (args): T => {
|
|
139
220
|
let err;
|
|
140
221
|
for (const matcher of matchers) {
|
|
141
222
|
try {
|
|
@@ -151,7 +232,7 @@ const tryMatches = (...matchers: OptimisationMatchFn[]): MatchFn => {
|
|
|
151
232
|
};
|
|
152
233
|
};
|
|
153
234
|
|
|
154
|
-
const AnyValue: MetaMatchFn = (args) => {
|
|
235
|
+
const AnyValue: MetaMatchFn<AnyTypeNodes> = (args) => {
|
|
155
236
|
const [type, ...rest] = args;
|
|
156
237
|
if (type === 'Case') {
|
|
157
238
|
return typeRules[type](rest);
|
|
@@ -172,7 +253,7 @@ const AnyValue: MetaMatchFn = (args) => {
|
|
|
172
253
|
|
|
173
254
|
return UnknownValue(args);
|
|
174
255
|
};
|
|
175
|
-
const UnknownValue: MetaMatchFn = (args) => {
|
|
256
|
+
const UnknownValue: MetaMatchFn<UnknownTypeNodes> = (args) => {
|
|
176
257
|
if (args === null) {
|
|
177
258
|
helped = true;
|
|
178
259
|
deprecated.legacyNull();
|
|
@@ -201,11 +282,13 @@ const UnknownValue: MetaMatchFn = (args) => {
|
|
|
201
282
|
}
|
|
202
283
|
};
|
|
203
284
|
const MatchValue =
|
|
204
|
-
|
|
285
|
+
<T extends AnyTypeNodes>(
|
|
286
|
+
matcher: (type: unknown) => type is T[0],
|
|
287
|
+
): MetaMatchFn<T | UnknownTypeNodes> =>
|
|
205
288
|
(args) => {
|
|
206
289
|
const [type, ...rest] = args;
|
|
207
290
|
if (matcher(type)) {
|
|
208
|
-
return typeRules[type](rest);
|
|
291
|
+
return typeRules[type as keyof typeof typeRules](rest) as T;
|
|
209
292
|
}
|
|
210
293
|
return UnknownValue(args);
|
|
211
294
|
};
|
|
@@ -230,7 +313,9 @@ const isTextValue = (
|
|
|
230
313
|
AbstractSQLRules2SQL.isTextValue(type)
|
|
231
314
|
);
|
|
232
315
|
};
|
|
233
|
-
const TextValue = MatchValue(
|
|
316
|
+
const TextValue = MatchValue<TextTypeNodes>(
|
|
317
|
+
isTextValue as typeof AbstractSQLRules2SQL.isTextValue,
|
|
318
|
+
);
|
|
234
319
|
|
|
235
320
|
const isNumericValue = (
|
|
236
321
|
type: unknown,
|
|
@@ -259,7 +344,9 @@ const isBooleanValue = (
|
|
|
259
344
|
AbstractSQLRules2SQL.isBooleanValue(type)
|
|
260
345
|
);
|
|
261
346
|
};
|
|
262
|
-
const BooleanValue = MatchValue(
|
|
347
|
+
const BooleanValue = MatchValue<BooleanTypeNodes>(
|
|
348
|
+
isBooleanValue as typeof AbstractSQLRules2SQL.isBooleanValue,
|
|
349
|
+
);
|
|
263
350
|
|
|
264
351
|
const isDateValue = (type: unknown): type is 'Now' | StrictDateTypeNodes[0] => {
|
|
265
352
|
return type === 'Now' || AbstractSQLRules2SQL.isDateValue(type);
|
|
@@ -267,16 +354,16 @@ const isDateValue = (type: unknown): type is 'Now' | StrictDateTypeNodes[0] => {
|
|
|
267
354
|
const DateValue = MatchValue(isDateValue);
|
|
268
355
|
|
|
269
356
|
const { isJSONValue } = AbstractSQLRules2SQL;
|
|
270
|
-
const JSONValue = MatchValue(isJSONValue);
|
|
357
|
+
const JSONValue = MatchValue<JSONTypeNodes>(isJSONValue);
|
|
271
358
|
|
|
272
359
|
const { isDurationValue } = AbstractSQLRules2SQL;
|
|
273
360
|
const DurationValue = MatchValue(isDurationValue);
|
|
274
361
|
|
|
275
362
|
const { isArrayValue } = AbstractSQLRules2SQL;
|
|
276
|
-
const ArrayValue = MatchValue(isArrayValue);
|
|
363
|
+
const ArrayValue = MatchValue<TextArrayTypeNodes>(isArrayValue);
|
|
277
364
|
|
|
278
365
|
const { isFieldValue } = AbstractSQLRules2SQL;
|
|
279
|
-
const Field: MetaMatchFn = (args) => {
|
|
366
|
+
const Field: MetaMatchFn<FieldNode | ReferencedFieldNode> = (args) => {
|
|
280
367
|
const [type, ...rest] = args;
|
|
281
368
|
if (isFieldValue(type)) {
|
|
282
369
|
return typeRules[type](rest);
|
|
@@ -290,7 +377,7 @@ const AnyNotNullValue = (args: any): boolean => {
|
|
|
290
377
|
};
|
|
291
378
|
|
|
292
379
|
const FieldOp =
|
|
293
|
-
(type: string): OptimisationMatchFn =>
|
|
380
|
+
(type: string): OptimisationMatchFn<AnyTypeNodes> =>
|
|
294
381
|
(args) => {
|
|
295
382
|
if (
|
|
296
383
|
AnyNotNullValue(args[0]) === false ||
|
|
@@ -309,12 +396,12 @@ const FieldOp =
|
|
|
309
396
|
const FieldEquals = FieldOp('Equals');
|
|
310
397
|
const FieldNotEquals = FieldOp('NotEquals');
|
|
311
398
|
|
|
312
|
-
const Comparison = (
|
|
399
|
+
const Comparison = <T extends AnyTypeNodes>(
|
|
313
400
|
comparison: keyof typeof AbstractSQLRules2SQL.comparisons,
|
|
314
|
-
): MatchFn => {
|
|
315
|
-
return matchArgs(comparison, AnyValue, AnyValue);
|
|
401
|
+
): MatchFn<T> => {
|
|
402
|
+
return matchArgs<T>(comparison, AnyValue, AnyValue);
|
|
316
403
|
};
|
|
317
|
-
const NumberMatch = (type:
|
|
404
|
+
const NumberMatch = <T extends AnyTypeNodes>(type: T[0]): MatchFn<T> => {
|
|
318
405
|
return matchArgs(type, (arg) => {
|
|
319
406
|
if (typeof arg !== 'number') {
|
|
320
407
|
throw new SyntaxError(`${type} expected number but got ${typeof arg}`);
|
|
@@ -323,45 +410,65 @@ const NumberMatch = (type: string): MatchFn => {
|
|
|
323
410
|
});
|
|
324
411
|
};
|
|
325
412
|
|
|
326
|
-
|
|
413
|
+
type ExtractNodeType<T, U> = T extends any[]
|
|
414
|
+
? T[0] extends U
|
|
415
|
+
? T
|
|
416
|
+
: never
|
|
417
|
+
: never;
|
|
418
|
+
const MathOp = <
|
|
419
|
+
T extends ExtractNodeType<NumberTypeNodes, AbstractSQLRules2SQL.MathOps>,
|
|
420
|
+
>(
|
|
421
|
+
type: T[0],
|
|
422
|
+
): MatchFn<T> => {
|
|
327
423
|
return matchArgs(type, NumericValue, NumericValue);
|
|
328
424
|
};
|
|
329
425
|
|
|
330
|
-
const ExtractNumericDatePart =
|
|
426
|
+
const ExtractNumericDatePart = <T extends ExtractNumericDateTypeNodes>(
|
|
427
|
+
type: T[0],
|
|
428
|
+
): MatchFn<T> => {
|
|
331
429
|
return matchArgs(type, DateValue);
|
|
332
430
|
};
|
|
333
431
|
|
|
334
|
-
const Concatenate: MatchFn = (args) => {
|
|
432
|
+
const Concatenate: MatchFn<ConcatenateNode> = (args) => {
|
|
335
433
|
checkMinArgs('Concatenate', args, 1);
|
|
336
434
|
return [
|
|
337
435
|
'Concatenate',
|
|
338
|
-
...args.map((arg) => {
|
|
436
|
+
...(args.map((arg) => {
|
|
339
437
|
if (!isAbstractSqlQuery(arg)) {
|
|
340
438
|
throw new SyntaxError(
|
|
341
439
|
`Expected AbstractSqlQuery array but got ${typeof arg}`,
|
|
342
440
|
);
|
|
343
441
|
}
|
|
344
442
|
return TextValue(arg);
|
|
345
|
-
})
|
|
443
|
+
}) as [
|
|
444
|
+
ReturnType<typeof TextValue>,
|
|
445
|
+
...Array<ReturnType<typeof TextValue>>,
|
|
446
|
+
]),
|
|
346
447
|
];
|
|
347
448
|
};
|
|
348
449
|
|
|
349
|
-
const ConcatenateWithSeparator: MatchFn = (
|
|
450
|
+
const ConcatenateWithSeparator: MatchFn<ConcatenateWithSeparatorNode> = (
|
|
451
|
+
args,
|
|
452
|
+
) => {
|
|
350
453
|
checkMinArgs('ConcatenateWithSeparator', args, 2);
|
|
351
454
|
return [
|
|
352
455
|
'ConcatenateWithSeparator',
|
|
353
|
-
...args.map((arg) => {
|
|
456
|
+
...(args.map((arg) => {
|
|
354
457
|
if (!isAbstractSqlQuery(arg)) {
|
|
355
458
|
throw new SyntaxError(
|
|
356
459
|
`Expected AbstractSqlQuery array but got ${typeof arg}`,
|
|
357
460
|
);
|
|
358
461
|
}
|
|
359
462
|
return TextValue(arg);
|
|
360
|
-
})
|
|
463
|
+
}) as [
|
|
464
|
+
ReturnType<typeof TextValue>,
|
|
465
|
+
ReturnType<typeof TextValue>,
|
|
466
|
+
...Array<ReturnType<typeof TextValue>>,
|
|
467
|
+
]),
|
|
361
468
|
];
|
|
362
469
|
};
|
|
363
470
|
|
|
364
|
-
const Text
|
|
471
|
+
const Text = matchArgs<TextNode>('Text', _.identity);
|
|
365
472
|
|
|
366
473
|
const Value = (arg: string): ValuesNodeTypes => {
|
|
367
474
|
const $arg = arg as boolean | string;
|
|
@@ -396,7 +503,7 @@ const Value = (arg: string): ValuesNodeTypes => {
|
|
|
396
503
|
}
|
|
397
504
|
};
|
|
398
505
|
|
|
399
|
-
const FromMatch: MetaMatchFn = (args) => {
|
|
506
|
+
const FromMatch: MetaMatchFn<FromTypeNode[keyof FromTypeNode]> = (args) => {
|
|
400
507
|
if (typeof args === 'string') {
|
|
401
508
|
deprecated.legacyTable();
|
|
402
509
|
return ['Table', args];
|
|
@@ -408,16 +515,16 @@ const FromMatch: MetaMatchFn = (args) => {
|
|
|
408
515
|
return typeRules[type](rest);
|
|
409
516
|
case 'Table':
|
|
410
517
|
checkArgs('Table', rest, 1);
|
|
411
|
-
return ['Table', rest[0]];
|
|
518
|
+
return ['Table', rest[0] as TableNode[1]];
|
|
412
519
|
default:
|
|
413
520
|
throw new SyntaxError(`From does not support ${type}`);
|
|
414
521
|
}
|
|
415
522
|
};
|
|
416
523
|
|
|
417
|
-
const MaybeAlias = (
|
|
524
|
+
const MaybeAlias = <T extends AnyTypeNodes>(
|
|
418
525
|
args: AbstractSqlQuery,
|
|
419
|
-
matchFn: MetaMatchFn
|
|
420
|
-
):
|
|
526
|
+
matchFn: MetaMatchFn<T>,
|
|
527
|
+
): T | AliasNode<T> => {
|
|
421
528
|
if (
|
|
422
529
|
args.length === 2 &&
|
|
423
530
|
args[0] !== 'Table' &&
|
|
@@ -433,24 +540,28 @@ const MaybeAlias = (
|
|
|
433
540
|
switch (type) {
|
|
434
541
|
case 'Alias':
|
|
435
542
|
checkArgs('Alias', rest, 2);
|
|
436
|
-
return [
|
|
543
|
+
return [
|
|
544
|
+
'Alias',
|
|
545
|
+
matchFn(getAbstractSqlQuery(rest, 0)),
|
|
546
|
+
rest[1] as AliasNode<T>[2],
|
|
547
|
+
];
|
|
437
548
|
default:
|
|
438
549
|
return matchFn(args);
|
|
439
550
|
}
|
|
440
551
|
};
|
|
441
552
|
|
|
442
|
-
const Lower = matchArgs('Lower', TextValue);
|
|
443
|
-
const Upper = matchArgs('Upper', TextValue);
|
|
553
|
+
const Lower = matchArgs<LowerNode>('Lower', TextValue);
|
|
554
|
+
const Upper = matchArgs<UpperNode>('Upper', TextValue);
|
|
444
555
|
|
|
445
556
|
const JoinMatch =
|
|
446
|
-
(joinType:
|
|
557
|
+
<T extends JoinTypeNodes>(joinType: T[0]): MatchFn<T> =>
|
|
447
558
|
(args) => {
|
|
448
559
|
if (args.length !== 1 && args.length !== 2) {
|
|
449
560
|
throw new SyntaxError(`"${joinType}" requires 1/2 arg(s)`);
|
|
450
561
|
}
|
|
451
562
|
const from = MaybeAlias(getAbstractSqlQuery(args, 0), FromMatch);
|
|
452
563
|
if (args.length === 1) {
|
|
453
|
-
return [joinType, from];
|
|
564
|
+
return [joinType, from] as T;
|
|
454
565
|
}
|
|
455
566
|
const [type, ...rest] = getAbstractSqlQuery(args, 1);
|
|
456
567
|
switch (type) {
|
|
@@ -458,7 +569,7 @@ const JoinMatch =
|
|
|
458
569
|
if (joinType !== 'CrossJoin') {
|
|
459
570
|
checkArgs('On', rest, 1);
|
|
460
571
|
const ruleBody = BooleanValue(getAbstractSqlQuery(rest, 0));
|
|
461
|
-
return [joinType, from, ['On', ruleBody]];
|
|
572
|
+
return [joinType, from, ['On', ruleBody]] as unknown as T;
|
|
462
573
|
}
|
|
463
574
|
default:
|
|
464
575
|
throw new SyntaxError(
|
|
@@ -472,14 +583,24 @@ const AddDateMatcher = tryMatches(
|
|
|
472
583
|
matchArgs('AddDateNumber', DateValue, NumericValue),
|
|
473
584
|
);
|
|
474
585
|
|
|
475
|
-
const SubtractDateMatcher = tryMatches
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
matchArgs('
|
|
586
|
+
const SubtractDateMatcher = tryMatches<
|
|
587
|
+
SubtractDateDateNode | SubtractDateDurationNode | SubtractDateNumberNode
|
|
588
|
+
>(
|
|
589
|
+
matchArgs<SubtractDateDateNode>('SubtractDateDate', DateValue, DateValue),
|
|
590
|
+
matchArgs<SubtractDateDurationNode>(
|
|
591
|
+
'SubtractDateDuration',
|
|
592
|
+
DateValue,
|
|
593
|
+
DurationValue,
|
|
594
|
+
),
|
|
595
|
+
matchArgs<SubtractDateNumberNode>(
|
|
596
|
+
'SubtractDateNumber',
|
|
597
|
+
DateValue,
|
|
598
|
+
NumericValue,
|
|
599
|
+
),
|
|
479
600
|
);
|
|
480
601
|
|
|
481
|
-
const typeRules
|
|
482
|
-
UnionQuery: (args) => {
|
|
602
|
+
const typeRules = {
|
|
603
|
+
UnionQuery: (args): UnionQueryNode => {
|
|
483
604
|
checkMinArgs('UnionQuery', args, 2);
|
|
484
605
|
return [
|
|
485
606
|
'UnionQuery',
|
|
@@ -500,16 +621,23 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
500
621
|
}),
|
|
501
622
|
];
|
|
502
623
|
},
|
|
503
|
-
SelectQuery: (args) => {
|
|
504
|
-
const tables:
|
|
505
|
-
|
|
624
|
+
SelectQuery: (args): SelectQueryNode => {
|
|
625
|
+
const tables: Array<
|
|
626
|
+
| FromNode
|
|
627
|
+
| InnerJoinNode
|
|
628
|
+
| LeftJoinNode
|
|
629
|
+
| RightJoinNode
|
|
630
|
+
| FullJoinNode
|
|
631
|
+
| CrossJoinNode
|
|
632
|
+
> = [];
|
|
633
|
+
let select: SelectNode[] = [];
|
|
506
634
|
const groups = {
|
|
507
|
-
Where: [] as
|
|
508
|
-
GroupBy: [] as
|
|
509
|
-
Having: [] as
|
|
510
|
-
OrderBy: [] as
|
|
511
|
-
Limit: [] as
|
|
512
|
-
Offset: [] as
|
|
635
|
+
Where: [] as WhereNode[],
|
|
636
|
+
GroupBy: [] as GroupByNode[],
|
|
637
|
+
Having: [] as HavingNode[],
|
|
638
|
+
OrderBy: [] as OrderByNode[],
|
|
639
|
+
Limit: [] as LimitNode[],
|
|
640
|
+
Offset: [] as OffsetNode[],
|
|
513
641
|
};
|
|
514
642
|
for (const arg of args) {
|
|
515
643
|
if (!isAbstractSqlQuery(arg)) {
|
|
@@ -531,7 +659,15 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
531
659
|
case 'RightJoin':
|
|
532
660
|
case 'FullJoin':
|
|
533
661
|
case 'CrossJoin':
|
|
534
|
-
tables.push(
|
|
662
|
+
tables.push(
|
|
663
|
+
typeRules[type](rest) as
|
|
664
|
+
| FromNode
|
|
665
|
+
| InnerJoinNode
|
|
666
|
+
| LeftJoinNode
|
|
667
|
+
| RightJoinNode
|
|
668
|
+
| FullJoinNode
|
|
669
|
+
| CrossJoinNode,
|
|
670
|
+
);
|
|
535
671
|
break;
|
|
536
672
|
case 'Where':
|
|
537
673
|
case 'GroupBy':
|
|
@@ -544,7 +680,16 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
544
680
|
`'SelectQuery' can only accept one '${type}'`,
|
|
545
681
|
);
|
|
546
682
|
}
|
|
547
|
-
groups[type] = [
|
|
683
|
+
groups[type] = [
|
|
684
|
+
typeRules[type](rest) as
|
|
685
|
+
| WhereNode
|
|
686
|
+
| GroupByNode
|
|
687
|
+
| HavingNode
|
|
688
|
+
| OrderByNode
|
|
689
|
+
| LimitNode
|
|
690
|
+
// The cast as any is because I couldn't find a way to automatically match up the correct type based upon the group we're assigning
|
|
691
|
+
| OffsetNode as any,
|
|
692
|
+
];
|
|
548
693
|
break;
|
|
549
694
|
default:
|
|
550
695
|
throw new SyntaxError(`'SelectQuery' does not support '${type}'`);
|
|
@@ -563,7 +708,7 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
563
708
|
...groups.Offset,
|
|
564
709
|
];
|
|
565
710
|
},
|
|
566
|
-
Select: (args) => {
|
|
711
|
+
Select: (args): SelectNode => {
|
|
567
712
|
checkArgs('Select', args, 1);
|
|
568
713
|
return [
|
|
569
714
|
'Select',
|
|
@@ -575,9 +720,9 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
575
720
|
}
|
|
576
721
|
return MaybeAlias(arg, AnyValue);
|
|
577
722
|
}),
|
|
578
|
-
]
|
|
723
|
+
];
|
|
579
724
|
},
|
|
580
|
-
From: (args) => {
|
|
725
|
+
From: (args): FromNode => {
|
|
581
726
|
checkArgs('From', args, 1);
|
|
582
727
|
return ['From', MaybeAlias(args[0] as AbstractSqlQuery, FromMatch)];
|
|
583
728
|
},
|
|
@@ -590,12 +735,12 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
590
735
|
const from = MaybeAlias(getAbstractSqlQuery(args, 0), FromMatch);
|
|
591
736
|
return ['CrossJoin', from];
|
|
592
737
|
},
|
|
593
|
-
Where: matchArgs('Where', BooleanValue),
|
|
738
|
+
Where: matchArgs<WhereNode>('Where', BooleanValue),
|
|
594
739
|
GroupBy: (args) => {
|
|
595
740
|
checkArgs('GroupBy', args, 1);
|
|
596
741
|
const groups = getAbstractSqlQuery(args, 0);
|
|
597
742
|
checkMinArgs('GroupBy groups', groups, 1);
|
|
598
|
-
return ['GroupBy', groups.map(AnyValue)] as
|
|
743
|
+
return ['GroupBy', groups.map(AnyValue)] as GroupByNode;
|
|
599
744
|
},
|
|
600
745
|
Having: matchArgs('Having', BooleanValue),
|
|
601
746
|
OrderBy: (args) => {
|
|
@@ -624,31 +769,38 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
624
769
|
},
|
|
625
770
|
Average: matchArgs('Average', NumericValue),
|
|
626
771
|
Sum: matchArgs('Sum', NumericValue),
|
|
627
|
-
Field: matchArgs('Field', _.identity),
|
|
628
|
-
ReferencedField: matchArgs(
|
|
629
|
-
|
|
772
|
+
Field: matchArgs<FieldNode>('Field', _.identity),
|
|
773
|
+
ReferencedField: matchArgs<ReferencedFieldNode>(
|
|
774
|
+
'ReferencedField',
|
|
775
|
+
_.identity,
|
|
776
|
+
_.identity,
|
|
777
|
+
),
|
|
778
|
+
Cast: matchArgs<CastNode>('Cast', AnyValue, _.identity),
|
|
630
779
|
Number: NumberMatch('Number'),
|
|
631
780
|
Real: NumberMatch('Real'),
|
|
632
781
|
Integer: NumberMatch('Integer'),
|
|
633
|
-
Boolean: matchArgs('Boolean', _.identity),
|
|
782
|
+
Boolean: matchArgs<BooleanNode>('Boolean', _.identity),
|
|
634
783
|
EmbeddedText: matchArgs('EmbeddedText', _.identity),
|
|
635
|
-
Null: matchArgs('Null'),
|
|
636
|
-
CurrentTimestamp: matchArgs('CurrentTimestamp'),
|
|
637
|
-
CurrentDate: matchArgs('CurrentDate'),
|
|
784
|
+
Null: matchArgs<NullNode>('Null'),
|
|
785
|
+
CurrentTimestamp: matchArgs<CurrentTimestampNode>('CurrentTimestamp'),
|
|
786
|
+
CurrentDate: matchArgs<CurrentDateNode>('CurrentDate'),
|
|
638
787
|
AggregateJSON: tryMatches(
|
|
639
|
-
Helper<OptimisationMatchFn
|
|
788
|
+
Helper<OptimisationMatchFn<AggregateJSONNode>>((args) => {
|
|
640
789
|
checkArgs('AggregateJSON', args, 1);
|
|
641
790
|
const fieldArg = getAbstractSqlQuery(args, 0);
|
|
642
791
|
if (!isFieldValue(fieldArg[0])) {
|
|
643
792
|
deprecated.legacyAggregateJSON();
|
|
644
|
-
return [
|
|
793
|
+
return [
|
|
794
|
+
'AggregateJSON',
|
|
795
|
+
['ReferencedField', ...fieldArg] as ReferencedFieldNode,
|
|
796
|
+
];
|
|
645
797
|
}
|
|
646
798
|
return false;
|
|
647
799
|
}),
|
|
648
|
-
matchArgs('AggregateJSON', Field),
|
|
800
|
+
matchArgs<AggregateJSONNode>('AggregateJSON', Field),
|
|
649
801
|
),
|
|
650
|
-
Equals: tryMatches(
|
|
651
|
-
Helper<OptimisationMatchFn
|
|
802
|
+
Equals: tryMatches<NotExistsNode | EqualsNode>(
|
|
803
|
+
Helper<OptimisationMatchFn<NotExistsNode>>((args) => {
|
|
652
804
|
checkArgs('Equals', args, 2);
|
|
653
805
|
let valueIndex;
|
|
654
806
|
if (args[0][0] === 'Null') {
|
|
@@ -661,10 +813,10 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
661
813
|
|
|
662
814
|
return ['NotExists', getAbstractSqlQuery(args, valueIndex)];
|
|
663
815
|
}),
|
|
664
|
-
Comparison('Equals'),
|
|
816
|
+
Comparison<EqualsNode>('Equals'),
|
|
665
817
|
),
|
|
666
|
-
NotEquals: tryMatches(
|
|
667
|
-
Helper<OptimisationMatchFn
|
|
818
|
+
NotEquals: tryMatches<NotEqualsNode | ExistsNode>(
|
|
819
|
+
Helper<OptimisationMatchFn<ExistsNode>>((args) => {
|
|
668
820
|
checkArgs('NotEquals', args, 2);
|
|
669
821
|
let valueIndex;
|
|
670
822
|
if (args[0][0] === 'Null') {
|
|
@@ -677,15 +829,15 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
677
829
|
|
|
678
830
|
return ['Exists', getAbstractSqlQuery(args, valueIndex)];
|
|
679
831
|
}),
|
|
680
|
-
Comparison('NotEquals'),
|
|
832
|
+
Comparison<NotEqualsNode>('NotEquals'),
|
|
681
833
|
),
|
|
682
|
-
GreaterThan: Comparison('GreaterThan'),
|
|
683
|
-
GreaterThanOrEqual: Comparison('GreaterThanOrEqual'),
|
|
684
|
-
LessThan: Comparison('LessThan'),
|
|
685
|
-
LessThanOrEqual: Comparison('LessThanOrEqual'),
|
|
686
|
-
Like: Comparison('Like'),
|
|
834
|
+
GreaterThan: Comparison<GreaterThanNode>('GreaterThan'),
|
|
835
|
+
GreaterThanOrEqual: Comparison<GreaterThanOrEqualNode>('GreaterThanOrEqual'),
|
|
836
|
+
LessThan: Comparison<LessThanNode>('LessThan'),
|
|
837
|
+
LessThanOrEqual: Comparison<LessThanOrEqualNode>('LessThanOrEqual'),
|
|
838
|
+
Like: Comparison<LikeNode>('Like'),
|
|
687
839
|
IsNotDistinctFrom: tryMatches(
|
|
688
|
-
Helper<OptimisationMatchFn
|
|
840
|
+
Helper<OptimisationMatchFn<NotExistsNode>>((args) => {
|
|
689
841
|
checkArgs('IsNotDistinctFrom', args, 2);
|
|
690
842
|
let valueIndex;
|
|
691
843
|
if (args[0][0] === 'Null') {
|
|
@@ -698,20 +850,19 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
698
850
|
|
|
699
851
|
return ['NotExists', getAbstractSqlQuery(args, valueIndex)];
|
|
700
852
|
}),
|
|
701
|
-
Helper<OptimisationMatchFn
|
|
702
|
-
checkArgs('
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
return ['Equals', ...args];
|
|
853
|
+
Helper<OptimisationMatchFn<EqualsNode>>((args) => {
|
|
854
|
+
checkArgs('IsNotDistinctFrom', args, 2);
|
|
855
|
+
const a = getAbstractSqlQuery(args, 0);
|
|
856
|
+
const b = getAbstractSqlQuery(args, 1);
|
|
857
|
+
if (isNotNullable(a) && isNotNullable(b)) {
|
|
858
|
+
return ['Equals', a, b];
|
|
708
859
|
}
|
|
709
860
|
return false;
|
|
710
861
|
}),
|
|
711
|
-
matchArgs('IsNotDistinctFrom', AnyValue, AnyValue),
|
|
862
|
+
matchArgs<IsNotDistinctFromNode>('IsNotDistinctFrom', AnyValue, AnyValue),
|
|
712
863
|
),
|
|
713
864
|
IsDistinctFrom: tryMatches(
|
|
714
|
-
Helper<OptimisationMatchFn
|
|
865
|
+
Helper<OptimisationMatchFn<ExistsNode>>((args) => {
|
|
715
866
|
checkArgs('IsDistinctFrom', args, 2);
|
|
716
867
|
let valueIndex;
|
|
717
868
|
if (args[0][0] === 'Null') {
|
|
@@ -724,34 +875,54 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
724
875
|
|
|
725
876
|
return ['Exists', getAbstractSqlQuery(args, valueIndex)];
|
|
726
877
|
}),
|
|
727
|
-
Helper<OptimisationMatchFn
|
|
878
|
+
Helper<OptimisationMatchFn<NotEqualsNode>>((args) => {
|
|
728
879
|
checkArgs('IsDistinctFrom', args, 2);
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
return ['NotEquals', ...args];
|
|
880
|
+
const a = getAbstractSqlQuery(args, 0);
|
|
881
|
+
const b = getAbstractSqlQuery(args, 1);
|
|
882
|
+
if (isNotNullable(a) && isNotNullable(b)) {
|
|
883
|
+
return ['NotEquals', a, b];
|
|
734
884
|
}
|
|
735
885
|
return false;
|
|
736
886
|
}),
|
|
737
|
-
matchArgs('IsDistinctFrom', AnyValue, AnyValue),
|
|
887
|
+
matchArgs<IsDistinctFromNode>('IsDistinctFrom', AnyValue, AnyValue),
|
|
738
888
|
),
|
|
739
|
-
Between: matchArgs('Between', AnyValue, AnyValue, AnyValue),
|
|
740
|
-
Add: tryMatches(MathOp('Add'), Helper(AddDateMatcher)),
|
|
741
|
-
Subtract: tryMatches
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
889
|
+
Between: matchArgs<BetweenNode>('Between', AnyValue, AnyValue, AnyValue),
|
|
890
|
+
Add: tryMatches(MathOp<AddNode>('Add'), Helper(AddDateMatcher)),
|
|
891
|
+
Subtract: tryMatches<
|
|
892
|
+
| SubtractNode
|
|
893
|
+
| SubtractDateDateNode
|
|
894
|
+
| SubtractDateNumberNode
|
|
895
|
+
| SubtractDateDurationNode
|
|
896
|
+
>(MathOp<SubtractNode>('Subtract'), Helper(SubtractDateMatcher)),
|
|
897
|
+
SubtractDateDate: matchArgs<SubtractDateDateNode>(
|
|
898
|
+
'SubtractDateDate',
|
|
899
|
+
DateValue,
|
|
900
|
+
DateValue,
|
|
901
|
+
),
|
|
902
|
+
SubtractDateNumber: matchArgs<SubtractDateNumberNode>(
|
|
903
|
+
'SubtractDateNumber',
|
|
904
|
+
DateValue,
|
|
905
|
+
NumericValue,
|
|
906
|
+
),
|
|
907
|
+
SubtractDateDuration: matchArgs<SubtractDateDurationNode>(
|
|
745
908
|
'SubtractDateDuration',
|
|
746
909
|
DateValue,
|
|
747
910
|
DurationValue,
|
|
748
911
|
),
|
|
749
|
-
AddDateDuration: matchArgs(
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
912
|
+
AddDateDuration: matchArgs<AddDateDurationNode>(
|
|
913
|
+
'AddDateDuration',
|
|
914
|
+
DateValue,
|
|
915
|
+
DurationValue,
|
|
916
|
+
),
|
|
917
|
+
AddDateNumber: matchArgs<AddDateNumberNode>(
|
|
918
|
+
'AddDateNumber',
|
|
919
|
+
DateValue,
|
|
920
|
+
NumericValue,
|
|
921
|
+
),
|
|
922
|
+
Multiply: MathOp<MultiplyNode>('Multiply'),
|
|
923
|
+
Divide: MathOp<DivideNode>('Divide'),
|
|
924
|
+
BitwiseAnd: MathOp<BitwiseAndNode>('BitwiseAnd'),
|
|
925
|
+
BitwiseShiftRight: MathOp<BitwiseShiftRightNode>('BitwiseShiftRight'),
|
|
755
926
|
Year: ExtractNumericDatePart('Year'),
|
|
756
927
|
Month: ExtractNumericDatePart('Month'),
|
|
757
928
|
Day: ExtractNumericDatePart('Day'),
|
|
@@ -763,9 +934,9 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
763
934
|
Concat: Concatenate,
|
|
764
935
|
Concatenate,
|
|
765
936
|
ConcatenateWithSeparator,
|
|
766
|
-
Replace: matchArgs('Replace', TextValue, TextValue, TextValue),
|
|
767
|
-
CharacterLength: matchArgs('CharacterLength', TextValue),
|
|
768
|
-
StrPos: matchArgs('StrPos', TextValue, TextValue),
|
|
937
|
+
Replace: matchArgs<ReplaceNode>('Replace', TextValue, TextValue, TextValue),
|
|
938
|
+
CharacterLength: matchArgs<CharacterLengthNode>('CharacterLength', TextValue),
|
|
939
|
+
StrPos: matchArgs<StrPosNode>('StrPos', TextValue, TextValue),
|
|
769
940
|
Substring: (args) => {
|
|
770
941
|
checkMinArgs('Substring', args, 2);
|
|
771
942
|
const str = TextValue(getAbstractSqlQuery(args, 0));
|
|
@@ -779,21 +950,21 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
779
950
|
});
|
|
780
951
|
return ['Substring', str, ...nums];
|
|
781
952
|
},
|
|
782
|
-
Right: matchArgs('Right', TextValue, NumericValue),
|
|
953
|
+
Right: matchArgs<RightNode>('Right', TextValue, NumericValue),
|
|
783
954
|
Tolower: Lower,
|
|
784
955
|
ToLower: Lower,
|
|
785
956
|
Lower,
|
|
786
957
|
Toupper: Upper,
|
|
787
958
|
ToUpper: Upper,
|
|
788
959
|
Upper,
|
|
789
|
-
Trim: matchArgs('Trim', TextValue),
|
|
790
|
-
Round: matchArgs('Round', NumericValue),
|
|
791
|
-
Floor: matchArgs('Floor', NumericValue),
|
|
792
|
-
Ceiling: matchArgs('Ceiling', NumericValue),
|
|
793
|
-
ToDate: matchArgs('ToDate', DateValue),
|
|
794
|
-
DateTrunc: matchArgs('DateTrunc', TextValue, DateValue),
|
|
795
|
-
ToTime: matchArgs('ToTime', DateValue),
|
|
796
|
-
ExtractJSONPathAsText: (args) => {
|
|
960
|
+
Trim: matchArgs<TrimNode>('Trim', TextValue),
|
|
961
|
+
Round: matchArgs<RoundNode>('Round', NumericValue),
|
|
962
|
+
Floor: matchArgs<FloorNode>('Floor', NumericValue),
|
|
963
|
+
Ceiling: matchArgs<CeilingNode>('Ceiling', NumericValue),
|
|
964
|
+
ToDate: matchArgs<ToDateNode>('ToDate', DateValue),
|
|
965
|
+
DateTrunc: matchArgs<DateTruncNode>('DateTrunc', TextValue, DateValue),
|
|
966
|
+
ToTime: matchArgs<ToTimeNode>('ToTime', DateValue),
|
|
967
|
+
ExtractJSONPathAsText: (args): ExtractJSONPathAsTextNode => {
|
|
797
968
|
checkMinArgs('ExtractJSONPathAsText', args, 1);
|
|
798
969
|
const json = JSONValue(getAbstractSqlQuery(args, 0));
|
|
799
970
|
const path = ArrayValue(getAbstractSqlQuery(args, 1));
|
|
@@ -803,17 +974,24 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
803
974
|
// Allow for populated and empty arrays
|
|
804
975
|
return ['TextArray', ...args.map(TextValue)];
|
|
805
976
|
},
|
|
806
|
-
ToJSON: matchArgs('ToJSON', AnyValue),
|
|
807
|
-
Any: matchArgs('Any', AnyValue, _.identity),
|
|
808
|
-
Coalesce: (args) => {
|
|
977
|
+
ToJSON: matchArgs<ToJSONNode>('ToJSON', AnyValue),
|
|
978
|
+
Any: matchArgs<AnyNode>('Any', AnyValue, _.identity),
|
|
979
|
+
Coalesce: (args): CoalesceNode => {
|
|
809
980
|
checkMinArgs('Coalesce', args, 2);
|
|
810
|
-
return [
|
|
981
|
+
return [
|
|
982
|
+
'Coalesce',
|
|
983
|
+
...(args.map(AnyValue) as [
|
|
984
|
+
AnyTypeNodes,
|
|
985
|
+
AnyTypeNodes,
|
|
986
|
+
...AnyTypeNodes[],
|
|
987
|
+
]),
|
|
988
|
+
];
|
|
811
989
|
},
|
|
812
990
|
Case: (args) => {
|
|
813
991
|
checkMinArgs('Case', args, 1);
|
|
814
992
|
return [
|
|
815
993
|
'Case',
|
|
816
|
-
...args.map((arg, index) => {
|
|
994
|
+
...args.map((arg, index): WhenNode | ElseNode => {
|
|
817
995
|
if (!isAbstractSqlQuery(arg)) {
|
|
818
996
|
throw new SyntaxError(
|
|
819
997
|
`Expected AbstractSqlQuery array but got ${typeof arg}`,
|
|
@@ -839,13 +1017,13 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
839
1017
|
] as CaseNode;
|
|
840
1018
|
},
|
|
841
1019
|
And: tryMatches(
|
|
842
|
-
Helper<OptimisationMatchFn
|
|
1020
|
+
Helper<OptimisationMatchFn<AnyTypeNodes>>((args) => {
|
|
843
1021
|
if (args.length !== 1) {
|
|
844
1022
|
return false;
|
|
845
1023
|
}
|
|
846
1024
|
return getAbstractSqlQuery(args, 0);
|
|
847
1025
|
}),
|
|
848
|
-
Helper<OptimisationMatchFn
|
|
1026
|
+
Helper<OptimisationMatchFn<AndNode>>((args) => {
|
|
849
1027
|
checkMinArgs('And', args, 2);
|
|
850
1028
|
// Collapse nested ANDs.
|
|
851
1029
|
let maybeHelped = false;
|
|
@@ -868,7 +1046,7 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
868
1046
|
|
|
869
1047
|
return ['And', ...conditions] as AndNode;
|
|
870
1048
|
}),
|
|
871
|
-
Helper<OptimisationMatchFn
|
|
1049
|
+
Helper<OptimisationMatchFn<BooleanNode | AndNode>>((args) => {
|
|
872
1050
|
checkMinArgs('And', args, 2);
|
|
873
1051
|
// Reduce any booleans
|
|
874
1052
|
let maybeHelped = false;
|
|
@@ -885,14 +1063,14 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
885
1063
|
return true;
|
|
886
1064
|
});
|
|
887
1065
|
if (containsFalse) {
|
|
888
|
-
return ['Boolean', false]
|
|
1066
|
+
return ['Boolean', false];
|
|
889
1067
|
}
|
|
890
1068
|
if (maybeHelped) {
|
|
891
1069
|
return ['And', ...conditions] as AndNode;
|
|
892
1070
|
}
|
|
893
1071
|
return false;
|
|
894
1072
|
}),
|
|
895
|
-
Helper<OptimisationMatchFn
|
|
1073
|
+
Helper<OptimisationMatchFn<AndNode>>((args) => {
|
|
896
1074
|
checkMinArgs('And', args, 2);
|
|
897
1075
|
// Optimise id != 1 AND id != 2 AND id != 3 -> id NOT IN [1, 2, 3]
|
|
898
1076
|
const fieldBuckets: Dictionary<AnyTypeNodes[]> = {};
|
|
@@ -964,13 +1142,13 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
964
1142
|
},
|
|
965
1143
|
),
|
|
966
1144
|
Or: tryMatches(
|
|
967
|
-
Helper<OptimisationMatchFn
|
|
1145
|
+
Helper<OptimisationMatchFn<AnyTypeNodes>>((args) => {
|
|
968
1146
|
if (args.length !== 1) {
|
|
969
1147
|
return false;
|
|
970
1148
|
}
|
|
971
1149
|
return getAbstractSqlQuery(args, 0);
|
|
972
1150
|
}),
|
|
973
|
-
Helper<OptimisationMatchFn
|
|
1151
|
+
Helper<OptimisationMatchFn<OrNode>>((args) => {
|
|
974
1152
|
checkMinArgs('Or', args, 2);
|
|
975
1153
|
// Collapse nested ORs.
|
|
976
1154
|
let maybeHelped = false;
|
|
@@ -993,7 +1171,7 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
993
1171
|
|
|
994
1172
|
return ['Or', ...conditions] as OrNode;
|
|
995
1173
|
}),
|
|
996
|
-
Helper<OptimisationMatchFn
|
|
1174
|
+
Helper<OptimisationMatchFn<BooleanNode | OrNode>>((args) => {
|
|
997
1175
|
checkMinArgs('Or', args, 2);
|
|
998
1176
|
// Reduce any booleans
|
|
999
1177
|
let maybeHelped = false;
|
|
@@ -1010,14 +1188,14 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
1010
1188
|
return true;
|
|
1011
1189
|
});
|
|
1012
1190
|
if (containsTrue) {
|
|
1013
|
-
return ['Boolean', true]
|
|
1191
|
+
return ['Boolean', true];
|
|
1014
1192
|
}
|
|
1015
1193
|
if (maybeHelped) {
|
|
1016
1194
|
return ['Or', ...conditions] as OrNode;
|
|
1017
1195
|
}
|
|
1018
1196
|
return false;
|
|
1019
1197
|
}),
|
|
1020
|
-
Helper<OptimisationMatchFn
|
|
1198
|
+
Helper<OptimisationMatchFn<InNode | OrNode>>((args) => {
|
|
1021
1199
|
checkMinArgs('Or', args, 2);
|
|
1022
1200
|
// Optimise id = 1 OR id = 2 OR id = 3 -> id IN [1, 2, 3]
|
|
1023
1201
|
const fieldBuckets: Dictionary<AnyTypeNodes[]> = {};
|
|
@@ -1095,12 +1273,12 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
1095
1273
|
if (args.length !== 1 && args.length !== 2) {
|
|
1096
1274
|
throw new SyntaxError(`"Bind" requires 1/2 arg(s)`);
|
|
1097
1275
|
}
|
|
1098
|
-
return ['Bind', ...args];
|
|
1276
|
+
return ['Bind', ...args] as BindNode;
|
|
1099
1277
|
},
|
|
1100
1278
|
Text,
|
|
1101
1279
|
Value: Text,
|
|
1102
1280
|
Date: matchArgs('Date', _.identity),
|
|
1103
|
-
Duration: (args) => {
|
|
1281
|
+
Duration: (args): DurationNode => {
|
|
1104
1282
|
checkArgs('Duration', args, 1);
|
|
1105
1283
|
|
|
1106
1284
|
let duration = args[0] as DurationNode[1];
|
|
@@ -1116,26 +1294,26 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
1116
1294
|
if (_(duration).omit('negative').isEmpty()) {
|
|
1117
1295
|
throw new SyntaxError('Invalid duration');
|
|
1118
1296
|
}
|
|
1119
|
-
return ['Duration', duration]
|
|
1297
|
+
return ['Duration', duration];
|
|
1120
1298
|
},
|
|
1121
|
-
Exists: tryMatches(
|
|
1122
|
-
Helper<OptimisationMatchFn
|
|
1299
|
+
Exists: tryMatches<ExistsNode | BooleanNode>(
|
|
1300
|
+
Helper<OptimisationMatchFn<BooleanNode>>((args) => {
|
|
1123
1301
|
checkArgs('Exists', args, 1);
|
|
1124
1302
|
const arg = getAbstractSqlQuery(args, 0);
|
|
1125
1303
|
if (isNotNullable(arg)) {
|
|
1126
|
-
return ['Boolean', true]
|
|
1304
|
+
return ['Boolean', true];
|
|
1127
1305
|
}
|
|
1128
1306
|
return false;
|
|
1129
1307
|
}),
|
|
1130
|
-
Helper<OptimisationMatchFn
|
|
1308
|
+
Helper<OptimisationMatchFn<BooleanNode>>((args) => {
|
|
1131
1309
|
checkArgs('Exists', args, 1);
|
|
1132
1310
|
const arg = getAbstractSqlQuery(args, 0);
|
|
1133
1311
|
if (isEmptySelectQuery(arg)) {
|
|
1134
|
-
return ['Boolean', false]
|
|
1312
|
+
return ['Boolean', false];
|
|
1135
1313
|
}
|
|
1136
1314
|
return false;
|
|
1137
1315
|
}),
|
|
1138
|
-
(args) => {
|
|
1316
|
+
(args): ExistsNode => {
|
|
1139
1317
|
checkArgs('Exists', args, 1);
|
|
1140
1318
|
const arg = getAbstractSqlQuery(args, 0);
|
|
1141
1319
|
const [type, ...rest] = arg;
|
|
@@ -1148,24 +1326,24 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
1148
1326
|
}
|
|
1149
1327
|
},
|
|
1150
1328
|
),
|
|
1151
|
-
NotExists: tryMatches(
|
|
1152
|
-
Helper<OptimisationMatchFn
|
|
1329
|
+
NotExists: tryMatches<BooleanNode | NotExistsNode>(
|
|
1330
|
+
Helper<OptimisationMatchFn<BooleanNode>>((args) => {
|
|
1153
1331
|
checkArgs('Exists', args, 1);
|
|
1154
1332
|
const arg = getAbstractSqlQuery(args, 0);
|
|
1155
1333
|
if (isNotNullable(arg)) {
|
|
1156
|
-
return ['Boolean', false]
|
|
1334
|
+
return ['Boolean', false];
|
|
1157
1335
|
}
|
|
1158
1336
|
return false;
|
|
1159
1337
|
}),
|
|
1160
|
-
Helper<OptimisationMatchFn
|
|
1338
|
+
Helper<OptimisationMatchFn<BooleanNode>>((args) => {
|
|
1161
1339
|
checkArgs('Exists', args, 1);
|
|
1162
1340
|
const arg = getAbstractSqlQuery(args, 0);
|
|
1163
1341
|
if (isEmptySelectQuery(arg)) {
|
|
1164
|
-
return ['Boolean', true]
|
|
1342
|
+
return ['Boolean', true];
|
|
1165
1343
|
}
|
|
1166
1344
|
return false;
|
|
1167
1345
|
}),
|
|
1168
|
-
(args) => {
|
|
1346
|
+
(args): NotExistsNode => {
|
|
1169
1347
|
checkArgs('NotExists', args, 1);
|
|
1170
1348
|
const arg = getAbstractSqlQuery(args, 0);
|
|
1171
1349
|
const [type, ...rest] = arg;
|
|
@@ -1178,30 +1356,32 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
1178
1356
|
}
|
|
1179
1357
|
},
|
|
1180
1358
|
),
|
|
1181
|
-
Not: tryMatches(
|
|
1182
|
-
Helper<OptimisationMatchFn
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1359
|
+
Not: tryMatches<NotNode | BooleanTypeNodes | NotEqualsNode | ExistsNode>(
|
|
1360
|
+
Helper<OptimisationMatchFn<BooleanTypeNodes | NotEqualsNode | ExistsNode>>(
|
|
1361
|
+
(args): BooleanTypeNodes | NotEqualsNode | ExistsNode | false => {
|
|
1362
|
+
checkArgs('Not', args, 1);
|
|
1363
|
+
const [type, ...rest] = getAbstractSqlQuery(args, 0);
|
|
1364
|
+
switch (type) {
|
|
1365
|
+
case 'Not':
|
|
1366
|
+
return BooleanValue(rest[0] as AbstractSqlQuery);
|
|
1367
|
+
case 'Equals':
|
|
1368
|
+
return typeRules.NotEquals(rest);
|
|
1369
|
+
case 'NotEquals':
|
|
1370
|
+
return typeRules.Equals(rest);
|
|
1371
|
+
case 'In':
|
|
1372
|
+
return typeRules.NotIn(rest);
|
|
1373
|
+
case 'NotIn':
|
|
1374
|
+
return typeRules.In(rest);
|
|
1375
|
+
case 'Exists':
|
|
1376
|
+
return typeRules.NotExists(rest);
|
|
1377
|
+
case 'NotExists':
|
|
1378
|
+
return typeRules.Exists(rest);
|
|
1379
|
+
default:
|
|
1380
|
+
return false;
|
|
1381
|
+
}
|
|
1382
|
+
},
|
|
1383
|
+
),
|
|
1384
|
+
matchArgs<NotNode>('Not', BooleanValue),
|
|
1205
1385
|
),
|
|
1206
1386
|
In: (args) => {
|
|
1207
1387
|
checkMinArgs('In', args, 2);
|
|
@@ -1213,7 +1393,7 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
1213
1393
|
);
|
|
1214
1394
|
}
|
|
1215
1395
|
return AnyValue(arg);
|
|
1216
|
-
});
|
|
1396
|
+
}) as [AnyTypeNodes, ...AnyTypeNodes[]];
|
|
1217
1397
|
return ['In', field, ...vals];
|
|
1218
1398
|
},
|
|
1219
1399
|
NotIn: (args) => {
|
|
@@ -1226,14 +1406,14 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
1226
1406
|
);
|
|
1227
1407
|
}
|
|
1228
1408
|
return AnyValue(arg);
|
|
1229
|
-
});
|
|
1409
|
+
}) as [AnyTypeNodes, ...AnyTypeNodes[]];
|
|
1230
1410
|
return ['NotIn', field, ...vals];
|
|
1231
1411
|
},
|
|
1232
|
-
InsertQuery: (args) => {
|
|
1233
|
-
const tables:
|
|
1234
|
-
let fields:
|
|
1235
|
-
let values:
|
|
1236
|
-
const where:
|
|
1412
|
+
InsertQuery: (args): InsertQueryNode => {
|
|
1413
|
+
const tables: FromNode[] = [];
|
|
1414
|
+
let fields: FieldsNode[] = [];
|
|
1415
|
+
let values: ValuesNode[] = [];
|
|
1416
|
+
const where: WhereNode[] = [];
|
|
1237
1417
|
for (const arg of args) {
|
|
1238
1418
|
if (!isAbstractSqlQuery(arg)) {
|
|
1239
1419
|
throw new SyntaxError('`InsertQuery` args must all be arrays');
|
|
@@ -1247,7 +1427,7 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
1247
1427
|
);
|
|
1248
1428
|
}
|
|
1249
1429
|
checkMinArgs('Update fields', rest, 1);
|
|
1250
|
-
fields = [arg];
|
|
1430
|
+
fields = [arg as FieldsNode];
|
|
1251
1431
|
break;
|
|
1252
1432
|
case 'Values':
|
|
1253
1433
|
if (values.length !== 0) {
|
|
@@ -1261,12 +1441,17 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
1261
1441
|
switch (valuesType) {
|
|
1262
1442
|
case 'SelectQuery':
|
|
1263
1443
|
case 'UnionQuery':
|
|
1264
|
-
values = [['Values', typeRules[valuesType](valuesRest)]];
|
|
1265
|
-
break;
|
|
1266
|
-
default:
|
|
1267
1444
|
values = [
|
|
1268
|
-
[
|
|
1445
|
+
[
|
|
1446
|
+
'Values',
|
|
1447
|
+
typeRules[valuesType](valuesRest) as
|
|
1448
|
+
| SelectQueryNode
|
|
1449
|
+
| UnionQueryNode,
|
|
1450
|
+
],
|
|
1269
1451
|
];
|
|
1452
|
+
break;
|
|
1453
|
+
default:
|
|
1454
|
+
values = [['Values', valuesArray.map(Value)] as ValuesNode];
|
|
1270
1455
|
}
|
|
1271
1456
|
}
|
|
1272
1457
|
break;
|
|
@@ -1294,26 +1479,20 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
1294
1479
|
if (
|
|
1295
1480
|
fields.length !== 0 &&
|
|
1296
1481
|
values.length !== 0 &&
|
|
1297
|
-
!['SelectQuery', 'UnionQuery'].includes(values[0][0]
|
|
1482
|
+
!['SelectQuery', 'UnionQuery'].includes(values[0][0]) &&
|
|
1298
1483
|
fields[0].length !== values[0].length
|
|
1299
1484
|
) {
|
|
1300
1485
|
throw new SyntaxError(
|
|
1301
1486
|
"'InsertQuery' requires Fields and Values components to have the same length or use a query for Values",
|
|
1302
1487
|
);
|
|
1303
1488
|
}
|
|
1304
|
-
return [
|
|
1305
|
-
'InsertQuery',
|
|
1306
|
-
...tables,
|
|
1307
|
-
...fields,
|
|
1308
|
-
...values,
|
|
1309
|
-
...where,
|
|
1310
|
-
] as AbstractSqlQuery;
|
|
1489
|
+
return ['InsertQuery', ...tables, ...fields, ...values, ...where];
|
|
1311
1490
|
},
|
|
1312
|
-
UpdateQuery: (args) => {
|
|
1313
|
-
const tables:
|
|
1314
|
-
let fields:
|
|
1315
|
-
let values:
|
|
1316
|
-
let where:
|
|
1491
|
+
UpdateQuery: (args): UpdateQueryNode => {
|
|
1492
|
+
const tables: FromNode[] = [];
|
|
1493
|
+
let fields: FieldsNode[] = [];
|
|
1494
|
+
let values: ValuesNode[] = [];
|
|
1495
|
+
let where: WhereNode[] = [];
|
|
1317
1496
|
for (const arg of args) {
|
|
1318
1497
|
if (!isAbstractSqlQuery(arg)) {
|
|
1319
1498
|
throw new SyntaxError('`UpdateQuery` args must all be arrays');
|
|
@@ -1327,7 +1506,7 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
1327
1506
|
);
|
|
1328
1507
|
}
|
|
1329
1508
|
checkMinArgs('Update fields', rest, 1);
|
|
1330
|
-
fields = [arg];
|
|
1509
|
+
fields = [arg as FieldsNode];
|
|
1331
1510
|
break;
|
|
1332
1511
|
case 'Values':
|
|
1333
1512
|
if (values.length !== 0) {
|
|
@@ -1338,7 +1517,7 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
1338
1517
|
checkArgs('Update values', rest, 1);
|
|
1339
1518
|
const valuesArray = getAbstractSqlQuery(rest, 0);
|
|
1340
1519
|
checkMinArgs('Update values array', valuesArray, 1);
|
|
1341
|
-
values = [['Values', valuesArray.map(Value)]
|
|
1520
|
+
values = [['Values', valuesArray.map(Value)]];
|
|
1342
1521
|
break;
|
|
1343
1522
|
case 'From':
|
|
1344
1523
|
tables.push(typeRules[type](rest));
|
|
@@ -1365,17 +1544,11 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
1365
1544
|
throw new SyntaxError("'UpdateQuery' requires a Values component");
|
|
1366
1545
|
}
|
|
1367
1546
|
|
|
1368
|
-
return [
|
|
1369
|
-
'UpdateQuery',
|
|
1370
|
-
...tables,
|
|
1371
|
-
...fields,
|
|
1372
|
-
...values,
|
|
1373
|
-
...where,
|
|
1374
|
-
] as AbstractSqlQuery;
|
|
1547
|
+
return ['UpdateQuery', ...tables, ...fields, ...values, ...where];
|
|
1375
1548
|
},
|
|
1376
|
-
DeleteQuery: (args) => {
|
|
1377
|
-
const tables:
|
|
1378
|
-
let where:
|
|
1549
|
+
DeleteQuery: (args): DeleteQueryNode => {
|
|
1550
|
+
const tables: FromNode[] = [];
|
|
1551
|
+
let where: WhereNode[] = [];
|
|
1379
1552
|
for (const arg of args) {
|
|
1380
1553
|
if (!isAbstractSqlQuery(arg)) {
|
|
1381
1554
|
throw new SyntaxError('`DeleteQuery` args must all be arrays');
|
|
@@ -1401,80 +1574,92 @@ const typeRules: Dictionary<MatchFn> = {
|
|
|
1401
1574
|
throw new SyntaxError("'DeleteQuery' must have a From component");
|
|
1402
1575
|
}
|
|
1403
1576
|
|
|
1404
|
-
return ['DeleteQuery', ...tables, ...where]
|
|
1577
|
+
return ['DeleteQuery', ...tables, ...where];
|
|
1405
1578
|
},
|
|
1406
1579
|
|
|
1407
1580
|
// Virtual functions
|
|
1408
1581
|
Now: rewriteMatch(
|
|
1409
1582
|
'Now',
|
|
1410
1583
|
[],
|
|
1411
|
-
Helper<MatchFn
|
|
1584
|
+
Helper<MatchFn<CurrentTimestampNode>>(() => ['CurrentTimestamp']),
|
|
1412
1585
|
),
|
|
1413
1586
|
Contains: rewriteMatch(
|
|
1414
1587
|
'Contains',
|
|
1415
1588
|
[TextValue, TextValue],
|
|
1416
|
-
Helper<MatchFn
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1589
|
+
Helper<MatchFn<LikeNode>>(
|
|
1590
|
+
([haystack, needle]: [TextTypeNodes, TextTypeNodes]) => [
|
|
1591
|
+
'Like',
|
|
1592
|
+
haystack,
|
|
1593
|
+
[
|
|
1594
|
+
'Concatenate',
|
|
1595
|
+
['EmbeddedText', '%'],
|
|
1596
|
+
escapeForLike(needle),
|
|
1597
|
+
['EmbeddedText', '%'],
|
|
1598
|
+
],
|
|
1424
1599
|
],
|
|
1425
|
-
|
|
1600
|
+
),
|
|
1426
1601
|
),
|
|
1427
1602
|
Substringof: rewriteMatch(
|
|
1428
1603
|
'Substringof',
|
|
1429
1604
|
[TextValue, TextValue],
|
|
1430
|
-
Helper<MatchFn
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1605
|
+
Helper<MatchFn<LikeNode>>(
|
|
1606
|
+
([needle, haystack]: [TextTypeNodes, TextTypeNodes]) => [
|
|
1607
|
+
'Like',
|
|
1608
|
+
haystack,
|
|
1609
|
+
[
|
|
1610
|
+
'Concatenate',
|
|
1611
|
+
['EmbeddedText', '%'],
|
|
1612
|
+
escapeForLike(needle),
|
|
1613
|
+
['EmbeddedText', '%'],
|
|
1614
|
+
],
|
|
1438
1615
|
],
|
|
1439
|
-
|
|
1616
|
+
),
|
|
1440
1617
|
),
|
|
1441
1618
|
Startswith: rewriteMatch(
|
|
1442
1619
|
'Startswith',
|
|
1443
1620
|
[TextValue, TextValue],
|
|
1444
|
-
Helper<MatchFn
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1621
|
+
Helper<MatchFn<LikeNode>>(
|
|
1622
|
+
([haystack, needle]: [TextTypeNodes, TextTypeNodes]) => [
|
|
1623
|
+
'Like',
|
|
1624
|
+
haystack,
|
|
1625
|
+
['Concatenate', escapeForLike(needle), ['EmbeddedText', '%']],
|
|
1626
|
+
],
|
|
1627
|
+
),
|
|
1449
1628
|
),
|
|
1450
1629
|
Endswith: rewriteMatch(
|
|
1451
1630
|
'Endswith',
|
|
1452
1631
|
[TextValue, TextValue],
|
|
1453
|
-
Helper<MatchFn
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1632
|
+
Helper<MatchFn<LikeNode>>(
|
|
1633
|
+
([haystack, needle]: [TextTypeNodes, TextTypeNodes]) => [
|
|
1634
|
+
'Like',
|
|
1635
|
+
haystack,
|
|
1636
|
+
['Concatenate', ['EmbeddedText', '%'], escapeForLike(needle)],
|
|
1637
|
+
],
|
|
1638
|
+
),
|
|
1458
1639
|
),
|
|
1459
1640
|
IndexOf: rewriteMatch(
|
|
1460
1641
|
'IndexOf',
|
|
1461
1642
|
[TextValue, TextValue],
|
|
1462
|
-
Helper<MatchFn
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1643
|
+
Helper<MatchFn<SubtractNode>>(
|
|
1644
|
+
([haystack, needle]: [TextTypeNodes, TextTypeNodes]) => [
|
|
1645
|
+
'Subtract',
|
|
1646
|
+
['StrPos', haystack, needle],
|
|
1647
|
+
['Number', 1],
|
|
1648
|
+
],
|
|
1649
|
+
),
|
|
1467
1650
|
),
|
|
1468
1651
|
Indexof: rewriteMatch(
|
|
1469
1652
|
'Indexof',
|
|
1470
1653
|
[TextValue, TextValue],
|
|
1471
|
-
Helper<MatchFn
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1654
|
+
Helper<MatchFn<SubtractNode>>(
|
|
1655
|
+
([haystack, needle]: [TextTypeNodes, TextTypeNodes]) => [
|
|
1656
|
+
'Subtract',
|
|
1657
|
+
['StrPos', haystack, needle],
|
|
1658
|
+
['Number', 1],
|
|
1659
|
+
],
|
|
1660
|
+
),
|
|
1476
1661
|
),
|
|
1477
|
-
}
|
|
1662
|
+
} satisfies Dictionary<MatchFn<AnyTypeNodes>>;
|
|
1478
1663
|
|
|
1479
1664
|
export const AbstractSQLOptimiser = (
|
|
1480
1665
|
abstractSQL: AbstractSqlQuery,
|
|
@@ -1511,7 +1696,7 @@ export const AbstractSQLOptimiser = (
|
|
|
1511
1696
|
];
|
|
1512
1697
|
break;
|
|
1513
1698
|
default:
|
|
1514
|
-
abstractSQL = AnyValue(abstractSQL);
|
|
1699
|
+
abstractSQL = AnyValue(abstractSQL) as AbstractSqlQuery;
|
|
1515
1700
|
}
|
|
1516
1701
|
} while (helped);
|
|
1517
1702
|
return abstractSQL;
|