@sap/cds-compiler 2.15.2 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +66 -1590
  2. package/bin/cdsc.js +42 -46
  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 +312 -143
  10. package/lib/api/options.js +15 -85
  11. package/lib/api/validate.js +6 -10
  12. package/lib/base/keywords.js +280 -110
  13. package/lib/base/message-registry.js +80 -24
  14. package/lib/base/messages.js +103 -52
  15. package/lib/base/model.js +44 -2
  16. package/lib/base/optionProcessorHelper.js +53 -21
  17. package/lib/checks/actionsFunctions.js +7 -5
  18. package/lib/checks/annotationsOData.js +1 -1
  19. package/lib/checks/cdsPersistence.js +1 -0
  20. package/lib/checks/elements.js +6 -6
  21. package/lib/checks/invalidTarget.js +1 -1
  22. package/lib/checks/nonexpandableStructured.js +1 -1
  23. package/lib/checks/queryNoDbArtifacts.js +2 -1
  24. package/lib/checks/selectItems.js +5 -1
  25. package/lib/checks/types.js +4 -2
  26. package/lib/checks/utils.js +2 -2
  27. package/lib/checks/validator.js +2 -1
  28. package/lib/compiler/assert-consistency.js +15 -10
  29. package/lib/compiler/builtins.js +127 -10
  30. package/lib/compiler/define.js +6 -4
  31. package/lib/compiler/extend.js +63 -12
  32. package/lib/compiler/finalize-parse-cdl.js +20 -9
  33. package/lib/compiler/index.js +25 -11
  34. package/lib/compiler/moduleLayers.js +7 -0
  35. package/lib/compiler/populate.js +16 -14
  36. package/lib/compiler/propagator.js +3 -3
  37. package/lib/compiler/resolve.js +194 -222
  38. package/lib/compiler/shared.js +56 -76
  39. package/lib/compiler/tweak-assocs.js +9 -10
  40. package/lib/compiler/utils.js +7 -2
  41. package/lib/edm/annotations/genericTranslation.js +60 -6
  42. package/lib/edm/annotations/preprocessAnnotations.js +10 -11
  43. package/lib/edm/csn2edm.js +39 -41
  44. package/lib/edm/edm.js +22 -15
  45. package/lib/edm/edmPreprocessor.js +66 -69
  46. package/lib/edm/edmUtils.js +12 -62
  47. package/lib/gen/Dictionary.json +8 -6
  48. package/lib/gen/language.checksum +1 -1
  49. package/lib/gen/language.interp +8 -30
  50. package/lib/gen/language.tokens +105 -114
  51. package/lib/gen/languageLexer.interp +1 -34
  52. package/lib/gen/languageLexer.js +889 -1007
  53. package/lib/gen/languageLexer.tokens +95 -106
  54. package/lib/gen/languageParser.js +20717 -22376
  55. package/lib/json/from-csn.js +73 -68
  56. package/lib/json/to-csn.js +13 -10
  57. package/lib/language/antlrParser.js +2 -2
  58. package/lib/language/docCommentParser.js +61 -38
  59. package/lib/language/errorStrategy.js +52 -40
  60. package/lib/language/genericAntlrParser.js +333 -259
  61. package/lib/language/language.g4 +600 -645
  62. package/lib/language/multiLineStringParser.js +14 -42
  63. package/lib/language/textUtils.js +44 -0
  64. package/lib/main.d.ts +27 -42
  65. package/lib/main.js +104 -81
  66. package/lib/model/csnRefs.js +2 -1
  67. package/lib/model/csnUtils.js +183 -285
  68. package/lib/model/revealInternalProperties.js +32 -9
  69. package/lib/model/sortViews.js +32 -31
  70. package/lib/optionProcessor.js +64 -57
  71. package/lib/render/.eslintrc.json +1 -1
  72. package/lib/render/DuplicateChecker.js +4 -7
  73. package/lib/render/manageConstraints.js +70 -2
  74. package/lib/render/toCdl.js +334 -339
  75. package/lib/render/toHdbcds.js +20 -16
  76. package/lib/render/toRename.js +44 -22
  77. package/lib/render/toSql.js +60 -54
  78. package/lib/render/utils/common.js +15 -1
  79. package/lib/render/utils/sql.js +20 -19
  80. package/lib/sql-identifier.js +6 -0
  81. package/lib/transform/db/.eslintrc.json +3 -2
  82. package/lib/transform/db/cdsPersistence.js +5 -15
  83. package/lib/transform/db/constraints.js +1 -1
  84. package/lib/transform/db/expansion.js +7 -6
  85. package/lib/transform/db/flattening.js +18 -19
  86. package/lib/transform/db/views.js +3 -3
  87. package/lib/transform/draft/.eslintrc.json +2 -2
  88. package/lib/transform/draft/db.js +6 -6
  89. package/lib/transform/draft/odata.js +6 -7
  90. package/lib/transform/forHanaNew.js +19 -22
  91. package/lib/transform/forOdataNew.js +13 -15
  92. package/lib/transform/localized.js +35 -25
  93. package/lib/transform/odata/toFinalBaseType.js +11 -9
  94. package/lib/transform/odata/typesExposure.js +3 -3
  95. package/lib/transform/odata/utils.js +1 -38
  96. package/lib/transform/transformUtilsNew.js +63 -77
  97. package/lib/transform/translateAssocsToJoins.js +6 -2
  98. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  99. package/lib/transform/universalCsn/coreComputed.js +11 -6
  100. package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
  101. package/lib/utils/file.js +31 -21
  102. package/lib/utils/timetrace.js +20 -21
  103. package/package.json +34 -4
  104. package/share/messages/syntax-expected-integer.md +9 -8
  105. package/doc/ApiMigration.md +0 -237
  106. package/doc/CommandLineMigration.md +0 -58
  107. package/doc/ErrorMessages.md +0 -175
  108. package/doc/FioriAnnotations.md +0 -94
  109. package/doc/ODataTransformation.md +0 -273
  110. package/lib/backends.js +0 -529
  111. package/lib/fix_antlr4-8_warning.js +0 -56
