@sap/cds-compiler 2.15.8 → 3.1.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 +102 -1590
  2. package/bin/.eslintrc.json +2 -1
  3. package/bin/cdsc.js +61 -46
  4. package/doc/API.md +11 -0
  5. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  6. package/doc/CHANGELOG_BETA.md +26 -5
  7. package/doc/CHANGELOG_DEPRECATED.md +55 -1
  8. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  9. package/doc/Versioning.md +20 -1
  10. package/lib/api/.eslintrc.json +2 -2
  11. package/lib/api/main.js +282 -156
  12. package/lib/api/options.js +17 -88
  13. package/lib/api/validate.js +6 -10
  14. package/lib/base/keywords.js +280 -110
  15. package/lib/base/message-registry.js +85 -25
  16. package/lib/base/messages.js +119 -89
  17. package/lib/base/model.js +46 -2
  18. package/lib/base/optionProcessorHelper.js +53 -21
  19. package/lib/checks/actionsFunctions.js +15 -12
  20. package/lib/checks/annotationsOData.js +1 -1
  21. package/lib/checks/cdsPersistence.js +1 -0
  22. package/lib/checks/elements.js +6 -6
  23. package/lib/checks/invalidTarget.js +1 -1
  24. package/lib/checks/nonexpandableStructured.js +1 -1
  25. package/lib/checks/queryNoDbArtifacts.js +2 -1
  26. package/lib/checks/selectItems.js +101 -15
  27. package/lib/checks/types.js +7 -8
  28. package/lib/checks/utils.js +2 -2
  29. package/lib/checks/validator.js +3 -3
  30. package/lib/compiler/assert-consistency.js +78 -21
  31. package/lib/compiler/base.js +6 -4
  32. package/lib/compiler/builtins.js +177 -10
  33. package/lib/compiler/checks.js +1 -1
  34. package/lib/compiler/define.js +28 -23
  35. package/lib/compiler/extend.js +75 -18
  36. package/lib/compiler/finalize-parse-cdl.js +25 -18
  37. package/lib/compiler/index.js +27 -11
  38. package/lib/compiler/moduleLayers.js +7 -0
  39. package/lib/compiler/populate.js +26 -39
  40. package/lib/compiler/propagator.js +12 -7
  41. package/lib/compiler/resolve.js +207 -236
  42. package/lib/compiler/shared.js +100 -93
  43. package/lib/compiler/tweak-assocs.js +13 -20
  44. package/lib/compiler/utils.js +20 -6
  45. package/lib/edm/annotations/preprocessAnnotations.js +12 -13
  46. package/lib/edm/csn2edm.js +35 -37
  47. package/lib/edm/edm.js +22 -13
  48. package/lib/edm/edmAnnoPreprocessor.js +349 -0
  49. package/lib/edm/edmInboundChecks.js +85 -0
  50. package/lib/edm/edmPreprocessor.js +338 -689
  51. package/lib/edm/edmUtils.js +97 -67
  52. package/lib/gen/Dictionary.json +29 -9
  53. package/lib/gen/language.checksum +1 -1
  54. package/lib/gen/language.interp +8 -31
  55. package/lib/gen/language.tokens +105 -114
  56. package/lib/gen/languageLexer.interp +1 -34
  57. package/lib/gen/languageLexer.js +892 -1007
  58. package/lib/gen/languageLexer.tokens +95 -106
  59. package/lib/gen/languageParser.js +20629 -22474
  60. package/lib/inspect/.eslintrc.json +4 -0
  61. package/lib/inspect/index.js +14 -0
  62. package/lib/inspect/inspectModelStatistics.js +81 -0
  63. package/lib/inspect/inspectPropagation.js +189 -0
  64. package/lib/inspect/inspectUtils.js +44 -0
  65. package/lib/json/from-csn.js +74 -69
  66. package/lib/json/to-csn.js +17 -14
  67. package/lib/language/antlrParser.js +2 -2
  68. package/lib/language/docCommentParser.js +61 -38
  69. package/lib/language/errorStrategy.js +52 -40
  70. package/lib/language/genericAntlrParser.js +424 -292
  71. package/lib/language/language.g4 +604 -687
  72. package/lib/language/multiLineStringParser.js +14 -42
  73. package/lib/language/textUtils.js +44 -0
  74. package/lib/main.d.ts +28 -42
  75. package/lib/main.js +104 -81
  76. package/lib/model/api.js +1 -1
  77. package/lib/model/csnRefs.js +57 -30
  78. package/lib/model/csnUtils.js +189 -287
  79. package/lib/model/revealInternalProperties.js +32 -10
  80. package/lib/model/sortViews.js +32 -31
  81. package/lib/modelCompare/compare.js +3 -0
  82. package/lib/optionProcessor.js +91 -57
  83. package/lib/render/.eslintrc.json +1 -1
  84. package/lib/render/DuplicateChecker.js +4 -7
  85. package/lib/render/manageConstraints.js +70 -2
  86. package/lib/render/toCdl.js +387 -367
  87. package/lib/render/toHdbcds.js +20 -16
  88. package/lib/render/toRename.js +44 -22
  89. package/lib/render/toSql.js +81 -59
  90. package/lib/render/utils/common.js +16 -3
  91. package/lib/render/utils/sql.js +20 -19
  92. package/lib/sql-identifier.js +6 -0
  93. package/lib/transform/db/.eslintrc.json +3 -2
  94. package/lib/transform/db/associations.js +43 -35
  95. package/lib/transform/db/cdsPersistence.js +5 -16
  96. package/lib/transform/db/constraints.js +1 -1
  97. package/lib/transform/db/expansion.js +7 -6
  98. package/lib/transform/db/flattening.js +16 -18
  99. package/lib/transform/db/transformExists.js +7 -5
  100. package/lib/transform/db/views.js +3 -3
  101. package/lib/transform/draft/.eslintrc.json +2 -2
  102. package/lib/transform/draft/db.js +6 -6
  103. package/lib/transform/draft/odata.js +6 -7
  104. package/lib/transform/forHanaNew.js +30 -24
  105. package/lib/transform/forOdataNew.js +14 -16
  106. package/lib/transform/localized.js +35 -25
  107. package/lib/transform/odata/toFinalBaseType.js +10 -10
  108. package/lib/transform/odata/typesExposure.js +17 -8
  109. package/lib/transform/odata/utils.js +1 -38
  110. package/lib/transform/transformUtilsNew.js +63 -77
  111. package/lib/transform/translateAssocsToJoins.js +2 -2
  112. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  113. package/lib/transform/universalCsn/coreComputed.js +11 -6
  114. package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
  115. package/lib/utils/file.js +31 -21
  116. package/lib/utils/moduleResolve.js +0 -1
  117. package/lib/utils/timetrace.js +20 -21
  118. package/package.json +34 -4
  119. package/share/messages/syntax-expected-integer.md +9 -8
  120. package/doc/ApiMigration.md +0 -237
  121. package/doc/CommandLineMigration.md +0 -58
  122. package/doc/ErrorMessages.md +0 -175
  123. package/doc/FioriAnnotations.md +0 -94
  124. package/doc/ODataTransformation.md +0 -273
  125. package/lib/backends.js +0 -529
  126. package/lib/checks/unknownMagic.js +0 -41
  127. package/lib/fix_antlr4-8_warning.js +0 -56
