@sap/cds-compiler 2.5.2 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +235 -9
- package/bin/cdsc.js +44 -27
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +37 -3
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +37 -123
- package/lib/api/options.js +27 -15
- package/lib/api/validate.js +34 -9
- package/lib/backends.js +9 -89
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +73 -11
- package/lib/base/messages.js +86 -30
- package/lib/base/model.js +6 -6
- package/lib/base/optionProcessorHelper.js +56 -22
- 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 +29 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +23 -7
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +8 -5
- package/lib/compiler/definer.js +157 -133
- package/lib/compiler/index.js +89 -31
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +375 -185
- package/lib/compiler/shared.js +49 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +104 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +388 -146
- package/lib/edm/edmUtils.js +104 -34
- package/lib/gen/Dictionary.json +22 -0
- 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 +5330 -4300
- package/lib/json/from-csn.js +110 -52
- package/lib/json/to-csn.js +434 -120
- package/lib/language/antlrParser.js +15 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +93 -26
- package/lib/language/language.g4 +172 -31
- package/lib/main.d.ts +216 -19
- package/lib/main.js +32 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +413 -149
- package/lib/model/csnUtils.js +286 -75
- package/lib/model/enrichCsn.js +50 -6
- package/lib/model/revealInternalProperties.js +22 -5
- package/lib/modelCompare/compare.js +39 -21
- package/lib/optionProcessor.js +35 -18
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +9 -6
- package/lib/render/toCdl.js +121 -36
- package/lib/render/toHdbcds.js +148 -98
- package/lib/render/toSql.js +114 -43
- 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 +281 -106
- package/lib/transform/db/draft.js +11 -8
- package/lib/transform/db/expansion.js +584 -0
- package/lib/transform/db/flattening.js +341 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +345 -65
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +131 -793
- package/lib/transform/forOdataNew.js +30 -24
- package/lib/transform/localized.js +39 -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 +144 -78
- package/lib/transform/translateAssocsToJoins.js +22 -27
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -14
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- 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,20 +12,31 @@
|
|
|
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;
|
|
26
33
|
let withLocations = false;
|
|
27
34
|
let dictionaryPrototype = null;
|
|
28
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
|
+
|
|
29
40
|
// IMPORTANT: the order of these properties determine the order of properties
|
|
30
41
|
// in the resulting CSN !!! Also check const `csnPropertyNames`.
|
|
31
42
|
const transformers = {
|
|
@@ -50,7 +61,7 @@ const transformers = {
|
|
|
50
61
|
all: ignore, // XSN TODO use quantifier
|
|
51
62
|
// type properties (without 'elements') ------------------------------------
|
|
52
63
|
localized: value,
|
|
53
|
-
type
|
|
64
|
+
type,
|
|
54
65
|
length: value,
|
|
55
66
|
precision: value,
|
|
56
67
|
scale: value,
|
|
@@ -59,7 +70,7 @@ const transformers = {
|
|
|
59
70
|
targetAspect,
|
|
60
71
|
target,
|
|
61
72
|
foreignKeys,
|
|
62
|
-
enum:
|
|
73
|
+
enum: enumDict,
|
|
63
74
|
items,
|
|
64
75
|
includes: arrayOf( artifactRef ), // also entities
|
|
65
76
|
// late expressions / query properties -------------------------------------
|
|
@@ -72,6 +83,7 @@ const transformers = {
|
|
|
72
83
|
where: condition, // also pathItem after 'cardinality' before 'args'
|
|
73
84
|
having: condition,
|
|
74
85
|
args, // also pathItem after 'where', before 'on'/'orderBy'
|
|
86
|
+
suffix: node => [].concat( ...node.suffix.map( xprArg ) ),
|
|
75
87
|
orderBy: arrayOf( orderBy ), // TODO XSN: make `sort` and `nulls` sibling properties
|
|
76
88
|
sort: value,
|
|
77
89
|
nulls: value,
|
|
@@ -80,14 +92,14 @@ const transformers = {
|
|
|
80
92
|
offset: expression,
|
|
81
93
|
on: onCondition,
|
|
82
94
|
// definitions, extensions, members ----------------------------------------
|
|
83
|
-
returns:
|
|
95
|
+
returns: definition, // storing the return type of actions
|
|
84
96
|
notNull: value,
|
|
85
97
|
default: expression,
|
|
86
98
|
// targetElement: ignore, // special display of foreign key, renameTo: select
|
|
87
99
|
value: enumValue, // do not list for select items as elements
|
|
88
100
|
query,
|
|
89
101
|
elements,
|
|
90
|
-
actions
|
|
102
|
+
actions, // TODO: just normal dictionary
|
|
91
103
|
// special: top-level, cardinality -----------------------------------------
|
|
92
104
|
sources,
|
|
93
105
|
definitions: sortedDict,
|
|
@@ -128,7 +140,7 @@ const transformers = {
|
|
|
128
140
|
// which should appear at that place in order.
|
|
129
141
|
const csnPropertyNames = {
|
|
130
142
|
virtual: [ 'abstract' ], // abstract is compiler v1 CSN property
|
|
131
|
-
kind: [ 'annotate', 'extend' ],
|
|
143
|
+
kind: [ 'annotate', 'extend', '$origin' ],
|
|
132
144
|
op: [ 'join', 'func', 'xpr' ], // TODO: 'func','xpr' into 'quantifier'? TODO: 'global'(scope)?
|
|
133
145
|
quantifier: [
|
|
134
146
|
'some', 'any', 'distinct', // 'all' explicitly listed
|
|
@@ -148,7 +160,7 @@ const csnPropertyNames = {
|
|
|
148
160
|
name: [ 'as', 'cast' ],
|
|
149
161
|
location: [ '$env', '$location' ], // --enrich-csn
|
|
150
162
|
expectedKind: [
|
|
151
|
-
'_type', '_targetAspect', '_target', '_includes', '_links', '_art', '_scope',
|
|
163
|
+
'_origin', '_type', '_targetAspect', '_target', '_includes', '_links', '_art', '_scope',
|
|
152
164
|
], // --enrich-csn
|
|
153
165
|
};
|
|
154
166
|
|
|
@@ -183,6 +195,22 @@ const operators = {
|
|
|
183
195
|
notLike: ternary( [ 'not', 'like' ], [ 'escape' ] ),
|
|
184
196
|
when: exprs => [ 'when', ...exprs[0], 'then', ...exprs[1] ],
|
|
185
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] ],
|
|
186
214
|
// xpr: (exprs) => [].concat( ...exprs ), see below - handled extra
|
|
187
215
|
};
|
|
188
216
|
|
|
@@ -194,7 +222,7 @@ const csnDirectValues = [ 'val' ]; // + all starting with '@' - TODO: still rele
|
|
|
194
222
|
/**
|
|
195
223
|
* Sort property names of CSN according to sequence which is also used by the compactModel function
|
|
196
224
|
* Only returns enumerable properties, except for certain hidden properties
|
|
197
|
-
* if requested (cloneOptions != false): $location,
|
|
225
|
+
* if requested (cloneOptions != false): $location, elements.
|
|
198
226
|
*
|
|
199
227
|
* If cloneOptions is false or if either cloneOptions.testMode or cloneOptions.testSortCsn
|
|
200
228
|
* are set, definitions are also sorted.
|
|
@@ -224,24 +252,36 @@ function sortCsn( csn, cloneOptions = false ) {
|
|
|
224
252
|
r[n] = sortCsn(val, cloneOptions);
|
|
225
253
|
}
|
|
226
254
|
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);
|
|
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 );
|
|
241
267
|
}
|
|
242
268
|
return r;
|
|
243
269
|
}
|
|
244
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
|
+
|
|
245
285
|
/**
|
|
246
286
|
* @param {object} csn
|
|
247
287
|
* @param {boolean} sort
|
|
@@ -353,10 +393,12 @@ function usings( srcDict ) {
|
|
|
353
393
|
* @param {object} csn
|
|
354
394
|
* @param {object} model
|
|
355
395
|
*/
|
|
396
|
+
|
|
397
|
+
|
|
356
398
|
function extensions( node, csn, model ) {
|
|
357
399
|
if (model.kind && model.kind !== 'source')
|
|
358
400
|
return undefined;
|
|
359
|
-
const exts = node.map(
|
|
401
|
+
const exts = node.map( definition );
|
|
360
402
|
|
|
361
403
|
// builtins are non-enumerable for smaller display
|
|
362
404
|
for (const name of Object.getOwnPropertyNames( model.definitions || {} ).sort()) {
|
|
@@ -371,17 +413,21 @@ function extensions( node, csn, model ) {
|
|
|
371
413
|
}
|
|
372
414
|
else if (gensrcFlavor) {
|
|
373
415
|
// From definitions (without redefinitions) with potential inferred elements:
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
if (
|
|
381
|
-
annotate.
|
|
382
|
-
|
|
383
|
-
|
|
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 );
|
|
384
428
|
}
|
|
429
|
+
if (Object.keys( annotate ).length > 1)
|
|
430
|
+
exts.push( annotate );
|
|
385
431
|
}
|
|
386
432
|
}
|
|
387
433
|
|
|
@@ -389,6 +435,58 @@ function extensions( node, csn, model ) {
|
|
|
389
435
|
(a, b) => (a.annotate || a.extend).localeCompare( b.annotate || b.extend )
|
|
390
436
|
);
|
|
391
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
|
+
|
|
392
490
|
// extract namespace/builtin annotations
|
|
393
491
|
function extractAnnotationsToExtension( art ) {
|
|
394
492
|
const name = art.name.absolute;
|
|
@@ -450,17 +548,29 @@ function sources( srcDict, csn ) {
|
|
|
450
548
|
}
|
|
451
549
|
}
|
|
452
550
|
|
|
453
|
-
function
|
|
454
|
-
const
|
|
455
|
-
for (const name in
|
|
456
|
-
const elem =
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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;
|
|
462
573
|
}
|
|
463
|
-
return ext;
|
|
464
574
|
}
|
|
465
575
|
|
|
466
576
|
function standard( node ) {
|
|
@@ -499,48 +609,96 @@ function set( prop, csn, node ) {
|
|
|
499
609
|
}
|
|
500
610
|
|
|
501
611
|
function targetAspect( val, csn, node ) {
|
|
612
|
+
const ta = (val.elements)
|
|
613
|
+
? addLocation( val.location, standard( val ) )
|
|
614
|
+
: artifactRef( val, true );
|
|
502
615
|
if (!gensrcFlavor || node.target && !node.target.$inferred)
|
|
503
|
-
return
|
|
616
|
+
return ta;
|
|
504
617
|
// For compatibility, put aspect in 'target' with parse.cdl and csn flavor 'gensrc'
|
|
505
|
-
csn.target =
|
|
618
|
+
csn.target = ta;
|
|
506
619
|
return undefined;
|
|
507
620
|
}
|
|
508
621
|
|
|
509
622
|
function target( val, _csn, node ) {
|
|
510
623
|
if (gensrcFlavor && node._origin && node._origin.$inferred === 'REDIRECTED')
|
|
511
624
|
val = node._origin.target;
|
|
512
|
-
if (
|
|
625
|
+
if (val.elements)
|
|
626
|
+
return standard( val ); // elements in target (parse-cdl)
|
|
627
|
+
if (!universalCsn || node.on)
|
|
513
628
|
return artifactRef( val, true );
|
|
514
|
-
|
|
629
|
+
const tref = artifactRef( val, true );
|
|
630
|
+
const proto = node.type && !node.type.$inferred ? node.type._artifact : node._origin;
|
|
631
|
+
return (proto && proto.target && artifactRef( proto.target, true ) === tref)
|
|
632
|
+
? undefined
|
|
633
|
+
: tref;
|
|
515
634
|
}
|
|
516
635
|
|
|
517
636
|
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()
|
|
637
|
+
if (!keepElements( node, obj ))
|
|
521
638
|
return undefined;
|
|
522
|
-
return standard( obj );
|
|
639
|
+
return standard( obj ); // no 'elements' with inferred elements with gensrc
|
|
523
640
|
}
|
|
524
641
|
|
|
525
642
|
function elements( dict, csn, node ) {
|
|
526
|
-
if (
|
|
643
|
+
if (node.from || // do not directly show query elements here
|
|
527
644
|
gensrcFlavor && (node.query || node.type) ||
|
|
528
|
-
|
|
645
|
+
!keepElements( node ))
|
|
646
|
+
// no 'elements' with SELECT or inferred elements with gensrc;
|
|
647
|
+
// hidden or visible 'elements' will be set in query()
|
|
648
|
+
return undefined;
|
|
649
|
+
return insertOrderDict( dict );
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function enumDict( dict, csn, node ) {
|
|
653
|
+
if (gensrcFlavor && dict[$inferred] ||
|
|
654
|
+
!keepElements( node ))
|
|
529
655
|
// no 'elements' with SELECT or inferred elements with gensrc;
|
|
530
|
-
// hidden 'elements' will be set in query()
|
|
656
|
+
// hidden or visible 'elements' will be set in query()
|
|
531
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 );
|
|
532
661
|
return insertOrderDict( dict );
|
|
533
662
|
}
|
|
534
663
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
664
|
+
function enumerableQueryElements( select ) {
|
|
665
|
+
if (!universalCsn || select === select._main._leadingQuery)
|
|
666
|
+
return false;
|
|
667
|
+
if (select.orderBy || select.$orderBy)
|
|
668
|
+
return true;
|
|
669
|
+
const alias = select._parent;
|
|
670
|
+
return alias.query && (alias.query._leadingQuery || alias.query) === select;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// Should we render the elements? (and items?)
|
|
674
|
+
function keepElements( node, line ) {
|
|
675
|
+
if (universalCsn)
|
|
676
|
+
// $expand = null/undefined: not elements not via expansion
|
|
677
|
+
// $expand = 'target'/'annotate': with redirections / individual annotations
|
|
678
|
+
return node.$expand !== 'origin';
|
|
679
|
+
if (!node.type || node.kind === 'type')
|
|
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
|
|
538
695
|
while (node) {
|
|
539
696
|
if (node.$expand !== 'origin')
|
|
540
697
|
return true;
|
|
541
698
|
node = node._origin;
|
|
542
699
|
}
|
|
543
|
-
|
|
700
|
+
// all in _origin chain only have expanded elements with 'origin':
|
|
701
|
+
return false; // no need to render elements
|
|
544
702
|
}
|
|
545
703
|
|
|
546
704
|
// for gensrcFlavor and namespace/builtin annotation extraction:
|
|
@@ -626,17 +784,17 @@ function sortedDict( dict ) {
|
|
|
626
784
|
return dictionary( dict, keys );
|
|
627
785
|
}
|
|
628
786
|
|
|
629
|
-
function
|
|
787
|
+
function actions( dict ) {
|
|
630
788
|
const keys = Object.keys( dict );
|
|
631
789
|
return (keys.length)
|
|
632
|
-
? dictionary( dict, keys )
|
|
790
|
+
? dictionary( dict, keys, 'actions' )
|
|
633
791
|
: undefined;
|
|
634
792
|
}
|
|
635
793
|
|
|
636
|
-
function dictionary( dict, keys ) {
|
|
794
|
+
function dictionary( dict, keys, prop ) {
|
|
637
795
|
const csn = Object.create( dictionaryPrototype );
|
|
638
796
|
for (const name of keys) {
|
|
639
|
-
const def = definition( dict[name] );
|
|
797
|
+
const def = definition( dict[name], null, null, prop );
|
|
640
798
|
if (def !== undefined)
|
|
641
799
|
csn[name] = def;
|
|
642
800
|
}
|
|
@@ -644,6 +802,8 @@ function dictionary( dict, keys ) {
|
|
|
644
802
|
}
|
|
645
803
|
|
|
646
804
|
function foreignKeys( dict, csn, node ) {
|
|
805
|
+
if (universalCsn && !target( node.target, csn, node ))
|
|
806
|
+
return;
|
|
647
807
|
if (gensrcFlavor && node._origin && node._origin.$inferred === 'REDIRECTED')
|
|
648
808
|
dict = node._origin.foreignKeys;
|
|
649
809
|
const keys = [];
|
|
@@ -657,7 +817,7 @@ function foreignKeys( dict, csn, node ) {
|
|
|
657
817
|
csn.keys = keys;
|
|
658
818
|
}
|
|
659
819
|
|
|
660
|
-
function definition( art ) {
|
|
820
|
+
function definition( art, _csn, _node, prop ) {
|
|
661
821
|
if (!art || typeof art !== 'object')
|
|
662
822
|
return undefined; // TODO: complain with strict
|
|
663
823
|
// Do not include namespace definitions or inferred construct (in gensrc):
|
|
@@ -669,22 +829,176 @@ function definition( art ) {
|
|
|
669
829
|
addLocation( art.targetElement.location, key );
|
|
670
830
|
return extra( key, art );
|
|
671
831
|
}
|
|
672
|
-
|
|
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;
|
|
842
|
+
}
|
|
843
|
+
|
|
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;
|
|
867
|
+
if (xsn._from) {
|
|
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;
|
|
875
|
+
}
|
|
876
|
+
else if (xsn.includes) {
|
|
877
|
+
csn.$origin = includesOrigin( xsn.includes, xsn );
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
else if (!xsn._main || xsn.kind === 'select') {
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
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;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
function hasExplicitProp( ref ) {
|
|
953
|
+
return ref && !ref.$inferred;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
function originRef( art, user ) {
|
|
957
|
+
const r = [];
|
|
958
|
+
// do not use name.element, as we allow `.`s in name
|
|
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;
|
|
967
|
+
}
|
|
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: {...}
|
|
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 ];
|
|
976
|
+
r.push( art.name.absolute );
|
|
977
|
+
r.reverse();
|
|
978
|
+
return r;
|
|
673
979
|
}
|
|
674
980
|
|
|
675
981
|
function kind( k, csn, node ) {
|
|
676
|
-
if (
|
|
982
|
+
if (k === 'annotate' || k === 'extend') {
|
|
677
983
|
// We just use `name.absolute` because it is very likely a "constructed"
|
|
678
984
|
// extensions. The CSN parser must produce name.path like for other refs.
|
|
679
|
-
|
|
680
|
-
|
|
985
|
+
if (!node._main)
|
|
986
|
+
csn[k] = node.name.absolute || artifactRef( node.name, true );
|
|
987
|
+
else if (k === 'extend')
|
|
988
|
+
csn.kind = k;
|
|
681
989
|
}
|
|
682
|
-
if ([
|
|
683
|
-
'element', 'key', 'param', 'enum', '
|
|
990
|
+
else if (![
|
|
991
|
+
'element', 'key', 'param', 'enum', 'select', '$join',
|
|
684
992
|
'$tableAlias', 'annotation', 'mixin',
|
|
685
|
-
].includes(k))
|
|
993
|
+
].includes(k)) {
|
|
994
|
+
csn.kind = k;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
function type( node, csn, xsn ) {
|
|
999
|
+
if (universalCsn && node.$inferred && xsn._origin)
|
|
686
1000
|
return undefined;
|
|
687
|
-
return
|
|
1001
|
+
return artifactRef( node, !node.$extra );
|
|
688
1002
|
}
|
|
689
1003
|
|
|
690
1004
|
function artifactRef( node, terse ) {
|
|
@@ -786,10 +1100,12 @@ function args( node ) {
|
|
|
786
1100
|
return dict;
|
|
787
1101
|
}
|
|
788
1102
|
|
|
789
|
-
// "Short" value form, e.g. for annotation assignments
|
|
790
1103
|
function value( node ) {
|
|
1104
|
+
// "Short" value form, e.g. for annotation assignments
|
|
791
1105
|
if (!node)
|
|
792
1106
|
return true; // `@aBool` short for `@aBool: true`
|
|
1107
|
+
if (universalCsn && node.$inferred === 'prop') // via propagator.js
|
|
1108
|
+
return undefined;
|
|
793
1109
|
if (node.$inferred && gensrcFlavor)
|
|
794
1110
|
return undefined;
|
|
795
1111
|
if (node.path) {
|
|
@@ -813,7 +1129,10 @@ function value( node ) {
|
|
|
813
1129
|
|
|
814
1130
|
function enumValue( v, csn, node ) {
|
|
815
1131
|
// Enums can have values but if enums are extended, their kind is 'element',
|
|
816
|
-
// 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)
|
|
817
1136
|
if (node.kind === 'enum' || node._parent && node._parent.kind === 'extend')
|
|
818
1137
|
Object.assign( csn, expression( v, true ) );
|
|
819
1138
|
}
|
|
@@ -832,23 +1151,7 @@ function onCondition( cond, csn, node ) {
|
|
|
832
1151
|
function condition( node ) {
|
|
833
1152
|
const expr = expression( node );
|
|
834
1153
|
// 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;
|
|
1154
|
+
return !expr.cast && !expr.func && expr.xpr || [ expr ];
|
|
852
1155
|
}
|
|
853
1156
|
|
|
854
1157
|
function expression( node, dollarExtra ) {
|
|
@@ -863,8 +1166,8 @@ function expression( node, dollarExtra ) {
|
|
|
863
1166
|
return { ref: [ node.param.val ], param: true }; // CDL rule for runtimes
|
|
864
1167
|
}
|
|
865
1168
|
if (node.path) {
|
|
866
|
-
//
|
|
867
|
-
return extra(
|
|
1169
|
+
// we would need to consider node.global here if we introduce that
|
|
1170
|
+
return extra( { ref: node.path.map( pathItem ) }, dollarExtraNode );
|
|
868
1171
|
}
|
|
869
1172
|
if (node.literal) {
|
|
870
1173
|
if (typeof node.val === node.literal || node.val === null)
|
|
@@ -888,10 +1191,12 @@ function expression( node, dollarExtra ) {
|
|
|
888
1191
|
arg0.xpr.unshift( quantifier.val );
|
|
889
1192
|
}
|
|
890
1193
|
}
|
|
1194
|
+
if (node.suffix)
|
|
1195
|
+
call.xpr = [].concat( ...node.suffix.map( xprArg ) );
|
|
891
1196
|
return extra( call, dollarExtraNode );
|
|
892
1197
|
}
|
|
893
1198
|
if (node.query)
|
|
894
|
-
return query( node.query, null, null, 1 );
|
|
1199
|
+
return query( node.query, null, null, null, 1 );
|
|
895
1200
|
if (!node.op) // parse error
|
|
896
1201
|
return { xpr: [] };
|
|
897
1202
|
else if (node.op.val === 'xpr')
|
|
@@ -901,7 +1206,7 @@ function expression( node, dollarExtra ) {
|
|
|
901
1206
|
return cast( expression( node.args[0] ), dollarExtraNode );
|
|
902
1207
|
// from here on: CDL input (no $extra possible - but $parens)
|
|
903
1208
|
else if (node.op.val !== ',')
|
|
904
|
-
return extra( { xpr: xpr( node ) }, dollarExtraNode, 1 );
|
|
1209
|
+
return extra( { xpr: xpr( node ) }, dollarExtraNode, (dollarExtra === 'sub-xpr' ? 1 : 0) );
|
|
905
1210
|
return (parensAsStrings)
|
|
906
1211
|
? { xpr: [ '(', ...xpr( node ), ')' ] }
|
|
907
1212
|
// the inner parens belong to the tuple construct, i.e. won't count as parens
|
|
@@ -911,15 +1216,7 @@ function expression( node, dollarExtra ) {
|
|
|
911
1216
|
function xpr( node ) {
|
|
912
1217
|
// if (!node.op) console.log(node)
|
|
913
1218
|
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
|
-
} );
|
|
1219
|
+
const exprs = node.args.map( xprArg );
|
|
923
1220
|
if (op instanceof Function)
|
|
924
1221
|
return op( exprs );
|
|
925
1222
|
if (node.quantifier)
|
|
@@ -929,6 +1226,27 @@ function xpr( node ) {
|
|
|
929
1226
|
return exprs[0].concat( ...exprs.slice(1).map( a => [ ...op, ...a ] ) );
|
|
930
1227
|
}
|
|
931
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
|
+
|
|
932
1250
|
function ternary( op1, op2 ) {
|
|
933
1251
|
return function ternaryOp( exprs ) {
|
|
934
1252
|
return (exprs[2])
|
|
@@ -952,7 +1270,7 @@ function binaryRightParen( op ) {
|
|
|
952
1270
|
};
|
|
953
1271
|
}
|
|
954
1272
|
|
|
955
|
-
function query( node, csn, xsn, expectedParens = 0 ) {
|
|
1273
|
+
function query( node, csn, xsn, _prop, expectedParens = 0 ) {
|
|
956
1274
|
if (node.op.val === 'SELECT') {
|
|
957
1275
|
if (xsn && xsn.query === node && xsn.$syntax === 'projection' &&
|
|
958
1276
|
node.from && node.from.path && !projectionAsQuery) {
|
|
@@ -969,7 +1287,10 @@ function query( node, csn, xsn, expectedParens = 0 ) {
|
|
|
969
1287
|
const gensrcSaved = gensrcFlavor;
|
|
970
1288
|
try {
|
|
971
1289
|
gensrcFlavor = false;
|
|
972
|
-
|
|
1290
|
+
if (enumerableQueryElements( node ))
|
|
1291
|
+
select.SELECT.elements = insertOrderDict( elems );
|
|
1292
|
+
else
|
|
1293
|
+
setHidden( select.SELECT, 'elements', insertOrderDict( elems ) );
|
|
973
1294
|
}
|
|
974
1295
|
finally {
|
|
975
1296
|
gensrcFlavor = gensrcSaved;
|
|
@@ -999,7 +1320,7 @@ function columns( xsnColumns, csn, xsn ) {
|
|
|
999
1320
|
addElementAsColumn( col, csnColumns );
|
|
1000
1321
|
}
|
|
1001
1322
|
}
|
|
1002
|
-
else {
|
|
1323
|
+
else { // null = use elements - TODO: still used by A2J? -> remove
|
|
1003
1324
|
for (const name in xsn.elements)
|
|
1004
1325
|
addElementAsColumn( xsn.elements[name], csnColumns );
|
|
1005
1326
|
}
|
|
@@ -1021,7 +1342,7 @@ function from( node ) {
|
|
|
1021
1342
|
return extra( join, node );
|
|
1022
1343
|
}
|
|
1023
1344
|
else if (node.query) {
|
|
1024
|
-
return addExplicitAs( query( node.query, null, null, 1 ), node.name );
|
|
1345
|
+
return addExplicitAs( query( node.query, null, null, null, 1 ), node.name );
|
|
1025
1346
|
}
|
|
1026
1347
|
else if (!node._artifact || node._artifact._main) { // CQL or follow assoc
|
|
1027
1348
|
return extra( addExplicitAs( artifactRef( node, false ), node.name ), node );
|
|
@@ -1057,25 +1378,15 @@ function addElementAsColumn( elem, cols ) {
|
|
|
1057
1378
|
col.excluding = Object.keys( elem.excludingDict );
|
|
1058
1379
|
// yes, the AS comes after the EXPAND
|
|
1059
1380
|
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
|
-
}
|
|
1381
|
+
// elements of sub queries (in expr) are hidden (not set via Object.assign):
|
|
1382
|
+
if (!expr.cast && expr.elements)
|
|
1383
|
+
setHidden( col, 'elements', expr.elements );
|
|
1067
1384
|
if (elem.type && !elem.type.$inferred || elem.target && !elem.target.$inferred)
|
|
1068
1385
|
cast( col, elem );
|
|
1069
1386
|
}
|
|
1070
1387
|
finally {
|
|
1071
1388
|
gensrcFlavor = gensrcSaved;
|
|
1072
1389
|
}
|
|
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
1390
|
if (elem.value && !elem.$inferred) {
|
|
1080
1391
|
const parens = elem.value.$parens;
|
|
1081
1392
|
if (parens)
|
|
@@ -1162,6 +1473,9 @@ function compactExpr( e ) { // TODO: options
|
|
|
1162
1473
|
function initModuleVars( options = { csnFlavor: 'gensrc' } ) {
|
|
1163
1474
|
gensrcFlavor = options.parseCdl || options.csnFlavor === 'gensrc' ||
|
|
1164
1475
|
options.toCsn && options.toCsn.flavor === 'gensrc';
|
|
1476
|
+
universalCsn = (options.csnFlavor === 'universal' ||
|
|
1477
|
+
options.toCsn && options.toCsn.flavor === 'universal' ) &&
|
|
1478
|
+
isBetaEnabled( options, 'enableUniversalCsn' ) && !options.parseCdl;
|
|
1165
1479
|
strictMode = options.testMode;
|
|
1166
1480
|
const proto = options.dictionaryPrototype;
|
|
1167
1481
|
// eslint-disable-next-line no-nested-ternary
|