@@ -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": {
@@ -81,26 +81,16 @@ function getAssocToSkippedIgnorer(csn, options, messageFunctions) {
81
81
  * @todo Why do we check for @cds.persistence.exists here if the parent-function only calls this for skip/abstract?
82
82
  */
83
83
  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';
84
+ if (options.sqlDialect === 'hana' &&
85
+ !member._ignore && member.target &&
86
+ isAssocOrComposition(member.type) &&
87
+ !isPersistedOnDatabase(csn.definitions[member.target])) {
86
88
  info(null, path,
87
- { target: member.target, anno: targetAnnotation },
89
+ { target: member.target, anno: '@cds.persistence.skip' },
88
90
  'Association has been removed as it\'s target $(TARGET) is annotated with $(ANNO)');
89
91
  member._ignore = true;
90
92
  }
91
93
  }
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
94
  }
105
95
 
106
96
  /**
@@ -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;
@@ -47,6 +47,7 @@ function removeLeadingSelf(csn) {
47
47
  * @param {object} iterateOptions
48
48
  */
49
49
  function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOptions = {}) {
50
+ const typeCache = Object.create(null); // TODO: Argument as well?
50
51
  /**
51
52
  * Remove .localized from the element and any sub-elements
52
53
  *
@@ -67,8 +68,8 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
67
68
  stack.push(...Object.values(current.elements));
68
69
  }
69
70
  }
70
- const { toFinalBaseType } = transformUtils.getTransformers(csn, options, pathDelimiter);
71
- const { getServiceName, getFinalBaseType } = getUtils(csn);
71
+ const { toFinalBaseType, csnUtils } = transformUtils.getTransformers(csn, options, pathDelimiter);
72
+ const { getServiceName, getFinalBaseTypeWithProps } = csnUtils;
72
73
 
73
74
  // We don't want to iterate over actions
74
75
  if (iterateOptions.skipDict && !iterateOptions.skipDict.actions)
@@ -82,14 +83,14 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
82
83
  cast: (parent, prop, cast, path) => {
83
84
  // Resolve cast already - we otherwise lose .localized
84
85
  if (cast.type && !isBuiltinType(cast.type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(cast.type, path) && !isODataItems(cast.type)))
85
- toFinalBaseType(parent.cast, resolved, true);
86
+ toFinalBaseType(parent.cast, resolved, true, typeCache);
86
87
  },
87
88
  // @ts-ignore
88
89
  type: (parent, prop, type, path) => {
89
90
  if (options.toOdata && parent.kind && parent.kind in ignoreOdataKinds)
90
91
  return;
91
92
  if (!isBuiltinType(type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(type, path) && !isODataItems(type))) {
92
- toFinalBaseType(parent, resolved);
93
+ toFinalBaseType(parent, resolved, true, typeCache);
93
94
  // structured types might not have the child-types replaced.
94
95
  // Drill down to ensure this.
95
96
  if (parent.elements) {
@@ -98,7 +99,7 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
98
99
  const elements = stack.pop();
99
100
  for (const e of Object.values(elements)) {
100
101
  if (e.type && !isBuiltinType(e.type))
101
- toFinalBaseType(e, resolved);
102
+ toFinalBaseType(e, resolved, true, typeCache);
102
103
 
103
104
  if (e.elements)
104
105
  stack.push(e.elements);
@@ -152,11 +153,11 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
152
153
  * @returns {boolean}
153
154
  */
154
155
  function isODataV4BuiltinFromService(typeName, path) {
155
- if (!options.toOdata || (options.toOdata && options.toOdata.version === 'v2') || typeof typeName !== 'string')
156
+ if (!options.toOdata || (options.odataVersion === 'v2') || typeof typeName !== 'string')
156
157
  return false;
157
158
 
158
159
  const typeServiceName = getServiceName(typeName);
159
- const finalBaseType = getFinalBaseType(typeName);
160
+ const finalBaseType = getFinalBaseTypeWithProps(typeName)?.type;
160
161
  // we need the service of the current definition
161
162
  const currDefServiceName = getServiceName(path[1]);
162
163
 
@@ -266,9 +267,8 @@ function flattenAllStructStepsInRefs(csn, options, resolved, pathDelimiter, iter
266
267
  * @param {object} iterateOptions
267
268
  */
268
269
  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);
270
+ const { flattenStructuredElement, csnUtils } = transformUtils.getTransformers(csn, options, pathDelimiter);
271
+ const { isAssocOrComposition, effectiveType } = csnUtils;
272
272
  const transformers = {
273
273
  elements: flatten,
274
274
  };
@@ -299,11 +299,13 @@ function flattenElements(csn, options, pathDelimiter, error, iterateOptions = {}
299
299
  const flatElems = flattenStructuredElement(element, elementName, [], path.concat([ 'elements', elementName ]));
300
300
 
301
301
  for (const flatElemName in flatElems) {
302
- if (parent[prop][flatElemName])
302
+ if (parent[prop][flatElemName]) {
303
303
  // TODO: combine message ID with generated FK duplicate
304
- // do the duplicate check in the consruct callback, requires to mark generated flat elements,
304
+ // do the duplicate check in the construct callback, requires to mark generated flat elements,
305
305
  // 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}"`);
306
+ error('name-duplicate-element', path.concat([ 'elements', elementName ]),
307
+ { '#': 'flatten-element-exist', name: flatElemName });
308
+ }
307
309
 
308
310
  const flatElement = flatElems[flatElemName];
309
311
 
@@ -609,8 +611,7 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
609
611
  ((options.toOdata && isDeepEqual(element, parent[prop][fk[0]], true)) ||
610
612
  !options.toOdata)) {
611
613
  // 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');
614
+ error('name-duplicate-element', eltPath, { '#': 'flatten-fkey-exists', name: fk[0], art: elementName });
614
615
  }
615
616
  // attach a proper $path
616
617
  setProp(element, '$path', eltPath);
@@ -619,10 +620,8 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
619
620
 
620
621
  // check for duplicate foreign keys
621
622
  Object.entries(refCount).forEach(([ name, occ ]) => {
622
- if (occ > 1) {
623
- error(null, eltPath, { name },
624
- 'Duplicate definition of foreign key element $(NAME)');
625
- }
623
+ if (occ > 1)
624
+ error('name-duplicate-element', eltPath, { '#': 'flatten-fkey-gen', name, art: elementName });
626
625
  });
627
626
  if (element.keys) {
628
627
  element.keys.forEach((key, i) => {
@@ -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 = 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
  });
@@ -41,9 +41,7 @@ function forEachDefinition(csn, cb) {
41
41
  * in HANA CDS style, used by 'toHana', toSql' and 'toRename'.
42
42
  * The behavior is controlled by the following options:
43
43
  * options = {
44
- * forHana.names // See the behavior of 'names' in toHana, toSql and toRename
45
- * forHana.alwaysResolveDerivedTypes // Always resolve derived type chains (by default, this is only
46
- * // done for 'quoted' names). FIXME: Should always be done in general.
44
+ * sqlMapping // See the behavior of 'sqlMapping' in toHana, toSql and toRename
47
45
  * }
48
46
  * The result model will always have 'options.forHana' set, to indicate that these transformations have happened.
49
47
  * The following transformations are made:
@@ -69,9 +67,9 @@ function forEachDefinition(csn, cb) {
69
67
  * - (110) Actions and functions (bound or unbound) are ignored.
70
68
  * - (120) (a) Services become contexts.
71
69
  * - (130) (not for to.hdbcds with hdbcds names): Elements having structured types are flattened into
72
- * multiple elements (using '_' or '.' as name separator, depending on 'forHana.names').
70
+ * multiple elements (using '_' or '.' as name separator, depending on 'sqlMapping').
73
71
  * - (140) (not for to.hdbcds with hdbcds names): Managed associations get explicit ON-conditions, with
74
- * generated foreign key elements (also using '_' or '.' as name separator, depending on 'forHana.names').
72
+ * generated foreign key elements (also using '_' or '.' as name separator, depending on 'sqlMapping').
75
73
  * - (150) (a) Elements from inherited (included) entities are copied into the receiving entity
76
74
  * (b) The 'include' property is removed from entities.
77
75
  * - (160) Projections become views, with MIXINs for association elements (adding $projection where
@@ -94,7 +92,7 @@ function forEachDefinition(csn, cb) {
94
92
  * (d) Managed association entries in ORDER BY
95
93
  * - (240) All artifacts (a), elements, foreign keys, parameters (b) that have a DB representation are annotated
96
94
  * with their database name (as '@cds.persistence.name') according to the naming convention chosen
97
- * in 'options.forHana.names'.
95
+ * in 'options.sqlMapping'.
98
96
  * - (250) Remove name space definitions again (only in forHanaNew). Maybe we can omit inserting namespace definitions
99
97
  * completely (TODO)
100
98
  *
@@ -108,17 +106,14 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
108
106
  /** @type {CSN.Model} */
109
107
  let csn = cloneCsnNonDict(inputModel, options);
110
108
 
111
-
112
-
113
109
  checkCSNVersion(csn, options);
114
110
 
115
- const pathDelimiter = (options.forHana.names === 'hdbcds') ? '.' : '_';
111
+ const pathDelimiter = (options.sqlMapping === 'hdbcds') ? '.' : '_';
116
112
 
117
113
  let message, error, warning, info; // message functions
118
114
  /** @type {() => void} */
119
115
  let throwWithAnyError;
120
116
  let artifactRef, inspectRef, effectiveType, get$combined,
121
- getFinalBaseType, // csnUtils (csnRefs)
122
117
  addDefaultTypeFacets, expandStructsInExpression; // transformUtils
123
118
 
124
119
  bindCsnReference();
@@ -130,14 +125,14 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
130
125
  bindCsnReference();
131
126
  }
132
127
 
133
- const dialect = options.forHana && options.forHana.dialect || options.toSql && options.toSql.dialect;
128
+ const dialect = options.sqlDialect;
134
129
  const doA2J = !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds');
135
130
  if (!doA2J)
136
131
  forEachDefinition(csn, handleMixinOnConditions);
137
132
 
138
133
  // Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
139
134
  const cleanup = validate.forHana(csn, {
140
- message, error, warning, info, inspectRef, effectiveType, artifactRef, csnUtils: getUtils(csn), csn, options, getFinalBaseType, isAspect
135
+ message, error, warning, info, inspectRef, effectiveType, artifactRef, csnUtils: getUtils(csn), csn, options, isAspect
141
136
  });
142
137
 
143
138
  // Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
@@ -222,6 +217,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
222
217
  flattenStructuredElement,
223
218
  flattenStructStepsInRef,
224
219
  isAssociationOperand, isDollarSelfOrProjectionOperand,
220
+ csnUtils,
225
221
  } = transformUtils.getTransformers(csn, options, pathDelimiter);
226
222
 
227
223
  const {
@@ -229,7 +225,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
229
225
  isAssocOrComposition,
230
226
  addStringAnnotationTo,
231
227
  cloneWithTransformations,
232
- } = getUtils(csn);
228
+ } = csnUtils;
233
229
 
234
230
  // (000) Rename primitive types, make UUID a String
235
231
  transformCsn(csn, {
@@ -409,8 +405,9 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
409
405
 
410
406
  function bindCsnReference(){
411
407
  ({ error, warning, info, message, throwWithAnyError } = makeMessageFunction(csn, options, moduleName));
412
- ({ artifactRef, inspectRef, effectiveType, getFinalBaseType, get$combined } = getUtils(csn));
413
408
  ({ addDefaultTypeFacets, expandStructsInExpression } = transformUtils.getTransformers(csn, options, pathDelimiter));
409
+ // TODO: Can we use csnUtils of the call above (transformUtils.getTransformers)?
410
+ ({ artifactRef, inspectRef, effectiveType, get$combined } = getUtils(csn));
414
411
  }
415
412
 
416
413
  function bindCsnReferenceOnly(){
@@ -509,7 +506,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
509
506
  function recursivelyApplyCommon(artifact, artifactName) {
510
507
  if (!artifact._ignore) {
511
508
  if (artifact.kind !== 'service' && artifact.kind !== 'context')
512
- addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.forHana.names, csn), artifact);
509
+ addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
513
510
 
514
511
  forEachMemberRecursively(artifact, (member, memberName, property, path) => {
515
512
  transformCommon(member, memberName, path);
@@ -517,7 +514,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
517
514
  // Virtual elements in entities and types are not annotated, as they have no DB representation.
518
515
  // In views they are, as we generate a null expression for them (null as <colname>)
519
516
  if ((!member.virtual || artifact.query))
520
- addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.forHana.names), member);
517
+ addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), member);
521
518
  }, [ 'definitions', artifactName ]);
522
519
  }
523
520
  }
@@ -543,7 +540,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
543
540
  function transformSelfInBacklinks(artifact, artifactName, dummy, path) {
544
541
  // Fixme: For toHana mixins must be transformed, for toSql -d hana
545
542
  // mixin elements must be transformed, why can't toSql also use mixins?
546
- if(artifact.kind === 'entity' || artifact.query || (options.toHana && options.toHana.names === 'hdbcds' && artifact.kind == 'type'))
543
+ if(artifact.kind === 'entity' || artifact.query || (options.forHana && options.sqlMapping === 'hdbcds' && artifact.kind === 'type'))
547
544
  doit(artifact.elements, path.concat([ 'elements' ]));
548
545
  if (artifact.query && artifact.query.SELECT && artifact.query.SELECT.mixin)
549
546
  doit(artifact.query.SELECT.mixin, path.concat([ 'query', 'SELECT', 'mixin' ]));
@@ -574,7 +571,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
574
571
  // For HANA: Report an error on
575
572
  // - view with parameters that has an element of type association/composition
576
573
  // - association that points to entity with parameters
577
- if (options.forHana.dialect === 'hana' && member.target && isAssocOrComposition(member.type) && !isBetaEnabled(options, 'assocsWithParams')) {
574
+ if (options.sqlDialect === 'hana' && member.target && isAssocOrComposition(member.type) && !isBetaEnabled(options, 'assocsWithParams')) {
578
575
  if (artifact.params) {
579
576
  // HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
580
577
  // SAP DBTech JDBC: [7]: feature not supported: parameterized sql view cannot support association: line 1 col 1 (at pos 0)
@@ -617,7 +614,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
617
614
  sql: 'Table-like entities with parameters are not supported for conversion to SQL',
618
615
  });
619
616
  }
620
- else if (options.forHana.dialect === 'sqlite') { // view with params
617
+ else if (options.sqlDialect === 'sqlite') { // view with params
621
618
  // Allow with plain
622
619
  error(null, [ 'definitions', artifactName ], `SQLite does not support entities with parameters`);
623
620
  }
@@ -626,8 +623,8 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
626
623
  if (pname.match(/\W/g) || pname.match(/^\d/) || pname.match(/^_/)) { // parameter name must be regular SQL identifier
627
624
  warning(null, [ 'definitions', artifactName, 'params', pname ], `Expecting regular SQL-Identifier`);
628
625
  }
629
- else if (options.forHana.names !== 'plain' && pname.toUpperCase() !== pname) { // not plain mode: param name must be all upper
630
- warning(null, [ 'definitions', artifactName, 'params', pname ], { name: options.forHana.names },
626
+ else if (options.sqlMapping !== 'plain' && pname.toUpperCase() !== pname) { // not plain mode: param name must be all upper
627
+ warning(null, [ 'definitions', artifactName, 'params', pname ], { name: options.sqlMapping },
631
628
  'Expecting parameter to be uppercase in naming mode $(NAME)');
632
629
  }
633
630
  }
@@ -1048,7 +1045,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
1048
1045
  * @returns {boolean}
1049
1046
  */
1050
1047
  function isMaxParameterLengthRestricted(type) {
1051
- return !(options.toSql && type === 'cds.String' && (options.toSql.dialect === 'sqlite' || options.toSql.dialect === 'plain'));
1048
+ return !(options.toSql && type === 'cds.String' && (options.sqlDialect === 'sqlite' || options.sqlDialect === 'plain'));
1052
1049
  }
1053
1050
 
1054
1051
  /**