@sap/cds-compiler 4.7.6 → 4.9.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 +63 -3
- package/bin/cds_remove_invalid_whitespace.js +135 -0
- package/bin/cds_update_annotations.js +180 -0
- package/bin/cds_update_identifiers.js +3 -4
- package/bin/cdsc.js +28 -1
- package/bin/cdshi.js +13 -3
- package/doc/CHANGELOG_BETA.md +24 -1
- package/lib/api/main.js +119 -46
- package/lib/api/options.js +51 -0
- package/lib/api/validate.js +1 -5
- package/lib/base/builtins.js +116 -0
- package/lib/base/keywords.js +5 -1
- package/lib/base/location.js +91 -14
- package/lib/base/message-registry.js +76 -46
- package/lib/base/messages.js +121 -35
- package/lib/base/model.js +4 -7
- package/lib/checks/actionsFunctions.js +3 -3
- package/lib/checks/annotationsOData.js +3 -0
- package/lib/checks/defaultValues.js +5 -2
- package/lib/checks/elements.js +2 -1
- package/lib/checks/enricher.js +2 -2
- package/lib/checks/queryNoDbArtifacts.js +5 -3
- package/lib/checks/utils.js +1 -1
- package/lib/checks/validator.js +8 -56
- package/lib/compiler/assert-consistency.js +11 -7
- package/lib/compiler/builtins.js +0 -74
- package/lib/compiler/checks.js +105 -29
- package/lib/compiler/define.js +37 -25
- package/lib/compiler/extend.js +35 -12
- package/lib/compiler/index.js +9 -10
- package/lib/compiler/lsp-api.js +5 -0
- package/lib/compiler/populate.js +13 -5
- package/lib/compiler/propagator.js +24 -18
- package/lib/compiler/resolve.js +47 -45
- package/lib/compiler/shared.js +61 -21
- package/lib/compiler/tweak-assocs.js +15 -90
- package/lib/compiler/utils.js +3 -3
- package/lib/compiler/xpr-rewrite.js +689 -0
- package/lib/compiler/{classes.js → xsn-model.js} +0 -16
- package/lib/edm/annotations/edmJson.js +7 -5
- package/lib/edm/annotations/genericTranslation.js +149 -71
- package/lib/edm/csn2edm.js +25 -9
- package/lib/edm/edm.js +7 -7
- package/lib/edm/edmInboundChecks.js +57 -5
- package/lib/edm/edmPreprocessor.js +54 -25
- package/lib/edm/edmUtils.js +3 -16
- package/lib/gen/Dictionary.json +138 -14
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +2085 -1989
- package/lib/json/csnVersion.js +7 -4
- package/lib/json/from-csn.js +21 -11
- package/lib/json/to-csn.js +8 -4
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/genericAntlrParser.js +23 -16
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +1 -1
- package/lib/main.d.ts +90 -14
- package/lib/main.js +9 -1
- package/lib/model/cloneCsn.js +21 -9
- package/lib/model/csnRefs.js +153 -42
- package/lib/model/csnUtils.js +14 -11
- package/lib/model/enrichCsn.js +4 -2
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/model/sortViews.js +14 -6
- package/lib/modelCompare/compare.js +135 -122
- package/lib/optionProcessor.js +49 -2
- package/lib/render/DuplicateChecker.js +6 -6
- package/lib/render/manageConstraints.js +1 -0
- package/lib/render/toCdl.js +6 -3
- package/lib/render/toHdbcds.js +4 -48
- package/lib/render/toSql.js +6 -3
- package/lib/transform/addTenantFields.js +58 -35
- package/lib/transform/db/applyTransformations.js +34 -1
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +11 -3
- package/lib/transform/db/flattening.js +71 -46
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/temporal.js +6 -3
- package/lib/transform/db/transformExists.js +2 -2
- package/lib/transform/db/views.js +1 -4
- package/lib/transform/effective/annotations.js +194 -0
- package/lib/transform/effective/main.js +11 -10
- package/lib/transform/effective/misc.js +45 -14
- package/lib/transform/effective/types.js +4 -3
- package/lib/transform/forOdata.js +29 -12
- package/lib/transform/forRelationalDB.js +104 -113
- package/lib/transform/localized.js +7 -6
- package/lib/transform/odata/flattening.js +228 -107
- package/lib/transform/odata/toFinalBaseType.js +10 -26
- package/lib/transform/odata/typesExposure.js +41 -25
- package/lib/transform/parseExpr.js +4 -7
- package/lib/transform/transformUtils.js +50 -43
- package/lib/transform/translateAssocsToJoins.js +48 -48
- package/lib/transform/universalCsn/coreComputed.js +2 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
- package/package.json +2 -2
- package/share/messages/README.md +4 -0
- package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
- package/share/messages/anno-missing-rewrite.md +45 -0
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/def-duplicate-autoexposed.md +1 -1
- package/share/messages/extend-repeated-intralayer.md +3 -16
- package/share/messages/extend-unrelated-layer.md +1 -1
- package/share/messages/message-explanations.json +2 -0
- package/share/messages/redirected-to-ambiguous.md +1 -1
- package/share/messages/redirected-to-complex.md +1 -1
- package/share/messages/redirected-to-unrelated.md +1 -1
- package/share/messages/rewrite-not-supported.md +1 -1
- package/share/messages/syntax-expecting-unsigned-int.md +2 -2
- package/share/messages/type-missing-enum-value.md +59 -0
- package/share/messages/wildcard-excluding-one.md +1 -1
- package/bin/.eslintrc.json +0 -17
- package/lib/api/.eslintrc.json +0 -37
- package/lib/checks/.eslintrc.json +0 -31
- package/lib/compiler/.eslintrc.json +0 -8
- package/lib/edm/.eslintrc.json +0 -46
- package/lib/inspect/.eslintrc.json +0 -4
- package/lib/json/.eslintrc.json +0 -4
- package/lib/language/.eslintrc.json +0 -4
- package/lib/model/.eslintrc.json +0 -13
- package/lib/modelCompare/utils/.eslintrc.json +0 -22
- package/lib/render/.eslintrc.json +0 -22
- package/lib/transform/.eslintrc.json +0 -13
- package/lib/transform/db/.eslintrc.json +0 -41
- package/lib/transform/draft/.eslintrc.json +0 -4
- package/lib/transform/effective/.eslintrc.json +0 -4
- package/lib/transform/universalCsn/.eslintrc.json +0 -37
- package/lib/utils/.eslintrc.json +0 -7
|
@@ -29,10 +29,10 @@ function translateAssocsToJoinsCSN(csn, options){
|
|
|
29
29
|
// Use the effective elements list as columns
|
|
30
30
|
forEachDefinition(model, art => {
|
|
31
31
|
if (art.$queries) {
|
|
32
|
-
for (
|
|
32
|
+
for (const query of art.$queries) {
|
|
33
33
|
query.columns = Object.values(query.elements);
|
|
34
34
|
// TODO: Remove viaAll
|
|
35
|
-
for (
|
|
35
|
+
for (const elemName in query.elements) {
|
|
36
36
|
const elem = query.elements[elemName];
|
|
37
37
|
if (elem.$inferred === '*')
|
|
38
38
|
delete elem.$inferred;
|
|
@@ -172,8 +172,8 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
172
172
|
// Transform each FROM table path into a join tree and attach the tree to the path object
|
|
173
173
|
function createInnerJoins(fromPathNode, env)
|
|
174
174
|
{
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
const fqat = env.lead.$tableAliases[fromPathNode.name.id].$fqat;
|
|
176
|
+
const joinTree = createJoinTree(env, undefined, fqat, 'inner', '$fqat', undefined);
|
|
177
177
|
|
|
178
178
|
replaceTableAliasInPlace( fromPathNode, joinTree);
|
|
179
179
|
}
|
|
@@ -185,11 +185,11 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
185
185
|
{
|
|
186
186
|
env.lead = query;
|
|
187
187
|
let joinTree = query.from;
|
|
188
|
-
for(
|
|
188
|
+
for(const tan in query.$tableAliases)
|
|
189
189
|
{
|
|
190
190
|
if(query.$tableAliases[tan].kind !== '$self') // don't drive into $projection/$self tableAlias (yet)
|
|
191
191
|
{
|
|
192
|
-
|
|
192
|
+
const ta = query.$tableAliases[tan];
|
|
193
193
|
joinTree = createJoinTree(env, joinTree, ta.$qat, 'left', '$qat', ta.$QA);
|
|
194
194
|
}
|
|
195
195
|
}
|
|
@@ -207,9 +207,9 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
207
207
|
*/
|
|
208
208
|
function createQAForFromClauseSubQuery(query, env)
|
|
209
209
|
{
|
|
210
|
-
for (
|
|
210
|
+
for (const taName in query.$tableAliases) {
|
|
211
211
|
if (query.$tableAliases[taName].kind !== '$self') {
|
|
212
|
-
|
|
212
|
+
const ta = query.$tableAliases[taName];
|
|
213
213
|
if(!ta.$QA) {
|
|
214
214
|
let alias = taName;
|
|
215
215
|
if (ta.name.$inferred === '$internal') {
|
|
@@ -306,8 +306,8 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
306
306
|
if(env.location === 'onCondFrom')
|
|
307
307
|
{
|
|
308
308
|
if(checkPathDictionary(pathNode, env)) {
|
|
309
|
-
|
|
310
|
-
|
|
309
|
+
const [ tableAlias, tail ] = constructTableAliasAndTailPath(pathNode.path);
|
|
310
|
+
const pathStr = translateONCondPath(tail).map(ps => ps.id).join(pathDelimiter);
|
|
311
311
|
replaceNodeContent(pathNode,
|
|
312
312
|
constructPathNode([ tableAlias, { id: pathStr, _artifact: pathNode._artifact } ]));
|
|
313
313
|
}
|
|
@@ -329,11 +329,11 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
329
329
|
tail = pathNode.path;
|
|
330
330
|
// if tail.length > 1, search bottom up for QA
|
|
331
331
|
// default to rootQA, _parent.$QA has precedence
|
|
332
|
-
|
|
332
|
+
const [QA, ps] = rightMostQA(tail, head._navigation._parent.$QA || head._navigation.$QA);
|
|
333
333
|
if(!QA) {
|
|
334
334
|
error(null, pathNode.$location,
|
|
335
335
|
{ name: pathName(pathNode.path) },
|
|
336
|
-
'
|
|
336
|
+
'Debug me: No QA found for generic path rewriting in $(NAME)')
|
|
337
337
|
return;
|
|
338
338
|
}
|
|
339
339
|
// if the found QA is the mixin QA and if the path length is one,
|
|
@@ -360,7 +360,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
360
360
|
// const revealInternalProperties = require('../model/revealInternalProperties.js');
|
|
361
361
|
// console.log('++++++++ Path tail: ', revealInternalProperties(tail[tail.length-1]._artifact));
|
|
362
362
|
// console.log('******** Flat FKs\n', tail[i]._artifact.$flatSrcFKs.map(f => revealInternalProperties(f._artifact)));
|
|
363
|
-
throw new CompilerAssertion('
|
|
363
|
+
throw new CompilerAssertion('Debug me: No flat FK found for FK rewriting');
|
|
364
364
|
}
|
|
365
365
|
// replace tail path with flattened foreign key including prefix
|
|
366
366
|
tail.splice(i, tail.length, fk);
|
|
@@ -431,7 +431,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
431
431
|
*/
|
|
432
432
|
function createJoinTree(env, joinTree, parentQat, joinType, qatAttribName, lastAssocQA)
|
|
433
433
|
{
|
|
434
|
-
for(
|
|
434
|
+
for(const childQatId in parentQat)
|
|
435
435
|
{
|
|
436
436
|
const childQat = parentQat[childQatId];
|
|
437
437
|
|
|
@@ -454,10 +454,10 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
454
454
|
if(childQat._filter)
|
|
455
455
|
{
|
|
456
456
|
// Filter conditions are unique for each JOIN, they don't need to be copied
|
|
457
|
-
|
|
457
|
+
const filter = childQat._filter;
|
|
458
458
|
rewritePathsInFilterExpression(filter, function(pathNode) {
|
|
459
459
|
return [ /* tableAlias=> */ constructTableAliasPathStep(childQat.$QA),
|
|
460
|
-
/* filterPath=> */ pathNode.path ];
|
|
460
|
+
/* filterPath=> */ pathNode.path ];
|
|
461
461
|
}, env);
|
|
462
462
|
|
|
463
463
|
if(!env.lead.$startFilters)
|
|
@@ -486,28 +486,28 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
486
486
|
|
|
487
487
|
function createJoinQA(joinType, lhs, rhs, assocQAT, assocSourceQA, env)
|
|
488
488
|
{
|
|
489
|
-
|
|
489
|
+
const node = { op: { val: 'join' }, join: { val: joinType }, args: [ lhs, rhs ] };
|
|
490
490
|
const assoc = assocQAT._origin;
|
|
491
491
|
if(isBetaEnabled(options, 'mapAssocToJoinCardinality')) {
|
|
492
492
|
node.cardinality = mapAssocToJoinCardinality(assoc);
|
|
493
493
|
}
|
|
494
494
|
// 'path steps' for the src/tgt table alias
|
|
495
|
-
|
|
496
|
-
|
|
495
|
+
const srcTableAlias = constructTableAliasPathStep(assocSourceQA);
|
|
496
|
+
const tgtTableAlias = constructTableAliasPathStep(assocQAT.$QA);
|
|
497
497
|
|
|
498
498
|
node.on = createOnCondition(assoc, srcTableAlias, tgtTableAlias, options.tenantDiscriminator);
|
|
499
499
|
|
|
500
500
|
if(assocQAT._filter)
|
|
501
501
|
{
|
|
502
502
|
// Filter conditions are unique for each JOIN, they don't need to be copied
|
|
503
|
-
|
|
503
|
+
const filter = assocQAT._filter;
|
|
504
504
|
rewritePathsInFilterExpression(filter, function(pathNode) {
|
|
505
505
|
return [ tgtTableAlias, pathNode.path ];
|
|
506
506
|
}, env);
|
|
507
507
|
|
|
508
508
|
// If toplevel ON cond op is AND add filter condition to the args array,
|
|
509
509
|
// create a new toplevel AND op otherwise
|
|
510
|
-
|
|
510
|
+
const onCond = (Array.isArray(node.on) ? node.on[0] : node.on);
|
|
511
511
|
|
|
512
512
|
if(onCond.op.val === 'and')
|
|
513
513
|
onCond.args.push(parenthesise(filter));
|
|
@@ -567,7 +567,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
567
567
|
// produce the ON condition for a given association
|
|
568
568
|
function createOnCondition(assoc, srcAlias, tgtAlias, compareTenants)
|
|
569
569
|
{
|
|
570
|
-
|
|
570
|
+
const prefixes = [ assoc.name.id ];
|
|
571
571
|
/* This is no art and can be removed once ON cond for published
|
|
572
572
|
and renamed backlink assocs are publicly available. Example:
|
|
573
573
|
|
|
@@ -600,12 +600,12 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
600
600
|
Put all src/tgt path siblings into the EQ term and create the proper path objects
|
|
601
601
|
with the src/tgt table alias path steps in front.
|
|
602
602
|
*/
|
|
603
|
-
|
|
603
|
+
const args = compareTenants && addTenantComparison(assoc) || [];
|
|
604
604
|
for(let i = 0; i < assoc.$flatSrcFKs.length; i++)
|
|
605
605
|
{
|
|
606
606
|
args.push({op: {val: '=' },
|
|
607
607
|
args: [ constructPathNode( [ srcAlias, prefixFK(assoc.$elementPrefix, assoc.$flatSrcFKs[i]) ] ),
|
|
608
|
-
constructPathNode( [ tgtAlias, assoc.$flatTgtFKs[i] ] ) ] });
|
|
608
|
+
constructPathNode( [ tgtAlias, assoc.$flatTgtFKs[i] ] ) ] });
|
|
609
609
|
}
|
|
610
610
|
return parenthesise((args.length > 1 ? { op: { val: 'and' }, args: [ ...args.map(parenthesise) ] } : args[0] ));
|
|
611
611
|
}
|
|
@@ -678,7 +678,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
678
678
|
args[1].path.push({ id: 'tenant' }); // no need for _artifact
|
|
679
679
|
const comparison = { op: {val: '=' }, args };
|
|
680
680
|
if (!cond) // for managed assoc
|
|
681
|
-
return comparison;
|
|
681
|
+
return [ comparison ];
|
|
682
682
|
return { op: { val: 'and' }, args: [ comparison, parenthesise(cond) ] };
|
|
683
683
|
}
|
|
684
684
|
|
|
@@ -761,7 +761,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
761
761
|
// If this is an ordinary expression, clone it and mangle its arguments
|
|
762
762
|
// this will substitute multiple backlink conditions ($self = ... AND $self = ...AND ...)
|
|
763
763
|
if(expr.op) {
|
|
764
|
-
|
|
764
|
+
const x = clone(expr);
|
|
765
765
|
if(expr.args)
|
|
766
766
|
x.args = expr.args.map(cloneOnCondition);
|
|
767
767
|
return x;
|
|
@@ -845,7 +845,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
845
845
|
$projection must be removed from $projection.yid (get's aliased with the mixinAssocQAT.$QA)
|
|
846
846
|
*/
|
|
847
847
|
if(env.assocStack.length < 2) {
|
|
848
|
-
|
|
848
|
+
const value = env.lead.elements[path[0].id].value;
|
|
849
849
|
/*
|
|
850
850
|
If the value is an expression in the select block, return the unmodified
|
|
851
851
|
expression. rewriteGenericPaths will check and rewrite these paths later
|
|
@@ -957,7 +957,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
957
957
|
if(head.id === env.assocStack.id()) {
|
|
958
958
|
// source side from view point of view (target side from forward point of view)
|
|
959
959
|
path = tail; // pop assoc step
|
|
960
|
-
|
|
960
|
+
const elt = env.lead._combined[path[0].id];
|
|
961
961
|
|
|
962
962
|
if(elt) {
|
|
963
963
|
if(Array.isArray(elt)) {
|
|
@@ -1008,7 +1008,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1008
1008
|
path = translateONCondPath(path, !hasDollarSelfPrefix ? assoc.$elementPrefix : undefined);
|
|
1009
1009
|
}
|
|
1010
1010
|
}
|
|
1011
|
-
|
|
1011
|
+
const pathStr = path.map(ps => ps.id).join(pathDelimiter);
|
|
1012
1012
|
return constructPathNode([ tableAlias, { id: pathStr, _artifact: pathNode._artifact } ]);
|
|
1013
1013
|
}
|
|
1014
1014
|
|
|
@@ -1209,11 +1209,11 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1209
1209
|
*/
|
|
1210
1210
|
function constructTableAliasAndTailPath(path, navigation=undefined)
|
|
1211
1211
|
{
|
|
1212
|
-
|
|
1212
|
+
const [head, ...tail] = path;
|
|
1213
1213
|
if(navigation === undefined)
|
|
1214
1214
|
navigation = head._navigation;
|
|
1215
1215
|
|
|
1216
|
-
|
|
1216
|
+
const QA = navigation.$QA || navigation._parent.$QA;
|
|
1217
1217
|
|
|
1218
1218
|
// First path step is table alias, use and pop it off
|
|
1219
1219
|
if(navigation.$QA && tail.length > 0)
|
|
@@ -1251,7 +1251,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1251
1251
|
{
|
|
1252
1252
|
if(!pathStr)
|
|
1253
1253
|
pathStr = '';
|
|
1254
|
-
|
|
1254
|
+
const assocStep = path.find(ps => {
|
|
1255
1255
|
if(pathStr.length > 0)
|
|
1256
1256
|
pathStr += pathDelimiter;
|
|
1257
1257
|
pathStr += ps.id;
|
|
@@ -1303,7 +1303,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1303
1303
|
pathStr += fk.name.id;
|
|
1304
1304
|
}
|
|
1305
1305
|
|
|
1306
|
-
|
|
1306
|
+
const tail = path.slice(path.indexOf(fkPs)+1);
|
|
1307
1307
|
// If foreign key is an association itself, apply substituteFKAliasForPath on tail
|
|
1308
1308
|
if(fk && fk.targetElement._artifact.target && tail.length)
|
|
1309
1309
|
return substituteFKAliasForPath(fk.targetElement, tail, pathStr);
|
|
@@ -1377,7 +1377,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1377
1377
|
if(ps._artifact && ps._artifact.target)
|
|
1378
1378
|
{
|
|
1379
1379
|
// if this is not the last path step, complain
|
|
1380
|
-
|
|
1380
|
+
const la1 = pathDict.path[pathDict.path.indexOf(ps)+1];
|
|
1381
1381
|
if(la1) {
|
|
1382
1382
|
if(ps._artifact.on)
|
|
1383
1383
|
{
|
|
@@ -1438,7 +1438,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1438
1438
|
*/
|
|
1439
1439
|
function mergePathIntoQAT(pathDict, env)
|
|
1440
1440
|
{
|
|
1441
|
-
|
|
1441
|
+
const path = pathDict.path;
|
|
1442
1442
|
|
|
1443
1443
|
if(path.length === 0)
|
|
1444
1444
|
return;
|
|
@@ -1505,7 +1505,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1505
1505
|
if(qatParent == undefined)
|
|
1506
1506
|
throw new CompilerAssertion('table alias/qathost not found for path: ' + pathAsStr(path));
|
|
1507
1507
|
|
|
1508
|
-
|
|
1508
|
+
const rootQat = qatParent;
|
|
1509
1509
|
|
|
1510
1510
|
// Create the very first QAT if it doesn't exist
|
|
1511
1511
|
// (filter condition for table alias prefix not allowed)
|
|
@@ -1526,7 +1526,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1526
1526
|
}
|
|
1527
1527
|
if(pathStep.args) {
|
|
1528
1528
|
// sort named arguments
|
|
1529
|
-
|
|
1529
|
+
const sortedNamedArgs = Object.create(null);
|
|
1530
1530
|
Object.keys(pathStep.args).sort().forEach(p => {
|
|
1531
1531
|
sortedNamedArgs[p] = compactExpr(pathStep.args[p]);
|
|
1532
1532
|
})
|
|
@@ -1687,10 +1687,10 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1687
1687
|
// eslint-disable-next-line no-unused-vars
|
|
1688
1688
|
function printPath(pathDict, env)
|
|
1689
1689
|
{
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1690
|
+
const alias = (pathDict.name && pathDict.name.id) || '<undefined>'
|
|
1691
|
+
const path = pathDict.path;
|
|
1692
|
+
const s = pathAsStr(path, '"');
|
|
1693
|
+
const me = env.lead && (env.lead.name.id || env.lead.op);
|
|
1694
1694
|
// eslint-disable-next-line no-console
|
|
1695
1695
|
console.log(me + ': ' + env.location + ': ' + s + ' alias: ' + alias);
|
|
1696
1696
|
}
|
|
@@ -1708,9 +1708,9 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1708
1708
|
else
|
|
1709
1709
|
newObj = {};
|
|
1710
1710
|
|
|
1711
|
-
|
|
1712
|
-
for (
|
|
1713
|
-
|
|
1711
|
+
const props = Object.getOwnPropertyNames(obj); // clone own properties only, not inherited ones
|
|
1712
|
+
for (const p of props) {
|
|
1713
|
+
const pd = Object.getOwnPropertyDescriptor(obj, p);
|
|
1714
1714
|
if (pd && pd.enumerable === false)
|
|
1715
1715
|
{
|
|
1716
1716
|
pd.value = obj[p]; // don't copy references
|
|
@@ -1740,10 +1740,10 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1740
1740
|
*/
|
|
1741
1741
|
function constructPathNode(pathSteps, alias, rewritten=true)
|
|
1742
1742
|
{
|
|
1743
|
-
|
|
1743
|
+
const node = {
|
|
1744
1744
|
$rewritten: rewritten,
|
|
1745
1745
|
path : pathSteps.map(p => {
|
|
1746
|
-
|
|
1746
|
+
const o = {};
|
|
1747
1747
|
Object.keys(p).forEach(k => {
|
|
1748
1748
|
if(!(rewritten && ['_'].includes(k[0])))
|
|
1749
1749
|
o[k] = p[k];
|
|
@@ -1784,7 +1784,7 @@ function walkQuery(query, env)
|
|
|
1784
1784
|
env.location = 'select';
|
|
1785
1785
|
if(env.walkover[env.location])
|
|
1786
1786
|
{
|
|
1787
|
-
for(
|
|
1787
|
+
for(const alias in query.elements)
|
|
1788
1788
|
walk(query.elements[alias].value, env);
|
|
1789
1789
|
|
|
1790
1790
|
env.location = 'Where';
|
|
@@ -1809,7 +1809,7 @@ function walkQuery(query, env)
|
|
|
1809
1809
|
|
|
1810
1810
|
function walkFrom(fromBlock)
|
|
1811
1811
|
{
|
|
1812
|
-
|
|
1812
|
+
const aliases = [];
|
|
1813
1813
|
env.position = fromBlock;
|
|
1814
1814
|
if(fromBlock)
|
|
1815
1815
|
{
|
|
@@ -5,6 +5,7 @@ const {
|
|
|
5
5
|
} = require('../../model/csnUtils');
|
|
6
6
|
const { setAnnotationIfNotDefined } = require('./utils');
|
|
7
7
|
const { CompilerAssertion } = require('../../base/error');
|
|
8
|
+
const { isMagicVariable } = require('../../base/builtins');
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Set @Core.Computed on the elements of views (and projections) as well
|
|
@@ -157,7 +158,7 @@ function setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils ) {
|
|
|
157
158
|
(
|
|
158
159
|
column.xpr || column.list || column.func || column.val !== undefined || column['#'] !== undefined || column.param ||
|
|
159
160
|
column.SELECT || column.SET ||
|
|
160
|
-
column.ref && [
|
|
161
|
+
column.ref && (isMagicVariable(column.ref[0]) || column.ref[0] === '$parameters')
|
|
161
162
|
);
|
|
162
163
|
}
|
|
163
164
|
|
|
@@ -8,8 +8,8 @@ const {
|
|
|
8
8
|
getUtils,
|
|
9
9
|
applyTransformations,
|
|
10
10
|
implicitAs,
|
|
11
|
-
isBuiltinType,
|
|
12
11
|
} = require('../../model/csnUtils');
|
|
12
|
+
const { isBuiltinType, propagationRules } = require('../../base/builtins');
|
|
13
13
|
const {
|
|
14
14
|
forEachValue, forEach,
|
|
15
15
|
} = require('../../utils/objectUtils');
|
|
@@ -29,24 +29,10 @@ module.exports = (csn, options) => {
|
|
|
29
29
|
} = csnUtils;
|
|
30
30
|
// Properties on definition level that we treat specially.
|
|
31
31
|
const definitionPropagationRules = {
|
|
32
|
-
|
|
33
|
-
'@cds.external': skip,
|
|
34
|
-
'@fiori.draft.enabled': onlyViaArtifact,
|
|
32
|
+
__proto__: null,
|
|
35
33
|
'@': nullStopsPropagation,
|
|
36
34
|
// Example: `type E : F;` does not have `elements`, but they are required for e.g. OData.
|
|
37
35
|
elements: onlyTypeDef,
|
|
38
|
-
'@cds.persistence.exists': skip,
|
|
39
|
-
'@cds.persistence.table': skip,
|
|
40
|
-
'@cds.persistence.calcview': skip,
|
|
41
|
-
'@cds.persistence.udf': skip,
|
|
42
|
-
'@cds.persistence.skip': notWithPersistenceTable,
|
|
43
|
-
'@sql.append': skip,
|
|
44
|
-
'@sql.prepend': skip,
|
|
45
|
-
'@sql.replace': skip,
|
|
46
|
-
'@Analytics.hidden': skip,
|
|
47
|
-
'@Analytics.visible': skip,
|
|
48
|
-
'@cds.autoexposed': skip,
|
|
49
|
-
'@cds.redirection.target': skip,
|
|
50
36
|
type: always,
|
|
51
37
|
doc: nullStopsPropagation,
|
|
52
38
|
length: always,
|
|
@@ -65,6 +51,16 @@ module.exports = (csn, options) => {
|
|
|
65
51
|
keys: always,
|
|
66
52
|
};
|
|
67
53
|
|
|
54
|
+
const ruleToFunction = {
|
|
55
|
+
__proto__: null,
|
|
56
|
+
never: skip,
|
|
57
|
+
onlyViaArtifact,
|
|
58
|
+
notWithPersistenceTable,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
for (const rule in propagationRules)
|
|
62
|
+
definitionPropagationRules[rule] = ruleToFunction[propagationRules[rule]];
|
|
63
|
+
|
|
68
64
|
// Properties on member level that we treat specially
|
|
69
65
|
const memberPropagationRules = {
|
|
70
66
|
key: skip,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-compiler",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.0",
|
|
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)",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"gentest3": "cross-env MAKEREFS=${MAKEREFS:-'true'} mocha --reporter-option maxDiffSize=0 test3/testRefFiles.js",
|
|
29
29
|
"coverage": "cross-env nyc mocha --reporter-option maxDiffSize=0 test/ test3/testRefFiles.js && nyc report --reporter=lcov",
|
|
30
30
|
"coverage:piper": "cross-env nyc mocha --reporter test/TestMochaReporter.js --reporter-options mochaFile=./coverage/TEST-results.xml --reporter-option maxDiffSize=0 --timeout 10000 test/ test3/ && nyc report --reporter=cobertura && nyc report --reporter=lcov",
|
|
31
|
-
"lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/ types/ && node scripts/linter/lintGrammar.js && node scripts/linter/lintTests.js test3/ && node scripts/linter/lintMessages.js && node scripts/linter/lintMessageIdCoverage.js lib/ && markdownlint README.md CHANGELOG.md doc/ internalDoc/ && cd share/messages && markdownlint .",
|
|
31
|
+
"lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/ types/ && node scripts/linter/lintGrammar.js && node scripts/linter/lintTests.js test3/ && node scripts/linter/lintMessages.js && node scripts/linter/lintMessageIdCoverage.js lib/ && markdownlint README.md CHANGELOG.md doc/ internalDoc/ && cd share/messages && markdownlint . && cd ../../ && node scripts/check-changelog.js",
|
|
32
32
|
"tslint": "tsc --pretty -p .",
|
|
33
33
|
"updateVocs": "node scripts/odataAnnotations/generateDictMain.js && npm run generateAllRefs",
|
|
34
34
|
"updateTocs": "node scripts/update-toc.js",
|
package/share/messages/README.md
CHANGED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# anno-missing-rewrite
|
|
2
|
+
|
|
3
|
+
A propagated annotation containing expressions can't be rewritten and would
|
|
4
|
+
end up with invalid paths.
|
|
5
|
+
|
|
6
|
+
While propagating annotations containing expressions such as `@anno: (path)`,
|
|
7
|
+
the compiler ensures that the path remains valid. If necessary, the paths
|
|
8
|
+
have to be rewritten, e.g. when being propagated to projections that rename
|
|
9
|
+
their source's elements. If rewriting is not possible, this error is emitted.
|
|
10
|
+
|
|
11
|
+
## Example
|
|
12
|
+
|
|
13
|
+
Erroneous code example:
|
|
14
|
+
|
|
15
|
+
```cds
|
|
16
|
+
type T : {
|
|
17
|
+
@anno: (sibling)
|
|
18
|
+
elem: String;
|
|
19
|
+
sibling: String;
|
|
20
|
+
};
|
|
21
|
+
type TString : T:elem; // ❌ there is no `sibling`
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The annotating `@anno` would be propagated to `TString`. However, because its
|
|
25
|
+
path refers to an element that is not reachable at `TString`, the path can't
|
|
26
|
+
be rewritten and compilation fails.
|
|
27
|
+
|
|
28
|
+
## How to Fix
|
|
29
|
+
|
|
30
|
+
Explicitly override the annotation. Either remove it by setting its value to
|
|
31
|
+
`null` or by using another value.
|
|
32
|
+
|
|
33
|
+
```cds
|
|
34
|
+
// (1) direct annotation
|
|
35
|
+
@anno: null
|
|
36
|
+
type TString : T:elem;
|
|
37
|
+
|
|
38
|
+
// (2) annotate statement
|
|
39
|
+
type TString : T:elem;
|
|
40
|
+
annotate TString with @(anno: null);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Variant (1) may not always be applicable, e.g. if annotations in a structured
|
|
44
|
+
type would need to be overridden. In those cases, use variant (2) and assign
|
|
45
|
+
annotations via the `annotate` statement.
|
|
@@ -9,30 +9,17 @@ They form a cyclic connection through their dependencies
|
|
|
9
9
|
|
|
10
10
|
## Example
|
|
11
11
|
|
|
12
|
-
Erroneous code example with
|
|
13
|
-
|
|
14
|
-
```cds
|
|
15
|
-
entity FooBar { }
|
|
16
|
-
|
|
17
|
-
extend FooBar { foo : Integer; }
|
|
18
|
-
extend FooBar { bar : Integer; }
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Due to multiple extensions in the example above, the order of `foo` and `bar`
|
|
22
|
-
inside `FooBar` may not be stable. You therefore can’t depend on it.
|
|
23
|
-
|
|
24
|
-
It's also possible to trigger this warning with multiple files.
|
|
25
|
-
Look at the following example:
|
|
12
|
+
Erroneous code example with multiple CDL files:
|
|
26
13
|
|
|
27
14
|
```cds
|
|
28
15
|
// (1) Definition.cds
|
|
29
16
|
using from './Extension.cds';
|
|
30
17
|
entity FooBar { };
|
|
31
|
-
extend FooBar { foo: Integer; };
|
|
18
|
+
extend FooBar { foo: Integer; }; // ❌
|
|
32
19
|
|
|
33
20
|
// (2) Extension.cds
|
|
34
21
|
using from './Definition.cds';
|
|
35
|
-
extend FooBar { bar: Integer; }
|
|
22
|
+
extend FooBar { bar: Integer; }; // ❌
|
|
36
23
|
```
|
|
37
24
|
|
|
38
25
|
Here we have a cyclic dependency between (1) and (2). Together they form one
|
|
@@ -2,6 +2,7 @@
|
|
|
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
|
+
"anno-missing-rewrite",
|
|
5
6
|
"check-proper-type-of",
|
|
6
7
|
"def-duplicate-autoexposed",
|
|
7
8
|
"def-missing-type",
|
|
@@ -12,6 +13,7 @@
|
|
|
12
13
|
"redirected-to-unrelated",
|
|
13
14
|
"rewrite-not-supported",
|
|
14
15
|
"syntax-expecting-unsigned-int",
|
|
16
|
+
"type-missing-enum-value",
|
|
15
17
|
"wildcard-excluding-one"
|
|
16
18
|
]
|
|
17
19
|
}
|
|
@@ -17,8 +17,8 @@ Erroneous code example:
|
|
|
17
17
|
|
|
18
18
|
<!-- cds-mode: ignore -->
|
|
19
19
|
```cds
|
|
20
|
-
type LengthIsUnsafe : String(9007199254740992);
|
|
21
|
-
type NotAnInteger : String(42.1);
|
|
20
|
+
type LengthIsUnsafe : String(9007199254740992); // ❌
|
|
21
|
+
type NotAnInteger : String(42.1); // ❌
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
In the erroneous example, the string length for the type `LengthIsUnsafe` is
|