@sap/cds-compiler 2.7.0 → 2.10.2
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 +103 -0
- package/lib/api/main.js +8 -10
- package/lib/api/options.js +13 -9
- package/lib/api/validate.js +11 -8
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +16 -0
- package/lib/base/messages.js +2 -0
- package/lib/base/model.js +1 -0
- package/lib/checks/onConditions.js +5 -0
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +38 -0
- package/lib/checks/validator.js +7 -2
- package/lib/compiler/assert-consistency.js +11 -5
- package/lib/compiler/builtins.js +2 -0
- package/lib/compiler/checks.js +3 -1
- package/lib/compiler/definer.js +87 -29
- package/lib/compiler/resolver.js +75 -16
- package/lib/compiler/shared.js +29 -9
- package/lib/edm/annotations/genericTranslation.js +182 -186
- package/lib/edm/csn2edm.js +93 -98
- package/lib/edm/edm.js +16 -20
- package/lib/edm/edmPreprocessor.js +274 -83
- package/lib/edm/edmUtils.js +29 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +12 -1
- package/lib/gen/language.tokens +57 -53
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +770 -744
- package/lib/gen/languageLexer.tokens +49 -46
- package/lib/gen/languageParser.js +4727 -4323
- package/lib/json/from-csn.js +52 -23
- package/lib/json/to-csn.js +185 -71
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +9 -0
- package/lib/language/language.g4 +90 -31
- package/lib/main.js +4 -0
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +7 -1
- package/lib/model/csnUtils.js +5 -4
- package/lib/optionProcessor.js +7 -1
- package/lib/render/.eslintrc.json +3 -1
- package/lib/render/toCdl.js +45 -9
- package/lib/render/toHdbcds.js +100 -34
- package/lib/render/toSql.js +12 -4
- package/lib/render/utils/common.js +5 -9
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/draft.js +6 -4
- package/lib/transform/db/expansion.js +14 -4
- package/lib/transform/db/flattening.js +13 -5
- package/lib/transform/db/transformExists.js +252 -58
- package/lib/transform/forHanaNew.js +7 -1
- package/lib/transform/forOdataNew.js +12 -8
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +44 -38
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +76 -0
- package/lib/transform/odata/structureFlattener.js +13 -10
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +33 -1
- package/lib/transform/translateAssocsToJoins.js +6 -4
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/package.json +1 -1
package/lib/compiler/definer.js
CHANGED
|
@@ -216,7 +216,8 @@ function getDefinerFunctions( model ) {
|
|
|
216
216
|
applyExtensions();
|
|
217
217
|
|
|
218
218
|
Object.keys( model.definitions ).forEach( preProcessArtifact );
|
|
219
|
-
const commonLanguagesEntity
|
|
219
|
+
const commonLanguagesEntity // TODO: remove beta after a grace period
|
|
220
|
+
= (options.addTextsLanguageAssoc || isBetaEnabled( options, 'addTextsLanguageAssoc' )) &&
|
|
220
221
|
model.definitions['sap.common.Languages'];
|
|
221
222
|
addTextsLanguageAssoc = !!(commonLanguagesEntity && commonLanguagesEntity.elements &&
|
|
222
223
|
commonLanguagesEntity.elements.code);
|
|
@@ -492,7 +493,7 @@ function getDefinerFunctions( model ) {
|
|
|
492
493
|
initDollarSelf( art ); // $self
|
|
493
494
|
if (art.params)
|
|
494
495
|
initParams( art ); // $parameters
|
|
495
|
-
if (art.includes && !(art.name.absolute in extensionsDict))
|
|
496
|
+
if (art.includes && !(art.name.absolute in extensionsDict)) // TODO: in next phase?
|
|
496
497
|
extensionsDict[art.name.absolute] = []; // structure with includes must be "extended"
|
|
497
498
|
|
|
498
499
|
if (!art.query)
|
|
@@ -607,14 +608,7 @@ function getDefinerFunctions( model ) {
|
|
|
607
608
|
if (query.on)
|
|
608
609
|
initExprForQuery( query.on, query );
|
|
609
610
|
// TODO: MIXIN with name = ...subquery (not yet supported anyway)
|
|
610
|
-
|
|
611
|
-
if (elem && (elem.value || elem.expand)) {
|
|
612
|
-
setProp( elem, '_block', query._block );
|
|
613
|
-
defineAnnotations( elem, elem, query._block );
|
|
614
|
-
initExprForQuery( elem.value, query );
|
|
615
|
-
initExpandInline( elem );
|
|
616
|
-
}
|
|
617
|
-
}
|
|
611
|
+
initSelectItems( query, query.columns );
|
|
618
612
|
if (query.where)
|
|
619
613
|
initExprForQuery( query.where, query );
|
|
620
614
|
if (query.having)
|
|
@@ -622,22 +616,41 @@ function getDefinerFunctions( model ) {
|
|
|
622
616
|
initMembers( query, query, query._block );
|
|
623
617
|
}
|
|
624
618
|
|
|
625
|
-
function
|
|
626
|
-
// TODO: forbid with :param, global:true, in ref-where, outside queries (CSN), ...
|
|
627
|
-
|
|
628
|
-
|
|
619
|
+
function initSelectItems( parent, columns ) {
|
|
620
|
+
// TODO: forbid expand/inline with :param, global:true, in ref-where, outside queries (CSN), ...
|
|
621
|
+
let wildcard = null;
|
|
622
|
+
for (const col of columns || parent.expand || parent.inline || []) {
|
|
623
|
+
if (!col) // parse error
|
|
629
624
|
continue;
|
|
630
|
-
if (
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
625
|
+
if (!columns) {
|
|
626
|
+
if (parent.value)
|
|
627
|
+
setProp( col, '_pathHead', parent ); // also set for '*' in expand/inline
|
|
628
|
+
else if (parent._pathHead)
|
|
629
|
+
setProp( col, '_pathHead', parent._pathHead );
|
|
630
|
+
}
|
|
631
|
+
if (col.val === '*') {
|
|
632
|
+
if (!wildcard) {
|
|
633
|
+
wildcard = col;
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
// a late syntax error (this code also runs with parse-cdl), i.e.
|
|
637
|
+
// no semantic loc (wouldn't be available for expand/inline anyway)
|
|
638
|
+
error( 'syntax-duplicate-clause', [ col.location, null ],
|
|
639
|
+
{ prop: '*', line: wildcard.location.line, col: wildcard.location.col },
|
|
640
|
+
'You have provided a $(PROP) already at line $(LINE), column $(COL)' );
|
|
641
|
+
// TODO: extra text variants for expand/inline? - probably not
|
|
642
|
+
col.val = null; // do not consider it for expandWildcard()
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
else if (col.value || col.expand) {
|
|
646
|
+
setProp( col, '_block', parent._block );
|
|
647
|
+
defineAnnotations( col, col, parent._block ); // TODO: complain with inline
|
|
648
|
+
// TODO: allow sub queries? at least in top-level expand without parallel ref
|
|
649
|
+
if (columns)
|
|
650
|
+
initExprForQuery( col.value, parent );
|
|
651
|
+
initSelectItems( col );
|
|
638
652
|
}
|
|
639
653
|
}
|
|
640
|
-
// TODO: allow sub queries in top-level expand without parallel ref
|
|
641
654
|
}
|
|
642
655
|
|
|
643
656
|
function initExprForQuery( expr, query ) {
|
|
@@ -656,9 +669,38 @@ function getDefinerFunctions( model ) {
|
|
|
656
669
|
}
|
|
657
670
|
else if (expr.path && expr.$expected === 'exists') {
|
|
658
671
|
expr.$expected = 'approved-exists';
|
|
672
|
+
approveExistsInChildren(expr);
|
|
659
673
|
}
|
|
660
674
|
}
|
|
661
675
|
|
|
676
|
+
/**
|
|
677
|
+
* If we have a valid top-level exists, exists in filters of sub-expressions can be translated,
|
|
678
|
+
* since we will have a top-level subquery after exists-processing in the forHanaNew.
|
|
679
|
+
*
|
|
680
|
+
* Recursively drill down into:
|
|
681
|
+
* - the .path
|
|
682
|
+
* - the .args
|
|
683
|
+
* - the .where.args
|
|
684
|
+
*
|
|
685
|
+
* Any $expected === 'exists' encountered along the way are turned into 'approved-exists'
|
|
686
|
+
*
|
|
687
|
+
* working: exists toE[exists toE] -> select from E where exists toE
|
|
688
|
+
* not working: toE[exists toE] -> we don't support subqueries in filters
|
|
689
|
+
*
|
|
690
|
+
* @param {object} exprOrPathElement starts w/ an expr but then subelem from .path or .where.args
|
|
691
|
+
*/
|
|
692
|
+
function approveExistsInChildren(exprOrPathElement) {
|
|
693
|
+
if (exprOrPathElement.$expected === 'exists')
|
|
694
|
+
exprOrPathElement.$expected = 'approved-exists';
|
|
695
|
+
// Drill down
|
|
696
|
+
if (exprOrPathElement.args)
|
|
697
|
+
exprOrPathElement.args.forEach(elem => approveExistsInChildren(elem));
|
|
698
|
+
else if (exprOrPathElement.where && exprOrPathElement.where.args)
|
|
699
|
+
exprOrPathElement.where.args.forEach(elem => approveExistsInChildren(elem));
|
|
700
|
+
else if (exprOrPathElement.path)
|
|
701
|
+
exprOrPathElement.path.forEach(elem => approveExistsInChildren(elem));
|
|
702
|
+
}
|
|
703
|
+
|
|
662
704
|
// table is table expression in FROM, becomes an alias
|
|
663
705
|
function initTableExpression( table, query, joinParents ) {
|
|
664
706
|
if (!table) // parse error
|
|
@@ -706,6 +748,8 @@ function getDefinerFunctions( model ) {
|
|
|
706
748
|
// ? ta._joinParent.args[ta.$joinArgsIndex] // in JOIN
|
|
707
749
|
// : ta._parent.from ) // directly in FROM
|
|
708
750
|
// Note for --raw-output: _joinParent pointing to CROSS JOIN node has not name
|
|
751
|
+
if (!tab) // parse error; time for #6241
|
|
752
|
+
return; // (parser method to only add non-null to array)
|
|
709
753
|
setProp( tab, '_joinParent', table );
|
|
710
754
|
tab.$joinArgsIndex = index;
|
|
711
755
|
initTableExpression( tab, query, joinParents );
|
|
@@ -859,6 +903,7 @@ function getDefinerFunctions( model ) {
|
|
|
859
903
|
* (which is basically the component name of the `parent` element plus a dot).
|
|
860
904
|
*/
|
|
861
905
|
function initMembers( construct, parent, block, initExtensions = false ) {
|
|
906
|
+
// TODO: split extend from init
|
|
862
907
|
const isQueryExtension = kindProperties[construct.kind].isExtension &&
|
|
863
908
|
(parent._main || parent).query;
|
|
864
909
|
let obj = construct;
|
|
@@ -914,7 +959,7 @@ function getDefinerFunctions( model ) {
|
|
|
914
959
|
forEachInOrder( construct, 'params', init );
|
|
915
960
|
const { returns } = construct;
|
|
916
961
|
if (returns) {
|
|
917
|
-
returns.kind = 'param';
|
|
962
|
+
returns.kind = (kindProperties[construct.kind].isExtension) ? construct.kind : 'param';
|
|
918
963
|
init( returns, '' ); // '' is special name for returns parameter
|
|
919
964
|
}
|
|
920
965
|
return;
|
|
@@ -983,6 +1028,7 @@ function getDefinerFunctions( model ) {
|
|
|
983
1028
|
}
|
|
984
1029
|
|
|
985
1030
|
function checkDefinitions( construct, parent, prop, dict = construct[prop] ) {
|
|
1031
|
+
// TODO: do differently, see also annotateMembers() in resolver
|
|
986
1032
|
// To have been checked by parsers:
|
|
987
1033
|
// - artifacts (CDL-only anyway) only inside [extend] context|service
|
|
988
1034
|
if (!dict)
|
|
@@ -1421,6 +1467,12 @@ function getDefinerFunctions( model ) {
|
|
|
1421
1467
|
|
|
1422
1468
|
model.extensions.push(annotationArtifact);
|
|
1423
1469
|
extendArtifact( exts, annotationArtifact ); // also sets _artifact link in extensions
|
|
1470
|
+
// if one of the annotate statement mentions 'returns', assume it
|
|
1471
|
+
// TODO: with warning/info?
|
|
1472
|
+
for (const ext of exts) {
|
|
1473
|
+
if (ext.$syntax === 'returns')
|
|
1474
|
+
annotationArtifact.$syntax = 'returns';
|
|
1475
|
+
}
|
|
1424
1476
|
}
|
|
1425
1477
|
}
|
|
1426
1478
|
}
|
|
@@ -1704,6 +1756,7 @@ function getDefinerFunctions( model ) {
|
|
|
1704
1756
|
}
|
|
1705
1757
|
|
|
1706
1758
|
function extendMembers( extensions, art, noExtend ) {
|
|
1759
|
+
// TODO: do the whole extension stuff lazily if the elements are requested
|
|
1707
1760
|
const elemExtensions = [];
|
|
1708
1761
|
extensions.sort( compareLayer );
|
|
1709
1762
|
for (const ext of extensions) {
|
|
@@ -1749,7 +1802,12 @@ function getDefinerFunctions( model ) {
|
|
|
1749
1802
|
[ 'elements', 'actions' ].forEach( (prop) => {
|
|
1750
1803
|
const dict = art._extend && art._extend[prop];
|
|
1751
1804
|
for (const name in dict) {
|
|
1752
|
-
|
|
1805
|
+
let obj = art;
|
|
1806
|
+
if (obj.targetAspect)
|
|
1807
|
+
obj = obj.targetAspect;
|
|
1808
|
+
while (obj.items)
|
|
1809
|
+
obj = obj.items;
|
|
1810
|
+
const validDict = obj[prop] || prop === 'elements' && obj.enum;
|
|
1753
1811
|
const member = validDict[name];
|
|
1754
1812
|
if (!member)
|
|
1755
1813
|
extendNothing( dict[name], prop, name, art, validDict );
|
|
@@ -1988,8 +2046,8 @@ function getDefinerFunctions( model ) {
|
|
|
1988
2046
|
|
|
1989
2047
|
if (isKey && isLocalized) { // key with localized is wrong - ignore localized
|
|
1990
2048
|
const errpos = elem.localized || elem.type || elem.name;
|
|
1991
|
-
warning( 'localized-key', [ errpos.location, elem ], {},
|
|
1992
|
-
'Keyword
|
|
2049
|
+
warning( 'localized-key', [ errpos.location, elem ], { keyword: 'localized' },
|
|
2050
|
+
'Keyword $(KEYWORD) is ignored for primary keys' );
|
|
1993
2051
|
}
|
|
1994
2052
|
}
|
|
1995
2053
|
if (textElems.length <= keys)
|
|
@@ -2131,9 +2189,9 @@ function getDefinerFunctions( model ) {
|
|
|
2131
2189
|
});
|
|
2132
2190
|
}
|
|
2133
2191
|
}
|
|
2134
|
-
|
|
2192
|
+
if (hasTruthyProp( orig, 'localized' )) { // use location of LOCALIZED keyword
|
|
2135
2193
|
const localized = orig.localized || orig.type || orig.name;
|
|
2136
|
-
elem.localized = { val:
|
|
2194
|
+
elem.localized = { val: null, $inferred: 'localized', location: localized.location };
|
|
2137
2195
|
}
|
|
2138
2196
|
}
|
|
2139
2197
|
if (fioriEnabled)
|
package/lib/compiler/resolver.js
CHANGED
|
@@ -560,8 +560,12 @@ function resolve( model ) {
|
|
|
560
560
|
// or should we use orig.location? - TODO: try to find test to see message
|
|
561
561
|
.$inferred = 'expand-element';
|
|
562
562
|
}
|
|
563
|
-
|
|
564
|
-
//
|
|
563
|
+
// Set elements expansion status (the if condition is always true, as no
|
|
564
|
+
// elements expansion will take place on artifact with existing other
|
|
565
|
+
// member property):
|
|
566
|
+
if (!art.$expand)
|
|
567
|
+
art.$expand = 'origin'; // if value stays, elements won't appear in CSN
|
|
568
|
+
// TODO: have some art.elements[SYM.$inferred] = 'expand-elements';
|
|
565
569
|
return true;
|
|
566
570
|
}
|
|
567
571
|
|
|
@@ -587,14 +591,45 @@ function resolve( model ) {
|
|
|
587
591
|
return false;
|
|
588
592
|
}
|
|
589
593
|
|
|
590
|
-
|
|
594
|
+
// About Helper property $expand for faster the XSN-to-CSN transformation
|
|
595
|
+
// - null/undefined: artifact, member, items does not contain expanded members
|
|
596
|
+
// - 'origin': all expanded (sub) elements have no new target/on and no new annotations
|
|
597
|
+
// that value is only on elements, types, and params -> no other members
|
|
598
|
+
// when set, only on elem/art with expanded elements
|
|
599
|
+
// - 'target': all expanded (sub) elements might only have new target/on, but
|
|
600
|
+
// no indivual annotations on any (sub) member
|
|
601
|
+
// when set, traverse all parents where the value has been 'origin' before
|
|
602
|
+
// - 'annotate': at least one inferred (sub) member has an individual annotation,
|
|
603
|
+
// not counting propagated ones; set up to the definition (main artifact)
|
|
604
|
+
// (only set with anno on $inferred elem)
|
|
605
|
+
// Usage according to CSN flavor:
|
|
606
|
+
// - gensrc: do not render enferred elements (including expanded elements),
|
|
607
|
+
// collect annotate statements with value 'annotate'
|
|
608
|
+
// - client: do not render expanded sub elements if artifact/member is no type, has a type,
|
|
609
|
+
// has $expand = 'origin', and all its _origin also have $expand = 'origin'
|
|
610
|
+
// (might sometimes render the elements unnecessarily, which is not wrong)
|
|
611
|
+
// - universal: do not render expanded sub elements if $expand = 'origin'
|
|
612
|
+
function setExpandStatus( elem, status ) {
|
|
613
|
+
// set on element
|
|
591
614
|
while (elem._main) {
|
|
592
615
|
elem = elem._parent;
|
|
593
|
-
if (
|
|
616
|
+
if (elem.$expand !== 'origin')
|
|
594
617
|
return;
|
|
595
|
-
elem.$expand =
|
|
618
|
+
elem.$expand = status; // meaning: expanded, containing assocs
|
|
596
619
|
for (let line = elem.items; line; line = line.items)
|
|
597
|
-
line.$expand =
|
|
620
|
+
line.$expand = status; // to-csn just uses the innermost $expand
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
function setExpandStatusAnnotate( elem, status ) {
|
|
624
|
+
for (;;) {
|
|
625
|
+
if (elem.$expand === status)
|
|
626
|
+
return; // already set
|
|
627
|
+
elem.$expand = status; // meaning: expanded, containing annos
|
|
628
|
+
for (let line = elem.items; line; line = line.items)
|
|
629
|
+
line.$expand = status; // to-csn just uses the innermost $expand
|
|
630
|
+
if (!elem._main)
|
|
631
|
+
return;
|
|
632
|
+
elem = elem._parent;
|
|
598
633
|
}
|
|
599
634
|
}
|
|
600
635
|
|
|
@@ -606,7 +641,7 @@ function resolve( model ) {
|
|
|
606
641
|
// PRE: elem has no target, assoc has target prop
|
|
607
642
|
if (elem.kind === '$tableAlias')
|
|
608
643
|
return false;
|
|
609
|
-
setExpandStatus( elem );
|
|
644
|
+
setExpandStatus( elem, 'target' );
|
|
610
645
|
let target = resolvePath( assoc.target, 'target', assoc );
|
|
611
646
|
// console.log( info( null, [ elem.location, elem ], {target,art:assoc,name:''+assoc.target},
|
|
612
647
|
// 'RED').toString())
|
|
@@ -719,6 +754,7 @@ function resolve( model ) {
|
|
|
719
754
|
const nullScope = {
|
|
720
755
|
kind: 'namespace', name: { absolute: autoScopeName, location }, location,
|
|
721
756
|
};
|
|
757
|
+
model.definitions[autoScopeName] = nullScope;
|
|
722
758
|
initArtifact( nullScope );
|
|
723
759
|
return nullScope;
|
|
724
760
|
}
|
|
@@ -1550,14 +1586,23 @@ function resolve( model ) {
|
|
|
1550
1586
|
}
|
|
1551
1587
|
}
|
|
1552
1588
|
if (art && art._annotate) {
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
//
|
|
1556
|
-
|
|
1557
|
-
|
|
1589
|
+
const aor = art.returns || art;
|
|
1590
|
+
const obj = aor.items || aor.targetAspect || aor;
|
|
1591
|
+
// Currently(?), effectiveType() does not calculate the effective type of
|
|
1592
|
+
// its line item:
|
|
1593
|
+
effectiveType( obj );
|
|
1594
|
+
if (art._annotate.elements)
|
|
1595
|
+
setExpandStatusAnnotate( aor, 'annotate' );
|
|
1558
1596
|
annotate( obj, 'element', 'elements', 'enum', art );
|
|
1559
1597
|
annotate( art, 'action', 'actions' );
|
|
1560
1598
|
annotate( art, 'param', 'params' );
|
|
1599
|
+
// const { returns } = art._annotate;
|
|
1600
|
+
// if (returns) {
|
|
1601
|
+
// const dict = returns.elements;
|
|
1602
|
+
// const env = obj.returns && obj.returns.elements || null;
|
|
1603
|
+
// for (const n in dict)
|
|
1604
|
+
// annotateMembers( env && env[n], dict[n], 'elements', n, parent, 'element' );
|
|
1605
|
+
// }
|
|
1561
1606
|
}
|
|
1562
1607
|
return;
|
|
1563
1608
|
|
|
@@ -1613,6 +1658,8 @@ function resolve( model ) {
|
|
|
1613
1658
|
ext.kind = 'annotate'; // after setMemberParent()!
|
|
1614
1659
|
setProp( art, '_extension', ext );
|
|
1615
1660
|
setProp( ext.name, '_artifact', art );
|
|
1661
|
+
if (art.returns)
|
|
1662
|
+
ext.$syntax = 'returns';
|
|
1616
1663
|
return ext;
|
|
1617
1664
|
}
|
|
1618
1665
|
|
|
@@ -1816,7 +1863,7 @@ function resolve( model ) {
|
|
|
1816
1863
|
return;
|
|
1817
1864
|
|
|
1818
1865
|
function resolveJoinOn( join ) {
|
|
1819
|
-
if (join.args) {
|
|
1866
|
+
if (join && join.args) { // JOIN
|
|
1820
1867
|
for (const j of join.args)
|
|
1821
1868
|
resolveJoinOn( j );
|
|
1822
1869
|
if (join.on)
|
|
@@ -1853,7 +1900,10 @@ function resolve( model ) {
|
|
|
1853
1900
|
}
|
|
1854
1901
|
const target = resolvePath( obj.target, 'target', art );
|
|
1855
1902
|
if (obj.on) {
|
|
1856
|
-
if (!art._main || !art._parent.elements && !art._parent.items) {
|
|
1903
|
+
if (!art._main || !art._parent.elements && !art._parent.items && !art._parent.targetAspect) {
|
|
1904
|
+
// TODO: test of .items a bit unclear - we should somehow restrict the
|
|
1905
|
+
// use of unmanaged assocs in MANY, at least with $self
|
|
1906
|
+
// TODO: $self usage in anonymous aspects to be corrected in Core Compiler
|
|
1857
1907
|
const isComposition = obj.type && obj.type.path && obj.type.path[0] &&
|
|
1858
1908
|
obj.type.path[0].id === 'cds.Composition';
|
|
1859
1909
|
message( 'assoc-as-type', [ obj.on.location, art ],
|
|
@@ -2294,6 +2344,9 @@ function resolve( model ) {
|
|
|
2294
2344
|
|
|
2295
2345
|
// TODO: there is no need to rewrite the on condition of non-leading queries,
|
|
2296
2346
|
// i.e. we could just have on = {…}
|
|
2347
|
+
// TODO: re-check $self rewrite (with managed composition of aspects),
|
|
2348
|
+
// and actually also $self inside anonymous aspect definitions
|
|
2349
|
+
// (not entirely urgent as we do not analyse it further, at least sole "$self")
|
|
2297
2350
|
function rewriteCondition( elem, assoc ) {
|
|
2298
2351
|
if (enableExpandElements && elem._parent && elem._parent.kind === 'element') {
|
|
2299
2352
|
// managed association as sub element not supported yet
|
|
@@ -2545,12 +2598,18 @@ function resolve( model ) {
|
|
|
2545
2598
|
const args = Array.isArray(expr.args) ? expr.args : Object.values( expr.args );
|
|
2546
2599
|
args.forEach( e => e && resolveExpr( e, e.$expected || expected, user, extDict ) );
|
|
2547
2600
|
}
|
|
2601
|
+
if (expr.suffix && !isBetaEnabled( options, 'windowFunctions' )) {
|
|
2602
|
+
const { location } = expr.suffix[0] || expr;
|
|
2603
|
+
error( null, [ location, user ], 'Window functions are not supported' );
|
|
2604
|
+
}
|
|
2605
|
+
if (expr.suffix)
|
|
2606
|
+
expr.suffix.forEach( s => s && resolveExpr( s, expected, user, extDict ) );
|
|
2548
2607
|
}
|
|
2549
2608
|
|
|
2550
2609
|
function resolveParamsAndWhere( step, expected, user, extDict, isLast ) {
|
|
2551
2610
|
const alias = step._navigation && step._navigation.kind === '$tableAlias' && step._navigation;
|
|
2552
2611
|
const type = alias || effectiveType( step._artifact );
|
|
2553
|
-
const art = type && type.target
|
|
2612
|
+
const art = (type && type.target) ? type.target._artifact : type;
|
|
2554
2613
|
if (!art)
|
|
2555
2614
|
return;
|
|
2556
2615
|
const entity = (art.kind === 'entity') &&
|
|
@@ -2561,7 +2620,7 @@ function resolve( model ) {
|
|
|
2561
2620
|
if (step.where)
|
|
2562
2621
|
resolveExpr( step.where, 'filter', user, environment( type ) );
|
|
2563
2622
|
}
|
|
2564
|
-
else if (step.where || step.cardinality ) {
|
|
2623
|
+
else if (step.where && step.where.location || step.cardinality ) {
|
|
2565
2624
|
const location = combinedLocation( step.where, step.cardinality );
|
|
2566
2625
|
// XSN TODO: filter$location including […]
|
|
2567
2626
|
message( 'expr-no-filter', [ location, user ], { '#': expected },
|
package/lib/compiler/shared.js
CHANGED
|
@@ -60,7 +60,7 @@ const kindProperties = {
|
|
|
60
60
|
|
|
61
61
|
function propExists( prop, parent ) {
|
|
62
62
|
const obj = parent.returns || parent;
|
|
63
|
-
return (obj.items || obj)[prop];
|
|
63
|
+
return (obj.items || obj.targetAspect || obj)[prop];
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
function artifactsEnv( art ) {
|
|
@@ -657,13 +657,28 @@ function fns( model, environment = artifactsEnv ) {
|
|
|
657
657
|
return false;
|
|
658
658
|
continue;
|
|
659
659
|
}
|
|
660
|
-
|
|
661
|
-
// do not check any elements of the path, e.g. $session
|
|
662
|
-
return art;
|
|
663
|
-
}
|
|
660
|
+
|
|
664
661
|
const fn = (spec.envFn && artItemsCount >= 0) ? spec.envFn : environment;
|
|
665
662
|
const env = fn( art, item.location, user, spec.assoc );
|
|
663
|
+
|
|
664
|
+
// do not check any elements of the path, e.g. $session - but still don't return path-head
|
|
665
|
+
if (art && art.$uncheckedElements) {
|
|
666
|
+
if (env && env[item.id]) // something like $user.id/$user.locale
|
|
667
|
+
return env[item.id];
|
|
668
|
+
|
|
669
|
+
// $user.foo - build our own valid path step obj
|
|
670
|
+
// Important: Don't directly modify item!
|
|
671
|
+
const obj = {
|
|
672
|
+
location: item.location,
|
|
673
|
+
kind: 'builtin',
|
|
674
|
+
name: { id: item.id, element: path.map(p => p.id).join('.') },
|
|
675
|
+
};
|
|
676
|
+
setLink(obj, art, '_parent');
|
|
677
|
+
return obj;
|
|
678
|
+
}
|
|
679
|
+
|
|
666
680
|
const sub = setLink( item, env && env[item.id] );
|
|
681
|
+
|
|
667
682
|
if (!sub)
|
|
668
683
|
return (sub === 0) ? 0 : errorNotFound( item, env );
|
|
669
684
|
else if (Array.isArray(sub)) // redefinitions
|
|
@@ -941,10 +956,11 @@ function linkToOrigin( origin, name, parent, prop, location, silentDep ) {
|
|
|
941
956
|
|
|
942
957
|
function setMemberParent( elem, name, parent, prop ) {
|
|
943
958
|
if (prop) { // extension or structure include
|
|
944
|
-
// TODO: consider ARRAY OF and RETURNS, COMPOSITION OF type
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
959
|
+
// TODO: consider nested ARRAY OF and RETURNS, COMPOSITION OF type
|
|
960
|
+
const p = parent.items || parent.targetAspect || parent;
|
|
961
|
+
if (!(prop in p))
|
|
962
|
+
p[prop] = Object.create(null);
|
|
963
|
+
dictAdd( p[prop], name, elem );
|
|
948
964
|
}
|
|
949
965
|
if (parent._outer)
|
|
950
966
|
parent = parent._outer;
|
|
@@ -1000,6 +1016,10 @@ function storeExtension( elem, name, prop, parent, block ) {
|
|
|
1000
1016
|
const kind = `_${ elem.kind }`; // _extend or _annotate
|
|
1001
1017
|
if (!parent[kind])
|
|
1002
1018
|
setProp( parent, kind, {} );
|
|
1019
|
+
// if (name === '' && prop === 'params') {
|
|
1020
|
+
// pushToDict( parent[kind], 'returns', elem ); // not really a dict
|
|
1021
|
+
// return;
|
|
1022
|
+
// }
|
|
1003
1023
|
if (!parent[kind][prop])
|
|
1004
1024
|
parent[kind][prop] = Object.create(null);
|
|
1005
1025
|
pushToDict( parent[kind][prop], name, elem );
|