@sap/cds-compiler 3.6.2 → 3.8.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 +109 -1
- package/README.md +3 -0
- package/bin/cdsc.js +12 -5
- package/doc/CHANGELOG_ARCHIVE.md +6 -6
- package/doc/CHANGELOG_BETA.md +35 -2
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/doc/DeprecatedOptions_v2.md +1 -1
- package/doc/NameResolution.md +1 -1
- package/lib/api/main.js +63 -23
- package/lib/api/options.js +1 -0
- package/lib/api/validate.js +5 -0
- package/lib/base/dictionaries.js +15 -3
- package/lib/base/keywords.js +2 -0
- package/lib/base/message-registry.js +120 -34
- package/lib/base/messages.js +51 -27
- package/lib/base/model.js +4 -2
- package/lib/base/shuffle.js +2 -1
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/defaultValues.js +1 -1
- package/lib/checks/elements.js +29 -1
- package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +10 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/onConditions.js +15 -9
- package/lib/checks/sql-snippets.js +2 -2
- package/lib/checks/types.js +5 -1
- package/lib/checks/validator.js +7 -3
- package/lib/compiler/assert-consistency.js +42 -26
- package/lib/compiler/base.js +50 -4
- package/lib/compiler/builtins.js +17 -8
- package/lib/compiler/checks.js +241 -246
- package/lib/compiler/define.js +113 -146
- package/lib/compiler/extend.js +889 -383
- package/lib/compiler/finalize-parse-cdl.js +5 -58
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/kick-start.js +7 -8
- package/lib/compiler/populate.js +297 -293
- package/lib/compiler/propagator.js +27 -18
- package/lib/compiler/resolve.js +146 -463
- package/lib/compiler/shared.js +36 -79
- package/lib/compiler/tweak-assocs.js +30 -28
- package/lib/compiler/utils.js +31 -5
- package/lib/edm/annotations/genericTranslation.js +131 -59
- package/lib/edm/annotations/preprocessAnnotations.js +3 -0
- package/lib/edm/csn2edm.js +22 -5
- package/lib/edm/edm.js +6 -4
- package/lib/edm/edmAnnoPreprocessor.js +1 -0
- package/lib/edm/edmPreprocessor.js +42 -26
- package/lib/gen/Dictionary.json +38 -2
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/languageLexer.js +1 -1
- package/lib/gen/languageParser.js +4828 -4472
- package/lib/inspect/inspectPropagation.js +20 -34
- package/lib/json/from-csn.js +140 -44
- package/lib/json/to-csn.js +114 -122
- package/lib/language/errorStrategy.js +2 -0
- package/lib/language/genericAntlrParser.js +156 -36
- package/lib/language/language.g4 +100 -58
- package/lib/language/textUtils.js +13 -0
- package/lib/main.d.ts +43 -3
- package/lib/main.js +4 -2
- package/lib/model/csnRefs.js +15 -3
- package/lib/model/csnUtils.js +12 -74
- package/lib/model/revealInternalProperties.js +4 -2
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +3 -0
- package/lib/render/manageConstraints.js +5 -2
- package/lib/render/toCdl.js +216 -104
- package/lib/render/toHdbcds.js +2 -9
- package/lib/render/toRename.js +14 -51
- package/lib/render/toSql.js +4 -3
- package/lib/render/utils/common.js +9 -5
- package/lib/transform/braceExpression.js +6 -0
- package/lib/transform/db/assertUnique.js +2 -1
- package/lib/transform/db/expansion.js +2 -0
- package/lib/transform/db/flattening.js +37 -36
- package/lib/transform/db/rewriteCalculatedElements.js +600 -0
- package/lib/transform/db/transformExists.js +4 -0
- package/lib/transform/db/views.js +40 -37
- package/lib/transform/forOdataNew.js +20 -15
- package/lib/transform/forRelationalDB.js +58 -41
- package/lib/transform/odata/typesExposure.js +50 -15
- package/lib/transform/parseExpr.js +16 -8
- package/lib/transform/transformUtilsNew.js +42 -14
- package/lib/transform/translateAssocsToJoins.js +60 -37
- package/lib/transform/universalCsn/coreComputed.js +15 -7
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +2 -1
package/lib/compiler/populate.js
CHANGED
|
@@ -24,15 +24,13 @@ const {
|
|
|
24
24
|
forEachGeneric,
|
|
25
25
|
} = require('../base/model');
|
|
26
26
|
const {
|
|
27
|
-
dictAdd, dictAddArray,
|
|
27
|
+
dictAdd, dictAddArray, dictFirst, dictForEach,
|
|
28
28
|
} = require('../base/dictionaries');
|
|
29
|
-
const { dictLocation } = require('../base/location');
|
|
30
29
|
const { weakLocation } = require('../base/messages');
|
|
31
30
|
const { CompilerAssertion } = require('../base/error');
|
|
32
31
|
|
|
33
32
|
const { kindProperties } = require('./base');
|
|
34
33
|
const {
|
|
35
|
-
pushLink,
|
|
36
34
|
setLink,
|
|
37
35
|
setArtifactLink,
|
|
38
36
|
annotationVal,
|
|
@@ -42,13 +40,14 @@ const {
|
|
|
42
40
|
splitIntoPath,
|
|
43
41
|
linkToOrigin,
|
|
44
42
|
setMemberParent,
|
|
43
|
+
proxyCopyMembers,
|
|
45
44
|
dependsOn,
|
|
46
|
-
traverseQueryPost,
|
|
47
45
|
setExpandStatus,
|
|
48
46
|
setExpandStatusAnnotate,
|
|
49
47
|
} = require('./utils');
|
|
50
48
|
|
|
51
49
|
const $inferred = Symbol.for('cds.$inferred');
|
|
50
|
+
const $location = Symbol.for('cds.$location');
|
|
52
51
|
|
|
53
52
|
|
|
54
53
|
// Export function of this file.
|
|
@@ -62,16 +61,18 @@ function populate( model ) {
|
|
|
62
61
|
resolvePath,
|
|
63
62
|
attachAndEmitValidNames,
|
|
64
63
|
initArtifact,
|
|
64
|
+
chooseAnnotationsInArtifact,
|
|
65
|
+
extendArtifactAfter,
|
|
65
66
|
} = model.$functions;
|
|
66
67
|
model.$volatileFunctions.environment = environment;
|
|
67
68
|
Object.assign( model.$functions, {
|
|
68
69
|
effectiveType,
|
|
69
|
-
|
|
70
|
+
getOrigin,
|
|
70
71
|
resolveType,
|
|
71
|
-
populateQuery,
|
|
72
72
|
} );
|
|
73
|
+
// let depth = 100;
|
|
73
74
|
|
|
74
|
-
|
|
75
|
+
let effectiveSeqNo = 0; // artifact number set after having set _effectiveType
|
|
75
76
|
/** @type {any} may also be a boolean */
|
|
76
77
|
let newAutoExposed = [];
|
|
77
78
|
|
|
@@ -96,146 +97,247 @@ function populate( model ) {
|
|
|
96
97
|
newAutoExposed = true; // internal error if auto-expose after here
|
|
97
98
|
return;
|
|
98
99
|
|
|
100
|
+
function traverseElementEnvironments( art ) {
|
|
101
|
+
navigationEnv( art );
|
|
102
|
+
if (art.$queries)
|
|
103
|
+
art.$queries.forEach( traverseElementEnvironments );
|
|
104
|
+
if (art.mixin)
|
|
105
|
+
dictForEach( art.mixin, navigationEnv );
|
|
106
|
+
if (art !== art._main?._leadingQuery) // already done
|
|
107
|
+
forEachMember( art, traverseElementEnvironments );
|
|
108
|
+
}
|
|
109
|
+
|
|
99
110
|
|
|
100
111
|
//--------------------------------------------------------------------------
|
|
101
112
|
// The central functions for path resolution - must work on-demand
|
|
102
113
|
//--------------------------------------------------------------------------
|
|
103
|
-
// Phase 2: call
|
|
104
|
-
|
|
105
|
-
function traverseElementEnvironments( art ) {
|
|
106
|
-
populateView( art );
|
|
107
|
-
environment( art );
|
|
108
|
-
if (art.elements$ || art.enum$)
|
|
109
|
-
mergeSpecifiedElementsOrEnum(art);
|
|
110
|
-
forEachMember( art, traverseElementEnvironments );
|
|
111
|
-
}
|
|
114
|
+
// Phase 2: call effectiveType() on-demand, which also calculates view elems
|
|
112
115
|
|
|
113
116
|
// Return effective search environment provided by artifact `art`, i.e. the
|
|
114
117
|
// `artifacts` or `elements` dictionary. For the latter, follow the `type`
|
|
115
118
|
// chain and resolve the association `target`. View elements are calculated
|
|
116
119
|
// on demand.
|
|
117
120
|
function environment( art, location, user, assocSpec ) {
|
|
118
|
-
if (!art)
|
|
119
|
-
return Object.create(null);
|
|
120
121
|
const env = navigationEnv( art, location, user, assocSpec );
|
|
122
|
+
if (env === 0)
|
|
123
|
+
return 0;
|
|
121
124
|
return env && env.elements || Object.create(null);
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
function navigationEnv( art, location, user, assocSpec ) {
|
|
125
|
-
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
128
|
+
// = effectiveType() on from-path, TODO: should actually already part of
|
|
129
|
+
// resolvePath() on FROM
|
|
130
|
+
if (!art)
|
|
131
|
+
return undefined;
|
|
132
|
+
let type = effectiveType( art );
|
|
133
|
+
while (type?.items) // TODO: disallow navigation to many sometimes
|
|
134
|
+
type = effectiveType( type.items );
|
|
135
|
+
if (!type?.target)
|
|
136
|
+
return type;
|
|
137
|
+
|
|
138
|
+
if (assocSpec === false) {
|
|
135
139
|
// TODO: combine this with setTargetReferenceKey&Co in getPathItem?
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
140
|
+
error( null, [ location, user ], {},
|
|
141
|
+
'Following an association is not allowed in an association key definition' );
|
|
142
|
+
} // TODO: else warning for assoc usage with falsy assocSpec
|
|
143
|
+
const target = resolvePath( type.target, 'target', type );
|
|
144
|
+
if (!target)
|
|
145
|
+
return target;
|
|
146
|
+
if (target && assocSpec && user)
|
|
147
|
+
dependsOn( user, target, location || user.location );
|
|
148
|
+
const effectiveTarget = effectiveType( target );
|
|
149
|
+
if (effectiveTarget === 0 && location)
|
|
150
|
+
dependsOn( user, user, (user.target || user.type || user.value || user).location );
|
|
151
|
+
// console.log('NT:',assocSpec,!!user,target)
|
|
152
|
+
return effectiveTarget;
|
|
146
153
|
}
|
|
147
154
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
/**
|
|
156
|
+
* Return the artifact having properties which are relevant for further name
|
|
157
|
+
* resolution on `art`: `target`, `elements`, `items`, also `enum`. Make sure
|
|
158
|
+
* that these properties actually exist, are complete and auto-corrected. Cache
|
|
159
|
+
* the result in property `_effectiveType`.
|
|
160
|
+
*
|
|
161
|
+
* - actions, functions: returns `art`, might have expanded `params`/`returns`
|
|
162
|
+
* - artifacts with direct or inherited `target`, `elements`, `items`, `enum`:
|
|
163
|
+
* returns `art`, these properties might have been auto-redirected / expanded
|
|
164
|
+
* - artifacts with direct or inherited scalar type: the built-in type
|
|
165
|
+
* - other artifacts: the last artifact in the origin-chain, i.e. the one which
|
|
166
|
+
* has neither a type nor some value path.
|
|
167
|
+
* - returns 0 with cyclic dependencies (with recursive element expansions, we
|
|
168
|
+
* have `elements: 0` instead).
|
|
169
|
+
* - returns null if a relevant reference points to nothing or is corrupted
|
|
170
|
+
* - returns false if a relevant reference points to a duplicate definition
|
|
171
|
+
*
|
|
172
|
+
* This function also infers type relevant properties:
|
|
173
|
+
*
|
|
174
|
+
* - views and queries: returns `art` with inferred query `elements`
|
|
175
|
+
* - column with `expand`: returns `art`, usually with inferred `elements`/`items`
|
|
176
|
+
* - more to come
|
|
177
|
+
*
|
|
178
|
+
* At the moment, it is assumed that includes, expansions, and localized has
|
|
179
|
+
* been applied earlier.
|
|
180
|
+
*
|
|
181
|
+
* Properties which are (usually) not relevant for the name resolution, like
|
|
182
|
+
* `length` and `cardinality`, cannot be simply accessed on the effective
|
|
183
|
+
* artifact. The effective artifact alone is not enough to check whether an
|
|
184
|
+
* artifact is an association or composition; it also does not give you the
|
|
185
|
+
* information about the technical base type of an enum.
|
|
186
|
+
*
|
|
187
|
+
* Calculating an effective association/composition does not imply calculating
|
|
188
|
+
* its effective target. Calculating an effective structure (entities, …)
|
|
189
|
+
* does not imply calculating the effective types of its elements. Calculating
|
|
190
|
+
* an effective array does not imply calculating its effective line type.
|
|
191
|
+
*/
|
|
159
192
|
function effectiveType( art ) {
|
|
160
|
-
if (
|
|
193
|
+
if (!art)
|
|
194
|
+
return art;
|
|
195
|
+
// if (--depth) throw Error(`ET: ${ Object.keys(art) }`)
|
|
196
|
+
if (art._effectiveType !== undefined)
|
|
161
197
|
return art._effectiveType;
|
|
162
198
|
|
|
163
199
|
// console.log(message( null, art.location, art, {}, 'Info','FT').toString())
|
|
164
200
|
const chain = [];
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
!art.target && !art.enum && !art.elements && !art.items) {
|
|
168
|
-
chain.push( art );
|
|
201
|
+
// console.log( 'ET-START:', art.kind, art.name )
|
|
202
|
+
while (art && art._effectiveType === undefined) {
|
|
169
203
|
setLink( art, '_effectiveType', 0 ); // initial setting in case of cycles
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if ('_effectiveType' in art) { // is the case for builtins
|
|
174
|
-
art = art._effectiveType;
|
|
175
|
-
}
|
|
176
|
-
else {
|
|
177
|
-
setLink( art, '_effectiveType', art );
|
|
178
|
-
if (art.expand && !art.value && !art.elements)
|
|
179
|
-
initFromColumns( art, art.expand );
|
|
180
|
-
// When not blocked (by future origin = false) and not REDIRECTED TO and not MIXIN
|
|
181
|
-
// try to implicitly redirect explicitly provided target:
|
|
182
|
-
else if (art.target && art._origin == null && !art.value && art.kind !== 'mixin')
|
|
183
|
-
redirectImplicitly( art, art );
|
|
184
|
-
}
|
|
204
|
+
chain.push( art );
|
|
205
|
+
art = getOrigin( art );
|
|
206
|
+
// console.log( 'ET-GO:', art?.name )
|
|
185
207
|
}
|
|
208
|
+
if (art)
|
|
209
|
+
art = art._effectiveType;
|
|
210
|
+
if (art === 0)
|
|
211
|
+
return art;
|
|
212
|
+
|
|
186
213
|
chain.reverse();
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
214
|
+
for (const a of chain) {
|
|
215
|
+
// Without type, value.path or _origin at beginning, link to itself:
|
|
216
|
+
chooseAnnotationsInArtifact( a );
|
|
217
|
+
art = populateArtifact( a, art ) || a;
|
|
218
|
+
if (a.elements$ || a.enum$)
|
|
219
|
+
mergeSpecifiedElementsOrEnum( a );
|
|
220
|
+
setLink( a, '_effectiveType', art );
|
|
221
|
+
a.$effectiveSeqNo = ++effectiveSeqNo;
|
|
222
|
+
// console.log( 'ET-DO:', effectiveSeqNo, a?.kind, a?.name, a._extensions?.elements?.length )
|
|
223
|
+
extendArtifactAfter( a ); // after setting _effectiveType (for messages)
|
|
190
224
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
225
|
+
// console.log( 'ET-END:', art?.kind, art?.name )
|
|
226
|
+
return art;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function populateArtifact( art, origEffective ) {
|
|
230
|
+
// Name-resolution relevant properties directly at artifact:
|
|
231
|
+
// ‹view›.elements of input must have been moved (to elements$) before!
|
|
232
|
+
// console.log('Q:',art.elements,art.enum,art.items,!!art.query)
|
|
233
|
+
if (art.includes) // first version of includes via effectiveTpe()
|
|
234
|
+
art.includes.forEach( i => effectiveType( i._artifact ) );
|
|
235
|
+
if (art.elements != null || art.enum != null || art.items != null)
|
|
236
|
+
return art;
|
|
237
|
+
if (art.target) {
|
|
238
|
+
// try to implicitly redirect explicitly provided target:
|
|
239
|
+
if (!origEffective?.target && art.kind !== 'mixin')
|
|
240
|
+
redirectImplicitly( art, art );
|
|
241
|
+
if (!art.expand)
|
|
242
|
+
return art;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// With properties to be calculated: ----------------------------------------
|
|
246
|
+
if (art.query && art.kind !== '$tableAlias') { // query entity
|
|
247
|
+
const leading = art.$queries[0];
|
|
248
|
+
if (!leading) // parse error
|
|
249
|
+
return null;
|
|
250
|
+
// assert that there is no _effectiveType on leading (should be the case, as
|
|
251
|
+
// you cannot refer to a query of another artifact)
|
|
252
|
+
populateQuery( leading );
|
|
253
|
+
setLink( leading, '_effectiveType', leading );
|
|
254
|
+
leading.$effectiveSeqNo = ++effectiveSeqNo;
|
|
255
|
+
return art;
|
|
256
|
+
}
|
|
257
|
+
if (art.from)
|
|
258
|
+
return populateQuery( art );
|
|
259
|
+
|
|
260
|
+
if (art.expand) {
|
|
261
|
+
// TODO: test that there is no CDL-style cast with expand
|
|
262
|
+
// (we could allow that later: then some basic structural check is needed)
|
|
263
|
+
if (!art.value) {
|
|
264
|
+
initFromColumns( art, art.expand );
|
|
265
|
+
if (origEffective?.target) // consider `{ … } as x: AssocType
|
|
266
|
+
redirectImplicitly( art, origEffective );
|
|
267
|
+
}
|
|
268
|
+
else if (art.value.path) {
|
|
269
|
+
expandFromColumns( art );
|
|
205
270
|
}
|
|
271
|
+
// TODO: if we allow CDL-style cast with expand in the future, we need to
|
|
272
|
+
// redirectImplicitly when casting to assoc type
|
|
273
|
+
return art;
|
|
206
274
|
}
|
|
207
|
-
|
|
275
|
+
if (!origEffective || origEffective.builtin) // TODO: builtin test needed?
|
|
276
|
+
return origEffective;
|
|
277
|
+
|
|
278
|
+
// With inherited auto-corrected name-resolution-relevant properties: -------
|
|
279
|
+
if (origEffective.target)
|
|
280
|
+
return redirectImplicitly( art, origEffective ) ? art : origEffective;
|
|
281
|
+
if (origEffective.elements)
|
|
282
|
+
return expandElements( art, origEffective ) ? art : origEffective;
|
|
283
|
+
if (origEffective.enum)
|
|
284
|
+
return expandEnum( art, origEffective ) ? art : origEffective;
|
|
285
|
+
if (origEffective.items)
|
|
286
|
+
return expandItems( art, origEffective ) ? art : origEffective;
|
|
287
|
+
if (origEffective.params || origEffective.returns)
|
|
288
|
+
return expandParams( art, origEffective );
|
|
289
|
+
return origEffective;
|
|
208
290
|
}
|
|
209
291
|
|
|
210
292
|
// TODO: test it in combination with top-level CAST function
|
|
211
|
-
function
|
|
293
|
+
// TODO: we could probably "extend" this function to all other cases where we
|
|
294
|
+
// set an _origin in Universal CSN
|
|
295
|
+
|
|
296
|
+
// TODO: add 2nd arg `considerSecondary` used in effectiveType(): prefers a
|
|
297
|
+
// predecessor without _effectiveType (includes, joins)
|
|
298
|
+
function getOrigin( art ) {
|
|
212
299
|
// Be careful when using it with art.target or art.enum or art.elements
|
|
213
|
-
if (art
|
|
300
|
+
if (!art)
|
|
301
|
+
return undefined; // TODO: null?
|
|
302
|
+
// if (--depth) throw Error(`GOR: ${ Object.keys(art) }`)
|
|
303
|
+
if (art._origin !== undefined)
|
|
214
304
|
return art._origin;
|
|
215
|
-
if (art.type)
|
|
305
|
+
if (art.type) // not stored in _origin
|
|
216
306
|
return resolveType( art.type, art );
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return art._origin;
|
|
307
|
+
return setLink( art, '_origin', getOriginRaw( art ) );
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function getOriginRaw( art ) {
|
|
311
|
+
if (!art._main) {
|
|
312
|
+
if (art.query)
|
|
313
|
+
return getOrigin( art.$queries?.[0] );
|
|
225
314
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
315
|
+
else {
|
|
316
|
+
if (art.value?.path)
|
|
317
|
+
return resolvePath( art.value, 'expr', art, null );
|
|
318
|
+
if (art.kind === 'select')
|
|
319
|
+
return getOrigin( dictFirst( art.$tableAliases ) );
|
|
320
|
+
// init sets _origin for alias to sub query, only need to handle ref here:
|
|
321
|
+
if (art.kind === '$tableAlias') {
|
|
322
|
+
// do not use navigationEnv(): it would always call effectiveType() on the
|
|
323
|
+
// source → we would would have a deeper callstack
|
|
324
|
+
const source = resolvePath( art, 'from', art._parent );
|
|
325
|
+
if (!source?._main)
|
|
326
|
+
return source; // direct entity (or undefined)
|
|
327
|
+
// Before having done the resolvePath cleanup, do not rely on resolvePath
|
|
328
|
+
// to call effectiveType() on the last assoc of a from ref:
|
|
329
|
+
const assoc = effectiveType( source );
|
|
330
|
+
return resolvePath( assoc?.target, 'target', assoc );
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return '';
|
|
234
334
|
}
|
|
235
335
|
|
|
236
336
|
function resolveType( ref, user ) {
|
|
237
|
-
if (
|
|
337
|
+
if (ref._artifact !== undefined)
|
|
238
338
|
return ref._artifact;
|
|
339
|
+
while (user._outer) // in items
|
|
340
|
+
user = user._outer;
|
|
239
341
|
if (ref.scope === 'typeOf') {
|
|
240
342
|
let struct = user;
|
|
241
343
|
while (struct.kind === 'element')
|
|
@@ -255,8 +357,6 @@ function populate( model ) {
|
|
|
255
357
|
}
|
|
256
358
|
return resolvePath( ref, 'typeOf', user );
|
|
257
359
|
}
|
|
258
|
-
while (user._outer) // in items
|
|
259
|
-
user = user._outer;
|
|
260
360
|
if (user.kind === 'event')
|
|
261
361
|
return resolvePath( ref, 'eventType', user );
|
|
262
362
|
if (user.kind === 'param' && user._parent &&
|
|
@@ -265,18 +365,18 @@ function populate( model ) {
|
|
|
265
365
|
return resolvePath( ref, 'type', user );
|
|
266
366
|
}
|
|
267
367
|
|
|
268
|
-
function getCardinality(
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (
|
|
272
|
-
return
|
|
273
|
-
|
|
368
|
+
function getCardinality( assoc ) {
|
|
369
|
+
while (assoc?._effectiveType) {
|
|
370
|
+
// if (--depth) throw Error(`GCARD: ${ Object.keys(art) }`)
|
|
371
|
+
if (assoc.cardinality)
|
|
372
|
+
return assoc.cardinality;
|
|
373
|
+
assoc = getOrigin( assoc );
|
|
274
374
|
}
|
|
275
375
|
return {};
|
|
276
376
|
}
|
|
277
377
|
|
|
278
378
|
function userQuery( user ) {
|
|
279
|
-
// TODO: we
|
|
379
|
+
// TODO: should we set _query links in define.js?
|
|
280
380
|
while (user._main) {
|
|
281
381
|
if (user.kind === 'select' || user.kind === '$join')
|
|
282
382
|
return user;
|
|
@@ -285,7 +385,7 @@ function populate( model ) {
|
|
|
285
385
|
return null;
|
|
286
386
|
}
|
|
287
387
|
|
|
288
|
-
//
|
|
388
|
+
// Expansions --------------------------------------------------------------
|
|
289
389
|
|
|
290
390
|
|
|
291
391
|
function expandItems( art, origin ) {
|
|
@@ -307,6 +407,10 @@ function populate( model ) {
|
|
|
307
407
|
}
|
|
308
408
|
|
|
309
409
|
function expandElements( art, struct ) {
|
|
410
|
+
if (art.kind === '$tableAlias') {
|
|
411
|
+
proxyCopyMembers( art, 'elements', struct.elements, art.path?.location, '$navElement' );
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
310
414
|
if (art.elements || art.kind === '$tableAlias' || art.kind === '$inline' ||
|
|
311
415
|
// no element expansions for "non-proper" types like
|
|
312
416
|
// entities (as parameter types) etc:
|
|
@@ -321,15 +425,7 @@ function populate( model ) {
|
|
|
321
425
|
const location = ref && ref.location || art.location;
|
|
322
426
|
// console.log( message( null, location, art, {target:struct,art}, 'Info','EXPAND-ELEM')
|
|
323
427
|
// .toString(), Object.keys(struct.elements))
|
|
324
|
-
|
|
325
|
-
const orig = struct.elements[name];
|
|
326
|
-
// const orig = elem.kind === '$navElement'
|
|
327
|
-
if (Array.isArray( orig )) // redefinitions
|
|
328
|
-
continue;
|
|
329
|
-
linkToOrigin( orig, name, art, 'elements', weakLocation( location ), true )
|
|
330
|
-
// or should we use orig.location? - TODO: try to find test to see message
|
|
331
|
-
.$inferred = 'expanded';
|
|
332
|
-
}
|
|
428
|
+
proxyCopyMembers( art, 'elements', struct.elements, weakLocation( location ) );
|
|
333
429
|
// Set elements expansion status (the if condition is always true, as no
|
|
334
430
|
// elements expansion will take place on artifact with existing other
|
|
335
431
|
// member property):
|
|
@@ -344,13 +440,7 @@ function populate( model ) {
|
|
|
344
440
|
return false;
|
|
345
441
|
const ref = art.type || art.value || art.name;
|
|
346
442
|
const location = weakLocation( ref && ref.location || art.location );
|
|
347
|
-
art.enum
|
|
348
|
-
for (const name in origin.enum) {
|
|
349
|
-
const orig = origin.enum[name];
|
|
350
|
-
linkToOrigin( orig, name, art, 'enum', location, true )
|
|
351
|
-
// or should we use orig.location? - TODO: try to find test to see message
|
|
352
|
-
.$inferred = 'expanded';
|
|
353
|
-
}
|
|
443
|
+
proxyCopyMembers( art, 'enum', origin.enum, weakLocation( location ) );
|
|
354
444
|
// Set elements expansion status (the if condition is always true, as no
|
|
355
445
|
// elements expansion will take place on artifact with existing other
|
|
356
446
|
// member property):
|
|
@@ -360,6 +450,30 @@ function populate( model ) {
|
|
|
360
450
|
return true;
|
|
361
451
|
}
|
|
362
452
|
|
|
453
|
+
function expandParams( art, origin ) {
|
|
454
|
+
if (!origin._main)
|
|
455
|
+
return origin; // not with entity (should not happen)
|
|
456
|
+
if (origin.params)
|
|
457
|
+
proxyCopyMembers( art, 'params', origin.params, null );
|
|
458
|
+
|
|
459
|
+
if (origin.returns) {
|
|
460
|
+
// TODO: make linkToOrigin() work for returns, kind/name?
|
|
461
|
+
const location = weakLocation( origin.returns.location );
|
|
462
|
+
art.returns = {
|
|
463
|
+
name: Object.assign( {}, art.name, { id: '', param: '', location } ),
|
|
464
|
+
kind: 'param',
|
|
465
|
+
location,
|
|
466
|
+
$inferred: 'expanded',
|
|
467
|
+
};
|
|
468
|
+
setLink( art.returns, '_parent', art );
|
|
469
|
+
setLink( art.returns, '_main', art._main || art );
|
|
470
|
+
setLink( art.returns, '_origin', origin.returns );
|
|
471
|
+
}
|
|
472
|
+
if (!art.$expand)
|
|
473
|
+
art.$expand = 'origin'; // if value stays, elements won't appear in CSN
|
|
474
|
+
return art;
|
|
475
|
+
}
|
|
476
|
+
|
|
363
477
|
/**
|
|
364
478
|
* Return true iff `art` is from a recursive expansion, i.e. if any of its
|
|
365
479
|
* expanded parents (including _outer) has the same non-expansion-origin.
|
|
@@ -403,48 +517,9 @@ function populate( model ) {
|
|
|
403
517
|
// Views
|
|
404
518
|
//--------------------------------------------------------------------------
|
|
405
519
|
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
function populateView( art ) {
|
|
410
|
-
if (!art._from || art._status === '_query')
|
|
411
|
-
return;
|
|
412
|
-
const resolveChain = [];
|
|
413
|
-
const fromChain = [ art ];
|
|
414
|
-
while (fromChain.length) {
|
|
415
|
-
const view = fromChain.pop();
|
|
416
|
-
if (view._status === '_query') // already fully resolved (status at def)
|
|
417
|
-
continue;
|
|
418
|
-
resolveChain.push( view );
|
|
419
|
-
for (const from of view._from) {
|
|
420
|
-
if (from._status) // status at the ref -> illegal recursion -> stop
|
|
421
|
-
continue;
|
|
422
|
-
setLink( from, '_status', '_query' );
|
|
423
|
-
// setLink before resolvePath - Cycle: view V as select from V.toV
|
|
424
|
-
let source = resolvePath( from, 'from', view ); // filter and args in resolveQuery
|
|
425
|
-
// console.log('ST:',msgName(source),from._status)
|
|
426
|
-
if (source && source._main) { // element -> should be assoc
|
|
427
|
-
const type = effectiveType( source );
|
|
428
|
-
source = type && type.target;
|
|
429
|
-
}
|
|
430
|
-
if (source && source._from && source._status !== '_query')
|
|
431
|
-
fromChain.push( source );
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
// console.log( resolveChain.map( v => msgName(v)+v._status ) );
|
|
435
|
-
resolveChain.reverse();
|
|
436
|
-
for (const view of resolveChain) {
|
|
437
|
-
if (view._status !== '_query' ) { // not already resolved
|
|
438
|
-
setLink( view, '_status', '_query' );
|
|
439
|
-
// must be run in order “sub query in FROM first”:
|
|
440
|
-
traverseQueryPost( view.query, null, populateQuery );
|
|
441
|
-
if (!view.$entity) {
|
|
442
|
-
model._entities.push( view );
|
|
443
|
-
view.$entity = ++model.$entity;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
520
|
+
// TODO: delete XSN._entities
|
|
521
|
+
// TODO: delete ENTITY._from
|
|
522
|
+
// TODO (after on-demand ext): delete XSN.$entity
|
|
448
523
|
|
|
449
524
|
/**
|
|
450
525
|
* Merge _specified_ elements with _inferred_ elements in the given view/element,
|
|
@@ -499,6 +574,7 @@ function populate( model ) {
|
|
|
499
574
|
for (const id in art.elements$) {
|
|
500
575
|
const selem = art.elements$[id]; // specified element
|
|
501
576
|
if (!selem.$replacement) {
|
|
577
|
+
// console.log( 'QED:', art.name, art.kind, art.elements )
|
|
502
578
|
error( 'query-unspecified-element', [ selem.name.location, selem ], { id },
|
|
503
579
|
'Element $(ID) does not result from the query' );
|
|
504
580
|
}
|
|
@@ -508,7 +584,7 @@ function populate( model ) {
|
|
|
508
584
|
function populateQuery( query ) {
|
|
509
585
|
if (query._combined || !query.from || !query.$tableAliases)
|
|
510
586
|
// already done or $join query or parse error
|
|
511
|
-
return;
|
|
587
|
+
return query;
|
|
512
588
|
setLink( query, '_combined', Object.create(null) );
|
|
513
589
|
query.$inlines = [];
|
|
514
590
|
forEachGeneric( query, '$tableAliases', resolveTabRef );
|
|
@@ -518,35 +594,18 @@ function populate( model ) {
|
|
|
518
594
|
for (const name in query.excludingDict)
|
|
519
595
|
resolveExcluding( name, query._combined, query.excludingDict, query );
|
|
520
596
|
}
|
|
521
|
-
return;
|
|
597
|
+
return query;
|
|
522
598
|
|
|
523
599
|
function resolveTabRef( alias ) {
|
|
600
|
+
// effectiveType() must not be called on $self, is unnecessary for mixins:
|
|
601
|
+
// TODO: have a test for `select from E { a, $self.a as b, $self.{ b as c } }`
|
|
602
|
+
// TODO: have a negative test for `select from E { $self.*, assoc.* }`
|
|
603
|
+
// (we might have those already)
|
|
524
604
|
if (alias.kind === 'mixin' || alias.kind === '$self')
|
|
525
605
|
return;
|
|
526
|
-
if (!alias.elements)
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
// // if (tab) setLink( alias, '_effectiveType', alias );
|
|
530
|
-
// const elements = alias.query ? alias.query.elements : environment( tab );
|
|
531
|
-
if (!('_origin' in alias) && alias.path) {
|
|
532
|
-
const tab = resolvePath( alias, 'from', query );
|
|
533
|
-
setLink( alias, '_origin', tab && navigationEnv( tab ) );
|
|
534
|
-
}
|
|
535
|
-
alias.elements = Object.create(null); // Set explicitly, as...
|
|
536
|
-
// ...with circular dep to source, no elements can be found.
|
|
537
|
-
const location = alias.path && alias.path.location;
|
|
538
|
-
const qtab = alias._origin;
|
|
539
|
-
if (!qtab || !qtab.elements)
|
|
540
|
-
return;
|
|
541
|
-
forEachGeneric( qtab, 'elements', ( origin, name ) => {
|
|
542
|
-
const elem = linkToOrigin( origin, name, alias, 'elements',
|
|
543
|
-
location || origin.name.location );
|
|
544
|
-
elem.kind = '$navElement';
|
|
545
|
-
// elem.name.select = query.name.select;
|
|
546
|
-
if (origin.masked)
|
|
547
|
-
elem.masked = Object.assign( { $inferred: 'nav' }, origin.masked );
|
|
548
|
-
});
|
|
549
|
-
}
|
|
606
|
+
if (!alias.elements) // could be false in hierarchical JOIN - TODO: necessary?
|
|
607
|
+
effectiveType( alias ); // element → $navElement expansion for $tableAlias
|
|
608
|
+
|
|
550
609
|
forEachGeneric( { elements: alias.elements }, 'elements', ( elem, name ) => {
|
|
551
610
|
if (elem.$duplicates !== true)
|
|
552
611
|
dictAddArray( query._combined, name, elem, null ); // not dictAdd()
|
|
@@ -568,16 +627,20 @@ function populate( model ) {
|
|
|
568
627
|
|
|
569
628
|
// query columns -----------------------------------------------------------
|
|
570
629
|
|
|
571
|
-
function expandFromColumns( elem
|
|
572
|
-
const path = elem.value
|
|
630
|
+
function expandFromColumns( elem ) {
|
|
631
|
+
const path = elem.value?.path;
|
|
573
632
|
if (!path || path.broken)
|
|
574
633
|
return null;
|
|
575
|
-
|
|
634
|
+
// If we allow CDL-style casts of `expand`s to associations in the future, we
|
|
635
|
+
// need to ignore an explicit type, i.e. not getOrigin():
|
|
636
|
+
const assoc = resolvePath( elem.value, 'expr', elem, null );
|
|
637
|
+
if (!effectiveType( assoc )?.target)
|
|
576
638
|
return initFromColumns( elem, elem.expand );
|
|
577
|
-
const { targetMax } = path[path.length - 1].cardinality ||
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
elem.items
|
|
639
|
+
const { targetMax } = path[path.length - 1].cardinality || getCardinality( assoc );
|
|
640
|
+
if (targetMax && (targetMax.val === '*' || targetMax.val > 1)) {
|
|
641
|
+
elem.items = { location: elem.expand[$location] };
|
|
642
|
+
setLink( elem.items, '_outer', elem );
|
|
643
|
+
}
|
|
581
644
|
return initFromColumns( elem, elem.expand );
|
|
582
645
|
}
|
|
583
646
|
|
|
@@ -623,7 +686,7 @@ function populate( model ) {
|
|
|
623
686
|
setMemberParent( col, id, query );
|
|
624
687
|
}
|
|
625
688
|
}
|
|
626
|
-
forEachGeneric( query, 'elements',
|
|
689
|
+
forEachGeneric( query, 'elements', initElem );
|
|
627
690
|
return true;
|
|
628
691
|
}
|
|
629
692
|
|
|
@@ -655,45 +718,20 @@ function populate( model ) {
|
|
|
655
718
|
return '';
|
|
656
719
|
}
|
|
657
720
|
|
|
658
|
-
function initElem( elem
|
|
721
|
+
function initElem( elem ) {
|
|
722
|
+
// TODO: we could share code with initMembers/init() in define.js
|
|
659
723
|
if (elem.type && !elem.type.$inferred)
|
|
660
|
-
return; // explicit type -> enough or
|
|
724
|
+
return; // explicit type -> enough or getOrigin()
|
|
661
725
|
if (elem.$inferred) {
|
|
662
726
|
// redirectImplicitly( elem, elem._origin );
|
|
663
727
|
return;
|
|
664
728
|
}
|
|
665
|
-
if (!elem.
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
const env = columnEnv( elem._pathHead, query );
|
|
670
|
-
const origin = setLink( elem, '_origin',
|
|
671
|
-
resolvePath( elem.value, 'expr', elem, env ) );
|
|
672
|
-
// console.log( message( null, elem.location, elem, {art:query}, 'Info','RED').toString(),
|
|
673
|
-
// elem.value)
|
|
674
|
-
// TODO: make this resolvePath() also part of directType() ?!
|
|
675
|
-
if (!origin || elem.expand)
|
|
676
|
-
return;
|
|
677
|
-
// TODO: or should we push elems with `expand` sibling to extra list for better messages?
|
|
678
|
-
if (elem.foreignKeys) { // REDIRECTED with explicit foreign keys
|
|
679
|
-
forEachGeneric( elem, 'foreignKeys', (key, name) => initKey( key, name, elem ) );
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
// now set things which are necessary for later sub phases:
|
|
683
|
-
const nav = pathNavigation( elem.value );
|
|
684
|
-
const item = elem.value.path[elem.value.path.length - 1];
|
|
685
|
-
if (nav.navigation && nav.item === item) {
|
|
686
|
-
// sourceElem, alias.sourceElem, mixin:
|
|
687
|
-
// redirectImplicitly( elem, origin );
|
|
688
|
-
pushLink( nav.navigation, '_projections', elem );
|
|
689
|
-
}
|
|
690
|
-
else if (elem._pathHead?.kind === '$inline' && elem.value.path.length === 1) {
|
|
691
|
-
const hpath = elem._pathHead.value?.path;
|
|
692
|
-
const head = hpath?.length === 1 && hpath[0]._navigation;
|
|
693
|
-
// Alias .{ elem } - but not with Alias.{ $magic }: consider for ON-rewrite
|
|
694
|
-
if (head?.kind === '$tableAlias' && item.id.charAt(0) !== '$')
|
|
695
|
-
pushLink( head.elements[item.id], '_projections', elem );
|
|
729
|
+
if (!elem.type && elem.value?.type) { // top-level CAST( expr AS type )
|
|
730
|
+
if (!elem.target) // TODO: we might issue an error if there is a target
|
|
731
|
+
elem.type = { ...elem.value.type, $inferred: 'cast' };
|
|
696
732
|
}
|
|
733
|
+
if (elem.foreignKeys) // REDIRECTED with explicit foreign keys
|
|
734
|
+
forEachGeneric( elem, 'foreignKeys', (key, name) => initKey( key, name, elem ) );
|
|
697
735
|
}
|
|
698
736
|
|
|
699
737
|
function initKey( key, name, elem ) {
|
|
@@ -733,8 +771,8 @@ function populate( model ) {
|
|
|
733
771
|
// console.log('S1:',location.line,location.col,
|
|
734
772
|
// envParent&&!!envParent._origin&&envParent._origin.name)
|
|
735
773
|
const env = columnEnv( envParent, query );
|
|
736
|
-
// console.log('S2:',location.line,location.col,
|
|
737
|
-
// envParent
|
|
774
|
+
// if (envParent) console.log('S2:',location.line,location.col,
|
|
775
|
+
// envParent?.name,envParent?._origin?.name,
|
|
738
776
|
// Object.keys(env),Object.keys(elements))
|
|
739
777
|
for (const name in env) {
|
|
740
778
|
const navElem = env[name];
|
|
@@ -793,8 +831,9 @@ function populate( model ) {
|
|
|
793
831
|
}
|
|
794
832
|
|
|
795
833
|
function columnEnv( envParent, query ) { // etc. wildcard._pathHead;
|
|
834
|
+
// if (envParent) console.log( 'CE:', envParent._origin, query );
|
|
796
835
|
return (envParent)
|
|
797
|
-
? environment(
|
|
836
|
+
? environment( getOrigin( envParent ) ) // not the col with expand, but the referred
|
|
798
837
|
: userQuery( query )._combined;
|
|
799
838
|
}
|
|
800
839
|
|
|
@@ -830,9 +869,9 @@ function populate( model ) {
|
|
|
830
869
|
setArtifactLink( path[0], origin );
|
|
831
870
|
setLink( queryElem, '_origin', origin );
|
|
832
871
|
// set _projections when inline with table alias:
|
|
833
|
-
const alias = pathHead?.value?.path?.[0]?._navigation;
|
|
834
|
-
if (alias?.kind === '$tableAlias')
|
|
835
|
-
|
|
872
|
+
// const alias = pathHead?.value?.path?.[0]?._navigation;
|
|
873
|
+
// if (alias?.kind === '$tableAlias')
|
|
874
|
+
// pushLink( alias.elements[name], '_projections', queryElem );
|
|
836
875
|
}
|
|
837
876
|
|
|
838
877
|
// called by expandWildcard():
|
|
@@ -847,7 +886,7 @@ function populate( model ) {
|
|
|
847
886
|
setArtifactLink( path[1], sourceElem );
|
|
848
887
|
// TODO: or should we set the _artifact/_effectiveType directly to the target?
|
|
849
888
|
setArtifactLink( queryElem.value, sourceElem );
|
|
850
|
-
pushLink( navElem, '_projections', queryElem );
|
|
889
|
+
// pushLink( navElem, '_projections', queryElem );
|
|
851
890
|
// TODO: _effectiveType?
|
|
852
891
|
}
|
|
853
892
|
|
|
@@ -1013,18 +1052,20 @@ function populate( model ) {
|
|
|
1013
1052
|
const exposed = preferred.length ? preferred : descendants;
|
|
1014
1053
|
if (exposed.length < 2)
|
|
1015
1054
|
return exposed || [];
|
|
1016
|
-
let min =
|
|
1055
|
+
let min = [];
|
|
1017
1056
|
for (const e of exposed) {
|
|
1018
|
-
if (
|
|
1019
|
-
min = e;
|
|
1057
|
+
if (min.every( m => m._ancestors?.includes( e ))) {
|
|
1058
|
+
min = [ e ];
|
|
1020
1059
|
}
|
|
1021
|
-
else if (
|
|
1060
|
+
else if (min.length !== 1 || !e._ancestors?.includes( min[0] )) {
|
|
1061
|
+
if (elemScope === '' && options.testMode)
|
|
1062
|
+
throw new CompilerAssertion( `Scope for ${ target } in service ${ service } is empty`);
|
|
1022
1063
|
if (elemScope === '')
|
|
1023
1064
|
return [];
|
|
1024
|
-
|
|
1065
|
+
min.push( e );
|
|
1025
1066
|
}
|
|
1026
1067
|
}
|
|
1027
|
-
return
|
|
1068
|
+
return min;
|
|
1028
1069
|
}
|
|
1029
1070
|
|
|
1030
1071
|
// Scoped redirections -----------------------------------------------------
|
|
@@ -1253,48 +1294,11 @@ function populate( model ) {
|
|
|
1253
1294
|
setLink( art, '_service', service );
|
|
1254
1295
|
setLink( art, '_block', model.$internal );
|
|
1255
1296
|
initArtifact( art, !!autoexposed );
|
|
1256
|
-
|
|
1257
|
-
populateView( art );
|
|
1297
|
+
effectiveType( art );
|
|
1258
1298
|
// TODO: try to set locations of elements locations of orig target elements
|
|
1259
1299
|
newAutoExposed.push( art );
|
|
1260
1300
|
return art;
|
|
1261
1301
|
}
|
|
1262
1302
|
}
|
|
1263
1303
|
|
|
1264
|
-
// Return condensed info about reference in select item
|
|
1265
|
-
// - tableAlias.elem -> { navigation: navElem, item: path[1], tableAlias }
|
|
1266
|
-
// - sourceElem (in query) -> { navigation: navElem, item: path[0], tableAlias }
|
|
1267
|
-
// - mixinElem -> { navigation: mixinElement, item: path[0] }
|
|
1268
|
-
// - $projection.elem -> also $self.item -> { item: path[1], tableAlias: $self }
|
|
1269
|
-
// - $self -> { item: undefined, tableAlias: $self }
|
|
1270
|
-
// - $parameters.P, :P -> {}
|
|
1271
|
-
// - $now, current_date -> {}
|
|
1272
|
-
// - undef, redef -> {}
|
|
1273
|
-
// With 'navigation': store that navigation._artifact is projected
|
|
1274
|
-
// With 'navigation': rewrite its ON condition
|
|
1275
|
-
// With navigation: Do KEY propagation
|
|
1276
|
-
//
|
|
1277
|
-
// TODO: copy of fn in resolve.js; used just once here - do it differently here
|
|
1278
|
-
// and then delete the function here
|
|
1279
|
-
function pathNavigation( ref ) {
|
|
1280
|
-
// currently, indirectly projectable elements are not included - we might
|
|
1281
|
-
// keep it this way! If we want them to be included - be aware: cycles
|
|
1282
|
-
if (!ref._artifact)
|
|
1283
|
-
return {};
|
|
1284
|
-
let item = ref.path && ref.path[0];
|
|
1285
|
-
const root = item && item._navigation;
|
|
1286
|
-
if (!root)
|
|
1287
|
-
return {};
|
|
1288
|
-
if (root.kind === '$navElement')
|
|
1289
|
-
return { navigation: root, item, tableAlias: root._parent };
|
|
1290
|
-
if (root.kind === 'mixin')
|
|
1291
|
-
return { navigation: root, item };
|
|
1292
|
-
item = ref.path[1];
|
|
1293
|
-
if (root.kind === '$self')
|
|
1294
|
-
return { item, tableAlias: root };
|
|
1295
|
-
if (root.kind !== '$tableAlias' || ref.path.length < 2)
|
|
1296
|
-
return {}; // should not happen
|
|
1297
|
-
return { navigation: root.elements[item.id], item, tableAlias: root };
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
1304
|
module.exports = populate;
|