@sap/cds-compiler 2.13.8 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/CHANGELOG.md +155 -1594
  2. package/bin/cdsc.js +144 -66
  3. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  4. package/doc/CHANGELOG_BETA.md +3 -4
  5. package/doc/CHANGELOG_DEPRECATED.md +35 -1
  6. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  7. package/doc/Versioning.md +20 -1
  8. package/lib/api/.eslintrc.json +2 -2
  9. package/lib/api/main.js +237 -122
  10. package/lib/api/options.js +17 -88
  11. package/lib/api/validate.js +12 -16
  12. package/lib/base/keywords.js +216 -109
  13. package/lib/base/message-registry.js +152 -37
  14. package/lib/base/messages.js +145 -83
  15. package/lib/base/model.js +44 -2
  16. package/lib/base/optionProcessorHelper.js +19 -0
  17. package/lib/checks/actionsFunctions.js +7 -5
  18. package/lib/checks/annotationsOData.js +11 -32
  19. package/lib/checks/arrayOfs.js +1 -34
  20. package/lib/checks/cdsPersistence.js +1 -0
  21. package/lib/checks/elements.js +6 -6
  22. package/lib/checks/invalidTarget.js +1 -1
  23. package/lib/checks/nonexpandableStructured.js +1 -1
  24. package/lib/checks/queryNoDbArtifacts.js +2 -1
  25. package/lib/checks/selectItems.js +5 -1
  26. package/lib/checks/types.js +4 -2
  27. package/lib/checks/utils.js +2 -2
  28. package/lib/checks/validator.js +4 -5
  29. package/lib/compiler/assert-consistency.js +16 -10
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +98 -9
  32. package/lib/compiler/checks.js +22 -70
  33. package/lib/compiler/define.js +61 -13
  34. package/lib/compiler/extend.js +79 -14
  35. package/lib/compiler/finalize-parse-cdl.js +46 -29
  36. package/lib/compiler/index.js +100 -37
  37. package/lib/compiler/moduleLayers.js +7 -0
  38. package/lib/compiler/populate.js +19 -18
  39. package/lib/compiler/propagator.js +7 -4
  40. package/lib/compiler/resolve.js +297 -234
  41. package/lib/compiler/shared.js +107 -102
  42. package/lib/compiler/tweak-assocs.js +16 -11
  43. package/lib/compiler/utils.js +5 -0
  44. package/lib/edm/annotations/genericTranslation.js +93 -21
  45. package/lib/edm/csn2edm.js +230 -115
  46. package/lib/edm/edm.js +305 -226
  47. package/lib/edm/edmPreprocessor.js +509 -438
  48. package/lib/edm/edmUtils.js +31 -45
  49. package/lib/gen/Dictionary.json +98 -22
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +10 -30
  52. package/lib/gen/language.tokens +105 -114
  53. package/lib/gen/languageLexer.interp +1 -34
  54. package/lib/gen/languageLexer.js +889 -1007
  55. package/lib/gen/languageLexer.tokens +95 -106
  56. package/lib/gen/languageParser.js +20786 -22199
  57. package/lib/json/csnVersion.js +10 -11
  58. package/lib/json/from-csn.js +59 -51
  59. package/lib/json/to-csn.js +10 -10
  60. package/lib/language/antlrParser.js +2 -2
  61. package/lib/language/docCommentParser.js +62 -39
  62. package/lib/language/errorStrategy.js +52 -40
  63. package/lib/language/genericAntlrParser.js +348 -229
  64. package/lib/language/language.g4 +629 -653
  65. package/lib/language/multiLineStringParser.js +14 -42
  66. package/lib/language/textUtils.js +44 -0
  67. package/lib/main.d.ts +46 -43
  68. package/lib/main.js +108 -79
  69. package/lib/model/csnRefs.js +34 -7
  70. package/lib/model/csnUtils.js +337 -332
  71. package/lib/model/enrichCsn.js +1 -0
  72. package/lib/model/revealInternalProperties.js +30 -10
  73. package/lib/model/sortViews.js +32 -31
  74. package/lib/modelCompare/compare.js +6 -6
  75. package/lib/optionProcessor.js +73 -46
  76. package/lib/render/.eslintrc.json +1 -1
  77. package/lib/render/DuplicateChecker.js +4 -7
  78. package/lib/render/manageConstraints.js +70 -2
  79. package/lib/render/toCdl.js +1042 -882
  80. package/lib/render/toHdbcds.js +195 -245
  81. package/lib/render/toRename.js +44 -22
  82. package/lib/render/toSql.js +225 -241
  83. package/lib/render/utils/common.js +145 -15
  84. package/lib/render/utils/sql.js +20 -19
  85. package/lib/sql-identifier.js +6 -0
  86. package/lib/transform/db/.eslintrc.json +4 -3
  87. package/lib/transform/db/associations.js +2 -2
  88. package/lib/transform/db/cdsPersistence.js +5 -15
  89. package/lib/transform/db/constraints.js +4 -2
  90. package/lib/transform/db/expansion.js +22 -16
  91. package/lib/transform/db/flattening.js +109 -80
  92. package/lib/transform/db/transformExists.js +7 -7
  93. package/lib/transform/db/views.js +9 -6
  94. package/lib/transform/draft/.eslintrc.json +2 -2
  95. package/lib/transform/draft/db.js +6 -6
  96. package/lib/transform/draft/odata.js +6 -7
  97. package/lib/transform/forHanaNew.js +62 -48
  98. package/lib/transform/forOdataNew.js +49 -50
  99. package/lib/transform/localized.js +31 -20
  100. package/lib/transform/odata/toFinalBaseType.js +16 -14
  101. package/lib/transform/odata/typesExposure.js +146 -198
  102. package/lib/transform/odata/utils.js +1 -38
  103. package/lib/transform/transformUtilsNew.js +67 -84
  104. package/lib/transform/translateAssocsToJoins.js +7 -3
  105. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  106. package/lib/transform/universalCsn/coreComputed.js +16 -9
  107. package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
  108. package/lib/utils/file.js +3 -3
  109. package/lib/utils/moduleResolve.js +13 -6
  110. package/lib/utils/timetrace.js +20 -21
  111. package/package.json +35 -4
  112. package/share/messages/message-explanations.json +2 -1
  113. package/share/messages/syntax-expected-integer.md +37 -0
  114. package/doc/ApiMigration.md +0 -237
  115. package/doc/CommandLineMigration.md +0 -58
  116. package/doc/ErrorMessages.md +0 -175
  117. package/doc/FioriAnnotations.md +0 -94
  118. package/doc/ODataTransformation.md +0 -273
  119. package/lib/backends.js +0 -529
  120. package/lib/fix_antlr4-8_warning.js +0 -56
  121. package/lib/transform/odata/attachPath.js +0 -96
  122. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  123. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  124. package/lib/transform/odata/referenceFlattener.js +0 -296
  125. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  126. package/lib/transform/odata/structuralPath.js +0 -72
  127. package/lib/transform/odata/structureFlattener.js +0 -171
