@sap/cds-compiler 3.8.2 → 3.9.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 (79) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/bin/cdsc.js +2 -2
  3. package/doc/CHANGELOG_BETA.md +26 -5
  4. package/lib/api/.eslintrc.json +3 -2
  5. package/lib/api/options.js +3 -1
  6. package/lib/api/validate.js +1 -1
  7. package/lib/base/message-registry.js +27 -18
  8. package/lib/base/messages.js +6 -1
  9. package/lib/base/model.js +2 -2
  10. package/lib/checks/.eslintrc.json +1 -0
  11. package/lib/checks/actionsFunctions.js +6 -6
  12. package/lib/checks/annotationsOData.js +1 -1
  13. package/lib/checks/elements.js +28 -17
  14. package/lib/checks/foreignKeys.js +1 -1
  15. package/lib/checks/invalidTarget.js +1 -1
  16. package/lib/checks/onConditions.js +11 -6
  17. package/lib/checks/queryNoDbArtifacts.js +1 -1
  18. package/lib/checks/types.js +1 -1
  19. package/lib/checks/utils.js +1 -1
  20. package/lib/checks/validator.js +3 -2
  21. package/lib/compiler/assert-consistency.js +7 -2
  22. package/lib/compiler/base.js +8 -4
  23. package/lib/compiler/builtins.js +7 -0
  24. package/lib/compiler/checks.js +73 -6
  25. package/lib/compiler/define.js +10 -5
  26. package/lib/compiler/extend.js +910 -1711
  27. package/lib/compiler/finalize-parse-cdl.js +1 -1
  28. package/lib/compiler/generate.js +838 -0
  29. package/lib/compiler/index.js +2 -0
  30. package/lib/compiler/populate.js +2 -2
  31. package/lib/compiler/propagator.js +20 -8
  32. package/lib/compiler/resolve.js +3 -3
  33. package/lib/compiler/shared.js +3 -1
  34. package/lib/edm/annotations/genericTranslation.js +6 -6
  35. package/lib/edm/csn2edm.js +1 -1
  36. package/lib/edm/edm.js +25 -11
  37. package/lib/edm/edmPreprocessor.js +47 -23
  38. package/lib/edm/edmUtils.js +37 -9
  39. package/lib/gen/Dictionary.json +5 -7
  40. package/lib/gen/language.checksum +1 -1
  41. package/lib/gen/language.interp +3 -1
  42. package/lib/gen/language.tokens +24 -23
  43. package/lib/gen/languageLexer.interp +4 -1
  44. package/lib/gen/languageLexer.js +792 -784
  45. package/lib/gen/languageLexer.tokens +12 -11
  46. package/lib/gen/languageParser.js +3564 -3493
  47. package/lib/json/from-csn.js +26 -4
  48. package/lib/json/to-csn.js +10 -6
  49. package/lib/language/antlrParser.js +11 -3
  50. package/lib/language/genericAntlrParser.js +2 -1
  51. package/lib/language/language.g4 +14 -3
  52. package/lib/model/csnRefs.js +10 -5
  53. package/lib/model/csnUtils.js +41 -76
  54. package/lib/modelCompare/utils/.eslintrc.json +1 -1
  55. package/lib/optionProcessor.js +7 -4
  56. package/lib/render/.eslintrc.json +1 -1
  57. package/lib/render/toCdl.js +244 -168
  58. package/lib/render/toHdbcds.js +18 -10
  59. package/lib/render/toSql.js +24 -2
  60. package/lib/transform/db/.eslintrc.json +4 -3
  61. package/lib/transform/db/cdsPersistence.js +1 -1
  62. package/lib/transform/db/expansion.js +11 -6
  63. package/lib/transform/db/flattening.js +22 -15
  64. package/lib/transform/db/rewriteCalculatedElements.js +50 -29
  65. package/lib/transform/db/temporal.js +1 -1
  66. package/lib/transform/db/views.js +1 -1
  67. package/lib/transform/draft/db.js +1 -1
  68. package/lib/transform/draft/odata.js +3 -4
  69. package/lib/transform/forOdataNew.js +5 -6
  70. package/lib/transform/forRelationalDB.js +7 -7
  71. package/lib/transform/odata/toFinalBaseType.js +6 -6
  72. package/lib/transform/odata/typesExposure.js +12 -3
  73. package/lib/transform/odata/utils.js +3 -0
  74. package/lib/transform/transformUtilsNew.js +11 -26
  75. package/lib/transform/translateAssocsToJoins.js +9 -9
  76. package/lib/transform/universalCsn/.eslintrc.json +3 -2
  77. package/lib/transform/universalCsn/coreComputed.js +1 -1
  78. package/lib/transform/universalCsn/universalCsnEnricher.js +6 -4
  79. package/package.json +1 -1
