@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.
Files changed (62) hide show
  1. package/CHANGELOG.md +101 -3
  2. package/LICENSE +32 -0
  3. package/README.md +14 -2
  4. package/bin/cdsse.js +0 -3
  5. package/doc/CHANGELOG_BETA.md +1 -1
  6. package/doc/CHANGELOG_DEPRECATED.md +1 -1
  7. package/lib/base/message-registry.js +9 -2
  8. package/lib/base/messages.js +1 -1
  9. package/lib/base/model.js +2 -0
  10. package/lib/checks/existsExpressionsOnlyForeignKeys.js +16 -10
  11. package/lib/checks/existsMustEndInAssoc.js +1 -1
  12. package/lib/checks/existsMustNotStartWithDollarSelf.js +31 -0
  13. package/lib/checks/validator.js +4 -2
  14. package/lib/compiler/assert-consistency.js +3 -2
  15. package/lib/compiler/builtins.js +5 -6
  16. package/lib/compiler/checks.js +37 -26
  17. package/lib/compiler/define.js +1 -1
  18. package/lib/compiler/extend.js +39 -50
  19. package/lib/compiler/finalize-parse-cdl.js +1 -1
  20. package/lib/compiler/lsp-api.js +1 -1
  21. package/lib/compiler/populate.js +2 -2
  22. package/lib/compiler/propagator.js +29 -6
  23. package/lib/compiler/resolve.js +13 -3
  24. package/lib/compiler/shared.js +157 -133
  25. package/lib/compiler/tweak-assocs.js +87 -29
  26. package/lib/compiler/xpr-rewrite.js +164 -160
  27. package/lib/edm/annotations/edmJson.js +206 -37
  28. package/lib/edm/csn2edm.js +13 -0
  29. package/lib/edm/edmUtils.js +2 -2
  30. package/lib/gen/BaseParser.js +106 -72
  31. package/lib/gen/CdlGrammar.checksum +1 -1
  32. package/lib/gen/CdlParser.js +1501 -1509
  33. package/lib/json/to-csn.js +8 -5
  34. package/lib/language/genericAntlrParser.js +0 -0
  35. package/lib/main.js +19 -16
  36. package/lib/model/csnRefs.js +589 -521
  37. package/lib/model/csnUtils.js +8 -5
  38. package/lib/model/enrichCsn.js +1 -0
  39. package/lib/parsers/AstBuildingParser.js +73 -28
  40. package/lib/render/toCdl.js +2 -1
  41. package/lib/render/toHdbcds.js +6 -3
  42. package/lib/render/toSql.js +5 -0
  43. package/lib/transform/db/applyTransformations.js +1 -1
  44. package/lib/transform/db/assertUnique.js +4 -1
  45. package/lib/transform/db/assocsToQueries/transformExists.js +3 -10
  46. package/lib/transform/db/assocsToQueries/utils.js +0 -5
  47. package/lib/transform/db/cdsPersistence.js +17 -18
  48. package/lib/transform/db/expansion.js +179 -3
  49. package/lib/transform/db/flattening.js +16 -5
  50. package/lib/transform/db/rewriteCalculatedElements.js +79 -283
  51. package/lib/transform/effective/main.js +8 -1
  52. package/lib/transform/forOdata.js +1 -1
  53. package/lib/transform/forRelationalDB.js +21 -80
  54. package/lib/transform/localized.js +75 -127
  55. package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +89 -63
  56. package/lib/transform/transformUtils.js +23 -21
  57. package/lib/transform/translateAssocsToJoins.js +7 -5
  58. package/lib/transform/tupleExpansion.js +16 -3
  59. package/package.json +3 -3
  60. package/doc/DeprecatedOptions_v2.md +0 -150
  61. package/doc/NameResolution.md +0 -837
  62. 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[ref.length - 1];
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[parent.ref.length - 1] === parent.as) // for a simple s that was expanded - for s.substructure this would not apply
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
- else if (parent.ref[parent.ref.length - 1] !== lastRef &&
235
- (insideColumns(scopedPath) || insideKeys(scopedPath)) &&
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} messageFunctions
25
+ * @param {object} _messageFunctions
27
26
  */
