@sap/cds-compiler 2.13.8 → 2.15.6

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 (78) hide show
  1. package/CHANGELOG.md +128 -4
  2. package/bin/cdsc.js +112 -37
  3. package/lib/api/main.js +63 -22
  4. package/lib/api/options.js +2 -3
  5. package/lib/api/validate.js +6 -6
  6. package/lib/base/message-registry.js +100 -17
  7. package/lib/base/messages.js +85 -64
  8. package/lib/base/optionProcessorHelper.js +19 -0
  9. package/lib/checks/annotationsOData.js +11 -32
  10. package/lib/checks/arrayOfs.js +1 -34
  11. package/lib/checks/validator.js +2 -4
  12. package/lib/compiler/assert-consistency.js +1 -0
  13. package/lib/compiler/base.js +1 -0
  14. package/lib/compiler/builtins.js +11 -0
  15. package/lib/compiler/checks.js +22 -70
  16. package/lib/compiler/define.js +59 -11
  17. package/lib/compiler/extend.js +20 -3
  18. package/lib/compiler/finalize-parse-cdl.js +26 -20
  19. package/lib/compiler/index.js +75 -26
  20. package/lib/compiler/populate.js +36 -17
  21. package/lib/compiler/propagator.js +4 -1
  22. package/lib/compiler/resolve.js +104 -16
  23. package/lib/compiler/shared.js +61 -27
  24. package/lib/compiler/tweak-assocs.js +7 -1
  25. package/lib/edm/annotations/genericTranslation.js +93 -21
  26. package/lib/edm/csn2edm.js +216 -98
  27. package/lib/edm/edm.js +305 -226
  28. package/lib/edm/edmPreprocessor.js +499 -423
  29. package/lib/edm/edmUtils.js +22 -22
  30. package/lib/gen/Dictionary.json +98 -22
  31. package/lib/gen/language.checksum +1 -1
  32. package/lib/gen/language.interp +3 -1
  33. package/lib/gen/languageParser.js +4636 -4368
  34. package/lib/json/csnVersion.js +10 -11
  35. package/lib/json/from-csn.js +3 -2
  36. package/lib/json/to-csn.js +0 -2
  37. package/lib/language/docCommentParser.js +2 -2
  38. package/lib/language/genericAntlrParser.js +47 -2
  39. package/lib/language/language.g4 +59 -27
  40. package/lib/main.d.ts +19 -1
  41. package/lib/main.js +6 -0
  42. package/lib/model/csnRefs.js +33 -6
  43. package/lib/model/csnUtils.js +193 -75
  44. package/lib/model/enrichCsn.js +1 -0
  45. package/lib/model/revealInternalProperties.js +2 -2
  46. package/lib/modelCompare/compare.js +6 -6
  47. package/lib/optionProcessor.js +62 -26
  48. package/lib/render/toCdl.js +844 -679
  49. package/lib/render/toHdbcds.js +189 -243
  50. package/lib/render/toSql.js +180 -198
  51. package/lib/render/utils/common.js +131 -15
  52. package/lib/transform/db/.eslintrc.json +1 -1
  53. package/lib/transform/db/associations.js +2 -2
  54. package/lib/transform/db/constraints.js +3 -1
  55. package/lib/transform/db/expansion.js +15 -10
  56. package/lib/transform/db/flattening.js +94 -64
  57. package/lib/transform/db/transformExists.js +7 -7
  58. package/lib/transform/db/views.js +6 -3
  59. package/lib/transform/forHanaNew.js +43 -26
  60. package/lib/transform/forOdataNew.js +43 -42
  61. package/lib/transform/localized.js +12 -7
  62. package/lib/transform/odata/toFinalBaseType.js +8 -6
  63. package/lib/transform/odata/typesExposure.js +145 -197
  64. package/lib/transform/transformUtilsNew.js +9 -12
  65. package/lib/transform/translateAssocsToJoins.js +5 -1
  66. package/lib/transform/universalCsn/coreComputed.js +5 -3
  67. package/lib/transform/universalCsn/universalCsnEnricher.js +27 -5
  68. package/lib/utils/moduleResolve.js +13 -6
  69. package/package.json +1 -1
  70. package/share/messages/message-explanations.json +2 -1
  71. package/share/messages/syntax-expected-integer.md +37 -0
  72. package/lib/transform/odata/attachPath.js +0 -96
  73. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  74. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  75. package/lib/transform/odata/referenceFlattener.js +0 -296
  76. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  77. package/lib/transform/odata/structuralPath.js +0 -72
  78. package/lib/transform/odata/structureFlattener.js +0 -171
@@ -8,35 +8,29 @@
8
8
 
9
9
  const { setProp } = require('../../base/model');
