@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.
Files changed (118) hide show
  1. package/CHANGELOG.md +110 -15
  2. package/bin/cdsc.js +13 -13
  3. package/bin/cdsse.js +2 -2
  4. package/doc/CHANGELOG_BETA.md +13 -6
  5. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  6. package/doc/NameResolution.md +21 -16
  7. package/lib/api/main.js +28 -63
  8. package/lib/api/options.js +3 -3
  9. package/lib/api/validate.js +0 -5
  10. package/lib/backends.js +15 -23
  11. package/lib/base/dictionaries.js +0 -8
  12. package/lib/base/error.js +26 -0
  13. package/lib/base/keywords.js +7 -17
  14. package/lib/base/location.js +9 -4
  15. package/lib/base/message-registry.js +25 -4
  16. package/lib/base/messages.js +16 -26
  17. package/lib/base/model.js +2 -63
  18. package/lib/base/optionProcessorHelper.js +158 -123
  19. package/lib/checks/annotationsOData.js +1 -1
  20. package/lib/checks/cdsPersistence.js +2 -1
  21. package/lib/checks/enricher.js +17 -1
  22. package/lib/checks/invalidTarget.js +3 -1
  23. package/lib/checks/managedWithoutKeys.js +3 -1
  24. package/lib/checks/selectItems.js +4 -4
  25. package/lib/checks/sql-snippets.js +27 -26
  26. package/lib/checks/types.js +1 -1
  27. package/lib/checks/validator.js +4 -7
  28. package/lib/compiler/assert-consistency.js +5 -3
  29. package/lib/compiler/builtins.js +8 -6
  30. package/lib/compiler/checks.js +14 -3
  31. package/lib/compiler/cycle-detector.js +1 -1
  32. package/lib/compiler/define.js +1103 -0
  33. package/lib/compiler/extend.js +983 -0
  34. package/lib/compiler/finalize-parse-cdl.js +231 -0
  35. package/lib/compiler/index.js +32 -13
  36. package/lib/compiler/kick-start.js +190 -0
  37. package/lib/compiler/moduleLayers.js +4 -4
  38. package/lib/compiler/populate.js +1226 -0
  39. package/lib/compiler/propagator.js +111 -46
  40. package/lib/compiler/resolve.js +1433 -0
  41. package/lib/compiler/shared.js +64 -37
  42. package/lib/compiler/tweak-assocs.js +529 -0
  43. package/lib/compiler/utils.js +197 -33
  44. package/lib/edm/.eslintrc.json +5 -0
  45. package/lib/edm/annotations/genericTranslation.js +5 -9
  46. package/lib/edm/annotations/preprocessAnnotations.js +2 -2
  47. package/lib/edm/csn2edm.js +9 -8
  48. package/lib/edm/edm.js +11 -12
  49. package/lib/edm/edmPreprocessor.js +137 -73
  50. package/lib/edm/edmUtils.js +116 -22
  51. package/lib/gen/Dictionary.json +10 -3
  52. package/lib/gen/language.checksum +1 -1
  53. package/lib/gen/language.interp +9 -1
  54. package/lib/gen/language.tokens +86 -83
  55. package/lib/gen/languageLexer.interp +10 -1
  56. package/lib/gen/languageLexer.js +860 -833
  57. package/lib/gen/languageLexer.tokens +78 -75
  58. package/lib/gen/languageParser.js +5282 -4265
  59. package/lib/json/from-csn.js +12 -1
  60. package/lib/json/to-csn.js +126 -66
  61. package/lib/language/docCommentParser.js +2 -2
  62. package/lib/language/genericAntlrParser.js +76 -3
  63. package/lib/language/language.g4 +297 -130
  64. package/lib/language/multiLineStringParser.js +5 -5
  65. package/lib/main.d.ts +468 -59
  66. package/lib/main.js +35 -9
  67. package/lib/model/api.js +3 -1
  68. package/lib/model/csnRefs.js +225 -156
  69. package/lib/model/csnUtils.js +192 -223
  70. package/lib/model/enrichCsn.js +70 -29
  71. package/lib/model/revealInternalProperties.js +27 -6
  72. package/lib/model/sortViews.js +2 -1
  73. package/lib/modelCompare/compare.js +17 -12
  74. package/lib/optionProcessor.js +5 -4
  75. package/lib/render/manageConstraints.js +35 -32
  76. package/lib/render/toCdl.js +73 -288
  77. package/lib/render/toHdbcds.js +25 -23
  78. package/lib/render/toSql.js +98 -41
  79. package/lib/render/utils/common.js +5 -10
  80. package/lib/render/utils/sql.js +4 -3
  81. package/lib/render/utils/stringEscapes.js +111 -0
  82. package/lib/sql-identifier.js +1 -1
  83. package/lib/transform/.eslintrc.json +5 -0
  84. package/lib/transform/db/.eslintrc.json +2 -0
  85. package/lib/transform/db/applyTransformations.js +35 -12
  86. package/lib/transform/db/assertUnique.js +1 -1
  87. package/lib/transform/db/associations.js +103 -305
  88. package/lib/transform/db/cdsPersistence.js +2 -2
  89. package/lib/transform/db/constraints.js +55 -52
  90. package/lib/transform/db/expansion.js +46 -24
  91. package/lib/transform/db/flattening.js +553 -102
  92. package/lib/transform/db/groupByOrderBy.js +3 -1
  93. package/lib/transform/db/transformExists.js +59 -6
  94. package/lib/transform/db/views.js +5 -4
  95. package/lib/transform/draft/.eslintrc.json +38 -0
  96. package/lib/transform/{db/draft.js → draft/db.js} +6 -5
  97. package/lib/transform/draft/odata.js +227 -0
  98. package/lib/transform/forHanaNew.js +67 -183
  99. package/lib/transform/forOdataNew.js +17 -171
  100. package/lib/transform/localized.js +34 -19
  101. package/lib/transform/odata/generateForeignKeyElements.js +1 -1
  102. package/lib/transform/odata/referenceFlattener.js +95 -89
  103. package/lib/transform/odata/structureFlattener.js +1 -1
  104. package/lib/transform/odata/toFinalBaseType.js +86 -12
  105. package/lib/transform/odata/typesExposure.js +5 -5
  106. package/lib/transform/odata/utils.js +2 -2
  107. package/lib/transform/transformUtilsNew.js +36 -22
  108. package/lib/transform/translateAssocsToJoins.js +2 -19
  109. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  110. package/lib/transform/universalCsn/coreComputed.js +170 -0
  111. package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
  112. package/lib/transform/universalCsn/utils.js +63 -0
  113. package/lib/utils/objectUtils.js +30 -0
  114. package/package.json +1 -1
  115. package/share/messages/README.md +26 -0
  116. package/lib/compiler/definer.js +0 -2361
  117. package/lib/compiler/resolver.js +0 -3079
  118. package/lib/transform/universalCsnEnricher.js +0 -237
