@sap/cds-compiler 4.7.6 → 4.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 (93) hide show
  1. package/CHANGELOG.md +37 -2
  2. package/bin/cdsc.js +15 -1
  3. package/bin/cdshi.js +13 -3
  4. package/doc/CHANGELOG_BETA.md +5 -1
  5. package/lib/api/main.js +61 -23
  6. package/lib/api/options.js +40 -0
  7. package/lib/base/builtins.js +89 -0
  8. package/lib/base/keywords.js +5 -1
  9. package/lib/base/location.js +91 -14
  10. package/lib/base/message-registry.js +50 -33
  11. package/lib/base/messages.js +71 -16
  12. package/lib/base/model.js +0 -2
  13. package/lib/checks/actionsFunctions.js +1 -1
  14. package/lib/checks/elements.js +2 -1
  15. package/lib/checks/enricher.js +2 -2
  16. package/lib/checks/queryNoDbArtifacts.js +2 -1
  17. package/lib/checks/utils.js +1 -1
  18. package/lib/checks/validator.js +6 -22
  19. package/lib/compiler/assert-consistency.js +3 -5
  20. package/lib/compiler/builtins.js +0 -74
  21. package/lib/compiler/checks.js +61 -11
  22. package/lib/compiler/define.js +3 -3
  23. package/lib/compiler/extend.js +2 -2
  24. package/lib/compiler/index.js +9 -9
  25. package/lib/compiler/populate.js +13 -5
  26. package/lib/compiler/propagator.js +3 -0
  27. package/lib/compiler/resolve.js +6 -20
  28. package/lib/compiler/shared.js +1 -1
  29. package/lib/compiler/tweak-assocs.js +2 -2
  30. package/lib/compiler/utils.js +3 -3
  31. package/lib/compiler/{classes.js → xsn-model.js} +0 -16
  32. package/lib/edm/annotations/edmJson.js +7 -5
  33. package/lib/edm/annotations/genericTranslation.js +113 -55
  34. package/lib/edm/csn2edm.js +25 -9
  35. package/lib/edm/edm.js +3 -3
  36. package/lib/edm/edmInboundChecks.js +24 -5
  37. package/lib/edm/edmPreprocessor.js +46 -20
  38. package/lib/edm/edmUtils.js +3 -16
  39. package/lib/gen/Dictionary.json +9 -0
  40. package/lib/gen/language.checksum +1 -1
  41. package/lib/gen/language.interp +1 -1
  42. package/lib/gen/languageParser.js +1941 -1850
  43. package/lib/json/csnVersion.js +7 -4
  44. package/lib/json/from-csn.js +8 -7
  45. package/lib/json/to-csn.js +12 -7
  46. package/lib/language/antlrParser.js +1 -1
  47. package/lib/language/genericAntlrParser.js +9 -10
  48. package/lib/language/multiLineStringParser.js +2 -2
  49. package/lib/language/textUtils.js +1 -1
  50. package/lib/main.d.ts +23 -0
  51. package/lib/main.js +8 -1
  52. package/lib/model/cloneCsn.js +15 -6
  53. package/lib/model/csnRefs.js +141 -35
  54. package/lib/model/csnUtils.js +1 -4
  55. package/lib/model/enrichCsn.js +1 -1
  56. package/lib/modelCompare/compare.js +106 -92
  57. package/lib/optionProcessor.js +23 -1
  58. package/lib/render/toCdl.js +3 -2
  59. package/lib/render/toHdbcds.js +4 -48
  60. package/lib/render/toSql.js +6 -3
  61. package/lib/transform/addTenantFields.js +58 -35
  62. package/lib/transform/db/applyTransformations.js +1 -1
  63. package/lib/transform/db/expansion.js +3 -0
  64. package/lib/transform/db/flattening.js +71 -46
  65. package/lib/transform/db/views.js +1 -4
  66. package/lib/transform/effective/main.js +6 -3
  67. package/lib/transform/effective/misc.js +18 -8
  68. package/lib/transform/effective/types.js +4 -3
  69. package/lib/transform/forOdata.js +8 -7
  70. package/lib/transform/forRelationalDB.js +103 -112
  71. package/lib/transform/odata/flattening.js +82 -44
  72. package/lib/transform/odata/toFinalBaseType.js +9 -25
  73. package/lib/transform/odata/typesExposure.js +28 -15
  74. package/lib/transform/parseExpr.js +0 -3
  75. package/lib/transform/transformUtils.js +12 -8
  76. package/lib/transform/translateAssocsToJoins.js +2 -2
  77. package/lib/transform/universalCsn/coreComputed.js +2 -1
  78. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -1
  79. package/package.json +2 -2
  80. package/share/messages/README.md +4 -0
  81. package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
  82. package/share/messages/check-proper-type-of.md +1 -1
  83. package/share/messages/def-duplicate-autoexposed.md +1 -1
  84. package/share/messages/extend-repeated-intralayer.md +3 -16
  85. package/share/messages/extend-unrelated-layer.md +1 -1
  86. package/share/messages/message-explanations.json +1 -0
  87. package/share/messages/redirected-to-ambiguous.md +1 -1
  88. package/share/messages/redirected-to-complex.md +1 -1
  89. package/share/messages/redirected-to-unrelated.md +1 -1
  90. package/share/messages/rewrite-not-supported.md +1 -1
  91. package/share/messages/syntax-expecting-unsigned-int.md +2 -2
  92. package/share/messages/type-missing-enum-value.md +59 -0
  93. package/share/messages/wildcard-excluding-one.md +1 -1
