@sap/cds 5.8.4 → 5.9.2

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 (248) hide show
  1. package/CHANGELOG.md +198 -77
  2. package/app/fiori/preview.js +16 -11
  3. package/app/fiori/routes.js +15 -8
  4. package/app/index.js +1 -1
  5. package/bin/build/buildTaskFactory.js +3 -3
  6. package/bin/build/buildTaskProviderFactory.js +1 -1
  7. package/bin/build/constants.js +1 -1
  8. package/bin/build/provider/buildTaskHandlerEdmx.js +12 -7
  9. package/bin/build/provider/buildTaskHandlerInternal.js +1 -1
  10. package/bin/build/provider/buildTaskProviderInternal.js +8 -2
  11. package/bin/build/provider/hana/2migration.js +27 -24
  12. package/bin/build/provider/hana/index.js +17 -18
  13. package/bin/build/provider/hana/migrationtable.js +9 -10
  14. package/bin/build/provider/java-cf/index.js +4 -5
  15. package/bin/build/provider/node-cf/index.js +99 -6
  16. package/bin/cds.js +17 -18
  17. package/bin/deploy/to-hana/cfUtil.js +16 -19
  18. package/bin/deploy/to-hana/hana.js +7 -24
  19. package/bin/deploy/to-hana/hdiDeployUtil.js +8 -4
  20. package/bin/mtx/in-cds.js +2 -2
  21. package/bin/serve.js +10 -3
  22. package/bin/utils/modules.js +7 -0
  23. package/bin/version.js +56 -3
  24. package/lib/compile/cdsc.js +7 -2
  25. package/lib/compile/etc/_localized.js +37 -25
  26. package/lib/compile/etc/csv.js +8 -8
  27. package/lib/compile/for/drafts.js +9 -0
  28. package/lib/compile/for/java.js +16 -0
  29. package/lib/compile/for/nodejs.js +12 -0
  30. package/lib/compile/index.js +3 -0
  31. package/lib/compile/minify.js +16 -2
  32. package/lib/compile/parse.js +2 -2
  33. package/lib/compile/resolve.js +35 -18
  34. package/lib/compile/to/json.js +3 -1
  35. package/lib/compile/to/sql.js +2 -2
  36. package/lib/compile/to/srvinfo.js +4 -2
  37. package/lib/connect/bindings.js +1 -1
  38. package/lib/connect/index.js +3 -4
  39. package/lib/core/entities.js +15 -14
  40. package/lib/core/index.js +39 -36
  41. package/lib/core/reflect.js +4 -2
  42. package/lib/deploy.js +114 -127
  43. package/lib/env/defaults.js +1 -0
  44. package/lib/env/index.js +165 -165
  45. package/lib/env/presets.js +1 -0
  46. package/lib/env/requires.js +121 -50
  47. package/lib/index.js +2 -0
  48. package/lib/log/format/kibana.js +2 -2
  49. package/lib/ql/SELECT.js +10 -0
  50. package/lib/ql/parse.js +1 -0
  51. package/lib/req/cds-context.js +4 -1
  52. package/lib/req/context.js +50 -56
  53. package/lib/req/event.js +1 -6
  54. package/lib/req/locale.js +6 -5
  55. package/lib/req/request.js +2 -0
  56. package/lib/req/user.js +7 -5
  57. package/lib/serve/Service-api.js +10 -7
  58. package/lib/serve/Service-dispatch.js +9 -11
  59. package/lib/serve/Service-methods.js +30 -41
  60. package/lib/serve/Transaction.js +10 -7
  61. package/lib/serve/adapters.js +11 -9
  62. package/lib/serve/factory.js +14 -9
  63. package/lib/serve/index.js +28 -15
  64. package/lib/utils/data.js +1 -1
  65. package/lib/utils/index.js +27 -30
  66. package/lib/utils/resources/index.js +101 -0
  67. package/lib/utils/resources/tar.js +71 -0
  68. package/lib/utils/resources/utils.js +11 -0
  69. package/libx/_runtime/audit/Service.js +36 -39
  70. package/libx/_runtime/audit/generic/personal/access.js +3 -4
  71. package/libx/_runtime/audit/generic/personal/modification.js +3 -4
  72. package/libx/_runtime/audit/utils/v2.js +1 -2
  73. package/libx/_runtime/auth/index.js +126 -84
  74. package/libx/_runtime/auth/strategies/JWT.js +12 -19
  75. package/libx/_runtime/auth/strategies/dummy.js +1 -5
  76. package/libx/_runtime/auth/strategies/dwc.js +11 -9
  77. package/libx/_runtime/auth/strategies/mock.js +0 -4
  78. package/libx/_runtime/auth/strategies/{utils/xssec.js → xssecUtils.js} +7 -4
  79. package/libx/_runtime/auth/strategies/xsuaa.js +12 -19
  80. package/libx/_runtime/auth/utils.js +22 -1
  81. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +104 -98
  82. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +8 -3
  83. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
  84. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
  85. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/language.js +2 -8
  86. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +4 -29
  87. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -1
  88. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +3 -2
  89. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +2 -2
  90. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +4 -6
  91. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +24 -21
  92. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +8 -2
  93. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +2 -0
  94. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -6
  95. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -12
  96. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +33 -9
  97. package/libx/_runtime/cds-services/adapter/odata-v4/utils/dispatcherUtils.js +56 -0
  98. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +2 -2
  99. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +10 -3
  100. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +9 -11
  101. package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +6 -3
  102. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +4 -2
  103. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/utils.js +1 -1
  104. package/libx/_runtime/cds-services/adapter/rest/utils/binary.js +1 -1
  105. package/libx/_runtime/cds-services/adapter/rest/utils/key-value-utils.js +2 -3
  106. package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +6 -4
  107. package/libx/_runtime/cds-services/adapter/rest/utils/result.js +1 -0
  108. package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +8 -5
  109. package/libx/_runtime/cds-services/services/Service.js +40 -0
  110. package/libx/_runtime/cds-services/services/utils/columns.js +4 -3
  111. package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -4
  112. package/libx/_runtime/cds-services/services/utils/differ.js +3 -3
  113. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +4 -4
  114. package/libx/_runtime/cds-services/util/assert.js +20 -14
  115. package/libx/_runtime/cds.js +9 -1
  116. package/libx/_runtime/common/aspects/any.js +5 -0
  117. package/libx/_runtime/common/aspects/entity.js +25 -7
  118. package/libx/_runtime/common/aspects/utils.js +2 -2
  119. package/libx/_runtime/common/composition/data.js +6 -0
  120. package/libx/_runtime/common/composition/insert.js +3 -2
  121. package/libx/_runtime/common/composition/tree.js +4 -10
  122. package/libx/_runtime/common/composition/update.js +4 -4
  123. package/libx/_runtime/common/constants/draft.js +29 -26
  124. package/libx/_runtime/common/error/constants.js +2 -2
  125. package/libx/_runtime/common/error/frontend.js +7 -15
  126. package/libx/_runtime/common/generic/auth/capabilities.js +59 -0
  127. package/libx/_runtime/common/generic/auth/constants.js +20 -0
  128. package/libx/_runtime/common/generic/auth/expand.js +54 -0
  129. package/libx/_runtime/common/generic/auth/index.js +32 -0
  130. package/libx/_runtime/common/generic/auth/insertOnly.js +15 -0
  131. package/libx/_runtime/common/generic/auth/readOnly.js +26 -0
  132. package/libx/_runtime/common/generic/auth/requires.js +34 -0
  133. package/libx/_runtime/common/generic/auth/restrict.js +298 -0
  134. package/libx/_runtime/common/generic/auth/restrictions.js +85 -0
  135. package/libx/_runtime/common/generic/auth/utils.js +213 -0
  136. package/libx/_runtime/common/generic/crud.js +8 -6
  137. package/libx/_runtime/common/generic/etag.js +1 -1
  138. package/libx/_runtime/common/generic/input.js +35 -35
  139. package/libx/_runtime/common/generic/sorting.js +2 -3
  140. package/libx/_runtime/common/generic/temporal.js +2 -2
  141. package/libx/_runtime/common/i18n/messages.properties +1 -1
  142. package/libx/_runtime/common/toggles/handler.js +21 -0
  143. package/libx/_runtime/common/utils/copy.js +10 -1
  144. package/libx/_runtime/common/utils/cqn2cqn4sql.js +111 -35
  145. package/libx/_runtime/common/utils/csn.js +63 -1
  146. package/libx/_runtime/common/utils/dollar.js +10 -1
  147. package/libx/_runtime/common/utils/draft.js +46 -7
  148. package/libx/_runtime/common/utils/entityFromCqn.js +13 -9
  149. package/libx/_runtime/common/utils/extensibilityUtils.js +18 -0
  150. package/libx/_runtime/common/utils/foreignKeyPropagations.js +88 -104
  151. package/libx/_runtime/common/utils/generateOnCond.js +4 -1
  152. package/libx/_runtime/common/utils/quotingStyles.js +2 -0
  153. package/libx/_runtime/common/utils/resolveStructured.js +25 -9
  154. package/libx/_runtime/common/utils/resolveView.js +4 -1
  155. package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -16
  156. package/libx/_runtime/common/utils/structured.js +33 -37
  157. package/libx/_runtime/common/utils/template.js +17 -8
  158. package/libx/_runtime/common/utils/templateProcessor.js +28 -28
  159. package/libx/_runtime/db/data-conversion/post-processing.js +118 -412
  160. package/libx/_runtime/db/expand/expandCQNToJoin.js +45 -41
  161. package/libx/_runtime/db/expand/rawToExpanded.js +29 -8
  162. package/libx/_runtime/db/generic/index.js +1 -3
  163. package/libx/_runtime/db/generic/input.js +5 -10
  164. package/libx/_runtime/db/generic/rewrite.js +5 -2
  165. package/libx/_runtime/db/generic/structured.js +2 -2
  166. package/libx/_runtime/db/query/delete.js +2 -2
  167. package/libx/_runtime/db/query/insert.js +1 -1
  168. package/libx/_runtime/db/query/update.js +9 -14
  169. package/libx/_runtime/db/sql-builder/CreateBuilder.js +4 -3
  170. package/libx/_runtime/db/sql-builder/FunctionBuilder.js +8 -8
  171. package/libx/_runtime/db/sql-builder/InsertBuilder.js +14 -1
  172. package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -2
  173. package/libx/_runtime/db/sql-builder/dataTypes.js +3 -3
  174. package/libx/_runtime/db/utils/columns.js +3 -3
  175. package/libx/_runtime/db/utils/normalizeTimeData.js +2 -2
  176. package/libx/_runtime/db/utils/propagateForeignKeys.js +6 -2
  177. package/libx/_runtime/extensibility/mps/index.js +5 -0
  178. package/libx/_runtime/extensibility/mps/service.js +111 -0
  179. package/libx/_runtime/extensibility/mps/tar.js +42 -0
  180. package/libx/_runtime/extensibility/mps/utils.js +11 -0
  181. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformREAD.js +0 -0
  182. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformRESULT.js +17 -5
  183. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformWRITE.js +1 -0
  184. package/libx/_runtime/extensibility/uiflex/index.js +54 -0
  185. package/libx/_runtime/extensibility/uiflex/service.js +276 -0
  186. package/libx/_runtime/{fiori → extensibility}/uiflex/utils.js +22 -7
  187. package/libx/_runtime/fiori/generic/activate.js +2 -2
  188. package/libx/_runtime/fiori/generic/before.js +4 -4
  189. package/libx/_runtime/fiori/generic/new.js +3 -3
  190. package/libx/_runtime/fiori/generic/patch.js +1 -1
  191. package/libx/_runtime/fiori/generic/read.js +58 -66
  192. package/libx/_runtime/fiori/generic/readOverDraft.js +74 -16
  193. package/libx/_runtime/fiori/utils/handler.js +6 -13
  194. package/libx/_runtime/fiori/utils/where.js +6 -5
  195. package/libx/_runtime/hana/Service.js +4 -10
  196. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +1 -1
  197. package/libx/_runtime/hana/driver.js +2 -2
  198. package/libx/_runtime/hana/execute.js +45 -75
  199. package/libx/_runtime/hana/pool.js +1 -1
  200. package/libx/_runtime/hana/streaming.js +2 -1
  201. package/libx/_runtime/index.js +6 -6
  202. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +5 -21
  203. package/libx/_runtime/messaging/Outbox.js +2 -2
  204. package/libx/_runtime/messaging/common-utils/AMQPClient.js +4 -14
  205. package/libx/_runtime/messaging/common-utils/connections.js +5 -7
  206. package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +30 -0
  207. package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -1
  208. package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +36 -30
  209. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -12
  210. package/libx/_runtime/messaging/enterprise-messaging.js +8 -8
  211. package/libx/_runtime/messaging/file-based.js +5 -5
  212. package/libx/_runtime/messaging/message-queuing.js +14 -12
  213. package/libx/_runtime/messaging/outbox/utils.js +18 -19
  214. package/libx/_runtime/messaging/redis-messaging.js +91 -0
  215. package/libx/_runtime/messaging/service.js +8 -6
  216. package/libx/_runtime/remote/Service.js +44 -8
  217. package/libx/_runtime/remote/utils/client.js +24 -19
  218. package/libx/_runtime/remote/utils/data.js +11 -11
  219. package/libx/_runtime/sqlite/Service.js +6 -9
  220. package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +5 -2
  221. package/libx/_runtime/types/api.js +10 -2
  222. package/libx/common/utils/ucsn.js +109 -0
  223. package/libx/gql/resolvers/crud/update.js +5 -0
  224. package/libx/gql/resolvers/parse/ast2cqn/columns.js +3 -1
  225. package/libx/gql/schema/typeDefMap.js +2 -2
  226. package/libx/odata/afterburner.js +110 -16
  227. package/libx/odata/cqn2odata.js +24 -27
  228. package/libx/odata/grammar.pegjs +9 -1
  229. package/libx/odata/parseToCqn.js +39 -0
  230. package/libx/odata/parser.js +1 -1
  231. package/libx/rest/RestAdapter.js +9 -1
  232. package/libx/rest/middleware/input.js +54 -0
  233. package/libx/rest/middleware/operation.js +14 -1
  234. package/libx/rest/middleware/parse.js +11 -7
  235. package/package.json +2 -2
  236. package/server.js +34 -19
  237. package/srv/audit-log.cds +2 -2
  238. package/srv/flex.cds +8 -2
  239. package/srv/flex.js +1 -1
  240. package/srv/mps.cds +23 -0
  241. package/srv/mps.js +1 -0
  242. package/libx/_runtime/auth/strategies/utils/uaa.js +0 -21
  243. package/libx/_runtime/common/generic/auth.js +0 -874
  244. package/libx/_runtime/common/toggles/alpha.js +0 -43
  245. package/libx/_runtime/db/generic/arrayed.js +0 -33
  246. package/libx/_runtime/fiori/uiflex/index.js +0 -35
  247. package/libx/_runtime/fiori/uiflex/service.js +0 -150
  248. package/libx/rest/utils/data.js +0 -60