@@ -279,6 +279,18 @@ const cdsToSqlTypes = {
279
279
  'cds.hana.BINARY': 'BINARY',
280
280
  'cds.hana.SMALLDECIMAL': 'DECIMAL',
281
281
  },
282
+ postgres: {
283
+ // TODO: Type mapping for binary types is not correct, yet.
284
+ // We can't use text types for binary on PostgreSQL due to NUL!
285
+ 'cds.String': 'VARCHAR',
286
+ 'cds.LargeString': 'text',
287
+ 'cds.hana.CLOB': 'text',
288
+ 'cds.LargeBinary': 'bytea',
289
+ 'cds.Binary': 'bytea',
290
+ 'cds.hana.BINARY': 'bytea',
291
+ 'cds.Double': 'double precision',
292
+ 'cds.hana.TINYINT': 'INTEGER',
293
+ },
282
294
  };
283
295
 
284
296
  /**
@@ -346,11 +358,10 @@ function hasHanaComment(obj, options) {
346
358
  * Return the comment of the given artifact or element.
347
359
  * Uses the first block (everything up to the first empty line (double \n)).
348
360
  * Remove leading/trailing whitespace.
349
- * Does not escape any characters.
361
+ * Does not escape any characters, use e.g. `getEscapedHanaComment()` for HDBCDS.
350
362
  *
351
363
  * @param {CSN.Artifact|CSN.Element} obj
352
364
  * @returns {string}
353
- * @todo Warning/info to user?
354
365
  */
