@sap/cds-compiler 6.5.2 → 6.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.
- package/CHANGELOG.md +26 -0
- package/lib/api/main.js +0 -3
- package/lib/base/builtins.js +2 -1
- package/lib/base/message-registry.js +0 -1
- package/lib/base/model.js +0 -1
- package/lib/checks/sql-snippets.js +8 -0
- package/lib/checks/validator.js +4 -2
- package/lib/compiler/define.js +1 -1
- package/lib/compiler/extend.js +54 -22
- package/lib/compiler/generate.js +31 -8
- package/lib/compiler/index.js +2 -2
- package/lib/compiler/kick-start.js +18 -28
- package/lib/compiler/propagator.js +20 -1
- package/lib/compiler/resolve.js +24 -0
- package/lib/compiler/shared.js +49 -9
- package/lib/compiler/utils.js +1 -0
- package/lib/edm/annotations/edmJson.js +111 -37
- package/lib/edm/annotations/genericTranslation.js +3 -1
- package/lib/main.d.ts +0 -3
- package/lib/main.js +2 -0
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/associations.js +24 -15
- package/lib/transform/db/flattening.js +1 -1
- package/lib/transform/db/views.js +0 -39
- package/lib/transform/effective/associations.js +5 -55
- package/lib/transform/effective/main.js +4 -2
- package/lib/transform/effective/misc.js +1 -1
- package/lib/transform/effective/types.js +36 -12
- package/lib/transform/forOdata.js +126 -3
- package/lib/transform/forRelationalDB.js +13 -4
- package/lib/transform/transformUtils.js +51 -1
- package/lib/transform/translateAssocsToJoins.js +43 -19
- package/package.json +1 -1
|
@@ -21,7 +21,7 @@ const { conditionAsTree, expressionAsTree } = require('../../model/xprAsTree');
|
|
|
21
21
|
* @returns {object}
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
24
|
+
function xpr2edmJson( carrier, anno, location, options, messageFunctions, genericTranslationHelpers ) {
|
|
25
25
|
const { message, error } = messageFunctions;
|
|
26
26
|
|
|
27
27
|
const annoVal = carrier[anno];
|
|
@@ -77,9 +77,9 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
77
77
|
};
|
|
78
78
|
//----------------------------------
|
|
79
79
|
// Error transformer
|
|
80
|
-
const notADynExpr = (parent, op, xpr, csnPath, parentParent, parentProp,
|
|
80
|
+
const notADynExpr = (parent, op, xpr, csnPath, parentParent, parentProp, ctx) => {
|
|
81
81
|
error('odata-anno-xpr', location, {
|
|
82
|
-
anno, op: txt ?? op, '#': 'notadynexpr',
|
|
82
|
+
anno, op: ctx.txt ?? op, '#': 'notadynexpr',
|
|
83
83
|
});
|
|
84
84
|
delete parent[op];
|
|
85
85
|
};
|
|
@@ -95,26 +95,26 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
95
95
|
'.': notADynExpr,
|
|
96
96
|
exists: notADynExpr,
|
|
97
97
|
SELECT: notADynExpr,
|
|
98
|
-
SET: (p, o) => notADynExpr(p, o, null, null, null, null, 'UNION'),
|
|
98
|
+
SET: (p, o, xpr, csnPath, parentParent, parentProp, ctx) => notADynExpr(p, o, null, null, null, null, Object.assign(ctx, { txt: 'UNION' })),
|
|
99
99
|
like: notADynExpr,
|
|
100
100
|
new: notADynExpr,
|
|
101
101
|
};
|
|
102
102
|
|
|
103
103
|
//----------------------------------
|
|
104
104
|
// list is a $Collection => []
|
|
105
|
-
transform.list = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
|
|
105
|
+
transform.list = (parent, prop, xpr, csnPath, parentParent, parentProp, ctx) => {
|
|
106
106
|
parentParent[parentProp] = xpr.filter(a => a);
|
|
107
|
-
transformExpression(parentParent, parentProp, transform);
|
|
107
|
+
transformExpression(parentParent, parentProp, transform, csnPath, ctx);
|
|
108
108
|
};
|
|
109
109
|
// XPR
|
|
110
|
-
transform.xpr = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
|
|
110
|
+
transform.xpr = (parent, prop, xpr, csnPath, parentParent, parentProp, ctx) => {
|
|
111
111
|
// eliminate 'xpr' node by pulling up xpr node to its parent
|
|
112
112
|
parentParent[parentProp] = xpr;
|
|
113
|
-
transformExpression(parentParent, parentProp, transform);
|
|
113
|
+
transformExpression(parentParent, parentProp, transform, csnPath, ctx);
|
|
114
114
|
};
|
|
115
115
|
//----------------------------------
|
|
116
116
|
// CASE
|
|
117
|
-
transform.case = (parent, prop, caseExpr) => {
|
|
117
|
+
transform.case = (parent, prop, caseExpr, csnPath, parentParent, parentProp, ctx) => {
|
|
118
118
|
// transform simple case expression into search case expression
|
|
119
119
|
// case <expr1> when <expr2> ... ===> case when <expr1> = <expr2> ...
|
|
120
120
|
let i = 0;
|
|
@@ -141,14 +141,14 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
141
141
|
curIf.$If.push(caseExpr[i]);
|
|
142
142
|
parent.$If = edmIf.$If;
|
|
143
143
|
delete parent.case;
|
|
144
|
-
transformExpression(parent, undefined, transform);
|
|
144
|
+
transformExpression(parent, undefined, transform, csnPath, ctx);
|
|
145
145
|
};
|
|
146
|
-
transform.$If = (_parent, _prop, expr) => {
|
|
147
|
-
transformExpression(expr, undefined, transform);
|
|
146
|
+
transform.$If = (_parent, _prop, expr, csnPath, parentParent, parentProp, ctx) => {
|
|
147
|
+
transformExpression(expr, undefined, transform, csnPath, ctx);
|
|
148
148
|
};
|
|
149
149
|
//----------------------------------
|
|
150
150
|
// Cast => $Cast
|
|
151
|
-
transform.cast = (parent, prop, castExpr, csnPath, parentParent, parentProp) => {
|
|
151
|
+
transform.cast = (parent, prop, castExpr, csnPath, parentParent, parentProp, ctx) => {
|
|
152
152
|
const csnType = castExpr[0];
|
|
153
153
|
// try to resolve to final scalar base type and use that instead of derived type
|
|
154
154
|
if (!isBuiltinType(csnType.type)) {
|
|
@@ -183,7 +183,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
183
183
|
|
|
184
184
|
|
|
185
185
|
parentParent[parentProp] = castFunc;
|
|
186
|
-
transformExpression(parentParent, parentProp, transform);
|
|
186
|
+
transformExpression(parentParent, parentProp, transform, csnPath, ctx);
|
|
187
187
|
};
|
|
188
188
|
//----------------------------------
|
|
189
189
|
const evalArgs = (argDef, args, propName) => {
|
|
@@ -215,11 +215,11 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
215
215
|
};
|
|
216
216
|
|
|
217
217
|
// Binary Operator Macro
|
|
218
|
-
const op = (opStr, exact = 2) => (parent, prop, xpr) => {
|
|
218
|
+
const op = (opStr, exact = 2) => (parent, prop, xpr, csnPath, parentParent, parentProp, ctx) => {
|
|
219
219
|
evalArgs({ exact }, xpr, prop);
|
|
220
220
|
parent[opStr] = xpr;
|
|
221
221
|
delete parent[prop];
|
|
222
|
-
transformExpression(parent, undefined, transform);
|
|
222
|
+
transformExpression(parent, undefined, transform, csnPath, ctx);
|
|
223
223
|
};
|
|
224
224
|
//----------------------------------
|
|
225
225
|
// LOGICAL
|
|
@@ -245,7 +245,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
245
245
|
transform.$Lt = noOp;
|
|
246
246
|
transform['<='] = op('$Le');
|
|
247
247
|
transform.$Le = noOp;
|
|
248
|
-
transform.in = (parent, prop, xpr) => {
|
|
248
|
+
transform.in = (parent, prop, xpr, csnPath, parentParent, parentProp, ctx) => {
|
|
249
249
|
let args = xpr[1].list;
|
|
250
250
|
if (!args) {
|
|
251
251
|
if (Array.isArray(xpr[1].xpr))
|
|
@@ -256,12 +256,12 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
256
256
|
evalArgs({ min: 1 }, args, prop);
|
|
257
257
|
parent.$In = [ xpr[0], args ];
|
|
258
258
|
delete parent[prop];
|
|
259
|
-
transformExpression(parent, undefined, transform);
|
|
259
|
+
transformExpression(parent, undefined, transform, csnPath, ctx);
|
|
260
260
|
};
|
|
261
261
|
transform.$In = noOp;
|
|
262
|
-
transform.between = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
|
|
262
|
+
transform.between = (parent, prop, xpr, csnPath, parentParent, parentProp, ctx) => {
|
|
263
263
|
evalArgs({ exact: 2 }, xpr.slice(1), prop);
|
|
264
|
-
transformExpression(xpr, undefined, transform);
|
|
264
|
+
transformExpression(xpr, undefined, transform, csnPath, ctx);
|
|
265
265
|
delete parent[prop];
|
|
266
266
|
parentParent[parentProp]
|
|
267
267
|
= {
|
|
@@ -271,28 +271,28 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
271
271
|
],
|
|
272
272
|
};
|
|
273
273
|
};
|
|
274
|
-
transform['||'] = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
|
|
274
|
+
transform['||'] = (parent, prop, xpr, csnPath, parentParent, parentProp, ctx) => {
|
|
275
275
|
evalArgs({ exact: 2 }, xpr, prop);
|
|
276
|
-
transformExpression(xpr, undefined, transform);
|
|
276
|
+
transformExpression(xpr, undefined, transform, csnPath, ctx);
|
|
277
277
|
delete parent[prop];
|
|
278
278
|
parentParent[parentProp].$Apply = xpr;
|
|
279
279
|
parentParent[parentProp].$Function = 'odata.concat';
|
|
280
280
|
};
|
|
281
281
|
//----------------------------------
|
|
282
282
|
// ARITHMETICAL AND UNARY
|
|
283
|
-
transform['+'] = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
|
|
283
|
+
transform['+'] = (parent, prop, xpr, csnPath, parentParent, parentProp, ctx) => {
|
|
284
284
|
if (Array.isArray(xpr)) {
|
|
285
285
|
op('$Add')(parent, prop, xpr);
|
|
286
286
|
}
|
|
287
287
|
else {
|
|
288
288
|
delete parent[prop];
|
|
289
289
|
parentParent[parentProp] = xpr;
|
|
290
|
-
transformExpression(parentParent, parentProp, transform);
|
|
290
|
+
transformExpression(parentParent, parentProp, transform, csnPath, ctx);
|
|
291
291
|
}
|
|
292
292
|
};
|
|
293
293
|
transform.$Add = noOp;
|
|
294
|
-
transform['-'] = (parent, prop, xpr) => {
|
|
295
|
-
op(Array.isArray(xpr) ? '$Sub' : '$Neg')(parent, prop, xpr);
|
|
294
|
+
transform['-'] = (parent, prop, xpr, csnPath, parentParent, parentProp, ctx) => {
|
|
295
|
+
op(Array.isArray(xpr) ? '$Sub' : '$Neg')(parent, prop, xpr, csnPath, parentParent, parentProp, ctx);
|
|
296
296
|
};
|
|
297
297
|
transform.$Sub = noOp;
|
|
298
298
|
transform.$Neg = noOp;
|
|
@@ -311,12 +311,84 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
311
311
|
else
|
|
312
312
|
parentParent[parentProp] = xpr;
|
|
313
313
|
};
|
|
314
|
-
transform['#'] = (parent, prop, xpr, csnPath, parentParent,
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
314
|
+
transform['#'] = (parent, prop, xpr, csnPath, parentParent, parentParentName, ctx) => {
|
|
315
|
+
// enum reference that was resolved by the compiler
|
|
316
|
+
if (!(parent['#'] && parent.val))
|
|
317
|
+
parent.EnumMember = getEnumMemberValue(xpr, ctx);
|
|
318
|
+
delete parent['#'];
|
|
319
319
|
};
|
|
320
|
+
//----------------------------------
|
|
321
|
+
// Enum type resolving helper functions
|
|
322
|
+
function getEnumMemberValue( enumMember, ctx ) {
|
|
323
|
+
// inner annotations play a role here?
|
|
324
|
+
|
|
325
|
+
const annoNameParts = anno.slice(1).split('.');
|
|
326
|
+
// The term of an annotation is the part between the first '@' and the second '.'
|
|
327
|
+
const termName = `${ annoNameParts[0] }.${ annoNameParts[1] }`;
|
|
328
|
+
const term = genericTranslationHelpers.getDictTerm(termName, options);
|
|
329
|
+
// get term's type
|
|
330
|
+
let typeName = term?.Type;
|
|
331
|
+
let type = genericTranslationHelpers.getDictType(typeName);
|
|
332
|
+
|
|
333
|
+
({ type, typeName } = resolveNestedTypePath(annoNameParts.slice(2), typeName, type));
|
|
334
|
+
|
|
335
|
+
if (type?.$kind === 'EnumType')
|
|
336
|
+
return valiteAndGetFullEnumMemberNameFromEnumType(enumMember, type, typeName);
|
|
337
|
+
if (type?.$kind === 'ComplexType') {
|
|
338
|
+
// If we end up with a ComplexType here, the enum member is defined in a property of the complex type, which is
|
|
339
|
+
// a collection. Only in that case the flattening of annotation stops in the core compiler.
|
|
340
|
+
// Here we need to continue resolving the substructure inside a collection item.
|
|
341
|
+
const indexOfAnnoInCsnPath = ctx.xprCsnPath.indexOf(anno);
|
|
342
|
+
// we take the part of the csnPath after the annotation and remove the numeric values, as these are the array indexes
|
|
343
|
+
const subStructAnnoNames = ctx.xprCsnPath.slice(indexOfAnnoInCsnPath + 1).filter(e => typeof e === 'string');
|
|
344
|
+
({ type, typeName } = resolveNestedTypePath(subStructAnnoNames, typeName, type));
|
|
345
|
+
if (type?.$kind === 'EnumType')
|
|
346
|
+
return valiteAndGetFullEnumMemberNameFromEnumType(enumMember, type, typeName);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (type && type.$kind !== 'EnumType') {
|
|
350
|
+
// resolved to not an EnumType -> warn about it and return what the input value
|
|
351
|
+
message('odata-anno-value', location, {
|
|
352
|
+
anno, value: `"#${ enumMember }"`, type: typeName, '#': 'std',
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
return enumMember;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function resolveNestedTypePath( annoNameParts, typeName, type ) {
|
|
359
|
+
for (let i = 0; i < annoNameParts.length; i++) {
|
|
360
|
+
if (type?.$kind === 'ComplexType') {
|
|
361
|
+
const properties = type.Properties;
|
|
362
|
+
const propName = Object.keys(properties).find(p => p === annoNameParts[i]);
|
|
363
|
+
if (propName) {
|
|
364
|
+
if (properties[propName].startsWith('Collection(')) {
|
|
365
|
+
// strip the 'Collection(...)' wrapper
|
|
366
|
+
typeName = properties[propName].slice(11, -1);
|
|
367
|
+
type = genericTranslationHelpers.getDictType(typeName);
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
type = genericTranslationHelpers.getDictType(properties[propName]);
|
|
371
|
+
typeName = properties[propName];
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return { type, typeName };
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function valiteAndGetFullEnumMemberNameFromEnumType( member, enumType, typeName ) {
|
|
380
|
+
const members = enumType?.Members;
|
|
381
|
+
if (members && !members.includes(member)) {
|
|
382
|
+
message('odata-anno-value', location, {
|
|
383
|
+
anno, type: typeName, value: `"#${ member }"`, rawvalues: members.map(m => `#${ m }`), '#': 'enum',
|
|
384
|
+
});
|
|
385
|
+
return member;
|
|
386
|
+
}
|
|
387
|
+
return `${ typeName }/${ member }`;
|
|
388
|
+
}
|
|
389
|
+
//----------------------------------
|
|
390
|
+
|
|
391
|
+
|
|
320
392
|
transform.ref = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
|
|
321
393
|
// until empty filter syntax is introduced for the annotation expressions,
|
|
322
394
|
// we ignore the filters in order to generate EDMX
|
|
@@ -332,7 +404,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
332
404
|
};
|
|
333
405
|
//----------------------------------
|
|
334
406
|
// Functions
|
|
335
|
-
transform.func = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
|
|
407
|
+
transform.func = (parent, prop, xpr, csnPath, parentParent, parentProp, ctx) => {
|
|
336
408
|
const rewriteArgs = (argDefs, evalVal = true) => {
|
|
337
409
|
Object.entries(argDefs).forEach(([ argName, argDef ]) => {
|
|
338
410
|
const [ foundProps, newArgs ]
|
|
@@ -723,7 +795,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
723
795
|
// $Record ???
|
|
724
796
|
$Collection: () => {
|
|
725
797
|
standard(parentParent, parentProp);
|
|
726
|
-
transformExpression(parentParent, parentProp, transform);
|
|
798
|
+
transformExpression(parentParent, parentProp, transform, csnPath, ctx);
|
|
727
799
|
},
|
|
728
800
|
$Path: () => {
|
|
729
801
|
oneArg(parent, xpr);
|
|
@@ -733,7 +805,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
733
805
|
anno, op: `${ xpr }(…)`, meta: 'string', '#': 'wrongval_meta',
|
|
734
806
|
});
|
|
735
807
|
}
|
|
736
|
-
transformExpression(parentParent, parentProp, transform);
|
|
808
|
+
transformExpression(parentParent, parentProp, transform, csnPath, ctx);
|
|
737
809
|
},
|
|
738
810
|
$Null: () => {
|
|
739
811
|
parent[xpr] = true;
|
|
@@ -753,7 +825,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
753
825
|
funcDef.forEach(f => f());
|
|
754
826
|
else
|
|
755
827
|
funcDef();
|
|
756
|
-
transformExpression(parent, undefined, transform);
|
|
828
|
+
transformExpression(parent, undefined, transform, csnPath, ctx);
|
|
757
829
|
}
|
|
758
830
|
else {
|
|
759
831
|
const funcName = xpr.startsWith('odata.') ? xpr : `odata.${ xpr }`;
|
|
@@ -770,7 +842,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
770
842
|
parentParent[parentProp].$Function = funcName;
|
|
771
843
|
delete parentParent[parentProp].func;
|
|
772
844
|
delete parentParent[parentProp].args;
|
|
773
|
-
transformExpression(parentParent, parentProp, transform);
|
|
845
|
+
transformExpression(parentParent, parentProp, transform, csnPath, ctx);
|
|
774
846
|
}
|
|
775
847
|
}
|
|
776
848
|
else {
|
|
@@ -787,7 +859,9 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
787
859
|
if (isAnnotationExpression(parent)) {
|
|
788
860
|
delete parent['='];
|
|
789
861
|
const edmJson = preTransformXpr(parent);
|
|
790
|
-
|
|
862
|
+
// csnPath to this certain expression is passed inside of the 'ctx' variable, in case it is needed for resolving types from the
|
|
863
|
+
// dictionary, for instance in enum symbols resolving, see transform['#']
|
|
864
|
+
parentParent[parentProp] = transformExpression({ $edmJson: edmJson }, undefined, transform, csnPath, { xprCsnPath: csnPath });
|
|
791
865
|
}
|
|
792
866
|
},
|
|
793
867
|
}, location);
|
|
@@ -468,7 +468,9 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
468
468
|
}
|
|
469
469
|
},
|
|
470
470
|
}, ctx.csnPath || ctx.location);
|
|
471
|
-
xpr2edmJson(carrier, knownAnno, ctx.location, options, messageFunctions
|
|
471
|
+
xpr2edmJson(carrier, knownAnno, ctx.location, options, messageFunctions, {
|
|
472
|
+
oDataDictionary, getDictTerm, getDictType,
|
|
473
|
+
});
|
|
472
474
|
}
|
|
473
475
|
});
|
|
474
476
|
|
package/lib/main.d.ts
CHANGED
package/lib/main.js
CHANGED
|
@@ -188,9 +188,11 @@ module.exports = {
|
|
|
188
188
|
$lsp: {
|
|
189
189
|
parse: (...args) => compiler.parseX(...args),
|
|
190
190
|
compile: (...args) => compiler.compileX(...args),
|
|
191
|
+
compileSync: (...args) => compiler.compileSyncX(...args),
|
|
191
192
|
getArtifactName: art => base.getArtifactName(art),
|
|
192
193
|
traverseSemanticTokens: (xsn, options) => lsp.traverseSemanticTokens(xsn, options),
|
|
193
194
|
getSemanticTokenOrigin: obj => lsp.getSemanticTokenOrigin(obj),
|
|
195
|
+
xsnToCsn: ( xsn, options ) => toCsn.compactModel( xsn, options ),
|
|
194
196
|
},
|
|
195
197
|
|
|
196
198
|
// CSN Model related functionality
|
|
@@ -270,7 +270,7 @@ function applyTransformations( csn, customTransformers = {}, artifactTransformer
|
|
|
270
270
|
* - the path to the property
|
|
271
271
|
*
|
|
272
272
|
* @param {object} parent The "parent" of which we transform a property of
|
|
273
|
-
* @param {string} prop The property of parent to start at
|
|
273
|
+
* @param {string|number} prop The property of parent to start at
|
|
274
274
|
* @param {object} customTransformers Map of prop to transform and function to apply
|
|
275
275
|
* @param {applyTransformationsOptions} [options={}]
|
|
276
276
|
* @param {CSN.Path} path Path pointing to parent
|
|
@@ -155,7 +155,6 @@ function getFKAccessFinalizer( csn, options, csnUtils, pathDelimiter, processOnI
|
|
|
155
155
|
*/
|
|
156
156
|
function handleManagedAssocSteps( artifact, artifactName ) {
|
|
157
157
|
const transformer = getTransformer();
|
|
158
|
-
const inColumnsTransformer = getTransformer(true);
|
|
159
158
|
|
|
160
159
|
if (options.transformation === 'effective')
|
|
161
160
|
processRefsInAnnotations(artifact, transformer, [ 'definitions', artifactName ]);
|
|
@@ -185,14 +184,13 @@ function getFKAccessFinalizer( csn, options, csnUtils, pathDelimiter, processOnI
|
|
|
185
184
|
if (processOnInQueries) {
|
|
186
185
|
queryTransformers.columns = (parent, prop, columns, path) => {
|
|
187
186
|
for (let i = 0; i < columns.length; i++) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
187
|
+
applyTransformationsOnNonDictionary(
|
|
188
|
+
columns,
|
|
189
|
+
i,
|
|
190
|
+
transformer,
|
|
191
|
+
{ drillRef: true, skipStandard: { on: true } },
|
|
192
|
+
path.concat( [ 'columns' ] )
|
|
193
|
+
);
|
|
196
194
|
}
|
|
197
195
|
};
|
|
198
196
|
queryTransformers.on = transform;
|
|
@@ -201,17 +199,27 @@ function getFKAccessFinalizer( csn, options, csnUtils, pathDelimiter, processOnI
|
|
|
201
199
|
if (options.transformation === 'effective' || options.transformation === 'odata')
|
|
202
200
|
queryTransformers.xpr = transform;
|
|
203
201
|
|
|
204
|
-
applyTransformationsOnNonDictionary(
|
|
202
|
+
applyTransformationsOnNonDictionary(
|
|
203
|
+
artifact,
|
|
204
|
+
artifact.query ? 'query' : 'projection',
|
|
205
|
+
queryTransformers,
|
|
206
|
+
{
|
|
207
|
+
drillRef: true,
|
|
208
|
+
skipDict: {
|
|
209
|
+
mixin: !(options.transformation in { odata: 1, effective: 1 }),
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
[ 'definitions', artifactName ]
|
|
213
|
+
);
|
|
205
214
|
}
|
|
206
215
|
|
|
207
216
|
/**
|
|
208
217
|
*
|
|
209
|
-
* @param {boolean} isColumns Whether the transformation is taking place on a column
|
|
210
218
|
* @returns {object}
|
|
211
219
|
*/
|
|
212
|
-
function getTransformer(
|
|
220
|
+
function getTransformer() {
|
|
213
221
|
return {
|
|
214
|
-
ref: (refOwner, prop, ref, path) => {
|
|
222
|
+
ref: (refOwner, prop, ref, path, grandParent) => {
|
|
215
223
|
// [<assoc base>.]<managed assoc>.<field>
|
|
216
224
|
if (ref.length > 1) {
|
|
217
225
|
const { links } = inspectRef(path);
|
|
@@ -232,10 +240,11 @@ function getFKAccessFinalizer( csn, options, csnUtils, pathDelimiter, processOnI
|
|
|
232
240
|
fkAlias = fks[0].as || fks[0].ref[fks[0].ref.length - 1];
|
|
233
241
|
const managedAssocStepName = ref[i];
|
|
234
242
|
const newFkName = `${ managedAssocStepName }${ pathDelimiter }${ fkAlias }`;
|
|
235
|
-
|
|
243
|
+
// only set alias for top level column refs
|
|
244
|
+
if (Array.isArray(grandParent) && path.at(-2) === 'columns') {
|
|
236
245
|
refOwner.ref = [ ...ref.slice(0, i), newFkName ];
|
|
237
246
|
if (!refOwner.as)
|
|
238
|
-
refOwner.as =
|
|
247
|
+
refOwner.as = ref.at(-1).id || ref.at(-1);
|
|
239
248
|
}
|
|
240
249
|
else {
|
|
241
250
|
refOwner.ref = [ ...ref.slice(0, i), newFkName ];
|
|
@@ -226,7 +226,7 @@ function getStructStepsFlattener( csn, options, messageFunctions, resolved, path
|
|
|
226
226
|
// Explicitly set implicit alias for things that are now flattened - but only in columns
|
|
227
227
|
// TODO: Can this be done elegantly during expand phase already?
|
|
228
228
|
if (parent.$implicitAlias) { // an expanded s -> s.a is marked with this - do not add implicit alias "a" there, we want s_a
|
|
229
|
-
if (parent.ref.at(-1) === parent.as) // for a simple s that was expanded - for s.substructure this would not apply
|
|
229
|
+
if (parent.ref.at(-1) === parent.as && !parent.ref.at(-1).includes('.') ) // for a simple s that was expanded - for s.substructure this would not apply
|
|
230
230
|
delete parent.as;
|
|
231
231
|
delete parent.$implicitAlias;
|
|
232
232
|
}
|
|
@@ -70,7 +70,6 @@ function getViewTransformer( csn, options, messageFunctions ) {
|
|
|
70
70
|
get$combined, isAssocOrComposition,
|
|
71
71
|
inspectRef, queryOrMain, // csnRefs
|
|
72
72
|
} = csnUtils;
|
|
73
|
-
const pathDelimiter = options.forHana && (options.sqlMapping === 'hdbcds') ? '.' : '_';
|
|
74
73
|
const { error, info } = messageFunctions;
|
|
75
74
|
const doA2J = !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds');
|
|
76
75
|
|
|
@@ -158,39 +157,6 @@ function getViewTransformer( csn, options, messageFunctions ) {
|
|
|
158
157
|
columnMap[elemName] = { ref: [ elemName ] };
|
|
159
158
|
}
|
|
160
159
|
|
|
161
|
-
/**
|
|
162
|
-
* So far, we only added foreign keys to elements - we also need to create corresponding columns
|
|
163
|
-
* and respect aliasing etc.
|
|
164
|
-
*
|
|
165
|
-
* @todo Maybe this can be done earlier, during flattening/expansion already?
|
|
166
|
-
* @param {object} columnMap
|
|
167
|
-
* @param {CSN.Element} elem
|
|
168
|
-
* @param {string} elemName
|
|
169
|
-
*/
|
|
170
|
-
function addForeignKeysToColumns( columnMap, elem, elemName ) {
|
|
171
|
-
const assocCol = columnMap[elemName];
|
|
172
|
-
if (assocCol && assocCol.ref) {
|
|
173
|
-
elem.keys.forEach((foreignKey) => {
|
|
174
|
-
const ref = cloneCsnNonDict(assocCol.ref, options);
|
|
175
|
-
ref[ref.length - 1] = [ getLastRefStepString(ref) ].concat(foreignKey.as).join(pathDelimiter);
|
|
176
|
-
const result = {
|
|
177
|
-
ref,
|
|
178
|
-
};
|
|
179
|
-
if (assocCol.as) {
|
|
180
|
-
const columnName = `${ assocCol.as }${ pathDelimiter }${ foreignKey.as }`;
|
|
181
|
-
result.as = columnName;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (assocCol.key)
|
|
185
|
-
result.key = true;
|
|
186
|
-
|
|
187
|
-
const colName = result.as || getLastRefStepString(ref);
|
|
188
|
-
columnMap[colName] = result;
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
160
|
/**
|
|
195
161
|
* Check for invalid association publishing (in Union or in Subquery) (for hdbcds) and
|
|
196
162
|
* create the __clone for publishing stuff.
|
|
@@ -354,11 +320,6 @@ function getViewTransformer( csn, options, messageFunctions ) {
|
|
|
354
320
|
if (isSelect) {
|
|
355
321
|
if (!columnMap[elemName])
|
|
356
322
|
addProjectionOrStarElement(query, isProjection, isSelectStar, $combined, columnMap, elemName);
|
|
357
|
-
|
|
358
|
-
// For associations - make sure that the foreign keys have the same "style"
|
|
359
|
-
// If A.assoc => A.assoc_id, else if assoc => assoc_id or assoc as Assoc => Assoc_id
|
|
360
|
-
if (elem.keys && doA2J)
|
|
361
|
-
addForeignKeysToColumns(columnMap, elem, elemName);
|
|
362
323
|
}
|
|
363
324
|
// Views must have at least one element that is not an unmanaged assoc
|
|
364
325
|
if (!elem.on && !elem.$ignore)
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
const { setProp } = require('../../base/model');
|
|
4
4
|
const flattening = require('../db/flattening');
|
|
5
5
|
const {
|
|
6
|
-
applyTransformations, forEachDefinition, forEachMemberRecursively,
|
|
6
|
+
applyTransformations, forEachDefinition, forEachMemberRecursively, forEachMember, applyTransformationsOnNonDictionary,
|
|
7
7
|
} = require('../../model/csnUtils');
|
|
8
8
|
const associations = require('../db/associations');
|
|
9
9
|
const backlinks = require('../db/backlinks');
|
|
10
|
-
const { cloneCsnNonDict } = require('../../model/cloneCsn');
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
/**
|
|
@@ -17,13 +16,14 @@ const { cloneCsnNonDict } = require('../../model/cloneCsn');
|
|
|
17
16
|
* - adding a corresponding on-condition
|
|
18
17
|
* @param {CSN.Model} csn Input CSN - will not be transformed
|
|
19
18
|
* @param {CSN.Options} options
|
|
20
|
-
* @param {object}
|
|
19
|
+
* @param {object} transformerUtils
|
|
21
20
|
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
|
|
22
21
|
* @todo Remove .keys afterwards
|
|
23
22
|
* @todo Add created foreign keys into .columns in case of a query?
|
|
24
23
|
* @returns {CSN.Model}
|
|
25
24
|
*/
|
|
26
|
-
function turnAssociationsIntoUnmanaged( csn, options,
|
|
25
|
+
function turnAssociationsIntoUnmanaged( csn, options, transformerUtils, messageFunctions ) {
|
|
26
|
+
const { csnUtils, expandManagedToFksInArray } = transformerUtils;
|
|
27
27
|
// TODO: Do we really need this?
|
|
28
28
|
forEachDefinition(csn, (artifact, artifactName) => {
|
|
29
29
|
setProp(artifact, '$path', [ 'definitions', artifactName ]);
|
|
@@ -62,56 +62,6 @@ function turnAssociationsIntoUnmanaged( csn, options, csnUtils, messageFunctions
|
|
|
62
62
|
associations.attachOnConditions(csn, csnUtils, '_', { allowArtifact: () => true }, options);
|
|
63
63
|
|
|
64
64
|
return csn;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Expand managed associations in an array and insert them in-place
|
|
68
|
-
*
|
|
69
|
-
* If requested, leave out the assocs themselves
|
|
70
|
-
*
|
|
71
|
-
* @param {boolean} [killAssoc=false]
|
|
72
|
-
* @returns {Function} applyTransformationsCallback
|
|
73
|
-
*/
|
|
74
|
-
function expandManagedToFksInArray( killAssoc = false ) {
|
|
75
|
-
return function expand(parent, prop, array, path) {
|
|
76
|
-
const newColumns = [];
|
|
77
|
-
for (let i = 0; i < array.length; i++) {
|
|
78
|
-
const col = array[i];
|
|
79
|
-
const element = csnUtils.getElement(col) || col.ref && csnUtils.inspectRef(path.concat(prop, i)).art;
|
|
80
|
-
if (!killAssoc || !element?.keys)
|
|
81
|
-
newColumns.push(col);
|
|
82
|
-
if (element?.keys)
|
|
83
|
-
element.keys.forEach(fk => addForeignKeyToColumns(fk, newColumns, col, options));
|
|
84
|
-
}
|
|
85
|
-
parent[prop] = newColumns;
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* FKs need to be added to the .columns
|
|
93
|
-
* @todo stolen from lib/transform/db/views.js
|
|
94
|
-
* @todo Can we maybe do this during expansion already?
|
|
95
|
-
* @param {object} foreignKey
|
|
96
|
-
* @param {object[]} columns
|
|
97
|
-
* @param {CSN.Column} associationColumn
|
|
98
|
-
* @param {CSN.Options} options
|
|
99
|
-
*/
|
|
100
|
-
function addForeignKeyToColumns( foreignKey, columns, associationColumn, options ) {
|
|
101
|
-
const ref = cloneCsnNonDict(associationColumn.ref, options);
|
|
102
|
-
ref[ref.length - 1] = [ implicitAs(ref) ].concat(foreignKey.as || foreignKey.ref).join('_');
|
|
103
|
-
const result = {
|
|
104
|
-
ref,
|
|
105
|
-
};
|
|
106
|
-
if (associationColumn.as) {
|
|
107
|
-
const columnName = `${ associationColumn.as }_${ foreignKey.as || implicitAs(foreignKey.ref) }`;
|
|
108
|
-
result.as = columnName;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (associationColumn.key)
|
|
112
|
-
result.key = true;
|
|
113
|
-
|
|
114
|
-
columns.push(result);
|
|
115
65
|
}
|
|
116
66
|
|
|
117
67
|
/**
|
|
@@ -121,7 +71,7 @@ function addForeignKeyToColumns( foreignKey, columns, associationColumn, options
|
|
|
121
71
|
* @param {object} csnUtils
|
|
122
72
|
* @param {object} messageFunctions
|
|
123
73
|
*/
|
|
124
|
-
function transformBacklinks( csn, options, csnUtils, messageFunctions ) {
|
|
74
|
+
function transformBacklinks( csn, options, { csnUtils }, messageFunctions ) {
|
|
125
75
|
forEachDefinition(csn, backlinks.getBacklinkTransformer(csnUtils, messageFunctions, options, '_', true));
|
|
126
76
|
}
|
|
127
77
|
|
|
@@ -90,8 +90,10 @@ function effectiveCsn( model, options, messageFunctions ) {
|
|
|
90
90
|
resolveTypesInActionsAfterFlattening(csnUtils);
|
|
91
91
|
|
|
92
92
|
processCalculatedElementsInEntities(csn, options);
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
// invalidate csnRef caches after flattening
|
|
94
|
+
transformerUtils.csnUtils = getUtils(csn, 'init-all');
|
|
95
|
+
associations.managedToUnmanaged(csn, options, transformerUtils, messageFunctions);
|
|
96
|
+
associations.transformBacklinks(csn, options, transformerUtils, messageFunctions);
|
|
95
97
|
const transformers = mergeTransformers([
|
|
96
98
|
options.remapOdataAnnotations ? annotations.remapODataAnnotations(csn) : {},
|
|
97
99
|
misc.removeDefinitionsAndProperties(csn, options),
|
|
@@ -87,10 +87,10 @@ function _removeDefinitionsAndProperties( csn, options ) {
|
|
|
87
87
|
// Set when we remove .key from temporal things, used in localized.js
|
|
88
88
|
$key: killProp,
|
|
89
89
|
includes: killProp,
|
|
90
|
-
enum: killProp,
|
|
91
90
|
keys: killProp,
|
|
92
91
|
excluding: killProp, // * is resolved, so has no effect anymore
|
|
93
92
|
targetAspect: killProp,
|
|
93
|
+
$calc: killProp,
|
|
94
94
|
};
|
|
95
95
|
|
|
96
96
|
if (!options.keepLocalized)
|