@sap/cds-compiler 6.2.2 → 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 (57) hide show
  1. package/CHANGELOG.md +35 -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/onConditions.js +2 -2
  11. package/lib/checks/queryNoDbArtifacts.js +16 -15
  12. package/lib/checks/types.js +1 -1
  13. package/lib/checks/utils.js +30 -6
  14. package/lib/checks/validator.js +4 -5
  15. package/lib/compiler/checks.js +47 -18
  16. package/lib/compiler/index.js +88 -6
  17. package/lib/compiler/resolve.js +7 -7
  18. package/lib/compiler/tweak-assocs.js +47 -25
  19. package/lib/gen/BaseParser.js +1 -1
  20. package/lib/gen/CdlGrammar.checksum +1 -1
  21. package/lib/gen/CdlParser.js +381 -378
  22. package/lib/gen/Dictionary.json +0 -2
  23. package/lib/model/csnRefs.js +9 -4
  24. package/lib/model/csnUtils.js +67 -2
  25. package/lib/optionProcessor.js +2 -3
  26. package/lib/parsers/AstBuildingParser.js +5 -6
  27. package/lib/render/toCdl.js +10 -4
  28. package/lib/render/utils/common.js +4 -2
  29. package/lib/transform/db/assertUnique.js +2 -1
  30. package/lib/transform/db/associations.js +37 -1
  31. package/lib/transform/db/assocsToQueries/transformExists.js +21 -32
  32. package/lib/transform/db/assocsToQueries/utils.js +1 -1
  33. package/lib/transform/db/cdsPersistence.js +1 -1
  34. package/lib/transform/db/expansion.js +37 -36
  35. package/lib/transform/draft/db.js +20 -20
  36. package/lib/transform/draft/odata.js +38 -40
  37. package/lib/transform/effective/associations.js +1 -1
  38. package/lib/transform/effective/flattening.js +40 -47
  39. package/lib/transform/effective/main.js +6 -4
  40. package/lib/transform/forOdata.js +135 -115
  41. package/lib/transform/forRelationalDB.js +151 -142
  42. package/lib/transform/localized.js +116 -109
  43. package/lib/transform/odata/adaptAnnotationRefs.js +21 -16
  44. package/lib/transform/odata/createForeignKeys.js +73 -70
  45. package/lib/transform/odata/flattening.js +216 -200
  46. package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +47 -45
  47. package/lib/transform/odata/toFinalBaseType.js +40 -39
  48. package/lib/transform/odata/typesExposure.js +151 -133
  49. package/lib/transform/odata/utils.js +7 -6
  50. package/lib/transform/parseExpr.js +165 -162
  51. package/lib/transform/transformUtils.js +184 -551
  52. package/lib/transform/translateAssocsToJoins.js +510 -571
  53. package/lib/transform/tupleExpansion.js +495 -0
  54. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
  55. package/package.json +1 -1
  56. package/lib/base/cleanSymbols.js +0 -17
  57. package/lib/checks/nonexpandableStructured.js +0 -39
@@ -1,4 +1,4 @@
1
- 'use strict'
1
+ 'use strict';
2
2
 
3
3
  const { applyTransformations, transformAnnotationExpression } = require('../../model/csnUtils');
4
4
  const { isBuiltinType } = require('../../base/builtins');