@@ -8,7 +8,7 @@ const { ensureNoDraftsSuffix, ensureDraftsSuffix, ensureUnlocalized } = require(
8
8
  const { isAsteriskColumn } = require('../../common/utils/rewriteAsterisks')
9
9
  const { getCQNUnionFrom } = require('../../common/utils/union')
10
10
 
11
- const { DRAFT_COLUMNS } = require('../../common/constants/draft')
11
+ const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
12
12
 
13
13
  const { filterKeys } = require('../../fiori/utils/handler')
14
14
 
@@ -20,6 +20,7 @@ const SKIP_MAPPING = Symbol.for('sap.cds.skipMapping')
20
20
  const IDENTIFIER = Symbol.for('sap.cds.identifier')
21
21
  const IS_ACTIVE = Symbol.for('sap.cds.isActive')
22
22
  const IS_UNION_DRAFT = Symbol.for('sap.cds.isUnionDraft')
23
+ const CLEANUP_KEYS = Symbol.for('sap.cds.cleanupKeys')
23
24
 
24
25
  function getCqnCopy(readToOneCQN) {
25
26
  // REVISIT: Use query.clone() instead
@@ -31,6 +32,7 @@ function getCqnCopy(readToOneCQN) {
31
32
  if (readToOneCQN[IDENTIFIER] !== undefined) readToOneCQNCopy[IDENTIFIER] = readToOneCQN[IDENTIFIER]
32
33
  if (readToOneCQN[IS_ACTIVE] !== undefined) readToOneCQNCopy[IS_ACTIVE] = readToOneCQN[IS_ACTIVE]
33
34
  if (readToOneCQN[IS_UNION_DRAFT] !== undefined) readToOneCQNCopy[IS_UNION_DRAFT] = readToOneCQN[IS_UNION_DRAFT]
35
+ if (readToOneCQN[CLEANUP_KEYS] !== undefined) readToOneCQNCopy[CLEANUP_KEYS] = readToOneCQN[CLEANUP_KEYS]
34
36
  return readToOneCQNCopy
35
37
  }
36
38
 
@@ -533,7 +535,6 @@ class JoinCQNFromExpanded {
533
535
  }
534
536
  }
535
537
  }
