@sap/cds-compiler 6.2.2 → 6.3.4

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 (63) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/bin/cdsc.js +11 -4
  3. package/lib/api/options.js +1 -1
  4. package/lib/base/message-registry.js +36 -7
  5. package/lib/base/messages.js +11 -4
  6. package/lib/base/model.js +0 -1
  7. package/lib/checks/assocOutsideService.js +17 -30
  8. package/lib/checks/checkForTypes.js +0 -18
  9. package/lib/checks/checkPathsInStoredCalcElement.js +2 -1
  10. package/lib/checks/enricher.js +15 -3
  11. package/lib/checks/onConditions.js +2 -2
  12. package/lib/checks/queryNoDbArtifacts.js +16 -15
  13. package/lib/checks/types.js +1 -1
  14. package/lib/checks/utils.js +30 -6
  15. package/lib/checks/validator.js +36 -37
  16. package/lib/compiler/assert-consistency.js +1 -1
  17. package/lib/compiler/checks.js +47 -18
  18. package/lib/compiler/extend.js +1 -1
  19. package/lib/compiler/index.js +88 -6
  20. package/lib/compiler/populate.js +1 -1
  21. package/lib/compiler/resolve.js +7 -7
  22. package/lib/compiler/tweak-assocs.js +48 -25
  23. package/lib/edm/annotations/edmJson.js +19 -19
  24. package/lib/gen/BaseParser.js +1 -1
  25. package/lib/gen/CdlGrammar.checksum +1 -1
  26. package/lib/gen/CdlParser.js +384 -383
  27. package/lib/gen/Dictionary.json +0 -2
  28. package/lib/json/to-csn.js +3 -2
  29. package/lib/model/csnRefs.js +9 -4
  30. package/lib/model/csnUtils.js +67 -2
  31. package/lib/optionProcessor.js +2 -3
  32. package/lib/parsers/AstBuildingParser.js +12 -11
  33. package/lib/render/toCdl.js +10 -4
  34. package/lib/render/utils/common.js +4 -2
  35. package/lib/transform/db/assertUnique.js +2 -1
  36. package/lib/transform/db/associations.js +37 -1
  37. package/lib/transform/db/assocsToQueries/transformExists.js +21 -32
  38. package/lib/transform/db/assocsToQueries/utils.js +1 -1
  39. package/lib/transform/db/cdsPersistence.js +1 -1
  40. package/lib/transform/db/expansion.js +37 -36
  41. package/lib/transform/draft/db.js +20 -20
  42. package/lib/transform/draft/odata.js +38 -40
  43. package/lib/transform/effective/associations.js +1 -1
  44. package/lib/transform/effective/flattening.js +40 -47
  45. package/lib/transform/effective/main.js +6 -4
  46. package/lib/transform/forOdata.js +135 -115
  47. package/lib/transform/forRelationalDB.js +151 -142
  48. package/lib/transform/localized.js +116 -109
  49. package/lib/transform/odata/adaptAnnotationRefs.js +21 -16
  50. package/lib/transform/odata/createForeignKeys.js +73 -70
  51. package/lib/transform/odata/flattening.js +216 -200
  52. package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +47 -45
  53. package/lib/transform/odata/toFinalBaseType.js +40 -39
  54. package/lib/transform/odata/typesExposure.js +151 -133
  55. package/lib/transform/odata/utils.js +7 -6
  56. package/lib/transform/parseExpr.js +165 -162
  57. package/lib/transform/transformUtils.js +184 -551
  58. package/lib/transform/translateAssocsToJoins.js +510 -571
  59. package/lib/transform/tupleExpansion.js +495 -0
  60. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
  61. package/package.json +1 -1
  62. package/lib/base/cleanSymbols.js +0 -17
  63. package/lib/checks/nonexpandableStructured.js +0 -39