@@ -66,9 +66,13 @@ function propExists( prop, parent ) {
66
66
  return (obj.items || obj.targetAspect || obj)[prop];
67
67
  }
68
68
 
69
- // Return the "old style" name structure with `absolute`, `action`, `param`,
70
- // `element`. The following code makes use of the fact that only member extensions
71
- // have a "sparse" name structure.
69
+ /**
70
+ * Return the "old style" name structure with `absolute`, `action`, `param`,
71
+ * `element`. The following code makes use of the fact that only member extensions
72
+ * have a "sparse" name structure.
73
+ *
74
+ * @param {XSN.Artifact} art
75
+ */
72
76
  function getArtifactName( art ) {
73
77
  if (!art.name || art.name.absolute) // no name or “old style”
74
78
  return art.name;
@@ -105,7 +109,7 @@ function getMemberNameProp( elem, kind ) {
105
109
  obj = obj.items;
106
110
  if (obj.elements || obj.enum)
107
111
  return 'element';
108
- throw CompilerAssertion( `Member not found in parent properties ${ Object.keys( obj ).join('+') }` );
112
+ throw new CompilerAssertion( `Member not found in parent properties ${ Object.keys( obj ).join('+') }` );
109
113
  }
110
114
 
111
115
  module.exports = {
@@ -191,6 +191,13 @@ const magicVariables = {
191
191
  // Require that elements are accessed, i.e. no $at, only $at.<element>.
192
192
  $requireElementAccess: true,
193
193
  },
194
+ $valid: { // dito
195
+ elements: {
196
+ from: {}, to: {},
197
+ },
198
+ // Require that elements are accessed, i.e. no $valid, only $valid.<element>.
199
+ $requireElementAccess: true,
200
+ },
194
201
  $now: {}, // Dito
195
202
  $session: {
196
203
  // In ABAP CDS session variables are accessed in a generic way via
@@ -16,11 +16,12 @@
16
16
  // const { hasArtifactTypeInformation } = require('../model/csnUtils')
17
17
  const builtins = require('../compiler/builtins');
18
18
  const {
19
- forEachGeneric, forEachDefinition, forEachMember,
19
+ forEachGeneric, forEachDefinition, forEachMember, isBetaEnabled,
20
20
  } = require('../base/model');
21
21
  const { CompilerAssertion } = require('../base/error');
22
22
  const { pathName } = require('./utils');
23
23
  const { forEachMemberRecursively } = require('../model/csnUtils');
24
+ const $location = Symbol.for('cds.$location');
24
25
 
25
26
  function check( model ) { // = XSN
26
27
  const {
@@ -34,11 +35,13 @@ function check( model ) { // = XSN
34
35
  forEachGeneric( model, 'vocabularies', checkAnnotationDefinition );
35
36
  return;
36
37
 
37
- function checkDefinition( art ) {
38
- checkGenericConstruct( art );
39
- forEachMember( art, member => checkMember(member) );
40
- if (art.$queries)
41
- art.$queries.forEach( checkQuery );
38
+ function checkDefinition( def ) {
39
+ checkGenericConstruct( def );
40
+ if (def.includes && def.elements)
41
+ checkElementIncludeOverride( def );
42
+ forEachMember( def, member => checkMember(member) );
43
+ if (def.$queries)
44
+ def.$queries.forEach( checkQuery );
42
45
  }
43
46
 
44
47
  function checkAnnotationDefinition( art ) {
@@ -498,6 +501,70 @@ function check( model ) { // = XSN
498
501
  checkTypeStructure(artifact.items);
499
502
  }
500
503
 
504
+ /**
505
+ * Report issues when an entity overrides structured elements of an included entity
506
+ * with a scalar one or vice versa.
507
+ *
508
+ * NOTE: Relies on element expansion.
509
+ */
510
+ function checkElementIncludeOverride( def ) {
511
+ if (!isBetaEnabled(model.options, 'v4preview'))
512
+ return; // this is a v4 check only
513
+
514
+ for (const name in def.elements) {
515
+ const element = def.elements[name];
516
+ // Element is new in `art`, not expanded; we can't check for !element._origin, due
517
+ // to calculated elements such as `a = b`.
518
+ if (element.$inferred !== 'include') {
519
+ for (const include of def.includes) {
520
+ if (include._artifact?.elements?.[name] !== undefined)
521
+ checkElementOverride( element, include._artifact.elements[name]);
522
+ }
523
+ }
524
+ }
525
+
526
+ return;
527
+
528
+ function checkElementOverride( elem, original ) {
529
+ const xorElements = !elem.elements !== !original.elements;
530
+ if (xorElements) {
531
+ // one of the two elements is not structured
532
+ const prop = !elem.elements ? 'new-not-structured' : 'old-not-structured';
533
+ const name = elem.name.id;
534
+ // Position at type/struct, not name
535
+ const loc = elem.type?.location || elem.elements?.[$location] || elem.location;
536
+ error('ref-invalid-override', [ loc, elem ],
537
+ { '#': prop, art: original._main, name });
538
+ return false;
539
+ }
540
+ else if (original.elements &&
541
+ !checkSubStructureOverride(elem, elem.elements, original.elements)) {
542
+ return false;
543
+ }
544
+ return true;
545
+ }
546
+
547
+ /**
548
+ * Ensure the new one has at least as many elements as the original.
549
+ */
550
+ function checkSubStructureOverride( user, elements, originals ) {
551
+ for (const element in originals) {
552
+ const elem = elements[element];
553
+ const orig = originals[element];
554
+ if (elem === undefined) {
555
+ const loc = [ elements[$location], user ];
556
+ error('ref-invalid-override', loc, { '#': 'missing', id: user.name.id, name: element });
557
+ return false; // only report one
558
+ }
559
+ else if (!checkElementOverride(elem, orig)) {
560
+ return false;
561
+ }
562
+ }
563
+ return true;
564
+ }
565
+ }
566
+
567
+
501
568
  // Former checkExpressions.js ----------------------------------------------
502
569
 
503
570
  /**
@@ -123,6 +123,7 @@ const {
123
123
  forEachGeneric,
124
124
  forEachInOrder,
125
125
  forEachMember,
126
+ isBetaEnabled,
126
127
  } = require('../base/model');
127
128
  const shuffleGen = require('../base/shuffle');
128
129
  const {
@@ -159,7 +160,7 @@ function define( model ) {
159
160
  const { options } = model;
160
161
  // Get simplified "resolve" functionality and the message function:
161
162
  const {
162
- error, warning, info, messages,
163
+ error, warning, info, messages, message,
163
164
  } = model.$messageFunctions;
164
165
  const {
165
166
  resolveUncheckedPath,
@@ -201,7 +202,7 @@ function define( model ) {
201
202
  setLink( model, '_entities', [] ); // for entities with includes
202
203
  model.$entity = 0;
203
204
  model.$compositionTargets = Object.create(null);
204
- model.$lateExtensions = Object.create(null); // TODO: rename to $collectedExtensions
205
+ model.$collectedExtensions = Object.create(null);
205
206
 
206
207
  initBuiltins( model );
207
208
  const sourceNames = shuffleArray( Object.keys( model.sources ) );
@@ -211,7 +212,7 @@ function define( model ) {
211
212
  initNamespaceAndUsing( model.sources[name] );
212
213
  dictForEach( model.definitions, initArtifact );
213
214
  dictForEach( model.vocabularies, initVocabulary );
214
- dictForEach( model.$lateExtensions, e => e._extensions.forEach( initExtension ) );
215
+ dictForEach( model.$collectedExtensions, e => e._extensions.forEach( initExtension ) );
215
216
 
216
217
  addI18nBlocks();
217
218
  }
@@ -398,8 +399,8 @@ function define( model ) {
398
399
  delete ext.name.path[0]._artifact; // might point to wrong JS object in phase 1
399
400
  ext.name.absolute = absolute; // definition might not be there yet, no _artifact link
400
401
  const location = { file: '' }; // stupid required location
401
- const late = model.$lateExtensions[absolute] ||
402
- (model.$lateExtensions[absolute] = {
402
+ const late = model.$collectedExtensions[absolute] ||
403
+ (model.$collectedExtensions[absolute] = {
403
404
  kind: 'annotate',
404
405
  name: { absolute, location },
405
406
  $inferred: '',
@@ -1165,6 +1166,10 @@ function define( model ) {
1165
1166
  if (!elem.target)
1166
1167
  elem.type = { ...elem.value.type, $inferred: 'cast' };
1167
1168
  }
1169
+ if (elem.value.stored?.val === true && !isBetaEnabled(options, 'calculatedElementsOnWrite')) {
1170
+ const loc = [ elem.value.stored.location, elem ];
1171
+ message( 'def-unsupported-calc-elem', loc, { '#': 'on-write' } );
1172
+ }
1168
1173
  elem.$syntax = 'calc';
1169
1174
  }
1170
1175
  }