@sap/cds 5.4.6 → 5.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/CHANGELOG.md +208 -2
  2. package/apis/ql.d.ts +17 -15
  3. package/app/index.js +1 -1
  4. package/bin/build/buildTaskEngine.js +26 -42
  5. package/bin/build/buildTaskFactory.js +6 -10
  6. package/bin/build/buildTaskHandler.js +2 -4
  7. package/bin/build/buildTaskProvider.js +3 -1
  8. package/bin/build/buildTaskProviderFactory.js +9 -15
  9. package/bin/build/constants.js +15 -3
  10. package/bin/build/index.js +5 -4
  11. package/bin/build/mtaUtil.js +8 -11
  12. package/bin/build/provider/buildTaskHandlerEdmx.js +63 -6
  13. package/bin/build/provider/buildTaskHandlerInternal.js +2 -34
  14. package/bin/build/provider/buildTaskProviderInternal.js +16 -42
  15. package/bin/build/provider/fiori/index.js +13 -24
  16. package/bin/build/provider/hana/2migration.js +17 -15
  17. package/bin/build/provider/hana/2tabledata.js +52 -48
  18. package/bin/build/provider/hana/index.js +27 -25
  19. package/bin/build/provider/hana/migrationtable.js +91 -67
  20. package/bin/build/provider/java-cf/index.js +14 -24
  21. package/bin/build/provider/mtx/index.js +12 -14
  22. package/bin/build/provider/node-cf/index.js +18 -32
  23. package/bin/cds.js +5 -5
  24. package/bin/serve.js +29 -23
  25. package/bin/version.js +0 -1
  26. package/lib/compile/etc/_localized.js +4 -9
  27. package/lib/compile/for/sql.js +5 -2
  28. package/lib/compile/parse.js +25 -17
  29. package/lib/compile/to/srvinfo.js +2 -1
  30. package/lib/connect/bindings.js +2 -1
  31. package/lib/connect/index.js +48 -49
  32. package/lib/core/classes.js +1 -1
  33. package/lib/core/reflect.js +10 -2
  34. package/lib/deploy.js +26 -23
  35. package/lib/env/defaults.js +13 -6
  36. package/lib/env/index.js +73 -78
  37. package/lib/env/requires.js +38 -19
  38. package/lib/index.js +9 -10
  39. package/lib/lazy.js +2 -2
  40. package/lib/log/index.js +33 -45
  41. package/lib/log/service/index.js +2 -2
  42. package/lib/ql/CREATE.js +14 -9
  43. package/lib/ql/DELETE.js +6 -5
  44. package/lib/ql/DROP.js +12 -9
  45. package/lib/ql/INSERT.js +40 -16
  46. package/lib/ql/Query.js +67 -40
  47. package/lib/ql/SELECT.js +162 -127
  48. package/lib/ql/UPDATE.js +74 -42
  49. package/lib/ql/Whereable.js +77 -87
  50. package/lib/ql/index.js +36 -24
  51. package/lib/ql/parse.js +35 -0
  52. package/lib/req/context.js +44 -8
  53. package/lib/req/locale.js +7 -7
  54. package/lib/serve/Service-api.js +21 -14
  55. package/lib/serve/Service-dispatch.js +28 -12
  56. package/lib/serve/Transaction.js +22 -10
  57. package/lib/serve/index.js +16 -11
  58. package/lib/utils/axios.js +23 -16
  59. package/lib/utils/data.js +35 -0
  60. package/lib/utils/tests.js +27 -18
  61. package/libx/_runtime/audit/generic/personal/access.js +81 -0
  62. package/libx/_runtime/audit/generic/personal/constants.js +4 -0
  63. package/libx/_runtime/audit/generic/personal/index.js +50 -0
  64. package/libx/_runtime/audit/generic/personal/modification.js +138 -0
  65. package/libx/_runtime/audit/generic/personal/utils.js +186 -0
  66. package/libx/_runtime/audit/utils/v2.js +10 -4
  67. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +5 -5
  68. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +6 -7
  69. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +5 -7
  70. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +5 -7
  71. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +2 -3
  72. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +4 -0
  73. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +7 -4
  74. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +59 -8
  75. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +11 -1
  76. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +6 -10
  77. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +3 -46
  78. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +2 -5
  79. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/createToCQN.js +2 -2
  80. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/deleteToCQN.js +4 -3
  81. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -2
  82. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
  83. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +1 -1
  84. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +2 -2
  85. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +16 -18
  86. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/EdmEntityType.js +6 -3
  87. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/format/RepresentationKind.js +4 -1
  88. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/core/OdataRequest.js +1 -0
  89. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/SerializerFactory.js +15 -2
  90. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/OperationValidator.js +1 -0
  91. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
  92. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +8 -1
  93. package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +6 -1
  94. package/libx/_runtime/cds-services/adapter/odata-v4/utils/omitValues.js +12 -5
  95. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -7
  96. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +7 -7
  97. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +14 -18
  98. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +13 -13
  99. package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +0 -1
  100. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +2 -1
  101. package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +2 -2
  102. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +2 -4
  103. package/libx/_runtime/cds-services/adapter/rest/utils/result.js +4 -2
  104. package/libx/_runtime/cds-services/services/Service.js +40 -5
  105. package/libx/_runtime/cds-services/services/utils/columns.js +13 -7
  106. package/libx/_runtime/cds-services/services/utils/compareJson.js +88 -4
  107. package/libx/_runtime/cds-services/services/utils/differ.js +24 -6
  108. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -2
  109. package/libx/_runtime/common/composition/data.js +44 -55
  110. package/libx/_runtime/common/composition/delete.js +97 -71
  111. package/libx/_runtime/common/composition/index.js +2 -1
  112. package/libx/_runtime/common/composition/insert.js +34 -11
  113. package/libx/_runtime/common/composition/tree.js +119 -92
  114. package/libx/_runtime/common/composition/update.js +4 -1
  115. package/libx/_runtime/common/composition/utils.js +1 -3
  116. package/libx/_runtime/common/constants/draft.js +12 -1
  117. package/libx/_runtime/common/generic/auth.js +6 -22
  118. package/libx/_runtime/common/generic/crud.js +14 -13
  119. package/libx/_runtime/common/generic/input.js +23 -26
  120. package/libx/_runtime/common/generic/put.js +1 -1
  121. package/libx/_runtime/common/generic/sorting.js +16 -16
  122. package/libx/_runtime/common/i18n/index.js +1 -1
  123. package/libx/_runtime/common/i18n/messages.properties +4 -0
  124. package/libx/_runtime/common/utils/backlinks.js +12 -5
  125. package/libx/_runtime/common/utils/cqn.js +6 -1
  126. package/libx/_runtime/common/utils/cqn2cqn4sql.js +102 -101
  127. package/libx/_runtime/common/utils/csn.js +47 -4
  128. package/libx/_runtime/common/utils/data.js +0 -37
  129. package/libx/_runtime/common/utils/enrichWithKeysFromWhere.js +1 -1
  130. package/libx/_runtime/common/utils/entityFromCqn.js +7 -24
  131. package/libx/_runtime/common/utils/foreignKeyPropagations.js +39 -7
  132. package/libx/_runtime/common/utils/generateOnCond.js +11 -12
  133. package/libx/_runtime/common/utils/onlyKeysRemain.js +10 -0
  134. package/libx/_runtime/common/utils/path.js +35 -0
  135. package/libx/_runtime/common/utils/postProcessing.js +86 -0
  136. package/libx/_runtime/common/utils/quotingStyles.js +37 -26
  137. package/libx/_runtime/common/utils/resolveView.js +223 -171
  138. package/libx/_runtime/common/utils/rewriteAsterisk.js +46 -26
  139. package/libx/_runtime/common/utils/structured.js +6 -12
  140. package/libx/_runtime/common/utils/template.js +10 -5
  141. package/libx/_runtime/common/utils/templateDelimiter.js +1 -0
  142. package/libx/_runtime/common/utils/templateProcessor.js +22 -30
  143. package/libx/_runtime/common/utils/union.js +31 -0
  144. package/libx/_runtime/common/utils/unionCqnTemplate.js +184 -0
  145. package/libx/_runtime/db/Service.js +1 -1
  146. package/libx/_runtime/db/data-conversion/timestamp.js +2 -9
  147. package/libx/_runtime/db/expand/expandCQNToJoin.js +204 -297
  148. package/libx/_runtime/db/expand/index.js +3 -3
  149. package/libx/_runtime/db/expand/rawToExpanded.js +36 -7
  150. package/libx/_runtime/db/generic/index.js +1 -1
  151. package/libx/_runtime/db/generic/input.js +5 -7
  152. package/libx/_runtime/db/generic/integrity.js +1 -1
  153. package/libx/_runtime/db/generic/rewrite.js +2 -10
  154. package/libx/_runtime/db/generic/update.js +13 -5
  155. package/libx/_runtime/db/generic/virtual.js +22 -58
  156. package/libx/_runtime/db/query/delete.js +7 -4
  157. package/libx/_runtime/db/query/insert.js +6 -4
  158. package/libx/_runtime/db/query/read.js +13 -20
  159. package/libx/_runtime/db/query/run.js +4 -1
  160. package/libx/_runtime/db/query/update.js +5 -4
  161. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +35 -2
  162. package/libx/_runtime/db/sql-builder/FunctionBuilder.js +17 -2
  163. package/libx/_runtime/db/sql-builder/InsertBuilder.js +6 -5
  164. package/libx/_runtime/db/sql-builder/ReferenceBuilder.js +10 -0
  165. package/libx/_runtime/db/sql-builder/SelectBuilder.js +35 -24
  166. package/libx/_runtime/db/sql-builder/UpdateBuilder.js +14 -4
  167. package/libx/_runtime/db/sql-builder/arrayed.js +4 -0
  168. package/libx/_runtime/db/utils/deep.js +8 -0
  169. package/libx/_runtime/db/utils/generateAliases.js +2 -1
  170. package/libx/_runtime/fiori/generic/activate.js +19 -15
  171. package/libx/_runtime/fiori/generic/before.js +3 -11
  172. package/libx/_runtime/fiori/generic/cancel.js +1 -1
  173. package/libx/_runtime/fiori/generic/delete.js +3 -1
  174. package/libx/_runtime/fiori/generic/edit.js +12 -2
  175. package/libx/_runtime/fiori/generic/new.js +5 -5
  176. package/libx/_runtime/fiori/generic/patch.js +0 -18
  177. package/libx/_runtime/fiori/generic/read.js +241 -189
  178. package/libx/_runtime/fiori/utils/delete.js +36 -7
  179. package/libx/_runtime/fiori/utils/handler.js +43 -44
  180. package/libx/_runtime/fiori/utils/where.js +30 -15
  181. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +4 -6
  182. package/libx/_runtime/hana/execute.js +2 -2
  183. package/libx/_runtime/hana/localized.js +4 -4
  184. package/libx/_runtime/hana/pool.js +29 -14
  185. package/libx/_runtime/hana/search2cqn4sql.js +2 -1
  186. package/libx/_runtime/hana/searchToContains.js +18 -14
  187. package/libx/_runtime/index.js +0 -5
  188. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +13 -5
  189. package/libx/_runtime/messaging/common-utils/naming-conventions.js +4 -1
  190. package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +31 -19
  191. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +1 -2
  192. package/libx/_runtime/messaging/enterprise-messaging.js +6 -4
  193. package/libx/_runtime/messaging/service.js +7 -6
  194. package/libx/_runtime/odata/cqn2odata.js +110 -43
  195. package/libx/_runtime/odata/index.js +26 -48
  196. package/libx/_runtime/odata/odata2cqn.js +1 -6154
  197. package/libx/_runtime/odata/odata2cqn.pegjs +559 -0
  198. package/libx/_runtime/odata/readToCqn.js +94 -64
  199. package/libx/_runtime/remote/Service.js +74 -21
  200. package/libx/_runtime/remote/cqn2odata/index.js +1 -5
  201. package/libx/_runtime/remote/utils/client.js +24 -101
  202. package/libx/_runtime/remote/utils/dataConversion.js +27 -12
  203. package/libx/_runtime/sqlite/Service.js +3 -5
  204. package/libx/_runtime/sqlite/execute.js +23 -24
  205. package/libx/_runtime/sqlite/localized.js +12 -7
  206. package/libx/_runtime/types/api.js +10 -0
  207. package/package.json +1 -1
  208. package/server.js +16 -2
  209. package/lib/ql/grammar.pegjs +0 -208
  210. package/lib/ql/parser.js +0 -1
  211. package/lib/ql/rt/DELETE.js +0 -29
  212. package/lib/ql/rt/INSERT.js +0 -23
  213. package/lib/ql/rt/Query.js +0 -84
  214. package/lib/ql/rt/SELECT.js +0 -174
  215. package/lib/ql/rt/UPDATE.js +0 -119
  216. package/lib/ql/rt/_helpers.js +0 -91
  217. package/lib/ql/rt/index.js +0 -32
  218. package/libx/_runtime/audit/generic/personal.js +0 -260
  219. package/libx/_runtime/cds-services/statements/BaseStatement.js +0 -72
  220. package/libx/_runtime/cds-services/statements/Create.js +0 -57
  221. package/libx/_runtime/cds-services/statements/Delete.js +0 -33
  222. package/libx/_runtime/cds-services/statements/Drop.js +0 -42
  223. package/libx/_runtime/cds-services/statements/Insert.js +0 -201
  224. package/libx/_runtime/cds-services/statements/Select.js +0 -826
  225. package/libx/_runtime/cds-services/statements/Update.js +0 -181
  226. package/libx/_runtime/cds-services/statements/Where.js +0 -726
  227. package/libx/_runtime/cds-services/statements/index.js +0 -25
  228. package/libx/_runtime/common/generic/resolve-mock.js +0 -9