355
366
  function getHanaComment(obj) {
356
367
  return obj.doc.split('\n\n')[0].trim();
@@ -375,7 +386,7 @@ function getSqlSnippets(options, obj) {
375
386
  * A function used to render a certain part of an expression object
376
387
  *
377
388
  * @callback renderPart
378
- * @param {object||array} expression
389
+ * @param {object|array} expression
379
390
  * @param {CdlRenderEnvironment} env
380
391
  * @this {{inline: Boolean, nestedExpr: Boolean}}
381
392
  * @returns {string}
@@ -397,6 +408,8 @@ function getSqlSnippets(options, obj) {
397
408
  * @property {renderPart} xpr
398
409
  * @property {renderPart} SELECT
399
410
  * @property {renderPart} SET
411
+ * @property {boolean} [inline]
412
+ * @property {boolean} [nestedExpr]
400
413
  */
401
414
 
402
415
  /**
@@ -40,18 +40,18 @@ function renderReferentialConstraint(constraint, indent, toUpperCase, csn, optio
40
40
  }
41
41
 
42
42
  const renderAsHdbconstraint = options.transformation === 'hdbcds' ||
43
- (options.toSql && options.toSql.src === 'hdi') ||
43
+ options.src === 'hdi' ||
44
44
  (options.manageConstraints && options.manageConstraints.src === 'hdi');
45
45
 
46
- const { names } = options.forHana;
47
- const forSqlite = options.toSql && options.toSql.dialect === 'sqlite';
46
+ const { sqlMapping } = options;
47
+ const forSqlite = options.sqlDialect === 'sqlite';
48
48
  let result = '';
49
49
  result += `${indent}CONSTRAINT ${quoteId(constraint.identifier)}\n`;
50
50
  if (renderAsHdbconstraint)
51
- result += `${indent}ON ${quoteId(getResultingName(csn, names, constraint.dependentTable))}\n`;
51
+ result += `${indent}ON ${quoteId(getResultingName(csn, sqlMapping, constraint.dependentTable))}\n`;
52
52
  if (!alterConstraint) {
53
53
  result += `${indent}FOREIGN KEY(${constraint.foreignKey.map(quoteId).join(', ')})\n`;
54
- result += `${indent}REFERENCES ${quoteId(getResultingName(csn, names, constraint.parentTable))}(${constraint.parentKey.map(quoteId).join(', ')})\n`;
54
+ result += `${indent}REFERENCES ${quoteId(getResultingName(csn, sqlMapping, constraint.parentTable))}(${constraint.parentKey.map(quoteId).join(', ')})\n`;
55
55
  const onDeleteRemark = constraint.onDeleteRemark ? ` -- ${constraint.onDeleteRemark}` : '';
56
56
 
57
57
  // omit 'RESTRICT' action for ON UPDATE / ON DELETE, because it interferes with deferred constraint check
@@ -65,12 +65,13 @@ function renderReferentialConstraint(constraint, indent, toUpperCase, csn, optio
65
65
  }
66
66
  }
67
67
  // constraint enforcement / validation must be switched off using sqlite pragma statement
68
- if (options.toSql && options.toSql.dialect !== 'sqlite') {
68
+ // Does not include HDBCDS.
69
+ if (options.toSql && !forSqlite) {
69
70
  result += `${indent}${!constraint.validated ? 'NOT ' : ''}VALIDATED\n`;
70
71
  result += `${indent}${!constraint.enforced ? 'NOT ' : ''}ENFORCED\n`;
71
72
  }
72
73
  // for sqlite, the DEFERRABLE keyword is required
73
- result += `${indent}${options.toSql && options.toSql.dialect === 'sqlite' ? 'DEFERRABLE ' : ''}INITIALLY DEFERRED`;
74
+ result += `${indent}${forSqlite ? 'DEFERRABLE ' : ''}INITIALLY DEFERRED`;
74
75
  return result;
75
76
  }
76
77
 
@@ -85,9 +86,9 @@ function getIdentifierUtils(options) {
85
86
  /**
86
87
  * Return 'name' with appropriate "-quotes.
87
88
  * Additionally perform the following conversions on 'name'
88
- * If 'options.toSql.names' is 'plain'
89
+ * If 'options.sqlMapping' is 'plain'
89
90
  * - replace '.' or '::' by '_'
90
- * else if 'options.toSql.names' is 'quoted'
91
+ * else if 'options.sqlMapping' is 'quoted'
91
92
  * - replace '::' by '.'
92
93
  * Complain about names that collide with known SQL keywords or functions
93
94
  *
@@ -97,13 +98,13 @@ function getIdentifierUtils(options) {
97
98
  function quoteSqlId(name) {
98
99
  name = prepareIdentifier(name);
99
100
 
100
- switch (options.toSql.names) {
101
+ switch (options.sqlMapping) {
101
102
  case 'plain':
102
- return smartId(name, options.toSql.dialect);
103
+ return smartId(name, options.sqlDialect);
103
104
  case 'quoted':
104
- return delimitedId(name, options.toSql.dialect);
105
+ return delimitedId(name, options.sqlDialect);
105
106
  case 'hdbcds':
106
- return delimitedId(name, options.toSql.dialect);
107
+ return delimitedId(name, options.sqlDialect);
107
108
  default:
108
109
  return undefined;
109
110
  }
@@ -111,9 +112,9 @@ function getIdentifierUtils(options) {
111
112
 
112
113
  /**
113
114
  * Prepare an identifier:
114
- * If 'options.toSql.names' is 'plain'
115
+ * If 'options.sqlMapping' is 'plain'
115
116
  * - replace '.' or '::' by '_'
116
- * else if 'options.toSql.names' is 'quoted'
117
+ * else if 'options.sqlMapping' is 'quoted'
117
118
  * - replace '::' by '.'
118
119
  *
119
120
  * @param {string} name Identifier to prepare
@@ -121,11 +122,11 @@ function getIdentifierUtils(options) {
121
122
  */
122
123
  function prepareIdentifier(name) {
123
124
  // Sanity check
124
- if (options.toSql.dialect === 'sqlite' && options.toSql.names !== 'plain')
125
- throw new ModelError(`Not expecting ${options.toSql.names} names for 'sqlite' dialect`);
125
+ if (options.sqlDialect === 'sqlite' && options.sqlMapping !== 'plain')
126
+ throw new ModelError(`Not expecting ${options.sqlMapping} names for 'sqlite' dialect`);
126
127
 
127
128
 
128
- switch (options.toSql.names) {
129
+ switch (options.sqlMapping) {
129
130
  case 'plain':
130
131
  return name.replace(/(\.|::)/g, '_');
131
132
  case 'quoted':
@@ -133,7 +134,7 @@ function getIdentifierUtils(options) {
133
134
  case 'hdbcds':
134
135
  return name;
135
136
  default:
136
- throw new ModelError(`No matching rendering found for naming mode ${options.toSql.names}`);
137
+ throw new ModelError(`No matching rendering found for naming mode ${options.sqlMapping}`);
137
138
  }
138
139
  }
139
140
  }
@@ -45,6 +45,12 @@ const sqlDialects = {
45
45
  effectiveName: name => name,
46
46
  asDelimitedId: name => `"${ name.replace(/"/g, '""')}"`,
47
47
  },
48
+ postgres: {
49
+ regularRegex: /^[A-Za-z_][A-Za-z_$0-9]*$/,
50
+ reservedWords: keywords.postgres,
51
+ effectiveName: name => name.toLowerCase(),
52
+ asDelimitedId: name => `"${ name.replace(/"/g, '""')}"`,
53
+ },
48
54
  hana: {
49
55
  regularRegex: /^[A-Za-z_][A-Za-z_$#0-9]*$/,
50
56
  reservedWords: keywords.hana,
@@ -17,15 +17,16 @@
17
17
  "sonarjs/prefer-single-boolean-return": "off",
18
18
  // Very whiny and nitpicky
19
19
  "sonarjs/cognitive-complexity": "off",
20
+ "sonarjs/no-duplicate-string": "off",
20
21
  // Does not recognize TS types
21
22
  "jsdoc/no-undefined-types": "off"
22
23
  },
23
24
  "parserOptions": {
24
- "ecmaVersion": 2018,
25
+ "ecmaVersion": 2020,
25
26
  "sourceType": "script"
26
27
  },
27
28
  "env": {
28
- "es6": true,
29
+ "es2020": true,
29
30
  "node": true
30
31
  },
31
32
  "settings": {
@@ -107,12 +107,12 @@ function attachOnConditions(csn, pathDelimiter) {
107
107
  * @param {string} pathDelimiter
108
108
  * @returns {(artifact: CSN.Artifact, artifactName: string) => void} Callback for forEachDefinition
109
109
  */
110
- function getManagedAssocStepsInOnConditionFinalizer(csn, pathDelimiter) {
110
+ function getFKAccessFinalizer(csn, pathDelimiter) {
111
111
  const {
112
112
  inspectRef,
113
113
  } = getUtils(csn);
114
114
 
115
- return handleManagedAssocStepsInOnCondition;
115
+ return handleManagedAssocSteps;
116
116
 
117
117
  /**
118
118
  * Loop over all elements and for all unmanaged associations translate
@@ -123,45 +123,53 @@ function getManagedAssocStepsInOnConditionFinalizer(csn, pathDelimiter) {
123
123
  * @param {CSN.Artifact} artifact Artifact to check
124
124
  * @param {string} artifactName Name of the artifact
125
125
  */
126
- function handleManagedAssocStepsInOnCondition(artifact, artifactName) {
127
- for (const elemName in artifact.elements) {
128
- const elem = artifact.elements[elemName];
129
- // The association is an unmanaged on
130
- if (!elem.keys && elem.target && elem.on) {
131
- applyTransformationsOnNonDictionary(elem, 'on', {
132
- ref: (refOwner, prop, ref, path) => {
133
- // [<assoc base>.]<managed assoc>.<field>
134
- if (ref.length > 1) {
135
- const { links } = inspectRef(path);
136
- if (links) {
137
- // eslint-disable-next-line for-direction
138
- for (let i = links.length - 1; i >= 0; i--) {
139
- const link = links[i];
140
- // We found the latest managed assoc path step
141
- if (link.art && link.art.target && link.art.keys &&
126
+ function handleManagedAssocSteps(artifact, artifactName) {
127
+ const transformer = {
128
+ ref: (refOwner, prop, ref, path) => {
129
+ // [<assoc base>.]<managed assoc>.<field>
130
+ if (ref.length > 1) {
131
+ const { links } = inspectRef(path);
132
+ if (links) {
133
+ // eslint-disable-next-line for-direction
134
+ for (let i = links.length - 1; i >= 0; i--) {
135
+ const link = links[i];
136
+ // We found the latest managed assoc path step
137
+ if (link.art && link.art.target && link.art.keys &&
142
138
  // Doesn't work when ref-target (filter condition) or similar is used
143
139
  !ref.slice(i).some(refElement => typeof refElement !== 'string')) {
144
- // We join the managed assoc with everything following it
145
- const sourceElementName = ref.slice(i).join(pathDelimiter);
146
- const source = findSource(links, i - 1) || artifact;
147
- // allow specifying managed assoc on the source side
148
- const fks = link.art.keys.filter(fk => ref[i] + pathDelimiter + fk.ref[0] === sourceElementName);
149
- if (fks && fks.length >= 1) {
150
- const fk = fks[0];
151
- const managedAssocStepName = refOwner.ref[i];
152
- const fkName = `${managedAssocStepName}${pathDelimiter}${fk.as}`;
153
- if (source && source.elements[fkName])
154
- refOwner.ref = [ ...ref.slice(0, i), fkName ];
155
- }
156
- }
140
+ // We join the managed assoc with everything following it
141
+ const sourceElementName = ref.slice(i).join(pathDelimiter);
142
+ const source = findSource(links, i - 1) || artifact;
143
+ // allow specifying managed assoc on the source side
144
+ const fks = link.art.keys.filter(fk => ref[i] + pathDelimiter + fk.ref[0] === sourceElementName);
145
+ if (fks && fks.length >= 1) {
146
+ const fk = fks[0];
147
+ const managedAssocStepName = refOwner.ref[i];
148
+ const fkName = `${managedAssocStepName}${pathDelimiter}${fk.as}`;
149
+ if (source && source.elements[fkName])
150
+ refOwner.ref = [ ...ref.slice(0, i), fkName ];
157
151
  }
158
152
  }
159
153
  }
160
- },
161
- }, {}, [ 'definitions', artifactName, 'elements', elemName ]);
162
- }
154
+ }
155
+ }
156
+ },
157
+ };
158
+ for (const elemName in artifact.elements) {
159
+ const elem = artifact.elements[elemName];
160
+ // The association is an unmanaged one
161
+ if (!elem.keys && elem.target && elem.on)
162
+ applyTransformationsOnNonDictionary(elem, 'on', transformer, {}, [ 'definitions', artifactName, 'elements', elemName ]);
163
+ }
164
+
165
+ if (artifact.query || artifact.projection) {
166
+ applyTransformationsOnNonDictionary(artifact, artifact.query ? 'query' : 'projection', {
167
+ orderBy: (parent, prop, thing, path) => applyTransformationsOnNonDictionary(parent, prop, transformer, {}, path),
168
+ groupBy: (parent, prop, thing, path) => applyTransformationsOnNonDictionary(parent, prop, transformer, {}, path),
169
+ }, {}, [ 'definitions', artifactName ]);
163
170
  }
164
171
 
172
+
165
173
  /**
166
174
  * Find out where the managed association is
167
175
  *
@@ -183,5 +191,5 @@ function getManagedAssocStepsInOnConditionFinalizer(csn, pathDelimiter) {
183
191
  }
184
192
  module.exports = {
185
193
  attachOnConditions,
186
- getManagedAssocStepsInOnConditionFinalizer,
194
+ getFKAccessFinalizer,
187
195
  };
@@ -78,29 +78,18 @@ function getAssocToSkippedIgnorer(csn, options, messageFunctions) {
78
78
  * @param {string} memberName
79
79
  * @param {string} prop
80
80
  * @param {CSN.Path} path
81
- * @todo Why do we check for @cds.persistence.exists here if the parent-function only calls this for skip/abstract?
82
81
  */
83
82
  function ignore(member, memberName, prop, path) {
84
- if (options.sqlDialect === 'hana' && !member._ignore && member.target && isAssocOrComposition(member.type) && isUnreachableAssociationTarget(csn.definitions[member.target])) {
85
- const targetAnnotation = hasAnnotationValue(csn.definitions[member.target], exists) ? exists : '@cds.persistence.skip';
83
+ if (options.sqlDialect === 'hana' &&
84
+ !member._ignore && member.target &&
85
+ isAssocOrComposition(member.type) &&
86
+ !isPersistedOnDatabase(csn.definitions[member.target])) {
86
87
  info(null, path,
87
- { target: member.target, anno: targetAnnotation },
88
+ { target: member.target, anno: '@cds.persistence.skip' },
88
89
  'Association has been removed as it\'s target $(TARGET) is annotated with $(ANNO)');
89
90
  member._ignore = true;
90
91
  }
91
92
  }
92
-
93
- /**
94
- * Check whether the given artifact is an unreachable association target because it will not "realy" hit the database:
95
- * - @cds.persistence.skip/exists
96
- * - abstract
97
- *
98
- * @param {CSN.Artifact} art
99
- * @returns {boolean}
100
- */
101
- function isUnreachableAssociationTarget(art) {
102
- return !isPersistedOnDatabase(art) || hasAnnotationValue(art, exists);
103
- }
104
93
  }
105
94
 
106
95
  /**
@@ -517,7 +517,7 @@ function createReferentialConstraints(csn, options) {
517
517
  onDeleteRemark = `Up_ link for Composition "${$foreignKeyConstraint.upLinkFor}" implies existential dependency`;
518
518
  referentialConstraints[`${getResultingName(csn, 'quoted', artifactName)}_${$foreignKeyConstraint.sourceAssociation}`] = {
519
519
  // constraint identifier start with `c__` to avoid name clashes
520
- identifier: `c__${getResultingName(csn, options.forHana.names, artifactName)}_${$foreignKeyConstraint.sourceAssociation}`,
520
+ identifier: `c__${getResultingName(csn, options.sqlMapping, artifactName)}_${$foreignKeyConstraint.sourceAssociation}`,
521
521
  foreignKey: dependentKey,
522
522
  parentKey,
523
523
  dependentTable: artifactName,
@@ -24,10 +24,11 @@ const { forEach } = require('../../utils/objectUtils');
24
24
  * @param {object} iterateOptions
25
25
  */
26
26
  function expandStructureReferences(csn, options, pathDelimiter, { error, info, throwWithAnyError }, iterateOptions = {}) {
27
+ const csnUtils = getUtils(csn);
27
28
  const {
28
- isStructured, get$combined, getFinalBaseType, getServiceName,
29
- } = getUtils(csn);
30
- let { effectiveType, inspectRef } = csnRefs(csn);
29
+ isStructured, get$combined, getFinalBaseTypeWithProps, getServiceName,
30
+ } = csnUtils;
31
+ let { effectiveType, inspectRef } = csnUtils;
31
32
 
32
33
  if (isBetaEnabled(options, 'nestedProjections'))
33
34
  rewriteExpandInline();
@@ -239,13 +240,13 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
239
240
  */
240
241
  function nextBase(parent, base) {
241
242
  if (parent.ref) {
242
- const finalBaseType = getFinalBaseType(parent._art.type);
243
+ const finalBaseType = getFinalBaseTypeWithProps(parent._art.type);
243
244
  const art = parent._art;
244
245
 
245
- if (finalBaseType === 'cds.Association' || finalBaseType === 'cds.Composition')
246
+ if (finalBaseType && (finalBaseType.type === 'cds.Association' || finalBaseType.type === 'cds.Composition'))
246
247
  return csn.definitions[art.target].elements;
247
248
 
248
- return art.elements || finalBaseType.elements;
249
+ return art.elements || finalBaseType?.elements;
249
250
  }
250
251
 
251
252
  return base;
@@ -67,8 +67,8 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
67
67
  stack.push(...Object.values(current.elements));
68
68
  }
69
69
  }
70
- const { toFinalBaseType } = transformUtils.getTransformers(csn, options, pathDelimiter);
71
- const { getServiceName, getFinalBaseType } = getUtils(csn);
70
+ const { toFinalBaseType, csnUtils } = transformUtils.getTransformers(csn, options, pathDelimiter);
71
+ const { getServiceName, getFinalBaseTypeWithProps } = csnUtils;
72
72
 
73
73
  // We don't want to iterate over actions
74
74
  if (iterateOptions.skipDict && !iterateOptions.skipDict.actions)
@@ -89,7 +89,7 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
89
89
  if (options.toOdata && parent.kind && parent.kind in ignoreOdataKinds)
90
90
  return;
91
91
  if (!isBuiltinType(type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(type, path) && !isODataItems(type))) {
92
- toFinalBaseType(parent, resolved);
92
+ toFinalBaseType(parent, resolved, true);
93
93
  // structured types might not have the child-types replaced.
94
94
  // Drill down to ensure this.
95
95
  if (parent.elements) {
@@ -98,7 +98,7 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
98
98
  const elements = stack.pop();
99
99
  for (const e of Object.values(elements)) {
100
100
  if (e.type && !isBuiltinType(e.type))
101
- toFinalBaseType(e, resolved);
101
+ toFinalBaseType(e, resolved, true);
102
102
 
103
103
  if (e.elements)
104
104
  stack.push(e.elements);
@@ -152,11 +152,11 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
152
152
  * @returns {boolean}
153
153
  */
154
154
  function isODataV4BuiltinFromService(typeName, path) {
155
- if (!options.toOdata || (options.toOdata && options.toOdata.version === 'v2') || typeof typeName !== 'string')
155
+ if (!options.toOdata || (options.odataVersion === 'v2') || typeof typeName !== 'string')
156
156
  return false;
157
157
 
158
158
  const typeServiceName = getServiceName(typeName);
159
- const finalBaseType = getFinalBaseType(typeName);
159
+ const finalBaseType = getFinalBaseTypeWithProps(typeName)?.type;
160
160
  // we need the service of the current definition
161
161
  const currDefServiceName = getServiceName(path[1]);
162
162
 
@@ -266,9 +266,8 @@ function flattenAllStructStepsInRefs(csn, options, resolved, pathDelimiter, iter
266
266
  * @param {object} iterateOptions
267
267
  */
268
268
  function flattenElements(csn, options, pathDelimiter, error, iterateOptions = {}) {
269
- const { isAssocOrComposition } = getUtils(csn);
270
- const { flattenStructuredElement } = transformUtils.getTransformers(csn, options, pathDelimiter);
271
- const { effectiveType } = csnRefs(csn);
269
+ const { flattenStructuredElement, csnUtils } = transformUtils.getTransformers(csn, options, pathDelimiter);
270
+ const { isAssocOrComposition, effectiveType } = csnUtils;
272
271
  const transformers = {
273
272
  elements: flatten,
274
273
  };
@@ -299,11 +298,13 @@ function flattenElements(csn, options, pathDelimiter, error, iterateOptions = {}
299
298
  const flatElems = flattenStructuredElement(element, elementName, [], path.concat([ 'elements', elementName ]));
300
299
 
301
300
  for (const flatElemName in flatElems) {
302
- if (parent[prop][flatElemName])
301
+ if (parent[prop][flatElemName]) {
303
302
  // TODO: combine message ID with generated FK duplicate
304
- // do the duplicate check in the consruct callback, requires to mark generated flat elements,
303
+ // do the duplicate check in the construct callback, requires to mark generated flat elements,
305
304
  // check: Error location should be the existing element like @odata.foreignKey4
306
- error(null, path.concat([ 'elements', elementName ]), `"${path[1]}.${elementName}": Flattened struct element name conflicts with existing element: "${flatElemName}"`);
305
+ error('name-duplicate-element', path.concat([ 'elements', elementName ]),
306
+ { '#': 'flatten-element-exist', name: flatElemName });
307
+ }
307
308
 
308
309
  const flatElement = flatElems[flatElemName];
309
310
 
@@ -609,8 +610,7 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
609
610
  ((options.toOdata && isDeepEqual(element, parent[prop][fk[0]], true)) ||
610
611
  !options.toOdata)) {
611
612
  // error location is the colliding element
612
- error(null, eltPath, { name: fk[0], art: elementName },
613
- 'Generated foreign key element $(NAME) for association $(ART) conflicts with existing element');
613
+ error('name-duplicate-element', eltPath, { '#': 'flatten-fkey-exists', name: fk[0], art: elementName });
614
614
  }
615
615
  // attach a proper $path
616
616
  setProp(element, '$path', eltPath);
@@ -619,10 +619,8 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
619
619
 
620
620
  // check for duplicate foreign keys
621
621
  Object.entries(refCount).forEach(([ name, occ ]) => {
622
- if (occ > 1) {
623
- error(null, eltPath, { name },
624
- 'Duplicate definition of foreign key element $(NAME)');
625
- }
622
+ if (occ > 1)
623
+ error('name-duplicate-element', eltPath, { '#': 'flatten-fkey-gen', name, art: elementName });
626
624
  });
627
625
  if (element.keys) {
628
626
  element.keys.forEach((key, i) => {
@@ -349,7 +349,7 @@ function handleExists(csn, options, error) {
349
349
  const newExpr = [];
350
350
  const query = walkCsnPath(csn, queryPath);
351
351
  const expr = walkCsnPath(csn, exprPath);
352
- const queryBase = query.SELECT.from.ref ? (query.SELECT.from.as || query.SELECT.from.ref[0]) : null;
352
+ const queryBase = query.SELECT.from.ref ? (query.SELECT.from.as || query.SELECT.from.ref) : null;
353
353
  const sources = getQuerySources(query.SELECT);
354
354
 
355
355
  for (let i = 0; i < expr.length; i++) {
@@ -457,7 +457,7 @@ function handleExists(csn, options, error) {
457
457
  *
458
458
  * A valid $self-backlink is handled in translateDollarSelfToWhere.
459
459
  *
460
- * For an ordinary unmanaged association, we do the the following for each part of the on-condition:
460
+ * For an ordinary unmanaged association, we do the following for each part of the on-condition:
461
461
  * - target side: We prefix the real target and cut off the assoc-name from the ref
462
462
  * - source side w/ leading $self: We remove the $self and add the source side entity/query source
463
463
  * - source side w/o leading $self: We simply add the source side entity/query source in front of the ref
@@ -730,15 +730,17 @@ function handleExists(csn, options, error) {
730
730
  /**
731
731
  * Get the name of the source-side query source
732
732
  *
733
- * @param {string|null} queryBase
733
+ * @param {string | Array | null} queryBase
734
734
  * @param {boolean} isPrefixedWithTableAlias
735
735
  * @param {CSN.Column} current
736
736
  * @param {CSN.Path} path
737
737
  * @returns {string}
738
738
  */
739
739
  function getBase(queryBase, isPrefixedWithTableAlias, current, path) {
740
- if (queryBase)
741
- return getRealName(csn, queryBase);
740
+ if (typeof queryBase === 'string') // alias
741
+ return queryBase;
742
+ else if (queryBase) // ref
743
+ return queryBase.length > 1 ? queryBase[queryBase.length - 1] : getRealName(csn, queryBase[0]);
742
744
  else if (isPrefixedWithTableAlias)
743
745
  return current.ref[0];
744
746
  return getParent(current, path);
@@ -3,7 +3,7 @@
3
3
  const {
4
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
 
@@ -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 = '.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
  });