@sap/cds-compiler 3.6.2 → 3.8.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 (89) hide show
  1. package/CHANGELOG.md +109 -1
  2. package/README.md +3 -0
  3. package/bin/cdsc.js +12 -5
  4. package/doc/CHANGELOG_ARCHIVE.md +6 -6
  5. package/doc/CHANGELOG_BETA.md +35 -2
  6. package/doc/CHANGELOG_DEPRECATED.md +2 -2
  7. package/doc/DeprecatedOptions_v2.md +1 -1
  8. package/doc/NameResolution.md +1 -1
  9. package/lib/api/main.js +63 -23
  10. package/lib/api/options.js +1 -0
  11. package/lib/api/validate.js +5 -0
  12. package/lib/base/dictionaries.js +15 -3
  13. package/lib/base/keywords.js +2 -0
  14. package/lib/base/message-registry.js +120 -34
  15. package/lib/base/messages.js +51 -27
  16. package/lib/base/model.js +4 -2
  17. package/lib/base/shuffle.js +2 -1
  18. package/lib/checks/arrayOfs.js +1 -1
  19. package/lib/checks/defaultValues.js +1 -1
  20. package/lib/checks/elements.js +29 -1
  21. package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +10 -6
  22. package/lib/checks/invalidTarget.js +1 -1
  23. package/lib/checks/nonexpandableStructured.js +1 -1
  24. package/lib/checks/onConditions.js +15 -9
  25. package/lib/checks/sql-snippets.js +2 -2
  26. package/lib/checks/types.js +5 -1
  27. package/lib/checks/validator.js +7 -3
  28. package/lib/compiler/assert-consistency.js +42 -26
  29. package/lib/compiler/base.js +50 -4
  30. package/lib/compiler/builtins.js +17 -8
  31. package/lib/compiler/checks.js +241 -246
  32. package/lib/compiler/define.js +113 -146
  33. package/lib/compiler/extend.js +889 -383
  34. package/lib/compiler/finalize-parse-cdl.js +5 -58
  35. package/lib/compiler/index.js +1 -1
  36. package/lib/compiler/kick-start.js +7 -8
  37. package/lib/compiler/populate.js +297 -293
  38. package/lib/compiler/propagator.js +27 -18
  39. package/lib/compiler/resolve.js +146 -463
  40. package/lib/compiler/shared.js +36 -79
  41. package/lib/compiler/tweak-assocs.js +30 -28
  42. package/lib/compiler/utils.js +31 -5
  43. package/lib/edm/annotations/genericTranslation.js +131 -59
  44. package/lib/edm/annotations/preprocessAnnotations.js +3 -0
  45. package/lib/edm/csn2edm.js +22 -5
  46. package/lib/edm/edm.js +6 -4
  47. package/lib/edm/edmAnnoPreprocessor.js +1 -0
  48. package/lib/edm/edmPreprocessor.js +42 -26
  49. package/lib/gen/Dictionary.json +38 -2
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +3 -1
  52. package/lib/gen/languageLexer.js +1 -1
  53. package/lib/gen/languageParser.js +4828 -4472
  54. package/lib/inspect/inspectPropagation.js +20 -34
  55. package/lib/json/from-csn.js +140 -44
  56. package/lib/json/to-csn.js +114 -122
  57. package/lib/language/errorStrategy.js +2 -0
  58. package/lib/language/genericAntlrParser.js +156 -36
  59. package/lib/language/language.g4 +100 -58
  60. package/lib/language/textUtils.js +13 -0
  61. package/lib/main.d.ts +43 -3
  62. package/lib/main.js +4 -2
  63. package/lib/model/csnRefs.js +15 -3
  64. package/lib/model/csnUtils.js +12 -74
  65. package/lib/model/revealInternalProperties.js +4 -2
  66. package/lib/modelCompare/compare.js +2 -1
  67. package/lib/optionProcessor.js +3 -0
  68. package/lib/render/manageConstraints.js +5 -2
  69. package/lib/render/toCdl.js +216 -104
  70. package/lib/render/toHdbcds.js +2 -9
  71. package/lib/render/toRename.js +14 -51
  72. package/lib/render/toSql.js +4 -3
  73. package/lib/render/utils/common.js +9 -5
  74. package/lib/transform/braceExpression.js +6 -0
  75. package/lib/transform/db/assertUnique.js +2 -1
  76. package/lib/transform/db/expansion.js +2 -0
  77. package/lib/transform/db/flattening.js +37 -36
  78. package/lib/transform/db/rewriteCalculatedElements.js +600 -0
  79. package/lib/transform/db/transformExists.js +4 -0
  80. package/lib/transform/db/views.js +40 -37
  81. package/lib/transform/forOdataNew.js +20 -15
  82. package/lib/transform/forRelationalDB.js +58 -41
  83. package/lib/transform/odata/typesExposure.js +50 -15
  84. package/lib/transform/parseExpr.js +16 -8
  85. package/lib/transform/transformUtilsNew.js +42 -14
  86. package/lib/transform/translateAssocsToJoins.js +60 -37
  87. package/lib/transform/universalCsn/coreComputed.js +15 -7
  88. package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
  89. package/package.json +2 -1
