@sap/cds-compiler 2.7.0 → 2.11.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 +167 -0
- package/bin/cdsc.js +42 -25
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +10 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +17 -33
- package/lib/api/options.js +25 -13
- package/lib/api/validate.js +33 -9
- package/lib/backends.js +9 -8
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +26 -2
- package/lib/base/messages.js +25 -9
- package/lib/base/model.js +5 -3
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/onConditions.js +5 -0
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/validator.js +7 -2
- package/lib/compiler/assert-consistency.js +18 -5
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +5 -2
- package/lib/compiler/definer.js +145 -120
- package/lib/compiler/index.js +16 -4
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +207 -47
- package/lib/compiler/shared.js +47 -200
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +94 -98
- package/lib/edm/edm.js +16 -20
- package/lib/edm/edmPreprocessor.js +302 -115
- package/lib/edm/edmUtils.js +31 -12
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5308 -4308
- package/lib/json/from-csn.js +59 -30
- package/lib/json/to-csn.js +354 -105
- package/lib/language/antlrParser.js +11 -0
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +81 -14
- package/lib/language/language.g4 +163 -31
- package/lib/main.d.ts +136 -17
- package/lib/main.js +7 -1
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +115 -32
- package/lib/model/csnUtils.js +71 -33
- package/lib/model/enrichCsn.js +36 -9
- package/lib/model/revealInternalProperties.js +20 -4
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -16
- package/lib/render/.eslintrc.json +3 -1
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/toCdl.js +60 -17
- package/lib/render/toHdbcds.js +122 -74
- package/lib/render/toSql.js +57 -32
- package/lib/render/utils/common.js +6 -10
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/constraints.js +273 -119
- package/lib/transform/db/draft.js +9 -6
- package/lib/transform/db/expansion.js +19 -7
- package/lib/transform/db/flattening.js +31 -7
- package/lib/transform/db/transformExists.js +344 -66
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +65 -436
- package/lib/transform/forOdataNew.js +21 -10
- package/lib/transform/localized.js +2 -0
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +44 -38
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +13 -10
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +55 -9
- package/lib/transform/translateAssocsToJoins.js +11 -17
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -3
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
package/lib/json/to-csn.js
CHANGED
|
@@ -33,12 +33,15 @@ let projectionAsQuery = false;
|
|
|
33
33
|
let withLocations = false;
|
|
34
34
|
let dictionaryPrototype = null;
|
|
35
35
|
|
|
36
|
+
// Properties for dictionaries, set in compileX() and TODO: parseX(), must be
|
|
37
|
+
// stored with symbols as keys, as we do not want to disallow any key name:
|
|
38
|
+
const $inferred = Symbol.for('cds.$inferred');
|
|
39
|
+
|
|
36
40
|
// IMPORTANT: the order of these properties determine the order of properties
|
|
37
41
|
// in the resulting CSN !!! Also check const `csnPropertyNames`.
|
|
38
42
|
const transformers = {
|
|
39
43
|
// early and modifiers (without null / not null) ---------------------------
|
|
40
44
|
kind,
|
|
41
|
-
_outer: ( _, csn, node ) => addOrigin( csn, node ),
|
|
42
45
|
id: n => n, // in path item
|
|
43
46
|
doc: value,
|
|
44
47
|
'@': value,
|
|
@@ -67,7 +70,7 @@ const transformers = {
|
|
|
67
70
|
targetAspect,
|
|
68
71
|
target,
|
|
69
72
|
foreignKeys,
|
|
70
|
-
enum:
|
|
73
|
+
enum: enumDict,
|
|
71
74
|
items,
|
|
72
75
|
includes: arrayOf( artifactRef ), // also entities
|
|
73
76
|
// late expressions / query properties -------------------------------------
|
|
@@ -80,6 +83,7 @@ const transformers = {
|
|
|
80
83
|
where: condition, // also pathItem after 'cardinality' before 'args'
|
|
81
84
|
having: condition,
|
|
82
85
|
args, // also pathItem after 'where', before 'on'/'orderBy'
|
|
86
|
+
suffix: node => [].concat( ...node.suffix.map( xprArg ) ),
|
|
83
87
|
orderBy: arrayOf( orderBy ), // TODO XSN: make `sort` and `nulls` sibling properties
|
|
84
88
|
sort: value,
|
|
85
89
|
nulls: value,
|
|
@@ -95,7 +99,7 @@ const transformers = {
|
|
|
95
99
|
value: enumValue, // do not list for select items as elements
|
|
96
100
|
query,
|
|
97
101
|
elements,
|
|
98
|
-
actions
|
|
102
|
+
actions, // TODO: just normal dictionary
|
|
99
103
|
// special: top-level, cardinality -----------------------------------------
|
|
100
104
|
sources,
|
|
101
105
|
definitions: sortedDict,
|
|
@@ -191,6 +195,22 @@ const operators = {
|
|
|
191
195
|
notLike: ternary( [ 'not', 'like' ], [ 'escape' ] ),
|
|
192
196
|
when: exprs => [ 'when', ...exprs[0], 'then', ...exprs[1] ],
|
|
193
197
|
case: exprs => [ 'case' ].concat( ...exprs, [ 'end' ] ),
|
|
198
|
+
over: exprs => [ 'over', { xpr: [].concat( ...exprs ) } ],
|
|
199
|
+
orderBy: exprs => [
|
|
200
|
+
'order', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
201
|
+
],
|
|
202
|
+
partitionBy: exprs => [
|
|
203
|
+
'partition', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
204
|
+
],
|
|
205
|
+
rows: exprs => [
|
|
206
|
+
'rows', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
207
|
+
],
|
|
208
|
+
preceding: postfix( [ 'preceding' ] ),
|
|
209
|
+
unboundedPreceding: [ 'unbounded', 'preceding' ],
|
|
210
|
+
currentRow: [ 'current', 'row' ],
|
|
211
|
+
unboundedFollowing: [ 'unbounded', 'following' ],
|
|
212
|
+
following: postfix( [ 'following' ] ),
|
|
213
|
+
frameBetween: exprs => [ 'between', ...exprs[0], 'and', ...exprs[1] ],
|
|
194
214
|
// xpr: (exprs) => [].concat( ...exprs ), see below - handled extra
|
|
195
215
|
};
|
|
196
216
|
|
|
@@ -232,22 +252,36 @@ function sortCsn( csn, cloneOptions = false ) {
|
|
|
232
252
|
r[n] = sortCsn(val, cloneOptions);
|
|
233
253
|
}
|
|
234
254
|
if (cloneOptions && typeof csn === 'object') {
|
|
235
|
-
if (csn
|
|
236
|
-
setHidden(r, '$sources', csn.$sources);
|
|
237
|
-
if (csn
|
|
238
|
-
setHidden(r, '$location', csn.$location);
|
|
239
|
-
if (csn
|
|
240
|
-
setHidden(r, '$path', csn.$path);
|
|
241
|
-
if (csn
|
|
242
|
-
setHidden(r, '$paths', csn.$paths);
|
|
243
|
-
if (csn
|
|
244
|
-
setHidden(r, 'elements', csnDictionary( csn.elements, false, cloneOptions ) );
|
|
245
|
-
if (csn
|
|
246
|
-
setHidden(r, '$tableConstraints', csn.$tableConstraints);
|
|
255
|
+
if ({}.hasOwnProperty.call( csn, '$sources' ) && !r.$sources)
|
|
256
|
+
setHidden( r, '$sources', csn.$sources );
|
|
257
|
+
if ({}.hasOwnProperty.call( csn, '$location' ) && !r.$location)
|
|
258
|
+
setHidden( r, '$location', csn.$location );
|
|
259
|
+
if ({}.hasOwnProperty.call( csn, '$path' )) // used in generic reference flattener
|
|
260
|
+
setHidden( r, '$path', csn.$path );
|
|
261
|
+
if ({}.hasOwnProperty.call( csn, '$paths' )) // used in generic reference flattener
|
|
262
|
+
setHidden( r, '$paths', csn.$paths );
|
|
263
|
+
if (hasNonEnumerable( csn, 'elements' ) && !r.elements) // non-enumerable 'elements'
|
|
264
|
+
setHidden( r, 'elements', csnDictionary( csn.elements, false, cloneOptions ) );
|
|
265
|
+
if (hasNonEnumerable( csn, '$tableConstraints' ) && !r.$tableConstraints)
|
|
266
|
+
setHidden( r, '$tableConstraints', csn.$tableConstraints );
|
|
247
267
|
}
|
|
248
268
|
return r;
|
|
249
269
|
}
|
|
250
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Check wether the given object has non enumerable property.
|
|
273
|
+
* Ensure that we don't take it from the prototype, only "directly" - we accidentally
|
|
274
|
+
* cloned elements with a cds.linked input otherwise.
|
|
275
|
+
*
|
|
276
|
+
* @param {object} object
|
|
277
|
+
* @param {string} property
|
|
278
|
+
* @returns
|
|
279
|
+
*/
|
|
280
|
+
function hasNonEnumerable(object, property) {
|
|
281
|
+
return {}.hasOwnProperty.call( object, property ) &&
|
|
282
|
+
!{}.propertyIsEnumerable.call( object, property );
|
|
283
|
+
}
|
|
284
|
+
|
|
251
285
|
/**
|
|
252
286
|
* @param {object} csn
|
|
253
287
|
* @param {boolean} sort
|
|
@@ -359,10 +393,12 @@ function usings( srcDict ) {
|
|
|
359
393
|
* @param {object} csn
|
|
360
394
|
* @param {object} model
|
|
361
395
|
*/
|
|
396
|
+
|
|
397
|
+
|
|
362
398
|
function extensions( node, csn, model ) {
|
|
363
399
|
if (model.kind && model.kind !== 'source')
|
|
364
400
|
return undefined;
|
|
365
|
-
const exts = node.map(
|
|
401
|
+
const exts = node.map( definition );
|
|
366
402
|
|
|
367
403
|
// builtins are non-enumerable for smaller display
|
|
368
404
|
for (const name of Object.getOwnPropertyNames( model.definitions || {} ).sort()) {
|
|
@@ -377,17 +413,21 @@ function extensions( node, csn, model ) {
|
|
|
377
413
|
}
|
|
378
414
|
else if (gensrcFlavor) {
|
|
379
415
|
// From definitions (without redefinitions) with potential inferred elements:
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
if (
|
|
387
|
-
annotate.
|
|
388
|
-
|
|
389
|
-
|
|
416
|
+
const annotate = { annotate: name };
|
|
417
|
+
if (art.$inferred)
|
|
418
|
+
Object.assign( annotate, annotationsAndDocComment( art, true ) );
|
|
419
|
+
if (art.$expand === 'annotate') {
|
|
420
|
+
if (art.actions)
|
|
421
|
+
attachAnnotations( annotate, 'actions', art.actions, art.$inferred );
|
|
422
|
+
else if (art.params)
|
|
423
|
+
attachAnnotations( annotate, 'params', art.params, art.$inferred );
|
|
424
|
+
const obj = art.returns || art;
|
|
425
|
+
const elems = (obj.items || obj).elements; // no targetAspect here
|
|
426
|
+
if (elems)
|
|
427
|
+
attachAnnotations( annotate, 'elements', elems, art.$inferred, art.returns );
|
|
390
428
|
}
|
|
429
|
+
if (Object.keys( annotate ).length > 1)
|
|
430
|
+
exts.push( annotate );
|
|
391
431
|
}
|
|
392
432
|
}
|
|
393
433
|
|
|
@@ -395,6 +435,58 @@ function extensions( node, csn, model ) {
|
|
|
395
435
|
(a, b) => (a.annotate || a.extend).localeCompare( b.annotate || b.extend )
|
|
396
436
|
);
|
|
397
437
|
|
|
438
|
+
/*
|
|
439
|
+
function attachElementAnnos( annotate, art ) {
|
|
440
|
+
while (art.items)
|
|
441
|
+
art = art.items;
|
|
442
|
+
if (art.elements) {
|
|
443
|
+
const elems = inferred( art.elements, art.$inferred );
|
|
444
|
+
if (Object.keys( elems ).length)
|
|
445
|
+
annotate.elements = elems;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function attachParamAnnos( annotate, art ) {
|
|
450
|
+
const inferredParent = art.$inferred;
|
|
451
|
+
if (art.params) {
|
|
452
|
+
const ext = Object.create( dictionaryPrototype );
|
|
453
|
+
for (const name in art.params) {
|
|
454
|
+
const par = art.params[name];
|
|
455
|
+
if (!inferredParent && !par.$inferred && par.$expand !== 'annotate')
|
|
456
|
+
continue;
|
|
457
|
+
const render = annotationsAndDocComment( par, true );
|
|
458
|
+
const subElems = par.$expand !== 'origin' && (par.items || par).elements;
|
|
459
|
+
if (subElems) {
|
|
460
|
+
const sub = inferred( subElems, par.$inferred );
|
|
461
|
+
if (Object.keys( sub ).length)
|
|
462
|
+
render.elements = sub;
|
|
463
|
+
}
|
|
464
|
+
if (Object.keys(render).length)
|
|
465
|
+
ext[name] = render;
|
|
466
|
+
}
|
|
467
|
+
if (obj.keys( ext ))
|
|
468
|
+
annotate.params = ext;
|
|
469
|
+
}
|
|
470
|
+
if (art.returns) {
|
|
471
|
+
const par = art.returns;
|
|
472
|
+
if (!inferredParent && !par.$inferred && par.$expand !== 'annotate')
|
|
473
|
+
return;
|
|
474
|
+
const render = annotationsAndDocComment( par, true );
|
|
475
|
+
const subElems = par.$expand !== 'origin' && (par.items || par).elements;
|
|
476
|
+
if (subElems) {
|
|
477
|
+
const sub = inferred( subElems, par.$inferred );
|
|
478
|
+
if (Object.keys( sub ).length)
|
|
479
|
+
render.elements = sub;
|
|
480
|
+
}
|
|
481
|
+
if (Object.keys(render).length)
|
|
482
|
+
const sub = inferred( subElems, par.$inferred );
|
|
483
|
+
if (Object.keys( sub ).length)
|
|
484
|
+
render.elements = sub;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return ext;
|
|
488
|
+
*/
|
|
489
|
+
|
|
398
490
|
// extract namespace/builtin annotations
|
|
399
491
|
function extractAnnotationsToExtension( art ) {
|
|
400
492
|
const name = art.name.absolute;
|
|
@@ -456,17 +548,29 @@ function sources( srcDict, csn ) {
|
|
|
456
548
|
}
|
|
457
549
|
}
|
|
458
550
|
|
|
459
|
-
function
|
|
460
|
-
const
|
|
461
|
-
for (const name in
|
|
462
|
-
const elem =
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
551
|
+
function attachAnnotations( annotate, prop, dict, inferred, returns = false ) {
|
|
552
|
+
const annoDict = Object.create( dictionaryPrototype );
|
|
553
|
+
for (const name in dict) {
|
|
554
|
+
const elem = dict[name];
|
|
555
|
+
const inf = inferred || elem.$inferred; // is probably always inferred if parent was
|
|
556
|
+
const sub = (inf) ? annotationsAndDocComment( elem, true ) : {};
|
|
557
|
+
if (elem.$expand === 'annotate') {
|
|
558
|
+
if (elem.params)
|
|
559
|
+
attachAnnotations( sub, 'params', elem.params, inf );
|
|
560
|
+
const obj = elem.returns || elem;
|
|
561
|
+
const elems = (obj.items || obj.targetAspect || obj).elements;
|
|
562
|
+
if (elems)
|
|
563
|
+
attachAnnotations( sub, 'elements', elems, inf, elem.returns );
|
|
564
|
+
}
|
|
565
|
+
if (Object.keys( sub ).length)
|
|
566
|
+
annoDict[name] = sub;
|
|
567
|
+
}
|
|
568
|
+
if (Object.keys( annoDict ).length) {
|
|
569
|
+
if (returns)
|
|
570
|
+
annotate.returns = { elements: annoDict };
|
|
571
|
+
else
|
|
572
|
+
annotate[prop] = annoDict;
|
|
468
573
|
}
|
|
469
|
-
return ext;
|
|
470
574
|
}
|
|
471
575
|
|
|
472
576
|
function standard( node ) {
|
|
@@ -505,10 +609,13 @@ function set( prop, csn, node ) {
|
|
|
505
609
|
}
|
|
506
610
|
|
|
507
611
|
function targetAspect( val, csn, node ) {
|
|
612
|
+
const ta = (val.elements)
|
|
613
|
+
? addLocation( val.location, standard( val ) )
|
|
614
|
+
: artifactRef( val, true );
|
|
508
615
|
if (!gensrcFlavor || node.target && !node.target.$inferred)
|
|
509
|
-
return
|
|
616
|
+
return ta;
|
|
510
617
|
// For compatibility, put aspect in 'target' with parse.cdl and csn flavor 'gensrc'
|
|
511
|
-
csn.target =
|
|
618
|
+
csn.target = ta;
|
|
512
619
|
return undefined;
|
|
513
620
|
}
|
|
514
621
|
|
|
@@ -527,11 +634,9 @@ function target( val, _csn, node ) {
|
|
|
527
634
|
}
|
|
528
635
|
|
|
529
636
|
function items( obj, csn, node ) {
|
|
530
|
-
if (!keepElements( node ))
|
|
531
|
-
// no 'elements' with SELECT or inferred elements with gensrc;
|
|
532
|
-
// hidden 'elements' will be set in query()
|
|
637
|
+
if (!keepElements( node, obj ))
|
|
533
638
|
return undefined;
|
|
534
|
-
return standard( obj );
|
|
639
|
+
return standard( obj ); // no 'elements' with inferred elements with gensrc
|
|
535
640
|
}
|
|
536
641
|
|
|
537
642
|
function elements( dict, csn, node ) {
|
|
@@ -544,6 +649,18 @@ function elements( dict, csn, node ) {
|
|
|
544
649
|
return insertOrderDict( dict );
|
|
545
650
|
}
|
|
546
651
|
|
|
652
|
+
function enumDict( dict, csn, node ) {
|
|
653
|
+
if (gensrcFlavor && dict[$inferred] ||
|
|
654
|
+
!keepElements( node ))
|
|
655
|
+
// no 'elements' with SELECT or inferred elements with gensrc;
|
|
656
|
+
// hidden or visible 'elements' will be set in query()
|
|
657
|
+
return undefined;
|
|
658
|
+
if (universalCsn && node.type && !node.type.$inferred && node.$expand === 'annotate')
|
|
659
|
+
// derived type of enum type with individual annotations: also set $origin
|
|
660
|
+
csn.$origin = originRef( node.type._artifact );
|
|
661
|
+
return insertOrderDict( dict );
|
|
662
|
+
}
|
|
663
|
+
|
|
547
664
|
function enumerableQueryElements( select ) {
|
|
548
665
|
if (!universalCsn || select === select._main._leadingQuery)
|
|
549
666
|
return false;
|
|
@@ -553,21 +670,35 @@ function enumerableQueryElements( select ) {
|
|
|
553
670
|
return alias.query && (alias.query._leadingQuery || alias.query) === select;
|
|
554
671
|
}
|
|
555
672
|
|
|
556
|
-
//
|
|
557
|
-
|
|
558
|
-
function keepElements( node ) {
|
|
673
|
+
// Should we render the elements? (and items?)
|
|
674
|
+
function keepElements( node, line ) {
|
|
559
675
|
if (universalCsn)
|
|
560
|
-
//
|
|
561
|
-
//
|
|
676
|
+
// $expand = null/undefined: not elements not via expansion
|
|
677
|
+
// $expand = 'target'/'annotate': with redirections / individual annotations
|
|
562
678
|
return node.$expand !== 'origin';
|
|
563
679
|
if (!node.type || node.kind === 'type')
|
|
564
680
|
return true;
|
|
681
|
+
// keep many SimpleType/Entity
|
|
682
|
+
if (line) {
|
|
683
|
+
if (!node.type)
|
|
684
|
+
return true;
|
|
685
|
+
const array = node.type._artifact; // see function items() in propagator.js
|
|
686
|
+
const ltype = line.type && line.type._artifact;
|
|
687
|
+
if (!array || // reference errors
|
|
688
|
+
array._main && !line.elements && !line.enum && !line.items && !line.notNull &&
|
|
689
|
+
(!ltype || !ltype._main)) // many Foo:bar -> not SimpleType
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
692
|
+
// even if expanded elements have no new target or direct annotation,
|
|
693
|
+
// they might have got one via propagation – any new target/annos during their
|
|
694
|
+
// way from the original structure type definition to the current usage
|
|
565
695
|
while (node) {
|
|
566
696
|
if (node.$expand !== 'origin')
|
|
567
697
|
return true;
|
|
568
698
|
node = node._origin;
|
|
569
699
|
}
|
|
570
|
-
|
|
700
|
+
// all in _origin chain only have expanded elements with 'origin':
|
|
701
|
+
return false; // no need to render elements
|
|
571
702
|
}
|
|
572
703
|
|
|
573
704
|
// for gensrcFlavor and namespace/builtin annotation extraction:
|
|
@@ -653,17 +784,17 @@ function sortedDict( dict ) {
|
|
|
653
784
|
return dictionary( dict, keys );
|
|
654
785
|
}
|
|
655
786
|
|
|
656
|
-
function
|
|
787
|
+
function actions( dict ) {
|
|
657
788
|
const keys = Object.keys( dict );
|
|
658
789
|
return (keys.length)
|
|
659
|
-
? dictionary( dict, keys )
|
|
790
|
+
? dictionary( dict, keys, 'actions' )
|
|
660
791
|
: undefined;
|
|
661
792
|
}
|
|
662
793
|
|
|
663
|
-
function dictionary( dict, keys ) {
|
|
794
|
+
function dictionary( dict, keys, prop ) {
|
|
664
795
|
const csn = Object.create( dictionaryPrototype );
|
|
665
796
|
for (const name of keys) {
|
|
666
|
-
const def = definition( dict[name] );
|
|
797
|
+
const def = definition( dict[name], null, null, prop );
|
|
667
798
|
if (def !== undefined)
|
|
668
799
|
csn[name] = def;
|
|
669
800
|
}
|
|
@@ -686,7 +817,7 @@ function foreignKeys( dict, csn, node ) {
|
|
|
686
817
|
csn.keys = keys;
|
|
687
818
|
}
|
|
688
819
|
|
|
689
|
-
function definition( art ) {
|
|
820
|
+
function definition( art, _csn, _node, prop ) {
|
|
690
821
|
if (!art || typeof art !== 'object')
|
|
691
822
|
return undefined; // TODO: complain with strict
|
|
692
823
|
// Do not include namespace definitions or inferred construct (in gensrc):
|
|
@@ -698,44 +829,150 @@ function definition( art ) {
|
|
|
698
829
|
addLocation( art.targetElement.location, key );
|
|
699
830
|
return extra( key, art );
|
|
700
831
|
}
|
|
701
|
-
|
|
832
|
+
const c = standard( art );
|
|
833
|
+
// The XSN of actions in extensions do not contain a returns yet - TODO?
|
|
834
|
+
const elems = c.elements;
|
|
835
|
+
if (elems && (prop === 'actions' || art.$syntax === 'returns')) {
|
|
836
|
+
delete c.elements;
|
|
837
|
+
c.returns = { elements: elems };
|
|
838
|
+
}
|
|
839
|
+
if (kind && kind !== 'key')
|
|
840
|
+
addOrigin( c, art, art._origin );
|
|
841
|
+
return c;
|
|
702
842
|
}
|
|
703
843
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
844
|
+
// create $origin specification for `includes` of `art`
|
|
845
|
+
function includesOrigin( includes, art ) {
|
|
846
|
+
const $origin = originRef( includes[0]._artifact );
|
|
847
|
+
if (includes.length === 1)
|
|
848
|
+
return $origin;
|
|
849
|
+
const result = { $origin };
|
|
850
|
+
for (const incl of includes.slice(1)) {
|
|
851
|
+
const aspect = incl._artifact;
|
|
852
|
+
for (const prop in aspect) {
|
|
853
|
+
if (prop.charAt(0) === '@' && (!art[prop] || art[prop].$inferred)) {
|
|
854
|
+
const anno = aspect[prop];
|
|
855
|
+
if (anno.val !== null)
|
|
856
|
+
// matererialize non-null annos (whether direct or inherited)
|
|
857
|
+
result[prop] = value( Object.create( anno, { $inferred: { value: null } } ) );
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return (Object.keys( result ).length === 1) ? $origin : result;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function addOrigin( csn, xsn, origin ) {
|
|
865
|
+
if (!universalCsn || hasExplicitProp( xsn.type ))
|
|
866
|
+
return;
|
|
707
867
|
if (xsn._from) {
|
|
708
|
-
|
|
868
|
+
const source = xsn._from[0]._origin;
|
|
869
|
+
csn.$origin = originRef( source );
|
|
870
|
+
if (source.params && !xsn.params)
|
|
871
|
+
csn.params = null; // discontinue `params` inheritance
|
|
872
|
+
if (source.actions && !xsn.actions)
|
|
873
|
+
csn.actions = null; // discontinue `actions` inheritance
|
|
874
|
+
return;
|
|
709
875
|
}
|
|
710
|
-
else if (xsn.includes
|
|
711
|
-
csn.$origin =
|
|
876
|
+
else if (xsn.includes) {
|
|
877
|
+
csn.$origin = includesOrigin( xsn.includes, xsn );
|
|
878
|
+
return;
|
|
712
879
|
}
|
|
713
|
-
else if (
|
|
714
|
-
|
|
715
|
-
while (origin._parent && origin._parent.$expand === 'origin')
|
|
716
|
-
origin = origin._origin || origin.type._artifact;
|
|
717
|
-
csn.$origin = originRef( origin );
|
|
880
|
+
else if (!xsn._main || xsn.kind === 'select') {
|
|
881
|
+
return;
|
|
718
882
|
}
|
|
719
|
-
|
|
883
|
+
const parent = getParent( xsn );
|
|
884
|
+
const parentOrigin = getOrigin( parent );
|
|
885
|
+
if (!xsn._origin || xsn._origin.kind === 'builtin') { // or $dollarVariable
|
|
886
|
+
if (parentOrigin && (!parent.enum || parent.$origin || !parent.type))
|
|
887
|
+
csn.$origin = null;
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
// Skip all proxies which do not make it into the CSN, as there are no
|
|
891
|
+
// individual annotations or redirection targets on it:
|
|
892
|
+
while (origin._parent && origin._parent.$expand === 'origin')
|
|
893
|
+
origin = origin._origin || origin.type._artifact;
|
|
894
|
+
// The while loop is not only for the else case below: when setting implicit
|
|
895
|
+
// prototypes, it is important that we do not have to follow the prototypes of
|
|
896
|
+
// other object; we would need to ensure a right order to avoid issues otherwise.
|
|
897
|
+
if (parentOrigin === getParent( origin )) {
|
|
898
|
+
// implicit prototype or shortened reference
|
|
899
|
+
const { id } = origin.name || {};
|
|
900
|
+
if (id && xsn.name && id !== xsn.name.id)
|
|
901
|
+
csn.$origin = id;
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
if (origin.kind === 'mixin') {
|
|
905
|
+
// currently, target and on are always set - nothing to do here, just set type
|
|
906
|
+
csn.type = 'cds.Association';
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
const ref = originRef( origin, xsn );
|
|
910
|
+
if (ref) {
|
|
911
|
+
csn.$origin = ref;
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
// An element of a query with a query in FROM:
|
|
915
|
+
const anon = definition( origin ); // use $origin: {...} if necessary
|
|
916
|
+
// as there are no implicit $origin prototypes on sub query elements (yet),
|
|
917
|
+
// we do not have to care about $origin not being set
|
|
918
|
+
const { $origin } = anon;
|
|
919
|
+
if ($origin && typeof $origin === 'object' && !Array.isArray( $origin )) {
|
|
920
|
+
// repeated anon: flatten
|
|
921
|
+
csn.$origin = Object.assign( $origin, anon );
|
|
922
|
+
}
|
|
923
|
+
else if (Object.keys( anon )
|
|
924
|
+
// (we can use the properties in `csn`, because addOrigin() is called last)
|
|
925
|
+
.every( p => p in csn || p === '$origin' || p === '$location')) {
|
|
926
|
+
// nothing new in $origin: {...}
|
|
927
|
+
addOrigin( csn, xsn, origin._origin );
|
|
928
|
+
}
|
|
929
|
+
else {
|
|
930
|
+
csn.$origin = anon;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
function getParent( art ) {
|
|
935
|
+
const parent = art._parent;
|
|
936
|
+
const main = parent._main;
|
|
937
|
+
return (main && parent === main._leadingQuery) ? main : parent;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function getOrigin( art ) {
|
|
941
|
+
if (art._origin)
|
|
942
|
+
return art._origin;
|
|
943
|
+
if (hasExplicitProp( art.type ))
|
|
944
|
+
return art.type._artifact;
|
|
945
|
+
if (art.includes)
|
|
946
|
+
return art.includes[0]._artifact;
|
|
947
|
+
if (art._from)
|
|
948
|
+
return art._from[0]._origin;
|
|
949
|
+
return undefined;
|
|
720
950
|
}
|
|
721
951
|
|
|
722
952
|
function hasExplicitProp( ref ) {
|
|
723
953
|
return ref && !ref.$inferred;
|
|
724
954
|
}
|
|
725
955
|
|
|
726
|
-
function originRef( art ) {
|
|
956
|
+
function originRef( art, user ) {
|
|
727
957
|
const r = [];
|
|
728
958
|
// do not use name.element, as we allow `.`s in name
|
|
729
|
-
let
|
|
730
|
-
while (
|
|
731
|
-
const nkind = normalizedKind[
|
|
732
|
-
if (
|
|
733
|
-
|
|
734
|
-
|
|
959
|
+
let parent = art;
|
|
960
|
+
while (parent._main && parent.kind !== 'select') {
|
|
961
|
+
const nkind = normalizedKind[parent.kind];
|
|
962
|
+
if (parent.name.id || !r.length)
|
|
963
|
+
// Return parameter is in XSN - kind: 'param', name.id: ''
|
|
964
|
+
// eslint-disable-next-line no-nested-ternary, max-len
|
|
965
|
+
r.push( !nkind ? parent.name.id : parent.name.id ? { [nkind]: parent.name.id } : { return: true } );
|
|
966
|
+
parent = parent._parent;
|
|
735
967
|
}
|
|
736
|
-
if (
|
|
737
|
-
|
|
968
|
+
if (user && parent._main && parent._main === user._main && parent !== user._main._leadingQuery)
|
|
969
|
+
// well, an element of an query in FROM (TODO: try with sub elem), but not the leading query
|
|
970
|
+
return null; // probably use $origin: {...}
|
|
738
971
|
// for sub query in FROM in sub query in FROM, we could condense the info
|
|
972
|
+
|
|
973
|
+
// Now the ref, with ["absolute", "action"] instead of ["absolute", {action:"action"}]
|
|
974
|
+
if (r.length === 1 && normalizedKind[art.kind] === 'action')
|
|
975
|
+
return [ art.name.absolute, art.name.id ];
|
|
739
976
|
r.push( art.name.absolute );
|
|
740
977
|
r.reverse();
|
|
741
978
|
return r;
|
|
@@ -750,13 +987,11 @@ function kind( k, csn, node ) {
|
|
|
750
987
|
else if (k === 'extend')
|
|
751
988
|
csn.kind = k;
|
|
752
989
|
}
|
|
753
|
-
else
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
csn.kind = k;
|
|
759
|
-
addOrigin( csn, node );
|
|
990
|
+
else if (![
|
|
991
|
+
'element', 'key', 'param', 'enum', 'select', '$join',
|
|
992
|
+
'$tableAlias', 'annotation', 'mixin',
|
|
993
|
+
].includes(k)) {
|
|
994
|
+
csn.kind = k;
|
|
760
995
|
}
|
|
761
996
|
}
|
|
762
997
|
|
|
@@ -865,10 +1100,12 @@ function args( node ) {
|
|
|
865
1100
|
return dict;
|
|
866
1101
|
}
|
|
867
1102
|
|
|
868
|
-
// "Short" value form, e.g. for annotation assignments
|
|
869
1103
|
function value( node ) {
|
|
1104
|
+
// "Short" value form, e.g. for annotation assignments
|
|
870
1105
|
if (!node)
|
|
871
1106
|
return true; // `@aBool` short for `@aBool: true`
|
|
1107
|
+
if (universalCsn && node.$inferred === 'prop') // via propagator.js
|
|
1108
|
+
return undefined;
|
|
872
1109
|
if (node.$inferred && gensrcFlavor)
|
|
873
1110
|
return undefined;
|
|
874
1111
|
if (node.path) {
|
|
@@ -892,7 +1129,10 @@ function value( node ) {
|
|
|
892
1129
|
|
|
893
1130
|
function enumValue( v, csn, node ) {
|
|
894
1131
|
// Enums can have values but if enums are extended, their kind is 'element',
|
|
895
|
-
// so we check whether the node is inside an extension.
|
|
1132
|
+
// so we check whether the node is inside an extension. (TODO: still?)
|
|
1133
|
+
if (universalCsn && v.$inferred)
|
|
1134
|
+
return;
|
|
1135
|
+
// (with gensrc, the symbol itself would not make it into the CSN)
|
|
896
1136
|
if (node.kind === 'enum' || node._parent && node._parent.kind === 'extend')
|
|
897
1137
|
Object.assign( csn, expression( v, true ) );
|
|
898
1138
|
}
|
|
@@ -911,7 +1151,7 @@ function onCondition( cond, csn, node ) {
|
|
|
911
1151
|
function condition( node ) {
|
|
912
1152
|
const expr = expression( node );
|
|
913
1153
|
// we do not set a hidden $parens on array - we could still do it if requested
|
|
914
|
-
return !expr.cast && expr.xpr || [ expr ];
|
|
1154
|
+
return !expr.cast && !expr.func && expr.xpr || [ expr ];
|
|
915
1155
|
}
|
|
916
1156
|
|
|
917
1157
|
function expression( node, dollarExtra ) {
|
|
@@ -951,10 +1191,12 @@ function expression( node, dollarExtra ) {
|
|
|
951
1191
|
arg0.xpr.unshift( quantifier.val );
|
|
952
1192
|
}
|
|
953
1193
|
}
|
|
1194
|
+
if (node.suffix)
|
|
1195
|
+
call.xpr = [].concat( ...node.suffix.map( xprArg ) );
|
|
954
1196
|
return extra( call, dollarExtraNode );
|
|
955
1197
|
}
|
|
956
1198
|
if (node.query)
|
|
957
|
-
return query( node.query, null, null, 1 );
|
|
1199
|
+
return query( node.query, null, null, null, 1 );
|
|
958
1200
|
if (!node.op) // parse error
|
|
959
1201
|
return { xpr: [] };
|
|
960
1202
|
else if (node.op.val === 'xpr')
|
|
@@ -964,7 +1206,7 @@ function expression( node, dollarExtra ) {
|
|
|
964
1206
|
return cast( expression( node.args[0] ), dollarExtraNode );
|
|
965
1207
|
// from here on: CDL input (no $extra possible - but $parens)
|
|
966
1208
|
else if (node.op.val !== ',')
|
|
967
|
-
return extra( { xpr: xpr( node ) }, dollarExtraNode, 1 );
|
|
1209
|
+
return extra( { xpr: xpr( node ) }, dollarExtraNode, (dollarExtra === 'sub-xpr' ? 1 : 0) );
|
|
968
1210
|
return (parensAsStrings)
|
|
969
1211
|
? { xpr: [ '(', ...xpr( node ), ')' ] }
|
|
970
1212
|
// the inner parens belong to the tuple construct, i.e. won't count as parens
|
|
@@ -974,15 +1216,7 @@ function expression( node, dollarExtra ) {
|
|
|
974
1216
|
function xpr( node ) {
|
|
975
1217
|
// if (!node.op) console.log(node)
|
|
976
1218
|
const op = operators[node.op.val] || node.op.val.split(' ');
|
|
977
|
-
const exprs = node.args.map(
|
|
978
|
-
const expr = expression( sub );
|
|
979
|
-
// return !sub.$parens && !expr.cast && expr.xpr || [ expr ]; if parensAsStrings is gone
|
|
980
|
-
if (expr.cast || !expr.xpr || sub.$parens && !parensAsStrings)
|
|
981
|
-
return [ expr ];
|
|
982
|
-
else if (sub.$parens && sub.op.val !== ',')
|
|
983
|
-
return [ '(', ...expr.xpr, ')' ];
|
|
984
|
-
return expr.xpr;
|
|
985
|
-
} );
|
|
1219
|
+
const exprs = node.args.map( xprArg );
|
|
986
1220
|
if (op instanceof Function)
|
|
987
1221
|
return op( exprs );
|
|
988
1222
|
if (node.quantifier)
|
|
@@ -992,6 +1226,27 @@ function xpr( node ) {
|
|
|
992
1226
|
return exprs[0].concat( ...exprs.slice(1).map( a => [ ...op, ...a ] ) );
|
|
993
1227
|
}
|
|
994
1228
|
|
|
1229
|
+
function xprArg( sub ) {
|
|
1230
|
+
const realXpr = sub.op && sub.op.val === 'xpr';
|
|
1231
|
+
const expr = expression( sub, 'sub-xpr' );
|
|
1232
|
+
// `sort`/`nulls` will be attached to arguments of orderBy
|
|
1233
|
+
// which might be either `path`s or `xpr`s
|
|
1234
|
+
const sortAndNulls = [];
|
|
1235
|
+
if (sub.sort)
|
|
1236
|
+
sortAndNulls.push( sub.sort.val );
|
|
1237
|
+
if (sub.nulls)
|
|
1238
|
+
sortAndNulls.push( ...[ 'nulls', sub.nulls.val ] );
|
|
1239
|
+
// return !sub.$parens && !expr.cast && !expr.func && expr.xpr || [ expr ];
|
|
1240
|
+
// if parensAsStrings is gone
|
|
1241
|
+
if (realXpr || expr.cast || expr.func || !expr.xpr || sub.$parens && !parensAsStrings)
|
|
1242
|
+
return [ expr, ...sortAndNulls ];
|
|
1243
|
+
else if (sub.$parens && sub.op.val !== ',')
|
|
1244
|
+
return [ '(', ...expr.xpr, ')' ];
|
|
1245
|
+
|
|
1246
|
+
expr.xpr.push( ...sortAndNulls );
|
|
1247
|
+
return expr.xpr;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
995
1250
|
function ternary( op1, op2 ) {
|
|
996
1251
|
return function ternaryOp( exprs ) {
|
|
997
1252
|
return (exprs[2])
|
|
@@ -1015,7 +1270,7 @@ function binaryRightParen( op ) {
|
|
|
1015
1270
|
};
|
|
1016
1271
|
}
|
|
1017
1272
|
|
|
1018
|
-
function query( node, csn, xsn, expectedParens = 0 ) {
|
|
1273
|
+
function query( node, csn, xsn, _prop, expectedParens = 0 ) {
|
|
1019
1274
|
if (node.op.val === 'SELECT') {
|
|
1020
1275
|
if (xsn && xsn.query === node && xsn.$syntax === 'projection' &&
|
|
1021
1276
|
node.from && node.from.path && !projectionAsQuery) {
|
|
@@ -1065,7 +1320,7 @@ function columns( xsnColumns, csn, xsn ) {
|
|
|
1065
1320
|
addElementAsColumn( col, csnColumns );
|
|
1066
1321
|
}
|
|
1067
1322
|
}
|
|
1068
|
-
else {
|
|
1323
|
+
else { // null = use elements - TODO: still used by A2J? -> remove
|
|
1069
1324
|
for (const name in xsn.elements)
|
|
1070
1325
|
addElementAsColumn( xsn.elements[name], csnColumns );
|
|
1071
1326
|
}
|
|
@@ -1087,7 +1342,7 @@ function from( node ) {
|
|
|
1087
1342
|
return extra( join, node );
|
|
1088
1343
|
}
|
|
1089
1344
|
else if (node.query) {
|
|
1090
|
-
return addExplicitAs( query( node.query, null, null, 1 ), node.name );
|
|
1345
|
+
return addExplicitAs( query( node.query, null, null, null, 1 ), node.name );
|
|
1091
1346
|
}
|
|
1092
1347
|
else if (!node._artifact || node._artifact._main) { // CQL or follow assoc
|
|
1093
1348
|
return extra( addExplicitAs( artifactRef( node, false ), node.name ), node );
|
|
@@ -1132,12 +1387,6 @@ function addElementAsColumn( elem, cols ) {
|
|
|
1132
1387
|
finally {
|
|
1133
1388
|
gensrcFlavor = gensrcSaved;
|
|
1134
1389
|
}
|
|
1135
|
-
// FIXME: Currently toHana requires that an '_ignore' property on the elem is
|
|
1136
|
-
// also visible on the column. Don't ignore virtual columns, let the
|
|
1137
|
-
// renderer decide how to render that column.
|
|
1138
|
-
if (!elem.virtual && elem._ignore)
|
|
1139
|
-
col._ignore = true;
|
|
1140
|
-
|
|
1141
1390
|
if (elem.value && !elem.$inferred) {
|
|
1142
1391
|
const parens = elem.value.$parens;
|
|
1143
1392
|
if (parens)
|