@sap/cds-compiler 6.1.0 → 6.3.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 (90) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/bin/cdsc.js +17 -6
  3. package/bin/cdsse.js +1 -1
  4. package/bin/cdsv2m.js +1 -1
  5. package/lib/api/main.js +29 -7
  6. package/lib/api/options.js +1 -1
  7. package/lib/base/builtins.js +9 -0
  8. package/lib/base/keywords.js +1 -1
  9. package/lib/base/message-registry.js +41 -10
  10. package/lib/base/messages.js +13 -6
  11. package/lib/base/model.js +1 -1
  12. package/lib/base/optionProcessorHelper.js +7 -2
  13. package/lib/checks/assocOutsideService.js +17 -30
  14. package/lib/checks/checkForTypes.js +0 -18
  15. package/lib/checks/checkPathsInStoredCalcElement.js +2 -1
  16. package/lib/checks/featureFlags.js +4 -1
  17. package/lib/checks/onConditions.js +2 -2
  18. package/lib/checks/queryNoDbArtifacts.js +16 -15
  19. package/lib/checks/types.js +1 -1
  20. package/lib/checks/utils.js +30 -6
  21. package/lib/checks/validator.js +4 -5
  22. package/lib/compiler/assert-consistency.js +3 -1
  23. package/lib/compiler/base.js +1 -1
  24. package/lib/compiler/builtins.js +1 -1
  25. package/lib/compiler/checks.js +85 -39
  26. package/lib/compiler/define.js +24 -5
  27. package/lib/compiler/extend.js +1 -1
  28. package/lib/compiler/finalize-parse-cdl.js +9 -1
  29. package/lib/compiler/generate.js +4 -4
  30. package/lib/compiler/index.js +88 -6
  31. package/lib/compiler/lsp-api.js +2 -0
  32. package/lib/compiler/populate.js +8 -8
  33. package/lib/compiler/propagator.js +1 -1
  34. package/lib/compiler/resolve.js +22 -21
  35. package/lib/compiler/shared.js +6 -6
  36. package/lib/compiler/tweak-assocs.js +53 -31
  37. package/lib/compiler/utils.js +9 -16
  38. package/lib/compiler/xpr-rewrite.js +2 -2
  39. package/lib/gen/BaseParser.js +35 -29
  40. package/lib/gen/CdlGrammar.checksum +1 -1
  41. package/lib/gen/CdlParser.js +1424 -1430
  42. package/lib/gen/Dictionary.json +1 -2
  43. package/lib/gen/cdlKeywords.json +26 -0
  44. package/lib/inspect/inspectPropagation.js +1 -1
  45. package/lib/json/from-csn.js +2 -2
  46. package/lib/json/to-csn.js +1 -1
  47. package/lib/language/multiLineStringParser.js +1 -1
  48. package/lib/model/cloneCsn.js +1 -0
  49. package/lib/model/csnRefs.js +9 -4
  50. package/lib/model/csnUtils.js +67 -2
  51. package/lib/optionProcessor.js +9 -9
  52. package/lib/parsers/AstBuildingParser.js +28 -26
  53. package/lib/parsers/identifiers.js +2 -30
  54. package/lib/render/toCdl.js +73 -13
  55. package/lib/render/toSql.js +127 -108
  56. package/lib/render/utils/common.js +4 -2
  57. package/lib/render/utils/sql.js +67 -0
  58. package/lib/transform/addTenantFields.js +4 -4
  59. package/lib/transform/db/assertUnique.js +2 -1
  60. package/lib/transform/db/associations.js +37 -1
  61. package/lib/transform/db/assocsToQueries/transformExists.js +21 -32
  62. package/lib/transform/db/assocsToQueries/utils.js +1 -1
  63. package/lib/transform/db/cdsPersistence.js +1 -1
  64. package/lib/transform/db/expansion.js +37 -36
  65. package/lib/transform/db/killAnnotations.js +1 -0
  66. package/lib/transform/db/processSqlServices.js +20 -2
  67. package/lib/transform/draft/db.js +20 -20
  68. package/lib/transform/draft/odata.js +38 -40
  69. package/lib/transform/effective/associations.js +1 -1
  70. package/lib/transform/effective/flattening.js +40 -47
  71. package/lib/transform/effective/main.js +6 -4
  72. package/lib/transform/forOdata.js +201 -92
  73. package/lib/transform/forRelationalDB.js +151 -142
  74. package/lib/transform/localized.js +116 -109
  75. package/lib/transform/odata/adaptAnnotationRefs.js +21 -16
  76. package/lib/transform/odata/createForeignKeys.js +73 -70
  77. package/lib/transform/odata/flattening.js +216 -200
  78. package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +47 -45
  79. package/lib/transform/odata/toFinalBaseType.js +40 -39
  80. package/lib/transform/odata/typesExposure.js +151 -133
  81. package/lib/transform/odata/utils.js +7 -6
  82. package/lib/transform/parseExpr.js +165 -162
  83. package/lib/transform/transformUtils.js +184 -551
  84. package/lib/transform/translateAssocsToJoins.js +511 -596
  85. package/lib/transform/tupleExpansion.js +495 -0
  86. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
  87. package/lib/utils/moduleResolve.js +1 -1
  88. package/package.json +2 -2
  89. package/lib/base/cleanSymbols.js +0 -17
  90. package/lib/checks/nonexpandableStructured.js +0 -39
