@sap/cds-compiler 2.12.0 → 2.13.6
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 +110 -15
- package/bin/cdsc.js +13 -13
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_BETA.md +13 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +28 -63
- package/lib/api/options.js +3 -3
- package/lib/api/validate.js +0 -5
- package/lib/backends.js +15 -23
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +7 -17
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +25 -4
- package/lib/base/messages.js +16 -26
- package/lib/base/model.js +2 -63
- package/lib/base/optionProcessorHelper.js +158 -123
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +27 -26
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +4 -7
- package/lib/compiler/assert-consistency.js +5 -3
- package/lib/compiler/builtins.js +8 -6
- package/lib/compiler/checks.js +14 -3
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1103 -0
- package/lib/compiler/extend.js +983 -0
- package/lib/compiler/finalize-parse-cdl.js +231 -0
- package/lib/compiler/index.js +32 -13
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1226 -0
- package/lib/compiler/propagator.js +111 -46
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +64 -37
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +197 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +5 -9
- package/lib/edm/annotations/preprocessAnnotations.js +2 -2
- package/lib/edm/csn2edm.js +9 -8
- package/lib/edm/edm.js +11 -12
- package/lib/edm/edmPreprocessor.js +137 -73
- package/lib/edm/edmUtils.js +116 -22
- package/lib/gen/Dictionary.json +10 -3
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +9 -1
- package/lib/gen/language.tokens +86 -83
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +860 -833
- package/lib/gen/languageLexer.tokens +78 -75
- package/lib/gen/languageParser.js +5282 -4265
- package/lib/json/from-csn.js +12 -1
- package/lib/json/to-csn.js +126 -66
- package/lib/language/docCommentParser.js +2 -2
- package/lib/language/genericAntlrParser.js +76 -3
- package/lib/language/language.g4 +297 -130
- package/lib/language/multiLineStringParser.js +5 -5
- package/lib/main.d.ts +468 -59
- package/lib/main.js +35 -9
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +225 -156
- package/lib/model/csnUtils.js +192 -223
- package/lib/model/enrichCsn.js +70 -29
- package/lib/model/revealInternalProperties.js +27 -6
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +17 -12
- package/lib/optionProcessor.js +5 -4
- package/lib/render/manageConstraints.js +35 -32
- package/lib/render/toCdl.js +73 -288
- package/lib/render/toHdbcds.js +25 -23
- package/lib/render/toSql.js +98 -41
- package/lib/render/utils/common.js +5 -10
- package/lib/render/utils/sql.js +4 -3
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +103 -305
- package/lib/transform/db/cdsPersistence.js +2 -2
- package/lib/transform/db/constraints.js +55 -52
- package/lib/transform/db/expansion.js +46 -24
- package/lib/transform/db/flattening.js +553 -102
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/transformExists.js +59 -6
- package/lib/transform/db/views.js +5 -4
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +6 -5
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +67 -183
- package/lib/transform/forOdataNew.js +17 -171
- package/lib/transform/localized.js +34 -19
- package/lib/transform/odata/generateForeignKeyElements.js +1 -1
- package/lib/transform/odata/referenceFlattener.js +95 -89
- package/lib/transform/odata/structureFlattener.js +1 -1
- package/lib/transform/odata/toFinalBaseType.js +86 -12
- package/lib/transform/odata/typesExposure.js +5 -5
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +36 -22
- package/lib/transform/translateAssocsToJoins.js +2 -19
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +170 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/objectUtils.js +30 -0
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2361
- package/lib/compiler/resolver.js +0 -3079
- package/lib/transform/universalCsnEnricher.js +0 -237
package/lib/model/csnRefs.js
CHANGED
|
@@ -181,6 +181,7 @@
|
|
|
181
181
|
|
|
182
182
|
const BUILTIN_TYPE = {};
|
|
183
183
|
const { locationString } = require('../base/location');
|
|
184
|
+
const { ModelError, CompilerAssertion } = require("../base/error");
|
|
184
185
|
|
|
185
186
|
// Properties in which artifact or members are defined - next property in the
|
|
186
187
|
// "csnPath" is the name or index of that property; 'args' (its value can be a
|
|
@@ -191,12 +192,16 @@ const artifactProperties = [ 'elements', 'columns', 'keys', 'mixin', 'enum',
|
|
|
191
192
|
// Mapping the “reference context string” to the reference semantics
|
|
192
193
|
// - lexical: false | Function - determines where to look first for “lexical names”
|
|
193
194
|
// - dynamic: String - describes the dynamic environment (if in query)
|
|
195
|
+
// - assoc: String, with dynamic: 'global' - what to do with assoc steps
|
|
196
|
+
// * 'static': visit elements of anonymous aspect if not last ref item
|
|
197
|
+
// * 'target': always follow target, including last ref item
|
|
198
|
+
// * other (& not provided) = follow target if not last ref item
|
|
194
199
|
const referenceSemantics = {
|
|
195
|
-
type: { lexical: false, dynamic: 'global' },
|
|
196
|
-
includes: { lexical: false, dynamic: 'global' },
|
|
197
|
-
target: { lexical: false, dynamic: 'global' },
|
|
198
|
-
targetAspect: { lexical: false, dynamic: 'global' },
|
|
199
|
-
from: { lexical: false, dynamic: 'global' },
|
|
200
|
+
type: { lexical: false, dynamic: 'global' }, // TODO: assoc: 'static', see Issue #8458
|
|
201
|
+
includes: { lexical: false, dynamic: 'global', assoc: 'static' }, // no elem ref anyway
|
|
202
|
+
target: { lexical: false, dynamic: 'global', assoc: 'static' }, // no elem ref anyway
|
|
203
|
+
targetAspect: { lexical: false, dynamic: 'global', assoc: 'static' },
|
|
204
|
+
from: { lexical: false, dynamic: 'global', assoc: 'target' },
|
|
200
205
|
keys: { lexical: false, dynamic: 'target' },
|
|
201
206
|
keys_origin: { lexical: false, dynamic: 'target' },
|
|
202
207
|
excluding: { lexical: false, dynamic: 'source' },
|
|
@@ -216,10 +221,15 @@ function justDollar() {
|
|
|
216
221
|
|
|
217
222
|
/**
|
|
218
223
|
* @param {CSN.Model} csn
|
|
224
|
+
* @param {boolean|string} [universalReady]
|
|
219
225
|
*/
|
|
220
|
-
function csnRefs( csn ) {
|
|
226
|
+
function csnRefs( csn, universalReady ) {
|
|
221
227
|
const cache = new WeakMap();
|
|
222
|
-
|
|
228
|
+
setCache( BUILTIN_TYPE, '_origin', null );
|
|
229
|
+
if (universalReady === 'init-all') {
|
|
230
|
+
for (const art of Object.values( csn.definitions || {}))
|
|
231
|
+
initDefinition( art );
|
|
232
|
+
}
|
|
223
233
|
// Functions which set the new `baseEnv`:
|
|
224
234
|
resolveRef.expandInline = function resolve_expandInline( ref, ...args ) {
|
|
225
235
|
return cached( ref, '_env', () => navigationEnv( resolveRef( ref, ...args ).art ) );
|
|
@@ -231,7 +241,15 @@ function csnRefs( csn ) {
|
|
|
231
241
|
} );
|
|
232
242
|
}
|
|
233
243
|
return {
|
|
234
|
-
effectiveType,
|
|
244
|
+
effectiveType,
|
|
245
|
+
artifactRef,
|
|
246
|
+
getOrigin,
|
|
247
|
+
inspectRef,
|
|
248
|
+
queryOrMain,
|
|
249
|
+
getColumn: ( elem ) => getCache( elem, '_column' ),
|
|
250
|
+
getElement: ( col ) => getCache( col, '_element' ),
|
|
251
|
+
initDefinition,
|
|
252
|
+
targetAspect,
|
|
235
253
|
__getCache_forEnrichCsnDebugging: obj => cache.get( obj ),
|
|
236
254
|
};
|
|
237
255
|
|
|
@@ -260,7 +278,7 @@ function csnRefs( csn ) {
|
|
|
260
278
|
return setCache( art, '_effectiveType', art );
|
|
261
279
|
|
|
262
280
|
if (getCache( art, '_effectiveType' ) === 0)
|
|
263
|
-
throw new
|
|
281
|
+
throw new ModelError( 'Circular type reference');
|
|
264
282
|
const type = getCache( art, '_effectiveType' ) || art;
|
|
265
283
|
chain.forEach( a => setCache( a, '_effectiveType', type ) );
|
|
266
284
|
return type;
|
|
@@ -269,19 +287,19 @@ function csnRefs( csn ) {
|
|
|
269
287
|
/**
|
|
270
288
|
* @param {CSN.Artifact} art
|
|
271
289
|
*/
|
|
272
|
-
function navigationEnv( art ) {
|
|
290
|
+
function navigationEnv( art, staticAssoc ) {
|
|
273
291
|
let env = effectiveType( art );
|
|
274
|
-
getOrigin( env ); // also set implicit origins
|
|
275
292
|
// here, we do not care whether it is semantically ok to navigate into sub
|
|
276
293
|
// elements of array items (that is the task of the core compiler /
|
|
277
294
|
// semantic check)
|
|
278
295
|
while (env.items)
|
|
279
296
|
env = effectiveType( env.items );
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
297
|
+
const target = (staticAssoc ? targetAspect( env ) : env.target);
|
|
298
|
+
if (typeof target !== 'string')
|
|
299
|
+
return target || env;
|
|
300
|
+
const def = csn.definitions[target];
|
|
301
|
+
initDefinition( def );
|
|
302
|
+
return def;
|
|
285
303
|
}
|
|
286
304
|
|
|
287
305
|
/**
|
|
@@ -293,26 +311,47 @@ function csnRefs( csn ) {
|
|
|
293
311
|
* could not be found.
|
|
294
312
|
*/
|
|
295
313
|
function artifactRef( ref, notFound ) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
314
|
+
// TODO: what about type ref?
|
|
315
|
+
if (typeof ref === 'string') {
|
|
316
|
+
const main = csn.definitions[ref];
|
|
317
|
+
if (main)
|
|
318
|
+
return initDefinition( main );
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
const art = cached( ref, '_ref', artifactPathRef );
|
|
322
|
+
if (art)
|
|
323
|
+
return art;
|
|
324
|
+
}
|
|
325
|
+
if (notFound !== undefined && typeof ref === 'string')
|
|
326
|
+
return notFound; // is only meant for builtins
|
|
327
|
+
// Backend bug workaround, TODO: delete next 2 lines
|
|
301
328
|
if (notFound !== undefined)
|
|
302
329
|
return notFound;
|
|
303
|
-
throw new
|
|
330
|
+
throw new ModelError( 'Undefined reference' );
|
|
304
331
|
}
|
|
305
332
|
|
|
306
333
|
function artifactPathRef( ref ) {
|
|
307
334
|
const [ head, ...tail ] = ref.ref;
|
|
308
335
|
let art = csn.definitions[pathId( head )];
|
|
336
|
+
initDefinition( art );
|
|
309
337
|
for (const elem of tail) {
|
|
310
|
-
const env = navigationEnv( art );
|
|
338
|
+
const env = navigationEnv( art ); // TODO: second argument true, see Issue #8458
|
|
311
339
|
art = env.elements[pathId( elem )];
|
|
312
340
|
}
|
|
313
341
|
return art;
|
|
314
342
|
}
|
|
315
343
|
|
|
344
|
+
function artifactFromRef( ref ) {
|
|
345
|
+
const [ head, ...tail ] = ref.ref;
|
|
346
|
+
let art = csn.definitions[pathId( head )];
|
|
347
|
+
initDefinition( art );
|
|
348
|
+
for (const elem of tail) {
|
|
349
|
+
const env = navigationEnv( art );
|
|
350
|
+
art = env.elements[pathId( elem )];
|
|
351
|
+
}
|
|
352
|
+
return navigationEnv( art );
|
|
353
|
+
}
|
|
354
|
+
|
|
316
355
|
// Return target when resolving references in 'keys'
|
|
317
356
|
function assocTarget( art, refCtx ) {
|
|
318
357
|
// Call contexts:
|
|
@@ -326,119 +365,104 @@ function csnRefs( csn ) {
|
|
|
326
365
|
|| art.$origin && art.$origin.target
|
|
327
366
|
|| art.cast.target;
|
|
328
367
|
const target = csn.definitions[targetName];
|
|
329
|
-
|
|
368
|
+
initDefinition( target );
|
|
330
369
|
return target;
|
|
331
370
|
}
|
|
332
371
|
|
|
333
372
|
function getOrigin( art ) {
|
|
334
|
-
|
|
335
|
-
if (origin && origin !== BUILTIN_TYPE)
|
|
336
|
-
cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
|
|
337
|
-
return origin;
|
|
373
|
+
return cached( art, '_origin', getOriginRaw );
|
|
338
374
|
}
|
|
339
375
|
|
|
340
376
|
function getOriginRaw( art ) {
|
|
341
377
|
if (art.type) // TODO: make robust against "linked" = only direct
|
|
342
378
|
return artifactRef( art.type, BUILTIN_TYPE );
|
|
343
|
-
if (
|
|
344
|
-
return
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
let isAction = art.kind === 'action' || art.kind === 'function';
|
|
354
|
-
for (const elem of tail) {
|
|
355
|
-
origin = originNavigation( origin, elem, isAction );
|
|
356
|
-
isAction = false;
|
|
379
|
+
if (typeof art.$origin === 'object') // null, […], {…}
|
|
380
|
+
return getOriginExplicit( art.$origin );
|
|
381
|
+
|
|
382
|
+
const parent = getCache( art, '_parent' );
|
|
383
|
+
if (parent === undefined && universalReady) {
|
|
384
|
+
const { $location } = art;
|
|
385
|
+
const location = $location &&
|
|
386
|
+
(typeof $location === 'string' ? $location : locationString( $location ));
|
|
387
|
+
const def = Object.keys( art ).join('+') + (location ? ':' + location : '');
|
|
388
|
+
throw new Error( `Inspecting non-initialized CSN node {${def}}` );
|
|
357
389
|
}
|
|
358
|
-
|
|
390
|
+
const step = getCache( art, '$origin$step' );
|
|
391
|
+
if (!step)
|
|
392
|
+
return null;
|
|
393
|
+
const origin = cached( parent, '_origin', getOriginRaw );
|
|
394
|
+
return originNavigation( origin, step );
|
|
359
395
|
}
|
|
360
396
|
|
|
361
|
-
function
|
|
362
|
-
if (
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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"]
|
|
385
|
-
art = art.returns;
|
|
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) {
|
|
409
|
-
art = art.items;
|
|
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;
|
|
397
|
+
function getOriginExplicit( $origin ) { // null, […], {…}
|
|
398
|
+
if (!$origin)
|
|
399
|
+
return null;
|
|
400
|
+
if (!Array.isArray( $origin )) // anonymous prototype in $origin
|
|
401
|
+
return getOriginExplicit( $origin.$origin );
|
|
402
|
+
const [ head, ...tail ] = $origin;
|
|
403
|
+
const main = csn.definitions[head];
|
|
404
|
+
initDefinition( main );
|
|
405
|
+
return tail.reduce( originNavigation, main );
|
|
422
406
|
}
|
|
423
407
|
|
|
424
|
-
function
|
|
425
|
-
if (!
|
|
426
|
-
return;
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
408
|
+
function originNavigation( art, step ) {
|
|
409
|
+
if (!step)
|
|
410
|
+
return null;
|
|
411
|
+
if (!effectiveType( art ))
|
|
412
|
+
throw new TypeError( 'Cyclic type definition' );
|
|
413
|
+
if (typeof step === 'string')
|
|
414
|
+
return navigationEnv( art, true ).elements[step];
|
|
415
|
+
|
|
416
|
+
if (step.action)
|
|
417
|
+
return effectiveArtFor( art, 'actions' )[step.action];
|
|
418
|
+
if (step.param)
|
|
419
|
+
return effectiveArtFor( art, 'params' )[step.param];
|
|
420
|
+
if (step.returns)
|
|
421
|
+
return effectiveArtFor( art, 'returns' );
|
|
422
|
+
if (step.enum)
|
|
423
|
+
return navigationEnv( art, true ).enum[step.enum];
|
|
424
|
+
if (step.items)
|
|
425
|
+
return effectiveType( art ).items;
|
|
426
|
+
if (step.target)
|
|
427
|
+
return targetAspect( effectiveType( art ) );
|
|
428
|
+
throw Error( `Illegal navigation step ${ Object.keys(step)[0]}` );
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function effectiveArtFor( art, property ) {
|
|
432
|
+
while (!art[property])
|
|
433
|
+
art = getOrigin( art );
|
|
434
|
+
return art[property];
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function initDefinition( main ) {
|
|
438
|
+
// TODO: some --test-mode check that the argument is in ‹csn›.definitions ?
|
|
439
|
+
if (getCache( main, '$queries' ) !== undefined) // already computed
|
|
440
|
+
return main;
|
|
441
|
+
traverseDef( main, null, null, null, initNode );
|
|
442
|
+
const queries = cached( main, '$queries', allQueries );
|
|
443
|
+
for (const qcache of queries || []) {
|
|
444
|
+
const { _select } = qcache;
|
|
445
|
+
traverseType( _select, main, null, null, initNode ); // also inits elements
|
|
446
|
+
if (_select.mixin) {
|
|
447
|
+
for (const n of Object.keys( _select.mixin ))
|
|
448
|
+
setCache( _select.mixin[n], '_parent', _select ); // relevant initNode() part
|
|
449
|
+
}
|
|
431
450
|
}
|
|
451
|
+
return main;
|
|
432
452
|
}
|
|
433
453
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
454
|
+
function initNode( art, parent, kind, name ) {
|
|
455
|
+
setCache( art, '_parent', parent );
|
|
456
|
+
if (art.type || !kind || kind === 'target') // with type, top-level, query or mixin
|
|
457
|
+
return;
|
|
458
|
+
const { $origin } = art;
|
|
459
|
+
if (typeof $origin === 'object') // null, […], {…}
|
|
460
|
+
return;
|
|
461
|
+
const step = $origin || name;
|
|
462
|
+
if (parent.$origin ||
|
|
463
|
+
parent.type && kind !== 'enum' && parent.$origin !== null ||
|
|
464
|
+
getCache( parent, '$origin$step' ))
|
|
465
|
+
setCache( art, '$origin$step', (kind === 'element' ? step : { [kind]: step }) );
|
|
442
466
|
}
|
|
443
467
|
|
|
444
468
|
/**
|
|
@@ -479,41 +503,35 @@ function csnRefs( csn ) {
|
|
|
479
503
|
function resolveRef( ref, refCtx, main, query, parent, baseEnv ) {
|
|
480
504
|
const path = (typeof ref === 'string') ? [ ref ] : ref.ref;
|
|
481
505
|
if (!Array.isArray( path ))
|
|
482
|
-
throw new
|
|
506
|
+
throw new ModelError( 'References must look like {ref:[...]}' );
|
|
483
507
|
|
|
484
508
|
const head = pathId( path[0] );
|
|
509
|
+
if (main) // TODO: improve, for csnpath starting with art
|
|
510
|
+
initDefinition( main );
|
|
485
511
|
if (ref.param)
|
|
486
512
|
return resolvePath( path, main.params[head], main, 'param' );
|
|
487
513
|
|
|
488
514
|
const semantics = referenceSemantics[refCtx] || {};
|
|
489
515
|
if (semantics.dynamic === 'global' || ref.global)
|
|
490
|
-
return resolvePath( path, csn.definitions[head], null, 'global',
|
|
516
|
+
return resolvePath( path, csn.definitions[head], null, 'global', semantics.assoc );
|
|
517
|
+
|
|
491
518
|
|
|
492
|
-
if (main) {
|
|
493
|
-
getOrigin( main );
|
|
494
|
-
cached( main, '$queries', allQueries );
|
|
495
|
-
}
|
|
496
519
|
let qcache = query && cache.get( query.projection || query );
|
|
497
|
-
// BACKEND ISSUE: you cannot call csnRefs(), inspect some refs, change the
|
|
498
|
-
// CSN and again inspect some refs without calling csnRefs() before!
|
|
499
|
-
// WORKAROUND: if no cached query, a backend has changed the CSN - re-eval cache
|
|
500
|
-
if (query && !qcache) {
|
|
501
|
-
setCache( main, '$queries', allQueries( main ) );
|
|
502
|
-
qcache = cache.get( query.projection || query );
|
|
503
|
-
}
|
|
504
520
|
// first the lexical scopes (due to query hierarchy) and $magic: ---------
|
|
505
521
|
if (semantics.lexical !== false) {
|
|
506
522
|
const tryAlias = path.length > 1 || ref.expand || ref.inline;
|
|
507
|
-
let
|
|
508
|
-
while (
|
|
509
|
-
const alias = tryAlias &&
|
|
523
|
+
let ncache = qcache && (semantics.lexical ? semantics.lexical( qcache ) : qcache);
|
|
524
|
+
while (ncache) {
|
|
525
|
+
const alias = tryAlias && ncache.$aliases[head];
|
|
510
526
|
if (alias)
|
|
511
527
|
return resolvePath( path, alias._select || alias._ref, null,
|
|
512
|
-
'alias',
|
|
513
|
-
const mixin =
|
|
514
|
-
if (mixin && {}.hasOwnProperty.call(
|
|
515
|
-
|
|
516
|
-
|
|
528
|
+
'alias', ncache.$queryNumber );
|
|
529
|
+
const mixin = ncache._select.mixin && ncache._select.mixin[head];
|
|
530
|
+
if (mixin && {}.hasOwnProperty.call( ncache._select.mixin, head )) {
|
|
531
|
+
setCache( mixin, '_parent', qcache._select );
|
|
532
|
+
return resolvePath( path, mixin, null, 'mixin', ncache.$queryNumber );
|
|
533
|
+
}
|
|
534
|
+
ncache = ncache.$next;
|
|
517
535
|
}
|
|
518
536
|
if (head.charAt(0) === '$') {
|
|
519
537
|
if (head !== '$self' && head !== '$projection')
|
|
@@ -542,7 +560,7 @@ function csnRefs( csn ) {
|
|
|
542
560
|
return resolvePath( path, found, alias._ref, 'source', name )
|
|
543
561
|
}
|
|
544
562
|
// console.log(query.SELECT,qcache,qcache.$next,main)
|
|
545
|
-
throw new
|
|
563
|
+
throw new ModelError ( `Path item ${ 0 }=${ head } refers to nothing, refCtx: ${ refCtx }` );
|
|
546
564
|
}
|
|
547
565
|
|
|
548
566
|
/**
|
|
@@ -552,14 +570,15 @@ function csnRefs( csn ) {
|
|
|
552
570
|
* @param [extraInfo]
|
|
553
571
|
*/
|
|
554
572
|
function resolvePath( path, art, parent, scope, extraInfo ) {
|
|
573
|
+
const staticAssoc = extraInfo === 'static' && scope === 'global';
|
|
555
574
|
/** @type {{idx, art?, env?}[]} */
|
|
556
575
|
const links = path.map( (_v, idx) => ({ idx }) );
|
|
557
576
|
// TODO: backends should be changed to enable uncommenting:
|
|
558
577
|
// if (!art) // does not work with test3/Associations/KeylessManagedAssociation/
|
|
559
|
-
// throw new
|
|
578
|
+
// throw new ModelError ( `Path item ${ 0 }=${ pathId( path[0] ) } refers to nothing, scope: ${ scope }`);
|
|
560
579
|
links[0].art = art;
|
|
561
580
|
for (let i = 1; i < links.length; ++i) { // yes, starting at 1, links[0] is set above
|
|
562
|
-
parent = navigationEnv( art );
|
|
581
|
+
parent = navigationEnv( art, staticAssoc );
|
|
563
582
|
links[i - 1].env = parent;
|
|
564
583
|
if (typeof path[i - 1] !== 'string')
|
|
565
584
|
setCache( path[i - 1], '_env', parent );
|
|
@@ -567,12 +586,12 @@ function csnRefs( csn ) {
|
|
|
567
586
|
if (!art) {
|
|
568
587
|
const env = links[i - 1].env;
|
|
569
588
|
const loc = env.name && env.name.$location || env.$location;
|
|
570
|
-
throw new
|
|
589
|
+
throw new ModelError ( `Path item ${ i }=${ pathId( path[i] ) } on ${ locationString( loc ) } refers to nothing` );
|
|
571
590
|
}
|
|
572
591
|
links[i].art = art;
|
|
573
592
|
}
|
|
574
593
|
const last = path[path.length - 1];
|
|
575
|
-
const fromRef = scope === 'global' && extraInfo;
|
|
594
|
+
const fromRef = scope === 'global' && extraInfo === 'target';
|
|
576
595
|
if (fromRef || typeof last !== 'string') {
|
|
577
596
|
const env = navigationEnv( art );
|
|
578
597
|
links[links.length - 1].env = env;
|
|
@@ -583,7 +602,7 @@ function csnRefs( csn ) {
|
|
|
583
602
|
if (typeof last !== 'string')
|
|
584
603
|
setCache( last, '_env', env )
|
|
585
604
|
}
|
|
586
|
-
return (extraInfo &&
|
|
605
|
+
return (extraInfo && scope !== 'global')
|
|
587
606
|
? { links, art, parent, scope, $env: extraInfo }
|
|
588
607
|
: { links, art, parent, scope };
|
|
589
608
|
}
|
|
@@ -604,8 +623,8 @@ function csnRefs( csn ) {
|
|
|
604
623
|
if (query.ref) { // ref in from
|
|
605
624
|
// console.log('SQ:',query,cache.get(query))
|
|
606
625
|
const as = query.as || implicitAs( query.ref );
|
|
607
|
-
const _ref =
|
|
608
|
-
getCache( fromSelect, '$aliases' )[as] = { _ref, elements: _ref.elements };
|
|
626
|
+
const _ref = cached( query, '_from', artifactFromRef )
|
|
627
|
+
getCache( fromSelect, '$aliases' )[as] = { _ref, elements: _ref.elements, _parent: query };
|
|
609
628
|
}
|
|
610
629
|
else {
|
|
611
630
|
const qcache = getQueryCache( parentQuery );
|
|
@@ -618,15 +637,18 @@ function csnRefs( csn ) {
|
|
|
618
637
|
if (select) {
|
|
619
638
|
cache.set( select, qcache ); // query and query.SELECT have the same cache qcache
|
|
620
639
|
qcache._select = select;
|
|
640
|
+
qcache._parent = main;
|
|
621
641
|
all.push( qcache );
|
|
622
642
|
}
|
|
623
643
|
}
|
|
624
644
|
} );
|
|
625
645
|
all.forEach( function initElements( qcache, index ) {
|
|
646
|
+
qcache._parent = main;
|
|
626
647
|
qcache.$queryNumber = index + 1;
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
648
|
+
const { elements } = (index ? qcache._select : main);
|
|
649
|
+
qcache.elements = elements;
|
|
650
|
+
const { columns } = qcache._select;
|
|
651
|
+
if (elements && columns)
|
|
630
652
|
columns.map( c => initColumnElement( c, qcache ) );
|
|
631
653
|
} );
|
|
632
654
|
return all;
|
|
@@ -665,7 +687,8 @@ function csnRefs( csn ) {
|
|
|
665
687
|
while (type.items)
|
|
666
688
|
type = type.items;
|
|
667
689
|
const elem = setCache( col, '_element', type.elements[as] );
|
|
668
|
-
//
|
|
690
|
+
if (elem) // TODO to.sql: something is strange if this is not set
|
|
691
|
+
setCache( elem, '_column', col );
|
|
669
692
|
if (col.expand)
|
|
670
693
|
col.expand.map( c => initColumnElement( c, elem ) );
|
|
671
694
|
}
|
|
@@ -723,7 +746,7 @@ function queryOrMain( query, main ) {
|
|
|
723
746
|
// 'projection'), but `leading` can be its `query` property:
|
|
724
747
|
if ((leading === query || leading === query.query) && main.elements)
|
|
725
748
|
return main;
|
|
726
|
-
throw new
|
|
749
|
+
throw new ModelError( `Query elements not available: ${ Object.keys( query ).join('+') }`);
|
|
727
750
|
}
|
|
728
751
|
|
|
729
752
|
/**
|
|
@@ -787,6 +810,52 @@ function traverseExpr( expr, parentQuery, callback ) {
|
|
|
787
810
|
}
|
|
788
811
|
}
|
|
789
812
|
|
|
813
|
+
function traverseDef( node, parent, kind, name, callback ) {
|
|
814
|
+
callback ( node, parent, kind, name );
|
|
815
|
+
if (node.params) {
|
|
816
|
+
for (const n of Object.keys( node.params ))
|
|
817
|
+
traverseType( node.params[n], node, 'param', n, callback );
|
|
818
|
+
}
|
|
819
|
+
if (node.returns)
|
|
820
|
+
traverseType( node.returns, node, 'returns', true, callback );
|
|
821
|
+
traverseType( node, parent, kind, name, callback );
|
|
822
|
+
if (node.actions) {
|
|
823
|
+
for (const n of Object.keys( node.actions ))
|
|
824
|
+
traverseDef( node.actions[n], node, 'action', n, callback )
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
function traverseType( node, parent, kind, name, callback ) {
|
|
829
|
+
callback ( node, parent, kind, name );
|
|
830
|
+
const target = targetAspect( node );
|
|
831
|
+
if (target && typeof target === 'object' && target.elements) {
|
|
832
|
+
callback ( target, node, 'target', true );
|
|
833
|
+
node = target;
|
|
834
|
+
}
|
|
835
|
+
else if (node.items) {
|
|
836
|
+
let items = 0;
|
|
837
|
+
while (node.items) {
|
|
838
|
+
callback ( node.items, node, 'items', ++items );
|
|
839
|
+
node = node.items;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (node.elements) {
|
|
843
|
+
for (const n of Object.keys( node.elements ))
|
|
844
|
+
traverseDef( node.elements[n], node, 'element', n, callback )
|
|
845
|
+
}
|
|
846
|
+
if (node.enum) {
|
|
847
|
+
for (const n of Object.keys( node.enum ))
|
|
848
|
+
traverseDef( node.enum[n], node, 'enum', n, callback )
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
function targetAspect( art ) {
|
|
853
|
+
const { $origin } = art;
|
|
854
|
+
return art.targetAspect ||
|
|
855
|
+
$origin && typeof $origin === 'object' && !Array.isArray( $origin ) && $origin.target ||
|
|
856
|
+
art.target;
|
|
857
|
+
}
|
|
858
|
+
|
|
790
859
|
function pathId( item ) {
|
|
791
860
|
return (typeof item === 'string') ? item : item.id;
|
|
792
861
|
}
|
|
@@ -803,7 +872,7 @@ function startCsnPath( csnPath, csn ) {
|
|
|
803
872
|
return { index: 1, main, parent, art };
|
|
804
873
|
}
|
|
805
874
|
if (csnPath.length < 2 || csnPath[0] !== 'definitions')
|
|
806
|
-
throw new
|
|
875
|
+
throw new CompilerAssertion( 'References outside definitions not supported yet');
|
|
807
876
|
const art = csn.definitions[csnPath[1]];
|
|
808
877
|
return { index: 2, main: art, parent: null, art };
|
|
809
878
|
}
|
|
@@ -830,9 +899,9 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
830
899
|
|
|
831
900
|
const prop = csnPath[index];
|
|
832
901
|
// array item, name/index of artifact/member, (named) argument
|
|
833
|
-
if (isName || Array.isArray( obj )) {
|
|
902
|
+
if (isName || Array.isArray( obj ) || prop === 'returns') {
|
|
834
903
|
// TODO: call some kind of resolve.setOrigin()
|
|
835
|
-
if (typeof isName === 'string') {
|
|
904
|
+
if (typeof isName === 'string' || prop === 'returns') {
|
|
836
905
|
parent = art;
|
|
837
906
|
art = obj[prop];
|
|
838
907
|
}
|