@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
@@ -2,77 +2,78 @@
2
2
 
3
3
  const { isBuiltinType } = require('../../base/builtins');
4
4
  const { setProp } = require('../../base/model');
5
- const { applyTransformations, implicitAs, copyAnnotations, isDeepEqual } = require('../../model/csnUtils');
5
+ const {
6
+ applyTransformations, implicitAs, copyAnnotations, isDeepEqual,
7
+ } = require('../../model/csnUtils');
6
8
  const { EdmTypeFacetNames } = require('../../edm/EdmPrimitiveTypeDefinitions');
7
9
  const { adaptAnnotationsRefs } = require('./adaptAnnotationRefs');
8
10
 
9
11
  function createForeignKeyElements(csn, options, messageFunctions, csnUtils, iterateOptions = {}) {
12
+ const { error } = messageFunctions;
13
+
14
+ applyTransformations(csn, { elements: createForeignKeysInCsn, params: createForeignKeysInCsn },
15
+ [], iterateOptions);
16
+
17
+ /**
18
+ * Process a given elements or params dictionary and create foreign key elements.
19
+ *
20
+ * @param {object} parent The thing HAVING params or elements
21
+ * @param {string} prop
22
+ * @param {object} dict The params or elements thing
23
+ * @param {CSN.Path} path
24
+ */
25
+ function createForeignKeysInCsn( parent, prop, dict, path ) {
26
+ const orderedElements = [];
27
+ // First, generate the FK elements for given element
28
+ Object.entries(dict).forEach(([ elementName, element ]) => {
29
+ orderedElements.push([ elementName, element ]);
30
+ if (!csnUtils.isManagedAssociation(element))
31
+ return;
32
+ const elementPath = path.concat(prop, elementName);
33
+ const generatedForeignKeys = createForeignKeysForElement(elementPath, element, elementName, csn, options, '_');
34
+
35
+ // Second, finalize the generated FK elements
36
+ const refCount = generatedForeignKeys.reduce((acc, fk) => {
37
+ // count duplicates
38
+ if (acc[fk.prefix])
39
+ acc[fk.prefix]++;
40
+ else
41
+ acc[fk.prefix] = 1;
42
+
43
+ // check for name clash with existing elements
44
+ if (parent[prop][fk.prefix] && isDeepEqual(element, parent[prop][fk.prefix], true)) {
45
+ // error location is the colliding element
46
+ error('name-duplicate-element', elementPath, { '#': 'flatten-fkey-exists', name: fk.prefix, art: elementName });
47
+ }
48
+ // attach a proper $path
49
+ setProp(element, '$path', elementPath);
50
+ return acc;
51
+ }, Object.create(null));
10
52
 
11
- const { error } = messageFunctions;
12
-
13
- applyTransformations(csn, { elements: createForeignKeysInCsn, params: createForeignKeysInCsn},
14
- [], iterateOptions);
15
-
16
- /**
17
- * Process a given elements or params dictionary and create foreign key elements.
18
- *
19
- * @param {object} parent The thing HAVING params or elements
20
- * @param {string} prop
21
- * @param {object} dict The params or elements thing
22
- * @param {CSN.Path} path
23
- */
24
- function createForeignKeysInCsn( parent, prop, dict, path ) {
25
- const orderedElements = [];
26
- // First, generate the FK elements for given element
27
- Object.entries(dict).forEach(([elementName, element]) => {
28
- orderedElements.push([ elementName, element ]);
29
- if (!csnUtils.isManagedAssociation(element)) return;
30
- const elementPath = path.concat(prop, elementName);
31
- const generatedForeignKeys = createForeignKeysForElement(elementPath, element, elementName, csn, options, '_');
32
-
33
- // Second, finalize the generated FK elements
34
- const refCount = generatedForeignKeys.reduce((acc, fk) => {
35
- // count duplicates
36
- if (acc[fk.prefix])
37
- acc[fk.prefix]++;
38
- else
39
- acc[fk.prefix] = 1;
40
-
41
- // check for name clash with existing elements
42
- if (parent[prop][fk.prefix] && isDeepEqual(element, parent[prop][fk.prefix], true)) {
43
- // error location is the colliding element
44
- error('name-duplicate-element', elementPath, { '#': 'flatten-fkey-exists', name: fk.prefix, art: elementName });
45
- }
46
- // attach a proper $path
47
- setProp(element, '$path', elementPath);
48
- return acc;
49
- }, Object.create(null));
50
-
51
- // set default for single foreign key from association (if available)
52
- if (element.default?.val !== undefined && generatedForeignKeys.length === 1)
53
+ // set default for single foreign key from association (if available)
54
+ if (element.default?.val !== undefined && generatedForeignKeys.length === 1)
53
55
  generatedForeignKeys[0].foreignKey.default = element.default;
54
56
 
55
- // check for duplicate foreign keys
56
- Object.entries(refCount).forEach(([ name, occ ]) => {
57
- if (occ > 1)
58
- error('name-duplicate-element', elementPath, { '#': 'flatten-fkey-gen', name, art: elementName });
59
- });
60
- if (element.keys) {
61
- if (options.transformation === 'effective')
62
- delete element.default;
63
- }
64
-
65
- adaptAnnotationsRefs(generatedForeignKeys, csnUtils, messageFunctions);
66
- setProp(element, '$generatedForeignKeys', generatedForeignKeys.map(gfk => {
67
- return { name: gfk.prefix, origin: gfk.originalKey, source: gfk.sourceElement } } ));
68
- orderedElements.push(...generatedForeignKeys.map(gfk => [ gfk.prefix, gfk.foreignKey ]));
57
+ // check for duplicate foreign keys
58
+ Object.entries(refCount).forEach(([ name, occ ]) => {
59
+ if (occ > 1)
60
+ error('name-duplicate-element', elementPath, { '#': 'flatten-fkey-gen', name, art: elementName });
69
61
  });
62
+ if (element.keys) {
63
+ if (options.transformation === 'effective')
64
+ delete element.default;
65
+ }
70
66
 
71
- parent[prop] = orderedElements.reduce((elementsAccumulator, [ name, element ]) => {
72
- elementsAccumulator[name] = element;
73
- return elementsAccumulator;
74
- }, Object.create(null));
75
- }
67
+ adaptAnnotationsRefs(generatedForeignKeys, csnUtils, messageFunctions);
68
+ setProp(element, '$generatedForeignKeys', generatedForeignKeys.map(gfk => ({ name: gfk.prefix, origin: gfk.originalKey, source: gfk.sourceElement }) ));
69
+ orderedElements.push(...generatedForeignKeys.map(gfk => [ gfk.prefix, gfk.foreignKey ]));
70
+ });
71
+
72
+ parent[prop] = orderedElements.reduce((elementsAccumulator, [ name, element ]) => {
73
+ elementsAccumulator[name] = element;
74
+ return elementsAccumulator;
75
+ }, Object.create(null));
76
+ }
76
77
 
77
78
  function createForeignKeysForElement(path, element, prefix, csn, options, pathDelimiter, lvl = 0, originalKey = {} ) {
78
79
  const special$self = !csn?.definitions?.$self && '$self';
@@ -112,14 +113,14 @@ function createForeignKeyElements(csn, options, messageFunctions, csnUtils, iter
112
113
  const continuePath = getContinuePath([ 'keys', keyIndex ]);
113
114
  const alias = key.as || implicitAs(key.ref);
114
115
  const result = csnUtils.inspectRef(continuePath);
115
- let gfks = createForeignKeysForElement(result, result.art, alias, csn, options, pathDelimiter, lvl + 1,
116
- lvl === 0 ? key : originalKey);
116
+ const gfks = createForeignKeysForElement(result, result.art, alias, csn, options, pathDelimiter, lvl + 1,
117
+ lvl === 0 ? key : originalKey);
117
118
  if (lvl === 0) {
118
119
  gfks.forEach(gfk => gfk.keyAnnotations.push( ...copyAnnotations(key, gfk.foreignKey)));
119
- Object.keys(key).forEach( prop => {
120
+ Object.keys(key).forEach( (prop) => {
120
121
  // once applied -> remove the annotations from the keys array, to keep the OData CSN size as small as possible
121
122
  if (prop[0] === '@')
122
- delete key[prop]
123
+ delete key[prop];
123
124
  });
124
125
  }
125
126
  fks = fks.concat(gfks);
@@ -138,18 +139,20 @@ function createForeignKeyElements(csn, options, messageFunctions, csnUtils, iter
138
139
  // we have reached a leaf element, create a foreign key
139
140
  else if ((finalElement.type == null || isBuiltinType(finalElement.type)) && !finalElement.on) {
140
141
  const newFk = Object.create(null);
141
- [ 'type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type', ...EdmTypeFacetNames.map(f => `@odata.${f}`) ].forEach((prop) => {
142
+ [ 'type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type', ...EdmTypeFacetNames.map(f => `@odata.${ f }`) ].forEach((prop) => {
142
143
  // copy props from original element to preserve derived types!
143
144
  if (element[prop] !== undefined)
144
145
  newFk[prop] = element[prop];
145
146
  });
146
- let result = { prefix, foreignKey: newFk, originalKey, keyAnnotations: [], sourceElement: finalElement }
147
+ const result = {
148
+ prefix, foreignKey: newFk, originalKey, keyAnnotations: [], sourceElement: finalElement,
149
+ };
147
150
  return [ result ];
148
151
  }
149
152
 
150
153
  fks.forEach((fk) => {
151
154
  // prepend current prefix
152
- fk.prefix = `${prefix}${pathDelimiter}${fk.prefix}`;
155
+ fk.prefix = `${ prefix }${ pathDelimiter }${ fk.prefix }`;
153
156
  // if this is the entry association, decorate the final foreign keys with the association props/annos
154
157
  if (lvl === 0) {
155
158
  fk.foreignKey['@odata.foreignKey4'] = prefix;
@@ -158,7 +161,7 @@ function createForeignKeyElements(csn, options, messageFunctions, csnUtils, iter
158
161
  fkPath.push(fk.prefix);
159
162
  setProp(fk.foreignKey, '$path', fkPath);
160
163
 
161
- const allowedOverwriteAnnotationNames = ['@odata.Type', ...EdmTypeFacetNames.map(f => `@odata.${f}`)];
164
+ const allowedOverwriteAnnotationNames = [ '@odata.Type', ...EdmTypeFacetNames.map(f => `@odata.${ f }`) ];
162
165
  const validAnnoNames = Object.keys(element).filter(pn => pn[0] === '@' && !allowedOverwriteAnnotationNames.includes(pn));
163
166
  copyAnnotations(element, fk.foreignKey, false, {}, validAnnoNames);
164
167
  const overwriteAnnoNames = Object.keys(element).filter(pn => allowedOverwriteAnnotationNames.includes(pn));