@sap/cds-compiler 2.10.4 → 2.12.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 +136 -0
- package/bin/.eslintrc.json +1 -2
- package/bin/cds_update_identifiers.js +10 -8
- package/bin/cdsc.js +58 -35
- package/bin/cdsse.js +1 -0
- package/bin/cdsv2m.js +3 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +16 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +10 -36
- package/lib/api/options.js +17 -8
- package/lib/api/validate.js +30 -3
- package/lib/backends.js +12 -13
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +3 -2
- package/lib/base/message-registry.js +64 -11
- package/lib/base/messages.js +38 -18
- package/lib/base/model.js +6 -4
- package/lib/base/optionProcessorHelper.js +148 -86
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/sql-snippets.js +93 -0
- package/lib/checks/unknownMagic.js +6 -3
- package/lib/checks/validator.js +8 -0
- package/lib/compiler/assert-consistency.js +14 -5
- package/lib/compiler/base.js +64 -0
- package/lib/compiler/builtins.js +62 -16
- package/lib/compiler/checks.js +34 -10
- package/lib/compiler/definer.js +91 -112
- package/lib/compiler/index.js +30 -30
- package/lib/compiler/propagator.js +8 -4
- package/lib/compiler/resolver.js +279 -63
- package/lib/compiler/shared.js +65 -230
- package/lib/compiler/utils.js +191 -0
- package/lib/edm/annotations/genericTranslation.js +35 -18
- package/lib/edm/annotations/preprocessAnnotations.js +1 -1
- package/lib/edm/csn2edm.js +4 -3
- package/lib/edm/edm.js +8 -8
- package/lib/edm/edmPreprocessor.js +61 -59
- package/lib/edm/edmUtils.js +14 -15
- package/lib/gen/Dictionary.json +82 -40
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +19 -1
- package/lib/gen/language.tokens +80 -73
- package/lib/gen/languageLexer.interp +27 -1
- package/lib/gen/languageLexer.js +925 -826
- package/lib/gen/languageLexer.tokens +72 -65
- package/lib/gen/languageParser.js +4817 -4102
- package/lib/json/from-csn.js +57 -26
- package/lib/json/to-csn.js +244 -51
- package/lib/language/antlrParser.js +12 -1
- package/lib/language/docCommentParser.js +1 -1
- package/lib/language/errorStrategy.js +26 -8
- package/lib/language/genericAntlrParser.js +106 -30
- package/lib/language/language.g4 +200 -70
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +220 -21
- package/lib/main.js +6 -3
- package/lib/model/api.js +2 -2
- package/lib/model/csnRefs.js +218 -86
- package/lib/model/csnUtils.js +99 -178
- package/lib/model/enrichCsn.js +84 -43
- package/lib/model/revealInternalProperties.js +25 -8
- package/lib/model/sortViews.js +8 -1
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -18
- package/lib/render/.eslintrc.json +1 -2
- package/lib/render/DuplicateChecker.js +2 -2
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +202 -82
- package/lib/render/toHdbcds.js +194 -135
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +91 -51
- package/lib/render/utils/common.js +24 -5
- package/lib/render/utils/sql.js +6 -4
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/applyTransformations.js +189 -0
- package/lib/transform/db/associations.js +389 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +275 -119
- package/lib/transform/db/draft.js +6 -4
- package/lib/transform/db/expansion.js +10 -9
- package/lib/transform/db/flattening.js +23 -8
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +106 -25
- package/lib/transform/db/views.js +485 -0
- package/lib/transform/forHanaNew.js +90 -1036
- package/lib/transform/forOdataNew.js +11 -3
- package/lib/transform/localized.js +5 -14
- package/lib/transform/odata/generateForeignKeyElements.js +2 -2
- package/lib/transform/transformUtilsNew.js +34 -20
- package/lib/transform/translateAssocsToJoins.js +15 -23
- package/lib/transform/universalCsnEnricher.js +217 -47
- package/lib/utils/file.js +13 -6
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +55 -27
- package/package.json +1 -1
- package/lib/transform/db/helpers.js +0 -58
package/lib/model/csnRefs.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
// a reference is context-dependent, especially if queries are involved. This
|
|
5
5
|
// module provides the corresponding resolve/inspect functions.
|
|
6
6
|
//
|
|
7
|
+
// This module should work with both client-style CSN and Universal CSN.
|
|
8
|
+
//
|
|
7
9
|
// See below for preconditions / things to consider – the functions in this
|
|
8
10
|
// module do not issue user-friendly messages for invalid references in a CSN,
|
|
9
11
|
// such messages are (hopefully) issued by the compile() function.
|
|
@@ -41,18 +43,18 @@
|
|
|
41
43
|
//
|
|
42
44
|
// 1. a well-formed CSN with valid references;
|
|
43
45
|
// 2. a compiled model, i.e. a CSN with all inferred information provided by
|
|
44
|
-
// the compile() function
|
|
45
|
-
//
|
|
46
|
+
// the compile() function for the CSN flavors `client` or `universal`
|
|
47
|
+
// (including the (non-)enumerable `elements` property in `client` CSN);
|
|
46
48
|
// 3. no (relevant) CSN changes between the calls of the same instance of
|
|
47
49
|
// inspectRef() - to enable caching.
|
|
48
50
|
//
|
|
49
51
|
// If any of these conditions are not given, our functions usually simply
|
|
50
52
|
// throws an exception (which might even be a plain TypeError), but it might
|
|
51
|
-
// also
|
|
53
|
+
// also just return any value. CSN processors can provide user-friendly error
|
|
52
54
|
// messages by calling the Core Compiler in case of exceptions. For details,
|
|
53
55
|
// see internalDoc/CoreCompiler.md#use-of-the-core-compiler-for-csn-processors.
|
|
54
56
|
|
|
55
|
-
// During a transformation, care must be
|
|
57
|
+
// During a transformation, care must be taken to adhere to these conditions.
|
|
56
58
|
// E.g. a structure flattening function cannot create an element `s_x` and
|
|
57
59
|
// delete `s` and then still expects inspectRef() to be able to resolve a
|
|
58
60
|
// reference `['s', 'x']`.
|
|
@@ -93,7 +95,7 @@
|
|
|
93
95
|
//
|
|
94
96
|
// The names in further path items are searched in the “navigation” environment
|
|
95
97
|
// of the path so far - it does not need to depend on the reference context (as
|
|
96
|
-
// we do not check the
|
|
98
|
+
// we do not check the validity here):
|
|
97
99
|
//
|
|
98
100
|
// 1. We search in the elements of the target entity for associations and
|
|
99
101
|
// compositions, and in the elements of the current object otherwise.
|
|
@@ -157,6 +159,24 @@
|
|
|
157
159
|
// hierarchy, query number, table aliases and links from a column to its
|
|
158
160
|
// respective inferred element.
|
|
159
161
|
|
|
162
|
+
// Properties in cache:
|
|
163
|
+
//
|
|
164
|
+
// - _effectiveType on def/member/items: cached result of effectiveType()
|
|
165
|
+
// - _origin on def/member/items: the "prototype"
|
|
166
|
+
// - $origin on def/member/items: whether implicit _origin refs have been set for direct members
|
|
167
|
+
// - _parent: not yet used (for debugging)
|
|
168
|
+
//
|
|
169
|
+
// - _env on non-string path item: environment provided by the ref so far,
|
|
170
|
+
// next path item is element in it
|
|
171
|
+
// - _ref on non-string `type` or `from` ref, or on alias: the referred def/elem
|
|
172
|
+
//
|
|
173
|
+
// - $queries on def: array of all queries of an entity
|
|
174
|
+
// - $queryNumber: the index position +1 of a query inside the $queries array
|
|
175
|
+
// - $aliases on query: dictionary of alias names to cache with _ref/_select and elements
|
|
176
|
+
// - _select: value of the `SELECT` property of a query (or value of `projection`)
|
|
177
|
+
// - elements: the elements of the query (original CSN elements from query or main)
|
|
178
|
+
// - _element on query column: the corresponding element
|
|
179
|
+
|
|
160
180
|
'use strict';
|
|
161
181
|
|
|
162
182
|
const BUILTIN_TYPE = {};
|
|
@@ -178,12 +198,13 @@ const referenceSemantics = {
|
|
|
178
198
|
targetAspect: { lexical: false, dynamic: 'global' },
|
|
179
199
|
from: { lexical: false, dynamic: 'global' },
|
|
180
200
|
keys: { lexical: false, dynamic: 'target' },
|
|
201
|
+
keys_origin: { lexical: false, dynamic: 'target' },
|
|
181
202
|
excluding: { lexical: false, dynamic: 'source' },
|
|
182
203
|
expand: { lexical: justDollar, dynamic: 'expand' }, // ...using baseEnv
|
|
183
204
|
inline: { lexical: justDollar, dynamic: 'inline' }, // ...using baseEnv
|
|
184
205
|
ref_where: { lexical: justDollar , dynamic: 'ref-target'}, // ...using baseEnv
|
|
185
206
|
on: { lexical: justDollar, dynamic: 'query' }, // assoc defs, redirected to
|
|
186
|
-
// there are also '
|
|
207
|
+
// there are also 'on_join' and 'on_mixin' with default semantics
|
|
187
208
|
orderBy: { lexical: query => query, dynamic: 'query' },
|
|
188
209
|
orderBy_set: { lexical: query => query.$next, dynamic: 'query' }, // to outer SELECT (from UNION)
|
|
189
210
|
// default: { lexical: query => query, dynamic: 'source' }
|
|
@@ -225,17 +246,19 @@ function csnRefs( csn ) {
|
|
|
225
246
|
const cachedType = getCache( art, '_effectiveType' );
|
|
226
247
|
if (cachedType !== undefined)
|
|
227
248
|
return cachedType;
|
|
228
|
-
else if (!art.type && !art.$origin ||
|
|
229
|
-
art.elements || art.target || art.targetAspect || art.enum)
|
|
230
|
-
return setCache( art, '_effectiveType', art );
|
|
231
249
|
|
|
232
250
|
const chain = [];
|
|
233
|
-
|
|
251
|
+
let origin;
|
|
252
|
+
while (getCache( art, '_effectiveType' ) === undefined &&
|
|
253
|
+
(origin = cached( art, '_origin', getOriginRaw )) &&
|
|
234
254
|
!art.elements && !art.target && !art.targetAspect && !art.enum && !art.items) {
|
|
235
255
|
chain.push( art );
|
|
236
256
|
setCache( art, '_effectiveType', 0 ); // initial setting in case of cycles
|
|
237
|
-
art =
|
|
257
|
+
art = origin;
|
|
238
258
|
}
|
|
259
|
+
if (!chain.length)
|
|
260
|
+
return setCache( art, '_effectiveType', art );
|
|
261
|
+
|
|
239
262
|
if (getCache( art, '_effectiveType' ) === 0)
|
|
240
263
|
throw new Error( 'Circular type reference');
|
|
241
264
|
const type = getCache( art, '_effectiveType' ) || art;
|
|
@@ -247,14 +270,18 @@ function csnRefs( csn ) {
|
|
|
247
270
|
* @param {CSN.Artifact} art
|
|
248
271
|
*/
|
|
249
272
|
function navigationEnv( art ) {
|
|
250
|
-
let
|
|
273
|
+
let env = effectiveType( art );
|
|
274
|
+
getOrigin( env ); // also set implicit origins
|
|
251
275
|
// here, we do not care whether it is semantically ok to navigate into sub
|
|
252
276
|
// elements of array items (that is the task of the core compiler /
|
|
253
277
|
// semantic check)
|
|
254
|
-
while (
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
278
|
+
while (env.items)
|
|
279
|
+
env = effectiveType( env.items );
|
|
280
|
+
if (!env.target)
|
|
281
|
+
return env;
|
|
282
|
+
const target = csn.definitions[env.target];
|
|
283
|
+
getOrigin( target );
|
|
284
|
+
return target;
|
|
258
285
|
}
|
|
259
286
|
|
|
260
287
|
/**
|
|
@@ -279,38 +306,129 @@ function csnRefs( csn ) {
|
|
|
279
306
|
function artifactPathRef( ref ) {
|
|
280
307
|
const [ head, ...tail ] = ref.ref;
|
|
281
308
|
let art = csn.definitions[pathId( head )];
|
|
282
|
-
for (const elem of tail)
|
|
283
|
-
|
|
309
|
+
for (const elem of tail) {
|
|
310
|
+
const env = navigationEnv( art );
|
|
311
|
+
art = env.elements[pathId( elem )];
|
|
312
|
+
}
|
|
284
313
|
return art;
|
|
285
314
|
}
|
|
286
315
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
316
|
+
// Return target when resolving references in 'keys'
|
|
317
|
+
function assocTarget( art, refCtx ) {
|
|
318
|
+
// Call contexts:
|
|
319
|
+
// 1. normal definition of association with explicit foreign keys
|
|
320
|
+
// 2. auto-redirected association with renaming of foreign keys
|
|
321
|
+
// (currently: `keys` always available on inherited associations)
|
|
322
|
+
// 3. user-induced redirection (in 'cast') with explicit foreign keys
|
|
323
|
+
// 4. original provided association def inside $origin with explicit foreign keys
|
|
324
|
+
// (outside $origin like 2)
|
|
325
|
+
const targetName = refCtx !== 'keys_origin' && art.target
|
|
326
|
+
|| art.$origin && art.$origin.target
|
|
327
|
+
|| art.cast.target;
|
|
328
|
+
const target = csn.definitions[targetName];
|
|
329
|
+
getOrigin( target ); // induce implicit $origin on target
|
|
330
|
+
return target;
|
|
292
331
|
}
|
|
293
332
|
|
|
294
|
-
function
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
333
|
+
function getOrigin( art ) {
|
|
334
|
+
const origin = cached( art, '_origin', getOriginRaw );
|
|
335
|
+
if (origin && origin !== BUILTIN_TYPE)
|
|
336
|
+
cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
|
|
337
|
+
return origin;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function getOriginRaw( art ) {
|
|
341
|
+
if (art.type) // TODO: make robust against "linked" = only direct
|
|
342
|
+
return artifactRef( art.type, BUILTIN_TYPE );
|
|
343
|
+
if (!art.$origin) // implicit $origin should have been set
|
|
344
|
+
return null;
|
|
345
|
+
// art.$origin must not be a string here - shortened refs should already
|
|
346
|
+
// have been used to set the _origin cache
|
|
347
|
+
// If $origin object, return $origin of that:
|
|
348
|
+
if (!Array.isArray( art.$origin )) // anonymous prototype in $origin
|
|
349
|
+
return cached( art.$origin, '_origin', getOriginRaw );
|
|
350
|
+
const [ head, ...tail ] = art.$origin;
|
|
351
|
+
let origin = csn.definitions[head];
|
|
352
|
+
// allow shorter $origin ref for actions/functions, just using a string:
|
|
353
|
+
let isAction = art.kind === 'action' || art.kind === 'function';
|
|
354
|
+
for (const elem of tail) {
|
|
355
|
+
origin = originNavigation( origin, elem, isAction );
|
|
356
|
+
isAction = false;
|
|
357
|
+
}
|
|
358
|
+
return origin;
|
|
300
359
|
}
|
|
301
360
|
|
|
302
|
-
function originNavigation( art, elem ) {
|
|
361
|
+
function originNavigation( art, elem, isAction ) {
|
|
303
362
|
if (typeof elem !== 'string') {
|
|
304
363
|
if (elem.action)
|
|
305
|
-
return art.actions[elem.action]
|
|
364
|
+
return art.actions[elem.action];
|
|
306
365
|
if (elem.param)
|
|
307
|
-
return
|
|
366
|
+
return art.params[elem.param];
|
|
367
|
+
if (elem.returns)
|
|
368
|
+
return art.returns;
|
|
308
369
|
}
|
|
309
|
-
if (
|
|
370
|
+
if (isAction)
|
|
371
|
+
return art.actions[elem];
|
|
372
|
+
// TODO: if we use effectiveType(), we might have more implicit prototypes
|
|
373
|
+
// we might then need a function like effectiveArtifact,
|
|
374
|
+
// which cares about actions/params
|
|
375
|
+
// let origin = cached( art, '_origin', getOriginRaw );
|
|
376
|
+
// while (art.items) {
|
|
377
|
+
// cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
|
|
378
|
+
// art = art.items;
|
|
379
|
+
// origin = cached( art, '_origin', getOriginRaw );
|
|
380
|
+
// }
|
|
381
|
+
// if (origin)
|
|
382
|
+
// cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
|
|
383
|
+
// console.log(art)
|
|
384
|
+
if (art.returns) // for ["main", {action: "act"}, "elem"]
|
|
310
385
|
art = art.returns;
|
|
311
|
-
|
|
386
|
+
return (art.elements || art.enum || targetAspect( art ).elements)[elem];
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function targetAspect( art ) {
|
|
390
|
+
const { $origin } = art;
|
|
391
|
+
return art.targetAspect ||
|
|
392
|
+
$origin && typeof $origin === 'object' && !Array.isArray( $origin ) && $origin.target ||
|
|
393
|
+
art.target;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// From the current CSN object, set implicit origin for the next navigation step
|
|
397
|
+
function setImplicitOrigin( art, origin ) {
|
|
398
|
+
setMembersImplicit( art.actions, origin.actions );
|
|
399
|
+
setMembersImplicit( art.params, origin.params );
|
|
400
|
+
if (art.returns) {
|
|
401
|
+
art = art.returns;
|
|
402
|
+
if (art.type || typeof art.$origin === 'object') // null, […], {…}
|
|
403
|
+
return true; // not implicit or shortened
|
|
404
|
+
origin = effectiveType( origin.returns );
|
|
405
|
+
setCache( art, '_origin', origin );
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
while (art.items) {
|
|
312
409
|
art = art.items;
|
|
313
|
-
|
|
410
|
+
if (art.type || typeof art.$origin === 'object') // null, […], {…}
|
|
411
|
+
return true; // not implicit or shortened
|
|
412
|
+
origin = effectiveType( origin.items );
|
|
413
|
+
setCache( art, '_origin', origin );
|
|
414
|
+
}
|
|
415
|
+
setMembersImplicit( art.elements, origin.elements );
|
|
416
|
+
// The enum base type is _not_ where we find the origins of the enum symbols.
|
|
417
|
+
// A derived type of an enum type with individual annotations on a symbol
|
|
418
|
+
// has both 'type' and '$origin'.
|
|
419
|
+
if (!art.type || art.$origin)
|
|
420
|
+
setMembersImplicit( art.enum, origin.enum );
|
|
421
|
+
return true;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function setMembersImplicit( members, originMembers ) {
|
|
425
|
+
if (!members)
|
|
426
|
+
return;
|
|
427
|
+
for (const name in members) {
|
|
428
|
+
const elem = members[name];
|
|
429
|
+
if (!elem.type && typeof elem.$origin !== 'object') // undefined or string
|
|
430
|
+
setCache( elem, '_origin', originMembers[elem.$origin || name] || false );
|
|
431
|
+
}
|
|
314
432
|
}
|
|
315
433
|
|
|
316
434
|
/**
|
|
@@ -365,13 +483,16 @@ function csnRefs( csn ) {
|
|
|
365
483
|
|
|
366
484
|
const head = pathId( path[0] );
|
|
367
485
|
if (ref.param)
|
|
368
|
-
return resolvePath( path, main.params[head], 'param' );
|
|
486
|
+
return resolvePath( path, main.params[head], main, 'param' );
|
|
369
487
|
|
|
370
488
|
const semantics = referenceSemantics[refCtx] || {};
|
|
371
489
|
if (semantics.dynamic === 'global' || ref.global)
|
|
372
|
-
return resolvePath( path, csn.definitions[head], 'global', refCtx === 'from' );
|
|
490
|
+
return resolvePath( path, csn.definitions[head], null, 'global', refCtx === 'from' );
|
|
373
491
|
|
|
374
|
-
|
|
492
|
+
if (main) {
|
|
493
|
+
getOrigin( main );
|
|
494
|
+
cached( main, '$queries', allQueries );
|
|
495
|
+
}
|
|
375
496
|
let qcache = query && cache.get( query.projection || query );
|
|
376
497
|
// BACKEND ISSUE: you cannot call csnRefs(), inspect some refs, change the
|
|
377
498
|
// CSN and again inspect some refs without calling csnRefs() before!
|
|
@@ -387,39 +508,38 @@ function csnRefs( csn ) {
|
|
|
387
508
|
while (cache) {
|
|
388
509
|
const alias = tryAlias && cache.$aliases[head];
|
|
389
510
|
if (alias)
|
|
390
|
-
return resolvePath( path, alias._select || alias,
|
|
511
|
+
return resolvePath( path, alias._select || alias._ref, null,
|
|
512
|
+
'alias', cache.$queryNumber );
|
|
391
513
|
const mixin = cache._select.mixin && cache._select.mixin[head];
|
|
392
514
|
if (mixin && {}.hasOwnProperty.call( cache._select.mixin, head ))
|
|
393
|
-
return resolvePath( path, mixin, 'mixin', cache.$queryNumber );
|
|
515
|
+
return resolvePath( path, mixin, null, 'mixin', cache.$queryNumber );
|
|
394
516
|
cache = cache.$next;
|
|
395
517
|
}
|
|
396
518
|
if (head.charAt(0) === '$') {
|
|
397
519
|
if (head !== '$self' && head !== '$projection')
|
|
398
520
|
return { scope: '$magic' };
|
|
399
521
|
const self = qcache && qcache.$queryNumber > 1 ? qcache._select : main;
|
|
400
|
-
return resolvePath( path, self, '$self' );
|
|
522
|
+
return resolvePath( path, self, null, '$self' );
|
|
401
523
|
}
|
|
402
524
|
}
|
|
403
525
|
// now the dynamic environment: ------------------------------------------
|
|
404
526
|
if (semantics.dynamic === 'target') { // ref in keys
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
// target
|
|
408
|
-
const target = csn.definitions[parent.target || parent.cast.target];
|
|
409
|
-
return resolvePath( path, target.elements[head], 'target' );
|
|
527
|
+
const target = assocTarget( parent, refCtx );
|
|
528
|
+
return resolvePath( path, target.elements[head], target, 'target' );
|
|
410
529
|
}
|
|
411
530
|
if (baseEnv) // ref-target (filter condition), expand, inline
|
|
412
|
-
return resolvePath( path, baseEnv.elements[head], semantics.dynamic );
|
|
531
|
+
return resolvePath( path, baseEnv.elements[head], baseEnv, semantics.dynamic );
|
|
413
532
|
if (!query) // outside queries - TODO: items?
|
|
414
|
-
return resolvePath( path, parent.elements[head], 'parent' );
|
|
533
|
+
return resolvePath( path, parent.elements[head], parent, 'parent' );
|
|
415
534
|
|
|
416
535
|
if (semantics.dynamic === 'query')
|
|
417
536
|
// TODO: for ON condition in expand, would need to use cached _element
|
|
418
|
-
return resolvePath( path, qcache.elements[head], 'query' );
|
|
537
|
+
return resolvePath( path, qcache.elements[head], null, 'query' );
|
|
419
538
|
for (const name in qcache.$aliases) {
|
|
420
|
-
const
|
|
539
|
+
const alias = qcache.$aliases[name];
|
|
540
|
+
const found = alias.elements[head];
|
|
421
541
|
if (found)
|
|
422
|
-
return resolvePath( path, found, 'source', name )
|
|
542
|
+
return resolvePath( path, found, alias._ref, 'source', name )
|
|
423
543
|
}
|
|
424
544
|
// console.log(query.SELECT,qcache,qcache.$next,main)
|
|
425
545
|
throw new Error ( `Path item ${ 0 }=${ head } refers to nothing, refCtx: ${ refCtx }` );
|
|
@@ -429,8 +549,9 @@ function csnRefs( csn ) {
|
|
|
429
549
|
* @param {CSN.Path} path
|
|
430
550
|
* @param {CSN.Artifact} art
|
|
431
551
|
* @param {string} [scope]
|
|
552
|
+
* @param [extraInfo]
|
|
432
553
|
*/
|
|
433
|
-
function resolvePath( path, art, scope, extraInfo ) {
|
|
554
|
+
function resolvePath( path, art, parent, scope, extraInfo ) {
|
|
434
555
|
/** @type {{idx, art?, env?}[]} */
|
|
435
556
|
const links = path.map( (_v, idx) => ({ idx }) );
|
|
436
557
|
// TODO: backends should be changed to enable uncommenting:
|
|
@@ -438,11 +559,11 @@ function csnRefs( csn ) {
|
|
|
438
559
|
// throw new Error ( `Path item ${ 0 }=${ pathId( path[0] ) } refers to nothing, scope: ${ scope }`);
|
|
439
560
|
links[0].art = art;
|
|
440
561
|
for (let i = 1; i < links.length; ++i) { // yes, starting at 1, links[0] is set above
|
|
441
|
-
|
|
442
|
-
links[i - 1].env =
|
|
562
|
+
parent = navigationEnv( art );
|
|
563
|
+
links[i - 1].env = parent;
|
|
443
564
|
if (typeof path[i - 1] !== 'string')
|
|
444
|
-
setCache( path[i - 1], '_env',
|
|
445
|
-
art =
|
|
565
|
+
setCache( path[i - 1], '_env', parent );
|
|
566
|
+
art = parent.elements[pathId( path[i] )];
|
|
446
567
|
if (!art) {
|
|
447
568
|
const env = links[i - 1].env;
|
|
448
569
|
const loc = env.name && env.name.$location || env.$location;
|
|
@@ -455,14 +576,16 @@ function csnRefs( csn ) {
|
|
|
455
576
|
if (fromRef || typeof last !== 'string') {
|
|
456
577
|
const env = navigationEnv( art );
|
|
457
578
|
links[links.length - 1].env = env;
|
|
458
|
-
if (fromRef)
|
|
579
|
+
if (fromRef) {
|
|
459
580
|
art = env;
|
|
581
|
+
parent = null;
|
|
582
|
+
}
|
|
460
583
|
if (typeof last !== 'string')
|
|
461
584
|
setCache( last, '_env', env )
|
|
462
585
|
}
|
|
463
586
|
return (extraInfo && !fromRef)
|
|
464
|
-
? { links, art, scope, $env: extraInfo }
|
|
465
|
-
: { links, art, scope };
|
|
587
|
+
? { links, art, parent, scope, $env: extraInfo }
|
|
588
|
+
: { links, art, parent, scope };
|
|
466
589
|
}
|
|
467
590
|
|
|
468
591
|
/**
|
|
@@ -481,7 +604,8 @@ function csnRefs( csn ) {
|
|
|
481
604
|
if (query.ref) { // ref in from
|
|
482
605
|
// console.log('SQ:',query,cache.get(query))
|
|
483
606
|
const as = query.as || implicitAs( query.ref );
|
|
484
|
-
|
|
607
|
+
const _ref = fromRef( query );
|
|
608
|
+
getCache( fromSelect, '$aliases' )[as] = { _ref, elements: _ref.elements };
|
|
485
609
|
}
|
|
486
610
|
else {
|
|
487
611
|
const qcache = getQueryCache( parentQuery );
|
|
@@ -517,7 +641,7 @@ function csnRefs( csn ) {
|
|
|
517
641
|
function getQueryCache( parentQuery ) {
|
|
518
642
|
if (!parentQuery)
|
|
519
643
|
return { $aliases: Object.create(null) };
|
|
520
|
-
const pcache = cache.get( parentQuery );
|
|
644
|
+
const pcache = cache.get( parentQuery.projection || parentQuery );
|
|
521
645
|
if (!parentQuery.SET) // SELECT / projection: real sub query
|
|
522
646
|
return { $aliases: Object.create(null), $next: pcache };
|
|
523
647
|
// the parent query is a SET: that is not a sub query
|
|
@@ -583,8 +707,8 @@ function csnRefs( csn ) {
|
|
|
583
707
|
// i.e. a value with an `elements` property.
|
|
584
708
|
// TODO: only used in forHanaNew - move somewhere else
|
|
585
709
|
/**
|
|
586
|
-
* @param {
|
|
587
|
-
* @param {
|
|
710
|
+
* @param {object} query node (object with SET or SELECT property)
|
|
711
|
+
* @param {object} main definition
|
|
588
712
|
*/
|
|
589
713
|
function queryOrMain( query, main ) {
|
|
590
714
|
while (query.SET)
|
|
@@ -607,8 +731,8 @@ function queryOrMain( query, main ) {
|
|
|
607
731
|
*
|
|
608
732
|
* @param {CSN.Query} query
|
|
609
733
|
* @param {CSN.QuerySelect} fromSelect
|
|
610
|
-
* @param {CSN.Query} parentQuery
|
|
611
|
-
* @param {(query: CSN.Query&CSN.QueryFrom, select: CSN.QuerySelectEnriched) => void} callback
|
|
734
|
+
* @param {CSN.Query} parentQuery
|
|
735
|
+
* @param {(query: CSN.Query&CSN.QueryFrom, select: CSN.QuerySelectEnriched, parentQuery: CSN.Query) => void} callback
|
|
612
736
|
*/
|
|
613
737
|
function traverseQuery( query, fromSelect, parentQuery, callback ) {
|
|
614
738
|
const select = query.SELECT || query.projection;
|
|
@@ -632,8 +756,9 @@ function traverseQuery( query, fromSelect, parentQuery, callback ) {
|
|
|
632
756
|
|
|
633
757
|
/**
|
|
634
758
|
* @param {CSN.QueryFrom} from
|
|
635
|
-
* @param {CSN.QuerySelect}
|
|
636
|
-
* @param {
|
|
759
|
+
* @param {CSN.QuerySelect} fromSelect
|
|
760
|
+
* @param {CSN.Query} parentQuery
|
|
761
|
+
* @param {(from: CSN.QueryFrom, select: CSN.QuerySelect, parentQuery: CSN.Query) => void} callback
|
|
637
762
|
*/
|
|
638
763
|
function traverseFrom( from, fromSelect, parentQuery, callback ) {
|
|
639
764
|
if (from.ref) {
|
|
@@ -671,30 +796,42 @@ function implicitAs( ref ) {
|
|
|
671
796
|
return id.substring( id.lastIndexOf('.') + 1 );
|
|
672
797
|
}
|
|
673
798
|
|
|
799
|
+
function startCsnPath( csnPath, csn ) {
|
|
800
|
+
const head = csnPath[0];
|
|
801
|
+
if (typeof head !== 'string') {
|
|
802
|
+
const { main, parent, art } = head;
|
|
803
|
+
return { index: 1, main, parent, art };
|
|
804
|
+
}
|
|
805
|
+
if (csnPath.length < 2 || csnPath[0] !== 'definitions')
|
|
806
|
+
throw new Error( 'References outside definitions not supported yet');
|
|
807
|
+
const art = csn.definitions[csnPath[1]];
|
|
808
|
+
return { index: 2, main: art, parent: null, art };
|
|
809
|
+
}
|
|
810
|
+
|
|
674
811
|
/**
|
|
675
812
|
* @param {CSN.Path} csnPath
|
|
676
813
|
* @param {CSN.Model} csn
|
|
677
814
|
*/
|
|
678
815
|
function analyseCsnPath( csnPath, csn, resolve ) {
|
|
679
|
-
if (csnPath[0] !== 'definitions')
|
|
680
|
-
throw new Error( 'References outside definitions not supported yet');
|
|
681
|
-
|
|
682
816
|
/** @type {object} */
|
|
683
|
-
let obj = csn;
|
|
684
|
-
let parent = null;
|
|
685
817
|
let query = null;
|
|
686
818
|
let refCtx = null;
|
|
687
|
-
let art = null;
|
|
688
819
|
/** @type {boolean|string|number} */
|
|
689
820
|
let isName = false;
|
|
690
821
|
let baseRef = null;
|
|
691
822
|
let baseEnv = null;
|
|
692
|
-
let main = csn
|
|
823
|
+
let { index, main, parent, art } = startCsnPath( csnPath, csn );
|
|
824
|
+
let obj = art;
|
|
825
|
+
|
|
826
|
+
for (; index < csnPath.length; index++) {
|
|
827
|
+
if (!obj && !resolve)
|
|
828
|
+
// For the semantic location, use current object as best guess
|
|
829
|
+
break;
|
|
693
830
|
|
|
694
|
-
for (let index = 0; index < csnPath.length; index++) {
|
|
695
831
|
const prop = csnPath[index];
|
|
696
832
|
// array item, name/index of artifact/member, (named) argument
|
|
697
833
|
if (isName || Array.isArray( obj )) {
|
|
834
|
+
// TODO: call some kind of resolve.setOrigin()
|
|
698
835
|
if (typeof isName === 'string') {
|
|
699
836
|
parent = art;
|
|
700
837
|
art = obj[prop];
|
|
@@ -707,7 +844,8 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
707
844
|
parent = null;
|
|
708
845
|
}
|
|
709
846
|
isName = prop;
|
|
710
|
-
|
|
847
|
+
// if we want to allow auto-redirect of user-provided target with renamed keys:
|
|
848
|
+
refCtx = (refCtx === '$origin' && prop === 'keys') ? 'keys_origin' : prop;
|
|
711
849
|
}
|
|
712
850
|
else if (prop === 'items' || prop === 'returns') {
|
|
713
851
|
art = obj[prop];
|
|
@@ -723,15 +861,13 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
723
861
|
}
|
|
724
862
|
else if (prop === 'where' && refCtx === 'ref') {
|
|
725
863
|
if (resolve)
|
|
726
|
-
baseEnv = resolve.ref_where( obj, baseRef, refCtx,
|
|
727
|
-
query, parent, baseEnv );
|
|
864
|
+
baseEnv = resolve.ref_where( obj, baseRef, refCtx, main, query, parent, baseEnv );
|
|
728
865
|
refCtx = 'ref_where';
|
|
729
866
|
}
|
|
730
867
|
else if (prop === 'expand' || prop === 'inline') {
|
|
731
868
|
if (obj.ref) {
|
|
732
869
|
if (resolve)
|
|
733
|
-
baseEnv = resolve.expandInline( obj, refCtx,
|
|
734
|
-
query, parent, baseEnv );
|
|
870
|
+
baseEnv = resolve.expandInline( obj, refCtx, main, query, parent, baseEnv );
|
|
735
871
|
refCtx = prop;
|
|
736
872
|
}
|
|
737
873
|
if (prop === 'expand')
|
|
@@ -739,9 +875,9 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
739
875
|
}
|
|
740
876
|
else if (prop === 'on') {
|
|
741
877
|
if (refCtx === 'from')
|
|
742
|
-
refCtx = '
|
|
878
|
+
refCtx = 'on_join';
|
|
743
879
|
else if (refCtx === 'mixin')
|
|
744
|
-
refCtx = '
|
|
880
|
+
refCtx = 'on_mixin';
|
|
745
881
|
else
|
|
746
882
|
refCtx = 'on'; // will use query elements with REDIRECTED TO
|
|
747
883
|
}
|
|
@@ -755,11 +891,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
755
891
|
else if (prop !== 'xpr') {
|
|
756
892
|
refCtx = prop;
|
|
757
893
|
}
|
|
758
|
-
|
|
759
894
|
obj = obj[prop];
|
|
760
|
-
if (!obj && !resolve)
|
|
761
|
-
// For the semantic location, use current object as best guess
|
|
762
|
-
break;
|
|
763
895
|
}
|
|
764
896
|
// console.log( 'CPATH:', csnPath, refCtx, obj, parent.$location );
|
|
765
897
|
if (!resolve)
|