@balena/abstract-sql-compiler 10.2.7 → 10.2.8-build-lodash-08b051cf4150dfa99f3cfafeb3011244760e9d38-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.
@@ -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,
@@ -107,6 +104,7 @@ import type {
107
104
  StartsWithNode,
108
105
  EscapeForLikeNode,
109
106
  EqualsAnyNode,
107
+ NotInNode,
110
108
  } from './AbstractSQLCompiler';
111
109
  import { isFieldTypeNode } from './AbstractSQLCompiler';
112
110
  import * as AbstractSQLRules2SQL from './AbstractSQLRules2SQL';
@@ -125,6 +123,8 @@ type OptimisationMatchFn<T extends AnyTypeNodes> =
125
123
  type MetaMatchFn<T extends AnyTypeNodes> = (args: AbstractSqlQuery) => T;
126
124
  type MatchFn<T extends AnyTypeNodes> = (args: AbstractSqlType[]) => T;
127
125
 
126
+ const identity = <T>(x: T): T => x;
127
+
128
128
  let helped = false;
129
129
  let noBinds = false;
130
130
  const Helper = <F extends (...args: any[]) => any>(fn: F) => {
@@ -328,9 +328,8 @@ const AnyNotNullValue = (args: any): boolean => {
328
328
  return args != null && args !== 'Null' && args[0] !== 'Null';
329
329
  };
330
330
 
331
- const FieldOp =
332
- (type: string): OptimisationMatchFn<AnyTypeNodes> =>
333
- (args) => {
331
+ const FieldOp = <T extends string>(type: T) =>
332
+ ((args) => {
334
333
  if (
335
334
  AnyNotNullValue(args[0]) === false ||
336
335
  AnyNotNullValue(args[1]) === false
@@ -344,7 +343,7 @@ const FieldOp =
344
343
  } else {
345
344
  return false;
346
345
  }
347
- };
346
+ }) satisfies OptimisationMatchFn<AnyTypeNodes>;
348
347
  const FieldEquals = FieldOp('Equals');
349
348
  const FieldNotEquals = FieldOp('NotEquals');
350
349
 
@@ -420,7 +419,7 @@ const ConcatenateWithSeparator: MatchFn<ConcatenateWithSeparatorNode> = (
420
419
  ];
421
420
  };
422
421
 
423
- const Text = matchArgs<TextNode>('Text', _.identity);
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', _.identity),
709
+ Field: matchArgs<FieldNode>('Field', identity),
711
710
  ReferencedField: matchArgs<ReferencedFieldNode>(
712
711
  'ReferencedField',
713
- _.identity,
714
- _.identity,
712
+ identity,
713
+ identity,
715
714
  ),
