@sap/cds-compiler 2.7.0 → 2.11.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 (87) hide show
  1. package/CHANGELOG.md +167 -0
  2. package/bin/cdsc.js +42 -25
  3. package/bin/cdsse.js +1 -0
  4. package/doc/CHANGELOG_BETA.md +10 -0
  5. package/lib/api/.eslintrc.json +2 -0
  6. package/lib/api/main.js +17 -33
  7. package/lib/api/options.js +25 -13
  8. package/lib/api/validate.js +33 -9
  9. package/lib/backends.js +9 -8
  10. package/lib/base/dictionaries.js +2 -1
  11. package/lib/base/keywords.js +32 -2
  12. package/lib/base/message-registry.js +26 -2
  13. package/lib/base/messages.js +25 -9
  14. package/lib/base/model.js +5 -3
  15. package/lib/base/optionProcessorHelper.js +56 -22
  16. package/lib/checks/onConditions.js +5 -0
  17. package/lib/checks/selectItems.js +4 -0
  18. package/lib/checks/types.js +26 -2
  19. package/lib/checks/unknownMagic.js +41 -0
  20. package/lib/checks/validator.js +7 -2
  21. package/lib/compiler/assert-consistency.js +18 -5
  22. package/lib/compiler/base.js +65 -0
  23. package/lib/compiler/builtins.js +30 -1
  24. package/lib/compiler/checks.js +5 -2
  25. package/lib/compiler/definer.js +145 -120
  26. package/lib/compiler/index.js +16 -4
  27. package/lib/compiler/propagator.js +5 -2
  28. package/lib/compiler/resolver.js +207 -47
  29. package/lib/compiler/shared.js +47 -200
  30. package/lib/compiler/utils.js +173 -0
  31. package/lib/edm/annotations/genericTranslation.js +183 -187
  32. package/lib/edm/csn2edm.js +94 -98
  33. package/lib/edm/edm.js +16 -20
  34. package/lib/edm/edmPreprocessor.js +302 -115
  35. package/lib/edm/edmUtils.js +31 -12
  36. package/lib/gen/language.checksum +1 -1
  37. package/lib/gen/language.interp +28 -1
  38. package/lib/gen/language.tokens +79 -69
  39. package/lib/gen/languageLexer.interp +28 -1
  40. package/lib/gen/languageLexer.js +879 -805
  41. package/lib/gen/languageLexer.tokens +71 -62
  42. package/lib/gen/languageParser.js +5308 -4308
  43. package/lib/json/from-csn.js +59 -30
  44. package/lib/json/to-csn.js +354 -105
  45. package/lib/language/antlrParser.js +11 -0
  46. package/lib/language/errorStrategy.js +1 -0
  47. package/lib/language/genericAntlrParser.js +81 -14
  48. package/lib/language/language.g4 +163 -31
  49. package/lib/main.d.ts +136 -17
  50. package/lib/main.js +7 -1
  51. package/lib/model/api.js +78 -0
  52. package/lib/model/csnRefs.js +115 -32
  53. package/lib/model/csnUtils.js +71 -33
  54. package/lib/model/enrichCsn.js +36 -9
  55. package/lib/model/revealInternalProperties.js +20 -4
  56. package/lib/modelCompare/compare.js +2 -1
  57. package/lib/optionProcessor.js +33 -16
  58. package/lib/render/.eslintrc.json +3 -1
  59. package/lib/render/DuplicateChecker.js +1 -1
  60. package/lib/render/toCdl.js +60 -17
  61. package/lib/render/toHdbcds.js +122 -74
  62. package/lib/render/toSql.js +57 -32
  63. package/lib/render/utils/common.js +6 -10
  64. package/lib/sql-identifier.js +6 -1
  65. package/lib/transform/db/constraints.js +273 -119
  66. package/lib/transform/db/draft.js +9 -6
  67. package/lib/transform/db/expansion.js +19 -7
  68. package/lib/transform/db/flattening.js +31 -7
  69. package/lib/transform/db/transformExists.js +344 -66
  70. package/lib/transform/db/views.js +438 -0
  71. package/lib/transform/forHanaNew.js +65 -436
  72. package/lib/transform/forOdataNew.js +21 -10
  73. package/lib/transform/localized.js +2 -0
  74. package/lib/transform/odata/attachPath.js +19 -4
  75. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  76. package/lib/transform/odata/referenceFlattener.js +44 -38
  77. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  78. package/lib/transform/odata/structuralPath.js +72 -0
  79. package/lib/transform/odata/structureFlattener.js +13 -10
  80. package/lib/transform/odata/typesExposure.js +22 -12
  81. package/lib/transform/transformUtilsNew.js +55 -9
  82. package/lib/transform/translateAssocsToJoins.js +11 -17
  83. package/lib/transform/universalCsnEnricher.js +67 -0
  84. package/lib/utils/file.js +5 -3
  85. package/lib/utils/term.js +65 -42
  86. package/lib/utils/timetrace.js +48 -26
  87. package/package.json +1 -1
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ const { getVariableReplacement } = require('../model/csnUtils');
4
+
5
+ // We only care about the "wild" ones - $at is validated by the compiler
6
+ const magicVariables = {
7
+ $user: [
8
+ 'id', // $user.id
9
+ 'locale', // $user.locale
10
+ ],
11
+ $session: [
12
+ // no valid ways for this
13
+ ],
14
+ };
15
+
16
+ /**
17
+ * Check that the given ref does not use magic variables for which we don't have
18
+ * a valid way of rendering.
19
+ *
20
+ * Valid ways:
21
+ * - We know what to do -> $user.id on HANA
22
+ * - The user tells us what to do -> options.variableReplacements
23
+ *
24
+ * @param {object} parent Object with the ref as a property
25
+ * @param {string} name Name of the ref property on parent
26
+ * @param {Array} ref to check
27
+ */
28
+ function unknownMagicVariable(parent, name, ref) {
29
+ if (parent.$scope && parent.$scope === '$magic') {
30
+ const [ head, ...rest ] = ref;
31
+ const tail = rest.join('.');
32
+ const magicVariable = magicVariables[head];
33
+ if (magicVariable && magicVariable.indexOf(tail) === -1 &&
34
+ getVariableReplacement(ref, this.options) === null)
35
+ this.error(null, parent.$location, { id: tail, elemref: parent }, 'No configuration for magic variable was provided - path $(ELEMREF), step $(ID)');
36
+ }
37
+ }
38
+
39
+ module.exports = {
40
+ ref: unknownMagicVariable,
41
+ };
@@ -23,13 +23,17 @@ const {
23
23
  // both
24
24
  const { validateOnCondition, validateMixinOnCondition } = require('./onConditions');
25
25
  const validateForeignKeys = require('./foreignKeys');
26
- const { checkTypeDefinitionHasType, checkElementTypeDefinitionHasType, checkTypeIsScalar } = require('./types');
26
+ const {
27
+ checkTypeDefinitionHasType, checkElementTypeDefinitionHasType,
28
+ checkTypeIsScalar, checkDecimalScale,
29
+ } = require('./types');
27
30
  const { checkPrimaryKey, checkVirtualElement, checkManagedAssoc } = require('./elements');
28
31
  const checkForInvalidTarget = require('./invalidTarget');
29
32
  const { validateAssociationsInItems } = require('./arrayOfs');
30
33
  const checkQueryForNoDBArtifacts = require('./queryNoDbArtifacts');
31
34
  const checkExplicitlyNullableKeys = require('./nullableKeys');
32
35
  const nonexpandableStructuredInExpression = require('./nonexpandableStructured');
36
+ const unknownMagic = require('./unknownMagic');
33
37
  const managedWithoutKeys = require('./managedWithoutKeys');
34
38
 
35
39
  const forHanaMemberValidators
@@ -37,6 +41,7 @@ const forHanaMemberValidators
37
41
  // For HANA CDS specifically, reject any default parameter values, as these are not supported.
38
42
  rejectParamDefaultsInHanaCds,
39
43
  checkTypeIsScalar,
44
+ checkDecimalScale,
40
45
  checkExplicitlyNullableKeys,
41
46
  managedWithoutKeys,
42
47
  warnAboutDefaultOnAssociationForHanaCds,
@@ -50,7 +55,7 @@ const forHanaArtifactValidators
50
55
  checkForEmptyOrOnlyVirtual,
51
56
  ];