@@ -3,12 +3,13 @@
3
3
  const {
4
4
  getUtils, walkCsnPath,
5
5
  applyTransformations, applyTransformationsOnNonDictionary,
6
- isBuiltinType, cloneCsn,
6
+ isBuiltinType, cloneCsnNonDict,
7
7
  copyAnnotations, implicitAs, isDeepEqual,
8
8
  } = require('../../model/csnUtils');
9
9
  const transformUtils = require('../transformUtilsNew');
10
10
  const { csnRefs } = require('../../model/csnRefs');
11
11
  const { setProp } = require('../../base/model');
12
+ const { forEach } = require('../../utils/objectUtils');
12
13
 
13
14
  /**
14
15
  * Strip off leading $self from refs where applicable
@@ -46,6 +47,7 @@ function removeLeadingSelf(csn) {
46
47
  * @param {object} iterateOptions
47
48
  */
48
49
  function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOptions = {}) {
50
+ const typeCache = Object.create(null); // TODO: Argument as well?
49
51
  /**
50
52
  * Remove .localized from the element and any sub-elements
51
53
  *
@@ -66,26 +68,29 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
66
68
  stack.push(...Object.values(current.elements));
67
69
  }
68
70
  }
69
- const { toFinalBaseType } = transformUtils.getTransformers(csn, options, pathDelimiter);
70
- const { getServiceName, getFinalBaseType } = getUtils(csn);
71
+ const { toFinalBaseType, csnUtils } = transformUtils.getTransformers(csn, options, pathDelimiter);
72
+ const { getServiceName, getFinalBaseTypeWithProps } = csnUtils;
71
73
 
72
74
  // We don't want to iterate over actions
73
75
  if (iterateOptions.skipDict && !iterateOptions.skipDict.actions)
74
76
  iterateOptions.skipDict.actions = true;
75
77
  else
76
78
  iterateOptions.skipDict = { actions: true };
79
+
80
+ const ignoreOdataKinds = { aspect: 1, event: 1, type: 1 };
81
+ const replaceWithDummyKinds = { action: 1, function: 1, event: 1 };
77
82
  applyTransformations(csn, {
78
- cast: (parent) => {
83
+ cast: (parent, prop, cast, path) => {
79
84
  // Resolve cast already - we otherwise lose .localized
80
- if (parent.cast.type && !isBuiltinType(parent.cast.type))
81
- toFinalBaseType(parent.cast, resolved, true);
85
+ if (cast.type && !isBuiltinType(cast.type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(cast.type, path) && !isODataItems(cast.type)))
86
+ toFinalBaseType(parent.cast, resolved, true, typeCache);
82
87
  },
83
88
  // @ts-ignore
84
- type: (parent, prop, type, csnPath) => {
85
- if (options.toOdata && parent.kind && [ 'aspect', 'event', 'type' ].includes(parent.kind))
89
+ type: (parent, prop, type, path) => {
90
+ if (options.toOdata && parent.kind && parent.kind in ignoreOdataKinds)
86
91
  return;
87
- if (!isBuiltinType(type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(type) && !isODataItems(type))) {
88
- toFinalBaseType(parent, resolved);
92
+ if (!isBuiltinType(type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(type, path) && !isODataItems(type))) {
93
+ toFinalBaseType(parent, resolved, true, typeCache);
89
94
  // structured types might not have the child-types replaced.
90
95
  // Drill down to ensure this.
91
96
  if (parent.elements) {
@@ -94,7 +99,7 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
94
99
  const elements = stack.pop();
95
100
  for (const e of Object.values(elements)) {
96
101
  if (e.type && !isBuiltinType(e.type))
97
- toFinalBaseType(e, resolved);
102
+ toFinalBaseType(e, resolved, true, typeCache);
98
103
 
99
104
  if (e.elements)
100
105
  stack.push(e.elements);
@@ -110,40 +115,6 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
110
115
  parent.type = 'cds.LargeString';
111
116
  delete parent.items;
112
117
  }
113
-
114
- /**
115
- * OData V4 only:
116
- * Do not replace a type ref if:
117
- * The type definition is terminating on a scalar type (that can also be a derived type chain)
118
- * AND the typeName (that is the start of that (derived) type chain is defined within the same
119
- * service as the artifact from which the type reference has to be resolved.
120
- *
121
- * @param {string} typeName
122
- * @returns {boolean}
123
- */
124
- function isODataV4BuiltinFromService(typeName) {
125
- if (!options.toOdata || (options.toOdata && options.toOdata.version === 'v2'))
126
- return false;
127
-
128
- const typeServiceName = getServiceName(typeName);
129
- const finalBaseType = getFinalBaseType(typeName);
130
- // we need the service of the current definition
131
- const currDefServiceName = getServiceName(csnPath[1]);
132
-
133
- return typeServiceName === currDefServiceName && isBuiltinType(finalBaseType);
134
- }
135
-
136
- /**
137
- * OData stops replacing types @ 'items', if the type ref is a user defined type
138
- * AND that type has items, don't do toFinalBaseType
139
- *
140
- * @param {string} typeName
141
- * @returns {boolean}
142
- */
143
- function isODataItems(typeName) {
144
- const typeDef = csn.definitions[typeName];
145
- return !!(options.toOdata && typeDef && typeDef.items);
146
- }
147
118
  },
