@sap/cds-compiler 2.12.0 → 2.15.2
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 +221 -15
- package/bin/cdsc.js +125 -50
- 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 +47 -84
- package/lib/api/options.js +5 -6
- package/lib/api/validate.js +6 -11
- 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 +114 -18
- package/lib/base/messages.js +101 -90
- package/lib/base/model.js +2 -63
- package/lib/base/optionProcessorHelper.js +177 -123
- package/lib/checks/annotationsOData.js +12 -33
- package/lib/checks/arrayOfs.js +1 -34
- 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 +6 -11
- package/lib/compiler/assert-consistency.js +6 -3
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +19 -6
- package/lib/compiler/checks.js +23 -60
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1151 -0
- package/lib/compiler/extend.js +1000 -0
- package/lib/compiler/finalize-parse-cdl.js +237 -0
- package/lib/compiler/index.js +107 -39
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1227 -0
- package/lib/compiler/propagator.js +114 -46
- package/lib/compiler/resolve.js +1521 -0
- package/lib/compiler/shared.js +126 -65
- package/lib/compiler/tweak-assocs.js +535 -0
- package/lib/compiler/utils.js +197 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -24
- package/lib/edm/annotations/preprocessAnnotations.js +2 -2
- package/lib/edm/csn2edm.js +219 -100
- package/lib/edm/edm.js +302 -230
- package/lib/edm/edmPreprocessor.js +554 -419
- package/lib/edm/edmUtils.js +138 -44
- package/lib/gen/Dictionary.json +100 -19
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -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 +5765 -4480
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +15 -3
- package/lib/json/to-csn.js +126 -68
- package/lib/language/docCommentParser.js +4 -4
- package/lib/language/genericAntlrParser.js +123 -5
- package/lib/language/language.g4 +355 -156
- package/lib/language/multiLineStringParser.js +5 -5
- package/lib/main.d.ts +486 -59
- package/lib/main.js +41 -9
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +252 -156
- package/lib/model/csnUtils.js +384 -297
- package/lib/model/enrichCsn.js +71 -29
- package/lib/model/revealInternalProperties.js +29 -8
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +23 -18
- package/lib/optionProcessor.js +63 -26
- package/lib/render/manageConstraints.js +35 -32
- package/lib/render/toCdl.js +897 -947
- package/lib/render/toHdbcds.js +205 -257
- package/lib/render/toSql.js +264 -225
- package/lib/render/utils/common.js +136 -25
- 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 +3 -1
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +104 -306
- package/lib/transform/db/cdsPersistence.js +2 -2
- package/lib/transform/db/constraints.js +58 -53
- package/lib/transform/db/expansion.js +60 -33
- package/lib/transform/db/flattening.js +582 -104
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/transformExists.js +66 -13
- package/lib/transform/db/views.js +11 -7
- 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 +109 -208
- package/lib/transform/forOdataNew.js +59 -212
- package/lib/transform/localized.js +46 -26
- package/lib/transform/odata/toFinalBaseType.js +85 -11
- package/lib/transform/odata/typesExposure.js +147 -199
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +44 -33
- package/lib/transform/translateAssocsToJoins.js +3 -20
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +172 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/objectUtils.js +30 -0
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/lib/compiler/definer.js +0 -2361
- package/lib/compiler/resolver.js +0 -3079
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -290
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
- 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,114 @@ 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];
|
|
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 );
|
|
387
406
|
}
|
|
388
407
|
|
|
389
|
-
function
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
return
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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;
|
|
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]}` );
|
|
422
429
|
}
|
|
423
430
|
|
|
424
|
-
function
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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
|
+
const { elements } = _select;
|
|
446
|
+
if (elements) {
|
|
447
|
+
for (const n of Object.keys( elements ))
|
|
448
|
+
traverseDef( elements[n], _select, 'element', n, initNode );
|
|
449
|
+
}
|
|
450
|
+
if (_select.mixin) {
|
|
451
|
+
for (const n of Object.keys( _select.mixin ))
|
|
452
|
+
setCache( _select.mixin[n], '_parent', _select ); // relevant initNode() part
|
|
453
|
+
}
|
|
431
454
|
}
|
|
455
|
+
return main;
|
|
432
456
|
}
|
|
433
457
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
458
|
+
function initNode( art, parent, kind, name ) {
|
|
459
|
+
setCache( art, '_parent', parent );
|
|
460
|
+
if (kind === 'target') {
|
|
461
|
+
// Prevent re-initialization of anonymous aspect with initDefinition():
|
|
462
|
+
// (that would be with parent: null which would be wrong)
|
|
463
|
+
setCache( art, '$queries', null );
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
if (art.type || !kind) // with type, top-level, query or mixin
|
|
467
|
+
return;
|
|
468
|
+
const { $origin } = art;
|
|
469
|
+
if (typeof $origin === 'object') // null, […], {…}
|
|
470
|
+
return;
|
|
471
|
+
const step = $origin || name;
|
|
472
|
+
if (parent.$origin ||
|
|
473
|
+
parent.type && kind !== 'enum' && parent.$origin !== null ||
|
|
474
|
+
getCache( parent, '$origin$step' ))
|
|
475
|
+
setCache( art, '$origin$step', (kind === 'element' ? step : { [kind]: step }) );
|
|
442
476
|
}
|
|
443
477
|
|
|
444
478
|
/**
|
|
@@ -479,41 +513,35 @@ function csnRefs( csn ) {
|
|
|
479
513
|
function resolveRef( ref, refCtx, main, query, parent, baseEnv ) {
|
|
480
514
|
const path = (typeof ref === 'string') ? [ ref ] : ref.ref;
|
|
481
515
|
if (!Array.isArray( path ))
|
|
482
|
-
throw new
|
|
516
|
+
throw new ModelError( 'References must look like {ref:[...]}' );
|
|
483
517
|
|
|
484
518
|
const head = pathId( path[0] );
|
|
519
|
+
if (main) // TODO: improve, for csnpath starting with art
|
|
520
|
+
initDefinition( main );
|
|
485
521
|
if (ref.param)
|
|
486
522
|
return resolvePath( path, main.params[head], main, 'param' );
|
|
487
523
|
|
|
488
524
|
const semantics = referenceSemantics[refCtx] || {};
|
|
489
525
|
if (semantics.dynamic === 'global' || ref.global)
|
|
490
|
-
return resolvePath( path, csn.definitions[head], null, 'global',
|
|
526
|
+
return resolvePath( path, csn.definitions[head], null, 'global', semantics.assoc );
|
|
527
|
+
|
|
491
528
|
|
|
492
|
-
if (main) {
|
|
493
|
-
getOrigin( main );
|
|
494
|
-
cached( main, '$queries', allQueries );
|
|
495
|
-
}
|
|
496
529
|
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
530
|
// first the lexical scopes (due to query hierarchy) and $magic: ---------
|
|
505
531
|
if (semantics.lexical !== false) {
|
|
506
532
|
const tryAlias = path.length > 1 || ref.expand || ref.inline;
|
|
507
|
-
let
|
|
508
|
-
while (
|
|
509
|
-
const alias = tryAlias &&
|
|
533
|
+
let ncache = qcache && (semantics.lexical ? semantics.lexical( qcache ) : qcache);
|
|
534
|
+
while (ncache) {
|
|
535
|
+
const alias = tryAlias && ncache.$aliases[head];
|
|
510
536
|
if (alias)
|
|
511
537
|
return resolvePath( path, alias._select || alias._ref, null,
|
|
512
|
-
'alias',
|
|
513
|
-
const mixin =
|
|
514
|
-
if (mixin && {}.hasOwnProperty.call(
|
|
515
|
-
|
|
516
|
-
|
|
538
|
+
'alias', ncache.$queryNumber );
|
|
539
|
+
const mixin = ncache._select.mixin && ncache._select.mixin[head];
|
|
540
|
+
if (mixin && {}.hasOwnProperty.call( ncache._select.mixin, head )) {
|
|
541
|
+
setCache( mixin, '_parent', qcache._select );
|
|
542
|
+
return resolvePath( path, mixin, null, 'mixin', ncache.$queryNumber );
|
|
543
|
+
}
|
|
544
|
+
ncache = ncache.$next;
|
|
517
545
|
}
|
|
518
546
|
if (head.charAt(0) === '$') {
|
|
519
547
|
if (head !== '$self' && head !== '$projection')
|
|
@@ -529,8 +557,19 @@ function csnRefs( csn ) {
|
|
|
529
557
|
}
|
|
530
558
|
if (baseEnv) // ref-target (filter condition), expand, inline
|
|
531
559
|
return resolvePath( path, baseEnv.elements[head], baseEnv, semantics.dynamic );
|
|
532
|
-
if (!query)
|
|
533
|
-
|
|
560
|
+
if (!query) { // outside queries - TODO: items?
|
|
561
|
+
let art = parent.elements[head];
|
|
562
|
+
// Ref to up_ in anonymous aspect
|
|
563
|
+
if (!art && head === 'up_') {
|
|
564
|
+
const up = getCache( parent, '_parent' );
|
|
565
|
+
const target = up && typeof up.target === 'string' && csn.definitions[up.target];
|
|
566
|
+
if (target && target.elements) {
|
|
567
|
+
initDefinition( target );
|
|
568
|
+
art = target.elements.up_;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return resolvePath( path, art, parent, 'parent' );
|
|
572
|
+
}
|
|
534
573
|
|
|
535
574
|
if (semantics.dynamic === 'query')
|
|
536
575
|
// TODO: for ON condition in expand, would need to use cached _element
|
|
@@ -542,7 +581,7 @@ function csnRefs( csn ) {
|
|
|
542
581
|
return resolvePath( path, found, alias._ref, 'source', name )
|
|
543
582
|
}
|
|
544
583
|
// console.log(query.SELECT,qcache,qcache.$next,main)
|
|
545
|
-
throw new
|
|
584
|
+
throw new ModelError ( `Path item ${ 0 }=${ head } refers to nothing, refCtx: ${ refCtx }` );
|
|
546
585
|
}
|
|
547
586
|
|
|
548
587
|
/**
|
|
@@ -552,14 +591,15 @@ function csnRefs( csn ) {
|
|
|
552
591
|
* @param [extraInfo]
|
|
553
592
|
*/
|
|
554
593
|
function resolvePath( path, art, parent, scope, extraInfo ) {
|
|
594
|
+
const staticAssoc = extraInfo === 'static' && scope === 'global';
|
|
555
595
|
/** @type {{idx, art?, env?}[]} */
|
|
556
596
|
const links = path.map( (_v, idx) => ({ idx }) );
|
|
557
597
|
// TODO: backends should be changed to enable uncommenting:
|
|
558
598
|
// if (!art) // does not work with test3/Associations/KeylessManagedAssociation/
|
|
559
|
-
// throw new
|
|
599
|
+
// throw new ModelError ( `Path item ${ 0 }=${ pathId( path[0] ) } refers to nothing, scope: ${ scope }`);
|
|
560
600
|
links[0].art = art;
|
|
561
601
|
for (let i = 1; i < links.length; ++i) { // yes, starting at 1, links[0] is set above
|
|
562
|
-
parent = navigationEnv( art );
|
|
602
|
+
parent = navigationEnv( art, staticAssoc );
|
|
563
603
|
links[i - 1].env = parent;
|
|
564
604
|
if (typeof path[i - 1] !== 'string')
|
|
565
605
|
setCache( path[i - 1], '_env', parent );
|
|
@@ -567,12 +607,12 @@ function csnRefs( csn ) {
|
|
|
567
607
|
if (!art) {
|
|
568
608
|
const env = links[i - 1].env;
|
|
569
609
|
const loc = env.name && env.name.$location || env.$location;
|
|
570
|
-
throw new
|
|
610
|
+
throw new ModelError ( `Path item ${ i }=${ pathId( path[i] ) } on ${ locationString( loc ) } refers to nothing` );
|
|
571
611
|
}
|
|
572
612
|
links[i].art = art;
|
|
573
613
|
}
|
|
574
614
|
const last = path[path.length - 1];
|
|
575
|
-
const fromRef = scope === 'global' && extraInfo;
|
|
615
|
+
const fromRef = scope === 'global' && extraInfo === 'target';
|
|
576
616
|
if (fromRef || typeof last !== 'string') {
|
|
577
617
|
const env = navigationEnv( art );
|
|
578
618
|
links[links.length - 1].env = env;
|
|
@@ -583,7 +623,7 @@ function csnRefs( csn ) {
|
|
|
583
623
|
if (typeof last !== 'string')
|
|
584
624
|
setCache( last, '_env', env )
|
|
585
625
|
}
|
|
586
|
-
return (extraInfo &&
|
|
626
|
+
return (extraInfo && scope !== 'global')
|
|
587
627
|
? { links, art, parent, scope, $env: extraInfo }
|
|
588
628
|
: { links, art, parent, scope };
|
|
589
629
|
}
|
|
@@ -604,8 +644,8 @@ function csnRefs( csn ) {
|
|
|
604
644
|
if (query.ref) { // ref in from
|
|
605
645
|
// console.log('SQ:',query,cache.get(query))
|
|
606
646
|
const as = query.as || implicitAs( query.ref );
|
|
607
|
-
const _ref =
|
|
608
|
-
getCache( fromSelect, '$aliases' )[as] = { _ref, elements: _ref.elements };
|
|
647
|
+
const _ref = cached( query, '_from', artifactFromRef )
|
|
648
|
+
getCache( fromSelect, '$aliases' )[as] = { _ref, elements: _ref.elements, _parent: query };
|
|
609
649
|
}
|
|
610
650
|
else {
|
|
611
651
|
const qcache = getQueryCache( parentQuery );
|
|
@@ -618,15 +658,18 @@ function csnRefs( csn ) {
|
|
|
618
658
|
if (select) {
|
|
619
659
|
cache.set( select, qcache ); // query and query.SELECT have the same cache qcache
|
|
620
660
|
qcache._select = select;
|
|
661
|
+
qcache._parent = main;
|
|
621
662
|
all.push( qcache );
|
|
622
663
|
}
|
|
623
664
|
}
|
|
624
665
|
} );
|
|
625
666
|
all.forEach( function initElements( qcache, index ) {
|
|
667
|
+
qcache._parent = main;
|
|
626
668
|
qcache.$queryNumber = index + 1;
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
669
|
+
const { elements } = (index ? qcache._select : main);
|
|
670
|
+
qcache.elements = elements;
|
|
671
|
+
const { columns } = qcache._select;
|
|
672
|
+
if (elements && columns)
|
|
630
673
|
columns.map( c => initColumnElement( c, qcache ) );
|
|
631
674
|
} );
|
|
632
675
|
return all;
|
|
@@ -665,7 +708,8 @@ function csnRefs( csn ) {
|
|
|
665
708
|
while (type.items)
|
|
666
709
|
type = type.items;
|
|
667
710
|
const elem = setCache( col, '_element', type.elements[as] );
|
|
668
|
-
//
|
|
711
|
+
if (elem) // TODO to.sql: something is strange if this is not set
|
|
712
|
+
setCache( elem, '_column', col );
|
|
669
713
|
if (col.expand)
|
|
670
714
|
col.expand.map( c => initColumnElement( c, elem ) );
|
|
671
715
|
}
|
|
@@ -680,6 +724,11 @@ function csnRefs( csn ) {
|
|
|
680
724
|
hidden = {};
|
|
681
725
|
cache.set( obj, hidden );
|
|
682
726
|
}
|
|
727
|
+
// TODO: we might keep the following with --test-mode
|
|
728
|
+
// if (hidden[prop] !== undefined) {
|
|
729
|
+
// console.log('RS:',prop,hidden[prop],val,obj)
|
|
730
|
+
// throw Error('RESET')
|
|
731
|
+
// }
|
|
683
732
|
hidden[prop] = val;
|
|
684
733
|
return val;
|
|
685
734
|
}
|
|
@@ -723,7 +772,7 @@ function queryOrMain( query, main ) {
|
|
|
723
772
|
// 'projection'), but `leading` can be its `query` property:
|
|
724
773
|
if ((leading === query || leading === query.query) && main.elements)
|
|
725
774
|
return main;
|
|
726
|
-
throw new
|
|
775
|
+
throw new ModelError( `Query elements not available: ${ Object.keys( query ).join('+') }`);
|
|
727
776
|
}
|
|
728
777
|
|
|
729
778
|
/**
|
|
@@ -787,6 +836,53 @@ function traverseExpr( expr, parentQuery, callback ) {
|
|
|
787
836
|
}
|
|
788
837
|
}
|
|
789
838
|
|
|
839
|
+
function traverseDef( node, parent, kind, name, callback ) {
|
|
840
|
+
callback ( node, parent, kind, name );
|
|
841
|
+
if (node.params) {
|
|
842
|
+
for (const n of Object.keys( node.params ))
|
|
843
|
+
traverseType( node.params[n], node, 'param', n, callback );
|
|
844
|
+
}
|
|
845
|
+
if (node.returns)
|
|
846
|
+
traverseType( node.returns, node, 'returns', true, callback );
|
|
847
|
+
traverseType( node, true, kind, name, callback );
|
|
848
|
+
if (node.actions) {
|
|
849
|
+
for (const n of Object.keys( node.actions ))
|
|
850
|
+
traverseDef( node.actions[n], node, 'action', n, callback )
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
function traverseType( node, parent, kind, name, callback ) {
|
|
855
|
+
if (parent !== true)
|
|
856
|
+
callback ( node, parent, kind, name );
|
|
857
|
+
const target = targetAspect( node );
|
|
858
|
+
if (target && typeof target === 'object' && target.elements) {
|
|
859
|
+
callback ( target, node, 'target', true );
|
|
860
|
+
node = target;
|
|
861
|
+
}
|
|
862
|
+
else if (node.items) {
|
|
863
|
+
let items = 0;
|
|
864
|
+
while (node.items) {
|
|
865
|
+
callback ( node.items, node, 'items', ++items );
|
|
866
|
+
node = node.items;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
if (node.elements) {
|
|
870
|
+
for (const n of Object.keys( node.elements ))
|
|
871
|
+
traverseDef( node.elements[n], node, 'element', n, callback )
|
|
872
|
+
}
|
|
873
|
+
if (node.enum) {
|
|
874
|
+
for (const n of Object.keys( node.enum ))
|
|
875
|
+
traverseDef( node.enum[n], node, 'enum', n, callback )
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
function targetAspect( art ) {
|
|
880
|
+
const { $origin } = art;
|
|
881
|
+
return art.targetAspect ||
|
|
882
|
+
$origin && typeof $origin === 'object' && !Array.isArray( $origin ) && $origin.target ||
|
|
883
|
+
art.target;
|
|
884
|
+
}
|
|
885
|
+
|
|
790
886
|
function pathId( item ) {
|
|
791
887
|
return (typeof item === 'string') ? item : item.id;
|
|
792
888
|
}
|
|
@@ -803,7 +899,7 @@ function startCsnPath( csnPath, csn ) {
|
|
|
803
899
|
return { index: 1, main, parent, art };
|
|
804
900
|
}
|
|
805
901
|
if (csnPath.length < 2 || csnPath[0] !== 'definitions')
|
|
806
|
-
throw new
|
|
902
|
+
throw new CompilerAssertion( 'References outside definitions not supported yet');
|
|
807
903
|
const art = csn.definitions[csnPath[1]];
|
|
808
904
|
return { index: 2, main: art, parent: null, art };
|
|
809
905
|
}
|
|
@@ -830,9 +926,9 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
830
926
|
|
|
831
927
|
const prop = csnPath[index];
|
|
832
928
|
// array item, name/index of artifact/member, (named) argument
|
|
833
|
-
if (isName || Array.isArray( obj )) {
|
|
929
|
+
if (isName || Array.isArray( obj ) || prop === 'returns') {
|
|
834
930
|
// TODO: call some kind of resolve.setOrigin()
|
|
835
|
-
if (typeof isName === 'string') {
|
|
931
|
+
if (typeof isName === 'string' || prop === 'returns') {
|
|
836
932
|
parent = art;
|
|
837
933
|
art = obj[prop];
|
|
838
934
|
}
|