@sap/cds-compiler 6.6.2 → 6.7.1
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 +28 -1
- package/bin/cdsc.js +2 -0
- package/bin/cdsse.js +1 -1
- package/lib/base/message-registry.js +6 -7
- package/lib/base/model.js +0 -72
- package/lib/checks/elements.js +1 -1
- package/lib/checks/featureFlags.js +2 -2
- package/lib/compiler/assert-consistency.js +3 -4
- package/lib/compiler/base.js +8 -0
- package/lib/compiler/builtins.js +8 -9
- package/lib/compiler/checks.js +27 -6
- package/lib/compiler/cycle-detector.js +4 -4
- package/lib/compiler/define.js +65 -83
- package/lib/compiler/extend.js +357 -325
- package/lib/compiler/finalize-parse-cdl.js +3 -4
- package/lib/compiler/generate.js +205 -203
- package/lib/compiler/kick-start.js +34 -49
- package/lib/compiler/populate.js +95 -28
- package/lib/compiler/propagator.js +3 -5
- package/lib/compiler/resolve.js +17 -13
- package/lib/compiler/shared.js +47 -19
- package/lib/compiler/tweak-assocs.js +2 -4
- package/lib/compiler/utils.js +84 -31
- package/lib/gen/BaseParser.js +924 -1055
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +5 -2
- package/lib/json/from-csn.js +25 -16
- package/lib/main.d.ts +13 -0
- package/lib/model/revealInternalProperties.js +18 -0
- package/lib/parsers/AstBuildingParser.js +22 -5
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/utils/sql.js +2 -2
- package/lib/render/utils/standardDatabaseFunctions.js +2 -2
- package/lib/transform/db/constraints.js +3 -4
- package/lib/transform/db/killAnnotations.js +1 -1
- package/lib/transform/db/processSqlServices.js +10 -11
- package/lib/transform/forOdata.js +7 -124
- package/lib/transform/odata/fioriTreeViews.js +173 -0
- package/lib/transform/odata/flattening.js +2 -2
- package/lib/transform/translateAssocsToJoins.js +7 -4
- package/package.json +1 -1
- package/share/messages/message-explanations.json +0 -2
- package/share/messages/type-unexpected-foreign-keys.md +0 -52
- package/share/messages/type-unexpected-on-condition.md +0 -52
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
// Kick-start: prepare to resolve all references
|
|
2
2
|
|
|
3
|
+
// Remark: it would probably be better to move this to extend.js
|
|
4
|
+
|
|
3
5
|
'use strict';
|
|
4
6
|
|
|
5
|
-
const {
|
|
6
|
-
const { isBetaEnabled, forEachGeneric } = require('../base/model');
|
|
7
|
+
const { isBetaEnabled } = require('../base/model');
|
|
7
8
|
const {
|
|
8
9
|
setLink,
|
|
9
10
|
annotationVal,
|
|
10
11
|
annotationIsFalse,
|
|
11
|
-
|
|
12
|
+
forEachGeneric,
|
|
12
13
|
} = require('./utils');
|
|
13
14
|
|
|
14
15
|
function kickStart( model ) {
|
|
15
16
|
const { options } = model;
|
|
16
17
|
const { message } = model.$messageFunctions;
|
|
17
18
|
|
|
18
|
-
const { resolveUncheckedPath,
|
|
19
|
+
const { resolveUncheckedPath, createGapArtifact } = model.$functions;
|
|
19
20
|
|
|
20
21
|
// Set _service link (sorted to set it on parent first). Could be set
|
|
21
|
-
// directly, but beware a
|
|
22
|
+
// directly, but beware a gap artifact becoming a service later.
|
|
22
23
|
Object.keys( model.definitions ).sort().forEach( setAncestorsAndService );
|
|
23
24
|
forEachGeneric( model, 'definitions', postProcessArtifact );
|
|
24
25
|
return;
|
|
@@ -40,8 +41,9 @@ function kickStart( model ) {
|
|
|
40
41
|
const art = model.definitions[name];
|
|
41
42
|
if (art._parent === undefined)
|
|
42
43
|
return; // nothing to do for builtins and redefinitions
|
|
43
|
-
if (art.query
|
|
44
|
-
|
|
44
|
+
if ((art.query || art._entityIncludes) &&
|
|
45
|
+
art._ancestors === undefined && art.kind === 'entity')
|
|
46
|
+
setProjectionAncestors( art ); // TODO: set to [] for other entities
|
|
45
47
|
|
|
46
48
|
let parent = art._parent;
|
|
47
49
|
if (parent === model.definitions.localized)
|
|
@@ -62,7 +64,7 @@ function kickStart( model ) {
|
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
function setProjectionAncestors( art ) {
|
|
67
|
+
function setProjectionAncestors( art ) { // TODO: rename
|
|
66
68
|
// Must be run after processLocalizedData() as we could have a projection
|
|
67
69
|
// on a generated entity.
|
|
68
70
|
|
|
@@ -70,19 +72,36 @@ function kickStart( model ) {
|
|
|
70
72
|
// no redirection target for E if Service2.E = projection on Service1.E and
|
|
71
73
|
// Service1.E = projection on E
|
|
72
74
|
|
|
73
|
-
//
|
|
74
|
-
// types and events (TODO: entity only)
|
|
75
|
-
//
|
|
75
|
+
// Remark: _ancestors are also set with entity includes in extend.js,
|
|
76
76
|
// Remark: _ancestors are also tested in populate.js for minmal exposure
|
|
77
|
+
//
|
|
78
|
+
// Remark: @cds.autoexpose is considered on-demand in populate.js
|
|
77
79
|
const chain = [];
|
|
78
80
|
const autoexposed = annotationVal( art['@cds.autoexposed'] );
|
|
79
81
|
// no need to set preferredRedirectionTarget in the while loop as we would
|
|
80
82
|
// use the projection having @cds.redirection.target anyhow instead of
|
|
81
83
|
// `art` anyway (if we do the no-x-service-implicit-redirection TODO above)
|
|
82
|
-
while (art?.
|
|
83
|
-
art.
|
|
84
|
-
|
|
84
|
+
while (art?.kind === 'entity' &&
|
|
85
|
+
(art.query?.from?.path || // direct select with one source
|
|
86
|
+
art._entityIncludes?.length) &&
|
|
87
|
+
!art._ancestors && art._ancestors !== 0) { // prevent inf-loop
|
|
85
88
|
setLink( art, '_ancestors', 0 ); // avoid infloop with cyclic from
|
|
89
|
+
if (art._entityIncludes?.length) {
|
|
90
|
+
if (art._entityIncludes.length === 1) {
|
|
91
|
+
chain.push( art );
|
|
92
|
+
art = art._entityIncludes[0];
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
setLink( art, '_ancestors', [] );
|
|
96
|
+
for (const incl of art._entityIncludes) {
|
|
97
|
+
if ((incl.query || incl._entityIncludes?.length) &&
|
|
98
|
+
incl._ancestors === undefined)
|
|
99
|
+
setProjectionAncestors( incl );
|
|
100
|
+
art._ancestors.push( ...(incl._ancestors || []), incl );
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
chain.push( art );
|
|
86
105
|
const name = resolveUncheckedPath( art.query.from, 'from', art );
|
|
87
106
|
art = name && (model.definitions[name] || createGapArtifact( name ));
|
|
88
107
|
if (autoexposed)
|
|
@@ -97,26 +116,7 @@ function kickStart( model ) {
|
|
|
97
116
|
}
|
|
98
117
|
}
|
|
99
118
|
|
|
100
|
-
function createGapArtifact( name, location = builtinLocation() ) {
|
|
101
|
-
// TODO: make it probably part of define.js
|
|
102
|
-
// TODO: make it work without location (or value undefined/null)
|
|
103
|
-
// TODO: change the location later if overwritten
|
|
104
|
-
const art = {
|
|
105
|
-
kind: 'namespace', name: { id: name, location }, location,
|
|
106
|
-
};
|
|
107
|
-
model.definitions[name] = art;
|
|
108
|
-
initMainArtifact( art );
|
|
109
|
-
return art;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
119
|
function postProcessArtifact( art ) {
|
|
113
|
-
tagCompositionTargets( art );
|
|
114
|
-
if (art.$queries) {
|
|
115
|
-
for (const query of art.$queries) {
|
|
116
|
-
if (query.mixin)
|
|
117
|
-
forEachGeneric( query, 'mixin', tagCompositionTargets );
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
120
|
if (!art._ancestors || art.kind !== 'entity')
|
|
121
121
|
return; // redirections only to entities
|
|
122
122
|
const service = art._service;
|
|
@@ -130,28 +130,13 @@ function kickStart( model ) {
|
|
|
130
130
|
if (ancestor._service === service || annotationIsFalse( art['@cds.redirection.target'] ))
|
|
131
131
|
return;
|
|
132
132
|
const desc = ancestor._descendants ||
|
|
133
|
-
|
|
133
|
+
setLink( ancestor, '_descendants', Object.create( null ) );
|
|
134
134
|
if (!desc[sname])
|
|
135
135
|
desc[sname] = [ art ];
|
|
136
136
|
else
|
|
137
137
|
desc[sname].push( art );
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
|
-
|
|
141
|
-
function tagCompositionTargets( elem ) {
|
|
142
|
-
// TODO: together with test for targetIsTargetAspect()
|
|
143
|
-
if (elem.target && isDirectComposition( elem )) {
|
|
144
|
-
// A target aspect would have already moved to property `targetAspect` in
|
|
145
|
-
// define.js (hm... more something for kick-start.js...)
|
|
146
|
-
// TODO: for safety, just use resolveUncheckedPath()
|
|
147
|
-
const target = resolveUncheckedPath( elem.target, 'target', elem );
|
|
148
|
-
if (target)
|
|
149
|
-
model.$compositionTargets[target] = true;
|
|
150
|
-
}
|
|
151
|
-
if (elem.targetAspect?.elements)
|
|
152
|
-
elem = elem.targetAspect;
|
|
153
|
-
forEachGeneric( elem, 'elements', tagCompositionTargets );
|
|
154
|
-
}
|
|
155
140
|
}
|
|
156
141
|
|
|
157
142
|
module.exports = kickStart;
|
package/lib/compiler/populate.js
CHANGED
|
@@ -17,12 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
'use strict';
|
|
19
19
|
|
|
20
|
-
const {
|
|
21
|
-
isDeprecatedEnabled,
|
|
22
|
-
forEachDefinition,
|
|
23
|
-
forEachMember,
|
|
24
|
-
forEachGeneric,
|
|
25
|
-
} = require('../base/model');
|
|
20
|
+
const { isDeprecatedEnabled } = require('../base/model');
|
|
26
21
|
const {
|
|
27
22
|
dictAdd, dictAddArray, dictFirst, dictForEach,
|
|
28
23
|
} = require('../base/dictionaries');
|
|
@@ -44,6 +39,9 @@ const {
|
|
|
44
39
|
setExpandStatusAnnotate,
|
|
45
40
|
dependsOnSilent,
|
|
46
41
|
columnRefStartsWithSelf,
|
|
42
|
+
forEachDefinition,
|
|
43
|
+
forEachMember,
|
|
44
|
+
forEachGeneric,
|
|
47
45
|
} = require('./utils');
|
|
48
46
|
const { typeParameters } = require('./builtins');
|
|
49
47
|
|
|
@@ -73,39 +71,48 @@ function populate( model ) {
|
|
|
73
71
|
} = model.$messageFunctions;
|
|
74
72
|
const {
|
|
75
73
|
resolvePath,
|
|
74
|
+
resolveUncheckedPath,
|
|
75
|
+
createGapArtifact,
|
|
76
76
|
nestedElements,
|
|
77
77
|
attachAndEmitValidNames,
|
|
78
78
|
initMainArtifact,
|
|
79
|
+
initArtifactParentLink,
|
|
79
80
|
extendArtifactBefore,
|
|
80
81
|
extendArtifactAfter,
|
|
82
|
+
extendArtifactAdd,
|
|
83
|
+
generateForEntity,
|
|
84
|
+
populateGeneratedEntity,
|
|
81
85
|
} = model.$functions;
|
|
82
86
|
Object.assign( model.$functions, {
|
|
83
87
|
effectiveType,
|
|
88
|
+
generateOnDemand,
|
|
84
89
|
getOrigin,
|
|
85
90
|
getInheritedProp,
|
|
86
91
|
mergeSpecifiedForeignKeys,
|
|
92
|
+
registerGeneratedEntity,
|
|
87
93
|
} );
|
|
88
94
|
// let depth = 100;
|
|
89
95
|
|
|
90
96
|
let effectiveSeqNo = 0; // artifact number set after having set _effectiveType
|
|
91
97
|
/** @type {any} may also be a boolean */
|
|
92
|
-
let
|
|
98
|
+
let newGeneratedEntities = [];
|
|
93
99
|
|
|
94
100
|
const ignoreSpecifiedElements
|
|
95
101
|
= isDeprecatedEnabled( model.options, 'ignoreSpecifiedQueryElements' );
|
|
96
102
|
|
|
97
103
|
forEachDefinition( model, traverseElementEnvironments );
|
|
98
|
-
while (
|
|
99
|
-
// console.log(
|
|
100
|
-
const all =
|
|
101
|
-
|
|
104
|
+
while (newGeneratedEntities.length) {
|
|
105
|
+
// console.log( newGeneratedEntities.map( a => a.name.id ) )
|
|
106
|
+
const all = newGeneratedEntities;
|
|
107
|
+
newGeneratedEntities = [];
|
|
102
108
|
all.forEach( traverseElementEnvironments );
|
|
103
109
|
}
|
|
104
|
-
|
|
110
|
+
newGeneratedEntities = true; // internal error if entity generation after here
|
|
105
111
|
return;
|
|
106
112
|
|
|
107
113
|
/** Make sure that effectiveType() is called on all members and items */
|
|
108
114
|
function traverseElementEnvironments( art ) {
|
|
115
|
+
// console.log('TEE:',require('../model/revealInternalProperties').ref(art))
|
|
109
116
|
// We leave out foreign keys (as they are traversed via forEachMember).
|
|
110
117
|
// Keys are handled in tweak-assocs.js
|
|
111
118
|
if (art.kind === 'key')
|
|
@@ -117,10 +124,15 @@ function populate( model ) {
|
|
|
117
124
|
art.$queries.forEach( traverseElementEnvironments );
|
|
118
125
|
if (art.mixin)
|
|
119
126
|
dictForEach( art.mixin, effectiveType );
|
|
120
|
-
if (art.targetAspect?.elements)
|
|
127
|
+
if (art.targetAspect?.elements) {
|
|
121
128
|
effectiveType( art.targetAspect );
|
|
122
|
-
|
|
129
|
+
forEachGeneric( art.targetAspect, 'elements', traverseElementEnvironments );
|
|
130
|
+
}
|
|
131
|
+
else if (art !== art._main?._leadingQuery) { // already done for leading query elems
|
|
132
|
+
while (art.items)
|
|
133
|
+
art = art.items;
|
|
123
134
|
forEachMember( art, traverseElementEnvironments );
|
|
135
|
+
}
|
|
124
136
|
}
|
|
125
137
|
|
|
126
138
|
|
|
@@ -191,6 +203,7 @@ function populate( model ) {
|
|
|
191
203
|
}
|
|
192
204
|
chain.reverse();
|
|
193
205
|
for (const a of chain) {
|
|
206
|
+
// console.log('PEA:',require('../model/revealInternalProperties').ref(a))
|
|
194
207
|
// Ensure that the _effectiveType of the parent has been calculated. This
|
|
195
208
|
// is usually the case, but might not be for elements of anonymous target
|
|
196
209
|
// aspects. Without it, extensions/annotations might get lost.
|
|
@@ -202,6 +215,10 @@ function populate( model ) {
|
|
|
202
215
|
// TODO: forbid $self+$self.elem inline, see expandWildcard()
|
|
203
216
|
// Without type, value.path or _origin at beginning, link to itself:
|
|
204
217
|
extendArtifactBefore( a );
|
|
218
|
+
populateGeneratedEntity( a );
|
|
219
|
+
if (a.includes)
|
|
220
|
+
a.includes.forEach( i => resolveInclude( i, a ) );
|
|
221
|
+
extendArtifactAdd( a );
|
|
205
222
|
art = populateArtifact( a, art ) || a;
|
|
206
223
|
setLink( a, '_effectiveType', art );
|
|
207
224
|
a.$effectiveSeqNo = ++effectiveSeqNo;
|
|
@@ -209,6 +226,8 @@ function populate( model ) {
|
|
|
209
226
|
if (a.elements$ || a.enum$)
|
|
210
227
|
mergeSpecifiedElementsOrEnum( a );
|
|
211
228
|
// console.log( 'ET-DO:', effectiveSeqNo, a?.kind, a?.name, a._extensions?.elements?.length )
|
|
229
|
+
if (a.kind === 'entity' && !a.query)
|
|
230
|
+
generateForEntity( a );
|
|
212
231
|
extendArtifactAfter( a ); // after setting _effectiveType (for messages)
|
|
213
232
|
if (a.typeProps$)
|
|
214
233
|
setSpecifiedElementTypeProperties( a );
|
|
@@ -217,13 +236,22 @@ function populate( model ) {
|
|
|
217
236
|
return art;
|
|
218
237
|
}
|
|
219
238
|
|
|
239
|
+
function resolveInclude( include, art ) {
|
|
240
|
+
// includes have been resolved with resolveUncheckedPath() with art/ext
|
|
241
|
+
// before, i.e. it is ok to just use `a` as "user"
|
|
242
|
+
const name = resolveUncheckedPath( include, 'include', art );
|
|
243
|
+
// use effectiveType(), which applies extensions, before resolvePath()
|
|
244
|
+
// where we check for the number of elements in the accept functions
|
|
245
|
+
if (name)
|
|
246
|
+
effectiveType( model.definitions[name] );
|
|
247
|
+
resolvePath( include, 'include', art );
|
|
248
|
+
}
|
|
249
|
+
|
|
220
250
|
function populateArtifact( art, origEffective ) {
|
|
221
251
|
// Name-resolution relevant properties directly at artifact:
|
|
222
252
|
// ‹view›.elements of input must have been moved (to elements$) before!
|
|
223
253
|
// console.log('Q:',art.elements,art.enum,art.items,!!art.query)
|
|
224
254
|
// console.log('PA:',require('../model/revealInternalProperties').ref(art))
|
|
225
|
-
if (art.includes) // first version of includes via effectiveTpe()
|
|
226
|
-
art.includes.forEach( i => effectiveType( i._artifact ) );
|
|
227
255
|
if (art.elements != null || art.enum != null || art.items != null)
|
|
228
256
|
return art;
|
|
229
257
|
if (art.target) {
|
|
@@ -292,6 +320,36 @@ function populate( model ) {
|
|
|
292
320
|
return origEffective;
|
|
293
321
|
}
|
|
294
322
|
|
|
323
|
+
/**
|
|
324
|
+
* Potentially create texts or target entity on demand.
|
|
325
|
+
*
|
|
326
|
+
* `nameOrArt` is the name of the requested entity or a gap artifact having that
|
|
327
|
+
* name. Returns the generated entity, or null if no such entity is created.
|
|
328
|
+
*/
|
|
329
|
+
function generateOnDemand( nameOrArt ) {
|
|
330
|
+
const gap = (typeof nameOrArt === 'string')
|
|
331
|
+
? model.definitions[nameOrArt] || createGapArtifact( nameOrArt )
|
|
332
|
+
: nameOrArt;
|
|
333
|
+
let base = gap._parent;
|
|
334
|
+
if (!base)
|
|
335
|
+
return null;
|
|
336
|
+
const chain = [];
|
|
337
|
+
// We could have requested Base.component.texts before Base.component:
|
|
338
|
+
// from Base, first create Base.component, then Base.component.texts
|
|
339
|
+
for (; base?.kind === 'namespace'; base = base._parent)
|
|
340
|
+
chain.push( base );
|
|
341
|
+
if (base?.kind !== 'entity' || base.query || base.$duplicates || !base.elements)
|
|
342
|
+
return null;
|
|
343
|
+
effectiveType( base ); // induces localized data and target entity creation
|
|
344
|
+
chain.reverse();
|
|
345
|
+
for (const art of chain) {
|
|
346
|
+
if (art.kind !== 'entity')
|
|
347
|
+
return null;
|
|
348
|
+
effectiveType( art ); // induces localized data and target entity creation
|
|
349
|
+
}
|
|
350
|
+
return gap.kind === 'entity' ? gap : null;
|
|
351
|
+
}
|
|
352
|
+
|
|
295
353
|
// TODO: test it in combination with top-level CAST function
|
|
296
354
|
// TODO: we could probably "extend" this function to all other cases where we
|
|
297
355
|
// set an _origin in Universal CSN
|
|
@@ -497,7 +555,6 @@ function populate( model ) {
|
|
|
497
555
|
|
|
498
556
|
// TODO: delete XSN._entities
|
|
499
557
|
// TODO: delete ENTITY._from - use _origin? instead _from[0]
|
|
500
|
-
// TODO (after on-demand ext): delete XSN.$entity
|
|
501
558
|
|
|
502
559
|
/**
|
|
503
560
|
* Merge _specified_ elements with _inferred_ elements in the given view/element,
|
|
@@ -1048,7 +1105,8 @@ function populate( model ) {
|
|
|
1048
1105
|
|
|
1049
1106
|
if (!exposed.length) {
|
|
1050
1107
|
const origTarget = target;
|
|
1051
|
-
|
|
1108
|
+
// TODO: probably calculate effectiveType of target earlier
|
|
1109
|
+
if (effectiveType( target ) && isAutoExposed( target ))
|
|
1052
1110
|
target = createAutoExposed( origTarget, service, elemScope );
|
|
1053
1111
|
const desc = origTarget._descendants ||
|
|
1054
1112
|
setLink( origTarget, '_descendants', Object.create( null ) );
|
|
@@ -1217,6 +1275,8 @@ function populate( model ) {
|
|
|
1217
1275
|
function isAutoExposed( target ) {
|
|
1218
1276
|
if (target.$autoexpose !== undefined)
|
|
1219
1277
|
return target.$autoexpose;
|
|
1278
|
+
// Remark: this can probably be simplified now that we have called
|
|
1279
|
+
// effectiveType() on the original target
|
|
1220
1280
|
const origTarget = target;
|
|
1221
1281
|
const chain = [];
|
|
1222
1282
|
const alias1 = target._from?.[0]; // TODO: delete ENTITY._from ?
|
|
@@ -1314,7 +1374,7 @@ function populate( model ) {
|
|
|
1314
1374
|
// console.log(absolute)
|
|
1315
1375
|
const location = weakRefLocation( target.name );
|
|
1316
1376
|
const from = { path: [ { id: target.name.id, location } ], location };
|
|
1317
|
-
|
|
1377
|
+
const art = registerGeneratedEntity( {
|
|
1318
1378
|
kind: 'entity',
|
|
1319
1379
|
name: { location, id: absolute },
|
|
1320
1380
|
location,
|
|
@@ -1325,7 +1385,7 @@ function populate( model ) {
|
|
|
1325
1385
|
name: { path: [ { id: 'cds.autoexposed', location } ], location },
|
|
1326
1386
|
$inferred: '$generated',
|
|
1327
1387
|
},
|
|
1328
|
-
};
|
|
1388
|
+
} );
|
|
1329
1389
|
// forward target parameters to projection
|
|
1330
1390
|
if (target.params) {
|
|
1331
1391
|
art.params = Object.create( null );
|
|
@@ -1342,19 +1402,26 @@ function populate( model ) {
|
|
|
1342
1402
|
} );
|
|
1343
1403
|
}
|
|
1344
1404
|
// TODO: do we need to tag the generated entity with elemScope = 'auto'?
|
|
1345
|
-
if (autoexposed) {
|
|
1346
|
-
Object.assign( autoexposed, art );
|
|
1347
|
-
art = autoexposed;
|
|
1348
|
-
}
|
|
1349
|
-
else {
|
|
1350
|
-
model.definitions[absolute] = art;
|
|
1351
|
-
}
|
|
1352
1405
|
setLink( art, '_service', service );
|
|
1353
1406
|
setLink( art, '_block', model.$internal );
|
|
1354
1407
|
initMainArtifact( art, !!autoexposed );
|
|
1355
1408
|
effectiveType( art );
|
|
1356
1409
|
// TODO: try to set locations of elements locations of orig target elements
|
|
1357
|
-
|
|
1410
|
+
newGeneratedEntities.push( art );
|
|
1411
|
+
return art;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
function registerGeneratedEntity( art ) {
|
|
1415
|
+
const { id } = art.name;
|
|
1416
|
+
const gap = model.definitions[id];
|
|
1417
|
+
if (gap)
|
|
1418
|
+
art = Object.assign( gap, art );
|
|
1419
|
+
else
|
|
1420
|
+
model.definitions[id] = art;
|
|
1421
|
+
newGeneratedEntities.push( art );
|
|
1422
|
+
// add gen entity to _subArtifacts of parent (not done in createGapArtifact()):
|
|
1423
|
+
if (art.$inferred !== 'autoexposed')
|
|
1424
|
+
initArtifactParentLink( art, model.definitions );
|
|
1358
1425
|
return art;
|
|
1359
1426
|
}
|
|
1360
1427
|
}
|
|
@@ -8,17 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
|
-
const {
|
|
12
|
-
forEachDefinition,
|
|
13
|
-
forEachMember,
|
|
14
|
-
forEachGeneric,
|
|
15
|
-
} = require( '../base/model');
|
|
16
11
|
const {
|
|
17
12
|
setLink,
|
|
18
13
|
linkToOrigin,
|
|
19
14
|
withAssociation,
|
|
20
15
|
viewFromPrimary,
|
|
21
16
|
copyExpr,
|
|
17
|
+
forEachDefinition,
|
|
18
|
+
forEachMember,
|
|
19
|
+
forEachGeneric,
|
|
22
20
|
} = require('./utils');
|
|
23
21
|
const { propagationRules } = require('../base/builtins');
|
|
24
22
|
const $inferred = Symbol.for( 'cds.$inferred' );
|
package/lib/compiler/resolve.js
CHANGED
|
@@ -38,13 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
'use strict';
|
|
40
40
|
|
|
41
|
-
const {
|
|
42
|
-
forEachDefinition,
|
|
43
|
-
forEachMember,
|
|
44
|
-
forEachGeneric,
|
|
45
|
-
forEachInOrder,
|
|
46
|
-
isDeprecatedEnabled,
|
|
47
|
-
} = require('../base/model');
|
|
41
|
+
const { isDeprecatedEnabled } = require('../base/model');
|
|
48
42
|
const { dictAdd } = require('../base/dictionaries');
|
|
49
43
|
const { weakLocation } = require('../base/location');
|
|
50
44
|
const { combinedLocation } = require('../base/location');
|
|
@@ -65,6 +59,10 @@ const {
|
|
|
65
59
|
compositionTextVariant,
|
|
66
60
|
targetCantBeAspect,
|
|
67
61
|
userParam,
|
|
62
|
+
forEachDefinition,
|
|
63
|
+
forEachMember,
|
|
64
|
+
forEachGeneric,
|
|
65
|
+
forEachInOrder,
|
|
68
66
|
} = require('./utils');
|
|
69
67
|
|
|
70
68
|
const detectCycles = require('./cycle-detector');
|
|
@@ -109,7 +107,9 @@ function resolve( model ) {
|
|
|
109
107
|
= isDeprecatedEnabled( options, 'ignoreSpecifiedQueryElements' );
|
|
110
108
|
|
|
111
109
|
forEachGeneric( model, 'sources', resolveUsings );
|
|
112
|
-
|
|
110
|
+
doResolve();
|
|
111
|
+
model.$functions.checkGenerateConditions();
|
|
112
|
+
return model;
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
115
|
* Resolve the using declarations in `using`.
|
|
@@ -173,7 +173,6 @@ function resolve( model ) {
|
|
|
173
173
|
error( '$internal-expecting-cyclic', null, {},
|
|
174
174
|
'INTERNAL: the compiler should have issued an Error[ref-cyclic]' );
|
|
175
175
|
}
|
|
176
|
-
return model;
|
|
177
176
|
}
|
|
178
177
|
|
|
179
178
|
//--------------------------------------------------------------------------
|
|
@@ -447,6 +446,8 @@ function resolve( model ) {
|
|
|
447
446
|
if (getInheritedProp( art, 'targetAspect' )) {
|
|
448
447
|
error( 'def-invalid-key', [ key.location, art ], { '#': 'composition' } );
|
|
449
448
|
// TODO: test with managed composition exposed with explicit KEY
|
|
449
|
+
// TODO: this should be checked in generate.js (with setting), see
|
|
450
|
+
// test3/ManagedCompositions/CompositionAsKey/
|
|
450
451
|
}
|
|
451
452
|
else if (art.target && getInheritedProp( art, 'on' )) {
|
|
452
453
|
error( 'def-invalid-key', [ key.location, art ], { '#': 'unmanaged' } );
|
|
@@ -587,7 +588,7 @@ function resolve( model ) {
|
|
|
587
588
|
}
|
|
588
589
|
|
|
589
590
|
resolveExprInAnnotations( art );
|
|
590
|
-
forEachMember(
|
|
591
|
+
forEachMember( obj.targetAspect || obj, resolveRefs );
|
|
591
592
|
// After the resolving of foreign keys (and adding implicit ones):
|
|
592
593
|
if (obj.target?.$inferred === '')
|
|
593
594
|
checkRedirectedUserTarget( art );
|
|
@@ -916,7 +917,8 @@ function resolve( model ) {
|
|
|
916
917
|
error( 'ref-invalid-calc-elem', [ include.location || art.value.location, art ],
|
|
917
918
|
{ '#': art._main.kind } );
|
|
918
919
|
}
|
|
919
|
-
else {
|
|
920
|
+
else if (art._main.kind !== 'extend') {
|
|
921
|
+
// if there was an error while extending, the main kind can be still 'extend'
|
|
920
922
|
error( 'def-invalid-calc-elem', loc, { '#': art._main.kind } );
|
|
921
923
|
}
|
|
922
924
|
}
|
|
@@ -1072,8 +1074,9 @@ function resolve( model ) {
|
|
|
1072
1074
|
const target = resolvePath( obj.target, 'target', art );
|
|
1073
1075
|
|
|
1074
1076
|
if (obj._columnParent && obj.type && !obj.type.$inferred && art._main && art._main.query) {
|
|
1075
|
-
// New association inside expand/inline: The on-condition can't be properly
|
|
1076
|
-
// so abort early. See #8797
|
|
1077
|
+
// New association inside expand/inline: The on-condition can't be properly
|
|
1078
|
+
// checked, so abort early. See #8797. If we'd allow this in the future, we
|
|
1079
|
+
// must consider composition targets in expand/inline for auto-redirections
|
|
1077
1080
|
error( 'query-unexpected-assoc', [ obj.name.location, art ], {},
|
|
1078
1081
|
'Unexpected new association in expand/inline' );
|
|
1079
1082
|
return; // avoid subsequent errors
|
|
@@ -1199,6 +1202,7 @@ function resolve( model ) {
|
|
|
1199
1202
|
return max && (typeof max.val !== 'number' || max.val > 1);
|
|
1200
1203
|
}
|
|
1201
1204
|
|
|
1205
|
+
// TODO: move to populate.js and call there
|
|
1202
1206
|
function addImplicitForeignKeys( art, obj, target ) {
|
|
1203
1207
|
if (!art.$inferred && !art.virtual?.val && isQuasiVirtualAssociation( obj )) {
|
|
1204
1208
|
if (!isDeprecatedEnabled( options, 'noQuasiVirtualAssocs' )) {
|