148
119
  // HANA/SQLite do not support array-of - turn into CLOB/Text
149
120
  items: (parent) => {
@@ -159,8 +130,7 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
159
130
 
160
131
  // Do not do for OData
161
132
  // TODO:factor out somewhere else
162
- if (!options.toOdata &&
163
- ([ 'action', 'function', 'event' ].includes(artifact.kind))) {
133
+ if (!options.toOdata && artifact.kind in replaceWithDummyKinds) {
164
134
  const dummy = { kind: artifact.kind };
165
135
  if (artifact.$location)
166
136
  setProp(dummy, '$location', artifact.$location);
@@ -169,6 +139,42 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
169
139
  }
170
140
  // TODO: skipDict options as default function arguments not via Object.assign
171
141
  } ], iterateOptions);
142
+
143
+
144
+ /**
145
+ * OData V4 only:
146
+ * Do not replace a type ref if:
147
+ * The type definition is terminating on a scalar type (that can also be a derived type chain)
148
+ * AND the typeName (that is the start of that (derived) type chain is defined within the same
149
+ * service as the artifact from which the type reference has to be resolved.
150
+ *
151
+ * @param {string} typeName
152
+ * @param {CSN.Path} path
153
+ * @returns {boolean}
154
+ */
155
+ function isODataV4BuiltinFromService(typeName, path) {
156
+ if (!options.toOdata || (options.odataVersion === 'v2') || typeof typeName !== 'string')
157
+ return false;
158
+
159
+ const typeServiceName = getServiceName(typeName);
160
+ const finalBaseType = getFinalBaseTypeWithProps(typeName)?.type;
161
+ // we need the service of the current definition
162
+ const currDefServiceName = getServiceName(path[1]);
163
+
164
+ return typeServiceName === currDefServiceName && isBuiltinType(finalBaseType);
165
+ }
166
+
167
+ /**
168
+ * OData stops replacing types @ 'items', if the type ref is a user defined type
169
+ * AND that type has items, don't do toFinalBaseType
170
+ *
171
+ * @param {string} typeName
172
+ * @returns {boolean}
173
+ */
174
+ function isODataItems(typeName) {
175
+ const typeDef = csn.definitions[typeName];
176
+ return !!(options.toOdata && typeDef && typeDef.items);
177
+ }
172
178
  }
