@sap/cds-compiler 3.6.2 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/CHANGELOG.md +109 -1
  2. package/README.md +3 -0
  3. package/bin/cdsc.js +12 -5
  4. package/doc/CHANGELOG_ARCHIVE.md +6 -6
  5. package/doc/CHANGELOG_BETA.md +35 -2
  6. package/doc/CHANGELOG_DEPRECATED.md +2 -2
  7. package/doc/DeprecatedOptions_v2.md +1 -1
  8. package/doc/NameResolution.md +1 -1
  9. package/lib/api/main.js +63 -23
  10. package/lib/api/options.js +1 -0
  11. package/lib/api/validate.js +5 -0
  12. package/lib/base/dictionaries.js +15 -3
  13. package/lib/base/keywords.js +2 -0
  14. package/lib/base/message-registry.js +120 -34
  15. package/lib/base/messages.js +51 -27
  16. package/lib/base/model.js +4 -2
  17. package/lib/base/shuffle.js +2 -1
  18. package/lib/checks/arrayOfs.js +1 -1
  19. package/lib/checks/defaultValues.js +1 -1
  20. package/lib/checks/elements.js +29 -1
  21. package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +10 -6
  22. package/lib/checks/invalidTarget.js +1 -1
  23. package/lib/checks/nonexpandableStructured.js +1 -1
  24. package/lib/checks/onConditions.js +15 -9
  25. package/lib/checks/sql-snippets.js +2 -2
  26. package/lib/checks/types.js +5 -1
  27. package/lib/checks/validator.js +7 -3
  28. package/lib/compiler/assert-consistency.js +42 -26
  29. package/lib/compiler/base.js +50 -4
  30. package/lib/compiler/builtins.js +17 -8
  31. package/lib/compiler/checks.js +241 -246
  32. package/lib/compiler/define.js +113 -146
  33. package/lib/compiler/extend.js +889 -383
  34. package/lib/compiler/finalize-parse-cdl.js +5 -58
  35. package/lib/compiler/index.js +1 -1
  36. package/lib/compiler/kick-start.js +7 -8
  37. package/lib/compiler/populate.js +297 -293
  38. package/lib/compiler/propagator.js +27 -18
  39. package/lib/compiler/resolve.js +146 -463
  40. package/lib/compiler/shared.js +36 -79
  41. package/lib/compiler/tweak-assocs.js +30 -28
  42. package/lib/compiler/utils.js +31 -5
  43. package/lib/edm/annotations/genericTranslation.js +131 -59
  44. package/lib/edm/annotations/preprocessAnnotations.js +3 -0
  45. package/lib/edm/csn2edm.js +22 -5
  46. package/lib/edm/edm.js +6 -4
  47. package/lib/edm/edmAnnoPreprocessor.js +1 -0
  48. package/lib/edm/edmPreprocessor.js +42 -26
  49. package/lib/gen/Dictionary.json +38 -2
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +3 -1
  52. package/lib/gen/languageLexer.js +1 -1
  53. package/lib/gen/languageParser.js +4828 -4472
  54. package/lib/inspect/inspectPropagation.js +20 -34
  55. package/lib/json/from-csn.js +140 -44
  56. package/lib/json/to-csn.js +114 -122
  57. package/lib/language/errorStrategy.js +2 -0
  58. package/lib/language/genericAntlrParser.js +156 -36
  59. package/lib/language/language.g4 +100 -58
  60. package/lib/language/textUtils.js +13 -0
  61. package/lib/main.d.ts +43 -3
  62. package/lib/main.js +4 -2
  63. package/lib/model/csnRefs.js +15 -3
  64. package/lib/model/csnUtils.js +12 -74
  65. package/lib/model/revealInternalProperties.js +4 -2
  66. package/lib/modelCompare/compare.js +2 -1
  67. package/lib/optionProcessor.js +3 -0
  68. package/lib/render/manageConstraints.js +5 -2
  69. package/lib/render/toCdl.js +216 -104
  70. package/lib/render/toHdbcds.js +2 -9
  71. package/lib/render/toRename.js +14 -51
  72. package/lib/render/toSql.js +4 -3
  73. package/lib/render/utils/common.js +9 -5
  74. package/lib/transform/braceExpression.js +6 -0
  75. package/lib/transform/db/assertUnique.js +2 -1
  76. package/lib/transform/db/expansion.js +2 -0
  77. package/lib/transform/db/flattening.js +37 -36
  78. package/lib/transform/db/rewriteCalculatedElements.js +600 -0
  79. package/lib/transform/db/transformExists.js +4 -0
  80. package/lib/transform/db/views.js +40 -37
  81. package/lib/transform/forOdataNew.js +20 -15
  82. package/lib/transform/forRelationalDB.js +58 -41
  83. package/lib/transform/odata/typesExposure.js +50 -15
  84. package/lib/transform/parseExpr.js +16 -8
  85. package/lib/transform/transformUtilsNew.js +42 -14
  86. package/lib/transform/translateAssocsToJoins.js +60 -37
  87. package/lib/transform/universalCsn/coreComputed.js +15 -7
  88. package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
  89. package/package.json +2 -1
