@sap/cds-compiler 2.11.2 → 2.13.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 (140) hide show
  1. package/CHANGELOG.md +175 -2
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +10 -8
  4. package/bin/cdsc.js +23 -17
  5. package/bin/cdsse.js +2 -2
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  8. package/doc/CHANGELOG_BETA.md +25 -6
  9. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  10. package/doc/NameResolution.md +21 -16
  11. package/lib/api/main.js +32 -79
  12. package/lib/api/options.js +3 -2
  13. package/lib/api/validate.js +2 -1
  14. package/lib/backends.js +16 -26
  15. package/lib/base/dictionaries.js +0 -8
  16. package/lib/base/error.js +26 -0
  17. package/lib/base/keywords.js +10 -19
  18. package/lib/base/location.js +9 -4
  19. package/lib/base/message-registry.js +75 -9
  20. package/lib/base/messages.js +31 -35
  21. package/lib/base/model.js +2 -62
  22. package/lib/base/optionProcessorHelper.js +246 -183
  23. package/lib/checks/.eslintrc.json +2 -0
  24. package/lib/checks/actionsFunctions.js +2 -1
  25. package/lib/checks/annotationsOData.js +1 -1
  26. package/lib/checks/cdsPersistence.js +2 -1
  27. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  28. package/lib/checks/enricher.js +17 -1
  29. package/lib/checks/foreignKeys.js +4 -4
  30. package/lib/checks/invalidTarget.js +3 -1
  31. package/lib/checks/managedInType.js +4 -4
  32. package/lib/checks/managedWithoutKeys.js +3 -1
  33. package/lib/checks/queryNoDbArtifacts.js +1 -3
  34. package/lib/checks/selectItems.js +4 -4
  35. package/lib/checks/sql-snippets.js +94 -0
  36. package/lib/checks/types.js +1 -1
  37. package/lib/checks/unknownMagic.js +1 -1
  38. package/lib/checks/validator.js +12 -7
  39. package/lib/compiler/assert-consistency.js +12 -8
  40. package/lib/compiler/base.js +0 -1
  41. package/lib/compiler/builtins.js +42 -21
  42. package/lib/compiler/checks.js +46 -12
  43. package/lib/compiler/cycle-detector.js +1 -1
  44. package/lib/compiler/define.js +1103 -0
  45. package/lib/compiler/extend.js +983 -0
  46. package/lib/compiler/finalize-parse-cdl.js +231 -0
  47. package/lib/compiler/index.js +46 -39
  48. package/lib/compiler/kick-start.js +190 -0
  49. package/lib/compiler/moduleLayers.js +4 -4
  50. package/lib/compiler/populate.js +1226 -0
  51. package/lib/compiler/propagator.js +113 -47
  52. package/lib/compiler/resolve.js +1433 -0
  53. package/lib/compiler/shared.js +100 -65
  54. package/lib/compiler/tweak-assocs.js +529 -0
  55. package/lib/compiler/utils.js +215 -33
  56. package/lib/edm/.eslintrc.json +5 -0
  57. package/lib/edm/annotations/genericTranslation.js +38 -25
  58. package/lib/edm/annotations/preprocessAnnotations.js +3 -3
  59. package/lib/edm/csn2edm.js +10 -9
  60. package/lib/edm/edm.js +19 -20
  61. package/lib/edm/edmPreprocessor.js +166 -95
  62. package/lib/edm/edmUtils.js +127 -34
  63. package/lib/gen/Dictionary.json +92 -43
  64. package/lib/gen/language.checksum +1 -1
  65. package/lib/gen/language.interp +11 -1
  66. package/lib/gen/language.tokens +86 -82
  67. package/lib/gen/languageLexer.interp +18 -1
  68. package/lib/gen/languageLexer.js +925 -847
  69. package/lib/gen/languageLexer.tokens +78 -74
  70. package/lib/gen/languageParser.js +5434 -4298
  71. package/lib/json/from-csn.js +59 -17
  72. package/lib/json/to-csn.js +189 -71
  73. package/lib/language/antlrParser.js +3 -3
  74. package/lib/language/docCommentParser.js +3 -3
  75. package/lib/language/errorStrategy.js +26 -8
  76. package/lib/language/genericAntlrParser.js +144 -53
  77. package/lib/language/language.g4 +424 -200
  78. package/lib/language/multiLineStringParser.js +536 -0
  79. package/lib/main.d.ts +550 -61
  80. package/lib/main.js +38 -11
  81. package/lib/model/api.js +3 -1
  82. package/lib/model/csnRefs.js +322 -198
  83. package/lib/model/csnUtils.js +226 -370
  84. package/lib/model/enrichCsn.js +124 -69
  85. package/lib/model/revealInternalProperties.js +29 -7
  86. package/lib/model/sortViews.js +10 -2
  87. package/lib/modelCompare/compare.js +17 -12
  88. package/lib/optionProcessor.js +8 -3
  89. package/lib/render/.eslintrc.json +1 -2
  90. package/lib/render/DuplicateChecker.js +1 -1
  91. package/lib/render/manageConstraints.js +36 -33
  92. package/lib/render/toCdl.js +174 -275
  93. package/lib/render/toHdbcds.js +203 -122
  94. package/lib/render/toRename.js +7 -10
  95. package/lib/render/toSql.js +161 -82
  96. package/lib/render/utils/common.js +22 -8
  97. package/lib/render/utils/sql.js +10 -7
  98. package/lib/render/utils/stringEscapes.js +111 -0
  99. package/lib/sql-identifier.js +1 -1
  100. package/lib/transform/.eslintrc.json +5 -0
  101. package/lib/transform/braceExpression.js +4 -2
  102. package/lib/transform/db/.eslintrc.json +2 -0
  103. package/lib/transform/db/applyTransformations.js +212 -0
  104. package/lib/transform/db/assertUnique.js +1 -1
  105. package/lib/transform/db/associations.js +187 -0
  106. package/lib/transform/db/cdsPersistence.js +150 -0
  107. package/lib/transform/db/constraints.js +61 -56
  108. package/lib/transform/db/expansion.js +50 -29
  109. package/lib/transform/db/flattening.js +556 -106
  110. package/lib/transform/db/groupByOrderBy.js +3 -1
  111. package/lib/transform/db/temporal.js +236 -0
  112. package/lib/transform/db/transformExists.js +103 -28
  113. package/lib/transform/db/views.js +92 -44
  114. package/lib/transform/draft/.eslintrc.json +38 -0
  115. package/lib/transform/{db/draft.js → draft/db.js} +9 -7
  116. package/lib/transform/draft/odata.js +227 -0
  117. package/lib/transform/forHanaNew.js +98 -783
  118. package/lib/transform/forOdataNew.js +22 -175
  119. package/lib/transform/localized.js +36 -32
  120. package/lib/transform/odata/generateForeignKeyElements.js +3 -3
  121. package/lib/transform/odata/referenceFlattener.js +95 -89
  122. package/lib/transform/odata/structureFlattener.js +1 -1
  123. package/lib/transform/odata/toFinalBaseType.js +86 -12
  124. package/lib/transform/odata/typesExposure.js +5 -5
  125. package/lib/transform/odata/utils.js +2 -2
  126. package/lib/transform/transformUtilsNew.js +47 -33
  127. package/lib/transform/translateAssocsToJoins.js +13 -30
  128. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  129. package/lib/transform/universalCsn/coreComputed.js +170 -0
  130. package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
  131. package/lib/transform/universalCsn/utils.js +63 -0
  132. package/lib/utils/file.js +8 -3
  133. package/lib/utils/objectUtils.js +30 -0
  134. package/lib/utils/timetrace.js +8 -2
  135. package/package.json +1 -1
  136. package/share/messages/README.md +26 -0
  137. package/lib/compiler/definer.js +0 -2349
  138. package/lib/compiler/resolver.js +0 -2922
  139. package/lib/transform/db/helpers.js +0 -58
  140. package/lib/transform/universalCsnEnricher.js +0 -67
