@sap/cds-compiler 6.3.6 → 6.4.6
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 +101 -3
- package/LICENSE +32 -0
- package/README.md +14 -2
- package/bin/cdsse.js +0 -3
- package/doc/CHANGELOG_BETA.md +1 -1
- package/doc/CHANGELOG_DEPRECATED.md +1 -1
- package/lib/base/message-registry.js +9 -2
- package/lib/base/messages.js +1 -1
- package/lib/base/model.js +2 -0
- package/lib/checks/existsExpressionsOnlyForeignKeys.js +16 -10
- package/lib/checks/existsMustEndInAssoc.js +1 -1
- package/lib/checks/existsMustNotStartWithDollarSelf.js +31 -0
- package/lib/checks/validator.js +4 -2
- package/lib/compiler/assert-consistency.js +3 -2
- package/lib/compiler/builtins.js +5 -6
- package/lib/compiler/checks.js +37 -26
- package/lib/compiler/define.js +1 -1
- package/lib/compiler/extend.js +39 -50
- package/lib/compiler/finalize-parse-cdl.js +1 -1
- package/lib/compiler/lsp-api.js +1 -1
- package/lib/compiler/populate.js +2 -2
- package/lib/compiler/propagator.js +29 -6
- package/lib/compiler/resolve.js +13 -3
- package/lib/compiler/shared.js +157 -133
- package/lib/compiler/tweak-assocs.js +87 -29
- package/lib/compiler/xpr-rewrite.js +164 -160
- package/lib/edm/annotations/edmJson.js +206 -37
- package/lib/edm/csn2edm.js +13 -0
- package/lib/edm/edmUtils.js +2 -2
- package/lib/gen/BaseParser.js +106 -72
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +1501 -1509
- package/lib/json/to-csn.js +8 -5
- package/lib/language/genericAntlrParser.js +0 -0
- package/lib/main.js +19 -16
- package/lib/model/csnRefs.js +589 -521
- package/lib/model/csnUtils.js +8 -5
- package/lib/model/enrichCsn.js +1 -0
- package/lib/parsers/AstBuildingParser.js +73 -28
- package/lib/render/toCdl.js +2 -1
- package/lib/render/toHdbcds.js +6 -3
- package/lib/render/toSql.js +5 -0
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/assertUnique.js +4 -1
- package/lib/transform/db/assocsToQueries/transformExists.js +3 -10
- package/lib/transform/db/assocsToQueries/utils.js +0 -5
- package/lib/transform/db/cdsPersistence.js +17 -18
- package/lib/transform/db/expansion.js +179 -3
- package/lib/transform/db/flattening.js +16 -5
- package/lib/transform/db/rewriteCalculatedElements.js +79 -283
- package/lib/transform/effective/main.js +8 -1
- package/lib/transform/forOdata.js +1 -1
- package/lib/transform/forRelationalDB.js +21 -80
- package/lib/transform/localized.js +75 -127
- package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +89 -63
- package/lib/transform/transformUtils.js +23 -21
- package/lib/transform/translateAssocsToJoins.js +7 -5
- package/lib/transform/tupleExpansion.js +16 -3
- package/package.json +3 -3
- package/doc/DeprecatedOptions_v2.md +0 -150
- package/doc/NameResolution.md +0 -837
- package/lib/transform/parseExpr.js +0 -415
|
@@ -210,7 +210,7 @@ function getStructStepsFlattener( csn, options, messageFunctions, resolved, path
|
|
|
210
210
|
const { links, art, scope } = inspectRef(path);
|
|
211
211
|
const resolvedLinkTypes = resolveLinkTypes(links);
|
|
212
212
|
setProp(parent, '$path', [ ...path ]);
|
|
213
|
-
const lastRef = ref
|
|
213
|
+
const lastRef = ref.at(-1);
|
|
214
214
|
const fn = (suspend = false, suspendPos = 0,
|
|
215
215
|
refFilter = _parent => true) => {
|
|
216
216
|
let refChanged = false;
|
|
@@ -226,13 +226,14 @@ function getStructStepsFlattener( csn, options, messageFunctions, resolved, path
|
|
|
226
226
|
// Explicitly set implicit alias for things that are now flattened - but only in columns
|
|
227
227
|
// TODO: Can this be done elegantly during expand phase already?
|
|
228
228
|
if (parent.$implicitAlias) { // an expanded s -> s.a is marked with this - do not add implicit alias "a" there, we want s_a
|
|
229
|
-
if (parent.ref
|
|
229
|
+
if (parent.ref.at(-1) === parent.as) // for a simple s that was expanded - for s.substructure this would not apply
|
|
230
230
|
delete parent.as;
|
|
231
231
|
delete parent.$implicitAlias;
|
|
232
232
|
}
|
|
233
|
-
// To handle explicitly written s.a - add implicit alias a, since after flattening it would otherwise be s_a
|
|
234
|
-
|
|
235
|
-
|
|
233
|
+
// To handle explicitly written s.a - add implicit alias a, since after flattening it would otherwise be s_a,
|
|
234
|
+
// also for 'from' clauses
|
|
235
|
+
else if (parent.ref.at(-1) !== lastRef &&
|
|
236
|
+
(insideColumns(scopedPath) || insideKeys(scopedPath) || isFromRef(scopedPath)) &&
|
|
236
237
|
!parent.as) {
|
|
237
238
|
parent.as = lastRef;
|
|
238
239
|
}
|
|
@@ -276,6 +277,16 @@ function getStructStepsFlattener( csn, options, messageFunctions, resolved, path
|
|
|
276
277
|
return path.length >= 3 && path[path.length - 2] === 'keys' && typeof path[path.length - 1] === 'number';
|
|
277
278
|
}
|
|
278
279
|
|
|
280
|
+
/**
|
|
281
|
+
* Whether the given path points to a `from` clause.
|
|
282
|
+
*
|
|
283
|
+
* @param {CSN.Path} path
|
|
284
|
+
* @returns {boolean}
|
|
285
|
+
*/
|
|
286
|
+
function isFromRef(path) {
|
|
287
|
+
return path.at(-1) === 'from';
|
|
288
|
+
}
|
|
289
|
+
|
|
279
290
|
return transformer;
|
|
280
291
|
}
|
|
281
292
|
|
|
@@ -9,7 +9,6 @@ const {
|
|
|
9
9
|
implicitAs,
|
|
10
10
|
} = require('../../model/csnUtils');
|
|
11
11
|
const { getBranches } = require('./flattening');
|
|
12
|
-
const { getColumnMap } = require('./views');
|
|
13
12
|
const { cloneCsnNonDict } = require('../../model/cloneCsn');
|
|
14
13
|
|
|
15
14
|
const cloneCsnOptions = { hiddenPropertiesToClone: [ '_art', '_links', '$env', '$scope' ] };
|
|
@@ -23,11 +22,10 @@ const cloneCsnOptions = { hiddenPropertiesToClone: [ '_art', '_links', '$env', '
|
|
|
23
22
|
* @param {CSN.Options} options
|
|
24
23
|
* @param {object} csnUtils
|
|
25
24
|
* @param {string} pathDelimiter
|
|
26
|
-
* @param {object}
|
|
25
|
+
* @param {object} _messageFunctions
|
|
27
26
|
*/
|
|
28
|
-
function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter,
|
|
27
|
+
function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter, _messageFunctions ) {
|
|
29
28
|
const { inspectRef, effectiveType } = csnUtils;
|
|
30
|
-
const { error } = messageFunctions;
|
|
31
29
|
|
|
32
30
|
const views = [];
|
|
33
31
|
const entities = [];
|
|
@@ -119,9 +117,6 @@ function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter
|
|
|
119
117
|
* Rewrite calculated-elements-columns in views/projections and replace them
|
|
120
118
|
* with their "root"-expression.
|
|
121
119
|
*
|
|
122
|
-
* As a first step, we ensure that all views/projections have a .columns (see {@link calculateColumns}) and that
|
|
123
|
-
* all calculated elements are addressed explicitly and not via a * (see {@link makeAllCalculatedElementsExplicitColumns}).
|
|
124
|
-
*
|
|
125
120
|
* Then, we check the `art` of each ref for a `.value` and rewrite accordingly.
|
|
126
121
|
* We need to ensure that the scope of the rewritten expressions is still correct!
|
|
127
122
|
* An `id` in the `.value` needs to point to the entity containing the element,
|
|
@@ -133,73 +128,64 @@ function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter
|
|
|
133
128
|
* @param {CSN.Path} path
|
|
134
129
|
*/
|
|
135
130
|
function rewriteInView( SELECT, elements, path ) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
131
|
+
expandStructSelectItems(SELECT);
|
|
132
|
+
|
|
133
|
+
const name = SELECT.from.args
|
|
134
|
+
? undefined
|
|
135
|
+
: SELECT.from.as || (SELECT.from.ref && implicitAs(SELECT.from.ref));
|
|
136
|
+
|
|
137
|
+
applyTransformationsOnNonDictionary({ SELECT }, 'SELECT', {
|
|
138
|
+
ref: transformRef,
|
|
139
|
+
}, {}, path);
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @param {object} parent
|
|
143
|
+
* @param {string} prop
|
|
144
|
+
* @param ref
|
|
145
|
+
* @param p
|
|
146
|
+
* @param root
|
|
147
|
+
*/
|
|
148
|
+
function transformRef(parent, prop, ref, p, root) {
|
|
149
|
+
const {
|
|
150
|
+
art, env, links, scope,
|
|
151
|
+
} = getRefInfo(parent, p);
|
|
152
|
+
|
|
153
|
+
// calc element publishes association, treat as regular unmanaged association
|
|
154
|
+
const calcElementIsAssoc = art?.value && art.target;
|
|
155
|
+
|
|
156
|
+
if (!art?.value || art.value.stored || calcElementIsAssoc)
|
|
157
|
+
return;
|
|
158
|
+
|
|
159
|
+
if (scope === 'inline' || scope === 'expand') {
|
|
160
|
+
// Calculated elements in expand/inline are not supported, yet.
|
|
161
|
+
// Error is reported in expansion.js
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
166
164
|
|
|
167
|
-
|
|
168
|
-
root[p[p.length - 1]].as = alias;
|
|
169
|
-
else
|
|
170
|
-
delete root[p[p.length - 1]].as;
|
|
165
|
+
const alias = parent.as || implicitAs(parent.ref);
|
|
171
166
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
else if (parent._element?.type)
|
|
177
|
-
root[p[p.length - 1]].cast = { type: parent._element.type };
|
|
167
|
+
// TODO: What about other scopes? expand/inline may become relevant again if we allow calc elements in them.
|
|
168
|
+
const value = (scope !== 'ref-target')
|
|
169
|
+
? absolutifyPaths(env, art, ref, links, name).value
|
|
170
|
+
: keepAssocStepsInRef(ref, links, art).value;
|
|
178
171
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
},
|
|
182
|
-
}, {}, path);
|
|
183
|
-
}
|
|
172
|
+
// TODO: Is a shallow copy enough?
|
|
173
|
+
root[p.at(-1)] = art.value.cast ? { xpr: [ value ] } : { ...value };
|
|
184
174
|
|
|
185
|
-
|
|
186
|
-
|
|
175
|
+
if (p.at(-2) === 'columns' || p.at(-2) === 'expand')
|
|
176
|
+
root[p.at(-1)].as = alias;
|
|
177
|
+
else
|
|
178
|
+
delete root[p.at(-1)].as;
|
|
187
179
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if (!SELECT.columns)
|
|
195
|
-
return false;
|
|
180
|
+
// If the calculated element has a type, use it. But only if the column did not have an explicit type.
|
|
181
|
+
// Note: We should not check `art.type`, because we only need the type for columns, not filters.
|
|
182
|
+
if (parent.cast)
|
|
183
|
+
root[p.at(-1)].cast = parent.cast;
|
|
184
|
+
else if (parent._element?.type)
|
|
185
|
+
root[p.at(-1)].cast = { type: parent._element.type };
|
|
196
186
|
|
|
197
|
-
|
|
198
|
-
if (column.expand || column.inline)
|
|
199
|
-
return true;
|
|
187
|
+
// TODO: Copy annotations? May become relevant in the future
|
|
200
188
|
}
|
|
201
|
-
|
|
202
|
-
return false;
|
|
203
189
|
}
|
|
204
190
|
|
|
205
191
|
/**
|
|
@@ -329,228 +315,38 @@ function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter
|
|
|
329
315
|
}
|
|
330
316
|
|
|
331
317
|
/**
|
|
332
|
-
*
|
|
333
|
-
* attach the .columns if they contain a calculated element so we can rewrite them in
|
|
334
|
-
* the later steps.
|
|
335
|
-
*
|
|
336
|
-
* @param {CSN.Elements} elements Artifact elements
|
|
337
|
-
* @param {object} carrier The thing that will "carry" the columns - .SELECT or .projection
|
|
338
|
-
* @returns {Function[]} Cleanup callbacks that remove `_`-links.
|
|
339
|
-
*/
|
|
340
|
-
function calculateColumns( elements, carrier ) {
|
|
341
|
-
carrier.columns = [ '*' ];
|
|
342
|
-
const cleanupCallbacks = makeAllCalculatedElementsExplicitColumns(elements, carrier, false);
|
|
343
|
-
if (carrier.columns.length === 1 && carrier.columns[0] === '*')
|
|
344
|
-
delete carrier.columns;
|
|
345
|
-
return cleanupCallbacks;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
*
|
|
350
|
-
* @param {CSN.QuerySelect} SELECT
|
|
351
|
-
* @returns {object}
|
|
352
|
-
*/
|
|
353
|
-
function getDirectlyAddressableElements( SELECT ) {
|
|
354
|
-
const { from } = SELECT;
|
|
355
|
-
if (from.ref) {
|
|
356
|
-
return from._art.elements;
|
|
357
|
-
}
|
|
358
|
-
else if (from.SELECT) {
|
|
359
|
-
return from.SELECT.elements;
|
|
360
|
-
}
|
|
361
|
-
else if (from.SET) {
|
|
362
|
-
// args[0] could be SELECT or UNION
|
|
363
|
-
return getDirectlyAddressableElements({ from: from.SET.args[0] });
|
|
364
|
-
}
|
|
365
|
-
else if (from.args) {
|
|
366
|
-
const mergedElements = Object.create(null);
|
|
367
|
-
for (const arg of from.args) {
|
|
368
|
-
if (arg.ref) {
|
|
369
|
-
for (const elementName in arg._art.elements)
|
|
370
|
-
mergedElements[elementName] = arg._art.elements[elementName];
|
|
371
|
-
}
|
|
372
|
-
else if (arg.SET) {
|
|
373
|
-
return getDirectlyAddressableElements({ from: arg.SET.args[0] });
|
|
374
|
-
}
|
|
375
|
-
else if (arg.SELECT) { // TODO: UNION
|
|
376
|
-
for (const elementName in arg.SELECT.elements)
|
|
377
|
-
mergedElements[elementName] = arg.SELECT.elements[elementName];
|
|
378
|
-
}
|
|
379
|
-
else if (arg.args) { // TODO: Is it safe to do recursion here?
|
|
380
|
-
for (const subarg of arg.args) {
|
|
381
|
-
const elements = getDirectlyAddressableElements({ from: subarg });
|
|
382
|
-
for (const elementName in elements)
|
|
383
|
-
mergedElements[elementName] = elements[elementName];
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
else {
|
|
387
|
-
throw new CompilerAssertion(`Unhandled arg type: ${ JSON.stringify(arg, null, 2) }`);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
return mergedElements;
|
|
391
|
-
}
|
|
392
|
-
throw new CompilerAssertion(`Unhandled query type: ${ JSON.stringify(SELECT, null, 2) }`);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* Ensure that all elements of the query that are calculated elements have an explicit column that we can rewrite.
|
|
397
|
-
* If a field originally comes in via the *, then we need to add an explicit column for it.
|
|
318
|
+
* Expands all references to structures to its separate leaf elements, adding columns if needed.
|
|
398
319
|
*
|
|
399
|
-
* @param {
|
|
400
|
-
* @param {CSN.QuerySelect} SELECT
|
|
401
|
-
* @param {boolean} containsExpandInline
|
|
402
|
-
* @returns {Function[]} Cleanup callbacks that remove `_`-links.
|
|
320
|
+
* @param {object} SELECT
|
|
403
321
|
*/
|
|
404
|
-
function
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
const leafElement = branch.steps[branch.steps.length - 1];
|
|
428
|
-
if (columnMap[branchName]) { // Existing column - don't overwrite, we need $env!
|
|
429
|
-
columns.push(columnMap[branchName]);
|
|
430
|
-
}
|
|
431
|
-
else {
|
|
432
|
-
// TODO: Hm, will we have a $env in the leaf of the thing then?
|
|
433
|
-
const column = { ref: [ ...originalRef, ...branches[branchName].ref.slice(1) ], as: branchName };
|
|
434
|
-
setProp(column, '_element', leafElement);
|
|
435
|
-
cleanupCallbacks.push(() => delete column._element);
|
|
436
|
-
columns.push(column);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
if (columnMap[name]) {
|
|
440
|
-
unfoldingMap[name] = [ false, [ ...columns ] ];
|
|
441
|
-
}
|
|
442
|
-
else if (hasStar) { // Via * - just append
|
|
443
|
-
starContainsCalculated = true;
|
|
444
|
-
unfoldingMap[name] = [ true, [ ...columns ] ];
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
else {
|
|
448
|
-
if (usesCalcOnRead(branches))
|
|
449
|
-
containsCalcOnRead = true;
|
|
450
|
-
if (!columnMap[name] && hasStar) { // Via * - just append
|
|
451
|
-
unfoldingMap[name] = [ true, [ { ref: [ name ] } ] ];
|
|
452
|
-
}
|
|
453
|
-
else { // just a random column - keep
|
|
454
|
-
unfoldingMap[name] = [ false, [ columnMap[name] ] ];
|
|
455
|
-
}
|
|
322
|
+
function expandStructSelectItems(SELECT) {
|
|
323
|
+
for (let i = 0; i < SELECT.columns?.length; i++) {
|
|
324
|
+
const column = SELECT.columns[i];
|
|
325
|
+
if (column.expand)
|
|
326
|
+
continue; // skip expand
|
|
327
|
+
|
|
328
|
+
if (column.ref && column._element) {
|
|
329
|
+
const columnName = column.as || implicitAs(column.ref);
|
|
330
|
+
const branches = getBranches(column._element, columnName, effectiveType, pathDelimiter);
|
|
331
|
+
const paths = Object.keys(branches);
|
|
332
|
+
if (paths.length > 1 || paths[0] !== columnName) {
|
|
333
|
+
SELECT.columns[i] = Object.entries(branches).map(([ name, branch ]) => {
|
|
334
|
+
const elem = branch.steps.at(-1);
|
|
335
|
+
// TODO: Table alias somehow?
|
|
336
|
+
const ref = [ ...column.ref, ...branch.ref.slice(1) ];
|
|
337
|
+
const newColumn = { ref, as: name };
|
|
338
|
+
|
|
339
|
+
// If the calculated element has a type, use it.
|
|
340
|
+
if (elem['@Core.Computed'] && elem.type) // very crude - we could walk the branches to see if we are dealing with a real calc element?
|
|
341
|
+
newColumn.cast = { type: elem.type };
|
|
342
|
+
|
|
343
|
+
return newColumn;
|
|
344
|
+
});
|
|
456
345
|
}
|
|
457
346
|
}
|
|
458
347
|
}
|
|
459
348
|
|
|
460
|
-
|
|
461
|
-
error('query-unsupported-calc', SELECT.$path, { '#': 'std' });
|
|
462
|
-
}
|
|
463
|
-
else if (containsCalcOnRead) {
|
|
464
|
-
const newColumns = [];
|
|
465
|
-
if (hasStar && !starContainsCalculated)
|
|
466
|
-
newColumns.push('*');
|
|
467
|
-
for (const name in elements) {
|
|
468
|
-
const [ isViaStar, columns ] = unfoldingMap[name];
|
|
469
|
-
if (isViaStar && starContainsCalculated || !isViaStar)
|
|
470
|
-
newColumns.push(...columns);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
SELECT.columns = newColumns;
|
|
474
|
-
}
|
|
475
|
-
return cleanupCallbacks;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* Returns true if any leaf node is a calculated element on-read.
|
|
480
|
-
* On-write behaves like regular elements, hence they do not count here.
|
|
481
|
-
*
|
|
482
|
-
* @param {object} branches
|
|
483
|
-
* @returns {boolean}
|
|
484
|
-
*/
|
|
485
|
-
function hasCalcOnReadLeaf( branches ) {
|
|
486
|
-
for (const branchName in branches) {
|
|
487
|
-
const branch = branches[branchName].steps;
|
|
488
|
-
const leaf = branch[branch.length - 1];
|
|
489
|
-
if (hasOnReadValue(leaf))
|
|
490
|
-
return true;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
return false;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
/**
|
|
497
|
-
* Returns true if the branch/column uses a calc-on-read,
|
|
498
|
-
* for example in a filter.
|
|
499
|
-
*
|
|
500
|
-
* TODO: Enable calculated elements next to nested projections
|
|
501
|
-
*
|
|
502
|
-
* @param {object} branches
|
|
503
|
-
* @returns {boolean}
|
|
504
|
-
*/
|
|
505
|
-
function usesCalcOnRead( branches ) {
|
|
506
|
-
let returnValue = false;
|
|
507
|
-
for (const branchName in branches) {
|
|
508
|
-
const column = branches[branchName]?.steps[0]?._column;
|
|
509
|
-
if (column) {
|
|
510
|
-
applyTransformationsOnNonDictionary({ column }, 'column', {
|
|
511
|
-
// eslint-disable-next-line no-loop-func
|
|
512
|
-
ref: (parent) => {
|
|
513
|
-
if (hasOnReadValue(parent))
|
|
514
|
-
returnValue = true;
|
|
515
|
-
},
|
|
516
|
-
}, {
|
|
517
|
-
drillRef: true,
|
|
518
|
-
// skip subqueries and nested projections
|
|
519
|
-
// calculated elements and nested projections
|
|
520
|
-
// only conflict on same level
|
|
521
|
-
skipStandard: [ 'SELECT', 'expand', 'inline' ],
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
return returnValue;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* A leaf can reference a column which in turn references a real element - that might have a .value.
|
|
531
|
-
* Find such cases.
|
|
532
|
-
*
|
|
533
|
-
* @param {object} baseLeaf Leaf to start at
|
|
534
|
-
* @returns {boolean}
|
|
535
|
-
*/
|
|
536
|
-
function hasOnReadValue( baseLeaf ) {
|
|
537
|
-
const visited = new WeakSet();
|
|
538
|
-
const stack = [ baseLeaf ];
|
|
539
|
-
while (stack.length > 0) {
|
|
540
|
-
const leaf = stack.pop();
|
|
541
|
-
if (!visited.has(leaf)) { // Don't re-process things
|
|
542
|
-
if (leaf.value && !leaf.value.stored)
|
|
543
|
-
return true;
|
|
544
|
-
else if (leaf._art)
|
|
545
|
-
stack.push(leaf._art);
|
|
546
|
-
else if (leaf['@Core.Computed'] && leaf._column && leaf._column !== baseLeaf)
|
|
547
|
-
stack.push(leaf._column);
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
visited.add(leaf);
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
return false;
|
|
349
|
+
SELECT.columns = SELECT.columns.flat(Infinity);
|
|
554
350
|
}
|
|
555
351
|
|
|
556
352
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
getUtils, mergeTransformers, applyTransformations,
|
|
4
|
+
getUtils, mergeTransformers, applyTransformations, forEachDefinition,
|
|
5
5
|
} = require('../../model/csnUtils');
|
|
6
6
|
const transformUtils = require('../transformUtils');
|
|
7
7
|
const effectiveFlattening = require('./flattening');
|
|
@@ -19,6 +19,8 @@ const { rewriteCalculatedElementsInViews, processCalculatedElementsInEntities }
|
|
|
19
19
|
const { cloneFullCsn } = require('../../model/cloneCsn');
|
|
20
20
|
const { featureFlags } = require('../featureFlags');
|
|
21
21
|
const getServiceFilterFunction = require('./service');
|
|
22
|
+
const { traverseQuery } = require('../../model/csnRefs');
|
|
23
|
+
const { expandWildcard } = require('../db/expansion');
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* This is just a PoC for now!
|
|
@@ -46,6 +48,11 @@ function effectiveCsn( model, options, messageFunctions ) {
|
|
|
46
48
|
let { csnUtils } = transformerUtils;
|
|
47
49
|
csnUtils.initAllDefinitions();
|
|
48
50
|
|
|
51
|
+
forEachDefinition(csn, (def) => {
|
|
52
|
+
if (def.query || def.projection)
|
|
53
|
+
traverseQuery(def.query || def, null, null, query => expandWildcard(query, csnUtils, options));
|
|
54
|
+
});
|
|
55
|
+
|
|
49
56
|
// Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
|
|
50
57
|
const cleanup = validate.forRelationalDB(csn, {
|
|
51
58
|
...messageFunctions, csnUtils, ...csnUtils, csn, options,
|
|
@@ -210,7 +210,7 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
|
210
210
|
|
|
211
211
|
// needs to be performed after creating foreign keys for the entire model,
|
|
212
212
|
// because of multiple managed associations in refs
|
|
213
|
-
replaceForeignKeyRefsInExpressionAnnotations(csn,
|
|
213
|
+
replaceForeignKeyRefsInExpressionAnnotations(csn, csnUtils, { skipArtifact: isExternalServiceMember });
|
|
214
214
|
|
|
215
215
|
bindCsnReferenceOnly();
|
|
216
216
|
|