@sap/cds-compiler 3.0.2 → 3.1.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 +65 -0
- package/bin/.eslintrc.json +2 -1
- package/bin/cdsc.js +19 -0
- package/doc/API.md +11 -0
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +24 -2
- package/doc/CHANGELOG_DEPRECATED.md +21 -1
- package/lib/api/main.js +7 -7
- package/lib/api/options.js +2 -3
- package/lib/base/message-registry.js +17 -5
- package/lib/base/messages.js +18 -39
- package/lib/base/model.js +2 -0
- package/lib/checks/actionsFunctions.js +8 -7
- package/lib/checks/selectItems.js +96 -14
- package/lib/checks/types.js +5 -8
- package/lib/checks/validator.js +1 -2
- package/lib/compiler/assert-consistency.js +64 -12
- package/lib/compiler/base.js +6 -4
- package/lib/compiler/builtins.js +58 -8
- package/lib/compiler/checks.js +1 -1
- package/lib/compiler/define.js +25 -22
- package/lib/compiler/extend.js +16 -10
- package/lib/compiler/finalize-parse-cdl.js +5 -9
- package/lib/compiler/index.js +2 -0
- package/lib/compiler/populate.js +34 -31
- package/lib/compiler/propagator.js +11 -6
- package/lib/compiler/resolve.js +14 -15
- package/lib/compiler/shared.js +53 -26
- package/lib/compiler/tweak-assocs.js +5 -11
- package/lib/compiler/utils.js +13 -4
- package/lib/edm/annotations/preprocessAnnotations.js +8 -4
- package/lib/edm/csn2edm.js +3 -3
- package/lib/edm/edm.js +9 -1
- package/lib/edm/edmAnnoPreprocessor.js +349 -0
- package/lib/edm/edmInboundChecks.js +85 -0
- package/lib/edm/edmPreprocessor.js +295 -638
- package/lib/edm/edmUtils.js +85 -5
- package/lib/gen/Dictionary.json +29 -9
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -2
- package/lib/gen/languageLexer.js +3 -0
- package/lib/gen/languageParser.js +4344 -4530
- package/lib/inspect/.eslintrc.json +4 -0
- package/lib/inspect/index.js +14 -0
- package/lib/inspect/inspectModelStatistics.js +81 -0
- package/lib/inspect/inspectPropagation.js +189 -0
- package/lib/inspect/inspectUtils.js +44 -0
- package/lib/json/from-csn.js +3 -2
- package/lib/json/to-csn.js +8 -6
- package/lib/language/genericAntlrParser.js +121 -63
- package/lib/language/language.g4 +19 -57
- package/lib/main.d.ts +1 -0
- package/lib/model/api.js +1 -1
- package/lib/model/csnRefs.js +55 -29
- package/lib/model/csnUtils.js +11 -7
- package/lib/model/revealInternalProperties.js +2 -3
- package/lib/modelCompare/compare.js +3 -0
- package/lib/optionProcessor.js +27 -0
- package/lib/render/toCdl.js +57 -32
- package/lib/render/toSql.js +24 -8
- package/lib/render/utils/common.js +3 -4
- package/lib/transform/db/associations.js +43 -35
- package/lib/transform/db/cdsPersistence.js +0 -1
- package/lib/transform/db/flattening.js +3 -4
- package/lib/transform/db/transformExists.js +7 -5
- package/lib/transform/draft/db.js +1 -1
- package/lib/transform/forHanaNew.js +11 -2
- package/lib/transform/forOdataNew.js +1 -1
- package/lib/transform/odata/typesExposure.js +14 -5
- package/lib/utils/moduleResolve.js +0 -1
- package/package.json +2 -2
- package/lib/checks/unknownMagic.js +0 -41
package/lib/compiler/populate.js
CHANGED
|
@@ -73,14 +73,8 @@ function populate( model ) {
|
|
|
73
73
|
/** @type {any} may also be a boolean */
|
|
74
74
|
let newAutoExposed = [];
|
|
75
75
|
|
|
76
|
-
// behavior depending on option `deprecated`:
|
|
77
|
-
const enableExpandElements = !isDeprecatedEnabled( options, '_noElementsExpansion' );
|
|
78
|
-
// TODO: we should get rid of noElementsExpansion soon; both
|
|
79
|
-
// beta.nestedProjections and beta.universalCsn do not work with it.
|
|
80
76
|
const scopedRedirections
|
|
81
|
-
=
|
|
82
|
-
!isDeprecatedEnabled( options, '_generatedEntityNameWithUnderscore' ) &&
|
|
83
|
-
!isDeprecatedEnabled( options, '_shortAutoexposed' ) &&
|
|
77
|
+
= !isDeprecatedEnabled( options, '_shortAutoexposed' ) &&
|
|
84
78
|
!isDeprecatedEnabled( options, '_longAutoexposed' ) &&
|
|
85
79
|
!isDeprecatedEnabled( options, '_noInheritedAutoexposeViaComposition' ) &&
|
|
86
80
|
!isDeprecatedEnabled( options, '_noScopedRedirections' );
|
|
@@ -109,6 +103,8 @@ function populate( model ) {
|
|
|
109
103
|
function traverseElementEnvironments( art ) {
|
|
110
104
|
populateView( art );
|
|
111
105
|
environment( art );
|
|
106
|
+
if (art.elements$)
|
|
107
|
+
mergeSpecifiedElements(art);
|
|
112
108
|
forEachMember( art, traverseElementEnvironments );
|
|
113
109
|
}
|
|
114
110
|
|
|
@@ -165,7 +161,7 @@ function populate( model ) {
|
|
|
165
161
|
// console.log(message( null, art.location, art, {}, 'Info','FT').toString())
|
|
166
162
|
const chain = [];
|
|
167
163
|
while (art && !('_effectiveType' in art) &&
|
|
168
|
-
(art.type || art._origin || art.value
|
|
164
|
+
(art.type || art._origin || art.value?.path || art.value?.type) &&
|
|
169
165
|
// TODO: really stop at art.enum? See #8942
|
|
170
166
|
!art.target && !art.enum && !art.elements && !art.items) {
|
|
171
167
|
chain.push( art );
|
|
@@ -195,7 +191,7 @@ function populate( model ) {
|
|
|
195
191
|
let eType = art;
|
|
196
192
|
if (eType._outer)
|
|
197
193
|
eType = effectiveType( eType._outer );
|
|
198
|
-
// collect the "latest" cardinality (calculate
|
|
194
|
+
// collect the "latest" cardinality (calculate lazily if necessary)
|
|
199
195
|
let cardinality = art.cardinality ||
|
|
200
196
|
art._effectiveType && (() => getCardinality( art._effectiveType ));
|
|
201
197
|
let prev = art;
|
|
@@ -222,10 +218,12 @@ function populate( model ) {
|
|
|
222
218
|
return art._origin;
|
|
223
219
|
if (art.type)
|
|
224
220
|
return resolveType( art.type, art );
|
|
221
|
+
if (art.value?.type)
|
|
222
|
+
return resolveType( art.value.type, art );
|
|
225
223
|
// console.log( 'EXPR-IN', art.kind, refString(art.name) )
|
|
226
224
|
if (!art._main || !art.value || !art.value.path)
|
|
227
225
|
return undefined;
|
|
228
|
-
if (art._pathHead && art.value) {
|
|
226
|
+
if (art._pathHead && art.value.path) {
|
|
229
227
|
setLink( art, '_origin', resolvePath( art.value, 'expr', art, null ) );
|
|
230
228
|
return art._origin;
|
|
231
229
|
}
|
|
@@ -295,7 +293,7 @@ function populate( model ) {
|
|
|
295
293
|
|
|
296
294
|
|
|
297
295
|
function expandItems( art, origin, eType ) {
|
|
298
|
-
if (
|
|
296
|
+
if (art.items)
|
|
299
297
|
return false;
|
|
300
298
|
if (isInParents( art, eType )) {
|
|
301
299
|
art.items = 0; // circular
|
|
@@ -312,8 +310,6 @@ function populate( model ) {
|
|
|
312
310
|
}
|
|
313
311
|
|
|
314
312
|
function expandElements( art, struct, eType ) {
|
|
315
|
-
if (!enableExpandElements)
|
|
316
|
-
return false;
|
|
317
313
|
if (art.elements || art.kind === '$tableAlias' ||
|
|
318
314
|
// no element expansions for "non-proper" types like
|
|
319
315
|
// entities (as parameter types) etc:
|
|
@@ -347,7 +343,7 @@ function populate( model ) {
|
|
|
347
343
|
}
|
|
348
344
|
|
|
349
345
|
function expandEnum( art, origin ) {
|
|
350
|
-
if (
|
|
346
|
+
if (art.enum)
|
|
351
347
|
return false;
|
|
352
348
|
const ref = art.type || art.value || art.name;
|
|
353
349
|
const location = weakLocation( ref && ref.location || art.location );
|
|
@@ -427,8 +423,6 @@ function populate( model ) {
|
|
|
427
423
|
setLink( view, '_status', '_query' );
|
|
428
424
|
// must be run in order “sub query in FROM first”:
|
|
429
425
|
traverseQueryPost( view.query, null, populateQuery );
|
|
430
|
-
if (view.elements$) // specified elements
|
|
431
|
-
mergeSpecifiedElements( view );
|
|
432
426
|
if (!view.$entity) {
|
|
433
427
|
model._entities.push( view );
|
|
434
428
|
view.$entity = ++model.$entity;
|
|
@@ -437,14 +431,25 @@ function populate( model ) {
|
|
|
437
431
|
}
|
|
438
432
|
}
|
|
439
433
|
|
|
440
|
-
|
|
434
|
+
/**
|
|
435
|
+
* Merge _specified_ elements with _inferred_ elements in the given view/element,
|
|
436
|
+
* where specified elements can appear through CSN.
|
|
437
|
+
*
|
|
438
|
+
* We only copy annotations, since they are not part of `columns`,
|
|
439
|
+
* but only appear in `elements` in CSN.
|
|
440
|
+
*
|
|
441
|
+
* This is important to ensure re-compilability.
|
|
442
|
+
*
|
|
443
|
+
* @param art
|
|
444
|
+
*/
|
|
445
|
+
function mergeSpecifiedElements( art ) {
|
|
441
446
|
// Later we use specified elements as proxies to inferred of leading query
|
|
442
447
|
// (No, we probably do not.)
|
|
443
|
-
for (const id in
|
|
444
|
-
const ielem =
|
|
445
|
-
const selem =
|
|
448
|
+
for (const id in art.elements) {
|
|
449
|
+
const ielem = art.elements[id]; // inferred element
|
|
450
|
+
const selem = art.elements$[id]; // specified element
|
|
446
451
|
if (!selem) {
|
|
447
|
-
info( 'query-missing-element', [ ielem.name.location,
|
|
452
|
+
info( 'query-missing-element', [ ielem.name.location, art ], { id },
|
|
448
453
|
'Element $(ID) is missing in specified elements' );
|
|
449
454
|
}
|
|
450
455
|
else {
|
|
@@ -454,10 +459,14 @@ function populate( model ) {
|
|
|
454
459
|
ielem[prop] = selem[prop];
|
|
455
460
|
}
|
|
456
461
|
selem.$replacement = true;
|
|
462
|
+
if (selem.elements) {
|
|
463
|
+
setLink(ielem, 'elements$', selem.elements);
|
|
464
|
+
delete selem.elements;
|
|
465
|
+
}
|
|
457
466
|
}
|
|
458
467
|
}
|
|
459
|
-
for (const id in
|
|
460
|
-
const selem =
|
|
468
|
+
for (const id in art.elements$) {
|
|
469
|
+
const selem = art.elements$[id]; // specified element
|
|
461
470
|
if (!selem.$replacement) {
|
|
462
471
|
error( 'query-unspecified-element', [ selem.name.location, selem ], { id },
|
|
463
472
|
'Element $(ID) does not result from the query' );
|
|
@@ -474,8 +483,6 @@ function populate( model ) {
|
|
|
474
483
|
forEachGeneric( query, '$tableAliases', resolveTabRef );
|
|
475
484
|
|
|
476
485
|
initFromColumns( query, query.columns );
|
|
477
|
-
// TODO: already in definer: complain about EXCLUDING with no wildcard
|
|
478
|
-
// (would have been automatically with a good CDL syntax: `* without (...)`)
|
|
479
486
|
if (query.excludingDict) {
|
|
480
487
|
for (const name in query.excludingDict)
|
|
481
488
|
resolveExcluding( name, query._combined, query.excludingDict, query );
|
|
@@ -1039,8 +1046,6 @@ function populate( model ) {
|
|
|
1039
1046
|
// which is not a context/service/namespace, or the definition itself.
|
|
1040
1047
|
// If inside service, it is the direct child of the (most inner) service.
|
|
1041
1048
|
function definitionScope( art ) {
|
|
1042
|
-
if (art._base) // with deprecated.generatedEntityNameWithUnderscore
|
|
1043
|
-
return art._base;
|
|
1044
1049
|
let base = art;
|
|
1045
1050
|
while (art._parent) {
|
|
1046
1051
|
if (art._parent.kind === 'service')
|
|
@@ -1112,10 +1117,8 @@ function populate( model ) {
|
|
|
1112
1117
|
// the name for dependent entities have already been created using `_` then
|
|
1113
1118
|
return `${ service.name.absolute }.${ name }`;
|
|
1114
1119
|
}
|
|
1115
|
-
if (isDeprecatedEnabled( options, '_longAutoexposed' ))
|
|
1116
|
-
|
|
1117
|
-
return `${ service.name.absolute }.${ dedot ? absolute.replace( /\./g, '_' ) : absolute }`;
|
|
1118
|
-
}
|
|
1120
|
+
if (isDeprecatedEnabled( options, '_longAutoexposed' ))
|
|
1121
|
+
return `${ service.name.absolute }.${ absolute }`;
|
|
1119
1122
|
const base = definitionScope( target );
|
|
1120
1123
|
if (base === target)
|
|
1121
1124
|
return `${ service.name.absolute }.${ absolute.substring( absolute.lastIndexOf('.') + 1 ) }`;
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Propagate properties in XSN
|
|
2
|
+
|
|
3
|
+
// See also internalDoc/PropagatedCsn.md.
|
|
4
|
+
// As opposed to that document, the propagator here works on XSN, not CSN.
|
|
5
|
+
// We also do not deep-copy member dictionaries here, but create proxy members
|
|
6
|
+
// which get their properties via propagation: we use function `onlyViaParent`
|
|
7
|
+
// if that property would not be propagated otherwise.
|
|
2
8
|
|
|
3
9
|
'use strict';
|
|
4
10
|
|
|
@@ -11,6 +17,7 @@ const {
|
|
|
11
17
|
const { setLink, linkToOrigin, withAssociation } = require('./utils');
|
|
12
18
|
// const { refString } = require( '../base/messages')
|
|
13
19
|
|
|
20
|
+
// Note that propagation here is also used for deep-copying (function `onlyViaParent`)
|
|
14
21
|
function propagate( model ) {
|
|
15
22
|
const props = {
|
|
16
23
|
'@com.sap.gtt.core.CoreModel.Indexable': never,
|
|
@@ -56,7 +63,6 @@ function propagate( model ) {
|
|
|
56
63
|
returns,
|
|
57
64
|
};
|
|
58
65
|
const { options } = model;
|
|
59
|
-
const enableExpandElements = !isDeprecatedEnabled( options, '_noElementsExpansion' );
|
|
60
66
|
// eslint-disable-next-line max-len
|
|
61
67
|
const oldVirtualNotNullPropagation = isDeprecatedEnabled( options, '_oldVirtualNotNullPropagation' );
|
|
62
68
|
|
|
@@ -189,7 +195,7 @@ function propagate( model ) {
|
|
|
189
195
|
if (!type || type._main)
|
|
190
196
|
return false;
|
|
191
197
|
// We do not consider the $expand status, as elements are already expanded
|
|
192
|
-
// by the resolve()
|
|
198
|
+
// by the resolve()
|
|
193
199
|
run( type );
|
|
194
200
|
return type[prop];
|
|
195
201
|
}
|
|
@@ -313,11 +319,10 @@ function propagate( model ) {
|
|
|
313
319
|
|
|
314
320
|
function items( prop, target, source ) {
|
|
315
321
|
// usually considered expensive, except:
|
|
316
|
-
// - array of Entity
|
|
322
|
+
// - array of Entity
|
|
317
323
|
const line = availableAtType( prop, target, source );
|
|
318
324
|
if (!line ||
|
|
319
|
-
line.type && line.type._artifact && line.type._artifact.kind === 'entity'
|
|
320
|
-
!line.elements && !line.enum && !line.items && !enableExpandElements)
|
|
325
|
+
line.type && line.type._artifact && line.type._artifact.kind === 'entity')
|
|
321
326
|
returns( prop, target, source, true );
|
|
322
327
|
}
|
|
323
328
|
}
|
package/lib/compiler/resolve.js
CHANGED
|
@@ -87,6 +87,7 @@ function resolve( model ) {
|
|
|
87
87
|
} = model.$messageFunctions;
|
|
88
88
|
const {
|
|
89
89
|
resolvePath,
|
|
90
|
+
checkAnnotate,
|
|
90
91
|
defineAnnotations,
|
|
91
92
|
attachAndEmitValidNames,
|
|
92
93
|
lateExtensions,
|
|
@@ -103,11 +104,6 @@ function resolve( model ) {
|
|
|
103
104
|
|
|
104
105
|
/** @type {any} may also be a boolean */
|
|
105
106
|
|
|
106
|
-
// behavior depending on option `deprecated`:
|
|
107
|
-
const enableExpandElements = !isDeprecatedEnabled( options, '_noElementsExpansion' );
|
|
108
|
-
// TODO: we should get rid of noElementsExpansion soon; both
|
|
109
|
-
// beta.nestedProjections and beta.universalCsn do not work with it.
|
|
110
|
-
|
|
111
107
|
return doResolve();
|
|
112
108
|
|
|
113
109
|
function doResolve() {
|
|
@@ -314,7 +310,7 @@ function resolve( model ) {
|
|
|
314
310
|
if (obj.type) // TODO: && !obj.type.$inferred ?
|
|
315
311
|
resolveTypeExpr( obj, art );
|
|
316
312
|
const type = effectiveType( obj ); // make sure implicitly redirected target exists
|
|
317
|
-
if (!obj.items && type && type.items
|
|
313
|
+
if (!obj.items && type && type.items) {
|
|
318
314
|
// TODO: shouldn't be this part of populate.js ?
|
|
319
315
|
const items = {
|
|
320
316
|
location: weakLocation( (obj.type || obj).location ),
|
|
@@ -327,8 +323,7 @@ function resolve( model ) {
|
|
|
327
323
|
}
|
|
328
324
|
if (obj.items) { // TODO: make this a while in v2 (also items proxy)
|
|
329
325
|
obj = obj.items || obj; // the object which has type properties
|
|
330
|
-
|
|
331
|
-
effectiveType(obj);
|
|
326
|
+
effectiveType(obj);
|
|
332
327
|
}
|
|
333
328
|
if (obj.type) { // TODO: && !obj.type.$inferred ?
|
|
334
329
|
if (obj !== (art.returns || art)) // not already checked
|
|
@@ -516,6 +511,8 @@ function resolve( model ) {
|
|
|
516
511
|
setArtifactLink( ext.name, art );
|
|
517
512
|
|
|
518
513
|
if (art) {
|
|
514
|
+
if (art.kind === 'annotate')
|
|
515
|
+
checkAnnotate( ext, art );
|
|
519
516
|
defineAnnotations( ext, art, ext._block, ext.kind );
|
|
520
517
|
// eslint-disable-next-line no-shadow
|
|
521
518
|
forEachMember( ext, ( elem, name, prop ) => {
|
|
@@ -538,7 +535,7 @@ function resolve( model ) {
|
|
|
538
535
|
}
|
|
539
536
|
else if (prop === 'actions') {
|
|
540
537
|
if (!feature) {
|
|
541
|
-
warning( 'anno-unexpected-actions', [ ext.name.location, art ], {},
|
|
538
|
+
warning( 'anno-unexpected-actions', [ ext.name.location, art._parent || art ], {},
|
|
542
539
|
'Actions and functions only exist top-level and for entities' );
|
|
543
540
|
}
|
|
544
541
|
else {
|
|
@@ -569,7 +566,7 @@ function resolve( model ) {
|
|
|
569
566
|
// Currently(?), effectiveType() does not calculate the effective type of
|
|
570
567
|
// its line item:
|
|
571
568
|
effectiveType( obj );
|
|
572
|
-
if (art._annotate.elements)
|
|
569
|
+
if (art._annotate.elements) // explicit $expand on aor needed
|
|
573
570
|
setExpandStatusAnnotate( aor, 'annotate' );
|
|
574
571
|
annotate( obj, 'element', 'elements', 'enum', art );
|
|
575
572
|
annotate( art, 'action', 'actions' );
|
|
@@ -593,6 +590,8 @@ function resolve( model ) {
|
|
|
593
590
|
// eslint-disable-next-line no-shadow
|
|
594
591
|
function annotate( obj, kind, prop, altProp, parent = obj ) {
|
|
595
592
|
const dict = art._annotate[prop];
|
|
593
|
+
if (dict && art._annotate[prop])
|
|
594
|
+
setExpandStatusAnnotate( art, 'annotate' );
|
|
596
595
|
const env = obj[prop] || altProp && obj[altProp] || null;
|
|
597
596
|
for (const n in dict)
|
|
598
597
|
annotateMembers( env && env[n], dict[n], prop, n, parent, kind );
|
|
@@ -615,7 +614,7 @@ function resolve( model ) {
|
|
|
615
614
|
|
|
616
615
|
function expandParameters( action ) {
|
|
617
616
|
// see also expandElements()
|
|
618
|
-
if (!
|
|
617
|
+
if (!effectiveType( action ))
|
|
619
618
|
return;
|
|
620
619
|
const chain = [];
|
|
621
620
|
// Should we be able to consider params and returns separately?
|
|
@@ -956,7 +955,7 @@ function resolve( model ) {
|
|
|
956
955
|
resolveBy( query.$orderBy, 'order-by-union', query.elements, query._parent );
|
|
957
956
|
if (query.orderBy) { // ORDER BY
|
|
958
957
|
// search in `query.elements` after having checked table aliases of the current query
|
|
959
|
-
resolveBy( query.orderBy, '
|
|
958
|
+
resolveBy( query.orderBy, 'order-by', query.elements );
|
|
960
959
|
// TODO: disallow resulting element ref if in expression!
|
|
961
960
|
// Necessary to check it in the compiler as it might work with other semantics on DB!
|
|
962
961
|
// (we could downgrade it to a warning if name is equal to unique source element name)
|
|
@@ -1264,7 +1263,7 @@ function resolve( model ) {
|
|
|
1264
1263
|
const typeArt = resolveType( art.type, user );
|
|
1265
1264
|
if (typeArt) {
|
|
1266
1265
|
resolveTypeArgumentsUnchecked( art, typeArt, user );
|
|
1267
|
-
checkTypeArguments( art );
|
|
1266
|
+
checkTypeArguments( art, typeArt );
|
|
1268
1267
|
}
|
|
1269
1268
|
}
|
|
1270
1269
|
|
|
@@ -1272,13 +1271,13 @@ function resolve( model ) {
|
|
|
1272
1271
|
* Check the type arguments on `artWithType`.
|
|
1273
1272
|
* If the effective type is an array or structured type, an error is emitted.
|
|
1274
1273
|
*/
|
|
1275
|
-
function checkTypeArguments( artWithType ) {
|
|
1274
|
+
function checkTypeArguments( artWithType, typeArt ) {
|
|
1276
1275
|
// Note: `_effectiveType` may point to `artWithType` itself, if the type is structured.
|
|
1277
1276
|
// Also: For enums, it points to the enum type, which is why this trick is needed.
|
|
1278
1277
|
// TODO(#8942): May not be necessary if effectiveType() is adapted. Furthermore, the enum
|
|
1279
1278
|
// trick may be removed if effectiveType() does not stop at enums.
|
|
1280
1279
|
const cyclic = new Set();
|
|
1281
|
-
let effectiveTypeArt = effectiveType(
|
|
1280
|
+
let effectiveTypeArt = effectiveType( typeArt );
|
|
1282
1281
|
while (effectiveTypeArt && effectiveTypeArt.enum && !cyclic.has(effectiveTypeArt)) {
|
|
1283
1282
|
cyclic.add(effectiveTypeArt);
|
|
1284
1283
|
const underlyingEnumType = directType(effectiveTypeArt);
|
package/lib/compiler/shared.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
const { searchName } = require('../base/messages');
|
|
7
7
|
const { dictAddArray } = require('../base/dictionaries');
|
|
8
|
+
const { isDeprecatedEnabled } = require('../base/model');
|
|
8
9
|
|
|
9
10
|
const {
|
|
10
11
|
setLink,
|
|
@@ -144,6 +145,13 @@ function fns( model ) {
|
|
|
144
145
|
rewrite: {
|
|
145
146
|
next: '_$next', dollar: true, escape: 'param', noDep: true, rewrite: true,
|
|
146
147
|
}, // TODO: assertion that there is no next/escape used
|
|
148
|
+
'order-by': {
|
|
149
|
+
next: '_$next',
|
|
150
|
+
dollar: true,
|
|
151
|
+
escape: 'param',
|
|
152
|
+
assoc: 'nav',
|
|
153
|
+
deprecatedSourceRefs: true,
|
|
154
|
+
},
|
|
147
155
|
'order-by-union': {
|
|
148
156
|
next: '_$next', dollar: true, escape: 'param', noDep: true, noExt: true,
|
|
149
157
|
},
|
|
@@ -160,6 +168,7 @@ function fns( model ) {
|
|
|
160
168
|
resolveUncheckedPath,
|
|
161
169
|
resolveTypeArgumentsUnchecked,
|
|
162
170
|
resolvePath,
|
|
171
|
+
checkAnnotate,
|
|
163
172
|
defineAnnotations,
|
|
164
173
|
attachAndEmitValidNames,
|
|
165
174
|
} );
|
|
@@ -602,6 +611,21 @@ function fns( model ) {
|
|
|
602
611
|
else if (r) {
|
|
603
612
|
return setArtifactLink( head, r );
|
|
604
613
|
}
|
|
614
|
+
else if (spec.deprecatedSourceRefs && env._combined &&
|
|
615
|
+
isDeprecatedEnabled( options, 'autoCorrectOrderBySourceRefs' )) {
|
|
616
|
+
// User has provided a source element without table alias where a query
|
|
617
|
+
// element is expected. Possible on many DBs (and compiler v1), in CAP
|
|
618
|
+
// only with table alias. Auto-correct it if no duplicate.
|
|
619
|
+
// TODO: we could use that info also in messages when the deprecated flag is not set
|
|
620
|
+
const s = env._combined[head.id];
|
|
621
|
+
if (s && !Array.isArray(s)) {
|
|
622
|
+
path.$prefix = s.name.alias; // pushing it to path directly could be problematic
|
|
623
|
+
warning( null, [ head.location, user ],
|
|
624
|
+
{ id: head.id, newcode: `${ s.name.alias }.${ head.id }` },
|
|
625
|
+
'Replace source element reference $(ID) by $(NEWCODE); auto-corrected' );
|
|
626
|
+
return setArtifactLink( head, s );
|
|
627
|
+
}
|
|
628
|
+
}
|
|
605
629
|
}
|
|
606
630
|
if (spec.noMessage || msgArt === true && extDict === model.definitions)
|
|
607
631
|
return null;
|
|
@@ -861,35 +885,38 @@ function fns( model ) {
|
|
|
861
885
|
}
|
|
862
886
|
}
|
|
863
887
|
|
|
888
|
+
// Issue messages for annotations on namespaces and builtins
|
|
889
|
+
// (TODO: really here?, probably split main artifacts vs returns)
|
|
890
|
+
// see also lateExtensions() where similar messages are reported
|
|
891
|
+
function checkAnnotate( construct, art ) {
|
|
892
|
+
// Namespaces cannot be annotated in CSN but because they exist as XSN artifacts
|
|
893
|
+
// they can still be applied. Namespace annotations are extracted in to-csn.js
|
|
894
|
+
// In parseCdl mode USINGs and other unknown references are generated as
|
|
895
|
+
// namespaces which would lead to false positives.
|
|
896
|
+
// TODO: should this really be different to annotate-unknown?
|
|
897
|
+
if (art.kind === 'namespace') {
|
|
898
|
+
info( 'anno-namespace', [ construct.name.location, construct ], {},
|
|
899
|
+
'Namespaces can\'t be annotated' );
|
|
900
|
+
}
|
|
901
|
+
// Builtin annotations would also get lost. Same as for namespaces:
|
|
902
|
+
// extracted in to-csn.js
|
|
903
|
+
else if (art.builtin === true) {
|
|
904
|
+
info( 'anno-builtin', [ construct.name.location, construct ], {},
|
|
905
|
+
'Builtin types should not be annotated. Use custom type instead' );
|
|
906
|
+
}
|
|
907
|
+
else if (construct.$syntax === 'returns' && art._block && art.kind !== 'action' &&
|
|
908
|
+
art.kind !== 'function' ) {
|
|
909
|
+
// `annotate ABC with returns {}` is handled just like `elements`. Warn if it is used
|
|
910
|
+
// for non-actions. We can't only check for !art.returns, because `action A();` is valid.
|
|
911
|
+
// `art._block` ensures that `art` is a defined def.
|
|
912
|
+
warning('anno-unexpected-returns', [ construct.name.location, construct ],
|
|
913
|
+
{ keyword: 'returns', kind: art.kind }, 'Unexpected $(KEYWORD) for $(KIND)');
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
864
917
|
// Set _block links for annotations (necessary for layering).
|
|
865
|
-
// Issue messages for annotations on namespaces and builtins (TODO: really here?)
|
|
866
918
|
// Also copy annotations from `construct` to `art` (TODO: separate that functionality).
|
|
867
919
|
function defineAnnotations( construct, art, block, priority = false ) {
|
|
868
|
-
if (!options.parseCdl && construct.kind === 'annotate') {
|
|
869
|
-
// Namespaces cannot be annotated in CSN but because they exist as XSN artifacts
|
|
870
|
-
// they can still be applied. Namespace annotations are extracted in to-csn.js
|
|
871
|
-
// In parseCdl mode USINGs and other unknown references are generated as
|
|
872
|
-
// namespaces which would lead to false positives.
|
|
873
|
-
// TODO: should this really be different to annotate-unknown?
|
|
874
|
-
if (art.kind === 'namespace') {
|
|
875
|
-
info( 'anno-namespace', [ construct.name.location, construct ], {},
|
|
876
|
-
'Namespaces can\'t be annotated' );
|
|
877
|
-
}
|
|
878
|
-
// Builtin annotations would also get lost. Same as for namespaces:
|
|
879
|
-
// extracted in to-csn.js
|
|
880
|
-
else if (art.builtin === true) {
|
|
881
|
-
info( 'anno-builtin', [ construct.name.location, construct ], {},
|
|
882
|
-
'Builtin types should not be annotated. Use custom type instead' );
|
|
883
|
-
}
|
|
884
|
-
else if (construct.$syntax === 'returns' && art._block && art.kind !== 'action' &&
|
|
885
|
-
art.kind !== 'function' ) {
|
|
886
|
-
// `annotate ABC with returns {}` is handled just like `elements`. Warn if it is used
|
|
887
|
-
// for non-actions. We can't only check for !art.returns, because `action A();` is valid.
|
|
888
|
-
// `art._block` ensures that `art` is a defined def.
|
|
889
|
-
warning('anno-unexpected-returns', [ construct.name.location, construct ],
|
|
890
|
-
{ keyword: 'returns', kind: art.kind }, 'Unexpected $(KEYWORD) for $(KIND)');
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
920
|
if (construct.doc)
|
|
894
921
|
art.doc = construct.doc; // e.g. through `extensions` array in CSN
|
|
895
922
|
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
5
|
const {
|
|
6
|
-
isDeprecatedEnabled,
|
|
7
6
|
forEachDefinition,
|
|
8
7
|
forEachGeneric,
|
|
9
8
|
forEachInOrder,
|
|
@@ -26,7 +25,6 @@ const $location = Symbol.for('cds.$location');
|
|
|
26
25
|
|
|
27
26
|
// Export function of this file.
|
|
28
27
|
function tweakAssocs( model ) {
|
|
29
|
-
const { options } = model;
|
|
30
28
|
// Get shared functionality and the message function:
|
|
31
29
|
const {
|
|
32
30
|
info, warning, error,
|
|
@@ -38,11 +36,6 @@ function tweakAssocs( model ) {
|
|
|
38
36
|
} = model.$functions;
|
|
39
37
|
const { environment } = model.$volatileFunctions;
|
|
40
38
|
|
|
41
|
-
// behavior depending on option `deprecated`:
|
|
42
|
-
const enableExpandElements = !isDeprecatedEnabled( options, '_noElementsExpansion' );
|
|
43
|
-
// TODO: we should get rid of noElementsExpansion soon; both
|
|
44
|
-
// beta.nestedProjections and beta.universalCsn do not work with it.
|
|
45
|
-
|
|
46
39
|
// Phase 5: rewrite associations
|
|
47
40
|
forEachDefinition( model, rewriteSimple );
|
|
48
41
|
// TODO: sequence not good enough with derived type of structure with
|
|
@@ -113,7 +106,7 @@ function tweakAssocs( model ) {
|
|
|
113
106
|
|
|
114
107
|
function rewriteAssociationCheck( element ) {
|
|
115
108
|
const elem = element.items || element; // TODO v2: nested items
|
|
116
|
-
if (elem.elements
|
|
109
|
+
if (elem.elements)
|
|
117
110
|
forEachGeneric( elem, 'elements', rewriteAssociationCheck );
|
|
118
111
|
if (!elem.target)
|
|
119
112
|
return;
|
|
@@ -207,7 +200,7 @@ function tweakAssocs( model ) {
|
|
|
207
200
|
|
|
208
201
|
function rewriteAssociation( element ) {
|
|
209
202
|
let elem = element.items || element; // TODO v2: nested items
|
|
210
|
-
if (elem.elements
|
|
203
|
+
if (elem.elements)
|
|
211
204
|
forEachGeneric( elem, 'elements', rewriteAssociation );
|
|
212
205
|
if (!originTarget( elem ))
|
|
213
206
|
return;
|
|
@@ -286,7 +279,7 @@ function tweakAssocs( model ) {
|
|
|
286
279
|
// same (TODO later: set status whether rewrite changes anything),
|
|
287
280
|
// especially problematic are refs starting with $self:
|
|
288
281
|
setExpandStatus( elem, 'target' );
|
|
289
|
-
if (
|
|
282
|
+
if (elem._parent && elem._parent.kind === 'element') {
|
|
290
283
|
// managed association as sub element not supported yet
|
|
291
284
|
error( null, [ elem.location, elem ], {},
|
|
292
285
|
// eslint-disable-next-line max-len
|
|
@@ -369,7 +362,8 @@ function tweakAssocs( model ) {
|
|
|
369
362
|
const item = expr.path[root.kind === '$self' ? 1 : 0];
|
|
370
363
|
if (!item)
|
|
371
364
|
return; // just $self
|
|
372
|
-
|
|
365
|
+
// corresponding elem in including structure
|
|
366
|
+
const elem = (assoc._main.items || assoc._main).elements[item.id];
|
|
373
367
|
if (!(Array.isArray(elem) || // no msg for redefs
|
|
374
368
|
elem === item._artifact || // redirection for explicit def
|
|
375
369
|
elem._origin === item._artifact)) {
|
package/lib/compiler/utils.js
CHANGED
|
@@ -95,6 +95,14 @@ function linkToOrigin( origin, name, parent, prop, location, silentDep ) {
|
|
|
95
95
|
return elem;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Set the member `elem` to have a _parent link to `parent` and a corresponding
|
|
100
|
+
* _main link. Also set the member's name accordingly, where argument `name`
|
|
101
|
+
* is most often the property `elem.name.id`.
|
|
102
|
+
*
|
|
103
|
+
* If argument `prop` is provided, add `elem` to the dictionary of that name,
|
|
104
|
+
* e.g. `elements`.
|
|
105
|
+
*/
|
|
98
106
|
function setMemberParent( elem, name, parent, prop ) {
|
|
99
107
|
if (prop) { // extension or structure include
|
|
100
108
|
// TODO: consider nested ARRAY OF and RETURNS, COMPOSITION OF type
|
|
@@ -107,9 +115,10 @@ function setMemberParent( elem, name, parent, prop ) {
|
|
|
107
115
|
parent = parent._outer;
|
|
108
116
|
setLink( elem, '_parent', parent );
|
|
109
117
|
setLink( elem, '_main', parent._main || parent );
|
|
110
|
-
const parentName = parent.name || parent._outer
|
|
111
|
-
|
|
112
|
-
|
|
118
|
+
const parentName = parent.name || parent._outer?.name;
|
|
119
|
+
if (parentName) // may not be available in e.g. cast() - TODO recheck (#9503)
|
|
120
|
+
elem.name.absolute = parentName.absolute;
|
|
121
|
+
if (!parentName || name == null)
|
|
113
122
|
return;
|
|
114
123
|
const normalized = kindProperties[elem.kind].normalized || elem.kind;
|
|
115
124
|
[ 'element', 'alias', 'select', 'param', 'action' ].forEach( ( kind ) => {
|
|
@@ -357,7 +366,7 @@ function traverseQueryExtra( main, callback ) {
|
|
|
357
366
|
// that value is only on elements, types, and params -> no other members
|
|
358
367
|
// when set, only on elem/art with expanded elements
|
|
359
368
|
// - 'target': all expanded (sub) elements might only have new target/on, but
|
|
360
|
-
// no
|
|
369
|
+
// no individual annotations on any (sub) member
|
|
361
370
|
// when set, traverse all parents where the value has been 'origin' before
|
|
362
371
|
// - 'annotate': at least one inferred (sub) member has an individual annotation,
|
|
363
372
|
// not counting propagated ones; set up to the definition (main artifact)
|
|
@@ -58,7 +58,7 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
58
58
|
let art = null;
|
|
59
59
|
|
|
60
60
|
forEachDefinition(csn, (artifact, artifactName) => {
|
|
61
|
-
if(artifactName
|
|
61
|
+
if(artifactName === serviceName || artifactName.startsWith(serviceName + '.')) {
|
|
62
62
|
art = artifactName;
|
|
63
63
|
handleAnnotations(artifactName, artifact);
|
|
64
64
|
artifact.elements && Object.entries(artifact.elements).forEach(([elementName, element]) => {
|
|
@@ -164,7 +164,7 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
164
164
|
}
|
|
165
165
|
else if (aNameWithoutQualifier === '@Common.ValueList.entity') {
|
|
166
166
|
// if both annotations are present, ignore 'entity' and raise a message
|
|
167
|
-
if (annoNames.map(x=>x.split('#')[0]).find(x=>(x
|
|
167
|
+
if (annoNames.map(x=>x.split('#')[0]).find(x=>(x==='@Common.ValueList.viaAssociation'))) {
|
|
168
168
|
warning(null, null, `in annotation preprocessing/@Common.ValueList: 'entity' is ignored, as 'viaAssociation' is present, ${ctx}`);
|
|
169
169
|
return false;
|
|
170
170
|
}
|
|
@@ -296,8 +296,12 @@ function preprocessAnnotations(csn, serviceName, options) {
|
|
|
296
296
|
|
|
297
297
|
//change the scalar anno into a "pseudo-structured" one
|
|
298
298
|
// TODO should be flattened, but then alphabetical order is destroyed
|
|
299
|
-
|
|
300
|
-
|
|
299
|
+
|
|
300
|
+
// Do not overwrite existing nested annotation values, instead give existing
|
|
301
|
+
// nested annotation precedence and remove outer annotation (always)
|
|
302
|
+
if(!carrier['@Common.Text.@UI.TextArrangement'] && textAnno) {
|
|
303
|
+
carrier['@Common.Text'] = { '$value': textAnno, '@UI.TextArrangement': value };
|
|
304
|
+
}
|
|
301
305
|
delete carrier[aName];
|
|
302
306
|
}
|
|
303
307
|
}
|
package/lib/edm/csn2edm.js
CHANGED
|
@@ -7,7 +7,7 @@ const NAVPROP_TRENNER = '_';
|
|
|
7
7
|
const VALUELIST_NAVPROP_PREFIX = '';
|
|
8
8
|
|
|
9
9
|
const edmUtils = require('./edmUtils.js')
|
|
10
|
-
const { initializeModel
|
|
10
|
+
const { initializeModel } = require('./edmPreprocessor.js');
|
|
11
11
|
const translate = require('./annotations/genericTranslation.js');
|
|
12
12
|
const { setProp } = require('../base/model');
|
|
13
13
|
const { cloneCsnNonDict, isEdmPropertyRendered, isBuiltinType } = require('../model/csnUtils');
|
|
@@ -326,7 +326,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
326
326
|
|
|
327
327
|
/* create the entitytypes and sets
|
|
328
328
|
Do not create an entity set if:
|
|
329
|
-
V4 containment:
|
|
329
|
+
V4 containment: $containerNames is set and not equal with the artifact name
|
|
330
330
|
Entity starts with 'localserviceNameized.' or ends with '_localized'
|
|
331
331
|
*/
|
|
332
332
|
edmUtils.foreach(schemaCsn.definitions,
|
|
@@ -433,7 +433,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
433
433
|
// CDXCORE-CDXCORE-173
|
|
434
434
|
if(options.isV2() && hasStream) {
|
|
435
435
|
attributes['m:HasStream'] = true;
|
|
436
|
-
assignAnnotation(entityCsn, '@Core.MediaType', hasStream);
|
|
436
|
+
edmUtils.assignAnnotation(entityCsn, '@Core.MediaType', hasStream);
|
|
437
437
|
}
|
|
438
438
|
|
|
439
439
|
Schema.append(new Edm.EntityType(v, attributes, properties, entityCsn));
|
package/lib/edm/edm.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const edmUtils = require('./edmUtils.js');
|
|
4
4
|
const { isBuiltinType } = require('../model/csnUtils.js');
|
|
5
5
|
const { forEach } = require("../utils/objectUtils");
|
|
6
|
+
const { isBetaEnabled } = require('../base/model.js');
|
|
6
7
|
|
|
7
8
|
// facet definitions, optional could either be true or array of edm types
|
|
8
9
|
// remove indicates wether or not the canonic facet shall be removed when applying @odata.Type
|
|
@@ -754,7 +755,14 @@ function getEdm(options, messageFunctions) {
|
|
|
754
755
|
}
|
|
755
756
|
}
|
|
756
757
|
|
|
757
|
-
class ComplexType extends TypeBase {
|
|
758
|
+
class ComplexType extends TypeBase {
|
|
759
|
+
constructor(v, details, csn) {
|
|
760
|
+
super(v, details, csn);
|
|
761
|
+
if(this.v4 && !!csn['@open'] && isBetaEnabled(options, 'odataOpenType')) {
|
|
762
|
+
this._edmAttributes['OpenType'] = true;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
758
766
|
class EntityType extends ComplexType
|
|
759
767
|
{
|
|
760
768
|
constructor(v, details, properties, csn)
|