@sap/cds-compiler 4.4.4 → 4.6.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 (82) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/bin/cdsc.js +18 -11
  3. package/bin/cdsv2m.js +7 -5
  4. package/doc/CHANGELOG_BETA.md +22 -0
  5. package/lib/api/main.js +306 -144
  6. package/lib/api/options.js +18 -6
  7. package/lib/api/validate.js +1 -1
  8. package/lib/base/message-registry.js +45 -10
  9. package/lib/base/messages.js +33 -16
  10. package/lib/base/model.js +4 -0
  11. package/lib/base/optionProcessorHelper.js +45 -176
  12. package/lib/checks/annotationsOData.js +49 -0
  13. package/lib/checks/elements.js +32 -34
  14. package/lib/checks/enricher.js +39 -3
  15. package/lib/checks/validator.js +8 -7
  16. package/lib/compiler/assert-consistency.js +40 -17
  17. package/lib/compiler/builtins.js +30 -53
  18. package/lib/compiler/checks.js +46 -14
  19. package/lib/compiler/cycle-detector.js +1 -4
  20. package/lib/compiler/define.js +35 -10
  21. package/lib/compiler/extend.js +21 -7
  22. package/lib/compiler/generate.js +3 -0
  23. package/lib/compiler/populate.js +5 -1
  24. package/lib/compiler/propagator.js +46 -9
  25. package/lib/compiler/resolve.js +94 -35
  26. package/lib/compiler/shared.js +60 -33
  27. package/lib/compiler/tweak-assocs.js +188 -92
  28. package/lib/compiler/utils.js +11 -1
  29. package/lib/edm/annotations/edmJson.js +41 -66
  30. package/lib/edm/annotations/genericTranslation.js +27 -9
  31. package/lib/edm/annotations/preprocessAnnotations.js +2 -3
  32. package/lib/edm/csn2edm.js +28 -11
  33. package/lib/edm/edmInboundChecks.js +58 -15
  34. package/lib/edm/edmPreprocessor.js +12 -16
  35. package/lib/edm/edmUtils.js +5 -2
  36. package/lib/gen/Dictionary.json +10 -0
  37. package/lib/gen/language.checksum +1 -1
  38. package/lib/gen/language.interp +15 -2
  39. package/lib/gen/language.tokens +1 -0
  40. package/lib/gen/languageParser.js +6557 -5618
  41. package/lib/json/from-csn.js +4 -5
  42. package/lib/json/to-csn.js +29 -4
  43. package/lib/language/antlrParser.js +19 -1
  44. package/lib/language/errorStrategy.js +28 -7
  45. package/lib/language/genericAntlrParser.js +118 -24
  46. package/lib/language/textUtils.js +16 -0
  47. package/lib/main.d.ts +28 -3
  48. package/lib/main.js +3 -0
  49. package/lib/model/csnRefs.js +4 -1
  50. package/lib/model/csnUtils.js +20 -14
  51. package/lib/model/revealInternalProperties.js +5 -2
  52. package/lib/optionProcessor.js +23 -22
  53. package/lib/render/manageConstraints.js +13 -29
  54. package/lib/render/toCdl.js +47 -26
  55. package/lib/render/toHdbcds.js +63 -42
  56. package/lib/render/toRename.js +6 -10
  57. package/lib/render/toSql.js +71 -117
  58. package/lib/render/utils/common.js +41 -6
  59. package/lib/transform/.eslintrc.json +9 -1
  60. package/lib/transform/addTenantFields.js +228 -0
  61. package/lib/transform/db/applyTransformations.js +57 -4
  62. package/lib/transform/db/assertUnique.js +4 -4
  63. package/lib/transform/db/backlinks.js +13 -1
  64. package/lib/transform/db/cdsPersistence.js +1 -1
  65. package/lib/transform/db/expansion.js +24 -3
  66. package/lib/transform/db/flattening.js +70 -71
  67. package/lib/transform/db/killAnnotations.js +37 -0
  68. package/lib/transform/db/rewriteCalculatedElements.js +46 -6
  69. package/lib/transform/db/temporal.js +1 -1
  70. package/lib/transform/draft/db.js +2 -16
  71. package/lib/transform/draft/odata.js +3 -3
  72. package/lib/transform/effective/associations.js +3 -5
  73. package/lib/transform/effective/main.js +6 -9
  74. package/lib/transform/forOdata.js +26 -55
  75. package/lib/transform/forRelationalDB.js +38 -18
  76. package/lib/transform/odata/toFinalBaseType.js +3 -3
  77. package/lib/transform/odata/typesExposure.js +14 -5
  78. package/lib/transform/transformUtils.js +47 -34
  79. package/lib/transform/translateAssocsToJoins.js +45 -11
  80. package/lib/transform/universalCsn/coreComputed.js +1 -1
  81. package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
  82. package/package.json +7 -6
