@sap/cds-compiler 5.3.0 → 5.4.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 (50) hide show
  1. package/CHANGELOG.md +30 -2
  2. package/bin/cdsc.js +1 -1
  3. package/doc/CHANGELOG_BETA.md +2 -2
  4. package/lib/api/options.js +4 -2
  5. package/lib/base/builtins.js +0 -10
  6. package/lib/base/keywords.js +3 -31
  7. package/lib/base/message-registry.js +23 -5
  8. package/lib/base/messages.js +1 -1
  9. package/lib/checks/existsMustEndInAssoc.js +7 -2
  10. package/lib/checks/foreignKeys.js +12 -7
  11. package/lib/compiler/assert-consistency.js +11 -3
  12. package/lib/compiler/builtins.js +2 -0
  13. package/lib/compiler/checks.js +88 -38
  14. package/lib/compiler/define.js +2 -2
  15. package/lib/compiler/shared.js +9 -10
  16. package/lib/compiler/xpr-rewrite.js +11 -0
  17. package/lib/compiler/xsn-model.js +1 -1
  18. package/lib/edm/csn2edm.js +2 -0
  19. package/lib/edm/edm.js +2 -1
  20. package/lib/edm/edmPreprocessor.js +14 -1
  21. package/lib/edm/edmUtils.js +17 -2
  22. package/lib/gen/BaseParser.js +291 -197
  23. package/lib/gen/CdlParser.js +1631 -1605
  24. package/lib/gen/Dictionary.json +74 -6
  25. package/lib/gen/language.checksum +1 -1
  26. package/lib/gen/language.interp +1 -1
  27. package/lib/gen/languageParser.js +1808 -1804
  28. package/lib/language/antlrParser.js +8 -4
  29. package/lib/language/genericAntlrParser.js +3 -3
  30. package/lib/model/csnUtils.js +6 -1
  31. package/lib/optionProcessor.js +4 -0
  32. package/lib/parsers/AstBuildingParser.js +172 -108
  33. package/lib/parsers/CdlGrammar.g4 +154 -134
  34. package/lib/parsers/Lexer.js +3 -3
  35. package/lib/parsers/identifiers.js +59 -0
  36. package/lib/render/toCdl.js +5 -5
  37. package/lib/render/utils/common.js +5 -0
  38. package/lib/render/utils/delta.js +23 -5
  39. package/lib/transform/db/expansion.js +2 -1
  40. package/lib/transform/db/rewriteCalculatedElements.js +11 -5
  41. package/lib/transform/db/transformExists.js +52 -26
  42. package/lib/transform/effective/annotations.js +147 -0
  43. package/lib/transform/effective/main.js +17 -3
  44. package/lib/transform/forOdata.js +53 -10
  45. package/lib/transform/forRelationalDB.js +8 -1
  46. package/lib/transform/odata/createForeignKeys.js +180 -0
  47. package/lib/transform/odata/flattening.js +135 -19
  48. package/lib/transform/odata/typesExposure.js +4 -3
  49. package/lib/transform/transformUtils.js +6 -6
  50. package/package.json +1 -1
@@ -481,6 +481,7 @@ function fns( model ) {
481
481
  artifact[par] = args[i];
482
482
  }
483
483
  args = args.slice( parameters.length );
484
+ // TODO: we could issue syntax-unexpected-argument here
484
485
  }
