@sap/cds-compiler 6.9.3 → 7.0.1

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 (69) hide show
  1. package/CHANGELOG.md +76 -2
  2. package/bin/cdsc.js +4 -33
  3. package/doc/IncompatibleChanges_v7.md +639 -0
  4. package/lib/api/main.js +4 -56
  5. package/lib/api/options.js +5 -15
  6. package/lib/api/validate.js +1 -0
  7. package/lib/base/builtins.js +1 -2
  8. package/lib/base/csnRefs.js +2 -6
  9. package/lib/base/message-registry.js +82 -76
  10. package/lib/base/messages.js +8 -5
  11. package/lib/base/optionProcessor.js +2 -72
  12. package/lib/base/specialOptions.js +20 -17
  13. package/lib/checks/defaultValues.js +1 -39
  14. package/lib/checks/hasPersistedElements.js +19 -3
  15. package/lib/checks/parameters.js +0 -34
  16. package/lib/checks/selectItems.js +2 -38
  17. package/lib/checks/typeParameters.js +162 -0
  18. package/lib/checks/validator.js +5 -8
  19. package/lib/compiler/assert-consistency.js +19 -5
  20. package/lib/compiler/checks.js +47 -43
  21. package/lib/compiler/define.js +6 -6
  22. package/lib/compiler/extend.js +102 -111
  23. package/lib/compiler/generate.js +4 -8
  24. package/lib/compiler/populate.js +4 -7
  25. package/lib/compiler/propagator.js +9 -9
  26. package/lib/compiler/resolve.js +205 -7
  27. package/lib/compiler/shared.js +76 -82
  28. package/lib/compiler/tweak-assocs.js +102 -22
  29. package/lib/compiler/utils.js +57 -12
  30. package/lib/compiler/xpr-rewrite.js +2 -15
  31. package/lib/edm/annotations/edmJson.js +14 -10
  32. package/lib/edm/annotations/genericTranslation.js +3 -1
  33. package/lib/edm/annotations/preprocessAnnotations.js +9 -26
  34. package/lib/edm/csn2edm.js +27 -20
  35. package/lib/edm/edmUtils.js +25 -0
  36. package/lib/gen/CdlGrammar.checksum +1 -1
  37. package/lib/gen/CdlParser.js +2237 -2241
  38. package/lib/gen/Dictionary.json +17 -2
  39. package/lib/json/from-csn.js +67 -52
  40. package/lib/json/to-csn.js +28 -25
  41. package/lib/language/textUtils.js +0 -13
  42. package/lib/main.d.ts +22 -59
  43. package/lib/main.js +1 -1
  44. package/lib/model/csnUtils.js +9 -8
  45. package/lib/parsers/AstBuildingParser.js +45 -55
  46. package/lib/parsers/Lexer.js +2 -0
  47. package/lib/parsers/identifiers.js +0 -9
  48. package/lib/render/toCdl.js +41 -40
  49. package/lib/render/toSql.js +8 -1
  50. package/lib/render/utils/common.js +1 -1
  51. package/lib/render/utils/sql.js +2 -3
  52. package/lib/tool-lib/enrichCsn.js +1 -2
  53. package/lib/transform/db/applyTransformations.js +7 -5
  54. package/lib/transform/db/assertUnique.js +8 -51
  55. package/lib/transform/db/associations.js +1 -1
  56. package/lib/transform/db/cdsPersistence.js +1 -15
  57. package/lib/transform/db/expansion.js +9 -12
  58. package/lib/transform/db/flattening.js +1 -1
  59. package/lib/transform/db/groupByOrderBy.js +0 -16
  60. package/lib/transform/db/views.js +57 -161
  61. package/lib/transform/draft/db.js +2 -2
  62. package/lib/transform/forOdata.js +25 -14
  63. package/lib/transform/forRelationalDB.js +93 -301
  64. package/lib/transform/localized.js +33 -102
  65. package/lib/transform/odata/flattening.js +11 -2
  66. package/lib/transform/transformUtils.js +25 -3
  67. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -2
  68. package/package.json +2 -2
  69. package/lib/render/toHdbcds.js +0 -1810
