@sap/cds-compiler 3.4.4 → 3.5.2

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 (129) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +1 -0
  3. package/bin/cds_update_identifiers.js +5 -5
  4. package/bin/cdsc.js +12 -12
  5. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  6. package/doc/CHANGELOG_BETA.md +9 -1
  7. package/doc/CHANGELOG_DEPRECATED.md +2 -0
  8. package/lib/api/main.js +58 -59
  9. package/lib/api/options.js +4 -2
  10. package/lib/api/validate.js +2 -2
  11. package/lib/base/cleanSymbols.js +2 -3
  12. package/lib/base/dictionaries.js +6 -6
  13. package/lib/base/error.js +2 -2
  14. package/lib/base/keywords.js +6 -6
  15. package/lib/base/location.js +11 -12
  16. package/lib/base/message-registry.js +124 -28
  17. package/lib/base/messages.js +247 -179
  18. package/lib/base/model.js +14 -11
  19. package/lib/base/node-helpers.js +9 -10
  20. package/lib/base/optionProcessorHelper.js +138 -129
  21. package/lib/checks/actionsFunctions.js +5 -5
  22. package/lib/checks/annotationsOData.js +4 -4
  23. package/lib/checks/arrayOfs.js +1 -1
  24. package/lib/checks/cdsPersistence.js +1 -1
  25. package/lib/checks/checkForTypes.js +3 -3
  26. package/lib/checks/defaultValues.js +3 -3
  27. package/lib/checks/elements.js +7 -7
  28. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  29. package/lib/checks/foreignKeys.js +1 -1
  30. package/lib/checks/invalidTarget.js +4 -4
  31. package/lib/checks/managedInType.js +1 -1
  32. package/lib/checks/managedWithoutKeys.js +1 -1
  33. package/lib/checks/nonexpandableStructured.js +5 -3
  34. package/lib/checks/nullableKeys.js +1 -1
  35. package/lib/checks/onConditions.js +5 -6
  36. package/lib/checks/parameters.js +1 -1
  37. package/lib/checks/queryNoDbArtifacts.js +2 -2
  38. package/lib/checks/selectItems.js +4 -4
  39. package/lib/checks/sql-snippets.js +4 -4
  40. package/lib/checks/types.js +7 -7
  41. package/lib/checks/utils.js +4 -4
  42. package/lib/checks/validator.js +16 -13
  43. package/lib/compiler/.eslintrc.json +1 -1
  44. package/lib/compiler/assert-consistency.js +0 -1
  45. package/lib/compiler/builtins.js +1 -1
  46. package/lib/compiler/checks.js +73 -15
  47. package/lib/compiler/define.js +3 -7
  48. package/lib/compiler/extend.js +212 -32
  49. package/lib/compiler/finalize-parse-cdl.js +7 -2
  50. package/lib/compiler/index.js +17 -14
  51. package/lib/compiler/populate.js +2 -5
  52. package/lib/compiler/propagator.js +2 -0
  53. package/lib/compiler/shared.js +23 -12
  54. package/lib/compiler/tweak-assocs.js +5 -6
  55. package/lib/compiler/utils.js +6 -0
  56. package/lib/edm/annotations/genericTranslation.js +553 -319
  57. package/lib/edm/annotations/preprocessAnnotations.js +39 -35
  58. package/lib/edm/csn2edm.js +88 -75
  59. package/lib/edm/edm.js +17 -3
  60. package/lib/edm/edmAnnoPreprocessor.js +5 -5
  61. package/lib/edm/edmPreprocessor.js +106 -76
  62. package/lib/edm/edmUtils.js +41 -2
  63. package/lib/gen/Dictionary.json +34 -0
  64. package/lib/gen/language.checksum +1 -1
  65. package/lib/gen/language.interp +66 -63
  66. package/lib/gen/language.tokens +81 -81
  67. package/lib/gen/languageLexer.interp +4 -10
  68. package/lib/gen/languageLexer.js +854 -869
  69. package/lib/gen/languageLexer.tokens +79 -81
  70. package/lib/gen/languageParser.js +14360 -14146
  71. package/lib/inspect/inspectModelStatistics.js +2 -2
  72. package/lib/inspect/inspectPropagation.js +6 -6
  73. package/lib/inspect/inspectUtils.js +2 -2
  74. package/lib/json/from-csn.js +82 -40
  75. package/lib/json/to-csn.js +82 -157
  76. package/lib/language/.eslintrc.json +1 -4
  77. package/lib/language/genericAntlrParser.js +59 -38
  78. package/lib/language/language.g4 +1508 -1490
  79. package/lib/language/multiLineStringParser.js +1 -1
  80. package/lib/main.js +3 -3
  81. package/lib/model/csnUtils.js +130 -122
  82. package/lib/model/revealInternalProperties.js +1 -1
  83. package/lib/model/sortViews.js +4 -6
  84. package/lib/modelCompare/utils/filter.js +4 -3
  85. package/lib/optionProcessor.js +5 -0
  86. package/lib/render/DuplicateChecker.js +1 -1
  87. package/lib/render/manageConstraints.js +12 -12
  88. package/lib/render/toCdl.js +225 -159
  89. package/lib/render/toHdbcds.js +63 -63
  90. package/lib/render/toRename.js +5 -5
  91. package/lib/render/toSql.js +55 -65
  92. package/lib/render/utils/common.js +20 -37
  93. package/lib/render/utils/delta.js +3 -3
  94. package/lib/render/utils/sql.js +22 -6
  95. package/lib/render/utils/stringEscapes.js +3 -3
  96. package/lib/transform/db/applyTransformations.js +3 -3
  97. package/lib/transform/db/assertUnique.js +13 -12
  98. package/lib/transform/db/associations.js +5 -5
  99. package/lib/transform/db/cdsPersistence.js +10 -8
  100. package/lib/transform/db/constraints.js +14 -14
  101. package/lib/transform/db/expansion.js +20 -22
  102. package/lib/transform/db/flattening.js +24 -42
  103. package/lib/transform/db/groupByOrderBy.js +3 -3
  104. package/lib/transform/db/temporal.js +6 -6
  105. package/lib/transform/db/transformExists.js +23 -23
  106. package/lib/transform/db/views.js +16 -16
  107. package/lib/transform/draft/db.js +10 -10
  108. package/lib/transform/draft/odata.js +2 -2
  109. package/lib/transform/forOdataNew.js +12 -40
  110. package/lib/transform/forRelationalDB.js +17 -7
  111. package/lib/transform/localized.js +2 -2
  112. package/lib/transform/odata/toFinalBaseType.js +41 -27
  113. package/lib/transform/odata/typesExposure.js +106 -62
  114. package/lib/transform/parseExpr.js +209 -106
  115. package/lib/transform/transformUtilsNew.js +2 -2
  116. package/lib/transform/translateAssocsToJoins.js +24 -19
  117. package/lib/transform/universalCsn/coreComputed.js +10 -10
  118. package/lib/transform/universalCsn/universalCsnEnricher.js +26 -26
  119. package/lib/transform/universalCsn/utils.js +3 -3
  120. package/lib/utils/file.js +5 -5
  121. package/lib/utils/moduleResolve.js +13 -13
  122. package/lib/utils/objectUtils.js +6 -6
  123. package/lib/utils/term.js +5 -2
  124. package/lib/utils/timetrace.js +51 -24
  125. package/package.json +5 -7
  126. package/share/messages/check-proper-type-of.md +1 -1
  127. package/share/messages/message-explanations.json +1 -1
  128. package/share/messages/redirected-to-complex.md +4 -4
  129. package/share/messages/{syntax-expecting-integer.md → syntax-expecting-unsigned-int.md} +7 -4
