@sap/cds-compiler 5.5.2 → 5.7.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 (57) hide show
  1. package/CHANGELOG.md +43 -1
  2. package/bin/cdsse.js +4 -0
  3. package/bin/cdsv2m.js +2 -1
  4. package/doc/Versioning.md +4 -4
  5. package/lib/api/options.js +1 -0
  6. package/lib/base/builtins.js +2 -2
  7. package/lib/base/dictionaries.js +1 -2
  8. package/lib/base/keywords.js +3 -1
  9. package/lib/base/lazyload.js +1 -1
  10. package/lib/base/message-registry.js +170 -143
  11. package/lib/base/messages.js +69 -59
  12. package/lib/base/model.js +3 -3
  13. package/lib/base/node-helpers.js +17 -16
  14. package/lib/base/optionProcessorHelper.js +13 -14
  15. package/lib/base/shuffle.js +4 -1
  16. package/lib/checks/structuredAnnoExpressions.js +1 -1
  17. package/lib/compiler/assert-consistency.js +1 -1
  18. package/lib/compiler/builtins.js +4 -1
  19. package/lib/compiler/extend.js +20 -5
  20. package/lib/compiler/resolve.js +45 -9
  21. package/lib/compiler/shared.js +1 -0
  22. package/lib/edm/annotations/edmJson.js +3 -3
  23. package/lib/edm/annotations/genericTranslation.js +5 -1
  24. package/lib/edm/annotations/vocabularyDefinitions.js +8 -2
  25. package/lib/edm/edmUtils.js +2 -1
  26. package/lib/gen/BaseParser.js +142 -103
  27. package/lib/gen/CdlParser.js +2240 -2201
  28. package/lib/gen/Dictionary.json +185 -6
  29. package/lib/json/from-csn.js +2 -0
  30. package/lib/json/to-csn.js +13 -4
  31. package/lib/language/docCommentParser.js +11 -5
  32. package/lib/language/errorStrategy.js +3 -3
  33. package/lib/language/genericAntlrParser.js +2 -0
  34. package/lib/model/csnUtils.js +6 -1
  35. package/lib/optionProcessor.js +5 -1
  36. package/lib/parsers/AstBuildingParser.js +200 -86
  37. package/lib/parsers/CdlGrammar.g4 +142 -86
  38. package/lib/parsers/Lexer.js +5 -3
  39. package/lib/parsers/index.js +1 -1
  40. package/lib/render/toCdl.js +6 -5
  41. package/lib/render/toHdbcds.js +1 -1
  42. package/lib/render/toSql.js +5 -3
  43. package/lib/render/utils/common.js +19 -6
  44. package/lib/render/utils/standardDatabaseFunctions.js +576 -0
  45. package/lib/transform/addTenantFields.js +2 -1
  46. package/lib/transform/db/expansion.js +3 -0
  47. package/lib/transform/db/flattening.js +18 -77
  48. package/lib/transform/db/groupByOrderBy.js +2 -2
  49. package/lib/transform/db/rewriteCalculatedElements.js +14 -19
  50. package/lib/transform/db/temporal.js +2 -1
  51. package/lib/transform/forOdata.js +1 -1
  52. package/lib/transform/odata/adaptAnnotationRefs.js +79 -0
  53. package/lib/transform/odata/createForeignKeys.js +25 -9
  54. package/lib/transform/odata/flattening.js +11 -1
  55. package/lib/transform/transformUtils.js +20 -85
  56. package/package.json +2 -1
  57. package/bin/cds_update_annotations.js +0 -180
@@ -82,7 +82,7 @@ const expWithFilter = [ 'from', 'expand', 'inline' ];
82
82
  // exception in case of an error, but push the corresponding error object to
83
83
  // that property (should be a vector).
