@sap/cds-compiler 2.10.2 → 2.11.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 +90 -5
- package/bin/.eslintrc.json +1 -2
- package/bin/cds_update_identifiers.js +3 -1
- package/bin/cdsc.js +49 -25
- package/bin/cdsse.js +1 -0
- package/bin/cdsv2m.js +3 -2
- package/doc/CHANGELOG_BETA.md +10 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +8 -36
- package/lib/api/options.js +15 -6
- package/lib/api/validate.js +30 -3
- package/lib/backends.js +12 -13
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +3 -2
- package/lib/base/message-registry.js +34 -10
- package/lib/base/messages.js +38 -18
- package/lib/base/model.js +5 -4
- package/lib/base/optionProcessorHelper.js +57 -23
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/unknownMagic.js +6 -3
- package/lib/compiler/assert-consistency.js +9 -2
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +62 -16
- package/lib/compiler/checks.js +2 -1
- package/lib/compiler/definer.js +66 -108
- package/lib/compiler/index.js +29 -29
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +225 -58
- package/lib/compiler/shared.js +53 -229
- package/lib/compiler/utils.js +184 -0
- package/lib/edm/annotations/genericTranslation.js +1 -1
- package/lib/edm/csn2edm.js +3 -2
- package/lib/edm/edmPreprocessor.js +34 -38
- package/lib/edm/edmUtils.js +3 -3
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +17 -1
- package/lib/gen/language.tokens +79 -73
- package/lib/gen/languageLexer.interp +19 -1
- package/lib/gen/languageLexer.js +779 -731
- package/lib/gen/languageLexer.tokens +71 -65
- package/lib/gen/languageParser.js +4668 -4072
- package/lib/json/from-csn.js +10 -10
- package/lib/json/to-csn.js +228 -47
- package/lib/language/antlrParser.js +11 -0
- package/lib/language/errorStrategy.js +26 -8
- package/lib/language/genericAntlrParser.js +73 -14
- package/lib/language/language.g4 +79 -3
- package/lib/main.d.ts +215 -18
- package/lib/main.js +3 -1
- package/lib/model/api.js +2 -2
- package/lib/model/csnRefs.js +117 -33
- package/lib/model/csnUtils.js +65 -133
- package/lib/model/enrichCsn.js +62 -37
- package/lib/model/revealInternalProperties.js +25 -8
- package/lib/model/sortViews.js +8 -1
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -18
- package/lib/render/.eslintrc.json +1 -2
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/toCdl.js +15 -8
- package/lib/render/toHdbcds.js +26 -49
- package/lib/render/toSql.js +61 -39
- package/lib/render/utils/common.js +1 -1
- package/lib/transform/db/applyTransformations.js +189 -0
- package/lib/transform/db/constraints.js +273 -119
- package/lib/transform/db/draft.js +3 -2
- package/lib/transform/db/expansion.js +6 -4
- package/lib/transform/db/flattening.js +19 -3
- package/lib/transform/db/transformExists.js +102 -9
- package/lib/transform/db/views.js +485 -0
- package/lib/transform/forHanaNew.js +93 -448
- package/lib/transform/forOdataNew.js +9 -2
- package/lib/transform/localized.js +2 -0
- package/lib/transform/odata/structuralPath.js +1 -5
- package/lib/transform/transformUtilsNew.js +22 -8
- package/lib/transform/translateAssocsToJoins.js +7 -15
- package/lib/utils/file.js +11 -5
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
- package/lib/transform/db/helpers.js +0 -58
package/lib/compiler/resolver.js
CHANGED
|
@@ -47,25 +47,29 @@ const {
|
|
|
47
47
|
const { dictLocation } = require('../base/location');
|
|
48
48
|
const { searchName, weakLocation } = require('../base/messages');
|
|
49
49
|
const { combinedLocation } = require('../base/location');
|
|
50
|
-
|
|
50
|
+
|
|
51
|
+
const { kindProperties } = require('./base');
|
|
51
52
|
const {
|
|
52
|
-
|
|
53
|
+
pushLink,
|
|
54
|
+
setLink,
|
|
55
|
+
annotationVal,
|
|
53
56
|
augmentPath,
|
|
54
57
|
splitIntoPath,
|
|
55
|
-
|
|
58
|
+
linkToOrigin,
|
|
59
|
+
setMemberParent,
|
|
60
|
+
withAssociation,
|
|
61
|
+
storeExtension,
|
|
62
|
+
dependsOn,
|
|
63
|
+
dependsOnSilent,
|
|
64
|
+
} = require('./utils');
|
|
56
65
|
|
|
57
66
|
const detectCycles = require('./cycle-detector');
|
|
58
67
|
const layers = require('./moduleLayers');
|
|
59
68
|
|
|
60
|
-
const {
|
|
61
|
-
kindProperties, fns, setLink, linkToOrigin, setMemberParent, withAssociation, storeExtension,
|
|
62
|
-
dependsOn, dependsOnSilent,
|
|
63
|
-
} = require('./shared');
|
|
64
|
-
|
|
65
69
|
const annotationPriorities = {
|
|
66
70
|
define: 1, extend: 2, annotate: 2, edmx: 3,
|
|
67
71
|
};
|
|
68
|
-
|
|
72
|
+
const $inferred = Symbol.for('cds.$inferred');
|
|
69
73
|
|
|
70
74
|
// Export function of this file. Resolve type references in augmented CSN
|
|
71
75
|
// `model`. If the model has a property argument `messages`, do not throw
|
|
@@ -73,21 +77,21 @@ const annotationPriorities = {
|
|
|
73
77
|
// that property (should be a vector).
|
|
74
78
|
function resolve( model ) {
|
|
75
79
|
const { options } = model;
|
|
76
|
-
// Get shared
|
|
80
|
+
// Get shared functionality and the message function:
|
|
81
|
+
const {
|
|
82
|
+
info, warning, error, message,
|
|
83
|
+
} = model.$messageFunctions;
|
|
77
84
|
const {
|
|
78
85
|
resolvePath,
|
|
79
86
|
resolveTypeArguments,
|
|
80
87
|
defineAnnotations,
|
|
81
88
|
attachAndEmitValidNames,
|
|
82
|
-
} = fns( model, environment );
|
|
83
|
-
const {
|
|
84
|
-
info, warning, error, message,
|
|
85
|
-
} = model.$messageFunctions;
|
|
86
|
-
const {
|
|
87
89
|
initArtifact,
|
|
88
90
|
lateExtensions,
|
|
89
91
|
projectionAncestor,
|
|
90
|
-
} =
|
|
92
|
+
} = model.$functions;
|
|
93
|
+
model.$volatileFunctions.environment = environment;
|
|
94
|
+
|
|
91
95
|
/** @type {any} may also be a boolean */
|
|
92
96
|
let newAutoExposed = [];
|
|
93
97
|
|
|
@@ -242,6 +246,7 @@ function resolve( model ) {
|
|
|
242
246
|
const chain = [];
|
|
243
247
|
while (art && !('_effectiveType' in art) &&
|
|
244
248
|
(art.type || art._origin || art.value && art.value.path) &&
|
|
249
|
+
// TODO: really stop at art.enum?
|
|
245
250
|
!art.target && !art.enum && !art.elements && !art.items) {
|
|
246
251
|
chain.push( art );
|
|
247
252
|
setProp( art, '_effectiveType', 0 ); // initial setting in case of cycles
|
|
@@ -273,13 +278,17 @@ function resolve( model ) {
|
|
|
273
278
|
// collect the "latest" cardinality (calculate lazyly if necessary)
|
|
274
279
|
let cardinality = art.cardinality ||
|
|
275
280
|
art._effectiveType && (() => getCardinality( art._effectiveType ));
|
|
281
|
+
let prev = art;
|
|
276
282
|
for (const a of chain) {
|
|
277
283
|
if (a.cardinality)
|
|
278
284
|
cardinality = a.cardinality;
|
|
279
285
|
if (a.expand && expandFromColumns( a, art, cardinality ) ||
|
|
280
286
|
art.target && redirectImplicitly( a, art ) ||
|
|
281
|
-
art.elements && expandElements( a, art, eType )
|
|
287
|
+
art.elements && expandElements( a, art, eType ) ||
|
|
288
|
+
art.items && expandItems( a, art, eType ))
|
|
282
289
|
art = a;
|
|
290
|
+
else if (art.enum && expandEnum( a, prev ))
|
|
291
|
+
prev = a; // do not set art - effective type is base
|
|
283
292
|
setProp( a, '_effectiveType', art );
|
|
284
293
|
}
|
|
285
294
|
}
|
|
@@ -351,13 +360,13 @@ function resolve( model ) {
|
|
|
351
360
|
while (struct.kind === 'element')
|
|
352
361
|
struct = struct._parent;
|
|
353
362
|
if (struct.kind === 'select') {
|
|
354
|
-
message( '
|
|
363
|
+
message( 'type-unexpected-typeof', [ ref.location, user ],
|
|
355
364
|
{ keyword: 'type of', '#': struct.kind } );
|
|
356
365
|
// we actually refer to an element in _combined; TODO: return null if
|
|
357
366
|
// not configurable; would produce illegal CSN with sub queries in FROM
|
|
358
367
|
}
|
|
359
368
|
else if (struct !== user._main) {
|
|
360
|
-
message( '
|
|
369
|
+
message( 'type-unexpected-typeof', [ ref.location, user ],
|
|
361
370
|
{ keyword: 'type of', '#': struct.kind } );
|
|
362
371
|
return setProp( ref, '_artifact', null );
|
|
363
372
|
}
|
|
@@ -405,7 +414,8 @@ function resolve( model ) {
|
|
|
405
414
|
for (const view of resolveChain.reverse()) {
|
|
406
415
|
if (view._status !== '_query' ) { // not already resolved
|
|
407
416
|
setProp( view, '_status', '_query' );
|
|
408
|
-
|
|
417
|
+
// must be run in order “sub query in FROM first”:
|
|
418
|
+
traverseQueryPost( view.query, null, populateQuery );
|
|
409
419
|
if (view.elements$) // specified elements
|
|
410
420
|
mergeSpecifiedElements( view );
|
|
411
421
|
if (!view.$entity) {
|
|
@@ -494,7 +504,8 @@ function resolve( model ) {
|
|
|
494
504
|
});
|
|
495
505
|
}
|
|
496
506
|
forEachGeneric( { elements: alias.elements }, 'elements', ( elem, name ) => {
|
|
497
|
-
|
|
507
|
+
if (elem.$duplicates !== true)
|
|
508
|
+
dictAddArray( query._combined, name, elem, null ); // not dictAdd()
|
|
498
509
|
});
|
|
499
510
|
}
|
|
500
511
|
}
|
|
@@ -535,13 +546,31 @@ function resolve( model ) {
|
|
|
535
546
|
setMemberParent( key, name, elem ); // TODO: set _block here if not present?
|
|
536
547
|
}
|
|
537
548
|
|
|
549
|
+
function expandItems( art, origin, eType ) {
|
|
550
|
+
if (!enableExpandElements || art.items)
|
|
551
|
+
return false;
|
|
552
|
+
if (isInParents( art, eType )) {
|
|
553
|
+
art.items = 0; // circular
|
|
554
|
+
return true;
|
|
555
|
+
}
|
|
556
|
+
const ref = art.type || art.value || art.name;
|
|
557
|
+
const location = ref && ref.location || art.location;
|
|
558
|
+
art.items = { $inferred: 'expand-element', location };
|
|
559
|
+
setProp( art.items, '_outer', art );
|
|
560
|
+
setProp( art.items, '_origin', origin.items );
|
|
561
|
+
if (!art.$expand)
|
|
562
|
+
art.$expand = 'origin'; // if value stays, elements won't appear in CSN
|
|
563
|
+
return true;
|
|
564
|
+
}
|
|
565
|
+
|
|
538
566
|
function expandElements( art, struct, eType ) {
|
|
539
567
|
if (!enableExpandElements)
|
|
540
568
|
return false;
|
|
541
569
|
if (art.elements || art.kind === '$tableAlias' ||
|
|
542
570
|
// no element expansions for "non-proper" types like
|
|
543
571
|
// entities (as parameter types) etc:
|
|
544
|
-
struct.kind !== 'type' && struct.kind !== 'element' &&
|
|
572
|
+
struct.kind !== 'type' && struct.kind !== 'element' && struct.kind !== 'param' &&
|
|
573
|
+
!struct._outer)
|
|
545
574
|
return false;
|
|
546
575
|
if (struct.elements === 0 || isInParents( art, eType )) {
|
|
547
576
|
art.elements = 0; // circular
|
|
@@ -565,7 +594,28 @@ function resolve( model ) {
|
|
|
565
594
|
// member property):
|
|
566
595
|
if (!art.$expand)
|
|
567
596
|
art.$expand = 'origin'; // if value stays, elements won't appear in CSN
|
|
568
|
-
// TODO: have some art.elements[SYM.$inferred] = 'expand-
|
|
597
|
+
// TODO: have some art.elements[SYM.$inferred] = 'expand-element';
|
|
598
|
+
return true;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function expandEnum( art, origin ) {
|
|
602
|
+
if (!enableExpandElements || art.enum)
|
|
603
|
+
return false;
|
|
604
|
+
const ref = art.type || art.value || art.name;
|
|
605
|
+
const location = weakLocation( ref && ref.location || art.location );
|
|
606
|
+
art.enum = Object.create(null);
|
|
607
|
+
for (const name in origin.enum) {
|
|
608
|
+
const orig = origin.enum[name];
|
|
609
|
+
linkToOrigin( orig, name, art, 'enum', location, true )
|
|
610
|
+
// or should we use orig.location? - TODO: try to find test to see message
|
|
611
|
+
.$inferred = 'expand-element';
|
|
612
|
+
}
|
|
613
|
+
// Set elements expansion status (the if condition is always true, as no
|
|
614
|
+
// elements expansion will take place on artifact with existing other
|
|
615
|
+
// member property):
|
|
616
|
+
if (!art.$expand)
|
|
617
|
+
art.$expand = 'origin'; // if value stays, elements won't appear in CSN
|
|
618
|
+
art.enum[$inferred] = 'expand-element';
|
|
569
619
|
return true;
|
|
570
620
|
}
|
|
571
621
|
|
|
@@ -603,7 +653,7 @@ function resolve( model ) {
|
|
|
603
653
|
// not counting propagated ones; set up to the definition (main artifact)
|
|
604
654
|
// (only set with anno on $inferred elem)
|
|
605
655
|
// Usage according to CSN flavor:
|
|
606
|
-
// - gensrc: do not render
|
|
656
|
+
// - gensrc: do not render inferred elements (including expanded elements),
|
|
607
657
|
// collect annotate statements with value 'annotate'
|
|
608
658
|
// - client: do not render expanded sub elements if artifact/member is no type, has a type,
|
|
609
659
|
// has $expand = 'origin', and all its _origin also have $expand = 'origin'
|
|
@@ -671,35 +721,69 @@ function resolve( model ) {
|
|
|
671
721
|
else if (exposed.length === 1) {
|
|
672
722
|
target = exposed[0];
|
|
673
723
|
}
|
|
674
|
-
else {
|
|
675
|
-
|
|
676
|
-
|
|
724
|
+
else if (elem === assoc) {
|
|
725
|
+
// `assoc: Association to ModelEntity`: user-provided target is to be auto-redirected
|
|
726
|
+
warning( 'type-ambiguous-target',
|
|
727
|
+
[ elem.target.location, elem ],
|
|
677
728
|
{
|
|
678
729
|
target,
|
|
679
|
-
|
|
680
|
-
art: definitionScope( target ),
|
|
730
|
+
// art: definitionScope( target ), - TODO extra debug info in message
|
|
681
731
|
sorted_arts: exposed,
|
|
682
|
-
'#': ( elemScope !== true ? 'std' : 'scoped' ),
|
|
683
732
|
}, {
|
|
684
733
|
// eslint-disable-next-line max-len
|
|
685
|
-
std: '
|
|
734
|
+
std: 'Replace target $(TARGET) by one of $(SORTED_ARTS); can\'t auto-redirect this association if multiple projections exist in this service',
|
|
686
735
|
// eslint-disable-next-line max-len
|
|
687
|
-
|
|
736
|
+
two: 'Replace target $(TARGET) by $(SORTED_ARTS) or $(SECOND); can\'t auto-redirect this association if multiple projections exist in this service',
|
|
688
737
|
});
|
|
738
|
+
// continuation semantics: no auto-redirection
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
// referred (and probably inferred) assoc (without a user-provided target at that place)
|
|
742
|
+
// HINT: consider bin/cdsv2m.js when changing the following message text
|
|
743
|
+
// No grouped and sub messages yet (TODO v3): mention at all target places with all assocs
|
|
744
|
+
const withAnno = annotationVal( exposed[0]['@cds.redirection.target'] );
|
|
745
|
+
for (const proj of exposed) {
|
|
746
|
+
// TODO: def-ambiguous-target (just v3, as the current is infamous and used in options),
|
|
747
|
+
message( 'redirected-implicitly-ambiguous',
|
|
748
|
+
[ weakLocation( proj.location ), proj ],
|
|
749
|
+
{
|
|
750
|
+
'#': withAnno && 'justOne',
|
|
751
|
+
target,
|
|
752
|
+
art: elem,
|
|
753
|
+
// art: definitionScope( target ), - TODO extra debug info in message
|
|
754
|
+
anno: 'cds.redirection.target',
|
|
755
|
+
sorted_arts: exposed,
|
|
756
|
+
}, {
|
|
757
|
+
// eslint-disable-next-line max-len
|
|
758
|
+
std: 'Add $(ANNO) to one of $(SORTED_ARTS) to select the entity as redirection target for $(TARGET) in this service; can\'t auto-redirect $(ART) otherwise',
|
|
759
|
+
// eslint-disable-next-line max-len
|
|
760
|
+
two: 'Add $(ANNO) to either $(SORTED_ARTS) or $(SECOND) to select the entity as redirection target for $(TARGET) in this service; can\'t auto-redirect $(ART) otherwise',
|
|
761
|
+
// eslint-disable-next-line max-len
|
|
762
|
+
justOne: 'Remove $(ANNO) from all but one of $(SORTED_ARTS) to have a unique redirection target for $(TARGET) in this service; can\'t auto-redirect $(ART) otherwise',
|
|
763
|
+
} );
|
|
764
|
+
}
|
|
689
765
|
// continuation semantics: no implicit redirections
|
|
690
766
|
}
|
|
691
767
|
}
|
|
692
768
|
if (elem.target) { // redirection for Association to / Composition of
|
|
693
769
|
if (elem.target._artifact === target) // no change (due to no implicit redirection)
|
|
694
770
|
return true;
|
|
771
|
+
const type = resolvePath( elem.type, 'type', elem ); // cds.Association or cds.Composition
|
|
695
772
|
const origin = {
|
|
696
|
-
kind: elem.kind,
|
|
773
|
+
kind: elem.kind, // necessary for rewrite, '$user-provided' would be best
|
|
697
774
|
name: elem.name,
|
|
775
|
+
type: {
|
|
776
|
+
path: [ { id: type.name.absolute, location: elem.type.location } ],
|
|
777
|
+
scope: 'global',
|
|
778
|
+
location: elem.type.location,
|
|
779
|
+
},
|
|
698
780
|
target: elem.target,
|
|
699
781
|
$inferred: 'REDIRECTED',
|
|
700
782
|
location: elem.target.location,
|
|
701
783
|
};
|
|
702
784
|
setLink( elem, origin, '_origin' );
|
|
785
|
+
setLink( elem.type, type, '_artifact' );
|
|
786
|
+
setLink( origin, elem, '_outer' );
|
|
703
787
|
setLink( origin, elem._parent, '_parent' );
|
|
704
788
|
if (elem._main) // remark: the param `elem` can also be a type
|
|
705
789
|
setLink( origin, elem._main, '_main' );
|
|
@@ -707,11 +791,11 @@ function resolve( model ) {
|
|
|
707
791
|
setLink( origin, elem._block, '_block' );
|
|
708
792
|
if (elem.foreignKeys) {
|
|
709
793
|
origin.foreignKeys = elem.foreignKeys;
|
|
710
|
-
delete elem.foreignKeys;
|
|
794
|
+
delete elem.foreignKeys; // will be rewritten
|
|
711
795
|
}
|
|
712
796
|
if (elem.on) {
|
|
713
797
|
origin.on = elem.on;
|
|
714
|
-
delete elem.on;
|
|
798
|
+
delete elem.on; // will be rewritten
|
|
715
799
|
}
|
|
716
800
|
}
|
|
717
801
|
elem.target = {
|
|
@@ -770,10 +854,7 @@ function resolve( model ) {
|
|
|
770
854
|
target._descendants[service.name.absolute] ||
|
|
771
855
|
[],
|
|
772
856
|
elemScope, target );
|
|
773
|
-
const preferred = descendants.filter( ( d )
|
|
774
|
-
const anno = d['@cds.redirection.target'];
|
|
775
|
-
return anno && (anno.val === undefined || anno.val );
|
|
776
|
-
} );
|
|
857
|
+
const preferred = descendants.filter( d => annotationVal( d['@cds.redirection.target'] ) );
|
|
777
858
|
const exposed = preferred.length ? preferred : descendants;
|
|
778
859
|
if (exposed.length < 2)
|
|
779
860
|
return exposed || [];
|
|
@@ -812,7 +893,7 @@ function resolve( model ) {
|
|
|
812
893
|
// Need to filter out auto-exposed, otherwise the behavior is
|
|
813
894
|
// processing-order dependent (not storing the autoexposed in
|
|
814
895
|
// _descendents would only be an alternative w/o recompilation)
|
|
815
|
-
return descendants.filter( d => !d['@cds.autoexposed'] );
|
|
896
|
+
return descendants.filter( d => !annotationVal( d['@cds.autoexposed'] ) );
|
|
816
897
|
}
|
|
817
898
|
// try scope as target first, even if it has @cds.redirection.target: false
|
|
818
899
|
if (isDirectProjection( elemScope, target ))
|
|
@@ -943,6 +1024,7 @@ function resolve( model ) {
|
|
|
943
1024
|
$inferred: 'autoexposed',
|
|
944
1025
|
'@cds.autoexposed': {
|
|
945
1026
|
name: { path: [ { id: 'cds.autoexposed', location } ], location },
|
|
1027
|
+
$inferred: 'autoexposed',
|
|
946
1028
|
},
|
|
947
1029
|
};
|
|
948
1030
|
// TODO: do we need to tag the generated entity with elemScope = 'auto'?
|
|
@@ -1440,8 +1522,9 @@ function resolve( model ) {
|
|
|
1440
1522
|
}
|
|
1441
1523
|
// Resolve projections/views
|
|
1442
1524
|
// if (art.query)console.log( info( null, [art.query.location,art.query], 'VQ:' ).toString() );
|
|
1443
|
-
|
|
1444
|
-
|
|
1525
|
+
|
|
1526
|
+
if (art.$queries)
|
|
1527
|
+
art.$queries.forEach( resolveQuery );
|
|
1445
1528
|
|
|
1446
1529
|
if (obj.type || obj._origin || obj.value && obj.value.path || obj.elements) // typed artifacts
|
|
1447
1530
|
effectiveType(obj); // set _effectiveType if appropriate, (future?): copy elems if extended
|
|
@@ -1586,6 +1669,11 @@ function resolve( model ) {
|
|
|
1586
1669
|
}
|
|
1587
1670
|
}
|
|
1588
1671
|
if (art && art._annotate) {
|
|
1672
|
+
if (art.kind === 'action' || art.kind === 'function') {
|
|
1673
|
+
expandParameters( art );
|
|
1674
|
+
if (art.returns)
|
|
1675
|
+
effectiveType( art.returns );
|
|
1676
|
+
}
|
|
1589
1677
|
const aor = art.returns || art;
|
|
1590
1678
|
const obj = aor.items || aor.targetAspect || aor;
|
|
1591
1679
|
// Currently(?), effectiveType() does not calculate the effective type of
|
|
@@ -1620,6 +1708,44 @@ function resolve( model ) {
|
|
|
1620
1708
|
annotateMembers( env && env[n], dict[n], prop, n, parent, kind );
|
|
1621
1709
|
}
|
|
1622
1710
|
}
|
|
1711
|
+
function expandParameters( action ) {
|
|
1712
|
+
// see also expandElements()
|
|
1713
|
+
if (!enableExpandElements || !effectiveType( action ))
|
|
1714
|
+
return;
|
|
1715
|
+
const chain = [];
|
|
1716
|
+
// Should we be able to consider params and returns separately?
|
|
1717
|
+
// Probably not, let to-csn omit unchanged params/returns.
|
|
1718
|
+
while (action._origin && !action.params) {
|
|
1719
|
+
chain.push( action );
|
|
1720
|
+
action = action._origin;
|
|
1721
|
+
}
|
|
1722
|
+
chain.reverse();
|
|
1723
|
+
for (const art of chain) {
|
|
1724
|
+
const origin = art._origin;
|
|
1725
|
+
if (!art.params && origin.params) {
|
|
1726
|
+
for (const name in origin.params) {
|
|
1727
|
+
// TODO: we could check _annotate here to decide whether we really
|
|
1728
|
+
// not to create proxies
|
|
1729
|
+
const orig = origin.params[name];
|
|
1730
|
+
linkToOrigin( orig, name, art, 'params', weakLocation( orig.location ), true )
|
|
1731
|
+
.$inferred = 'expand-param';
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
if (!art.returns && origin.returns) {
|
|
1735
|
+
// TODO: make linkToOrigin() work for returns, kind/name?
|
|
1736
|
+
const location = weakLocation( origin.returns.location );
|
|
1737
|
+
art.returns = {
|
|
1738
|
+
name: Object.assign( {}, art.name, { id: '', param: '', location } ),
|
|
1739
|
+
kind: 'param',
|
|
1740
|
+
location,
|
|
1741
|
+
$inferred: 'expand-param',
|
|
1742
|
+
};
|
|
1743
|
+
setProp( art.returns, '_parent', art );
|
|
1744
|
+
setProp( art.returns, '_main', art._main || art );
|
|
1745
|
+
setProp( art.returns, '_origin', origin.returns );
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1623
1749
|
|
|
1624
1750
|
function extensionFor( art ) {
|
|
1625
1751
|
if (art.kind === 'annotate')
|
|
@@ -1826,7 +1952,7 @@ function resolve( model ) {
|
|
|
1826
1952
|
function resolveQuery( query ) {
|
|
1827
1953
|
if (!query._main) // parse error
|
|
1828
1954
|
return;
|
|
1829
|
-
|
|
1955
|
+
traverseQueryPost( query, null, populateQuery );
|
|
1830
1956
|
forEachGeneric( query, '$tableAliases', ( alias ) => {
|
|
1831
1957
|
// console.log( info( null, [alias.location,alias], 'SQA:' ).toString() );
|
|
1832
1958
|
if (alias.kind === 'mixin')
|
|
@@ -1967,6 +2093,7 @@ function resolve( model ) {
|
|
|
1967
2093
|
dependsOnSilent(art, key);
|
|
1968
2094
|
}
|
|
1969
2095
|
});
|
|
2096
|
+
obj.foreignKeys[$inferred] = 'keys';
|
|
1970
2097
|
}
|
|
1971
2098
|
|
|
1972
2099
|
function addForeignKeyNavigations( art ) {
|
|
@@ -2135,8 +2262,6 @@ function resolve( model ) {
|
|
|
2135
2262
|
// Only top-level queries and sub queries in FROM
|
|
2136
2263
|
|
|
2137
2264
|
function rewriteSimple( art ) {
|
|
2138
|
-
// If we have a proper seperation of view elements and elements of the
|
|
2139
|
-
// primary query, we can delete this function.
|
|
2140
2265
|
// return;
|
|
2141
2266
|
if (!art.includes && !art.query) {
|
|
2142
2267
|
// console.log(message( null, art.location, art, {target:art._target},
|
|
@@ -2149,7 +2274,7 @@ function resolve( model ) {
|
|
|
2149
2274
|
}
|
|
2150
2275
|
|
|
2151
2276
|
function rewriteView( view ) {
|
|
2152
|
-
|
|
2277
|
+
traverseQueryExtra( view, ( query ) => {
|
|
2153
2278
|
forEachGeneric( query, 'elements', rewriteAssociation );
|
|
2154
2279
|
} );
|
|
2155
2280
|
if (view.includes) // entities with structure includes:
|
|
@@ -2157,6 +2282,7 @@ function resolve( model ) {
|
|
|
2157
2282
|
}
|
|
2158
2283
|
|
|
2159
2284
|
// Check explicit ON / keys with REDIRECTED TO
|
|
2285
|
+
// TODO: run on all queries, but this is potentially incompatible
|
|
2160
2286
|
function rewriteViewCheck( view ) {
|
|
2161
2287
|
traverseQueryPost( view.query, false, ( query ) => {
|
|
2162
2288
|
forEachGeneric( query, 'elements', rewriteAssociationCheck );
|
|
@@ -2382,7 +2508,7 @@ function resolve( model ) {
|
|
|
2382
2508
|
resolveExpr( cond, rewriteExpr, elem, nav.tableAlias );
|
|
2383
2509
|
}
|
|
2384
2510
|
else {
|
|
2385
|
-
// TODO: support that
|
|
2511
|
+
// TODO: support that, now that the ON condition is rewritten in the right order
|
|
2386
2512
|
error( null, [ elem.value.location, elem ],
|
|
2387
2513
|
'Selecting unmanaged associations from a sub query is not supported' );
|
|
2388
2514
|
}
|
|
@@ -2551,6 +2677,8 @@ function resolve( model ) {
|
|
|
2551
2677
|
}
|
|
2552
2678
|
|
|
2553
2679
|
function resolveExpr( expr, expected, user, extDict, expandOrInline) {
|
|
2680
|
+
// TODO: when we have rewritten the resolvePath functions,
|
|
2681
|
+
// define a traverseExpr() in ./utils.js
|
|
2554
2682
|
// TODO: extra "expected" 'expand'/'inline' instead o param `expandOrInline`
|
|
2555
2683
|
if (!expr || typeof expr === 'string') // parse error or keywords in {xpr:...}
|
|
2556
2684
|
return;
|
|
@@ -2587,7 +2715,7 @@ function resolve( model ) {
|
|
|
2587
2715
|
else if (expr.query) {
|
|
2588
2716
|
const { query } = expr;
|
|
2589
2717
|
if (query.kind || query._leadingQuery) { // UNION has _leadingQuery
|
|
2590
|
-
traverseQueryPost( query, false, resolveQuery );
|
|
2718
|
+
// traverseQueryPost( query, false, resolveQuery );
|
|
2591
2719
|
}
|
|
2592
2720
|
else {
|
|
2593
2721
|
error( 'expr-no-subquery', [ expr.location, user ], {},
|
|
@@ -2598,9 +2726,10 @@ function resolve( model ) {
|
|
|
2598
2726
|
const args = Array.isArray(expr.args) ? expr.args : Object.values( expr.args );
|
|
2599
2727
|
args.forEach( e => e && resolveExpr( e, e.$expected || expected, user, extDict ) );
|
|
2600
2728
|
}
|
|
2601
|
-
if (expr.suffix &&
|
|
2729
|
+
if (expr.suffix && isDeprecatedEnabled( options )) {
|
|
2602
2730
|
const { location } = expr.suffix[0] || expr;
|
|
2603
|
-
error( null, [ location, user ],
|
|
2731
|
+
error( null, [ location, user ], { prop: 'deprecated' },
|
|
2732
|
+
'Window functions are not supported if $(PROP) options are set' );
|
|
2604
2733
|
}
|
|
2605
2734
|
if (expr.suffix)
|
|
2606
2735
|
expr.suffix.forEach( s => s && resolveExpr( s, expected, user, extDict ) );
|
|
@@ -2785,11 +2914,11 @@ function navProjection( navigation, preferred ) {
|
|
|
2785
2914
|
: navigation._projections[0] || null;
|
|
2786
2915
|
}
|
|
2787
2916
|
|
|
2788
|
-
// Query tree post-order traversal - called for everything which
|
|
2917
|
+
// Query tree post-order traversal - called for everything which contributes to the query
|
|
2918
|
+
// i.e. is necessary to calculate the elements of the query
|
|
2789
2919
|
// except "real ones": operands of UNION etc, JOIN with ON, and sub queries in FROM
|
|
2920
|
+
// NOTE: does not run on non-referred sub queries! Consider using ‹main›.$queries instead!
|
|
2790
2921
|
function traverseQueryPost( query, simpleOnly, callback ) {
|
|
2791
|
-
while (Array.isArray(query)) // query in parentheses, TODO: remove
|
|
2792
|
-
query = query[0];
|
|
2793
2922
|
if (!query) // parser error
|
|
2794
2923
|
return;
|
|
2795
2924
|
if (!query.op) { // in FROM (not JOIN)
|
|
@@ -2809,13 +2938,51 @@ function traverseQueryPost( query, simpleOnly, callback ) {
|
|
|
2809
2938
|
// console.log('FE:')
|
|
2810
2939
|
}
|
|
2811
2940
|
else if (query.args) { // JOIN, UNION, INTERSECT
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2941
|
+
if (!query.join && simpleOnly == null) {
|
|
2942
|
+
// enough for elements: traverse only first args for UNION/INTERSECT
|
|
2943
|
+
// TODO: we might use this also when we do not rewrite associations
|
|
2944
|
+
// in non-referred sub queries
|
|
2945
|
+
traverseQueryPost( query.args[0], simpleOnly, callback );
|
|
2946
|
+
}
|
|
2947
|
+
else {
|
|
2948
|
+
for (const q of query.args)
|
|
2949
|
+
traverseQueryPost( q, simpleOnly, callback );
|
|
2950
|
+
// The ON condition has to be traversed extra, because it must be evaluated
|
|
2951
|
+
// after the complete FROM has been traversed. It is also not necessary to
|
|
2952
|
+
// evaluate it in populateQuery().
|
|
2953
|
+
}
|
|
2817
2954
|
}
|
|
2818
2955
|
// else: with parse error (`select from <EOF>`, `select distinct from;`)
|
|
2819
2956
|
}
|
|
2820
2957
|
|
|
2958
|
+
// Call callback on all queries in dependency order, i.e. starting with query Q
|
|
2959
|
+
// 1. sub queries in FROM sources of Q
|
|
2960
|
+
// 2. Q itself, except if non-referred query, but with right UNION parts
|
|
2961
|
+
// 3. sub queries in ON in FROM of Q
|
|
2962
|
+
// 4. sub queries in columns, WHERE, HAVING
|
|
2963
|
+
function traverseQueryExtra( main, callback ) {
|
|
2964
|
+
if (!main.$queries)
|
|
2965
|
+
return;
|
|
2966
|
+
// with a top-level UNION, $queries[0] is just the left
|
|
2967
|
+
traverseQueryPost( main.query, false, (q) => { // also with right of UNION (to be compatible)
|
|
2968
|
+
setProp( q, '_status', 'extra' );
|
|
2969
|
+
callback( q );
|
|
2970
|
+
} );
|
|
2971
|
+
for (const query of main.$queries.slice(1)) {
|
|
2972
|
+
if (query._status === 'extra' || query._parent.kind === '$tableAlias')
|
|
2973
|
+
continue; // if parent is alias, query is FROM source -> run by traverseQueryPost
|
|
2974
|
+
// we are now in the top-level (parent is entity) or a non-referred query (parent is query)
|
|
2975
|
+
setProp( query, '_status', 'extra' ); // do not call callback() in non-referred query
|
|
2976
|
+
// console.log( 'A:', query.name,query._status)
|
|
2977
|
+
traverseQueryPost( query, null, (q) => {
|
|
2978
|
+
if (q._status !== 'extra') {
|
|
2979
|
+
// console.log( 'T:', q.name)
|
|
2980
|
+
setProp( q, '_status', 'extra' );
|
|
2981
|
+
callback( q );
|
|
2982
|
+
}
|
|
2983
|
+
// else console.log( 'E:', q.name)
|
|
2984
|
+
} );
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2821
2988
|
module.exports = resolve;
|