@sap/cds-compiler 3.6.2 → 3.8.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 +109 -1
- package/README.md +3 -0
- package/bin/cdsc.js +12 -5
- package/doc/CHANGELOG_ARCHIVE.md +6 -6
- package/doc/CHANGELOG_BETA.md +35 -2
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/doc/DeprecatedOptions_v2.md +1 -1
- package/doc/NameResolution.md +1 -1
- package/lib/api/main.js +63 -23
- package/lib/api/options.js +1 -0
- package/lib/api/validate.js +5 -0
- package/lib/base/dictionaries.js +15 -3
- package/lib/base/keywords.js +2 -0
- package/lib/base/message-registry.js +120 -34
- package/lib/base/messages.js +51 -27
- package/lib/base/model.js +4 -2
- package/lib/base/shuffle.js +2 -1
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/defaultValues.js +1 -1
- package/lib/checks/elements.js +29 -1
- package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +10 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/onConditions.js +15 -9
- package/lib/checks/sql-snippets.js +2 -2
- package/lib/checks/types.js +5 -1
- package/lib/checks/validator.js +7 -3
- package/lib/compiler/assert-consistency.js +42 -26
- package/lib/compiler/base.js +50 -4
- package/lib/compiler/builtins.js +17 -8
- package/lib/compiler/checks.js +241 -246
- package/lib/compiler/define.js +113 -146
- package/lib/compiler/extend.js +889 -383
- package/lib/compiler/finalize-parse-cdl.js +5 -58
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/kick-start.js +7 -8
- package/lib/compiler/populate.js +297 -293
- package/lib/compiler/propagator.js +27 -18
- package/lib/compiler/resolve.js +146 -463
- package/lib/compiler/shared.js +36 -79
- package/lib/compiler/tweak-assocs.js +30 -28
- package/lib/compiler/utils.js +31 -5
- package/lib/edm/annotations/genericTranslation.js +131 -59
- package/lib/edm/annotations/preprocessAnnotations.js +3 -0
- package/lib/edm/csn2edm.js +22 -5
- package/lib/edm/edm.js +6 -4
- package/lib/edm/edmAnnoPreprocessor.js +1 -0
- package/lib/edm/edmPreprocessor.js +42 -26
- package/lib/gen/Dictionary.json +38 -2
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/languageLexer.js +1 -1
- package/lib/gen/languageParser.js +4828 -4472
- package/lib/inspect/inspectPropagation.js +20 -34
- package/lib/json/from-csn.js +140 -44
- package/lib/json/to-csn.js +114 -122
- package/lib/language/errorStrategy.js +2 -0
- package/lib/language/genericAntlrParser.js +156 -36
- package/lib/language/language.g4 +100 -58
- package/lib/language/textUtils.js +13 -0
- package/lib/main.d.ts +43 -3
- package/lib/main.js +4 -2
- package/lib/model/csnRefs.js +15 -3
- package/lib/model/csnUtils.js +12 -74
- package/lib/model/revealInternalProperties.js +4 -2
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +3 -0
- package/lib/render/manageConstraints.js +5 -2
- package/lib/render/toCdl.js +216 -104
- package/lib/render/toHdbcds.js +2 -9
- package/lib/render/toRename.js +14 -51
- package/lib/render/toSql.js +4 -3
- package/lib/render/utils/common.js +9 -5
- package/lib/transform/braceExpression.js +6 -0
- package/lib/transform/db/assertUnique.js +2 -1
- package/lib/transform/db/expansion.js +2 -0
- package/lib/transform/db/flattening.js +37 -36
- package/lib/transform/db/rewriteCalculatedElements.js +600 -0
- package/lib/transform/db/transformExists.js +4 -0
- package/lib/transform/db/views.js +40 -37
- package/lib/transform/forOdataNew.js +20 -15
- package/lib/transform/forRelationalDB.js +58 -41
- package/lib/transform/odata/typesExposure.js +50 -15
- package/lib/transform/parseExpr.js +16 -8
- package/lib/transform/transformUtilsNew.js +42 -14
- package/lib/transform/translateAssocsToJoins.js +60 -37
- package/lib/transform/universalCsn/coreComputed.js +15 -7
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +2 -1
|
@@ -17,6 +17,7 @@ function translateAssocsToJoinsCSN(csn, options){
|
|
|
17
17
|
// Do not re-complain about localized
|
|
18
18
|
const compileOptions = { ...options, $skipNameCheck: true };
|
|
19
19
|
delete compileOptions.csnFlavor;
|
|
20
|
+
// console.log('CSN passed by A2J to compiler:',JSON.stringify(csn,null,2))
|
|
20
21
|
const model = recompileX(csn, compileOptions);
|
|
21
22
|
timetrace.stop('A2J: Recompiling model');
|
|
22
23
|
timetrace.start('A2J: Translating associations to joins');
|
|
@@ -195,7 +196,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
195
196
|
'leaf' QAT and to the respective $tableAlias which is used to link paths to the correct
|
|
196
197
|
table alias. Subqueries are not considered in the mergePathIntoQat(), so a subquery QA
|
|
197
198
|
must be created and added separately to the lead query $tableAlias'es.
|
|
198
|
-
Also the name of the subquery (the alias) needs to be set to the final QA alias name.
|
|
199
|
+
Also, the name of the subquery (the alias) needs to be set to the final QA alias name.
|
|
199
200
|
*/
|
|
200
201
|
function createQAForFromClauseSubQuery(query, env)
|
|
201
202
|
{
|
|
@@ -203,7 +204,14 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
203
204
|
if (query.$tableAliases[taName].kind !== '$self') {
|
|
204
205
|
let ta = query.$tableAliases[taName];
|
|
205
206
|
if(!ta.$QA) {
|
|
206
|
-
|
|
207
|
+
let alias = taName;
|
|
208
|
+
if (ta.name.$inferred === '$internal') {
|
|
209
|
+
// query has no explicit table alias, i.e. is internal: make it visible and remove `$`
|
|
210
|
+
alias = ta.name.alias.replace(/^[$]/, '_');
|
|
211
|
+
ta.$inferred = undefined;
|
|
212
|
+
ta.name.$inferred = undefined;
|
|
213
|
+
}
|
|
214
|
+
ta.$QA = createQA(env, ta._origin, alias, undefined);
|
|
207
215
|
incAliasCount(env, ta.$QA);
|
|
208
216
|
if(ta.name && ta.name.id) {
|
|
209
217
|
ta.name.id = ta.$QA.name.id;
|
|
@@ -251,22 +259,24 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
251
259
|
*/
|
|
252
260
|
function substituteDollarSelf(pathNode)
|
|
253
261
|
{
|
|
254
|
-
let
|
|
255
|
-
|
|
262
|
+
let pathValue = pathNode;
|
|
263
|
+
let [head, ...tail] = pathValue.path;
|
|
264
|
+
while(tail.length && head._navigation?.kind === '$self') {
|
|
256
265
|
const self = head;
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if(
|
|
266
|
+
[head, ...tail] = tail;
|
|
267
|
+
if(head) {
|
|
268
|
+
pathValue = self._navigation._origin.elements[head.id].value;
|
|
269
|
+
// core compiler has already caught $self.<assoc>.<postfix> and
|
|
270
|
+
// non-path $self expressions with postfix path
|
|
271
|
+
if(pathValue.path) {
|
|
272
|
+
if(tail.length)
|
|
264
273
|
pathValue = constructPathNode([...pathValue.path, ...tail], pathValue.alias, false);
|
|
265
|
-
|
|
266
|
-
replaceNodeContent(pathNode, pathValue);
|
|
274
|
+
[head, ...tail] = pathValue.path;
|
|
267
275
|
}
|
|
268
276
|
}
|
|
269
277
|
}
|
|
278
|
+
if(head)
|
|
279
|
+
replaceNodeContent(pathNode, pathValue);
|
|
270
280
|
}
|
|
271
281
|
|
|
272
282
|
/*
|
|
@@ -304,7 +314,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
304
314
|
// pop ta ps
|
|
305
315
|
if(head._navigation.kind !== '$tableAlias')
|
|
306
316
|
tail = pathNode.path;
|
|
307
|
-
// if tail.
|
|
317
|
+
// if tail.length > 1, search bottom up for QA
|
|
308
318
|
// default to rootQA, _parent.$QA has precedence
|
|
309
319
|
let [QA, ps] = rightMostQA(tail, head._navigation._parent.$QA || head._navigation.$QA);
|
|
310
320
|
if(!QA) {
|
|
@@ -636,7 +646,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
636
646
|
}
|
|
637
647
|
|
|
638
648
|
env.assocStack.push(assoc);
|
|
639
|
-
|
|
649
|
+
const onCond = cloneOnCondition(assoc.on);
|
|
640
650
|
env.assocStack.pop();
|
|
641
651
|
return onCond;
|
|
642
652
|
}
|
|
@@ -655,8 +665,8 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
655
665
|
}
|
|
656
666
|
|
|
657
667
|
function cloneOnCondExprStream(expr) {
|
|
658
|
-
|
|
659
|
-
|
|
668
|
+
const args = expr.args;
|
|
669
|
+
const result = { op: { val: expr.op.val }, args: [ ] };
|
|
660
670
|
for(let i = 0; i < args.length; i++)
|
|
661
671
|
{
|
|
662
672
|
if(args[i].op && args[i].op.val === 'xpr')
|
|
@@ -668,7 +678,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
668
678
|
else if(i < args.length-2 && args[i].path &&
|
|
669
679
|
args[i+1]?.literal === 'token' && args[i+1]?.val === '=' && args[i+2].path)
|
|
670
680
|
{
|
|
671
|
-
|
|
681
|
+
const fwdAssoc = getForwardAssociation(args[i].path, args[i+2].path);
|
|
672
682
|
if(fwdAssoc)
|
|
673
683
|
{
|
|
674
684
|
//env.assocStack.includes(fwdAssoc) => recursion
|
|
@@ -702,7 +712,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
702
712
|
|
|
703
713
|
// If this is a backlink condition, produce the
|
|
704
714
|
// ON cond of the forward assoc with swapped src/tgt aliases
|
|
705
|
-
|
|
715
|
+
const fwdAssoc = getForwardAssociationExpr(expr);
|
|
706
716
|
if(fwdAssoc) {
|
|
707
717
|
if(env.assocStack.length === 2) {
|
|
708
718
|
// reuse (ugly) error message from forHana
|
|
@@ -729,25 +739,38 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
729
739
|
}
|
|
730
740
|
|
|
731
741
|
// The src/tgtAliases need to be swapped for ON Condition of the forward assoc.
|
|
732
|
-
//
|
|
733
|
-
//
|
|
734
|
-
//
|
|
735
|
-
//
|
|
736
|
-
//
|
|
742
|
+
// If the QAT assoc is a mixin and forward assoc was propagated, the original
|
|
743
|
+
// forward definition must have a target in the query source otherwise the ON cond
|
|
744
|
+
// is not resolvable (exception propagated mixins, as these are defined against the
|
|
745
|
+
// view signature and not a query source). If the target is not part of the query source,
|
|
746
|
+
// raise an error. Swap source and target otherwise.
|
|
737
747
|
function swapTableAliasesForFwdAssoc(fwdAssoc, srcAlias, tgtAlias) {
|
|
738
|
-
|
|
748
|
+
const newSrcAlias = tgtAlias;
|
|
739
749
|
let newTgtAlias = {};
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
750
|
+
|
|
751
|
+
let i = 0;
|
|
752
|
+
let fwdOrigin = fwdAssoc;
|
|
753
|
+
while(fwdOrigin._origin) {
|
|
754
|
+
fwdOrigin = fwdOrigin._origin;
|
|
755
|
+
i++;
|
|
756
|
+
}
|
|
757
|
+
// If fwdAssoc was propagated and the origin is not a mixin itself (which always
|
|
758
|
+
// points to the signature of the current view and ensures that the ON cond is
|
|
759
|
+
// resolvable) make sure that the original assoc target is contained in the local
|
|
760
|
+
// query source
|
|
761
|
+
if(assoc.kind === 'mixin' && i > 0 && fwdOrigin.kind !== 'mixin') {
|
|
762
|
+
const tas = Object.values(env.lead.$tableAliases);
|
|
763
|
+
const i = tas.findIndex(ta => ta._artifact === fwdOrigin.target._artifact);
|
|
764
|
+
if(i >= 0 && tas[i].$QA) {
|
|
765
|
+
newTgtAlias.id = tas[i].$QA.name.id;
|
|
766
|
+
newTgtAlias._artifact = tas[i]._effectiveType;
|
|
767
|
+
newTgtAlias._navigation = tas[i].$QA.path[0]._navigation;
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
error(null, [ assocQAT._origin.location, assocQAT._origin ], { name: fwdOrigin.target._artifact.name.id, art: assoc.name.id },
|
|
771
|
+
'Expected association target $(NAME) of association $(ART) to be a query source');
|
|
772
|
+
newTgtAlias = Object.assign(newTgtAlias, srcAlias);
|
|
773
|
+
}
|
|
751
774
|
}
|
|
752
775
|
else {
|
|
753
776
|
newTgtAlias = Object.assign(newTgtAlias, srcAlias);
|
|
@@ -989,7 +1012,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
989
1012
|
A QA (QueryArtifact) is a representative for a table/view that must appear
|
|
990
1013
|
in the FROM clause either named directly or indirectly through an association.
|
|
991
1014
|
*/
|
|
992
|
-
function createQA(env, artifact, alias
|
|
1015
|
+
function createQA(env, artifact, alias, namedArgs=undefined)
|
|
993
1016
|
{
|
|
994
1017
|
if(alias === undefined) {
|
|
995
1018
|
throw new CompilerAssertion('no alias provided');
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
forEachDefinition, forAllQueries, getNormalizedQuery,
|
|
4
|
+
forEachDefinition, forAllQueries, getNormalizedQuery, forEachMemberRecursively,
|
|
5
5
|
} = require('../../model/csnUtils');
|
|
6
6
|
const { setAnnotationIfNotDefined } = require('./utils');
|
|
7
7
|
const { CompilerAssertion } = require('../../base/error');
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Set @Core.Computed on the elements of views (and projections)
|
|
10
|
+
* Set @Core.Computed on the elements of views (and projections) as well
|
|
11
|
+
* as on calculated elements of entities and aspects.
|
|
11
12
|
*
|
|
12
13
|
* @param {CSN.Model} csn
|
|
13
14
|
* @param {object} csnUtils
|
|
14
15
|
*/
|
|
15
|
-
function
|
|
16
|
+
function setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils ) {
|
|
16
17
|
const {
|
|
17
18
|
artifactRef, getColumn, getElement, getOrigin,
|
|
18
19
|
} = csnUtils;
|
|
@@ -24,6 +25,12 @@ function setCoreComputedOnViews( csn, csnUtils ) {
|
|
|
24
25
|
traverseQueryAndAttachCoreComputed(query, query.SELECT.elements || artifact.elements);
|
|
25
26
|
}, path);
|
|
26
27
|
}
|
|
28
|
+
else if (artifact.kind === 'entity' || artifact.kind === 'aspect') {
|
|
29
|
+
forEachMemberRecursively(artifact, (element) => {
|
|
30
|
+
if (element.value && !element.value?.ref) // calculated elements, but simple references are ignored
|
|
31
|
+
setAnnotationIfNotDefined(element, '@Core.Computed', true);
|
|
32
|
+
}, path);
|
|
33
|
+
}
|
|
27
34
|
});
|
|
28
35
|
/**
|
|
29
36
|
* Attach @Core.Computed to elements resulting from calculated fields
|
|
@@ -84,7 +91,7 @@ function setCoreComputedOnViews( csn, csnUtils ) {
|
|
|
84
91
|
* @todo cleanup throw(s) - but leave in during dev
|
|
85
92
|
*/
|
|
86
93
|
function getElementFromFrom( name, base ) {
|
|
87
|
-
if (base.SELECT
|
|
94
|
+
if (base.SELECT?.elements?.[name]) {
|
|
88
95
|
return getAncestor(base.SELECT.elements[name], name, base.SELECT);
|
|
89
96
|
}
|
|
90
97
|
else if (base.ref) {
|
|
@@ -100,7 +107,7 @@ function setCoreComputedOnViews( csn, csnUtils ) {
|
|
|
100
107
|
return checkJoinSources(base.args, name);
|
|
101
108
|
}
|
|
102
109
|
|
|
103
|
-
throw new CompilerAssertion(JSON.stringify(base));
|
|
110
|
+
throw new CompilerAssertion(`Element “${name}” not found in: ${JSON.stringify(base)}`);
|
|
104
111
|
}
|
|
105
112
|
|
|
106
113
|
/**
|
|
@@ -146,7 +153,8 @@ function setCoreComputedOnViews( csn, csnUtils ) {
|
|
|
146
153
|
function needsCoreComputed( column ) {
|
|
147
154
|
return column &&
|
|
148
155
|
(
|
|
149
|
-
column.xpr || column.func || column.val !== undefined || column.param ||
|
|
156
|
+
column.xpr || column.list || column.func || column.val !== undefined || column.param ||
|
|
157
|
+
column.SELECT || column.SET ||
|
|
150
158
|
column.ref && [ '$at', '$now', '$user', '$session' ].includes(column.ref[0])
|
|
151
159
|
);
|
|
152
160
|
}
|
|
@@ -175,5 +183,5 @@ function setCoreComputedOnViews( csn, csnUtils ) {
|
|
|
175
183
|
}
|
|
176
184
|
|
|
177
185
|
module.exports = {
|
|
178
|
-
|
|
186
|
+
setCoreComputedOnViewsAndCalculatedElements,
|
|
179
187
|
};
|
|
@@ -13,7 +13,7 @@ const {
|
|
|
13
13
|
const {
|
|
14
14
|
forEachValue, forEach,
|
|
15
15
|
} = require('../../utils/objectUtils');
|
|
16
|
-
const {
|
|
16
|
+
const { setCoreComputedOnViewsAndCalculatedElements } = require('./coreComputed');
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Loop through a universal CSN and enrich it with the properties/annotations
|
|
@@ -134,7 +134,7 @@ module.exports = (csn, options) => {
|
|
|
134
134
|
* `@Core.Computed' must be calculated manually as this annotation
|
|
135
135
|
* is not set in the universal csn flavor.
|
|
136
136
|
*/
|
|
137
|
-
|
|
137
|
+
setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils );
|
|
138
138
|
/**
|
|
139
139
|
* Construct an extensions object which maps a built-in type to it's annotations
|
|
140
140
|
*/
|
|
@@ -273,7 +273,7 @@ module.exports = (csn, options) => {
|
|
|
273
273
|
|
|
274
274
|
/**
|
|
275
275
|
* Walk over properties on member level and propagate all relevant properties
|
|
276
|
-
* from
|
|
276
|
+
* from its prototype.
|
|
277
277
|
*/
|
|
278
278
|
function propagateOnMemberLevel() {
|
|
279
279
|
applyTransformations(csn, {
|
|
@@ -320,7 +320,7 @@ module.exports = (csn, options) => {
|
|
|
320
320
|
}, []);
|
|
321
321
|
|
|
322
322
|
/**
|
|
323
|
-
* Propagate properties to the `member` from
|
|
323
|
+
* Propagate properties to the `member` from its prototype.
|
|
324
324
|
* For that to work we calculate the prototype chain of the member and
|
|
325
325
|
* propagate properties along this prototype chain until we reach the `member`
|
|
326
326
|
* passed to this function.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-compiler",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.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)",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"xmakeAfterInstall": "npm run gen",
|
|
20
20
|
"xmakePrepareRelease": "echo \"$(node scripts/stripReadme.js README.md)\" > README.md && node scripts/assertSnapshotVersioning.js && node scripts/assertChangelog.js && node scripts/cleanup.js --remove-dev",
|
|
21
21
|
"test": "node scripts/verifyGrammarChecksum.js && mocha --reporter min --reporter-option maxDiffSize=0 scripts/testLazyLoading.js && mocha --parallel --reporter min --reporter-option maxDiffSize=0 test/ test3/",
|
|
22
|
+
"testci": "node scripts/verifyGrammarChecksum.js && mocha --reporter-option maxDiffSize=0 scripts/testLazyLoading.js && mocha --parallel --reporter-option maxDiffSize=0 test/ test3/",
|
|
22
23
|
"testverbose": "node scripts/verifyGrammarChecksum.js && mocha --parallel test/ test3/",
|
|
23
24
|
"test3": "node scripts/verifyGrammarChecksum.js && mocha --reporter-option maxDiffSize=0 test3/",
|
|
24
25
|
"deployTest3SQL": "deployRefs=true mocha --reporter-option maxDiffSize=0 test3/test.deploy.hana-sql.js",
|