84
84
  function resolve( model ) {
85
- // const { options } = model;
85
+ const { options } = model;
86
86
  // Get shared functionality and the message function:
87
87
  const {
88
88
  info, warning, error, message,
@@ -90,6 +90,7 @@ function resolve( model ) {
90
90
  const {
91
91
  resolvePath,
92
92
  resolveDefinitionName,
93
+ attachAndEmitValidNames,
93
94
  traverseExpr,
94
95
  effectiveType,
95
96
  getOrigin,
@@ -103,7 +104,7 @@ function resolve( model ) {
103
104
  } );
104
105
 
105
106
  const ignoreSpecifiedElements
106
- = isDeprecatedEnabled( model.options, 'ignoreSpecifiedQueryElements' );
107
+ = isDeprecatedEnabled( options, 'ignoreSpecifiedQueryElements' );
107
108
 
108
109
  return doResolve();
109
110
 
@@ -127,7 +128,7 @@ function resolve( model ) {
127
128
  // Phase 4: resolve all artifacts:
128
129
  forEachDefinition( model, resolveRefs );
129
130
  forEachGeneric( model, 'vocabularies', resolveRefs );
130
- if (model.options.lspMode) {
131
+ if (options.lspMode) {
131
132
  for (const name in model.sources)
132
133
  resolveDefinitionName( model.sources[name].namespace );
133
134
  }
@@ -405,7 +406,7 @@ function resolve( model ) {
405
406
  const allowedInMain = [ 'entity', 'aspect', 'event' ].includes( adHocOrMainKind( art ) );
406
407
  const isTopLevelElement = parent && (parent.kind !== 'element' || parent.targetAspect);
407
408
 
408
- if (model.options.lspMode && art.name && !art._main)
409
+ if (options.lspMode && art.name && !art._main)
409
410
  resolveDefinitionName( art );
410
411
 
411
412
  // Check KEY (TODO: make this an extra function)
@@ -551,9 +552,11 @@ function resolve( model ) {
551
552
  addForeignKeyNavigations( art );
552
553
  }
553
554
 
554
- resolveExpr( art.default, 'default', art );
555
+ resolveExprWithEnum( art.default, 'default', art );
556
+
555
557
  // TODO: distinguish not by $syntax (it is semantics), but whether in query
556
- resolveExpr( art.value, (art.$syntax === 'calc' ? 'calc' : 'column'), art );
558
+ const valueCtx = (art.$syntax === 'calc') ? 'calc' : 'column';
559
+ resolveExprWithEnum( art.value, valueCtx, art );
557
560
  if (art.type?.$inferred === 'cast')
558
561
  inferTypePropertiesFromCast( art );
559
562
  if (art.value) {
@@ -612,7 +615,7 @@ function resolve( model ) {
612
615
  // If specified type is `null`, type could not be resolved.
613
616
  else if (!compToAssoc && sType && sType !== iType &&
614
617
  // Special case for $recompilation: allow one level of type indirection. See #12113.
615
- (!model.options.$recompile || sType !== iType.type?._artifact)) {
618
+ (!options.$recompile || sType !== iType.type?._artifact)) {
616
619
  const typeName = !iTypeArt && 'typeExtra' || // no inferred type prop
617
620
  iType?.name && sType?.name && 'typeName' || // both types are named
618
621
  'type'; // unknown type names
@@ -1391,8 +1394,13 @@ function resolve( model ) {
1391
1394
 
1392
1395
  function resolveExprInAnnotations( art ) {
1393
1396
  for (const anno in art) {
1394
- if (anno.charAt(0) === '@')
1397
+ if (anno.charAt(0) === '@') {
1398
+ const { name } = art[anno];
1399
+ const annoDef = model.vocabularies?.[name.id];
1400
+ if (annoDef)
1401
+ setLink( name, '_artifact', annoDef );
1395
1402
  resolveAnnoExpr( art[anno], art );
1403
+ }
1396
1404
  }
1397
1405
  }
1398
1406
 
@@ -1400,7 +1408,7 @@ function resolve( model ) {
1400
1408
  if (expr.$tokenTexts) {
1401
1409
  if (!anno.kind)
1402
1410
  initAnnotationForExpression( anno, art );
1403
- resolveExpr( expr, 'annotation', anno );
1411
+ resolveExprWithEnum( expr, 'annotation', anno );
1404
1412
  }
1405
1413
  else if (expr.literal === 'array') {
1406
1414
  expr.val.forEach( val => resolveAnnoExpr( val, art, anno ) );
@@ -1428,6 +1436,34 @@ function resolve( model ) {
1428
1436
  // Might be useful for future recursive types.
1429
1437
  }
1430
1438
 
1439
+ function resolveExprWithEnum( expr, exprCtx, art ) {
1440
+ traverseExpr( expr, exprCtx, art, resolveExprItem );
1441
+ const sym = expr?.sym;
1442
+ const assignment = art;
1443
+ // Check enum as top-level annotation value:
1444
+ if (art?.kind === '$annotation')
1445
+ art = art === expr && art.name._artifact;
1446
+ if (!art)
1447
+ return;
1448
+ const symbols = effectiveType( art )?.enum;
1449
+ if (!sym || !symbols)
1450
+ return;
1451
+ // TODO: or warning if enum symbol but non-enum type?
1452
+ if (symbols[sym.id]) {
1453
+ setLink( sym, '_artifact', symbols[sym.id] );
1454
+ }
1455
+ else {
1456
+ let type = art._effectiveType;
1457
+ // inferred enums can't be extended (yet): show underlying enum
1458
+ while (type.enum[$inferred])
1459
+ type = getOrigin( type );
1460
+ const err = message( 'ref-undefined-enum', [ sym.location, assignment ],
1461
+ { id: sym.id, type } );
1462
+ if (options.newParser)
1463
+ attachAndEmitValidNames( err, symbols );
1464
+ }
1465
+ }
1466
+
1431
1467
  function resolveExpr( expr, exprCtx, user ) {
1432
1468
  traverseExpr( expr, exprCtx, user, resolveExprItem );
1433
1469
  }
@@ -1172,6 +1172,7 @@ function fns( model ) {
1172
1172
  // TODO: if it becomes non-configurable, we can omit this warning
1173
1173
  let id = pathName( path );
1174
1174
  let head = path[0]._artifact || { _parent: art };
1175
+ // eslint-disable-next-line sonarjs/no-nested-assignment
1175
1176
  while ((head = head?._parent) && head.kind === 'builtin')
1176
1177
  id = `${ head.name.id }.${ id }`;
1177
1178
  const msgId = (art.$uncheckedElements) ? 'ref-unknown-var' : 'ref-undefined-var';
@@ -538,8 +538,8 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
538
538
  }
539
539
  else {
540
540
  const facetVal = facetArg.args[0].val;
541
- const isNaN = Number.isNaN(Number.parseInt(facetVal, 10));
542
- if (isNaN && options.isV4() && facetName === 'Scale' && facetVal !== 'variable') {
541
+ const isNan = Number.isNaN(Number.parseInt(facetVal, 10));
542
+ if (isNan && options.isV4() && facetName === 'Scale' && facetVal !== 'variable') {
543
543
  error('odata-anno-xpr-args', location, {
544
544
  anno,
545
545
  op: `${facetFuncName}(…)`,
@@ -548,7 +548,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
548
548
  '#': 'wrongval_meta_list',
549
549
  });
550
550
  }
551
- else if (isNaN && facetName !== 'Scale') {
551
+ else if (isNan && facetName !== 'Scale') {
552
552
  error('odata-anno-xpr-args', location, {
553
553
  anno, op: `${facetFuncName}(…)`, meta: 'number', '#': 'wrongval_meta',
554
554
  });
@@ -64,7 +64,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
64
64
  // Crawl over the csn and trigger the annotation translation for all kinds
65
65
  // of annotated things.
66
66
  // Note: only works for single service
67
- // Note: we assume that all objects ly flat in the service, i.e. objName always
67
+ // Note: we assume that all objects lie flat in the service, i.e. objName always
68
68
  // looks like <service name, can contain dots>.<id>
69
69
  forEachDefinition(reqDefs, (def, defName) => {
70
70
  if (defName === serviceName || defName.startsWith(`${serviceName}.`)) {
@@ -1345,6 +1345,10 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
1345
1345
  // if DataFieldAbstract is expected and no explicit type is provided, automatically choose DataField
1346
1346
  if (dTypeName === 'UI.DataFieldAbstract')
1347
1347
  actualTypeName = 'UI.DataField';
1348
+ // if SemanticObjectMappingAbstract is expected and no explicit type
1349
+ // is provided, automatically choose SemanticObjectMappingType
1350
+ else if (dTypeName === 'Common.SemanticObjectMappingAbstract')
1351
+ actualTypeName = 'Common.SemanticObjectMappingType';
1348
1352
 
1349
1353
  else
1350
1354
  actualTypeName = dTypeName;
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  /*
4
- OASIS: https://github.com/oasis-tcs/odata-vocabularies/tree/master/vocabularies
4
+ OASIS: https://github.com/oasis-tcs/odata-vocabularies/tree/main/vocabularies
5
5
  Aggregation (published)
6
6
  Authorization (published)
7
7
  Capabilities (published)
@@ -12,12 +12,13 @@
12
12
  Temporal (published)
13
13
  Validation (published)
14
14
 
15
- SAP: https://github.com/SAP/odata-vocabularies/tree/master/vocabularies
15
+ SAP: https://github.com/SAP/odata-vocabularies/tree/main/vocabularies
16
16
  Analytics (published)
17
17
  CodeList (published)
18
18
  Common (published)
19
19
  Communication (published)
20
20
  DataIntegration (published)
21
+ EntityRelationship (experimental)
21
22
  Graph (published, experimental)
22
23
  Hierarchy (published, experimental)
23
24
  HTML5 (published, experimental)
@@ -75,6 +76,11 @@ const vocabularyDefinitions = {
75
76
  inc: { Alias: 'DataIntegration', Namespace: 'com.sap.vocabularies.DataIntegration.v1' },
76
77
  int: { filename: 'DataIntegration.xml' },
77
78
  },
79
+ EntityRelationship: {
80
+ ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/EntityRelationship.xml' },
81
+ inc: { Alias: 'EntityRelationship', Namespace: 'com.sap.vocabularies.EntityRelationship.v1' },
82
+ int: { filename: 'EntityRelationship.xml' },
83
+ },
78
84
  Graph: {
79
85
  ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Graph.xml' },
80
86
  inc: { Alias: 'Graph', Namespace: 'com.sap.vocabularies.Graph.v1' },
@@ -866,8 +866,9 @@ function createSchemaRef( serviceRoots, targetSchemaName ) {
866
866
  function path4( def, _path = def['@path'] ) {
867
867
  if (_path)
868
868
  return _path.replace(/^\//, '');
869
+ const last = def.name.split('.').at(-1); // > my.very.CatalogService --> CatalogService
869
870
  return ( // generate one from the service's name
870
- /[^.]+$/.exec(def.name)[0] // > my.very.CatalogService --> CatalogService
871
+ last
871
872
  .replace(/Service$/, '') // > CatalogService --> Catalog
872
873
  .replace(/([a-z0-9])([A-Z])/g, (_, c, C) => `${c}-${C.toLowerCase()}`) // > ODataFooBarX9 --> odata-foo-bar-x9
873
874
  .replace(/_/g, '-') // > foo_bar_baz --> foo-bar-baz