@@ -8,61 +8,62 @@ function replaceForeignKeyRefsInExpressionAnnotations(csn, options, messageFunct
8
8
  const transformers = {
9
9
  elements: processRef,
10
10
  params: processRef,
11
- actions: processRef
11
+ actions: processRef,
12
12
  // '@': processRef
13
13
  };
14
14
  applyTransformations(csn, transformers, [ processRef ], iterateOptions);
15
15
 
16
16
  function processRef(parent, prop, _dict, path) {
17
- transformAnnotationExpression(parent, prop,
18
- {
19
- ref: (parent, _prop, ref, path, _p, _ppn, ctx) => {
20
- const { art, links } =
21
- (parent._art && parent._links) ?
22
- { art: parent._art, links: parent._links } :
23
- csnUtils.inspectRef(path);
24
- // if a reference points to a structure(managed assoc or structured element), then we do not process
25
- // as we can't guess which specific foreign key is targeted
26
- if (!art || csnUtils.isManagedAssociation(art) || csnUtils.isStructured(art)) return;
17
+ transformAnnotationExpression(parent, prop, {
18
+ ref: (parent, _prop, ref, path, _p, _ppn, ctx) => {
19
+ const { art, links }
20
+ = (parent._art && parent._links)
21
+ ? { art: parent._art, links: parent._links }
22
+ : csnUtils.inspectRef(path);
23
+ // if a reference points to a structure(managed assoc or structured element), then we do not process
24
+ // as we can't guess which specific foreign key is targeted
25
+ if (!art || csnUtils.isManagedAssociation(art) || csnUtils.isStructured(art))
26
+ return;
27
27
 
28
- const allMngAssocsInRef = links.filter(link => csnUtils.isManagedAssociation(link.art));
29
- if (!allMngAssocsInRef.length) return;
30
- let firstAssocToProcess = allMngAssocsInRef[0];
28
+ const allMngAssocsInRef = links.filter(link => csnUtils.isManagedAssociation(link.art));
29
+ if (!allMngAssocsInRef.length)
30
+ return;
31
+ let firstAssocToProcess = allMngAssocsInRef[0];
31
32
 
32
- const mngAssocsWithFilter = allMngAssocsInRef.filter(assoc => typeof ref[assoc.idx] !== 'string');
33
- if (mngAssocsWithFilter.length) {
34
- const refTail = links.slice(mngAssocsWithFilter.at(-1).idx + 1);
35
- firstAssocToProcess = refTail.find(link => csnUtils.isManagedAssociation(link.art));
36
- }
33
+ const mngAssocsWithFilter = allMngAssocsInRef.filter(assoc => typeof ref[assoc.idx] !== 'string');
34
+ if (mngAssocsWithFilter.length) {
35
+ const refTail = links.slice(mngAssocsWithFilter.at(-1).idx + 1);
36
+ firstAssocToProcess = refTail.find(link => csnUtils.isManagedAssociation(link.art));
37
+ }
37
38
 
38
- const match = findMatchingForeignKeyForAssoc(firstAssocToProcess, art, ref, links);
39
- if (match) {
40
- const refHead = ref.slice(0, match.idx);
41
- parent.ref = [...refHead, match.fkName];
42
- if (ctx?.annoExpr?.['=']) {
43
- ctx.annoExpr['='] = true;
44
- }
45
- }
39
+ const match = findMatchingForeignKeyForAssoc(firstAssocToProcess, art, ref, links);
40
+ if (match) {
41
+ const refHead = ref.slice(0, match.idx);
42
+ parent.ref = [ ...refHead, match.fkName ];
43
+ if (ctx?.annoExpr?.['='])
44
+ ctx.annoExpr['='] = true;
46
45
  }
47
46
  },
48
- path);
47
+ },
48
+ path);
49
49
  }
50
50
 
51
51
  function findMatchingForeignKeyForAssoc(assoc, refArt, ref, links) {
52
- if (!assoc) return undefined;
52
+ if (!assoc)
53
+ return undefined;
53
54
 
54
55
  const expectedFkName = findExpectedFkName(assoc, ref, links);
55
56
  const gfks = assoc.art?.$generatedForeignKeys;
56
- if (!gfks) return undefined;
57
+ if (!gfks)
58
+ return undefined;
57
59
  const matchedFk = gfks.find(fk => fk.source === refArt && fk.name === expectedFkName);
58
- if (matchedFk) {
60
+ if (matchedFk)
59
61
  return { fkName: matchedFk.name, idx: assoc.idx };
60
- } else {
61
- // try to find FK substitution in the next assoc in the ref (if there is such assoc)
62
- const refTail = links.slice(assoc.idx + 1);
63
- const nextAssoc = refTail.find(link => csnUtils.isManagedAssociation(link.art));
64
- return findMatchingForeignKeyForAssoc(nextAssoc, refArt, ref, links);
65
- }
62
+
63
+ // try to find FK substitution in the next assoc in the ref (if there is such assoc)
64
+ const refTail = links.slice(assoc.idx + 1);
65
+ const nextAssoc = refTail.find(link => csnUtils.isManagedAssociation(link.art));
66
+ return findMatchingForeignKeyForAssoc(nextAssoc, refArt, ref, links);
66
67
 
67
68
 
68
69
  function findExpectedFkName(assoc, ref, links) {
@@ -77,16 +78,17 @@ function replaceForeignKeyRefsInExpressionAnnotations(csn, options, messageFunct
77
78
  bufferRef.push(ref[i]);
78
79
  if (csnUtils.isManagedAssociation(link.art)) {
79
80
  const subFkName = findExpectedFkName(link, ref, links);
80
- if (!subFkName) return undefined;
81
- expectedFkName += bufferRef.length > 1 ?
82
- `_${bufferRef.slice(0, -1).join('_')}_${subFkName}` :
83
- `_${subFkName}`;
81
+ if (!subFkName)
82
+ return undefined;
83
+ expectedFkName += bufferRef.length > 1
84
+ ? `_${ bufferRef.slice(0, -1).join('_') }_${ subFkName }`
85
+ : `_${ subFkName }`;
84
86
  break;
85
- } else if (isBuiltinType(link.art.type)) {
86
- expectedFkName += `_${refAliasMapping[bufferRef.join('_')] || ref[i]}`;
87
+ }
88
+ else if (isBuiltinType(link.art.type)) {
89
+ expectedFkName += `_${ refAliasMapping[bufferRef.join('_')] || ref[i] }`;
87
90
  bufferRef = [];
88
91
  }
89
-
90
92
  }
91
93
  return expectedFkName;
92
94
  }
@@ -21,8 +21,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
21
21
  expandToFinalBaseType(member.items, defName);
22
22
  expandToFinalBaseType(member.returns, defName);
23
23
  expandToFinalBaseType(member.returns && member.returns.items, defName);
24
-
25
- }, ['definitions', defName]);
24
+ }, [ 'definitions', defName ]);
26
25
 