28
- function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter, messageFunctions ) {
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
- const containsExpandInline = hasExpandInline(SELECT);
137
- let cleanupCallbacks;
138
- if (!SELECT.columns) // needs to happen for all subqueries!
139
- cleanupCallbacks = calculateColumns(elements, SELECT);
140
- else
141
- cleanupCallbacks = makeAllCalculatedElementsExplicitColumns(elements, SELECT, containsExpandInline);
142
-
143
- const name = SELECT.from.args ? undefined : SELECT.from.as || (SELECT.from.ref && implicitAs(SELECT.from.ref));
144
-
145
- if (!containsExpandInline) {
146
- applyTransformationsOnNonDictionary({ SELECT }, 'SELECT', {
147
- ref: (parent, prop, ref, p, root) => {
148
- const {
149
- art, env, links, scope,
150
- } = getRefInfo(parent, p);
151
-
152
- // calc element publishes association, treat as regular
153
- // unmanaged association
154
- const calcElementIsAssoc = art?.value && art.target;
155
- // TODO: Calculated elements on-write
156
- if (art?.value && !art.value.stored && !calcElementIsAssoc) {
157
- const alias = parent.as || implicitAs(parent.ref);
158
- // TODO: What about other scopes? expand/inline?
159
- const value = (scope !== 'ref-target') ? absolutifyPaths(env, art, ref, links, name).value : keepAssocStepsInRef(ref, links, art).value;
160
-
161
- // Is a shallow copy enough?
162
- if (art.value.cast)
163
- root[p[p.length - 1]] = { xpr: [ value ] };
164
- else
165
- root[p[p.length - 1]] = { ...value };
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
- if (p[p.length - 2] === 'columns')
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
- // If the calculated element has a type, use it. But only if the column did not have an explicit type.
173
- // Note: We should not check `art.type`, because we only need the type for columns, not filters.
174
- if (parent.cast)
175
- root[p[p.length - 1]].cast = parent.cast;
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
- // TODO: Copy annotations? May become relevant in the future
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
- cleanupCallbacks.forEach(fn => fn());
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
- * @param {CSN.QuerySelect} SELECT
191
- * @returns {boolean}
192
- */
193
- function hasExpandInline( SELECT ) {
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
- for (const column of SELECT.columns) {
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
- * For a `view V as select from E;` or a `entity P as projection on E;` calculate and
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 {CSN.Elements} elements
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 makeAllCalculatedElementsExplicitColumns( elements, SELECT, containsExpandInline ) {
405
- const cleanupCallbacks = [];
406
- const root = getDirectlyAddressableElements(SELECT);
407
- const columnMap = getColumnMap( { SELECT }, csnUtils );
408
- const hasStar = SELECT.columns.includes('*');
409
- const unfoldingMap = {};
410
- let starContainsCalculated = false;
411
- let containsCalcOnRead = false;
412
- for (const name in elements) {
413
- const originalRef = columnMap[name] && columnMap[name].ref || [ name ];
414
-
415
- if (columnMap[name] || hasStar) {
416
- let element;
417
- if (columnMap[name]?.expand || columnMap[name]?.inline)
418
- element = elements[name]; // only the direct thing in .elements has the .excluding respected properly!
419
- else
420
- element = columnMap[name]?._art || columnMap[name]?._element || root[name] || elements[name];
421
- const branches = getBranches(element, name, effectiveType, pathDelimiter); // TODO: is our elements[name] really the root[name]?
422
- if (hasCalcOnReadLeaf(branches)) {
423
- containsCalcOnRead = true;
424
- const columns = [];
425
- for (const branchName in branches) {
426
- const branch = branches[branchName];
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
- if (containsExpandInline && containsCalcOnRead) {
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, options, messageFunctions, csnUtils, { skipArtifact: isExternalServiceMember });
213
+ replaceForeignKeyRefsInExpressionAnnotations(csn, csnUtils, { skipArtifact: isExternalServiceMember });
214
214
 
215
215
  bindCsnReferenceOnly();
216
216