@sap/cds-compiler 6.9.2 → 7.0.1

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 (69) hide show
  1. package/CHANGELOG.md +86 -2
  2. package/bin/cdsc.js +4 -33
  3. package/doc/IncompatibleChanges_v7.md +639 -0
  4. package/lib/api/main.js +4 -56
  5. package/lib/api/options.js +6 -15
  6. package/lib/api/validate.js +1 -0
  7. package/lib/base/builtins.js +1 -2
  8. package/lib/base/csnRefs.js +2 -6
  9. package/lib/base/message-registry.js +82 -76
  10. package/lib/base/messages.js +23 -4
  11. package/lib/base/optionProcessor.js +2 -72
  12. package/lib/base/specialOptions.js +20 -17
  13. package/lib/checks/defaultValues.js +1 -39
  14. package/lib/checks/hasPersistedElements.js +19 -3
  15. package/lib/checks/parameters.js +0 -34
  16. package/lib/checks/selectItems.js +2 -38
  17. package/lib/checks/typeParameters.js +162 -0
  18. package/lib/checks/validator.js +5 -8
  19. package/lib/compiler/assert-consistency.js +19 -5
  20. package/lib/compiler/checks.js +47 -43
  21. package/lib/compiler/define.js +6 -6
  22. package/lib/compiler/extend.js +102 -111
  23. package/lib/compiler/generate.js +4 -8
  24. package/lib/compiler/populate.js +4 -7
  25. package/lib/compiler/propagator.js +9 -9
  26. package/lib/compiler/resolve.js +205 -7
  27. package/lib/compiler/shared.js +76 -82
  28. package/lib/compiler/tweak-assocs.js +102 -22
  29. package/lib/compiler/utils.js +57 -12
  30. package/lib/compiler/xpr-rewrite.js +2 -15
  31. package/lib/edm/annotations/edmJson.js +14 -10
  32. package/lib/edm/annotations/genericTranslation.js +3 -1
  33. package/lib/edm/annotations/preprocessAnnotations.js +9 -26
  34. package/lib/edm/csn2edm.js +27 -20
  35. package/lib/edm/edmUtils.js +25 -0
  36. package/lib/gen/CdlGrammar.checksum +1 -1
  37. package/lib/gen/CdlParser.js +2237 -2241
  38. package/lib/gen/Dictionary.json +17 -2
  39. package/lib/json/from-csn.js +67 -52
  40. package/lib/json/to-csn.js +28 -25
  41. package/lib/language/textUtils.js +0 -13
  42. package/lib/main.d.ts +34 -59
  43. package/lib/main.js +1 -1
  44. package/lib/model/csnUtils.js +9 -8
  45. package/lib/parsers/AstBuildingParser.js +45 -55
  46. package/lib/parsers/Lexer.js +2 -0
  47. package/lib/parsers/identifiers.js +0 -9
  48. package/lib/render/toCdl.js +41 -40
  49. package/lib/render/toSql.js +8 -1
  50. package/lib/render/utils/common.js +1 -1
  51. package/lib/render/utils/sql.js +2 -3
  52. package/lib/tool-lib/enrichCsn.js +1 -2
  53. package/lib/transform/db/applyTransformations.js +7 -5
  54. package/lib/transform/db/assertUnique.js +8 -51
  55. package/lib/transform/db/associations.js +1 -1
  56. package/lib/transform/db/cdsPersistence.js +1 -15
  57. package/lib/transform/db/expansion.js +9 -12
  58. package/lib/transform/db/flattening.js +1 -1
  59. package/lib/transform/db/groupByOrderBy.js +0 -16
  60. package/lib/transform/db/views.js +57 -161
  61. package/lib/transform/draft/db.js +2 -2
  62. package/lib/transform/forOdata.js +25 -14
  63. package/lib/transform/forRelationalDB.js +93 -301
  64. package/lib/transform/localized.js +33 -102
  65. package/lib/transform/odata/flattening.js +11 -2
  66. package/lib/transform/transformUtils.js +25 -3
  67. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -2
  68. package/package.json +2 -2
  69. package/lib/render/toHdbcds.js +0 -1810