@@ -47,8 +47,6 @@ function translateAssocsToJoinsCSN(csn, options){
47
47
  deduplicateMessages( options.messages );
48
48
  }
49
49
 
50
- // If A2J reports error - end! Continuing with a broken CSN makes no sense
51
- makeMessageFunction(model, options).throwWithAnyError();
52
50
  // FIXME: Move this somewhere more appropriate
53
51
  const newCsn = compactModel(model, compileOptions);
54
52
  //require('fs').writeFileSync('./csnoutput_a2j.json', JSON.stringify(newCsn, null,2))
@@ -57,7 +55,7 @@ function translateAssocsToJoinsCSN(csn, options){
57
55
 
58
56
  function translateAssocsToJoins(model, inputOptions = {})
59
57
  {
60
- const { error, warning } = makeMessageFunction(model, inputOptions);
58
+ const { error, warning, throwWithError } = makeMessageFunction(model, inputOptions);
61
59
 
62
60
  const options = model.options || inputOptions;
63
61
 
@@ -70,6 +68,9 @@ function translateAssocsToJoins(model, inputOptions = {})
70
68
  forEachDefinition(model, prepareAssociations);
71
69
  forEachDefinition(model, transformQueries);
72
70
 
71
+ // If A2J reports error - end! Continuing with a broken model makes no sense
72
+ throwWithError();
73
+
73
74
  return model;
74
75
 
75
76
  function prepareAssociations(art)
@@ -263,8 +264,12 @@ function translateAssocsToJoins(model, inputOptions = {})
263
264
  /*
264
265
  Substitute $self/$projection expression with its value
265
266
  */
266
- function substituteDollarSelf(pathNode)
267
+ function substituteDollarSelf(pathNode, env)
267
268
  {
269
+ // do not substitute $self values for outer order by clauses
270
+ if (env?.location === 'UnionOuterOrderBy')
271
+ return;
272
+
268
273
  let pathValue = pathNode;
269
274
  let [head, ...tail] = pathValue.path;
270
275
  while(tail.length && head._navigation?.kind === '$self') {
@@ -311,12 +316,14 @@ function translateAssocsToJoins(model, inputOptions = {})
311
316
  {
312
317
  // Paths without _navigation in ORDER BY are select item aliases, they must
313
318
  // be rendered verbatim
314
- if((env.location === 'OrderBy' && !pathNode.path[0]._navigation))
319
+ let [head, ...tail] = pathNode.path;
320
+ if((env.location === 'OrderBy' && !head._navigation)||
321
+ env.location === 'UnionOuterOrderBy' && (!head._navigation || ['$self', '$projection'].includes(head.id)))
315
322
  return;
316
323
 
317
324
  // path outside ON cond:
318
325
  // spin the crystal ball to identify the correct table alias
319
- let [head, ...tail] = pathNode.path;
326
+
320
327
  // pop ta ps
321
328
  if(head._navigation.kind !== '$tableAlias')
322
329
  tail = pathNode.path;
@@ -488,7 +495,7 @@ function translateAssocsToJoins(model, inputOptions = {})
488
495
  let srcTableAlias = constructTableAliasPathStep(assocSourceQA);
489
496
  let tgtTableAlias = constructTableAliasPathStep(assocQAT.$QA);
490
497
 
491
- node.on = createOnCondition(assoc, srcTableAlias, tgtTableAlias);
498
+ node.on = createOnCondition(assoc, srcTableAlias, tgtTableAlias, options.tenantAsColumn);
492
499
 
493
500
  if(assocQAT._filter)
494
501
  {
@@ -558,7 +565,7 @@ function translateAssocsToJoins(model, inputOptions = {})
558
565
  return xsnCard;
559
566
  }
560
567
  // produce the ON condition for a given association
561
- function createOnCondition(assoc, srcAlias, tgtAlias)
568
+ function createOnCondition(assoc, srcAlias, tgtAlias, compareTenants)
562
569
  {
563
570
  let prefixes = [ assoc.name.id ];
564
571
  /* This is no art and can be removed once ON cond for published
@@ -593,7 +600,7 @@ function translateAssocsToJoins(model, inputOptions = {})
593
600
  Put all src/tgt path siblings into the EQ term and create the proper path objects
594
601
  with the src/tgt table alias path steps in front.
595
602
  */
596
- let args = [];
603
+ let args = []; // TODO: tenant comparison?
597
604
  for(let i = 0; i < assoc.$flatSrcFKs.length; i++)
598
605
  {
599
606
  args.push({op: {val: '=' },
@@ -654,7 +661,25 @@ function translateAssocsToJoins(model, inputOptions = {})
654
661
  env.assocStack.push(assoc);
655
662
  const onCond = cloneOnCondition(assoc.on);
656
663
  env.assocStack.pop();
657
- return onCond;
664
+ return compareTenants ? addTenantComparison(assoc, onCond) : onCond;
665
+ }
666
+
667
+ // Add tenant comparison
668
+ function addTenantComparison(assoc, cond) {
669
+ // It is enough to test whether the target is tenant-dependent. If it is,
670
+ // the current query must also be (check in addTenantFields). If we allow
671
+ // assocs from tenant-independent entities to tenant-dependent ones, we
672
+ // also need to use the current query = `env.lead`.
673
+ if(annotationVal(assoc.target._artifact['@cds.tenant.independent']))
674
+ return cond;
675
+
676
+ const args = [ constructPathNode([ srcAlias ]), constructPathNode([ tgtAlias ]) ];
677
+ args[0].path.push({ id: 'tenant' }); // no need for _artifact
678
+ args[1].path.push({ id: 'tenant' }); // no need for _artifact
679
+ const comparison = { op: {val: '=' }, args };
680
+ if (!cond) // for managed assoc
681
+ return comparison;
682
+ return { op: { val: 'and' }, args: [ comparison, parenthesise(cond) ] };
658
683
  }
659
684
 
660
685
  // make foreign key absolute to its main entity
@@ -1041,6 +1066,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1041
1066
  return node;
1042
1067
  }
1043
1068
 
1069
+ // Remark CW: why boolean and not just truthy/falsy as usual? See annotationVal() below
1044
1070
  function isBooleanAnnotation(prop, val=true) {
1045
1071
  return prop && prop.val !== undefined && prop.val === val && prop.literal === 'boolean';
1046
1072
  }
@@ -1438,7 +1464,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1438
1464
  {
1439
1465
  // speciality for OrderBy: If path has no _navigation don't merge it.
1440
1466
  // Path is alias to select item expression
1441
- if(env.location === 'OrderBy')
1467
+ if(['OrderBy', 'UnionOuterOrderBy'].includes(env.location))
1442
1468
  return;
1443
1469
 
1444
1470
  // env.pathStep is set in walkPath for walk on filter conditions
@@ -1769,6 +1795,9 @@ function walkQuery(query, env)
1769
1795
  walk(query.having, env);
1770
1796
  env.location = 'OrderBy';
1771
1797
  walk(query.orderBy, env);
1798
+ env.location = 'UnionOuterOrderBy';
1799
+ // outer orderBy's of anonymous union
1800
+ walk(query.$orderBy, env);
1772
1801
  if(query.limit) {
1773
1802
  env.location = 'Limit';
1774
1803
  walk(query.limit.rows, env);
@@ -1896,4 +1925,9 @@ function walkPath(node, env)
1896
1925
  return path;
1897
1926
  }
1898
1927
 
1928
+ function annotationVal( anno ) {
1929
+ // XSN TODO: also set `val:true` but no location for anno short form
1930
+ return anno && (anno.val === undefined || anno.val);
1931
+ }
1932
+
1899
1933
  module.exports = { translateAssocsToJoinsCSN };
@@ -157,7 +157,7 @@ function setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils ) {
157
157
  (
158
158
  column.xpr || column.list || column.func || column.val !== undefined || column['#'] !== undefined || column.param ||
159
159
  column.SELECT || column.SET ||
160
- column.ref && [ '$at', '$valid', '$now', '$user', '$session', '$parameters' ].includes(column.ref[0])
160
+ column.ref && [ '$at', '$valid', '$now', '$user', '$tenant', '$session', '$parameters' ].includes(column.ref[0])
161
161
  );
162
162
  }
163
163
 
@@ -328,9 +328,9 @@ module.exports = (csn, options) => {
328
328
  * passed to this function.
329
329
  *
330
330
  * @param {CSN.Element} member
331
- * @param {object} [except=null] List of properties which should not be propagated along the origin chain
331
+ * @param {object} [except] List of properties which should not be propagated along the origin chain
332
332
  * of the `member`
333
- * @param {object} [force=null] Overwrite any member propagation rules or any except and always propagate the corresponding keys
333
+ * @param {object} [force] Overwrite any member propagation rules or any except and always propagate the corresponding keys
334
334
  */
335
335
  function propagateMemberPropsFromOrigin( member, except = null, force = null ) {
336
336
  const memberChain = getOriginChain(member);
@@ -613,8 +613,8 @@ module.exports = (csn, options) => {
613
613
  * @param {object} to
614
614
  * @param {Function} getCustomRule getter for the `memberProps` or `defProps`
615
615
  * which shall be used for retrieving custom rules
616
- * @param {object} [except=null] array of properties which should not be propagated
617
- * @param {object} [force=null] Force propagation of the contained keys via a custom rule.
616
+ * @param {object} [except] array of properties which should not be propagated
617
+ * @param {object} [force] Force propagation of the contained keys via a custom rule.
618
618
  */
619
619
  function copyProperties( from, to, getCustomRule, except = null, force = null ) {
620
620
  const keys = Object.keys(from);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "4.4.4",
3
+ "version": "4.6.0",
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)",
@@ -18,15 +18,16 @@
18
18
  "gen": "node ./scripts/build.js && node scripts/genGrammarChecksum.js",
19
19
  "xmakeAfterInstall": "npm run gen",
20
20
  "xmakePrepareRelease": "echo \"$(node scripts/stripReadme.js README.md)\" > README.md && node scripts/assertSnapshotVersioning.js && node scripts/assertChangelog.js && node scripts/cleanup.js --remove-dev",
21
- "test": "node scripts/verifyGrammarChecksum.js && mocha --reporter min --reporter-option maxDiffSize=0 scripts/testLazyLoading.js && mocha --parallel --reporter min --reporter-option maxDiffSize=0 test/ test3/",
22
- "testci": "node scripts/verifyGrammarChecksum.js && mocha --reporter-option maxDiffSize=0 scripts/testLazyLoading.js && mocha --parallel --reporter-option maxDiffSize=0 test/ test3/",
23
- "testverbose": "node scripts/verifyGrammarChecksum.js && mocha --parallel test/ test3/",
21
+ "test": "npm run test:piper",
22
+ "test:ci": "node scripts/verifyGrammarChecksum.js && mocha --timeout 10000 --reporter-option maxDiffSize=0 scripts/testLazyLoading.js && mocha --parallel --reporter-option maxDiffSize=0 test/ test3/",
23
+ "test:piper": "node scripts/verifyGrammarChecksum.js && npm run coverage:piper",
24
24
  "test3": "node scripts/verifyGrammarChecksum.js && mocha --reporter-option maxDiffSize=0 test3/",
25
25
  "deployHanaSql": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 test3/test.deploy.hana-sql.js",
26
26
  "deployHdiHdbcds": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 test3/test.deploy.hdi.hdbcds.js",
27
27
  "deployGitDiffs": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 test3/test.deploy.git-diffs.js",
28
28
  "gentest3": "cross-env MAKEREFS=${MAKEREFS:-'true'} mocha --reporter-option maxDiffSize=0 test3/testRefFiles.js",
29
- "coverage": "cross-env nyc mocha --reporter-option maxDiffSize=0 test/ test3/ && nyc report --reporter=lcov",
29
+ "coverage": "cross-env nyc mocha --reporter-option maxDiffSize=0 test/ test3/testRefFiles.js && nyc report --reporter=lcov",
30
+ "coverage:piper": "cross-env nyc mocha --reporter test/TestMochaReporter.js --reporter-options mochaFile=./coverage/TEST-results.xml --reporter-option maxDiffSize=0 --timeout 10000 test/ test3/ && nyc report --reporter=cobertura && nyc report --reporter=lcov",
30
31
  "lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/ types/ && node scripts/linter/lintGrammar.js && node scripts/linter/lintTests.js test3/ && node scripts/linter/lintMessages.js && node scripts/linter/lintMessageIdCoverage.js lib/ && markdownlint README.md CHANGELOG.md doc/ internalDoc/ && cd share/messages && markdownlint .",
31
32
  "tslint": "tsc --pretty -p .",
32
33
  "updateVocs": "node scripts/odataAnnotations/generateDictMain.js && npm run generateAllRefs",
@@ -40,7 +41,7 @@
40
41
  "generateToSqlRefs": "cross-env MAKEREFS='true' mocha test/testToSql.js",
41
42
  "generateToRenameRefs": "cross-env MAKEREFS='true' mocha test/testToRename.js",
42
43
  "generateDraftRefs": "cross-env MAKEREFS='true' mocha test/testDraft.js",
43
- "generateAllRefs": "node scripts/verifyGrammarChecksum.js && cross-env MAKEREFS='true' mocha --reporter-option maxDiffSize=0 test/ test3/"
44
+ "generateAllRefs": "node scripts/verifyGrammarChecksum.js && cross-env MAKEREFS=force mocha --reporter-option maxDiffSize=0 test/ test3/"
44
45
  },
45
46
  "keywords": [
46
47
  "CDS"