10
10
  const { defNameWithoutServiceOrContextName, isArtifactInService } = require('./utils');
11
- const { cloneCsn, isBuiltinType, forEachDefinition, forEachMember, cloneCsnDictionary } = require('../../model/csnUtils');
11
+ const { cloneCsnNonDict, isBuiltinType, forEachDefinition, forEachMember } = require('../../model/csnUtils');
12
12
  const { copyAnnotations } = require('../../model/csnUtils');
13
13
 
14
- /**
15
- * @param {CSN.Model} csn
16
- * @param {function} whatsMyServiceName
17
- * @param {CSN.Options} options
18
- * @param {*} csnUtils
19
- * @param {object} message message object with { error } function
20
- */
21
- function typesExposure(csn, whatsMyServiceName, autoexposeSchemaName, options, csnUtils, message) {
14
+ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackSchemaName, options, csnUtils, message) {
22
15
  const { error } = message;
23
16
  // are we working with OData proxies or cross-service refs
24
17
  const isMultiSchema = options.toOdata.version === 'v4' && (options.toOdata.odataProxies || options.toOdata.odataXServiceRefs);
25
18
  // collect in this variable all the newly exposed types
26
- const exposedStructTypes = [];
27
19
  const schemas = Object.create(null);
20
+ const exposedTypes = Object.create(null);
28
21
  // walk through the definitions of the given CSN and expose types where needed
29
22
  forEachDefinition(csn, (def, defName, propertyName, path) => {
30
23
  // we do expose types only for definition from inside services
31
24
  const serviceName = whatsMyServiceName(defName, false);
32
- if (serviceName) {
25
+ // run type exposure only on requested services if not in multi schema mode
26
+ // multi schema mode requires a proper type exposure for all services as a prerequisite
27
+ // for the proxy exposure
28
+ if (serviceName && requestedServiceNames.includes(serviceName)) {
33
29
  if (def.kind === 'type' || def.kind === 'entity') {
34
30
  forEachMember(def, (element, elementName, propertyName, path) => {
35
31
  if (propertyName === 'elements' || propertyName === 'params') {
36
- const artificialtName = `${isMultiSchema ?
37
- defNameWithoutServiceOrContextName(defName, serviceName)
38
- : defNameWithoutServiceOrContextName(defName, serviceName).replace(/\./g, '_')}_${elementName}`;
39
- exposeTypeOf(element, element.key || propertyName === 'params', elementName, defName, serviceName, artificialtName, path);
32
+ const newTypeName = getNewTypeName(element, elementName, defName, serviceName);
33
+ exposeTypeOf(element, element.key || propertyName === 'params', elementName, defName, serviceName, newTypeName, path);
40
34
  }
41
35
  }, path);
42
36
  }
@@ -55,46 +49,26 @@ function typesExposure(csn, whatsMyServiceName, autoexposeSchemaName, options, c
55
49
  });
56
50
 
57
51
  return schemas;
58
- /**
59
- * General function used for exposing a type of given element
60
- * @param {object} node
61
- * @param {string} memberName
62
- * @param {string} service
63
- * @param {string} artificialName
64
- * @param {CSN.Path} path
65
- */
66
- function exposeTypeOf(node, isKey, memberName, defName, service, artificialName, path) {
67
- if (isArrayed(node))
68
- exposeArrayOfTypeOf(node, isKey, memberName, defName, service, artificialName, path);
69
- else if (csnUtils.isStructured(node))
70
- exposeStructTypeOf(node, isKey, memberName, defName, service, artificialName, path);
71
- }
72
-
73
- /**
74
- * Check if a node is arrayed
75
- * @param {object} node
76
- */
77
- function isArrayed(node) {
78
- return node.items || (node.type && csnUtils.getFinalTypeDef(node.type).items);
79
- }
80
52
 
81
- /**
53
+ /**
82
54
  * If an 'action' uses structured types as parameters or return values that are not exposed in 'service'
83
55
  * (because the types are anonymous or have a definition outside of 'service'),
84
56
  * create equivalent types in 'service' and make 'action' use them instead,
85
57
  * @param {Object} action
86
58
  * @param {String} actionName
87
- * @param {String} service
59
+ * @param {String} serviceName
88
60
  */
89
- function exposeTypesOfAction(action, actionName, defName, service, path) {
61
+ function exposeTypesOfAction(action, actionName, defName, serviceName, path) {
90
62
  if (action.returns) {
91
63
  const artificialName = `return_${actionName.replace(/\./g, '_')}`;
92
- exposeTypeOf(action.returns, false, actionName, defName, service, artificialName, path.concat(['returns']));
64
+ const newTypeName = getNewTypeName(action.returns, undefined, artificialName, serviceName);
65
+ exposeTypeOf(action.returns, false, actionName, defName, serviceName, newTypeName, path.concat(['returns']));
93
66
  }
94
67
 
95
68
  action.params && Object.entries(action.params).forEach(([paramName, param]) => {
96
- const artificialName = `param_${actionName.replace(/\./g, '_')}_${paramName}`;
97
- exposeTypeOf(param, false, actionName, defName, service, artificialName, path.concat(['params', paramName]));
69
+ const artificialName = `param_${actionName.replace(/\./g, '_')}`;//_${paramName}`;
70
+ const newTypeName = getNewTypeName(param, paramName, artificialName, serviceName);
71
+ exposeTypeOf(param, false, actionName, defName, serviceName, newTypeName, path.concat(['params', paramName]));
98
72
  });
99
73
  }
100
74
 
@@ -104,93 +78,139 @@ function typesExposure(csn, whatsMyServiceName, autoexposeSchemaName, options, c
104
78
  * for a value of the 'node.type' property.
105
79
  * @param {Object} node
106
80
  * @param {String} memberName
107
- * @param {String} service
108
- * @param {String} artificialName
81
+ * @param {String} serviceName
82
+ * @param {String} newTypeName
109
83
  */
110
- function exposeStructTypeOf(node, isKey, memberName, defName, service, artificialName, path, parentName) {
111
- if (node.items) exposeStructTypeOf(node.items, isKey, memberName, defName, service, artificialName, path);
112
-
113
- // start conservative, assume we're in a named type
114
- let isAnonymous = false;
115
-
116
- if (isExposableStructure(node)) {
117
- let typeDef = node.type ? csnUtils.getCsnDef(node.type) : /* structure|anonymous type */ node;
118
- let newTypeId = node.type ? `${isMultiSchema ? node.type : node.type.replace(/\./g, '_')}` : artificialName;
119
- let newTypeFullName =
84
+ function exposeTypeOf(node, isKey, memberName, defName, serviceName, newTypeName, path, parentName) {
85
+ const { isExposable, typeDef, typeName, elements, isAnonymous } = isTypeExposable(node);
86
+ if (isExposable) {
87
+ // this is the name used to register the new type in csn.definitions
88
+ let fullQualifiedNewTypeName =
120
89
  isMultiSchema
121
- ? node.type ? getTypeNameInMultiSchema(node.type, service) : getAnonymousTypeNameInMultiSchema(artificialName, parentName || defName)
122
- : `${service}.${newTypeId}`;
90
+ ? (node.type || (node.items && node.items.type)
91
+ ? getTypeNameInMultiSchema(node.type|| (node.items && node.items.type), serviceName)
92
+ : getAnonymousTypeNameInMultiSchema(newTypeName, parentName || defName))
93
+ : `${serviceName}.${newTypeName}`;
123
94
 
124
- // With the redirection of sub elements, the element which is of named type with an association is now expanded and contains the association
125
- // and the new target. Consequently, we now have both type and elements properties in this case, and the elements should be taken as a priority
126
- // as the correct target is there and no longer in the type definition
127
- let newTypeElements = (node.type && node.elements) ? node.elements : typeDef.elements;
128
- // if node and typeDef are identical, we're anonymous
129
- isAnonymous = node === typeDef;
130
- // if we've left the anonymous world, we're no longer in a key def
131
- if (!isAnonymous)
95
+ // as soon as we leave of the anonymous world,
96
+ // we're no longer in a key def => don't set notNull:true on named types
97
+ if (!isAnonymous && isKey)
132
98
  isKey = false;
133
99
 
134
- let newType = exposeStructType(newTypeFullName, newTypeElements, memberName, path);
135
- if (!newType) {
136
- // Error already reported
137
- return;
100
+ // check if that type is already defined
101
+ let newType = csn.definitions[fullQualifiedNewTypeName];
102
+ if (newType) {
103
+ // error, if it was not exposed by us
104
+ if (!exposedTypes[fullQualifiedNewTypeName]) {
105
+ error(null, path, `Cannot create artificial type "${fullQualifiedNewTypeName}" for "${memberName}" because the name is already used`);
106
+ return;
107
+ }
138
108
  }
109
+ else {
110
+ /* Expose new structured type
111
+ * Treat items.elements as ordinary elements for now.
112
+ */
113
+ newType = createNewStructType(elements);
114
+ if (node.$location)
115
+ setProp(newType, '$location', node.$location);
139
116
 
140
- if (node.$location) setProp(newType, '$location', node.$location);
141
- setProp(newType, '$exposedBy', 'typeExposure');
117
+ csn.definitions[fullQualifiedNewTypeName] = newType;
118
+ exposedTypes[fullQualifiedNewTypeName] = 1;
142
119
 
143
- // Recurse into elements of 'type' (if any) and expose them as well (is needed)
144
- newType.elements && Object.entries(newType.elements).forEach(([elemName, newElem]) => {
145
- if (node.elements && node.elements[elemName].$location) setProp(newElem, '$location', node.elements[elemName].$location);
146
- exposeStructTypeOf(newElem,
147
- isKey,
148
- memberName,
149
- typeDef.kind === 'type' ? node.type : defName,
150
- service,
151
- isMultiSchema ? `${newTypeFullName}_${elemName}` : `${newTypeId}_${elemName}`,
152
- path,
153
- newTypeFullName);
154
- });
155
- typeDef.kind === 'type' ? copyAnnotations(typeDef, newType) : copyAnnotations(node, newType);
156
- delete node.elements;
157
- node.type = newTypeFullName;
120
+ // Recurse into elements of 'type' (if any) and expose them as well (is needed)
121
+ newType.elements && Object.entries(newType.elements).forEach(([elemName, newElem]) => {
122
+ if (node.elements && node.elements[elemName].$location)
123
+ setProp(newElem, '$location', node.elements[elemName].$location);
124
+ defName = typeDef.kind === 'type' ? typeName : defName;
125
+ exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
126
+ getNewTypeName(newElem, elemName, newTypeName, serviceName), path, fullQualifiedNewTypeName);
127
+ });
128
+ copyAnnotations(typeDef, newType);
129
+ // if the origin type had items, add items to exposed type
130
+ if(typeDef.kind === 'type') {
131
+ if(typeDef.items) {
132
+ newType.items = { elements: newType.elements };
133
+ delete newType.elements;
134
+ }
135
+ }
136
+ }
137
+ // adjust current node to new type
138
+ if(node.items) {
139
+ delete node.items.elements;
140
+ delete node.type;
141
+ node.items.type = fullQualifiedNewTypeName;
142
+ }
143
+ else {
144
+ delete node.elements;
145
+ node.type = fullQualifiedNewTypeName;
146
+ }
158
147
  }
159
148
 
160
- /**
161
- * Returns whether the 'node' is for exposing the in service.
162
- * There are 2 cases when we would like to expose a type is the service:
163
- * 1. If the node is of user-defined type which is not part of the service
164
- * 2. When we have structured element (the object has property 'elements')
165
- * @param {Object} node
149
+ /**
150
+ * Check if the node's type can be exposed:
151
+ * 1) If it's an anonymous structured type (items.elements || elements)
152
+ * 2) If it's a named type resolve to the final type definition and
153
+ * check if this is a structured type
154
+ * Returns an object that indicates
155
+ * - wether or not the type needs exposure
156
+ * - the elements dictionary that needs to be cloned
157
+ * - the typeDef, either the resolved type def or the node itself
158
+ * - if structured type was anonymously defined
159
+ * @param {object} node
160
+ * @returns {object} { isExposable, typeDef, typeName, elements, isAnonymous }
166
161
  */
167
- function isExposableStructure(node) {
168
- let finalNodeType = node.type ? csnUtils.getFinalType(node.type) : undefined;
169
- return finalNodeType && isArtifactInService(finalNodeType, service)
170
- ? false
171
- : csnUtils.isStructured(node);
162
+ function isTypeExposable(node) {
163
+ let typeName = undefined;
164
+ let typeDef = node;
165
+ let elements = (node.items && node.items.elements || node.elements)
166
+ // anonymous structured type
167
+ if(elements)
168
+ return { isExposable: true, typeDef, typeName, elements, isAnonymous: true };
169
+ // named type, resolve the type to inspect it
170
+ let type = node.items && node.items.type || node.type;
171
+ if(type) {
172
+ typeName = (type.ref && csnUtils.getFinalType(type)) || type;
173
+ typeDef = csnUtils.getFinalTypeDef(typeName);
174
+ const rc = { isExposable: true, typeDef, typeName, isAnonymous: false };
175
+ if(!isBuiltinType(typeName) && !isArtifactInService(typeName, serviceName)) {
176
+ while(!isBuiltinType(typeName)) {
177
+ typeDef = csnUtils.getFinalTypeDef(typeName);
178
+ if(typeDef) {
179
+ if((rc.elements = (typeDef.items && typeDef.items.elements || typeDef.elements)) !== undefined)
180
+ return rc;
181
+ type = typeDef.items && typeDef.items.type || typeDef.type;
182
+ typeName = (type.ref && csnUtils.getFinalType(type)) || type;
183
+ }
184
+ else {
185
+ throw Error(`Please debug me: ${typeName} not found`);
186
+ }
187
+ }
188
+ }
189
+ }
190
+ return { isExposable: false };
172
191
  }
173
192
 
193
+
174
194
  /**
175
195
  * Calculate the new type name that will be exposed in multi schema,
176
196
  * in case that the element has a named type.
177
197
  *
178
198
  * @param {string} typeName type of the element
179
- * @param {string} service current service name
199
+ * @param {string} serviceName current service name
180
200
  */
181
- function getTypeNameInMultiSchema(typeName, service) {
201
+ function getTypeNameInMultiSchema(typeName, serviceName) {
182
202
  const typeService = whatsMyServiceName(typeName);
183
203
  if (typeService) {
184
204
  // new type name without any prefixes
185
205
  const typePlainName = defNameWithoutServiceOrContextName(typeName, typeService);
186
- const newSchemaName = `${service}.${typeService}`;
206
+ const newSchemaName = `${serviceName}.${typeService}`;
187
207
  createSchema(newSchemaName);
188
208
  // return the new type name
189
209
  return `${newSchemaName}.${typePlainName.replace(/\./g, '_')}`;
190
210
  } else {
191
211
  const typeContext = csnUtils.getContextOfArtifact(typeName);
192
212
  const typeNamespace = csnUtils.getNamespaceOfArtifact(typeName);
193
- const newSchemaName = `${service}.${typeContext || typeNamespace || autoexposeSchemaName}`;
213
+ const newSchemaName = `${serviceName}.${typeContext || typeNamespace || fallBackSchemaName}`;
194
214
  // new type name without any prefixes
195
215
  const typePlainName = typeContext ? defNameWithoutServiceOrContextName(typeName, typeContext)
196
216
  : typeName.replace(`${typeNamespace}.`, '');
@@ -209,7 +229,7 @@ function typesExposure(csn, whatsMyServiceName, autoexposeSchemaName, options, c
209
229
  */
210
230
  function getAnonymousTypeNameInMultiSchema(typeName, parentName) {
211
231
  let currPrefix = parentName.substring(0, parentName.lastIndexOf('.'));
212
- const newSchemaName = currPrefix || autoexposeSchemaName;
232
+ const newSchemaName = currPrefix || fallBackSchemaName;
213
233
  // new type name without any prefixes
214
234
  const typePlainName = defNameWithoutServiceOrContextName(typeName, newSchemaName);
215
235
 
@@ -222,121 +242,49 @@ function typesExposure(csn, whatsMyServiceName, autoexposeSchemaName, options, c
222
242
  * @param {string} name
223
243
  */
224
244
  function createSchema(name) {
225
- schemas[`${name}`] = { kind: 'schema', name };
245
+ schemas[name] = { kind: 'schema', name };
226
246
  }
227
247
 
228
248
  /**
229
- * Expose a new type definition in the 'definitions' of the CSN and return that type(reusing such a type
230
- * if it already exists).
231
- * The new type has name 'typeName', elements which are 'elements'.
232
- * 'parentName' is used for error reporting.x
233
- * @param {String} typeName
249
+ * create a new structured type for 'elements'
234
250
  * @param {Object} elements
235
- * @param {String} parentName
236
251
  */
237
- function exposeStructType(typeName, elements, parentName, path) {
238
- // If type already exists, reuse it (complain if not created here)
239
- let type = csn.definitions[typeName];
240
- if (type) {
241
- if (!exposedStructTypes.includes(typeName)) {
242
- error(null, path, `Cannot create artificial type "${typeName}" for "${parentName}" because the name is already used`);
243
- return null;
244
- }
245
- return type;
246
- }
247
-
252
+ function createNewStructType(elements) {
248
253
  // Create a type with empty elements
249
- type = {
254
+ const type = {
250
255
  kind: 'type',
251
256
  elements: Object.create(null),
252
257
  };
258
+ setProp(type, '$exposedBy', 'typeExposure');
253
259
 
254
- // Duplicate the type's elements
260
+ // Duplicate elements
255
261
  Object.entries(elements).forEach(([elemName, element]) => {
256
- if (type.elements[elemName]) {
257
- const path = ['definitions', typeName, 'elements', elemName];
258
- error(null, path, `"${elemName}": Element name conflicts with existing element`);
259
- }
260
- let cloned = cloneCsn(element, options);
262
+ let cloned = cloneCsnNonDict(element, options);
261
263
  // if this was an anonymous sub element of a key, mark it as not nullable
262
264
  if(isAnonymous && isKey && !cloned.key && cloned.notNull === undefined)
263
265
  cloned.notNull = true;
264
266
  type.elements[elemName] = cloned;
265
267
  });
266
-
267
- // add to the CSN
268
- csn.definitions[typeName] = type;
269
- // store typeName in set of exposed struct types
270
- exposedStructTypes.push(typeName);
271
268
  return type;
272
269
  }
273
270
  }
274
271
 
275
-
276
- // If a member is of type "array of <named type|anonymous type>", we expose the arrayed type,
277
- // like we expose structures in structured mode
278
- function exposeArrayOfTypeOf(node, isKey, memberName, defName, service, artificialName, path) {
279
- // if anonymously defined in place -> we always expose the type
280
- // this would be definition like 'elem: array of { ... }'
281
- // and we use the artificial name for the new type name
282
- if (node.items && !node.type) {
283
- exposeStructTypeOf(node.items, isKey, memberName, defName, service, artificialName, path.concat('items'));
284
- }
285
- // we can have both of the 'type' and 'items' in the cases:
286
- // 1. 'elem: Foo' and 'type Foo: array of Baz' and 'type Baz: { ... }'
287
- // or 2. 'elem: Foo' and type Foo: array of Integer|String|...'
288
- else if (node.type) {
289
- let finalType = csnUtils.getFinalTypeDef(node.type);
290
- if (finalType.items) {
291
- if (!isArtifactInService(node.type, service)) {
292
- let typeId = `${service}.${node.type.replace(/\./g, '_')}`;
293
- let newType = exposeArrayedType(node.items || finalType.items, typeId);
294
- // When we have in the model something like:
295
- // type Foo: array of Bar; type Bar: { qux: Integer };
296
- // In the type Foo we expand the first level of elements of the items like we have in CDL this:
297
- // type Foo: array of { qux: Integer };
298
- expandFirstLevelOfArrayed(newType);
299
- node.type = typeId;
300
- }
301
- // case 1. - as we keep the type property, the items property is removed
302
- if (node.items) delete node.items;
303
- }
304
- }
305
-
306
- function exposeArrayedType(items, typeId) {
307
- let newType = csn.definitions[typeId];
308
- if (newType) {
309
- if (!exposedStructTypes.includes(typeId)) {
310
- error(null, newType.$path, `Cannot create artificial type "${typeId}" because the name is already used`);
311
- }
312
- return newType;
313
- }
314
- // create empty type
315
- newType = {
316
- kind: 'type',
317
- items: Object.create(null),
318
- }
319
-
320
- // copy over the items
321
- newType.items = cloneCsn(items, options);
322
- csn.definitions[typeId] = newType;
323
- exposedStructTypes.push(typeId);
324
- return newType;
325
- }
326
- }
327
-
328
- // In case we have in the model something like:
329
- // type Foo: array of Bar; type Bar: { qux: Integer };
330
- // In the type Foo we expand the first level of elements of the items like we have in CDL this:
331
- // type Foo: array of { qux: Integer };
332
- function expandFirstLevelOfArrayed(def) {
333
- if (def.items.type && !isBuiltinType(def.items.type)) {
334
- let finalType = csnUtils.getFinalTypeDef(def.items.type);
335
- if (csnUtils.isStructured(finalType)) {
336
- if (!def.items.elements) def.items.elements = cloneCsnDictionary(finalType.elements, options);
337
- delete def.items.type;
338
- }
339
- }
272
+ /*
273
+ * Calculate the name of the exposed type based on the information, the element can provide
274
+ * If the element is typed, use the type name
275
+ * else assume it's a de-anonymized type, concatenate the element name to the defName
276
+ */
277
+ function getNewTypeName(element, elementName, typeNamePrefix, serviceName) {
278
+ // for the new type name node.type has precedence over node.items.type
279
+ const typeName = (!element.elements && element.type || element.items && !element.items.elements && element.items.type);
280
+ return typeName
281
+ ? `${isMultiSchema
282
+ ? typeName.split('.').pop() // use leaf element
283
+ : typeName.replace(/\./g, '_')}` // concatenate path
284
+ : ( elementName // returns has no elementName, return the precalculated prefix
285
+ ? `${defNameWithoutServiceOrContextName(typeNamePrefix, serviceName).replace(/\./g, '_')}_${elementName}`
286
+ : typeNamePrefix
287
+ );
340
288
  }
341
289
  }
342
290
 
@@ -9,8 +9,9 @@ const { setProp } = require('../base/model');
9
9
  const { csnRefs } = require('../model/csnRefs');
10
10
 
11
11
  const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
12
- const { cloneCsn, cloneCsnDictionary, getUtils, isBuiltinType } = require('../model/csnUtils');
12
+ const { cloneCsnNonDict, cloneCsnDictionary, getUtils, isBuiltinType } = require('../model/csnUtils');
13
13
  const { ModelError } = require("../base/error");
14
+ const { forEach } = require('../utils/objectUtils');
14
15
 
15
16
  // Return the public functions of this module, with 'model' captured in a closure (for definitions, options etc).
16
17
  // Use 'pathDelimiter' for flattened names (e.g. of struct elements or foreign key elements).
@@ -258,7 +259,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
258
259
  result[eName] = e;
259
260
  }
260
261
  }
261
- Object.entries(struct).forEach(([childName, childElem]) => {
262
+ forEach(struct, (childName, childElem) => {
262
263
  if (isStructured(childElem)) {
263
264
  // Descend recursively into structured children
264
265
  let grandChildElems = flattenStructuredElement(childElem, childName, elementPath, pathInCsn.concat('elements',childName));
@@ -276,7 +277,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
276
277
  } else {
277
278
  // Primitive child - clone it and restore its cross references
278
279
  let flatElemName = elemName + pathDelimiter + childName;
279
- let flatElem = cloneCsn(childElem, options);
280
+ let flatElem = cloneCsnNonDict(childElem, options);
280
281
  // Don't take over notNull from leaf elements
281
282
  delete flatElem.notNull;
282
283
  setProp(flatElem, '_flatElementNameWithDots', elementPath.concat(childName).join('.'));
@@ -285,7 +286,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
285
286
  });
286
287
 
287
288
  // Fix all collected flat elements (names, annotations, properties, origin ..)
288
- Object.values(result).forEach(flatElem => {
289
+ forEach(result, (name, flatElem) => {
289
290
  // Copy annotations from struct (not overwriting, because deep annotations should have precedence)
290
291
  copyAnnotations(elem, flatElem, false);
291
292
  // Copy selected type properties
@@ -413,11 +414,11 @@ function getTransformers(model, options, pathDelimiter = '_') {
413
414
  throw Error('Failed to obtain final base type for reference : ' + node.type.ref.join('/'));
414
415
  if(finalBaseType.elements) {
415
416
  // This changes the order - to be discussed!
416
- node.elements = cloneCsn(finalBaseType, options).elements; // copy elements
417
+ node.elements = cloneCsnNonDict(finalBaseType, options).elements; // copy elements
417
418
  delete node.type; // delete the type reference as edm processing does not expect it
418
419
  } else if(finalBaseType.items) {
419
420
  // This changes the order - to be discussed!
420
- node.items = cloneCsn(finalBaseType.items, options); // copy items
421
+ node.items = cloneCsnNonDict(finalBaseType.items, options); // copy items
421
422
  delete node.type;
422
423
  } else {
423
424
  node.type=finalBaseType;
@@ -431,12 +432,8 @@ function getTransformers(model, options, pathDelimiter = '_') {
431
432
  let typeDef = typeof node.type === 'string' ? getCsnDef(node.type) : node.type;
432
433
  // Nothing to do if type is an array or a struct type
433
434
  if (typeDef.items || typeDef.elements) {
434
- // Expand structured types on entity elements for flat OData
435
- if(!(options.transformation === 'hdbcds' || options.toSql))
436
- return;
437
-
438
435
  // cloneCsn only works correctly if we start "from the top"
439
- const cloneTypeDef = cloneCsn(typeDef, options);
436
+ const cloneTypeDef = cloneCsnNonDict(typeDef, options);
440
437
  // With hdbcds-hdbcds, don't resolve structured types - but propagate ".items", to turn into LargeString later on.
441
438
  if(typeDef.items) {
442
439
  delete node.type;
@@ -899,7 +896,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
899
896
  let elements = artifact.elements;
900
897
  if (elements) {
901
898
  path.push('elements', null);
902
- Object.entries(elements).forEach(([name, obj]) => {
899
+ forEach(elements, (name, obj) => {
903
900
  path[path.length - 1] = name;
904
901
  recurseElements(obj, path, callback);
905
902
  });
@@ -44,7 +44,7 @@ function translateAssocsToJoinsCSN(csn, options){
44
44
  }
45
45
 
46
46
  // If A2J reports error - end! Continuing with a broken CSN makes no sense
47
- makeMessageFunction(model, options).throwWithError();
47
+ makeMessageFunction(model, options).throwWithAnyError();
48
48
  // FIXME: Move this somewhere more appropriate
49
49
  const compact = compactModel(model, compileOptions);
50
50
  return compact;
@@ -1818,6 +1818,10 @@ function walkPath(node, env)
1818
1818
  // or that are parameters ($parameters or escaped paths (':')
1819
1819
  //path.length && path[ path.length-1 ]._artifact
1820
1820
  const art = path && path.length && path[path.length-1]._artifact;
1821
+
1822
+ // regardless of the position in the query, ignore paths that have virtual path steps
1823
+ if(art && path.some(ps => ps._artifact && ps._artifact.virtual && ps._artifact.virtual.val))
1824
+ return path;
1821
1825
  if(art && !internalArtifactKinds.includes(art.kind))
1822
1826
  {
1823
1827
  if(env.callback)
@@ -137,9 +137,11 @@ function setCoreComputedOnViews(csn) {
137
137
  * @returns {boolean}
138
138
  */
139
139
  function needsCoreComputed(column) {
140
- return column && ( column.xpr || column.func || column.val !== undefined || column.param || column.SELECT || column.SET || column.ref && column.ref[0] in {
141
- $at: 1, $now: 1, $user: 1, $session: 1,
142
- });
140
+ return column &&
141
+ (
142
+ column.xpr || column.func || column.val !== undefined || column.param || column.SELECT || column.SET ||
143
+ column.ref && [ '$at', '$now', '$user', '$session' ].includes(column.ref[0])
144
+ );
143
145
  }
144
146
 
145
147
  /**
@@ -288,7 +288,11 @@ module.exports = (csn, options) => {
288
288
  if (returns.target)
289
289
  calculateForeignKeys(returns);
290
290
  },
291
- items: (parent, prop, items) => propagateMemberPropsFromOrigin(items, { '@': true, doc: true }),
291
+ items: (parent, prop, items) => {
292
+ // items in items must be propagated
293
+ // `specialItemsRule()` does not cover this case
294
+ propagateMemberPropsFromOrigin(items, { '@': true, doc: true }, { items: onlyWithTypeRef });
295
+ },
292
296
  elements: (parent, prop, elements) => {
293
297
  forEachValue(elements, (e) => {
294
298
  // within query elements we have to propagate the `enum` prop
@@ -358,13 +362,31 @@ module.exports = (csn, options) => {
358
362
  return !origin;
359
363
  }
360
364
  }
365
+
366
+ /**
367
+ * Some properties must only be copied over if the target
368
+ * is a type reference pointing to an element of a type or
369
+ * an aspect.
370
+ *
371
+ * @param {string} prop
372
+ * @param {CSN.Element} target
373
+ * @param {CSN.Element} source
374
+ */
375
+ function onlyWithTypeRef(prop, target, source) {
376
+ const typeIsTypeRef = Boolean(typeof target.type === 'object' && target.type.ref);
377
+ if (typeIsTypeRef) {
378
+ const referencedArtifact = csn.definitions[target.type.ref[0]];
379
+ if (referencedArtifact.kind in { type: true, aspect: true })
380
+ target[prop] = source[prop];
381
+ }
382
+ }
361
383
  }
362
384
 
363
385
  /**
364
386
  * Identify the sources of the passed object and propagate the relevant
365
387
  * properties/annotations along it's $origin chain.
366
388
  *
367
- * @param {Object} art Target object for propagation
389
+ * @param {object} art Target object for propagation
368
390
  */
369
391
  function propagateOnArtifactLevel(art) {
370
392
  // check if art was already processed by the status flag
@@ -415,7 +437,7 @@ module.exports = (csn, options) => {
415
437
  copyProperties(definition.$origin, definition, getDefinitionPropagationRuleFor);
416
438
 
417
439
  // We need to propagate .elements to type artifacts - but our direct origin might not have .elements,
418
- // because they are not propagated to members. We check if our root had elements (so we know that we should have some aswell)
440
+ // because they are not propagated to members. We check if our root had elements (so we know that we should have some as well)
419
441
  // and then walk the origin-chain until we find the first .elements
420
442
  if (definition.kind === 'type' && root.elements && !definition.elements) {
421
443
  const firstOriginWithElements = getFirstOriginWithElements(source);
@@ -580,7 +602,7 @@ module.exports = (csn, options) => {
580
602
  * @param {Function} getCustomRule getter for the `memberProps` or `defProps`
581
603
  * which shall be used for retrieving custom rules
582
604
  * @param {object} [except=null] array of properties which should not be propagated
583
- * @param {object} [force=null] Force propagation of the contained keys via rule "always"
605
+ * @param {object} [force=null] Force propagation of the contained keys via a custom rule.
584
606
  */
585
607
  function copyProperties(from, to, getCustomRule, except = null, force = null) {
586
608
  const keys = Object.keys(from);
@@ -589,7 +611,7 @@ function copyProperties(from, to, getCustomRule, except = null, force = null) {
589
611
  if (except && !(force && force[key]) && (key.charAt(0) in except || key in except))
590
612
  continue;
591
613
  if (!(key in to)) {
592
- const func = force && force[key] ? always : getCustomRule(key);
614
+ const func = force && force[key] ? force[key] : getCustomRule(key);
593
615
  if (func)
594
616
  func(key, to, from);
595
617
  }