@sap/cds-compiler 3.9.2 → 4.0.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 (96) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/README.md +0 -1
  3. package/bin/cdsc.js +11 -23
  4. package/bin/cdsse.js +3 -3
  5. package/doc/API.md +5 -0
  6. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  7. package/doc/CHANGELOG_BETA.md +17 -1
  8. package/doc/CHANGELOG_DEPRECATED.md +28 -0
  9. package/lib/api/.eslintrc.json +1 -1
  10. package/lib/api/main.js +26 -8
  11. package/lib/api/options.js +2 -0
  12. package/lib/base/error.js +2 -0
  13. package/lib/base/message-registry.js +144 -65
  14. package/lib/base/messages.js +213 -107
  15. package/lib/base/model.js +11 -11
  16. package/lib/checks/.eslintrc.json +1 -1
  17. package/lib/checks/annotationsOData.js +2 -2
  18. package/lib/checks/elements.js +1 -1
  19. package/lib/checks/enricher.js +26 -3
  20. package/lib/checks/onConditions.js +67 -12
  21. package/lib/checks/queryNoDbArtifacts.js +106 -105
  22. package/lib/checks/sql-snippets.js +2 -0
  23. package/lib/checks/types.js +12 -6
  24. package/lib/checks/validator.js +2 -2
  25. package/lib/compiler/assert-consistency.js +10 -8
  26. package/lib/compiler/builtins.js +8 -2
  27. package/lib/compiler/checks.js +52 -35
  28. package/lib/compiler/define.js +31 -26
  29. package/lib/compiler/extend.js +120 -65
  30. package/lib/compiler/finalize-parse-cdl.js +12 -43
  31. package/lib/compiler/generate.js +16 -5
  32. package/lib/compiler/index.js +8 -5
  33. package/lib/compiler/kick-start.js +4 -3
  34. package/lib/compiler/populate.js +96 -95
  35. package/lib/compiler/propagator.js +7 -8
  36. package/lib/compiler/resolve.js +377 -103
  37. package/lib/compiler/shared.js +794 -517
  38. package/lib/compiler/tweak-assocs.js +8 -6
  39. package/lib/compiler/utils.js +44 -0
  40. package/lib/edm/annotations/genericTranslation.js +24 -6
  41. package/lib/edm/csn2edm.js +47 -45
  42. package/lib/edm/edm.js +34 -31
  43. package/lib/edm/edmAnnoPreprocessor.js +0 -23
  44. package/lib/edm/edmInboundChecks.js +7 -2
  45. package/lib/edm/edmPreprocessor.js +18 -17
  46. package/lib/edm/edmUtils.js +8 -4
  47. package/lib/gen/Dictionary.json +18 -0
  48. package/lib/gen/language.checksum +1 -1
  49. package/lib/gen/language.interp +4 -2
  50. package/lib/gen/languageParser.js +5006 -4582
  51. package/lib/json/from-csn.js +159 -114
  52. package/lib/json/to-csn.js +60 -89
  53. package/lib/language/antlrParser.js +17 -13
  54. package/lib/language/docCommentParser.js +11 -1
  55. package/lib/language/genericAntlrParser.js +13 -10
  56. package/lib/language/language.g4 +168 -97
  57. package/lib/main.d.ts +128 -36
  58. package/lib/main.js +1 -1
  59. package/lib/model/csnRefs.js +24 -5
  60. package/lib/model/csnUtils.js +9 -8
  61. package/lib/model/revealInternalProperties.js +7 -12
  62. package/lib/modelCompare/compare.js +1 -1
  63. package/lib/modelCompare/utils/filter.js +40 -2
  64. package/lib/optionProcessor.js +0 -3
  65. package/lib/render/toCdl.js +247 -214
  66. package/lib/render/toHdbcds.js +197 -181
  67. package/lib/render/toSql.js +325 -289
  68. package/lib/render/utils/common.js +42 -4
  69. package/lib/render/utils/delta.js +1 -1
  70. package/lib/render/utils/sql.js +3 -3
  71. package/lib/transform/braceExpression.js +2 -2
  72. package/lib/transform/db/.eslintrc.json +1 -1
  73. package/lib/transform/db/applyTransformations.js +3 -3
  74. package/lib/transform/db/associations.js +24 -12
  75. package/lib/transform/db/expansion.js +17 -18
  76. package/lib/transform/db/flattening.js +17 -21
  77. package/lib/transform/db/rewriteCalculatedElements.js +171 -64
  78. package/lib/transform/db/views.js +3 -4
  79. package/lib/transform/draft/db.js +21 -12
  80. package/lib/transform/draft/odata.js +4 -0
  81. package/lib/transform/forOdataNew.js +11 -10
  82. package/lib/transform/forRelationalDB.js +12 -7
  83. package/lib/transform/localized.js +5 -3
  84. package/lib/transform/odata/toFinalBaseType.js +5 -5
  85. package/lib/transform/odata/typesExposure.js +3 -3
  86. package/lib/transform/parseExpr.js +3 -0
  87. package/lib/transform/transformUtilsNew.js +43 -23
  88. package/lib/transform/translateAssocsToJoins.js +7 -6
  89. package/lib/transform/universalCsn/.eslintrc.json +1 -1
  90. package/lib/transform/universalCsn/coreComputed.js +7 -5
  91. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -12
  92. package/lib/utils/file.js +3 -3
  93. package/lib/utils/moduleResolve.js +1 -1
  94. package/package.json +2 -2
  95. package/share/messages/{duplicate-autoexposed.md → def-duplicate-autoexposed.md} +5 -1
  96. package/share/messages/message-explanations.json +1 -1
