@balena/odata-to-abstract-sql 8.0.2 → 9.0.0
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/out/odata-to-abstract-sql.d.ts +3 -4
- package/out/odata-to-abstract-sql.js +71 -120
- package/out/odata-to-abstract-sql.js.map +1 -1
- package/package.json +13 -8
- package/.eslintrc.cjs +0 -10
- package/.github/workflows/flowzone.yml +0 -21
- package/.husky/pre-commit +0 -2
- package/.lintstagedrc +0 -5
- package/.versionbot/CHANGELOG.yml +0 -13398
- package/CHANGELOG.md +0 -4415
- package/repo.yml +0 -12
- package/src/odata-to-abstract-sql.ts +0 -2117
- package/test/chai-sql-types.d.ts +0 -25
- package/test/chai-sql.ts +0 -454
- package/test/expand.ts +0 -606
- package/test/filterby.ts +0 -2164
- package/test/model.sbvr +0 -79
- package/test/orderby.ts +0 -544
- package/test/paging.ts +0 -75
- package/test/resource_parsing.ts +0 -773
- package/test/select.ts +0 -418
- package/test/stress.ts +0 -14
- package/test/test.ts +0 -106
- package/tsconfig.js.json +0 -13
- package/tsconfig.json +0 -18
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import memoize from 'memoizee';
|
|
2
|
-
import type { AbstractSqlQuery, AbstractSqlModel, AbstractSqlTable, DurationNode, SelectNode, FromNode, WhereNode, OrderByNode, LimitNode, OffsetNode, NumberTypeNodes, FieldsNode, ValuesNode, ReferencedFieldNode, AliasNode, BooleanTypeNodes, SelectQueryNode, BindNode,
|
|
2
|
+
import type { AbstractSqlQuery, AbstractSqlModel, AbstractSqlTable, DurationNode, SelectNode, FromNode, WhereNode, OrderByNode, LimitNode, OffsetNode, NumberTypeNodes, FieldsNode, ValuesNode, ReferencedFieldNode, AliasNode, BooleanTypeNodes, SelectQueryNode, BindNode, TableNode, Definition as ModernDefinition, ResourceNode, UnionQueryNode, FromTypeNodes, FieldNode, CountNode, AddNode, InsertQueryNode, DeleteQueryNode, UpdateQueryNode, DurationTypeNodes, SubtractNode, MultiplyNode, DivideNode, NullNode, TextTypeNodes, AnyTypeNodes, StrictDateTypeNodes, JoinTypeNodes } from '@balena/abstract-sql-compiler';
|
|
3
3
|
import type { ODataBinds, ODataQuery, SupportedMethod, ExpandPropertyPath, ResourceOptions, OrderByOption, OrderByPropertyPath, FilterOption, BindReference, PropertyPath, MethodCall } from '@balena/odata-parser';
|
|
4
4
|
export type { ODataBinds, ODataQuery, SupportedMethod };
|
|
5
5
|
type InternalSupportedMethod = Exclude<SupportedMethod, 'MERGE'> | 'PUT-INSERT';
|
|
@@ -90,7 +90,7 @@ export declare class OData2AbstractSQL {
|
|
|
90
90
|
ResolveRelationship(resource: string | Resource, relationship: string): any;
|
|
91
91
|
AddCountField(path: any, query: Query): void;
|
|
92
92
|
AddSelectFields(path: any, query: Query, resource: Resource): void;
|
|
93
|
-
AliasSelectField(resource: Resource, fieldName: string
|
|
93
|
+
AliasSelectField(resource: Resource, fieldName: string): ReferencedFieldNode | AliasNode<ReferencedFieldNode>;
|
|
94
94
|
ReferencedField(resource: Resource, resourceField: string): ReferencedFieldNode;
|
|
95
95
|
BooleanMatch(match: true | false | [string, ...any[]], optional: true): BooleanTypeNodes | undefined;
|
|
96
96
|
BooleanMatch(match: true | false | [string, ...any[]]): BooleanTypeNodes;
|
|
@@ -136,6 +136,5 @@ export declare class OData2AbstractSQL {
|
|
|
136
136
|
Synonym(sqlName: string): string;
|
|
137
137
|
getTableReference(resource: Resource, extraBindVars: ODataBinds, bindVarsLength: number, bypassDefinition?: boolean, tableAlias?: string, isModifyOperation?: boolean): FromTypeNodes;
|
|
138
138
|
rewriteDefinition(definition: Definition, extraBindVars: ODataBinds, bindVarsLength: number): ModernDefinition;
|
|
139
|
-
|
|
140
|
-
rewriteResourceAsTable(abstractSql: AbstractSqlQuery): void;
|
|
139
|
+
rewriteResourceAsTable(abstractSql: AnyTypeNodes): void;
|
|
141
140
|
}
|
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.OData2AbstractSQL = exports.isBindReference = exports.rewriteBinds = exports.odataNameToSqlName = exports.sqlNameToODataName = void 0;
|
|
7
|
-
const lodash_1 = __importDefault(require("lodash"));
|
|
8
|
-
const memoizee_1 = __importDefault(require("memoizee"));
|
|
9
|
-
const string_hash_1 = __importDefault(require("string-hash"));
|
|
10
|
-
const abstract_sql_compiler_1 = require("@balena/abstract-sql-compiler");
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import memoize from 'memoizee';
|
|
3
|
+
import stringHash from 'string-hash';
|
|
4
|
+
import { isAliasNode, isFromNode, isSelectNode, isSelectQueryNode, isTableNode, } from '@balena/abstract-sql-compiler';
|
|
11
5
|
class KeySyntaxError extends SyntaxError {
|
|
12
6
|
}
|
|
13
7
|
const convertToModernDefinition = (definition) => {
|
|
@@ -48,14 +42,14 @@ const containsQueryOption = (opts) => {
|
|
|
48
42
|
const addNestedFieldSelect = (selectNode, fromNode, fieldName, fieldNameAlias) => {
|
|
49
43
|
let aliasName;
|
|
50
44
|
let tableOrSubqueryNode;
|
|
51
|
-
if (
|
|
45
|
+
if (isAliasNode(fromNode)) {
|
|
52
46
|
tableOrSubqueryNode = fromNode[1];
|
|
53
47
|
aliasName = fromNode[2];
|
|
54
48
|
}
|
|
55
49
|
else {
|
|
56
50
|
tableOrSubqueryNode = fromNode;
|
|
57
51
|
}
|
|
58
|
-
if (
|
|
52
|
+
if (isTableNode(tableOrSubqueryNode)) {
|
|
59
53
|
selectNode.push([
|
|
60
54
|
'Alias',
|
|
61
55
|
['ReferencedField', aliasName ?? tableOrSubqueryNode[1], fieldName],
|
|
@@ -63,17 +57,17 @@ const addNestedFieldSelect = (selectNode, fromNode, fieldName, fieldNameAlias) =
|
|
|
63
57
|
]);
|
|
64
58
|
return;
|
|
65
59
|
}
|
|
66
|
-
if (!
|
|
60
|
+
if (!isSelectQueryNode(tableOrSubqueryNode)) {
|
|
67
61
|
throw new Error(`Adding a nested field select to a subquery containing a ${tableOrSubqueryNode[0]} is not supported`);
|
|
68
62
|
}
|
|
69
63
|
if (aliasName == null) {
|
|
70
64
|
throw new Error('Found unaliased SelectQueryNode');
|
|
71
65
|
}
|
|
72
|
-
const nestedSelectNode = tableOrSubqueryNode.find(
|
|
66
|
+
const nestedSelectNode = tableOrSubqueryNode.find(isSelectNode);
|
|
73
67
|
if (nestedSelectNode == null) {
|
|
74
68
|
throw new Error(`Cannot find SelectNode in subquery`);
|
|
75
69
|
}
|
|
76
|
-
const nestedFromNode = tableOrSubqueryNode.find(
|
|
70
|
+
const nestedFromNode = tableOrSubqueryNode.find(isFromNode);
|
|
77
71
|
if (nestedFromNode == null) {
|
|
78
72
|
throw new Error(`Cannot find FromNode in subquery`);
|
|
79
73
|
}
|
|
@@ -128,8 +122,8 @@ class Query {
|
|
|
128
122
|
return [queryType, ...compiled, ...this.extras];
|
|
129
123
|
}
|
|
130
124
|
}
|
|
131
|
-
|
|
132
|
-
|
|
125
|
+
export const sqlNameToODataName = memoize((sqlName) => sqlName.replace(/-/g, '__').replace(/ /g, '_'), { primitive: true });
|
|
126
|
+
export const odataNameToSqlName = memoize((odataName) => odataName.replace(/__/g, '-').replace(/_/g, ' '), { primitive: true });
|
|
133
127
|
const modifyAbstractSql = (match, abstractSql, fn) => {
|
|
134
128
|
if (Array.isArray(abstractSql)) {
|
|
135
129
|
if (abstractSql[0] === match) {
|
|
@@ -142,7 +136,7 @@ const modifyAbstractSql = (match, abstractSql, fn) => {
|
|
|
142
136
|
}
|
|
143
137
|
}
|
|
144
138
|
};
|
|
145
|
-
const rewriteBinds = (definition, existingBinds, inc = 0) => {
|
|
139
|
+
export const rewriteBinds = (definition, existingBinds, inc = 0) => {
|
|
146
140
|
const { binds } = definition;
|
|
147
141
|
if (binds == null || binds.length === 0) {
|
|
148
142
|
return;
|
|
@@ -155,17 +149,14 @@ const rewriteBinds = (definition, existingBinds, inc = 0) => {
|
|
|
155
149
|
});
|
|
156
150
|
existingBinds.push(...binds);
|
|
157
151
|
};
|
|
158
|
-
|
|
159
|
-
const isBindReference = (maybeBind) => {
|
|
152
|
+
export const isBindReference = (maybeBind) => {
|
|
160
153
|
return (maybeBind != null &&
|
|
161
154
|
typeof maybeBind === 'object' &&
|
|
162
155
|
'bind' in maybeBind &&
|
|
163
156
|
(typeof maybeBind.bind === 'string' || typeof maybeBind.bind === 'number'));
|
|
164
157
|
};
|
|
165
|
-
exports.isBindReference = isBindReference;
|
|
166
158
|
const isDynamicResource = (resource) => {
|
|
167
|
-
return
|
|
168
|
-
resource.fields.some((f) => f.computed != null));
|
|
159
|
+
return resource.definition != null;
|
|
169
160
|
};
|
|
170
161
|
const addBodyKey = (resourceName, fieldName, bind, bodyKeys, extraBodyVars) => {
|
|
171
162
|
const qualifiedIDField = resourceName + '.' + fieldName;
|
|
@@ -174,7 +165,7 @@ const addBodyKey = (resourceName, fieldName, bind, bodyKeys, extraBodyVars) => {
|
|
|
174
165
|
extraBodyVars[qualifiedIDField] = bind;
|
|
175
166
|
}
|
|
176
167
|
};
|
|
177
|
-
class OData2AbstractSQL {
|
|
168
|
+
export class OData2AbstractSQL {
|
|
178
169
|
clientModel;
|
|
179
170
|
methods;
|
|
180
171
|
extraBodyVars = {};
|
|
@@ -188,25 +179,25 @@ class OData2AbstractSQL {
|
|
|
188
179
|
this.methods = methods;
|
|
189
180
|
const MAX_ALIAS_LENGTH = 63;
|
|
190
181
|
const shortAliases = generateShortAliases(clientModel);
|
|
191
|
-
this.checkAlias = (
|
|
182
|
+
this.checkAlias = memoize((alias) => {
|
|
192
183
|
let aliasLength = alias.length;
|
|
193
184
|
if (minimizeAliases === false && aliasLength <= MAX_ALIAS_LENGTH) {
|
|
194
185
|
return alias;
|
|
195
186
|
}
|
|
196
|
-
alias = (
|
|
187
|
+
alias = _(alias.toLowerCase())
|
|
197
188
|
.split('.')
|
|
198
189
|
.map((part) => {
|
|
199
190
|
if (minimizeAliases === false && aliasLength <= MAX_ALIAS_LENGTH) {
|
|
200
191
|
return part;
|
|
201
192
|
}
|
|
202
193
|
aliasLength -= part.length;
|
|
203
|
-
const shortAlias = (
|
|
194
|
+
const shortAlias = _(part)
|
|
204
195
|
.split('-')
|
|
205
196
|
.map((part2) => {
|
|
206
|
-
part2 = (
|
|
197
|
+
part2 = _(part2)
|
|
207
198
|
.split(' ')
|
|
208
199
|
.map((part3) => {
|
|
209
|
-
part3 = (
|
|
200
|
+
part3 = _(part3)
|
|
210
201
|
.split('$')
|
|
211
202
|
.map((part4) => shortAliases[part4] ?? part4)
|
|
212
203
|
.join('$');
|
|
@@ -226,7 +217,7 @@ class OData2AbstractSQL {
|
|
|
226
217
|
if (aliasLength <= MAX_ALIAS_LENGTH) {
|
|
227
218
|
return alias;
|
|
228
219
|
}
|
|
229
|
-
const hashStr = (
|
|
220
|
+
const hashStr = stringHash(alias).toString(36) + '$';
|
|
230
221
|
return (hashStr +
|
|
231
222
|
alias.slice(hashStr.length + alias.length - MAX_ALIAS_LENGTH));
|
|
232
223
|
}, {
|
|
@@ -243,7 +234,7 @@ class OData2AbstractSQL {
|
|
|
243
234
|
this.reset();
|
|
244
235
|
this.bindVarsLength = bindVarsLength;
|
|
245
236
|
let tree;
|
|
246
|
-
if (
|
|
237
|
+
if (_.isEmpty(path)) {
|
|
247
238
|
tree = ['$serviceroot'];
|
|
248
239
|
}
|
|
249
240
|
else if (['$metadata', '$serviceroot'].includes(path.resource)) {
|
|
@@ -337,7 +328,7 @@ class OData2AbstractSQL {
|
|
|
337
328
|
throw new Error('Cannot navigate links');
|
|
338
329
|
}
|
|
339
330
|
if (path.link.key != null) {
|
|
340
|
-
if (
|
|
331
|
+
if (isBindReference(path.link.key)) {
|
|
341
332
|
query.where.push([
|
|
342
333
|
comparison.eq,
|
|
343
334
|
referencedField,
|
|
@@ -374,9 +365,7 @@ class OData2AbstractSQL {
|
|
|
374
365
|
'SelectQuery',
|
|
375
366
|
[
|
|
376
367
|
'Select',
|
|
377
|
-
(resource.modifyFields ?? resource.fields)
|
|
378
|
-
.filter((field) => field.computed == null)
|
|
379
|
-
.map((field) => {
|
|
368
|
+
(resource.modifyFields ?? resource.fields).map((field) => {
|
|
380
369
|
const alias = field.fieldName;
|
|
381
370
|
const bindVar = bindVars?.find((v) => v[0] === alias);
|
|
382
371
|
const value = bindVar?.[1] ?? ['Null'];
|
|
@@ -411,19 +400,19 @@ class OData2AbstractSQL {
|
|
|
411
400
|
throw new Error('Only SelectQuery or Table definitions supported for inserts');
|
|
412
401
|
}
|
|
413
402
|
const tableName = unionResource.modifyName ?? unionResource.name;
|
|
414
|
-
const isTableBeingModified = (part) =>
|
|
403
|
+
const isTableBeingModified = (part) => isTableNode(part) && part[1] === tableName;
|
|
415
404
|
if (isTableBeingModified(definition.abstractSql)) {
|
|
416
405
|
definition.abstractSql = bindVarSelectQuery;
|
|
417
406
|
}
|
|
418
407
|
else {
|
|
419
408
|
let found = false;
|
|
420
409
|
const replaceInsertTableNodeWithBinds = (part) => {
|
|
421
|
-
if (
|
|
410
|
+
if (isFromNode(part)) {
|
|
422
411
|
if (isTableBeingModified(part[1])) {
|
|
423
412
|
found = true;
|
|
424
413
|
return ['From', ['Alias', bindVarSelectQuery, tableName]];
|
|
425
414
|
}
|
|
426
|
-
else if (
|
|
415
|
+
else if (isAliasNode(part[1])) {
|
|
427
416
|
const [, aliasedNode, alias] = part[1];
|
|
428
417
|
if (isTableBeingModified(aliasedNode)) {
|
|
429
418
|
found = true;
|
|
@@ -497,7 +486,7 @@ class OData2AbstractSQL {
|
|
|
497
486
|
const { key } = path;
|
|
498
487
|
if (key != null) {
|
|
499
488
|
if (method === 'PUT' || method === 'PUT-INSERT' || method === 'POST') {
|
|
500
|
-
if (
|
|
489
|
+
if (isBindReference(key)) {
|
|
501
490
|
addBodyKey(resource.resourceName, resource.idField, key, bodyKeys, this.extraBodyVars);
|
|
502
491
|
}
|
|
503
492
|
else {
|
|
@@ -510,7 +499,7 @@ class OData2AbstractSQL {
|
|
|
510
499
|
}
|
|
511
500
|
}
|
|
512
501
|
BaseKey(resource, key, followedForeignKey) {
|
|
513
|
-
if (
|
|
502
|
+
if (isBindReference(key)) {
|
|
514
503
|
if (followedForeignKey != null) {
|
|
515
504
|
throw new KeySyntaxError('Using a key bind after a navigation expression is not supported.');
|
|
516
505
|
}
|
|
@@ -523,7 +512,7 @@ class OData2AbstractSQL {
|
|
|
523
512
|
return [comparison.eq, referencedField, bind];
|
|
524
513
|
}
|
|
525
514
|
const fieldNames = Object.keys(key);
|
|
526
|
-
const sqlFieldNames = fieldNames.map(
|
|
515
|
+
const sqlFieldNames = fieldNames.map(odataNameToSqlName);
|
|
527
516
|
if (followedForeignKey != null) {
|
|
528
517
|
if (sqlFieldNames.includes(followedForeignKey)) {
|
|
529
518
|
throw new SyntaxError(`Specified already navigated field as part of key: ${followedForeignKey}`);
|
|
@@ -544,7 +533,7 @@ class OData2AbstractSQL {
|
|
|
544
533
|
return (((index.type === 'UNIQUE' && index.predicate == null) ||
|
|
545
534
|
index.type === 'PRIMARY KEY') &&
|
|
546
535
|
sqlFieldNames.length === index.fields.length &&
|
|
547
|
-
|
|
536
|
+
_.isEqual(index.fields.slice().sort(), sqlFieldNames));
|
|
548
537
|
})) {
|
|
549
538
|
throw new KeySyntaxError('Specified fields for path key that are not directly unique');
|
|
550
539
|
}
|
|
@@ -559,7 +548,7 @@ class OData2AbstractSQL {
|
|
|
559
548
|
return ['And', ...namedKeys];
|
|
560
549
|
}
|
|
561
550
|
Bind(bind, optional = false) {
|
|
562
|
-
if (
|
|
551
|
+
if (isBindReference(bind)) {
|
|
563
552
|
return ['Bind', bind.bind];
|
|
564
553
|
}
|
|
565
554
|
if (optional) {
|
|
@@ -613,7 +602,7 @@ class OData2AbstractSQL {
|
|
|
613
602
|
resource = this.clientModel.tables[relationshipMapping[1][0]];
|
|
614
603
|
}
|
|
615
604
|
else {
|
|
616
|
-
let sqlName =
|
|
605
|
+
let sqlName = odataNameToSqlName(resourceName);
|
|
617
606
|
sqlName = this.Synonym(sqlName);
|
|
618
607
|
resource = this.clientModel.tables[sqlName];
|
|
619
608
|
}
|
|
@@ -624,7 +613,7 @@ class OData2AbstractSQL {
|
|
|
624
613
|
if (parentResource) {
|
|
625
614
|
let resourceAlias2;
|
|
626
615
|
if (resourceName.includes('__') && !resource.name.includes('-')) {
|
|
627
|
-
const verb =
|
|
616
|
+
const verb = odataNameToSqlName(resourceName).split('-')[0];
|
|
628
617
|
resourceAlias2 = verb + '-' + resource.name;
|
|
629
618
|
}
|
|
630
619
|
else {
|
|
@@ -663,7 +652,7 @@ class OData2AbstractSQL {
|
|
|
663
652
|
? resource.modifyFields
|
|
664
653
|
: resource.fields;
|
|
665
654
|
for (const { fieldName } of fields) {
|
|
666
|
-
resourceMappings[
|
|
655
|
+
resourceMappings[sqlNameToODataName(fieldName)] = [
|
|
667
656
|
tableAlias,
|
|
668
657
|
fieldName,
|
|
669
658
|
];
|
|
@@ -688,12 +677,12 @@ class OData2AbstractSQL {
|
|
|
688
677
|
if (!resourceRelations) {
|
|
689
678
|
throw new SyntaxError(`Could not resolve relationship for '${resourceName}'`);
|
|
690
679
|
}
|
|
691
|
-
const relationshipPath = (
|
|
680
|
+
const relationshipPath = _(relationship)
|
|
692
681
|
.split('__')
|
|
693
|
-
.map(
|
|
682
|
+
.map(odataNameToSqlName)
|
|
694
683
|
.flatMap((sqlName) => this.Synonym(sqlName).split('-'))
|
|
695
684
|
.value();
|
|
696
|
-
const relationshipMapping =
|
|
685
|
+
const relationshipMapping = _.get(resourceRelations, relationshipPath);
|
|
697
686
|
if (!relationshipMapping?.$) {
|
|
698
687
|
throw new SyntaxError(`Could not resolve relationship mapping from '${resourceName}' to '${relationshipPath}'`);
|
|
699
688
|
}
|
|
@@ -710,31 +699,24 @@ class OData2AbstractSQL {
|
|
|
710
699
|
this.AddJoins(query, resource, path.options.$select.properties);
|
|
711
700
|
odataFieldNames = path.options.$select.properties.map((prop) => {
|
|
712
701
|
const field = this.Property(prop);
|
|
713
|
-
|
|
714
|
-
const resourceField = field.resource.fields.find(({ fieldName }) => fieldName === sqlName);
|
|
715
|
-
return [field.resource, field.name, resourceField?.computed];
|
|
702
|
+
return [field.resource, field.name];
|
|
716
703
|
});
|
|
717
704
|
}
|
|
718
705
|
else {
|
|
719
706
|
odataFieldNames = resource.fields.map((field) => [
|
|
720
707
|
resource,
|
|
721
|
-
|
|
722
|
-
field.computed,
|
|
708
|
+
sqlNameToODataName(field.fieldName),
|
|
723
709
|
]);
|
|
724
710
|
}
|
|
725
|
-
const fields =
|
|
711
|
+
const fields = _.differenceWith(odataFieldNames, query.select, (a, b) => a[1] === _.last(b)).map((args) => this.AliasSelectField(...args));
|
|
726
712
|
query.select = query.select.concat(fields);
|
|
727
713
|
}
|
|
728
|
-
AliasSelectField(resource, fieldName
|
|
729
|
-
if (computed) {
|
|
730
|
-
computed = this.rewriteComputed(computed, resource.name, resource.tableAlias);
|
|
731
|
-
return ['Alias', computed, alias];
|
|
732
|
-
}
|
|
714
|
+
AliasSelectField(resource, fieldName) {
|
|
733
715
|
const referencedField = this.ReferencedField(resource, fieldName);
|
|
734
|
-
if (referencedField[2] ===
|
|
716
|
+
if (referencedField[2] === fieldName) {
|
|
735
717
|
return referencedField;
|
|
736
718
|
}
|
|
737
|
-
return ['Alias', referencedField,
|
|
719
|
+
return ['Alias', referencedField, fieldName];
|
|
738
720
|
}
|
|
739
721
|
ReferencedField(resource, resourceField) {
|
|
740
722
|
const mapping = this.ResourceMapping(resource);
|
|
@@ -780,7 +762,7 @@ class OData2AbstractSQL {
|
|
|
780
762
|
case 'and':
|
|
781
763
|
case 'or':
|
|
782
764
|
return [
|
|
783
|
-
|
|
765
|
+
_.capitalize(type),
|
|
784
766
|
...rest.map((v) => this.BooleanMatch(v)),
|
|
785
767
|
];
|
|
786
768
|
case 'not': {
|
|
@@ -817,7 +799,7 @@ class OData2AbstractSQL {
|
|
|
817
799
|
throw new SyntaxError(`Boolean does not support ${type}`);
|
|
818
800
|
}
|
|
819
801
|
}
|
|
820
|
-
else if (
|
|
802
|
+
else if (isBindReference(match)) {
|
|
821
803
|
return this.Bind(match);
|
|
822
804
|
}
|
|
823
805
|
else {
|
|
@@ -842,7 +824,7 @@ class OData2AbstractSQL {
|
|
|
842
824
|
throw new SyntaxError('Unexpected function name');
|
|
843
825
|
}
|
|
844
826
|
const args = properties.args.map((v) => this.Operand(v));
|
|
845
|
-
return [sqlName ??
|
|
827
|
+
return [sqlName ?? _.capitalize(name), ...args];
|
|
846
828
|
}
|
|
847
829
|
Operand(match) {
|
|
848
830
|
for (const matcher of [
|
|
@@ -1021,7 +1003,7 @@ class OData2AbstractSQL {
|
|
|
1021
1003
|
throw new SyntaxError(`${method} is not a number function`);
|
|
1022
1004
|
}
|
|
1023
1005
|
}
|
|
1024
|
-
else if (
|
|
1006
|
+
else if (isBindReference(match)) {
|
|
1025
1007
|
return this.Bind(match);
|
|
1026
1008
|
}
|
|
1027
1009
|
else if (optional) {
|
|
@@ -1072,7 +1054,7 @@ class OData2AbstractSQL {
|
|
|
1072
1054
|
}
|
|
1073
1055
|
}
|
|
1074
1056
|
DateMatch(match, optional = false) {
|
|
1075
|
-
if (
|
|
1057
|
+
if (_.isDate(match)) {
|
|
1076
1058
|
return ['Date', match];
|
|
1077
1059
|
}
|
|
1078
1060
|
else if (Array.isArray(match) && match[0] === 'call') {
|
|
@@ -1105,11 +1087,11 @@ class OData2AbstractSQL {
|
|
|
1105
1087
|
if (match == null || typeof match !== 'object') {
|
|
1106
1088
|
return;
|
|
1107
1089
|
}
|
|
1108
|
-
const duration = (
|
|
1090
|
+
const duration = _(match)
|
|
1109
1091
|
.pick('negative', 'day', 'hour', 'minute', 'second')
|
|
1110
|
-
.omitBy(
|
|
1092
|
+
.omitBy(_.isNil)
|
|
1111
1093
|
.value();
|
|
1112
|
-
if ((
|
|
1094
|
+
if (_(duration).omit('negative').isEmpty()) {
|
|
1113
1095
|
return;
|
|
1114
1096
|
}
|
|
1115
1097
|
return ['Duration', duration];
|
|
@@ -1243,14 +1225,14 @@ class OData2AbstractSQL {
|
|
|
1243
1225
|
}
|
|
1244
1226
|
const existingJoin = query.joins.find((join) => {
|
|
1245
1227
|
const existingFrom = join[1];
|
|
1246
|
-
return ((
|
|
1228
|
+
return ((isTableNode(existingFrom) &&
|
|
1247
1229
|
existingFrom[1] === navigation.resource.tableAlias) ||
|
|
1248
|
-
(
|
|
1230
|
+
(isAliasNode(existingFrom) &&
|
|
1249
1231
|
existingFrom[2] === navigation.resource.tableAlias));
|
|
1250
1232
|
});
|
|
1251
1233
|
if (existingJoin != null) {
|
|
1252
1234
|
const existingJoinPredicate = existingJoin[2]?.[1];
|
|
1253
|
-
if (!
|
|
1235
|
+
if (!_.isEqual(navigation.where, existingJoinPredicate)) {
|
|
1254
1236
|
throw new KeySyntaxError(`Adding JOINs on the same resource with different ON clauses is not supported. Found ${navigation.resource.tableAlias}`);
|
|
1255
1237
|
}
|
|
1256
1238
|
throw new SyntaxError(`Could not navigate resources '${resource.name}' and '${extraResource}'`);
|
|
@@ -1260,8 +1242,8 @@ class OData2AbstractSQL {
|
|
|
1260
1242
|
}
|
|
1261
1243
|
AddNavigation(query, resource, extraResource) {
|
|
1262
1244
|
const navigation = this.NavigateResources(resource, extraResource);
|
|
1263
|
-
if (!query.from.some((from) => (
|
|
1264
|
-
(
|
|
1245
|
+
if (!query.from.some((from) => (isTableNode(from) && from[1] === navigation.resource.tableAlias) ||
|
|
1246
|
+
(isAliasNode(from) && from[2] === navigation.resource.tableAlias))) {
|
|
1265
1247
|
query.fromResource(this, navigation.resource);
|
|
1266
1248
|
query.where.push(navigation.where);
|
|
1267
1249
|
return navigation.resource;
|
|
@@ -1280,7 +1262,7 @@ class OData2AbstractSQL {
|
|
|
1280
1262
|
this.defaultResource = undefined;
|
|
1281
1263
|
}
|
|
1282
1264
|
Synonym(sqlName) {
|
|
1283
|
-
return (
|
|
1265
|
+
return _(sqlName)
|
|
1284
1266
|
.split('-')
|
|
1285
1267
|
.map((namePart) => {
|
|
1286
1268
|
const synonym = this.clientModel.synonyms[namePart];
|
|
@@ -1296,10 +1278,10 @@ class OData2AbstractSQL {
|
|
|
1296
1278
|
if (tableAlias == null) {
|
|
1297
1279
|
return tableRef;
|
|
1298
1280
|
}
|
|
1299
|
-
if (
|
|
1281
|
+
if (isTableNode(tableRef) && tableRef[1] === tableAlias) {
|
|
1300
1282
|
return tableRef;
|
|
1301
1283
|
}
|
|
1302
|
-
else if (
|
|
1284
|
+
else if (isAliasNode(tableRef)) {
|
|
1303
1285
|
if (tableRef[2] === tableAlias) {
|
|
1304
1286
|
return tableRef;
|
|
1305
1287
|
}
|
|
@@ -1309,27 +1291,9 @@ class OData2AbstractSQL {
|
|
|
1309
1291
|
return ['Alias', tableRef, tableAlias];
|
|
1310
1292
|
}
|
|
1311
1293
|
};
|
|
1312
|
-
if (bypassDefinition !== true) {
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
return maybeAlias(definition.abstractSql);
|
|
1316
|
-
}
|
|
1317
|
-
const computedFields = resource.fields.filter((f) => f.computed != null);
|
|
1318
|
-
if (computedFields.length > 0) {
|
|
1319
|
-
const computedFieldQuery = new Query();
|
|
1320
|
-
computedFieldQuery.select = [
|
|
1321
|
-
['Field', '*'],
|
|
1322
|
-
...computedFields.map((field) => this.AliasSelectField(resource, (0, exports.sqlNameToODataName)(field.fieldName), field.computed, field.fieldName)),
|
|
1323
|
-
];
|
|
1324
|
-
computedFieldQuery.fromResource(this, {
|
|
1325
|
-
tableAlias: resource.name,
|
|
1326
|
-
...resource,
|
|
1327
|
-
}, {
|
|
1328
|
-
extraBindVars,
|
|
1329
|
-
bindVarsLength,
|
|
1330
|
-
}, true);
|
|
1331
|
-
return maybeAlias(computedFieldQuery.compile('SelectQuery'));
|
|
1332
|
-
}
|
|
1294
|
+
if (bypassDefinition !== true && resource.definition != null) {
|
|
1295
|
+
const definition = this.rewriteDefinition(resource.definition, extraBindVars, bindVarsLength);
|
|
1296
|
+
return maybeAlias(definition.abstractSql);
|
|
1333
1297
|
}
|
|
1334
1298
|
return maybeAlias([
|
|
1335
1299
|
'Table',
|
|
@@ -1339,23 +1303,11 @@ class OData2AbstractSQL {
|
|
|
1339
1303
|
]);
|
|
1340
1304
|
}
|
|
1341
1305
|
rewriteDefinition(definition, extraBindVars, bindVarsLength) {
|
|
1342
|
-
const rewrittenDefinition =
|
|
1343
|
-
|
|
1306
|
+
const rewrittenDefinition = _.cloneDeep(convertToModernDefinition(definition));
|
|
1307
|
+
rewriteBinds(rewrittenDefinition, extraBindVars, bindVarsLength);
|
|
1344
1308
|
this.rewriteResourceAsTable(rewrittenDefinition.abstractSql);
|
|
1345
1309
|
return rewrittenDefinition;
|
|
1346
1310
|
}
|
|
1347
|
-
rewriteComputed(computed, tableName, tableAlias) {
|
|
1348
|
-
const rewrittenComputed = lodash_1.default.cloneDeep(computed);
|
|
1349
|
-
this.rewriteResourceAsTable(rewrittenComputed);
|
|
1350
|
-
if (tableAlias != null && tableAlias !== tableName) {
|
|
1351
|
-
modifyAbstractSql('ReferencedField', rewrittenComputed, (referencedField) => {
|
|
1352
|
-
if (referencedField[1] === tableName) {
|
|
1353
|
-
referencedField[1] = tableAlias;
|
|
1354
|
-
}
|
|
1355
|
-
});
|
|
1356
|
-
}
|
|
1357
|
-
return rewrittenComputed;
|
|
1358
|
-
}
|
|
1359
1311
|
rewriteResourceAsTable(abstractSql) {
|
|
1360
1312
|
modifyAbstractSql('Resource', abstractSql, (resource) => {
|
|
1361
1313
|
const resourceName = resource[1];
|
|
@@ -1368,7 +1320,6 @@ class OData2AbstractSQL {
|
|
|
1368
1320
|
});
|
|
1369
1321
|
}
|
|
1370
1322
|
}
|
|
1371
|
-
exports.OData2AbstractSQL = OData2AbstractSQL;
|
|
1372
1323
|
const addAliases = (shortAliases, lowerCaseAliasParts) => {
|
|
1373
1324
|
const trie = {};
|
|
1374
1325
|
const buildTrie = (aliasPart) => {
|
|
@@ -1396,7 +1347,7 @@ const addAliases = (shortAliases, lowerCaseAliasParts) => {
|
|
|
1396
1347
|
if (node.$suffix != null) {
|
|
1397
1348
|
shortAliases[str + node.$suffix] = str;
|
|
1398
1349
|
}
|
|
1399
|
-
|
|
1350
|
+
_.forEach(node, (value, key) => {
|
|
1400
1351
|
if (key === '$suffix') {
|
|
1401
1352
|
return;
|
|
1402
1353
|
}
|
|
@@ -1418,17 +1369,17 @@ const getRelationships = (relationships, nestedRelationships = []) => {
|
|
|
1418
1369
|
};
|
|
1419
1370
|
const generateShortAliases = (clientModel) => {
|
|
1420
1371
|
const shortAliases = {};
|
|
1421
|
-
const aliasParts = (
|
|
1372
|
+
const aliasParts = _(getRelationships(clientModel.relationships))
|
|
1422
1373
|
.union(Object.keys(clientModel.synonyms))
|
|
1423
1374
|
.reject((key) => key === '$')
|
|
1424
1375
|
.map((alias) => alias.toLowerCase())
|
|
1425
1376
|
.value();
|
|
1426
|
-
let origAliasParts = (
|
|
1377
|
+
let origAliasParts = _(aliasParts)
|
|
1427
1378
|
.flatMap((aliasPart) => aliasPart.split(/-| |\$/))
|
|
1428
1379
|
.uniq()
|
|
1429
1380
|
.value();
|
|
1430
1381
|
addAliases(shortAliases, origAliasParts);
|
|
1431
|
-
origAliasParts = (
|
|
1382
|
+
origAliasParts = _(aliasParts)
|
|
1432
1383
|
.flatMap((aliasPart) => aliasPart.split(/-| /))
|
|
1433
1384
|
.filter((aliasPart) => aliasPart.includes('$'))
|
|
1434
1385
|
.flatMap((aliasPart) => {
|
|
@@ -1441,7 +1392,7 @@ const generateShortAliases = (clientModel) => {
|
|
|
1441
1392
|
.uniq()
|
|
1442
1393
|
.value();
|
|
1443
1394
|
addAliases(shortAliases, origAliasParts);
|
|
1444
|
-
origAliasParts = (
|
|
1395
|
+
origAliasParts = _(aliasParts)
|
|
1445
1396
|
.flatMap((aliasPart) => aliasPart.split('-'))
|
|
1446
1397
|
.filter((aliasPart) => aliasPart.includes(' '))
|
|
1447
1398
|
.flatMap((aliasPart) => [
|
|
@@ -1454,7 +1405,7 @@ const generateShortAliases = (clientModel) => {
|
|
|
1454
1405
|
.uniq()
|
|
1455
1406
|
.value();
|
|
1456
1407
|
addAliases(shortAliases, origAliasParts);
|
|
1457
|
-
origAliasParts = (
|
|
1408
|
+
origAliasParts = _(aliasParts)
|
|
1458
1409
|
.filter((aliasPart) => aliasPart.includes('-'))
|
|
1459
1410
|
.flatMap((aliasPart) => [
|
|
1460
1411
|
aliasPart,
|