52
57
 
53
- const forHanaCsnValidators = [ nonexpandableStructuredInExpression ];
58
+ const forHanaCsnValidators = [ nonexpandableStructuredInExpression, unknownMagic ];
54
59
  /**
55
60
  * @type {Array<(query: CSN.Query, path: CSN.Path) => void>}
56
61
  */
@@ -98,6 +98,8 @@ function assertConsistency( model, stage ) {
98
98
  '$blocks',
99
99
  '$newfeatures',
100
100
  '$messageFunctions',
101
+ '$functions',
102
+ '$volatileFunctions',
101
103
  ],
102
104
  },
103
105
  ':parser': { // top-level from parser
@@ -246,7 +248,7 @@ function assertConsistency( model, stage ) {
246
248
  optional: [
247
249
  'name', '$parens', 'quantifier', 'mixin', 'excludingDict', 'columns', 'elements', '_deps',
248
250
  'where', 'groupBy', 'having', 'orderBy', '$orderBy', 'limit',
249
- '_projections', '_block', '_parent', '_main', '_effectiveType',
251
+ '_projections', '_block', '_parent', '_main', '_effectiveType', '$expand',
250
252
  '$tableAliases', 'kind', '_$next', '_combined', '$inlines',
251
253
  ],
252
254
  },
@@ -324,7 +326,7 @@ function assertConsistency( model, stage ) {
324
326
  requires: [ 'location' ],
325
327
  optional: [
326
328
  'path', 'elements', '_outer',
327
- 'scope', '_artifact', '$inferred',
329
+ 'scope', '_artifact', '$inferred', '$expand',
328
330
  '_effectiveType', // by propagation
329
331
  ],
330
332
  },
@@ -351,6 +353,7 @@ function assertConsistency( model, stage ) {
351
353
  $delimited: { parser: true, test: isBoolean },
352
354
  scope: { test: isScope },
353
355
  func: { test: TODO },
356
+ suffix: { test: TODO },
354
357
  kind: {
355
358
  isRequired: !stageParser && (() => true),
356
359
  // required to be set by Core Compiler even with parse errors
@@ -398,6 +401,7 @@ function assertConsistency( model, stage ) {
398
401
  optional: [
399
402
  'args',
400
403
  'func',
404
+ 'suffix',
401
405
  'quantifier',
402
406
  '$inferred',
403
407
  '$parens',
@@ -426,7 +430,7 @@ function assertConsistency( model, stage ) {
426
430
  struct: { inherits: 'val', test: isDictionary( definition ) }, // def because double @
427
431
  args: {
428
432
  inherits: 'value',
429
- optional: [ 'name', '$duplicate', '$expected' ],
433
+ optional: [ 'name', '$duplicate', '$expected', 'args', 'suffix' ],
430
434
  test: args,
431
435
  },
432
436
  on: { kind: true, inherits: 'value', test: expression },
@@ -477,6 +481,7 @@ function assertConsistency( model, stage ) {
477
481
  },
478
482
  items: {
479
483
  kind: true,
484
+ also: [ 0 ], // 0 for cyclic expansions
480
485
  requires: [ 'location' ],
481
486
  optional: [
482
487
  'enum',
@@ -533,7 +538,7 @@ function assertConsistency( model, stage ) {
533
538
  ],
534
539
  optional: [
535
540
  '_effectiveType', '$parens',
536
- '_deps',
541
+ '_deps', '$expand',
537
542
  // query specific
538
543
  'where', 'columns', 'mixin', 'quantifier', 'offset',
539
544
  'orderBy', '$orderBy', 'groupBy', 'excludingDict', 'having',
@@ -574,7 +579,11 @@ function assertConsistency( model, stage ) {
574
579
  $duplicates: { parser: true, kind: true, test: TODO }, // array of arts or true
575
580
  $extension: { kind: true, test: TODO }, // TODO: introduce $applied instead or $status
576
581
  $inferred: { parser: true, kind: true, test: isString },
577
- $expand: { kind: true, test: isString },
582
+
583
+ // Helper property for the XSN-to-CSN transformation, see function setExpandStatus():
584
+ // client, universal: render expanded elements? gensrc: produce annotate statements?
585
+ $expand: { kind: true, test: isString }, // TODO: rename it to $elementsExpand ?
586
+
578
587
  $autoexpose: { kind: [ 'entity' ], test: isBoolean, also: [ null, 'Composition' ] },
579
588
  $a2j: { kind: true, enumerable: true, test: TODO },
580
589
  $extra: { parser: true, test: TODO }, // for unexpected properties in CSN
@@ -582,6 +591,8 @@ function assertConsistency( model, stage ) {
582
591
  $sources: { parser: true, test: isArray( isString ) },
583
592
  $expected: { parser: true, test: isString },
584
593
  $messageFunctions: { test: TODO },
594
+ $functions: { test: TODO },
595
+ $volatileFunctions: { test: TODO },
585
596
  };
586
597
  let _noSyntaxErrors = null;
587
598
  assertProp( model, null, stageParser ? ':parser' : ':model', null, true );
@@ -653,6 +664,8 @@ function assertConsistency( model, stage ) {
653
664
  }
654
665
 
655
666
  function standard( node, parent, prop, spec, name ) {
667
+ if (spec.also && spec.also.includes( node ))
668
+ return;
656
669
  isObject( node, parent, prop, spec, name );
657
670
 
658
671
  const names = Object.getOwnPropertyNames( node );
@@ -0,0 +1,65 @@
1
+ // Base Definitions for the Core Compiler
2
+
3
+
4
+ 'use strict';
5
+
6
+ const dictKinds = {
7
+ definitions: 'absolute',
8
+ elements: 'element',
9
+ enum: 'enum',
10
+ foreignKeys: 'key',
11
+ actions: 'action',
12
+ params: 'param',
13
+ };
14
+
15
+ const kindProperties = {
16
+ // TODO: also foreignKeys ?
17
+ namespace: { artifacts: true }, // on-the-fly context
18
+ context: { artifacts: true, normalized: 'namespace' },
19
+ service: { artifacts: true, normalized: 'namespace' },
20
+ entity: { elements: true, actions: true, params: () => false },
21
+ select: { normalized: 'select', elements: true },
22
+ $join: { normalized: 'select' },
23
+ $tableAlias: { normalized: 'alias' }, // table alias in select
24
+ $self: { normalized: 'alias' }, // table alias in select
25
+ $navElement: { normalized: 'element' },
26
+ $inline: { normalized: 'element' }, // column with inline property
27
+ event: { elements: true },
28
+ type: { elements: propExists, enum: propExists },
29
+ aspect: { elements: propExists },
30
+ annotation: { elements: propExists, enum: propExists },
31
+ enum: { normalized: 'element' },
32
+ element: { elements: propExists, enum: propExists, dict: 'elements' },
33
+ mixin: { normalized: 'alias' },
34
+ action: {
35
+ params: () => false, elements: () => false, enum: () => false, dict: 'actions',
36
+ }, // no extend params, only annotate
37
+ function: {
38
+ params: () => false, elements: () => false, enum: () => false, normalized: 'action',
39
+ }, // no extend params, only annotate
40
+ key: { normalized: 'element' },
41
+ param: { elements: () => false, enum: () => false, dict: 'params' },
42
+ source: { artifacts: true }, // TODO -> $source
43
+ using: {},
44
+ extend: {
45
+ isExtension: true,
46
+ noDep: 'special',
47
+ elements: true, /* only for parse-cdl */
48
+ actions: true, /* only for parse-cdl */
49
+ },
50
+ annotate: {
51
+ isExtension: true, noDep: 'special', elements: true, enum: true, actions: true, params: true,
52
+ },
53
+ builtin: {}, // = CURRENT_DATE, TODO: improve
54
+ $parameters: {}, // $parameters in query entities
55
+ };
56
+
57
+ function propExists( prop, parent ) {
58
+ const obj = parent.returns || parent;
59
+ return (obj.items || obj.targetAspect || obj)[prop];
60
+ }
61
+
62
+ module.exports = {
63
+ dictKinds,
64
+ kindProperties,
65
+ };
@@ -4,7 +4,7 @@
4
4
 
5
5
  const { forEachInDict } = require('../base/dictionaries');
6
6
  const { builtinLocation } = require('../base/location');
7
- const { setProp } = require('../base/model');
7
+ const { setProp } = require('./utils');
8
8
 
9
9
  const core = {
10
10
  String: { parameters: [ 'length' ], category: 'string' },
@@ -83,6 +83,8 @@ const specialFunctions = {
83
83
  const magicVariables = {
84
84
  $user: {
85
85
  elements: { id: {}, locale: {} },
86
+ // Allow $user.<any>
87
+ $uncheckedElements: true,
86
88
  // Allow shortcut in CDL: `$user` becomes `$user.id` in CSN.
87
89
  $autoElement: 'id',
88
90
  }, // CDS-specific, not part of SQL
@@ -165,6 +167,31 @@ function isRelationTypeName(typeName) {
165
167
  return typeCategories.relation.includes(typeName);
166
168
  }
167
169
 
170
+ /**
171
+ * Checks whether the given absolute path is inside a reserved namespace.
172
+ *
173
+ * @param {string} absolute
174
+ * @returns {boolean}
175
+ */
176
+ function isInReservedNamespace(absolute) {
177
+ return absolute.startsWith( 'cds.') &&
178
+ !absolute.match(/^cds\.foundation(\.|$)/) &&
179
+ !absolute.match(/^cds\.outbox(\.|$)/); // Requested by Node runtime
180
+ }
181
+
182
+ /**
183
+ * Tell if a type is (directly) a builtin type
184
+ * Note that in CSN builtins are not in the definition of the model, so we can only
185
+ * check against their absolute names. Builtin types are "cds.<something>", i.e. they
186
+ * are directly in 'cds', but not for example in 'cds.foundation'.
187
+ *
188
+ * @param {string} type
189
+ * @returns {boolean}
190
+ */
191
+ function isBuiltinType(type) {
192
+ return typeof type === 'string' && isInReservedNamespace(type);
193
+ }
194
+
168
195
  /**
169
196
  * Add CDS builtins like the `cds` namespace with types like `cds.Integer` to
170
197
  * `definitions` of the XSN model as well as to `$builtins`.
@@ -267,6 +294,8 @@ module.exports = {
267
294
  functionsWithoutParens,
268
295
  specialFunctions,
269
296
  initBuiltins,
297
+ isInReservedNamespace,
298
+ isBuiltinType,
270
299
  isIntegerTypeName,
271
300
  isDecimalTypeName,
272
301
  isNumericTypeName,
@@ -15,7 +15,9 @@
15
15
 
16
16
  // const { hasArtifactTypeInformation } = require('../model/csnUtils')
17
17
  const builtins = require('../compiler/builtins');
18
- const { forEachGeneric, forEachDefinition, forEachMember } = require('../base/model');
18
+ const {
19
+ forEachGeneric, forEachDefinition, forEachMember,
20
+ } = require('../base/model');
19
21
 
20
22
  function check( model ) { // = XSN
21
23
  const {
@@ -630,8 +632,9 @@ function check( model ) { // = XSN
630
632
  * @returns {void}
631
633
  */
632
634
  function checkTokenStreamExpression(xpr, allowAssocTail) {
635
+ const args = Array.isArray(xpr.args) ? xpr.args : Object.values(xpr.args || {});
633
636
  // Check for illegal argument usage within the expression
634
- for (const arg of xpr.args || []) {
637
+ for (const arg of args) {
635
638
  if (isVirtualElement(arg))
636
639
  error(null, arg.location, 'Virtual elements can\'t be used in an expression');
637
640