@sap/cds-compiler 2.5.0 → 2.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +191 -9
- package/bin/cdsc.js +2 -2
- package/doc/CHANGELOG_BETA.md +33 -3
- package/lib/api/main.js +29 -101
- package/lib/api/options.js +15 -11
- package/lib/api/validate.js +12 -8
- package/lib/backends.js +0 -81
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +63 -9
- package/lib/base/messages.js +63 -21
- package/lib/base/model.js +2 -3
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +25 -7
- package/lib/checks/selectItems.js +25 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +38 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +16 -7
- package/lib/compiler/builtins.js +2 -0
- package/lib/compiler/checks.js +6 -4
- package/lib/compiler/definer.js +99 -42
- package/lib/compiler/index.js +73 -27
- package/lib/compiler/resolver.js +288 -157
- package/lib/compiler/shared.js +31 -11
- package/lib/edm/annotations/genericTranslation.js +182 -186
- package/lib/edm/csn2edm.js +103 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +361 -114
- package/lib/edm/edmUtils.js +103 -33
- package/lib/gen/Dictionary.json +22 -0
- 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 +4713 -4279
- package/lib/json/from-csn.js +103 -45
- package/lib/json/to-csn.js +296 -117
- package/lib/language/antlrParser.js +4 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +21 -12
- package/lib/language/language.g4 +99 -31
- package/lib/main.d.ts +81 -3
- package/lib/main.js +30 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +329 -142
- package/lib/model/csnUtils.js +235 -58
- package/lib/model/enrichCsn.js +18 -1
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/modelCompare/compare.js +37 -20
- package/lib/optionProcessor.js +9 -3
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +8 -5
- package/lib/render/toCdl.js +112 -33
- package/lib/render/toHdbcds.js +134 -64
- package/lib/render/toSql.js +91 -38
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +29 -13
- package/lib/transform/db/draft.js +8 -6
- package/lib/transform/db/expansion.js +582 -0
- package/lib/transform/db/flattening.js +325 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +284 -63
- package/lib/transform/forHanaNew.js +98 -381
- package/lib/transform/forOdataNew.js +21 -22
- package/lib/transform/localized.js +37 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +134 -78
- package/lib/transform/translateAssocsToJoins.js +17 -14
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +0 -11
- package/lib/utils/moduleResolve.js +6 -8
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
package/lib/compiler/resolver.js
CHANGED
|
@@ -45,9 +45,7 @@ const {
|
|
|
45
45
|
dictAdd, dictAddArray,
|
|
46
46
|
} = require('../base/dictionaries');
|
|
47
47
|
const { dictLocation } = require('../base/location');
|
|
48
|
-
const {
|
|
49
|
-
makeMessageFunction, searchName, weakLocation,
|
|
50
|
-
} = require('../base/messages');
|
|
48
|
+
const { searchName, weakLocation } = require('../base/messages');
|
|
51
49
|
const { combinedLocation } = require('../base/location');
|
|
52
50
|
const { pushLink } = require('./utils');
|
|
53
51
|
const {
|
|
@@ -84,7 +82,7 @@ function resolve( model ) {
|
|
|
84
82
|
} = fns( model, environment );
|
|
85
83
|
const {
|
|
86
84
|
info, warning, error, message,
|
|
87
|
-
} =
|
|
85
|
+
} = model.$messageFunctions;
|
|
88
86
|
const {
|
|
89
87
|
initArtifact,
|
|
90
88
|
lateExtensions,
|
|
@@ -95,6 +93,8 @@ function resolve( model ) {
|
|
|
95
93
|
|
|
96
94
|
// behavior depending on option `deprecated`:
|
|
97
95
|
const enableExpandElements = !isDeprecatedEnabled( options, 'noElementsExpansion' );
|
|
96
|
+
// TODO: we should get rid of noElementsExpansion soon; both
|
|
97
|
+
// beta.nestedProjections and beta.universalCsn do not work with it.
|
|
98
98
|
const scopedRedirections
|
|
99
99
|
= enableExpandElements &&
|
|
100
100
|
!isDeprecatedEnabled( options, 'generatedEntityNameWithUnderscore' ) &&
|
|
@@ -319,6 +319,7 @@ function resolve( model ) {
|
|
|
319
319
|
return null;
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
+
// TODO: test it in combination with top-level CAST function
|
|
322
323
|
function directType( art ) {
|
|
323
324
|
// Be careful when using it with art.target or art.enum or art.elements
|
|
324
325
|
if (art._origin || art.builtin)
|
|
@@ -326,14 +327,14 @@ function resolve( model ) {
|
|
|
326
327
|
if (art.type)
|
|
327
328
|
return resolveType( art.type, art );
|
|
328
329
|
// console.log( 'EXPR-IN', art.kind, refString(art.name) )
|
|
329
|
-
if (!art._main)
|
|
330
|
+
if (!art._main || !art.value || !art.value.path)
|
|
330
331
|
return undefined;
|
|
331
332
|
if (art._pathHead && art.value) {
|
|
332
333
|
setProp( art, '_origin', resolvePath( art.value, 'expr', art, null ) );
|
|
333
334
|
return art._origin;
|
|
334
335
|
}
|
|
335
336
|
const query = userQuery( art ) || art._parent;
|
|
336
|
-
if (query.kind !== 'select'
|
|
337
|
+
if (query.kind !== 'select')
|
|
337
338
|
return undefined;
|
|
338
339
|
// Reached an element in a query which is a simple ref -> return referred artifact
|
|
339
340
|
// TODO: remember that we still have to resolve path arguments and filters
|
|
@@ -424,7 +425,7 @@ function resolve( model ) {
|
|
|
424
425
|
info( 'query-missing-element', [ ielem.name.location, view ], { id },
|
|
425
426
|
'Element $(ID) is missing in specified elements' );
|
|
426
427
|
}
|
|
427
|
-
else
|
|
428
|
+
else {
|
|
428
429
|
for (const prop in selem) {
|
|
429
430
|
// just annotation assignments and doc comments for the moment
|
|
430
431
|
if (prop.charAt(0) === '@' || prop === 'doc')
|
|
@@ -435,7 +436,7 @@ function resolve( model ) {
|
|
|
435
436
|
}
|
|
436
437
|
for (const id in view.elements$) {
|
|
437
438
|
const selem = view.elements$[id]; // specified element
|
|
438
|
-
if (!
|
|
439
|
+
if (!selem.$replacement) {
|
|
439
440
|
error( 'query-unspecified-element', [ selem.name.location, selem ], { id },
|
|
440
441
|
'Element $(ID) does not result from the query' );
|
|
441
442
|
}
|
|
@@ -445,8 +446,7 @@ function resolve( model ) {
|
|
|
445
446
|
function traverseElementEnvironments( art ) {
|
|
446
447
|
populateView( art );
|
|
447
448
|
environment( art );
|
|
448
|
-
|
|
449
|
-
forEachGeneric( art, 'elements', traverseElementEnvironments );
|
|
449
|
+
forEachMember( art, traverseElementEnvironments );
|
|
450
450
|
}
|
|
451
451
|
|
|
452
452
|
function populateQuery( query ) {
|
|
@@ -454,6 +454,7 @@ function resolve( model ) {
|
|
|
454
454
|
// already done or $join query or parse error
|
|
455
455
|
return;
|
|
456
456
|
setProp( query, '_combined', Object.create(null) );
|
|
457
|
+
query.$inlines = [];
|
|
457
458
|
forEachGeneric( query, '$tableAliases', resolveTabRef );
|
|
458
459
|
|
|
459
460
|
initFromColumns( query, query.columns );
|
|
@@ -540,7 +541,8 @@ function resolve( model ) {
|
|
|
540
541
|
if (art.elements || art.kind === '$tableAlias' ||
|
|
541
542
|
// no element expansions for "non-proper" types like
|
|
542
543
|
// entities (as parameter types) etc:
|
|
543
|
-
struct.kind !== 'type' && struct.kind !== 'element' &&
|
|
544
|
+
struct.kind !== 'type' && struct.kind !== 'element' && struct.kind !== 'param' &&
|
|
545
|
+
!struct._outer)
|
|
544
546
|
return false;
|
|
545
547
|
if (struct.elements === 0 || isInParents( art, eType )) {
|
|
546
548
|
art.elements = 0; // circular
|
|
@@ -559,8 +561,12 @@ function resolve( model ) {
|
|
|
559
561
|
// or should we use orig.location? - TODO: try to find test to see message
|
|
560
562
|
.$inferred = 'expand-element';
|
|
561
563
|
}
|
|
562
|
-
|
|
563
|
-
//
|
|
564
|
+
// Set elements expansion status (the if condition is always true, as no
|
|
565
|
+
// elements expansion will take place on artifact with existing other
|
|
566
|
+
// member property):
|
|
567
|
+
if (!art.$expand)
|
|
568
|
+
art.$expand = 'origin'; // if value stays, elements won't appear in CSN
|
|
569
|
+
// TODO: have some art.elements[SYM.$inferred] = 'expand-elements';
|
|
564
570
|
return true;
|
|
565
571
|
}
|
|
566
572
|
|
|
@@ -586,14 +592,45 @@ function resolve( model ) {
|
|
|
586
592
|
return false;
|
|
587
593
|
}
|
|
588
594
|
|
|
589
|
-
|
|
595
|
+
// About Helper property $expand for faster the XSN-to-CSN transformation
|
|
596
|
+
// - null/undefined: artifact, member, items does not contain expanded members
|
|
597
|
+
// - 'origin': all expanded (sub) elements have no new target/on and no new annotations
|
|
598
|
+
// that value is only on elements, types, and params -> no other members
|
|
599
|
+
// when set, only on elem/art with expanded elements
|
|
600
|
+
// - 'target': all expanded (sub) elements might only have new target/on, but
|
|
601
|
+
// no indivual annotations on any (sub) member
|
|
602
|
+
// when set, traverse all parents where the value has been 'origin' before
|
|
603
|
+
// - 'annotate': at least one inferred (sub) member has an individual annotation,
|
|
604
|
+
// not counting propagated ones; set up to the definition (main artifact)
|
|
605
|
+
// (only set with anno on $inferred elem)
|
|
606
|
+
// Usage according to CSN flavor:
|
|
607
|
+
// - gensrc: do not render enferred elements (including expanded elements),
|
|
608
|
+
// collect annotate statements with value 'annotate'
|
|
609
|
+
// - client: do not render expanded sub elements if artifact/member is no type, has a type,
|
|
610
|
+
// has $expand = 'origin', and all its _origin also have $expand = 'origin'
|
|
611
|
+
// (might sometimes render the elements unnecessarily, which is not wrong)
|
|
612
|
+
// - universal: do not render expanded sub elements if $expand = 'origin'
|
|
613
|
+
function setExpandStatus( elem, status ) {
|
|
614
|
+
// set on element
|
|
590
615
|
while (elem._main) {
|
|
591
616
|
elem = elem._parent;
|
|
592
|
-
if (
|
|
617
|
+
if (elem.$expand !== 'origin')
|
|
593
618
|
return;
|
|
594
|
-
elem.$expand =
|
|
619
|
+
elem.$expand = status; // meaning: expanded, containing assocs
|
|
620
|
+
for (let line = elem.items; line; line = line.items)
|
|
621
|
+
line.$expand = status; // to-csn just uses the innermost $expand
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
function setExpandStatusAnnotate( elem, status ) {
|
|
625
|
+
for (;;) {
|
|
626
|
+
if (elem.$expand === status)
|
|
627
|
+
return; // already set
|
|
628
|
+
elem.$expand = status; // meaning: expanded, containing annos
|
|
595
629
|
for (let line = elem.items; line; line = line.items)
|
|
596
|
-
line.$expand =
|
|
630
|
+
line.$expand = status; // to-csn just uses the innermost $expand
|
|
631
|
+
if (!elem._main)
|
|
632
|
+
return;
|
|
633
|
+
elem = elem._parent;
|
|
597
634
|
}
|
|
598
635
|
}
|
|
599
636
|
|
|
@@ -605,7 +642,7 @@ function resolve( model ) {
|
|
|
605
642
|
// PRE: elem has no target, assoc has target prop
|
|
606
643
|
if (elem.kind === '$tableAlias')
|
|
607
644
|
return false;
|
|
608
|
-
setExpandStatus( elem );
|
|
645
|
+
setExpandStatus( elem, 'target' );
|
|
609
646
|
let target = resolvePath( assoc.target, 'target', assoc );
|
|
610
647
|
// console.log( info( null, [ elem.location, elem ], {target,art:assoc,name:''+assoc.target},
|
|
611
648
|
// 'RED').toString())
|
|
@@ -718,6 +755,7 @@ function resolve( model ) {
|
|
|
718
755
|
const nullScope = {
|
|
719
756
|
kind: 'namespace', name: { absolute: autoScopeName, location }, location,
|
|
720
757
|
};
|
|
758
|
+
model.definitions[autoScopeName] = nullScope;
|
|
721
759
|
initArtifact( nullScope );
|
|
722
760
|
return nullScope;
|
|
723
761
|
}
|
|
@@ -926,26 +964,53 @@ function resolve( model ) {
|
|
|
926
964
|
return art;
|
|
927
965
|
}
|
|
928
966
|
|
|
967
|
+
// TODO: probably do this already in definer.js
|
|
968
|
+
function ensureColumnName( col, query ) {
|
|
969
|
+
if (col.name)
|
|
970
|
+
return col.name.id;
|
|
971
|
+
if (col.inline || col.val === '*')
|
|
972
|
+
return '';
|
|
973
|
+
const path = col.value &&
|
|
974
|
+
(col.value.path || !col.value.args && col.value.func && col.value.func.path);
|
|
975
|
+
if (path) {
|
|
976
|
+
const last = !path.broken && path.length && path[path.length - 1];
|
|
977
|
+
if (last) {
|
|
978
|
+
col.name = { id: last.id, location: last.location, $inferred: 'as' };
|
|
979
|
+
return col.name.id;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
else if (col.value || col.expand) {
|
|
983
|
+
error( 'query-req-name', [ col.value && col.value.location || col.location, query ], {},
|
|
984
|
+
'Alias name is required for this select item' );
|
|
985
|
+
}
|
|
986
|
+
// invent a name for code completion in expression
|
|
987
|
+
col.name = {
|
|
988
|
+
id: '',
|
|
989
|
+
location: col.value && col.value.location || col.location,
|
|
990
|
+
$inferred: 'none',
|
|
991
|
+
};
|
|
992
|
+
return '';
|
|
993
|
+
}
|
|
994
|
+
|
|
929
995
|
// TODO: make this function shorter - make part of this (e.g. setting
|
|
930
996
|
// parent/name) also be part of definer.js
|
|
931
997
|
// TODO: query is actually the elemParent, where the new elements are added to
|
|
932
|
-
|
|
998
|
+
// top-level: just query, columns
|
|
999
|
+
// inline: + elements (TODO: remove), colParent
|
|
1000
|
+
// expand: just query (which is a column/element), columns=array of expand
|
|
1001
|
+
function initFromColumns( query, columns, inlineHead = undefined ) {
|
|
933
1002
|
const elemsParent = query.items || query;
|
|
934
|
-
if (!
|
|
935
|
-
elements = Object.create(null); // explicitly prov
|
|
1003
|
+
if (!inlineHead) {
|
|
936
1004
|
elemsParent.elements = Object.create(null);
|
|
937
1005
|
if (query._main._leadingQuery === query) // never the case for 'expand'
|
|
938
1006
|
query._main.elements = elemsParent.elements;
|
|
939
1007
|
}
|
|
940
|
-
let wildcard = false;
|
|
941
|
-
let inline = 0;
|
|
942
1008
|
|
|
943
1009
|
for (const col of columns || [ { val: '*' } ]) {
|
|
944
1010
|
if (col.val === '*') {
|
|
945
|
-
|
|
946
|
-
|
|
1011
|
+
const siblings = wildcardSiblings( columns, query );
|
|
1012
|
+
expandWildcard( col, siblings, inlineHead, query );
|
|
947
1013
|
}
|
|
948
|
-
col.kind = 'element';
|
|
949
1014
|
if ((col.expand || col.inline) && !isBetaEnabled( options, 'nestedProjections' )) {
|
|
950
1015
|
error( null, [ col.location, query ], { prop: (col.expand ? 'expand' : 'inline') },
|
|
951
1016
|
'Unsupported nested $(PROP)' );
|
|
@@ -957,137 +1022,135 @@ function resolve( model ) {
|
|
|
957
1022
|
col.name = {};
|
|
958
1023
|
// a name for this internal symtab entry (e.g. '.2' to avoid clashes
|
|
959
1024
|
// with real elements) is only relevant for for `cdsc -R`/debugging
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1025
|
+
const q = userQuery( query );
|
|
1026
|
+
q.$inlines.push( col );
|
|
1027
|
+
// or use userQuery( query ) in the following, too?
|
|
1028
|
+
setMemberParent( col, `.${ q.$inlines.length }`, query );
|
|
1029
|
+
initFromColumns( query, col.inline, col );
|
|
964
1030
|
continue;
|
|
965
1031
|
}
|
|
966
|
-
if (!col
|
|
967
|
-
const
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
error( '
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
else if (path.length && !path.broken) {
|
|
974
|
-
const last = path[path.length - 1];
|
|
975
|
-
if (last)
|
|
976
|
-
col.name = { id: last.id, location: last.location, $inferred: 'as' };
|
|
977
|
-
}
|
|
978
|
-
if (!col.name) {
|
|
979
|
-
// invent a name for code completion in expression
|
|
980
|
-
col.name = {
|
|
981
|
-
id: '',
|
|
982
|
-
location: col.value && col.value.location || col.location,
|
|
983
|
-
$inferred: 'none',
|
|
984
|
-
};
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
const { id } = col.name;
|
|
988
|
-
dictAdd( elements, id, col, ( name, location ) => {
|
|
989
|
-
error( 'duplicate-definition', [ location, query ], { name, '#': 'element' } );
|
|
990
|
-
});
|
|
991
|
-
setMemberParent( col, id, query );
|
|
992
|
-
if (!wildcard) {
|
|
993
|
-
if (col.$duplicates !== true)
|
|
994
|
-
dictAdd( elemsParent.elements, id, col );
|
|
995
|
-
col.$replacement = true;
|
|
1032
|
+
else if (!col.$replacement) {
|
|
1033
|
+
const id = ensureColumnName( col, query );
|
|
1034
|
+
col.kind = 'element';
|
|
1035
|
+
dictAdd( elemsParent.elements, id, col, ( name, location ) => {
|
|
1036
|
+
error( 'duplicate-definition', [ location, query ], { name, '#': 'element' } );
|
|
1037
|
+
});
|
|
1038
|
+
setMemberParent( col, id, query );
|
|
996
1039
|
}
|
|
997
1040
|
}
|
|
998
|
-
if (wildcard)
|
|
999
|
-
expandWildcard( elements, wildcard );
|
|
1000
1041
|
forEachGeneric( query, 'elements', e => initElem( e, query ) );
|
|
1001
1042
|
return true;
|
|
1043
|
+
}
|
|
1002
1044
|
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
else {
|
|
1058
|
-
selElem.$inferred = 'query';
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
else if (Array.isArray(navElem)) {
|
|
1062
|
-
const names = navElem.filter( e => !e.$duplicates)
|
|
1063
|
-
.map( e => `${ e.name.alias }.${ e.name.element }` );
|
|
1064
|
-
if (names.length) {
|
|
1065
|
-
error( 'wildcard-ambiguous', [ location, query ], { id: name, names },
|
|
1066
|
-
'Ambiguous wildcard, select $(ID) explicitly with $(NAMES)' );
|
|
1067
|
-
}
|
|
1045
|
+
// col ($replacement set before *)
|
|
1046
|
+
// false if two cols have same name
|
|
1047
|
+
function wildcardSiblings( columns, query ) {
|
|
1048
|
+
const siblings = Object.create(null);
|
|
1049
|
+
if (!columns)
|
|
1050
|
+
return siblings;
|
|
1051
|
+
|
|
1052
|
+
let seenWildcard = null;
|
|
1053
|
+
for (const col of columns) {
|
|
1054
|
+
const id = ensureColumnName( col, query );
|
|
1055
|
+
if (id) {
|
|
1056
|
+
col.$replacement = !seenWildcard;
|
|
1057
|
+
siblings[id] = !(id in siblings) && col;
|
|
1058
|
+
}
|
|
1059
|
+
else if (col.val === '*') {
|
|
1060
|
+
seenWildcard = true;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
return siblings;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// TODO: make struct.* are to be added at place, not sub-wildcards first,
|
|
1067
|
+
// see test3/Queries/ExpandInlineCreate/Excluding.cds
|
|
1068
|
+
// TODO: disallow $self.elem.* and $self.*, toSelf.* (circular dependency)
|
|
1069
|
+
function expandWildcard( wildcard, siblingElements, colParent, query ) {
|
|
1070
|
+
const { elements } = query.items || query;
|
|
1071
|
+
let location = wildcard.location || query.from && query.from.location || query.location;
|
|
1072
|
+
const inferred = query._main.$inferred;
|
|
1073
|
+
const excludingDict = (colParent || query).excludingDict || Object.create(null);
|
|
1074
|
+
|
|
1075
|
+
const envParent = wildcard._pathHead; // TODO: rename _pathHead to _pathEnv
|
|
1076
|
+
// console.log('S1:',location.line,location.col,
|
|
1077
|
+
// envParent&&!!envParent._origin&&envParent._origin.name)
|
|
1078
|
+
const env = columnEnv( envParent, query );
|
|
1079
|
+
// console.log('S2:',location.line,location.col,
|
|
1080
|
+
// envParent&&!!envParent._origin&&envParent._origin.name,
|
|
1081
|
+
// Object.keys(env),Object.keys(elements))
|
|
1082
|
+
for (const name in env) {
|
|
1083
|
+
const navElem = env[name];
|
|
1084
|
+
// TODO: if it is an array, filter out those with masked
|
|
1085
|
+
if (excludingDict[name] || navElem.masked && navElem.masked.val)
|
|
1086
|
+
continue;
|
|
1087
|
+
const sibling = siblingElements[name];
|
|
1088
|
+
if (sibling) { // is explicitly provided (without duplicate)
|
|
1089
|
+
if (!inferred && !envParent) // not yet for expand/inline
|
|
1090
|
+
reportReplacement( sibling, navElem, query );
|
|
1091
|
+
if (!sibling.$replacement) {
|
|
1092
|
+
sibling.$replacement = true;
|
|
1093
|
+
sibling.kind = 'element';
|
|
1094
|
+
dictAdd( elements, name, sibling, ( _name, loc ) => {
|
|
1095
|
+
// there can be a definition from a previous inline with the same name:
|
|
1096
|
+
error( 'duplicate-definition', [ loc, query ], { name, '#': 'element' } );
|
|
1097
|
+
});
|
|
1098
|
+
setMemberParent( sibling, name, query );
|
|
1068
1099
|
}
|
|
1069
|
-
else {
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1100
|
+
// else {
|
|
1101
|
+
// sibling.$inferred = 'query';
|
|
1102
|
+
// }
|
|
1103
|
+
}
|
|
1104
|
+
else if (Array.isArray(navElem)) {
|
|
1105
|
+
const names = navElem.filter( e => !e.$duplicates)
|
|
1106
|
+
.map( e => `${ e.name.alias }.${ e.name.element }` );
|
|
1107
|
+
if (names.length) {
|
|
1108
|
+
error( 'wildcard-ambiguous', [ location, query ], { id: name, names },
|
|
1109
|
+
'Ambiguous wildcard, select $(ID) explicitly with $(NAMES)' );
|
|
1079
1110
|
}
|
|
1080
1111
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1112
|
+
else {
|
|
1113
|
+
location = weakLocation( location );
|
|
1114
|
+
const origin = envParent ? navElem : navElem._origin;
|
|
1115
|
+
const elem = linkToOrigin( origin, name, query, null, location );
|
|
1116
|
+
// TODO: check assocToMany { * }
|
|
1117
|
+
dictAdd( elements, name, elem, ( _name, loc ) => {
|
|
1118
|
+
// there can be a definition from a previous inline with the same name:
|
|
1119
|
+
error( 'duplicate-definition', [ loc, query ], { name, '#': 'element' } );
|
|
1120
|
+
});
|
|
1121
|
+
elem.$inferred = '*';
|
|
1122
|
+
elem.name.$inferred = '*';
|
|
1123
|
+
if (envParent)
|
|
1124
|
+
setWildcardExpandInline( elem, envParent, origin, name, location );
|
|
1125
|
+
else
|
|
1126
|
+
setElementOrigin( elem, navElem, name, location );
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
if (envParent || query.kind !== 'select') {
|
|
1130
|
+
// already done in populateQuery (TODO: change that and check whether
|
|
1131
|
+
// `*` is allowed at all in definer)
|
|
1132
|
+
const user = colParent || query;
|
|
1133
|
+
for (const name in user.excludingDict)
|
|
1134
|
+
resolveExcluding( name, env, excludingDict, query );
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
function reportReplacement( sibling, navElem, query ) {
|
|
1139
|
+
// TODO: bring this much less often = only if shadowed elem does not appear
|
|
1140
|
+
// in expr and if not projected as other name.
|
|
1141
|
+
// Probably needs to be reported at a later phase
|
|
1142
|
+
const path = sibling.value && sibling.value.path;
|
|
1143
|
+
if (!sibling.target || sibling.target.$inferred || // not explicit REDIRECTED TO
|
|
1144
|
+
path && path[path.length - 1].id !== sibling.name.id) { // or renamed
|
|
1145
|
+
const { id } = sibling.name;
|
|
1146
|
+
if (Array.isArray(navElem)) {
|
|
1147
|
+
info( 'wildcard-excluding-many', [ sibling.name.location, query ], { id },
|
|
1148
|
+
'This select item replaces $(ID) from two or more sources' );
|
|
1149
|
+
}
|
|
1150
|
+
else {
|
|
1151
|
+
info( 'wildcard-excluding-one', [ sibling.name.location, query ],
|
|
1152
|
+
{ id, alias: navElem._parent.name.id },
|
|
1153
|
+
'This select item replaces $(ID) from table alias $(ALIAS)' );
|
|
1091
1154
|
}
|
|
1092
1155
|
}
|
|
1093
1156
|
}
|
|
@@ -1378,6 +1441,7 @@ function resolve( model ) {
|
|
|
1378
1441
|
}
|
|
1379
1442
|
// Resolve projections/views
|
|
1380
1443
|
// if (art.query)console.log( info( null, [art.query.location,art.query], 'VQ:' ).toString() );
|
|
1444
|
+
// TODO: here, any order should be ok, i.e. just loop over $queries
|
|
1381
1445
|
traverseQueryPost( art.query, false, resolveQuery );
|
|
1382
1446
|
|
|
1383
1447
|
if (obj.type || obj._origin || obj.value && obj.value.path || obj.elements) // typed artifacts
|
|
@@ -1523,14 +1587,28 @@ function resolve( model ) {
|
|
|
1523
1587
|
}
|
|
1524
1588
|
}
|
|
1525
1589
|
if (art && art._annotate) {
|
|
1526
|
-
if (art
|
|
1527
|
-
art
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1590
|
+
if (art.kind === 'action' || art.kind === 'function') {
|
|
1591
|
+
expandParameters( art );
|
|
1592
|
+
if (art.returns)
|
|
1593
|
+
effectiveType( art.returns );
|
|
1594
|
+
}
|
|
1595
|
+
const aor = art.returns || art;
|
|
1596
|
+
const obj = aor.items || aor.targetAspect || aor;
|
|
1597
|
+
// Currently(?), effectiveType() does not calculate the effective type of
|
|
1598
|
+
// its line item:
|
|
1599
|
+
effectiveType( obj );
|
|
1600
|
+
if (art._annotate.elements)
|
|
1601
|
+
setExpandStatusAnnotate( aor, 'annotate' );
|
|
1531
1602
|
annotate( obj, 'element', 'elements', 'enum', art );
|
|
1532
1603
|
annotate( art, 'action', 'actions' );
|
|
1533
1604
|
annotate( art, 'param', 'params' );
|
|
1605
|
+
// const { returns } = art._annotate;
|
|
1606
|
+
// if (returns) {
|
|
1607
|
+
// const dict = returns.elements;
|
|
1608
|
+
// const env = obj.returns && obj.returns.elements || null;
|
|
1609
|
+
// for (const n in dict)
|
|
1610
|
+
// annotateMembers( env && env[n], dict[n], 'elements', n, parent, 'element' );
|
|
1611
|
+
// }
|
|
1534
1612
|
}
|
|
1535
1613
|
return;
|
|
1536
1614
|
|
|
@@ -1548,6 +1626,42 @@ function resolve( model ) {
|
|
|
1548
1626
|
annotateMembers( env && env[n], dict[n], prop, n, parent, kind );
|
|
1549
1627
|
}
|
|
1550
1628
|
}
|
|
1629
|
+
function expandParameters( action ) {
|
|
1630
|
+
// see also expandElements()
|
|
1631
|
+
if (!enableExpandElements || !effectiveType( action ))
|
|
1632
|
+
return;
|
|
1633
|
+
const chain = [];
|
|
1634
|
+
// Should we be able to consider params and returns separately?
|
|
1635
|
+
// Probably not, let to-csn omit unchanged params/returns.
|
|
1636
|
+
while (action._origin && !action.params) {
|
|
1637
|
+
chain.push( action );
|
|
1638
|
+
action = action._origin;
|
|
1639
|
+
}
|
|
1640
|
+
chain.reverse();
|
|
1641
|
+
for (const art of chain) {
|
|
1642
|
+
const origin = art._origin;
|
|
1643
|
+
if (!art.params && origin.params) {
|
|
1644
|
+
for (const name in origin.params) {
|
|
1645
|
+
// TODO: we could check _annotate here to decide whether we really
|
|
1646
|
+
// not to create proxies
|
|
1647
|
+
const orig = origin.params[name];
|
|
1648
|
+
linkToOrigin( orig, name, art, 'params', weakLocation( orig.location ), true )
|
|
1649
|
+
.$inferred = 'expand-param';
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
if (!art.returns && origin.returns) {
|
|
1653
|
+
// TODO: make linkToOrigin() work for returns, kind/name?
|
|
1654
|
+
const location = weakLocation( origin.returns.location );
|
|
1655
|
+
art.returns = {
|
|
1656
|
+
name: Object.assign( {}, art.name, { id: '', param: '', location } ),
|
|
1657
|
+
kind: 'param',
|
|
1658
|
+
location,
|
|
1659
|
+
$inferred: 'expand-param',
|
|
1660
|
+
};
|
|
1661
|
+
setProp( art.returns, '_origin', origin.returns );
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1551
1665
|
|
|
1552
1666
|
function extensionFor( art ) {
|
|
1553
1667
|
if (art.kind === 'annotate')
|
|
@@ -1586,6 +1700,8 @@ function resolve( model ) {
|
|
|
1586
1700
|
ext.kind = 'annotate'; // after setMemberParent()!
|
|
1587
1701
|
setProp( art, '_extension', ext );
|
|
1588
1702
|
setProp( ext.name, '_artifact', art );
|
|
1703
|
+
if (art.returns)
|
|
1704
|
+
ext.$syntax = 'returns';
|
|
1589
1705
|
return ext;
|
|
1590
1706
|
}
|
|
1591
1707
|
|
|
@@ -1761,8 +1877,11 @@ function resolve( model ) {
|
|
|
1761
1877
|
// pure path has been resolved, resolve args and filter now:
|
|
1762
1878
|
resolveExpr( alias, 'from', query._parent );
|
|
1763
1879
|
} );
|
|
1880
|
+
for (const col of query.$inlines)
|
|
1881
|
+
resolveExpr( col.value, 'expr', col, undefined, true );
|
|
1882
|
+
// for (const col of query.$inlines)
|
|
1883
|
+
// if (!col.value.path) throw Error(col.name.element)
|
|
1764
1884
|
if (query !== query._main._leadingQuery) // will be done later
|
|
1765
|
-
// TODO: rethink elements(view) === elements(view._leadingQuery)
|
|
1766
1885
|
forEachGeneric( query, 'elements', resolveRefs );
|
|
1767
1886
|
if (query.from)
|
|
1768
1887
|
resolveJoinOn( query.from );
|
|
@@ -1786,7 +1905,7 @@ function resolve( model ) {
|
|
|
1786
1905
|
return;
|
|
1787
1906
|
|
|
1788
1907
|
function resolveJoinOn( join ) {
|
|
1789
|
-
if (join.args) {
|
|
1908
|
+
if (join && join.args) { // JOIN
|
|
1790
1909
|
for (const j of join.args)
|
|
1791
1910
|
resolveJoinOn( j );
|
|
1792
1911
|
if (join.on)
|
|
@@ -1823,7 +1942,10 @@ function resolve( model ) {
|
|
|
1823
1942
|
}
|
|
1824
1943
|
const target = resolvePath( obj.target, 'target', art );
|
|
1825
1944
|
if (obj.on) {
|
|
1826
|
-
if (!art._main || !art._parent.elements && !art._parent.items) {
|
|
1945
|
+
if (!art._main || !art._parent.elements && !art._parent.items && !art._parent.targetAspect) {
|
|
1946
|
+
// TODO: test of .items a bit unclear - we should somehow restrict the
|
|
1947
|
+
// use of unmanaged assocs in MANY, at least with $self
|
|
1948
|
+
// TODO: $self usage in anonymous aspects to be corrected in Core Compiler
|
|
1827
1949
|
const isComposition = obj.type && obj.type.path && obj.type.path[0] &&
|
|
1828
1950
|
obj.type.path[0].id === 'cds.Composition';
|
|
1829
1951
|
message( 'assoc-as-type', [ obj.on.location, art ],
|
|
@@ -2264,6 +2386,9 @@ function resolve( model ) {
|
|
|
2264
2386
|
|
|
2265
2387
|
// TODO: there is no need to rewrite the on condition of non-leading queries,
|
|
2266
2388
|
// i.e. we could just have on = {…}
|
|
2389
|
+
// TODO: re-check $self rewrite (with managed composition of aspects),
|
|
2390
|
+
// and actually also $self inside anonymous aspect definitions
|
|
2391
|
+
// (not entirely urgent as we do not analyse it further, at least sole "$self")
|
|
2267
2392
|
function rewriteCondition( elem, assoc ) {
|
|
2268
2393
|
if (enableExpandElements && elem._parent && elem._parent.kind === 'element') {
|
|
2269
2394
|
// managed association as sub element not supported yet
|
|
@@ -2515,12 +2640,18 @@ function resolve( model ) {
|
|
|
2515
2640
|
const args = Array.isArray(expr.args) ? expr.args : Object.values( expr.args );
|
|
2516
2641
|
args.forEach( e => e && resolveExpr( e, e.$expected || expected, user, extDict ) );
|
|
2517
2642
|
}
|
|
2643
|
+
if (expr.suffix && !isBetaEnabled( options, 'windowFunctions' )) {
|
|
2644
|
+
const { location } = expr.suffix[0] || expr;
|
|
2645
|
+
error( null, [ location, user ], 'Window functions are not supported' );
|
|
2646
|
+
}
|
|
2647
|
+
if (expr.suffix)
|
|
2648
|
+
expr.suffix.forEach( s => s && resolveExpr( s, expected, user, extDict ) );
|
|
2518
2649
|
}
|
|
2519
2650
|
|
|
2520
2651
|
function resolveParamsAndWhere( step, expected, user, extDict, isLast ) {
|
|
2521
2652
|
const alias = step._navigation && step._navigation.kind === '$tableAlias' && step._navigation;
|
|
2522
2653
|
const type = alias || effectiveType( step._artifact );
|
|
2523
|
-
const art = type && type.target
|
|
2654
|
+
const art = (type && type.target) ? type.target._artifact : type;
|
|
2524
2655
|
if (!art)
|
|
2525
2656
|
return;
|
|
2526
2657
|
const entity = (art.kind === 'entity') &&
|
|
@@ -2531,7 +2662,7 @@ function resolve( model ) {
|
|
|
2531
2662
|
if (step.where)
|
|
2532
2663
|
resolveExpr( step.where, 'filter', user, environment( type ) );
|
|
2533
2664
|
}
|
|
2534
|
-
else if (step.where || step.cardinality ) {
|
|
2665
|
+
else if (step.where && step.where.location || step.cardinality ) {
|
|
2535
2666
|
const location = combinedLocation( step.where, step.cardinality );
|
|
2536
2667
|
// XSN TODO: filter$location including […]
|
|
2537
2668
|
message( 'expr-no-filter', [ location, user ], { '#': expected },
|