@@ -9,17 +9,17 @@ const enrichCsn = require('./enricher');
9
9
 
10
10
  // forRelationalDB
11
11
  const { validateSelectItems } = require('./selectItems');
12
- const { rejectParamDefaultsInHanaCds, warnAboutDefaultOnAssociationForHanaCds } = require('./defaultValues');
12
+ const { validateDefaultValues } = require('./defaultValues');
13
13
  const validateCdsPersistenceAnnotation = require('./cdsPersistence');
14
14
  const navigationIntoMany = require('./manyNavigations');
15
15
  const expandToMany = require('./manyExpand');
16
16
  const checkUsedTypesForAnonymousAspectComposition = require('./managedInType');
17
- const validateHasPersistedElements = require('./hasPersistedElements');
17
+ const { validateHasPersistedElements, validateQueryHasPersistedElements } = require('./hasPersistedElements');
18
18
  const checkForHanaTypes = require('./checkForTypes');
19
19
  const { checkAnnotationExpression } = require('./structuredAnnoExpressions');
20
20
  const checkForParams = require('./parameters');
21
+ const checkTypeParameters = require('./typeParameters');
21
22
  // forOdata
22
- const { validateDefaultValues } = require('./defaultValues');
23
23
  const { checkActionOrFunction } = require('./actionsFunctions');
