@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.
Files changed (128) hide show
  1. package/CHANGELOG.md +221 -15
  2. package/bin/cdsc.js +125 -50
  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 +47 -84
  8. package/lib/api/options.js +5 -6
  9. package/lib/api/validate.js +6 -11
  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 +114 -18
  16. package/lib/base/messages.js +101 -90
  17. package/lib/base/model.js +2 -63
  18. package/lib/base/optionProcessorHelper.js +177 -123
  19. package/lib/checks/annotationsOData.js +12 -33
  20. package/lib/checks/arrayOfs.js +1 -34
  21. package/lib/checks/cdsPersistence.js +2 -1
  22. package/lib/checks/enricher.js +17 -1
  23. package/lib/checks/invalidTarget.js +3 -1
  24. package/lib/checks/managedWithoutKeys.js +3 -1
  25. package/lib/checks/selectItems.js +4 -4
  26. package/lib/checks/sql-snippets.js +27 -26
  27. package/lib/checks/types.js +1 -1
  28. package/lib/checks/validator.js +6 -11
  29. package/lib/compiler/assert-consistency.js +6 -3
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +19 -6
  32. package/lib/compiler/checks.js +23 -60
  33. package/lib/compiler/cycle-detector.js +1 -1
  34. package/lib/compiler/define.js +1151 -0
  35. package/lib/compiler/extend.js +1000 -0
  36. package/lib/compiler/finalize-parse-cdl.js +237 -0
  37. package/lib/compiler/index.js +107 -39
  38. package/lib/compiler/kick-start.js +190 -0
  39. package/lib/compiler/moduleLayers.js +4 -4
  40. package/lib/compiler/populate.js +1227 -0
  41. package/lib/compiler/propagator.js +114 -46
  42. package/lib/compiler/resolve.js +1521 -0
  43. package/lib/compiler/shared.js +126 -65
  44. package/lib/compiler/tweak-assocs.js +535 -0
  45. package/lib/compiler/utils.js +197 -33
  46. package/lib/edm/.eslintrc.json +5 -0
  47. package/lib/edm/annotations/genericTranslation.js +38 -24
  48. package/lib/edm/annotations/preprocessAnnotations.js +2 -2
  49. package/lib/edm/csn2edm.js +219 -100
  50. package/lib/edm/edm.js +302 -230
  51. package/lib/edm/edmPreprocessor.js +554 -419
  52. package/lib/edm/edmUtils.js +138 -44
  53. package/lib/gen/Dictionary.json +100 -19
  54. package/lib/gen/language.checksum +1 -1
  55. package/lib/gen/language.interp +11 -1
  56. package/lib/gen/language.tokens +86 -83
  57. package/lib/gen/languageLexer.interp +10 -1
  58. package/lib/gen/languageLexer.js +860 -833
  59. package/lib/gen/languageLexer.tokens +78 -75
  60. package/lib/gen/languageParser.js +5765 -4480
  61. package/lib/json/csnVersion.js +10 -11
  62. package/lib/json/from-csn.js +15 -3
  63. package/lib/json/to-csn.js +126 -68
  64. package/lib/language/docCommentParser.js +4 -4
  65. package/lib/language/genericAntlrParser.js +123 -5
  66. package/lib/language/language.g4 +355 -156
  67. package/lib/language/multiLineStringParser.js +5 -5
  68. package/lib/main.d.ts +486 -59
  69. package/lib/main.js +41 -9
  70. package/lib/model/api.js +3 -1
  71. package/lib/model/csnRefs.js +252 -156
  72. package/lib/model/csnUtils.js +384 -297
  73. package/lib/model/enrichCsn.js +71 -29
  74. package/lib/model/revealInternalProperties.js +29 -8
  75. package/lib/model/sortViews.js +2 -1
  76. package/lib/modelCompare/compare.js +23 -18
  77. package/lib/optionProcessor.js +63 -26
  78. package/lib/render/manageConstraints.js +35 -32
  79. package/lib/render/toCdl.js +897 -947
  80. package/lib/render/toHdbcds.js +205 -257
  81. package/lib/render/toSql.js +264 -225
  82. package/lib/render/utils/common.js +136 -25
  83. package/lib/render/utils/sql.js +4 -3
  84. package/lib/render/utils/stringEscapes.js +111 -0
  85. package/lib/sql-identifier.js +1 -1
  86. package/lib/transform/.eslintrc.json +5 -0
  87. package/lib/transform/db/.eslintrc.json +3 -1
  88. package/lib/transform/db/applyTransformations.js +35 -12
  89. package/lib/transform/db/assertUnique.js +1 -1
  90. package/lib/transform/db/associations.js +104 -306
  91. package/lib/transform/db/cdsPersistence.js +2 -2
  92. package/lib/transform/db/constraints.js +58 -53
  93. package/lib/transform/db/expansion.js +60 -33
  94. package/lib/transform/db/flattening.js +582 -104
  95. package/lib/transform/db/groupByOrderBy.js +3 -1
  96. package/lib/transform/db/transformExists.js +66 -13
  97. package/lib/transform/db/views.js +11 -7
  98. package/lib/transform/draft/.eslintrc.json +38 -0
  99. package/lib/transform/{db/draft.js → draft/db.js} +6 -5
  100. package/lib/transform/draft/odata.js +227 -0
  101. package/lib/transform/forHanaNew.js +109 -208
  102. package/lib/transform/forOdataNew.js +59 -212
  103. package/lib/transform/localized.js +46 -26
  104. package/lib/transform/odata/toFinalBaseType.js +85 -11
  105. package/lib/transform/odata/typesExposure.js +147 -199
  106. package/lib/transform/odata/utils.js +2 -2
  107. package/lib/transform/transformUtilsNew.js +44 -33
  108. package/lib/transform/translateAssocsToJoins.js +3 -20
  109. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  110. package/lib/transform/universalCsn/coreComputed.js +172 -0
  111. package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
  112. package/lib/transform/universalCsn/utils.js +63 -0
  113. package/lib/utils/moduleResolve.js +13 -6
  114. package/lib/utils/objectUtils.js +30 -0
  115. package/package.json +1 -1
  116. package/share/messages/README.md +26 -0
  117. package/share/messages/message-explanations.json +2 -1
  118. package/share/messages/syntax-expected-integer.md +37 -0
  119. package/lib/compiler/definer.js +0 -2361
  120. package/lib/compiler/resolver.js +0 -3079
  121. package/lib/transform/odata/attachPath.js +0 -96
  122. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  123. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  124. package/lib/transform/odata/referenceFlattener.js +0 -290
  125. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  126. package/lib/transform/odata/structuralPath.js +0 -72
  127. package/lib/transform/odata/structureFlattener.js +0 -171
  128. 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,114 @@ 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];
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 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;
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 setMembersImplicit( members, originMembers ) {
425
- if (!members)
426
- return;
427
- for (const name in members) {
428
- const elem = members[name];
429
- if (!elem.type && typeof elem.$origin !== 'object') // undefined or string
430
- setCache( elem, '_origin', originMembers[elem.$origin || name] || false );
431
+ 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
- * 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 ));
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 Error( 'References must look like {ref:[...]}' );
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', refCtx === 'from' );
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 cache = qcache && (semantics.lexical ? semantics.lexical( qcache ) : qcache);
508
- while (cache) {
509
- const alias = tryAlias && cache.$aliases[head];
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', 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;
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) // outside queries - TODO: items?
533
- return resolvePath( path, parent.elements[head], parent, 'parent' );
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 Error ( `Path item ${ 0 }=${ head } refers to nothing, refCtx: ${ refCtx }` );
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 Error ( `Path item ${ 0 }=${ pathId( path[0] ) } refers to nothing, scope: ${ scope }`);
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 Error ( `Path item ${ i }=${ pathId( path[i] ) } on ${ locationString( loc ) } refers to nothing` );
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 && !fromRef)
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 = fromRef( query );
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
- qcache.elements = (index ? qcache._select : main).elements;
628
- const columns = qcache._select.columns;
629
- if (qcache.elements && columns)
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
- // if requested, we could set a _column link in element
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 Error( `Query elements not available: ${ Object.keys( query ).join('+') }`);
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 Error( 'References outside definitions not supported yet');
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
  }