27
26
  expandToFinalBaseType(def, defName);
28
27
  expandToFinalBaseType(def.items, defName);
@@ -64,18 +63,16 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
64
63
  // type Foo: array of Bar; type Bar: { qux: Integer };
65
64
  // In the type Foo we expand the first level of elements of the items or
66
65
  // type Foo: array of { qux: Integer };
67
- if (def.kind === 'type' && def.items && isArtifactInSomeService(defName, services)) {
66
+ if (def.kind === 'type' && def.items && isArtifactInSomeService(defName, services))
68
67
  expandFirstLevelOfArrayed(def);
69
- }
70
68
  });
71
69
 
72
- if(isBetaEnabled(options, 'odataTerms')) {
70
+ if (isBetaEnabled(options, 'odataTerms')) {
73
71
  forEachGeneric(csn, 'vocabularies', (def, defName) => {
74
72
  forEachMemberRecursively(def, (member) => {
75
73
  expandToFinalBaseType(member, defName);
76
74
  expandToFinalBaseType(member.items, defName);
77
-
78
- }, ['vocabularies', defName]);
75
+ }, [ 'vocabularies', defName ]);
79
76
 
80
77
  expandToFinalBaseType(def, defName);
81
78
  expandToFinalBaseType(def.items, defName);
@@ -96,12 +93,14 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
96
93
  }
97
94
 
98
95
  function expandToFinalBaseType(node, defName) {
99
- if (!node) return;
100
- if (node.kind === 'event') return;
96
+ if (!node)
97
+ return;
98
+ if (node.kind === 'event')
99
+ return;
101
100
 
102
- if(node.type && !isBuiltinType(node.type)) {
101
+ if (node.type && !isBuiltinType(node.type)) {
103
102
  const finalBaseType = csnUtils.getFinalTypeInfo(node.type);
104
- if(finalBaseType == null) {
103
+ if (finalBaseType == null) {
105
104
  /*
106
105
  type could not be resolved, delete type property to be equal to a typeless element
107
106
  definition. Today, all type refs must be resolvable, input validations
@@ -138,8 +137,9 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
138
137
  else if (csnUtils.isStructured(finalBaseType)) {
139
138
  cloneElements(node, finalBaseType);
140
139
  }
141
- else if (node.type && node.items)
140
+ else if (node.type && node.items) {
142
141
  delete node.type;
142
+ }
143
143
  }
144
144
  else {
145
145
  /*
@@ -157,6 +157,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
157
157
  type B: C; -> { ... }
158
158
  type C { .... };
159
159
  */
160
+ // eslint-disable-next-line no-lonely-if
160
161
  if (isBuiltinType(finalBaseType.type)) {
161
162
  /*
162
163
  use transformUtils::toFinalBaseType for the moment,
@@ -173,12 +174,12 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
173
174
  }
174
175
  }
175
176
  }
176
- if (/*the resolved type is not built in*/ !isBuiltinType(node.type)) {
177
+ if (/* the resolved type is not built in */ !isBuiltinType(node.type)) {
177
178
  // handle array of defined via a named type
178
179
  // example in actions: 'action act() return Primitive; type Primitive: array of String;'
179
180
  const currService = csnUtils.getServiceName(defName);
180
181
  const isArrayOfBuiltin = finalBaseType.items &&
181
- isBuiltinType(csnUtils.getFinalTypeInfo(finalBaseType.items.type)?.type)
182
+ isBuiltinType(csnUtils.getFinalTypeInfo(finalBaseType.items.type)?.type);
182
183
  if (isArrayOfBuiltin && (!isArtifactInService(node.type, currService) || !isV4)) {
183
184
  node.items = finalBaseType.items;
184
185
  delete node.type;
@@ -190,60 +191,61 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
190
191
  function cloneElements(node, finalBaseType) {
191
192
  let clone;
192
193
  // do the clone only if really needed
193
- if((finalBaseType.items && !node.items) ||
194
+ if ((finalBaseType.items && !node.items) ||
194
195
  (finalBaseType.elements && !node.elements)) {
195
196
  // clone the definition not another clone
196
- let _type = node._type;
197
- while(_type._type && !_type.items && !_type.elements)
197
+ let { _type } = node;
198
+ while (_type._type && !_type.items && !_type.elements)
198
199
  _type = _type._type;
199
200
  clone = cloneCsnNonDict(_type, { ...options, hiddenPropertiesToClone: [ '_type' ] });
200
201
  fitClonedElementsIntoParent(clone, node, _type.$path);
201
202
  }
202
203
  if (finalBaseType.items) {
203
204
  delete node.type;
204
- if(!node.items)
205
+ if (!node.items)
205
206
  Object.assign(node, { items: clone.items });
206
207
  }
207
208
  if (finalBaseType.elements) {
208
- if(!finalBaseType.items)
209
+ if (!finalBaseType.items)
209
210
  delete node.type;
210
- if(!node.elements)
211
+ if (!node.elements)
211
212
  Object.assign(node, { elements: clone.elements });
212
213
  }
213
214
  }
214
215
 
215
216
  function fitClonedElementsIntoParent(clone, node, typeRefCsnPath) {
216
217
  const f = (p) => {
217
- const [h, ...t ] = p;
218
- return `${h}:${t.join('.')}`;
219
- }
218
+ const [ h, ...t ] = p;
219
+ return `${ h }:${ t.join('.') }`;
220
+ };
220
221
  const typeRefStr = f(node.type.ref);
221
222
  const typeRefRootPath = $path2path(typeRefCsnPath);
222
223
  forEachMemberRecursively(clone, (elt, eltName, prop, location) => {
223
224
  const usingPositionStr = f($path2path(location));
224
225
  const eltRootPath = $path2path(elt.$path);
225
226
 
226
- Object.keys(elt).filter(pn => pn[0] === '@').forEach(anno => {
227
+ Object.keys(elt).filter(pn => pn[0] === '@').forEach((anno) => {
227
228
  transformAnnotationExpression(elt, anno, {
228
229
  ref: (parent, prop, xpr, csnPath, _p, _ppn, ctx) => {
229
230
  let prefixMatch = true;
230
231
  const head = xpr[0].id || xpr[0];
231
232
  if (head === '$self') {
232
- for (let i = 1; i < typeRefRootPath.length && prefixMatch; i++){
233
+ for (let i = 1; i < typeRefRootPath.length && prefixMatch; i++)
233
234
  prefixMatch = (xpr[i].id || xpr[i]) === typeRefRootPath[i];
234
- }
235
- if(prefixMatch && xpr.length > typeRefRootPath.length) {
236
- if(xpr.length >= eltRootPath.length)
237
- parent[prop] = [ ...xpr.slice(eltRootPath.length-1)];
235
+
236
+ if (prefixMatch && xpr.length > typeRefRootPath.length) {
237
+ if (xpr.length >= eltRootPath.length)
238
+ parent[prop] = [ ...xpr.slice(eltRootPath.length - 1) ];
238
239
  else
239
- parent[prop] = [ '$self', ...xpr.slice(typeRefRootPath.length)];
240
- if(ctx?.annoExpr?.['='])
240
+ parent[prop] = [ '$self', ...xpr.slice(typeRefRootPath.length) ];
241
+ if (ctx?.annoExpr?.['='])
241
242
  ctx.annoExpr['='] = true;
242
243
  }
243
244
  else {
244
- error('odata-anno-xpr-ref', csnPath,
245
- { anno, elemref: xpr.join('.'), name: usingPositionStr, code: typeRefStr });
246
- }
245
+ error('odata-anno-xpr-ref', csnPath, {
246
+ anno, elemref: xpr.join('.'), name: usingPositionStr, code: typeRefStr,
247
+ });
248
+ }
247
249
  }
248
250
  },
249
251
  }, elt.$path);
@@ -260,8 +262,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
260
262
  - the referred type is defined in the service
261
263
  */
262
264
  function isExpandable(finalBaseType) {
263
- // in V4 we should use TypeDefinitions whenever possible, thus in case the final type of a field is
264
- // a builtin from the service - do not expand to the final base type
265
+ // in V4 we should use TypeDefinitions whenever possible, thus in case the final type of a field is
266
+ // a builtin from the service - do not expand to the final base type
265
267
 
266
268
  const currService = csnUtils.getServiceName(defName);
267
269
  const isBuiltin = isBuiltinType(finalBaseType.type);
@@ -270,7 +272,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
270
272
  return !isV4 || !(isBuiltin && !isAssoc && isInCurServ);
271
273
  }
272
274
 
273
- // convert $path to path starting at main artifact
275
+ // convert $path to path starting at main artifact
274
276
  function $path2path( p ) {
275
277
  const path = [];
276
278
  let env = csn;
@@ -279,7 +281,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
279
281
  env = env[ps];
280
282
  if (env && env.constructor === Object) {
281
283
  path.push(ps);
282
- // jump over many items but not if this is an element
284
+ // jump over many items but not if this is an element
283
285
  if (env.items) {
284
286
  env = env.items;
285
287
  if (p[i + 1] === 'items')
@@ -291,7 +293,6 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, e
291
293
  }
292
294
  return path;
293
295
  }
294
-
295
296
  }
296
297
  }
297
298