@sap/cds-compiler 4.0.2 → 4.1.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.
Files changed (84) hide show
  1. package/CHANGELOG.md +100 -5
  2. package/bin/cdsc.js +12 -12
  3. package/doc/CHANGELOG_BETA.md +11 -0
  4. package/lib/api/main.js +31 -11
  5. package/lib/api/validate.js +1 -1
  6. package/lib/base/location.js +6 -7
  7. package/lib/base/message-registry.js +84 -38
  8. package/lib/base/messages.js +11 -10
  9. package/lib/base/model.js +6 -2
  10. package/lib/checks/defaultValues.js +6 -6
  11. package/lib/checks/foreignKeys.js +0 -5
  12. package/lib/checks/onConditions.js +17 -12
  13. package/lib/checks/queryNoDbArtifacts.js +132 -72
  14. package/lib/checks/sql-snippets.js +15 -4
  15. package/lib/checks/types.js +3 -3
  16. package/lib/checks/utils.js +1 -1
  17. package/lib/compiler/assert-consistency.js +44 -16
  18. package/lib/compiler/base.js +1 -0
  19. package/lib/compiler/builtins.js +7 -8
  20. package/lib/compiler/checks.js +274 -197
  21. package/lib/compiler/classes.js +62 -0
  22. package/lib/compiler/cycle-detector.js +3 -3
  23. package/lib/compiler/define.js +63 -50
  24. package/lib/compiler/extend.js +38 -20
  25. package/lib/compiler/finalize-parse-cdl.js +2 -1
  26. package/lib/compiler/generate.js +0 -8
  27. package/lib/compiler/index.js +9 -7
  28. package/lib/compiler/kick-start.js +2 -0
  29. package/lib/compiler/populate.js +139 -110
  30. package/lib/compiler/propagator.js +4 -3
  31. package/lib/compiler/resolve.js +157 -126
  32. package/lib/compiler/shared.js +706 -404
  33. package/lib/compiler/tweak-assocs.js +21 -10
  34. package/lib/compiler/utils.js +228 -36
  35. package/lib/edm/annotations/genericTranslation.js +1 -1
  36. package/lib/edm/edm.js +4 -1
  37. package/lib/edm/edmPreprocessor.js +5 -4
  38. package/lib/edm/edmUtils.js +2 -4
  39. package/lib/gen/Dictionary.json +34 -10
  40. package/lib/gen/language.checksum +1 -1
  41. package/lib/gen/language.interp +1 -1
  42. package/lib/gen/languageParser.js +3987 -3963
  43. package/lib/json/from-csn.js +43 -47
  44. package/lib/json/to-csn.js +11 -11
  45. package/lib/language/antlrParser.js +2 -1
  46. package/lib/language/genericAntlrParser.js +52 -43
  47. package/lib/language/language.g4 +59 -59
  48. package/lib/language/multiLineStringParser.js +2 -0
  49. package/lib/main.d.ts +5 -0
  50. package/lib/model/csnRefs.js +37 -19
  51. package/lib/model/csnUtils.js +20 -16
  52. package/lib/model/revealInternalProperties.js +29 -21
  53. package/lib/modelCompare/compare.js +112 -39
  54. package/lib/modelCompare/utils/filter.js +54 -24
  55. package/lib/optionProcessor.js +6 -6
  56. package/lib/render/manageConstraints.js +20 -17
  57. package/lib/render/toCdl.js +34 -20
  58. package/lib/render/toHdbcds.js +2 -2
  59. package/lib/render/toRename.js +4 -9
  60. package/lib/render/toSql.js +77 -26
  61. package/lib/render/utils/common.js +3 -3
  62. package/lib/render/utils/unique.js +52 -0
  63. package/lib/transform/db/applyTransformations.js +61 -20
  64. package/lib/transform/db/assertUnique.js +7 -8
  65. package/lib/transform/db/associations.js +2 -2
  66. package/lib/transform/db/cdsPersistence.js +8 -8
  67. package/lib/transform/db/expansion.js +17 -21
  68. package/lib/transform/db/flattening.js +23 -23
  69. package/lib/transform/db/rewriteCalculatedElements.js +20 -14
  70. package/lib/transform/db/temporal.js +1 -1
  71. package/lib/transform/db/transformExists.js +8 -7
  72. package/lib/transform/db/views.js +73 -33
  73. package/lib/transform/draft/db.js +11 -9
  74. package/lib/transform/draft/odata.js +1 -1
  75. package/lib/transform/{forOdataNew.js → forOdata.js} +6 -6
  76. package/lib/transform/forRelationalDB.js +69 -75
  77. package/lib/transform/localized.js +6 -5
  78. package/lib/transform/odata/toFinalBaseType.js +3 -3
  79. package/lib/transform/{transformUtilsNew.js → transformUtils.js} +4 -101
  80. package/lib/transform/translateAssocsToJoins.js +14 -28
  81. package/package.json +1 -1
  82. package/share/messages/check-proper-type-of.md +1 -1
  83. package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
  84. package/share/messages/message-explanations.json +1 -1
