@sap/cds-compiler 4.4.4 → 4.6.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 +88 -0
- package/bin/cdsc.js +18 -11
- package/bin/cdsv2m.js +7 -5
- package/doc/CHANGELOG_BETA.md +22 -0
- package/lib/api/main.js +306 -144
- package/lib/api/options.js +18 -6
- package/lib/api/validate.js +1 -1
- package/lib/base/message-registry.js +45 -10
- package/lib/base/messages.js +33 -16
- package/lib/base/model.js +4 -0
- package/lib/base/optionProcessorHelper.js +45 -176
- package/lib/checks/annotationsOData.js +49 -0
- package/lib/checks/elements.js +32 -34
- package/lib/checks/enricher.js +39 -3
- package/lib/checks/validator.js +8 -7
- package/lib/compiler/assert-consistency.js +40 -17
- package/lib/compiler/builtins.js +30 -53
- package/lib/compiler/checks.js +46 -14
- package/lib/compiler/cycle-detector.js +1 -4
- package/lib/compiler/define.js +35 -10
- package/lib/compiler/extend.js +21 -7
- package/lib/compiler/generate.js +3 -0
- package/lib/compiler/populate.js +5 -1
- package/lib/compiler/propagator.js +46 -9
- package/lib/compiler/resolve.js +94 -35
- package/lib/compiler/shared.js +60 -33
- package/lib/compiler/tweak-assocs.js +188 -92
- package/lib/compiler/utils.js +11 -1
- package/lib/edm/annotations/edmJson.js +41 -66
- package/lib/edm/annotations/genericTranslation.js +27 -9
- package/lib/edm/annotations/preprocessAnnotations.js +2 -3
- package/lib/edm/csn2edm.js +28 -11
- package/lib/edm/edmInboundChecks.js +58 -15
- package/lib/edm/edmPreprocessor.js +12 -16
- package/lib/edm/edmUtils.js +5 -2
- package/lib/gen/Dictionary.json +10 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +15 -2
- package/lib/gen/language.tokens +1 -0
- package/lib/gen/languageParser.js +6557 -5618
- package/lib/json/from-csn.js +4 -5
- package/lib/json/to-csn.js +29 -4
- package/lib/language/antlrParser.js +19 -1
- package/lib/language/errorStrategy.js +28 -7
- package/lib/language/genericAntlrParser.js +118 -24
- package/lib/language/textUtils.js +16 -0
- package/lib/main.d.ts +28 -3
- package/lib/main.js +3 -0
- package/lib/model/csnRefs.js +4 -1
- package/lib/model/csnUtils.js +20 -14
- package/lib/model/revealInternalProperties.js +5 -2
- package/lib/optionProcessor.js +23 -22
- package/lib/render/manageConstraints.js +13 -29
- package/lib/render/toCdl.js +47 -26
- package/lib/render/toHdbcds.js +63 -42
- package/lib/render/toRename.js +6 -10
- package/lib/render/toSql.js +71 -117
- package/lib/render/utils/common.js +41 -6
- package/lib/transform/.eslintrc.json +9 -1
- package/lib/transform/addTenantFields.js +228 -0
- package/lib/transform/db/applyTransformations.js +57 -4
- package/lib/transform/db/assertUnique.js +4 -4
- package/lib/transform/db/backlinks.js +13 -1
- package/lib/transform/db/cdsPersistence.js +1 -1
- package/lib/transform/db/expansion.js +24 -3
- package/lib/transform/db/flattening.js +70 -71
- package/lib/transform/db/killAnnotations.js +37 -0
- package/lib/transform/db/rewriteCalculatedElements.js +46 -6
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/draft/db.js +2 -16
- package/lib/transform/draft/odata.js +3 -3
- package/lib/transform/effective/associations.js +3 -5
- package/lib/transform/effective/main.js +6 -9
- package/lib/transform/forOdata.js +26 -55
- package/lib/transform/forRelationalDB.js +38 -18
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/odata/typesExposure.js +14 -5
- package/lib/transform/transformUtils.js +47 -34
- package/lib/transform/translateAssocsToJoins.js +45 -11
- package/lib/transform/universalCsn/coreComputed.js +1 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +7 -6
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const {
|
|
6
6
|
forEachGeneric,
|
|
7
|
+
forEachMember,
|
|
7
8
|
forEachInOrder,
|
|
8
9
|
} = require('../base/model');
|
|
9
10
|
const { dictLocation, weakLocation, weakRefLocation } = require('../base/location');
|
|
@@ -32,6 +33,7 @@ function tweakAssocs( model ) {
|
|
|
32
33
|
info, warning, error,
|
|
33
34
|
} = model.$messageFunctions;
|
|
34
35
|
const {
|
|
36
|
+
resolvePath,
|
|
35
37
|
traverseExpr,
|
|
36
38
|
checkExpr,
|
|
37
39
|
checkOnCondition,
|
|
@@ -62,8 +64,6 @@ function tweakAssocs( model ) {
|
|
|
62
64
|
function rewriteArtifact( art ) {
|
|
63
65
|
// return;
|
|
64
66
|
if (!art.query) {
|
|
65
|
-
// console.log(message( null, art.location, art, {target:art._target},
|
|
66
|
-
// 'Info','RAS').toString())
|
|
67
67
|
rewriteAssociation( art );
|
|
68
68
|
forEachGeneric( art, 'elements', rewriteAssociation );
|
|
69
69
|
}
|
|
@@ -78,6 +78,8 @@ function tweakAssocs( model ) {
|
|
|
78
78
|
traverseQueryPost( art.query, false, ( query ) => {
|
|
79
79
|
forEachGeneric( query, 'elements', rewriteAssociationCheck );
|
|
80
80
|
} );
|
|
81
|
+
|
|
82
|
+
checkForAnnotationRefs( art );
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
// function rewriteView( view ) {
|
|
@@ -122,6 +124,79 @@ function tweakAssocs( model ) {
|
|
|
122
124
|
}
|
|
123
125
|
}
|
|
124
126
|
|
|
127
|
+
function checkForAnnotationRefs( art ) {
|
|
128
|
+
// TODO: no check for type inheritance yet
|
|
129
|
+
const origin = art._origin ||
|
|
130
|
+
art.includes && art.includes[art.includes.length - 1]._artifact;
|
|
131
|
+
// Make sure not to waste time if no inherited annotation has checked refs:
|
|
132
|
+
if (!origin?.$contains?.$annotation)
|
|
133
|
+
return;
|
|
134
|
+
for (const prop in origin) {
|
|
135
|
+
const anno = prop.charAt(0) === '@' && !art[prop] && origin[prop];
|
|
136
|
+
// Remark: to be on the academic safe side, we should consider the
|
|
137
|
+
// annotations which are not propagated, but they never have references as
|
|
138
|
+
// value. So “no” for the moment. We also do not perform these checks in
|
|
139
|
+
// propagator.js, as it should go away in compiler v6.
|
|
140
|
+
if (anno.kind) { // i.e. with values refs
|
|
141
|
+
art[prop] = { ...anno, $inferred: 'prop' };
|
|
142
|
+
setLink( art[prop], '_outer', art );
|
|
143
|
+
const errorRef = checkAnnotationForRefs( art[prop], art );
|
|
144
|
+
if (errorRef) {
|
|
145
|
+
const valid = errorRef.path[errorRef.path.length - 1]._artifact;
|
|
146
|
+
error( 'anno-missing-rewrite', [ weakLocation( art.location ), art ], {
|
|
147
|
+
'#': (valid ? 'unrelated' : 'std'),
|
|
148
|
+
anno: prop,
|
|
149
|
+
art: origin,
|
|
150
|
+
elemref: errorRef,
|
|
151
|
+
} );
|
|
152
|
+
}
|
|
153
|
+
art.$contains ??= {};
|
|
154
|
+
art.$contains.$annotation = true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
forEachMember( art, checkForAnnotationRefs );
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function checkAnnotationForRefs( expr, user ) {
|
|
161
|
+
if (expr.$tokenTexts)
|
|
162
|
+
return traverseExpr( expr, 'annoRewrite', user, checkAnnotationRef );
|
|
163
|
+
if (expr.literal === 'array')
|
|
164
|
+
return expr.val.find( val => checkAnnotationForRefs( val, user ) );
|
|
165
|
+
if (expr.literal !== 'struct')
|
|
166
|
+
return null;
|
|
167
|
+
const struct = Object.values( expr.struct );
|
|
168
|
+
return struct.find( val => checkAnnotationForRefs( val, user ) );
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function checkAnnotationRef( ref, refCtx, user ) {
|
|
172
|
+
const origPath = ref.path;
|
|
173
|
+
if (!origPath[origPath.length - 1]._artifact) // already wrong in original
|
|
174
|
+
return null;
|
|
175
|
+
ref = { ...ref, path: [ ...origPath.map( item => ({ ...item }) ) ] };
|
|
176
|
+
if (!resolvePath( ref, refCtx, user ))
|
|
177
|
+
return ref;
|
|
178
|
+
return ref.path.some( isUnrelated ) && ref;
|
|
179
|
+
|
|
180
|
+
function isUnrelated( item, idx ) {
|
|
181
|
+
let elem = item._artifact;
|
|
182
|
+
const orig = origPath[idx]._artifact;
|
|
183
|
+
// With includes, we allow shadowing: an included element might seem to be
|
|
184
|
+
// unrelated.
|
|
185
|
+
if (elem._main?.includes && elem._main === (user._main || user))
|
|
186
|
+
return false;
|
|
187
|
+
if (!elem._effectiveType) // safety
|
|
188
|
+
return false;
|
|
189
|
+
// With redirections, the originally referred object might not be the same
|
|
190
|
+
// or even the direct _origin
|
|
191
|
+
do {
|
|
192
|
+
if (elem === orig)
|
|
193
|
+
return false;
|
|
194
|
+
elem = elem._origin;
|
|
195
|
+
} while (elem);
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
125
200
|
function rewriteAssociationCheck( element ) {
|
|
126
201
|
const elem = element.items || element; // TODO v5: nested items
|
|
127
202
|
if (elem.elements)
|
|
@@ -287,7 +362,7 @@ function tweakAssocs( model ) {
|
|
|
287
362
|
elem[prop] = { $inferred: 'NULL', val: undefined, location };
|
|
288
363
|
break;
|
|
289
364
|
}
|
|
290
|
-
origin = origin
|
|
365
|
+
origin = getOrigin( origin );
|
|
291
366
|
}
|
|
292
367
|
}
|
|
293
368
|
}
|
|
@@ -303,6 +378,10 @@ function tweakAssocs( model ) {
|
|
|
303
378
|
}
|
|
304
379
|
|
|
305
380
|
function rewriteKeys( elem, assoc ) {
|
|
381
|
+
addConditionFromAssocPublishing( elem, assoc, null );
|
|
382
|
+
if (elem.on)
|
|
383
|
+
return; // foreign keys were transformed into ON-condition
|
|
384
|
+
|
|
306
385
|
// TODO: split this function: create foreign keys without `targetElement`
|
|
307
386
|
// already in Phase 2: redirectImplicitly()
|
|
308
387
|
// console.log(message( null, elem.location, elem, {art:assoc,target:assoc.target},
|
|
@@ -324,8 +403,6 @@ function tweakAssocs( model ) {
|
|
|
324
403
|
} );
|
|
325
404
|
if (elem.foreignKeys) // Possibly no fk was set
|
|
326
405
|
elem.foreignKeys[$inferred] = 'rewrite';
|
|
327
|
-
|
|
328
|
-
addConditionFromAssocPublishing( elem, assoc );
|
|
329
406
|
}
|
|
330
407
|
|
|
331
408
|
// TODO: there is no need to rewrite the on condition of non-leading queries,
|
|
@@ -340,35 +417,32 @@ function tweakAssocs( model ) {
|
|
|
340
417
|
setExpandStatus( elem, 'target' );
|
|
341
418
|
if (elem._parent?.kind === 'element') {
|
|
342
419
|
// managed association as sub element not supported yet
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
420
|
+
// TODO: Only report once for multi-include chains, see
|
|
421
|
+
// Associations/SubElements/UnmanagedInSubElement.err.cds
|
|
422
|
+
error( 'type-unsupported-rewrite', [ elem.location, elem ], { '#': 'sub-element' } );
|
|
346
423
|
return;
|
|
347
424
|
}
|
|
348
425
|
const nav = (elem._main?.query && elem.value)
|
|
349
426
|
? pathNavigation( elem.value ) // redirected source elem or mixin
|
|
350
427
|
: { navigation: assoc }; // redirected user-provided
|
|
351
|
-
|
|
428
|
+
elem.on = copyExpr( assoc.on,
|
|
352
429
|
// replace location in ON except if from mixin element
|
|
353
|
-
|
|
354
|
-
elem.on = cond;
|
|
355
|
-
addConditionFromAssocPublishing( elem, assoc );
|
|
356
|
-
// `cond` still points to the original condition; does not include possible assoc filter
|
|
430
|
+
nav.tableAlias && elem.name.location );
|
|
357
431
|
elem.on.$inferred = 'copy';
|
|
358
432
|
|
|
359
433
|
const { navigation } = nav;
|
|
360
434
|
if (!navigation) // TODO: what about $projection.assoc as myAssoc ?
|
|
361
435
|
return; // should not happen: $projection, $magic, or ref to const
|
|
436
|
+
|
|
362
437
|
// Currently, having an unmanaged association inside a struct is not
|
|
363
438
|
// supported by this function:
|
|
364
439
|
if (navigation !== assoc && navigation._origin !== assoc) { // TODO: re-check
|
|
365
440
|
// For "assoc1.assoc2" and "struct.elem1.assoc2"
|
|
366
441
|
if (elem._redirected !== null) // null = already reported
|
|
367
442
|
error( 'rewrite-not-supported', [ elem.target.location, elem ] );
|
|
368
|
-
return;
|
|
369
443
|
}
|
|
370
|
-
if (!nav.tableAlias || nav.tableAlias.path) {
|
|
371
|
-
traverseExpr(
|
|
444
|
+
else if (!nav.tableAlias || nav.tableAlias.path) {
|
|
445
|
+
traverseExpr( elem.on, 'rewrite-on', elem,
|
|
372
446
|
expr => rewriteExpr( expr, elem, nav.tableAlias ) );
|
|
373
447
|
}
|
|
374
448
|
else {
|
|
@@ -376,6 +450,12 @@ function tweakAssocs( model ) {
|
|
|
376
450
|
error( null, [ elem.value.location, elem ], {},
|
|
377
451
|
'Selecting unmanaged associations from a sub query is not supported' );
|
|
378
452
|
}
|
|
453
|
+
|
|
454
|
+
// filter was copied in original element already
|
|
455
|
+
// TODO: not correct for e.g. composition-of-inline-aspect (#12223)
|
|
456
|
+
if (elem.$inferred !== 'include')
|
|
457
|
+
addConditionFromAssocPublishing( elem, assoc, nav );
|
|
458
|
+
|
|
379
459
|
elem.on.$inferred = 'rewrite';
|
|
380
460
|
}
|
|
381
461
|
|
|
@@ -387,56 +467,67 @@ function tweakAssocs( model ) {
|
|
|
387
467
|
*
|
|
388
468
|
* The added condition (filter) is already rewritten relative to `elem`.
|
|
389
469
|
*/
|
|
390
|
-
function addConditionFromAssocPublishing( elem, assoc ) {
|
|
391
|
-
const publishAssoc = (elem._main?.query
|
|
470
|
+
function addConditionFromAssocPublishing( elem, assoc, nav ) {
|
|
471
|
+
const publishAssoc = (elem._main?.query || elem.$syntax === 'calc') &&
|
|
472
|
+
elem.value?.path?.length > 0;
|
|
392
473
|
if (!publishAssoc)
|
|
393
474
|
return;
|
|
394
475
|
|
|
476
|
+
nav ??= (elem._main?.query && elem.value)
|
|
477
|
+
? pathNavigation( elem.value ) // redirected source elem or mixin
|
|
478
|
+
: { navigation: assoc }; // redirected user-provided
|
|
479
|
+
|
|
395
480
|
const { location } = elem.name;
|
|
396
481
|
const lastStep = elem.value.path[elem.value.path.length - 1];
|
|
482
|
+
if (!lastStep || !lastStep.where)
|
|
483
|
+
return;
|
|
397
484
|
|
|
398
|
-
if (lastStep
|
|
399
|
-
|
|
400
|
-
|
|
485
|
+
if (lastStep.cardinality) {
|
|
486
|
+
elem.cardinality ??= { ...assoc.cardinality };
|
|
487
|
+
elem.cardinality.location = location;
|
|
401
488
|
for (const card of [ 'sourceMin', 'targetMin', 'targetMax' ]) {
|
|
402
489
|
if (lastStep.cardinality[card])
|
|
403
490
|
elem.cardinality[card] = copyExpr( lastStep.cardinality[card], location );
|
|
404
491
|
}
|
|
405
492
|
}
|
|
406
493
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
elem.foreignKeys = undefined;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
if (elem.on) {
|
|
418
|
-
elem.on = {
|
|
419
|
-
op: { val: 'and', location },
|
|
420
|
-
args: [
|
|
421
|
-
// TODO: Get rid of $parens
|
|
422
|
-
{ ...elem.on, $parens: [ assoc.location ] },
|
|
423
|
-
filterToCondition( lastStep, elem ),
|
|
424
|
-
],
|
|
425
|
-
location,
|
|
426
|
-
$inferred: 'copy',
|
|
427
|
-
};
|
|
494
|
+
// If there are foreign keys, transform them into an ON-condition first.
|
|
495
|
+
if (assoc.foreignKeys) {
|
|
496
|
+
const cond = foreignKeysToOnCondition( elem, assoc, nav );
|
|
497
|
+
if (cond) {
|
|
498
|
+
elem.on = cond;
|
|
499
|
+
elem.foreignKeys = undefined;
|
|
428
500
|
}
|
|
429
501
|
}
|
|
502
|
+
|
|
503
|
+
elem.on = {
|
|
504
|
+
op: { val: 'and', location },
|
|
505
|
+
args: [
|
|
506
|
+
// TODO: Get rid of $parens
|
|
507
|
+
{ ...elem.on, $parens: [ assoc.location ] },
|
|
508
|
+
filterToCondition( lastStep, elem, nav ),
|
|
509
|
+
],
|
|
510
|
+
location,
|
|
511
|
+
$inferred: 'copy',
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
elem.$filtered = {
|
|
515
|
+
val: true,
|
|
516
|
+
literal: 'boolean',
|
|
517
|
+
location,
|
|
518
|
+
$inferred: '$generated',
|
|
519
|
+
};
|
|
430
520
|
}
|
|
431
521
|
|
|
432
522
|
/**
|
|
433
|
-
* Transform a filter on `
|
|
434
|
-
* Paths inside the filter are rewritten relative to `
|
|
523
|
+
* Transform a filter on `assocPathStep` into an ON-condition.
|
|
524
|
+
* Paths inside the filter are rewritten relative to `assoc`, so they can be redirected
|
|
525
|
+
* using `rewriteExpr()` later on. `$self` paths remain unchanged.
|
|
435
526
|
*/
|
|
436
|
-
function filterToCondition(
|
|
437
|
-
const cond = copyExpr(
|
|
527
|
+
function filterToCondition( assocPathStep, elem, nav ) {
|
|
528
|
+
const cond = copyExpr( assocPathStep.where );
|
|
438
529
|
// TODO: Get rid of $parens
|
|
439
|
-
cond.$parens = [
|
|
530
|
+
cond.$parens = [ assocPathStep.location ];
|
|
440
531
|
traverseExpr( cond, 'rewrite-filter', elem, (expr) => {
|
|
441
532
|
if (!expr.path || expr.path.length === 0)
|
|
442
533
|
return;
|
|
@@ -450,15 +541,17 @@ function tweakAssocs( model ) {
|
|
|
450
541
|
}
|
|
451
542
|
else if (!root.builtin && root.kind !== 'builtin') {
|
|
452
543
|
expr.path.unshift({
|
|
453
|
-
id:
|
|
544
|
+
id: assocPathStep.id,
|
|
454
545
|
location: elem.name.location,
|
|
455
546
|
});
|
|
456
|
-
setLink( expr.path[0], '_artifact',
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
547
|
+
setLink( expr.path[0], '_artifact', assocPathStep._artifact );
|
|
548
|
+
if (assocPathStep._navigation?.kind === 'mixin') {
|
|
549
|
+
// _navigation link necessary because condition is rewritten
|
|
550
|
+
// inside the same view (needed for mixins).
|
|
551
|
+
setLink( expr.path[0], '_navigation', assocPathStep._navigation );
|
|
552
|
+
}
|
|
553
|
+
// up to here, filter is relative to original association
|
|
554
|
+
rewriteExpr( expr, elem, nav?.tableAlias );
|
|
462
555
|
}
|
|
463
556
|
} );
|
|
464
557
|
|
|
@@ -467,13 +560,12 @@ function tweakAssocs( model ) {
|
|
|
467
560
|
}
|
|
468
561
|
|
|
469
562
|
// Caller must ensure ON-condition correctness via rewriteExpr()!
|
|
470
|
-
function foreignKeysToOnCondition( elem ) {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
throw new CompilerAssertion('rewriting keys to ON-condition: no tableAlias but not inline');
|
|
563
|
+
function foreignKeysToOnCondition( elem, assoc, nav ) {
|
|
564
|
+
if (model.options.testMode && !nav.tableAlias && !elem._pathHead && elem.$syntax !== 'calc')
|
|
565
|
+
throw new CompilerAssertion('rewriting keys to cond: no tableAlias but not inline/calc');
|
|
474
566
|
|
|
475
|
-
if (!nav.tableAlias || elem._parent?.kind === 'element' ||
|
|
476
|
-
(nav && nav.item !== elem.value.path[elem.value.path.length - 1])) {
|
|
567
|
+
if ((!nav.tableAlias && elem.$syntax !== 'calc') || elem._parent?.kind === 'element' ||
|
|
568
|
+
(nav && nav.item && nav.item !== elem.value.path[elem.value.path.length - 1])) {
|
|
477
569
|
// - no nav.tableAlias for mixins or inside inline; mixins can't have managed assocs, though.
|
|
478
570
|
// - _parent is element for expand
|
|
479
571
|
// - nav.item is different for multi-path steps e.g. `sub.assoc`, which is not supported, yet
|
|
@@ -483,38 +575,39 @@ function tweakAssocs( model ) {
|
|
|
483
575
|
}
|
|
484
576
|
|
|
485
577
|
let cond = [];
|
|
486
|
-
forEachInOrder(
|
|
578
|
+
forEachInOrder( assoc, 'foreignKeys', function keyToCond( fKey ) {
|
|
487
579
|
// Format: lhs = rhs
|
|
488
580
|
// assoc.id = assoc_id
|
|
489
|
-
// lhs and rhs look the same
|
|
490
|
-
// rhs is rewritten
|
|
581
|
+
// lhs and rhs look the same but are rewritten differently. We must ensure that
|
|
582
|
+
// the rhs is rewritten to a projected element (or it must remain the assoc's
|
|
583
|
+
// foreign key in case of calc elements).
|
|
491
584
|
const lhs = {
|
|
492
585
|
path: [
|
|
493
|
-
{ id:
|
|
586
|
+
{ id: assoc.name.id, location: elem.name.location },
|
|
494
587
|
...copyExpr( fKey.targetElement.path ),
|
|
495
588
|
],
|
|
496
589
|
location: elem.name.location,
|
|
497
590
|
};
|
|
498
|
-
setLink( lhs.path[0], '_artifact',
|
|
499
|
-
setLink( lhs, '_artifact', lhs.path[lhs.path.length - 1] );
|
|
591
|
+
setLink( lhs.path[0], '_artifact', assoc );
|
|
592
|
+
setLink( lhs, '_artifact', lhs.path[lhs.path.length - 1]._artifact );
|
|
500
593
|
|
|
501
|
-
|
|
502
|
-
prependSelfToPath( lhs.path, elem );
|
|
594
|
+
rewritePath( lhs, lhs.path[0], assoc, elem, elem.value.location ); // different to rhs!
|
|
503
595
|
|
|
504
596
|
const rhs = {
|
|
505
597
|
path: [
|
|
506
598
|
// use origin's name; elem could have alias
|
|
507
|
-
{ id:
|
|
599
|
+
{ id: assoc.name.id, location: elem.name.location },
|
|
508
600
|
...copyExpr( fKey.targetElement.path ),
|
|
509
601
|
],
|
|
510
602
|
location: elem.name.location,
|
|
511
603
|
};
|
|
512
|
-
setLink( rhs.path[0], '_artifact',
|
|
513
|
-
setLink( rhs, '_artifact', rhs.path[rhs.path.length - 1] );
|
|
604
|
+
setLink( rhs.path[0], '_artifact', assoc );
|
|
605
|
+
setLink( rhs, '_artifact', rhs.path[rhs.path.length - 1]._artifact );
|
|
514
606
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
607
|
+
if (elem.$syntax !== 'calc') { // different to lhs!
|
|
608
|
+
const projectedFk = firstProjectionForPath( rhs.path, nav.tableAlias, elem );
|
|
609
|
+
rewritePath( rhs, projectedFk.item, elem, projectedFk.elem, elem.value.location );
|
|
610
|
+
}
|
|
518
611
|
|
|
519
612
|
const fkCond = {
|
|
520
613
|
op: { val: 'ixpr', location: elem.name.location },
|
|
@@ -593,31 +686,34 @@ function tweakAssocs( model ) {
|
|
|
593
686
|
nav.item ? nav.item.location : expr.path[0].location );
|
|
594
687
|
}
|
|
595
688
|
}
|
|
596
|
-
else {
|
|
689
|
+
else { // from ON cond of element that was included (i.e. from included structure)
|
|
597
690
|
const root = expr.path[0]._navigation || expr.path[0]._artifact;
|
|
598
691
|
if (root.builtin || root.kind !== '$self' && root.kind !== 'element')
|
|
599
692
|
return;
|
|
600
693
|
const item = expr.path[root.kind === '$self' ? 1 : 0];
|
|
601
694
|
if (!item)
|
|
602
|
-
return;
|
|
603
|
-
// corresponding elem in including structure
|
|
604
|
-
|
|
695
|
+
return; // just $self
|
|
696
|
+
// corresponding elem in including structure or…
|
|
697
|
+
let elem = (assoc._main.items || assoc._main).elements[item.id];
|
|
698
|
+
if (assoc.$syntax === 'calc' && assoc._origin === elem) {
|
|
699
|
+
// … calc element where "elem" points to the referenced (possibly included)
|
|
700
|
+
// sibling element (association).
|
|
701
|
+
elem = assoc;
|
|
702
|
+
}
|
|
605
703
|
if (!elem)
|
|
606
704
|
return; // See #11755
|
|
607
|
-
if (!(
|
|
608
|
-
elem === item._artifact || // redirection for explicit def
|
|
705
|
+
if (!(elem === item._artifact || // redirection for explicit def
|
|
609
706
|
elem._origin === item._artifact)) {
|
|
610
707
|
const art = assoc._origin;
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
} );
|
|
708
|
+
// eslint-disable-next-line max-len
|
|
709
|
+
warning( 'rewrite-shadowed', [ elem.name.location, elem ], { art: art && effectiveType( art ) }, {
|
|
710
|
+
// eslint-disable-next-line max-len
|
|
711
|
+
std: 'This element is not originally referred to in the ON-condition of association $(ART)',
|
|
712
|
+
// eslint-disable-next-line max-len
|
|
713
|
+
element: 'This element is not originally referred to in the ON-condition of association $(MEMBER) of $(ART)',
|
|
714
|
+
} );
|
|
619
715
|
}
|
|
620
|
-
rewritePath( expr, item, assoc,
|
|
716
|
+
rewritePath( expr, item, assoc, elem, null );
|
|
621
717
|
}
|
|
622
718
|
}
|
|
623
719
|
|
|
@@ -680,9 +776,9 @@ function tweakAssocs( model ) {
|
|
|
680
776
|
|
|
681
777
|
function prependSelfToPath( path, elem ) {
|
|
682
778
|
const root = { id: '$self', location: path[0].location };
|
|
683
|
-
path.unshift( root );
|
|
684
779
|
setLink( root, '_navigation', elem._parent.$tableAliases.$self );
|
|
685
780
|
setArtifactLink( root, elem._parent );
|
|
781
|
+
path.unshift( root );
|
|
686
782
|
}
|
|
687
783
|
|
|
688
784
|
function rewriteItem( elem, item, name, assoc, forKeys ) {
|
|
@@ -719,12 +815,12 @@ function tweakAssocs( model ) {
|
|
|
719
815
|
env = env.target._artifact?._effectiveType;
|
|
720
816
|
elem = setArtifactLink( item, env?.elements?.[name] );
|
|
721
817
|
|
|
722
|
-
if (elem
|
|
818
|
+
if (elem)
|
|
723
819
|
return elem;
|
|
724
820
|
// TODO: better (extra message), TODO: do it
|
|
725
821
|
error( 'query-undefined-element', [ item.location, assoc ],
|
|
726
822
|
{ id: name || item.id, '#': 'redirected' } );
|
|
727
|
-
return
|
|
823
|
+
return null;
|
|
728
824
|
}
|
|
729
825
|
}
|
|
730
826
|
|
package/lib/compiler/utils.js
CHANGED
|
@@ -29,7 +29,8 @@ function pushLink( obj, prop, value ) {
|
|
|
29
29
|
// for annotations:
|
|
30
30
|
|
|
31
31
|
function annotationVal( anno ) {
|
|
32
|
-
|
|
32
|
+
// XSN TODO: set val but no location for anno short form
|
|
33
|
+
return anno && (anno.val === undefined || anno.val);
|
|
33
34
|
}
|
|
34
35
|
function annotationIsFalse( anno ) { // falsy, but not null (unset)
|
|
35
36
|
return anno && (anno.val === false || anno.val === 0 || anno.val === '');
|
|
@@ -90,6 +91,7 @@ function setArtifactLink( obj, value ) {
|
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
function linkToOrigin( origin, name, parent, prop, location, silentDep ) {
|
|
94
|
+
// TODO: should `key` propagation be part of this?
|
|
93
95
|
location ||= weakLocation( origin.name.location ); // not ??=
|
|
94
96
|
const elem = {
|
|
95
97
|
name: { location, id: origin.name.id },
|
|
@@ -153,12 +155,19 @@ function setMemberParent( elem, name, parent, prop ) {
|
|
|
153
155
|
setLink( elem, '_main', parent._main || parent );
|
|
154
156
|
}
|
|
155
157
|
|
|
158
|
+
function createAndLinkCalcDepElement( elem ) {
|
|
159
|
+
const r = { kind: '$calculation' }; // no name, like /items
|
|
160
|
+
elem.$calcDepElement = r;
|
|
161
|
+
setLink( r, '_outer', elem );
|
|
162
|
+
}
|
|
163
|
+
|
|
156
164
|
/**
|
|
157
165
|
* Adds a dependency user -> art with the given location.
|
|
158
166
|
*
|
|
159
167
|
* @param {XSN.Artifact} user
|
|
160
168
|
* @param {XSN.Artifact} art
|
|
161
169
|
* @param {XSN.Location} location
|
|
170
|
+
* @param {XSN.Artifact} [semanticLoc]
|
|
162
171
|
*/
|
|
163
172
|
function dependsOn( user, art, location, semanticLoc = undefined ) {
|
|
164
173
|
while (user._outer && !user.kind)
|
|
@@ -683,6 +692,7 @@ module.exports = {
|
|
|
683
692
|
dependsOn,
|
|
684
693
|
dependsOnSilent,
|
|
685
694
|
setMemberParent,
|
|
695
|
+
createAndLinkCalcDepElement,
|
|
686
696
|
storeExtension,
|
|
687
697
|
withAssociation,
|
|
688
698
|
pathName,
|