173
179
 
174
180
  /**
@@ -258,12 +264,11 @@ function flattenAllStructStepsInRefs(csn, options, resolved, pathDelimiter, iter
258
264
  * @param {CSN.Options} options
259
265
  * @param {string} pathDelimiter
260
266
  * @param {Function} error
261
- * @param {Object} iterateOptions
267
+ * @param {object} iterateOptions
262
268
  */
263
269
  function flattenElements(csn, options, pathDelimiter, error, iterateOptions = {}) {
264
- const { isAssocOrComposition } = getUtils(csn);
265
- const { flattenStructuredElement } = transformUtils.getTransformers(csn, options, pathDelimiter);
266
- const { effectiveType } = csnRefs(csn);
270
+ const { flattenStructuredElement, csnUtils } = transformUtils.getTransformers(csn, options, pathDelimiter);
271
+ const { isAssocOrComposition, effectiveType } = csnUtils;
267
272
  const transformers = {
268
273
  elements: flatten,
269
274
  };
@@ -284,7 +289,7 @@ function flattenElements(csn, options, pathDelimiter, error, iterateOptions = {}
284
289
  function flatten(parent, prop, dict, path) {
285
290
  if (!parent[prop].$orderedElements)
286
291
  setProp(parent[prop], '$orderedElements', []);
287
- Object.entries(dict).forEach(([ elementName, element ]) => {
292
+ forEach(dict, (elementName, element) => {
288
293
  if (element.elements) {
289
294
  // Ignore the structured element, replace it by its flattened form
290
295
  // TODO: use $ignore - _ is for links
@@ -294,11 +299,13 @@ function flattenElements(csn, options, pathDelimiter, error, iterateOptions = {}
294
299
  const flatElems = flattenStructuredElement(element, elementName, [], path.concat([ 'elements', elementName ]));
295
300
 
296
301
  for (const flatElemName in flatElems) {
297
- if (parent[prop][flatElemName])
298
- // TODO: combine message ID with generated FK duplicate
299
- // do the duplicate check in the consruct callback, requires to mark generated flat elements,
300
- // check: Error location should be the existing element like @odata.foreignKey4
301
- error(null, path.concat([ 'elements', elementName ]), `"${path[1]}.${elementName}": Flattened struct element name conflicts with existing element: "${flatElemName}"`);
302
+ if (parent[prop][flatElemName]) {
303
+ // TODO: combine message ID with generated FK duplicate
304
+ // do the duplicate check in the construct callback, requires to mark generated flat elements,
305
+ // check: Error location should be the existing element like @odata.foreignKey4
306
+ error('name-duplicate-element', path.concat([ 'elements', elementName ]),
307
+ { '#': 'flatten-element-exist', name: flatElemName });
308
+ }
302
309
 
303
310
  const flatElement = flatElems[flatElemName];
304
311
 
@@ -315,16 +322,16 @@ function flattenElements(csn, options, pathDelimiter, error, iterateOptions = {}
315
322
  const firstRef = onPart.ref[0];
316
323
 
317
324
  /*
318
- when element is defined in the current name resolution scope, like
319
- entity E {
320
- key x: Integer;
321
- s : {
322
- y : Integer;
323
- a3 : association to E on a3.x = y;
324
- }
325
- }
326
- We need to replace y with s_y and a3 with s_a3 - we must take care to not escape our local scope
327
- */
325
+ when element is defined in the current name resolution scope, like
326
+ entity E {
327
+ key x: Integer;
328
+ s : {
329
+ y : Integer;
330
+ a3 : association to E on a3.x = y;
331
+ }
332
+ }
333
+ We need to replace y with s_y and a3 with s_a3 - we must take care to not escape our local scope
334
+ */
328
335
  const prefix = flatElement._flatElementNameWithDots.split('.').slice(0, -1).join(pathDelimiter);
329
336
  const possibleFlatName = prefix + pathDelimiter + firstRef;
330
337
 
@@ -418,11 +425,11 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
418
425
  }
419
426
  });
420
427
  },
421
- }, [], {
428
+ }, [], Object.assign({
422
429
  skipIgnore: false,
423
430
  allowArtifact: artifact => (artifact.kind === 'entity' || artifact.kind === 'type'),
424
431
  skipDict: { actions: true },
425
- });
432
+ }, iterateOptions));
426
433
  }
427
434
  createForeignKeyElements();
428
435
 
@@ -469,7 +476,7 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
469
476
  const flat = flattenStructuredElement(art, ref[ref.length - 1], [], pathToKey);
470
477
  Object.keys(flat).forEach((flatElemName) => {
471
478
  const key = assoc.keys[i];
472
- const clone = cloneCsn(assoc.keys[i], options);
479
+ const clone = cloneCsnNonDict(assoc.keys[i], options);
473
480
  if (clone.as) {
474
481
  const lastRef = clone.ref[clone.ref.length - 1];
475
482
  // Cut off the last ref part from the beginning of the flat name
@@ -533,7 +540,7 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
533
540
  * @returns {object} The clone of base
534
541
  */
535
542
  function cloneAndExtendRef(key, base, ref) {
536
- const clone = cloneCsn(base, options);
543
+ const clone = cloneCsnNonDict(base, options);
537
544
  if (key.ref) {
538
545
  // We build a ref that contains the aliased fk - that element will be created later on, so this ref is not resolvable yet
539
546
  // Therefore we keep it as $ref - ref is the non-aliased, resolvable "clone"
@@ -604,8 +611,7 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
604
611
  ((options.toOdata && isDeepEqual(element, parent[prop][fk[0]], true)) ||
605
612
  !options.toOdata)) {
606
613
  // error location is the colliding element
607
- error(null, eltPath, { name: fk[0], art: elementName },
608
- 'Generated foreign key element $(NAME) for association $(ART) conflicts with existing element');
614
+ error('name-duplicate-element', eltPath, { '#': 'flatten-fkey-exists', name: fk[0], art: elementName });
609
615
  }
610
616
  // attach a proper $path
611
617
  setProp(element, '$path', eltPath);
@@ -614,10 +620,8 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
614
620
 
615
621
  // check for duplicate foreign keys
616
622
  Object.entries(refCount).forEach(([ name, occ ]) => {
617
- if (occ > 1) {
618
- error(null, eltPath, { name },
619
- 'Duplicate definition of foreign key element $(NAME)');
620
- }
623
+ if (occ > 1)
624
+ error('name-duplicate-element', eltPath, { '#': 'flatten-fkey-gen', name, art: elementName });
621
625
  });
622
626
  if (element.keys) {
623
627
  element.keys.forEach((key, i) => {
@@ -682,17 +686,21 @@ function createForeignKeysInternal(path, element, prefix, csn, options, pathDeli
682
686
  return fks;
683
687
 
684
688
  let finalElement = element;
689
+ let finalTypeName; // TODO: Find a way to not rely on $path?
685
690
  // TODO: effectiveType's return value is 'path' for the next inspectRef
686
691
  if (element.type && !isBuiltinType(element.type)) {
687
692
  const tmpElt = effectiveType(element);
688
693
  // effective type resolves to structs and enums only but not scalars
689
694
  if (Object.keys(tmpElt).length) {
690
695
  finalElement = tmpElt;
696
+ finalTypeName = finalElement.$path[1];
691
697
  }
692
698
  else {
693
699
  // unwind a derived type chain to a scalar type
694
- while (finalElement.type && !isBuiltinType(finalElement.type))
700
+ while (finalElement.type && !isBuiltinType(finalElement.type)) {
701
+ finalTypeName = finalElement.type;
695
702
  finalElement = csn.definitions[finalElement.type];
703
+ }
696
704
  }
697
705
  }
698
706
 
@@ -709,7 +717,7 @@ function createForeignKeysInternal(path, element, prefix, csn, options, pathDeli
709
717
  }
710
718
  // TODO: has managed assoc keys?
711
719
  finalElement.keys.forEach((key, keyIndex) => {
712
- const continuePath = isInspectRefResult ? [ path, 'keys', keyIndex ] : [ ...path, 'keys', keyIndex ];
720
+ const continuePath = getContinuePath([ 'keys', keyIndex ]);
713
721
  const alias = key.as || implicitAs(key.ref);
714
722
  const result = inspectRef(continuePath);
715
723
  fks = fks.concat(createForeignKeysInternal(result, result.art, alias, csn, options, pathDelimiter, lvl + 1));
@@ -735,12 +743,33 @@ function createForeignKeysInternal(path, element, prefix, csn, options, pathDeli
735
743
  Object.entries(finalElement.elements).forEach(([ elemName, elem ]) => {
736
744
  // Skip already produced foreign keys
737
745
  if (!elem['@odata.foreignKey4']) {
738
- const continuePath = isInspectRefResult ? [ path, 'elements', elemName ] : [ ...path, 'elements', elemName ];
746
+ const continuePath = getContinuePath([ 'elements', elemName ]);
739
747
  fks = fks.concat(createForeignKeysInternal(continuePath, elem, elemName, csn, options, pathDelimiter, lvl + 1));
740
748
  }
741
749
  });
742
750
  }
743
751
 
752
+ /**
753
+ * Get the path to continue resolving references
754
+ *
755
+ * If we are currently inside of a type, we need to start our path fresh from that given type.
756
+ * Otherwise, we would try to resolve .elements on a thing that does not exist.
757
+ *
758
+ * We also respect if we have a previous inspectRef result as our base.
759
+ *
760
+ * @param {Array} additions
761
+ * @returns {CSN.Path}
762
+ */
763
+ function getContinuePath(additions) {
764
+ if (csn.definitions[finalElement.type])
765
+ return [ 'definitions', finalElement.type, ...additions ];
766
+ else if (finalTypeName)
767
+ return [ 'definitions', finalTypeName, ...additions ];
768
+ else if (isInspectRefResult)
769
+ return [ path, ...additions ];
770
+ return [ ...path, ...additions ];
771
+ }
772
+
744
773
  fks.forEach((fk) => {
745
774
  // prepend current prefix
746
775
  fk[0] = `${prefix}${pathDelimiter}${fk[0]}`;
@@ -110,8 +110,8 @@ function handleExists(csn, options, error) {
110
110
  sources[join.as] = join.as;
111
111
  }
112
112
  else if (join.args) {
113
- const subsources = getJoinSources(join.args);
114
- sources = Object.assign(sources, subsources);
113
+ const subSources = getJoinSources(join.args);
114
+ sources = Object.assign(sources, subSources);
115
115
  }
116
116
  else if (join.ref) {
117
117
  sources[join.ref[join.ref.length - 1]] = join.ref[join.ref.length - 1];
@@ -196,7 +196,7 @@ function handleExists(csn, options, error) {
196
196
  const stack = [ [ null, startAssoc, startRest, startIndex ] ];
197
197
  const { links } = inspectRef(path);
198
198
  while (stack.length > 0) {
199
- // previous: to nest "up" if the previous assoc did not originaly have a filter
199
+ // previous: to nest "up" if the previous assoc did not originally have a filter
200
200
  // assoc: the assoc path step
201
201
  // rest: path steps after assoc
202
202
  // index: index of after-assoc in the overall ref-array - so we know where to start looking for the next assoc
@@ -411,14 +411,14 @@ function handleExists(csn, options, error) {
411
411
  * Translate an `EXISTS <managed assoc>` into a part of a WHERE condition.
412
412
  *
413
413
  * For each of the foreign keys, do:
414
- * + build the target side by prefixing `target` infront of the ref
414
+ * + build the target side by prefixing `target` in front of the ref
415
415
  * + build the source side by prefixing `base` (if not already part of `current`)
416
- * and the assoc name itself (current) infront of the ref
416
+ * and the assoc name itself (current) in front of the ref
417
417
  * + Compare source and target with `=`
418
418
  *
419
419
  * If there is more than one foreign key, join with `and`.
420
420
  *
421
- * The new tokens are immediatly added to the WHERE of the subselect
421
+ * The new tokens are immediately added to the WHERE of the subselect
422
422
  *
423
423
  * @param {CSN.Element} root
424
424
  * @param {string} target
@@ -752,7 +752,7 @@ function handleExists(csn, options, error) {
752
752
  *
753
753
  * @param {string} target
754
754
  * @param {TokenStream} where
755
- * @returns {TokenStream} The input-where with the refs "absolutified"
755
+ * @returns {TokenStream} The input-where with the refs transformed to absolute ones
756
756
  */
757
757
  function remapExistingWhere(target, where) {
758
758
  return where.map((part) => {
@@ -1,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  const {
4
- getUtils, cloneCsn, applyTransformationsOnNonDictionary,
4
+ getUtils, cloneCsnNonDict, applyTransformationsOnNonDictionary,
5
5
  } = require('../../model/csnUtils');
6
- const { implicitAs, csnRefs } = require('../../model/csnRefs');
6
+ const { implicitAs } = require('../../model/csnRefs');
7
7
  const { isBetaEnabled } = require('../../base/model');
8
8
  const { ModelError } = require('../../base/error');
9
9
 
@@ -69,9 +69,9 @@ function usesMixinAssociation(query, association, associationName) {
69
69
  function getViewTransformer(csn, options, messageFunctions, transformCommon) {
70
70
  const {
71
71
  get$combined, isAssocOrComposition,
72
+ inspectRef, queryOrMain, // csnRefs
72
73
  } = getUtils(csn);
73
- const { inspectRef, queryOrMain } = csnRefs(csn);
74
- const pathDelimiter = (options.forHana.names === 'hdbcds') ? '.' : '_';
74
+ const pathDelimiter = options.forHana && (options.sqlMapping === 'hdbcds') ? '.' : '_';
75
75
  const { error, info } = messageFunctions;
76
76
  const doA2J = !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds');
77
77
 
@@ -209,7 +209,7 @@ function getViewTransformer(csn, options, messageFunctions, transformCommon) {
209
209
  const assocCol = columnMap[elemName];
210
210
  if (assocCol && assocCol.ref) {
211
211
  elem.keys.forEach((foreignKey) => {
212
- const ref = cloneCsn(assocCol.ref, options);
212
+ const ref = cloneCsnNonDict(assocCol.ref, options);
213
213
  ref[ref.length - 1] = [ getLastRefStepString(ref) ].concat(foreignKey.as).join(pathDelimiter);
214
214
  const result = {
215
215
  ref,
@@ -277,7 +277,7 @@ function getViewTransformer(csn, options, messageFunctions, transformCommon) {
277
277
 
278
278
  // Copy the association element to the MIXIN clause under its alias name
279
279
  // Needs to be a deep copy, as we transform the on-condition
280
- const mixinElem = cloneCsn(elem, options);
280
+ const mixinElem = cloneCsnNonDict(elem, options);
281
281
  // Perform common transformations on the newly generated MIXIN element (won't be reached otherwise)
282
282
  transformCommon(mixinElem, mixinElemName);
283
283
 
@@ -457,6 +457,9 @@ function checkIsNotMixinByItself(query, columnMap, elementName) {
457
457
  if (query && query.SELECT && query.SELECT.mixin) {
458
458
  const col = columnMap[elementName];
459
459
 
460
+ if (!col.ref) // No ref -> new association, but not a mixin.
461
+ return true;
462
+
460
463
  // Use getLastRefStepString - with hdbcds.hdbcds and malicious CSN input we might have .id
461
464
  const realName = getLastRefStepString(col.ref);
462
465
  // If the element is not part of the mixin => True
@@ -23,11 +23,11 @@
23
23
  "sonarjs/no-duplicate-string": "off"
24
24
  },
25
25
  "parserOptions": {
26
- "ecmaVersion": 2018,
26
+ "ecmaVersion": 2020,
27
27
  "sourceType": "script"
28
28
  },
29
29
  "env": {
30
- "es6": true,
30
+ "es2020": true,
31
31
  "node": true
32
32
  },
33
33
  "settings": {
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const {
4
- hasAnnotationValue, getUtils, getServiceNames, forEachDefinition,
4
+ hasAnnotationValue, getServiceNames, forEachDefinition,
5
5
  getResultingName, forEachMemberRecursively,
6
6
  } = require('../../model/csnUtils');
7
7
  const { setProp, isDeprecatedEnabled } = require('../../base/model');
@@ -19,15 +19,15 @@ const booleanBuiltin = 'cds.Boolean';
19
19
  * @param {object} messageFunctions
20
20
  */
21
21
  function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
22
- const draftSuffix = isDeprecatedEnabled(options, 'generatedEntityNameWithUnderscore') ? '_drafts' : '.drafts';
22
+ const draftSuffix = isDeprecatedEnabled(options, '_generatedEntityNameWithUnderscore') ? '_drafts' : '.drafts';
23
23
  // All services of the model - needed for drafts
24
24
  const allServices = getServiceNames(csn);
25
25
  const draftRoots = new WeakMap();
26
26
  const {
27
27
  createForeignKeyElement, createAndAddDraftAdminDataProjection, createScalarElement, createAssociationElement,
28
- addElement, copyAndAddElement, createAssociationPathComparison,
28
+ addElement, copyAndAddElement, createAssociationPathComparison, csnUtils,
29
29
  } = getTransformers(csn, options, pathDelimiter);
30
- const { getCsnDef, isComposition } = getUtils(csn);
30
+ const { getCsnDef, isComposition } = csnUtils;
31
31
  const { error, warning } = messageFunctions;
32
32
 
33
33
  forEachDefinition(csn, generateDraft);
@@ -155,7 +155,7 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
155
155
  'Generated entity $(NAME) conflicts with existing artifact');
156
156
  }
157
157
 
158
- const persistenceName = getResultingName(csn, options.forHana.names, draftsArtifactName);
158
+ const persistenceName = getResultingName(csn, options.sqlMapping, draftsArtifactName);
159
159
  // Duplicate the artifact as a draft shadow entity
160
160
  if (csn.definitions[persistenceName]) {
161
161
  const definingDraftRoot = draftRoots.get(csn.definitions[persistenceName]);
@@ -187,7 +187,7 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
187
187
  for (const elemName in artifact.elements) {
188
188
  const origElem = artifact.elements[elemName];
189
189
  let elem;
190
- if ((isDeprecatedEnabled(options, 'renderVirtualElements') && origElem.virtual) || !origElem.virtual)
190
+ if ((isDeprecatedEnabled(options, '_renderVirtualElements') && origElem.virtual) || !origElem.virtual)
191
191
  elem = copyAndAddElement(origElem, draftsArtifact, draftsArtifactName, elemName)[elemName];
192
192
  if (elem) {
193
193
  // Remove "virtual" - cap/issues 4956
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { forEachDefinition, getUtils, getServiceNames } = require('../../model/csnUtils');
3
+ const { forEachDefinition, getServiceNames } = require('../../model/csnUtils');
4
4
  const { forEach } = require('../../utils/objectUtils');
5
5
  const { isArtifactInSomeService, getServiceOfArtifact } = require('../odata/utils');
6
6
  const { getTransformers } = require('../transformUtilsNew');
@@ -30,15 +30,14 @@ function generateDrafts(csn, options, services) {
30
30
  createAssociationElement, createAssociationPathComparison,
31
31
  addElement, createAction, assignAction,
32
32
  resetAnnotation,
33
+ csnUtils,
33
34
  } = getTransformers(csn, options);
34
-
35
35
  const {
36
36
  getFinalType,
37
37
  getServiceName,
38
38
  hasAnnotationValue,
39
- getFinalBaseType,
40
- getFinalTypeDef,
41
- } = getUtils(csn);
39
+ getFinalBaseTypeWithProps,
40
+ } = csnUtils;
42
41
 
43
42
  const { error, info } = makeMessageFunction(csn, options, 'for.odata');
44
43
 
@@ -197,8 +196,8 @@ function generateDrafts(csn, options, services) {
197
196
  stack.push(elem);
198
197
  }
199
198
  else if (elem.type) { // types - possibly structured
200
- const typeDef = elem.type.ref ? getFinalBaseType(elem.type) : getFinalTypeDef(elem.type);
201
- if (typeDef.elements)
199
+ const typeDef = getFinalBaseTypeWithProps(elem.type);
200
+ if (typeDef?.elements)
202
201
  stack.push(typeDef);
203
202
  }
204
203
  });