@balena/abstract-sql-compiler 10.2.8 → 10.2.9
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 +69 -1
- package/CHANGELOG.md +11 -0
- package/out/AbstractSQLCompiler.d.ts +6 -7
- package/out/AbstractSQLCompiler.js +7 -2
- package/out/AbstractSQLCompiler.js.map +1 -1
- package/out/AbstractSQLOptimiser.js +17 -23
- package/out/AbstractSQLOptimiser.js.map +1 -1
- package/out/AbstractSQLRules2SQL.js +9 -13
- package/out/AbstractSQLRules2SQL.js.map +1 -1
- package/out/AbstractSQLSchemaOptimiser.js +3 -3
- package/out/AbstractSQLSchemaOptimiser.js.map +1 -1
- package/out/referenced-fields.js +10 -10
- package/out/referenced-fields.js.map +1 -1
- package/package.json +3 -5
- package/src/AbstractSQLCompiler.ts +24 -14
- package/src/AbstractSQLOptimiser.ts +20 -24
- package/src/AbstractSQLRules2SQL.ts +11 -18
- package/src/AbstractSQLSchemaOptimiser.ts +3 -4
- package/src/referenced-fields.ts +10 -7
@@ -1,6 +1,3 @@
|
|
1
|
-
import _ from 'lodash';
|
2
|
-
|
3
|
-
import type { Dictionary } from 'lodash';
|
4
1
|
import type {
|
5
2
|
AbstractSqlQuery,
|
6
3
|
AbstractSqlType,
|
@@ -126,6 +123,8 @@ type OptimisationMatchFn<T extends AnyTypeNodes> =
|
|
126
123
|
type MetaMatchFn<T extends AnyTypeNodes> = (args: AbstractSqlQuery) => T;
|
127
124
|
type MatchFn<T extends AnyTypeNodes> = (args: AbstractSqlType[]) => T;
|
128
125
|
|
126
|
+
const identity = <T>(x: T): T => x;
|
127
|
+
|
129
128
|
let helped = false;
|
130
129
|
let noBinds = false;
|
131
130
|
const Helper = <F extends (...args: any[]) => any>(fn: F) => {
|
@@ -420,7 +419,7 @@ const ConcatenateWithSeparator: MatchFn<ConcatenateWithSeparatorNode> = (
|
|
420
419
|
];
|
421
420
|
};
|
422
421
|
|
423
|
-
const Text = matchArgs<TextNode>('Text',
|
422
|
+
const Text = matchArgs<TextNode>('Text', identity);
|
424
423
|
|
425
424
|
const Value = (arg: string | AbstractSqlQuery): ValuesNodeTypes => {
|
426
425
|
switch (arg) {
|
@@ -707,20 +706,20 @@ const typeRules = {
|
|
707
706
|
},
|
708
707
|
Average: matchArgs('Average', NumericValue),
|
709
708
|
Sum: matchArgs('Sum', NumericValue),
|
710
|
-
Field: matchArgs<FieldNode>('Field',
|
709
|
+
Field: matchArgs<FieldNode>('Field', identity),
|
711
710
|
ReferencedField: matchArgs<ReferencedFieldNode>(
|
712
711
|
'ReferencedField',
|
713
|
-
|
714
|
-
|
712
|
+
identity,
|
713
|
+
identity,
|
715
714
|
),
|
716
|
-
Cast: matchArgs<CastNode>('Cast', AnyValue,
|
715
|
+
Cast: matchArgs<CastNode>('Cast', AnyValue, identity),
|
717
716
|
// eslint-disable-next-line id-denylist
|
718
717
|
Number: NumberMatch('Number'),
|
719
718
|
Real: NumberMatch('Real'),
|
720
719
|
Integer: NumberMatch('Integer'),
|
721
720
|
// eslint-disable-next-line id-denylist
|
722
|
-
Boolean: matchArgs<BooleanNode>('Boolean',
|
723
|
-
EmbeddedText: matchArgs('EmbeddedText',
|
721
|
+
Boolean: matchArgs<BooleanNode>('Boolean', identity),
|
722
|
+
EmbeddedText: matchArgs('EmbeddedText', identity),
|
724
723
|
Null: matchArgs<NullNode>('Null'),
|
725
724
|
CurrentTimestamp: matchArgs<CurrentTimestampNode>('CurrentTimestamp'),
|
726
725
|
CurrentDate: matchArgs<CurrentDateNode>('CurrentDate'),
|
@@ -903,7 +902,7 @@ const typeRules = {
|
|
903
902
|
return ['TextArray', ...args.map(TextValue)];
|
904
903
|
},
|
905
904
|
ToJSON: matchArgs<ToJSONNode>('ToJSON', AnyValue),
|
906
|
-
Any: matchArgs<AnyNode>('Any', AnyValue,
|
905
|
+
Any: matchArgs<AnyNode>('Any', AnyValue, identity),
|
907
906
|
Coalesce: (args): CoalesceNode => {
|
908
907
|
checkMinArgs('Coalesce', args, 2);
|
909
908
|
return [
|
@@ -956,7 +955,7 @@ const typeRules = {
|
|
956
955
|
checkMinArgs('And', args, 2);
|
957
956
|
// Collapse nested ANDs.
|
958
957
|
let maybeHelped = false;
|
959
|
-
const conditions =
|
958
|
+
const conditions = args.flatMap((arg) => {
|
960
959
|
if (!isAbstractSqlQuery(arg)) {
|
961
960
|
throw new SyntaxError(
|
962
961
|
`Expected AbstractSqlQuery array but got ${typeof arg}`,
|
@@ -1059,7 +1058,7 @@ const typeRules = {
|
|
1059
1058
|
return [
|
1060
1059
|
'NotIn',
|
1061
1060
|
fieldBucket[0][1],
|
1062
|
-
...
|
1061
|
+
...fieldBucket.flatMap((field) => field.slice(2)),
|
1063
1062
|
];
|
1064
1063
|
}
|
1065
1064
|
});
|
@@ -1091,7 +1090,7 @@ const typeRules = {
|
|
1091
1090
|
checkMinArgs('Or', args, 2);
|
1092
1091
|
// Collapse nested ORs.
|
1093
1092
|
let maybeHelped = false;
|
1094
|
-
const conditions =
|
1093
|
+
const conditions = args.flatMap((arg) => {
|
1095
1094
|
if (!isAbstractSqlQuery(arg)) {
|
1096
1095
|
throw new SyntaxError(
|
1097
1096
|
`Expected AbstractSqlQuery array but got ${typeof arg}`,
|
@@ -1194,7 +1193,7 @@ const typeRules = {
|
|
1194
1193
|
return [
|
1195
1194
|
'In',
|
1196
1195
|
fieldBucket[0][1],
|
1197
|
-
...
|
1196
|
+
...fieldBucket.flatMap((field) => field.slice(2)),
|
1198
1197
|
];
|
1199
1198
|
}
|
1200
1199
|
});
|
@@ -1232,24 +1231,21 @@ const typeRules = {
|
|
1232
1231
|
),
|
1233
1232
|
Text,
|
1234
1233
|
Value: Text,
|
1235
|
-
Date: matchArgs('Date',
|
1234
|
+
Date: matchArgs('Date', identity),
|
1236
1235
|
Duration: (args): DurationNode => {
|
1237
1236
|
checkArgs('Duration', args, 1);
|
1238
1237
|
|
1239
|
-
|
1238
|
+
const duration = args[0] as DurationNode[1];
|
1240
1239
|
if (duration == null || typeof duration !== 'object') {
|
1241
1240
|
throw new SyntaxError(
|
1242
1241
|
`Duration must be an object, got ${typeof duration}`,
|
1243
1242
|
);
|
1244
1243
|
}
|
1245
|
-
|
1246
|
-
|
1247
|
-
.omitBy(_.isNil)
|
1248
|
-
.value();
|
1249
|
-
if (_(duration).omit('negative').isEmpty()) {
|
1244
|
+
const { negative, day, hour, minute, second } = duration;
|
1245
|
+
if (day == null && hour == null && minute == null && second == null) {
|
1250
1246
|
throw new SyntaxError('Invalid duration');
|
1251
1247
|
}
|
1252
|
-
return ['Duration',
|
1248
|
+
return ['Duration', { negative, day, hour, minute, second }];
|
1253
1249
|
},
|
1254
1250
|
Exists: tryMatches<ExistsNode | BooleanNode>(
|
1255
1251
|
Helper<OptimisationMatchFn<BooleanNode>>((args) => {
|
@@ -1602,7 +1598,7 @@ const typeRules = {
|
|
1602
1598
|
],
|
1603
1599
|
),
|
1604
1600
|
),
|
1605
|
-
} satisfies
|
1601
|
+
} satisfies Record<string, MatchFn<AnyTypeNodes>>;
|
1606
1602
|
|
1607
1603
|
export const AbstractSQLOptimiser = (
|
1608
1604
|
abstractSQL: AbstractSqlQuery,
|
@@ -1,8 +1,4 @@
|
|
1
|
-
import _ from 'lodash';
|
2
|
-
|
3
1
|
import sbvrTypes from '@balena/sbvr-types';
|
4
|
-
|
5
|
-
import type { Dictionary } from 'lodash';
|
6
2
|
import type {
|
7
3
|
AbstractSqlQuery,
|
8
4
|
AbstractSqlType,
|
@@ -36,7 +32,7 @@ type MetaMatchFn = (args: AbstractSqlQuery, indent: string) => string;
|
|
36
32
|
type MatchFn = (args: AbstractSqlType[], indent: string) => string;
|
37
33
|
|
38
34
|
let fieldOrderings: Binding[] = [];
|
39
|
-
let fieldOrderingsLookup:
|
35
|
+
let fieldOrderingsLookup: Record<string, number> = {};
|
40
36
|
let engine: Engines = Engines.postgres;
|
41
37
|
let noBinds = false;
|
42
38
|
|
@@ -702,7 +698,7 @@ const AddBind = (bind: Binding): string => {
|
|
702
698
|
}
|
703
699
|
};
|
704
700
|
|
705
|
-
const typeRules:
|
701
|
+
const typeRules: Record<string, MatchFn> = {
|
706
702
|
UnionQuery: (args, indent) => {
|
707
703
|
checkMinArgs('UnionQuery', args, 2);
|
708
704
|
return args
|
@@ -1340,31 +1336,28 @@ const typeRules: Dictionary<MatchFn> = {
|
|
1340
1336
|
throw new SyntaxError('Durations not supported on: ' + engine);
|
1341
1337
|
}
|
1342
1338
|
// TODO: The abstract sql type should accommodate this
|
1343
|
-
|
1339
|
+
const duration = args[0] as DurationNode[1];
|
1344
1340
|
if (duration == null || typeof duration !== 'object') {
|
1345
1341
|
throw new SyntaxError(
|
1346
1342
|
`Duration must be an object, got ${typeof duration}`,
|
1347
1343
|
);
|
1348
1344
|
}
|
1349
|
-
|
1350
|
-
|
1351
|
-
.omitBy(_.isNil)
|
1352
|
-
.value() as Dictionary<string>;
|
1353
|
-
if (_(duration).omit('negative').isEmpty()) {
|
1345
|
+
const { negative, day, hour, minute, second } = duration;
|
1346
|
+
if (day == null && hour == null && minute == null && second == null) {
|
1354
1347
|
throw new SyntaxError('Invalid duration');
|
1355
1348
|
}
|
1356
1349
|
return (
|
1357
1350
|
"INTERVAL '" +
|
1358
|
-
(
|
1359
|
-
(
|
1351
|
+
(negative ? '-' : '') +
|
1352
|
+
(day ?? '0') +
|
1360
1353
|
' ' +
|
1361
|
-
(
|
1362
|
-
(
|
1354
|
+
(negative ? '-' : '') +
|
1355
|
+
(hour ?? '0') +
|
1363
1356
|
':' +
|
1364
|
-
(
|
1357
|
+
(minute ?? '0') +
|
1365
1358
|
':' +
|
1366
1359
|
// Force seconds to be at least 0.0 - required for mysql
|
1367
|
-
Number(
|
1360
|
+
Number(second ?? 0).toLocaleString('en', {
|
1368
1361
|
minimumFractionDigits: 1,
|
1369
1362
|
}) +
|
1370
1363
|
"'" +
|
@@ -7,7 +7,6 @@ export const enum Engines {
|
|
7
7
|
import { AbstractSQLOptimiser } from './AbstractSQLOptimiser';
|
8
8
|
export { Binding, SqlResult } from './AbstractSQLRules2SQL';
|
9
9
|
import sbvrTypes from '@balena/sbvr-types';
|
10
|
-
import _ from 'lodash';
|
11
10
|
import type {
|
12
11
|
AbstractSqlModel,
|
13
12
|
AbstractSqlQuery,
|
@@ -80,7 +79,8 @@ export const optimizeSchema = (
|
|
80
79
|
count === 1 &&
|
81
80
|
(ruleBody[0] === 'NotExists' ||
|
82
81
|
(ruleBody[0] === 'Equals' &&
|
83
|
-
|
82
|
+
ruleBody[2][0] === 'Number' &&
|
83
|
+
ruleBody[2][1] === 0)) &&
|
84
84
|
isSelectQueryNode(ruleBody[1])
|
85
85
|
) {
|
86
86
|
const selectQueryNodes = ruleBody[1].slice(1);
|
@@ -120,8 +120,7 @@ export const optimizeSchema = (
|
|
120
120
|
convertReferencedFieldsToFields(whereNode);
|
121
121
|
|
122
122
|
const tableName = fromNode[1];
|
123
|
-
const table =
|
124
|
-
abstractSqlModel.tables,
|
123
|
+
const table = Object.values(abstractSqlModel.tables).find(
|
125
124
|
(t) => t.name === tableName,
|
126
125
|
);
|
127
126
|
if (table) {
|
package/src/referenced-fields.ts
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
import _ from 'lodash';
|
2
1
|
import type {
|
3
2
|
AbstractSqlQuery,
|
4
3
|
AbstractSqlType,
|
@@ -76,7 +75,11 @@ export const getReferencedFields: EngineInstance['getReferencedFields'] = (
|
|
76
75
|
) => {
|
77
76
|
const referencedFields = getRuleReferencedFields(ruleBody);
|
78
77
|
|
79
|
-
|
78
|
+
const result: { [key: string]: string[] } = {};
|
79
|
+
for (const key of Object.keys(referencedFields)) {
|
80
|
+
result[key] = [...new Set(referencedFields[key].update)];
|
81
|
+
}
|
82
|
+
return result;
|
80
83
|
};
|
81
84
|
|
82
85
|
export interface RuleReferencedFields {
|
@@ -267,7 +270,8 @@ export const getRuleReferencedFields: EngineInstance['getRuleReferencedFields']
|
|
267
270
|
const referencedFields: RuleReferencedFields = {};
|
268
271
|
if (
|
269
272
|
ruleBody[0] === 'Equals' &&
|
270
|
-
|
273
|
+
ruleBody[2][0] === 'Number' &&
|
274
|
+
ruleBody[2][1] === 0 &&
|
271
275
|
isSelectQueryNode(ruleBody[1])
|
272
276
|
) {
|
273
277
|
const select = ruleBody[1].find(isSelectNode)!;
|
@@ -281,7 +285,7 @@ export const getRuleReferencedFields: EngineInstance['getRuleReferencedFields']
|
|
281
285
|
for (const method of Object.keys(tableRefs) as Array<
|
282
286
|
keyof typeof tableRefs
|
283
287
|
>) {
|
284
|
-
tableRefs[method] =
|
288
|
+
tableRefs[method] = [...new Set(tableRefs[method])];
|
285
289
|
}
|
286
290
|
}
|
287
291
|
|
@@ -321,10 +325,9 @@ const checkQuery = (query: AbstractSqlQuery): ModifiedFields | undefined => {
|
|
321
325
|
return { table: tableName, action: 'delete' };
|
322
326
|
}
|
323
327
|
|
324
|
-
const fields =
|
328
|
+
const fields = query
|
325
329
|
.filter((v): v is FieldsNode => v != null && v[0] === 'Fields')
|
326
|
-
.flatMap((v) => v[1])
|
327
|
-
.value();
|
330
|
+
.flatMap((v) => v[1]);
|
328
331
|
return { table: tableName, action: 'update', fields };
|
329
332
|
};
|
330
333
|
export const getModifiedFields: EngineInstance['getModifiedFields'] = (
|