@sap/cds-compiler 6.4.6 → 6.5.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 +42 -1156
- package/README.md +1 -10
- package/bin/cdsc.js +1 -1
- package/doc/IncompatibleChanges_v5.md +436 -0
- package/doc/IncompatibleChanges_v6.md +659 -0
- package/doc/Versioning.md +3 -7
- package/lib/api/main.js +1 -0
- package/lib/api/options.js +5 -0
- package/lib/api/validate.js +3 -0
- package/lib/base/message-registry.js +31 -8
- package/lib/base/messages.js +1 -1
- package/lib/base/model.js +3 -2
- package/lib/checks/actionsFunctions.js +6 -3
- package/lib/checks/existsInForbiddenPlaces.js +32 -0
- package/lib/checks/validator.js +2 -0
- package/lib/compiler/assert-consistency.js +3 -5
- package/lib/compiler/checks.js +4 -8
- package/lib/compiler/define.js +330 -558
- package/lib/compiler/extend.js +302 -18
- package/lib/compiler/finalize-parse-cdl.js +2 -10
- package/lib/compiler/generate.js +33 -5
- package/lib/compiler/kick-start.js +8 -7
- package/lib/compiler/populate.js +25 -70
- package/lib/compiler/propagator.js +1 -2
- package/lib/compiler/resolve.js +4 -13
- package/lib/compiler/shared.js +18 -5
- package/lib/compiler/tweak-assocs.js +13 -9
- package/lib/compiler/utils.js +98 -2
- package/lib/compiler/xpr-rewrite.js +2 -1
- package/lib/edm/annotations/edmJson.js +9 -6
- package/lib/edm/annotations/genericTranslation.js +8 -4
- package/lib/edm/csn2edm.js +3 -4
- package/lib/edm/edmInboundChecks.js +1 -2
- package/lib/edm/edmPreprocessor.js +3 -3
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +1 -1
- package/lib/gen/Dictionary.json +16 -1
- package/lib/json/from-csn.js +4 -6
- package/lib/json/to-csn.js +3 -3
- package/lib/model/csnRefs.js +19 -5
- package/lib/model/enrichCsn.js +4 -2
- package/lib/optionProcessor.js +8 -4
- package/lib/render/toSql.js +0 -4
- package/lib/render/utils/sql.js +3 -2
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/assertUnique.js +3 -3
- package/lib/transform/db/assocsToQueries/normalizeFrom.js +33 -0
- package/lib/transform/db/assocsToQueries/transformExists.js +14 -3
- package/lib/transform/db/assocsToQueries/utils.js +1 -1
- package/lib/transform/db/backlinks.js +4 -4
- package/lib/transform/db/cdsPersistence.js +4 -4
- package/lib/transform/db/constraints.js +4 -4
- package/lib/transform/db/expansion.js +5 -5
- package/lib/transform/db/flattening.js +4 -5
- package/lib/transform/db/rewriteCalculatedElements.js +3 -3
- package/lib/transform/db/temporal.js +11 -11
- package/lib/transform/draft/db.js +2 -0
- package/lib/transform/draft/odata.js +5 -7
- package/lib/transform/effective/flattening.js +1 -2
- package/lib/transform/forOdata.js +3 -3
- package/lib/transform/forRelationalDB.js +1 -1
- package/lib/transform/odata/createForeignKeys.js +1 -2
- package/lib/transform/odata/flattening.js +1 -2
- package/lib/transform/odata/toFinalBaseType.js +52 -55
- package/lib/transform/transformUtils.js +3 -4
- package/package.json +1 -1
- package/doc/CHANGELOG_BETA.md +0 -464
- package/doc/CHANGELOG_DEPRECATED.md +0 -235
package/lib/compiler/define.js
CHANGED
|
@@ -87,7 +87,8 @@
|
|
|
87
87
|
// --------- TODO: end in extra markdown document -------------------------------
|
|
88
88
|
|
|
89
89
|
// Sub phase 1 (addXYZ) - only for main artifacts
|
|
90
|
-
// - set _block links for main definitions, vocabulary and
|
|
90
|
+
// - set _block links for main definitions, vocabulary, extensions and
|
|
91
|
+
// annotations on those
|
|
91
92
|
// - store definitions (including context extensions), NO duplicate check
|
|
92
93
|
// - artifact name check
|
|
93
94
|
// - Note: the only allow name resolving is resolveUncheckedPath(),
|
|
@@ -132,9 +133,13 @@ const {
|
|
|
132
133
|
const { kindProperties, dictKinds } = require('./base');
|
|
133
134
|
const {
|
|
134
135
|
setLink,
|
|
136
|
+
initItemsLinks,
|
|
135
137
|
setMemberParent,
|
|
136
138
|
createAndLinkCalcDepElement,
|
|
137
|
-
|
|
139
|
+
initExprAnnoBlock,
|
|
140
|
+
initDollarSelf,
|
|
141
|
+
initDollarParameters,
|
|
142
|
+
initBoundSelfParam,
|
|
138
143
|
dependsOnSilent,
|
|
139
144
|
pathName,
|
|
140
145
|
targetCantBeAspect,
|
|
@@ -172,13 +177,12 @@ function define( model ) {
|
|
|
172
177
|
Object.assign( model.$functions, {
|
|
173
178
|
shuffleDict,
|
|
174
179
|
shuffleArray,
|
|
175
|
-
|
|
176
|
-
initMembers,
|
|
180
|
+
initMainArtifact,
|
|
181
|
+
initMembers, // for finalize-parser-cdl.js
|
|
182
|
+
targetIsTargetAspect,
|
|
183
|
+
checkRedefinition,
|
|
177
184
|
initSelectItems,
|
|
178
185
|
} );
|
|
179
|
-
|
|
180
|
-
let boundSelfParamType = true; // special `$self` for binding param must still be initialised
|
|
181
|
-
|
|
182
186
|
return doDefine();
|
|
183
187
|
|
|
184
188
|
/**
|
|
@@ -204,16 +208,18 @@ function define( model ) {
|
|
|
204
208
|
const sourceNames = shuffleArray( Object.keys( model.sources ) );
|
|
205
209
|
for (const name of sourceNames)
|
|
206
210
|
addSource( model.sources[name] );
|
|
211
|
+
|
|
212
|
+
// Phase 2:
|
|
207
213
|
for (const name of sourceNames)
|
|
208
214
|
initNamespaceAndUsing( model.sources[name] );
|
|
209
|
-
dictForEach( model.definitions,
|
|
215
|
+
dictForEach( model.definitions, initMainArtifact );
|
|
210
216
|
dictForEach( model.vocabularies, initVocabulary );
|
|
211
217
|
dictForEach( model.$collectedExtensions, e => e._extensions.forEach( initExtension ) );
|
|
212
218
|
|
|
213
|
-
addI18nBlocks();
|
|
219
|
+
addI18nBlocks(); // TODO: part of extend.js?
|
|
214
220
|
|
|
215
221
|
const { $self } = model.definitions;
|
|
216
|
-
if ($self) {
|
|
222
|
+
if ($self && $self.kind !== 'namespace') { // TODO v7: non-config error
|
|
217
223
|
message( 'name-deprecated-$self', [ $self.name.location, $self ], { name: '$self' },
|
|
218
224
|
'Do not use $(NAME) as name for an artifact definition' );
|
|
219
225
|
}
|
|
@@ -234,7 +240,7 @@ function define( model ) {
|
|
|
234
240
|
|
|
235
241
|
let namespace = src.namespace?.name;
|
|
236
242
|
let prefix = '';
|
|
237
|
-
if (namespace?.path
|
|
243
|
+
if (namespace?.path) {
|
|
238
244
|
namespace.id = pathName( namespace.path );
|
|
239
245
|
prefix = `${ namespace.id }.`;
|
|
240
246
|
}
|
|
@@ -242,12 +248,12 @@ function define( model ) {
|
|
|
242
248
|
error( 'reserved-namespace-cds', [ src.namespace.name.location, src.namespace.name ],
|
|
243
249
|
{ name: 'cds' },
|
|
244
250
|
'The namespace $(NAME) is reserved for CDS builtins' );
|
|
245
|
-
namespace = null;
|
|
251
|
+
namespace = null; // TODO: reconsider
|
|
246
252
|
}
|
|
247
253
|
if (src.$frontend !== 'json') { // CDL input
|
|
248
254
|
// TODO: set _block to builtin
|
|
249
255
|
if (src.artifacts) {
|
|
250
|
-
//
|
|
256
|
+
// addBlockArtifact() adds usings to src.artifacts: shuffleDict must be assigned first
|
|
251
257
|
src.artifacts = shuffleDict( src.artifacts );
|
|
252
258
|
addPathPrefixes( src.artifacts, prefix ); // before addUsing
|
|
253
259
|
}
|
|
@@ -258,14 +264,14 @@ function define( model ) {
|
|
|
258
264
|
shuffleArray( src.usings ).forEach( u => addUsing( u, src ) );
|
|
259
265
|
if (namespace?.id) // successfully set a full name for namespace
|
|
260
266
|
addNamespace( namespace, src );
|
|
261
|
-
if (src.artifacts) { //
|
|
267
|
+
if (src.artifacts) { // addBlockArtifact needs usings for context extensions
|
|
262
268
|
src.artifacts = shuffleDict( src.artifacts );
|
|
263
|
-
dictForEach( src.artifacts, a =>
|
|
269
|
+
dictForEach( src.artifacts, a => addBlockArtifact( a, src, prefix ) );
|
|
264
270
|
}
|
|
265
271
|
}
|
|
266
272
|
else if (src.definitions) { // CSN input
|
|
267
273
|
prefix = ''; // also for addVocabulary() below
|
|
268
|
-
dictForEach( shuffleDict( src.definitions ), def =>
|
|
274
|
+
dictForEach( shuffleDict( src.definitions ), def => addMainArtifact( def, src, prefix ) );
|
|
269
275
|
}
|
|
270
276
|
if (src.vocabularies) {
|
|
271
277
|
if (!model.vocabularies)
|
|
@@ -277,11 +283,13 @@ function define( model ) {
|
|
|
277
283
|
}
|
|
278
284
|
}
|
|
279
285
|
|
|
280
|
-
function
|
|
286
|
+
function addMainArtifact( art, block, prefix ) {
|
|
287
|
+
setLink( art, '_block', block );
|
|
288
|
+
initExprAnnoBlock( art, block );
|
|
281
289
|
art.name.id ??= prefix + pathName( art.name.path );
|
|
282
290
|
const absolute = art.name.id;
|
|
283
291
|
// TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
|
|
284
|
-
if (
|
|
292
|
+
if (isInReservedNamespace( absolute )) {
|
|
285
293
|
error( 'reserved-namespace-cds', [ art.name.location, art ], { name: 'cds' },
|
|
286
294
|
'The namespace $(NAME) is reserved for CDS builtins' );
|
|
287
295
|
const builtin = model.definitions[absolute];
|
|
@@ -297,8 +305,6 @@ function define( model ) {
|
|
|
297
305
|
}
|
|
298
306
|
return;
|
|
299
307
|
}
|
|
300
|
-
setLink( art, '_block', block );
|
|
301
|
-
initExprAnnoBlock( art, block );
|
|
302
308
|
// dictAdd might set $duplicates
|
|
303
309
|
dictAdd( model.definitions, absolute, art );
|
|
304
310
|
}
|
|
@@ -345,7 +351,7 @@ function define( model ) {
|
|
|
345
351
|
return;
|
|
346
352
|
}
|
|
347
353
|
const path = decl.extern?.path;
|
|
348
|
-
if (!path ||
|
|
354
|
+
if (!path || !path[0]) // syntax error
|
|
349
355
|
return;
|
|
350
356
|
decl.extern.id = pathName( path );
|
|
351
357
|
if (!decl.name)
|
|
@@ -354,7 +360,7 @@ function define( model ) {
|
|
|
354
360
|
// TODO: check name: no "."
|
|
355
361
|
const found = src.artifacts[name];
|
|
356
362
|
// a real `using` declaration is “nicer” than a compiler-generated one:
|
|
357
|
-
if (found
|
|
363
|
+
if (found?.$inferred === 'path-prefix' && found.extern.id === decl.extern.id)
|
|
358
364
|
src.artifacts[name] = decl;
|
|
359
365
|
else
|
|
360
366
|
dictAddArray( src.artifacts, name, decl );
|
|
@@ -377,15 +383,15 @@ function define( model ) {
|
|
|
377
383
|
$inferred: 'namespace',
|
|
378
384
|
};
|
|
379
385
|
}
|
|
380
|
-
function
|
|
386
|
+
function addBlockArtifact( art, block, prefix ) {
|
|
381
387
|
if (art.kind === 'using')
|
|
382
388
|
return;
|
|
383
|
-
|
|
389
|
+
addMainArtifact( art, block, prefix );
|
|
384
390
|
if (art.artifacts) {
|
|
385
391
|
const p = `${ art.name.id }.`;
|
|
386
392
|
// path prefixes (usings) must be added before extensions in artifacts:
|
|
387
393
|
addPathPrefixes( art.artifacts, p );
|
|
388
|
-
dictForEach( art.artifacts, a =>
|
|
394
|
+
dictForEach( art.artifacts, a => addBlockArtifact( a, art, p ) );
|
|
389
395
|
}
|
|
390
396
|
if (art.extensions) { // requires using to be known!
|
|
391
397
|
art.extensions.forEach( e => e.name && addExtension( e, art ) );
|
|
@@ -418,55 +424,8 @@ function define( model ) {
|
|
|
418
424
|
// Set block number for debugging (--raw-output):
|
|
419
425
|
// eslint-disable-next-line no-multi-assign
|
|
420
426
|
ext.$effectiveSeqNo = model.$blocks[absolute] = (model.$blocks[absolute] || 0) + 1;
|
|
421
|
-
// add "namespace" for the case that ext.artifacts is empty (TODO: later)
|
|
422
|
-
// now add all definitions in ext.artifacts:
|
|
423
427
|
const prefix = `${ absolute }.`;
|
|
424
|
-
dictForEach( ext.artifacts, a =>
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
function initExtension( parent ) {
|
|
428
|
-
forEachMember( parent, function initExtensionMember( sub, name, prop ) {
|
|
429
|
-
if (sub.kind !== 'extend' && sub.kind !== 'annotate')
|
|
430
|
-
return; // for defs inside, set somewhere else - TODO: rethink
|
|
431
|
-
if (prop === 'params' && name === '') // RETURNS
|
|
432
|
-
sub.name = { id: '', location: sub.location };
|
|
433
|
-
setLink( sub, '_block', parent._block );
|
|
434
|
-
initExprAnnoBlock( sub, parent._block );
|
|
435
|
-
setLink( sub, '_parent', parent );
|
|
436
|
-
setLink( sub, '_main', parent._main || parent );
|
|
437
|
-
initExtension( sub );
|
|
438
|
-
} );
|
|
439
|
-
if (parent.kind !== 'extend')
|
|
440
|
-
return;
|
|
441
|
-
// TODO: sub queries? expand/inline?
|
|
442
|
-
parent.columns?.forEach( c => setLink( c, '_block', parent._block ) );
|
|
443
|
-
if (parent.scale && !parent.precision) {
|
|
444
|
-
// TODO: where could we store the location of the name?
|
|
445
|
-
error( 'syntax-missing-type-property', [ parent.scale.location ],
|
|
446
|
-
{ prop: 'scale', otherprop: 'precision' },
|
|
447
|
-
'Type extension with property $(PROP) must also have property $(OTHERPROP)' );
|
|
448
|
-
parent.scale = undefined; // no consequential error
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
function initExprAnnoBlock( art, block ) {
|
|
453
|
-
// remark: `art` could also be the extension
|
|
454
|
-
for (const prop in art) {
|
|
455
|
-
if (prop.charAt(0) !== '@')
|
|
456
|
-
continue;
|
|
457
|
-
const anno = art[prop];
|
|
458
|
-
// _block links needed for `cast( … as Type )`, `[ ..., cast( … as Type ) ]`
|
|
459
|
-
if (anno.literal === 'array') {
|
|
460
|
-
anno.kind = '$annotation';
|
|
461
|
-
for (const item of anno.val)
|
|
462
|
-
setLink( item, '_block', block );
|
|
463
|
-
}
|
|
464
|
-
else if (anno.$tokenTexts) {
|
|
465
|
-
// remark: it wouldn't hurt to set it always...
|
|
466
|
-
anno.kind = '$annotation';
|
|
467
|
-
setLink( anno, '_block', block );
|
|
468
|
-
}
|
|
469
|
-
}
|
|
428
|
+
dictForEach( ext.artifacts, a => addBlockArtifact( a, ext, prefix ) );
|
|
470
429
|
}
|
|
471
430
|
|
|
472
431
|
function addVocabulary( vocab, block, prefix ) {
|
|
@@ -476,6 +435,8 @@ function define( model ) {
|
|
|
476
435
|
dictAdd( model.vocabularies, name.id, vocab );
|
|
477
436
|
}
|
|
478
437
|
|
|
438
|
+
// I18n code (eventually part of extend.js) -----------------------------------
|
|
439
|
+
|
|
479
440
|
/**
|
|
480
441
|
* Add (optional) translations into the XSN model.
|
|
481
442
|
*/
|
|
@@ -522,32 +483,9 @@ function define( model ) {
|
|
|
522
483
|
}
|
|
523
484
|
|
|
524
485
|
// Phase 2 ("init"), top-level & main -----------------------------------------
|
|
525
|
-
// Functions called from top-level: initNamespaceAndUsing(),
|
|
486
|
+
// Functions called from top-level: initNamespaceAndUsing(), initMainArtifact(),
|
|
526
487
|
// initVocabulary(), initExtension()
|
|
527
488
|
|
|
528
|
-
// TODO: message ids
|
|
529
|
-
function checkRedefinition( art ) {
|
|
530
|
-
if (!art.$duplicates || !art.name.id ||
|
|
531
|
-
art.$errorReported === 'syntax-duplicate-extend')
|
|
532
|
-
return;
|
|
533
|
-
if (art.kind === 'annotate' || art.kind === 'extend')
|
|
534
|
-
return; // extensions are merged into a super-annotate; $duplicates are only kept for LSP
|
|
535
|
-
if (art._main) {
|
|
536
|
-
error( 'duplicate-definition', [ art.name.location, art ], {
|
|
537
|
-
name: art.name.id,
|
|
538
|
-
'#': kindProperties[art.kind].normalized || art.kind,
|
|
539
|
-
} );
|
|
540
|
-
}
|
|
541
|
-
else if (!art.builtin) {
|
|
542
|
-
// TODO: better messages with definitions with the same name as builtin,
|
|
543
|
-
// especially if there is just one
|
|
544
|
-
error( 'duplicate-definition', [ art.name.location, art ], {
|
|
545
|
-
name: art.name.id,
|
|
546
|
-
'#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
|
|
547
|
-
} );
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
489
|
function initNamespaceAndUsing( src ) {
|
|
552
490
|
if (src.$frontend && src.$frontend !== 'cdl')
|
|
553
491
|
return;
|
|
@@ -586,13 +524,12 @@ function define( model ) {
|
|
|
586
524
|
}
|
|
587
525
|
}
|
|
588
526
|
|
|
589
|
-
function
|
|
590
|
-
if (!reInit)
|
|
527
|
+
function initMainArtifact( art, reInit = false ) {
|
|
528
|
+
if (!reInit) // not for auto-exposed entity
|
|
591
529
|
initArtifactParentLink( art, model.definitions );
|
|
592
|
-
const block = art._block;
|
|
593
530
|
checkRedefinition( art );
|
|
594
531
|
initDollarSelf( art ); // $self
|
|
595
|
-
initMembers( art
|
|
532
|
+
initMembers( art );
|
|
596
533
|
if (art.params)
|
|
597
534
|
initDollarParameters( art ); // $parameters
|
|
598
535
|
if (art.query) {
|
|
@@ -601,9 +538,105 @@ function define( model ) {
|
|
|
601
538
|
}
|
|
602
539
|
}
|
|
603
540
|
|
|
541
|
+
function initVocabulary( art ) {
|
|
542
|
+
initArtifactParentLink( art, model.vocabularies );
|
|
543
|
+
checkRedefinition( art );
|
|
544
|
+
initMembers( art );
|
|
545
|
+
|
|
546
|
+
if (art.query) {
|
|
547
|
+
initArtifactQuery( art );
|
|
548
|
+
error( 'def-unsupported-projection', [ art.location, art ], null,
|
|
549
|
+
'Projections for annotation definitions are not supported' );
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function initExtension( parent ) {
|
|
554
|
+
// TODO: re-think
|
|
555
|
+
forEachMember( parent, function initExtensionMember( sub, name, prop ) {
|
|
556
|
+
if (sub.kind !== 'extend' && sub.kind !== 'annotate')
|
|
557
|
+
return; // for defs inside, set somewhere else - TODO: rethink
|
|
558
|
+
if (prop === 'params' && name === '') // RETURNS
|
|
559
|
+
sub.name = { id: '', location: sub.location };
|
|
560
|
+
setLink( sub, '_block', parent._block );
|
|
561
|
+
initExprAnnoBlock( sub, parent._block );
|
|
562
|
+
setLink( sub, '_parent', parent );
|
|
563
|
+
setLink( sub, '_main', parent._main || parent );
|
|
564
|
+
initExtension( sub );
|
|
565
|
+
} );
|
|
566
|
+
if (parent.kind !== 'extend')
|
|
567
|
+
return;
|
|
568
|
+
// TODO: sub queries? expand/inline?
|
|
569
|
+
parent.columns?.forEach( c => setLink( c, '_block', parent._block ) );
|
|
570
|
+
if (parent.scale && !parent.precision) {
|
|
571
|
+
// TODO: where could we store the location of the name?
|
|
572
|
+
error( 'syntax-missing-type-property', [ parent.scale.location ],
|
|
573
|
+
{ prop: 'scale', otherprop: 'precision' },
|
|
574
|
+
'Type extension with property $(PROP) must also have property $(OTHERPROP)' );
|
|
575
|
+
parent.scale = undefined; // no consequential error
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Make the `_parent` pointer of artifact `A.B.C` point to `A.B`. If no `A.B`
|
|
581
|
+
* exists in XSN.definitions, create an artifact with kind `namespace` (better
|
|
582
|
+
* would be `$gap`). Add `A.B.C` to the `_subArtifacts` dictionary of `A.B`.
|
|
583
|
+
* Do recursively if necessary, `_parent` of `A.B` is `A` in this example.
|
|
584
|
+
*/
|
|
585
|
+
function initArtifactParentLink( art, definitions, path, pathIndex ) {
|
|
586
|
+
setLink( art, '_parent', null );
|
|
587
|
+
const { id } = art.name;
|
|
588
|
+
const dot = id.lastIndexOf( '.' );
|
|
589
|
+
if (dot < 0)
|
|
590
|
+
return;
|
|
591
|
+
const prefix = id.substring( 0, dot );
|
|
592
|
+
let parent = definitions[prefix];
|
|
593
|
+
if (!parent) {
|
|
594
|
+
path ??= art.name.path;
|
|
595
|
+
pathIndex ??= path?.length - 1;
|
|
596
|
+
const pathItemOrName = (path && pathIndex) ? path[--pathIndex] : art.name;
|
|
597
|
+
const location = weakLocation( pathItemOrName.location );
|
|
598
|
+
parent = { kind: 'namespace', name: { id: prefix, location }, location };
|
|
599
|
+
definitions[prefix] = parent;
|
|
600
|
+
initArtifactParentLink( parent, definitions, path, pathIndex );
|
|
601
|
+
}
|
|
602
|
+
setLink( art, '_parent', parent );
|
|
603
|
+
if (!parent._subArtifacts)
|
|
604
|
+
setLink( parent, '_subArtifacts', Object.create( null ) );
|
|
605
|
+
if (art.$duplicates !== true) // no redef or "first def"
|
|
606
|
+
parent._subArtifacts[id.substring( dot + 1 )] = art; // not dictAdd()
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// TODO: message ids
|
|
610
|
+
// Called by initMainArtifact() and initVocabulary() and for members
|
|
611
|
+
function checkRedefinition( art ) {
|
|
612
|
+
if (art.kind === 'annotate' || art.kind === 'extend') // move this check to call in extend.js?
|
|
613
|
+
return; // extensions are merged into a super-annotate; $duplicates are only kept for LSP
|
|
614
|
+
if (!art.$duplicates || !art.name.id ||
|
|
615
|
+
art.$errorReported === 'syntax-duplicate-extend')
|
|
616
|
+
return;
|
|
617
|
+
if (art._main) {
|
|
618
|
+
error( 'duplicate-definition', [ art.name.location, art ], {
|
|
619
|
+
name: art.name.id,
|
|
620
|
+
'#': kindProperties[art.kind].normalized || art.kind,
|
|
621
|
+
} );
|
|
622
|
+
}
|
|
623
|
+
else if (!art.builtin) {
|
|
624
|
+
// TODO: better messages with definitions with the same name as builtin,
|
|
625
|
+
// especially if there is just one
|
|
626
|
+
error( 'duplicate-definition', [ art.name.location, art ], {
|
|
627
|
+
name: art.name.id,
|
|
628
|
+
'#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
|
|
629
|
+
} );
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Phase 2, init queries ------------------------------------------------------
|
|
634
|
+
|
|
604
635
|
/**
|
|
605
636
|
* Restrict the query of `art` to only simple projections, i.e. those without 'group by', etc.
|
|
606
637
|
*
|
|
638
|
+
* TODO v8 and for `type`: do in parsers
|
|
639
|
+
*
|
|
607
640
|
* @param {XSN.Artifact} art
|
|
608
641
|
*/
|
|
609
642
|
function restrictToSimpleProjection( art ) {
|
|
@@ -659,88 +692,6 @@ function define( model ) {
|
|
|
659
692
|
}
|
|
660
693
|
}
|
|
661
694
|
|
|
662
|
-
function initVocabulary( art ) {
|
|
663
|
-
initArtifactParentLink( art, model.vocabularies );
|
|
664
|
-
checkRedefinition( art );
|
|
665
|
-
const block = art._block;
|
|
666
|
-
initMembers( art, art, block );
|
|
667
|
-
|
|
668
|
-
if (art.query) {
|
|
669
|
-
initArtifactQuery( art );
|
|
670
|
-
error( 'def-unsupported-projection', [ art.location, art ], null,
|
|
671
|
-
'Projections for annotation definitions are not supported' );
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
function initArtifactParentLink( art, definitions, path, pathIndex ) {
|
|
676
|
-
setLink( art, '_parent', null );
|
|
677
|
-
const { id } = art.name;
|
|
678
|
-
const dot = id.lastIndexOf( '.' );
|
|
679
|
-
if (dot < 0)
|
|
680
|
-
return;
|
|
681
|
-
const prefix = id.substring( 0, dot );
|
|
682
|
-
let parent = definitions[prefix];
|
|
683
|
-
if (!parent) {
|
|
684
|
-
path ??= art.name.path;
|
|
685
|
-
pathIndex ??= path?.length - 1;
|
|
686
|
-
const pathItemOrName = (path && pathIndex) ? path[--pathIndex] : art.name;
|
|
687
|
-
const location = weakLocation( pathItemOrName.location );
|
|
688
|
-
parent = { kind: 'namespace', name: { id: prefix, location }, location };
|
|
689
|
-
definitions[prefix] = parent;
|
|
690
|
-
initArtifactParentLink( parent, definitions, path, pathIndex );
|
|
691
|
-
}
|
|
692
|
-
setLink( art, '_parent', parent );
|
|
693
|
-
if (!parent._subArtifacts)
|
|
694
|
-
setLink( parent, '_subArtifacts', Object.create( null ) );
|
|
695
|
-
if (art.$duplicates !== true) // no redef or "first def"
|
|
696
|
-
parent._subArtifacts[id.substring( dot + 1 )] = art; // not dictAdd()
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
// Init special things: -------------------------------------------------------
|
|
700
|
-
|
|
701
|
-
function initDollarSelf( art ) {
|
|
702
|
-
// TODO: use setMemberParent() ?
|
|
703
|
-
const self = {
|
|
704
|
-
name: { id: '$self', location: art.location },
|
|
705
|
-
kind: '$self',
|
|
706
|
-
location: art.location,
|
|
707
|
-
};
|
|
708
|
-
setLink( self, '_parent', art );
|
|
709
|
-
setLink( self, '_main', art ); // used on main artifact
|
|
710
|
-
setLink( self, '_origin', art );
|
|
711
|
-
art.$tableAliases = Object.create( null );
|
|
712
|
-
art.$tableAliases.$self = self;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
function initDollarParameters( art ) {
|
|
716
|
-
// TODO: use setMemberParent() ?
|
|
717
|
-
const parameters = {
|
|
718
|
-
name: { id: '$parameters' },
|
|
719
|
-
kind: '$parameters',
|
|
720
|
-
location: art.location,
|
|
721
|
-
deprecated: true, // hide in code completion
|
|
722
|
-
};
|
|
723
|
-
setLink( parameters, '_parent', art );
|
|
724
|
-
setLink( parameters, '_main', art );
|
|
725
|
-
// Search for :const after :param. If there will be a possibility in the
|
|
726
|
-
// future that we can extend <query>.columns, we must be sure to use
|
|
727
|
-
// _block of that new column after :param (or just allow $parameters there).
|
|
728
|
-
setLink( parameters, '_block', art._block );
|
|
729
|
-
if (art.params) {
|
|
730
|
-
parameters.elements = art.params;
|
|
731
|
-
parameters.$tableAliases = art.params; // TODO: find better name - $lexical?
|
|
732
|
-
}
|
|
733
|
-
art.$tableAliases.$parameters = parameters;
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
// From here til EOF, reexamine code ---------------------------------------
|
|
737
|
-
// See populate:
|
|
738
|
-
// - userQuery() or _query property?
|
|
739
|
-
// - initFromColumns()
|
|
740
|
-
// - ensureColumnName()
|
|
741
|
-
|
|
742
|
-
// Init queries: --------------------------------------------------------------
|
|
743
|
-
|
|
744
695
|
// art is:
|
|
745
696
|
// - entity/event/type for top-level queries (including UNION args)
|
|
746
697
|
// - $tableAlias for sub query in FROM - TODO: what about UNION there?
|
|
@@ -749,7 +700,7 @@ function define( model ) {
|
|
|
749
700
|
if (!query) // parse error
|
|
750
701
|
return query;
|
|
751
702
|
if (query.from) { // select
|
|
752
|
-
initQuery();
|
|
703
|
+
initQuery( query, art );
|
|
753
704
|
initTableExpression( query.from, query, [] );
|
|
754
705
|
if (query.mixin)
|
|
755
706
|
initMixins( query, art );
|
|
@@ -794,21 +745,21 @@ function define( model ) {
|
|
|
794
745
|
return undefined;
|
|
795
746
|
}
|
|
796
747
|
return query._leadingQuery || query;
|
|
748
|
+
}
|
|
797
749
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
}
|
|
750
|
+
function initQuery( query, art ) {
|
|
751
|
+
setLink( query, '_block', art._block );
|
|
752
|
+
const main = art._main || art;
|
|
753
|
+
setLink( query, '_$next',
|
|
754
|
+
(art.kind === '$tableAlias' ? art._parent._$next : art ) );
|
|
755
|
+
query.kind = 'select';
|
|
756
|
+
query.name = { location: query.location, id: main.$queries.length + 1 };
|
|
757
|
+
setMemberParent( query, null, main );
|
|
758
|
+
// console.log(art.kind,art.name,query.name,query._$next.name)
|
|
759
|
+
main.$queries.push( query );
|
|
760
|
+
setLink( query, '_parent', art ); // _parent should point to alias/main/query
|
|
761
|
+
query.$tableAliases = Object.create( null ); // table aliases and mixin definitions
|
|
762
|
+
dependsOnSilent( main, query );
|
|
812
763
|
}
|
|
813
764
|
|
|
814
765
|
// table is table expression in FROM, becomes an alias
|
|
@@ -816,7 +767,7 @@ function define( model ) {
|
|
|
816
767
|
if (!table) // parse error
|
|
817
768
|
return;
|
|
818
769
|
if (table.path) { // path in FROM
|
|
819
|
-
if (!table.path.length
|
|
770
|
+
if (!table.path.length)
|
|
820
771
|
// parse error (e.g. final ',' in FROM), projection on <eof>
|
|
821
772
|
return;
|
|
822
773
|
if (!table.name) {
|
|
@@ -826,7 +777,7 @@ function define( model ) {
|
|
|
826
777
|
// TODO: if we have too much time, we can calculate the real location with '.'
|
|
827
778
|
table.name = { $inferred: 'as', id, location: last.location };
|
|
828
779
|
}
|
|
829
|
-
addAsAlias();
|
|
780
|
+
addAsAlias( table, query, joinParents );
|
|
830
781
|
// _origin is set when we resolve the ref
|
|
831
782
|
if (query._parent.kind !== 'select')
|
|
832
783
|
query._main._from.push( table ); // store tabref if outside "real" subquery
|
|
@@ -838,7 +789,7 @@ function define( model ) {
|
|
|
838
789
|
const id = `$_select_${ query._main.$queries.length + 1 }__`;
|
|
839
790
|
table.name = { id, location: table.location, $inferred: '$internal' };
|
|
840
791
|
}
|
|
841
|
-
addAsAlias();
|
|
792
|
+
addAsAlias( table, query, joinParents );
|
|
842
793
|
// Store _origin to leading query of table.query for name resolution
|
|
843
794
|
setLink( table, '_origin', initQueryExpression( table.query, table ) );
|
|
844
795
|
}
|
|
@@ -878,37 +829,36 @@ function define( model ) {
|
|
|
878
829
|
// TODO: probably set this to query if we switch to name restriction in JOIN
|
|
879
830
|
}
|
|
880
831
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
}
|
|
895
|
-
else {
|
|
896
|
-
error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
|
|
897
|
-
}
|
|
898
|
-
} );
|
|
899
|
-
// also add to JOIN nodes for name restrictions:
|
|
900
|
-
for (const p of joinParents) {
|
|
901
|
-
// for JOIN alias restriction, we cannot use $duplicates, as it is
|
|
902
|
-
// already used for duplicate aliases of queries:
|
|
903
|
-
dictAddArray( p.$tableAliases, table.name.id, table );
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
function addAsAlias( table, query, joinParents ) {
|
|
835
|
+
table.kind = '$tableAlias';
|
|
836
|
+
setLink( table, '_block', query._block );
|
|
837
|
+
setMemberParent( table, table.name.id, query );
|
|
838
|
+
dictAdd( query.$tableAliases, table.name.id, table, ( name, loc, tableAlias ) => {
|
|
839
|
+
if (tableAlias.name.$inferred === '$internal') {
|
|
840
|
+
const semanticLoc = tableAlias.query?.name ? tableAlias.query : tableAlias;
|
|
841
|
+
// TODO: the semanticLoc query is not initialized yet, and thus cannot
|
|
842
|
+
// be used here
|
|
843
|
+
error( 'name-missing-alias', [ tableAlias.location, semanticLoc ],
|
|
844
|
+
{ '#': 'duplicate', code: 'as ‹alias›' } );
|
|
904
845
|
}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
'#': (table.name.$inferred ? '$tableImplicit' : '$tableAlias'),
|
|
908
|
-
name: '$',
|
|
909
|
-
keyword: 'as',
|
|
910
|
-
} );
|
|
846
|
+
else {
|
|
847
|
+
error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
|
|
911
848
|
}
|
|
849
|
+
} );
|
|
850
|
+
// also add to JOIN nodes for name restrictions:
|
|
851
|
+
for (const p of joinParents) {
|
|
852
|
+
// for JOIN alias restriction, we cannot use $duplicates, as it is
|
|
853
|
+
// already used for duplicate aliases of queries:
|
|
854
|
+
dictAddArray( p.$tableAliases, table.name.id, table );
|
|
855
|
+
}
|
|
856
|
+
if (table.name?.id[0] === '$' && table.name.$inferred !== '$internal') {
|
|
857
|
+
message( 'name-invalid-dollar-alias', [ table.name.location, table ], {
|
|
858
|
+
'#': (table.name.$inferred ? '$tableImplicit' : '$tableAlias'),
|
|
859
|
+
name: '$',
|
|
860
|
+
keyword: 'as',
|
|
861
|
+
} );
|
|
912
862
|
}
|
|
913
863
|
}
|
|
914
864
|
|
|
@@ -921,7 +871,7 @@ function define( model ) {
|
|
|
921
871
|
initExprForQuery( query.where, query );
|
|
922
872
|
if (query.having)
|
|
923
873
|
initExprForQuery( query.having, query );
|
|
924
|
-
initMembers( query
|
|
874
|
+
initMembers( query );
|
|
925
875
|
}
|
|
926
876
|
|
|
927
877
|
function initExprForQuery( expr, query ) {
|
|
@@ -939,11 +889,6 @@ function define( model ) {
|
|
|
939
889
|
const args = Array.isArray( expr.args ) ? expr.args : Object.values( expr.args );
|
|
940
890
|
args.forEach( e => initExprForQuery( e, query ) );
|
|
941
891
|
}
|
|
942
|
-
else if (expr.path && expr.$expected === 'exists') {
|
|
943
|
-
// TODO: does really the parser has to set $expected?
|
|
944
|
-
expr.$expected = 'approved-exists';
|
|
945
|
-
approveExistsInChildren( expr );
|
|
946
|
-
}
|
|
947
892
|
}
|
|
948
893
|
|
|
949
894
|
function initMixins( query, art ) {
|
|
@@ -972,15 +917,18 @@ function define( model ) {
|
|
|
972
917
|
let wildcard = !!inExtension; // no `extend … with columns { * }`
|
|
973
918
|
// TODO: forbid expand/inline in ref-where, outside queries (CSN), ...
|
|
974
919
|
let hasItems = false;
|
|
920
|
+
let colIndex = 0;
|
|
975
921
|
for (const col of columns || parent.expand || parent.inline || []) {
|
|
922
|
+
++colIndex;
|
|
976
923
|
if (!col) // parse error
|
|
977
924
|
continue;
|
|
978
925
|
hasItems = true;
|
|
979
926
|
if (!columns) { // expand or inline
|
|
980
927
|
if (parent.value)
|
|
981
|
-
setLink( col, '_columnParent', parent ); // also set for '*' in expand/inline
|
|
928
|
+
setLink( col, '_columnParent', parent ); // TODO?: also set for '*' in expand/inline
|
|
982
929
|
else if (parent._columnParent)
|
|
983
930
|
setLink( col, '_columnParent', parent._columnParent );
|
|
931
|
+
// colParent not set for `{ col } as struct`, except if that is in expand/inline
|
|
984
932
|
}
|
|
985
933
|
if (col.val === '*') {
|
|
986
934
|
if (!wildcard) {
|
|
@@ -1009,10 +957,13 @@ function define( model ) {
|
|
|
1009
957
|
}
|
|
1010
958
|
}
|
|
1011
959
|
// Either expression (value), expand, new virtual or new association
|
|
1012
|
-
else
|
|
960
|
+
else {
|
|
1013
961
|
col.kind = 'element';
|
|
1014
962
|
if (!col._block)
|
|
1015
963
|
setLink( col, '_block', parent._block );
|
|
964
|
+
setMemberParent( col, null, parent );
|
|
965
|
+
// remark: _parent might be change if col is in expand of to-many assoc
|
|
966
|
+
ensureColumnName( col, colIndex, parent, !columns );
|
|
1016
967
|
if (col.inline) { // `@anno elem.{ * }` does not work
|
|
1017
968
|
if (col.doc) {
|
|
1018
969
|
message( 'syntax-unexpected-anno', [ col.doc.location, col ],
|
|
@@ -1029,10 +980,12 @@ function define( model ) {
|
|
|
1029
980
|
// TODO: allow sub queries? at least in top-level expand without parallel ref
|
|
1030
981
|
if (columns && !inExtension) // not (yet) in `extend … with columns {…}`
|
|
1031
982
|
initExprForQuery( col.value, parent );
|
|
1032
|
-
|
|
1033
|
-
|
|
983
|
+
if (col.expand || col.inline)
|
|
984
|
+
initSelectItems( col, null, user ); // TODO: use col, remove 3rd param?
|
|
1034
985
|
|
|
1035
|
-
|
|
986
|
+
initMembers( col ); // with #13933, TODO: only for enums
|
|
987
|
+
checkCdlTypeCast( col );
|
|
988
|
+
}
|
|
1036
989
|
}
|
|
1037
990
|
|
|
1038
991
|
if (hasItems && !wildcard && parent.excludingDict && !options.$recompile) {
|
|
@@ -1044,76 +997,75 @@ function define( model ) {
|
|
|
1044
997
|
}
|
|
1045
998
|
}
|
|
1046
999
|
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1000
|
+
/**
|
|
1001
|
+
* Ensure that the column has a name.
|
|
1002
|
+
*
|
|
1003
|
+
* @param col
|
|
1004
|
+
* @param {number} colIndex
|
|
1005
|
+
* @param query
|
|
1006
|
+
* @param {boolean} insideExpand
|
|
1007
|
+
* Whether the column is inside 'expand'.
|
|
1008
|
+
* Anonymous 'expands' don't have a column parent, hence why we need to know this explicitly.
|
|
1009
|
+
*/
|
|
1010
|
+
function ensureColumnName( col, colIndex, query, insideExpand ) {
|
|
1011
|
+
if (col.name)
|
|
1012
|
+
return col.name.id;
|
|
1013
|
+
if (col.inline || col.val === '*' || col.val === '**') // '**' = duplicate '*'
|
|
1014
|
+
return '';
|
|
1015
|
+
// TODO: should we give technical name (colIndex) for error messages?
|
|
1016
|
+
const path = col.value &&
|
|
1017
|
+
(col.value.path || !col.value.args && col.value.func?.path);
|
|
1018
|
+
const last = path?.length && path[path.length - 1];
|
|
1019
|
+
if (last) {
|
|
1020
|
+
col.name = { id: last.id || '', location: last.location, $inferred: 'as' };
|
|
1021
|
+
return col.name.id;
|
|
1022
|
+
}
|
|
1023
|
+
else if ((col.expand || col.value) &&
|
|
1024
|
+
!path && // no parse error (path without last item)
|
|
1025
|
+
(insideExpand || query._parent.kind !== 'select')) { // not sub-selects
|
|
1026
|
+
error( 'query-req-name',
|
|
1027
|
+
// TODO: message function: `query` should work directly
|
|
1028
|
+
[ (col.value || col).location, (query.name ? query : query._parent ) ],
|
|
1029
|
+
{},
|
|
1030
|
+
'Alias name is required for this select item' );
|
|
1031
|
+
}
|
|
1032
|
+
col.name = {
|
|
1033
|
+
// NOTE: If the alias is changed, corresponding name-clash tests must be updated as well!
|
|
1034
|
+
id: `$_column_${ colIndex }`,
|
|
1035
|
+
location: (col.value || col).location,
|
|
1036
|
+
$inferred: '$internal',
|
|
1037
|
+
};
|
|
1038
|
+
return col.name.id;
|
|
1039
|
+
}
|
|
1053
1040
|
|
|
1041
|
+
function checkCdlTypeCast( col ) { // with #13933:
|
|
1054
1042
|
// We don't allow CDL-style casts to anonymous structures. We reject it already here
|
|
1055
1043
|
// and not in checks.js to ensure that it's rejected in parseCdl.
|
|
1044
|
+
// TODO: via <guard> in CdlGrammar.g4
|
|
1056
1045
|
if (col.elements) {
|
|
1057
|
-
error('type-invalid-cast', [ col.elements[$location] ?? col.location, col ],
|
|
1058
|
-
|
|
1046
|
+
error( 'type-invalid-cast', [ (col.elements[$location] ?? col.location), col ],
|
|
1047
|
+
{ '#': 'to-inline-structure' } );
|
|
1059
1048
|
}
|
|
1060
|
-
else if (col.expand && (col.type || col.
|
|
1049
|
+
else if (col.expand && (col.type || col.items)) {
|
|
1061
1050
|
const loc = (col.type?.location || col.elements?.[$location] ||
|
|
1062
|
-
|
|
1063
|
-
error('type-invalid-cast', [ loc, col ], { '#': 'expand' });
|
|
1051
|
+
col.items?.location || col.location);
|
|
1052
|
+
error( 'type-invalid-cast', [ loc, col ], { '#': 'expand' } );
|
|
1064
1053
|
}
|
|
1054
|
+
// Remark: there are quite a few consequential error, TODO: avoid them
|
|
1065
1055
|
}
|
|
1066
1056
|
|
|
1067
|
-
/**
|
|
1068
|
-
* If we have a valid top-level exists, exists in filters of sub-expressions can be translated,
|
|
1069
|
-
* since we will have a top-level subquery after exists-processing in the forRelationalDB.
|
|
1070
|
-
*
|
|
1071
|
-
* Recursively drill down into:
|
|
1072
|
-
* - the .path
|
|
1073
|
-
* - the .args
|
|
1074
|
-
* - the .where.args
|
|
1075
|
-
*
|
|
1076
|
-
* Any $expected === 'exists' encountered along the way are turned into 'approved-exists'
|
|
1077
|
-
*
|
|
1078
|
-
* working: exists toE[exists toE] -> select from E where exists toE
|
|
1079
|
-
* not working: toE[exists toE] -> we don't support subqueries in filters
|
|
1080
|
-
*
|
|
1081
|
-
* @param {object} exprOrPathElement starts w/ an expr but then subelem from .path or .where.args
|
|
1082
|
-
*/
|
|
1083
|
-
function approveExistsInChildren( exprOrPathElement ) {
|
|
1084
|
-
if (!exprOrPathElement) // may be null in case of parse error
|
|
1085
|
-
return;
|
|
1086
|
-
if (exprOrPathElement.$expected === 'exists')
|
|
1087
|
-
exprOrPathElement.$expected = 'approved-exists';
|
|
1088
|
-
// Drill down
|
|
1089
|
-
if (Array.isArray(exprOrPathElement.args))
|
|
1090
|
-
exprOrPathElement.args.forEach( elem => approveExistsInChildren( elem ) );
|
|
1091
|
-
else if (exprOrPathElement.where?.args)
|
|
1092
|
-
exprOrPathElement.where.args.forEach( elem => approveExistsInChildren( elem ) );
|
|
1093
|
-
else if (exprOrPathElement.path)
|
|
1094
|
-
exprOrPathElement.path.forEach( elem => approveExistsInChildren( elem ) );
|
|
1095
|
-
}
|
|
1096
|
-
// TODO: we might issue 'expr-unexpected-exists' and 'expr-no-subquery' already in
|
|
1097
|
-
// define.js (using a to-be-written expression traversal function in utils.js)
|
|
1098
|
-
|
|
1099
1057
|
// Members (elements, enum, actions, params): ---------------------------------
|
|
1100
1058
|
|
|
1101
1059
|
/**
|
|
1102
1060
|
* Set property `_parent` for all elements in `parent` to `parent` and do so
|
|
1103
1061
|
* recursively for all sub elements.
|
|
1104
1062
|
*
|
|
1105
|
-
* If not for extensions: construct === parent
|
|
1106
|
-
*
|
|
1107
1063
|
* Param `initExtensions` is for parse.cdl - TODO delete
|
|
1108
|
-
*
|
|
1109
|
-
* TODO: separate extension!
|
|
1110
1064
|
*/
|
|
1111
|
-
function initMembers(
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
let obj = initItemsLinks( construct, block );
|
|
1116
|
-
initExprAnnoBlock( construct, block );
|
|
1065
|
+
function initMembers( parent ) {
|
|
1066
|
+
const block = parent._block;
|
|
1067
|
+
let obj = initItemsLinks( parent, block );
|
|
1068
|
+
initExprAnnoBlock( parent, block );
|
|
1117
1069
|
if (obj.target && targetIsTargetAspect( obj )) {
|
|
1118
1070
|
obj.targetAspect = obj.target;
|
|
1119
1071
|
delete obj.target;
|
|
@@ -1121,290 +1073,110 @@ function define( model ) {
|
|
|
1121
1073
|
const { targetAspect } = obj;
|
|
1122
1074
|
if (targetAspect) {
|
|
1123
1075
|
if (obj.foreignKeys) {
|
|
1124
|
-
error( 'type-unexpected-foreign-keys', [ obj.foreignKeys[$location],
|
|
1076
|
+
error( 'type-unexpected-foreign-keys', [ obj.foreignKeys[$location], parent ] );
|
|
1125
1077
|
delete obj.foreignKeys; // continuation semantics: not specified
|
|
1126
1078
|
}
|
|
1127
1079
|
if (obj.on && !obj.target) {
|
|
1128
|
-
error( 'type-unexpected-on-condition', [ obj.on.location,
|
|
1080
|
+
error( 'type-unexpected-on-condition', [ obj.on.location, parent ] );
|
|
1129
1081
|
delete obj.on; // continuation semantics: not specified
|
|
1130
1082
|
}
|
|
1131
|
-
if (targetAspect.elements)
|
|
1132
|
-
initAnonymousAspect();
|
|
1133
|
-
}
|
|
1134
|
-
if (obj !== parent && obj.elements && parent.enum) { // applying the extension
|
|
1135
|
-
initElementsAsEnum();
|
|
1136
|
-
}
|
|
1137
|
-
else {
|
|
1138
|
-
if (checkDefinitions( construct, parent, 'elements', obj.elements || false ))
|
|
1139
|
-
forEachInOrder( obj, 'elements', init );
|
|
1140
|
-
if (checkDefinitions( construct, parent, 'enum', obj.enum || false ))
|
|
1141
|
-
forEachGeneric( obj, 'enum', init );
|
|
1083
|
+
if (targetAspect.elements) // eslint-disable-next-line no-multi-assign
|
|
1084
|
+
parent = obj = initAnonymousAspect( parent, obj, targetAspect );
|
|
1142
1085
|
}
|
|
1086
|
+
forEachInOrder( obj, 'elements', (...args) => initArtifact( parent, ...args ) );
|
|
1087
|
+
forEachGeneric( obj, 'enum', (...args) => initArtifact( parent, ...args ) );
|
|
1088
|
+
forEachInOrder( obj, 'foreignKeys', (...args) => initArtifact( parent, ...args ) );
|
|
1089
|
+
forEachGeneric( parent, 'actions', (...args) => initArtifact( parent, ...args ) );
|
|
1090
|
+
forEachInOrder( parent, 'params', (...args) => initArtifact( parent, ...args ) );
|
|
1143
1091
|
|
|
1144
|
-
|
|
1145
|
-
forEachInOrder( obj, 'foreignKeys', init );
|
|
1146
|
-
if (checkDefinitions( construct, parent, 'actions' ))
|
|
1147
|
-
forEachGeneric( construct, 'actions', init );
|
|
1148
|
-
if (checkDefinitions( construct, parent, 'params' ))
|
|
1149
|
-
forEachInOrder( construct, 'params', init );
|
|
1150
|
-
const { returns } = construct;
|
|
1092
|
+
const { returns } = parent;
|
|
1151
1093
|
if (returns) {
|
|
1152
|
-
const { kind } =
|
|
1094
|
+
const { kind } = parent;
|
|
1153
1095
|
returns.kind = (kind === 'extend' || kind === 'annotate') ? kind : 'param';
|
|
1154
|
-
|
|
1096
|
+
initArtifact( parent, returns, '' ); // '' is special name for returns parameter
|
|
1155
1097
|
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
function initElementsAsEnum() {
|
|
1159
|
-
// in extensions, extended enums are represented as elements
|
|
1160
|
-
let hasElement = false;
|
|
1161
|
-
for (const n in obj.elements) {
|
|
1162
|
-
const e = obj.elements[n];
|
|
1163
|
-
if (e.kind === 'extend')
|
|
1164
|
-
continue;
|
|
1165
|
-
const noVal = e.value?.val === undefined && e.value?.sym === undefined;
|
|
1166
|
-
// TODO: forbid #symbol as enum value
|
|
1167
|
-
if (e.$syntax === 'element' || // `extend … with elements` or `extend with { element … }`
|
|
1168
|
-
noVal && e.$syntax !== 'enum' || // no value in CDL input
|
|
1169
|
-
e.virtual || e.key || e.masked || e.type || e.elements || e.items || e.stored) {
|
|
1170
|
-
// We do not want to complain separately about all element properties:
|
|
1171
|
-
error( 'ext-unexpected-element', [ e.location, construct ],
|
|
1172
|
-
{ name: e.name.id, code: 'extend … with enum' },
|
|
1173
|
-
// eslint-disable-next-line @stylistic/max-len
|
|
1174
|
-
'Unexpected elements like $(NAME) in an extension for an enum. Additionally, use $(CODE) when extending enums' );
|
|
1175
|
-
// Don't emit 'ext-expecting-enum' if this error is emitted.
|
|
1176
|
-
return;
|
|
1177
|
-
}
|
|
1178
|
-
e.kind = 'enum';
|
|
1179
|
-
if (noVal || e.$syntax !== 'enum')
|
|
1180
|
-
hasElement = true; // warning with CDL input or `name: {}` in CSN input
|
|
1181
|
-
}
|
|
1182
|
-
if (hasElement) {
|
|
1183
|
-
// This message is similar to the one above. In v6, we could probably
|
|
1184
|
-
// turn this warning into an error, remove `$syntax: 'element',
|
|
1185
|
-
// and use the above `ext-unexpected-element` only for CSN input.
|
|
1186
|
-
warning( 'ext-expecting-enum', [ obj.elements[$location], construct ],
|
|
1187
|
-
{ code: 'extend … with enum' }, 'Use $(CODE) when extending enums' );
|
|
1188
|
-
}
|
|
1189
|
-
forEachGeneric( { enum: obj.elements }, 'enum', init );
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
function initAnonymousAspect() {
|
|
1193
|
-
// TODO: main?
|
|
1194
|
-
const inEntity = parent._main?.kind === 'entity';
|
|
1195
|
-
// TODO: also allow indirectly (component in component in entity)?
|
|
1196
|
-
setLink( targetAspect, '_outer', obj );
|
|
1197
|
-
setLink( targetAspect, '_parent', parent._parent );
|
|
1198
|
-
setLink( targetAspect, '_main', null ); // for name resolution
|
|
1199
|
-
|
|
1200
|
-
parent = targetAspect;
|
|
1201
|
-
construct = parent; // avoid extension behavior
|
|
1202
|
-
targetAspect.kind = 'aspect'; // TODO: probably '$aspect' to detect
|
|
1203
|
-
setLink( targetAspect, '_block', block );
|
|
1204
|
-
initDollarSelf( targetAspect );
|
|
1205
|
-
// allow ref of up_ in anonymous aspect inside entity
|
|
1206
|
-
// (TODO: complain if used and the managed composition is included into
|
|
1207
|
-
// another entity - might induce auto-redirection):
|
|
1208
|
-
if (inEntity && !targetAspect.elements.up_) {
|
|
1209
|
-
const up = {
|
|
1210
|
-
name: { id: 'up_' },
|
|
1211
|
-
kind: '$navElement',
|
|
1212
|
-
location: obj.location,
|
|
1213
|
-
};
|
|
1214
|
-
setLink( up, '_parent', targetAspect );
|
|
1215
|
-
setLink( up, '_main', targetAspect ); // used on main artifact
|
|
1216
|
-
// recompilation case: both target and targetAspect → allow up_ in that case, too:
|
|
1217
|
-
const name = obj.target && resolveUncheckedPath( obj.target, 'target', obj );
|
|
1218
|
-
const entity = name && model.definitions[name];
|
|
1219
|
-
if (entity && entity.elements)
|
|
1220
|
-
setLink( up, '_origin', entity.elements.up_ );
|
|
1221
|
-
// processAspectComposition/expand() sets _origin to element of
|
|
1222
|
-
// generated target entity
|
|
1223
|
-
targetAspect.$tableAliases.up_ = up;
|
|
1224
|
-
}
|
|
1225
|
-
obj = targetAspect;
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
function init( elem, name, prop ) {
|
|
1229
|
-
if (!elem.kind) // wrong CSN input
|
|
1230
|
-
elem.kind = dictKinds[prop];
|
|
1231
|
-
if (!elem.name && !elem._outer) {
|
|
1232
|
-
const ref = elem.targetElement || elem.kind === 'element' && elem.value;
|
|
1233
|
-
if (ref && ref.path) {
|
|
1234
|
-
elem.name = Object.assign( { $inferred: 'as' },
|
|
1235
|
-
ref.path[ref.path.length - 1] );
|
|
1236
|
-
}
|
|
1237
|
-
else { // RETURNS, parser robustness
|
|
1238
|
-
elem.name = { id: name, location: elem.location };
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
// if (!kindProperties[ elem.kind ]) console.log(elem.kind,elem.name)
|
|
1242
|
-
if ((elem.kind === 'extend' || elem.kind === 'annotate') && !initExtensions) {
|
|
1243
|
-
storeExtension( elem, name, prop, parent, block );
|
|
1244
|
-
return;
|
|
1245
|
-
}
|
|
1246
|
-
if (isQueryExtension && elem.kind === 'element') {
|
|
1247
|
-
error( 'extend-query', [ elem.location, construct ], // TODO: searchName ?
|
|
1248
|
-
{ code: 'extend projection' },
|
|
1249
|
-
'Use $(CODE) to add select items to the query entity' );
|
|
1250
|
-
return;
|
|
1251
|
-
}
|
|
1098
|
+
}
|
|
1252
1099
|
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
//
|
|
1281
|
-
//
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
delete elem.target;
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1100
|
+
function initAnonymousAspect( parent, obj, targetAspect ) {
|
|
1101
|
+
// TODO: main?
|
|
1102
|
+
const inEntity = parent._main?.kind === 'entity';
|
|
1103
|
+
// TODO: also allow indirectly (component in component in entity)?
|
|
1104
|
+
setLink( targetAspect, '_outer', obj );
|
|
1105
|
+
setLink( targetAspect, '_parent', parent._parent );
|
|
1106
|
+
setLink( targetAspect, '_main', null ); // for name resolution
|
|
1107
|
+
|
|
1108
|
+
targetAspect.kind = 'aspect'; // TODO: probably '$aspect' to detect
|
|
1109
|
+
setLink( targetAspect, '_block', parent._block );
|
|
1110
|
+
initDollarSelf( targetAspect );
|
|
1111
|
+
// allow ref of up_ in anonymous aspect inside entity
|
|
1112
|
+
// (TODO: complain if used and the managed composition is included into
|
|
1113
|
+
// another entity - might induce auto-redirection):
|
|
1114
|
+
if (inEntity && !targetAspect.elements.up_) {
|
|
1115
|
+
const up = {
|
|
1116
|
+
name: { id: 'up_' },
|
|
1117
|
+
kind: '$navElement',
|
|
1118
|
+
location: obj.location,
|
|
1119
|
+
};
|
|
1120
|
+
setLink( up, '_parent', targetAspect );
|
|
1121
|
+
setLink( up, '_main', targetAspect ); // used on main artifact
|
|
1122
|
+
// recompilation case: both target and targetAspect → allow up_ in that case, too:
|
|
1123
|
+
const name = obj.target && resolveUncheckedPath( obj.target, 'target', obj );
|
|
1124
|
+
const entity = name && model.definitions[name];
|
|
1125
|
+
if (entity && entity.elements)
|
|
1126
|
+
setLink( up, '_origin', entity.elements.up_ );
|
|
1127
|
+
// processAspectComposition/expand() sets _origin to element of
|
|
1128
|
+
// generated target entity
|
|
1129
|
+
targetAspect.$tableAliases.up_ = up;
|
|
1130
|
+
}
|
|
1131
|
+
return targetAspect;
|
|
1288
1132
|
}
|
|
1289
1133
|
|
|
1290
|
-
function
|
|
1291
|
-
if (!
|
|
1292
|
-
|
|
1293
|
-
if (
|
|
1294
|
-
const
|
|
1295
|
-
if (
|
|
1296
|
-
|
|
1297
|
-
|
|
1134
|
+
function initArtifact( parent, elem, name, prop ) {
|
|
1135
|
+
if (!elem.kind) // wrong CSN input
|
|
1136
|
+
elem.kind = dictKinds[prop];
|
|
1137
|
+
if (!elem.name && !elem._outer) {
|
|
1138
|
+
const ref = elem.targetElement || elem.kind === 'element' && elem.value;
|
|
1139
|
+
if (ref && ref.path) {
|
|
1140
|
+
elem.name = Object.assign( { $inferred: 'as' },
|
|
1141
|
+
ref.path[ref.path.length - 1] );
|
|
1142
|
+
}
|
|
1143
|
+
else { // RETURNS, parser robustness
|
|
1144
|
+
elem.name = { id: name, location: elem.location };
|
|
1298
1145
|
}
|
|
1299
|
-
boundSelfParamType = '$self';
|
|
1300
|
-
}
|
|
1301
|
-
const first = params[Object.keys( params )[0] || ''];
|
|
1302
|
-
const type = first?.type || first?.items?.type; // this sequence = no derived type
|
|
1303
|
-
const path = type?.path;
|
|
1304
|
-
if (path?.length === 1 && path[0]?.id === '$self') { // TODO: no where: ?
|
|
1305
|
-
const $self = main.$tableAliases?.$self ||
|
|
1306
|
-
main.kind === 'extend' && { name: { id: '$self' } };
|
|
1307
|
-
// remark: an 'extend' has no "table alias" `$self` (relevant for parse-cdl)
|
|
1308
|
-
setLink( type, '_artifact', $self );
|
|
1309
|
-
setLink( path[0], '_artifact', $self );
|
|
1310
1146
|
}
|
|
1311
|
-
}
|
|
1312
1147
|
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
* Does nothing, it `obj.items` does not exist.
|
|
1316
|
-
*
|
|
1317
|
-
* @param {XSN.Artifact} obj
|
|
1318
|
-
* @param {object} block
|
|
1319
|
-
* @return {XSN.Artifact}
|
|
1320
|
-
*/
|
|
1321
|
-
function initItemsLinks( obj, block ) {
|
|
1322
|
-
let { items } = obj;
|
|
1323
|
-
while (items) {
|
|
1324
|
-
setLink( items, '_outer', obj );
|
|
1325
|
-
setLink( items, '_parent', obj._parent );
|
|
1326
|
-
setLink( items, '_block', block );
|
|
1327
|
-
obj = items;
|
|
1328
|
-
items = obj.items;
|
|
1329
|
-
}
|
|
1330
|
-
return obj;
|
|
1331
|
-
}
|
|
1148
|
+
if (!elem._block)
|
|
1149
|
+
setLink( elem, '_block', parent._block );
|
|
1332
1150
|
|
|
1333
|
-
|
|
1151
|
+
// don't dump with `entity T {}; extend T with { extend e {}; e {}; e {} };`:
|
|
1152
|
+
setMemberParent( elem, name, parent );
|
|
1153
|
+
checkRedefinition( elem );
|
|
1154
|
+
initMembers( elem );
|
|
1155
|
+
if (elem.kind === 'action' || elem.kind === 'function')
|
|
1156
|
+
initBoundSelfParam( elem.params, elem._main );
|
|
1334
1157
|
|
|
1335
|
-
|
|
1336
|
-
function checkDefinitions( construct, parent, prop, dict = construct[prop] ) {
|
|
1337
|
-
// TODO: do differently, see also annotateMembers() in resolver
|
|
1338
|
-
// To have been checked by parsers:
|
|
1339
|
-
// - artifacts (CDL-only anyway) only inside [extend] context|service
|
|
1340
|
-
if (!dict)
|
|
1341
|
-
return false;
|
|
1342
|
-
const feature = kindProperties[parent.kind ?? 'element'][prop];
|
|
1343
|
-
if (feature &&
|
|
1344
|
-
(feature === true || construct.kind !== 'extend' || feature( prop, parent )))
|
|
1345
|
-
return true;
|
|
1346
|
-
const location = dict[$location];
|
|
1158
|
+
// for a correct home path, setMemberParent needed to be called
|
|
1347
1159
|
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
else if (prop === 'params') {
|
|
1368
|
-
if (!feature) {
|
|
1369
|
-
// Note: This error can't be triggered at the moment. But as we likely want to
|
|
1370
|
-
// allow extensions with params in the future, we keep the code.
|
|
1371
|
-
error( 'def-unexpected-params', [ location, construct ], {},
|
|
1372
|
-
'Parameters only exist for entities, actions or functions' );
|
|
1373
|
-
}
|
|
1374
|
-
else {
|
|
1375
|
-
// remark: we could allow this
|
|
1376
|
-
error( 'extend-with-params', [ location, construct ], {},
|
|
1377
|
-
'Extending artifacts with parameters is not supported' );
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
else if (feature) { // allowed in principle, but not with extend
|
|
1381
|
-
if (!Object.keys( dict ).length) {
|
|
1382
|
-
warning( 'ext-ignoring-elements', [ location, construct ], {},
|
|
1383
|
-
'Only structures with directly specified elements can be extended by elements' );
|
|
1384
|
-
return false;
|
|
1385
|
-
}
|
|
1386
|
-
else if (parent.$inferred === 'include') { // special case for better error message
|
|
1387
|
-
const variant = (construct.enum || construct.elements) ? 'elements' : 'std';
|
|
1388
|
-
error( 'ref-expected-direct-structure', [ location, construct ],
|
|
1389
|
-
{ '#': variant, art: parent } );
|
|
1390
|
-
}
|
|
1391
|
-
else {
|
|
1392
|
-
error( 'extend-type', [ location, construct ], {},
|
|
1393
|
-
'Only structures or enum types can be extended with elements/enums' );
|
|
1394
|
-
}
|
|
1395
|
-
}
|
|
1396
|
-
else if (prop === 'elements') {
|
|
1397
|
-
error( 'def-unexpected-elements', [ location, construct ], {},
|
|
1398
|
-
'Elements only exist in entities, types or typed constructs' );
|
|
1399
|
-
}
|
|
1400
|
-
else if (prop === 'columns') {
|
|
1401
|
-
error( 'extend-columns', [ location, construct ], { art: construct } );
|
|
1402
|
-
}
|
|
1403
|
-
else { // if (prop === 'enum') {
|
|
1404
|
-
error( 'def-unexpected-enum', [ location, construct ], {},
|
|
1405
|
-
'Enum symbols can only be defined for types or typed constructs' );
|
|
1160
|
+
if (!elem.value || elem.kind !== 'element' ||
|
|
1161
|
+
elem.$syntax === 'enum' && parent.kind === 'extend') // ambiguous in parse-cdl
|
|
1162
|
+
return;
|
|
1163
|
+
// -> it's a calculated element
|
|
1164
|
+
if (!elem.type && elem.value.type) { // top-level CAST( expr AS type )
|
|
1165
|
+
if (!elem.target)
|
|
1166
|
+
elem.type = { ...elem.value.type, $inferred: 'cast' };
|
|
1167
|
+
}
|
|
1168
|
+
elem.$syntax = 'calc';
|
|
1169
|
+
// TODO: it is not just "syntax" - maybe better test for `$calcDepElement`?
|
|
1170
|
+
createAndLinkCalcDepElement( elem );
|
|
1171
|
+
|
|
1172
|
+
// Special case (hack) for calculated elements that use composition+filter:
|
|
1173
|
+
// See "Notes on `$enclosed`" in `ExposingAssocWithFilter.md` for details.
|
|
1174
|
+
// TODO: hm, only for inferred type - then just do not infer it in that case
|
|
1175
|
+
if (elem.target && elem.value.path?.[elem.value.path.length - 1]?.where) {
|
|
1176
|
+
delete elem.type;
|
|
1177
|
+
delete elem.on;
|
|
1178
|
+
delete elem.target;
|
|
1406
1179
|
}
|
|
1407
|
-
return construct === parent;
|
|
1408
1180
|
}
|
|
1409
1181
|
|
|
1410
1182
|
/**
|