@@ -1,13 +1,14 @@
1
1
  'use strict';
2
2
 
3
- const { forEachDefinition, forEachMemberRecursively,
3
+ const {
4
+ forEachDefinition, forEachMemberRecursively,
4
5
  getServiceNames, applyAnnotationsFromExtensions,
5
- transformAnnotationExpression } = require('../../model/csnUtils');
6
+ transformAnnotationExpression,
7
+ } = require('../../model/csnUtils');
6
8
  const { forEach } = require('../../utils/objectUtils');
7
9
  const { isArtifactInSomeService, getServiceOfArtifact } = require('../odata/utils');
8
10
  const { getTransformers } = require('../transformUtils');
9
11
  const { makeMessageFunction } = require('../../base/messages');
10
- const { isBetaEnabled } = require('../../base/model');
11
12
 
12
13
  /**
13
14
  * - Generate artificial draft fields if requested
@@ -59,7 +60,7 @@ function generateDrafts( csn, options, services, messageFunctions ) {
59
60
  const filterDict = Object.create(null);
60
61
 
61
62
  // validate the 'DRAFT.DraftAdministrativeData_DraftMessage' type if already present in the model
62
- if (isBetaEnabled(options, 'draftMessages') || options.draftMessages) {
63
+ if (options.draftMessages) {
63
64
  const draftAdminDataMessagesType = csn.definitions['DRAFT.DraftAdministrativeData_DraftMessage'];
64
65
  if (draftAdminDataMessagesType && !isValidDraftAdminDataMessagesType(draftAdminDataMessagesType)) {
65
66
  error(null, [ 'definitions', 'DRAFT.DraftAdministrativeData_DraftMessage' ], { name: 'DRAFT.DraftAdministrativeData_DraftMessage' },
@@ -98,7 +99,7 @@ function generateDrafts( csn, options, services, messageFunctions ) {
98
99
  artifact.actions && artifact.actions.draftPrepare)
99
100
  return;
100
101
 
101
- if(!visitedArtifacts[artifactName])
102
+ if (!visitedArtifacts[artifactName])
102
103
  visitedArtifacts[artifactName] = artifact;
103
104
 
104
105
  const draftPrepare = createAction('draftPrepare', artifactName, 'SideEffectsQualifier', 'cds.String');
@@ -118,7 +119,7 @@ function generateDrafts( csn, options, services, messageFunctions ) {
118
119
 
119
120
  // Generate the DraftAdministrativeData projection into the service, unless there is already one
120
121
  // @ts-ignore
121
- const draftAdminDataProjectionName = `${getServiceOfArtifact(artifactName, services)}.DraftAdministrativeData`;
122
+ const draftAdminDataProjectionName = `${ getServiceOfArtifact(artifactName, services) }.DraftAdministrativeData`;
122
123
  let draftAdminDataProjection = csn.definitions[draftAdminDataProjectionName];
123
124
  if (!draftAdminDataProjection) {
124
125
  // @ts-ignore
@@ -180,19 +181,15 @@ function generateDrafts( csn, options, services, messageFunctions ) {
180
181
  // ... on SiblingEntity.IsActiveEntity != IsActiveEntity ...
181
182
  siblingEntity.SiblingEntity.on = createAssociationPathComparison('SiblingEntity', 'IsActiveEntity', '!=', 'IsActiveEntity');
182
183
 
183
- if (isBetaEnabled(options, 'draftMessages') || options.draftMessages) {
184
+ if (options.draftMessages) {
184
185
  const draftMessages = { DraftMessages: { '@Core.Computed': true, virtual: true, items: { type: 'DRAFT.DraftAdministrativeData_DraftMessage' } } };
185
186
  addElement(draftMessages, artifact, artifactName);
186
187
 
187
188
  if (!artifact['@Common.SideEffects#alwaysFetchMessages'] && artifact['@Common.SideEffects#alwaysFetchMessages'] !== null) {
188
- setAnnotation(artifact, '@Common.SideEffects#alwaysFetchMessages.SourceEntities', ['']);
189
- setAnnotation(artifact, '@Common.SideEffects#alwaysFetchMessages.TargetProperties', ['DraftMessages'] );
189
+ setAnnotation(artifact, '@Common.SideEffects#alwaysFetchMessages.SourceEntities', [ '' ]);
190
+ setAnnotation(artifact, '@Common.SideEffects#alwaysFetchMessages.TargetProperties', [ 'DraftMessages' ] );
190
191
  }
191
- setAnnotation(artifact, '@Common.Messages', { '=': 'DraftMessages', ref: ['DraftMessages'] });
192
- setAnnotationAddressViaNavigationPath(artifactName, services);
193
- }
194
-
195
- if (options.addAnnotationAddressViaNavigationPath) {
192
+ setAnnotation(artifact, '@Common.Messages', { '=': 'DraftMessages', ref: [ 'DraftMessages' ] });
196
193
  setAnnotationAddressViaNavigationPath(artifactName, services);
197
194
  }
198
195
 
@@ -249,50 +246,51 @@ function generateDrafts( csn, options, services, messageFunctions ) {
249
246
  }
250
247
  }
251
248
 
252
- /*
253
- * After draft decoration, all visited artifacts are supposed to have the draft state elements
254
- * Is/HasActiveEntity, HasDraftEntity. Now, $draft.<postfix> (with postfix defined as magic variable
255
- * in the core compiler builtins) needs to be translated into $self.<postfix>.
256
- *
257
- * It has to be processed after the late 'applyAnnotationsFromExtensions' which could also merge in
258
- * some $draft path expressions.
249
+ /*
250
+ * After draft decoration, all visited artifacts are supposed to have the draft state elements
251
+ * Is/HasActiveEntity, HasDraftEntity. Now, $draft.<postfix> (with postfix defined as magic variable
252
+ * in the core compiler builtins) needs to be translated into $self.<postfix>.
253
+ *
254
+ * It has to be processed after the late 'applyAnnotationsFromExtensions' which could also merge in
255
+ * some $draft path expressions.
259
256
  */