@@ -61,58 +61,20 @@ function usesMixinAssociation( query, association, associationName ) {
61
61
  /**
62
62
  * @param {CSN.Model} csn
63
63
  * @param {CSN.Options} options
64
- * @param {{error: Function, info: Function}} messageFunctions
64
+ * @param {{error: Function, info: Function}} _messageFunctions
65
65
  * @returns {(query: CSN.Query, artifact: CSN.Artifact, artName: string, path: CSN.Path) => void} Transformer function for views
66
66
  */
67
- function getViewTransformer( csn, options, messageFunctions ) {
67
+ function getViewTransformer( csn, options, _messageFunctions ) {
68
68
  const csnUtils = getUtils(csn);
69
69
  const {
70
- get$combined, isAssocOrComposition,
70
+ get$combined,
71
71
  inspectRef, queryOrMain, // csnRefs
72
72
  } = csnUtils;
73
- const { error, info } = messageFunctions;
74
- const doA2J = !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds');
75
73
 
76
74
  return transformViewOrEntity;
77
75
 
78
76
  /**
79
- *
80
- * check all queries/subqueries for mixin publishing inside of unions -> forbidden in hdbcds
81
- *
82
- * @param {CSN.Query} query
83
- * @param {CSN.Elements} elements
84
- * @param {CSN.Path} path
85
77
  */
86
- function checkForMixinPublishing( query, elements, path ) {
87
- for (const elementName in elements) {
88
- const element = elements[elementName];
89
- if (element.target) {
90
- let colLocation;
91
- for (let i = 0; i < query.SELECT.columns.length; i++) {
92
- const col = query.SELECT.columns[i];
93
- if (col.ref && col.ref.length === 1) {
94
- if (!colLocation && col.ref[0] === elementName)
95
- colLocation = i;
96
-
97
-
98
- if (col.as === elementName)
99
- colLocation = i;
100
- }
101
- }
102
- if (colLocation) {
103
- const matchingCol = query.SELECT.columns[colLocation];
104
- const possibleMixinName = matchingCol.ref[0];
105
- const isMixin = query.SELECT.mixin[possibleMixinName] !== undefined;
106
- if (element.target && isMixin) {
107
- error(null, path.concat([ 'columns', colLocation ]), { id: elementName, name: possibleMixinName, '#': possibleMixinName === elementName ? 'std' : 'renamed' }, {
108
- std: 'Element $(ID) is a mixin association and can\'t be published in a UNION',
109
- renamed: 'Element $(ID) is a mixin association ($(NAME))and can\'t be published in a UNION',
110
- });
111
- }
112
- }
113
- }
114
- }
115
- }
116
78
 
117
79
  /**
118
80
  * For things that are not explicitly found in the columns but still present in the elements, add them to the columnMap.
@@ -158,8 +120,7 @@ function getViewTransformer( csn, options, messageFunctions ) {
158
120
  }
159
121
 
160
122
  /**
161
- * Check for invalid association publishing (in Union or in Subquery) (for hdbcds) and
162
- * create the __clone for publishing stuff.
123
+ * Create the __clone for publishing association elements.
163
124
  *
164
125
  * @todo Factor out the checks
165
126
  * @param {CSN.Query} query
@@ -169,95 +130,63 @@ function getViewTransformer( csn, options, messageFunctions ) {
169
130
  * @param {CSN.Element} elem
170
131
  * @param {string} elemName
171
132
  * @param {CSN.Path} elementsPath Path pointing to elements
172
- * @param {CSN.Path} queryPath Path pointing to the query
173
133
  */
174
- function handleAssociationElement( query, elements, columnMap, publishedMixins, elem, elemName, elementsPath, queryPath ) {
175
- if (isUnion(queryPath) && options.transformation === 'hdbcds') {
176
- if (doA2J) {
177
- info('query-ignoring-assoc-in-union', queryPath, { name: elemName, '#': elem.keys ? 'managed' : 'std' });
178
- elem.$ignore = true;
179
- }
180
- else {
181
- error(null, queryPath, { name: elemName }, 'Association $(NAME) can\'t be published in a SAP HANA CDS UNION');
182
- }
183
- }
184
- else if (queryPath.length > 4 && options.transformation === 'hdbcds') { // path.length > 4 -> is a subquery
185
- error(null, queryPath, { name: elemName },
186
- 'Association $(NAME) can\'t be published in a subquery');
187
- }
188
- else {
189
- const isNotMixinByItself = checkIsNotMixinByItself(query, columnMap, elemName);
190
- const { mixinElement, mixinName } = getMixinAssocOfQueryIfPublished(query, elem, elemName);
191
- if (isNotMixinByItself || mixinElement !== undefined) {
192
- // If the mixin is only published and not used, only display the __ clone. Kill the "original".
193
- if (mixinElement !== undefined && !usesMixinAssociation(query, elem, elemName))
194
- delete query.SELECT.mixin[mixinName];
195
-
196
-
197
- // Create an unused alias name for the MIXIN - use 3 _ to avoid collision with usings
198
- let mixinElemName = `___${ mixinName || elemName }`;
199
- while (elements[mixinElemName])
200
- mixinElemName = `_${ mixinElemName }`;
201
-
202
- // Copy the association element to the MIXIN clause under its alias name
203
- // Needs to be a deep copy, as we transform the on-condition
204
- const mixinElem = cloneCsnNonDict(elem, options);
205
-
206
- if (query.SELECT && !query.SELECT.mixin)
207
- query.SELECT.mixin = Object.create(null);
208
-
209
- // Clone 'on'-condition, pre-pending '$projection' to paths where appropriate,
210
- // and fixing the association alias just created
211
-
212
- if (mixinElem.on) {
213
- mixinElem.on = applyTransformationsOnNonDictionary(mixinElem, 'on', {
214
- ref: (parent, prop, ref, refpath) => {
215
- if (ref[0] === elemName) {
216
- ref[0] = mixinElemName;
217
- }
218
- else if (!(ref[0] && ref[0].startsWith('$'))) {
134
+ function handleAssociationElement( query, elements, columnMap, publishedMixins, elem, elemName, elementsPath, _queryPath ) {
135
+ const isNotMixinByItself = checkIsNotMixinByItself(query, columnMap, elemName);
136
+ const { mixinElement, mixinName } = getMixinAssocOfQueryIfPublished(query, elem, elemName);
137
+ if (isNotMixinByItself || mixinElement !== undefined) {
138
+ // If the mixin is only published and not used, only display the __ clone. Kill the "original".
139
+ if (mixinElement !== undefined && !usesMixinAssociation(query, elem, elemName))
140
+ delete query.SELECT.mixin[mixinName];
141
+
142
+
143
+ // Create an unused alias name for the MIXIN - use 3 _ to avoid collision with usings
144
+ let mixinElemName = `___${ mixinName || elemName }`;
145
+ while (elements[mixinElemName])
146
+ mixinElemName = `_${ mixinElemName }`;
147
+
148
+ // Copy the association element to the MIXIN clause under its alias name
149
+ // Needs to be a deep copy, as we transform the on-condition
150
+ const mixinElem = cloneCsnNonDict(elem, options);
151
+
152
+ if (query.SELECT && !query.SELECT.mixin)
153
+ query.SELECT.mixin = Object.create(null);
154
+
155
+ // Clone 'on'-condition, pre-pending '$projection' to paths where appropriate,
156
+ // and fixing the association alias just created
157
+
158
+ if (mixinElem.on) {
159
+ mixinElem.on = applyTransformationsOnNonDictionary(mixinElem, 'on', {
160
+ ref: (parent, prop, ref, refpath) => {
161
+ if (ref[0] === elemName) {
162
+ ref[0] = mixinElemName;
163
+ }
164
+ else if (!(ref[0] && ref[0].startsWith('$'))) {
165
+ ref.unshift('$projection');
166
+ }
167
+ else if (ref[0] && ref[0].startsWith('$')) {
168
+ // TODO: I think this is non-sense. Stuff with $ is either magic or must start with $self, right?
169
+ const { scope } = inspectRef(refpath);
170
+ if (scope !== '$magic' && scope !== '$self')
219
171
  ref.unshift('$projection');
220
- }
221
- else if (ref[0] && ref[0].startsWith('$')) {
222
- // TODO: I think this is non-sense. Stuff with $ is either magic or must start with $self, right?
223
- const { scope } = inspectRef(refpath);
224
- if (scope !== '$magic' && scope !== '$self')
225
- ref.unshift('$projection');
226
- }
227
- parent.ref = ref;
228
- return ref;
229
- },
230
- }, {}, elementsPath.concat(elemName));
231
- }
172
+ }
173
+ parent.ref = ref;
174
+ return ref;
175
+ },
176
+ }, {}, elementsPath.concat(elemName));
177
+ }
232
178
 
233
- if (!mixinElem.$ignore)
234
- columnMap[elemName] = { ref: [ mixinElemName ], as: elemName };
179
+ if (!mixinElem.$ignore)
180
+ columnMap[elemName] = { ref: [ mixinElemName ], as: elemName };
235
181
 
236
- if (query.SELECT) {
237
- query.SELECT.mixin[mixinElemName] = mixinElem;
182
+ if (query.SELECT) {
183
+ query.SELECT.mixin[mixinElemName] = mixinElem;
238
184
 
239
- publishedMixins.set(mixinElem, true);
240
- }
185
+ publishedMixins.set(mixinElem, true);
241
186
  }
242
187
  }
243
188
  }
244
189
 
245
- /**
246
- * If following an association, explicitly set the implicit alias
247
- * due to an issue with HANA - only for hdbcds-hdbcds, I assume flattening
248
- * takes care of this for the other cases already
249
- *
250
- * @param {CSN.Column} col
251
- * @param {CSN.Path} path
252
- */
253
- function addImplicitAliasWithAssoc( col, path ) {
254
- if (!col.as && col.ref && col.ref.length > 1) {
255
- const { links } = inspectRef(path);
256
- if (links && links.slice(0, -1).some(({ art }) => art && isAssocOrComposition(art)))
257
- col.as = getLastRefStepString(col.ref);
258
- }
259
- }
260
-
261
190
  /**
262
191
  * If simply selecting from a param like `:param`, we need to add an implicit alias like `:param as param`
263
192
  * due to an issue with HANA
@@ -298,16 +227,11 @@ function getViewTransformer( csn, options, messageFunctions ) {
298
227
  const elementsPath = elements === artifact.elements ? path.slice(0, 2).concat('elements') : path.concat('elements');
299
228
  const queryPath = path;
300
229
 
301
- let hasNonAssocElements = false;
302
230
  const isSelect = query && query.SELECT;
303
231
  const isProjection = !!artifact.projection || query && query.SELECT && !query.SELECT.columns;
304
232
  const columnMap = getColumnMap(query, csnUtils);
305
233
  const isSelectStar = query && query.SELECT && query.SELECT.columns && query.SELECT.columns.indexOf('*') !== -1;
306
234
 
307
- // check all queries/subqueries for mixin publishing inside of unions -> forbidden in hdbcds
308
- if (query && options.transformation === 'hdbcds' && query.SELECT && query.SELECT.mixin && path.indexOf('SET') !== -1)
309
- checkForMixinPublishing(query, elements, path);
310
-
311
235
  // Second walk through the entity elements: Deal with associations (might also result in new elements)
312
236
  // Will be initialized JIT inside the elements-loop
313
237
  let $combined;
@@ -321,9 +245,6 @@ function getViewTransformer( csn, options, messageFunctions ) {
321
245
  if (!columnMap[elemName])
322
246
  addProjectionOrStarElement(query, isProjection, isSelectStar, $combined, columnMap, elemName);
323
247
  }
324
- // Views must have at least one element that is not an unmanaged assoc
325
- if (!elem.on && !elem.$ignore)
326
- hasNonAssocElements = true;
327
248
 
328
249
  // (180 b) Create MIXINs for association elements in projections or views (those that are not mixins by themselves)
329
250
  // CDXCORE-585: Allow mixin associations to be used and published in parallel
@@ -331,21 +252,14 @@ function getViewTransformer( csn, options, messageFunctions ) {
331
252
  handleAssociationElement(query, elements, columnMap, publishedMixins, elem, elemName, elementsPath, queryPath);
332
253
  }
333
254
 
334
- if (query && !hasNonAssocElements) {
335
- // Complain if there are no elements other than unmanaged associations or associations without keys.
336
- error('def-missing-element', [ 'definitions', artName ], { '#': 'view' });
337
- }
338
-
339
255
  if (isSelect) {
340
256
  // Build new columns from the column map - bring elements and columns back in sync basically
341
257
  query.SELECT.columns = Object.keys(elements).filter(elem => !elements[elem].$ignore && !(elements[elem].target && ignoreAssociations)).map(key => stripLeadingSelf(columnMap[key]));
342
258
  // If following an association, explicitly set the implicit alias
343
259
  // due to an issue with HANA - this seems to only have an effect on ref files with hdbcds-hdbcds, so only run then
344
260
  const columnProcessors = [];
345
- if (options.transformation === 'hdbcds' || options.transformation === 'sql' && options.sqlDialect === 'hana')
261
+ if (options.transformation === 'sql' && options.sqlDialect === 'hana')
346
262
  columnProcessors.push(addImplicitAliasWithLonelyParam);
347
- if (options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')
348
- columnProcessors.push(addImplicitAliasWithAssoc);
349
263
 
350
264
  if (columnProcessors.length > 0)
351
265
  processColumns(columnProcessors, query.SELECT.columns, path.concat('columns'));
@@ -353,7 +267,7 @@ function getViewTransformer( csn, options, messageFunctions ) {
353
267
  delete query.SELECT.excluding; // just to make the output of the new transformer the same as the old
354
268
 
355
269
  // A2J turned usages into JOINs, we must now remove all non-published mixins (i.e. only keep the clones)
356
- if (query.SELECT.mixin && doA2J) {
270
+ if (query.SELECT.mixin) {
357
271
  for (const [ name, mixin ] of Object.entries(query.SELECT.mixin)) {
358
272
  if (!publishedMixins.has(mixin))
359
273
  delete query.SELECT.mixin[name];
@@ -363,21 +277,6 @@ function getViewTransformer( csn, options, messageFunctions ) {
363
277
  }
364
278
  }
365
279
 
366
- /**
367
- * Walk the given path and check if we are in a UNION.
368
- * This will return true when it is called on the subquery inside of a SET.args property.
369
- *
370
- * @param {CSN.Path} path
371
- * @returns {boolean}
372
- */
373
- function isUnion( path ) {
374
- const subquery = path[path.length - 1];
375
- const queryIndex = path[path.length - 2];
376
- const args = path[path.length - 3];
377
- const unionOperator = path[path.length - 4];
378
- return path.length > 3 && (subquery === 'SET' || subquery === 'SELECT') && typeof queryIndex === 'number' && queryIndex >= 0 && args === 'args' && unionOperator === 'SET';
379
- }
380
-
381
280
  /**
382
281
  * Strip of leading $self of the ref
383
282
  *
@@ -487,16 +386,13 @@ function getColumnMap( query, csnUtils ) {
487
386
  *
488
387
  * @param {object} query
489
388
  * @param {object} csnUtils
490
- * @param {CSN.Options} options
389
+ * @param {CSN.Options} _options
491
390
  */
492
- function ensureColumnNames( query, csnUtils, options ) {
391
+ function ensureColumnNames( query, csnUtils, _options ) {
493
392
  const select = query.SELECT || query.projection;
494
393
  for (const col of select?.columns || []) {
495
394
  if (col !== '*' && !columnAlias(col)) {
496
- if (options.transformation === 'hdbcds')
497
- col.as = csnUtils.getColumnName(col);
498
- else
499
- setProp(col, 'as', csnUtils.getColumnName(col));
395
+ setProp(col, 'as', csnUtils.getColumnName(col));
500
396
  }
501
397
  }
502
398
  }
@@ -239,7 +239,7 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
239
239
  // Note that we may need to do the HANA transformation steps for managed associations
240
240
  // (foreign key field generation, generatedFieldName, creating ON-condition) by hand,
241
241
  // because the corresponding transformation steps have already been done on all artifacts
242
- // when we come here). Only for to.hdbcds with hdbcds names this is not required.
242
+ // when we come here).
243
243
  /**
244
244
  * The given association has a key named DraftUUID
245
245
  *
@@ -273,7 +273,7 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
273
273
  }
274
274
 
275
275
  const draftUUIDKey = getDraftUUIDKey(draftAdministrativeData.DraftAdministrativeData);
276
- if (!(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds') && draftUUIDKey) {
276
+ if (draftUUIDKey) {
277
277
  const source = csn.definitions[draftAdministrativeData.DraftAdministrativeData.target];
278
278
  const sourceElement = source.elements[draftUUIDKey.ref[0]];
279
279
  const targetElement = {};
@@ -33,7 +33,7 @@ const { addLocalizationViews } = require('./localized');
33
33
  const { cloneFullCsn } = require('../base/cloneCsn');
34
34
  const { csnRefs } = require('../base/csnRefs');
35
35
  const replaceForeignKeyRefsInExpressionAnnotations = require('./odata/foreignKeyRefsInXprAnnos');
36
- const { isAnnotationExpression, primaryExprProperties } = require('../base/builtins');
36
+ const { isAnnotationExpression, primaryExprProperties, isBuiltinType } = require('../base/builtins');
37
37
 
38
38
  // Transformation for ODATA. Expects a CSN 'inputModel', processes it for ODATA.
39
39
  // The result should be suitable for consumption by EDMX processors (annotations and metadata)
@@ -70,8 +70,9 @@ const { isAnnotationExpression, primaryExprProperties } = require('../base/built
70
70
  // (EdmPreproc candidate, check with RT if @Core.Computed required by them)
71
71
  // - Rename shorthand annotations according to a builtin list (EdmPreproc Candidate)
72
72
  // e.g. @label -> @Common.Label
73
- // - If the association target is annotated with @cds.odata.valuelist, annotate the
74
- // association with @Common.ValueList.viaAssociation (EdmPreproc Candidate)
73
+ // - For managed associations with a target that is annotated with @cds.odata.valuelist: annotate the
74
+ // association with @Common.ValueList.viaAssociation. Must happen before "flattening" so that
75
+ // the annotation is copied to the FKs .(EdmPreproc Candidate)
75
76
  // - Check for @Analytics.Measure and @Aggregation.default (Linter check candidate, remove)
76
77
  // - Check annotations. If annotation starts with '@sap...' it must have a string or boolean value
77
78
  // (Linter check candidate)
@@ -230,7 +231,7 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
230
231
  = flattening.getStructRefFlatteningTransformer(csn, inspectRef, effectiveType, options, resolved, '_');
231
232
 
232
233
  const allMgdAssocDefs = flattening.allInOneFlattening(csn, refFlattener, adaptRefs,
233
- inspectRef, getFinalTypeInfo, isExternalServiceMember, error, csnUtils, options);
234
+ inspectRef, getFinalTypeInfo, isExternalServiceMember, messageFunctions, csnUtils, options);
234
235
  flattening.flattenAllStructStepsInRefs(csn, refFlattener, adaptRefs,
235
236
  inspectRef, effectiveType, csnUtils, error, options,
236
237
  { // skip: ['action', 'aspect', 'event', 'function', 'type'],
@@ -317,7 +318,7 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
317
318
  // hana to allow naming mode "hdbcds"
318
319
  def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.sqlMapping, csn, 'hana');
319
320
 
320
- forEachMemberRecursively(def, (member, memberName, propertyName) => {
321
+ forEachMemberRecursively(def, (member, memberName, propertyName, path) => {
321
322
  // Annotate elements, foreign keys, parameters, etc. with their DB names if requested
322
323
  // Only these are actually required and don't annotate virtual elements in entities or types
323
324
  // as they have no DB representation (although in views)
@@ -330,6 +331,9 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
330
331
  memberName, options.sqlMapping, 'hana'); // hana to allow "hdbcds"
331
332
  }
332
333
 
334
+
335
+ checkForComplexTypesWithDefaultValues(member, memberName, path);
336
+
333
337
  processDynamicFieldControlAnnotations(member);
334
338
 
335
339
  // Mark fields with @odata.on.insert/update as @Core.Computed
@@ -383,6 +387,14 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
383
387
  //--------------------------------------------------------------------
384
388
  // HELPER SECTION STARTS HERE
385
389
 
390
+
391
+ // in structured OData, elements which are structured are not allowed to have default values
392
+ function checkForComplexTypesWithDefaultValues(member, memberName, path) {
393
+ const resolvedType = getFinalTypeInfo(member.type);
394
+ if (structuredOData && !isBuiltinType(resolvedType?.type) && member.default !== undefined)
395
+ error('odata-unexpected-default-struct', path, { name: memberName });
396
+ }
397
+
386
398
  // Transform @readonly/@mandatory/@disabled into @Common.FieldControl annotation
387
399
  // with a when/then/else expression consisting of the input from the annotations.
388
400
  function processDynamicFieldControlAnnotations(node) {
@@ -406,7 +418,6 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
406
418
  };
407
419
 
408
420
  const fieldControl = {
409
- '=': true,
410
421
  xpr: createFieldControlExpression(definedAnnotations),
411
422
  };
412
423
 
@@ -591,12 +602,12 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
591
602
 
592
603
  if (shouldSet(minVal)) {
593
604
  setAnnotation(node, '@Validation.Minimum', minVal);
594
- if (min['='] !== undefined)
605
+ if (typeof min === 'object' && min.val !== undefined)
595
606
  setAnnotation(node, '@Validation.Minimum.@Validation.Exclusive', true);
596
607
  }
597
608
  if (shouldSet(maxVal)) {
598
609
  setAnnotation(node, '@Validation.Maximum', maxVal);
599
- if (max['='] !== undefined)
610
+ if (typeof max === 'object' && max.val !== undefined)
600
611
  setAnnotation(node, '@Validation.Maximum.@Validation.Exclusive', true);
601
612
  }
602
613
  }
@@ -653,17 +664,17 @@ function transform4odataWithCsn(inputModel, options, messageFunctions) {
653
664
  }
654
665
  }
655
666
 
656
- // (4.5) If the member is an association whose target has @cds.odata.valuelist annotate it
657
- // with @Common.ValueList.viaAssociation.
658
- // Do this only if the association is navigable(@odata.navigable) and the enclosing artifact is
659
- // a service member (don't pollute the CSN with unnecessary annotations, that is ensured by the caller
660
- // of this function).
667
+ // If member is a managed association whose target has @cds.odata.valuelist
668
+ // and that is navigable (@odata.navigable), then annotate it with @Common.ValueList.viaAssociation.
669
+ // During flattening the anno will be copied to the association's FKs.
670
+ // Don't add anno to unmanaged assocs, as value list annotation only applies to Property, not NavProperty
661
671
  function addCommonValueListviaAssociation(member, memberName) {
662
672
  const vlAnno = '@Common.ValueList.viaAssociation';
663
673
  if (isAssociation(member)) {
674
+ const managed = member.on === undefined;
664
675
  const navigable = member['@odata.navigable'] !== false; // navigable disabled only if explicitly set to false
665
676
  const targetDef = getCsnDef(member.target);
666
- if (navigable && targetDef['@cds.odata.valuelist'] && !member[vlAnno])
677
+ if (managed && navigable && targetDef['@cds.odata.valuelist'] && !member[vlAnno])
667
678
  setAnnotation(member, vlAnno, { '=': memberName });
668
679
  }
669
680
  }