@@ -139,6 +139,7 @@ function getParentContextName( csn, artifactName ) {
139
139
  *
140
140
  * Context A.B will be created by addIntermediateContexts
141
141
  *
142
+ * @param {CSN.Model} csn
142
143
  * @param {Function[]} killList Array to add cleanup functions to
143
144
  */
144
145
  function addContextMarkers( csn, killList ) {
@@ -171,7 +172,7 @@ function addContextMarkers( csn, killList ) {
171
172
  *
172
173
  * A and A.B.C.D -> A.B and A.B.C are possible candidates
173
174
  *
174
- *
175
+ * @param {CSN.Model} csn
175
176
  * @param {string} parentName Name of the parent context
176
177
  * @param {string} artifactName Name of the current context
177
178
  * @returns {string[]} All possible context names inbetween
@@ -311,6 +312,25 @@ const cdsToHdbcdsTypes = {
311
312
  'cds.Int64': 'cds.Integer64',
312
313
  };
313
314
 
315
+ /**
316
+ * Default lengths for CDS types.
317
+ */
318
+ const sqlDefaultLengths = {
319
+ hana: {
320
+ 'cds.String': 5000,
321
+ },
322
+ default: {
323
+ 'cds.String': 255,
324
+ 'cds.Binary': 5000,
325
+ },
326
+ };
327
+
328
+ function getDefaultTypeLengths( sqlDialect ) {
329
+ if (!sqlDefaultLengths[sqlDialect])
330
+ return sqlDefaultLengths.default;
331
+ return { ...sqlDefaultLengths.default, ...sqlDefaultLengths[sqlDialect] };
332
+ }
333
+
314
334
  /**
315
335
  * Get the element matching the column
316
336
  *
@@ -467,9 +487,10 @@ function withoutCast( xpr ) {
467
487
  * (no trailing LF, don't indent if inline)
468
488
  *
469
489
  * @param {ExpressionConfiguration} rendererBase
490
+ * @param {boolean} [adaptPath] If true, `env.path` will be adapted for lists and subExpr.
470
491
  * @returns {ExpressionRenderer} Expression rendering utility
471
492
  */
472
- function createExpressionRenderer( rendererBase ) {
493
+ function createExpressionRenderer( rendererBase, adaptPath = false ) {
473
494
  const renderer = Object.create(rendererBase);
474
495
  renderer.visitExpr = visitExpr;
475
496
  /**
@@ -484,6 +505,7 @@ function createExpressionRenderer( rendererBase ) {
484
505
  // are nested. This information is used for adding parentheses around
485
506
  // expressions (see `this.xpr()`).
486
507
  renderObj.isNestedXpr = false;
508
+ renderObj.adaptPath = adaptPath;
487
509
  return renderObj.visitExpr(x);
488
510
  };
489
511
  /**
@@ -495,6 +517,7 @@ function createExpressionRenderer( rendererBase ) {
495
517
  const renderObj = Object.create(renderer);
496
518
  renderObj.env = env || this?.env;
497
519
  renderObj.isNestedXpr = true;
520
+ renderObj.adaptPath = adaptPath;
498
521
  return renderObj.visitExpr(x);
499
522
  };
500
523
 
@@ -519,7 +542,14 @@ function visitExpr( x ) {
519
542
  // Compound expression, e.g. for on- or where-conditions.
520
543
  // If xpr is part of an array, it's always a nested xpr,
521
544
  // e.g. CSN for `(1=1 or 2=2) and 3=3`.
522
- const tokens = x.map(item => this.renderSubExpr(item));
545
+ const tokens = x.map((item, i) => {
546
+ if (this.adaptPath) {
547
+ // We want to keep the prototype of the original env.
548
+ const env = Object.assign(Object.create(Object.getPrototypeOf(this.env || {})), this.env, { path: [ ...this.env.path, i ] });
549
+ return this.renderSubExpr(item, env);
550
+ }
551
+ return this.renderSubExpr(item);
552
+ });
523
553
  return beautifyExprArray(tokens);
524
554
  }
525
555
  else if (typeof x !== 'object' || x === null) {
@@ -531,7 +561,14 @@ function visitExpr( x ) {
531
561
  }
532
562
  else if (x.list) {
533
563
  // Render as non-nested expr.
534
- return `(${x.list.map(item => this.renderExpr(item)).join(', ')})`;
564
+ return `(${x.list.map((item, i) => {
565
+ if (this.adaptPath) {
566
+ // We want to keep the prototype of the original env.
567
+ const env = Object.assign(Object.create(Object.getPrototypeOf(this.env || {})), this.env, { path: [ ...this.env.path, 'list', i ] });
568
+ return this.renderExpr(item, env);
569
+ }
570
+ return this.renderExpr(item);
571
+ }).join(', ')})`;
535
572
  }
536
573
  else if (x.val !== undefined) {
537
574
  return this.val(x);
@@ -581,4 +618,5 @@ module.exports = {
581
618
  funcWithoutParen,
582
619
  getSqlSnippets,
583
620
  withoutCast,
621
+ getDefaultTypeLengths,
584
622
  };
@@ -24,7 +24,7 @@ class DeltaRenderer {
24
24
  // TODO: May also include 'RENAME' at a later stage
25
25
  const alterEnv = this.scopedFunctions.activateAlterMode(env);
26
26
  const elements = Object.entries(elementsObj)
27
- .map(([ name, elt ]) => this.scopedFunctions.renderElement(artifactName, name, elt, duplicateChecker, null, alterEnv))
27
+ .map(([ name, elt ]) => this.scopedFunctions.renderElement(name, elt, duplicateChecker, null, alterEnv))
28
28
  .filter(s => s !== '');
29
29
 
30
30
  if (elements.length)
@@ -42,7 +42,7 @@ function renderReferentialConstraint( constraint, indent, toUpperCase, csn, opti
42
42
  const onDeleteRemark = constraint.onDeleteRemark ? ` -- ${constraint.onDeleteRemark}` : '';
43
43
 
44
44
  // omit 'RESTRICT' action for ON UPDATE / ON DELETE, because it interferes with deferred constraint check
45
- if (sqlDialect === 'sqlite') {
45
+ if (sqlDialect === 'sqlite' || sqlDialect === 'postgres') {
46
46
  if (constraint.onDelete === 'CASCADE' )
47
47
  result += `${indent}ON DELETE ${constraint.onDelete}${onDeleteRemark}\n`;
48
48
  }
@@ -57,8 +57,8 @@ function renderReferentialConstraint( constraint, indent, toUpperCase, csn, opti
57
57
  result += `${indent}${!constraint.validated ? 'NOT ' : ''}VALIDATED\n`;
58
58
  result += `${indent}${!constraint.enforced ? 'NOT ' : ''}ENFORCED\n`;
59
59
  }
60
- // for sqlite, the DEFERRABLE keyword is required
61
- result += `${indent}${sqlDialect === 'sqlite' ? 'DEFERRABLE ' : ''}INITIALLY DEFERRED`;
60
+ // for sqlite and postgreSQL, the DEFERRABLE keyword is required
61
+ result += `${indent}${sqlDialect === 'sqlite' || sqlDialect === 'postgres' ? 'DEFERRABLE ' : ''}INITIALLY DEFERRED`;
62
62
  return result;
63
63
  }
64
64
 
@@ -22,7 +22,7 @@ function binarycomparison(expression, token, index){
22
22
  return index + 3;
23
23
  }
24
24
 
25
- function beetween(expression, token, index){
25
+ function between(expression, token, index){
26
26
  let start = index-1, end = index+4;
27
27
  if(expression[index-1] === 'not'){
28
28
  start -= 1;
@@ -57,7 +57,7 @@ const bracers = {
57
57
  '>=': binarycomparison,
58
58
  '<=': binarycomparison,
59
59
  '!=': binarycomparison,
60
- 'between': beetween,
60
+ 'between': between,
61
61
  'like': like
62
62
  }
63
63
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "root": true,
3
3
  "plugins": ["sonarjs", "jsdoc"],
4
- "extends": ["../../../.eslintrc-ydkjsi.json", "plugin:sonarjs/recommended", "plugin:jsdoc/recommended"],
4
+ "extends": ["plugin:jsdoc/recommended", "../../../.eslintrc-ydkjsi.json", "plugin:sonarjs/recommended"],
5
5
  "rules": {
6
6
  "prefer-const": "error",
7
7
  "quotes": ["error", "single", "avoid-escape"],
@@ -60,7 +60,7 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
60
60
  !{}.propertyIsEnumerable.call( _parent, _prop ) ||
61
61
  (typeof _prop === 'string' && _prop.startsWith('@')) ||
62
62
  (options.skipIgnore && node._ignore) ||
63
- (options.skipStandard && options.skipStandard[_prop])
63
+ options.skipStandard?.[_prop]
64
64
  )
65
65
  return;
66
66
 
@@ -137,17 +137,17 @@ function applyTransformationsInternal( parent, prop, customTransformers, artifac
137
137
  csnPath.push( _prop );
138
138
  _path.forEach( ( s, i ) => {
139
139
  if (s && typeof s === 'object') {
140
- csnPath.push( i );
141
140
  if (options.drillRef) {
142
141
  standard(_path, i, s);
143
142
  }
144
143
  else {
144
+ csnPath.push( i );
145
145
  if (s.args)
146
146
  standard( s, 'args', s.args );
147
147
  if (s.where)
148
148
  standard( s, 'where', s.where );
149
+ csnPath.pop();
149
150
  }
150
- csnPath.pop();
151
151
  }
152
152
  } );
153
153
  csnPath.pop();
@@ -118,7 +118,10 @@ function getFKAccessFinalizer( csn, csnUtils, pathDelimiter ) {
118
118
  * Loop over all elements and for all unmanaged associations translate
119
119
  * <assoc base>.<managed assoc>.<fk> to <assoc base>.<managed assoc>_<fk>
120
120
  *
121
- * Or in other words: Allow using the foreign keys of managed associations in on-conditions
121
+ * Or in other words: Allow using the foreign keys of managed associations in
122
+ * on-conditions / calculated elements on-write.
123
+ *
124
+ * Expects that flattening has already been performed.
122
125
  *
123
126
  * @param {CSN.Artifact} artifact Artifact to check
124
127
  * @param {string} artifactName Name of the artifact
@@ -130,6 +133,7 @@ function getFKAccessFinalizer( csn, csnUtils, pathDelimiter ) {
130
133
  if (ref.length > 1) {
131
134
  const { links } = inspectRef(path);
132
135
  if (links) {
136
+ let fkAlias = '';
133
137
  // eslint-disable-next-line for-direction
134
138
  for (let i = links.length - 1; i >= 0; i--) {
135
139
  const link = links[i];
@@ -137,19 +141,25 @@ function getFKAccessFinalizer( csn, csnUtils, pathDelimiter ) {
137
141
  if (link.art && link.art.target && link.art.keys &&
138
142
  // Doesn't work when ref-target (filter condition) or similar is used
139
143
  !ref.slice(i).some(refElement => typeof refElement !== 'string')) {
140
- // We join the managed assoc with everything following it
141
- const sourceElementName = ref.slice(i).join(pathDelimiter);
142
- const source = findSource(links, i - 1) || artifact;
143
- // allow specifying managed assoc on the source side
144
- const fks = link.art.keys.filter(fk => ref[i] + pathDelimiter + fk.ref[0] === sourceElementName);
145
- if (fks && fks.length >= 1) {
146
- const fk = fks[0];
147
- const managedAssocStepName = refOwner.ref[i];
148
- const fkName = `${managedAssocStepName}${pathDelimiter}${fk.as}`;
149
- if (source && source.elements[fkName])
150
- refOwner.ref = [ ...ref.slice(0, i), fkName ];
144
+ const fkRef = ref[i + 1];
145
+ const fkName = (!fkAlias ? fkRef : `${fkRef}${pathDelimiter}${fkAlias}`);
146
+ const fks = link.art.keys.filter(key => key.ref[0] === fkName);
147
+
148
+ if (fks.length >= 1) { // after flattening, at most one FK will remain.
149
+ // `.as` is set for SQL, but not for OData -> fall back to implicit alias
150
+ fkAlias = fks[0].as || fks[0].ref[fks[0].ref.length - 1];
151
+ const source = findSource(links, i - 1) || artifact;
152
+ const managedAssocStepName = ref[i];
153
+ const newFkName = `${managedAssocStepName}${pathDelimiter}${fkAlias}`;
154
+ if (source?.elements[newFkName])
155
+ refOwner.ref = [ ...ref.slice(0, i), newFkName ];
151
156
  }
152
157
  }
158
+ else {
159
+ fkAlias = '';
160
+ // Ignore last path step and unmanaged associations.
161
+ // Structures should have been already flattened.
162
+ }
153
163
  }
154
164
  }
155
165
  }
@@ -160,6 +170,8 @@ function getFKAccessFinalizer( csn, csnUtils, pathDelimiter ) {
160
170
  // The association is an unmanaged one
161
171
  if (!elem.keys && elem.target && elem.on)
162
172
  applyTransformationsOnNonDictionary(elem, 'on', transformer, {}, [ 'definitions', artifactName, 'elements', elemName ]);
173
+ else if (elem.value?.stored)
174
+ applyTransformationsOnNonDictionary(elem, 'value', transformer, {}, [ 'definitions', artifactName, 'elements', elemName ]);
163
175
  }
164
176
 
165
177
  if (artifact.query || artifact.projection) {
@@ -59,14 +59,14 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
59
59
 
60
60
  /**
61
61
  * Turn .expand/.inline into normal refs. @cds.persistence.skip .expand with to-many (and all transitive views).
62
- * For such skipped things, error for usage of assoc pointing to them and and ignore publishing of assoc pointing to them.
62
+ * For such skipped things, error for usage of assoc pointing to them and ignore publishing of assoc pointing to them.
63
63
  */
64
64
  function rewriteExpandInline() {
65
65
  let cleanup = [];
66
66
  let _dependents;
67
67
 
68
68
  const entity = findAnEntity();
69
- const toDummyfy = [];
69
+ const toDummify = [];
70
70
 
71
71
  applyTransformations(csn, {
72
72
  columns: (parent, name, columns, path) => {
@@ -110,7 +110,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
110
110
  if (options.transformation !== 'odata') {
111
111
  applyTransformations(csn, {
112
112
  target: (parent, name, target, path) => {
113
- if (toDummyfy.indexOf(target) !== -1) {
113
+ if (toDummify.indexOf(target) !== -1) {
114
114
  publishing.push({
115
115
  parent, name, target, path: [ ...path ],
116
116
  });
@@ -161,7 +161,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
161
161
 
162
162
  const pathStep = obj.ref[j].id ? obj.ref[j].id : obj.ref[j];
163
163
  const target = art.target ? art.target : pathStep;
164
- if (toDummyfy.indexOf(target) !== -1) {
164
+ if (toDummify.indexOf(target) !== -1) {
165
165
  error( null, obj.$path, {
166
166
  id: pathStep, elemref: obj, name,
167
167
  }, 'Unexpected “@cds.persistence.skip” annotation on Association target $(NAME) of $(ID) in path $(ELEMREF) was skipped because of .expand in conjunction with to-many');
@@ -174,7 +174,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
174
174
  if (art) {
175
175
  const pathStep = obj.ref[obj.ref.length - 1].id ? obj.ref[obj.ref.length - 1].id : obj.ref[obj.ref.length - 1];
176
176
  const target = art.target ? art.target : pathStep;
177
- if (toDummyfy.indexOf(target) !== -1)
177
+ if (toDummify.indexOf(target) !== -1)
178
178
  kill.push(i);
179
179
  }
180
180
  }
@@ -221,7 +221,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
221
221
  stack.push([ dependent, dependentName ]);
222
222
  });
223
223
  }
224
- toDummyfy.push(n);
224
+ toDummify.push(n);
225
225
  }
226
226
  }
227
227
 
@@ -229,7 +229,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
229
229
  * Replace the artifacts in `toDummify` with simple dummy views as produced by createDummyView.
230
230
  */
231
231
  function dummyfy() {
232
- for (const artifactName of [ ...new Set(toDummyfy) ])
232
+ for (const artifactName of [ ...new Set(toDummify) ])
233
233
  csn.definitions[artifactName] = createDummyView(entity);
234
234
  }
235
235
 
@@ -366,7 +366,8 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
366
366
  }
367
367
  else { // preserve stuff like .cast for redirection
368
368
  const thing = base[currentAlias[currentAlias.length - 1]];
369
- if (current?._art?.value || thing?.value)
369
+ const value = current?._art?.value || thing?.value;
370
+ if (value && !value.stored)
370
371
  error('query-unsupported-calc', current.$path || col.$path, { '#': 'inside' });
371
372
  expanded.push(Object.assign({}, current, { ref: currentRef, as: currentAlias.join(pathDelimiter) } ));
372
373
  }
@@ -396,7 +397,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
396
397
  * With a .cast.on or .on in a .expand/.inline, we need to change the references,
397
398
  * since we change the overall scope of things (by "heaving" them up into "normal refs").
398
399
  *
399
- * So anything that does not have a $self/$projection infron get's the so-far-traveled alias,
400
+ * So anything that does not have a $self/$projection infront gets the so-far-traveled alias,
400
401
  * since after the transformation it will basically be in "top-level".
401
402
  *
402
403
  * @param {object} parent
@@ -529,7 +530,7 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
529
530
  if (col.ref && col.$scope !== '$magic') {
530
531
  const _art = col._art || inspectRef(path.concat(i)).art;
531
532
  if (_art && isStructured(_art))
532
- newThing.push(...expandRef(_art, col.ref, col.as, col.key || false, withAlias));
533
+ newThing.push(...expandRef(_art, col, withAlias));
533
534
 
534
535
  else
535
536
  newThing.push(col);
@@ -552,16 +553,14 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
552
553
  * Iterative, to not run into stack overflow.
553
554
  *
554
555
  * @param {CSN.Element} art
555
- * @param {Array} ref
556
- * @param {Array} alias
557
- * @param {boolean} isKey True if the ref obj has property key: true
556
+ * @param {object} root Column, ref in order by, etc.
558
557
  * @param {boolean} withAlias
559
558
  * @returns {Array}
560
559
  */
561
- function expandRef( art, ref, alias, isKey, withAlias ) {
560
+ function expandRef( art, root, withAlias ) {
562
561
  const expanded = [];
563
562
  /** @type {Array<[CSN.Element, any[], any[]]>} */
564
- const stack = [ [ art, ref, [ alias || ref[ref.length - 1] ] ] ];
563
+ const stack = [ [ art, root.ref, [ root.as || implicitAs(root.ref) ] ] ];
565
564
  while (stack.length > 0) {
566
565
  const [ current, currentRef, currentAlias ] = stack.pop();
567
566
  if (isStructured(current)) {
@@ -569,16 +568,16 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
569
568
  stack.push([ e, currentRef.concat(n), currentAlias.concat(n) ]);
570
569
  }
571
570
  else {
572
- const obj = { ref: currentRef };
571
+ const obj = { ...root, ...{ ref: currentRef } };
573
572
  if (withAlias) {
574
573
  const newAlias = currentAlias.join(pathDelimiter);
575
574
  // if (alias !== undefined) // explicit alias
576
575
  obj.as = newAlias;
577
576
  // alias was implicit - to later distinguish expanded s -> s.a from explicitly written s.a
578
- if (alias === undefined)
577
+ if (root.as === undefined)
579
578
  setProp(obj, '$implicitAlias', true);
580
579
  }
581
- if (isKey)
580
+ if (root.key)
582
581
  obj.key = true;
583
582
  expanded.push(obj);
584
583
  }
@@ -90,38 +90,31 @@ function resolveTypeReferences( csn, options, resolved, pathDelimiter, iterateOp
90
90
  return;
91
91
  if (!isBuiltinType(type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(type, path) && !isODataItems(type))) {
92
92
  toFinalBaseType(parent, resolved, true);
93
+
94
+ if (parent.items) // items could have unresolved types
95
+ toFinalBaseType(parent.items, resolved, true);
96
+
93
97
  // structured types might not have the child-types replaced.
94
98
  // Drill down to ensure this.
95
- if (parent.elements) {
96
- const stack = [ parent.elements ];
99
+ let nextElements = parent.elements || parent.items?.elements;
100
+ if (nextElements) {
101
+ const stack = [ nextElements ];
97
102
  while (stack.length > 0) {
98
103
  const elements = stack.pop();
99
104
  for (const e of Object.values(elements)) {
100
105
  if (e.type && !isBuiltinType(e.type))
101
106
  toFinalBaseType(e, resolved, true);
102
-
103
- if (e.elements)
104
- stack.push(e.elements);
107
+ nextElements = e.elements || e.items?.elements;
108
+ if (nextElements)
109
+ stack.push(nextElements);
105
110
  }
106
111
  }
107
112
  }
113
+
108
114
  const directLocalized = parent.localized || false;
109
115
  if (!directLocalized && !options.toOdata)
110
116
  removeLocalized(parent);
111
117
  }
112
- // HANA/SQLite do not support array-of - turn into CLOB/Text
113
- if (parent.items && !options.toOdata) {
114
- parent.type = 'cds.LargeString';
115
- delete parent.items;
116
- }
117
- },
118
- // HANA/SQLite do not support array-of - turn into CLOB/Text
119
- items: (parent) => {
120
- // OData has no LargeString substitution and doesn't expand types under items
121
- if (!options.toOdata) {
122
- parent.type = 'cds.LargeString';
123
- delete parent.items;
124
- }
125
118
  },
126
119
  }, [ (definitions, artifactName, artifact) => {
127
120
  // Replace events, actions and functions with simple dummies - they don't have effect on forRelationalDB stuff
@@ -520,7 +513,7 @@ function handleManagedAssociationsAndCreateForeignKeys( csn, options, error, war
520
513
  setProp(assoc.keys[i], inferredAlias, true);
521
514
  if (!(options.toOdata && assoc.keys[i].ref.length === 1))
522
515
  // In OData backend there are no aliases assigned when the same as the ref
523
- // TODO: remove the if after the new flattening in OData has been compleated
516
+ // TODO: remove the if after the new flattening in OData has been completed
524
517
  assoc.keys[i].as = assoc.keys[i].ref[assoc.keys[i].ref.length - 1];
525
518
  collector.push(assoc.keys[i]);
526
519
  }
@@ -699,13 +692,16 @@ function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathD
699
692
  }
700
693
  else {
701
694
  // unwind a derived type chain to a scalar type
702
- while (finalElement.type && !isBuiltinType(finalElement.type)) {
695
+ while (finalElement?.type && !isBuiltinType(finalElement?.type)) {
703
696
  finalTypeName = finalElement.type;
704
697
  finalElement = csn.definitions[finalElement.type];
705
698
  }
706
699
  }
707
700
  }
708
701
 
702
+ if (!finalElement)
703
+ return [];
704
+
709
705
  if (finalElement.target && !finalElement.on) {
710
706
  const hasKeys = !!finalElement.keys;
711
707
  if (!hasKeys) {
@@ -741,7 +737,7 @@ function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathD
741
737
  });
742
738
  }
743
739
  // we have reached a leaf element, create a foreign key
744
- else if (finalElement && (finalElement.type == null || isBuiltinType(finalElement.type))) {
740
+ else if (finalElement.type == null || isBuiltinType(finalElement.type)) {
745
741
  const newFk = Object.create(null);
746
742
  for (const prop of [ 'type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type' ]) {
747
743
  // copy props from original element to preserve derived types!