@@ -63,7 +63,6 @@ function getUtils( model, universalReady ) {
63
63
  addStringAnnotationTo,
64
64
  getServiceName,
65
65
  hasAnnotationValue,
66
- cloneWithTransformations,
67
66
  getFinalBaseTypeWithProps,
68
67
  get$combined,
69
68
  getQueryPrimarySource,
@@ -81,7 +80,7 @@ function getUtils( model, universalReady ) {
81
80
  }
82
81
 
83
82
  /**
84
- * Get the union of all elements from the from clause
83
+ * Get the union of all elements from the "from" clause
85
84
  * - descend into unions, following the lead query
86
85
  * - merge all queries in case of joins
87
86
  * - follow subqueries
@@ -93,7 +92,7 @@ function getUtils( model, universalReady ) {
93
92
  function getSources( query, isSubquery = false ) {
94
93
  // Remark CW: better just a while along query.SET.args[0]
95
94
  if (query.SET) {
96
- if (query.SET.args[0].SELECT && query.SET.args[0].SELECT.elements)
95
+ if (query.SET.args[0].SELECT?.elements)
97
96
  return mergeElementsIntoMap(Object.create(null), query.SET.args[0].SELECT.elements, query.SET.args[0].$location);
98
97
 
99
98
  return getSources(query.SET.args[0], isSubquery);
@@ -111,9 +110,12 @@ function getUtils( model, universalReady ) {
111
110
  if (isSubquery && !query.SELECT.elements)
112
111
  throw new ModelError('Expected subquery to have .elements');
113
112
 
114
- return mergeElementsIntoMap(Object.create(null), isSubquery ? query.SELECT.elements : art.elements, art.$location,
115
- query.SELECT.from.as || query.SELECT.from.ref[query.SELECT.from.ref.length - 1],
116
- query.SELECT.from.ref[query.SELECT.from.ref.length - 1] || query.SELECT.from.as );
113
+ const elements = isSubquery ? query.SELECT.elements : art.elements;
114
+ // sub-queries also have an alias that is reachable by outer queries, in contrast to `from.as`.
115
+ const parent = query.as || query.SELECT.from.as || implicitAs(query.SELECT.from.ref);
116
+ // for better error messages, we refer to the actual reference name first
117
+ const errorParent = implicitAs(query.SELECT.from.ref);
118
+ return mergeElementsIntoMap(Object.create(null), elements, art.$location, parent, errorParent);
117
119
  }
118
120
  else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
119
121
  return getSources(query.SELECT.from, true);
@@ -131,7 +133,7 @@ function getUtils( model, universalReady ) {
131
133
  }
132
134
  else if (arg.ref) {
133
135
  const art = artifactRef(arg);
134
- elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || arg.ref[arg.ref.length - 1], arg.ref[arg.ref.length - 1] || arg.as);
136
+ elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || implicitAs(arg.ref), implicitAs(arg.ref) || arg.as);
135
137
  }
136
138
  else if (arg.SELECT || arg.SET) {
137
139
  elements = mergeElementMaps(elements, getSources(arg));
@@ -176,27 +178,13 @@ function getUtils( model, universalReady ) {
176
178
  existingMap[elementName] = [];
177
179
 
178
180
  existingMap[elementName].push({
179
- element, name: elementName, source: $location, parent: getBaseName(parent), errorParent,
181
+ element, name: elementName, source: $location, parent, errorParent,
180
182
  });
181
183
  }
182
184
 
183
185
  return existingMap;
184
186
  }
185
187
 
186
- /**
187
- * Return the name part of the artifact name - no namespace etc.
188
- * @param {string|object} name Absolute name of the artifact
189
- */
190
- function getBaseName( name ) {
191
- if (!name)
192
- return name;
193
-
194
- if (name.id)
195
- return name.id.substring( name.id.lastIndexOf('.') + 1 );
196
-
197
- return name.substring( name.lastIndexOf('.') + 1 );
198
- }
199
-
200
188
  /**
201
189
  * Return the left-most, primary source of the given query.
202
190
  * @param {*} query Definition's query object
@@ -341,57 +329,6 @@ function getUtils( model, universalReady ) {
341
329
  }
342
330
  }
343
331
 
344
- /**
345
- * Clone 'node', transforming nodes therein recursively. Object 'transformers' is expected
346
- * to contain a mapping of property 'key' names to transformer functions. The node's properties
347
- * are walked recursively, calling each transformer function on its corresponding property
348
- * 'key' of 'node', replacing 'value' in 'resultNode' with the function's return value
349
- * (returning 'undefined' will delete the property).
350
- * If no transformation function is found for 'key', the first letter of 'key' is tried
351
- * instead (this seems to be intended for handling annotations that start with '@' ?)
352
- *
353
- * Regardless of their names, transformers are never applied to dictionary elements.
354
- *
355
- * The transformer functions are called with the following signature:
356
- * transformer(value, node, resultNode, key)
357
- *
358
- * @param {any} rootNode Node to transform
359
- * @param {any} transformers Object defining transformer functions
360
- * @returns {object}
361
- */
362
- function cloneWithTransformations( rootNode, transformers ) {
363
- return transformNode(rootNode);
364
-
365
- // This general transformation function will be applied to each node recursively
366
- function transformNode( node ) {
367
- // Return primitive values and null unchanged, but let objects and dictionaries through
368
- // (Note that 'node instanceof Object' would be false for dictionaries).
369
- if (node === null || typeof node !== 'object')
370
- return node;
371
-
372
- // Simply return if node is to be ignored
373
- if (node === undefined || node._ignore)
374
- return undefined;
375
- // Transform arrays element-wise
376
- if (Array.isArray(node))
377
- return node.map(transformNode);
378
-
379
- // Things not having 'proto' are dictionaries
380
- const proto = Object.getPrototypeOf(node);
381
- // Iterate own properties of 'node' and transform them into 'resultNode'
382
- const resultNode = Object.create(proto);
383
- for (const key of Object.keys(node)) {
384
- // Dictionary always use transformNode(), other objects their transformer according to key
385
- const transformer = (proto === null || proto === undefined) ? transformNode : transformers[key] || transformers[key.charAt(0)];
386
- // Apply transformer, or use transformNode() if there is none
387
- const resultValue = (transformer || transformNode)(node[key], node, resultNode, key);
388
- if (resultValue !== undefined)
389
- resultNode[key] = resultValue;
390
- }
391
- return resultNode;
392
- }
393
- }
394
-
395
332
  /**
396
333
  * Resolve to the final type of a type, that means follow type chains, references, etc.
397
334
  * Input is a fully qualified type name, i.e. string, or type ref, i.e. `{ ref: [...] }`.
@@ -423,6 +360,7 @@ function getUtils( model, universalReady ) {
423
360
  // - r({ref: ['\\', '\\', '\\\\'] }) != r({ref: ['\\', '\\\\', '\\'] })
424
361
  // - r({ref: ['\\', '\\', '\\\\'] }) != r({ref: ['\\', '\\\\2:\\\\'] })
425
362
  const resolvedKey = (typeof type === 'object')
363
+ // eslint-disable-next-line sonarjs/no-nested-template-literals
426
364
  ? `ref[${ type.ref.length }]:${ type.ref.map((val, i) => `${ i }:${ val }`).join('\\') }`
427
365
  : `type:${ type }`;
428
366
 
@@ -930,7 +868,7 @@ function getUnderscoredName( startIndex, parts, csn ) {
930
868
  for (let i = startIndex; i < parts.length; i++) {
931
869
  const namePart = parts.slice(0, i).join('.');
932
870
  const art = csn.definitions[namePart];
933
- if (art && !(art.kind === 'namespace' || art.kind === 'context' || art.kind === 'service')) {
871
+ if (art && !(art.kind === 'context' || art.kind === 'service')) {
934
872
  const prefix = parts.slice(0, i - 1).join('.');
935
873
  const suffix = parts.slice(i - 1).join('_');
936
874
  const result = [];
@@ -61,6 +61,7 @@ function tableAliasAsLink( art, parent, name ) {
61
61
  * @param {string} [nameOrPath]
62
62
  */
63
63
  function revealInternalProperties( model, nameOrPath ) {
64
+ // return model;
64
65
  const transformers = {
65
66
  messages: m => m,
66
67
  name: shortenName,
@@ -71,6 +72,7 @@ function revealInternalProperties( model, nameOrPath ) {
71
72
  artifacts: artifactDictionary,
72
73
  definitions: artifactDictionary,
73
74
  vocabularies: dictionary,
75
+ $lateExtensions: dictionary,
74
76
  elements,
75
77
  columns,
76
78
  expand: columns,
@@ -95,6 +97,7 @@ function revealInternalProperties( model, nameOrPath ) {
95
97
  $compositionTargets: d => d, // dictionary( boolean )
96
98
  _extend: reveal,
97
99
  _annotate: reveal,
100
+ _annotateS: artifactIdentifier,
98
101
  _deps: dependencyInfo,
99
102
  _status: primOrString, // is a string anyway
100
103
  $annotations: as => as.map( $annotation ),
@@ -170,8 +173,7 @@ function revealInternalProperties( model, nameOrPath ) {
170
173
  if (!Array.isArray(deps))
171
174
  return primOrString( deps );
172
175
  return deps
173
- .filter( d => d.location )
174
- .map( d => artifactIdentifier( d.art ) );
176
+ .map( d => `${ d.location ? '' : '-' }${ artifactIdentifier( d.art ) }`);
175
177
  }
176
178
 
177
179
  function layerExtends( dict ) {
@@ -120,6 +120,7 @@ function getArtifactComparator(otherModel, options, addedEntities, deletedEntiti
120
120
  // Arguments are interchanged in this case: `artifact` from beforeModel and `otherArtifact` from afterModel.
121
121
  if (isPersisted && !isPersistedOther) {
122
122
  deletedEntities[name] = artifact;
123
+ // eslint-disable-next-line sonarjs/no-duplicated-branches
123
124
  } else if(isPersistedAsView(artifact) && isPersistedOther) { // view turned into table - need to render a drop for the view
124
125
  deletedEntities[name] = artifact;
125
126
  }
@@ -254,7 +255,7 @@ module.exports = {
254
255
  /**
255
256
  * A ModelDiff encapsulates the changes between two models ("before" and "after"). It contains information
256
257
  * about changes to .elements and removed artifacts.
257
- *
258
+ *
258
259
  * @typedef {object} ModelDiff
259
260
  * @property {CSN.Definitions} definitions The artifacts present in the "after" model
260
261
  * @property {CSN.Definitions} deletions The artifacts present in the "before", but not in the "after"
@@ -226,6 +226,7 @@ optionProcessor.command('O, toOdata')
226
226
  .option(' --odata-x-service-refs')
227
227
  .option(' --odata-foreign-keys')
228
228
  .option(' --odata-v2-partial-constr')
229
+ .option(' --odata-voc-refs <list>')
229
230
  .option('-c, --csn')
230
231
  .option('-f, --odata-format <format>', ['flat', 'structured'])
231
232
  .option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'], { aliases: [ '--names' ] })
@@ -254,6 +255,8 @@ optionProcessor.command('O, toOdata')
254
255
  --odata-foreign-keys Render foreign keys in structured format (V4 only)
255
256
  --odata-v2-partial-constr Render referential constraints also for partial principal key tuple
256
257
  (Not spec compliant and V2 only)
258
+ --odata-voc-refs <list> JSON array of adhoc vocabulary definitions
259
+ [ { alias, ns, uri }, ... ]
257
260
  -n, --sql-mapping <style> Annotate artifacts and elements with "@cds.persistence.name", which is
258
261
  the corresponding database name (see "--sql-mapping" for "toHana or "toSql")
259
262
  plain : (default) Names in uppercase and flattened with underscores
@@ -23,12 +23,15 @@ const { sortCsn } = require('../json/to-csn');
23
23
  * @param {CSN.Options} options
24
24
  */
25
25
  function alterConstraintsWithCsn( csn, options ) {
26
- const { error } = makeMessageFunction(csn, options, 'manageConstraints');
26
+ const { error, warning } = makeMessageFunction(csn, options, 'manageConstraints');
27
27
 
28
28
  const {
29
- drop, alter, src, violations,
29
+ drop, alter, src, violations, sqlDialect,
30
30
  } = options || {};
31
31
 
32
+ if (!sqlDialect || sqlDialect === 'h2' || sqlDialect === 'plain')
33
+ warning(null, null, { prop: sqlDialect || 'plain' }, 'Referential Constraints are not available for sql dialect $(PROP)');
34
+
32
35
  if (drop && alter)
33
36
  error(null, null, 'Option “--drop” can\'t be combined with “--alter”');
34
37