@@ -420,17 +420,10 @@ function getTransformers(model, options, pathDelimiter = '_') {
420
420
  if (typeof type === 'string' && isBuiltinType(type))
421
421
  return;
422
422
 
423
- let typeRef = null;
424
- if (resolved.has(type)) {
425
- typeRef = resolved.get(type)?.art
426
- // The cached entry may not be resolved, yet.
427
- if (typeRef.type && !isBuiltinType(typeRef.type))
428
- typeRef = getFinalTypeInfo(typeRef.type);
429
- } else {
430
- typeRef = getFinalTypeInfo(type);
431
- }
423
+ const typeRef = getFinalTypeInfo(type, (t) => resolved.get(t)?.art || csnUtils.artifactRef(t));
432
424
  if(!typeRef)
433
425
  return;
426
+
434
427
  if (typeRef.elements || typeRef.items) {
435
428
  // Copy elements/items and we're finished. No need to look up actual base type,
436
429
  // since it must also be structured and must contain at least as many elements,
@@ -909,7 +902,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
909
902
  if (annotation === undefined) {
910
903
  throw new CompilerAssertion('Annotation ' + fromName + ' not found in ' + JSON.stringify(node));
911
904
  }
912
- if(node[toName] === undefined || node[toName] === null) {
905
+ if(node[toName] == null) {
913
906
  delete node[fromName];
914
907
  node[toName] = annotation;
915
908
  }
@@ -931,9 +924,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
931
924
  if (value === undefined) {
932
925
  throw new CompilerAssertion('Annotation value must not be undefined');
933
926
  }
934
-
935
- if(node[name] === undefined || node[name] === null)
936
- node[name] = value;
927
+ node[name] ??= value;
937
928
  }
938
929
 
939
930
  /**
@@ -1352,93 +1343,6 @@ function getTransformers(model, options, pathDelimiter = '_') {
1352
1343
 
1353
1344
  }
1354
1345
 
1355
- /**
1356
- * Modify the given CSN/artifact in-place, applying the given customTransformations.
1357
- * Dictionaries are correctly handled - a "type" transformer will not be called on an entity called "type".
1358
- *
1359
- * A custom transformation function has the following signature:
1360
- * (any, object, string, CSN.Path) => undefined
1361
- *
1362
- * Given that we have a custom transformation for "type" and stumble upon a thing like below:
1363
- *
1364
- * {
1365
- * type: "cds.String",
1366
- * anotherProp: 1
1367
- * }
1368
- *
1369
- * The input for the function would be:
1370
- *
1371
- * ("cds.String", { type: <>, anotherProp: <>}, "type", [xy, "type"])
1372
- *
1373
- * @param {CSN.Model} csn
1374
- * @param {object} customTransformations Dictionary of functions to apply - if the property matches a key in this dict, it will be called
1375
- * @param {boolean} [transformNonEnumerableElements=false] Transform non-enumerable elements to work with cds linked...
1376
- * @returns {CSN.Model|CSN.Artifact} Return the CSN/artifact
1377
- */
1378
- function transformModel(csn, customTransformations, transformNonEnumerableElements=false){
1379
- const transformers = {
1380
- elements: dictionary,
1381
- definitions: dictionary,
1382
- actions: dictionary,
1383
- params: dictionary,
1384
- enum: dictionary,
1385
- mixin: dictionary,
1386
- args: dictionary
1387
- };
1388
-
1389
- const csnPath = [];
1390
- if (csn.definitions)
1391
- dictionary( csn, 'definitions', csn.definitions );
1392
- else {
1393
- // fake it till you make it
1394
- const obj = { definitions: Object.create(null)};
1395
- obj.definitions.thing = csn;
1396
- dictionary(obj, 'definitions', obj.definitions);
1397
- }
1398
-
1399
- return csn;
1400
-
1401
- function standard( parent, prop, node ) {
1402
- // checking for .kind and .type is safe because annotations with such properties, are already flattened out
1403
- const isAnnotation = () => (typeof prop === 'string' && prop.startsWith('@') && !node.kind && !node.type);
1404
- if (!node || node._ignore || typeof node !== 'object' || !{}.propertyIsEnumerable.call( parent, prop ) || isAnnotation())
1405
- return;
1406
-
1407
- csnPath.push( prop );
1408
-
1409
- if (Array.isArray(node)) {
1410
- node.forEach( (n, i) => standard( node, i, n ) );
1411
- }
1412
- else {
1413
- const iterateOver = Object.getOwnPropertyNames( node );
1414
- // cds-linked resolves types and add's them to elements as non-enum - need to be processed
1415
- if(transformNonEnumerableElements && node.elements && !Object.prototype.propertyIsEnumerable.call(node, 'elements')){
1416
- iterateOver.push('elements');
1417
- }
1418
- for (const name of iterateOver) {
1419
- if(customTransformations[name])
1420
- customTransformations[name](node[name], node, name, csnPath.concat(name))
1421
-
1422
- const trans = transformers[name] || standard;
1423
- trans( node, name, node[name] );
1424
- }
1425
- }
1426
- csnPath.pop();
1427
- }
1428
- function dictionary( node, prop, dict ) {
1429
- csnPath.push( prop );
1430
-
1431
- if (Array.isArray(dict)) {
1432
- dict.forEach( (n, i) => standard(dict, i, n))
1433
- } else {
1434
- for (const name of Object.getOwnPropertyNames( dict ))
1435
- standard( dict, name, dict[name] );
1436
- }
1437
-
1438
- csnPath.pop();
1439
- }
1440
- }
1441
-
1442
1346
  /**
1443
1347
  * Mandatory input transformation for all backends:
1444
1348
  * Replace
@@ -1465,7 +1369,6 @@ function rewriteBuiltinTypeRef(csn) {
1465
1369
  module.exports = {
1466
1370
  // This function retrieves the actual exports
1467
1371
  getTransformers,
1468
- transformModel,
1469
1372
  RelationalOperators,
1470
1373
  rewriteBuiltinTypeRef,
1471
1374
  };
@@ -1124,9 +1124,14 @@ function translateAssocsToJoins(model, inputOptions = {})
1124
1124
  // get paths of managed assocs (unmanaged assocs are not allowed in FK paths)
1125
1125
  if(element.foreignKeys)
1126
1126
  {
1127
- for(let fkn in element.foreignKeys)
1127
+ for(const fkn in element.foreignKeys)
1128
1128
  {
1129
- let fk = element.foreignKeys[fkn];
1129
+ const fk = element.foreignKeys[fkn];
1130
+ // ignore an unmanaged association
1131
+ if(fk.targetElement._artifact.target &&
1132
+ fk.targetElement._artifact.on &&
1133
+ !fk.targetElement._artifact.foreignKeys)
1134
+ continue;
1130
1135
  // once a fk is to be followed, treat all sub-paths as srcSide, this will add fk.name.id only
1131
1136
  if(srcSide)
1132
1137
  paths = paths.concat(flattenElement(fk.targetElement._artifact, true, fk.name.id, fk.targetElement.path.map(ps => ps.id).join(pathDelimiter)));
@@ -1146,9 +1151,9 @@ function translateAssocsToJoins(model, inputOptions = {})
1146
1151
  // get paths of plain structured elements
1147
1152
  else if(element.elements)
1148
1153
  {
1149
- for(let n in element.elements)
1154
+ for(const n in element.elements)
1150
1155
  {
1151
- let elt = element.elements[n];
1156
+ const elt = element.elements[n];
1152
1157
  paths = paths.concat(flattenElement(elt, true, elt.name.id, elt.name.id));
1153
1158
  }
1154
1159
  }
@@ -1384,7 +1389,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1384
1389
 
1385
1390
  All prefix trees are put underneath the $tableAlias structure with attribute $qat or $fqat.
1386
1391
  Each path step appears exactly once for a given filter condition in the prefix tree and
1387
- has a link to it's definition (origin). The default filter is an empty string ''.
1392
+ has a link to its definition (origin). The default filter is an empty string ''.
1388
1393
 
1389
1394
  A special note on paths in filter conditions. Filter paths are treated like postfix
1390
1395
  paths to an association path step, meaning, they are inserted into the assoc's $qat or $fqat
@@ -1409,32 +1414,13 @@ function translateAssocsToJoins(model, inputOptions = {})
1409
1414
 
1410
1415
  let [head, ...tail] = path;
1411
1416
 
1412
- if(['$projection', '$self'].includes(head.id) && tail.length && head._navigation.kind === '$self') {
1413
- // make sure not to truncate tail
1414
- if(tail.length > 1)
1415
- [head, ...tail] = tail;
1416
- else
1417
- head = tail[0];
1418
- /*
1419
- if the head is a path (it better be;) then use it as
1420
- anchor for _navigation and just merge the tail into that QAT
1421
- example:
1422
- entity E { key id: Integer; toE: association to E; toF: association to F;}
1423
- entity F { key id: Integer; toE: association to E; }
1424
- view V as select from E { toE, $projection.toE.toF.id };
1425
- */
1426
- let value = env.lead.elements[head.id].value;
1427
- if(value.path) {
1428
- head = value.path[0];
1429
- }
1430
- else // value is another expression, don't consider it
1431
- return;
1432
- }
1433
-
1434
-
1435
1417
  // qatParent is the node where the starting qat is attached to
1436
1418
  let qatParent = undefined;
1437
1419
 
1420
+ // Note: If head is $self, we would need to resolve it if the path follows associations.
1421
+ // However, that is already rejected by SQL backend checks. For example $self paths
1422
+ // can't be "$self.assoc.foo.bar".
1423
+
1438
1424
  // FROM and filter paths do not have a _navigation, but for filter paths
1439
1425
  // the corresponding path step (to where the filter was attached to) is in env.pathStep
1440
1426
  if(!head._navigation)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "4.0.2",
3
+ "version": "4.1.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)",
@@ -43,4 +43,4 @@ view ViewFoo as select from Foo {
43
43
 
44
44
  ## Related Messages
45
45
 
46
- - `check-proper-type`
46
+ - `def-missing-type`
@@ -1,14 +1,12 @@
1
- # check-proper-type
1
+ # def-missing-type
2
2
 
3
3
  A type artifact doesn’t have proper type information.
4
4
 
5
5
  The message's severity is `Info` but may be raised to `Error` in the SQL,
6
6
  SAP HANA, and OData backends. These backends require types to have type
7
- information. Otherwise they aren’t able to render elements that use this
7
+ information. Otherwise, they aren’t able to render elements that use this
8
8
  type (for example, to SQL columns).
9
9
 
10
- This message affects CSN input and shouldn’t appear if CDL input is used.
11
-
12
10
  ## Example
13
11
 
14
12
  Erroneous code example:
@@ -23,7 +21,7 @@ Erroneous code example:
23
21
  }
24
22
  ```
25
23
 
26
- `MainType` is of kind "type" but has not further type information.
24
+ `MainType` is of kind "type" but has not further type-information.
27
25
 
28
26
  ## How to Fix
29
27
 
@@ -2,9 +2,9 @@
2
2
  "$comment": "This is an auto-generated file! Do not edit this file directly!",
3
3
  "messages": [
4
4
  "anno-duplicate-unrelated-layer",
5
- "check-proper-type",
6
5
  "check-proper-type-of",
7
6
  "def-duplicate-autoexposed",
7
+ "def-missing-type",
8
8
  "extend-repeated-intralayer",
9
9
  "extend-unrelated-layer",
10
10
  "redirected-to-ambiguous",