@@ -9,8 +9,7 @@ const { foreignKeyPropagations } = require('./foreignKeyPropagations')
9
9
  const search2cqn4sql = require('./search2cqn4sql')
10
10
  const { getEntityNameFromCQN } = require('./entityFromCqn')
11
11
  const getError = require('../../common/error')
12
-
13
- const SYMBOL_FROM_ANNOTATION = Symbol.for('sap.cds.FROM_ANNOTATION')
12
+ const { getPathFromRef, getEntityFromPath } = require('../../common/utils/path')
14
13
 
15
14
  const PARENT_ALIAS = '_parent_'
16
15
  const PARENT_ALIAS_REGEX = new RegExp('^' + PARENT_ALIAS + '\\d*$')
@@ -48,9 +47,7 @@ const _addParentAlias = (where, alias) => {
48
47
 
49
48
  const _addAliasToElement = (e, alias) => {
50
49
  if (e.ref) {
51
- const aliased = { ref: [alias, ...e.ref] }
52
- if (e[SYMBOL_FROM_ANNOTATION]) aliased[SYMBOL_FROM_ANNOTATION] = true
53
- return aliased
50
+ return { ref: [alias, ...e.ref] }
54
51
  }
55
52
 
56
53
  if (e.list) {
@@ -95,10 +92,10 @@ const _getTargetFromRef = ref => {
95
92
 
96
93
  const _getEntityName = (fromClause, entity, i) => {
97
94
  const targetName = _getTargetFromRef(fromClause.ref[i])
98
- return i === 0 ? targetName : entity.elements[targetName].target
95
+ return i === 0 ? targetName : entity && entity.elements[targetName] && entity.elements[targetName].target
99
96
  }
100
97
 
101
- const convertPathExpressionToWhere = (fromClause, model) => {
98
+ const convertPathExpressionToWhere = (fromClause, model, options) => {
102
99
  if (fromClause.ref.length === 1) {
103
100
  const target = _getTargetFromRef(fromClause.ref[0])
104
101
  const alias = fromClause.as
@@ -108,6 +105,7 @@ const convertPathExpressionToWhere = (fromClause, model) => {
108
105
 
109
106
  let previousSelect, previousEntityName, previousTableAlias, structParent
110
107
  let prefix = []
108
+ let columns
111
109
  for (let i = 0; i < fromClause.ref.length; i++) {
112
110
  const entity = structParent || model.definitions[previousEntityName]
113
111
  const element = _elementFromRef(fromClause.ref[i], entity)
@@ -116,14 +114,16 @@ const convertPathExpressionToWhere = (fromClause, model) => {
116
114
  prefix.push(element.name)
117
115
  structParent = element
118
116
  continue
119
- }
120
-
121
- if (element && element.isAssociation && fromClause.ref[i].where) {
122
- const target = element._target
123
- _modifyNavigationInWhere(fromClause.ref[i].where, target)
117
+ } else if (element && element.isAssociation) {
118
+ _modifyNavigationInWhere(fromClause.ref[i].where, element._target)
119
+ } else if (element && previousSelect && i === fromClause.ref.length - 1) {
120
+ columns = [{ ref: [...prefix, element.name] }]
121
+ fromClause.ref.splice(-1)
122
+ continue
124
123
  }
125
124
 
126
125
  const currentEntityName = _getEntityName(fromClause, entity, i)
126
+ if (!currentEntityName) continue
127
127
  const tableAlias = `T${i}`
128
128
  const currentSelect = SELECT.from(`${currentEntityName} as ${tableAlias}`)
129
129
 
@@ -145,7 +145,7 @@ const convertPathExpressionToWhere = (fromClause, model) => {
145
145
  model,
146
146
  prefix
147
147
  )
148
-
148
+ _convertSelect(previousSelect, model, options)
149
149
  currentSelect.where('exists', previousSelect)
150
150
  }
151
151
 
@@ -159,7 +159,8 @@ const convertPathExpressionToWhere = (fromClause, model) => {
159
159
  return {
160
160
  target: previousEntityName,
161
161
  alias: previousTableAlias,
162
- where: previousSelect && previousSelect.SELECT && previousSelect.SELECT.where
162
+ where: previousSelect && previousSelect.SELECT && previousSelect.SELECT.where,
163
+ columns
163
164
  }
164
165
  }
165
166
 
@@ -272,21 +273,17 @@ const _getLambdaSubSelect = (cqn, index, lambdaOp, model, options) => {
272
273
  const innerAlias = FOREIGN_ALIAS + options.lambdaIteration
273
274
  cqn.SELECT.from.as = outerAlias
274
275
 
276
+ const queryTarget = getEntityFromPath(getPathFromRef(cqn.SELECT.from.ref), model)
277
+
275
278
  const nav = cqn.SELECT.where[index].ref.map(el => (el.id ? el.id : el))
276
- const navName = nav[0]
277
279
  const last = cqn.SELECT.where[index].ref.slice(-1)[0]
278
- // REVISIT: for some reason cqn._target is not the csn entity here
279
- const queryTarget =
280
- typeof cqn._target.name === 'string'
281
- ? model.definitions[cqn._target.name.split('[')[0]]
282
- : cqn._target.name.id
283
- ? model.definitions[cqn._target.name.id]
284
- : cqn._target
280
+ const navName = queryTarget.elements[nav[0]] ? nav[0] : nav[nav.length - 1]
285
281
  const navElement = queryTarget.elements[navName]
286
282
  const lastElement = nav.reduce((csn, segment) => {
287
283
  if (csn.items) return csn // arrayed not supported
288
284
  if (csn.elements) {
289
285
  const next = csn.elements[segment]
286
+ if (!next) return csn
290
287
  if (next.target) return model.definitions[next.target]
291
288
  return next
292
289
  }
@@ -298,7 +295,7 @@ const _getLambdaSubSelect = (cqn, index, lambdaOp, model, options) => {
298
295
  ? nav.length > 1
299
296
  ? last.where.map(_unshiftRefsWithNavigation(nav.slice(1)))
300
297
  : last.where
301
- : [{ val: 1 }, '=', { val: 1 }]
298
+ : undefined
302
299
  const onConditionOptions = {
303
300
  associationNames: navName,
304
301
  csn: model,
@@ -310,7 +307,21 @@ const _getLambdaSubSelect = (cqn, index, lambdaOp, model, options) => {
310
307
  const onCondition = getOnCond(navElement, onConditionOptions)
311
308
 
312
309
  const subSelect = SELECT.from({ ref: [navElement.target], as: innerAlias })
313
- if (condition) subSelect.where(condition)
310
+ if (condition) {
311
+ if (subSelect.SELECT.from.as) {
312
+ for (let i = 0; i < condition.length; i++) {
313
+ if (
314
+ condition[i].ref &&
315
+ condition[i].ref.length > 1 &&
316
+ condition[i].ref.every(r => typeof r === 'string') &&
317
+ condition[i].ref[0] === navName
318
+ ) {
319
+ condition[i].ref[0] = subSelect.SELECT.from.as
320
+ }
321
+ }
322
+ }
323
+ subSelect.where(condition)
324
+ }
314
325
  subSelect.where(onCondition)
315
326
  if (cds.env.effective.odata.structs) {
316
327
  flattenStructuredSelect(subSelect, model)
@@ -429,22 +440,24 @@ const _convertWhereIfSkip = (whereCQN, index) => {
429
440
  OPERATIONS.includes(whereCQN[index + 1]) ? whereCQN.splice(index + 1, 2) : whereCQN.splice(index - 2, 2)
430
441
  }
431
442
 
432
- const _convertOrderByOrWhereCQN = (orderByOrWhereCQN, cqn, model, processFn) => {
433
- const queryTarget = model.definitions[ensureNoDraftsSuffix(getEntityNameFromCQN(cqn))]
443
+ const _convertOrderByOrWhereCQN = (orderByOrWhereCQN, target, model, processFn) => {
444
+ const queryTarget = model.definitions[ensureNoDraftsSuffix(target)]
445
+
434
446
  orderByOrWhereCQN.forEach((el, index) => {
435
447
  if (el.ref && _skip(queryTarget, el.ref, model)) processFn(orderByOrWhereCQN, index)
436
448
  })
437
449
  }
438
450
 
439
- const _convertOrderByOrWhereIfSkip = (cqn, model) => {
451
+ const _convertOrderByOrWhereIfSkip = (cqn, target, model) => {
440
452
  if (cqn.SELECT.orderBy && cqn.SELECT.orderBy.length > 1) {
441
- _convertOrderByOrWhereCQN(cqn.SELECT.orderBy, cqn, model, _convertOrderByIfSkip)
453
+ _convertOrderByOrWhereCQN(cqn.SELECT.orderBy, target, model, _convertOrderByIfSkip)
442
454
  }
443
455
 
444
456
  if (cqn.SELECT.where) {
445
- _convertOrderByOrWhereCQN(cqn.SELECT.where, cqn, model, _convertWhereIfSkip)
457
+ _convertOrderByOrWhereCQN(cqn.SELECT.where, target, model, _convertWhereIfSkip)
446
458
  }
447
459
  }
460
+
448
461
  const _convertExpand = expand => {
449
462
  expand.forEach(expandElement => {
450
463
  if (expandElement.ref && expandElement.ref[0]) {
@@ -470,9 +483,39 @@ const _convertRefWhereInExpand = columns => {
470
483
  }
471
484
  }
472
485
 
486
+ const _defaultColumns4 = entity => {
487
+ if (entity && entity.elements) {
488
+ const addAliasToColumns = []
489
+ for (const column in entity.elements) {
490
+ if (column === 'DraftAdministrativeData_DraftUUID') continue
491
+ const e = entity.elements[column]
492
+ if (e.isAssociation || e.virtual) continue
493
+ else if (entity._isDraftEnabled && column === 'IsActiveEntity') {
494
+ addAliasToColumns.push({ val: true, as: 'IsActiveEntity', cast: { type: 'cds.Boolean' } })
495
+ } else if (entity._isDraftEnabled && column === 'HasActiveEntity') {
496
+ addAliasToColumns.push({ val: false, as: 'HasActiveEntity', cast: { type: 'cds.Boolean' } })
497
+ } else if (entity._isDraftEnabled && column === 'HasDraftEntity') {
498
+ const draftName = `${entity.name}_drafts`
499
+ const subSelect = SELECT.from(draftName).columns([1])
500
+ for (const k in entity.keys) {
501
+ if (k !== 'IsActiveEntity') subSelect.where([{ ref: [entity.name, k] }, '=', { ref: [draftName, k] }])
502
+ }
503
+ addAliasToColumns.push({
504
+ xpr: ['case', 'when', 'exists', subSelect, 'then', 'true', 'else', 'false', 'end'],
505
+ as: 'HasDraftEntity',
506
+ cast: { type: 'cds.Boolean' }
507
+ })
508
+ } else addAliasToColumns.push({ ref: [column], as: column })
509
+ }
510
+ return addAliasToColumns
511
+ }
512
+ }
513
+
514
+ // eslint-disable-next-line complexity
473
515
  const _convertSelect = (cqn, model, options) => {
474
- // remove virtual and with skip annotated fields in orderby and where
475
- _convertOrderByOrWhereIfSkip(cqn, model)
516
+ // REVISIT: still needed?
517
+ // Moved from old SELECT.from where this done much too eager, always for all queries
518
+ if (!cqn.SELECT.columns && cqn._target) cqn.SELECT.columns = _defaultColumns4(cqn._target)
476
519
 
477
520
  // lambda functions
478
521
  _convertLambda(cqn, model, options)
@@ -488,6 +531,9 @@ const _convertSelect = (cqn, model, options) => {
488
531
 
489
532
  // no path expression
490
533
  if (!cqnSelectFromRef || (cqnSelectFromRef.length === 1 && !cqnSelectFromRef[0].where)) {
534
+ // remove virtual and with skip annotated fields in orderby and where
535
+ _convertOrderByOrWhereIfSkip(cqn, getEntityNameFromCQN(cqn), model)
536
+
491
537
  if (cqnSelectFromRef && cqn.SELECT.search && !options.suppressSearch) {
492
538
  searchOptions = { ...searchOptions, ...{ targetName: cqnSelectFromRef[0] } }
493
539
  search2cqn4sql(cqn, model, searchOptions)
@@ -506,8 +552,12 @@ const _convertSelect = (cqn, model, options) => {
506
552
  }
507
553
 
508
554
  // path expression handling
509
- const { target, alias, where, cardinality } = convertPathExpressionToWhere(cqn.SELECT.from, model)
510
- const select = SELECT.from(target)
555
+ const { target, alias, where, cardinality, columns } = convertPathExpressionToWhere(cqn.SELECT.from, model, options)
556
+
557
+ // remove virtual and with skip annotated fields in orderby and where
558
+ _convertOrderByOrWhereIfSkip(cqn, target, model)
559
+
560
+ const select = SELECT.from(target, columns || cqn.SELECT.columns)
511
561
 
512
562
  if (alias) {
513
563
  select.SELECT.from.as = alias
@@ -533,7 +583,11 @@ const _convertSelect = (cqn, model, options) => {
533
583
 
534
584
  // We add all previous properties ot the newly created query.
535
585
  // Reason is to not lose the query API functionality
536
- Object.assign(select.SELECT, cqn.SELECT, { from: select.SELECT.from, where: select.SELECT.where })
586
+ Object.assign(select.SELECT, cqn.SELECT, {
587
+ columns: select.SELECT.columns,
588
+ from: select.SELECT.from,
589
+ where: select.SELECT.where
590
+ })
537
591
 
538
592
  if (select.SELECT.columns && cds.env.effective.odata.structs) {
539
593
  flattenStructuredSelect(select, model)
@@ -542,35 +596,6 @@ const _convertSelect = (cqn, model, options) => {
542
596
  return select
543
597
  }
544
598
 
545
- const _getElement = (column, columns, target) => {
546
- if (!target) return
547
-
548
- if (columns) {
549
- // if columns is defined, column is index and row[column] should contain value that belongs to name in columns with same index
550
- return target.elements[columns[column]]
551
- }
552
-
553
- return target.elements[column]
554
- }
555
-
556
- const _handleArrayedElements = (rows, target, columns) => {
557
- for (const row of rows) {
558
- for (const column in row) {
559
- const element = _getElement(column, columns, target)
560
-
561
- if (element && element.is2one) {
562
- _handleArrayedElements([row[column]], element._target, columns)
563
- } else if (element && element.is2many) {
564
- _handleArrayedElements(row[column], element._target, columns)
565
- } else if (element && element._isStructured) {
566
- _handleArrayedElements([row[column]], element, columns)
567
- } else if (Array.isArray(row[column])) {
568
- row[column] = JSON.stringify(row[column])
569
- }
570
- }
571
- }
572
- }
573
-
574
599
  const _convertInsert = (cqn, model) => {
575
600
  // resolve path expression
576
601
  const resolvedIntoClause = _convertPathExpressionForInsertOrDelete(cqn.INSERT.into, model)
@@ -585,22 +610,11 @@ const _convertInsert = (cqn, model) => {
585
610
  Object.assign(insert.INSERT, cqn.INSERT, { into: resolvedIntoClause })
586
611
 
587
612
  const targetName = insert.INSERT.into.name || insert.INSERT.into
588
- const queryTarget = model.definitions[ensureNoDraftsSuffix(targetName)]
589
-
590
- if (cds.env.effective.odata.version !== 'v2') {
591
- if (cqn.INSERT.entries) {
592
- _handleArrayedElements(cqn.INSERT.entries, queryTarget)
593
- } else if (cqn.INSERT.rows) {
594
- _handleArrayedElements(cqn.INSERT.rows, queryTarget, cqn.INSERT.columns)
595
- } else if (cqn.INSERT.values) {
596
- _handleArrayedElements([cqn.INSERT.values], queryTarget, cqn.INSERT.columns)
597
- }
598
- }
599
613
 
600
614
  const target = model.definitions[targetName]
601
615
  if (!target) return insert
602
616
 
603
- const resolvedView = resolveView(insert, model, 'db')
617
+ const resolvedView = resolveView(insert, model, cds.db)
604
618
 
605
619
  if (cqn.INSERT.into.ref && cqn.INSERT.into.ref.length > 1) {
606
620
  const copyFrom = [...cqn.INSERT.into.ref]
@@ -612,6 +626,7 @@ const _convertInsert = (cqn, model) => {
612
626
  }
613
627
 
614
628
  function _modifyNavigationInWhere(whereClause, target) {
629
+ if (!whereClause) return
615
630
  whereClause.forEach(e => {
616
631
  if (e.ref && e.ref.length > 1 && target.elements[e.ref[0]]) {
617
632
  const element = target.elements[e.ref[0]]
@@ -633,10 +648,10 @@ const _plainDelete = (cqn, model) => {
633
648
  const target = model.definitions[name]
634
649
  if (!target) return cqn
635
650
 
636
- return resolveView(cqn, model, 'db')
651
+ return resolveView(cqn, model, cds.db)
637
652
  }
638
653
 
639
- const _convertDelete = (cqn, model) => {
654
+ const _convertDelete = (cqn, model, options) => {
640
655
  // .from is plain string or csn entity
641
656
  if (
642
657
  typeof cqn.DELETE.from === 'string' ||
@@ -646,7 +661,7 @@ const _convertDelete = (cqn, model) => {
646
661
  return _plainDelete(cqn, model)
647
662
  }
648
663
 
649
- const { target, alias, where } = convertPathExpressionToWhere(cqn.DELETE.from, model)
664
+ const { target, alias, where } = convertPathExpressionToWhere(cqn.DELETE.from, model, options)
650
665
  const deleet = DELETE('x')
651
666
  Object.assign(deleet.DELETE, cqn.DELETE, { from: target, where: undefined })
652
667
 
@@ -657,37 +672,30 @@ const _convertDelete = (cqn, model) => {
657
672
  const targetEntity = model.definitions[target]
658
673
  if (!targetEntity) return deleet
659
674
 
660
- return resolveView(deleet, model, 'db')
675
+ return resolveView(deleet, model, cds.db)
661
676
  }
662
677
 
663
678
  function _plainUpdate(cqn, model) {
664
679
  const name = cqn.UPDATE.entity.name || (cqn.UPDATE.entity.ref && cqn.UPDATE.entity.ref[0]) || cqn.UPDATE.entity
665
- const queryTarget = model.definitions[ensureNoDraftsSuffix(name)]
666
-
667
- if (cds.env.effective.odata.version !== 'v2') {
668
- cqn.UPDATE.data && _handleArrayedElements([cqn.UPDATE.data], queryTarget)
669
- cqn.UPDATE.with && _handleArrayedElements([cqn.UPDATE.with], queryTarget)
670
- }
671
-
672
680
  const target = model.definitions[name]
673
681
  if (!target) return cqn
674
682
 
675
- return resolveView(cqn, model, 'db')
683
+ return resolveView(cqn, model, cds.db)
676
684
  }
677
685
 
678
- const _convertUpdate = (cqn, model) => {
686
+ const _convertUpdate = (cqn, model, options) => {
679
687
  // REVISIT flatten structured types, currently its done in SQL builder
680
688
 
681
689
  // .into is plain string or csn entity
682
690
  if (
683
691
  typeof cqn.UPDATE.entity === 'string' ||
684
692
  cqn.UPDATE.entity.name ||
685
- (cqn.UPDATE.entity.ref && typeof cqn.UPDATE.entity.ref[0] === 'string')
693
+ (cqn.UPDATE.entity.ref && typeof cqn.UPDATE.entity.ref[0] === 'string' && cqn.UPDATE.entity.ref.length === 1)
686
694
  ) {
687
695
  return _plainUpdate(cqn, model)
688
696
  }
689
697
 
690
- const { target, alias, where } = convertPathExpressionToWhere(cqn.UPDATE.entity, model)
698
+ const { target, alias, where } = convertPathExpressionToWhere(cqn.UPDATE.entity, model, options)
691
699
 
692
700
  // link .with and .data and set query target and remove current where clause
693
701
  // REVISIT: update statement does not accept cqn partial as input
@@ -698,17 +706,10 @@ const _convertUpdate = (cqn, model) => {
698
706
  if (where) update.where(where)
699
707
  if (cqn.UPDATE.where) update.where(_addAliasToExpression(cqn.UPDATE.where, alias))
700
708
 
701
- const queryTarget = model.definitions[target]
702
-
703
- if (cds.env.effective.odata.version !== 'v2') {
704
- cqn.UPDATE.data && _handleArrayedElements([cqn.UPDATE.data], queryTarget)
705
- cqn.UPDATE.with && _handleArrayedElements([cqn.UPDATE.with], queryTarget)
706
- }
707
-
708
709
  const targetEntity = model.definitions[target]
709
710
  if (!targetEntity) return update
710
711
 
711
- return resolveView(update, model, 'db')
712
+ return resolveView(update, model, cds.db)
712
713
  }
713
714
 
714
715
  /**
@@ -729,15 +730,15 @@ const cqn2cqn4sql = (cqn, model, options = { suppressSearch: false }) => {
729
730
  }
730
731
 
731
732
  if (cqn.UPDATE) {
732
- return _convertUpdate(cqn, model)
733
+ return _convertUpdate(cqn, model, options)
733
734
  }
734
735
 
735
736
  if (cqn.INSERT) {
736
- return _convertInsert(cqn, model)
737
+ return _convertInsert(cqn, model, options)
737
738
  }
738
739
 
739
740
  if (cqn.DELETE) {
740
- return _convertDelete(cqn, model)
741
+ return _convertDelete(cqn, model, options)
741
742
  }
742
743
 
743
744
  return cqn
@@ -1,6 +1,7 @@
1
1
  const cds = require('../../cds')
2
2
 
3
3
  const { ensureNoDraftsSuffix } = require('./draft')
4
+ const { getFlatArray } = require('../../db/utils/deep')
4
5
 
5
6
  const getEtagElement = entity => {
6
7
  return Object.values(entity.elements).find(element => element['@odata.etag'])
@@ -41,7 +42,7 @@ const _isDependent = (assoc, parent, target) => {
41
42
  * doing as aspect is difficult due to no global definitons per tenant
42
43
  */
43
44
  const getDependents = (entity, model) => {
44
- if (entity._dependents !== undefined) return entity._dependents
45
+ if (entity.own('__dependents')) return entity.__dependents
45
46
 
46
47
  /** @type {Array|boolean} */
47
48
  let dependents = []
@@ -61,8 +62,49 @@ const getDependents = (entity, model) => {
61
62
  }
62
63
 
63
64
  if (dependents.length === 0) dependents = false
64
- Object.defineProperty(entity, '_dependents', { value: dependents })
65
- return dependents
65
+ return entity.set('__dependents', dependents)
66
+ }
67
+
68
+ const _getUps = (entity, model, previous = []) => {
69
+ const ups = []
70
+ for (const def of Object.values(model.definitions)) {
71
+ if (def.kind !== 'entity') continue
72
+ if (!def.associations) continue
73
+ for (const assoc of Object.values(def.associations)) {
74
+ if (assoc.target !== entity.name) continue
75
+ ups.push({
76
+ entity: assoc.parent,
77
+ assoc: assoc,
78
+ previous
79
+ })
80
+ }
81
+ }
82
+ return ups
83
+ }
84
+
85
+ const _ifDataSubject = (entity, role) => {
86
+ return entity['@PersonalData.EntitySemantics'] === 'DataSubject' && entity['@PersonalData.DataSubjectRole'] === role
87
+ }
88
+
89
+ const getDataSubject = (entity, model, role) => {
90
+ const hash = '__dataSubject4' + role
91
+ if (entity.own(hash)) return entity[hash]
92
+
93
+ if (_ifDataSubject(entity, role)) return entity.set(hash, { entity: entity, assoc: [], previous: [] })
94
+
95
+ let dataSubject
96
+ let ups = _getUps(entity, model)
97
+ while (!dataSubject) {
98
+ for (const { entity, assoc, previous } of ups) {
99
+ if (_ifDataSubject(entity, role)) dataSubject = { entity, assoc, previous }
100
+ }
101
+ if (dataSubject) break
102
+ ups = getFlatArray([
103
+ ...ups.map(up => [..._getUps(up.entity, model, up.previous.concat({ entity: up.entity, assoc: up.assoc }))])
104
+ ])
105
+ }
106
+
107
+ return entity.set(hash, dataSubject)
66
108
  }
67
109
 
68
110
  const _findRootEntity = (entities, edmName) => {
@@ -166,5 +208,6 @@ module.exports = {
166
208
  findCsnTargetFor,
167
209
  getElementDeep,
168
210
  getDependents,
169
- isRootEntity
211
+ isRootEntity,
212
+ getDataSubject
170
213
  }
@@ -1,39 +1,3 @@
1
- // REVISIT: use follow projection
2
-
3
- const getError = require('../error')
4
-
5
- const _renameData = (query, data) => {
6
- if (query.SELECT && query.SELECT.columns) {
7
- for (const col of query.SELECT.columns) {
8
- if (typeof col === 'object' && col.ref && col.as && col.ref[0] !== col.as) {
9
- if (data[col.as]) {
10
- data[col.ref[0]] = data[col.as]
11
- delete data[col.as]
12
- }
13
- }
14
- }
15
- }
16
- }
17
-
18
- const getTargetData = (target, data = {}) => {
19
- if (target.query) {
20
- _renameData(target.query, data)
21
-
22
- if (target.query._target) {
23
- return getTargetData(target.query._target, data)
24
- }
25
-
26
- if (!target.query.from || target.query.from.length > 1 || target.query.where) {
27
- // REVISIT: when is this the case?! getTargetData only used in deleteDraftUtils...
28
- throw getError(501, 'Insert, Update or Delete on views with join|union|where is not supported')
29
- }
30
-
31
- return { target: target.query.from[0].absolute, data }
32
- }
33
-
34
- return { target, data }
35
- }
36
-
37
1
  // REVISIT: remove once not needed anymore
38
2
  const getDataFromCQN = query => (query.INSERT && query.INSERT.entries) || (query.UPDATE && query.UPDATE.data)
39
3
 
@@ -47,7 +11,6 @@ const setDataFromCQN = req => {
47
11
  }
48
12
 
49
13
  module.exports = {
50
- getTargetData,
51
14
  getDataFromCQN,
52
15
  setDataFromCQN
53
16
  }
@@ -85,7 +85,7 @@ module.exports = (data, { query, target }, { model }) => {
85
85
  } else if (query.UPDATE) {
86
86
  const where = _getWhereFromUpdate(query, target, model)
87
87
  if (!where || !where.length) return
88
-
88
+ if (!data) data = query.UPDATE.data = {} // REVISIT: We should not expect data to be present always!
89
89
  _addKeysFromWhereToData(where, target, data)
90
90
  }
91
91
  }
@@ -1,38 +1,21 @@
1
1
  const { ensureNoDraftsSuffix } = require('../../common/utils/draft')
2
2
 
3
- const _entityFromRef = ref => {
4
- if (ref) return ref[0].id || ref[0]
5
- }
6
-
7
3
  const getEntityNameFromCQN = cqn => {
8
- while (cqn.SELECT) {
9
- cqn = cqn.SELECT.from
10
- }
4
+ while (cqn.SELECT) cqn = cqn.SELECT.from
11
5
 
12
- return _getEntityNameFromUnionCQN(cqn) || _entityFromRef(cqn.ref)
13
- }
6
+ // Do the most likely first -> {ref}
7
+ if (cqn.ref) {
8
+ return cqn.ref[0].id || cqn.ref[0]
9
+ }
14
10
 
15
- const _getEntityNameFromUnionCQN = cqn => {
16
11
  // TODO cleanup
17
12
  // REVISIT infer should do this for req.target
18
13
  // REVISIT2 No, req.target doesn't make sense for joins
19
14
  if (cqn.SET) {
20
- return cqn.SET.args
21
- .map(arg => {
22
- return getEntityNameFromCQN(arg)
23
- })
24
- .filter(name => {
25
- return name !== 'DRAFT.DraftAdministrativeData'
26
- })[0]
15
+ return cqn.SET.args.map(getEntityNameFromCQN).find(n => n !== 'DRAFT.DraftAdministrativeData')
27
16
  }
28
17
  if (cqn.join) {
29
- return cqn.args
30
- .map(arg => {
31
- return getEntityNameFromCQN(arg)
32
- })
33
- .filter(name => {
34
- return name !== 'DRAFT.DraftAdministrativeData'
35
- })[0]
18
+ return cqn.args.map(getEntityNameFromCQN).find(n => n !== 'DRAFT.DraftAdministrativeData')
36
19
  }
37
20
  }
38
21