716
- Cast: matchArgs<CastNode>('Cast', AnyValue, _.identity),
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', _.identity),
723
- EmbeddedText: matchArgs('EmbeddedText', _.identity),
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, _.identity),
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 = _.flatMap(args, (arg) => {
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}`,
@@ -1002,7 +1001,17 @@ const typeRules = {
1002
1001
  Helper<OptimisationMatchFn<AndNode>>((args) => {
1003
1002
  checkMinArgs('And', args, 2);
1004
1003
  // Optimise id != 1 AND id != 2 AND id != 3 -> id NOT IN [1, 2, 3]
1005
- const fieldBuckets: Dictionary<AnyTypeNodes[]> = {};
1004
+ const fieldBuckets: Record<
1005
+ string,
1006
+ Array<
1007
+ [
1008
+ 'NotEquals' | 'NotIn',
1009
+ FieldNode | ReferencedFieldNode,
1010
+ AbstractSqlType,
1011
+ ...AbstractSqlType[],
1012
+ ]
1013
+ >
1014
+ > = {};
1006
1015
  const others: AnyTypeNodes[] = [];
1007
1016
  let maybeHelped = false;
1008
1017
  args.map((arg) => {
@@ -1014,7 +1023,7 @@ const typeRules = {
1014
1023
  if (arg[0] === 'NotEquals') {
1015
1024
  const fieldBool = FieldNotEquals(arg.slice(1));
1016
1025
  if (fieldBool !== false) {
1017
- const fieldRef = fieldBool[1] as string;
1026
+ const fieldRef = `${fieldBool[1]}`;
1018
1027
  if (fieldBuckets[fieldRef] == null) {
1019
1028
  fieldBuckets[fieldRef] = [fieldBool];
1020
1029
  } else {
@@ -1025,13 +1034,13 @@ const typeRules = {
1025
1034
  return;
1026
1035
  }
1027
1036
  } else if (arg[0] === 'NotIn') {
1028
- const fieldRef = arg[1] as string;
1037
+ const fieldRef = `${arg[1]}`;
1029
1038
  if (fieldBuckets[fieldRef] == null) {
1030
- fieldBuckets[fieldRef] = [arg];
1039
+ fieldBuckets[fieldRef] = [arg as NotInNode];
1031
1040
  } else {
1032
1041
  // We're adding a second match, so that means we can optimise
1033
1042
  maybeHelped = true;
1034
- fieldBuckets[fieldRef].push(arg);
1043
+ fieldBuckets[fieldRef].push(arg as NotInNode);
1035
1044
  }
1036
1045
  return;
1037
1046
  }
@@ -1049,7 +1058,7 @@ const typeRules = {
1049
1058
  return [
1050
1059
  'NotIn',
1051
1060
  fieldBucket[0][1],
1052
- ..._.flatMap(fieldBucket, (field) => field.slice(2)),
1061
+ ...fieldBucket.flatMap((field) => field.slice(2)),
1053
1062
  ];
1054
1063
  }
1055
1064
  });
@@ -1081,7 +1090,7 @@ const typeRules = {
1081
1090
  checkMinArgs('Or', args, 2);
1082
1091
  // Collapse nested ORs.
1083
1092
  let maybeHelped = false;
1084
- const conditions = _.flatMap(args, (arg) => {
1093
+ const conditions = args.flatMap((arg) => {
1085
1094
  if (!isAbstractSqlQuery(arg)) {
1086
1095
  throw new SyntaxError(
1087
1096
  `Expected AbstractSqlQuery array but got ${typeof arg}`,
@@ -1127,7 +1136,17 @@ const typeRules = {
1127
1136
  Helper<OptimisationMatchFn<InNode | OrNode>>((args) => {
1128
1137
  checkMinArgs('Or', args, 2);
1129
1138
  // Optimise id = 1 OR id = 2 OR id = 3 -> id IN [1, 2, 3]
1130
- const fieldBuckets: Dictionary<AnyTypeNodes[]> = {};
1139
+ const fieldBuckets: Record<
1140
+ string,
1141
+ Array<
1142
+ [
1143
+ 'Equals' | 'In',
1144
+ FieldNode | ReferencedFieldNode,
1145
+ AbstractSqlType,
1146
+ ...AbstractSqlType[],
1147
+ ]
1148
+ >
1149
+ > = {};
1131
1150
  const others: AnyTypeNodes[] = [];
1132
1151
  let maybeHelped = false;
1133
1152
  args.map((arg) => {
@@ -1139,7 +1158,7 @@ const typeRules = {
1139
1158
  if (arg[0] === 'Equals') {
1140
1159
  const fieldBool = FieldEquals(arg.slice(1));
1141
1160
  if (fieldBool !== false) {
1142
- const fieldRef = fieldBool[1] as string;
1161
+ const fieldRef = `${fieldBool[1]}`;
1143
1162
  if (fieldBuckets[fieldRef] == null) {
1144
1163
  fieldBuckets[fieldRef] = [fieldBool];
1145
1164
  } else {
@@ -1150,13 +1169,13 @@ const typeRules = {
1150
1169
  return;
1151
1170
  }
1152
1171
  } else if (arg[0] === 'In') {
1153
- const fieldRef = arg[1] as string;
1172
+ const fieldRef = `${arg[1]}`;
1154
1173
  if (fieldBuckets[fieldRef] == null) {
1155
- fieldBuckets[fieldRef] = [arg];
1174
+ fieldBuckets[fieldRef] = [arg as InNode];
1156
1175
  } else {
1157
1176
  // We're adding a second match, so that means we can optimise
1158
1177
  maybeHelped = true;
1159
- fieldBuckets[fieldRef].push(arg);
1178
+ fieldBuckets[fieldRef].push(arg as InNode);
1160
1179
  }
1161
1180
  return;
1162
1181
  }
@@ -1174,7 +1193,7 @@ const typeRules = {
1174
1193
  return [
1175
1194
  'In',
1176
1195
  fieldBucket[0][1],
1177
- ..._.flatMap(fieldBucket, (field) => field.slice(2)),
1196
+ ...fieldBucket.flatMap((field) => field.slice(2)),
1178
1197
  ];
1179
1198
  }
1180
1199
  });
@@ -1212,24 +1231,21 @@ const typeRules = {
1212
1231
  ),
1213
1232
  Text,
1214
1233
  Value: Text,
1215
- Date: matchArgs('Date', _.identity),
1234
+ Date: matchArgs('Date', identity),
1216
1235
  Duration: (args): DurationNode => {
1217
1236
  checkArgs('Duration', args, 1);
1218
1237
 
1219
- let duration = args[0] as DurationNode[1];
1238
+ const duration = args[0] as DurationNode[1];
1220
1239
  if (duration == null || typeof duration !== 'object') {
1221
1240
  throw new SyntaxError(
1222
1241
  `Duration must be an object, got ${typeof duration}`,
1223
1242
  );
1224
1243
  }
1225
- duration = _(duration)
1226
- .pick('negative', 'day', 'hour', 'minute', 'second')
1227
- .omitBy(_.isNil)
1228
- .value();
1229
- if (_(duration).omit('negative').isEmpty()) {
1244
+ const { negative, day, hour, minute, second } = duration;
1245
+ if (day == null && hour == null && minute == null && second == null) {
1230
1246
  throw new SyntaxError('Invalid duration');
1231
1247
  }
1232
- return ['Duration', duration];
1248
+ return ['Duration', { negative, day, hour, minute, second }];
1233
1249
  },
1234
1250
  Exists: tryMatches<ExistsNode | BooleanNode>(
1235
1251
  Helper<OptimisationMatchFn<BooleanNode>>((args) => {
@@ -1582,7 +1598,7 @@ const typeRules = {
1582
1598
  ],
1583
1599
  ),
1584
1600
  ),
1585
- } satisfies Dictionary<MatchFn<AnyTypeNodes>>;
1601
+ } satisfies Record<string, MatchFn<AnyTypeNodes>>;
1586
1602
 
1587
1603
  export const AbstractSQLOptimiser = (
1588
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: Dictionary<number> = {};
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: Dictionary<MatchFn> = {
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
- let duration = args[0] as DurationNode[1];
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
- duration = _(duration)
1350
- .pick('negative', 'day', 'hour', 'minute', 'second')
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
- (duration.negative ? '-' : '') +
1359
- (duration.day ?? '0') +
1351
+ (negative ? '-' : '') +
1352
+ (day ?? '0') +
1360
1353
  ' ' +
1361
- (duration.negative ? '-' : '') +
1362
- (duration.hour ?? '0') +
1354
+ (negative ? '-' : '') +
1355
+ (hour ?? '0') +
1363
1356
  ':' +
1364
- (duration.minute ?? '0') +
1357
+ (minute ?? '0') +
1365
1358
  ':' +
1366
1359
  // Force seconds to be at least 0.0 - required for mysql
1367
- Number(duration.second ?? 0).toLocaleString('en', {
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
- _.isEqual(ruleBody[2], ['Number', 0]))) &&
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 = _.find(
124
- abstractSqlModel.tables,
123
+ const table = Object.values(abstractSqlModel.tables).find(
125
124
  (t) => t.name === tableName,
126
125
  );
127
126
  if (table) {
@@ -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
- return _.mapValues(referencedFields, ({ update }) => _.uniq(update));
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
- _.isEqual(ruleBody[2], ['Number', 0]) &&
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] = _.uniq(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 = _<FieldsNode | AbstractSqlType>(query)
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'] = (