@@ -1,9 +1,10 @@
1
1
  'use strict';
2
2
 
3
- const { setProp } = require('../base/model');
4
- const { csnRefs } = require('../model/csnRefs');
3
+ const { csnRefs, implicitAs } = require('../model/csnRefs');
4
+ const { applyTransformations, applyTransformationsOnNonDictionary } = require('../transform/db/applyTransformations');
5
5
  const { isBuiltinType } = require('../compiler/builtins.js')
6
6
  const { sortCsn, cloneCsnDictionary: _cloneCsnDictionary } = require('../json/to-csn');
7
+ const { ModelError } = require("../base/error");
7
8
  const version = require('../../package.json').version;
8
9
 
9
10
  // Low-level utility functions to work with compact CSN.
@@ -36,15 +37,15 @@ const version = require('../../package.json').version;
36
37
  * Get utility functions for a given CSN.
37
38
  * @param {CSN.Model} model (Compact) CSN model
38
39
  */
39
- function getUtils(model) {
40
- const { artifactRef, inspectRef, effectiveType, getOrigin } = csnRefs(model);
40
+ function getUtils(model, universalReady) {
41
+ const { artifactRef, inspectRef, effectiveType, getOrigin, targetAspect, getColumn, getElement, initDefinition } = csnRefs(model, universalReady);
41
42
 
42
43
  return {
43
44
  getCsnDef,
44
45
  isStructured,
45
46
  getFinalType,
46
47
  getFinalTypeDef,
47
- isManagedAssociationElement,
48
+ isManagedAssociation,
48
49
  isAssocOrComposition,
49
50
  isAssociation,
50
51
  isComposition,
@@ -61,6 +62,11 @@ function getUtils(model) {
61
62
  effectiveType,
62
63
  get$combined,
63
64
  getOrigin,
65
+ getQueryPrimarySource,
66
+ targetAspect,
67
+ getColumn,
68
+ getElement,
69
+ initDefinition
64
70
  };
65
71
 
66
72
  /**
@@ -70,135 +76,156 @@ function getUtils(model) {
70
76
  * @returns {object}
71
77
  */
72
78
  function get$combined(query) {
73
- const sources = getSources(query);
74
- return sources;
79
+ return getSources(query);
80
+ }
75
81
 
76
- /**
77
- * Get the union of all elements from the from clause
78
- * - descend into unions, following the lead query
79
- * - merge all queries in case of joins
80
- * - follow subqueries
81
- *
82
- * @param {CSN.Query} query Query to check
83
- * @returns {object} Map of sources
84
- */
85
- function getSources(query, isSubquery=false) {
86
- // Remark CW: better just a while along query.SET.args[0]
87
- if (query.SET) {
88
- if (query.SET.args[0].SELECT && query.SET.args[0].SELECT.elements)
89
- return mergeElementsIntoMap(Object.create(null), query.SET.args[0].SELECT.elements, query.SET.args[0].$location);
82
+ /**
83
+ * Get the union of all elements from the from clause
84
+ * - descend into unions, following the lead query
85
+ * - merge all queries in case of joins
86
+ * - follow subqueries
87
+ *
88
+ * @param {CSN.Query} query Query to check
89
+ * @param {boolean} [isSubquery]
90
+ * @returns {object} Map of sources
91
+ */
92
+ function getSources(query, isSubquery=false) {
93
+ // Remark CW: better just a while along query.SET.args[0]
94
+ if (query.SET) {
95
+ if (query.SET.args[0].SELECT && query.SET.args[0].SELECT.elements)
96
+ return mergeElementsIntoMap(Object.create(null), query.SET.args[0].SELECT.elements, query.SET.args[0].$location);
90
97
 
91
- return getSources(query.SET.args[0], isSubquery);
98
+ return getSources(query.SET.args[0], isSubquery);
99
+ }
100
+ else if (query.SELECT) {
101
+ if (query.SELECT.from.args) {
102
+ return walkArgs(query.SELECT.from.args);
92
103
  }
93
- else if (query.SELECT) {
94
- if (query.SELECT.from.args) {
95
- return walkArgs(query.SELECT.from.args);
96
- }
97
- else if (query.SELECT.from.ref) {
98
- let art = artifactRef(query.SELECT.from);
104
+ else if (query.SELECT.from.ref) {
105
+ let art = artifactRef(query.SELECT.from);
99
106
 
100
- if(art.target)
101
- art = artifactRef(art.target);
107
+ if(art.target)
108
+ art = artifactRef(art.target);
102
109
 
103
- if(isSubquery && !query.SELECT.elements)
104
- throw new Error('Expected subquery to have .elements');
110
+ if(isSubquery && !query.SELECT.elements)
111
+ throw new ModelError('Expected subquery to have .elements');
105
112
 
106
- return mergeElementsIntoMap(Object.create(null), isSubquery ? query.SELECT.elements : art.elements, art.$location,
107
- query.SELECT.from.as || query.SELECT.from.ref[query.SELECT.from.ref.length - 1],
108
- query.SELECT.from.ref[query.SELECT.from.ref.length - 1] || query.SELECT.from.as );
109
- }
110
- else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
111
- return getSources(query.SELECT.from, true);
112
- }
113
+ return mergeElementsIntoMap(Object.create(null), isSubquery ? query.SELECT.elements : art.elements, art.$location,
114
+ query.SELECT.from.as || query.SELECT.from.ref[query.SELECT.from.ref.length - 1],
115
+ query.SELECT.from.ref[query.SELECT.from.ref.length - 1] || query.SELECT.from.as );
116
+ }
117
+ else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
118
+ return getSources(query.SELECT.from, true);
113
119
  }
120
+ }
114
121
 
115
- function walkArgs(args) {
116
- let elements = Object.create(null);
117
- for (const arg of args) {
118
- if (arg.args) {
119
- elements = mergeElementMaps(elements, walkArgs(arg.args));
120
- }
121
- else if (arg.ref) {
122
- const art = artifactRef(arg);
123
- elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || arg.ref[arg.ref.length - 1], arg.ref[arg.ref.length - 1] || arg.as);
124
- }
125
- else if (arg.SELECT || arg.SET) {
126
- elements = mergeElementMaps(elements, getSources(arg));
127
- }
128
- }
122
+ return {};
123
+ }
129
124
 
130
- return elements;
125
+ function walkArgs(args) {
126
+ let elements = Object.create(null);
127
+ for (const arg of args) {
128
+ if (arg.args) {
129
+ elements = mergeElementMaps(elements, walkArgs(arg.args));
130
+ }
131
+ else if (arg.ref) {
132
+ const art = artifactRef(arg);
133
+ elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || arg.ref[arg.ref.length - 1], arg.ref[arg.ref.length - 1] || arg.as);
134
+ }
135
+ else if (arg.SELECT || arg.SET) {
136
+ elements = mergeElementMaps(elements, getSources(arg));
131
137
  }
138
+ }
132
139
 
133
- return {};
134
-
135
- /**
136
- * Merge two maps of elements together
137
- *
138
- * @param {object} mapA Map a - will be returned
139
- * @param {object} mapB Map b - will not be returned
140
- * @returns {object} mapA
141
- */
142
- function mergeElementMaps(mapA, mapB) {
143
- for (const elementName in mapB) {
144
- if (!mapA[elementName])
145
- mapA[elementName] = [];
146
-
147
- mapB[elementName].forEach(e => mapA[elementName].push(e));
148
- }
140
+ return elements;
141
+ }
149
142
 
150
- return mapA;
151
- }
143
+ /**
144
+ * Merge two maps of elements together
145
+ *
146
+ * @param {object} mapA Map a - will be returned
147
+ * @param {object} mapB Map b - will not be returned
148
+ * @returns {object} mapA
149
+ */
150
+ function mergeElementMaps(mapA, mapB) {
151
+ for (const elementName in mapB) {
152
+ if (!mapA[elementName])
153
+ mapA[elementName] = [];
152
154
 
153
- /**
154
- * Merge elements into an existing map
155
- *
156
- * @param {any} existingMap map to merge into - will be returned
157
- * @param {object} elements elements to merge into the map
158
- * @param {CSN.Location} $location $location of the elements - where they come from
159
- * @param {any} [parent] Name of the parent of the elements, alias before ref
160
- * @param {any} [error_parent] Parent name to use for error messages, ref before alias
161
- * @returns {object} existingMap
162
- */
163
- function mergeElementsIntoMap(existingMap, elements, $location, parent, error_parent) {
164
- for (const elementName in elements) {
165
- const element = elements[elementName];
166
- if (!existingMap[elementName])
167
- existingMap[elementName] = [];
168
-
169
-
170
- existingMap[elementName].push({
171
- element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
172
- });
173
- }
155
+ mapB[elementName].forEach(e => mapA[elementName].push(e));
156
+ }
174
157
 
175
- return existingMap;
176
- }
158
+ return mapA;
159
+ }
160
+
161
+ /**
162
+ * Merge elements into an existing map
163
+ *
164
+ * @param {any} existingMap map to merge into - will be returned
165
+ * @param {object} elements elements to merge into the map
166
+ * @param {CSN.Location} $location $location of the elements - where they come from
167
+ * @param {any} [parent] Name of the parent of the elements, alias before ref
168
+ * @param {any} [error_parent] Parent name to use for error messages, ref before alias
169
+ * @returns {object} existingMap
170
+ */
171
+ function mergeElementsIntoMap(existingMap, elements, $location, parent, error_parent) {
172
+ for (const elementName in elements) {
173
+ const element = elements[elementName];
174
+ if (!existingMap[elementName])
175
+ existingMap[elementName] = [];
176
+
177
+
178
+ existingMap[elementName].push({
179
+ element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
180
+ });
177
181
  }
178
182
 
179
- /**
183
+ return existingMap;
184
+ }
185
+
186
+ /**
180
187
  * Return the name part of the artifact name - no namespace etc.
181
188
  * @param {string|object} name Absolute name of the artifact
182
189
  */
183
- function getBaseName(name) {
184
- if (!name)
185
- return name;
190
+ function getBaseName(name) {
191
+ if (!name)
192
+ return name;
186
193
 
187
- if (name.id)
188
- return name.id.substring( name.id.lastIndexOf('.')+1 );
194
+ if (name.id)
195
+ return name.id.substring( name.id.lastIndexOf('.')+1 );
196
+
197
+ return name.substring( name.lastIndexOf('.')+1 )
198
+ }
189
199
 
190
- return name.substring( name.lastIndexOf('.')+1 )
200
+ /**
201
+ * Return the left-most, primary source of the given query.
202
+ * @param {*} query Definition's query object
203
+ */
204
+ function getQueryPrimarySource(query) {
205
+ if (!query)
206
+ return undefined;
207
+ else if (query.SELECT) {
208
+ return getQueryPrimarySource(query.SELECT);
209
+ } else if (query.SET) {
210
+ return getQueryPrimarySource(query.SET);
211
+ } else if (query.from) {
212
+ return getQueryPrimarySource(query.from);
213
+ } else if (query.ref) {
214
+ return query;
215
+ } else if (query.args) {
216
+ return getQueryPrimarySource(query.args[0]);
191
217
  }
218
+ return undefined;
192
219
  }
193
220
 
194
221
 
195
222
  /**
196
223
  * Create an object to track visited objects identified by a unique string.
197
- * @param {string} [id] Initial entry (optional)
224
+ * @param {string} [initialId] Initial entry (optional)
198
225
  */
199
- function createVisited(id) {
226
+ function createVisited(initialId) {
200
227
  let visited = Object.create(null);
201
- check(id);
228
+ check(initialId);
202
229
  return { check };
203
230
 
204
231
  /**
@@ -209,7 +236,7 @@ function getUtils(model) {
209
236
  function check(id) {
210
237
  if (!id) return;
211
238
  if (visited[id]) {
212
- throw new Error('Circular dependency');
239
+ throw new ModelError('Circular dependency');
213
240
  }
214
241
  visited[id] = true;
215
242
  }
@@ -223,14 +250,14 @@ function getUtils(model) {
223
250
  if (model.definitions[defName])
224
251
  return model.definitions[defName]
225
252
  else
226
- throw new Error(`Nonexistent definition in the model: '${defName}'`);
253
+ throw new ModelError(`Nonexistent definition in the model: '${defName}'`);
227
254
  }
228
255
 
229
256
  /**
230
257
  * Returns true if an artifact is a structured type
231
258
  * or a typedef of a structured type.
232
259
  *
233
- * @param {CSN.Artifact} obj
260
+ * @param {object} obj
234
261
  */
235
262
  function isStructured(obj) {
236
263
  return obj.elements ||
@@ -276,7 +303,7 @@ function getUtils(model) {
276
303
 
277
304
  // Return true if 'node' is a managed association element
278
305
  // TODO: what about elements having a type, which (finally) is an assoc?
279
- function isManagedAssociationElement(node) {
306
+ function isManagedAssociation(node) {
280
307
  return node.target !== undefined && node.on === undefined && node.keys;
281
308
  }
282
309
 
@@ -397,7 +424,7 @@ function getUtils(model) {
397
424
  function getServiceName(artifactName) {
398
425
  for(;;) {
399
426
  let idx = artifactName.lastIndexOf('.');
400
- if (idx == -1) return null;
427
+ if (idx === -1) return null;
401
428
  artifactName = artifactName.substring(0, idx);
402
429
  let artifact = model.definitions[artifactName];
403
430
  if (artifact && artifact.kind === 'service') {
@@ -514,7 +541,7 @@ function getUtils(model) {
514
541
  if (cycleCheck) {
515
542
  let visited = path.length? type + ':' + path.join('.') : type;
516
543
  if (cycleCheck[visited])
517
- throw new Error('Circular type chain on type ' + type);
544
+ throw new ModelError('Circular type chain on type ' + type);
518
545
  else
519
546
  cycleCheck[visited] = true;
520
547
  }
@@ -627,7 +654,7 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
627
654
  }
628
655
 
629
656
  path = [...path]; // Copy
630
- const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'foreignKeys', 'actions', 'params']);
657
+ const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'actions', 'params']);
631
658
  propsWithMembers.forEach((prop) => forEachGeneric( construct, prop, callback, path, iterateOptions ));
632
659
  }
633
660
 
@@ -684,45 +711,6 @@ function forEachGeneric( obj, prop, callback, path = [], iterateOptions = {}) {
684
711
  }
685
712
  }
686
713
 
687
- /**
688
- * For each property named 'ref' in 'node' (recursively), call callback(ref, node, path)
689
- *
690
- * @param {object} node
691
- * @param {refCallback|refCallback[]} callback
692
- * @param {CSN.Path} path
693
- */
694
- function forEachRef(node, callback, path = []) {
695
- if (node === null || typeof node !== 'object') {
696
- // Primitive node
697
- return;
698
- }
699
-
700
- if(node._ignore){
701
- return;
702
- }
703
-
704
- if(Array.isArray(node)){
705
- for (let i = 0; i < node.length; i++) {
706
- // Descend recursively
707
- forEachRef(node[i], callback, path.concat([i]));
708
- }
709
- } else {
710
- for (let name in node) {
711
- if (!Object.hasOwnProperty.call( node, name ))
712
- continue;
713
- // If ref found within a non-dictionary, call callback
714
- if (name === 'ref' && Object.getPrototypeOf(node)) {
715
- if(Array.isArray(callback))
716
- callback.forEach(cb => cb( node.ref, node, path ));
717
- else
718
- callback( node.ref, node, path );
719
- }
720
- // Descend recursively
721
- forEachRef(node[name], callback, path.concat([name]));
722
- }
723
- }
724
- }
725
-
726
714
  // Like Object.assign() but copies also non enumerable properties
727
715
  function assignAll(target, ...sources) {
728
716
  sources.forEach(source => {
@@ -808,39 +796,6 @@ function forAllQueries(query, callback, path = []){
808
796
  }
809
797
  }
810
798
 
811
- function forAllElements(artifact, artifactName, cb, includeActions = false){
812
- if(artifact.elements) {
813
- cb(artifact, artifact.elements, ['definitions', artifactName, 'elements']);
814
- }
815
-
816
- if(includeActions && artifact.actions) {
817
- Object.entries(artifact.actions).forEach( ([actionName, action]) => {
818
- const path = ['definitions', artifactName, 'actions', actionName];
819
- if(action.params) {
820
- Object.entries(action.params).forEach( ([paramName, param]) => {
821
- if(param.elements)
822
- cb(param, param.elements, path.concat(['params', paramName, 'elements']));
823
- });
824
- }
825
- if(action.returns && action.returns.elements)
826
- cb(action.returns, action.returns.elements,path.concat(['returns', 'elements']));
827
- });
828
- }
829
-
830
- if(artifact.query) {
831
- forAllQueries(artifact.query, (q, p) => {
832
- const s = q.SELECT;
833
- if(s) {
834
- if(s.elements) {
835
- cb(s, s.elements, [...p, 'elements']);
836
- } else if(s.$elements) { // huh?, is just refloc output
837
- cb(s, s.$elements, [...p, '$elements']);
838
- }
839
- }
840
- }, ['definitions', artifactName, 'query'])
841
- }
842
- }
843
-
844
799
  /**
845
800
  * Compare a given annotation value with an expectation value and return
846
801
  *
@@ -859,11 +814,14 @@ function forAllElements(artifact, artifactName, cb, includeActions = false){
859
814
  * @param {CSN.Artifact} artifact
860
815
  * @param {string} annotationName Name of the annotation (including the at-sign)
861
816
  * @param {any} expected
817
+ * @param {boolean} caseInsensitive
862
818
  * @returns {boolean}
863
819
  */
864
- function hasAnnotationValue(artifact, annotationName, expected = true) {
820
+ function hasAnnotationValue(artifact, annotationName, expected = true, caseInsensitive = false) {
865
821
  if(expected === false)
866
822
  return artifact[annotationName] === expected || artifact[annotationName] === null;
823
+ else if (typeof artifact[annotationName] === 'string' && caseInsensitive === true)
824
+ return artifact[annotationName].toLowerCase() === expected.toLowerCase();
867
825
  else
868
826
  return artifact[annotationName] === expected;
869
827
  }
@@ -900,48 +858,48 @@ function isEdmPropertyRendered(elementCsn, options) {
900
858
 
901
859
  /**
902
860
  * Return the resulting database name for (absolute) 'artifactName', depending on the current naming
903
- * convention.
861
+ * mode.
904
862
  *
905
- * - For the 'hdbcds' naming convention, this means converting '.' to '::' on
863
+ * - For the 'hdbcds' naming mode, this means converting '.' to '::' on
906
864
  * the border between namespace and top-level artifact and correctly replacing some '.' with '_'.
907
- * - For the 'plain' naming convention, it means converting all '.' to '_' and uppercasing.
908
- * - For the 'quoted' naming convention, this means correctly replacing some '.' with '_'.
865
+ * - For the 'plain' naming mode, it means converting all '.' to '_' and upper-casing.
866
+ * - For the 'quoted' naming mode, this means correctly replacing some '.' with '_'.
909
867
  *
910
868
  * If the old function signature is used - with a namespace as the third argument - the result might be wrong,
911
869
  * since the '.' -> '_' conversion for quoted/hdbcds is missing.
912
870
  *
913
- * @param {string} artifactName The name of the artifact
914
- * @param {('plain'|'quoted'|'hdbcds')} namingConvention The naming convention to use
871
+ * @param {string} artifactName The fully qualified name of the artifact
872
+ * @param {('plain'|'quoted'|'hdbcds')} sqlMapping The naming mode to use
915
873
  * @param {CSN.Model|string|undefined} csn
916
- * @returns {string} The resulting database name for (absolute) 'artifactName', depending on the current naming convention.
874
+ * @returns {string} The resulting database name for (absolute) 'artifactName', depending on the current naming mode.
917
875
  */
918
- function getArtifactDatabaseNameOf(artifactName, namingConvention, csn) {
876
+ function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn) {
919
877
  if(csn && typeof csn === 'object' && csn.definitions)
920
- if (namingConvention === 'quoted' || namingConvention === 'hdbcds') {
921
- return getResultingName(csn, namingConvention, artifactName);
878
+ if (sqlMapping === 'quoted' || sqlMapping === 'hdbcds') {
879
+ return getResultingName(csn, sqlMapping, artifactName);
922
880
  }
923
- else if (namingConvention === 'plain') {
881
+ else if (sqlMapping === 'plain') {
924
882
  return artifactName.replace(/\./g, '_').toUpperCase();
925
883
  } else {
926
- throw new Error('Unknown naming convention: ' + namingConvention);
884
+ throw new Error('Unknown naming mode: ' + sqlMapping);
927
885
  }
928
886
  else {
929
887
  console.error(`This invocation of "getArtifactCdsPersistenceName" is deprecated, as it doesn't produce correct output with definition names containing dots - please provide a CSN as the third parameter.`);
930
- if (namingConvention === 'hdbcds') {
888
+ if (sqlMapping === 'hdbcds') {
931
889
  if (csn) {
932
890
  const namespace = String(csn);
933
891
  return `${namespace}::${artifactName.substring(namespace.length + 1)}`;
934
892
  }
935
893
  return artifactName;
936
894
  }
937
- else if (namingConvention === 'plain') {
895
+ else if (sqlMapping === 'plain') {
938
896
  return artifactName.replace(/\./g, '_').toUpperCase();
939
897
  }
940
- else if (namingConvention === 'quoted') {
898
+ else if (sqlMapping === 'quoted') {
941
899
  return artifactName;
942
900
  }
943
901
  else {
944
- throw new Error('Unknown naming convention: ' + namingConvention);
902
+ throw new Error('Unknown naming mode: ' + sqlMapping);
945
903
  }
946
904
  }
947
905
  }
@@ -1027,135 +985,29 @@ function getUnderscoredName(startIndex, parts, csn) {
1027
985
 
1028
986
 
1029
987
  /**
1030
- * Return the resulting database element name for 'elemName', depending on the current naming
1031
- * convention.
1032
- * - For the 'hdbcds' naming convention, this is just 'elemName'.
1033
- * - For the 'plain' naming convention, it means converting all '.' to '_' and uppercasing.
1034
- * - For the 'quoted' naming convention, it means converting all '.' to '_'.
1035
- * No other naming conventions are accepted
988
+ * Return the resulting database element name for 'elemName', depending on the current
989
+ * naming mode.
990
+ * - For the 'hdbcds' naming mode, this is just 'elemName'.
991
+ * - For the 'plain' naming mode, it means converting all '.' to '_' and upper-casing.
992
+ * - For the 'quoted' naming mode, it means converting all '.' to '_'.
993
+ * No other naming modes are accepted!
1036
994
  *
1037
- * @param {string} elemName Name of the element
1038
- * @param {('plain'|'quoted'|'hdbcds')} namingConvention The naming convention to use
1039
- * @returns {string} The resulting database element name for 'elemName', depending on the current naming convention.
995
+ * @param {string} elemName The name of the element
996
+ * @param {('plain'|'quoted'|'hdbcds')} sqlMapping The naming mode to use
997
+ * @returns {string} The resulting database element name for 'elemName', depending on the current naming mode.
1040
998
  */
1041
- function getElementDatabaseNameOf(elemName, namingConvention) {
1042
- if (namingConvention === 'hdbcds') {
999
+ function getElementDatabaseNameOf(elemName, sqlMapping) {
1000
+ if (sqlMapping === 'hdbcds') {
1043
1001
  return elemName;
1044
1002
  }
1045
- else if (namingConvention === 'plain') {
1003
+ else if (sqlMapping === 'plain') {
1046
1004
  return elemName.replace(/\./g, '_').toUpperCase();
1047
1005
  }
1048
- else if (namingConvention === 'quoted') {
1006
+ else if (sqlMapping === 'quoted') {
1049
1007
  return elemName.replace(/\./g, '_');
1050
1008
  }
1051
1009
  else {
1052
- throw new Error('Unknown naming convention: ' + namingConvention);
1053
- }
1054
- }
1055
-
1056
-
1057
- /**
1058
- * Loop through the model, applying the custom transformations on the node's matching.
1059
- *
1060
- * Each transformer gets:
1061
- * - the parent having the property
1062
- * - the name of the property
1063
- * - the value of the property
1064
- * - the path to the property
1065
- *
1066
- * @param {object} csn CSN to enrich in-place
1067
- * @param {object} customTransformers Map of prop to transform and function to apply
1068
- * @param {Function[]} [artifactTransformers=[]] Transformations to run on the artifacts, like forEachDefinition
1069
- * @param {Boolean} [skipIgnore=true] Wether to skip _ignore elements or not
1070
- * @param {object} [options={}] "skipArtifact": (artifact, name) => Boolean to skip certain artifacts, drillRef: boolean - whether to drill into infix/args
1071
- * @returns {object} CSN with transformations applied
1072
- */
1073
- function applyTransformations( csn, customTransformers={}, artifactTransformers=[], skipIgnore = true, options = {} ) {
1074
- const transformers = {
1075
- elements: dictionary,
1076
- definitions: dictionary,
1077
- actions: dictionary,
1078
- params: dictionary,
1079
- enum: dictionary,
1080
- mixin: dictionary,
1081
- ref: pathRef,
1082
- //type: simpleRef,
1083
- //target: simpleRef,
1084
- //includes: simpleRef,
1085
- }
1086
-
1087
- const csnPath = [];
1088
- if (csn.definitions)
1089
- definitions( csn, 'definitions', csn.definitions );
1090
- return csn;
1091
-
1092
- function standard( parent, prop, node ) {
1093
- if (!node || typeof node !== 'object' || !{}.propertyIsEnumerable.call( parent, prop ) || (typeof prop === 'string' && prop.startsWith('@')) || (skipIgnore && node._ignore))
1094
- return;
1095
-
1096
- csnPath.push( prop );
1097
-
1098
- if (Array.isArray(node)) {
1099
- node.forEach( (n, i) => standard( node, i, n ) );
1100
- }
1101
-
1102
- else {
1103
- for (let name of Object.getOwnPropertyNames( node )) {
1104
- const trans = transformers[name] || standard;
1105
- if(customTransformers[name])
1106
- customTransformers[name](node, name, node[name], csnPath, parent, prop);
1107
-
1108
- trans( node, name, node[name], csnPath );
1109
- }
1110
- }
1111
- csnPath.pop();
1112
- }
1113
-
1114
- function dictionary( node, prop, dict ) {
1115
- // Allow skipping dicts like actions in forHanaNew
1116
- if(options.skipDict && options.skipDict[prop])
1117
- return;
1118
- csnPath.push( prop );
1119
- for (let name of Object.getOwnPropertyNames( dict )) {
1120
- standard( dict, name, dict[name] );
1121
- }
1122
- if (!Object.prototype.propertyIsEnumerable.call( node, prop ))
1123
- setProp(node, '$' + prop, dict);
1124
- csnPath.pop();
1125
- }
1126
-
1127
- function definitions( node, prop, dict ) {
1128
- csnPath.push( prop );
1129
- for (let name of Object.getOwnPropertyNames( dict )) {
1130
- const skip = options && options.skipArtifact && options.skipArtifact(dict[name], name) || false;
1131
- if(!skip) {
1132
- artifactTransformers.forEach(fn => fn(dict, name, dict[name]));
1133
- standard( dict, name, dict[name] );
1134
- }
1135
- }
1136
- if (!Object.prototype.propertyIsEnumerable.call( node, prop ))
1137
- setProp(node, '$' + prop, dict);
1138
- csnPath.pop();
1139
- }
1140
-
1141
- //Keep looping through the pathRef
1142
- function pathRef( node, prop, path ) {
1143
- csnPath.push( prop );
1144
- path.forEach( function step( s, i ) {
1145
- if (s && typeof s === 'object') {
1146
- csnPath.push( i );
1147
- if(options.drillRef) {
1148
- standard(path, i, s);
1149
- } else {
1150
- if (s.args)
1151
- standard( s, 'args', s.args );
1152
- if (s.where)
1153
- standard( s, 'where', s.where );
1154
- }
1155
- csnPath.pop();
1156
- }
1157
- } );
1158
- csnPath.pop();
1010
+ throw new Error('Unknown naming mode: ' + sqlMapping);
1159
1011
  }
1160
1012
  }
1161
1013
 
@@ -1233,7 +1085,7 @@ function setDependencies( csn ) {
1233
1085
  * @returns {boolean}
1234
1086
  */
1235
1087
  function isPersistedOnDatabase(art) {
1236
- return !([ 'entity', 'view' ].includes(art.kind) && (art.abstract || hasAnnotationValue(art, '@cds.persistence.skip')));
1088
+ return !('entity' === art.kind && (art.abstract || hasAnnotationValue(art, '@cds.persistence.skip')));
1237
1089
  }
1238
1090
 
1239
1091
  /**
@@ -1301,10 +1153,10 @@ function mergeOptions(...optionsObjects) {
1301
1153
  }
1302
1154
  // Check against improper overwriting
1303
1155
  if (isObject(left) && !Array.isArray(left) && (Array.isArray(right) || isScalar(right))) {
1304
- throw new Error(`Cannot overwrite structured option "${name}" with array or scalar value`);
1156
+ throw new ModelError(`Cannot overwrite structured option "${name}" with array or scalar value`);
1305
1157
  }
1306
1158
  if ((isScalar(left) && typeof left !== 'boolean' || Array.isArray(left)) && isObject(right) && !Array.isArray(right)) {
1307
- throw new Error(`Cannot overwrite non-boolean scalar or array option "${name}" with structured value`);
1159
+ throw new ModelError(`Cannot overwrite non-boolean scalar or array option "${name}" with structured value`);
1308
1160
  }
1309
1161
 
1310
1162
  // Copy or overwrite properties from right to left
@@ -1338,38 +1190,6 @@ function mergeOptions(...optionsObjects) {
1338
1190
  }
1339
1191
  }
1340
1192
 
1341
- // Return the name of the top-level artifact surrounding the artifact 'name'
1342
- // in 'model'.
1343
- // We define "top-level artifact" to be an artifact that has either no parent or only
1344
- // ancestors of kind 'namespace'. Note that it is possible for a non-top-level artifact
1345
- // to have a namespace as parent and e.g. a context as grandparent (weird but true).
1346
- // Will return the artifact 'name' if it is a top-level artifact itself, and 'undefined'
1347
- // if there is no artifact surrounding 'name' in the model
1348
- // TODO: to be checked by author: still intended behaviour with 'cds' prefix?
1349
- // TODO: Can this be replaced by getRootArtifactName? Or maybe not rely on namespace-hacking...
1350
- // FIXME: This only works with namespace-hacking, i.e. adding them as artifacts...
1351
- function getTopLevelArtifactNameOf(name, model) {
1352
- let dotIdx = name.indexOf('.');
1353
- if (dotIdx == -1) {
1354
- // No '.' in the name, i.e. no parent - this is a top-level artifact (if it exists)
1355
- return model.definitions[name] ? name : undefined;
1356
- }
1357
- // If the first name part is not in the model, there is nothing to find
1358
- if (!model.definitions[name.substring(0, dotIdx)]) {
1359
- return undefined;
1360
- }
1361
- // Skip forward through '.'s until finding a non-namespace
1362
- while (dotIdx != -1 && (!model.definitions[name.substring(0, dotIdx)] || model.definitions[name.substring(0, dotIdx)].kind === 'namespace')) {
1363
- dotIdx = name.indexOf('.', dotIdx + 1);
1364
- }
1365
- if (dotIdx == -1) {
1366
- // This is a top-level artifact
1367
- return name;
1368
- }
1369
- // The skipped part of 'name' is the top-level artifact name
1370
- return name.substring(0, dotIdx);
1371
- }
1372
-
1373
1193
  /**
1374
1194
  * If the artifact with the name given is part of a context (or multiple), return the top-most context.
1375
1195
  * Else, return the artifact itself. Namespaces are not of concern here.
@@ -1491,7 +1311,7 @@ function forEachPath(node, callback) {
1491
1311
  * @returns {boolean}
1492
1312
  */
1493
1313
  function hasValidSkipOrExists(artifact) {
1494
- return (artifact.kind === 'entity' || artifact.kind === 'view') &&
1314
+ return artifact.kind === 'entity' &&
1495
1315
  (hasAnnotationValue(artifact, '@cds.persistence.exists', true) || hasAnnotationValue(artifact, '@cds.persistence.skip', true))
1496
1316
 
1497
1317
  }
@@ -1560,7 +1380,7 @@ function getServiceNames(csn) {
1560
1380
  }
1561
1381
 
1562
1382
  /**
1563
- * Check wether the artifact is @cds.persistence.skip
1383
+ * Check whether the artifact is @cds.persistence.skip
1564
1384
  *
1565
1385
  * @param {CSN.Artifact} artifact
1566
1386
  * @returns {Boolean}
@@ -1579,8 +1399,8 @@ function isSkipped(artifact) {
1579
1399
  function walkCsnPath(csn, path) {
1580
1400
  /** @type {object} */
1581
1401
  let obj = csn;
1582
- for(let i = 0; i < path.length; i++){
1583
- obj = obj[path[i]];
1402
+ for(const segment of path){
1403
+ obj = obj[segment];
1584
1404
  }
1585
1405
 
1586
1406
  return obj;
@@ -1589,16 +1409,16 @@ function walkCsnPath(csn, path) {
1589
1409
  /**
1590
1410
  * If provided, get the replacement string for the given magic variable ref.
1591
1411
  * No validation is done that the ref is actually magic!
1592
- *
1593
- * @param {array} ref
1594
- * @param {CSN.Options} options
1595
- * @returns {string|null}
1412
+ *
1413
+ * @param {array} ref
1414
+ * @param {CSN.Options} options
1415
+ * @returns {string|null}
1596
1416
  */
1597
1417
  function getVariableReplacement(ref, options) {
1598
1418
  if(options && options.variableReplacements) {
1599
1419
  let replacement = options.variableReplacements;
1600
- for(let i = 0; i < ref.length; i++) {
1601
- replacement = replacement[ref[i]];
1420
+ for(const segment of ref) {
1421
+ replacement = replacement[segment];
1602
1422
  if(replacement === undefined)
1603
1423
  return null;
1604
1424
  }
@@ -1614,6 +1434,42 @@ function getVariableReplacement(ref, options) {
1614
1434
  }
1615
1435
  }
1616
1436
 
1437
+ /**
1438
+ *
1439
+ * @param {object} obj
1440
+ * @param {*} other
1441
+ * @param {boolean} noExtendedProps
1442
+ * @returns {boolean} returns equality
1443
+ *
1444
+ * noExtendedProps remove '$', '_' and '@' properties from
1445
+ * the comparision. This eliminates false negatives such as
1446
+ * mismatching $locations or @odata.foreignKey4.
1447
+ */
1448
+ function isDeepEqual(obj, other, noExtendedProps) {
1449
+ let objectKeys = Object.keys(obj);
1450
+ let otherKeys = Object.keys(other);
1451
+
1452
+ if(noExtendedProps) {
1453
+ objectKeys = objectKeys.filter(k => !['@', '$', '_'].includes(k[0]));
1454
+ otherKeys = otherKeys.filter(k => !['@', '$', '_'].includes(k[0]));
1455
+ }
1456
+ if (objectKeys.length !== otherKeys.length)
1457
+ return false;
1458
+
1459
+ for (let key of objectKeys) {
1460
+ const areValuesObjects = (obj[key] != null && typeof obj[key] === 'object')
1461
+ && (other[key] !== null && typeof other[key] === 'object');
1462
+
1463
+ if (areValuesObjects) {
1464
+ if (!isDeepEqual(obj[key], other[key], noExtendedProps))
1465
+ return false;
1466
+ } else if (obj[key] !== other[key]) {
1467
+ return false;
1468
+ }
1469
+ }
1470
+ return true;
1471
+ }
1472
+
1617
1473
  module.exports = {
1618
1474
  getUtils,
1619
1475
  cloneCsn,
@@ -1624,9 +1480,7 @@ module.exports = {
1624
1480
  forEachDefinition,
1625
1481
  forEachMember,
1626
1482
  forEachMemberRecursively,
1627
- forEachRef,
1628
1483
  forAllQueries,
1629
- forAllElements,
1630
1484
  hasAnnotationValue,
1631
1485
  isEdmPropertyRendered,
1632
1486
  getArtifactDatabaseNameOf,
@@ -1634,12 +1488,12 @@ module.exports = {
1634
1488
  getUnderscoredName,
1635
1489
  getElementDatabaseNameOf,
1636
1490
  applyTransformations,
1491
+ applyTransformationsOnNonDictionary,
1637
1492
  setDependencies,
1638
1493
  isPersistedOnDatabase,
1639
1494
  generatedByCompilerVersion,
1640
1495
  getNormalizedQuery,
1641
1496
  mergeOptions,
1642
- getTopLevelArtifactNameOf,
1643
1497
  getRootArtifactName,
1644
1498
  getLastPartOfRef,
1645
1499
  getParentNamesOf,
@@ -1654,5 +1508,7 @@ module.exports = {
1654
1508
  getServiceNames,
1655
1509
  isSkipped,
1656
1510
  walkCsnPath,
1657
- getVariableReplacement
1511
+ getVariableReplacement,
1512
+ implicitAs,
1513
+ isDeepEqual,
1658
1514
  };