@@ -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, artifactRef, getOrigin, inspectRef, queryOrMain,
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 Error( 'Circular type reference');
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
- if (!env.target)
281
- return env;
282
- const target = csn.definitions[env.target];
283
- getOrigin( target );
284
- return target;
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
- const art = (typeof ref === 'string')
297
- ? csn.definitions[ref]
298
- : cached( ref, '_ref', artifactPathRef );
299
- if (art)
300
- return art;
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 Error( 'Undefined reference' );
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
- getOrigin( target ); // induce implicit $origin on target
368
+ initDefinition( target );
330
369
  return target;
331
370
  }
332
371
 
333
372
  function getOrigin( art ) {
334
- const origin = cached( art, '_origin', getOriginRaw );
335
- if (origin && origin !== BUILTIN_TYPE)
336
- cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
337
- return origin;
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 (!art.$origin) // implicit $origin should have been set
344
- return null;
345
- // art.$origin must not be a string here - shortened refs should already
346
- // have been used to set the _origin cache
347
- // If $origin object, return $origin of that:
348
- if (!Array.isArray( art.$origin )) // anonymous prototype in $origin
349
- return cached( art.$origin, '_origin', getOriginRaw );
350
- const [ head, ...tail ] = art.$origin;
351
- let origin = csn.definitions[head];
352
- // allow shorter $origin ref for actions/functions, just using a string:
353
- let isAction = art.kind === 'action' || art.kind === 'function';
354
- for (const elem of tail) {
355
- origin = originNavigation( origin, elem, isAction );
356
- isAction = false;
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
- return origin;
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 originNavigation( art, elem, isAction ) {
362
- if (typeof elem !== 'string') {
363
- if (elem.action)
364
- return art.actions[elem.action];
365
- if (elem.param)
366
- return art.params[elem.param];
367
- if (elem.returns)
368
- return art.returns;
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 setMembersImplicit( members, originMembers ) {
425
- if (!members)
426
- return;
427
- for (const name in members) {
428
- const elem = members[name];
429
- if (!elem.type && typeof elem.$origin !== 'object') // undefined or string
430
- setCache( elem, '_origin', originMembers[elem.$origin || name] || false );
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
- * Return the entity we select from
436
- *
437
- * @param {CSN.ArtifactReferencePath} ref
438
- * @returns {CSN.Definition}
439
- */
440
- function fromRef( ref ) {
441
- return navigationEnv( artifactRef( ref ));
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 Error( 'References must look like {ref:[...]}' );
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', refCtx === 'from' );
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 cache = qcache && (semantics.lexical ? semantics.lexical( qcache ) : qcache);
508
- while (cache) {
509
- const alias = tryAlias && cache.$aliases[head];
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', cache.$queryNumber );
513
- const mixin = cache._select.mixin && cache._select.mixin[head];
514
- if (mixin && {}.hasOwnProperty.call( cache._select.mixin, head ))
515
- return resolvePath( path, mixin, null, 'mixin', cache.$queryNumber );
516
- cache = cache.$next;
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 Error ( `Path item ${ 0 }=${ head } refers to nothing, refCtx: ${ refCtx }` );
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 Error ( `Path item ${ 0 }=${ pathId( path[0] ) } refers to nothing, scope: ${ scope }`);
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 Error ( `Path item ${ i }=${ pathId( path[i] ) } on ${ locationString( loc ) } refers to nothing` );
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 && !fromRef)
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 = fromRef( query );
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
- qcache.elements = (index ? qcache._select : main).elements;
628
- const columns = qcache._select.columns;
629
- if (qcache.elements && columns)
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
- // if requested, we could set a _column link in element
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 Error( `Query elements not available: ${ Object.keys( query ).join('+') }`);
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 Error( 'References outside definitions not supported yet');
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
  }