@sap/cds-compiler 6.2.2 → 6.3.4
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 +49 -0
- package/bin/cdsc.js +11 -4
- package/lib/api/options.js +1 -1
- package/lib/base/message-registry.js +36 -7
- package/lib/base/messages.js +11 -4
- package/lib/base/model.js +0 -1
- package/lib/checks/assocOutsideService.js +17 -30
- package/lib/checks/checkForTypes.js +0 -18
- package/lib/checks/checkPathsInStoredCalcElement.js +2 -1
- package/lib/checks/enricher.js +15 -3
- package/lib/checks/onConditions.js +2 -2
- package/lib/checks/queryNoDbArtifacts.js +16 -15
- package/lib/checks/types.js +1 -1
- package/lib/checks/utils.js +30 -6
- package/lib/checks/validator.js +36 -37
- package/lib/compiler/assert-consistency.js +1 -1
- package/lib/compiler/checks.js +47 -18
- package/lib/compiler/extend.js +1 -1
- package/lib/compiler/index.js +88 -6
- package/lib/compiler/populate.js +1 -1
- package/lib/compiler/resolve.js +7 -7
- package/lib/compiler/tweak-assocs.js +48 -25
- package/lib/edm/annotations/edmJson.js +19 -19
- package/lib/gen/BaseParser.js +1 -1
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +384 -383
- package/lib/gen/Dictionary.json +0 -2
- package/lib/json/to-csn.js +3 -2
- package/lib/model/csnRefs.js +9 -4
- package/lib/model/csnUtils.js +67 -2
- package/lib/optionProcessor.js +2 -3
- package/lib/parsers/AstBuildingParser.js +12 -11
- package/lib/render/toCdl.js +10 -4
- package/lib/render/utils/common.js +4 -2
- package/lib/transform/db/assertUnique.js +2 -1
- package/lib/transform/db/associations.js +37 -1
- package/lib/transform/db/assocsToQueries/transformExists.js +21 -32
- package/lib/transform/db/assocsToQueries/utils.js +1 -1
- package/lib/transform/db/cdsPersistence.js +1 -1
- package/lib/transform/db/expansion.js +37 -36
- package/lib/transform/draft/db.js +20 -20
- package/lib/transform/draft/odata.js +38 -40
- package/lib/transform/effective/associations.js +1 -1
- package/lib/transform/effective/flattening.js +40 -47
- package/lib/transform/effective/main.js +6 -4
- package/lib/transform/forOdata.js +135 -115
- package/lib/transform/forRelationalDB.js +151 -142
- package/lib/transform/localized.js +116 -109
- package/lib/transform/odata/adaptAnnotationRefs.js +21 -16
- package/lib/transform/odata/createForeignKeys.js +73 -70
- package/lib/transform/odata/flattening.js +216 -200
- package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +47 -45
- package/lib/transform/odata/toFinalBaseType.js +40 -39
- package/lib/transform/odata/typesExposure.js +151 -133
- package/lib/transform/odata/utils.js +7 -6
- package/lib/transform/parseExpr.js +165 -162
- package/lib/transform/transformUtils.js +184 -551
- package/lib/transform/translateAssocsToJoins.js +510 -571
- package/lib/transform/tupleExpansion.js +495 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
- package/package.json +1 -1
- package/lib/base/cleanSymbols.js +0 -17
- 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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const allMngAssocsInRef = links.filter(link => csnUtils.isManagedAssociation(link.art));
|
|
29
|
+
if (!allMngAssocsInRef.length)
|
|
30
|
+
return;
|
|
31
|
+
let firstAssocToProcess = allMngAssocsInRef[0];
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
47
|
+
},
|
|
48
|
+
path);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function findMatchingForeignKeyForAssoc(assoc, refArt, ref, links) {
|
|
52
|
-
if (!assoc)
|
|
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)
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
if (!subFkName)
|
|
82
|
+
return undefined;
|
|
83
|
+
expectedFkName += bufferRef.length > 1
|
|
84
|
+
? `_${ bufferRef.slice(0, -1).join('_') }_${ subFkName }`
|
|
85
|
+
: `_${ subFkName }`;
|
|
84
86
|
break;
|
|
85
|
-
}
|
|
86
|
-
|
|
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)
|
|
100
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
264
|
-
|
|
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
|
-
|
|
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
|
|