260
257
  function rewriteDollarDraft() {
261
-
262
258
  function $draft2$self(member) {
263
- Object.keys(member).forEach(pn => {
264
- if(pn[0] === '@') {
259
+ Object.keys(member).forEach((pn) => {
260
+ if (pn[0] === '@') {
265
261
  transformAnnotationExpression(member, pn, {
266
- ref: (_parent, _prop, xpr, _path, _p, _ppn, ctx) => {
267
- if(xpr[0] === '$draft') {
268
- xpr[0] = '$self';
269
- if(ctx?.annoExpr?.['='])
270
- ctx.annoExpr['='] = true;
271
- }
262
+ ref: (_parent, _prop, xpr, _path, _p, _ppn, ctx) => {
263
+ if (xpr[0] === '$draft') {
264
+ xpr[0] = '$self';
265
+ if (ctx?.annoExpr?.['='])
266
+ ctx.annoExpr['='] = true;
272
267
  }
273
268
  },
274
- );
269
+ });
275
270
  }
276
271
  });
277
272
  }
278
273
 
279
274
  // entity parameters are not substituted as the EDM param entity is not draft enabled
280
- Object.entries(visitedArtifacts).forEach(([artName, art]) => {
275
+ Object.entries(visitedArtifacts).forEach(([ artName, art ]) => {
281
276
  $draft2$self(art);
282
- forEachMemberRecursively(art, $draft2$self,
277
+ forEachMemberRecursively(
278
+ art, $draft2$self,
283
279
  [ 'definitions', artName ],
284
280
  true, { elementsOnly: true }
285
281
  );
286
- if(art.actions) {
287
- Object.entries(art.actions).forEach(([actionName, action]) => {
282
+ if (art.actions) {
283
+ Object.entries(art.actions).forEach(([ actionName, action ]) => {
288
284
  $draft2$self(action);
289
- forEachMemberRecursively(action, $draft2$self,
290
- [ 'definitions', artName, 'actions', actionName ]);
291
- if(action.returns)
285
+ forEachMemberRecursively(
286
+ action, $draft2$self,
287
+ [ 'definitions', artName, 'actions', actionName ]
288
+ );
289
+ if (action.returns)
292
290
  $draft2$self(action.returns);
293
- })
291
+ });
294
292
  }
295
- })
293
+ });
296
294
  }
297
295
 
298
296
  // Set the @Common.AddressViaNavigationPath annotation to the service of
@@ -43,7 +43,7 @@ function turnAssociationsIntoUnmanaged( csn, options, csnUtils, messageFunctions
43
43
  orderBy: expandManagedToFksInArray(true),
44
44
  }, [], { allowArtifact: artifact => artifact.query !== undefined || artifact.projection !== undefined });
45
45
 
46
- forEachDefinition(csn, associations.getFKAccessFinalizer(csn, csnUtils, '_', true));
46
+ forEachDefinition(csn, associations.getFKAccessFinalizer(csn, options, csnUtils, '_', true));
47
47
  applyTransformations(csn, {
48
48
  elements: (_parent, prop, elements, path) => {
49
49
  forEachMember(_parent, (element, elementName, _prop, elPath) => {
@@ -55,54 +55,47 @@ function flattenRefs(csn, options, csnUtils, messageFunctions) {
55
55
  const refFlattener = getStructStepsFlattener(csn, options, messageFunctions, resolved, '_', adaptRefs);
56
56
 
57
57
  forEachDefinition(csn, (def, defName) => {
58
- if (def.kind === 'entity') {
59
- applyTransformationsOnNonDictionary(csn.definitions, defName, refFlattener, { processAnnotations: true, skipDict: { actions: 1 } }, [ 'definitions' ]);
60
-
61
- adaptRefs.forEach(fn => fn());
62
- adaptRefs.length = 0;
63
-
64
- // explicit binding parameter of bound action
65
- if (def.actions) {
66
- const special$self = !csn?.definitions?.$self && '$self';
67
- Object.entries(def.actions).forEach(([ an, a ]) => {
68
- if (a.params) {
69
- const params = Object.entries(a.params);
70
- const firstParam = params[0][1];
71
- const type = firstParam?.items?.type || firstParam?.type;
72
- if (type === special$self) {
73
- const bindingParamName = params[0][0];
74
- const markBindingParam = {
75
- ref: (parent, prop, xpr) => {
76
- if ((xpr[0].id || xpr[0]) === bindingParamName)
77
- setProp(parent, '$bparam', true);
78
- },
79
- };
80
-
81
- Object.keys(a)
82
- .filter(pn => pn.startsWith('@') && a[pn])
83
- .forEach((pn) => {
84
- transformAnnotationExpression(a, pn, [ markBindingParam, refFlattener ], [ 'definitions', defName, 'actions', an ]);
85
- adaptRefs.forEach(fn => fn(true, 1, parent => parent.$bparam));
86
- adaptRefs.length = 0;
87
- });
88
-
89
-
90
- forEachMemberRecursively(a, (member, memberName, prop, path) => {
91
- Object.keys(member).filter(pn => pn.startsWith('@') && member[pn]).forEach((pn) => {
92
- transformAnnotationExpression(member, pn, [ markBindingParam, refFlattener ], path);
93
- adaptRefs.forEach(fn => fn(true, 1, parent => parent.$bparam));
94
- adaptRefs.length = 0;
95
- });
96
- }, [ 'definitions', defName, 'actions', an ]);
97
- }
58
+ applyTransformationsOnNonDictionary(csn.definitions, defName, refFlattener, { processAnnotations: true, skipDict: { actions: 1 } }, [ 'definitions' ]);
59
+
60
+ adaptRefs.forEach(fn => fn());
61
+ adaptRefs.length = 0;
62
+
63
+ // explicit binding parameter of bound action
64
+ if (def.actions) {
65
+ const special$self = !csn?.definitions?.$self && '$self';
66
+ Object.entries(def.actions).forEach(([ an, a ]) => {
67
+ if (a.params) {
68
+ const params = Object.entries(a.params);
69
+ const firstParam = params[0][1];
70
+ const type = firstParam?.items?.type || firstParam?.type;
71
+ if (type === special$self) {
72
+ const bindingParamName = params[0][0];
73
+ const markBindingParam = {
74
+ ref: (parent, prop, xpr) => {
75
+ if ((xpr[0].id || xpr[0]) === bindingParamName)
76
+ setProp(parent, '$bparam', true);
77
+ },
78
+ };
79
+
80
+ Object.keys(a)
81
+ .filter(pn => pn.startsWith('@') && a[pn])
82
+ .forEach((pn) => {
83
+ transformAnnotationExpression(a, pn, [ markBindingParam, refFlattener ], [ 'definitions', defName, 'actions', an ]);
84
+ adaptRefs.forEach(fn => fn(true, 1, parent => parent.$bparam));
85
+ adaptRefs.length = 0;
86
+ });
87
+
88
+
89
+ forEachMemberRecursively(a, (member, memberName, prop, path) => {
90
+ Object.keys(member).filter(pn => pn.startsWith('@') && member[pn]).forEach((pn) => {
91
+ transformAnnotationExpression(member, pn, [ markBindingParam, refFlattener ], path);
92
+ adaptRefs.forEach(fn => fn(true, 1, parent => parent.$bparam));
93
+ adaptRefs.length = 0;
94
+ });
95
+ }, [ 'definitions', defName, 'actions', an ]);
98
96
  }
99
- });
100
- }
101
- }
102
- else {
103
- applyTransformationsOnNonDictionary(csn.definitions, defName, refFlattener, { processAnnotations: false }, [ 'definitions' ]);
104
- adaptRefs.forEach(fn => fn());
105
- adaptRefs.length = 0;
97
+ }
98
+ });
106
99
  }
107
100
  });
108
101
 
@@ -39,10 +39,12 @@ function effectiveCsn( model, options, messageFunctions ) {
39
39
  messageFunctions.setModel(csn);
40
40
 
41
41
  const transformerUtils = transformUtils.getTransformers(csn, options, messageFunctions, '_');
42
- const { expandStructsInExpression } = transformerUtils;
43
42
  const redoProjections = queries.projectionToSELECTAndAddColumns(csn);
44
43
 
45
- let csnUtils = getUtils(csn, 'init-all');
44
+ // re-use from transformers; otherwise we have two caches and tuple expansion
45
+ // would use a different one than handleExists, leading to unnecessary re-initialization
46
+ let { csnUtils } = transformerUtils;
47
+ csnUtils.initAllDefinitions();
46
48
 
47
49
  // Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
48
50
  const cleanup = validate.forRelationalDB(csn, {
@@ -53,13 +55,13 @@ function effectiveCsn( model, options, messageFunctions ) {
53
55
  rewriteCalculatedElementsInViews(csn, options, csnUtils, '_', messageFunctions);
54
56
 
55
57
  // Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
56
- handleExists(csn, options, messageFunctions.error, csnUtils.inspectRef, csnUtils.initDefinition, csnUtils.dropDefinitionCache);
58
+ handleExists(csn, options, messageFunctions, csnUtils);
57
59
 
58
60
  // Check if structured elements and managed associations are compared in an expression
59
61
  // and expand these structured elements. This tuple expansion allows all other
60
62
  // subsequent procession steps to see plain paths in expressions.
61
63
  // If errors are detected, throwWithAnyError() will return from further processing
62
- expandStructsInExpression(csn, { drillRef: true });
64
+ transformerUtils.expandStructsInExpression({ drillRef: true });
63
65
 
64
66
  messageFunctions.throwWithAnyError();
65
67