@sap/cds-compiler 2.5.2 → 2.11.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 +235 -9
- package/bin/cdsc.js +44 -27
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +37 -3
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +37 -123
- package/lib/api/options.js +27 -15
- package/lib/api/validate.js +34 -9
- package/lib/backends.js +9 -89
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +73 -11
- package/lib/base/messages.js +86 -30
- package/lib/base/model.js +6 -6
- package/lib/base/optionProcessorHelper.js +56 -22
- 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 +29 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +23 -7
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +8 -5
- package/lib/compiler/definer.js +157 -133
- package/lib/compiler/index.js +89 -31
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +375 -185
- package/lib/compiler/shared.js +49 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +104 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +388 -146
- package/lib/edm/edmUtils.js +104 -34
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5330 -4300
- package/lib/json/from-csn.js +110 -52
- package/lib/json/to-csn.js +434 -120
- package/lib/language/antlrParser.js +15 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +93 -26
- package/lib/language/language.g4 +172 -31
- package/lib/main.d.ts +216 -19
- package/lib/main.js +32 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +413 -149
- package/lib/model/csnUtils.js +286 -75
- package/lib/model/enrichCsn.js +50 -6
- package/lib/model/revealInternalProperties.js +22 -5
- package/lib/modelCompare/compare.js +39 -21
- package/lib/optionProcessor.js +35 -18
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +9 -6
- package/lib/render/toCdl.js +121 -36
- package/lib/render/toHdbcds.js +148 -98
- package/lib/render/toSql.js +114 -43
- 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 +281 -106
- package/lib/transform/db/draft.js +11 -8
- package/lib/transform/db/expansion.js +584 -0
- package/lib/transform/db/flattening.js +341 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +345 -65
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +131 -793
- package/lib/transform/forOdataNew.js +30 -24
- package/lib/transform/localized.js +39 -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 +144 -78
- package/lib/transform/translateAssocsToJoins.js +22 -27
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -14
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- 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,29 +45,30 @@ 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
|
+
|
|
51
|
+
const { kindProperties } = require('./base');
|
|
53
52
|
const {
|
|
54
|
-
|
|
53
|
+
pushLink,
|
|
54
|
+
setLink,
|
|
55
55
|
augmentPath,
|
|
56
56
|
splitIntoPath,
|
|
57
|
-
|
|
57
|
+
linkToOrigin,
|
|
58
|
+
setMemberParent,
|
|
59
|
+
withAssociation,
|
|
60
|
+
storeExtension,
|
|
61
|
+
dependsOn,
|
|
62
|
+
dependsOnSilent,
|
|
63
|
+
} = require('./utils');
|
|
58
64
|
|
|
59
65
|
const detectCycles = require('./cycle-detector');
|
|
60
66
|
const layers = require('./moduleLayers');
|
|
61
67
|
|
|
62
|
-
const {
|
|
63
|
-
kindProperties, fns, setLink, linkToOrigin, setMemberParent, withAssociation, storeExtension,
|
|
64
|
-
dependsOn, dependsOnSilent,
|
|
65
|
-
} = require('./shared');
|
|
66
|
-
|
|
67
68
|
const annotationPriorities = {
|
|
68
69
|
define: 1, extend: 2, annotate: 2, edmx: 3,
|
|
69
70
|
};
|
|
70
|
-
|
|
71
|
+
const $inferred = Symbol.for('cds.$inferred');
|
|
71
72
|
|
|
72
73
|
// Export function of this file. Resolve type references in augmented CSN
|
|
73
74
|
// `model`. If the model has a property argument `messages`, do not throw
|
|
@@ -75,26 +76,28 @@ const annotationPriorities = {
|
|
|
75
76
|
// that property (should be a vector).
|
|
76
77
|
function resolve( model ) {
|
|
77
78
|
const { options } = model;
|
|
78
|
-
// Get shared
|
|
79
|
+
// Get shared functionality and the message function:
|
|
80
|
+
const {
|
|
81
|
+
info, warning, error, message,
|
|
82
|
+
} = model.$messageFunctions;
|
|
79
83
|
const {
|
|
80
84
|
resolvePath,
|
|
81
85
|
resolveTypeArguments,
|
|
82
86
|
defineAnnotations,
|
|
83
87
|
attachAndEmitValidNames,
|
|
84
|
-
} = fns( model, environment );
|
|
85
|
-
const {
|
|
86
|
-
info, warning, error, message,
|
|
87
|
-
} = makeMessageFunction( model, model.options, 'compile' );
|
|
88
|
-
const {
|
|
89
88
|
initArtifact,
|
|
90
89
|
lateExtensions,
|
|
91
90
|
projectionAncestor,
|
|
92
|
-
} =
|
|
91
|
+
} = model.$functions;
|
|
92
|
+
model.$volatileFunctions.environment = environment;
|
|
93
|
+
|
|
93
94
|
/** @type {any} may also be a boolean */
|
|
94
95
|
let newAutoExposed = [];
|
|
95
96
|
|
|
96
97
|
// behavior depending on option `deprecated`:
|
|
97
98
|
const enableExpandElements = !isDeprecatedEnabled( options, 'noElementsExpansion' );
|
|
99
|
+
// TODO: we should get rid of noElementsExpansion soon; both
|
|
100
|
+
// beta.nestedProjections and beta.universalCsn do not work with it.
|
|
98
101
|
const scopedRedirections
|
|
99
102
|
= enableExpandElements &&
|
|
100
103
|
!isDeprecatedEnabled( options, 'generatedEntityNameWithUnderscore' ) &&
|
|
@@ -242,6 +245,7 @@ function resolve( model ) {
|
|
|
242
245
|
const chain = [];
|
|
243
246
|
while (art && !('_effectiveType' in art) &&
|
|
244
247
|
(art.type || art._origin || art.value && art.value.path) &&
|
|
248
|
+
// TODO: really stop at art.enum?
|
|
245
249
|
!art.target && !art.enum && !art.elements && !art.items) {
|
|
246
250
|
chain.push( art );
|
|
247
251
|
setProp( art, '_effectiveType', 0 ); // initial setting in case of cycles
|
|
@@ -273,13 +277,17 @@ function resolve( model ) {
|
|
|
273
277
|
// collect the "latest" cardinality (calculate lazyly if necessary)
|
|
274
278
|
let cardinality = art.cardinality ||
|
|
275
279
|
art._effectiveType && (() => getCardinality( art._effectiveType ));
|
|
280
|
+
let prev = art;
|
|
276
281
|
for (const a of chain) {
|
|
277
282
|
if (a.cardinality)
|
|
278
283
|
cardinality = a.cardinality;
|
|
279
284
|
if (a.expand && expandFromColumns( a, art, cardinality ) ||
|
|
280
285
|
art.target && redirectImplicitly( a, art ) ||
|
|
281
|
-
art.elements && expandElements( a, art, eType )
|
|
286
|
+
art.elements && expandElements( a, art, eType ) ||
|
|
287
|
+
art.items && expandItems( a, art, eType ))
|
|
282
288
|
art = a;
|
|
289
|
+
else if (art.enum && expandEnum( a, prev ))
|
|
290
|
+
prev = a; // do not set art - effective type is base
|
|
283
291
|
setProp( a, '_effectiveType', art );
|
|
284
292
|
}
|
|
285
293
|
}
|
|
@@ -319,6 +327,7 @@ function resolve( model ) {
|
|
|
319
327
|
return null;
|
|
320
328
|
}
|
|
321
329
|
|
|
330
|
+
// TODO: test it in combination with top-level CAST function
|
|
322
331
|
function directType( art ) {
|
|
323
332
|
// Be careful when using it with art.target or art.enum or art.elements
|
|
324
333
|
if (art._origin || art.builtin)
|
|
@@ -326,14 +335,14 @@ function resolve( model ) {
|
|
|
326
335
|
if (art.type)
|
|
327
336
|
return resolveType( art.type, art );
|
|
328
337
|
// console.log( 'EXPR-IN', art.kind, refString(art.name) )
|
|
329
|
-
if (!art._main)
|
|
338
|
+
if (!art._main || !art.value || !art.value.path)
|
|
330
339
|
return undefined;
|
|
331
340
|
if (art._pathHead && art.value) {
|
|
332
341
|
setProp( art, '_origin', resolvePath( art.value, 'expr', art, null ) );
|
|
333
342
|
return art._origin;
|
|
334
343
|
}
|
|
335
344
|
const query = userQuery( art ) || art._parent;
|
|
336
|
-
if (query.kind !== 'select'
|
|
345
|
+
if (query.kind !== 'select')
|
|
337
346
|
return undefined;
|
|
338
347
|
// Reached an element in a query which is a simple ref -> return referred artifact
|
|
339
348
|
// TODO: remember that we still have to resolve path arguments and filters
|
|
@@ -404,7 +413,7 @@ function resolve( model ) {
|
|
|
404
413
|
for (const view of resolveChain.reverse()) {
|
|
405
414
|
if (view._status !== '_query' ) { // not already resolved
|
|
406
415
|
setProp( view, '_status', '_query' );
|
|
407
|
-
traverseQueryPost( view.query,
|
|
416
|
+
traverseQueryPost( view.query, null, populateQuery );
|
|
408
417
|
if (view.elements$) // specified elements
|
|
409
418
|
mergeSpecifiedElements( view );
|
|
410
419
|
if (!view.$entity) {
|
|
@@ -424,7 +433,7 @@ function resolve( model ) {
|
|
|
424
433
|
info( 'query-missing-element', [ ielem.name.location, view ], { id },
|
|
425
434
|
'Element $(ID) is missing in specified elements' );
|
|
426
435
|
}
|
|
427
|
-
else
|
|
436
|
+
else {
|
|
428
437
|
for (const prop in selem) {
|
|
429
438
|
// just annotation assignments and doc comments for the moment
|
|
430
439
|
if (prop.charAt(0) === '@' || prop === 'doc')
|
|
@@ -435,7 +444,7 @@ function resolve( model ) {
|
|
|
435
444
|
}
|
|
436
445
|
for (const id in view.elements$) {
|
|
437
446
|
const selem = view.elements$[id]; // specified element
|
|
438
|
-
if (!
|
|
447
|
+
if (!selem.$replacement) {
|
|
439
448
|
error( 'query-unspecified-element', [ selem.name.location, selem ], { id },
|
|
440
449
|
'Element $(ID) does not result from the query' );
|
|
441
450
|
}
|
|
@@ -445,8 +454,7 @@ function resolve( model ) {
|
|
|
445
454
|
function traverseElementEnvironments( art ) {
|
|
446
455
|
populateView( art );
|
|
447
456
|
environment( art );
|
|
448
|
-
|
|
449
|
-
forEachGeneric( art, 'elements', traverseElementEnvironments );
|
|
457
|
+
forEachMember( art, traverseElementEnvironments );
|
|
450
458
|
}
|
|
451
459
|
|
|
452
460
|
function populateQuery( query ) {
|
|
@@ -454,6 +462,7 @@ function resolve( model ) {
|
|
|
454
462
|
// already done or $join query or parse error
|
|
455
463
|
return;
|
|
456
464
|
setProp( query, '_combined', Object.create(null) );
|
|
465
|
+
query.$inlines = [];
|
|
457
466
|
forEachGeneric( query, '$tableAliases', resolveTabRef );
|
|
458
467
|
|
|
459
468
|
initFromColumns( query, query.columns );
|
|
@@ -493,7 +502,8 @@ function resolve( model ) {
|
|
|
493
502
|
});
|
|
494
503
|
}
|
|
495
504
|
forEachGeneric( { elements: alias.elements }, 'elements', ( elem, name ) => {
|
|
496
|
-
|
|
505
|
+
if (elem.$duplicates !== true)
|
|
506
|
+
dictAddArray( query._combined, name, elem, null ); // not dictAdd()
|
|
497
507
|
});
|
|
498
508
|
}
|
|
499
509
|
}
|
|
@@ -534,13 +544,31 @@ function resolve( model ) {
|
|
|
534
544
|
setMemberParent( key, name, elem ); // TODO: set _block here if not present?
|
|
535
545
|
}
|
|
536
546
|
|
|
547
|
+
function expandItems( art, origin, eType ) {
|
|
548
|
+
if (!enableExpandElements || art.items)
|
|
549
|
+
return false;
|
|
550
|
+
if (isInParents( art, eType )) {
|
|
551
|
+
art.items = 0; // circular
|
|
552
|
+
return true;
|
|
553
|
+
}
|
|
554
|
+
const ref = art.type || art.value || art.name;
|
|
555
|
+
const location = ref && ref.location || art.location;
|
|
556
|
+
art.items = { $inferred: 'expand-element', location };
|
|
557
|
+
setProp( art.items, '_outer', art );
|
|
558
|
+
setProp( art.items, '_origin', origin.items );
|
|
559
|
+
if (!art.$expand)
|
|
560
|
+
art.$expand = 'origin'; // if value stays, elements won't appear in CSN
|
|
561
|
+
return true;
|
|
562
|
+
}
|
|
563
|
+
|
|
537
564
|
function expandElements( art, struct, eType ) {
|
|
538
565
|
if (!enableExpandElements)
|
|
539
566
|
return false;
|
|
540
567
|
if (art.elements || art.kind === '$tableAlias' ||
|
|
541
568
|
// no element expansions for "non-proper" types like
|
|
542
569
|
// entities (as parameter types) etc:
|
|
543
|
-
struct.kind !== 'type' && struct.kind !== 'element' &&
|
|
570
|
+
struct.kind !== 'type' && struct.kind !== 'element' && struct.kind !== 'param' &&
|
|
571
|
+
!struct._outer)
|
|
544
572
|
return false;
|
|
545
573
|
if (struct.elements === 0 || isInParents( art, eType )) {
|
|
546
574
|
art.elements = 0; // circular
|
|
@@ -559,8 +587,33 @@ function resolve( model ) {
|
|
|
559
587
|
// or should we use orig.location? - TODO: try to find test to see message
|
|
560
588
|
.$inferred = 'expand-element';
|
|
561
589
|
}
|
|
562
|
-
|
|
563
|
-
//
|
|
590
|
+
// Set elements expansion status (the if condition is always true, as no
|
|
591
|
+
// elements expansion will take place on artifact with existing other
|
|
592
|
+
// member property):
|
|
593
|
+
if (!art.$expand)
|
|
594
|
+
art.$expand = 'origin'; // if value stays, elements won't appear in CSN
|
|
595
|
+
// TODO: have some art.elements[SYM.$inferred] = 'expand-element';
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function expandEnum( art, origin ) {
|
|
600
|
+
if (!enableExpandElements || art.enum)
|
|
601
|
+
return false;
|
|
602
|
+
const ref = art.type || art.value || art.name;
|
|
603
|
+
const location = weakLocation( ref && ref.location || art.location );
|
|
604
|
+
art.enum = Object.create(null);
|
|
605
|
+
for (const name in origin.enum) {
|
|
606
|
+
const orig = origin.enum[name];
|
|
607
|
+
linkToOrigin( orig, name, art, 'enum', location, true )
|
|
608
|
+
// or should we use orig.location? - TODO: try to find test to see message
|
|
609
|
+
.$inferred = 'expand-element';
|
|
610
|
+
}
|
|
611
|
+
// Set elements expansion status (the if condition is always true, as no
|
|
612
|
+
// elements expansion will take place on artifact with existing other
|
|
613
|
+
// member property):
|
|
614
|
+
if (!art.$expand)
|
|
615
|
+
art.$expand = 'origin'; // if value stays, elements won't appear in CSN
|
|
616
|
+
art.enum[$inferred] = 'expand-element';
|
|
564
617
|
return true;
|
|
565
618
|
}
|
|
566
619
|
|
|
@@ -586,14 +639,45 @@ function resolve( model ) {
|
|
|
586
639
|
return false;
|
|
587
640
|
}
|
|
588
641
|
|
|
589
|
-
|
|
642
|
+
// About Helper property $expand for faster the XSN-to-CSN transformation
|
|
643
|
+
// - null/undefined: artifact, member, items does not contain expanded members
|
|
644
|
+
// - 'origin': all expanded (sub) elements have no new target/on and no new annotations
|
|
645
|
+
// that value is only on elements, types, and params -> no other members
|
|
646
|
+
// when set, only on elem/art with expanded elements
|
|
647
|
+
// - 'target': all expanded (sub) elements might only have new target/on, but
|
|
648
|
+
// no indivual annotations on any (sub) member
|
|
649
|
+
// when set, traverse all parents where the value has been 'origin' before
|
|
650
|
+
// - 'annotate': at least one inferred (sub) member has an individual annotation,
|
|
651
|
+
// not counting propagated ones; set up to the definition (main artifact)
|
|
652
|
+
// (only set with anno on $inferred elem)
|
|
653
|
+
// Usage according to CSN flavor:
|
|
654
|
+
// - gensrc: do not render inferred elements (including expanded elements),
|
|
655
|
+
// collect annotate statements with value 'annotate'
|
|
656
|
+
// - client: do not render expanded sub elements if artifact/member is no type, has a type,
|
|
657
|
+
// has $expand = 'origin', and all its _origin also have $expand = 'origin'
|
|
658
|
+
// (might sometimes render the elements unnecessarily, which is not wrong)
|
|
659
|
+
// - universal: do not render expanded sub elements if $expand = 'origin'
|
|
660
|
+
function setExpandStatus( elem, status ) {
|
|
661
|
+
// set on element
|
|
590
662
|
while (elem._main) {
|
|
591
663
|
elem = elem._parent;
|
|
592
|
-
if (
|
|
664
|
+
if (elem.$expand !== 'origin')
|
|
593
665
|
return;
|
|
594
|
-
elem.$expand =
|
|
666
|
+
elem.$expand = status; // meaning: expanded, containing assocs
|
|
595
667
|
for (let line = elem.items; line; line = line.items)
|
|
596
|
-
line.$expand =
|
|
668
|
+
line.$expand = status; // to-csn just uses the innermost $expand
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
function setExpandStatusAnnotate( elem, status ) {
|
|
672
|
+
for (;;) {
|
|
673
|
+
if (elem.$expand === status)
|
|
674
|
+
return; // already set
|
|
675
|
+
elem.$expand = status; // meaning: expanded, containing annos
|
|
676
|
+
for (let line = elem.items; line; line = line.items)
|
|
677
|
+
line.$expand = status; // to-csn just uses the innermost $expand
|
|
678
|
+
if (!elem._main)
|
|
679
|
+
return;
|
|
680
|
+
elem = elem._parent;
|
|
597
681
|
}
|
|
598
682
|
}
|
|
599
683
|
|
|
@@ -605,7 +689,7 @@ function resolve( model ) {
|
|
|
605
689
|
// PRE: elem has no target, assoc has target prop
|
|
606
690
|
if (elem.kind === '$tableAlias')
|
|
607
691
|
return false;
|
|
608
|
-
setExpandStatus( elem );
|
|
692
|
+
setExpandStatus( elem, 'target' );
|
|
609
693
|
let target = resolvePath( assoc.target, 'target', assoc );
|
|
610
694
|
// console.log( info( null, [ elem.location, elem ], {target,art:assoc,name:''+assoc.target},
|
|
611
695
|
// 'RED').toString())
|
|
@@ -718,6 +802,7 @@ function resolve( model ) {
|
|
|
718
802
|
const nullScope = {
|
|
719
803
|
kind: 'namespace', name: { absolute: autoScopeName, location }, location,
|
|
720
804
|
};
|
|
805
|
+
model.definitions[autoScopeName] = nullScope;
|
|
721
806
|
initArtifact( nullScope );
|
|
722
807
|
return nullScope;
|
|
723
808
|
}
|
|
@@ -926,26 +1011,53 @@ function resolve( model ) {
|
|
|
926
1011
|
return art;
|
|
927
1012
|
}
|
|
928
1013
|
|
|
1014
|
+
// TODO: probably do this already in definer.js
|
|
1015
|
+
function ensureColumnName( col, query ) {
|
|
1016
|
+
if (col.name)
|
|
1017
|
+
return col.name.id;
|
|
1018
|
+
if (col.inline || col.val === '*')
|
|
1019
|
+
return '';
|
|
1020
|
+
const path = col.value &&
|
|
1021
|
+
(col.value.path || !col.value.args && col.value.func && col.value.func.path);
|
|
1022
|
+
if (path) {
|
|
1023
|
+
const last = !path.broken && path.length && path[path.length - 1];
|
|
1024
|
+
if (last) {
|
|
1025
|
+
col.name = { id: last.id, location: last.location, $inferred: 'as' };
|
|
1026
|
+
return col.name.id;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
else if (col.value || col.expand) {
|
|
1030
|
+
error( 'query-req-name', [ col.value && col.value.location || col.location, query ], {},
|
|
1031
|
+
'Alias name is required for this select item' );
|
|
1032
|
+
}
|
|
1033
|
+
// invent a name for code completion in expression
|
|
1034
|
+
col.name = {
|
|
1035
|
+
id: '',
|
|
1036
|
+
location: col.value && col.value.location || col.location,
|
|
1037
|
+
$inferred: 'none',
|
|
1038
|
+
};
|
|
1039
|
+
return '';
|
|
1040
|
+
}
|
|
1041
|
+
|
|
929
1042
|
// TODO: make this function shorter - make part of this (e.g. setting
|
|
930
1043
|
// parent/name) also be part of definer.js
|
|
931
1044
|
// TODO: query is actually the elemParent, where the new elements are added to
|
|
932
|
-
|
|
1045
|
+
// top-level: just query, columns
|
|
1046
|
+
// inline: + elements (TODO: remove), colParent
|
|
1047
|
+
// expand: just query (which is a column/element), columns=array of expand
|
|
1048
|
+
function initFromColumns( query, columns, inlineHead = undefined ) {
|
|
933
1049
|
const elemsParent = query.items || query;
|
|
934
|
-
if (!
|
|
935
|
-
elements = Object.create(null); // explicitly prov
|
|
1050
|
+
if (!inlineHead) {
|
|
936
1051
|
elemsParent.elements = Object.create(null);
|
|
937
1052
|
if (query._main._leadingQuery === query) // never the case for 'expand'
|
|
938
1053
|
query._main.elements = elemsParent.elements;
|
|
939
1054
|
}
|
|
940
|
-
let wildcard = false;
|
|
941
|
-
let inline = 0;
|
|
942
1055
|
|
|
943
1056
|
for (const col of columns || [ { val: '*' } ]) {
|
|
944
1057
|
if (col.val === '*') {
|
|
945
|
-
|
|
946
|
-
|
|
1058
|
+
const siblings = wildcardSiblings( columns, query );
|
|
1059
|
+
expandWildcard( col, siblings, inlineHead, query );
|
|
947
1060
|
}
|
|
948
|
-
col.kind = 'element';
|
|
949
1061
|
if ((col.expand || col.inline) && !isBetaEnabled( options, 'nestedProjections' )) {
|
|
950
1062
|
error( null, [ col.location, query ], { prop: (col.expand ? 'expand' : 'inline') },
|
|
951
1063
|
'Unsupported nested $(PROP)' );
|
|
@@ -957,137 +1069,135 @@ function resolve( model ) {
|
|
|
957
1069
|
col.name = {};
|
|
958
1070
|
// a name for this internal symtab entry (e.g. '.2' to avoid clashes
|
|
959
1071
|
// with real elements) is only relevant for for `cdsc -R`/debugging
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1072
|
+
const q = userQuery( query );
|
|
1073
|
+
q.$inlines.push( col );
|
|
1074
|
+
// or use userQuery( query ) in the following, too?
|
|
1075
|
+
setMemberParent( col, `.${ q.$inlines.length }`, query );
|
|
1076
|
+
initFromColumns( query, col.inline, col );
|
|
964
1077
|
continue;
|
|
965
1078
|
}
|
|
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;
|
|
1079
|
+
else if (!col.$replacement) {
|
|
1080
|
+
const id = ensureColumnName( col, query );
|
|
1081
|
+
col.kind = 'element';
|
|
1082
|
+
dictAdd( elemsParent.elements, id, col, ( name, location ) => {
|
|
1083
|
+
error( 'duplicate-definition', [ location, query ], { name, '#': 'element' } );
|
|
1084
|
+
});
|
|
1085
|
+
setMemberParent( col, id, query );
|
|
996
1086
|
}
|
|
997
1087
|
}
|
|
998
|
-
if (wildcard)
|
|
999
|
-
expandWildcard( elements, wildcard );
|
|
1000
1088
|
forEachGeneric( query, 'elements', e => initElem( e, query ) );
|
|
1001
1089
|
return true;
|
|
1090
|
+
}
|
|
1002
1091
|
|
|
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
|
-
}
|
|
1092
|
+
// col ($replacement set before *)
|
|
1093
|
+
// false if two cols have same name
|
|
1094
|
+
function wildcardSiblings( columns, query ) {
|
|
1095
|
+
const siblings = Object.create(null);
|
|
1096
|
+
if (!columns)
|
|
1097
|
+
return siblings;
|
|
1098
|
+
|
|
1099
|
+
let seenWildcard = null;
|
|
1100
|
+
for (const col of columns) {
|
|
1101
|
+
const id = ensureColumnName( col, query );
|
|
1102
|
+
if (id) {
|
|
1103
|
+
col.$replacement = !seenWildcard;
|
|
1104
|
+
siblings[id] = !(id in siblings) && col;
|
|
1105
|
+
}
|
|
1106
|
+
else if (col.val === '*') {
|
|
1107
|
+
seenWildcard = true;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
return siblings;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// TODO: make struct.* are to be added at place, not sub-wildcards first,
|
|
1114
|
+
// see test3/Queries/ExpandInlineCreate/Excluding.cds
|
|
1115
|
+
// TODO: disallow $self.elem.* and $self.*, toSelf.* (circular dependency)
|
|
1116
|
+
function expandWildcard( wildcard, siblingElements, colParent, query ) {
|
|
1117
|
+
const { elements } = query.items || query;
|
|
1118
|
+
let location = wildcard.location || query.from && query.from.location || query.location;
|
|
1119
|
+
const inferred = query._main.$inferred;
|
|
1120
|
+
const excludingDict = (colParent || query).excludingDict || Object.create(null);
|
|
1121
|
+
|
|
1122
|
+
const envParent = wildcard._pathHead; // TODO: rename _pathHead to _pathEnv
|
|
1123
|
+
// console.log('S1:',location.line,location.col,
|
|
1124
|
+
// envParent&&!!envParent._origin&&envParent._origin.name)
|
|
1125
|
+
const env = columnEnv( envParent, query );
|
|
1126
|
+
// console.log('S2:',location.line,location.col,
|
|
1127
|
+
// envParent&&!!envParent._origin&&envParent._origin.name,
|
|
1128
|
+
// Object.keys(env),Object.keys(elements))
|
|
1129
|
+
for (const name in env) {
|
|
1130
|
+
const navElem = env[name];
|
|
1131
|
+
// TODO: if it is an array, filter out those with masked
|
|
1132
|
+
if (excludingDict[name] || navElem.masked && navElem.masked.val)
|
|
1133
|
+
continue;
|
|
1134
|
+
const sibling = siblingElements[name];
|
|
1135
|
+
if (sibling) { // is explicitly provided (without duplicate)
|
|
1136
|
+
if (!inferred && !envParent) // not yet for expand/inline
|
|
1137
|
+
reportReplacement( sibling, navElem, query );
|
|
1138
|
+
if (!sibling.$replacement) {
|
|
1139
|
+
sibling.$replacement = true;
|
|
1140
|
+
sibling.kind = 'element';
|
|
1141
|
+
dictAdd( elements, name, sibling, ( _name, loc ) => {
|
|
1142
|
+
// there can be a definition from a previous inline with the same name:
|
|
1143
|
+
error( 'duplicate-definition', [ loc, query ], { name, '#': 'element' } );
|
|
1144
|
+
});
|
|
1145
|
+
setMemberParent( sibling, name, query );
|
|
1068
1146
|
}
|
|
1069
|
-
else {
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1147
|
+
// else {
|
|
1148
|
+
// sibling.$inferred = 'query';
|
|
1149
|
+
// }
|
|
1150
|
+
}
|
|
1151
|
+
else if (Array.isArray(navElem)) {
|
|
1152
|
+
const names = navElem.filter( e => !e.$duplicates)
|
|
1153
|
+
.map( e => `${ e.name.alias }.${ e.name.element }` );
|
|
1154
|
+
if (names.length) {
|
|
1155
|
+
error( 'wildcard-ambiguous', [ location, query ], { id: name, names },
|
|
1156
|
+
'Ambiguous wildcard, select $(ID) explicitly with $(NAMES)' );
|
|
1079
1157
|
}
|
|
1080
1158
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1159
|
+
else {
|
|
1160
|
+
location = weakLocation( location );
|
|
1161
|
+
const origin = envParent ? navElem : navElem._origin;
|
|
1162
|
+
const elem = linkToOrigin( origin, name, query, null, location );
|
|
1163
|
+
// TODO: check assocToMany { * }
|
|
1164
|
+
dictAdd( elements, name, elem, ( _name, loc ) => {
|
|
1165
|
+
// there can be a definition from a previous inline with the same name:
|
|
1166
|
+
error( 'duplicate-definition', [ loc, query ], { name, '#': 'element' } );
|
|
1167
|
+
});
|
|
1168
|
+
elem.$inferred = '*';
|
|
1169
|
+
elem.name.$inferred = '*';
|
|
1170
|
+
if (envParent)
|
|
1171
|
+
setWildcardExpandInline( elem, envParent, origin, name, location );
|
|
1172
|
+
else
|
|
1173
|
+
setElementOrigin( elem, navElem, name, location );
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
if (envParent || query.kind !== 'select') {
|
|
1177
|
+
// already done in populateQuery (TODO: change that and check whether
|
|
1178
|
+
// `*` is allowed at all in definer)
|
|
1179
|
+
const user = colParent || query;
|
|
1180
|
+
for (const name in user.excludingDict)
|
|
1181
|
+
resolveExcluding( name, env, excludingDict, query );
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
function reportReplacement( sibling, navElem, query ) {
|
|
1186
|
+
// TODO: bring this much less often = only if shadowed elem does not appear
|
|
1187
|
+
// in expr and if not projected as other name.
|
|
1188
|
+
// Probably needs to be reported at a later phase
|
|
1189
|
+
const path = sibling.value && sibling.value.path;
|
|
1190
|
+
if (!sibling.target || sibling.target.$inferred || // not explicit REDIRECTED TO
|
|
1191
|
+
path && path[path.length - 1].id !== sibling.name.id) { // or renamed
|
|
1192
|
+
const { id } = sibling.name;
|
|
1193
|
+
if (Array.isArray(navElem)) {
|
|
1194
|
+
info( 'wildcard-excluding-many', [ sibling.name.location, query ], { id },
|
|
1195
|
+
'This select item replaces $(ID) from two or more sources' );
|
|
1196
|
+
}
|
|
1197
|
+
else {
|
|
1198
|
+
info( 'wildcard-excluding-one', [ sibling.name.location, query ],
|
|
1199
|
+
{ id, alias: navElem._parent.name.id },
|
|
1200
|
+
'This select item replaces $(ID) from table alias $(ALIAS)' );
|
|
1091
1201
|
}
|
|
1092
1202
|
}
|
|
1093
1203
|
}
|
|
@@ -1378,7 +1488,9 @@ function resolve( model ) {
|
|
|
1378
1488
|
}
|
|
1379
1489
|
// Resolve projections/views
|
|
1380
1490
|
// if (art.query)console.log( info( null, [art.query.location,art.query], 'VQ:' ).toString() );
|
|
1381
|
-
|
|
1491
|
+
|
|
1492
|
+
if (art.$queries)
|
|
1493
|
+
art.$queries.forEach( resolveQuery );
|
|
1382
1494
|
|
|
1383
1495
|
if (obj.type || obj._origin || obj.value && obj.value.path || obj.elements) // typed artifacts
|
|
1384
1496
|
effectiveType(obj); // set _effectiveType if appropriate, (future?): copy elems if extended
|
|
@@ -1523,14 +1635,28 @@ function resolve( model ) {
|
|
|
1523
1635
|
}
|
|
1524
1636
|
}
|
|
1525
1637
|
if (art && art._annotate) {
|
|
1526
|
-
if (art
|
|
1527
|
-
art
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1638
|
+
if (art.kind === 'action' || art.kind === 'function') {
|
|
1639
|
+
expandParameters( art );
|
|
1640
|
+
if (art.returns)
|
|
1641
|
+
effectiveType( art.returns );
|
|
1642
|
+
}
|
|
1643
|
+
const aor = art.returns || art;
|
|
1644
|
+
const obj = aor.items || aor.targetAspect || aor;
|
|
1645
|
+
// Currently(?), effectiveType() does not calculate the effective type of
|
|
1646
|
+
// its line item:
|
|
1647
|
+
effectiveType( obj );
|
|
1648
|
+
if (art._annotate.elements)
|
|
1649
|
+
setExpandStatusAnnotate( aor, 'annotate' );
|
|
1531
1650
|
annotate( obj, 'element', 'elements', 'enum', art );
|
|
1532
1651
|
annotate( art, 'action', 'actions' );
|
|
1533
1652
|
annotate( art, 'param', 'params' );
|
|
1653
|
+
// const { returns } = art._annotate;
|
|
1654
|
+
// if (returns) {
|
|
1655
|
+
// const dict = returns.elements;
|
|
1656
|
+
// const env = obj.returns && obj.returns.elements || null;
|
|
1657
|
+
// for (const n in dict)
|
|
1658
|
+
// annotateMembers( env && env[n], dict[n], 'elements', n, parent, 'element' );
|
|
1659
|
+
// }
|
|
1534
1660
|
}
|
|
1535
1661
|
return;
|
|
1536
1662
|
|
|
@@ -1548,6 +1674,44 @@ function resolve( model ) {
|
|
|
1548
1674
|
annotateMembers( env && env[n], dict[n], prop, n, parent, kind );
|
|
1549
1675
|
}
|
|
1550
1676
|
}
|
|
1677
|
+
function expandParameters( action ) {
|
|
1678
|
+
// see also expandElements()
|
|
1679
|
+
if (!enableExpandElements || !effectiveType( action ))
|
|
1680
|
+
return;
|
|
1681
|
+
const chain = [];
|
|
1682
|
+
// Should we be able to consider params and returns separately?
|
|
1683
|
+
// Probably not, let to-csn omit unchanged params/returns.
|
|
1684
|
+
while (action._origin && !action.params) {
|
|
1685
|
+
chain.push( action );
|
|
1686
|
+
action = action._origin;
|
|
1687
|
+
}
|
|
1688
|
+
chain.reverse();
|
|
1689
|
+
for (const art of chain) {
|
|
1690
|
+
const origin = art._origin;
|
|
1691
|
+
if (!art.params && origin.params) {
|
|
1692
|
+
for (const name in origin.params) {
|
|
1693
|
+
// TODO: we could check _annotate here to decide whether we really
|
|
1694
|
+
// not to create proxies
|
|
1695
|
+
const orig = origin.params[name];
|
|
1696
|
+
linkToOrigin( orig, name, art, 'params', weakLocation( orig.location ), true )
|
|
1697
|
+
.$inferred = 'expand-param';
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
if (!art.returns && origin.returns) {
|
|
1701
|
+
// TODO: make linkToOrigin() work for returns, kind/name?
|
|
1702
|
+
const location = weakLocation( origin.returns.location );
|
|
1703
|
+
art.returns = {
|
|
1704
|
+
name: Object.assign( {}, art.name, { id: '', param: '', location } ),
|
|
1705
|
+
kind: 'param',
|
|
1706
|
+
location,
|
|
1707
|
+
$inferred: 'expand-param',
|
|
1708
|
+
};
|
|
1709
|
+
setProp( art.returns, '_parent', art );
|
|
1710
|
+
setProp( art.returns, '_main', art._main || art );
|
|
1711
|
+
setProp( art.returns, '_origin', origin.returns );
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1551
1715
|
|
|
1552
1716
|
function extensionFor( art ) {
|
|
1553
1717
|
if (art.kind === 'annotate')
|
|
@@ -1586,6 +1750,8 @@ function resolve( model ) {
|
|
|
1586
1750
|
ext.kind = 'annotate'; // after setMemberParent()!
|
|
1587
1751
|
setProp( art, '_extension', ext );
|
|
1588
1752
|
setProp( ext.name, '_artifact', art );
|
|
1753
|
+
if (art.returns)
|
|
1754
|
+
ext.$syntax = 'returns';
|
|
1589
1755
|
return ext;
|
|
1590
1756
|
}
|
|
1591
1757
|
|
|
@@ -1752,7 +1918,7 @@ function resolve( model ) {
|
|
|
1752
1918
|
function resolveQuery( query ) {
|
|
1753
1919
|
if (!query._main) // parse error
|
|
1754
1920
|
return;
|
|
1755
|
-
|
|
1921
|
+
traverseQueryPost( query, null, populateQuery );
|
|
1756
1922
|
forEachGeneric( query, '$tableAliases', ( alias ) => {
|
|
1757
1923
|
// console.log( info( null, [alias.location,alias], 'SQA:' ).toString() );
|
|
1758
1924
|
if (alias.kind === 'mixin')
|
|
@@ -1761,8 +1927,11 @@ function resolve( model ) {
|
|
|
1761
1927
|
// pure path has been resolved, resolve args and filter now:
|
|
1762
1928
|
resolveExpr( alias, 'from', query._parent );
|
|
1763
1929
|
} );
|
|
1930
|
+
for (const col of query.$inlines)
|
|
1931
|
+
resolveExpr( col.value, 'expr', col, undefined, true );
|
|
1932
|
+
// for (const col of query.$inlines)
|
|
1933
|
+
// if (!col.value.path) throw Error(col.name.element)
|
|
1764
1934
|
if (query !== query._main._leadingQuery) // will be done later
|
|
1765
|
-
// TODO: rethink elements(view) === elements(view._leadingQuery)
|
|
1766
1935
|
forEachGeneric( query, 'elements', resolveRefs );
|
|
1767
1936
|
if (query.from)
|
|
1768
1937
|
resolveJoinOn( query.from );
|
|
@@ -1786,7 +1955,7 @@ function resolve( model ) {
|
|
|
1786
1955
|
return;
|
|
1787
1956
|
|
|
1788
1957
|
function resolveJoinOn( join ) {
|
|
1789
|
-
if (join.args) {
|
|
1958
|
+
if (join && join.args) { // JOIN
|
|
1790
1959
|
for (const j of join.args)
|
|
1791
1960
|
resolveJoinOn( j );
|
|
1792
1961
|
if (join.on)
|
|
@@ -1823,7 +1992,10 @@ function resolve( model ) {
|
|
|
1823
1992
|
}
|
|
1824
1993
|
const target = resolvePath( obj.target, 'target', art );
|
|
1825
1994
|
if (obj.on) {
|
|
1826
|
-
if (!art._main || !art._parent.elements && !art._parent.items) {
|
|
1995
|
+
if (!art._main || !art._parent.elements && !art._parent.items && !art._parent.targetAspect) {
|
|
1996
|
+
// TODO: test of .items a bit unclear - we should somehow restrict the
|
|
1997
|
+
// use of unmanaged assocs in MANY, at least with $self
|
|
1998
|
+
// TODO: $self usage in anonymous aspects to be corrected in Core Compiler
|
|
1827
1999
|
const isComposition = obj.type && obj.type.path && obj.type.path[0] &&
|
|
1828
2000
|
obj.type.path[0].id === 'cds.Composition';
|
|
1829
2001
|
message( 'assoc-as-type', [ obj.on.location, art ],
|
|
@@ -2264,6 +2436,9 @@ function resolve( model ) {
|
|
|
2264
2436
|
|
|
2265
2437
|
// TODO: there is no need to rewrite the on condition of non-leading queries,
|
|
2266
2438
|
// i.e. we could just have on = {…}
|
|
2439
|
+
// TODO: re-check $self rewrite (with managed composition of aspects),
|
|
2440
|
+
// and actually also $self inside anonymous aspect definitions
|
|
2441
|
+
// (not entirely urgent as we do not analyse it further, at least sole "$self")
|
|
2267
2442
|
function rewriteCondition( elem, assoc ) {
|
|
2268
2443
|
if (enableExpandElements && elem._parent && elem._parent.kind === 'element') {
|
|
2269
2444
|
// managed association as sub element not supported yet
|
|
@@ -2468,6 +2643,8 @@ function resolve( model ) {
|
|
|
2468
2643
|
}
|
|
2469
2644
|
|
|
2470
2645
|
function resolveExpr( expr, expected, user, extDict, expandOrInline) {
|
|
2646
|
+
// TODO: when we have rewritten the resolvePath functions,
|
|
2647
|
+
// define a traverseExpr() in ./utils.js
|
|
2471
2648
|
// TODO: extra "expected" 'expand'/'inline' instead o param `expandOrInline`
|
|
2472
2649
|
if (!expr || typeof expr === 'string') // parse error or keywords in {xpr:...}
|
|
2473
2650
|
return;
|
|
@@ -2504,7 +2681,7 @@ function resolve( model ) {
|
|
|
2504
2681
|
else if (expr.query) {
|
|
2505
2682
|
const { query } = expr;
|
|
2506
2683
|
if (query.kind || query._leadingQuery) { // UNION has _leadingQuery
|
|
2507
|
-
traverseQueryPost( query, false, resolveQuery );
|
|
2684
|
+
// traverseQueryPost( query, false, resolveQuery );
|
|
2508
2685
|
}
|
|
2509
2686
|
else {
|
|
2510
2687
|
error( 'expr-no-subquery', [ expr.location, user ], {},
|
|
@@ -2515,12 +2692,19 @@ function resolve( model ) {
|
|
|
2515
2692
|
const args = Array.isArray(expr.args) ? expr.args : Object.values( expr.args );
|
|
2516
2693
|
args.forEach( e => e && resolveExpr( e, e.$expected || expected, user, extDict ) );
|
|
2517
2694
|
}
|
|
2695
|
+
if (expr.suffix && isDeprecatedEnabled( options )) {
|
|
2696
|
+
const { location } = expr.suffix[0] || expr;
|
|
2697
|
+
error( null, [ location, user ], { prop: 'deprecated' },
|
|
2698
|
+
'Window functions are not supported if $(PROP) options are set' );
|
|
2699
|
+
}
|
|
2700
|
+
if (expr.suffix)
|
|
2701
|
+
expr.suffix.forEach( s => s && resolveExpr( s, expected, user, extDict ) );
|
|
2518
2702
|
}
|
|
2519
2703
|
|
|
2520
2704
|
function resolveParamsAndWhere( step, expected, user, extDict, isLast ) {
|
|
2521
2705
|
const alias = step._navigation && step._navigation.kind === '$tableAlias' && step._navigation;
|
|
2522
2706
|
const type = alias || effectiveType( step._artifact );
|
|
2523
|
-
const art = type && type.target
|
|
2707
|
+
const art = (type && type.target) ? type.target._artifact : type;
|
|
2524
2708
|
if (!art)
|
|
2525
2709
|
return;
|
|
2526
2710
|
const entity = (art.kind === 'entity') &&
|
|
@@ -2531,7 +2715,7 @@ function resolve( model ) {
|
|
|
2531
2715
|
if (step.where)
|
|
2532
2716
|
resolveExpr( step.where, 'filter', user, environment( type ) );
|
|
2533
2717
|
}
|
|
2534
|
-
else if (step.where || step.cardinality ) {
|
|
2718
|
+
else if (step.where && step.where.location || step.cardinality ) {
|
|
2535
2719
|
const location = combinedLocation( step.where, step.cardinality );
|
|
2536
2720
|
// XSN TODO: filter$location including […]
|
|
2537
2721
|
message( 'expr-no-filter', [ location, user ], { '#': expected },
|
|
@@ -2699,8 +2883,6 @@ function navProjection( navigation, preferred ) {
|
|
|
2699
2883
|
// Query tree post-order traversal - called for everything which makes a query
|
|
2700
2884
|
// except "real ones": operands of UNION etc, JOIN with ON, and sub queries in FROM
|
|
2701
2885
|
function traverseQueryPost( query, simpleOnly, callback ) {
|
|
2702
|
-
while (Array.isArray(query)) // query in parentheses, TODO: remove
|
|
2703
|
-
query = query[0];
|
|
2704
2886
|
if (!query) // parser error
|
|
2705
2887
|
return;
|
|
2706
2888
|
if (!query.op) { // in FROM (not JOIN)
|
|
@@ -2720,11 +2902,19 @@ function traverseQueryPost( query, simpleOnly, callback ) {
|
|
|
2720
2902
|
// console.log('FE:')
|
|
2721
2903
|
}
|
|
2722
2904
|
else if (query.args) { // JOIN, UNION, INTERSECT
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2905
|
+
if (!query.join && simpleOnly == null) {
|
|
2906
|
+
// enough for elements: traverse only first args for UNION/INTERSECT
|
|
2907
|
+
// TODO: we might use this also when we do not rewrite associations
|
|
2908
|
+
// in non-referred sub queries
|
|
2909
|
+
traverseQueryPost( query.args[0], simpleOnly, callback );
|
|
2910
|
+
}
|
|
2911
|
+
else {
|
|
2912
|
+
for (const q of query.args)
|
|
2913
|
+
traverseQueryPost( q, simpleOnly, callback );
|
|
2914
|
+
// The ON condition has to be traversed extra, because it must be evaluated
|
|
2915
|
+
// after the complete FROM has been traversed. It is also not necessary to
|
|
2916
|
+
// evaluate it in populateQuery().
|
|
2917
|
+
}
|
|
2728
2918
|
}
|
|
2729
2919
|
// else: with parse error (`select from <EOF>`, `select distinct from;`)
|
|
2730
2920
|
}
|