@@ -4,7 +4,6 @@
4
4
  'use strict';
5
5
 
6
6
  const { searchName } = require('../base/messages');
7
- const { dictAddArray } = require('../base/dictionaries');
8
7
  const { isDeprecatedEnabled } = require('../base/model');
9
8
 
10
9
  const {
@@ -183,8 +182,6 @@ function fns( model ) {
183
182
  resolveUncheckedPath,
184
183
  resolveTypeArgumentsUnchecked,
185
184
  resolvePath,
186
- checkAnnotate,
187
- copyAnnotationsForExtensions,
188
185
  attachAndEmitValidNames,
189
186
  } );
190
187
  return;
@@ -253,16 +250,12 @@ function fns( model ) {
253
250
  }
254
251
 
255
252
  function checkSourceRef( art, path ) { // for FROM
256
- if (art.kind === 'entity' )
257
- return false;
258
- if (art.kind !== 'element')
259
- return true;
253
+ if (!art._main)
254
+ return (art.kind !== 'entity');
260
255
  const elem = path.find( item => item._artifact._main )._artifact;
261
- // TODO: better error location if error for main
262
- if (elem._main.kind !== 'entity' )
263
- return true; // elem not starting at entity
264
- VolatileFns.environment( art ); // sets _effectiveType on art
265
- return !(art._effectiveType || art).target;
256
+ // at least the last main definition should be an entity
257
+ // an additional check for target would need effectiveType()
258
+ return (elem && elem._main.kind !== 'entity');
266
259
  }
267
260
 
268
261
  // Return absolute name for unchecked path `ref`. We first try searching for