@@ -43,7 +43,7 @@ const { version } = require('../../package.json');
43
43
  * Get utility functions for a given CSN. Re-exports functions of `csnRefs()`.
44
44
  * @param {CSN.Model} model (Compact) CSN model
45
45
  */
46
- function getUtils(model, universalReady) {
46
+ function getUtils( model, universalReady ) {
47
47
  const _csnRefs = csnRefs(model, universalReady);
48
48
  const { artifactRef } = _csnRefs;
49
49
  /** Cache for getFinalBaseTypeWithProps(). Specific to the current model. */
@@ -77,7 +77,7 @@ function getUtils(model, universalReady) {
77
77
  * @param {CSN.Query} query
78
78
  * @returns {object}
79
79
  */
80
- function get$combined(query) {
80
+ function get$combined( query ) {
81
81
  return getSources(query);
82
82
  }
83
83
 
@@ -91,7 +91,7 @@ function getUtils(model, universalReady) {
91
91
  * @param {boolean} [isSubquery]
92
92
  * @returns {object} Map of sources
93
93
  */
94
- function getSources(query, isSubquery = false) {
94
+ function getSources( query, isSubquery = false ) {
95
95
  // Remark CW: better just a while along query.SET.args[0]
96
96
  if (query.SET) {
97
97
  if (query.SET.args[0].SELECT && query.SET.args[0].SELECT.elements)
@@ -124,7 +124,7 @@ function getUtils(model, universalReady) {
124
124
  return {};
125
125
  }
126
126
 
127
- function walkArgs(args) {
127
+ function walkArgs( args ) {
128
128
  let elements = Object.create(null);
129
129
  for (const arg of args) {
130
130
  if (arg.args) {
@@ -149,7 +149,7 @@ function getUtils(model, universalReady) {
149
149
  * @param {object} mapB Map b - will not be returned
150
150
  * @returns {object} mapA
151
151
  */
152
- function mergeElementMaps(mapA, mapB) {
152
+ function mergeElementMaps( mapA, mapB ) {
153
153
  for (const elementName in mapB) {
154
154
  if (!mapA[elementName])
155
155
  mapA[elementName] = [];
@@ -170,7 +170,7 @@ function getUtils(model, universalReady) {
170
170
  * @param {any} [errorParent] Parent name to use for error messages, ref before alias
171
171
  * @returns {object} existingMap
172
172
  */
173
- function mergeElementsIntoMap(existingMap, elements, $location, parent, errorParent) {
173
+ function mergeElementsIntoMap( existingMap, elements, $location, parent, errorParent ) {
174
174
  for (const elementName in elements) {
175
175
  const element = elements[elementName];
176
176
  if (!existingMap[elementName])
@@ -188,7 +188,7 @@ function getUtils(model, universalReady) {
188
188
  * Return the name part of the artifact name - no namespace etc.
189
189
  * @param {string|object} name Absolute name of the artifact
190
190
  */
191
- function getBaseName(name) {
191
+ function getBaseName( name ) {
192
192
  if (!name)
193
193
  return name;
194
194
 
@@ -202,7 +202,7 @@ function getUtils(model, universalReady) {
202
202
  * Return the left-most, primary source of the given query.
203
203
  * @param {*} query Definition's query object
204
204
  */
205
- function getQueryPrimarySource(query) {
205
+ function getQueryPrimarySource( query ) {
206
206
  if (!query)
207
207
  return undefined;
208
208
  else if (query.SELECT)
@@ -224,7 +224,7 @@ function getUtils(model, universalReady) {
224
224
  * Create an object to track visited objects identified by a unique string.
225
225
  * @param {string} [initialId] Initial entry (optional)
226
226
  */
227
- function createVisited(initialId) {
227
+ function createVisited( initialId ) {
228
228
  const visited = Object.create(null);
229
229
  check(initialId);
230
230
  return { check };
@@ -234,7 +234,7 @@ function getUtils(model, universalReady) {
234
234
  * add it to the list of visited identifiers.
235
235
  * @param {string} id unique identifier
236
236
  */
237
- function check(id) {
237
+ function check( id ) {
238
238
  if (!id)
239
239
  return;
240
240
  if (visited[id])
@@ -248,7 +248,7 @@ function getUtils(model, universalReady) {
248
248
  * Get the CSN definition for an artifact name.
249
249
  * @param {string} defName Absolute name of the artifact
250
250
  */
251
- function getCsnDef(defName) {
251
+ function getCsnDef( defName ) {
252
252
  if (model.definitions[defName])
253
253
  return model.definitions[defName];
254
254
  throw new ModelError(`Nonexistent definition in the model: '${ defName }'`);
@@ -261,7 +261,7 @@ function getUtils(model, universalReady) {
261
261
  * @param {object} obj
262
262
  * @returns {boolean}
263
263
  */
264
- function isStructured(obj) {
264
+ function isStructured( obj ) {
265
265
  return !!(obj.elements || (obj.type && getFinalBaseTypeWithProps(obj.type)?.elements));
266
266
  }
267
267
 
@@ -272,7 +272,7 @@ function getUtils(model, universalReady) {
272
272
  * @param {string} typeName Absolute type name
273
273
  * @returns {object}
274
274
  */
275
- function getFinalTypeDef(typeName) {
275
+ function getFinalTypeDef( typeName ) {
276
276
  const visited = createVisited(typeName);
277
277
  let type = model.definitions[typeName];
278
278
  if (!type)
@@ -291,7 +291,7 @@ function getUtils(model, universalReady) {
291
291
  * @param {string} typeName Absolute type name
292
292
  * @returns {string}
293
293
  */
294
- function getFinalType(typeName) {
294
+ function getFinalType( typeName ) {
295
295
  const visited = createVisited(typeName);
296
296
  let type = model.definitions[typeName];
297
297
  while (type && type.type) {
@@ -304,7 +304,7 @@ function getUtils(model, universalReady) {
304
304
 
305
305
  // Return true if 'node' is a managed association element
306
306
  // TODO: what about elements having a type, which (finally) is an assoc?
307
- function isManagedAssociation(node) {
307
+ function isManagedAssociation( node ) {
308
308
  return node.target !== undefined && node.on === undefined && node.keys;
309
309
  }
310
310
 
@@ -313,7 +313,7 @@ function getUtils(model, universalReady) {
313
313
  * to any of them.
314
314
  * @param {string} typeName Absolute type name
315
315
  */
316
- function isAssocOrComposition(typeName) {
316
+ function isAssocOrComposition( typeName ) {
317
317
  if (typeName === 'cds.Association' || typeName === 'cds.Composition')
318
318
  return true;
319
319
  const visited = createVisited(typeName);
@@ -331,7 +331,7 @@ function getUtils(model, universalReady) {
331
331
  * Returns if a type is an association or a typedef to it.
332
332
  * @param {string} typeName Absolute type name
333
333
  */
334
- function isAssociation(typeName) {
334
+ function isAssociation( typeName ) {
335
335
  if (typeName === 'cds.Association')
336
336
  return true;
337
337
  const visited = createVisited(typeName);
@@ -349,7 +349,7 @@ function getUtils(model, universalReady) {
349
349
  * Returns if a type is an composition or a typedef to it.
350
350
  * @param {string} typeName Absolute type name
351
351
  */
352
- function isComposition(typeName) {
352
+ function isComposition( typeName ) {
353
353
  if (typeName === 'cds.Composition')
354
354
  return true;
355
355
  const visited = createVisited(typeName);
@@ -367,7 +367,7 @@ function getUtils(model, universalReady) {
367
367
  * Return the namespace part of the artifact name.
368
368
  * @param {string} name Absolute name of artifact
369
369
  */
370
- function getNamespaceOfArtifact(name) {
370
+ function getNamespaceOfArtifact( name ) {
371
371
  let lastDotIdx = name.lastIndexOf('.');
372
372
  if (lastDotIdx === -1)
373
373
  return undefined;
@@ -386,7 +386,7 @@ function getUtils(model, universalReady) {
386
386
  * Return the context part of the artifact name if any.
387
387
  * @param {string} name Absolute name of artifact
388
388
  */
389
- function getContextOfArtifact(name) {
389
+ function getContextOfArtifact( name ) {
390
390
  let lastDotIdx = name.lastIndexOf('.');
391
391
  while (model.definitions[name]) {
392
392
  if (model.definitions[name].kind === 'context' || model.definitions[name].kind === 'service')
@@ -406,7 +406,7 @@ function getUtils(model, universalReady) {
406
406
  * @param {any} theValue string value of the annotation
407
407
  * @param {any} node Node to add the annotation to
408
408
  */
409
- function addStringAnnotationTo(absoluteName, theValue, node) {
409
+ function addStringAnnotationTo( absoluteName, theValue, node ) {
410
410
  // Sanity check
411
411
  if (!absoluteName.startsWith('@'))
412
412
  throw Error(`Annotation name should start with "@": ${ absoluteName }`);
@@ -425,7 +425,7 @@ function getUtils(model, universalReady) {
425
425
  * @param {string} artifactName Absolute name of artifact
426
426
  * @returns {string|null}
427
427
  */
428
- function getServiceName(artifactName) {
428
+ function getServiceName( artifactName ) {
429
429
  for (;;) {
430
430
  const idx = artifactName.lastIndexOf('.');
431
431
  if (idx === -1)
@@ -455,11 +455,11 @@ function getUtils(model, universalReady) {
455
455
  * @param {any} transformers Object defining transformer functions
456
456
  * @returns {object}
457
457
  */
458
- function cloneWithTransformations(rootNode, transformers) {
458
+ function cloneWithTransformations( rootNode, transformers ) {
459
459
  return transformNode(rootNode);
460
460
 
461
461
  // This general transformation function will be applied to each node recursively
462
- function transformNode(node) {
462
+ function transformNode( node ) {
463
463
  // Return primitive values and null unchanged, but let objects and dictionaries through
464
464
  // (Note that 'node instanceof Object' would be false for dictionaries).
465
465
  if (node === null || typeof node !== 'object')
@@ -488,7 +488,7 @@ function getUtils(model, universalReady) {
488
488
  }
489
489
  }
490
490
 
491
- function _normalizeTypeRef(type) {
491
+ function _normalizeTypeRef( type ) {
492
492
  if (type && typeof type === 'object' && type.ref?.length === 1)
493
493
  type = type.ref[0]; // simplify type: no element -> simple string can be used
494
494
  return type;
@@ -512,7 +512,7 @@ function getUtils(model, universalReady) {
512
512
  * @param {string|object} type Type as string or type ref, i.e. `{ ref: [...] }`
513
513
  * @returns {object|null}
514
514
  */
515
- function getFinalBaseTypeWithProps(type) {
515
+ function getFinalBaseTypeWithProps( type ) {
516
516
  type = _normalizeTypeRef(type);
517
517
  if (!type)
518
518
  return null;
@@ -566,7 +566,7 @@ function getUtils(model, universalReady) {
566
566
  *
567
567
  * @param {object} typeProps
568
568
  */
569
- function _cacheResolved(typeProps) {
569
+ function _cacheResolved( typeProps ) {
570
570
  finalBaseTypeCache[resolvedKey] = typeProps;
571
571
  return typeProps;
572
572
  }
@@ -577,7 +577,7 @@ function getUtils(model, universalReady) {
577
577
  * @param obj
578
578
  * @returns {boolean} True, if structured/arrayed/invalid, false if scalar.
579
579
  */
580
- function _cacheNonScalar(obj) {
580
+ function _cacheNonScalar( obj ) {
581
581
  if (!obj) { // Reference has no proper type, e.g. due to `type of View:calculated`.
582
582
  _cacheResolved(null);
583
583
  return true;
@@ -597,7 +597,7 @@ function getUtils(model, universalReady) {
597
597
  * @param {object} target
598
598
  * @param {object} source
599
599
  */
600
- function _copyTypeProps(target, source) {
600
+ function _copyTypeProps( target, source ) {
601
601
  target.type = source.type;
602
602
  const typeProps = [ ...typeParameters.list, 'enum', 'default', 'localized' ];
603
603
  for (const param of typeProps) {
@@ -622,7 +622,7 @@ function getUtils(model, universalReady) {
622
622
  * @param {object} csn Top-level CSN. You can pass non-dictionary values.
623
623
  * @param {CSN.Options} options CSN Options, only used for `dictionaryPrototype`, `testMode`, and `testSortCsn`
624
624
  */
625
- function cloneCsnNonDict(csn, options) {
625
+ function cloneCsnNonDict( csn, options ) {
626
626
  return sortCsn(csn, options);
627
627
  }
628
628
 
@@ -641,7 +641,7 @@ function cloneCsnNonDict(csn, options) {
641
641
  * @param {CSN.Options} options Only cloneOptions.dictionaryPrototype is
642
642
  * used and cloneOptions are passed to sortCsn().
643
643
  */
644
- function cloneCsnDictionary(csn, options) {
644
+ function cloneCsnDictionary( csn, options ) {
645
645
  return _cloneCsnDictionary(csn, options);
646
646
  }
647
647
 
@@ -652,7 +652,7 @@ function cloneCsnDictionary(csn, options) {
652
652
  * @param {any} value
653
653
  * @returns {any}
654
654
  */
655
- function cloneAnnotationValue(value) {
655
+ function cloneAnnotationValue( value ) {
656
656
  return _cloneAnnotationValue( value );
657
657
  }
658
658
 
@@ -685,7 +685,7 @@ function forEachDefinition( csn, callback, iterateOptions = {} ) {
685
685
  * @param constructCallback
686
686
  */
687
687
  function forEachMember( construct, callback, path = [], ignoreIgnore = true, iterateOptions = {},
688
- constructCallback = (_construct, _prop, _path) => {}) {
688
+ constructCallback = (_construct, _prop, _path) => {} ) {
689
689
  // Allow processing _ignored elements if requested
690
690
  if (ignoreIgnore && construct._ignore)
691
691
  return;
@@ -728,7 +728,7 @@ function forEachMember( construct, callback, path = [], ignoreIgnore = true, ite
728
728
  * @param {constructCallback|constructCallback[]} callback
729
729
  */
730
730
  function forEachMemberWithQuery( construct, callback, path = [], ignoreIgnore = true, iterateOptions = {},
731
- constructCallback = (_construct, _prop, _path) => {}) {
731
+ constructCallback = (_construct, _prop, _path) => {} ) {
732
732
  forEachMember(construct, callback, path, ignoreIgnore, iterateOptions, constructCallback);
733
733
  if (construct.query) {
734
734
  forAllQueries(construct.query, (q, p) => {
@@ -751,7 +751,7 @@ function forEachMemberWithQuery( construct, callback, path = [], ignoreIgnore =
751
751
  * @param {constructCallback|constructCallback[]} callback
752
752
  */
753
753
  function forEachMemberRecursively( construct, callback, path = [], ignoreIgnore = true, iterateOptions = {},
754
- constructCallback = (_construct, _prop, _path) => {}) {
754
+ constructCallback = (_construct, _prop, _path) => {} ) {
755
755
  forEachMember( construct, ( member, memberName, prop, subpath, parent ) => {
756
756
  if (Array.isArray(callback))
757
757
  callback.forEach(cb => cb( member, memberName, prop, subpath, parent ));
@@ -775,7 +775,7 @@ function forEachMemberRecursively( construct, callback, path = [], ignoreIgnore
775
775
  * @param {constructCallback|constructCallback[]} callback
776
776
  */
777
777
  function forEachMemberRecursivelyWithQuery( construct, callback, path = [], ignoreIgnore = true, iterateOptions = {},
778
- constructCallback = (_construct, _prop, _path) => {}) {
778
+ constructCallback = (_construct, _prop, _path) => {} ) {
779
779
  forEachMemberRecursively(construct, callback, path, ignoreIgnore, iterateOptions, constructCallback);
780
780
  if (construct.query) {
781
781
  forAllQueries(construct.query, (q, p) => {
@@ -798,7 +798,7 @@ function forEachMemberRecursivelyWithQuery( construct, callback, path = [], igno
798
798
  * @param {CSN.Path} path
799
799
  * @param {object} iterateOptions can be used to skip certain kinds from being iterated
800
800
  */
801
- function forEachGeneric( construct, prop, callback, path = [], iterateOptions = {}) {
801
+ function forEachGeneric( construct, prop, callback, path = [], iterateOptions = {} ) {
802
802
  const dict = construct[prop];
803
803
  for (const name in dict) {
804
804
  if (!Object.prototype.hasOwnProperty.call(dict, name))
@@ -810,7 +810,7 @@ function forEachGeneric( construct, prop, callback, path = [], iterateOptions =
810
810
  continue;
811
811
  executeCallbacks( dictObj, name );
812
812
  }
813
- function executeCallbacks(o, name ) {
813
+ function executeCallbacks( o, name ) {
814
814
  const p = iterateOptions.pathWithoutProp ? [ name ] : [ prop, name ];
815
815
 
816
816
  if (Array.isArray(callback))
@@ -820,72 +820,66 @@ function forEachGeneric( construct, prop, callback, path = [], iterateOptions =
820
820
  }
821
821
  }
822
822
 
823
+ const queryTraversalProperties = [ 'args', 'xpr', 'columns', 'where', 'having' ];
824
+
823
825
  /**
824
- * @param {CSN.Query} mainQuery
825
- * @param {queryCallback|queryCallback[]} queryCallback
826
+ * @param {CSN.Query} query
827
+ * @param {queryCallback} queryCallback
826
828
  * @param {CSN.Path} path
827
829
  */
828
- function forAllQueries(mainQuery, queryCallback, path = []) {
829
- return traverseQuery(mainQuery, queryCallback, path);
830
- function traverseQuery( query, callback, queryPath ) {
831
- if (query.SELECT) {
832
- // The projection is turned into a normalized query - there
833
- // is no real SELECT, it is fake
834
- if (!(path.length === 3 && path[2] === 'projection'))
835
- queryPath.push('SELECT');
836
- executeCallbacks();
837
- query = query.SELECT;
838
- }
839
- else if (query.SET) {
840
- queryPath.push('SET');
841
- executeCallbacks();
842
- query = query.SET;
843
- }
830
+ function forAllQueries( query, queryCallback, path ) {
831
+ if (query.SELECT) {
832
+ // The projection is turned into a normalized query - there
833
+ // is no real SELECT, it is fake
834
+ if (!(path.length === 3 && path[2] === 'projection'))
835
+ path.push('SELECT');
836
+ queryCallback( query, path );
837
+ query = query.SELECT;
838
+ }
839
+ else if (query.SET) {
840
+ path.push('SET');
841
+ queryCallback( query, path );
842
+ query = query.SET;
843
+ }
844
844
 
845
- if (query.from)
846
- traverseFrom( query.from, callback, queryPath.concat([ 'from' ]) );
845
+ if (query.from)
846
+ traverseFrom( query.from, queryCallback, [ ...path, 'from' ] );
847
847
 
848
- for (const prop of [ 'args', 'xpr', 'columns', 'where', 'having' ]) {
849
- // all properties which could have sub queries (directly or indirectly)
850
- const expr = query[prop];
851
- if (expr && typeof expr === 'object') {
852
- if (Array.isArray(expr)) {
853
- for (let i = 0; i < expr.length; i++)
854
- traverseQuery(expr[i], callback, queryPath.concat([ prop, i ]));
855
- }
856
- else {
857
- for (const argName of Object.keys( expr ))
858
- traverseQuery(expr[argName], callback, queryPath.concat([ prop, argName ]));
859
- }
848
+ for (const prop of queryTraversalProperties) {
849
+ // all properties which could have sub queries (directly or indirectly)
850
+ const expr = query[prop];
851
+ if (expr && typeof expr === 'object') {
852
+ if (Array.isArray(expr)) {
853
+ for (let i = 0; i < expr.length; i++)
854
+ forAllQueries(expr[i], queryCallback, [ ...path, prop, i ]);
855
+ }
856
+ else {
857
+ for (const argName of Object.keys( expr ))
858
+ forAllQueries(expr[argName], queryCallback, [ ...path, prop, argName ]);
860
859
  }
861
- }
862
- function executeCallbacks() {
863
- if (Array.isArray(callback))
864
- callback.forEach(cb => cb( query, queryPath ));
865
- else
866
- callback( query, queryPath );
867
860
  }
868
861
  }
862
+ }
869
863
 
870
- /**
871
- * @param {CSN.QueryFrom} from
872
- * @param {Function} callback
873
- * @param {CSN.Path} csnPath
874
- */
875
- function traverseFrom( from, callback, csnPath = [] ) {
876
- if (from.ref) { // ignore
877
- return;
878
- }
879
- else if (from.args) { // join
880
- for (let i = 0; i < from.args.length; i++)
881
- traverseFrom(from.args[i], callback, csnPath.concat([ 'args', i ]));
882
- }
883
- else {
884
- traverseQuery( from, callback, csnPath );
885
- } // sub query in FROM
864
+ /**
865
+ * @param {CSN.QueryFrom} from
866
+ * @param {queryCallback} queryCallback
867
+ * @param {CSN.Path} path
868
+ */
869
+ function traverseFrom( from, queryCallback, path = [] ) {
870
+ if (from.ref) {
871
+ // ignore
872
+ }
873
+ else if (from.args) { // join
874
+ for (let i = 0; i < from.args.length; i++)
875
+ traverseFrom(from.args[i], queryCallback, [ ...path, 'args', i ]);
876
+ }
877
+ else {
878
+ forAllQueries( from, queryCallback, path ); // sub query in FROM
886
879
  }
887
880
  }
888
881
 
882
+
889
883
  /**
890
884
  * Compare a given annotation value with an expectation value and return
891
885
  *
@@ -907,7 +901,7 @@ function forAllQueries(mainQuery, queryCallback, path = []) {
907
901
  * @param {boolean} caseInsensitive
908
902
  * @returns {boolean}
909
903
  */
910
- function hasAnnotationValue(artifact, annotationName, expected = true, caseInsensitive = false) {
904
+ function hasAnnotationValue( artifact, annotationName, expected = true, caseInsensitive = false ) {
911
905
  if (expected === false)
912
906
  return artifact[annotationName] === expected || artifact[annotationName] === null;
913
907
  else if (typeof artifact[annotationName] === 'string' && caseInsensitive === true)
@@ -925,7 +919,7 @@ function hasAnnotationValue(artifact, annotationName, expected = true, caseInsen
925
919
  * @param {CSN.Element} elementCsn
926
920
  * @param {ODataOptions} options EDM specific options
927
921
  */
928
- function isEdmPropertyRendered(elementCsn, options) {
922
+ function isEdmPropertyRendered( elementCsn, options ) {
929
923
  // FKs are rendered in
930
924
  // V2/V4 flat: always on
931
925
  // V4 struct: on/off
@@ -962,7 +956,7 @@ function isEdmPropertyRendered(elementCsn, options) {
962
956
  * @returns {string} The resulting database name for (absolute) 'artifactName', depending on the current naming mode.
963
957
  */
964
958
  // eslint-disable-next-line no-unused-vars
965
- function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn, sqlDialect = 'plain') {
959
+ function getArtifactDatabaseNameOf( artifactName, sqlMapping, csn, sqlDialect = 'plain' ) {
966
960
  if (csn && typeof csn === 'object' && csn.definitions) {
967
961
  isValidMappingDialectCombi(sqlDialect, sqlMapping);
968
962
  if (sqlMapping === 'quoted' || sqlMapping === 'hdbcds')
@@ -992,7 +986,7 @@ function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn, sqlDialect = '
992
986
  * @param {string} artifactName Artifact name to use
993
987
  * @returns {string} The resulting name
994
988
  */
995
- function getResultingName(csn, namingMode, artifactName) {
989
+ function getResultingName( csn, namingMode, artifactName ) {
996
990
  if (namingMode === 'plain' || artifactName.indexOf('.') === -1)
997
991
  return artifactName;
998
992
 
@@ -1026,7 +1020,7 @@ function getResultingName(csn, namingMode, artifactName) {
1026
1020
  * @param {CSN.Model} csn
1027
1021
  * @returns {string[]|null} Array of at most 2 strings: if both: [prefix, suffix], otherwise just one - or null
1028
1022
  */
1029
- function getUnderscoredName(startIndex, parts, csn) {
1023
+ function getUnderscoredName( startIndex, parts, csn ) {
1030
1024
  for (let i = startIndex; i < parts.length; i++) {
1031
1025
  const namePart = parts.slice(0, i).join('.');
1032
1026
  const art = csn.definitions[namePart];
@@ -1058,7 +1052,7 @@ function getUnderscoredName(startIndex, parts, csn) {
1058
1052
  return null;
1059
1053
  }
1060
1054
 
1061
- function isValidMappingDialectCombi(sqlDialect, sqlMapping) {
1055
+ function isValidMappingDialectCombi( sqlDialect, sqlMapping ) {
1062
1056
  if (sqlMapping === 'hdbcds' && sqlDialect !== 'hana')
1063
1057
  throw new Error(`sqlMapping "hdbcds" must only be used with sqlDialect "hana" - found: ${ sqlDialect }`);
1064
1058
  return true;
@@ -1082,7 +1076,7 @@ function isValidMappingDialectCombi(sqlDialect, sqlMapping) {
1082
1076
  * @returns {string} The resulting database element name for 'elemName', depending on the current naming mode.
1083
1077
  */
1084
1078
  // eslint-disable-next-line no-unused-vars
1085
- function getElementDatabaseNameOf(elemName, sqlMapping, sqlDialect = 'plain') {
1079
+ function getElementDatabaseNameOf( elemName, sqlMapping, sqlDialect = 'plain' ) {
1086
1080
  isValidMappingDialectCombi(sqlDialect, sqlMapping);
1087
1081
  if (sqlMapping === 'hdbcds')
1088
1082
  return elemName;
@@ -1134,7 +1128,7 @@ function setDependencies( csn, refs = csnRefs(csn) ) {
1134
1128
  cleanup, csn, _dependents, _dependencies,
1135
1129
  };
1136
1130
 
1137
- function handleArgs(artifact, artifactName, args) {
1131
+ function handleArgs( artifact, artifactName, args ) {
1138
1132
  for (const arg of args) {
1139
1133
  if (arg.args)
1140
1134
  handleArgs(artifact, artifactName, arg.args);
@@ -1143,20 +1137,20 @@ function setDependencies( csn, refs = csnRefs(csn) ) {
1143
1137
  }
1144
1138
  }
1145
1139
 
1146
- function handleDependency(dependency, dependant, dependantName) {
1140
+ function handleDependency( dependency, dependant, dependantName ) {
1147
1141
  dependant[_dependencies].add(dependency);
1148
1142
  initDependents(dependency);
1149
1143
  dependency[_dependents][dependantName] = dependant;
1150
1144
  }
1151
1145
 
1152
- function initDependents(obj) {
1146
+ function initDependents( obj ) {
1153
1147
  if (!obj[_dependents]) {
1154
1148
  obj[_dependents] = Object.create(null);
1155
1149
  cleanup.push(() => delete obj[_dependents]);
1156
1150
  }
1157
1151
  }
1158
1152
 
1159
- function initDependencies(obj) {
1153
+ function initDependencies( obj ) {
1160
1154
  if (!obj[_dependencies]) {
1161
1155
  obj[_dependencies] = new Set();
1162
1156
  cleanup.push(() => delete obj[_dependencies]);
@@ -1171,7 +1165,7 @@ function setDependencies( csn, refs = csnRefs(csn) ) {
1171
1165
  * @param {CSN.Artifact} art
1172
1166
  * @returns {boolean}
1173
1167
  */
1174
- function isPersistedOnDatabase(art) {
1168
+ function isPersistedOnDatabase( art ) {
1175
1169
  return !(art.kind === 'entity' && (art.abstract || hasAnnotationValue(art, '@cds.persistence.skip')));
1176
1170
  }
1177
1171
  /**
@@ -1180,7 +1174,7 @@ function isPersistedOnDatabase(art) {
1180
1174
  * @param {CSN.Artifact} artifact
1181
1175
  * @returns {boolean}
1182
1176
  */
1183
- function isPersistedAsView(artifact) {
1177
+ function isPersistedAsView( artifact ) {
1184
1178
  return artifact && artifact.kind === 'entity' &&
1185
1179
  !artifact._ignore &&
1186
1180
  !artifact.abstract &&
@@ -1194,7 +1188,7 @@ function isPersistedAsView(artifact) {
1194
1188
  * @param {CSN.Artifact} artifact
1195
1189
  * @returns {boolean}
1196
1190
  */
1197
- function isPersistedAsTable(artifact) {
1191
+ function isPersistedAsTable( artifact ) {
1198
1192
  return artifact.kind === 'entity' &&
1199
1193
  !artifact._ignore &&
1200
1194
  !artifact.abstract &&
@@ -1219,7 +1213,7 @@ function generatedByCompilerVersion() {
1219
1213
  * @param {CSN.Artifact} art Artifact with a query or a projection
1220
1214
  * @returns {object} Object with a query property.
1221
1215
  */
1222
- function getNormalizedQuery(art) {
1216
+ function getNormalizedQuery( art ) {
1223
1217
  if (art.projection)
1224
1218
  return { query: { SELECT: art.projection } };
1225
1219
 
@@ -1234,7 +1228,7 @@ function getNormalizedQuery(art) {
1234
1228
  * @param {CSN.Model} csn
1235
1229
  * @returns {string} Name of the root
1236
1230
  */
1237
- function getRootArtifactName(artifactName, csn) {
1231
+ function getRootArtifactName( artifactName, csn ) {
1238
1232
  const parts = artifactName.split('.');
1239
1233
 
1240
1234
  if (parts.length === 1)
@@ -1262,7 +1256,7 @@ function getRootArtifactName(artifactName, csn) {
1262
1256
  // 'foo.bar.wiz' => 'wiz'
1263
1257
  // 'foo' => 'foo';
1264
1258
  // 'foo::bar' => 'bar'
1265
- function getLastPartOf(name) {
1259
+ function getLastPartOf( name ) {
1266
1260
  return name.substring(name.search(/[^.:]+$/));
1267
1261
  }
1268
1262
 
@@ -1272,7 +1266,7 @@ function getLastPartOf(name) {
1272
1266
  // ['foo.bar.wiz'] => 'wiz'
1273
1267
  // ['foo'] => 'foo';
1274
1268
  // ['foo::bar'] => 'bar'
1275
- function getLastPartOfRef(ref) {
1269
+ function getLastPartOfRef( ref ) {
1276
1270
  const lastPathStep = ref[ref.length - 1];
1277
1271
  return getLastPartOf(lastPathStep.id || lastPathStep);
1278
1272
  }
@@ -1286,7 +1280,7 @@ function getLastPartOfRef(ref) {
1286
1280
  * @param {object} toNode
1287
1281
  * @param {boolean} [overwrite]
1288
1282
  */
1289
- function copyAnnotations(fromNode, toNode, overwrite = false) {
1283
+ function copyAnnotations( fromNode, toNode, overwrite = false ) {
1290
1284
  // Ignore if no toNode (in case of errors)
1291
1285
  if (!toNode)
1292
1286
  return;
@@ -1310,7 +1304,7 @@ function copyAnnotations(fromNode, toNode, overwrite = false) {
1310
1304
  * @param {object} toNode
1311
1305
  * @param {boolean} [overwrite]
1312
1306
  */
1313
- function copyAnnotationsAndDoc(fromNode, toNode, overwrite = false) {
1307
+ function copyAnnotationsAndDoc( fromNode, toNode, overwrite = false ) {
1314
1308
  // Ignore if no toNode (in case of errors)
1315
1309
  if (!toNode)
1316
1310
  return;
@@ -1340,7 +1334,7 @@ function copyAnnotationsAndDoc(fromNode, toNode, overwrite = false) {
1340
1334
  * filter: Positive filter. If it returns true, annotations for the referenced artifact
1341
1335
  * will be applied.
1342
1336
  */
1343
- function applyAnnotationsFromExtensions(csn, config) {
1337
+ function applyAnnotationsFromExtensions( csn, config ) {
1344
1338
  if (!csn.extensions)
1345
1339
  return;
1346
1340
 
@@ -1360,7 +1354,7 @@ function applyAnnotationsFromExtensions(csn, config) {
1360
1354
  }
1361
1355
  }
1362
1356
 
1363
- function applyAnnotationsToElements(ext, def) {
1357
+ function applyAnnotationsToElements( ext, def ) {
1364
1358
  // Only the definition is arrayed but the extension is not since
1365
1359
  // `items` is not expected in `extensions` by the CSN frontend and not
1366
1360
  // generated by the CDL parser for `annotate E:arrayed.elem`.
@@ -1380,7 +1374,7 @@ function applyAnnotationsFromExtensions(csn, config) {
1380
1374
  }
1381
1375
  }
1382
1376
 
1383
- function isAspect(node) {
1377
+ function isAspect( node ) {
1384
1378
  return node && node.kind === 'aspect';
1385
1379
  }
1386
1380
 
@@ -1390,7 +1384,7 @@ function isAspect(node) {
1390
1384
  * @param {CSN.Artifact} artifact
1391
1385
  * @returns {boolean}
1392
1386
  */
1393
- function hasValidSkipOrExists(artifact) {
1387
+ function hasValidSkipOrExists( artifact ) {
1394
1388
  return artifact.kind === 'entity' &&
1395
1389
  (hasAnnotationValue(artifact, '@cds.persistence.exists', true) || hasAnnotationValue(artifact, '@cds.persistence.skip', true));
1396
1390
  }
@@ -1402,7 +1396,7 @@ function hasValidSkipOrExists(artifact) {
1402
1396
  * @param {string} artifactName artifact name to get the namespace for
1403
1397
  * @returns {string | null} The namespace name
1404
1398
  */
1405
- function getNamespace(csn, artifactName) {
1399
+ function getNamespace( csn, artifactName ) {
1406
1400
  const parts = artifactName.split('.');
1407
1401
  let seen = parts[0];
1408
1402
  const art = csn.definitions[seen];
@@ -1432,7 +1426,7 @@ function getNamespace(csn, artifactName) {
1432
1426
  * @param {CSN.Model} csn
1433
1427
  * @param {CSN.Options} options
1434
1428
  */
1435
- function sortCsnDefinitionsForTests(csn, options) {
1429
+ function sortCsnDefinitionsForTests( csn, options ) {
1436
1430
  if (!options.testMode && !options.testSortCsn)
1437
1431
  return;
1438
1432
  const sorted = Object.create(null);
@@ -1448,7 +1442,7 @@ function sortCsnDefinitionsForTests(csn, options) {
1448
1442
  * @param {CSN.Model} csn
1449
1443
  * @returns {string[]}
1450
1444
  */
1451
- function getServiceNames(csn) {
1445
+ function getServiceNames( csn ) {
1452
1446
  const result = [];
1453
1447
  forEachDefinition(csn, (artifact, artifactName) => {
1454
1448
  if (artifact.kind === 'service' && !artifact.abstract)
@@ -1464,7 +1458,7 @@ function getServiceNames(csn) {
1464
1458
  * @param {CSN.Path} path
1465
1459
  * @returns {object} Whatever is at the end of path
1466
1460
  */
1467
- function walkCsnPath(csn, path) {
1461
+ function walkCsnPath( csn, path ) {
1468
1462
  /** @type {object} */
1469
1463
  let obj = csn;
1470
1464
  for (const segment of path)
@@ -1482,7 +1476,7 @@ function walkCsnPath(csn, path) {
1482
1476
  * @param {CSN.Options} options
1483
1477
  * @returns {string|null}
1484
1478
  */
1485
- function getVariableReplacement(ref, options) {
1479
+ function getVariableReplacement( ref, options ) {
1486
1480
  if (options && options.variableReplacements) {
1487
1481
  let replacement = options.variableReplacements;
1488
1482
  for (const segment of ref) {
@@ -1511,7 +1505,7 @@ function getVariableReplacement(ref, options) {
1511
1505
  * the comparison. This eliminates false negatives such as
1512
1506
  * mismatching $locations or @odata.foreignKey4.
1513
1507
  */
1514
- function isDeepEqual(obj, other, noExtendedProps) {
1508
+ function isDeepEqual( obj, other, noExtendedProps ) {
1515
1509
  let objectKeys = Object.keys(obj);
1516
1510
  let otherKeys = Object.keys(other);
1517
1511
 
@@ -1537,6 +1531,19 @@ function isDeepEqual(obj, other, noExtendedProps) {
1537
1531
  return true;
1538
1532
  }
1539
1533
 
1534
+ /**
1535
+ * Returns a function that, if called, calls all functions inside
1536
+ * the given `functions` array with the same arguments.
1537
+ *
1538
+ * @param {Function[]} functions
1539
+ * @param {object} thisArg Argument that will be passed to all functions as `this`.
1540
+ */
1541
+ function functionList( functions, thisArg ) {
1542
+ return function iterateFunctions(...args) {
1543
+ return functions.map(f => f.apply(thisArg, args));
1544
+ };
1545
+ }
1546
+
1540
1547
  module.exports = {
1541
1548
  getUtils,
1542
1549
  cloneCsn: cloneCsnNonDict, // Umbrella relies on this name
@@ -1581,4 +1588,5 @@ module.exports = {
1581
1588
  getVariableReplacement,
1582
1589
  implicitAs,
1583
1590
  isDeepEqual,
1591
+ functionList,
1584
1592
  };