24
24
  const {
25
25
  checkCoreMediaTypeAllowance, checkAnalytics,
@@ -57,18 +57,16 @@ const { timetrace } = require('../utils/timetrace');
57
57
 
58
58
  const forRelationalDBMemberValidators
59
59
  = [
60
- // For HANA CDS specifically, reject any default parameter values, as these are not supported.
61
- rejectParamDefaultsInHanaCds,
62
60
  checkTypeIsScalar,
63
61
  checkDecimalScale,
64
62
  checkExplicitlyNullableKeys,
65
63
  managedWithoutKeys,
66
- warnAboutDefaultOnAssociationForHanaCds,
67
64
  // sql.prepend/append
68
65
  checkSqlAnnotationOnElement,
69
66
  // no temporal annotations on calc elements
70
67
  rejectAnnotationsOnCalcElement,
71
68
  checkElementTypeDefinitionHasType,
69
+ checkTypeParameters,
72
70
  ];
73
71
 
74
72
  const forRelationalDBArtifactValidators = [
@@ -100,6 +98,7 @@ const forRelationalDBQueryValidators = [
100
98
  // TODO reason why this is forRelationalDB exclusive
101
99
  validateSelectItems,
102
100
  checkQueryForNoDBArtifacts,
101
+ validateQueryHasPersistedElements,
103
102
  ];
104
103
 
105
104
  const forOdataMemberValidators
@@ -214,8 +213,6 @@ function getDBCsnValidators( options ) {
214
213
  */
215
214
  function forRelationalDB( csn, that ) {
216
215
  const memberValidators = [ ...forRelationalDBMemberValidators, ...commonMemberValidators ];
217
- if (that.options.transformation === 'hdbcds')
218
- memberValidators.push(checkForParams.memberValidator);
219
216
  // skip artifacts / elements which are not persisted on the database from being validated
220
217
  const iterateOptions = {
221
218
  skipArtifact: artifact => artifact.abstract ||
@@ -110,6 +110,7 @@ function assertConsistency( model, stage ) {
110
110
  '$functions',
111
111
  '$assert',
112
112
  '_sortedSources',
113
+ '_associations', // backlink associations
113
114
  ],
114
115
  },
115
116
  ':parser': { // top-level from parser
@@ -268,6 +269,7 @@ function assertConsistency( model, stage ) {
268
269
  also: [ false ],
269
270
  },
270
271
  $keysNavigation: { kind: true, test: TODO },
272
+ $backlink: { kind: true, test: TODO },
271
273
  $filtered: { kind: true, inherits: 'value' }, // for assoc+filter
272
274
  $enclosed: { kind: true, inherits: 'value' }, // for comp+filter
273
275
  params: { kind: true, inherits: 'definitions' },
@@ -375,7 +377,7 @@ function assertConsistency( model, stage ) {
375
377
  requires: [ 'location' ],
376
378
  optional: [
377
379
  'path', 'elements', '_outer', '_parent', '_main', '_block', 'kind',
378
- 'scope', '_artifact', '$inferred', '$expand', '$inCycle',
380
+ 'scope', '_artifact', '$inferred', '$expand', '$inCycle', '$backlink',
379
381
  '$tableAliases', '_$next',
380
382
  '_origin', '_effectiveType', '$effectiveSeqNo', '_extensions', '$contains',
381
383
  ],
@@ -429,7 +431,7 @@ function assertConsistency( model, stage ) {
429
431
  kind: [ 'entity', 'view', 'type', 'aspect' ],
430
432
  test: isString, // CSN parser should check for 'entity', 'view', 'projection'
431
433
  },
432
- $tokenTexts: {
434
+ $tokenTexts: { // TODO: rename to $isExpression
433
435
  parser: true,
434
436
  test: isStringOrBool,
435
437
  },
@@ -498,7 +500,7 @@ function assertConsistency( model, stage ) {
498
500
  'literal', 'val', 'sym', 'struct', 'variant', 'path', 'name', '$duplicates', 'upTo',
499
501
  // expressions as annotation values
500
502
  '$tokenTexts', 'op', 'args', 'func', '_artifact', 'type', '$typeArgs',
501
- 'scale', 'srid', 'length', 'precision', 'scope', '$parens',
503
+ 'scale', 'srid', 'length', 'precision', 'scope', '$parens', '$errorReported',
502
504
  '_block', '_outer', // for annotation assignments
503
505
  ],
504
506
  // TODO: restrict path to #simplePath
@@ -567,9 +569,20 @@ function assertConsistency( model, stage ) {
567
569
  expectedKind: { kind: [ 'extend' ], test: locationVal( isString ) },
568
570
  virtual: { kind: true, test: locationVal() },
569
571
  key: { kind: true, test: locationVal(), also: [ null, undefined ] },
570
- masked: { kind: true, test: locationVal() },
571
572
  notNull: { kind: true, test: locationVal() },
572
- includes: { kind: true, inherits: 'type', test: isArray() },
573
+ includes: {
574
+ kind: true,
575
+ test: isArray(),
576
+ inherits: 'type',
577
+ optional: [
578
+ 'path', 'scope', '_artifact', '$inferred', '$postponeInclude',
579
+ ],
580
+ },
581
+ $postponeInclude: {
582
+ kind: true,
583
+ parser: true,
584
+ test: isNumber,
585
+ },
573
586
  returns: {
574
587
  kind: [ 'action', 'function' ],
575
588
  requires: [ 'kind', 'location' ],
@@ -677,6 +690,7 @@ function assertConsistency( model, stage ) {
677
690
  _projections: { kind: true, test: TODO },
678
691
  _complexProjections: { kind: true, test: TODO }, // for projected paths with filters
679
692
  _entities: { test: TODO },
693
+ _associations: { test: TODO },
680
694
  $compositionTargets: { test: isDictionary( isBoolean ) },
681
695
  $collectedExtensions: { test: TODO },
682
696
  _upperAspects: { kind: [ 'type', 'entity' ], test: isArray( TODO ) },
@@ -565,7 +565,8 @@ function check( model ) {
565
565
  }
566
566
 
567
567
  function checkDefaultValue( art ) {
568
- if (!art._effectiveType)
568
+ const effective = art._effectiveType;
569
+ if (!effective)
569
570
  return;
570
571
  if (art.kind !== 'element' && art.kind !== 'type' && art.kind !== 'param')
571
572
  return;
@@ -590,21 +591,37 @@ function check( model ) {
590
591
  }
591
592
  }
592
593
 
593
- const isMap = art._effectiveType?.name.id === 'cds.Map';
594
- if (isMap) {
595
- error( 'type-unexpected-default', [ defaultValue.location, art ], {
596
- '#': 'map', keyword: 'default', type: 'cds.Map',
597
- } );
594
+ if (effective.elements) {
595
+ const kind = art.type?._artifact?.kind;
596
+ const isEntity = (kind === 'entity' || kind === '$self');
597
+ if (isEntity || Object.keys( effective.elements ).length !== 1) {
598
+ message( 'type-unexpected-default-struct', [ defaultValue.location, art ], {
599
+ '#': (isEntity ? kind : art.kind), keyword: 'default',
600
+ }, {
601
+ std: 'Unexpected $(KEYWORD) for a structure',
602
+ // eslint-disable-next-line @stylistic/max-len
603
+ param: 'Unexpected $(KEYWORD) for a structured parameter with not exactly one sub element',
604
+ // eslint-disable-next-line @stylistic/max-len
605
+ type: 'Unexpected $(KEYWORD) for a structured type definition with not exactly one element',
606
+ // eslint-disable-next-line @stylistic/max-len
607
+ element: 'Unexpected $(KEYWORD) for a structured element with not exactly one sub element',
608
+ $self: 'Unexpected $(KEYWORD) for a binding parameter',
609
+ entity: 'Unexpected $(KEYWORD) for an entity-typed parameter',
610
+ } );
611
+ }
598
612
  }
599
- else if (art._effectiveType?.elements) {
600
- // TODO: error for v7
601
- warning( 'type-unexpected-default-struct', [ defaultValue.location, art ], {
602
- '#': art.kind, keyword: 'default',
613
+ else if (effective.items) {
614
+ const kind = art.items?.type?._artifact?.kind;
615
+ message( 'type-unexpected-default-array', [ defaultValue.location, art ], {
616
+ '#': (kind === '$self' ? kind : 'std'), keyword: 'default',
603
617
  }, {
604
- std: 'Unexpected $(KEYWORD) for a structure',
605
- param: 'Unexpected $(KEYWORD) for a structured parameter',
606
- type: 'Unexpected $(KEYWORD) for a structured type definition',
607
- element: 'Unexpected $(KEYWORD) for a structured element',
618
+ std: 'Unexpected $(KEYWORD) for an array',
619
+ $self: 'Unexpected $(KEYWORD) for a binding parameter',
620
+ } );
621
+ }
622
+ else if (effective.name.id === 'cds.Map') {
623
+ error( 'type-unexpected-default', [ defaultValue.location, art ], {
624
+ '#': 'map', keyword: 'default', type: 'cds.Map',
608
625
  } );
609
626
  }
610
627
  }
@@ -1008,30 +1025,11 @@ function check( model ) {
1008
1025
  return false;
1009
1026
  }
1010
1027
 
1011
- /**
1012
- * Returns true if the given annotation accepts expressions as values.
1013
- *
1014
- * @param {object} anno
1015
- * @param {XSN.Artifact} art
1016
- * @returns {boolean}
1017
- */
1018
- function checkAnnotationAcceptsExpressions( anno, art ) {
1019
- const name = anno.name?.id;
1020
- if (!name)
1021
- return true;
1022
- if (!propagationRules[`@${ name }`] || acceptsExprValues[`@${ name }`])
1023
- return true;
1024
- error( 'anno-unexpected-expr', [ anno.location, art ], { anno: name },
1025
- 'Unexpected expression as value for $(ANNO)' );
1026
- return false;
1027
- }
1028
-
1029
1028
  function checkAnnotationAssignment1( art, anno ) {
1030
1029
  const name = anno.name?.id;
1031
- if (art.$contains?.$annotation && anno.kind === '$annotation' && anno._outer) {
1032
- if (checkAnnotationAcceptsExpressions( anno, art ))
1033
- checkAnnotationExpressions( anno, art );
1034
- }
1030
+ const forbid = name && propagationRules[`@${ name }`] &&
1031
+ !acceptsExprValues[`@${ name }`];
1032
+ checkAnnotationExpressions( anno, art, forbid && name );
1035
1033
 
1036
1034
  // Has been slightly adapted for model.vocabularies but comments need to be
1037
1035
  // adapted, etc.
@@ -1142,16 +1140,22 @@ function check( model ) {
1142
1140
  /**
1143
1141
  * Check the expressions inside annotations.
1144
1142
  */
1145
- function checkAnnotationExpressions( anno, art ) {
1146
- if (anno.$tokenTexts) {
1147
- checkGenericExpression( anno, art, null, 'anno' );
1143
+ function checkAnnotationExpressions( anno, art, forbid ) {
1144
+ if (!anno.sym && anno.$tokenTexts) {
1145
+ if (forbid) {
1146
+ error( 'anno-unexpected-expr', [ anno.location, art ], { anno: forbid },
1147
+ 'Unexpected expression as value for $(ANNO)' );
1148
+ }
1149
+ else {
1150
+ checkGenericExpression( anno, art, null, 'anno' );
1151
+ }
1148
1152
  }
1149
1153
  else if (anno.literal === 'array') {
1150
- anno.val.forEach( val => checkAnnotationExpressions( val, art ) );
1154
+ anno.val.forEach( val => checkAnnotationExpressions( val, art, forbid ) );
1151
1155
  }
1152
1156
  else if (anno.literal === 'struct') {
1153
1157
  const struct = Object.values(anno.struct);
1154
- struct.forEach(val => checkAnnotationExpressions( val, art ));
1158
+ struct.forEach(val => checkAnnotationExpressions( val, art, forbid ));
1155
1159
  }
1156
1160
  }
1157
1161
 
@@ -1246,8 +1250,8 @@ function check( model ) {
1246
1250
  if (value.literal === 'enum') {
1247
1251
  if (expectedEnum) {
1248
1252
  // Enum symbol provided and expected
1249
- if (!expectedEnum[value.sym.id]) {
1250
- // ... but no such constant
1253
+ if (!expectedEnum[value.sym.id] && value.sym._artifact === undefined) {
1254
+ // ... but no such constant and not yet reported
1251
1255
  warning( null, loc, { id: `#${ value.sym.id }`, anno }, 'Enum symbol $(ID) not found in enum for annotation $(ANNO)' );
1252
1256
  }
1253
1257
  }
@@ -213,9 +213,9 @@ function define( model ) {
213
213
  addI18nBlocks(); // TODO: part of extend.js?
214
214
 
215
215
  const { $self } = model.definitions;
216
- if ($self && $self.kind !== 'namespace') { // TODO v7: non-config error
217
- message( 'name-deprecated-$self', [ $self.name.location, $self ], { name: '$self' },
218
- 'Do not use $(NAME) as name for an artifact definition' );
216
+ if ($self && $self.kind !== 'namespace') {
217
+ error( 'name-deprecated-$self', [ $self.name.location, $self ], { name: '$self' },
218
+ 'Do not use $(NAME) as name for an artifact definition' );
219
219
  }
220
220
  }
221
221
 
@@ -279,7 +279,7 @@ function define( model ) {
279
279
 
280
280
  function addMainArtifact( art, block, prefix ) {
281
281
  setLink( art, '_block', block );
282
- initExprAnnoBlock( art, block );
282
+ initExprAnnoBlock( art, block, error ); // TODO: why also here?
283
283
  art.name.id ??= prefix + pathName( art.name.path );
284
284
  const absolute = art.name.id;
285
285
  // TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
@@ -394,7 +394,7 @@ function define( model ) {
394
394
 
395
395
  function addExtension( ext, block ) {
396
396
  setLink( ext, '_block', block );
397
- initExprAnnoBlock( ext, block );
397
+ initExprAnnoBlock( ext, block, error );
398
398
  const absolute = ext.name && resolveUncheckedPath( ext.name, '_uncheckedExtension', ext );
399
399
  if (!absolute) // broken path
400
400
  return;
@@ -1048,7 +1048,7 @@ function define( model ) {
1048
1048
  function initMembers( parent ) {
1049
1049
  // TODO: combine with initMembers() - better structuring
1050
1050
  const block = parent._block;
1051
- initExprAnnoBlock( parent, block );
1051
+ initExprAnnoBlock( parent, block, error );
1052
1052
  const obj = initItemsLinks( parent, block ); // down many / array of
1053
1053
 
1054
1054
  if (obj.scale && !obj.precision &&