@sap/cds-compiler 3.7.2 → 3.8.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 (70) hide show
  1. package/CHANGELOG.md +71 -4
  2. package/bin/cdsc.js +3 -0
  3. package/doc/CHANGELOG_ARCHIVE.md +6 -6
  4. package/doc/CHANGELOG_BETA.md +15 -0
  5. package/doc/DeprecatedOptions_v2.md +1 -1
  6. package/doc/NameResolution.md +1 -1
  7. package/lib/api/main.js +61 -22
  8. package/lib/api/options.js +1 -0
  9. package/lib/api/validate.js +5 -0
  10. package/lib/base/dictionaries.js +5 -3
  11. package/lib/base/keywords.js +2 -0
  12. package/lib/base/message-registry.js +64 -22
  13. package/lib/base/messages.js +12 -7
  14. package/lib/base/model.js +3 -2
  15. package/lib/checks/arrayOfs.js +1 -1
  16. package/lib/checks/defaultValues.js +1 -1
  17. package/lib/checks/hasPersistedElements.js +1 -1
  18. package/lib/checks/invalidTarget.js +1 -1
  19. package/lib/checks/onConditions.js +9 -6
  20. package/lib/checks/sql-snippets.js +2 -2
  21. package/lib/checks/types.js +1 -2
  22. package/lib/compiler/assert-consistency.js +25 -6
  23. package/lib/compiler/base.js +51 -2
  24. package/lib/compiler/builtins.js +15 -6
  25. package/lib/compiler/checks.js +4 -4
  26. package/lib/compiler/define.js +59 -80
  27. package/lib/compiler/extend.js +717 -498
  28. package/lib/compiler/finalize-parse-cdl.js +4 -3
  29. package/lib/compiler/index.js +1 -1
  30. package/lib/compiler/kick-start.js +2 -2
  31. package/lib/compiler/populate.js +17 -9
  32. package/lib/compiler/propagator.js +12 -5
  33. package/lib/compiler/resolve.js +26 -173
  34. package/lib/compiler/shared.js +20 -58
  35. package/lib/compiler/tweak-assocs.js +1 -1
  36. package/lib/compiler/utils.js +2 -2
  37. package/lib/edm/annotations/genericTranslation.js +124 -46
  38. package/lib/edm/csn2edm.js +22 -1
  39. package/lib/edm/edmPreprocessor.js +41 -21
  40. package/lib/gen/Dictionary.json +4 -0
  41. package/lib/gen/language.checksum +1 -1
  42. package/lib/gen/language.interp +3 -1
  43. package/lib/gen/languageLexer.js +1 -1
  44. package/lib/gen/languageParser.js +4844 -4508
  45. package/lib/inspect/inspectPropagation.js +20 -36
  46. package/lib/json/from-csn.js +56 -7
  47. package/lib/json/to-csn.js +71 -110
  48. package/lib/language/errorStrategy.js +1 -0
  49. package/lib/language/genericAntlrParser.js +49 -9
  50. package/lib/language/language.g4 +106 -83
  51. package/lib/language/textUtils.js +13 -0
  52. package/lib/main.d.ts +43 -3
  53. package/lib/main.js +4 -2
  54. package/lib/model/csnRefs.js +19 -4
  55. package/lib/model/csnUtils.js +11 -74
  56. package/lib/model/revealInternalProperties.js +3 -0
  57. package/lib/optionProcessor.js +3 -0
  58. package/lib/render/toCdl.js +203 -104
  59. package/lib/render/toHdbcds.js +0 -1
  60. package/lib/render/toRename.js +14 -51
  61. package/lib/transform/braceExpression.js +6 -0
  62. package/lib/transform/db/rewriteCalculatedElements.js +55 -14
  63. package/lib/transform/forOdataNew.js +20 -15
  64. package/lib/transform/forRelationalDB.js +21 -14
  65. package/lib/transform/parseExpr.js +2 -0
  66. package/lib/transform/transformUtilsNew.js +36 -9
  67. package/lib/transform/translateAssocsToJoins.js +11 -4
  68. package/lib/transform/universalCsn/coreComputed.js +15 -7
  69. package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
  70. package/package.json +2 -1
package/lib/main.js CHANGED
@@ -26,6 +26,7 @@ const compiler = lazyload('./compiler');
26
26
  const shared = lazyload('./compiler/shared');
27
27
  const define = lazyload('./compiler/define');
28
28
  const builtins = lazyload('./compiler/builtins');
29
+ const base = lazyload('./compiler/base');
29
30
  const finalizeParseCdl = lazyload('./compiler/finalize-parse-cdl');
