@sap/cds-compiler 2.5.2 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +235 -9
- package/bin/cdsc.js +44 -27
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +37 -3
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +37 -123
- package/lib/api/options.js +27 -15
- package/lib/api/validate.js +34 -9
- package/lib/backends.js +9 -89
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +73 -11
- package/lib/base/messages.js +86 -30
- package/lib/base/model.js +6 -6
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +25 -7
- package/lib/checks/selectItems.js +29 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +23 -7
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +8 -5
- package/lib/compiler/definer.js +157 -133
- package/lib/compiler/index.js +89 -31
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +375 -185
- package/lib/compiler/shared.js +49 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +104 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +388 -146
- package/lib/edm/edmUtils.js +104 -34
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5330 -4300
- package/lib/json/from-csn.js +110 -52
- package/lib/json/to-csn.js +434 -120
- package/lib/language/antlrParser.js +15 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +93 -26
- package/lib/language/language.g4 +172 -31
- package/lib/main.d.ts +216 -19
- package/lib/main.js +32 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +413 -149
- package/lib/model/csnUtils.js +286 -75
- package/lib/model/enrichCsn.js +50 -6
- package/lib/model/revealInternalProperties.js +22 -5
- package/lib/modelCompare/compare.js +39 -21
- package/lib/optionProcessor.js +35 -18
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +9 -6
- package/lib/render/toCdl.js +121 -36
- package/lib/render/toHdbcds.js +148 -98
- package/lib/render/toSql.js +114 -43
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +281 -106
- package/lib/transform/db/draft.js +11 -8
- package/lib/transform/db/expansion.js +584 -0
- package/lib/transform/db/flattening.js +341 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +345 -65
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +131 -793
- package/lib/transform/forOdataNew.js +30 -24
- package/lib/transform/localized.js +39 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +144 -78
- package/lib/transform/translateAssocsToJoins.js +22 -27
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -14
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
package/lib/model/csnRefs.js
CHANGED
|
@@ -1,27 +1,161 @@
|
|
|
1
1
|
// CSN functionality for resolving references
|
|
2
|
+
|
|
3
|
+
// Resolving references in a CSN can be a bit tricky, because the semantics of
|
|
4
|
+
// a reference is context-dependent, especially if queries are involved. This
|
|
5
|
+
// module provides the corresponding resolve/inspect functions.
|
|
2
6
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
//
|
|
8
|
-
//
|
|
7
|
+
// See below for preconditions / things to consider – the functions in this
|
|
8
|
+
// module do not issue user-friendly messages for invalid references in a CSN,
|
|
9
|
+
// such messages are (hopefully) issued by the compile() function.
|
|
10
|
+
|
|
11
|
+
// The main export function `csnRefs` of this module is called with a CSN as
|
|
12
|
+
// input and returns functions which analyse references in the provided CSN:
|
|
9
13
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
14
|
+
// const { csnRefs } = require('../model/csnRefs');
|
|
15
|
+
// function myCsnAnalyser( csn ) {
|
|
16
|
+
// const { inspectRef } = csnRefs( csn );
|
|
17
|
+
// …
|
|
18
|
+
// const { links, art } = inspectRef( csnPath );
|
|
19
|
+
// // → art is the CSN node which is referred to by the reference
|
|
20
|
+
// // → links provides some info about each reference path step
|
|
21
|
+
// …
|
|
22
|
+
// }
|
|
13
23
|
//
|
|
24
|
+
// You can see the results of the CSN refs functions by using our client tool:
|
|
25
|
+
// cdsc --enrich-csn MyModel.cds
|
|
26
|
+
// It is also used by our references tests, for details see ./enrichCsn.js.
|
|
27
|
+
|
|
14
28
|
// Terminology used in this file:
|
|
15
29
|
//
|
|
16
30
|
// - ref (reference): a { ref: <path> } object (or sometimes also a string)
|
|
17
|
-
// referring an artifact or member
|
|
31
|
+
// referring an artifact or member (element, …)
|
|
18
32
|
// - path: an array of strings or { id: … } objects for the dot-connected names
|
|
19
33
|
// used as reference
|
|
20
34
|
// - csnPath: an array of strings and numbers (e.g. ['definitions', 'S.E',
|
|
21
|
-
// 'query', 'SELECT', 'from', 'ref', 0]); they are the property
|
|
22
|
-
// array indexes which navigate from the CSN root to the
|
|
35
|
+
// 'query', 'SELECT', 'from', 'ref', 0, 'where', 2]); they are the property
|
|
36
|
+
// names and array indexes which navigate from the CSN root to the reference.
|
|
37
|
+
|
|
38
|
+
// ## PRECONDITIONS / THINGS TO CONSIDER -------------------------------------
|
|
39
|
+
|
|
40
|
+
// The functions in this module expect
|
|
41
|
+
//
|
|
42
|
+
// 1. a well-formed CSN with valid references;
|
|
43
|
+
// 2. a compiled model, i.e. a CSN with all inferred information provided by
|
|
44
|
+
// the compile() function, including the (non-)enumerable `elements`
|
|
45
|
+
// property of sub `SELECT`s in a FROM;
|
|
46
|
+
// 3. no (relevant) CSN changes between the calls of the same instance of
|
|
47
|
+
// inspectRef() - to enable caching.
|
|
48
|
+
//
|
|
49
|
+
// If any of these conditions are not given, our functions usually simply
|
|
50
|
+
// throws an exception (which might even be a plain TypeError), but it might
|
|
51
|
+
// also jsut return any value. CSN processors can provide user-friendly error
|
|
52
|
+
// messages by calling the Core Compiler in case of exceptions. For details,
|
|
53
|
+
// see internalDoc/CoreCompiler.md#use-of-the-core-compiler-for-csn-processors.
|
|
54
|
+
|
|
55
|
+
// During a transformation, care must be taked to adhere to these conditions.
|
|
56
|
+
// E.g. a structure flattening function cannot create an element `s_x` and
|
|
57
|
+
// delete `s` and then still expects inspectRef() to be able to resolve a
|
|
58
|
+
// reference `['s', 'x']`.
|
|
59
|
+
|
|
60
|
+
// The functions in this module also use an internal cache. The second call of
|
|
61
|
+
// inspectRef() in the following example might lead to a wrong result or an
|
|
62
|
+
// exception if the assignment to `inspectRef` is not uncommented:
|
|
63
|
+
//
|
|
64
|
+
// let { inspectRef } = csnRefs( csn );
|
|
65
|
+
// const csnPath = ['definitions','P','projection','columns',0];
|
|
66
|
+
// const subElement = inspectRef( csnPath ); // type T is involved
|
|
67
|
+
// csn.definitions.T.type = 'some.other.type';
|
|
68
|
+
// // ({ inspectRef } = csnRefs( csn )); // invalidate caches
|
|
69
|
+
// … = inspectRef( csnPath ); // type T - using the cached or the new?
|
|
70
|
+
//
|
|
71
|
+
// On request, we might add a functions for individual cache invalidations or
|
|
72
|
+
// low-level versions of inspectRef() for performance.
|
|
73
|
+
|
|
74
|
+
// ## NAME RESOLUTION OVERVIEW -----------------------------------------------
|
|
23
75
|
|
|
24
|
-
//
|
|
76
|
+
// The most interesting part of a reference is always: where to search for the
|
|
77
|
+
// name in its first path item? The general search is always as follows, with
|
|
78
|
+
// the exact behavior being dependent on the “reference context” (e.g. “reference
|
|
79
|
+
// in a `on` condition of a `mixin` definition”):
|
|
80
|
+
//
|
|
81
|
+
// 1. We search in environments constructed by “defining” names “around” the
|
|
82
|
+
// lexical position of the reference. In a CSN, these could be the
|
|
83
|
+
// (explicit and implicit) table alias names and `mixin` definitions of the
|
|
84
|
+
// current query and its parent queries (according to the query hiearchy).
|
|
85
|
+
// 2. If the search according to (1) was not successful and the name starts
|
|
86
|
+
// with a `$`, we could consider the name to be a “magic” variable with
|
|
87
|
+
// `$self` (and `$projection`) being a special magic variable.
|
|
88
|
+
// 3. Otherwise, we would search in a “dynamic” environment, which could be
|
|
89
|
+
// `‹csn›.definitions` for global references like `type`, the elements of
|
|
90
|
+
// the current element's parent, the combined elements of the query source
|
|
91
|
+
// entities, the resulting elements of the current query, or something
|
|
92
|
+
// special (elements of the association's target, …).
|
|
93
|
+
//
|
|
94
|
+
// The names in further path items are searched in the “navigation” environment
|
|
95
|
+
// of the path so far - it does not need to depend on the reference context (as
|
|
96
|
+
// we do not check the validility here):
|
|
97
|
+
//
|
|
98
|
+
// 1. We search in the elements of the target entity for associations and
|
|
99
|
+
// compositions, and in the elements of the current object otherwise.
|
|
100
|
+
// 2. If there is an `items`, we check for `elements`/`target` inside `items`.
|
|
101
|
+
// 3. `elements`/`target`/`items` inherited from the “effective type” are also
|
|
102
|
+
// considered.
|
|
103
|
+
|
|
104
|
+
// For details about the name resolution in CSN, see
|
|
105
|
+
// internalDoc/CsnSyntax.md#helper-property-for-simplified-name-resolution
|
|
106
|
+
// and doc/NameResolution.md. Here comes a summary.
|
|
107
|
+
|
|
108
|
+
// ## IMPLEMENTATION OVERVIEW ------------------------------------------------
|
|
109
|
+
|
|
110
|
+
// The main function `inspectRef` works as follows:
|
|
111
|
+
//
|
|
112
|
+
// 1. For ease of use, the input is the “CSN path” as explained above, e.g.
|
|
113
|
+
// ['definitions', 'P', 'query', 'SELECT', 'from', 'ref', 0, 'where', 2]
|
|
114
|
+
// 2. This is condensed into a “reference context” string, e.g. `ref_where`;
|
|
115
|
+
// that might also depend on sibling properties along the way, e.g.
|
|
116
|
+
// ['definitions', 'P', 'query', 'SELECT', 'columns', 0, 'expand', 0] leads
|
|
117
|
+
// to `expand` if there is a `‹csn›.definitions.P.query.SELECT.columns[0].ref`
|
|
118
|
+
// and to `columns` otherwise.
|
|
119
|
+
// 3. Additionally, other useful CSN nodes are collected like the current query;
|
|
120
|
+
// the queries of a definition are also prepared for further inspection.
|
|
121
|
+
// 4. If applicable, a “base environment” is calculated; e.g. references in
|
|
122
|
+
// `ref_where` are resolved against the elements of the entity referred to
|
|
123
|
+
// by the outer `ref`.
|
|
124
|
+
// 5. We look up the “reference semantics” in constant `referenceSemantics`
|
|
125
|
+
// using the “reference context” string as key.
|
|
126
|
+
// 6. The property `lexical` determines whether to search in “lexical
|
|
127
|
+
// environments” (table aliases and `mixin`s) starting from which query, and
|
|
128
|
+
// whether to do something special for names starting with `$`.
|
|
129
|
+
// 7. The property `dynamic` determines where to search if the lexical search
|
|
130
|
+
// was not successful.
|
|
131
|
+
// 8. The remaining reference path is resolved as well - the final referred CSN
|
|
132
|
+
// node is returned as well as information about each path step.
|
|
133
|
+
|
|
134
|
+
// We usually cache calculated data. For the following reasons, we now use a
|
|
135
|
+
// WeakMap as cache instead of adding non-enumerable properties to the CSN:
|
|
136
|
+
//
|
|
137
|
+
// - CSN consumers should not have access to the cached data, as we might
|
|
138
|
+
// change the way how we calculate things.
|
|
139
|
+
// - Avoid memory leaks.
|
|
140
|
+
// - Natural cache invalidation if there is no handle anymore to the functions
|
|
141
|
+
// returned by `csnRefs`.
|
|
142
|
+
|
|
143
|
+
// Our cache looks like follows:
|
|
144
|
+
|
|
145
|
+
// - Each object in the CSN could have an cache entry which itself is an object
|
|
146
|
+
// which contains cached data. Such data can be a link to a CSN node (like
|
|
147
|
+
// `_effectiveType`/`elements`), scalar (like `$queryNumber`) or link to
|
|
148
|
+
// another cache object (like `$next`).
|
|
149
|
+
// - Usually, each CSN object has an individual cache object.
|
|
150
|
+
// - For CSN queries nodes, cache objects are _shared_: both the CSN nodes
|
|
151
|
+
// `‹query› = { SELECT: ‹select›, … }` and `‹select›` share the same cache
|
|
152
|
+
// object; a UNION `‹set_query› = { SET: args: [‹query1›, …] }` and ‹query1›
|
|
153
|
+
// (which can itself be a `SELECT` or `SET`) share also the same cache
|
|
154
|
+
// object; this way, the relevant query elements are directly available.
|
|
155
|
+
// - The cache objects for all queries of an entity are initialized as soon as
|
|
156
|
+
// any reference in the entity is inspected: with data for the query
|
|
157
|
+
// hierarchy, query number, table aliases and links from a column to its
|
|
158
|
+
// respective inferred element.
|
|
25
159
|
|
|
26
160
|
'use strict';
|
|
27
161
|
|
|
@@ -34,19 +168,24 @@ const { locationString } = require('../base/location');
|
|
|
34
168
|
const artifactProperties = [ 'elements', 'columns', 'keys', 'mixin', 'enum',
|
|
35
169
|
'params', 'actions', 'definitions', 'extensions' ]; // + 'args', see above
|
|
36
170
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
171
|
+
// Mapping the “reference context string” to the reference semantics
|
|
172
|
+
// - lexical: false | Function - determines where to look first for “lexical names”
|
|
173
|
+
// - dynamic: String - describes the dynamic environment (if in query)
|
|
174
|
+
const referenceSemantics = {
|
|
175
|
+
type: { lexical: false, dynamic: 'global' },
|
|
176
|
+
includes: { lexical: false, dynamic: 'global' },
|
|
177
|
+
target: { lexical: false, dynamic: 'global' },
|
|
178
|
+
targetAspect: { lexical: false, dynamic: 'global' },
|
|
179
|
+
from: { lexical: false, dynamic: 'global' },
|
|
42
180
|
keys: { lexical: false, dynamic: 'target' },
|
|
43
|
-
excluding: { lexical: false },
|
|
44
|
-
expand: { lexical: justDollar },
|
|
45
|
-
inline: { lexical: justDollar },
|
|
46
|
-
|
|
181
|
+
excluding: { lexical: false, dynamic: 'source' },
|
|
182
|
+
expand: { lexical: justDollar, dynamic: 'expand' }, // ...using baseEnv
|
|
183
|
+
inline: { lexical: justDollar, dynamic: 'inline' }, // ...using baseEnv
|
|
184
|
+
ref_where: { lexical: justDollar , dynamic: 'ref-target'}, // ...using baseEnv
|
|
47
185
|
on: { lexical: justDollar, dynamic: 'query' }, // assoc defs, redirected to
|
|
48
|
-
|
|
49
|
-
|
|
186
|
+
// there are also 'join_on' and 'mixin_on' with default semantics
|
|
187
|
+
orderBy: { lexical: query => query, dynamic: 'query' },
|
|
188
|
+
orderBy_set: { lexical: query => query.$next, dynamic: 'query' }, // to outer SELECT (from UNION)
|
|
50
189
|
// default: { lexical: query => query, dynamic: 'source' }
|
|
51
190
|
}
|
|
52
191
|
|
|
@@ -60,25 +199,24 @@ function justDollar() {
|
|
|
60
199
|
function csnRefs( csn ) {
|
|
61
200
|
const cache = new WeakMap();
|
|
62
201
|
|
|
63
|
-
//
|
|
64
|
-
resolveRef.
|
|
65
|
-
return cached(
|
|
202
|
+
// Functions which set the new `baseEnv`:
|
|
203
|
+
resolveRef.expandInline = function resolve_expandInline( ref, ...args ) {
|
|
204
|
+
return cached( ref, '_env', () => navigationEnv( resolveRef( ref, ...args ).art ) );
|
|
66
205
|
}
|
|
67
|
-
resolveRef.
|
|
68
|
-
return cached(
|
|
69
|
-
resolveRef(
|
|
70
|
-
return getCache(
|
|
206
|
+
resolveRef.ref_where = function resolve_ref_where( pathItem, baseRef, ...args ) {
|
|
207
|
+
return cached( pathItem, '_env', () => {
|
|
208
|
+
resolveRef( baseRef, ...args ); // sets _env cache for non-string ref items
|
|
209
|
+
return getCache( pathItem, '_env' );
|
|
71
210
|
} );
|
|
72
211
|
}
|
|
73
212
|
return {
|
|
74
|
-
effectiveType, artifactRef, inspectRef, queryOrMain,
|
|
213
|
+
effectiveType, artifactRef, getOrigin, inspectRef, queryOrMain,
|
|
75
214
|
__getCache_forEnrichCsnDebugging: obj => cache.get( obj ),
|
|
76
215
|
};
|
|
77
216
|
|
|
78
217
|
/**
|
|
79
218
|
* Return the type relevant for name resolution, i.e. the object which has a
|
|
80
|
-
* `target`, `elements`, `enum` property, or no `type` property.
|
|
81
|
-
* confusion with the "base type", we do not use the term "final type".
|
|
219
|
+
* `target`, `elements`, `enum` property, or no `type` property.
|
|
82
220
|
* (This function could be simplified if we would use JS prototypes for type refs.)
|
|
83
221
|
*
|
|
84
222
|
* @param {CSN.ArtifactWithRefs} art
|
|
@@ -87,16 +225,19 @@ function csnRefs( csn ) {
|
|
|
87
225
|
const cachedType = getCache( art, '_effectiveType' );
|
|
88
226
|
if (cachedType !== undefined)
|
|
89
227
|
return cachedType;
|
|
90
|
-
else if (!art.type || art.elements || art.target || art.targetAspect || art.enum)
|
|
91
|
-
return setCache( art, '_effectiveType', art );
|
|
92
228
|
|
|
93
229
|
const chain = [];
|
|
94
|
-
|
|
95
|
-
|
|
230
|
+
let origin;
|
|
231
|
+
while (getCache( art, '_effectiveType' ) === undefined &&
|
|
232
|
+
(origin = cached( art, '_origin', getOriginRaw )) &&
|
|
233
|
+
!art.elements && !art.target && !art.targetAspect && !art.enum && !art.items) {
|
|
96
234
|
chain.push( art );
|
|
97
235
|
setCache( art, '_effectiveType', 0 ); // initial setting in case of cycles
|
|
98
|
-
art =
|
|
236
|
+
art = origin;
|
|
99
237
|
}
|
|
238
|
+
if (!chain.length)
|
|
239
|
+
return setCache( art, '_effectiveType', art );
|
|
240
|
+
|
|
100
241
|
if (getCache( art, '_effectiveType' ) === 0)
|
|
101
242
|
throw new Error( 'Circular type reference');
|
|
102
243
|
const type = getCache( art, '_effectiveType' ) || art;
|
|
@@ -112,10 +253,16 @@ function csnRefs( csn ) {
|
|
|
112
253
|
// here, we do not care whether it is semantically ok to navigate into sub
|
|
113
254
|
// elements of array items (that is the task of the core compiler /
|
|
114
255
|
// semantic check)
|
|
115
|
-
while (type.items)
|
|
116
|
-
type
|
|
256
|
+
while (type.items) {
|
|
257
|
+
cached( type, '$origin', _a => setImplicitOrigin( type, origin ) );
|
|
258
|
+
type = effectiveType( type.items );
|
|
259
|
+
}
|
|
117
260
|
// cannot navigate along targetAspect!
|
|
118
|
-
|
|
261
|
+
const env = (type.target) ? csn.definitions[type.target] : type;
|
|
262
|
+
const origin = cached( env, '_origin', getOriginRaw );
|
|
263
|
+
if (origin && origin !== BUILTIN_TYPE)
|
|
264
|
+
cached( env, '$origin', _a => setImplicitOrigin( env, origin ) );
|
|
265
|
+
return env;
|
|
119
266
|
}
|
|
120
267
|
|
|
121
268
|
/**
|
|
@@ -140,11 +287,104 @@ function csnRefs( csn ) {
|
|
|
140
287
|
function artifactPathRef( ref ) {
|
|
141
288
|
const [ head, ...tail ] = ref.ref;
|
|
142
289
|
let art = csn.definitions[pathId( head )];
|
|
143
|
-
for (const elem of tail)
|
|
144
|
-
|
|
290
|
+
for (const elem of tail) {
|
|
291
|
+
const env = navigationEnv( art );
|
|
292
|
+
art = env.elements[pathId( elem )];
|
|
293
|
+
}
|
|
145
294
|
return art;
|
|
146
295
|
}
|
|
147
296
|
|
|
297
|
+
function getOrigin( art, alsoType ) {
|
|
298
|
+
const origin = cached( art, '_origin', getOriginRaw );
|
|
299
|
+
if (origin && origin !== BUILTIN_TYPE)
|
|
300
|
+
cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
|
|
301
|
+
return art.type && !alsoType ? undefined : origin;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function getOriginRaw( art ) {
|
|
305
|
+
if (art.type) // TODO: make robust against "linked" = only direct
|
|
306
|
+
return artifactRef( art.type, BUILTIN_TYPE );
|
|
307
|
+
if (!art.$origin) // implicit $origin should have been set
|
|
308
|
+
return null;
|
|
309
|
+
// art.$origin must not be a string here - shortened refs should already
|
|
310
|
+
// have been used to set the _origin cache
|
|
311
|
+
if (!Array.isArray( art.$origin )) // anonymous prototype in $origin
|
|
312
|
+
return cached( art.$origin, '_origin', getOriginRaw );
|
|
313
|
+
const [ head, ...tail ] = art.$origin;
|
|
314
|
+
let origin = csn.definitions[head];
|
|
315
|
+
// allow shorter $origin ref for actions/functions, just using a string:
|
|
316
|
+
let isAction = art.kind === 'action' || art.kind === 'function';
|
|
317
|
+
for (const elem of tail) {
|
|
318
|
+
origin = originNavigation( origin, elem, isAction );
|
|
319
|
+
isAction = false;
|
|
320
|
+
}
|
|
321
|
+
return origin;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function originNavigation( art, elem, isAction ) {
|
|
325
|
+
if (typeof elem !== 'string') {
|
|
326
|
+
if (elem.action)
|
|
327
|
+
return art.actions[elem.action];
|
|
328
|
+
if (elem.param)
|
|
329
|
+
return art.params[elem.param];
|
|
330
|
+
if (elem.returns)
|
|
331
|
+
return art.returns;
|
|
332
|
+
}
|
|
333
|
+
if (isAction)
|
|
334
|
+
return art.actions[elem];
|
|
335
|
+
// TODO: if we use effectiveType(), we might have more implicit prototypes
|
|
336
|
+
// we might then need a function like effectiveArtifact,
|
|
337
|
+
// which cares about actions/params
|
|
338
|
+
// let origin = cached( art, '_origin', getOriginRaw );
|
|
339
|
+
// while (art.items) {
|
|
340
|
+
// cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
|
|
341
|
+
// art = art.items;
|
|
342
|
+
// origin = cached( art, '_origin', getOriginRaw );
|
|
343
|
+
// }
|
|
344
|
+
// if (origin)
|
|
345
|
+
// cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
|
|
346
|
+
return (art.elements || art.enum || (art.targetAspect || art.target).elements)[elem];
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// From the current CSN object, set implicit origin for the next navigation step
|
|
350
|
+
// Currently (TODO: ?) `elements` only, i.e. what is needed for name resolution.
|
|
351
|
+
function setImplicitOrigin( art, origin ) {
|
|
352
|
+
setMembersImplicit( art.actions, origin.actions );
|
|
353
|
+
setMembersImplicit( art.params, origin.params );
|
|
354
|
+
if (art.returns) {
|
|
355
|
+
art = art.returns;
|
|
356
|
+
if (art.type || typeof art.$origin === 'object') // null, […], {…}
|
|
357
|
+
return true; // not implicit or shortened
|
|
358
|
+
origin = effectiveType( origin.returns );
|
|
359
|
+
setCache( art, '_origin', origin );
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
while (art.items) {
|
|
363
|
+
art = art.items;
|
|
364
|
+
if (art.type || typeof art.$origin === 'object') // null, […], {…}
|
|
365
|
+
return true; // not implicit or shortened
|
|
366
|
+
origin = effectiveType( origin.items );
|
|
367
|
+
setCache( art, '_origin', origin );
|
|
368
|
+
}
|
|
369
|
+
setMembersImplicit( art.elements, origin.elements );
|
|
370
|
+
// The enum base type is _not_ where we find the origins of the enum symbols.
|
|
371
|
+
// A derived type of an enum type with individual annotations on a symbol
|
|
372
|
+
// has both 'type' and '$origin'.
|
|
373
|
+
if (!art.type || art.$origin)
|
|
374
|
+
setMembersImplicit( art.enum, origin.enum );
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function setMembersImplicit( members, originMembers ) {
|
|
379
|
+
if (!members)
|
|
380
|
+
return;
|
|
381
|
+
for (const name in members) {
|
|
382
|
+
const elem = members[name];
|
|
383
|
+
if (!elem.type && typeof elem.$origin !== 'object') // undefined or string
|
|
384
|
+
setCache( elem, '_origin', originMembers[elem.$origin || name] || false );
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
148
388
|
/**
|
|
149
389
|
* Return the entity we select from
|
|
150
390
|
*
|
|
@@ -158,100 +398,114 @@ function csnRefs( csn ) {
|
|
|
158
398
|
/**
|
|
159
399
|
* @param {CSN.Path} csnPath
|
|
160
400
|
*
|
|
401
|
+
* - return value `art`: the “resulting” CSN of the reference
|
|
402
|
+
*
|
|
403
|
+
* - return value `links`: array of { art, env } in length of ref.path where
|
|
404
|
+
* art = the definition or element reached by the ref path so far
|
|
405
|
+
* env = the “navigation environment” provided by `art`
|
|
406
|
+
* (not set for last item, except for `from` reference or with filter)
|
|
407
|
+
*
|
|
161
408
|
* - return value `scope`
|
|
162
409
|
* global: first item is name of definition
|
|
163
410
|
* param: first item is parameter of definition (with param: true)
|
|
164
411
|
* parent: first item is elem of parent (definition or outer elem)
|
|
165
412
|
* target: first item is elem in target (for keys of assocs)
|
|
166
413
|
* $magic: magic variable (path starts with $magic, see also $self)
|
|
414
|
+
* $self: first item is $self or $projection
|
|
167
415
|
* // now values only in queries:
|
|
168
416
|
* mixin: first item is mixin
|
|
169
417
|
* alias: first item is table alias
|
|
170
|
-
*
|
|
171
|
-
* source: first item is element in a query source
|
|
418
|
+
* source: first item is element in a source of the current query
|
|
172
419
|
* query: first item is element of current query
|
|
173
420
|
* ref-target: first item is element of target of outer ref item
|
|
174
421
|
* (used for filter condition)
|
|
175
|
-
* expand: ref is "path continuation" of
|
|
176
|
-
* inline: ref is "path continuation" of
|
|
422
|
+
* expand: ref is "path continuation" of a ref with EXPAND
|
|
423
|
+
* inline: ref is "path continuation" of a ref with INLINE
|
|
424
|
+
*
|
|
425
|
+
* - return value `$env` is set with certain values of `scope`:
|
|
426
|
+
* with 'alias': the query number _n_ (the _n_th SELECT)
|
|
427
|
+
* with 'source': the table alias name for the source entity
|
|
177
428
|
*/
|
|
178
429
|
function inspectRef( csnPath ) {
|
|
179
430
|
return analyseCsnPath( csnPath, csn, resolveRef );
|
|
180
431
|
}
|
|
181
432
|
|
|
182
|
-
function resolveRef(
|
|
183
|
-
const path = (typeof
|
|
433
|
+
function resolveRef( ref, refCtx, main, query, parent, baseEnv ) {
|
|
434
|
+
const path = (typeof ref === 'string') ? [ ref ] : ref.ref;
|
|
184
435
|
if (!Array.isArray( path ))
|
|
185
|
-
throw new Error( '
|
|
436
|
+
throw new Error( 'References must look like {ref:[...]}' );
|
|
186
437
|
|
|
187
438
|
const head = pathId( path[0] );
|
|
188
|
-
if (
|
|
189
|
-
return
|
|
439
|
+
if (ref.param)
|
|
440
|
+
return resolvePath( path, main.params[head], 'param' );
|
|
190
441
|
|
|
191
|
-
const
|
|
192
|
-
if (
|
|
193
|
-
return
|
|
442
|
+
const semantics = referenceSemantics[refCtx] || {};
|
|
443
|
+
if (semantics.dynamic === 'global' || ref.global)
|
|
444
|
+
return resolvePath( path, csn.definitions[head], 'global', refCtx === 'from' );
|
|
194
445
|
|
|
446
|
+
const origin = cached( main, '_origin', getOriginRaw )
|
|
447
|
+
if (origin)
|
|
448
|
+
cached( main, '$origin', _a => setImplicitOrigin( main, origin ) );
|
|
195
449
|
cached( main, '$queries', allQueries );
|
|
196
|
-
let
|
|
450
|
+
let qcache = query && cache.get( query.projection || query );
|
|
197
451
|
// BACKEND ISSUE: you cannot call csnRefs(), inspect some refs, change the
|
|
198
452
|
// CSN and again inspect some refs without calling csnRefs() before!
|
|
199
453
|
// WORKAROUND: if no cached query, a backend has changed the CSN - re-eval cache
|
|
200
|
-
if (query && !
|
|
454
|
+
if (query && !qcache) {
|
|
201
455
|
setCache( main, '$queries', allQueries( main ) );
|
|
202
|
-
|
|
456
|
+
qcache = cache.get( query.projection || query );
|
|
203
457
|
}
|
|
204
458
|
// first the lexical scopes (due to query hierarchy) and $magic: ---------
|
|
205
|
-
if (
|
|
206
|
-
const tryAlias = path.length > 1 ||
|
|
207
|
-
let
|
|
208
|
-
while (
|
|
209
|
-
const alias = tryAlias &&
|
|
459
|
+
if (semantics.lexical !== false) {
|
|
460
|
+
const tryAlias = path.length > 1 || ref.expand || ref.inline;
|
|
461
|
+
let cache = qcache && (semantics.lexical ? semantics.lexical( qcache ) : qcache);
|
|
462
|
+
while (cache) {
|
|
463
|
+
const alias = tryAlias && cache.$aliases[head];
|
|
210
464
|
if (alias)
|
|
211
|
-
return
|
|
212
|
-
const mixin =
|
|
213
|
-
if (mixin && {}.hasOwnProperty.call(
|
|
214
|
-
return
|
|
215
|
-
|
|
465
|
+
return resolvePath( path, alias._select || alias._ref, 'alias', cache.$queryNumber );
|
|
466
|
+
const mixin = cache._select.mixin && cache._select.mixin[head];
|
|
467
|
+
if (mixin && {}.hasOwnProperty.call( cache._select.mixin, head ))
|
|
468
|
+
return resolvePath( path, mixin, 'mixin', cache.$queryNumber );
|
|
469
|
+
cache = cache.$next;
|
|
216
470
|
}
|
|
217
471
|
if (head.charAt(0) === '$') {
|
|
218
472
|
if (head !== '$self' && head !== '$projection')
|
|
219
473
|
return { scope: '$magic' };
|
|
220
|
-
const self =
|
|
221
|
-
return
|
|
474
|
+
const self = qcache && qcache.$queryNumber > 1 ? qcache._select : main;
|
|
475
|
+
return resolvePath( path, self, '$self' );
|
|
222
476
|
}
|
|
223
477
|
}
|
|
224
478
|
// now the dynamic environment: ------------------------------------------
|
|
225
|
-
if (
|
|
479
|
+
if (semantics.dynamic === 'target') { // ref in keys
|
|
226
480
|
// not selecting the corresponding element for a select column works,
|
|
227
481
|
// because explicit keys can only be provided with explicit redirection
|
|
228
482
|
// target
|
|
229
483
|
const target = csn.definitions[parent.target || parent.cast.target];
|
|
230
|
-
return
|
|
484
|
+
return resolvePath( path, target.elements[head], 'target' );
|
|
231
485
|
}
|
|
232
486
|
if (baseEnv) // ref-target (filter condition), expand, inline
|
|
233
|
-
return
|
|
487
|
+
return resolvePath( path, baseEnv.elements[head], semantics.dynamic );
|
|
234
488
|
if (!query) // outside queries - TODO: items?
|
|
235
|
-
return
|
|
489
|
+
return resolvePath( path, parent.elements[head], 'parent' );
|
|
236
490
|
|
|
237
|
-
if (
|
|
491
|
+
if (semantics.dynamic === 'query')
|
|
238
492
|
// TODO: for ON condition in expand, would need to use cached _element
|
|
239
|
-
return
|
|
240
|
-
for (const name in
|
|
241
|
-
const found =
|
|
493
|
+
return resolvePath( path, qcache.elements[head], 'query' );
|
|
494
|
+
for (const name in qcache.$aliases) {
|
|
495
|
+
const found = qcache.$aliases[name].elements[head];
|
|
242
496
|
if (found)
|
|
243
|
-
return
|
|
497
|
+
return resolvePath( path, found, 'source', name )
|
|
244
498
|
}
|
|
245
|
-
// console.log(query.SELECT,
|
|
246
|
-
throw new Error ( `Path item ${ 0 }=${ head } refers to nothing,
|
|
499
|
+
// console.log(query.SELECT,qcache,qcache.$next,main)
|
|
500
|
+
throw new Error ( `Path item ${ 0 }=${ head } refers to nothing, refCtx: ${ refCtx }` );
|
|
247
501
|
}
|
|
248
502
|
|
|
249
503
|
/**
|
|
250
504
|
* @param {CSN.Path} path
|
|
251
505
|
* @param {CSN.Artifact} art
|
|
252
|
-
* @param {string
|
|
506
|
+
* @param {string} [scope]
|
|
253
507
|
*/
|
|
254
|
-
function
|
|
508
|
+
function resolvePath( path, art, scope, extraInfo ) {
|
|
255
509
|
/** @type {{idx, art?, env?}[]} */
|
|
256
510
|
const links = path.map( (_v, idx) => ({ idx }) );
|
|
257
511
|
// TODO: backends should be changed to enable uncommenting:
|
|
@@ -302,59 +556,64 @@ function csnRefs( csn ) {
|
|
|
302
556
|
if (query.ref) { // ref in from
|
|
303
557
|
// console.log('SQ:',query,cache.get(query))
|
|
304
558
|
const as = query.as || implicitAs( query.ref );
|
|
305
|
-
|
|
559
|
+
const _ref = fromRef( query );
|
|
560
|
+
getCache( fromSelect, '$aliases' )[as] = { _ref, elements: _ref.elements };
|
|
306
561
|
}
|
|
307
562
|
else {
|
|
308
|
-
const
|
|
563
|
+
const qcache = getQueryCache( parentQuery );
|
|
564
|
+
if (query !== main)
|
|
565
|
+
cache.set( query, qcache );
|
|
566
|
+
|
|
309
567
|
if (fromSelect)
|
|
310
|
-
getCache( fromSelect, 'aliases' )[query.as] =
|
|
568
|
+
getCache( fromSelect, '$aliases' )[query.as] = qcache;
|
|
311
569
|
const select = query.SELECT || query.projection;
|
|
312
570
|
if (select) {
|
|
313
|
-
cache.set( select,
|
|
314
|
-
|
|
315
|
-
all.push(
|
|
571
|
+
cache.set( select, qcache ); // query and query.SELECT have the same cache qcache
|
|
572
|
+
qcache._select = select;
|
|
573
|
+
all.push( qcache );
|
|
316
574
|
}
|
|
317
575
|
}
|
|
318
576
|
} );
|
|
319
|
-
all.forEach( function initElements(
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
const columns =
|
|
323
|
-
if (
|
|
324
|
-
columns.map( c => initColumnElement( c,
|
|
577
|
+
all.forEach( function initElements( qcache, index ) {
|
|
578
|
+
qcache.$queryNumber = index + 1;
|
|
579
|
+
qcache.elements = (index ? qcache._select : main).elements;
|
|
580
|
+
const columns = qcache._select.columns;
|
|
581
|
+
if (qcache.elements && columns)
|
|
582
|
+
columns.map( c => initColumnElement( c, qcache ) );
|
|
325
583
|
} );
|
|
326
584
|
return all;
|
|
327
585
|
}
|
|
328
586
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
587
|
+
/**
|
|
588
|
+
* Return the cache object for a new query.
|
|
589
|
+
* Might re-use cache object with the `parentQuery`, or use `parentQuery`
|
|
590
|
+
* for link to next lexical environment.
|
|
591
|
+
*/
|
|
592
|
+
|
|
593
|
+
function getQueryCache( parentQuery ) {
|
|
594
|
+
if (!parentQuery)
|
|
595
|
+
return { $aliases: Object.create(null) };
|
|
596
|
+
const pcache = cache.get( parentQuery );
|
|
597
|
+
if (!parentQuery.SET) // SELECT / projection: real sub query
|
|
598
|
+
return { $aliases: Object.create(null), $next: pcache };
|
|
599
|
+
// the parent query is a SET: that is not a sub query
|
|
600
|
+
// (works, as no sub queries are allowed in ORDER BY)
|
|
601
|
+
return (!pcache._select) // no leading query yet
|
|
602
|
+
? pcache // share cache with parent query
|
|
603
|
+
: { $aliases: Object.create(null), $next: pcache.$next };
|
|
345
604
|
}
|
|
346
605
|
|
|
347
|
-
function initColumnElement( col,
|
|
606
|
+
function initColumnElement( col, parentElementOrQueryCache ) {
|
|
348
607
|
if (col === '*')
|
|
349
608
|
return;
|
|
350
609
|
if (col.inline) {
|
|
351
|
-
col.inline.map( c => initColumnElement( c,
|
|
610
|
+
col.inline.map( c => initColumnElement( c, parentElementOrQueryCache ) );
|
|
352
611
|
return;
|
|
353
612
|
}
|
|
354
613
|
setCache( col, '_parent', // not set for query (has property _select)
|
|
355
|
-
!
|
|
614
|
+
!parentElementOrQueryCache._select && parentElementOrQueryCache );
|
|
356
615
|
const as = col.as || col.func || implicitAs( col.ref );
|
|
357
|
-
let type =
|
|
616
|
+
let type = parentElementOrQueryCache;
|
|
358
617
|
while (type.items)
|
|
359
618
|
type = type.items;
|
|
360
619
|
const elem = setCache( col, '_element', type.elements[as] );
|
|
@@ -398,6 +657,7 @@ function csnRefs( csn ) {
|
|
|
398
657
|
|
|
399
658
|
// Return value of a query SELECT for the query node, or the main artifact,
|
|
400
659
|
// i.e. a value with an `elements` property.
|
|
660
|
+
// TODO: only used in forHanaNew - move somewhere else
|
|
401
661
|
/**
|
|
402
662
|
* @param {CSN.Query} query node (object with SET or SELECT property)
|
|
403
663
|
* @param {CSN.Definition} main
|
|
@@ -423,15 +683,16 @@ function queryOrMain( query, main ) {
|
|
|
423
683
|
*
|
|
424
684
|
* @param {CSN.Query} query
|
|
425
685
|
* @param {CSN.QuerySelect} fromSelect
|
|
686
|
+
* @param {CSN.Query} parentQuery
|
|
426
687
|
* @param {(query: CSN.Query&CSN.QueryFrom, select: CSN.QuerySelectEnriched) => void} callback
|
|
427
688
|
*/
|
|
428
689
|
function traverseQuery( query, fromSelect, parentQuery, callback ) {
|
|
429
|
-
|
|
690
|
+
const select = query.SELECT || query.projection;
|
|
691
|
+
if (select) {
|
|
430
692
|
callback( query, fromSelect, parentQuery );
|
|
431
|
-
const select = query.SELECT || query.projection;
|
|
432
693
|
traverseFrom( select.from, select, parentQuery, callback );
|
|
433
694
|
for (const prop of [ 'columns', 'where', 'having' ]) {
|
|
434
|
-
// all properties which
|
|
695
|
+
// all properties which can have sub queries (`join-on` also can)
|
|
435
696
|
const expr = select[prop];
|
|
436
697
|
if (expr)
|
|
437
698
|
expr.forEach( q => traverseExpr( q, query, callback ) );
|
|
@@ -440,19 +701,16 @@ function traverseQuery( query, fromSelect, parentQuery, callback ) {
|
|
|
440
701
|
else if (query.SET) {
|
|
441
702
|
callback( query, fromSelect, parentQuery );
|
|
442
703
|
const { args } = query.SET;
|
|
443
|
-
for (const q of args || [])
|
|
444
|
-
|
|
445
|
-
traverseQuery( q, fromSelect, query, callback );
|
|
446
|
-
else
|
|
447
|
-
traverseQuery( q, null, query, callback );
|
|
448
|
-
}
|
|
704
|
+
for (const q of args || [])
|
|
705
|
+
traverseQuery( q, null, query, callback );
|
|
449
706
|
}
|
|
450
707
|
}
|
|
451
708
|
|
|
452
709
|
/**
|
|
453
710
|
* @param {CSN.QueryFrom} from
|
|
454
|
-
* @param {CSN.QuerySelect}
|
|
455
|
-
* @param {
|
|
711
|
+
* @param {CSN.QuerySelect} fromSelect
|
|
712
|
+
* @param {CSN.Query} parentQuery
|
|
713
|
+
* @param {(from: CSN.QueryFrom, select: CSN.QuerySelect, parentQuery: CSN.Query) => void} callback
|
|
456
714
|
*/
|
|
457
715
|
function traverseFrom( from, fromSelect, parentQuery, callback ) {
|
|
458
716
|
if (from.ref) {
|
|
@@ -502,12 +760,13 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
502
760
|
let obj = csn;
|
|
503
761
|
let parent = null;
|
|
504
762
|
let query = null;
|
|
505
|
-
let
|
|
763
|
+
let refCtx = null;
|
|
506
764
|
let art = null;
|
|
507
765
|
/** @type {boolean|string|number} */
|
|
508
766
|
let isName = false;
|
|
509
|
-
let
|
|
767
|
+
let baseRef = null;
|
|
510
768
|
let baseEnv = null;
|
|
769
|
+
let main = csn.definitions[csnPath[1]];
|
|
511
770
|
|
|
512
771
|
for (let index = 0; index < csnPath.length; index++) {
|
|
513
772
|
const prop = csnPath[index];
|
|
@@ -520,8 +779,12 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
520
779
|
isName = false;
|
|
521
780
|
}
|
|
522
781
|
else if (artifactProperties.includes( String(prop) )) {
|
|
782
|
+
if (refCtx === 'target' || refCtx === 'targetAspect') { // with 'elements'
|
|
783
|
+
main = art = obj; // $self refers to the anonymous aspect
|
|
784
|
+
parent = null;
|
|
785
|
+
}
|
|
523
786
|
isName = prop;
|
|
524
|
-
|
|
787
|
+
refCtx = prop;
|
|
525
788
|
}
|
|
526
789
|
else if (prop === 'items' || prop === 'returns') {
|
|
527
790
|
art = obj[prop];
|
|
@@ -532,41 +795,42 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
532
795
|
else if (prop === 'SELECT' || prop === 'SET' || prop === 'projection') {
|
|
533
796
|
query = obj;
|
|
534
797
|
parent = null;
|
|
535
|
-
|
|
798
|
+
baseEnv = null;
|
|
799
|
+
refCtx = prop;
|
|
536
800
|
}
|
|
537
|
-
else if (prop === 'where' &&
|
|
801
|
+
else if (prop === 'where' && refCtx === 'ref') {
|
|
538
802
|
if (resolve)
|
|
539
|
-
baseEnv = resolve.
|
|
540
|
-
|
|
541
|
-
|
|
803
|
+
baseEnv = resolve.ref_where( obj, baseRef, refCtx, csn.definitions[csnPath[1]],
|
|
804
|
+
query, parent, baseEnv );
|
|
805
|
+
refCtx = 'ref_where';
|
|
542
806
|
}
|
|
543
807
|
else if (prop === 'expand' || prop === 'inline') {
|
|
544
808
|
if (obj.ref) {
|
|
545
809
|
if (resolve)
|
|
546
|
-
baseEnv = resolve.
|
|
547
|
-
|
|
548
|
-
|
|
810
|
+
baseEnv = resolve.expandInline( obj, refCtx, csn.definitions[csnPath[1]],
|
|
811
|
+
query, parent, baseEnv );
|
|
812
|
+
refCtx = prop;
|
|
549
813
|
}
|
|
550
814
|
if (prop === 'expand')
|
|
551
815
|
isName = prop;
|
|
552
816
|
}
|
|
553
817
|
else if (prop === 'on') {
|
|
554
|
-
if (
|
|
555
|
-
|
|
556
|
-
else if (
|
|
557
|
-
|
|
818
|
+
if (refCtx === 'from')
|
|
819
|
+
refCtx = 'join_on';
|
|
820
|
+
else if (refCtx === 'mixin')
|
|
821
|
+
refCtx = 'mixin_on';
|
|
558
822
|
else
|
|
559
|
-
|
|
823
|
+
refCtx = 'on'; // will use query elements with REDIRECTED TO
|
|
560
824
|
}
|
|
561
825
|
else if (prop === 'ref') {
|
|
562
|
-
|
|
563
|
-
|
|
826
|
+
baseRef = obj; // needs to be inspected for filter conditions
|
|
827
|
+
refCtx = prop;
|
|
564
828
|
}
|
|
565
829
|
else if (prop === 'orderBy') {
|
|
566
|
-
|
|
830
|
+
refCtx = (query.SET ? 'orderBy_set' : 'orderBy');
|
|
567
831
|
}
|
|
568
832
|
else if (prop !== 'xpr') {
|
|
569
|
-
|
|
833
|
+
refCtx = prop;
|
|
570
834
|
}
|
|
571
835
|
|
|
572
836
|
obj = obj[prop];
|
|
@@ -574,10 +838,10 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
574
838
|
// For the semantic location, use current object as best guess
|
|
575
839
|
break;
|
|
576
840
|
}
|
|
577
|
-
// console.log( 'CPATH:', csnPath,
|
|
841
|
+
// console.log( 'CPATH:', csnPath, refCtx, obj, parent.$location );
|
|
578
842
|
if (!resolve)
|
|
579
843
|
return { query }; // for constructSemanticLocationFromCsnPath
|
|
580
|
-
return resolve( obj,
|
|
844
|
+
return resolve( obj, refCtx, main, query, parent, baseEnv );
|
|
581
845
|
}
|
|
582
846
|
|
|
583
847
|
module.exports = {
|