@sap/cds-compiler 2.11.4 → 2.12.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/CHANGELOG.md +58 -1
- package/bin/cds_update_identifiers.js +7 -7
- package/bin/cdsc.js +9 -10
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +12 -0
- package/lib/api/main.js +2 -0
- package/lib/api/options.js +2 -2
- package/lib/base/message-registry.js +31 -2
- package/lib/base/model.js +1 -0
- package/lib/base/optionProcessorHelper.js +97 -69
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/sql-snippets.js +93 -0
- package/lib/checks/validator.js +8 -0
- package/lib/compiler/assert-consistency.js +5 -3
- package/lib/compiler/base.js +0 -1
- package/lib/compiler/checks.js +32 -9
- package/lib/compiler/definer.js +25 -4
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/propagator.js +3 -2
- package/lib/compiler/resolver.js +97 -6
- package/lib/compiler/shared.js +12 -1
- package/lib/compiler/utils.js +7 -0
- package/lib/edm/annotations/genericTranslation.js +34 -17
- package/lib/edm/annotations/preprocessAnnotations.js +1 -1
- package/lib/edm/csn2edm.js +1 -1
- package/lib/edm/edm.js +8 -8
- package/lib/edm/edmPreprocessor.js +30 -23
- package/lib/edm/edmUtils.js +11 -12
- package/lib/gen/Dictionary.json +82 -40
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/language.tokens +15 -14
- package/lib/gen/languageLexer.interp +9 -1
- package/lib/gen/languageLexer.js +830 -779
- package/lib/gen/languageLexer.tokens +7 -6
- package/lib/gen/languageParser.js +2401 -2282
- package/lib/json/from-csn.js +47 -16
- package/lib/json/to-csn.js +17 -5
- package/lib/language/antlrParser.js +3 -3
- package/lib/language/docCommentParser.js +1 -1
- package/lib/language/genericAntlrParser.js +68 -51
- package/lib/language/language.g4 +128 -74
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +5 -3
- package/lib/main.js +3 -2
- package/lib/model/csnRefs.js +116 -68
- package/lib/model/csnUtils.js +40 -48
- package/lib/model/enrichCsn.js +30 -14
- package/lib/optionProcessor.js +3 -3
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +193 -79
- package/lib/render/toHdbcds.js +179 -95
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +57 -40
- package/lib/render/utils/common.js +24 -5
- package/lib/render/utils/sql.js +6 -4
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/associations.js +389 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +6 -4
- package/lib/transform/db/draft.js +3 -2
- package/lib/transform/db/expansion.js +4 -5
- package/lib/transform/db/flattening.js +5 -6
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +36 -23
- package/lib/transform/forHanaNew.js +35 -626
- package/lib/transform/forOdataNew.js +5 -4
- package/lib/transform/localized.js +3 -14
- package/lib/transform/odata/generateForeignKeyElements.js +2 -2
- package/lib/transform/transformUtilsNew.js +13 -13
- package/lib/transform/translateAssocsToJoins.js +8 -8
- package/lib/transform/universalCsnEnricher.js +217 -47
- package/lib/utils/file.js +2 -1
- package/lib/utils/timetrace.js +8 -2
- package/package.json +1 -1
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
getUtils, getNormalizedQuery, hasAnnotationValue, forEachMember,
|
|
5
|
+
} = require('../../model/csnUtils');
|
|
6
|
+
const { implicitAs } = require('../../model/csnRefs');
|
|
7
|
+
const { setProp } = require('../../base/model');
|
|
8
|
+
const { getTransformers } = require('../transformUtilsNew');
|
|
9
|
+
|
|
10
|
+
const validToString = '@cds.valid.to';
|
|
11
|
+
const validFromString = '@cds.valid.from';
|
|
12
|
+
/**
|
|
13
|
+
* Get the forEachDefinition callback function that adds a where condition to views that
|
|
14
|
+
* - are annotated with @cds.valid.from and @cds.valid.to,
|
|
15
|
+
* - have only one @cds.valid.from and @cds.valid.to,
|
|
16
|
+
* - and both annotations come from the same entity
|
|
17
|
+
*
|
|
18
|
+
* If the view has one of the annotations but the other conditions are not met, an error will be raised.
|
|
19
|
+
*
|
|
20
|
+
* @param {CSN.Model} csn
|
|
21
|
+
* @param {object} messageFunctions
|
|
22
|
+
* @param {Function} messageFunctions.info
|
|
23
|
+
* @returns {(artifact: CSN.Artifact, artifactName: string) => void} Callback for forEachDefinition applying the where-condition to views.
|
|
24
|
+
*/
|
|
25
|
+
function getViewDecorator(csn, messageFunctions) {
|
|
26
|
+
const { info } = messageFunctions;
|
|
27
|
+
const { get$combined } = getUtils(csn);
|
|
28
|
+
return addTemporalWhereConditionToView;
|
|
29
|
+
/**
|
|
30
|
+
* Add a where condition to views that
|
|
31
|
+
* - are annotated with @cds.valid.from and @cds.valid.to,
|
|
32
|
+
* - have only one @cds.valid.from and @cds.valid.to,
|
|
33
|
+
* - and both annotations come from the same entity
|
|
34
|
+
*
|
|
35
|
+
* If the view has one of the annotations but the other conditions are not met, an error will be raised.
|
|
36
|
+
*
|
|
37
|
+
* @param {CSN.Artifact} artifact
|
|
38
|
+
* @param {string} artifactName
|
|
39
|
+
*/
|
|
40
|
+
function addTemporalWhereConditionToView(artifact, artifactName) {
|
|
41
|
+
const normalizedQuery = getNormalizedQuery(artifact);
|
|
42
|
+
if (normalizedQuery && normalizedQuery.query && normalizedQuery.query.SELECT) {
|
|
43
|
+
// BLOCKER: We need information to handle $combined
|
|
44
|
+
// What we are trying to achieve by this:
|
|
45
|
+
// Forbid joining/selecting from two or more temporal entities
|
|
46
|
+
// Idea: Follow the query-tree and check each from
|
|
47
|
+
// Collect all source-entities and compute our own $combined
|
|
48
|
+
const $combined = get$combined(normalizedQuery.query);
|
|
49
|
+
const [ from, to ] = getFromToElements($combined);
|
|
50
|
+
// exactly one validFrom & validTo
|
|
51
|
+
if (from.length === 1 && to.length === 1) {
|
|
52
|
+
// and both are from the same origin
|
|
53
|
+
if (from[0].source === to[0].source && from[0].parent === to[0].parent) {
|
|
54
|
+
if (!hasFalsyTemporalAnnotations(normalizedQuery.query.SELECT, artifact.elements, from[0], to[0])) {
|
|
55
|
+
const fromPath = {
|
|
56
|
+
ref: [
|
|
57
|
+
from[0].parent,
|
|
58
|
+
from[0].name,
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const toPath = {
|
|
63
|
+
ref: [
|
|
64
|
+
to[0].parent,
|
|
65
|
+
to[0].name,
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
const atFrom = { ref: [ '$at', 'from' ] };
|
|
71
|
+
const atTo = { ref: [ '$at', 'to' ] };
|
|
72
|
+
|
|
73
|
+
const cond = [ '(', fromPath, '<', atTo, 'and', toPath, '>', atFrom, ')' ];
|
|
74
|
+
|
|
75
|
+
if (normalizedQuery.query.SELECT.where) { // if there is an existing where-clause, extend it by adding 'and (temporal clause)'
|
|
76
|
+
normalizedQuery.query.SELECT.where = [ '(', ...normalizedQuery.query.SELECT.where, ')', 'and', ...cond ];
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
normalizedQuery.query.SELECT.where = cond;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
info(null, [ 'definitions', artifactName ], `No temporal WHERE clause added as "${from[0].error_parent}"."${from[0].name}" and "${to[0].error_parent}"."${to[0].name}" are not of same origin`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else if (from.length > 0 || to.length > 0) {
|
|
88
|
+
const missingAnnotation = from.length > to.length ? validToString : validFromString;
|
|
89
|
+
info(null, [ 'definitions', artifactName ],
|
|
90
|
+
{ anno: missingAnnotation },
|
|
91
|
+
'No temporal WHERE clause added because $(ANNO) is missing');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get all elements tagged with @cds.valid.from/to from the union of all entities of the from-clause.
|
|
98
|
+
*
|
|
99
|
+
* @param {any} combined union of all entities of the from-clause
|
|
100
|
+
* @returns {Array[]} Array where first field is array of elements with @cds.valid.from, second field is array of elements with @cds.valid.to.
|
|
101
|
+
*/
|
|
102
|
+
function getFromToElements(combined) {
|
|
103
|
+
const from = [];
|
|
104
|
+
const to = [];
|
|
105
|
+
for (const name in combined) {
|
|
106
|
+
let elt = combined[name];
|
|
107
|
+
if (!Array.isArray(elt))
|
|
108
|
+
elt = [ elt ];
|
|
109
|
+
elt.forEach((e) => {
|
|
110
|
+
if (hasAnnotationValue(e.element, validFromString))
|
|
111
|
+
from.push(e);
|
|
112
|
+
|
|
113
|
+
if (hasAnnotationValue(e.element, validToString))
|
|
114
|
+
to.push(e);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return [ from, to ];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Check if the given SELECT has a falsy @cds.valid.from and a falsy @cds.valid.to
|
|
123
|
+
*
|
|
124
|
+
* @param {CSN.QuerySelect} SELECT
|
|
125
|
+
* @param {CSN.Elements} elements
|
|
126
|
+
* @param {object} from
|
|
127
|
+
* @param {object} to
|
|
128
|
+
* @returns {boolean} True if both are present and false.
|
|
129
|
+
*/
|
|
130
|
+
function hasFalsyTemporalAnnotations(SELECT, elements, from, to) {
|
|
131
|
+
let fromElement = elements[from.name];
|
|
132
|
+
let toElement = elements[to.name];
|
|
133
|
+
|
|
134
|
+
if (SELECT.columns) {
|
|
135
|
+
for (const col of SELECT.columns) {
|
|
136
|
+
if (col.ref) {
|
|
137
|
+
const implicitAlias = implicitAs(col.ref);
|
|
138
|
+
if (implicitAlias === from.name)
|
|
139
|
+
fromElement = elements[col.as || implicitAlias];
|
|
140
|
+
else if (implicitAlias === to.name)
|
|
141
|
+
toElement = elements[col.as || implicitAlias];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return fromElement && toElement &&
|
|
146
|
+
hasAnnotationValue(fromElement, validFromString, false) &&
|
|
147
|
+
hasAnnotationValue(toElement, validToString, false);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get the forEachDefinition callback function that collects all usages of @cds.valid.from/to/key and checks that
|
|
153
|
+
* - the assignment is on a valid element
|
|
154
|
+
* - the annotation is only assigned once
|
|
155
|
+
* - key is only used in conjunction with from and to
|
|
156
|
+
*
|
|
157
|
+
* Furthermore, @cds.valid.from and @cds.valid.key is processed - @cds.valid.from is marked as key or marked as unique if @cds.valid.key is used.
|
|
158
|
+
* If @cds.valid.key is used, the real key-elements have their key-property removed (set non-enumerable as $key) and instead the @cds.valid.key-marked elements have it added.
|
|
159
|
+
*
|
|
160
|
+
* @param {CSN.Model} csn
|
|
161
|
+
* @param {CSN.Options} options
|
|
162
|
+
* @param {string} pathDelimiter
|
|
163
|
+
* @param {object} messageFunctions
|
|
164
|
+
* @param {Function} messageFunctions.error
|
|
165
|
+
* @returns {(artifact: CSN.Artifact, artifactName: string) => void} Callback for forEachDefinition processing the annotations.
|
|
166
|
+
*/
|
|
167
|
+
function getAnnotationHandler(csn, options, pathDelimiter, messageFunctions) {
|
|
168
|
+
const { error } = messageFunctions;
|
|
169
|
+
const {
|
|
170
|
+
extractValidFromToKeyElement, checkAssignment, checkMultipleAssignments, recurseElements,
|
|
171
|
+
} = getTransformers(csn, options, pathDelimiter);
|
|
172
|
+
|
|
173
|
+
return handleTemporalAnnotations;
|
|
174
|
+
/**
|
|
175
|
+
* @param {CSN.Artifact} artifact
|
|
176
|
+
* @param {string} artifactName
|
|
177
|
+
*/
|
|
178
|
+
function handleTemporalAnnotations(artifact, artifactName) {
|
|
179
|
+
const validFrom = [];
|
|
180
|
+
const validTo = [];
|
|
181
|
+
const validKey = [];
|
|
182
|
+
|
|
183
|
+
recurseElements(artifact, [ 'definitions', artifactName ], (member, path) => {
|
|
184
|
+
const [ f, t, k ] = extractValidFromToKeyElement(member, path);
|
|
185
|
+
validFrom.push(...f);
|
|
186
|
+
validTo.push(...t);
|
|
187
|
+
validKey.push(...k);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (artifact.kind === 'entity' && !artifact.query) {
|
|
191
|
+
validFrom.forEach(obj => checkAssignment(validFromString, obj.element, obj.path, artifact));
|
|
192
|
+
validTo.forEach(obj => checkAssignment(validToString, obj.element, obj.path, artifact));
|
|
193
|
+
validKey.forEach(obj => checkAssignment('@cds.valid.key', obj.element, obj.path, artifact));
|
|
194
|
+
checkMultipleAssignments(validFrom, validFromString, artifact, artifactName);
|
|
195
|
+
checkMultipleAssignments(validTo, validToString, artifact, artifactName, true);
|
|
196
|
+
checkMultipleAssignments(validKey, '@cds.valid.key', artifact, artifactName);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// if there is an cds.valid.key, make this the only primary key
|
|
200
|
+
// otherwise add all cds.valid.from to primary key tuple
|
|
201
|
+
if (validKey.length) {
|
|
202
|
+
if (!validFrom.length || !validTo.length) {
|
|
203
|
+
error(null, [ 'definitions', artifactName ],
|
|
204
|
+
'Expecting “@cds.valid.from” and “@cds.valid.to” if “@cds.valid.key” is used');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
forEachMember(artifact, (member) => {
|
|
208
|
+
if (member.key) {
|
|
209
|
+
member.unique = true;
|
|
210
|
+
delete member.key;
|
|
211
|
+
// Remember that this element was a key in the original artifact.
|
|
212
|
+
// This is needed for localized convenience view generation.
|
|
213
|
+
setProp(member, '$key', true);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
validKey.forEach((member) => {
|
|
217
|
+
member.element.key = true;
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
validFrom.forEach((member) => {
|
|
221
|
+
member.element.unique = true;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
validFrom.forEach((member) => {
|
|
226
|
+
member.element.key = true;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
module.exports = {
|
|
234
|
+
getViewDecorator,
|
|
235
|
+
getAnnotationHandler,
|
|
236
|
+
};
|
|
@@ -365,16 +365,23 @@ function handleExists(csn, options, error) {
|
|
|
365
365
|
const subselect = getSubselect(root.target, ref, sources);
|
|
366
366
|
|
|
367
367
|
const target = subselect.SELECT.from.as; // use subquery alias as target - prevent shadowing
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
368
|
+
const extension = root.keys ? translateManagedAssocToWhere(root, target, isPrefixedWithTableAlias, base, current) : translateUnmanagedAssocToWhere(root, target, isPrefixedWithTableAlias, base, current);
|
|
369
|
+
if (extension.length > 3)
|
|
370
|
+
subselect.SELECT.where.push('('); // add braces around the on-condition part to ensure precedence is kept
|
|
371
|
+
|
|
372
|
+
subselect.SELECT.where.push(...extension);
|
|
373
|
+
|
|
374
|
+
if (extension.length > 3)
|
|
375
|
+
subselect.SELECT.where.push(')');
|
|
374
376
|
|
|
375
377
|
newExpr.push('exists');
|
|
376
|
-
if (ref && ref.where)
|
|
377
|
-
|
|
378
|
+
if (ref && ref.where) {
|
|
379
|
+
const remappedWhere = remapExistingWhere(target, ref.where);
|
|
380
|
+
if (remappedWhere.length > 3)
|
|
381
|
+
subselect.SELECT.where.push(...[ 'and', '(', ...remappedWhere, ')' ]);
|
|
382
|
+
else
|
|
383
|
+
subselect.SELECT.where.push(...[ 'and', ...remappedWhere ]);
|
|
384
|
+
}
|
|
378
385
|
|
|
379
386
|
newExpr.push(subselect);
|
|
380
387
|
toContinue.push([ exprPath.concat(newExpr.length - 1), exprPath.concat([ newExpr.length - 1, 'SELECT', 'where' ]) ]);
|
|
@@ -412,21 +419,24 @@ function handleExists(csn, options, error) {
|
|
|
412
419
|
*
|
|
413
420
|
* @param {CSN.Element} root
|
|
414
421
|
* @param {string} target
|
|
415
|
-
* @param {CSN.Query} subselect This subselect will in the end replace <assoc> in EXISTS <assoc>
|
|
416
422
|
* @param {boolean} isPrefixedWithTableAlias
|
|
417
423
|
* @param {string} base
|
|
418
424
|
* @param {Token} current
|
|
425
|
+
* @returns {object[]} The stuff to add to the where
|
|
419
426
|
*/
|
|
420
|
-
function translateManagedAssocToWhere(root, target,
|
|
427
|
+
function translateManagedAssocToWhere(root, target, isPrefixedWithTableAlias, base, current) {
|
|
428
|
+
const whereExtension = [];
|
|
421
429
|
for (let j = 0; j < root.keys.length; j++) {
|
|
422
430
|
const lop = { ref: [ target, ...root.keys[j].ref ] }; // target side
|
|
423
431
|
const rop = { ref: (isPrefixedWithTableAlias ? [] : [ base ]).concat([ ...toRawRef(current.ref), ...root.keys[j].ref ]) }; // source side
|
|
424
432
|
|
|
425
433
|
if (j > 0)
|
|
426
|
-
|
|
434
|
+
whereExtension.push('and');
|
|
427
435
|
|
|
428
|
-
|
|
436
|
+
whereExtension.push(...[ lop, '=', rop ]);
|
|
429
437
|
}
|
|
438
|
+
|
|
439
|
+
return whereExtension;
|
|
430
440
|
}
|
|
431
441
|
|
|
432
442
|
/**
|
|
@@ -452,12 +462,13 @@ function handleExists(csn, options, error) {
|
|
|
452
462
|
*
|
|
453
463
|
* @param {CSN.Element} root
|
|
454
464
|
* @param {string} target
|
|
455
|
-
* @param {CSN.Query} subselect This subselect will in the end replace <assoc> in EXISTS <assoc>
|
|
456
465
|
* @param {boolean} isPrefixedWithTableAlias
|
|
457
466
|
* @param {string} base
|
|
458
467
|
* @param {Token} current
|
|
468
|
+
* @returns {object[]} The stuff to add to the where
|
|
459
469
|
*/
|
|
460
|
-
function translateUnmanagedAssocToWhere(root, target,
|
|
470
|
+
function translateUnmanagedAssocToWhere(root, target, isPrefixedWithTableAlias, base, current) {
|
|
471
|
+
const whereExtension = [];
|
|
461
472
|
for (let j = 0; j < root.on.length; j++) {
|
|
462
473
|
const part = root.on[j];
|
|
463
474
|
|
|
@@ -465,7 +476,7 @@ function handleExists(csn, options, error) {
|
|
|
465
476
|
// but also keep along stuff like null and undefined, so compiler
|
|
466
477
|
// can have a chance to complain/ we can fail later nicely maybe
|
|
467
478
|
if (!(part && part.ref)) {
|
|
468
|
-
|
|
479
|
+
whereExtension.push(part);
|
|
469
480
|
continue;
|
|
470
481
|
}
|
|
471
482
|
|
|
@@ -475,30 +486,32 @@ function handleExists(csn, options, error) {
|
|
|
475
486
|
// Dollar Self Backlink
|
|
476
487
|
if (isValidDollarSelf(root.on[j], root.$path.concat([ 'on', j ]), root.on[j + 1], root.on[j + 2], root.$path.concat([ 'on', j + 2 ]))) {
|
|
477
488
|
if (root.on[j].ref[0] === '$self' && root.on[j].ref.length === 1)
|
|
478
|
-
|
|
489
|
+
whereExtension.push(...translateDollarSelfToWhere(base, target, root.on[j + 2], root.$path.concat([ 'on', j + 2 ])));
|
|
479
490
|
else
|
|
480
|
-
|
|
491
|
+
whereExtension.push(...translateDollarSelfToWhere(base, target, root.on[j], root.$path.concat([ 'on', j ])));
|
|
481
492
|
|
|
482
493
|
j += 2;
|
|
483
494
|
}
|
|
484
495
|
else if (links && links[0].art === root) { // target side
|
|
485
|
-
|
|
496
|
+
whereExtension.push({ ref: [ target, ...part.ref.slice(1) ] });
|
|
486
497
|
}
|
|
487
498
|
else if (part.$scope === '$self') { // source side - "absolute" scope
|
|
488
499
|
// cut off the $self, as we prefix the entity name now
|
|
489
|
-
|
|
500
|
+
whereExtension.push({ ref: [ base, ...part.ref.slice(1) ] });
|
|
490
501
|
}
|
|
491
502
|
else if (art) { // source side - with local scope
|
|
492
503
|
if (isPrefixedWithTableAlias)
|
|
493
|
-
|
|
504
|
+
whereExtension.push({ ref: [ ...current.ref.slice(0, -1), ...part.ref ] });
|
|
494
505
|
else
|
|
495
|
-
|
|
506
|
+
whereExtension.push({ ref: [ base, ...current.ref.slice(0, -1), ...part.ref ] });
|
|
496
507
|
}
|
|
497
508
|
else { // operator - or any other leftover
|
|
498
|
-
|
|
509
|
+
whereExtension.push(part);
|
|
499
510
|
}
|
|
500
511
|
}
|
|
501
512
|
|
|
513
|
+
return whereExtension;
|
|
514
|
+
|
|
502
515
|
/**
|
|
503
516
|
* Check that an expression triple is a valid $self
|
|
504
517
|
*
|
|
@@ -706,7 +719,7 @@ function handleExists(csn, options, error) {
|
|
|
706
719
|
*
|
|
707
720
|
* @param {string} base The source entity/query source name
|
|
708
721
|
* @param {string} target The target entity/query source name
|
|
709
|
-
* @param {
|
|
722
|
+
* @param {object} assoc The association element - the "not-$self" side of the comparison
|
|
710
723
|
* @param {CSN.Path} path
|
|
711
724
|
* @returns {TokenStream} The WHERE representing the $self comparison
|
|
712
725
|
*/
|