@sap/cds-compiler 2.5.0 → 2.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +191 -9
- package/bin/cdsc.js +2 -2
- package/doc/CHANGELOG_BETA.md +33 -3
- package/lib/api/main.js +29 -101
- package/lib/api/options.js +15 -11
- package/lib/api/validate.js +12 -8
- package/lib/backends.js +0 -81
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +63 -9
- package/lib/base/messages.js +63 -21
- package/lib/base/model.js +2 -3
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +25 -7
- package/lib/checks/selectItems.js +25 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +38 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +16 -7
- package/lib/compiler/builtins.js +2 -0
- package/lib/compiler/checks.js +6 -4
- package/lib/compiler/definer.js +99 -42
- package/lib/compiler/index.js +73 -27
- package/lib/compiler/resolver.js +288 -157
- package/lib/compiler/shared.js +31 -11
- package/lib/edm/annotations/genericTranslation.js +182 -186
- package/lib/edm/csn2edm.js +103 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +361 -114
- package/lib/edm/edmUtils.js +103 -33
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +12 -1
- package/lib/gen/language.tokens +57 -53
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +770 -744
- package/lib/gen/languageLexer.tokens +49 -46
- package/lib/gen/languageParser.js +4713 -4279
- package/lib/json/from-csn.js +103 -45
- package/lib/json/to-csn.js +296 -117
- package/lib/language/antlrParser.js +4 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +21 -12
- package/lib/language/language.g4 +99 -31
- package/lib/main.d.ts +81 -3
- package/lib/main.js +30 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +329 -142
- package/lib/model/csnUtils.js +235 -58
- package/lib/model/enrichCsn.js +18 -1
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/modelCompare/compare.js +37 -20
- package/lib/optionProcessor.js +9 -3
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +8 -5
- package/lib/render/toCdl.js +112 -33
- package/lib/render/toHdbcds.js +134 -64
- package/lib/render/toSql.js +91 -38
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +29 -13
- package/lib/transform/db/draft.js +8 -6
- package/lib/transform/db/expansion.js +582 -0
- package/lib/transform/db/flattening.js +325 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +284 -63
- package/lib/transform/forHanaNew.js +98 -381
- package/lib/transform/forOdataNew.js +21 -22
- package/lib/transform/localized.js +37 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +134 -78
- package/lib/transform/translateAssocsToJoins.js +17 -14
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +0 -11
- package/lib/utils/moduleResolve.js +6 -8
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
package/lib/json/to-csn.js
CHANGED
|
@@ -12,14 +12,21 @@
|
|
|
12
12
|
'use strict';
|
|
13
13
|
|
|
14
14
|
const { locationString } = require('../base/messages');
|
|
15
|
-
const { isDeprecatedEnabled } = require('../base/model');
|
|
15
|
+
const { isBetaEnabled, isDeprecatedEnabled } = require('../base/model');
|
|
16
16
|
|
|
17
17
|
const compilerVersion = require('../../package.json').version;
|
|
18
18
|
const creator = `CDS Compiler v${ compilerVersion }`;
|
|
19
19
|
const csnVersion = '2.0';
|
|
20
20
|
|
|
21
|
+
const normalizedKind = {
|
|
22
|
+
param: 'param',
|
|
23
|
+
action: 'action',
|
|
24
|
+
function: 'action',
|
|
25
|
+
};
|
|
26
|
+
|
|
21
27
|
/** @type {boolean|string} */
|
|
22
28
|
let gensrcFlavor = true; // good enough here...
|
|
29
|
+
let universalCsn = false;
|
|
23
30
|
let strictMode = false; // whether to dump with unknown properties (in standard)
|
|
24
31
|
let parensAsStrings = false;
|
|
25
32
|
let projectionAsQuery = false;
|
|
@@ -31,6 +38,7 @@ let dictionaryPrototype = null;
|
|
|
31
38
|
const transformers = {
|
|
32
39
|
// early and modifiers (without null / not null) ---------------------------
|
|
33
40
|
kind,
|
|
41
|
+
_outer: ( _, csn, node ) => addOrigin( csn, node ),
|
|
34
42
|
id: n => n, // in path item
|
|
35
43
|
doc: value,
|
|
36
44
|
'@': value,
|
|
@@ -50,7 +58,7 @@ const transformers = {
|
|
|
50
58
|
all: ignore, // XSN TODO use quantifier
|
|
51
59
|
// type properties (without 'elements') ------------------------------------
|
|
52
60
|
localized: value,
|
|
53
|
-
type
|
|
61
|
+
type,
|
|
54
62
|
length: value,
|
|
55
63
|
precision: value,
|
|
56
64
|
scale: value,
|
|
@@ -72,6 +80,7 @@ const transformers = {
|
|
|
72
80
|
where: condition, // also pathItem after 'cardinality' before 'args'
|
|
73
81
|
having: condition,
|
|
74
82
|
args, // also pathItem after 'where', before 'on'/'orderBy'
|
|
83
|
+
suffix: node => [].concat( ...node.suffix.map( xprArg ) ),
|
|
75
84
|
orderBy: arrayOf( orderBy ), // TODO XSN: make `sort` and `nulls` sibling properties
|
|
76
85
|
sort: value,
|
|
77
86
|
nulls: value,
|
|
@@ -80,14 +89,14 @@ const transformers = {
|
|
|
80
89
|
offset: expression,
|
|
81
90
|
on: onCondition,
|
|
82
91
|
// definitions, extensions, members ----------------------------------------
|
|
83
|
-
returns:
|
|
92
|
+
returns: definition, // storing the return type of actions
|
|
84
93
|
notNull: value,
|
|
85
94
|
default: expression,
|
|
86
95
|
// targetElement: ignore, // special display of foreign key, renameTo: select
|
|
87
96
|
value: enumValue, // do not list for select items as elements
|
|
88
97
|
query,
|
|
89
98
|
elements,
|
|
90
|
-
actions
|
|
99
|
+
actions, // TODO: just normal dictionary
|
|
91
100
|
// special: top-level, cardinality -----------------------------------------
|
|
92
101
|
sources,
|
|
93
102
|
definitions: sortedDict,
|
|
@@ -128,7 +137,7 @@ const transformers = {
|
|
|
128
137
|
// which should appear at that place in order.
|
|
129
138
|
const csnPropertyNames = {
|
|
130
139
|
virtual: [ 'abstract' ], // abstract is compiler v1 CSN property
|
|
131
|
-
kind: [ 'annotate', 'extend' ],
|
|
140
|
+
kind: [ 'annotate', 'extend', '$origin' ],
|
|
132
141
|
op: [ 'join', 'func', 'xpr' ], // TODO: 'func','xpr' into 'quantifier'? TODO: 'global'(scope)?
|
|
133
142
|
quantifier: [
|
|
134
143
|
'some', 'any', 'distinct', // 'all' explicitly listed
|
|
@@ -148,7 +157,7 @@ const csnPropertyNames = {
|
|
|
148
157
|
name: [ 'as', 'cast' ],
|
|
149
158
|
location: [ '$env', '$location' ], // --enrich-csn
|
|
150
159
|
expectedKind: [
|
|
151
|
-
'_type', '_targetAspect', '_target', '_includes', '_links', '_art', '_scope',
|
|
160
|
+
'_origin', '_type', '_targetAspect', '_target', '_includes', '_links', '_art', '_scope',
|
|
152
161
|
], // --enrich-csn
|
|
153
162
|
};
|
|
154
163
|
|
|
@@ -183,6 +192,13 @@ const operators = {
|
|
|
183
192
|
notLike: ternary( [ 'not', 'like' ], [ 'escape' ] ),
|
|
184
193
|
when: exprs => [ 'when', ...exprs[0], 'then', ...exprs[1] ],
|
|
185
194
|
case: exprs => [ 'case' ].concat( ...exprs, [ 'end' ] ),
|
|
195
|
+
over: exprs => [ 'over', { xpr: [].concat( ...exprs ) } ],
|
|
196
|
+
orderBy: exprs => [
|
|
197
|
+
'order', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
198
|
+
],
|
|
199
|
+
partitionBy: exprs => [
|
|
200
|
+
'partition', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
201
|
+
],
|
|
186
202
|
// xpr: (exprs) => [].concat( ...exprs ), see below - handled extra
|
|
187
203
|
};
|
|
188
204
|
|
|
@@ -194,7 +210,7 @@ const csnDirectValues = [ 'val' ]; // + all starting with '@' - TODO: still rele
|
|
|
194
210
|
/**
|
|
195
211
|
* Sort property names of CSN according to sequence which is also used by the compactModel function
|
|
196
212
|
* Only returns enumerable properties, except for certain hidden properties
|
|
197
|
-
* if requested (cloneOptions != false): $location,
|
|
213
|
+
* if requested (cloneOptions != false): $location, elements.
|
|
198
214
|
*
|
|
199
215
|
* If cloneOptions is false or if either cloneOptions.testMode or cloneOptions.testSortCsn
|
|
200
216
|
* are set, definitions are also sorted.
|
|
@@ -224,24 +240,36 @@ function sortCsn( csn, cloneOptions = false ) {
|
|
|
224
240
|
r[n] = sortCsn(val, cloneOptions);
|
|
225
241
|
}
|
|
226
242
|
if (cloneOptions && typeof csn === 'object') {
|
|
227
|
-
if (csn
|
|
228
|
-
setHidden(r, '$sources', csn.$sources);
|
|
229
|
-
if (csn
|
|
230
|
-
setHidden(r, '$location', csn.$location);
|
|
231
|
-
if (csn
|
|
232
|
-
setHidden(r, '$
|
|
233
|
-
if (csn
|
|
234
|
-
setHidden(r, '$
|
|
235
|
-
if (csn
|
|
236
|
-
setHidden(r, '
|
|
237
|
-
if (csn
|
|
238
|
-
setHidden(r, '
|
|
239
|
-
if (csn.$tableConstraints && !r.$tableConstraints)
|
|
240
|
-
setHidden(r, '$tableConstraints', csn.$tableConstraints);
|
|
243
|
+
if ({}.hasOwnProperty.call( csn, '$sources' ) && !r.$sources)
|
|
244
|
+
setHidden( r, '$sources', csn.$sources );
|
|
245
|
+
if ({}.hasOwnProperty.call( csn, '$location' ) && !r.$location)
|
|
246
|
+
setHidden( r, '$location', csn.$location );
|
|
247
|
+
if ({}.hasOwnProperty.call( csn, '$path' )) // used in generic reference flattener
|
|
248
|
+
setHidden( r, '$path', csn.$path );
|
|
249
|
+
if ({}.hasOwnProperty.call( csn, '$paths' )) // used in generic reference flattener
|
|
250
|
+
setHidden( r, '$paths', csn.$paths );
|
|
251
|
+
if (hasNonEnumerable( csn, 'elements' ) && !r.elements) // non-enumerable 'elements'
|
|
252
|
+
setHidden( r, 'elements', csnDictionary( csn.elements, false, cloneOptions ) );
|
|
253
|
+
if (hasNonEnumerable( csn, '$tableConstraints' ) && !r.$tableConstraints)
|
|
254
|
+
setHidden( r, '$tableConstraints', csn.$tableConstraints );
|
|
241
255
|
}
|
|
242
256
|
return r;
|
|
243
257
|
}
|
|
244
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Check wether the given object has non enumerable property.
|
|
261
|
+
* Ensure that we don't take it from the prototype, only "directly" - we accidentally
|
|
262
|
+
* cloned elements with a cds.linked input otherwise.
|
|
263
|
+
*
|
|
264
|
+
* @param {object} object
|
|
265
|
+
* @param {string} property
|
|
266
|
+
* @returns
|
|
267
|
+
*/
|
|
268
|
+
function hasNonEnumerable(object, property) {
|
|
269
|
+
return {}.hasOwnProperty.call( object, property ) &&
|
|
270
|
+
!{}.propertyIsEnumerable.call( object, property );
|
|
271
|
+
}
|
|
272
|
+
|
|
245
273
|
/**
|
|
246
274
|
* @param {object} csn
|
|
247
275
|
* @param {boolean} sort
|
|
@@ -353,10 +381,12 @@ function usings( srcDict ) {
|
|
|
353
381
|
* @param {object} csn
|
|
354
382
|
* @param {object} model
|
|
355
383
|
*/
|
|
384
|
+
|
|
385
|
+
|
|
356
386
|
function extensions( node, csn, model ) {
|
|
357
387
|
if (model.kind && model.kind !== 'source')
|
|
358
388
|
return undefined;
|
|
359
|
-
const exts = node.map(
|
|
389
|
+
const exts = node.map( definition );
|
|
360
390
|
|
|
361
391
|
// builtins are non-enumerable for smaller display
|
|
362
392
|
for (const name of Object.getOwnPropertyNames( model.definitions || {} ).sort()) {
|
|
@@ -371,17 +401,21 @@ function extensions( node, csn, model ) {
|
|
|
371
401
|
}
|
|
372
402
|
else if (gensrcFlavor) {
|
|
373
403
|
// From definitions (without redefinitions) with potential inferred elements:
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
if (
|
|
381
|
-
annotate.
|
|
382
|
-
|
|
383
|
-
|
|
404
|
+
const annotate = { annotate: name };
|
|
405
|
+
if (art.$inferred)
|
|
406
|
+
Object.assign( annotate, annotationsAndDocComment( art, true ) );
|
|
407
|
+
if (art.$expand === 'annotate') {
|
|
408
|
+
if (art.actions)
|
|
409
|
+
attachAnnotations( annotate, 'actions', art.actions, art.$inferred );
|
|
410
|
+
else if (art.params)
|
|
411
|
+
attachAnnotations( annotate, 'params', art.params, art.$inferred );
|
|
412
|
+
const obj = art.returns || art;
|
|
413
|
+
const elems = (obj.items || obj).elements; // no targetAspect here
|
|
414
|
+
if (elems)
|
|
415
|
+
attachAnnotations( annotate, 'elements', elems, art.$inferred, art.returns );
|
|
384
416
|
}
|
|
417
|
+
if (Object.keys( annotate ).length > 1)
|
|
418
|
+
exts.push( annotate );
|
|
385
419
|
}
|
|
386
420
|
}
|
|
387
421
|
|
|
@@ -389,6 +423,58 @@ function extensions( node, csn, model ) {
|
|
|
389
423
|
(a, b) => (a.annotate || a.extend).localeCompare( b.annotate || b.extend )
|
|
390
424
|
);
|
|
391
425
|
|
|
426
|
+
/*
|
|
427
|
+
function attachElementAnnos( annotate, art ) {
|
|
428
|
+
while (art.items)
|
|
429
|
+
art = art.items;
|
|
430
|
+
if (art.elements) {
|
|
431
|
+
const elems = inferred( art.elements, art.$inferred );
|
|
432
|
+
if (Object.keys( elems ).length)
|
|
433
|
+
annotate.elements = elems;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function attachParamAnnos( annotate, art ) {
|
|
438
|
+
const inferredParent = art.$inferred;
|
|
439
|
+
if (art.params) {
|
|
440
|
+
const ext = Object.create( dictionaryPrototype );
|
|
441
|
+
for (const name in art.params) {
|
|
442
|
+
const par = art.params[name];
|
|
443
|
+
if (!inferredParent && !par.$inferred && par.$expand !== 'annotate')
|
|
444
|
+
continue;
|
|
445
|
+
const render = annotationsAndDocComment( par, true );
|
|
446
|
+
const subElems = par.$expand !== 'origin' && (par.items || par).elements;
|
|
447
|
+
if (subElems) {
|
|
448
|
+
const sub = inferred( subElems, par.$inferred );
|
|
449
|
+
if (Object.keys( sub ).length)
|
|
450
|
+
render.elements = sub;
|
|
451
|
+
}
|
|
452
|
+
if (Object.keys(render).length)
|
|
453
|
+
ext[name] = render;
|
|
454
|
+
}
|
|
455
|
+
if (obj.keys( ext ))
|
|
456
|
+
annotate.params = ext;
|
|
457
|
+
}
|
|
458
|
+
if (art.returns) {
|
|
459
|
+
const par = art.returns;
|
|
460
|
+
if (!inferredParent && !par.$inferred && par.$expand !== 'annotate')
|
|
461
|
+
return;
|
|
462
|
+
const render = annotationsAndDocComment( par, true );
|
|
463
|
+
const subElems = par.$expand !== 'origin' && (par.items || par).elements;
|
|
464
|
+
if (subElems) {
|
|
465
|
+
const sub = inferred( subElems, par.$inferred );
|
|
466
|
+
if (Object.keys( sub ).length)
|
|
467
|
+
render.elements = sub;
|
|
468
|
+
}
|
|
469
|
+
if (Object.keys(render).length)
|
|
470
|
+
const sub = inferred( subElems, par.$inferred );
|
|
471
|
+
if (Object.keys( sub ).length)
|
|
472
|
+
render.elements = sub;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return ext;
|
|
476
|
+
*/
|
|
477
|
+
|
|
392
478
|
// extract namespace/builtin annotations
|
|
393
479
|
function extractAnnotationsToExtension( art ) {
|
|
394
480
|
const name = art.name.absolute;
|
|
@@ -450,17 +536,29 @@ function sources( srcDict, csn ) {
|
|
|
450
536
|
}
|
|
451
537
|
}
|
|
452
538
|
|
|
453
|
-
function
|
|
454
|
-
const
|
|
455
|
-
for (const name in
|
|
456
|
-
const elem =
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
539
|
+
function attachAnnotations( annotate, prop, dict, inferred, returns = false ) {
|
|
540
|
+
const annoDict = Object.create( dictionaryPrototype );
|
|
541
|
+
for (const name in dict) {
|
|
542
|
+
const elem = dict[name];
|
|
543
|
+
const inf = inferred || elem.$inferred; // is probably always inferred if parent was
|
|
544
|
+
const sub = (inf) ? annotationsAndDocComment( elem, true ) : {};
|
|
545
|
+
if (elem.$expand === 'annotate') {
|
|
546
|
+
if (elem.params)
|
|
547
|
+
attachAnnotations( sub, 'params', elem.params, inf );
|
|
548
|
+
const obj = elem.returns || elem;
|
|
549
|
+
const elems = (obj.items || obj.targetAspect || obj).elements;
|
|
550
|
+
if (elems)
|
|
551
|
+
attachAnnotations( sub, 'elements', elems, inf, elem.returns );
|
|
552
|
+
}
|
|
553
|
+
if (Object.keys( sub ).length)
|
|
554
|
+
annoDict[name] = sub;
|
|
555
|
+
}
|
|
556
|
+
if (Object.keys( annoDict ).length) {
|
|
557
|
+
if (returns)
|
|
558
|
+
annotate.returns = { elements: annoDict };
|
|
559
|
+
else
|
|
560
|
+
annotate[prop] = annoDict;
|
|
462
561
|
}
|
|
463
|
-
return ext;
|
|
464
562
|
}
|
|
465
563
|
|
|
466
564
|
function standard( node ) {
|
|
@@ -499,48 +597,73 @@ function set( prop, csn, node ) {
|
|
|
499
597
|
}
|
|
500
598
|
|
|
501
599
|
function targetAspect( val, csn, node ) {
|
|
600
|
+
const ta = (val.elements)
|
|
601
|
+
? addLocation( val.location, standard( val ) )
|
|
602
|
+
: artifactRef( val, true );
|
|
502
603
|
if (!gensrcFlavor || node.target && !node.target.$inferred)
|
|
503
|
-
return
|
|
604
|
+
return ta;
|
|
504
605
|
// For compatibility, put aspect in 'target' with parse.cdl and csn flavor 'gensrc'
|
|
505
|
-
csn.target =
|
|
606
|
+
csn.target = ta;
|
|
506
607
|
return undefined;
|
|
507
608
|
}
|
|
508
609
|
|
|
509
610
|
function target( val, _csn, node ) {
|
|
510
611
|
if (gensrcFlavor && node._origin && node._origin.$inferred === 'REDIRECTED')
|
|
511
612
|
val = node._origin.target;
|
|
512
|
-
if (
|
|
613
|
+
if (val.elements)
|
|
614
|
+
return standard( val ); // elements in target (parse-cdl)
|
|
615
|
+
if (!universalCsn || node.on)
|
|
513
616
|
return artifactRef( val, true );
|
|
514
|
-
|
|
617
|
+
const tref = artifactRef( val, true );
|
|
618
|
+
const proto = node.type && !node.type.$inferred ? node.type._artifact : node._origin;
|
|
619
|
+
return (proto && proto.target && artifactRef( proto.target, true ) === tref)
|
|
620
|
+
? undefined
|
|
621
|
+
: tref;
|
|
515
622
|
}
|
|
516
623
|
|
|
517
624
|
function items( obj, csn, node ) {
|
|
518
|
-
if (!keepElements( node )
|
|
519
|
-
// no 'elements' with SELECT or inferred elements with gensrc;
|
|
520
|
-
// hidden 'elements' will be set in query()
|
|
625
|
+
if (!keepElements( node ))
|
|
521
626
|
return undefined;
|
|
522
|
-
return standard( obj );
|
|
627
|
+
return standard( obj ); // no 'elements' with inferred elements with gensrc
|
|
523
628
|
}
|
|
524
629
|
|
|
525
630
|
function elements( dict, csn, node ) {
|
|
526
|
-
if (
|
|
631
|
+
if (node.from || // do not directly show query elements here
|
|
527
632
|
gensrcFlavor && (node.query || node.type) ||
|
|
528
|
-
|
|
633
|
+
!keepElements( node ))
|
|
529
634
|
// no 'elements' with SELECT or inferred elements with gensrc;
|
|
530
|
-
// hidden 'elements' will be set in query()
|
|
635
|
+
// hidden or visible 'elements' will be set in query()
|
|
531
636
|
return undefined;
|
|
532
637
|
return insertOrderDict( dict );
|
|
533
638
|
}
|
|
534
639
|
|
|
535
|
-
|
|
536
|
-
|
|
640
|
+
function enumerableQueryElements( select ) {
|
|
641
|
+
if (!universalCsn || select === select._main._leadingQuery)
|
|
642
|
+
return false;
|
|
643
|
+
if (select.orderBy || select.$orderBy)
|
|
644
|
+
return true;
|
|
645
|
+
const alias = select._parent;
|
|
646
|
+
return alias.query && (alias.query._leadingQuery || alias.query) === select;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// Should we render the elements? (and items?)
|
|
537
650
|
function keepElements( node ) {
|
|
651
|
+
if (universalCsn)
|
|
652
|
+
// $expand = null/undefined: not elements not via expansion
|
|
653
|
+
// $expand = 'target'/'annotate': with redirections / individual annotations
|
|
654
|
+
return node.$expand !== 'origin';
|
|
655
|
+
if (!node.type || node.kind === 'type')
|
|
656
|
+
return true;
|
|
657
|
+
// even if expanded elements have no new target or direct annotation,
|
|
658
|
+
// they might have got one via propagation – any new target/annos during their
|
|
659
|
+
// way from the original structure type definition to the current usage
|
|
538
660
|
while (node) {
|
|
539
661
|
if (node.$expand !== 'origin')
|
|
540
662
|
return true;
|
|
541
663
|
node = node._origin;
|
|
542
664
|
}
|
|
543
|
-
|
|
665
|
+
// all in _origin chain only have expanded elements with 'origin':
|
|
666
|
+
return false; // no need to render elements
|
|
544
667
|
}
|
|
545
668
|
|
|
546
669
|
// for gensrcFlavor and namespace/builtin annotation extraction:
|
|
@@ -626,17 +749,17 @@ function sortedDict( dict ) {
|
|
|
626
749
|
return dictionary( dict, keys );
|
|
627
750
|
}
|
|
628
751
|
|
|
629
|
-
function
|
|
752
|
+
function actions( dict ) {
|
|
630
753
|
const keys = Object.keys( dict );
|
|
631
754
|
return (keys.length)
|
|
632
|
-
? dictionary( dict, keys )
|
|
755
|
+
? dictionary( dict, keys, 'actions' )
|
|
633
756
|
: undefined;
|
|
634
757
|
}
|
|
635
758
|
|
|
636
|
-
function dictionary( dict, keys ) {
|
|
759
|
+
function dictionary( dict, keys, prop ) {
|
|
637
760
|
const csn = Object.create( dictionaryPrototype );
|
|
638
761
|
for (const name of keys) {
|
|
639
|
-
const def = definition( dict[name] );
|
|
762
|
+
const def = definition( dict[name], null, null, prop );
|
|
640
763
|
if (def !== undefined)
|
|
641
764
|
csn[name] = def;
|
|
642
765
|
}
|
|
@@ -644,6 +767,8 @@ function dictionary( dict, keys ) {
|
|
|
644
767
|
}
|
|
645
768
|
|
|
646
769
|
function foreignKeys( dict, csn, node ) {
|
|
770
|
+
if (universalCsn && !target( node.target, csn, node ))
|
|
771
|
+
return;
|
|
647
772
|
if (gensrcFlavor && node._origin && node._origin.$inferred === 'REDIRECTED')
|
|
648
773
|
dict = node._origin.foreignKeys;
|
|
649
774
|
const keys = [];
|
|
@@ -657,7 +782,7 @@ function foreignKeys( dict, csn, node ) {
|
|
|
657
782
|
csn.keys = keys;
|
|
658
783
|
}
|
|
659
784
|
|
|
660
|
-
function definition( art ) {
|
|
785
|
+
function definition( art, _csn, _node, prop ) {
|
|
661
786
|
if (!art || typeof art !== 'object')
|
|
662
787
|
return undefined; // TODO: complain with strict
|
|
663
788
|
// Do not include namespace definitions or inferred construct (in gensrc):
|
|
@@ -669,22 +794,79 @@ function definition( art ) {
|
|
|
669
794
|
addLocation( art.targetElement.location, key );
|
|
670
795
|
return extra( key, art );
|
|
671
796
|
}
|
|
672
|
-
|
|
797
|
+
const c = standard( art );
|
|
798
|
+
// The XSN of actions in extensions do not contain a returns yet - TODO?
|
|
799
|
+
const elems = c.elements;
|
|
800
|
+
if (elems && (prop === 'actions' || art.$syntax === 'returns')) {
|
|
801
|
+
delete c.elements;
|
|
802
|
+
c.returns = { elements: elems };
|
|
803
|
+
}
|
|
804
|
+
return c;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
function addOrigin( csn, xsn ) {
|
|
808
|
+
if (!universalCsn)
|
|
809
|
+
return csn;
|
|
810
|
+
if (xsn._from) {
|
|
811
|
+
csn.$origin = originRef( xsn._from[0]._origin );
|
|
812
|
+
}
|
|
813
|
+
else if (xsn.includes && xsn.includes.length > 1) {
|
|
814
|
+
csn.$origin = { $origin: originRef( xsn.includes[0]._artifact ) };
|
|
815
|
+
}
|
|
816
|
+
else if (xsn._origin && !hasExplicitProp( xsn.type ) && xsn._origin.kind !== 'builtin') {
|
|
817
|
+
let origin = xsn._origin;
|
|
818
|
+
while (origin._parent && origin._parent.$expand === 'origin')
|
|
819
|
+
origin = origin._origin || origin.type._artifact;
|
|
820
|
+
csn.$origin = originRef( origin );
|
|
821
|
+
}
|
|
822
|
+
return csn;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
function hasExplicitProp( ref ) {
|
|
826
|
+
return ref && !ref.$inferred;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
function originRef( art ) {
|
|
830
|
+
const r = [];
|
|
831
|
+
// do not use name.element, as we allow `.`s in name
|
|
832
|
+
let main = art;
|
|
833
|
+
while (main._main && main.kind !== 'select') {
|
|
834
|
+
const nkind = normalizedKind[main.kind];
|
|
835
|
+
if (main.name.id || !r.length) // { param: "" } only for return, not elements inside
|
|
836
|
+
r.push( nkind ? { [nkind]: main.name.id } : main.name.id );
|
|
837
|
+
main = main._parent;
|
|
838
|
+
}
|
|
839
|
+
if (main._main) // well, an element of an query in FROM
|
|
840
|
+
return definition( art ); // use $origin: {}
|
|
841
|
+
// for sub query in FROM in sub query in FROM, we could condense the info
|
|
842
|
+
r.push( art.name.absolute );
|
|
843
|
+
r.reverse();
|
|
844
|
+
return r;
|
|
673
845
|
}
|
|
674
846
|
|
|
675
847
|
function kind( k, csn, node ) {
|
|
676
|
-
if (
|
|
848
|
+
if (k === 'annotate' || k === 'extend') {
|
|
677
849
|
// We just use `name.absolute` because it is very likely a "constructed"
|
|
678
850
|
// extensions. The CSN parser must produce name.path like for other refs.
|
|
679
|
-
|
|
680
|
-
|
|
851
|
+
if (!node._main)
|
|
852
|
+
csn[k] = node.name.absolute || artifactRef( node.name, true );
|
|
853
|
+
else if (k === 'extend')
|
|
854
|
+
csn.kind = k;
|
|
855
|
+
}
|
|
856
|
+
else {
|
|
857
|
+
if (![
|
|
858
|
+
'element', 'key', 'param', 'enum', 'select', '$join',
|
|
859
|
+
'$tableAlias', 'annotation', 'mixin',
|
|
860
|
+
].includes(k))
|
|
861
|
+
csn.kind = k;
|
|
862
|
+
addOrigin( csn, node );
|
|
681
863
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
function type( node, csn, xsn ) {
|
|
867
|
+
if (universalCsn && node.$inferred && xsn._origin)
|
|
686
868
|
return undefined;
|
|
687
|
-
return
|
|
869
|
+
return artifactRef( node, !node.$extra );
|
|
688
870
|
}
|
|
689
871
|
|
|
690
872
|
function artifactRef( node, terse ) {
|
|
@@ -786,10 +968,12 @@ function args( node ) {
|
|
|
786
968
|
return dict;
|
|
787
969
|
}
|
|
788
970
|
|
|
789
|
-
// "Short" value form, e.g. for annotation assignments
|
|
790
971
|
function value( node ) {
|
|
972
|
+
// "Short" value form, e.g. for annotation assignments
|
|
791
973
|
if (!node)
|
|
792
974
|
return true; // `@aBool` short for `@aBool: true`
|
|
975
|
+
if (universalCsn && node.$inferred === 'prop') // via propagator.js
|
|
976
|
+
return undefined;
|
|
793
977
|
if (node.$inferred && gensrcFlavor)
|
|
794
978
|
return undefined;
|
|
795
979
|
if (node.path) {
|
|
@@ -832,23 +1016,7 @@ function onCondition( cond, csn, node ) {
|
|
|
832
1016
|
function condition( node ) {
|
|
833
1017
|
const expr = expression( node );
|
|
834
1018
|
// we do not set a hidden $parens on array - we could still do it if requested
|
|
835
|
-
return !expr.cast && expr.xpr || [ expr ];
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
// TODO: quoted magic names like $now should be complained about in the compiler
|
|
839
|
-
|
|
840
|
-
function pathRef( path ) {
|
|
841
|
-
const ref = { ref: path.map( pathItem ) };
|
|
842
|
-
const nav = path[0]._navigation;
|
|
843
|
-
if (nav && nav.kind !== '$self' && nav.kind !== 'element' && nav.name.select != null) {
|
|
844
|
-
setHidden( ref, '$env', (nav.kind === '$navElement')
|
|
845
|
-
? nav.name.alias
|
|
846
|
-
: nav.name.select );
|
|
847
|
-
}
|
|
848
|
-
else if ( path[0]._artifact && path[0]._artifact.query ) {
|
|
849
|
-
setHidden( ref, '$env', true );
|
|
850
|
-
}
|
|
851
|
-
return ref;
|
|
1019
|
+
return !expr.cast && !expr.func && expr.xpr || [ expr ];
|
|
852
1020
|
}
|
|
853
1021
|
|
|
854
1022
|
function expression( node, dollarExtra ) {
|
|
@@ -863,8 +1031,8 @@ function expression( node, dollarExtra ) {
|
|
|
863
1031
|
return { ref: [ node.param.val ], param: true }; // CDL rule for runtimes
|
|
864
1032
|
}
|
|
865
1033
|
if (node.path) {
|
|
866
|
-
//
|
|
867
|
-
return extra(
|
|
1034
|
+
// we would need to consider node.global here if we introduce that
|
|
1035
|
+
return extra( { ref: node.path.map( pathItem ) }, dollarExtraNode );
|
|
868
1036
|
}
|
|
869
1037
|
if (node.literal) {
|
|
870
1038
|
if (typeof node.val === node.literal || node.val === null)
|
|
@@ -888,10 +1056,12 @@ function expression( node, dollarExtra ) {
|
|
|
888
1056
|
arg0.xpr.unshift( quantifier.val );
|
|
889
1057
|
}
|
|
890
1058
|
}
|
|
1059
|
+
if (node.suffix)
|
|
1060
|
+
call.xpr = [].concat( ...node.suffix.map( xprArg ) );
|
|
891
1061
|
return extra( call, dollarExtraNode );
|
|
892
1062
|
}
|
|
893
1063
|
if (node.query)
|
|
894
|
-
return query( node.query, null, null, 1 );
|
|
1064
|
+
return query( node.query, null, null, null, 1 );
|
|
895
1065
|
if (!node.op) // parse error
|
|
896
1066
|
return { xpr: [] };
|
|
897
1067
|
else if (node.op.val === 'xpr')
|
|
@@ -901,7 +1071,7 @@ function expression( node, dollarExtra ) {
|
|
|
901
1071
|
return cast( expression( node.args[0] ), dollarExtraNode );
|
|
902
1072
|
// from here on: CDL input (no $extra possible - but $parens)
|
|
903
1073
|
else if (node.op.val !== ',')
|
|
904
|
-
return extra( { xpr: xpr( node ) }, dollarExtraNode, 1 );
|
|
1074
|
+
return extra( { xpr: xpr( node ) }, dollarExtraNode, (dollarExtra === 'sub-xpr' ? 1 : 0) );
|
|
905
1075
|
return (parensAsStrings)
|
|
906
1076
|
? { xpr: [ '(', ...xpr( node ), ')' ] }
|
|
907
1077
|
// the inner parens belong to the tuple construct, i.e. won't count as parens
|
|
@@ -911,15 +1081,7 @@ function expression( node, dollarExtra ) {
|
|
|
911
1081
|
function xpr( node ) {
|
|
912
1082
|
// if (!node.op) console.log(node)
|
|
913
1083
|
const op = operators[node.op.val] || node.op.val.split(' ');
|
|
914
|
-
const exprs = node.args.map(
|
|
915
|
-
const expr = expression( sub );
|
|
916
|
-
// return !sub.$parens && !expr.cast && expr.xpr || [ expr ]; if parensAsStrings is gone
|
|
917
|
-
if (expr.cast || !expr.xpr || sub.$parens && !parensAsStrings)
|
|
918
|
-
return [ expr ];
|
|
919
|
-
else if (sub.$parens && sub.op.val !== ',')
|
|
920
|
-
return [ '(', ...expr.xpr, ')' ];
|
|
921
|
-
return expr.xpr;
|
|
922
|
-
} );
|
|
1084
|
+
const exprs = node.args.map( xprArg );
|
|
923
1085
|
if (op instanceof Function)
|
|
924
1086
|
return op( exprs );
|
|
925
1087
|
if (node.quantifier)
|
|
@@ -929,6 +1091,27 @@ function xpr( node ) {
|
|
|
929
1091
|
return exprs[0].concat( ...exprs.slice(1).map( a => [ ...op, ...a ] ) );
|
|
930
1092
|
}
|
|
931
1093
|
|
|
1094
|
+
function xprArg( sub ) {
|
|
1095
|
+
const realXpr = sub.op && sub.op.val === 'xpr';
|
|
1096
|
+
const expr = expression( sub, 'sub-xpr' );
|
|
1097
|
+
// `sort`/`nulls` will be attached to arguments of orderBy
|
|
1098
|
+
// which might be either `path`s or `xpr`s
|
|
1099
|
+
const sortAndNulls = [];
|
|
1100
|
+
if (sub.sort)
|
|
1101
|
+
sortAndNulls.push( sub.sort.val );
|
|
1102
|
+
if (sub.nulls)
|
|
1103
|
+
sortAndNulls.push( ...[ 'nulls', sub.nulls.val ] );
|
|
1104
|
+
// return !sub.$parens && !expr.cast && !expr.func && expr.xpr || [ expr ];
|
|
1105
|
+
// if parensAsStrings is gone
|
|
1106
|
+
if (realXpr || expr.cast || expr.func || !expr.xpr || sub.$parens && !parensAsStrings)
|
|
1107
|
+
return [ expr, ...sortAndNulls ];
|
|
1108
|
+
else if (sub.$parens && sub.op.val !== ',')
|
|
1109
|
+
return [ '(', ...expr.xpr, ')' ];
|
|
1110
|
+
|
|
1111
|
+
expr.xpr.push( ...sortAndNulls );
|
|
1112
|
+
return expr.xpr;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
932
1115
|
function ternary( op1, op2 ) {
|
|
933
1116
|
return function ternaryOp( exprs ) {
|
|
934
1117
|
return (exprs[2])
|
|
@@ -952,7 +1135,7 @@ function binaryRightParen( op ) {
|
|
|
952
1135
|
};
|
|
953
1136
|
}
|
|
954
1137
|
|
|
955
|
-
function query( node, csn, xsn, expectedParens = 0 ) {
|
|
1138
|
+
function query( node, csn, xsn, _prop, expectedParens = 0 ) {
|
|
956
1139
|
if (node.op.val === 'SELECT') {
|
|
957
1140
|
if (xsn && xsn.query === node && xsn.$syntax === 'projection' &&
|
|
958
1141
|
node.from && node.from.path && !projectionAsQuery) {
|
|
@@ -969,7 +1152,10 @@ function query( node, csn, xsn, expectedParens = 0 ) {
|
|
|
969
1152
|
const gensrcSaved = gensrcFlavor;
|
|
970
1153
|
try {
|
|
971
1154
|
gensrcFlavor = false;
|
|
972
|
-
|
|
1155
|
+
if (enumerableQueryElements( node ))
|
|
1156
|
+
select.SELECT.elements = insertOrderDict( elems );
|
|
1157
|
+
else
|
|
1158
|
+
setHidden( select.SELECT, 'elements', insertOrderDict( elems ) );
|
|
973
1159
|
}
|
|
974
1160
|
finally {
|
|
975
1161
|
gensrcFlavor = gensrcSaved;
|
|
@@ -1021,7 +1207,7 @@ function from( node ) {
|
|
|
1021
1207
|
return extra( join, node );
|
|
1022
1208
|
}
|
|
1023
1209
|
else if (node.query) {
|
|
1024
|
-
return addExplicitAs( query( node.query, null, null, 1 ), node.name );
|
|
1210
|
+
return addExplicitAs( query( node.query, null, null, null, 1 ), node.name );
|
|
1025
1211
|
}
|
|
1026
1212
|
else if (!node._artifact || node._artifact._main) { // CQL or follow assoc
|
|
1027
1213
|
return extra( addExplicitAs( artifactRef( node, false ), node.name ), node );
|
|
@@ -1057,25 +1243,15 @@ function addElementAsColumn( elem, cols ) {
|
|
|
1057
1243
|
col.excluding = Object.keys( elem.excludingDict );
|
|
1058
1244
|
// yes, the AS comes after the EXPAND
|
|
1059
1245
|
addExplicitAs( col, elem.name, neqPath( elem.value ) );
|
|
1060
|
-
//
|
|
1061
|
-
if (!expr.cast)
|
|
1062
|
-
|
|
1063
|
-
setHidden( col, '$env', expr.$env );
|
|
1064
|
-
if (expr.elements)
|
|
1065
|
-
setHidden( col, 'elements', expr.elements );
|
|
1066
|
-
}
|
|
1246
|
+
// elements of sub queries (in expr) are hidden (not set via Object.assign):
|
|
1247
|
+
if (!expr.cast && expr.elements)
|
|
1248
|
+
setHidden( col, 'elements', expr.elements );
|
|
1067
1249
|
if (elem.type && !elem.type.$inferred || elem.target && !elem.target.$inferred)
|
|
1068
1250
|
cast( col, elem );
|
|
1069
1251
|
}
|
|
1070
1252
|
finally {
|
|
1071
1253
|
gensrcFlavor = gensrcSaved;
|
|
1072
1254
|
}
|
|
1073
|
-
// FIXME: Currently toHana requires that an '_ignore' property on the elem is
|
|
1074
|
-
// also visible on the column. Don't ignore virtual columns, let the
|
|
1075
|
-
// renderer decide how to render that column.
|
|
1076
|
-
if (!elem.virtual && elem._ignore)
|
|
1077
|
-
col._ignore = true;
|
|
1078
|
-
|
|
1079
1255
|
if (elem.value && !elem.$inferred) {
|
|
1080
1256
|
const parens = elem.value.$parens;
|
|
1081
1257
|
if (parens)
|
|
@@ -1162,6 +1338,9 @@ function compactExpr( e ) { // TODO: options
|
|
|
1162
1338
|
function initModuleVars( options = { csnFlavor: 'gensrc' } ) {
|
|
1163
1339
|
gensrcFlavor = options.parseCdl || options.csnFlavor === 'gensrc' ||
|
|
1164
1340
|
options.toCsn && options.toCsn.flavor === 'gensrc';
|
|
1341
|
+
universalCsn = (options.csnFlavor === 'universal' ||
|
|
1342
|
+
options.toCsn && options.toCsn.flavor === 'universal' ) &&
|
|
1343
|
+
isBetaEnabled( options, 'enableUniversalCsn' ) && !options.parseCdl;
|
|
1165
1344
|
strictMode = options.testMode;
|
|
1166
1345
|
const proto = options.dictionaryPrototype;
|
|
1167
1346
|
// eslint-disable-next-line no-nested-ternary
|