@sap/cds-compiler 5.3.0 → 5.3.2

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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,13 @@
7
7
  Note: `beta` fixes, changes and features are usually not listed in this ChangeLog but [here](doc/CHANGELOG_BETA.md).
8
8
  The compiler behavior concerning `beta` features can change at any time without notice.
9
9
 
10
+ ## Version 5.3.2 - 2024-10-08
11
+
12
+ ### Fixed
13
+
14
+ - to.sql|hdi|hdbcds|effective: Handle subexpressions in conjunction with exists predicate.
15
+
16
+
10
17
  ## Version 5.3.0 - 2024-09-25
11
18
 
12
19
  ### Added
@@ -660,11 +660,12 @@ function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter
660
660
 
661
661
  /**
662
662
  * @param {CSN.Model} csn
663
+ * @param {CSN.Options} options
663
664
  */
664
- function processCalculatedElementsInEntities( csn ) {
665
+ function processCalculatedElementsInEntities( csn, options ) {
665
666
  forEachDefinition(csn, (artifact, definitionName) => {
666
667
  if (artifact.kind === 'entity' && !(artifact.query || artifact.projection))
667
- removeDummyValueInEntity(artifact, [ 'definitions', definitionName ]);
668
+ removeDummyValueInEntity(artifact, [ 'definitions', definitionName ], options);
668
669
  });
669
670
  }
670
671
 
@@ -674,14 +675,19 @@ function processCalculatedElementsInEntities( csn ) {
674
675
  *
675
676
  * @param {CSN.Artifact} artifact
676
677
  * @param {CSN.Path} path
678
+ * @param {CSN.Options} options
677
679
  * @todo calculated elements that "live" on the database?
678
680
  * @todo error when artifact is empty afterwards? Probably better as a CSN check!
679
681
  */
680
- function removeDummyValueInEntity( artifact, path ) {
682
+ function removeDummyValueInEntity( artifact, path, options ) {
681
683
  applyTransformationsOnDictionary(artifact.elements, {
682
684
  value: (parent, prop, value, p, elements) => {
683
- if (!value.stored)
684
- delete elements[p[p.length - 1]];
685
+ if (!value.stored) {
686
+ if (options.transformation === 'effective' && parent.on)
687
+ delete parent.value;
688
+ else
689
+ delete elements[p.at(-1)];
690
+ }
685
691
  },
686
692
  }, {}, path.concat( 'elements' ));
687
693
  }
@@ -409,6 +409,7 @@ function handleExists( csn, options, error, inspectRef, initDefinition, dropDefi
409
409
  }
410
410
 
411
411
  /**
412
+ *
412
413
  * Translate an `EXISTS <unmanaged assoc>` into a part of a WHERE condition.
413
414
  *
414
415
  * A valid $self-backlink is handled in translateDollarSelfToWhere.
@@ -428,53 +429,77 @@ function handleExists( csn, options, error, inspectRef, initDefinition, dropDefi
428
429
  */
429
430
  function translateUnmanagedAssocToWhere( root, target, isPrefixedWithTableAlias, base, current ) {
430
431
  const whereExtension = [];
431
- for (let j = 0; j < root.on.length; j++) {
432
- const part = root.on[j];
432
+
433
+ for (let j = 0; j < root.on.length; j++)
434
+ j = processExpressionPart(root.on, root.$path.concat('on'), j, whereExtension);
435
+
436
+ return whereExtension;
437
+
438
+ /**
439
+ * Process the given expression and apply the steps described above.
440
+ *
441
+ * @param {Array} expression Expression we are processing
442
+ * @param {CSN.Path} path Path to the expression
443
+ * @param {number} expressionIndex Index in the current expression, imporant for paths and stuff
444
+ * @param {Array} collector Array to collect the processed expressionparts into
445
+ * @returns {number} How far along expression we have processed - so the main loop can jump ahead
446
+ */
447
+ function processExpressionPart(expression, path, expressionIndex, collector) {
448
+ const part = expression[expressionIndex];
449
+
450
+ if (part?.xpr) {
451
+ const xpr = { xpr: [] };
452
+ for (let i = 0; i < part.xpr.length; i++)
453
+ i = processExpressionPart(part.xpr, path.concat(expressionIndex, 'xpr'), i, xpr.xpr);
454
+
455
+ collector.push(xpr);
456
+ return expressionIndex;
457
+ }
433
458
 
434
459
  // we can only resolve stuff on refs - skip literals like =
435
460
  // but also keep along stuff like null and undefined, so compiler
436
461
  // can have a chance to complain/ we can fail later nicely maybe
437
462
  if (!(part && part.ref)) {
438
- whereExtension.push(part);
439
- continue;
463
+ collector.push(part);
464
+ return expressionIndex;
440
465
  }
441
466
 
442
467
  // root.$path should be safe - we can only reference things in exists that exist when we enrich
443
468
  // so all of them should have a $path.
444
- const { art, links } = inspectRef(root.$path.concat([ 'on', j ]));
469
+ const { art, links } = inspectRef(path.concat(expressionIndex));
445
470
  // Dollar Self Backlink
446
- if (isValidDollarSelf(root.on[j], root.$path.concat([ 'on', j ]), root.on[j + 1], root.on[j + 2], root.$path.concat([ 'on', j + 2 ]))) {
447
- if (root.on[j].ref[0] === '$self' && root.on[j].ref.length === 1)
448
- whereExtension.push(...translateDollarSelfToWhere(base, target, root.on[j + 2], root.$path.concat([ 'on', j + 2 ])));
471
+ if (isValidDollarSelf(expression[expressionIndex], path.concat(expressionIndex), expression[expressionIndex + 1], expression[expressionIndex + 2], path.concat(expressionIndex + 2 ))) {
472
+ if (expression[expressionIndex].ref[0] === '$self' && expression[expressionIndex].ref.length === 1)
473
+ collector.push(...translateDollarSelfToWhere(base, target, expression[expressionIndex + 2], path.concat(expressionIndex + 2 )));
449
474
  else
450
- whereExtension.push(...translateDollarSelfToWhere(base, target, root.on[j], root.$path.concat([ 'on', j ])));
475
+ collector.push(...translateDollarSelfToWhere(base, target, expression[expressionIndex], path.concat(expressionIndex)));
451
476
 
452
- j += 2;
477
+ return expressionIndex + 2;
453
478
  }
454
479
  else if (links && links[0].art === root) { // target side
455
- whereExtension.push({ ref: [ target, ...part.ref.slice(1) ] });
480
+ collector.push({ ref: [ target, ...part.ref.slice(1) ] });
456
481
  }
457
482
  else if (part.$scope === '$self') { // source side - "absolute" scope
458
483
  const column = part._art._column;
459
484
  if (column && column.as) { // Replace with the "original" expression (the .ref, .xpr etc.)
460
- whereExtension.push(translateToSourceSide(column));
485
+ collector.push(translateToSourceSide(column));
461
486
  }
462
487
  else {
463
- whereExtension.push(assignAndDeleteAs({}, part, { ref: [ base, ...part.ref.slice(1) ] }));
488
+ collector.push(assignAndDeleteAs({}, part, { ref: [ base, ...part.ref.slice(1) ] }));
464
489
  }
465
490
  }
466
491
  else if (art) { // source side - with local scope
467
492
  if (isPrefixedWithTableAlias || part.$scope === 'alias')
468
- whereExtension.push({ ref: [ ...current.ref.slice(0, -1), ...part.ref ] });
493
+ collector.push({ ref: [ ...current.ref.slice(0, -1), ...part.ref ] });
469
494
  else
470
- whereExtension.push({ ref: [ base, ...current.ref.slice(0, -1), ...part.ref ] });
495
+ collector.push({ ref: [ base, ...current.ref.slice(0, -1), ...part.ref ] });
471
496
  }
472
497
  else { // operator - or any other leftover
473
- whereExtension.push(part);
498
+ collector.push(part);
474
499
  }
475
- }
476
500
 
477
- return whereExtension;
501
+ return expressionIndex;
502
+ }
478
503
 
479
504
 
480
505
  /**
@@ -75,7 +75,7 @@ function effectiveCsn( model, options, messageFunctions ) {
75
75
  // ensure getElement works on flattened struct_assoc columns
76
76
  csnUtils = getUtils(csn, 'init-all');
77
77
 
78
- processCalculatedElementsInEntities(csn);
78
+ processCalculatedElementsInEntities(csn, options);
79
79
  associations.managedToUnmanaged(csn, options, csnUtils, messageFunctions);
80
80
  associations.transformBacklinks(csn, options, csnUtils, messageFunctions);
81
81
  const transformers = mergeTransformers([ options.addCdsPersistenceName ? misc.attachPersistenceName(csn, options, csnUtils) : {}, options.remapOdataAnnotations ? annotations.remapODataAnnotations(csn) : {}, misc.removeDefinitionsAndProperties(csn, options) ], null);
@@ -248,7 +248,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
248
248
  }
249
249
  });
250
250
 
251
- processCalculatedElementsInEntities(csn);
251
+ processCalculatedElementsInEntities(csn, options);
252
252
 
253
253
  timetrace.start('Transform CSN')
254
254
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "5.3.0",
3
+ "version": "5.3.2",
4
4
  "description": "CDS (Core Data Services) compiler and backends",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "author": "SAP SE (https://www.sap.com)",