@sap/cds-compiler 4.1.2 → 4.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +101 -1
- package/bin/cdsc.js +6 -3
- package/doc/CHANGELOG_BETA.md +5 -0
- package/doc/CHANGELOG_DEPRECATED.md +15 -0
- package/lib/api/main.js +2 -2
- package/lib/api/options.js +2 -2
- package/lib/api/validate.js +24 -24
- package/lib/base/message-registry.js +41 -6
- package/lib/base/messages.js +7 -0
- package/lib/base/model.js +37 -8
- package/lib/checks/elements.js +11 -10
- package/lib/checks/manyNavigations.js +33 -0
- package/lib/checks/onConditions.js +5 -2
- package/lib/checks/queryNoDbArtifacts.js +2 -3
- package/lib/checks/selectItems.js +4 -55
- package/lib/checks/utils.js +3 -2
- package/lib/checks/validator.js +3 -1
- package/lib/compiler/.eslintrc.json +2 -1
- package/lib/compiler/assert-consistency.js +27 -24
- package/lib/compiler/base.js +6 -2
- package/lib/compiler/builtins.js +34 -34
- package/lib/compiler/checks.js +179 -208
- package/lib/compiler/classes.js +2 -2
- package/lib/compiler/cycle-detector.js +6 -6
- package/lib/compiler/define.js +66 -45
- package/lib/compiler/extend.js +81 -72
- package/lib/compiler/finalize-parse-cdl.js +26 -26
- package/lib/compiler/generate.js +61 -45
- package/lib/compiler/index.js +47 -49
- package/lib/compiler/kick-start.js +8 -7
- package/lib/compiler/moduleLayers.js +1 -1
- package/lib/compiler/populate.js +42 -35
- package/lib/compiler/propagator.js +6 -6
- package/lib/compiler/resolve.js +170 -126
- package/lib/compiler/shared.js +122 -45
- package/lib/compiler/tweak-assocs.js +93 -40
- package/lib/compiler/utils.js +15 -12
- package/lib/edm/.eslintrc.json +40 -1
- package/lib/edm/annotations/genericTranslation.js +721 -707
- package/lib/edm/annotations/preprocessAnnotations.js +88 -77
- package/lib/edm/csn2edm.js +389 -378
- package/lib/edm/edm.js +678 -772
- package/lib/edm/edmAnnoPreprocessor.js +132 -146
- package/lib/edm/edmInboundChecks.js +29 -27
- package/lib/edm/edmPreprocessor.js +686 -646
- package/lib/edm/edmUtils.js +277 -296
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +1253 -1276
- package/lib/json/from-csn.js +34 -4
- package/lib/json/to-csn.js +4 -4
- package/lib/language/language.g4 +2 -5
- package/lib/main.d.ts +61 -1
- package/lib/model/csnUtils.js +31 -2
- package/lib/model/revealInternalProperties.js +1 -1
- package/lib/modelCompare/compare.js +37 -2
- package/lib/modelCompare/utils/filter.js +1 -1
- package/lib/optionProcessor.js +15 -3
- package/lib/render/toCdl.js +30 -4
- package/lib/render/toSql.js +5 -9
- package/lib/render/utils/common.js +8 -6
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/cdsPersistence.js +1 -1
- package/lib/transform/db/constraints.js +47 -17
- package/lib/transform/db/expansion.js +121 -47
- package/lib/transform/db/flattening.js +75 -7
- package/lib/transform/forOdata.js +4 -1
- package/lib/transform/forRelationalDB.js +80 -62
- package/lib/transform/localized.js +91 -54
- package/lib/transform/transformUtils.js +9 -10
- package/lib/utils/file.js +7 -7
- package/lib/utils/moduleResolve.js +210 -121
- package/lib/utils/objectUtils.js +1 -1
- package/package.json +5 -5
package/lib/compiler/resolve.js
CHANGED
|
@@ -62,13 +62,14 @@ const {
|
|
|
62
62
|
testExpr,
|
|
63
63
|
targetMaxNotOne,
|
|
64
64
|
traverseQueryPost,
|
|
65
|
+
linkToOrigin,
|
|
65
66
|
} = require('./utils');
|
|
66
67
|
|
|
67
68
|
const detectCycles = require('./cycle-detector');
|
|
68
69
|
|
|
69
|
-
const $location = Symbol.for('cds.$location');
|
|
70
|
+
const $location = Symbol.for( 'cds.$location' );
|
|
70
71
|
|
|
71
|
-
const $inferred = Symbol.for('cds.$inferred');
|
|
72
|
+
const $inferred = Symbol.for( 'cds.$inferred' );
|
|
72
73
|
|
|
73
74
|
// TODO: make this part of specExpected in shared.js
|
|
74
75
|
// (standard: not possible on last if !ref.$expected → ref.scope: '$exists')
|
|
@@ -99,7 +100,7 @@ function resolve( model ) {
|
|
|
99
100
|
} );
|
|
100
101
|
|
|
101
102
|
const ignoreSpecifiedElements
|
|
102
|
-
= isDeprecatedEnabled(model.options, 'ignoreSpecifiedQueryElements');
|
|
103
|
+
= isDeprecatedEnabled( model.options, 'ignoreSpecifiedQueryElements' );
|
|
103
104
|
|
|
104
105
|
return doResolve();
|
|
105
106
|
|
|
@@ -134,9 +135,9 @@ function resolve( model ) {
|
|
|
134
135
|
std: 'Illegal circular reference to $(ART)',
|
|
135
136
|
element: 'Illegal circular reference to element $(MEMBER) of $(ART)',
|
|
136
137
|
target: 'Illegal circular reference to target $(ART)',
|
|
137
|
-
});
|
|
138
|
+
} );
|
|
138
139
|
}
|
|
139
|
-
});
|
|
140
|
+
} );
|
|
140
141
|
if (model.$assert) {
|
|
141
142
|
error( '$internal-expecting-cyclic', null, {},
|
|
142
143
|
'INTERNAL: the compiler should have issued an Error[ref-cyclic]' );
|
|
@@ -157,22 +158,52 @@ function resolve( model ) {
|
|
|
157
158
|
return;
|
|
158
159
|
// TODO: what about elements where _origin is set without value?
|
|
159
160
|
// TODO: or should we push elems with `expand` sibling to extra list for
|
|
160
|
-
//
|
|
161
|
+
// better messages? (Whatever that means exactly.)
|
|
161
162
|
const nav = pathNavigation( elem.value );
|
|
162
163
|
const { path } = elem.value;
|
|
163
|
-
|
|
164
|
-
if (
|
|
165
|
-
|
|
166
|
-
// redirectImplicitly( elem, origin );
|
|
167
|
-
pushLink( nav.navigation, '_projections', elem );
|
|
168
|
-
}
|
|
169
|
-
else if (elem._pathHead?.kind === '$inline' && path.length === 1) {
|
|
164
|
+
|
|
165
|
+
if (elem._pathHead?.kind === '$inline' && path.length === 1) {
|
|
166
|
+
const item = path[0];
|
|
170
167
|
const hpath = elem._pathHead.value?.path;
|
|
171
168
|
const head = hpath?.length === 1 && hpath[0]._navigation;
|
|
172
169
|
// Alias .{ elem } - but not with Alias.{ $magic }: consider for ON-rewrite
|
|
173
170
|
if (head?.kind === '$tableAlias' && item.id.charAt(0) !== '$')
|
|
174
171
|
pushLink( head.elements[item.id], '_projections', elem );
|
|
175
172
|
}
|
|
173
|
+
else if (nav.navigation) { // not set for $self.…
|
|
174
|
+
// Path could start with table alias; get start index
|
|
175
|
+
let index = path.indexOf(nav.item);
|
|
176
|
+
if (index === -1)
|
|
177
|
+
return;
|
|
178
|
+
|
|
179
|
+
let navItem = nav.navigation;
|
|
180
|
+
if (path[index].where || path[index].args)
|
|
181
|
+
return;
|
|
182
|
+
++index;
|
|
183
|
+
while (navItem && index < path.length) {
|
|
184
|
+
const step = path[index];
|
|
185
|
+
if (!step?.id || step.where || step.args)
|
|
186
|
+
break;
|
|
187
|
+
if (!navItem.elements?.[step.id]) {
|
|
188
|
+
const elements = navItem._origin?.elements ||
|
|
189
|
+
navItem._origin?.target?._artifact?.elements;
|
|
190
|
+
if (!elements)
|
|
191
|
+
break;
|
|
192
|
+
// Only link available path steps (navigation tree).
|
|
193
|
+
const origin = elements[step.id];
|
|
194
|
+
const member = linkToOrigin( origin, step.id, navItem, 'elements',
|
|
195
|
+
navItem.path?.location, true );
|
|
196
|
+
member.$inferred = 'expanded';
|
|
197
|
+
member.kind = '$navElement';
|
|
198
|
+
}
|
|
199
|
+
navItem = navItem.elements[step.id];
|
|
200
|
+
setLink( step, '_navigation', navItem );
|
|
201
|
+
++index;
|
|
202
|
+
}
|
|
203
|
+
// Last path step, if found, is a simple projection
|
|
204
|
+
if (index === path.length && navItem)
|
|
205
|
+
pushLink( navItem, '_projections', elem );
|
|
206
|
+
}
|
|
176
207
|
} );
|
|
177
208
|
}
|
|
178
209
|
}
|
|
@@ -361,7 +392,7 @@ function resolve( model ) {
|
|
|
361
392
|
for (const include of art.includes) {
|
|
362
393
|
const struct = include._artifact;
|
|
363
394
|
if (struct && struct.kind !== 'type' && struct.elements &&
|
|
364
|
-
Object.values( struct.elements ).some( e => e.targetAspect)) {
|
|
395
|
+
Object.values( struct.elements ).some( e => e.targetAspect )) {
|
|
365
396
|
message( 'type-managed-composition', [ include.location, art ],
|
|
366
397
|
{ '#': struct.kind, art: struct } );
|
|
367
398
|
}
|
|
@@ -385,7 +416,7 @@ function resolve( model ) {
|
|
|
385
416
|
}
|
|
386
417
|
if (obj.items) { // TODO: make this a while in v2 (also items proxy)
|
|
387
418
|
obj = obj.items || obj; // the object which has type properties
|
|
388
|
-
effectiveType(obj);
|
|
419
|
+
effectiveType( obj );
|
|
389
420
|
}
|
|
390
421
|
if (obj.type) { // TODO: && !obj.type.$inferred ?
|
|
391
422
|
if (obj !== (art.returns || art)) // not already checked
|
|
@@ -412,13 +443,13 @@ function resolve( model ) {
|
|
|
412
443
|
if (elemtype.category === 'relation' && obj.type.path.length > 0 &&
|
|
413
444
|
!obj.target && !obj.targetAspect) {
|
|
414
445
|
const isCsn = (obj._block && obj._block.$frontend === 'json');
|
|
415
|
-
error('type-missing-target', [ obj.type.location, obj ],
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
446
|
+
error( 'type-missing-target', [ obj.type.location, obj ],
|
|
447
|
+
{ '#': isCsn ? 'csn' : 'std', type: elemtype.name.absolute }, {
|
|
448
|
+
// We don't say "use 'association to <target>" because the type could be used
|
|
449
|
+
// in action parameters, etc. as well.
|
|
450
|
+
std: 'The type $(TYPE) can\'t be used directly because it\'s compiler internal',
|
|
451
|
+
csn: 'Type $(TYPE) is missing a target',
|
|
452
|
+
} );
|
|
422
453
|
}
|
|
423
454
|
}
|
|
424
455
|
}
|
|
@@ -454,7 +485,7 @@ function resolve( model ) {
|
|
|
454
485
|
}
|
|
455
486
|
if (obj.foreignKeys) { // silent dependencies
|
|
456
487
|
// Avoid strange ref-cyclic if managed composition is key (check comes later)
|
|
457
|
-
// TODO: the following is already done by
|
|
488
|
+
// TODO: the following is already done by addImplicitForeignKeys()!
|
|
458
489
|
if (obj.$inferred !== 'aspect-composition') {
|
|
459
490
|
forEachGeneric( obj, 'foreignKeys', (elem) => {
|
|
460
491
|
dependsOnSilent( art, elem );
|
|
@@ -470,9 +501,9 @@ function resolve( model ) {
|
|
|
470
501
|
inferTypePropertiesFromCast( art );
|
|
471
502
|
if (art.value) {
|
|
472
503
|
if (art.$syntax === 'calc')
|
|
473
|
-
checkCalculatedElement(art);
|
|
504
|
+
checkCalculatedElement( art );
|
|
474
505
|
else if (art.type && !art.$inferred )
|
|
475
|
-
checkStructureCast(art);
|
|
506
|
+
checkStructureCast( art );
|
|
476
507
|
}
|
|
477
508
|
|
|
478
509
|
forEachMember( art, resolveRefs, art.targetAspect );
|
|
@@ -482,8 +513,8 @@ function resolve( model ) {
|
|
|
482
513
|
|
|
483
514
|
if (!ignoreSpecifiedElements && art.elements$ && art.elements) {
|
|
484
515
|
for (const id in art.elements$) {
|
|
485
|
-
resolveRefs(art.elements$[id]);
|
|
486
|
-
checkSpecifiedElement(art.elements[id], art.elements$[id]);
|
|
516
|
+
resolveRefs( art.elements$[id] );
|
|
517
|
+
checkSpecifiedElement( art.elements[id], art.elements$[id] );
|
|
487
518
|
}
|
|
488
519
|
}
|
|
489
520
|
|
|
@@ -508,96 +539,97 @@ function resolve( model ) {
|
|
|
508
539
|
|
|
509
540
|
// Check explicit types: If either side has one, so must the other.
|
|
510
541
|
const sType = specifiedElement.type?._artifact;
|
|
511
|
-
const iType = getInferredPropFromOrigin('type')?._artifact || inferredElement;
|
|
542
|
+
const iType = getInferredPropFromOrigin( 'type' )?._artifact || inferredElement;
|
|
512
543
|
|
|
513
544
|
// xor: could be missing a type;
|
|
514
545
|
// FIXME: The coding above returns incorrect iType for expand on associations
|
|
515
546
|
if (!specifiedElement.type && inferredElement.type) {
|
|
516
|
-
error('query-mismatched-element', [ specifiedElement.location, user ], {
|
|
547
|
+
error( 'query-mismatched-element', [ specifiedElement.location, user ], {
|
|
517
548
|
'#': !specifiedElement.type ? 'missing' : 'extra', name: user.name.id, prop: 'type',
|
|
518
|
-
});
|
|
549
|
+
} );
|
|
519
550
|
return;
|
|
520
551
|
}
|
|
521
552
|
// If specified type is `null`, type could not be resolved.
|
|
522
553
|
else if (sType && sType !== iType) {
|
|
523
554
|
const typeName = iType?.name && sType?.name ? 'typeName' : 'type';
|
|
524
|
-
|
|
555
|
+
const othertype = typeName !== 'type' && iType || '';
|
|
556
|
+
error( 'query-mismatched-element', [
|
|
525
557
|
specifiedElement.type.location || specifiedElement.location, user,
|
|
526
558
|
], {
|
|
527
559
|
'#': typeName,
|
|
528
560
|
name: user.name.id,
|
|
529
561
|
type: sType,
|
|
530
|
-
othertype
|
|
531
|
-
});
|
|
562
|
+
othertype,
|
|
563
|
+
} );
|
|
532
564
|
return;
|
|
533
565
|
}
|
|
534
566
|
|
|
535
567
|
// This relies on (element) expansion! Check that both sides have the following properties.
|
|
536
568
|
// On the inferred side, they are likely expanded.
|
|
537
|
-
if (!hasXorPropMismatch('elements') && !hasXorPropMismatch('items') &&
|
|
538
|
-
!hasXorPropMismatch('target') && !hasXorPropMismatch('enum')) {
|
|
569
|
+
if (!hasXorPropMismatch( 'elements' ) && !hasXorPropMismatch( 'items' ) &&
|
|
570
|
+
!hasXorPropMismatch( 'target' ) && !hasXorPropMismatch( 'enum' )) {
|
|
539
571
|
// Element are already traversed via elements$ merging.
|
|
540
572
|
|
|
541
573
|
// only check items, if the specified one is not expanded/inferred
|
|
542
574
|
if (specifiedElement.items && !specifiedElement.items.$inferred)
|
|
543
|
-
checkSpecifiedElement(inferredElement.items, specifiedElement.items, specifiedElement);
|
|
575
|
+
checkSpecifiedElement( inferredElement.items, specifiedElement.items, specifiedElement );
|
|
544
576
|
|
|
545
|
-
if (specifiedElement.target &&
|
|
577
|
+
if (specifiedElement.target?._artifact && inferredElement.target?._artifact &&
|
|
546
578
|
specifiedElement.target._artifact !== inferredElement.target._artifact) {
|
|
547
|
-
error('query-mismatched-element', [
|
|
579
|
+
error( 'query-mismatched-element', [
|
|
548
580
|
specifiedElement.target.location || specifiedElement.location, user,
|
|
549
581
|
], {
|
|
550
582
|
'#': 'target',
|
|
551
583
|
name: user.name.id,
|
|
552
584
|
target: specifiedElement.target,
|
|
553
585
|
art: inferredElement.target,
|
|
554
|
-
});
|
|
586
|
+
} );
|
|
555
587
|
}
|
|
556
588
|
|
|
557
589
|
if (specifiedElement.foreignKeys) {
|
|
558
|
-
const sKeys = Object.keys(specifiedElement.foreignKeys);
|
|
590
|
+
const sKeys = Object.keys( specifiedElement.foreignKeys );
|
|
559
591
|
/** @type {any} */
|
|
560
592
|
let iKeys = inferredElement;
|
|
561
593
|
if (inferredElement._effectiveType !== 0) {
|
|
562
594
|
while (iKeys._origin && !iKeys.foreignKeys)
|
|
563
595
|
iKeys = iKeys._origin;
|
|
564
596
|
}
|
|
565
|
-
iKeys = Object.keys(iKeys.foreignKeys || {});
|
|
566
|
-
if (sKeys.length !== iKeys.length || sKeys.some( fkey => !iKeys.includes(fkey))) {
|
|
567
|
-
error('query-mismatched-element', [
|
|
597
|
+
iKeys = Object.keys( iKeys.foreignKeys || {} );
|
|
598
|
+
if (sKeys.length !== iKeys.length || sKeys.some( fkey => !iKeys.includes( fkey ) )) {
|
|
599
|
+
error( 'query-mismatched-element', [
|
|
568
600
|
specifiedElement.foreignKeys.location || specifiedElement.location, user,
|
|
569
601
|
], {
|
|
570
602
|
'#': 'foreignKeys',
|
|
571
603
|
name: user.name.id,
|
|
572
604
|
target: specifiedElement.target,
|
|
573
605
|
art: inferredElement.target,
|
|
574
|
-
});
|
|
606
|
+
} );
|
|
575
607
|
}
|
|
576
608
|
}
|
|
577
609
|
|
|
578
610
|
if (specifiedElement.virtual) {
|
|
579
|
-
const iVirtual = getInferredPropFromOrigin('virtual')?.val || false;
|
|
611
|
+
const iVirtual = getInferredPropFromOrigin( 'virtual' )?.val || false;
|
|
580
612
|
if (!specifiedElement.virtual.val !== !iVirtual) {
|
|
581
|
-
error('query-mismatched-element', [
|
|
613
|
+
error( 'query-mismatched-element', [
|
|
582
614
|
specifiedElement.virtual.location || specifiedElement.location, user,
|
|
583
615
|
], {
|
|
584
616
|
'#': 'prop', prop: 'virtual', name: user.name.id,
|
|
585
|
-
});
|
|
617
|
+
} );
|
|
586
618
|
}
|
|
587
619
|
}
|
|
588
620
|
|
|
589
621
|
// If cardinality is not specified, the compiler uses the inferred one.
|
|
590
622
|
if (specifiedElement.cardinality) {
|
|
591
623
|
const sCardinality = specifiedElement.cardinality;
|
|
592
|
-
const iCardinality = getInferredPropFromOrigin('cardinality');
|
|
624
|
+
const iCardinality = getInferredPropFromOrigin( 'cardinality' );
|
|
593
625
|
if (!iCardinality) {
|
|
594
|
-
error('query-mismatched-element', [
|
|
626
|
+
error( 'query-mismatched-element', [
|
|
595
627
|
sCardinality.location || specifiedElement.location, user,
|
|
596
628
|
], {
|
|
597
629
|
'#': 'extra',
|
|
598
630
|
prop: 'cardinality',
|
|
599
631
|
name: user.name.id,
|
|
600
|
-
});
|
|
632
|
+
} );
|
|
601
633
|
}
|
|
602
634
|
else {
|
|
603
635
|
// Note: Cardinality does not have sourceMin (CSN "srcmin").
|
|
@@ -609,7 +641,7 @@ function resolve( model ) {
|
|
|
609
641
|
for (const prop in props) {
|
|
610
642
|
if (sCardinality[prop]?.val === iCardinality[prop]?.val)
|
|
611
643
|
continue;
|
|
612
|
-
error('query-mismatched-element', [
|
|
644
|
+
error( 'query-mismatched-element', [
|
|
613
645
|
sCardinality[prop]?.location || sCardinality.location || specifiedElement.location,
|
|
614
646
|
user,
|
|
615
647
|
], {
|
|
@@ -617,29 +649,29 @@ function resolve( model ) {
|
|
|
617
649
|
'#': !sCardinality[prop] ? 'missing' : (iCardinality[prop] ? 'prop' : 'extra'),
|
|
618
650
|
prop: `cardinality.${ props[prop] }`,
|
|
619
651
|
name: user.name.id,
|
|
620
|
-
});
|
|
652
|
+
} );
|
|
621
653
|
}
|
|
622
654
|
}
|
|
623
655
|
}
|
|
624
656
|
|
|
625
657
|
if (specifiedElement.value) {
|
|
626
|
-
error('query-unexpected-property', [
|
|
658
|
+
error( 'query-unexpected-property', [
|
|
627
659
|
specifiedElement.value.location || specifiedElement.location, user,
|
|
628
660
|
], {
|
|
629
661
|
'#': 'calculatedElement', prop: 'value', name: user.name.id,
|
|
630
|
-
});
|
|
662
|
+
} );
|
|
631
663
|
}
|
|
632
664
|
|
|
633
665
|
if (specifiedElement.key) { // TODO: `|| inferredElement.key?.val`, once to.sql is fixed
|
|
634
666
|
// TODO: Do not use _origin chain for key; has been propagated in propagateKeyProps().
|
|
635
|
-
const iKey = getInferredPropFromOrigin('key')?.val;
|
|
667
|
+
const iKey = getInferredPropFromOrigin( 'key' )?.val;
|
|
636
668
|
// If "key" is specified or truthy in the inferred element, the values must match.
|
|
637
669
|
if (!iKey !== !specifiedElement.key?.val) {
|
|
638
|
-
error('query-mismatched-element', [
|
|
670
|
+
error( 'query-mismatched-element', [
|
|
639
671
|
specifiedElement.key?.location || specifiedElement.location, user,
|
|
640
672
|
], {
|
|
641
673
|
'#': specifiedElement.key ? 'prop' : 'missing', prop: 'key', name: user.name.id,
|
|
642
|
-
});
|
|
674
|
+
} );
|
|
643
675
|
}
|
|
644
676
|
}
|
|
645
677
|
|
|
@@ -653,9 +685,9 @@ function resolve( model ) {
|
|
|
653
685
|
const sEnumEntry = sEnumValues[name];
|
|
654
686
|
const iEnumEntry = iEnumValues[name]?._effectiveType || iEnumValues[name];
|
|
655
687
|
if (!iEnumEntry) {
|
|
656
|
-
error('query-mismatched-element', [ specifiedElement.location, user ], {
|
|
688
|
+
error( 'query-mismatched-element', [ specifiedElement.location, user ], {
|
|
657
689
|
'#': 'enumExtra', name: user.name.id, id: name,
|
|
658
|
-
});
|
|
690
|
+
} );
|
|
659
691
|
break;
|
|
660
692
|
}
|
|
661
693
|
else {
|
|
@@ -663,9 +695,9 @@ function resolve( model ) {
|
|
|
663
695
|
const iVal = iEnumEntry.value?.val || iEnumEntry.value?.['#'] || name;
|
|
664
696
|
const sVal = sEnumEntry.value?.val || sEnumEntry.value?.['#'] || name;
|
|
665
697
|
if (iVal !== sVal) {
|
|
666
|
-
error('query-mismatched-element', [ specifiedElement.location, user ], {
|
|
698
|
+
error( 'query-mismatched-element', [ specifiedElement.location, user ], {
|
|
667
699
|
'#': 'enumVal', name: user.name.id, id: name,
|
|
668
|
-
});
|
|
700
|
+
} );
|
|
669
701
|
break;
|
|
670
702
|
}
|
|
671
703
|
}
|
|
@@ -682,7 +714,7 @@ function resolve( model ) {
|
|
|
682
714
|
!inferredElement.value?.[prop] !== !specifiedElement[prop]) {
|
|
683
715
|
error( 'query-mismatched-element', [ specifiedElement.location, specifiedElement ], {
|
|
684
716
|
'#': specifiedElement[prop] ? 'extra' : 'missing', name: user.name.id, prop,
|
|
685
|
-
});
|
|
717
|
+
} );
|
|
686
718
|
return true;
|
|
687
719
|
}
|
|
688
720
|
return false;
|
|
@@ -692,8 +724,8 @@ function resolve( model ) {
|
|
|
692
724
|
// Inferred property via _origin chain (0 === circular).
|
|
693
725
|
let element = inferredElement;
|
|
694
726
|
if (element._effectiveType !== 0) {
|
|
695
|
-
while (getOrigin(element) && !element[prop])
|
|
696
|
-
element = getOrigin(element);
|
|
727
|
+
while (getOrigin( element ) && !element[prop])
|
|
728
|
+
element = getOrigin( element );
|
|
697
729
|
}
|
|
698
730
|
return element[prop];
|
|
699
731
|
}
|
|
@@ -731,21 +763,21 @@ function resolve( model ) {
|
|
|
731
763
|
while (parent.kind === 'element')
|
|
732
764
|
parent = parent._parent;
|
|
733
765
|
|
|
734
|
-
if (!allowedInKind.includes(art._main.kind)) {
|
|
766
|
+
if (!allowedInKind.includes( art._main.kind )) {
|
|
735
767
|
if (art.$inferred === 'include') {
|
|
736
768
|
// even for include-chains, we find the correct ref due to element-expansion.
|
|
737
|
-
const include = art._main.includes.find(i => i._artifact === art._origin._main);
|
|
738
|
-
error('ref-invalid-calc-elem', [ include.location || art.value.location, art ],
|
|
739
|
-
|
|
769
|
+
const include = art._main.includes.find( i => i._artifact === art._origin._main );
|
|
770
|
+
error( 'ref-invalid-calc-elem', [ include.location || art.value.location, art ],
|
|
771
|
+
{ '#': art._main.kind } );
|
|
740
772
|
}
|
|
741
773
|
else {
|
|
742
774
|
error( 'def-invalid-calc-elem', loc, { '#': art._main.kind } );
|
|
743
775
|
}
|
|
744
776
|
}
|
|
745
|
-
else if (!allowedInKind.includes(parent.kind)) {
|
|
777
|
+
else if (!allowedInKind.includes( parent.kind )) {
|
|
746
778
|
error( 'def-invalid-calc-elem', loc, { '#': parent.kind } );
|
|
747
779
|
}
|
|
748
|
-
else if (effectiveType(art)?.elements) {
|
|
780
|
+
else if (effectiveType( art )?.elements) {
|
|
749
781
|
// For inferred (e.g. included) calc elements, this error is already emitted at the origin.
|
|
750
782
|
if (!art.$inferred) {
|
|
751
783
|
if (art.type)
|
|
@@ -754,7 +786,7 @@ function resolve( model ) {
|
|
|
754
786
|
error( 'ref-unexpected-structured', [ art.value.location, art ], { '#': 'expr' } );
|
|
755
787
|
}
|
|
756
788
|
}
|
|
757
|
-
else if (effectiveType(art)?.items) {
|
|
789
|
+
else if (effectiveType( art )?.items) {
|
|
758
790
|
// For inferred (e.g. included) calc elements, this error is already emitted at the origin.
|
|
759
791
|
if (!art.$inferred) {
|
|
760
792
|
const isCast = art.type?.$inferred === 'cast';
|
|
@@ -770,7 +802,7 @@ function resolve( model ) {
|
|
|
770
802
|
if (art[prop]?.val) {
|
|
771
803
|
// probably better than a parse error (which is good for DEFAULT vs calc),
|
|
772
804
|
// also appears with parse-cdl:
|
|
773
|
-
error('def-invalid-calc-elem', loc, { '#': prop });
|
|
805
|
+
error( 'def-invalid-calc-elem', loc, { '#': prop } );
|
|
774
806
|
return; // one error is enough
|
|
775
807
|
}
|
|
776
808
|
}
|
|
@@ -782,22 +814,19 @@ function resolve( model ) {
|
|
|
782
814
|
? art.value.args[0]?._artifact
|
|
783
815
|
: art.value._artifact;
|
|
784
816
|
if (elem && art.type) { // has explicit type
|
|
785
|
-
if (art.type._artifact?.elements)
|
|
786
|
-
error('type-cast
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
else if (elem.elements) { // TODO: calc elements
|
|
790
|
-
error('type-cast-structured', [ art.type.location, art ], {},
|
|
791
|
-
'Structured elements can\'t be cast to a different type');
|
|
792
|
-
}
|
|
817
|
+
if (art.type._artifact?.elements)
|
|
818
|
+
error( 'type-invalid-cast', [ art.type.location, art ], { '#': 'to-structure' } );
|
|
819
|
+
else if (elem.elements) // TODO: calc elements
|
|
820
|
+
error( 'type-invalid-cast', [ art.type.location, art ], { '#': 'from-structure' } );
|
|
793
821
|
}
|
|
794
822
|
}
|
|
795
823
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
824
|
+
/**
|
|
825
|
+
* Return type containing the assoc spec (keys, on); note that no
|
|
826
|
+
* propagation/rewrite has been done yet, cyclic dependency must have been
|
|
827
|
+
* checked before!
|
|
828
|
+
*/
|
|
799
829
|
function getAssocSpec( type ) {
|
|
800
|
-
// only to be called without cycles
|
|
801
830
|
let unmanaged = null;
|
|
802
831
|
while (type) {
|
|
803
832
|
if (type.on) // if unmanaged, continue trying to find targetAspect
|
|
@@ -857,6 +886,11 @@ function resolve( model ) {
|
|
|
857
886
|
// (we could downgrade it to a warning if name is equal to unique source element name)
|
|
858
887
|
// TODO: Some helping text mentioning an alias name would be useful
|
|
859
888
|
}
|
|
889
|
+
for (const limit of query.$limit || []) // LIMIT from UNION:
|
|
890
|
+
resolveLimit( limit );
|
|
891
|
+
if (query.limit)
|
|
892
|
+
resolveLimit( query.limit );
|
|
893
|
+
|
|
860
894
|
return;
|
|
861
895
|
|
|
862
896
|
function resolveJoinOn( join ) {
|
|
@@ -864,27 +898,35 @@ function resolve( model ) {
|
|
|
864
898
|
for (const j of join.args)
|
|
865
899
|
resolveJoinOn( j );
|
|
866
900
|
if (join.on)
|
|
867
|
-
resolveExpr( join.on, 'join-on',
|
|
868
|
-
// TODO: check restrictions according to join "query"
|
|
901
|
+
resolveExpr( join.on, 'join-on', join );
|
|
869
902
|
}
|
|
870
903
|
}
|
|
871
904
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
905
|
+
/**
|
|
906
|
+
* Note the strange name resolution (dynamic part) for ORDER BY: the same
|
|
907
|
+
* as for select items if it is an expression, but first look at select
|
|
908
|
+
* item alias (i.e. like `$projection.NAME` if it is a path. If it is an
|
|
909
|
+
* ORDER BY of an UNION, do not allow any dynamic path in an expression,
|
|
910
|
+
* and only allow the elements of the leading query if it is a path.
|
|
911
|
+
*
|
|
912
|
+
* This seems to be similar, but different in SQLite 3.22.0: ORDER BY seems
|
|
913
|
+
* to bind stronger than UNION (see <SQLite>/src/parse.y), and the name
|
|
914
|
+
* resolution seems to use select item aliases from all SELECTs of the
|
|
915
|
+
* UNION (see <SQLite>/test/tkt2822.test).
|
|
916
|
+
*/
|
|
882
917
|
function resolveBy( array, refMode, exprMode ) {
|
|
883
918
|
for (const value of array ) {
|
|
884
919
|
if (value)
|
|
885
920
|
resolveExpr( value, (value.path ? refMode : exprMode), query );
|
|
886
921
|
}
|
|
887
922
|
}
|
|
923
|
+
|
|
924
|
+
function resolveLimit( limit ) {
|
|
925
|
+
if (limit.rows)
|
|
926
|
+
resolveExpr( limit.rows, 'limit-rows', query );
|
|
927
|
+
if (limit.offset)
|
|
928
|
+
resolveExpr( limit.offset, 'limit-offset', query );
|
|
929
|
+
}
|
|
888
930
|
}
|
|
889
931
|
|
|
890
932
|
function resolveTarget( art, obj ) {
|
|
@@ -915,7 +957,7 @@ function resolve( model ) {
|
|
|
915
957
|
{ '#': isComposition ? 'comp' : 'std' }, {
|
|
916
958
|
std: 'An unmanaged association can\'t be defined as type',
|
|
917
959
|
comp: 'An unmanaged composition can\'t be defined as type',
|
|
918
|
-
});
|
|
960
|
+
} );
|
|
919
961
|
// TODO: also warning if inside structure
|
|
920
962
|
}
|
|
921
963
|
else {
|
|
@@ -973,7 +1015,7 @@ function resolve( model ) {
|
|
|
973
1015
|
const serviceKeys = keyElementNames( issue.target.elements );
|
|
974
1016
|
const modelKeys = keyElementNames( modelTarget.elements );
|
|
975
1017
|
if (modelKeys.length !== serviceKeys.length) {
|
|
976
|
-
issue.id = modelKeys.find( id => !serviceKeys.includes( id ));
|
|
1018
|
+
issue.id = modelKeys.find( id => !serviceKeys.includes( id ) );
|
|
977
1019
|
issue['#'] = 'missing';
|
|
978
1020
|
}
|
|
979
1021
|
else if (!modelKeys.every( (id, index) => id === serviceKeys[index] )) {
|
|
@@ -1026,7 +1068,7 @@ function resolve( model ) {
|
|
|
1026
1068
|
}
|
|
1027
1069
|
|
|
1028
1070
|
function addImplicitForeignKeys( art, obj, target ) {
|
|
1029
|
-
obj.foreignKeys = Object.create(null);
|
|
1071
|
+
obj.foreignKeys = Object.create( null );
|
|
1030
1072
|
forEachInOrder( target, 'elements', ( elem, name ) => {
|
|
1031
1073
|
if (elem.key && elem.key.val) {
|
|
1032
1074
|
const { location } = obj.target;
|
|
@@ -1042,14 +1084,14 @@ function resolve( model ) {
|
|
|
1042
1084
|
// the following should be done automatically, since we run resolveRefs after that
|
|
1043
1085
|
setArtifactLink( key.targetElement, elem );
|
|
1044
1086
|
setArtifactLink( key.targetElement.path[0], elem );
|
|
1045
|
-
setLink( key, '_effectiveType', effectiveType(elem) );
|
|
1046
|
-
dependsOn(key, elem, location);
|
|
1087
|
+
setLink( key, '_effectiveType', effectiveType( elem ) );
|
|
1088
|
+
dependsOn( key, elem, location );
|
|
1047
1089
|
// TODO TMP: instead, make managed composition of aspects and unmanaged
|
|
1048
1090
|
// assocs not depend on their `on` condition (empty `_deps` after resolve)
|
|
1049
1091
|
if (art.$inferred !== 'aspect-composition')
|
|
1050
1092
|
dependsOnSilent( art, key );
|
|
1051
1093
|
}
|
|
1052
|
-
});
|
|
1094
|
+
} );
|
|
1053
1095
|
obj.foreignKeys[$inferred] = 'keys';
|
|
1054
1096
|
}
|
|
1055
1097
|
|
|
@@ -1078,7 +1120,7 @@ function resolve( model ) {
|
|
|
1078
1120
|
* for derived association like for `type DerivedT: T`, or exposed ones.
|
|
1079
1121
|
*/
|
|
1080
1122
|
function addForeignKeyNavigations( art, silent = false ) {
|
|
1081
|
-
art.$keysNavigation = Object.create(null);
|
|
1123
|
+
art.$keysNavigation = Object.create( null );
|
|
1082
1124
|
const keys = [];
|
|
1083
1125
|
// Basically sort foreign keys according to length of target element ref.
|
|
1084
1126
|
// This way, we complain about ref to sub element (`elem.sub`) even if it
|
|
@@ -1089,7 +1131,7 @@ function resolve( model ) {
|
|
|
1089
1131
|
const arr = keys[path.length] || (keys[path.length] = []);
|
|
1090
1132
|
arr.push( key );
|
|
1091
1133
|
}
|
|
1092
|
-
});
|
|
1134
|
+
} );
|
|
1093
1135
|
for (const key of keys.flat()) {
|
|
1094
1136
|
let dict = art.$keysNavigation;
|
|
1095
1137
|
const { path } = key.targetElement;
|
|
@@ -1102,7 +1144,7 @@ function resolve( model ) {
|
|
|
1102
1144
|
if (item === last)
|
|
1103
1145
|
setArtifactLink( nav, key );
|
|
1104
1146
|
else
|
|
1105
|
-
nav.$keysNavigation = Object.create(null);
|
|
1147
|
+
nav.$keysNavigation = Object.create( null );
|
|
1106
1148
|
}
|
|
1107
1149
|
else if (item === last || nav._artifact) {
|
|
1108
1150
|
if (silent)
|
|
@@ -1187,7 +1229,7 @@ function resolve( model ) {
|
|
|
1187
1229
|
target: 'The redirected target $(ART) is a complex view',
|
|
1188
1230
|
// eslint-disable-next-line max-len
|
|
1189
1231
|
targetOp: 'The redirected target $(ART) is a complex view with $(KEYWORD)',
|
|
1190
|
-
});
|
|
1232
|
+
} );
|
|
1191
1233
|
break;
|
|
1192
1234
|
}
|
|
1193
1235
|
target = from._artifact;
|
|
@@ -1203,7 +1245,7 @@ function resolve( model ) {
|
|
|
1203
1245
|
let redirected = null;
|
|
1204
1246
|
chain.reverse();
|
|
1205
1247
|
let news = [ { chain, sources: [ target ] } ];
|
|
1206
|
-
const dict = Object.create(null);
|
|
1248
|
+
const dict = Object.create( null );
|
|
1207
1249
|
while (news.length) {
|
|
1208
1250
|
const outer = news;
|
|
1209
1251
|
news = [];
|
|
@@ -1328,7 +1370,7 @@ function resolve( model ) {
|
|
|
1328
1370
|
function resolveParamsAndWhere( step, expected, user, isLast ) {
|
|
1329
1371
|
const alias = (step._navigation?.kind === '$tableAlias') ? step._navigation : null;
|
|
1330
1372
|
const type = alias || effectiveType( step._artifact );
|
|
1331
|
-
const art =
|
|
1373
|
+
const art = type?.target ? type.target._artifact : type;
|
|
1332
1374
|
if (!art)
|
|
1333
1375
|
return;
|
|
1334
1376
|
const entity = art.kind === 'entity' &&
|
|
@@ -1361,8 +1403,8 @@ function resolve( model ) {
|
|
|
1361
1403
|
|
|
1362
1404
|
function resolveParams( dict, art, entity, expected, user, stepLocation ) {
|
|
1363
1405
|
if (!entity || !entity.params) {
|
|
1364
|
-
let first = dict[Object.keys(dict)[0]];
|
|
1365
|
-
if (Array.isArray(first))
|
|
1406
|
+
let first = dict[Object.keys( dict )[0]];
|
|
1407
|
+
if (Array.isArray( first ))
|
|
1366
1408
|
first = first[0];
|
|
1367
1409
|
error( 'expr-unexpected-argument',
|
|
1368
1410
|
[ dict[$location] || dictLocation( dict, first?.name?.location || stepLocation ),
|
|
@@ -1377,7 +1419,7 @@ function resolve( model ) {
|
|
|
1377
1419
|
return;
|
|
1378
1420
|
}
|
|
1379
1421
|
const exp = (expected === 'from') ? 'from-args' : expected;
|
|
1380
|
-
if (Array.isArray(dict)) {
|
|
1422
|
+
if (Array.isArray( dict )) {
|
|
1381
1423
|
const loc = [ dict[0] && dict[0].location || stepLocation, user ];
|
|
1382
1424
|
error( 'expr-expected-named-argument', loc, {},
|
|
1383
1425
|
'Expected named parameters for the entity' );
|
|
@@ -1389,7 +1431,7 @@ function resolve( model ) {
|
|
|
1389
1431
|
for (const name in dict) {
|
|
1390
1432
|
const param = art.params[name];
|
|
1391
1433
|
const arg = dict[name];
|
|
1392
|
-
for (const a of Array.isArray(arg) ? arg : [ arg ]) {
|
|
1434
|
+
for (const a of Array.isArray( arg ) ? arg : [ arg ]) {
|
|
1393
1435
|
setArtifactLink( a.name, param );
|
|
1394
1436
|
if (!param) {
|
|
1395
1437
|
error( 'expr-undefined-param', [ a.name.location, user ], { art, id: name },
|
|
@@ -1401,20 +1443,22 @@ function resolve( model ) {
|
|
|
1401
1443
|
}
|
|
1402
1444
|
}
|
|
1403
1445
|
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1446
|
+
/**
|
|
1447
|
+
* Return condensed info about reference in select item
|
|
1448
|
+
* - tableAlias.elem -> { navigation: navElem, item: path[1], tableAlias }
|
|
1449
|
+
* - sourceElem (in query) -> { navigation: navElem, item: path[0], tableAlias }
|
|
1450
|
+
* - mixinElem -> { navigation: mixinElement, item: path[0] }
|
|
1451
|
+
* - $projection.elem -> also $self.item -> { item: path[1], tableAlias: $self }
|
|
1452
|
+
* - $self -> { item: undefined, tableAlias: $self }
|
|
1453
|
+
* - $parameters.P, :P -> {}
|
|
1454
|
+
* - $now, current_date -> {}
|
|
1455
|
+
* - undef, redef -> {}
|
|
1456
|
+
* With 'navigation': store that navigation._artifact is projected
|
|
1457
|
+
* With 'navigation': rewrite its ON condition
|
|
1458
|
+
* With navigation: Do KEY propagation
|
|
1459
|
+
*
|
|
1460
|
+
* TODO: re-think this function, copied in populate.js and tweak-assocs.js
|
|
1461
|
+
*/
|
|
1418
1462
|
function pathNavigation( ref ) {
|
|
1419
1463
|
// currently, indirectly projectable elements are not included - we might
|
|
1420
1464
|
// keep it this way! If we want them to be included - be aware: cycles
|