@@ -29,8 +29,9 @@ const tenantDef = {
29
29
  '@cds.api.ignore': true, // and/or $generated: 'tenant' for the full Universal CSN?
30
30
  };
31
31
 
32
- function addTenantFields( csn, options ) {
33
- const { error, throwWithError } = createMessageFunctions( options, 'tenant', csn );
32
+ function addTenantFields( csn, options, messageFunctions ) {
33
+ const { error, throwWithError }
34
+ = messageFunctions ?? createMessageFunctions( options, 'tenant', csn );
34
35
  const { tenantDiscriminator } = options;
35
36
  const tenantName = tenantDiscriminator === true ? 'tenant' : tenantDiscriminator;
36
37
  if (tenantName !== 'tenant') {
@@ -47,17 +48,21 @@ function addTenantFields( csn, options ) {
47
48
  const { definitions } = csn;
48
49
  if (!definitions)
49
50
  return csn;
50
- const { initDefinition, artifactRef, effectiveType } = csnRefs( csn );
51
+ const {
52
+ initDefinition,
53
+ artifactRef,
54
+ effectiveType,
55
+ msgLocations,
56
+ } = csnRefs( csn, true );
51
57
 
52
58
  const typeCache = new WeakMap();
53
- const csnPath = [ 'definitions', '' ];
59
+ const csnPath = [ null ];
54
60
  let independent;
55
61
  let projection;
56
62
 
57
63
  for (const name in definitions) {
58
- const art = definitions[name];
59
- initDefinition( art );
60
- csnPath[1] = name;
64
+ const art = initDefinition( name );
65
+ csnPath[0] = art;
61
66
  independent = art[annoTenantIndep];
62
67
  projection = art.query || art.projection && art;
63
68
 
@@ -68,9 +73,11 @@ function addTenantFields( csn, options ) {
68
73
  handleElements( art );
69
74
  if (projection)
70
75
  traverseQuery( projection, null, null, handleQuery );
76
+ // Note: handleQuery sets csnPath[0]; store if needed afterwards
71
77
  }
72
78
  else if (!independent && independent != null) {
73
- error( 'tenant-invalid-anno-value', csnPath, { anno: annoTenantIndep, value: independent },
79
+ error( 'tenant-invalid-anno-value', msgLocations( csnPath ),
80
+ { anno: annoTenantIndep, value: independent },
74
81
  // eslint-disable-next-line max-len
75
82
  'Can\'t add $(ANNO) with value $(VALUE) to a non-entity, which is always tenant-independent' );
76
83
  }
@@ -88,16 +95,15 @@ function addTenantFields( csn, options ) {
88
95
  // the cache of csnRefs):
89
96
  for (const name in definitions) {
90
97
  const art = definitions[name];
91
- if (isTenantDepEntity( art ))
92
- art.elements = { [tenantName]: { ...tenantDef }, ...art.elements };
93
- // consider non-enumerable `elements` of subqueries if that is supported
98
+ addTenantFieldToArt(art, options);
94
99
  }
95
100
 
96
101
  (csn.extensions || []).forEach( ( ext, idx ) => {
97
102
  const tenant = ext.elements?.[tenantName];
98
103
  const name = ext.annotate || ext.extend; // extend should not happen
99
104
  if (tenant && isTenantDepEntity( definitions[name] )) {
100
- error( 'tenant-unexpected-ext', [ 'extensions', idx, 'elements', 'tenant' ],
105
+ error( 'tenant-unexpected-ext',
106
+ msgLocations( [ 'extensions', idx, 'elements', 'tenant' ] ),
101
107
  { name: tenantName },
102
108
  'Can\'t annotate element $(NAME) of a tenant-dependent entity' );
103
109
  }
@@ -112,7 +118,7 @@ function addTenantFields( csn, options ) {
112
118
  const names = art.includes
113
119
  .filter( name => isTenantDepEntity( csn.definitions[name] ) );
114
120
  if (names.length) {
115
- error( 'tenant-invalid-include', csnPath, { names }, {
121
+ error( 'tenant-invalid-include', msgLocations( csnPath ), { names }, {
116
122
  // eslint-disable-next-line max-len
117
123
  std: 'Can\'t include the tenant-dependent entities $(NAMES) into a tenant-independent definition',
118
124
  // eslint-disable-next-line max-len
@@ -125,12 +131,13 @@ function addTenantFields( csn, options ) {
125
131
  function handleElements( art ) {
126
132
  const { elements } = art;
127
133
  if (elements[tenantName]) {
128
- error( 'tenant-unexpected-element', [ ...csnPath, 'elements', tenantName ],
134
+ error( 'tenant-unexpected-element',
135
+ msgLocations( [ ...csnPath, 'elements', tenantName ] ),
129
136
  { name: tenantName, option: 'tenantDiscriminator' },
130
137
  'Can\'t have entity with element $(NAME) when using option $(OPTION)' );
131
138
  }
132
139
  else if (!independent && !Object.values( elements ).some( e => e.key )) {
133
- error( 'tenant-expecting-key', csnPath, {},
140
+ error( 'tenant-expecting-key', msgLocations( csnPath ), {},
134
141
  'There must be a key in a tenant-dependent entity' );
135
142
  }
136
143
  else {
@@ -147,18 +154,16 @@ function addTenantFields( csn, options ) {
147
154
  return;
148
155
 
149
156
  if (query !== projection && !independent) {
150
- error( 'tenant-unsupported-query', csnPath, {},
157
+ error( 'tenant-unsupported-query', msgLocations( csnPath ), {},
151
158
  'Can\'t yet have tenant-dependent non-simple query entities' );
152
159
  projection = null;
153
160
  return;
154
161
  }
155
162
 
156
- if (query.projection)
157
- csnPath.push( 'projection' );
158
- else if (query.SELECT)
159
- csnPath.push( 'query', 'SELECT' );
160
- else
163
+ if (!query.projection && !query.SELECT)
161
164
  return; // query.SET or query.join
165
+ csnPath[0] = query;
166
+ csnPath.push( query.SELECT ? 'SELECT' : 'projection' );
162
167
 
163
168
  const select = query.SELECT || query.projection;
164
169
  if (select.mixin)
@@ -177,7 +182,7 @@ function addTenantFields( csn, options ) {
177
182
  else if (query !== projection && select.columns) {
178
183
  checkColumnCasts( select.columns );
179
184
  }
180
- csnPath.length = 2;
185
+ csnPath.length = 1;
181
186
  }
182
187
 
183
188
  function handleQuerySource( query ) {
@@ -185,7 +190,7 @@ function addTenantFields( csn, options ) {
185
190
  const art = query.ref[0]; // yes, the base
186
191
  if (csn.definitions[art][annoTenantIndep])
187
192
  return true;
188
- error( 'tenant-invalid-query-source', csnPath, { art, '#': independent }, {
193
+ error( 'tenant-invalid-query-source', msgLocations( csnPath ), { art, '#': independent }, {
189
194
  std: 'Can\'t use a tenant-dependent query source $(ART) in a tenant-independent entity',
190
195
  event: 'Can\'t use a tenant-dependent query source $(ART) in an event',
191
196
  } );
@@ -194,12 +199,12 @@ function addTenantFields( csn, options ) {
194
199
  if (query !== (projection.SELECT || projection.projection)?.from) // with `join`
195
200
  return false;
196
201
  if ((query.as || implicitAs( query.ref )) === tenantName) {
197
- error( 'tenant-invalid-alias-name', csnPath,
202
+ error( 'tenant-invalid-alias-name', msgLocations( csnPath ),
198
203
  { name: tenantName, '#': (query.as ? 'std' : 'implicit') } );
199
204
  }
200
205
  const art = artifactRef.from( query );
201
206
  if (art[annoTenantIndep]) {
202
- error( 'tenant-expecting-tenant-source', csnPath, { art: query },
207
+ error( 'tenant-expecting-tenant-source', msgLocations( csnPath ), { art: query },
203
208
  // TODO: better the final entity name of assoc navigation in FROM
204
209
  // eslint-disable-next-line max-len
205
210
  'Expecting the query source $(ART) to be tenant-dependent for a tenant-dependent query entity' );
@@ -212,7 +217,7 @@ function addTenantFields( csn, options ) {
212
217
  for (const name in mixin) {
213
218
  csnPath[csnPath.length - 1] = name;
214
219
  if (name === tenantName && !independent)
215
- error( 'tenant-invalid-alias-name', csnPath, { name, '#': 'mixin' } );
220
+ error( 'tenant-invalid-alias-name', msgLocations( csnPath ), { name, '#': 'mixin' } );
216
221
  handleAssociations( mixin[name], null );
217
222
  }
218
223
  csnPath.length -= 2;
@@ -220,7 +225,7 @@ function addTenantFields( csn, options ) {
220
225
 
221
226
  function checkExcluding( excludeList ) {
222
227
  if (excludeList.includes( tenantName )) {
223
- error( 'tenant-invalid-excluding', csnPath, { name: tenantName },
228
+ error( 'tenant-invalid-excluding', msgLocations( csnPath ), { name: tenantName },
224
229
  'Can\'t exclude $(NAME) from the query source of a tenant-dependent entity' );
225
230
  }
226
231
  }
@@ -241,7 +246,7 @@ function addTenantFields( csn, options ) {
241
246
  for (const col of columns) {
242
247
  ++csnPath[csnPath.length - 1];
243
248
  if (col.expand || col.inline) {
244
- error( 'tenant-unsupported-expand-inline', csnPath, {},
249
+ error( 'tenant-unsupported-expand-inline', msgLocations( csnPath ), {},
245
250
  'Can\'t use expand/inline in a tenant-dependent entity' );
246
251
  }
247
252
  if (col.key != null) // yes, also with key: false
@@ -275,12 +280,20 @@ function addTenantFields( csn, options ) {
275
280
  return null;
276
281
 
277
282
  if (elem.target) {
278
- if (!csn.definitions[elem.target][annoTenantIndep]) {
279
- if (independent)
280
- error( 'tenant-invalid-target', csnPath, { target: elem.target } );
283
+ const { target } = elem;
284
+ if (csn.definitions[target][annoTenantIndep]) {
285
+ if (!independent && isComposition( elem ))
286
+ error( 'tenant-invalid-composition', msgLocations( csnPath ), { target } );
281
287
  }
282
- else if (!independent && isComposition( elem )) {
283
- error( 'tenant-invalid-composition', csnPath, { target: elem.target } );
288
+ else if (independent) {
289
+ if (target.endsWith( '.DraftAdministrativeData' ) && csnPath.length === 3 &&
290
+ csnPath[1] === 'elements' && csnPath[2] === 'DraftAdministrativeData') {
291
+ error( 'tenant-invalid-draft', msgLocations( csnPath ), {},
292
+ 'A tenant-independent entity can\'t be draft-enabled' );
293
+ }
294
+ else {
295
+ error( 'tenant-invalid-target', msgLocations( csnPath ), { target } );
296
+ }
284
297
  }
285
298
  }
286
299
  else if (elem.type && (independent || !elem.elements && !elem.items)) {
@@ -290,10 +303,11 @@ function addTenantFields( csn, options ) {
290
303
  if (independent) {
291
304
  if (!dep || dep === 'Composition')
292
305
  return true; // check elements (assocs could be redirected)
293
- error( 'tenant-invalid-target', csnPath, { type: elem.type, '#': 'type' } );
306
+ error( 'tenant-invalid-target', msgLocations( csnPath ), { type: elem.type, '#': 'type' } );
294
307
  }
295
308
  else if (dep && dep !== 'dependent') {
296
- error( 'tenant-invalid-composition', csnPath, { type: elem.type, '#': 'type' } );
309
+ error( 'tenant-invalid-composition', msgLocations( csnPath ),
310
+ { type: elem.type, '#': 'type' } );
297
311
  }
298
312
  }
299
313
  else {
@@ -398,6 +412,15 @@ function isTenantDepEntity( art ) {
398
412
  return art?.kind === 'entity' && !art[annoTenantIndep];
399
413
  }
400
414
 
415
+ function addTenantFieldToArt( art, options ) {
416
+ const tenantName = options.tenantDiscriminator === true ? 'tenant' : options.tenantDiscriminator;
417
+
418
+ if (isTenantDepEntity( art ))
419
+ art.elements = { [tenantName]: { ...tenantDef }, ...art.elements };
420
+ // consider non-enumerable `elements` of subqueries if that is supported
421
+ }
422
+
401
423
  module.exports = {
402
424
  addTenantFields,
425
+ addTenantFieldToArt,
403
426
  };
@@ -13,7 +13,7 @@
13
13
 
14
14
 
15
15
  const { setProp } = require('../../base/model');
16
- const { isAnnotationExpression } = require('../../compiler/builtins');
16
+ const { isAnnotationExpression } = require('../../base/builtins');
17
17
 
18
18
 
19
19
  /**
@@ -58,6 +58,9 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
58
58
  orderBy: (parent, name, orderBy, path) => {
59
59
  parent.orderBy = expand(orderBy, path.concat('orderBy'));
60
60
  },
61
+ list: (parent, name, list, path) => {
62
+ parent.list = expand(list, path.concat('list'));
63
+ },
61
64
  };
62
65
 
63
66
  // To not have a whole model loop for such a "small" thing, we kill all non-sql-backend relevant annotations here
@@ -1,38 +1,42 @@
1
1
  'use strict';
2
2
 
3
3
  const {
4
- applyTransformations, applyTransformationsOnNonDictionary,
5
- isBuiltinType, cardinality2str,
6
- copyAnnotations, implicitAs, isDeepEqual, findAnnotationExpression,
4
+ applyTransformations,
5
+ applyTransformationsOnNonDictionary,
6
+ cardinality2str,
7
+ copyAnnotations,
8
+ implicitAs,
9
+ isDeepEqual,
10
+ findAnnotationExpression,
7
11
  } = require('../../model/csnUtils');
12
+ const { isBuiltinType, isMagicVariable } = require('../../base/builtins');
8
13
  const transformUtils = require('../transformUtils');
9
14
  const { csnRefs } = require('../../model/csnRefs');
10
15
  const { setProp, isBetaEnabled } = require('../../base/model');
11
16
  const { forEach } = require('../../utils/objectUtils');
12
17
  const { transformExpression } = require('./applyTransformations');
13
18
  const { cloneCsnNonDict } = require('../../model/cloneCsn');
19
+
14
20
  /**
15
- * Strip off leading $self from refs where applicable
21
+ * Strip off leading $self from refs where applicable.
22
+ * Only relevant for HDBCDS, because handling of `$self` is not implemented there.
16
23
  *
17
- * @param {CSN.Model} csn
24
+ * @param {object} parent
25
+ * @param {string} prop
26
+ * @param {CSN.Elements} elements
18
27
  */
19
- function removeLeadingSelf( csn ) {
20
- const magicVars = [ '$now', '$self', '$projection', '$user', '$tenant', '$session', '$at' ];
21
- applyTransformations(csn, {
22
- elements: (parent, prop, elements) => {
23
- for (const [ elementName, element ] of Object.entries(elements)) {
24
- if (element.on) {
25
- applyTransformationsOnNonDictionary(elements, elementName, {
26
- ref: (root, name, ref) => {
27
- // Renderers seem to expect it to not be there...
28
- if (ref[0] === '$self' && ref.length > 1 && !magicVars.includes(ref[1]))
29
- root.ref = ref.slice(1);
30
- },
31
- });
32
- }
33
- }
34
- }, /* only for kind entity and view */ /* do not go into .actions */
35
- }, [], { skipIgnore: false, allowArtifact: artifact => (artifact.kind === 'entity'), skipDict: { actions: true } });
28
+ function removeLeadingSelf( parent, prop, elements ) {
29
+ for (const [ elementName, element ] of Object.entries(elements)) {
30
+ if (element.on) {
31
+ applyTransformationsOnNonDictionary(elements, elementName, {
32
+ ref: (root, name, ref) => {
33
+ // HDBCDS renderers seem to expect it to not be there...
34
+ if (ref[0] === '$self' && ref.length > 1 && !isMagicVariable(ref[1]) && ref[1] !== '$projection' && ref[1] !== '$self')
35
+ root.ref.shift();
36
+ },
37
+ });
38
+ }
39
+ }
36
40
  }
37
41
 
38
42
  /**
@@ -79,34 +83,49 @@ function resolveTypeReferences( csn, options, messageFunctions, resolved, pathDe
79
83
 
80
84
  const ignoreOdataKinds = { aspect: 1, event: 1, type: 1 };
81
85
  const replaceWithDummyKinds = { action: 1, function: 1, event: 1 };
86
+ const stripItems = options.transformation === 'hdbcds' || options.transformation === 'sql';
87
+ const removeItems = new Set();
82
88
  applyTransformations(csn, {
83
89
  type: (node, prop, type, path, parent, parentProp) => {
84
90
  if (options.toOdata && node.kind && node.kind in ignoreOdataKinds)
85
91
  return;
86
92
  if (parentProp === 'cast') {
87
93
  const e = csnUtils.getFinalTypeInfo(type, t => resolved.get(t)?.art || csnUtils.artifactRef(t));
94
+ if (e.items && stripItems)
95
+ removeItems.add(node);
88
96
  if (!e || e.items || e.elements)
89
97
  return;
90
98
  }
91
99
  if (!isBuiltinType(type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(type, path) && !isODataItems(type))) {
92
100
  toFinalBaseType(node, resolved, true);
93
101
 
94
- if (node.items) // items could have unresolved types
95
- toFinalBaseType(node.items, resolved, true);
96
-
97
- // structured types might not have the child-types replaced.
98
- // Drill down to ensure this.
99
- const nextElements = node.elements || node.items?.elements;
100
- const stack = nextElements ? [ nextElements ] : [];
101
- while (stack.length > 0) {
102
- const elements = stack.pop();
103
- for (const e of Object.values(elements)) {
104
- toFinalBaseType(e, resolved, true);
105
- if (!options.toOdata && e.items) // items could have unresolved types
106
- toFinalBaseType(e.items, resolved, true);
107
- const next = e.elements || e.items?.elements;
108
- if (next)
109
- stack.push(next);
102
+
103
+ if (node.items && stripItems) {
104
+ removeItems.add(node);
105
+ }
106
+ else {
107
+ if (node.items) // items could have unresolved types
108
+ toFinalBaseType(node.items, resolved, true);
109
+
110
+ // structured types might not have the child-types replaced.
111
+ // Drill down to ensure this.
112
+ const nextElements = node.elements || node.items?.elements;
113
+ const stack = nextElements ? [ nextElements ] : [];
114
+ while (stack.length > 0) {
115
+ const elements = stack.pop();
116
+ for (const e of Object.values(elements)) {
117
+ toFinalBaseType(e, resolved, true);
118
+ if (stripItems && e.items) {
119
+ removeItems.add(e);
120
+ }
121
+ else {
122
+ if (!options.toOdata && e.items) // items could have unresolved types
123
+ toFinalBaseType(e.items, resolved, true);
124
+ const next = e.elements || e.items?.elements;
125
+ if (next)
126
+ stack.push(next);
127
+ }
128
+ }
110
129
  }
111
130
  }
112
131
 
@@ -115,6 +134,7 @@ function resolveTypeReferences( csn, options, messageFunctions, resolved, pathDe
115
134
  removeLocalized(node);
116
135
  }
117
136
  },
137
+ items: node => removeItems.add(node),
118
138
  }, [ (definitions, artifactName, artifact) => {
119
139
  // Replace events, actions and functions with simple dummies - they don't have effect on forRelationalDB stuff
120
140
  // and that way they contain no references and don't hurt.
@@ -123,6 +143,8 @@ function resolveTypeReferences( csn, options, messageFunctions, resolved, pathDe
123
143
  // TODO:factor out somewhere else
124
144
  if (!options.toOdata && artifact.kind in replaceWithDummyKinds) {
125
145
  const dummy = { kind: artifact.kind };
146
+ if (artifact.kind === 'event')
147
+ dummy.elements = {}; // events must be structured for recompilation
126
148
  if (artifact.$location)
127
149
  setProp(dummy, '$location', artifact.$location);
128
150
 
@@ -131,6 +153,13 @@ function resolveTypeReferences( csn, options, messageFunctions, resolved, pathDe
131
153
  // TODO: skipDict options as default function arguments not via Object.assign
132
154
  } ], iterateOptions);
133
155
 
156
+ // no support for array-of - turn into CLOB/Text
157
+ for (const node of removeItems) {
158
+ node.type = 'cds.LargeString';
159
+ delete node.items;
160
+ }
161
+ removeItems.clear();
162
+
134
163
 
135
164
  /**
136
165
  * OData V4 only:
@@ -439,17 +468,13 @@ function linkForeignKeyAnnotationExtensionsToAssociation( csn, options ) {
439
468
  */
440
469
  function handleManagedAssociationsAndCreateForeignKeys( csn, options, messageFunctions, pathDelimiter, flattenKeyRefs, csnUtils, iterateOptions = {} ) {
441
470
  const { error, warning } = messageFunctions;
442
- const { isManagedAssociation, inspectRef, isStructured } = csnUtils;
471
+ const { inspectRef, isStructured } = csnUtils;
443
472
  const { flattenStructStepsInRef, flattenStructuredElement } = transformUtils.getTransformers(csn, options, messageFunctions, pathDelimiter);
444
473
  if (flattenKeyRefs) {
445
474
  applyTransformations(csn, {
446
- elements: (parent, prop, elements, path) => {
447
- Object.entries(elements).forEach(([ elementName, element ]) => {
448
- if (isManagedAssociation(element)) {
449
- // replace foreign keys that are managed associations by their respective foreign keys
450
- flattenFKs(element, elementName, [ ...path, 'elements', elementName ]);
451
- }
452
- });
475
+ keys: (element, prop, keys, path) => {
476
+ // replace foreign keys that are managed associations by their respective foreign keys
477
+ flattenFKs(element, path.at(-1), path);
453
478
  },
454
479
  }, [], Object.assign({
455
480
  skipIgnore: false,
@@ -62,10 +62,9 @@ function usesMixinAssociation( query, association, associationName ) {
62
62
  * @param {CSN.Model} csn
63
63
  * @param {CSN.Options} options
64
64
  * @param {{error: Function, info: Function}} messageFunctions
65
- * @param {Function} transformCommon For the time being: Pass from outside
66
65
  * @returns {(query: CSN.Query, artifact: CSN.Artifact, artName: string, path: CSN.Path) => void} Transformer function for views
67
66
  */
68
- function getViewTransformer( csn, options, messageFunctions, transformCommon ) {
67
+ function getViewTransformer( csn, options, messageFunctions ) {
69
68
  const csnUtils = getUtils(csn);
70
69
  const {
71
70
  get$combined, isAssocOrComposition,
@@ -237,8 +236,6 @@ function getViewTransformer( csn, options, messageFunctions, transformCommon ) {
237
236
  // Copy the association element to the MIXIN clause under its alias name
238
237
  // Needs to be a deep copy, as we transform the on-condition
239
238
  const mixinElem = cloneCsnNonDict(elem, options);
240
- // Perform common transformations on the newly generated MIXIN element (won't be reached otherwise)
241
- transformCommon(mixinElem, mixinElemName);
242
239
 
243
240
  if (query.SELECT && !query.SELECT.mixin)
244
241
  query.SELECT.mixin = Object.create(null);
@@ -38,7 +38,7 @@ function effectiveCsn( model, options, messageFunctions ) {
38
38
  messageFunctions.setModel(csn);
39
39
 
40
40
  const { expandStructsInExpression } = transformUtils.getTransformers(csn, options, messageFunctions, '_');
41
- queries.projectionToSELECTAndAddColumns(csn);
41
+ const redoProjections = queries.projectionToSELECTAndAddColumns(csn);
42
42
 
43
43
  let csnUtils = getUtils(csn, 'init-all');
44
44
 
@@ -63,7 +63,7 @@ function effectiveCsn( model, options, messageFunctions ) {
63
63
  // Expand a structured thing in: keys, columns, order by, group by
64
64
  expansion.expandStructureReferences(csn, options, '_', messageFunctions, csnUtils);
65
65
 
66
- const resolveTypesInActionsAfterFlattening = types.resolve(csn, csnUtils);
66
+ const resolveTypesInActionsAfterFlattening = types.resolve(csn, csnUtils, options);
67
67
 
68
68
  // Remove properties attached by validator - they do not "grow" as the model grows.
69
69
  cleanup();
@@ -81,7 +81,10 @@ function effectiveCsn( model, options, messageFunctions ) {
81
81
  associations.transformBacklinks(csn, options, csnUtils, messageFunctions);
82
82
  generateDrafts(csn, options, '_', messageFunctions);
83
83
  misc.attachPersistenceName(csn, options, csnUtils);
84
- misc.removeDefinitionsAndProperties(csn);
84
+ misc.removeDefinitionsAndProperties(csn, options);
85
+
86
+ if (!options.resolveProjections)
87
+ redoProjections.forEach(fn => fn());
85
88
 
86
89
  messageFunctions.throwWithError();
87
90
 
@@ -14,9 +14,11 @@ function attachPersistenceName( csn, options, csnUtils ) {
14
14
  const { addStringAnnotationTo } = csnUtils;
15
15
 
16
16
  forEachDefinition(csn, (artifact, artifactName) => {
17
- addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
17
+ if (artifact.kind === 'entity') {
18
+ addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
18
19
 
19
- forEachMemberRecursively(artifact, (member, memberName) => addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), member), [ 'definitions', artifactName ]);
20
+ forEachMemberRecursively(artifact, (member, memberName) => addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), member), [ 'definitions', artifactName ]);
21
+ }
20
22
  });
21
23
  }
22
24
 
@@ -39,20 +41,28 @@ function killProp( parent, prop ) {
39
41
  * - includes
40
42
  * - localized
41
43
  * @param {CSN.Model} csn
44
+ * @param {CSN.Options} options
42
45
  * @todo Callback-like architecture and merge with persistence name?
43
46
  */
44
- function _removeDefinitionsAndProperties( csn ) {
47
+ function _removeDefinitionsAndProperties( csn, options ) {
45
48
  const killers = {
46
49
  $ignore: (a, b, c, path, parentParent) => {
47
50
  const tail = path[path.length - 1];
48
51
  delete parentParent[tail];
49
52
  },
50
53
  kind: (artifact, a, b, path) => {
51
- if (artifact.kind === 'aspect' || artifact.kind === 'type')
52
- delete csn.definitions[path[1]];
53
-
54
- else if (artifact['@cds.persistence.skip'] === 'if-unused')
55
- artifact['@cds.persistence.skip'] = false;
54
+ if (artifact.kind === 'aspect' || artifact.kind === 'type') {
55
+ if (artifact.elements || artifact.items || options.resolveSimpleTypes)
56
+ delete csn.definitions[path[1]];
57
+ }
58
+ else {
59
+ if (artifact.kind === 'event') {
60
+ delete artifact.projection;
61
+ delete artifact.query;
62
+ }
63
+ if (artifact['@cds.persistence.skip'] === 'if-unused')
64
+ artifact['@cds.persistence.skip'] = false;
65
+ }
56
66
  },
57
67
  // Still used in flattenStructuredElements - in db/flattening.js
58
68
  _flatElementNameWithDots: killProp,
@@ -17,10 +17,11 @@ const { cloneCsnDict, cloneCsnNonDict } = require('../../model/cloneCsn');
17
17
  * @todo What about annotations on the type?
18
18
  * @param {CSN.Model} csn will be transformed
19
19
  * @param {object} csnUtils
20
+ * @param {CSN.Options} options
20
21
  * @returns {Function} Callback to resolve things (actions and their returns) later - as for them, $self would lead to unresolvable constructs at this point
21
22
  * so we can call this callback after flattening is done - then we can safely resolve their types.
22
23
  */
23
- function resolveTypes( csn, csnUtils ) {
24
+ function resolveTypes( csn, csnUtils, options ) {
24
25
  const { getFinalTypeInfo } = csnUtils;
25
26
  const later = [];
26
27
  applyTransformations(csn, {
@@ -51,7 +52,7 @@ function resolveTypes( csn, csnUtils ) {
51
52
  *
52
53
  * Drill down into .elements and .items
53
54
  *
54
- * @param {object} parent Object with a .type propertie
55
+ * @param {object} parent Object with a .type property
55
56
  */
56
57
  function resolveType( parent ) {
57
58
  // TODO: I assume there can be cases with a type ref but still having .elements already? Subelement anno?
@@ -67,7 +68,7 @@ function resolveTypes( csn, csnUtils ) {
67
68
  parent.items = cloneCsnNonDict(final.items);
68
69
  delete parent.type;
69
70
  }
70
- else if (final?.type) {
71
+ else if (final?.type && (options.resolveSimpleTypes || parent.type.ref?.length > 1)) {
71
72
  forEachKey(final, (key) => { // copy `type` + properties (default, etc.)
72
73
  if (parent[key] === undefined || key === 'type')
73
74
  parent[key] = final[key];
@@ -119,6 +119,11 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
119
119
  if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
120
120
  enrichUniversalCsn(csn, options);
121
121
 
122
+ // - Generate artificial draft fields on a structured CSN if requested, flattening and struct
123
+ // expansion do their magic including foreign key generation and annotation propagation.
124
+ // Tenantenizer has to decorate the DraftAdministrativeData, so draft decoration must be done before.
125
+ generateDrafts(csn, options, services, messageFunctions);
126
+
122
127
  if (options.tenantDiscriminator)
123
128
  addTenantFields(csn, options);
124
129
 
@@ -171,9 +176,6 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
171
176
  // rendering which may has to publish external definitions
172
177
  expandToFinalBaseType(csn, transformers, csnUtils, services, options, error);
173
178
 
174
- // - Generate artificial draft fields on a structured CSN if requested, flattening and struct
175
- // expansion do their magic including foreign key generation and annotation propagation.
176
- generateDrafts(csn, options, services, messageFunctions);
177
179
 
178
180
  // Check if structured elements and managed associations are compared in an expression
179
181
  // and expand these structured elements. This tuple expansion allows all other
@@ -189,8 +191,7 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
189
191
  const { adaptRefs, transformer: refFlattener } = flattening.getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, options, resolved, '_');
190
192
 
191
193
  flattening.allInOneFlattening(csn, refFlattener, adaptRefs, inspectRef, isExternalServiceMember, error, csnUtils, options);
192
-
193
- flattening.flattenAllStructStepsInRefs(csn, refFlattener, adaptRefs,
194
+ flattening.flattenAllStructStepsInRefs(csn, refFlattener, adaptRefs, inspectRef, effectiveType, csnUtils, error, options,
194
195
  { //skip: ['action', 'aspect', 'event', 'function', 'type'],
195
196
  skipArtifact: isExternalServiceMember,
196
197
  });
@@ -199,8 +200,8 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
199
200
  // rewritten path expressions
200
201
  forEachDefinition(csn, (def) => {
201
202
  ['elements', 'params'].forEach(dictName => {
202
- if(def[`$${dictName}`])
203
- def[dictName] = def[`$${dictName}`];
203
+ if(def[`$flat${dictName}`])
204
+ def[dictName] = def[`$flat${dictName}`];
204
205
  })
205
206
  if(def.$flatAnnotations) {
206
207
  Object.entries(def.$flatAnnotations).forEach(([an, av]) => {