30
31
 
31
32
  // The compiler version (taken from package.json)
@@ -157,9 +158,10 @@ module.exports = {
157
158
  // INTERNAL functions for the cds-lsp package and friends - before you use
158
159
  // it, you MUST talk with us - there can be potential incompatibilities with
159
160
  // new releases (even having the same major version):
160
- $lsp: { parse: (...args) => compiler.parseX(...args),
161
+ $lsp: {
162
+ parse: (...args) => compiler.parseX(...args),
161
163
  compile: (...args) => compiler.compileX(...args),
162
- getArtifactName: a => a.name
164
+ getArtifactName: (...args) => base.getArtifactName(...args),
163
165
  },
164
166
 
165
167
  // CSN Model related functionality
@@ -620,6 +620,9 @@ function csnRefs( csn, universalReady ) {
620
620
  return resolvePath( path, art, parent, 'parent' );
621
621
  }
622
622
 
623
+ if (!qcache)
624
+ throw new CompilerAssertion( `Query not in cache at: ${ locationString(query.$location) }` );
625
+
623
626
  if (semantics.dynamic === 'query')
624
627
  // TODO: for ON condition in expand, would need to use cached _element
625
628
  return resolvePath( path, qcache.elements[head], null, 'query' );
@@ -637,10 +640,18 @@ function csnRefs( csn, universalReady ) {
637
640
  /**
638
641
  * @param {CSN.Path} path
639
642
  * @param {CSN.Artifact} art
643
+ * @param {CSN.Artifact} parent
640
644
  * @param {string} [scope]
641
645
  * @param [extraInfo]
642
646
  */
643
647
  function resolvePath( path, art, parent, scope, extraInfo ) {
648
+ if (!art && path.length > 1) {
649
+ // TODO: For path.length===1, it may be that `art` is undefined, e.g. for CSN paths such
650
+ // as `[…, 'on', 1]` where the path segment refers to `=`.
651
+ // TODO: Check the call-side.
652
+ const loc = locationString(parent?.$location);
653
+ throw new ModelError(`Path item 0='${ pathId(path[0]) }' refers to nothing; in ${ loc }; path=${ JSON.stringify(path) }`);
654
+ }
644
655
  const staticAssoc = extraInfo === 'static' && scope === 'global';
645
656
  /** @type {{idx, art?, env?}[]} */
646
657
  const links = path.map( (_v, idx) => ({ idx }) );
@@ -706,8 +717,12 @@ function csnRefs( csn, universalReady ) {
706
717
  if (query !== main)
707
718
  cache.set( query, qcache );
708
719
 
709
- if (fromSelect)
710
- getCache( fromSelect, '$aliases' )[query.as] = qcache;
720
+ if (fromSelect) {
721
+ const $queryNumber = all.length + 1;
722
+ const alias = query.as || `$_select_${ $queryNumber }__`;
723
+ getCache(fromSelect, '$aliases')[alias] = qcache;
724
+ }
725
+
711
726
  const select = query.SELECT || query.projection;
712
727
  if (select) {
713
728
  cache.set( select, qcache ); // query and query.SELECT have the same cache qcache
@@ -870,8 +885,8 @@ function traverseFrom( from, fromSelect, parentQuery, callback ) {
870
885
  }
871
886
  else if (from.args) { // join
872
887
  from.args.forEach( arg => traverseFrom( arg, fromSelect, parentQuery, callback ) );
873
- if (from.on) // join-on, potentially having a sub query
874
- from.on.forEach( arg => traverseQuery( arg, null, fromSelect, callback ) );
888
+ if (from.on) // join-on, potentially having a sub query (in xpr)
889
+ from.on.forEach(arg => traverseExpr(arg, fromSelect, callback));
875
890
  }
876
891
  else { // sub query in FROM
877
892
  traverseQuery( from, fromSelect, parentQuery, callback );
@@ -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: [...] }`.
@@ -931,7 +868,7 @@ function getUnderscoredName( startIndex, parts, csn ) {
931
868
  for (let i = startIndex; i < parts.length; i++) {
932
869
  const namePart = parts.slice(0, i).join('.');
933
870
  const art = csn.definitions[namePart];
934
- if (art && !(art.kind === 'namespace' || art.kind === 'context' || art.kind === 'service')) {
871
+ if (art && !(art.kind === 'context' || art.kind === 'service')) {
935
872
  const prefix = parts.slice(0, i - 1).join('.');
936
873
  const suffix = parts.slice(i - 1).join('_');
937
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 ),
@@ -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