485
486
  else if (args.length > 0 && !typeArtifact?.builtin) {
486
487
  // One or two arguments are interpreted as either length or precision/scale.
@@ -503,13 +504,10 @@ function fns( model ) {
503
504
  // Warn about left-over arguments.
504
505
  if (args.length > 0) {
505
506
  const loc = [ args[0].location, user ];
506
- if (typeArtifact?.builtin) {
507
+ if (typeArtifact?.builtin)
507
508
  message( 'type-ignoring-argument', loc, { art: typeArtifact } );
508
- }
509
- else if (options.testMode) {
510
- // Ensure: parser reports error and sets $typeArgs to undefined if more than 2 parameter
511
- throw new CompilerAssertion( 'More than 2 type arguments set by parser' );
512
- }
509
+ // when the parser exits rule unsuccessfully/prematurely, $typeArgs might
510
+ // still have a length > 2 → no testMode dump
513
511
  }
514
512
  artifact.$typeArgs = undefined;
515
513
  }
@@ -751,9 +749,10 @@ function fns( model ) {
751
749
  }
752
750
  case '$parameters': {
753
751
  // TODO: if ref.scope='param' is handled, test that here, too ?
754
- const { id } = path[1];
755
- message( 'ref-obsolete-parameters', [ head.location, user ],
756
- { code: `$parameters.${ id }`, newcode: `:${ id }` },
752
+ const id = path[1]?.id;
753
+ const code = id ? `$parameters.${ id }` : '$parameters';
754
+ const newcode = id ? `:${ id }` : ':‹param›';
755
+ message( 'ref-obsolete-parameters', [ head.location, user ], { code, newcode },
757
756
  'Obsolete $(CODE) - replace by $(NEWCODE)' );
758
757
  return art;
759
758
  }
@@ -1632,7 +1631,7 @@ function fns( model ) {
1632
1631
 
1633
1632
  for (let set = query._main.query; set.op; set = set.args[0]) {
1634
1633
  const right = set.args[1];
1635
- if (query.name.id >= (right._leadingQuery || right).name.id)
1634
+ if (query.name.id >= ((right._leadingQuery || right).name?.id ?? 0))
1636
1635
  return { txt: 'setQuery', op: set.op.val };
1637
1636
  }
1638
1637
  throw new CompilerAssertion( 'Did we pass the leading query as argument?' );
@@ -152,6 +152,7 @@ const {
152
152
  } = require('./utils');
153
153
  const { CompilerAssertion } = require('../base/error');
154
154
  const { isBetaEnabled } = require('../base/model');
155
+ const { isSimpleCdlIdentifier } = require('../parsers/identifiers');
155
156
 
156
157
  // Config object passed around all "rewrite" functions.
157
158
  class AnnoRewriteConfig {
@@ -319,6 +320,16 @@ function xprRewriteFns( model ) {
319
320
  }
320
321
  }
321
322
 
323
+ if (expr.$tokenTexts === true) {
324
+ // TODO: do not do with Universal-CSN (and gensrc, but that does not matter)
325
+ // We rewrite the string value for backward compatibility with "old-school" tools that
326
+ // only understand the string representation. But we only do so for simple strings, which
327
+ // is the case if this path expression has $tokenTexts. It then is of the form `@a: (path)`.
328
+ const isSimpleStep = step => !step.where && !step.args && isSimpleCdlIdentifier( step.id );
329
+ if (expr.path.every(isSimpleStep))
330
+ expr.$tokenTexts = expr.path.map( step => step.id ).join('.');
331
+ }
332
+
322
333
  if (model.options.testMode) {
323
334
  // re-resolve the modified path; all paths steps must match what we rewrote
324
335
  const ref = { ...expr, path: [ ...expr.path.map(item => ({ ...item })) ] };
@@ -19,7 +19,7 @@
19
19
 
20
20
  'use strict';
21
21
 
22
- class XsnSource {
22
+ class XsnSource { // TODO: should be subclass of XsnArtifact
23
23
  kind = 'source';
24
24
  location;
25
25
  usings = [];
@@ -328,6 +328,8 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
328
328
  href._jsonOnlyAttributes['Edm.String'] = 'https://cap.cloud.sap';
329
329
  const watermark = new Edm.Annotation(v, 'Core.Links', new Edm.Collection(v, new Edm.Record(v, rel, href)));
330
330
  // watermark._edmAttributes['Qualifier'] = 'CAP';
331
+ if (options.isV2())
332
+ watermark._xmlOnlyAttributes.xmlns = 'http://docs.oasis-open.org/odata/ns/edm';
331
333
  return watermark;
332
334
  }
333
335
 
package/lib/edm/edm.js CHANGED
@@ -1239,7 +1239,8 @@ function getEdm( options, messageFunctions ) {
1239
1239
  class Annotations extends AnnotationBase {
1240
1240
  constructor(version, target) {
1241
1241
  super(version, { Target: target });
1242
- this.setXml( { xmlns: this.v2 ? 'http://docs.oasis-open.org/odata/ns/edm' : undefined } );
1242
+ if (this.v2)
1243
+ this._xmlOnlyAttributes.xmlns = 'http://docs.oasis-open.org/odata/ns/edm';
1243
1244
  }
1244
1245
 
1245
1246
  toJSONattributes(json) {
@@ -107,9 +107,19 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
107
107
  /*
108
108
  Enrich the CSN by de-anonymizing and exposing types that are required to make the service self contained.
109
109
  */
110
+ // add cds.Map structured type into definitions to allow types exposure to pull in this type
111
+ if (options.isV4() && csn.definitions['cds.Map'] == null) {
112
+ const cdsMap = { '@open': true, elements: Object.create(null) };
113
+ setProp(cdsMap, '$emtpyMapType', true);
114
+ csn.definitions['cds.Map'] = cdsMap;
115
+ }
116
+
110
117
  const schemas = typesExposure(csn, whatsMyServiceRootName, requestedServiceNames,
111
118
  fallBackSchemaName, options, csnUtils, { error });
112
119
 
120
+ if (options.isV4() && csn.definitions['cds.Map']?.$emptyMapType)
121
+ csn.definitions['cds.Map'] = undefined;
122
+
113
123
  // Get an overview about all schemas (including the services)
114
124
  const schemaNames = [ ...serviceRootNames ];
115
125
  schemaNames.push(...Object.keys(schemas));
@@ -2159,7 +2169,10 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
2159
2169
 
2160
2170
 
2161
2171
  function mapCdsToEdmProp( obj ) {
2162
- if (obj.type && isBuiltinType(obj.type) && !obj.target && !obj.targetAspect) {
2172
+ if (edmUtils.convertMapToOpenStruct(obj, _options.odataVersion === 'v4')) {
2173
+ return;
2174
+ }
2175
+ else if (obj.type && isBuiltinType(obj.type) && !obj.target && !obj.targetAspect) {
2163
2176
  const edmType = edmUtils.mapCdsToEdmType(obj, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
2164
2177
  edmUtils.assignProp(obj, '_edmType', edmType);
2165
2178
  }
@@ -92,6 +92,7 @@ function isParameterizedEntity( artifact ) {
92
92
  // Return true if 'artifact' is structured (i.e. has elements, like a structured type or an entity)
93
93
  function isStructuredArtifact( artifact ) {
94
94
  // FIXME: No derived types etc yet
95
+ // FIXME: Don't forget cds.Map; maybe use csnUtils.isStructured()?
95
96
  return (artifact.items && artifact.items.elements || artifact.elements);
96
97
  }
97
98
 
@@ -553,8 +554,10 @@ function mapCdsToEdmType( csn, messageFunctions, isV2 = false, isMediaType = fal
553
554
  */
554
555
  }[cdsType];
555
556
  if (!edmType) {
556
- error('ref-unsupported-type', location, { type: cdsType, '#': 'odata' });
557
- edmType = 'Edm.PrimitiveType';
557
+ error('ref-unsupported-type', location,
558
+ { type: cdsType, version: (isV2 ? '2.0' : '4.0'), '#': 'odata' });
559
+ // return a version compatible type to avoid later compatibility failures
560
+ edmType = isV2 ? 'Edm.String' : 'Edm.PrimitiveType';
558
561
  }
559
562
 
560
563
 
@@ -873,8 +876,20 @@ function createSchemaRef( serviceRoots, targetSchemaName ) {
873
876
  }
874
877
  }
875
878
 
879
+ // convert cds.Map without elements into empty open struct for a (type) definition
880
+ function convertMapToOpenStruct( node, isV4 ) {
881
+ const typeDef = node.items || node;
882
+ if (node.kind && isV4 && typeDef.type === 'cds.Map' && typeDef.elements == null) {
883
+ typeDef.elements = Object.create(null);
884
+ typeDef.type = undefined;
885
+ assignAnnotation(node, '@open', true);
886
+ return true;
887
+ }
888
+ return false;
889
+ }
876
890
 
877
891
  module.exports = {
892
+ convertMapToOpenStruct,
878
893
  assignAnnotation,
879
894
  assignProp,
880
895
  createSchemaRef,