@@ -307,13 +300,12 @@ function fns( model ) {
307
300
  function resolvePath( ref, expected, user, extDict, msgArt ) {
308
301
  if (ref == null) // no references -> nothing to do
309
302
  return undefined;
310
- if ('_artifact' in ref) // also true for _artifact: undefined
303
+ if (ref._artifact !== undefined)
311
304
  return ref._artifact;
312
305
  if (!ref.path || ref.path.broken || !ref.path.length) {
313
306
  // incomplete type AST or empty env (already reported)
314
307
  return setArtifactLink( ref, undefined );
315
308
  }
316
- setArtifactLink( ref, 0 ); // avoid cycles for type T: association to T.m;
317
309
 
318
310
  let spec = specExpected[expected];
319
311
  const { path } = ref;
@@ -324,10 +316,12 @@ function fns( model ) {
324
316
 
325
317
  if (ref.scope === 'param') {
326
318
  if (!spec.escape) {
327
- error( 'ref-unexpected-scope', [ ref.location, user ], {},
328
- 'Unexpected parameter reference' );
319
+ error( 'ref-unexpected-scope', [ ref.location, user ], { '#': 'std' } );
329
320
  return setArtifactLink( ref, null );
330
321
  }
322
+ if (user.$syntax === 'calc')
323
+ error('ref-unexpected-scope', [ ref.location, user ], { '#': 'calc' } );
324
+
331
325
  spec = specExpected[spec.escape];
332
326
  // In queries and query entities, the first lexical search environment
333
327
  // are the parameters, otherwise the block. It is currently ensured that
@@ -363,8 +357,12 @@ function fns( model ) {
363
357
  return setArtifactLink( ref, art );
364
358
  }
365
359
  else if (!spec.envFn && user._pathHead) {
366
- if (art.kind === '$self')
367
- rejectBareSelf( spec, path, user, extDict );
360
+ if (art.kind === '$self') {
361
+ const headEnv = VolatileFns.environment( user._pathHead ) &&
362
+ user._pathHead._origin &&
363
+ VolatileFns.environment( user._pathHead._origin );
364
+ rejectBareSelf( spec, path, user, headEnv );
365
+ }
368
366
  }
369
367
  else if (art.kind === 'using') {
370
368
  const def = model.definitions[art.name.absolute];
@@ -464,13 +462,13 @@ function fns( model ) {
464
462
  spec.noDep === 'only-entity' && art.kind !== 'entity')) {
465
463
  const { location } = ref; // || combinedLocation( head, path[tail.length] );
466
464
  // TODO: location of last path item if not main artifact
467
- if (!art._main || spec.assoc !== 'from') {
468
- dependsOn( user, art, location );
469
- }
470
- else {
465
+ if (spec.assoc === 'from' && art._main) {
471
466
  dependsOn( user, art._main, location );
472
467
  VolatileFns.environment( art, location, user );
473
- // Without on-demand resolve, we can simply signal 'undefined "x"'
468
+ }
469
+ else if (art.kind !== 'select') { // no real dependency to bare $self
470
+ dependsOn( user, art, location );
471
+ // Without on-demand resolve, we can simply signal 'undefined "x"'
474
472
  // instead of 'illegal cycle' in the following case:
475
473
  // element elem: type of elem.x;
476
474
  }
@@ -599,7 +597,7 @@ function fns( model ) {
599
597
  // if (head.id === 'k') {console.log(Object.keys(user));
600
598
  // throw new CompilerAssertion(JSON.stringify(user.name))}
601
599
  // if head._artifact is set or is null then it was already computed once
602
- if ('_artifact' in head)
600
+ if (head._artifact !== undefined)
603
601
  return Array.isArray(head._artifact) ? false : head._artifact;
604
602
  // console.log(pathName(path), !spec.next && !extDict &&
605
603
  // (spec.useDefinitions || env.$frontend === 'json' || env))
@@ -617,7 +615,7 @@ function fns( model ) {
617
615
  break; // TODO: probably remove _$next link
618
616
  const e = art.artifacts || art.$tableAliases || Object.create(null);
619
617
  const r = e[head.id];
620
- if (r) {
618
+ if (r && r.$inferred !== '$internal') {
621
619
  if (Array.isArray(r)) { // redefinitions
622
620
  setArtifactLink( head, r );
623
621
  return false;
@@ -651,11 +649,14 @@ function fns( model ) {
651
649
  if (r[0].kind === '$navElement' && r.every( e => !e._parent.$duplicates )) {
652
650
  // only complain about ambiguous source elements if we do not have
653
651
  // duplicate table aliases, only mention non-ambiguous source elems
654
- const names = r.filter( e => !e.$duplicates )
655
- .map( e => `${ e.name.alias }.${ e.name.element }` );
656
- if (names.length) {
657
- error( 'ref-ambiguous', [ head.location, user ], { id: head.id, names },
658
- 'Ambiguous $(ID), replace by $(NAMES)' );
652
+ const uniqueNames = r.filter( e => !e.$duplicates);
653
+ if (uniqueNames.length) {
654
+ const names = uniqueNames.filter( e => e._parent.$inferred !== '$internal' )
655
+ .map( e => `${ e.name.alias }.${ e.name.element }` );
656
+ let variant = names.length === uniqueNames.length ? 'std' : 'few';
657
+ if (names.length === 0)
658
+ variant = 'none';
659
+ error( 'ref-ambiguous', [ head.location, user ], { '#': variant, id: head.id, names });
659
660
  }
660
661
  }
661
662
  setArtifactLink( head, r );
@@ -772,12 +773,13 @@ function fns( model ) {
772
773
 
773
774
  const fn = (spec.envFn && artItemsCount >= 0) ? spec.envFn : VolatileFns.environment;
774
775
  const env = fn( art, item.location, user, spec.assoc );
775
- const sub = setArtifactLink( item, env && env[item.id] );
776
+ const sub = setArtifactLink( item, env?.[item.id] );
776
777
 
777
778
  if (!sub) {
778
779
  // element was not found in environment
779
- if (sub === 0)
780
- return 0;
780
+
781
+ // TODO (done?): if `env` was 0, we might set a dependency to induce an
782
+ // illegal-cycle error instead of reporting via `errorNotFound`.
781
783
  if (art.$uncheckedElements) { // magic variable / replacement variable
782
784
  signalNotFound( 'ref-unknown-var', [ item.location, user ], [ env ],
783
785
  { id: pathName( path ) } );
@@ -935,7 +937,7 @@ function fns( model ) {
935
937
  msg.validNames = Object.create( null );
936
938
  for (const name of Object.keys( valid )) {
937
939
  // ignore internal types such as cds.Association
938
- if (valid[name].internal || valid[name].deprecated)
940
+ if (valid[name].internal || valid[name].deprecated || valid[name].$inferred === '$internal')
939
941
  continue;
940
942
  msg.validNames[name] = valid[name];
941
943
  }
@@ -949,51 +951,6 @@ function fns( model ) {
949
951
  { std: `Valid: ${ names.sort().join(', ') }`, zero: 'No valid names' });
950
952
  }
951
953
  }
952
-
953
- // Issue messages for annotations on namespaces and builtins
954
- // (TODO: really here?, probably split main artifacts vs returns)
955
- // see also lateExtensions() where similar messages are reported
956
- function checkAnnotate( construct, art ) {
957
- // TODO: Handle extend statements properly: Different message for empty extend?
958
-
959
- // Namespaces cannot be annotated in CSN but because they exist as XSN artifacts
960
- // they can still be applied. Namespace annotations are extracted in to-csn.js
961
- // In parseCdl mode USINGs and other unknown references are generated as
962
- // namespaces which would lead to false positives.
963
- // TODO: should this really be different to annotate-unknown?
964
- if (art.kind === 'namespace') {
965
- info( 'anno-namespace', [ construct.name.location, construct ], {},
966
- 'Namespaces can\'t be annotated' );
967
- }
968
- // Builtin annotations would also get lost. Same as for namespaces:
969
- // extracted in to-csn.js
970
- else if (art.builtin === true) {
971
- info( 'anno-builtin', [ construct.name.location, construct ], {},
972
- 'Builtin types should not be annotated. Use custom type instead' );
973
- }
974
- else if (construct.$syntax === 'returns' && art._block && art.kind !== 'action' &&
975
- art.kind !== 'function' ) {
976
- // `annotate ABC with returns {}` is handled just like `elements`. Warn if it is used
977
- // for non-actions. We can't only check for !art.returns, because `action A();` is valid.
978
- // `art._block` ensures that `art` is a defined def.
979
- warning('anno-unexpected-returns', [ construct.name.location, construct ],
980
- { keyword: 'returns', meta: art.kind }, 'Unexpected $(KEYWORD) for $(META)');
981
- }
982
- }
983
-
984
- // Copy annotations from `ext` to `art`, overwriting inferred ones.
985
- // TODO: move to extend.js if not used anymore in define.js
986
- function copyAnnotationsForExtensions( ext, art ) {
987
- for (const annoProp in ext) {
988
- if (annoProp.charAt(0) === '@' || annoProp === 'doc') {
989
- const extAnno = ext[annoProp];
990
- if (art[annoProp]?.$inferred)
991
- art[annoProp] = extAnno; // overwrite $inferred annos
992
- else
993
- dictAddArray( art, annoProp, extAnno );
994
- }
995
- }
996
- }
997
954
  }
998
955
 
999
956
  module.exports = {
@@ -3,7 +3,6 @@
3
3
  'use strict';
4
4
 
5
5
  const {
6
- forEachDefinition,
7
6
  forEachGeneric,
8
7
  forEachInOrder,
9
8
  } = require('../base/model');
@@ -31,19 +30,13 @@ function tweakAssocs( model ) {
31
30
  } = model.$messageFunctions;
32
31
  const {
33
32
  effectiveType,
34
- directType,
33
+ getOrigin,
35
34
  resolveExpr,
36
35
  } = model.$functions;
37
36
  const { environment } = model.$volatileFunctions;
38
37
 
39
38
  // Phase 5: rewrite associations
40
- forEachDefinition( model, rewriteSimple );
41
- // TODO: sequence not good enough with derived type of structure with
42
- // includes: first "direct" structures, then _entities, then the rest.
43
- // v2: We might run a silent cycle detection earlier, then we could use the
44
- // SCC number (_scc.lowlink) to sort.
45
- model._entities.forEach( rewriteView );
46
- model._entities.forEach( rewriteViewCheck );
39
+ model._entities.forEach( rewriteArtifact );
47
40
  // Think hard whether an on condition rewrite can lead to a new cylcic
48
41
  // dependency. If so, we need other messages anyway. TODO: probably do
49
42
  // another cyclic check with testMode.js
@@ -55,33 +48,41 @@ function tweakAssocs( model ) {
55
48
  //--------------------------------------------------------------------------
56
49
  // Only top-level queries and sub queries in FROM
57
50
 
58
- function rewriteSimple( art ) {
51
+ function rewriteArtifact( art ) {
59
52
  // return;
60
- if (!art.includes && !art.query) {
53
+ if (!art.query) {
61
54
  // console.log(message( null, art.location, art, {target:art._target},
62
55
  // 'Info','RAS').toString())
63
56
  rewriteAssociation( art );
64
57
  forEachGeneric( art, 'elements', rewriteAssociation );
65
58
  }
59
+ else {
60
+ traverseQueryExtra( art, ( query ) => {
61
+ forEachGeneric( query, 'elements', rewriteAssociation );
62
+ } );
63
+ }
66
64
  if (art._service)
67
65
  forEachGeneric( art, 'elements', excludeAssociation );
68
- }
69
66
 
70
- function rewriteView( view ) {
71
- traverseQueryExtra( view, ( query ) => {
72
- forEachGeneric( query, 'elements', rewriteAssociation );
67
+ traverseQueryPost( art.query, false, ( query ) => {
68
+ forEachGeneric( query, 'elements', rewriteAssociationCheck );
73
69
  } );
74
- if (view.includes) // entities with structure includes:
75
- forEachGeneric( view, 'elements', rewriteAssociation );
76
70
  }
77
71
 
72
+ // function rewriteView( view ) {
73
+ // // TODO: we could sort according to the $effectiveSeqNo instead
74
+ // // (and then remove traverseQueryExtra)
75
+ // if (view.includes) // entities with structure includes:
76
+ // forEachGeneric( view, 'elements', rewriteAssociation );
77
+ // }
78
+
78
79
  // Check explicit ON / keys with REDIRECTED TO
79
80
  // TODO: run on all queries, but this is potentially incompatible
80
- function rewriteViewCheck( view ) {
81
- traverseQueryPost( view.query, false, ( query ) => {
82
- forEachGeneric( query, 'elements', rewriteAssociationCheck );
83
- } );
84
- }
81
+ // function rewriteViewCheck( view ) {
82
+ // traverseQueryPost( view.query, false, ( query ) => {
83
+ // forEachGeneric( query, 'elements', rewriteAssociationCheck );
84
+ // } );
85
+ // }
85
86
 
86
87
  function excludeAssociation( elem ) {
87
88
  const target = elem.target && elem.target._artifact;
@@ -112,7 +113,7 @@ function tweakAssocs( model ) {
112
113
  if (!elem.target)
113
114
  return;
114
115
  if (elem.on && !elem.on.$inferred) {
115
- const assoc = directType( elem );
116
+ const assoc = getOrigin( elem );
116
117
  if (assoc && assoc.foreignKeys) {
117
118
  error( 'rewrite-key-for-unmanaged', [ elem.on.location, elem ],
118
119
  { keyword: 'on', art: assocWithExplicitSpec( assoc ) },
@@ -121,7 +122,7 @@ function tweakAssocs( model ) {
121
122
  }
122
123
  }
123
124
  else if (elem.foreignKeys && !inferredForeignKeys( elem.foreignKeys )) {
124
- const assoc = directType( elem );
125
+ const assoc = getOrigin( elem );
125
126
  if (assoc && assoc.on) {
126
127
  error( 'rewrite-on-for-managed',
127
128
  [ elem.foreignKeys[$location] || dictLocation( elem.foreignKeys ), elem ],
@@ -195,7 +196,7 @@ function tweakAssocs( model ) {
195
196
  function assocWithExplicitSpec( assoc ) {
196
197
  while (assoc.foreignKeys && inferredForeignKeys( assoc.foreignKeys, 'keys') ||
197
198
  assoc.on && assoc.on.$inferred)
198
- assoc = directType( assoc );
199
+ assoc = getOrigin( assoc );
199
200
  return assoc;
200
201
  }
201
202
 
@@ -211,6 +212,7 @@ function tweakAssocs( model ) {
211
212
  // With cyclic dependencies on select items, testing for the _effectiveType to
212
213
  // be 0 (test above) is not enough if we we have an explicit redirection
213
214
  // target -> avoid infloop ourselves with _status.
215
+ // TODO: this should be good now
214
216
  const chain = [];
215
217
  while (!elem.on && !elem.foreignKeys) {
216
218
  chain.push( elem );
@@ -220,7 +222,7 @@ function tweakAssocs( model ) {
220
222
  return;
221
223
  }
222
224
  setLink( elem, '_status', 'rewrite' );
223
- elem = directType( elem );
225
+ elem = getOrigin( elem );
224
226
  if (!elem || elem.builtin) // safety
225
227
  return;
226
228
  }
@@ -236,7 +238,7 @@ function tweakAssocs( model ) {
236
238
  }
237
239
 
238
240
  function originTarget( elem ) {
239
- const assoc = !elem.expand && directType( elem );
241
+ const assoc = !elem.expand && getOrigin( elem );
240
242
  const ftype = assoc && effectiveType( assoc );
241
243
  return ftype && ftype.target && ftype.target._artifact;
242
244
  }
@@ -254,7 +256,7 @@ function tweakAssocs( model ) {
254
256
  const fk = linkToOrigin( orig, name, elem, 'foreignKeys', elem.location );
255
257
  fk.$inferred = 'rewrite'; // Override existing value; TODO: other $inferred value?
256
258
  // TODO: re-check for case that foreign key is managed association
257
- if ('_effectiveType' in orig)
259
+ if (orig._effectiveType !== undefined)
258
260
  setLink( fk, '_effectiveType', orig._effectiveType);
259
261
  const te = copyExpr( orig.targetElement, elem.location );
260
262
  if (elem._redirected) {
@@ -10,7 +10,7 @@
10
10
 
11
11
  'use strict';
12
12
 
13
- const { dictAdd, pushToDict } = require('../base/dictionaries');
13
+ const { dictAdd, pushToDict, dictFirst } = require('../base/dictionaries');
14
14
  const { kindProperties } = require('./base');
15
15
 
16
16
  // for links, i.e., properties starting with an underscore '_':
@@ -55,7 +55,7 @@ function annotationLocation( anno ) {
55
55
  * @param {*} [val]
56
56
  * @param {string} [literal]
57
57
  */
58
- function annotateWith( art, anno, location = art.location, val = true, literal = 'boolean' ) {
58
+ function setAnnotation( art, anno, location = art.location, val = true, literal = 'boolean' ) {
59
59
  if (art[anno]) // do not overwrite user-defined including null
60
60
  return;
61
61
  art[anno] = {
@@ -71,10 +71,11 @@ function annotateWith( art, anno, location = art.location, val = true, literal =
71
71
 
72
72
  // The link (_artifact,_effectiveType,...) usually has the artifact as value.
73
73
  // Falsy values are:
74
- // - undefined: not computed yet, parse error, no ref
75
- // - null: no valid reference, param:true if that is not allowed
74
+ // - undefined: not computed yet, parse error (TODO: null), no ref
75
+ // - null: ref to unknown, param:true if that is not allowed (TODO: false)
76
76
  // - false (only complete ref): multiple definitions, rejected
77
77
  // - 0 (for _effectiveType only): circular reference
78
+ // - '' (for _origin only): no origin provided
78
79
  function setLink( obj, prop, value ) {
79
80
  Object.defineProperty( obj, prop, { value, configurable: true, writable: true } );
80
81
  return value;
@@ -105,6 +106,20 @@ function linkToOrigin( origin, name, parent, prop, location, silentDep ) {
105
106
  return elem;
106
107
  }
107
108
 
109
+ function proxyCopyMembers( art, dictProp, originDict, location, kind ) {
110
+ art[dictProp] = Object.create( null );
111
+ for (const name in originDict) {
112
+ const origin = originDict[name];
113
+ const member = linkToOrigin( origin, name, art, dictProp,
114
+ location || origin.location, true );
115
+ member.$inferred = 'expanded';
116
+ if (kind)
117
+ member.kind = kind;
118
+ if (kind && origin.masked) // TODO: remove!
119
+ member.masked = Object.assign( { $inferred: 'nav' }, origin.masked );
120
+ }
121
+ }
122
+
108
123
  /**
109
124
  * Set the member `elem` to have a _parent link to `parent` and a corresponding
110
125
  * _main link. Also set the member's name accordingly, where argument `name`
@@ -370,6 +385,15 @@ function traverseQueryExtra( main, callback ) {
370
385
  }
371
386
  }
372
387
 
388
+ // Returns what was available at view._from[0] before:
389
+ // (think first whether to really use this function)
390
+ function viewFromPrimary( view ) {
391
+ let query = view.$queries?.[0];
392
+ while (query?._origin?.kind === 'select') // sub query in from
393
+ query = query._origin;
394
+ return dictFirst( query?.$tableAliases );
395
+ }
396
+
373
397
  // About Helper property $expand for faster the XSN-to-CSN transformation
374
398
  // - null/undefined: artifact, member, items does not contain expanded members
375
399
  // - 'origin': all expanded (sub) elements have no new target/on and no new annotations
@@ -424,10 +448,11 @@ module.exports = {
424
448
  annotationIsFalse,
425
449
  annotationHasEllipsis,
426
450
  annotationLocation,
427
- annotateWith,
451
+ setAnnotation,
428
452
  setLink,
429
453
  setArtifactLink,
430
454
  linkToOrigin,
455
+ proxyCopyMembers,
431
456
  dependsOn,
432
457
  dependsOnSilent,
433
458
  setMemberParent,
@@ -441,6 +466,7 @@ module.exports = {
441
466
  targetMaxNotOne,
442
467
  traverseQueryPost,
443
468
  traverseQueryExtra,
469
+ viewFromPrimary,
444
470
  setExpandStatus,
445
471
  setExpandStatusAnnotate,
446
472
  isDirectComposition,