@@ -38,18 +38,16 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
38
38
  columns: (parent, name, columns, path) => {
39
39
  const artifact = csn.definitions[path[1]];
40
40
  csnUtils.initDefinition(artifact); // potentially not initialized, yet
41
- if (!artifact['@cds.persistence.table']) {
42
- const root = csnUtils.get$combined({ SELECT: parent });
43
- // TODO: replace with the correct options.transformation?
44
- // Do not expand the * in OData for a moment, not to introduce changes
45
- // while the OData CSN is still official
46
- const isComplexQuery = parent.from.join !== undefined;
47
- if (!options.toOdata)
48
- parent.columns = replaceStar(root, columns, parent.excluding, isComplexQuery);
49
- // FIXME(v6): Remove argument "isComplexOrNestedQuery"; we use path.length > 4 to check
50
- // if we're inside the outermost "columns". If so, always prepend a table alias. See #11662
51
- parent.columns = expand(parent.columns, path.concat('columns'), true, isComplexQuery || path.length > 4);
52
- }
41
+ const root = csnUtils.get$combined({ SELECT: parent });
42
+ // TODO: replace with the correct options.transformation?
43
+ // Do not expand the * in OData for a moment, not to introduce changes
44
+ // while the OData CSN is still official
45
+ const isComplexQuery = parent.from.join !== undefined;
46
+ if (!options.toOdata)
47
+ parent.columns = replaceStar(root, columns, parent.excluding, isComplexQuery);
48
+ // FIXME(v6): Remove argument "isComplexOrNestedQuery"; we use path.length > 4 to check
49
+ // if we're inside the outermost "columns". If so, always prepend a table alias. See #11662
50
+ parent.columns = expand(parent.columns, path.concat('columns'), true, isComplexQuery || path.length > 4);
53
51
  },