536
-
537
538
  // only as second step handle expand to many, or else keys might still be unknown
538
539
  this._toMany({
539
540
  entity,
@@ -611,11 +612,7 @@ class JoinCQNFromExpanded {
611
612
  }
612
613
 
613
614
  _isNavigationToOne(activeTable, target) {
614
- return (
615
- target &&
616
- ((activeTable && target.type === 'cds.Composition') || target.type === 'cds.Association') &&
617
- target.is2one
618
- )
615
+ return target && ((activeTable && target.isComposition) || target._isAssociationStrict) && target.is2one
619
616
  }
620
617
 
621
618
  _getTarget(entity, column, parentAlias) {
@@ -649,7 +646,7 @@ class JoinCQNFromExpanded {
649
646
  const activeTableRequired =
650
647
  readToOneCQN[IS_UNION_DRAFT] || // > REVISIT: blocks expanding comp2one
651
648
  readToOneCQN[IS_ACTIVE] ||
652
- (element && element.type === 'cds.Association' && !element['@odata.draft.enclosed']) ||
649
+ (element && element._isAssociationStrict && !element['@odata.draft.enclosed']) ||
653
650
  !this._csn.definitions[target]._isDraftEnabled
654
651
 
655
652
  const colTarget = target && ensureUnlocalized(target)
@@ -677,9 +674,9 @@ class JoinCQNFromExpanded {
677
674
 
678
675
  const expandedEntity = this._getEntityForTable(target)
679
676
  if (readToOneCQN[IS_UNION_DRAFT] && expandedEntity.drafts) {
680
- const cols = column.expand.filter(c => !c.expand && !DRAFT_COLUMNS.includes(c.ref[0])).map(c => c.ref[0])
677
+ const cols = column.expand.filter(c => !c.expand && !(c.ref[0] in DRAFT_COLUMNS_MAP)).map(c => c.ref[0])
681
678
  const ks = Object.keys(expandedEntity.keys).filter(
682
- c => !expandedEntity.keys[c].isAssociation && !DRAFT_COLUMNS.includes(c)
679
+ c => !expandedEntity.keys[c].isAssociation && !(c in DRAFT_COLUMNS_MAP)
683
680
  )
684
681
  const user = (cds.context && cds.context.user && cds.context.user.id) || 'anonymous'
685
682
  const unionFrom = getCQNUnionFrom(cols, expandedEntity.name, expandedEntity.name + '.drafts', ks, user)
@@ -746,8 +743,19 @@ class JoinCQNFromExpanded {
746
743
  return col
747
744
  })
748
745
 
746
+ const targetEntity = this._getEntityForTable(target)
747
+ if (
748
+ 'IsActiveEntity' in targetEntity.elements &&
749
+ this._isNotIncludedIn(givenColumns)('IsActiveEntity') &&
750
+ typeof activeTableRequired === 'boolean'
751
+ ) {
752
+ const col = this._createCalculatedBooleanColumn('IsActiveEntity', activeTableRequired)
753
+ col[CLEANUP_KEYS] = true
754
+ givenColumns.push(col)
755
+ }
756
+
749
757
  this._expandedToFlat({
750
- entity: this._getEntityForTable(target),
758
+ entity: targetEntity,
751
759
  givenColumns,
752
760
  readToOneCQN,
753
761
  tableAlias,
@@ -959,6 +967,12 @@ class JoinCQNFromExpanded {
959
967
 
960
968
  if (!column[SKIP_MAPPING]) {
961
969
  mappings[column[IDENTIFIER] || identifier] = as
970
+ // REVISIT: remove flag cds.env.features.auto_fetch_expand_keys after two month grace period
971
+ if (column[CLEANUP_KEYS] && !cds.env.features.auto_fetch_expand_keys) {
972
+ delete aliasedElement[CLEANUP_KEYS]
973
+ if (!mappings[CLEANUP_KEYS]) mappings[CLEANUP_KEYS] = {}
974
+ mappings[CLEANUP_KEYS][column[IDENTIFIER] || identifier] = as
975
+ }
962
976
  }
963
977
 
964
978
  return aliasedElement
@@ -983,7 +997,7 @@ class JoinCQNFromExpanded {
983
997
  if (splitted.length > 1 && entity.elements[splitted[0]]) return true
984
998
 
985
999
  // Draft column
986
- return DRAFT_COLUMNS.includes(ref[0])
1000
+ return ref[0] in DRAFT_COLUMNS_MAP
987
1001
  }
988
1002
 
989
1003
  _toMany({
@@ -1031,12 +1045,14 @@ class JoinCQNFromExpanded {
1031
1045
  readToOneCQN.columns.push({
1032
1046
  val: readToOneCQN[IS_ACTIVE],
1033
1047
  as: 'IsActiveEntity',
1034
- cast: { type: 'cds.Boolean' }
1048
+ cast: { type: 'cds.Boolean' },
1049
+ [CLEANUP_KEYS]: true
1035
1050
  })
1036
1051
  } else {
1037
1052
  readToOneCQN.columns.push({
1038
1053
  as: `${tableAlias}_${name}`,
1039
- ref: [tableAlias, name]
1054
+ ref: [tableAlias, name],
1055
+ [CLEANUP_KEYS]: true
1040
1056
  })
1041
1057
  }
1042
1058
  }
@@ -1092,7 +1108,7 @@ class JoinCQNFromExpanded {
1092
1108
 
1093
1109
  const expandActive =
1094
1110
  readToOneCQN[IS_ACTIVE] ||
1095
- (element.type === 'cds.Association' && !element['@odata.draft.enclosed']) ||
1111
+ (element._isAssociationStrict && !element['@odata.draft.enclosed']) ||
1096
1112
  !this._csn.definitions[colTarget]._isDraftEnabled
1097
1113
  const ref = this._getJoinRef(entity.elements, colRef[0], expandActive, defaultLanguageThis)
1098
1114
  const tableAlias = this._createAlias(toManyTree.concat(colRef).join(':'))
@@ -1135,7 +1151,7 @@ class JoinCQNFromExpanded {
1135
1151
  cqn = this._adaptWhereOrderBy(cqn, tableAlias)
1136
1152
 
1137
1153
  if (isUnion) {
1138
- const cols = column.expand.filter(c => !c.expand && !DRAFT_COLUMNS.includes(c.ref[0])).map(c => c.ref[0])
1154
+ const cols = column.expand.filter(c => !c.expand && !(c.ref[0] in DRAFT_COLUMNS_MAP)).map(c => c.ref[0])
1139
1155
  // ensure the join columns are selected
1140
1156
  for (const each of joinColumns) {
1141
1157
  const col = each.ref[each.ref.length - 1]
@@ -1146,7 +1162,7 @@ class JoinCQNFromExpanded {
1146
1162
  if (each.expand) {
1147
1163
  const assoc = expandedEntity.associations[each.ref[0]]
1148
1164
  if (assoc.is2one) {
1149
- const fks = Object.values(expandedEntity.elements).filter(ele => ele['@odata.foreignKey4'] === assoc.name)
1165
+ const fks = Object.values(expandedEntity.elements).filter(ele => ele._foreignKey4 === assoc.name)
1150
1166
  cols.push(...fks.map(fk => fk.name))
1151
1167
  }
1152
1168
  }
@@ -1154,7 +1170,7 @@ class JoinCQNFromExpanded {
1154
1170
 
1155
1171
  if (!cqn[IS_ACTIVE]) {
1156
1172
  const ks = Object.keys(expandedEntity.keys).filter(
1157
- c => !expandedEntity.keys[c].isAssociation && !DRAFT_COLUMNS.includes(c)
1173
+ c => !expandedEntity.keys[c].isAssociation && !(c in DRAFT_COLUMNS_MAP)
1158
1174
  )
1159
1175
  const user = (cds.context && cds.context.user && cds.context.user.id) || 'anonymous'
1160
1176
  const unionFrom = getCQNUnionFrom(cols, ref.replace(/_drafts$/, ''), ref, ks, user)
@@ -1189,30 +1205,12 @@ class JoinCQNFromExpanded {
1189
1205
 
1190
1206
  _getJoinRef(elements, column, isActive, defaultLanguage) {
1191
1207
  const assoc = elements[column]
1192
- if (typeof isActive !== 'boolean' || isActive || assoc.type !== 'cds.Composition') {
1208
+ if (typeof isActive !== 'boolean' || isActive || !assoc.isComposition) {
1193
1209
  return defaultLanguage ? ensureUnlocalized(assoc.target) : assoc.target
1194
1210
  }
1195
1211
  return assoc.target + '_drafts'
1196
1212
  }
1197
1213
 
1198
- /**
1199
- * Get the list of key columns in ref format.
1200
- * Add the table alias to avoid ambiguity issues.
1201
- *
1202
- * @param tableAlias
1203
- * @param isActive
1204
- * @param expandedEntity
1205
- * @returns {Array}
1206
- * @private
1207
- */
1208
- _getKeyColumnForTarget(tableAlias, isActive, expandedEntity) {
1209
- return getAllKeys(expandedEntity)
1210
- .filter(column => typeof isActive !== 'boolean' || column !== 'IsActiveEntity')
1211
- .map(column => {
1212
- return { ref: [tableAlias, column] }
1213
- })
1214
- }
1215
-
1216
1214
  _getLimitInSelect(cqn, columns, limit, orderBy, expandedEntity) {
1217
1215
  const select = {
1218
1216
  SELECT: {
@@ -1391,7 +1389,10 @@ class JoinCQNFromExpanded {
1391
1389
  value = entry[`${parentAlias}_${key}`] || entry[`${parentAlias}_${key}`.toUpperCase()]
1392
1390
  }
1393
1391
 
1394
- if (value === undefined && cds.env.effective.odata && cds.env.effective.odata.structs) {
1392
+ if (
1393
+ value === undefined &&
1394
+ ((cds.env.effective.odata && cds.env.effective.odata.structs) || cds.env.features.ucsn_struct_conversion)
1395
+ ) {
1395
1396
  // here, it should be a structured key
1396
1397
  const keys = Object.keys(entry).filter(k => k.startsWith(key + '_'))
1397
1398
  if (keys.length) {
@@ -1572,7 +1573,7 @@ class JoinCQNFromExpanded {
1572
1573
  return entry =>
1573
1574
  !columns.some(
1574
1575
  column =>
1575
- (typeof column === 'object' && column.ref && column.ref[1] === entry) ||
1576
+ (typeof column === 'object' && column.ref && column.ref[column.ref.length - 1] === entry) ||
1576
1577
  ('val' in column && column.as === entry)
1577
1578
  )
1578
1579
  }
@@ -1605,12 +1606,15 @@ class JoinCQNFromExpanded {
1605
1606
  _addMissingKeyColumns(columns, tableAlias, keys, isActive, entity) {
1606
1607
  for (const key of keys.filter(this._isNotIncludedIn(columns))) {
1607
1608
  if (key === 'IsActiveEntity' && typeof isActive === 'boolean') {
1608
- columns.push(this._createCalculatedBooleanColumn(key, isActive))
1609
+ const col = this._createCalculatedBooleanColumn(key, isActive)
1610
+ col[CLEANUP_KEYS] = true
1611
+ columns.push(col)
1609
1612
  } else if (!entity.elements[key].elements) {
1610
1613
  // > don't add if complex key
1611
1614
  columns.push({
1612
1615
  ref: [tableAlias, key],
1613
- as: `${tableAlias}_${key}`
1616
+ as: `${tableAlias}_${key}`,
1617
+ [CLEANUP_KEYS]: true
1614
1618
  })
1615
1619
  }
1616
1620
  }
@@ -1,8 +1,11 @@
1
+ const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
2
+ const DRAFT_COLUMNS_ARRAY = Object.keys(DRAFT_COLUMNS_MAP)
3
+
1
4
  const EXPAND = Symbol.for('sap.cds.expand')
2
5
  const GET_KEY_VALUE = Symbol.for('sap.cds.getKeyValue')
3
6
  const TO_MANY = Symbol.for('sap.cds.toMany')
4
7
  const TO_ACTIVE = Symbol.for('sap.cds.toActive')
5
- const { DRAFT_COLUMNS } = require('../../common/constants/draft')
8
+ const CLEANUP_KEYS = Symbol.for('sap.cds.cleanupKeys')
6
9
 
7
10
  class RawToExpanded {
8
11
  constructor(configs, queries, one, rootEntity) {
@@ -22,8 +25,13 @@ class RawToExpanded {
22
25
  async toExpanded() {
23
26
  const { queries, mappings } = this._configs
24
27
 
28
+ const conversionMapper = new Map()
29
+ for (const each of queries) {
30
+ if (each._conversionMapper) for (const [k, v] of [...each._conversionMapper]) conversionMapper.set(k, v)
31
+ }
32
+
25
33
  for (let i = 0, length = this._queries.length; i < length; i++) {
26
- const { _conversionMapper: conversionMapper = new Map(), _toManyTree: toManyTree = [] } = queries[i]
34
+ const { _toManyTree: toManyTree = [] } = queries[i]
27
35
  const result = await this._queries[i]
28
36
  if (toManyTree.length === 0) {
29
37
  this._parseMainResult(result, mappings, conversionMapper, toManyTree)
@@ -39,6 +47,10 @@ class RawToExpanded {
39
47
  for (const entry of result) {
40
48
  const parsed = this._parseRaw({ mappings, toManyTree, conversionMapper, entry })
41
49
 
50
+ if (mappings[CLEANUP_KEYS]) {
51
+ for (const key of Object.keys(mappings[CLEANUP_KEYS])) delete parsed[key]
52
+ }
53
+
42
54
  if (parsed) {
43
55
  this._result.push(parsed)
44
56
  }
@@ -50,7 +62,7 @@ class RawToExpanded {
50
62
  const el = mapping[key]
51
63
  // if we expand automatically in groupby we have no other columns than objects
52
64
  if (typeof el !== 'string') return false
53
- if (result[el] !== null && !DRAFT_COLUMNS.some(col => el.endsWith(`_${col}`))) {
65
+ if (result[el] !== null && !DRAFT_COLUMNS_ARRAY.some(col => el.endsWith(`_${col}`))) {
54
66
  return false
55
67
  }
56
68
  }
@@ -99,7 +111,12 @@ class RawToExpanded {
99
111
  else expandedItems = expandedItems.filter(ele => ele.IsActiveEntity === rootIsActiveEntity)
100
112
  }
101
113
 
102
- row[key] = expandedItems
114
+ row[key] = !mapping[CLEANUP_KEYS]
115
+ ? expandedItems
116
+ : expandedItems.map(item => {
117
+ for (const key of Object.keys(mapping[CLEANUP_KEYS])) delete item[key]
118
+ return item
119
+ })
103
120
  } else if (typeof mapping === 'object') {
104
121
  // > Will be true in case of 1:1 expands
105
122
  // check if the expanded entry doesn't exists
@@ -122,11 +139,14 @@ class RawToExpanded {
122
139
  else if (rootIsActiveEntity) row[key] = parsed && parsed.IsActiveEntity !== false ? parsed : null
123
140
  else row[key] = parsed && parsed.IsActiveEntity === rootIsActiveEntity ? parsed : null
124
141
  }
142
+ if (mapping[CLEANUP_KEYS]) {
143
+ for (const key in mapping[CLEANUP_KEYS]) delete parsed[key]
144
+ }
125
145
  } else {
126
146
  // > No expand convert the result directly.
127
147
  const rawValue = entry[mapping]
128
148
  // Assume a DB will not return undefined, but always null
129
- row[key] = this._convertValue(rawValue, conversionMapper.get(mapping))
149
+ this._convertValue(rawValue, conversionMapper.get(mapping), mapping, row, key)
130
150
 
131
151
  isEntityNull = this._isNull(isEntityNull, rawValue)
132
152
  }
@@ -164,12 +184,13 @@ class RawToExpanded {
164
184
  * @returns {*}
165
185
  * @private
166
186
  */
167
- _convertValue(value, converter) {
187
+ _convertValue(value, converter, key, row, unaliasedKey) {
168
188
  if (converter) {
169
- return converter(value)
189
+ converter(value, key, row, unaliasedKey)
190
+ return
170
191
  }
171
192
 
172
- return value
193
+ row[unaliasedKey || key] = value
173
194
  }
174
195
 
175
196
  _parseExpandResult(result, mappings, conversionMapper, toManyTree) {
@@ -10,7 +10,6 @@ const UPDATE = require('./update')
10
10
  const DELETE = require('./delete')
11
11
  // after
12
12
  const structured = require('./structured')
13
- const arrayed = require('./arrayed')
14
13
 
15
14
  /*
16
15
  * this module is required by cds-pg. -> in case of incompatible changes, we should let them know.
@@ -24,6 +23,5 @@ module.exports = {
24
23
  READ,
25
24
  UPDATE,
26
25
  DELETE,
27
- structured,
28
- arrayed
26
+ structured
29
27
  }
@@ -25,7 +25,7 @@ const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
25
25
  const _isManaged = (category, event) =>
26
26
  (category === '@cds.on.insert' && event === 'CREATE') || (category === '@cds.on.update' && event === 'UPDATE')
27
27
 
28
- const _shouldGenerateUUID = element => element.key && !DRAFT_COLUMNS_MAP[element.name] && element.type === 'cds.UUID'
28
+ const _shouldGenerateUUID = element => element.key && !DRAFT_COLUMNS_MAP[element.name] && element.isUUID
29
29
 
30
30
  const _processComplexCategory = ({ row, key, val, category, req, element }) => {
31
31
  const categoryArgs = category.args
@@ -55,7 +55,7 @@ const _processComplexCategory = ({ row, key, val, category, req, element }) => {
55
55
  if ('val' in dfault) row[key] = dfault.val
56
56
  else if ('ref' in dfault && dfault.ref[0] === '$now') {
57
57
  row[key] =
58
- categoryArgs.type === 'cds.DateTime'
58
+ categoryArgs._type === 'cds.DateTime'
59
59
  ? new Date(req.timestamp).toISOString().replace(/\.\d\d\d/, '')
60
60
  : req.timestamp
61
61
  }
@@ -80,9 +80,9 @@ const _processCategory = ({ category, row, key, element, val, req }) => {
80
80
 
81
81
  for (const keyName in element._target.keys) {
82
82
  const k = element._target.keys[keyName]
83
- if (k.type === 'cds.Association' || k.name === 'IsActiveEntity') continue
83
+ if (k.isAssociation || k.name === 'IsActiveEntity') continue
84
84
 
85
- if (k.type !== 'cds.UUID' && !(k.name in val)) {
85
+ if (!k.isUUID && !(k.name in val)) {
86
86
  req.error(400, 'ASSERT_NOT_NULL', key + '[' + k + ']', [key + '[' + k + ']'])
87
87
  return
88
88
  }
@@ -139,12 +139,7 @@ const _pickCRUD = element => {
139
139
  return { categories } // > no need to continue
140
140
  }
141
141
 
142
- if (
143
- element.notNull &&
144
- element['@assert.notNull'] !== false &&
145
- !element.default &&
146
- element.type !== 'cds.Association'
147
- ) {
142
+ if (element.notNull && element['@assert.notNull'] !== false && !element.default && !element._isAssociationStrict) {
148
143
  categories.push('!default')
149
144
  }
150
145
 
@@ -12,8 +12,11 @@ const _isLinked = req => {
12
12
  }
13
13
 
14
14
  function handler(req) {
15
- // REVISIT: req.target._unresolved for join queries
16
- if (!this.model || typeof req.query === 'string' /* || !req.target || req.target._unresolved */) {
15
+ if (typeof req.query === 'string') return
16
+
17
+ if (!this.model) {
18
+ // best-effort rewrite of path in from
19
+ req.query = cqn2cqn4sql(req.query, { definitions: {} }, { service: this })
17
20
  return
18
21
  }
19
22
 
@@ -73,14 +73,14 @@ const _flatToStructured = (structuredType, data, prefixes = [], subData = data)
73
73
  * @returns {Promise}
74
74
  */
75
75
  module.exports = function (result, req) {
76
+ // REVISIT if result has a property length that is 0 we will return here
76
77
  if (!this.model || result == null || result.length === 0) return
77
78
 
78
- if (!Array.isArray(result)) result = [result]
79
-
80
79
  // REVISIT: No entity for sets/unions outside of common draft scenarios
81
80
  const entity = getEntityFromCQN(req, this)
82
81
  if (!entity) return
83
82
 
83
+ if (!Array.isArray(result)) result = [result]
84
84
  const structuredTypes = _getStructuredTypes(entity)
85
85
  for (let i = 0; i < result.length; i++) {
86
86
  const d = result[i]
@@ -7,7 +7,7 @@ const deleteFn = (executeDeleteCQN, executeUpdateCQN) => async (model, dbc, quer
7
7
  const isoTs = timestampToISO(timestamp)
8
8
 
9
9
  let result
10
- if (hasDeepDelete(model, query)) {
10
+ if (model && hasDeepDelete(model, query)) {
11
11
  let cqns = await getDeepDeleteCQNs(model, req)
12
12
 
13
13
  // the delete chunks, i.e., how many deletes can be processed in parallel
@@ -25,7 +25,7 @@ const deleteFn = (executeDeleteCQN, executeUpdateCQN) => async (model, dbc, quer
25
25
  result = results[results.length - 1]
26
26
  } else {
27
27
  result = await executeDeleteCQN(model, dbc, query, user, locale, isoTs)
28
- if (result) {
28
+ if (result && model) {
29
29
  const updateCQNs = await getSetNullParentForeignKeyCQNs(model, req)
30
30
  for (const cqn of updateCQNs) {
31
31
  await executeUpdateCQN(model, dbc, cqn, user, locale, isoTs)
@@ -6,7 +6,7 @@ const insert = executeInsertCQN => async (model, dbc, query, req) => {
6
6
  const { user, locale, timestamp } = req
7
7
  const isoTs = timestampToISO(timestamp)
8
8
 
9
- if (hasDeepInsert(model, query)) {
9
+ if (model && hasDeepInsert(model, query)) {
10
10
  const cqns = getFlatArray(getDeepInsertCQNs(model, query))
11
11
 
12
12
  // return array of individual results
@@ -12,7 +12,8 @@ const _includesCompositionTarget = (cqns, target) => {
12
12
 
13
13
  const _getFilteredCqns = (cqns, model) => {
14
14
  // right to left processing necessary!
15
- for (let i = cqns.length - 1; i >= 0; i--) {
15
+ // no need to process first (= root)
16
+ for (let i = cqns.length - 1; i > 0; i--) {
16
17
  const cqn = cqns[i]
17
18
 
18
19
  const entity = model && cqn.UPDATE && model.definitions[cqn.UPDATE.entity]
@@ -26,21 +27,15 @@ const _getFilteredCqns = (cqns, model) => {
26
27
  let moreThanManaged = Object.keys(cqn.UPDATE.data).some(
27
28
  k => entity.elements[k]['@cds.on.update'] === undefined || !cqn.UPDATE.data[k].startsWith('$')
28
29
  )
29
-
30
30
  if (moreThanManaged) continue
31
31
 
32
- // REVISIT: remove feature flag update_header_item after grace period of at least two months (> April release)
33
- if (cds.env.features.update_header_item !== false) {
34
- const comps = Object.values(entity.associations || {}).filter(assoc => assoc._isCompositionEffective)
35
-
36
- for (const comp of comps) {
37
- if (_includesCompositionTarget(cqns, comp.target)) {
38
- moreThanManaged = true
39
- break
40
- }
32
+ const comps = Object.values(entity.associations || {}).filter(assoc => assoc._isCompositionEffective)
33
+ for (const comp of comps) {
34
+ if (_includesCompositionTarget(cqns, comp.target)) {
35
+ moreThanManaged = true
36
+ break
41
37
  }
42
38
  }
43
-
44
39
  if (moreThanManaged) continue
45
40
 
46
41
  // remove current cqn
@@ -54,7 +49,7 @@ const update = executeUpdateCQN => async (model, dbc, req) => {
54
49
  const { query, user, locale, timestamp } = req
55
50
  const isoTs = timestampToISO(timestamp)
56
51
 
57
- if (hasDeepUpdate(model, query)) {
52
+ if (model && hasDeepUpdate(model, query)) {
58
53
  // REVISIT: _activeData gets set in case of draftActivate for performance, but this is a layer violation
59
54
  let selectData = req._ && req._.query && req._.query._activeData
60
55
 
@@ -71,7 +66,7 @@ const update = executeUpdateCQN => async (model, dbc, req) => {
71
66
  const chunks = []
72
67
  for (const each of cqns) chunks.push(each.filter(e => e.DELETE).length)
73
68
 
74
- // remove queries that only want to update @cds.on.update properties
69
+ // remove child queries that only want to update @cds.on.update properties
75
70
  cqns = _getFilteredCqns(getFlatArray(cqns), model)
76
71
 
77
72
  if (cqns.length === 0) return 0
@@ -102,8 +102,9 @@ class CreateBuilder extends BaseBuilder {
102
102
  if (structuredType[property].elements) {
103
103
  this._flattenStructuredElement(`${prefix}_${property}`, structuredType[property].elements, flattenedElements)
104
104
  } else if (
105
- structuredType[property].type === 'cds.Association' &&
106
- (structuredType[property].keys || structuredType[property].foreignKeys)
105
+ structuredType[property].isAssociation &&
106
+ (structuredType[property].keys ||
107
+ (Array.isArray(structuredType[property].foreignKeys) && structuredType[property].foreignKeys))
107
108
  ) {
108
109
  // OLD CSN
109
110
  flattenedElements.push(...this._association(`${prefix}_${property}`, structuredType[property]))
@@ -118,7 +119,7 @@ class CreateBuilder extends BaseBuilder {
118
119
  }
119
120
 
120
121
  _association(associationName, element) {
121
- const keys = element.foreignKeys || element.keys // OLD CSN
122
+ const keys = element.keys || (Array.isArray(element.foreignKeys) && element.foreignKeys) // OLD CSN
122
123
  if (keys) {
123
124
  return keys.map(key => {
124
125
  const ref = key.ref ? key.ref[0] : key // OLD CSN
@@ -56,12 +56,13 @@ class FunctionBuilder extends BaseBuilder {
56
56
  }
57
57
 
58
58
  _escapeLikeParameters(parameters) {
59
- for (const parameter of parameters) {
60
- if (parameter.val) parameter.val = parameter.val.replace(/(\^|_|%)/g, '^$1')
61
- else if (parameter.func) parameter.args = this._escapeLikeParameters(parameter.args)
62
- }
63
-
64
- return parameters
59
+ return parameters.map(parameter => {
60
+ if (parameter.val) return { ...parameter, val: parameter.val.replace(/(\^|_|%)/g, '^$1') }
61
+ if (parameter.func) {
62
+ return { ...parameter, args: this._escapeLikeParameters(parameter.args) }
63
+ }
64
+ return parameter
65
+ })
65
66
  }
66
67
 
67
68
  _handleFunction() {
@@ -117,8 +118,7 @@ class FunctionBuilder extends BaseBuilder {
117
118
  const functionName = this._functionName()
118
119
  const not = functionName.startsWith('not') ? 'NOT ' : ''
119
120
  const columns = this._columns(args)
120
- const params = args.slice(1)
121
- this._escapeLikeParameters(params)
121
+ const params = this._escapeLikeParameters(args.slice(1))
122
122
 
123
123
  const _pattern = (() => {
124
124
  if (functionName.includes('contains')) return _ => ["'%'", _, "'%'"]
@@ -67,6 +67,12 @@ class InsertBuilder extends BaseBuilder {
67
67
 
68
68
  const entityName = this._into()
69
69
 
70
+ // table alias as string
71
+ if (this._obj.INSERT.into.as) {
72
+ this._outputObj.sql.push('AS')
73
+ this._outputObj.sql.push(this._obj.INSERT.into.as)
74
+ }
75
+
70
76
  // side effect: sets this.uuidKeys if found any
71
77
  this._findUuidKeys(entityName)
72
78
 
@@ -89,6 +95,7 @@ class InsertBuilder extends BaseBuilder {
89
95
  this._entries(annotatedColumns)
90
96
  }
91
97
 
98
+ // INSERT INTO ... WITH ... AS SELECT FROM ...
92
99
  if (this._obj.INSERT.as) {
93
100
  this._as(this._obj.INSERT.as)
94
101
  }
@@ -116,6 +123,12 @@ class InsertBuilder extends BaseBuilder {
116
123
  return this._obj.INSERT.into
117
124
  }
118
125
 
126
+ if (this._obj.INSERT.into.ref) {
127
+ const dbName = this._getDatabaseName(this._obj.INSERT.into.ref[0])
128
+ this._outputObj.sql.push(this._quoteElement(dbName))
129
+ return this._obj.INSERT.into.ref[0]
130
+ }
131
+
119
132
  const dbName = this._getDatabaseName(this._obj.INSERT.into.name)
120
133
  this._outputObj.sql.push(this._quoteElement(dbName))
121
134
  return this._obj.INSERT.into.name
@@ -139,7 +152,7 @@ class InsertBuilder extends BaseBuilder {
139
152
  const uuidKeys = []
140
153
  if (this._csn && this._csn.definitions[entityName] && this._csn.definitions[entityName].keys) {
141
154
  for (const key of Object.values(this._csn.definitions[entityName].keys)) {
142
- if (key.type === 'cds.UUID') {
155
+ if (key.isUUID) {
143
156
  uuidKeys.push(key.name)
144
157
  }
145
158
  }
@@ -1,7 +1,7 @@
1
1
  const cds = require('../../cds')
2
2
 
3
3
  const BaseBuilder = require('./BaseBuilder')
4
- const { DRAFT_COLUMNS } = require('../../common/constants/draft')
4
+ const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
5
5
 
6
6
  /**
7
7
  * SelectBuilder is used to take a CQN object as an input and to build a SQL Select string from it.
@@ -81,6 +81,7 @@ class SelectBuilder extends BaseBuilder {
81
81
  }
82
82
 
83
83
  this._columns(noQuoting)
84
+
84
85
  this._from()
85
86
 
86
87
  if (this._obj.SELECT.where && this._obj.SELECT.where.length) {
@@ -331,7 +332,7 @@ class SelectBuilder extends BaseBuilder {
331
332
  _quoteDraftSpecificColumns(element) {
332
333
  if (this._quotingStyle === 'plain' && element.ref) {
333
334
  element.ref = element.ref.map(el => {
334
- if (DRAFT_COLUMNS.includes(el)) {
335
+ if (el in DRAFT_COLUMNS_MAP) {
335
336
  return this._quote(el)
336
337
  }
337
338
 
@@ -25,12 +25,12 @@ typeConversionMap.set('cds.LargeBinary', 'BLOB')
25
25
  * @private
26
26
  */
27
27
  const convertDataType = (element, csn, options) => {
28
- const converted = options.typeConversion.get(element.type)
28
+ const converted = options.typeConversion.get(element._type)
29
29
 
30
30
  if (!converted) {
31
31
  // no type in map
32
- const newType = csn.definitions[element.type]
33
- return newType ? convertDataType(newType, csn, options) : element.type
32
+ const newType = csn.definitions[element._type]
33
+ return newType ? convertDataType(newType, csn, options) : element._type
34
34
  }
35
35
 
36
36
  if (typeof converted === 'string') {
@@ -1,7 +1,7 @@
1
1
  const cds = require('../../cds')
2
2
  const resolveStructured = require('../../common/utils/resolveStructured')
3
3
 
4
- const { DRAFT_COLUMNS } = require('../../common/constants/draft')
4
+ const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
5
5
 
6
6
  /**
7
7
  * This method gets all columns for an entity.
@@ -21,8 +21,8 @@ const getColumns = (entity, { _4db, onlyKeys } = { _4db: true, onlyKeys: false }
21
21
  const element = elements[elementName]
22
22
  if (onlyKeys && !element.key) continue
23
23
  if (element.isAssociation) continue
24
- if (_4db && entity._isDraftEnabled && DRAFT_COLUMNS.includes(elementName)) continue
25
- if (cds.env.effective.odata.structs && element.elements) {
24
+ if (_4db && entity._isDraftEnabled && elementName in DRAFT_COLUMNS_MAP) continue
25
+ if ((cds.env.effective.odata.structs || cds.env.features.ucsn_struct_conversion) && element.elements) {
26
26
  columnNames.push(...resolveStructured({ structName: elementName, structProperties: [] }, element.elements, false))
27
27
  continue
28
28
  }