54
52
  groupBy: (parent, name, groupBy, path) => {
55
53
  parent.groupBy = expand(groupBy, path.concat('groupBy'));
@@ -86,24 +84,22 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
86
84
  // (which is the thing inside SET/SELECT)
87
85
  // We can directly use SELECT here, as only projections and SELECT can have .columns
88
86
  const root = csnUtils.get$combined({ SELECT: parent });
89
- if (!artifact['@cds.persistence.table']) {
90
- // Make root look like normal .elements - we never cared about conflict afaik anyway
91
- Object.keys(root).forEach((key) => {
92
- root[key] = root[key][0].element;
87
+ // Make root look like normal .elements - we never cared about conflict afaik anyway
88
+ Object.keys(root).forEach((key) => {
89
+ root[key] = root[key][0].element;
90
+ });
91
+ const rewritten = rewrite(root, parent.columns, parent.excluding);
92
+ /*
93
+ * Do not remove unexpandable many columns in OData
94
+ */
95
+ if (rewritten.toMany.length > 0 && !options.toOdata) {
96
+ markAsToDummify(artifact, path[1]);
97
+ rewritten.toMany.forEach(({ art }) => {
98
+ error( null, art.$path || [ 'definitions', path[1] ], { name: `${ art.$env || path[1] }:${ art.ref.map(r => r.id || r) }` }, 'Unexpected .expand with to-many association $(NAME)');
93
99
  });
94
- const rewritten = rewrite(root, parent.columns, parent.excluding);
95
- /*
96
- * Do not remove unexpandable many columns in OData
97
- */
98
- if (rewritten.toMany.length > 0 && !options.toOdata) {
99
- markAsToDummify(artifact, path[1]);
100
- rewritten.toMany.forEach(({ art }) => {
101
- error( null, art.$path || [ 'definitions', path[1] ], { name: `${ art.$env || path[1] }:${ art.ref.map(r => r.id || r) }` }, 'Unexpected .expand with to-many association $(NAME)');
102
- });
103
- }
104
- else {
105
- parent.columns = rewritten.columns;
106
- }
100
+ }
101
+ else {
102
+ parent.columns = rewritten.columns;
107
103
  }
108
104
  },
109
105
  });
@@ -280,11 +276,13 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
280
276
  const allToMany = [];
281
277
  const newThing = [];
282
278
  const containsExpandInline = columns.some(col => col.expand || col.inline);
283
- if (containsExpandInline) // Replace stars - needs to happen before resolving .expand/.inline since the .expand/.inline first path step affects the root *
284
- columns = replaceStar(root, columns, excluding);
285
- else
279
+ if (!containsExpandInline)
286
280
  return { columns, toMany: [] };
287
281
 
282
+ // Replace stars - needs to happen before resolving .expand/.inline since the
283
+ // .expand/.inline first path step affects the root *
284
+ columns = replaceStar(root, columns, excluding);
285
+
288
286
  for (const col of columns) {
289
287
  if (col.expand || col.inline) {
290
288
  const { expanded, toManys } = expandInline(root, col, col.ref || [], col.expand ? [ dbName(col) ] : []);
@@ -364,7 +362,10 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
364
362
  }
365
363
  else if (current.on || current.cast?.on) {
366
364
  rewriteOn(current, [ currentAlias.slice(0, -1).join(pathDelimiter) ]);
367
- expanded.push(Object.assign({}, current, { ref: currentRef, as: currentAlias.join(pathDelimiter) } ));
365
+ const expandedCol = Object.assign({}, current, { as: currentAlias.join(pathDelimiter) } );
366
+ if (currentRef.length)
367
+ expandedCol.ref = currentRef;
368
+ expanded.push(expandedCol);
368
369
  }
369
370
  else if (current.val !== undefined || current.func !== undefined) {
370
371
  expanded.push(Object.assign(current, { as: currentAlias.join(pathDelimiter) }));
@@ -440,7 +441,7 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
440
441
  function rewriteOnCondition( on, currentRef, stack ) {
441
442
  for (let i = 0; i < on.length; i++) {
442
443
  const part = on[i];
443
- if (part.ref && part.ref[0] !== '$self' && part.ref[0] !== '$projection') {
444
+ if (part.ref && part.$scope !== '$magic' && part.$scope !== '$self' && part.$scope !== '$projection') {
444
445
  part.ref = currentRef[0] ? [ currentRef[0], ...part.ref ] : part.ref;
445
446
  on[i] = part;
446
447
  stack.push([ part, part.ref ]);
@@ -461,7 +462,7 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
461
462
  function rewriteSingleExpressionArray( expressionArray, currentRef, stack ) {
462
463
  for (let i = 0; i < expressionArray.length; i++) {
463
464
  const part = expressionArray[i];
464
- if (part.ref) {
465
+ if (part.ref && part.$scope !== '$magic' && part.$scope !== '$self') {
465
466
  part.ref = currentRef.concat(part.ref);
466
467
  expressionArray[i] = part;
467
468
  stack.push([ part, part.ref ]);
@@ -657,7 +658,7 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
657
658
  obj.ref = [ root.$env, ...obj.ref ];
658
659
 
659
660
  if (iterateOptions.keepKeysOrigin) {
660
- setProp(obj, '$originalKeyRef', { ref: root.ref, as: root.as });
661
+ setProp(obj, '$originalKeyRef', root);
661
662
  setProp(obj, '$path', root.$path);
662
663
  }
663
664
 
@@ -4,7 +4,7 @@ const {
4
4
  getServiceNames, forEachDefinition,
5
5
  getResultingName, forEachMemberRecursively, applyAnnotationsFromExtensions,
6
6
  } = require('../../model/csnUtils');
7
- const { setProp, isBetaEnabled } = require('../../base/model');
7
+ const { setProp } = require('../../base/model');
8
8
  const { getTransformers } = require('../transformUtils');
9
9
  const { ModelError } = require('../../base/error');
10
10
  const { forEach } = require('../../utils/objectUtils');
@@ -56,10 +56,10 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
56
56
 
57
57
  // Redirect associations/compositions between draft shadow nodes
58
58
  for (const name in draftNodes) {
59
- const shadowNode = csn.definitions[`${name}${draftSuffix}`];
59
+ const shadowNode = csn.definitions[`${ name }${ draftSuffix }`];
60
60
  // Might not exist because of previous errors
61
61
  if (shadowNode)
62
- redirectDraftTargets(csn.definitions[`${name}${draftSuffix}`], draftNodes);
62
+ redirectDraftTargets(csn.definitions[`${ name }${ draftSuffix }`], draftNodes);
63
63
  }
64
64
  }
65
65
  }
@@ -84,7 +84,7 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
84
84
  const draftNodeName = elem.target;
85
85
  // Sanity check
86
86
  if (!draftNode)
87
- throw new ModelError(`Expecting target to be resolved: ${JSON.stringify(elem, null, 2)}`);
87
+ throw new ModelError(`Expecting target to be resolved: ${ JSON.stringify(elem, null, 2) }`);
88
88
 
89
89
  // Ignore composition if not part of a service
90
90
  if (!isPartOfService(draftNodeName)) {
@@ -115,11 +115,11 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
115
115
  function generateDraftForHana( artifact, artifactName, draftRootName ) {
116
116
  // Sanity check
117
117
  if (!isPartOfService(artifactName))
118
- throw new ModelError(`Expecting artifact to be part of a service: ${JSON.stringify(artifact)}`);
118
+ throw new ModelError(`Expecting artifact to be part of a service: ${ JSON.stringify(artifact) }`);
119
119
 
120
120
 
121
121
  // The name of the draft shadow entity we should generate
122
- const draftsArtifactName = `${artifactName}${draftSuffix}`;
122
+ const draftsArtifactName = `${ artifactName }${ draftSuffix }`;
123
123
 
124
124
  generatedArtifacts[draftsArtifactName] = true;
125
125
 
@@ -132,7 +132,7 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
132
132
  // Ignore boolean return value. We know that we're inside a service or else we wouldn't have reached this code.
133
133
  const matchingService = getMatchingService(artifactName) || '';
134
134
  // Generate the DraftAdministrativeData projection into the service, unless there is already one
135
- const draftAdminDataProjectionName = `${matchingService}.DraftAdministrativeData`;
135
+ const draftAdminDataProjectionName = `${ matchingService }.DraftAdministrativeData`;
136
136
  let draftAdminDataProjection = csn.definitions[draftAdminDataProjectionName];
137
137
  if (!draftAdminDataProjection) {
138
138
  generatedArtifacts[draftAdminDataProjectionName] = true;
@@ -232,10 +232,9 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
232
232
  draftAdministrativeData.DraftAdministrativeData.notNull = true;
233
233
  addElement(draftAdministrativeData, draftsArtifact, artifactName);
234
234
 
235
- if (isBetaEnabled(options, 'draftMessages') || options.draftMessages) {
236
- const draftMessages = { DraftMessages: { '@Core.Computed': true, virtual: true, items: { type: 'DRAFT.DraftAdministrativeData_DraftMessage' } } };
237
- addElement(draftMessages, draftsArtifact, artifactName);
238
- }
235
+ const draftMessages = { DraftMessages: { '@Core.Computed': true, virtual: true, items: { type: 'DRAFT.DraftAdministrativeData_DraftMessage' } } };
236
+ addElement(draftMessages, draftsArtifact, artifactName);
237
+
239
238
  // Note that we may need to do the HANA transformation steps for managed associations
240
239
  // (foreign key field generation, generatedFieldName, creating ON-condition) by hand,
241
240
  // because the corresponding transformation steps have already been done on all artifacts
@@ -278,18 +277,19 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
278
277
  const sourceElement = source.elements[draftUUIDKey.ref[0]];
279
278
  const targetElement = {};
280
279
  forEach(sourceElement, (key, value) => {
281
- if(!key.startsWith('@') && key !== 'key')
280
+ if (!key.startsWith('@') && key !== 'key')
282
281
  targetElement[key] = value;
283
- })
282
+ });
284
283
 
285
- if(sourceElement.key) targetElement.notNull = true;
284
+ if (sourceElement.key)
285
+ targetElement.notNull = true;
286
286
 
287
- draftsArtifact.elements['DraftAdministrativeData' + (options.sqlMapping === 'hdbcds' ? '.' : '_') + draftUUIDKey.ref[0]] = targetElement;
287
+ draftsArtifact.elements[`DraftAdministrativeData${ options.sqlMapping === 'hdbcds' ? '.' : '_' }${ draftUUIDKey.ref[0] }`] = targetElement;
288
288
 
289
289
  draftAdministrativeData.DraftAdministrativeData.on = createAssociationPathComparison('DraftAdministrativeData',
290
290
  getNameForRef(draftUUIDKey),
291
291
  '=',
292
- `DraftAdministrativeData${pathDelimiter}DraftUUID`);
292
+ `DraftAdministrativeData${ pathDelimiter }DraftUUID`);
293
293
  // The notNull has been transferred to the foreign key field and must be removed on the association
294
294
  delete draftAdministrativeData.DraftAdministrativeData.notNull;
295
295
 
@@ -337,9 +337,9 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
337
337
  function getDraftShadowEntityFor( draftNode, draftNodeName ) {
338
338
  // Sanity check
339
339
  if (!draftNodes[draftNodeName])
340
- throw new ModelError(`Not a draft node: ${draftNodeName}`);
340
+ throw new ModelError(`Not a draft node: ${ draftNodeName }`);
341
341
 
342
- return { shadowTarget: csn.definitions[`${draftNodeName}${draftSuffix}`], shadowTargetName: `${draftNodeName}${draftSuffix}` };
342
+ return { shadowTarget: csn.definitions[`${ draftNodeName }${ draftSuffix }`], shadowTargetName: `${ draftNodeName }${ draftSuffix }` };
343
343
  }
344
344
  }
345
345
 
@@ -351,7 +351,7 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
351
351
  */
352
352
  function isPartOfService( artifactName ) {
353
353
  for (const serviceName of allServices) {
354
- if (artifactName.startsWith(`${serviceName}.`))
354
+ if (artifactName.startsWith(`${ serviceName }.`))
355
355
  return true;
356
356
  }
357
357
 
@@ -369,7 +369,7 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
369
369
  /** @type {false|string} */
370
370
  let match = false;
371
371
  for (const serviceName of allServices) {
372
- if (artifactName.startsWith(`${serviceName}.`) && (!match || serviceName.length < match.length))
372
+ if (artifactName.startsWith(`${ serviceName }.`) && (!match || serviceName.length < match.length))
373
373
  match = serviceName;
374
374
  }
375
375
  return match;
@@ -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