@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
@@ -1,8 +1,10 @@
1
1
  const cds = require('../../cds')
2
2
  let LOG = cds.log('app')
3
3
  let _event
4
+ const PERSISTENCE_TABLE = '@cds.persistence.table'
4
5
 
5
6
  const getError = require('../error')
7
+ const { getEntityNameFromDeleteCQN, getEntityNameFromUpdateCQN } = require('../utils/cqn')
6
8
 
7
9
  const _setInverseTransition = (mapping, ref, mapped) => {
8
10
  const existing = mapping.get(ref)
@@ -50,15 +52,15 @@ const _inverseTransition = transition => {
50
52
  return inverseTransition
51
53
  }
52
54
 
53
- const revertData = (data, transition) => {
55
+ const revertData = (data, transition, service) => {
54
56
  if (!transition || !transition.mapping.size) return data
55
57
  const inverseTransition = _inverseTransition(transition)
56
58
  return Array.isArray(data)
57
- ? data.map(entry => _newData(entry, inverseTransition, true))
58
- : _newData(data, inverseTransition, true)
59
+ ? data.map(entry => _newData(entry, inverseTransition, true, service))
60
+ : _newData(data, inverseTransition, true, service)
59
61
  }
60
62
 
61
- const _newSubData = (newData, key, transition, el, inverse) => {
63
+ const _newSubData = (newData, key, transition, el, inverse, service) => {
62
64
  const val = newData[key]
63
65
  if ((!Array.isArray(val) && typeof val === 'object') || (Array.isArray(val) && val.length !== 0)) {
64
66
  let mapped = transition.mapping.get(key)
@@ -67,13 +69,13 @@ const _newSubData = (newData, key, transition, el, inverse) => {
67
69
  transition.mapping.set(key, mapped)
68
70
  }
69
71
  if (!mapped.transition) {
70
- const subTransition = getTransition(el._target)
72
+ const subTransition = getTransition(el._target, service)
71
73
  mapped.transition = inverse ? _inverseTransition(subTransition) : subTransition
72
74
  }
73
75
  if (Array.isArray(val)) {
74
- newData[key] = val.map(singleVal => _newData(singleVal, mapped.transition, inverse))
76
+ newData[key] = val.map(singleVal => _newData(singleVal, mapped.transition, inverse, service))
75
77
  } else {
76
- newData[key] = _newData(val, mapped.transition, inverse)
78
+ newData[key] = _newData(val, mapped.transition, inverse, service)
77
79
  }
78
80
  }
79
81
  }
@@ -95,7 +97,7 @@ const _newNestedData = (queryTarget, newData, ref, value) => {
95
97
  }
96
98
  }
97
99
 
98
- const _newData = (data, transition, inverse) => {
100
+ const _newData = (data, transition, inverse, service) => {
99
101
  // no transition -> nothing to do
100
102
  if (transition.target && transition.target.name === transition.queryTarget.name) return data
101
103
 
@@ -109,7 +111,11 @@ const _newData = (data, transition, inverse) => {
109
111
  for (const key in newData) {
110
112
  const el = queryTarget && queryTarget.elements && queryTarget.elements[key]
111
113
  const isAssoc = el && el.isAssociation
112
- if (isAssoc) _newSubData(newData, key, transition, el, inverse)
114
+ if (isAssoc) {
115
+ if (newData[key] || (newData[key] === null && service.name === 'db')) {
116
+ _newSubData(newData, key, transition, el, inverse, service)
117
+ }
118
+ }
113
119
 
114
120
  const mapped = transition.mapping.get(key)
115
121
  if (!mapped) {
@@ -142,7 +148,7 @@ const _newData = (data, transition, inverse) => {
142
148
  return newData
143
149
  }
144
150
 
145
- const _newColumns = (columns = [], transition, force = false, withAlias = false) => {
151
+ const _newColumns = (columns = [], transition, service, withAlias = false) => {
146
152
  const newColumns = []
147
153
 
148
154
  columns.forEach(column => {
@@ -167,11 +173,21 @@ const _newColumns = (columns = [], transition, force = false, withAlias = false)
167
173
 
168
174
  // ensure that renaming of a redirected assoc are also respected
169
175
  if (column.expand) {
176
+ // column.ref might be structured elements
177
+ let def
178
+ column.ref.forEach((ref, i) => {
179
+ if (i === 0) {
180
+ def = transition.queryTarget.elements[ref]
181
+ } else {
182
+ def = def.elements[ref]
183
+ }
184
+ })
185
+
170
186
  // reuse _newColumns with new transition
171
- const expandTarget = transition.queryTarget.elements[column.ref[column.ref.length - 1]]._target
172
- const subtransition = getTransition(expandTarget, force)
187
+ const expandTarget = def._target
188
+ const subtransition = getTransition(expandTarget, service)
173
189
 
174
- newColumn.expand = _newColumns(column.expand, subtransition, force, withAlias)
190
+ newColumn.expand = _newColumns(column.expand, subtransition, service, withAlias)
175
191
  }
176
192
  newColumns.push(newColumn)
177
193
  })
@@ -194,19 +210,44 @@ const _newInsertColumns = (columns = [], transition) => {
194
210
  return newColumns
195
211
  }
196
212
 
197
- const _newEntries = (entries = [], transition) => entries.map(entry => _newData(entry, transition))
213
+ const _newWhereRef = (newWhereElement, transition, alias, tableName, isSubSelect) => {
214
+ const newRef = Array.isArray(newWhereElement.ref) ? [...newWhereElement.ref] : [newWhereElement.ref]
215
+ if (newRef[0] === alias) {
216
+ const mapped = transition.mapping.get(newRef[1])
217
+ if (mapped) newRef[1] = mapped.ref[0]
218
+ } else if (newRef[0] === tableName) {
219
+ newRef[0] = transition.target.name
220
+ const mapped = transition.mapping.get(newRef[1])
221
+ if (mapped) newRef[1] = mapped.ref[0]
222
+ } else {
223
+ const mapped = transition.mapping.get(newRef[0])
224
+ if (isSubSelect && mapped) {
225
+ newRef.unshift(transition.target.name)
226
+ newRef[1] = mapped.ref[0]
227
+ } else {
228
+ if (mapped) newRef[0] = mapped.ref[0]
229
+ }
230
+ }
231
+ newWhereElement.ref = newRef
232
+ }
198
233
 
199
- const _newWhere = (where = [], transition) => {
200
- const newWhere = []
234
+ const _newEntries = (entries = [], transition, service) =>
235
+ entries.map(entry => _newData(entry, transition, false, service))
201
236
 
202
- where.forEach(whereElement => {
203
- const mapped = whereElement.ref && transition.mapping.get(whereElement.ref[0])
204
- if (mapped && mapped.ref) {
205
- const newWhereElement = { ...whereElement }
206
- newWhereElement.ref = [...mapped.ref, ...newWhereElement.ref.slice(mapped.ref.length)]
207
- newWhere.push(newWhereElement)
208
- } else if (!mapped) {
209
- newWhere.push(whereElement)
237
+ const _newWhere = (where = [], transition, tableName, alias, isSubselect = false) => {
238
+ const newWhere = where.map(whereElement => {
239
+ const newWhereElement = { ...whereElement }
240
+ if (!whereElement.ref && !whereElement.SELECT) return whereElement
241
+ if (whereElement.SELECT && whereElement.SELECT.where) {
242
+ newWhereElement.SELECT.where = _newWhere(whereElement.SELECT.where, transition, tableName, alias, true)
243
+ return newWhereElement
244
+ } else {
245
+ if (newWhereElement.ref) {
246
+ _newWhereRef(newWhereElement, transition, alias, tableName, isSubselect)
247
+ return newWhereElement
248
+ } else {
249
+ return whereElement
250
+ }
210
251
  }
211
252
  })
212
253
 
@@ -239,7 +280,7 @@ const _rewriteQueryPath = (path, transitions) => {
239
280
  if (f.id) {
240
281
  return {
241
282
  id: target.name,
242
- where: _newWhere(f.where, transitions[0])
283
+ where: _newWhere(f.where, transitions[0], f.id)
243
284
  }
244
285
  }
245
286
  } else {
@@ -252,14 +293,14 @@ const _rewriteQueryPath = (path, transitions) => {
252
293
  const transitionMapping = transitions[i - 1].mapping.get(f.id)
253
294
  return {
254
295
  id: (transitionMapping && transitionMapping.ref && transitionMapping.ref[0]) || f.id,
255
- where: _newWhere(f.where, transitions[i])
296
+ where: _newWhere(f.where, transitions[i], f.id)
256
297
  }
257
298
  }
258
299
  }
259
300
  })
260
301
  }
261
302
 
262
- const _newUpdate = (query, transitions) => {
303
+ const _newUpdate = (query, transitions, service) => {
263
304
  const targetTransition = transitions[transitions.length - 1]
264
305
  const targetName = targetTransition.target.name
265
306
  const newUpdate = { ...query.UPDATE }
@@ -269,9 +310,16 @@ const _newUpdate = (query, transitions) => {
269
310
  ref: _rewriteQueryPath(query.UPDATE.entity, transitions)
270
311
  }
271
312
  : targetName
272
- if (newUpdate.data) newUpdate.data = _newData(newUpdate.data, targetTransition)
273
- if (newUpdate.with) newUpdate.with = _newData(newUpdate.with, targetTransition)
274
- if (newUpdate.where) newUpdate.where = _newWhere(newUpdate.where, targetTransition)
313
+ if (newUpdate.data) newUpdate.data = _newData(newUpdate.data, targetTransition, false, service)
314
+ if (newUpdate.with) newUpdate.with = _newData(newUpdate.with, targetTransition, false, service)
315
+ if (newUpdate.where) {
316
+ newUpdate.where = _newWhere(
317
+ newUpdate.where,
318
+ targetTransition,
319
+ getEntityNameFromUpdateCQN(query),
320
+ query.UPDATE.entity.as
321
+ )
322
+ }
275
323
  Object.defineProperty(newUpdate, '_transitions', {
276
324
  enumerable: false,
277
325
  value: transitions
@@ -279,7 +327,7 @@ const _newUpdate = (query, transitions) => {
279
327
  return newUpdate
280
328
  }
281
329
 
282
- const _newSelect = (query, transitions, force) => {
330
+ const _newSelect = (query, transitions, service) => {
283
331
  const targetTransition = transitions[transitions.length - 1]
284
332
  const newSelect = { ...query.SELECT }
285
333
  newSelect.from = {
@@ -287,11 +335,19 @@ const _newSelect = (query, transitions, force) => {
287
335
  ref: _rewriteQueryPath(query.SELECT.from, transitions)
288
336
  }
289
337
  if (!newSelect.columns && targetTransition.mapping.size) newSelect.columns = _initialColumns(targetTransition)
290
- if (newSelect.columns) newSelect.columns = _newColumns(newSelect.columns, targetTransition, force, true)
338
+ if (newSelect.columns)
339
+ newSelect.columns = _newColumns(newSelect.columns, targetTransition, service, service.kind !== 'app-service')
291
340
  if (newSelect.having) newSelect.having = _newColumns(newSelect.having, targetTransition)
292
341
  if (newSelect.groupBy) newSelect.groupBy = _newColumns(newSelect.groupBy, targetTransition)
293
342
  if (newSelect.orderBy) newSelect.orderBy = _newColumns(newSelect.orderBy, targetTransition)
294
- if (newSelect.where) newSelect.where = _newWhere(newSelect.where, targetTransition)
343
+ if (newSelect.where) {
344
+ newSelect.where = _newWhere(
345
+ newSelect.where,
346
+ targetTransition,
347
+ query.SELECT.from && query.SELECT.from.ref[0],
348
+ query.SELECT.from && query.SELECT.from.as
349
+ )
350
+ }
295
351
  Object.defineProperty(newSelect, '_transitions', {
296
352
  enumerable: false,
297
353
  value: transitions
@@ -299,7 +355,7 @@ const _newSelect = (query, transitions, force) => {
299
355
  return newSelect
300
356
  }
301
357
 
302
- const _newInsert = (query, transitions) => {
358
+ const _newInsert = (query, transitions, service) => {
303
359
  const targetTransition = transitions[transitions.length - 1]
304
360
  const targetName = targetTransition.target.name
305
361
  const newInsert = { ...query.INSERT }
@@ -310,7 +366,7 @@ const _newInsert = (query, transitions) => {
310
366
  }
311
367
  : targetName
312
368
  if (newInsert.columns) newInsert.columns = _newInsertColumns(newInsert.columns, targetTransition)
313
- if (newInsert.entries) newInsert.entries = _newEntries(newInsert.entries, targetTransition)
369
+ if (newInsert.entries) newInsert.entries = _newEntries(newInsert.entries, targetTransition, service)
314
370
  Object.defineProperty(newInsert, '_transitions', {
315
371
  enumerable: false,
316
372
  value: transitions
@@ -328,7 +384,14 @@ const _newDelete = (query, transitions) => {
328
384
  ref: _rewriteQueryPath(query.DELETE.from, transitions)
329
385
  }
330
386
  : targetName
331
- if (newDelete.where) newDelete.where = _newWhere(newDelete.where, targetTransition)
387
+ if (newDelete.where) {
388
+ newDelete.where = _newWhere(
389
+ newDelete.where,
390
+ targetTransition,
391
+ getEntityNameFromDeleteCQN(query),
392
+ query.DELETE.from.as
393
+ )
394
+ }
332
395
  Object.defineProperty(newDelete, '_transitions', {
333
396
  enumerable: false,
334
397
  value: transitions
@@ -336,73 +399,41 @@ const _newDelete = (query, transitions) => {
336
399
  return newDelete
337
400
  }
338
401
 
339
- const _isPersistenceTable = target => {
340
- const persistenceTableTerm = '@cds.persistence.table'
341
- const hasOwnProperty = Object.prototype.hasOwnProperty
342
- const isPersistenceTable = hasOwnProperty.call(target, persistenceTableTerm) && target[persistenceTableTerm]
343
- return isPersistenceTable
344
- }
345
-
346
- const _queryColumns = (target, force = false) => {
347
- const columns = (target && target.query && target.query.SELECT && target.query.SELECT.columns) || []
348
- if (force) return columns
349
-
350
- // If the entity is annotated with the annotation `@cds.persistence.table`
351
- // and elements aliases exist, the aliases must be used as column references.
352
- // The reason is that in this scenario, the cds compiler generate a table
353
- // instead of a view.
354
- if (_isPersistenceTable(target)) {
355
- return columns.map(column => {
356
- return { ref: [column.as || column.ref[0]] }
357
- })
358
- }
402
+ const _isPersistenceTable = target =>
403
+ Object.prototype.hasOwnProperty.call(target, PERSISTENCE_TABLE) && target[PERSISTENCE_TABLE]
359
404
 
360
- return columns
361
- }
405
+ const _findRenamed = (cqnColumns, column) =>
406
+ cqnColumns.find(
407
+ cqnColumn =>
408
+ cqnColumn.as &&
409
+ ((column.ref && column.ref[column.ref.length - 1] === cqnColumn.as) ||
410
+ (column.as === cqnColumn.as && Object.prototype.hasOwnProperty.call(cqnColumn, 'val')))
411
+ )
362
412
 
363
- // Find aliased column from the projection and set it as ref
364
- const _renameColumns = (target, columns, force = false) => {
365
- const queryColumns = _queryColumns(target, force)
366
-
367
- const renamedColumns = columns.map(column => {
368
- const queryColumn =
369
- column.ref &&
370
- queryColumns.find(
371
- queryColumn =>
372
- ((queryColumn.ref && queryColumn.as && queryColumn.ref[queryColumn.ref.length - 1] !== queryColumn.as) ||
373
- queryColumn.val) &&
374
- column.ref[0] === queryColumn.as
375
- )
376
-
377
- if (queryColumn) {
378
- const renamedColumn = { as: column.ref[0] }
379
- if (column.as) renamedColumn.as = column.as
380
- if (queryColumn.ref) {
381
- const aliasOfTargetEntity = target.query.SELECT && target.query.SELECT.from && target.query.SELECT.from.as
382
- renamedColumn.ref =
383
- aliasOfTargetEntity && queryColumn.ref[0] === aliasOfTargetEntity ? queryColumn.ref.slice(1) : queryColumn.ref
384
- }
385
- if (queryColumn.val) renamedColumn.val = queryColumn.val
386
- return renamedColumn
413
+ const _queryColumns = (target, columns = [], persistenceTable = false, force = false) => {
414
+ if (!(target && target.query && target.query.SELECT)) return columns
415
+ const cqnColumns = target.query.SELECT.columns || []
416
+ const from = target.query.SELECT.from
417
+ const isTargetAliased = from.as && !target.query._target.query && cqnColumns.some(c => c.ref && c.ref[0] === from.as)
418
+ if (!columns.length) columns = Object.keys(target.elements).map(e => ({ ref: [e], as: e }))
419
+ return columns.reduce((res, column) => {
420
+ const renamed = _findRenamed(cqnColumns, column)
421
+ if (renamed) {
422
+ if (renamed.val) return res.concat({ as: renamed.as, val: renamed.val })
423
+ // There could be some `where` clause inside `ref` which we don't support yet
424
+ if (!renamed.ref || renamed.ref.some(e => typeof e !== 'string') || renamed.xpr) return res
425
+ if (isTargetAliased) renamed.ref.shift()
426
+ // If the entity is annotated with the annotation `@cds.persistence.table`
427
+ // and elements aliases exist, the aliases must be used as column references.
428
+ // The reason is that in this scenario, the cds compiler generate a table
429
+ // instead of a view. If forced, skip this.
430
+ column.ref = !force && persistenceTable ? [renamed.as] : [...renamed.ref]
387
431
  }
388
-
389
- return column
390
- })
391
-
392
- return renamedColumns
432
+ res.push(column)
433
+ return _appendForeignKeys(res, target, columns, column)
434
+ }, [])
393
435
  }
394
436
 
395
- const _merge = (aliased, inherited) =>
396
- inherited.reduce(
397
- (res, el1) =>
398
- (!aliased.some(el2 => el2.ref && ((el1.as && el2.ref[0] === el1.as) || (el1.ref && el2.ref[0] === el1.ref[0]))) &&
399
- res.concat(el1)) ||
400
- res,
401
- []
402
- )
403
-
404
- const _starColumns = elements => Object.keys(elements).map(element => ({ ref: [element] }))
405
-
406
437
  const _mappedValue = (col, alias) => {
407
438
  const key = col.as || col.ref[0]
408
439
 
@@ -414,35 +445,33 @@ const _mappedValue = (col, alias) => {
414
445
  return [key, { val: col.val }]
415
446
  }
416
447
 
417
- // Don't check for table in case of external services (-> force)
418
- // Reason for hasOwnProperty: All projections have target in their prototype
419
- // Revisit: Check if this is really intended
420
- const _isProjection = (target, force) => target.query && target.query._target && (force || !_isPersistenceTable(target))
421
-
422
- const getDBTable = (target, force) => {
423
- if (_isProjection(target, force)) {
424
- return getDBTable(target.query._target, force)
448
+ const getDBTable = target => {
449
+ if (target.query && target.query._target && !_isPersistenceTable(target)) {
450
+ return getDBTable(target.query._target)
425
451
  }
426
452
  return target
427
453
  }
428
454
 
429
- const _includeForeignKeys = (columns, target, force) => {
430
- for (const column of _queryColumns(target, force)) {
431
- const columnName = column.ref && column.ref[0]
432
- const el = column.as && target.elements && target.elements[column.as]
433
- if (columnName && el && el.name && el.type === 'cds.Association' && el.keys) {
434
- for (const rootKeys of el.keys) {
435
- // REVISIT: Check if this also works for deeply nested ones
436
- // .as and .ref has a different meaning here
437
- // .as means the original property name, if the foreign key is renamed
438
- const rootKeyName = rootKeys.as || (rootKeys.ref && rootKeys.ref[0])
439
- columns.push({
440
- ref: [`${columnName}_${rootKeyName}`],
441
- as: `${el.name}_${rootKeyName}`
455
+ const _appendForeignKeys = (newColumns, target, columns, { as, ref = [] }) => {
456
+ const el = target.elements[as] || target.query._target.elements[ref[ref.length - 1]]
457
+ if (el && el.isAssociation && el.keys) {
458
+ for (const key of el.keys) {
459
+ // .as and .ref has a different meaning here
460
+ // .as means the original property name, if the foreign key is renamed
461
+ const keyName = key.as || key.ref[0]
462
+ const keyAlias = key.ref[0]
463
+ const found = columns.find(col => col.as === `${as}_${keyAlias}`)
464
+ if (found) {
465
+ found.ref = [`${ref.join('_')}_${keyName}`]
466
+ } else {
467
+ newColumns.push({
468
+ ref: [`${ref.join('_')}_${keyName}`],
469
+ as: `${as}_${keyAlias}`
442
470
  })
443
471
  }
444
472
  }
445
473
  }
474
+ return newColumns
446
475
  }
447
476
 
448
477
  const _checkForForbiddenViews = queryTarget => {
@@ -463,43 +492,24 @@ const _checkForForbiddenViews = queryTarget => {
463
492
  }
464
493
  }
465
494
 
466
- const _filterColumnsOnce = (target, columns = [], force) => {
467
- if (columns.length > 0) return columns
468
-
469
- const cqnColumns = _queryColumns(target, force)
470
-
471
- const plainColumns = cqnColumns
472
- // There could be some `where` clause inside `ref` which we don't support yet
473
- .filter(c => c !== '*' && !(c.ref && c.ref.some(ele => typeof ele !== 'string')) && !c.xpr)
474
- .map(c => ({ ref: (c.as && [c.as]) || c.ref }))
475
- _includeForeignKeys(columns, target, force)
476
-
477
- const _target = target.query && target.query._target
478
- if (_target && cqnColumns.some(c => c === '*')) {
479
- // 'alias' is required to properly perform mapping
480
- const starColumns = _starColumns(_target.elements).map(c => (c.as && c) || { as: c.ref[0], ref: c.ref })
481
- columns.push(..._merge(plainColumns, starColumns))
482
- }
483
-
484
- columns.push(...plainColumns)
485
-
486
- return columns
487
- }
488
-
489
- const _getTransitionData = (target, columns, force, skipForbiddenViewCheck) => {
495
+ const _getTransitionData = (target, columns, service, skipForbiddenViewCheck) => {
490
496
  // REVISIT: Find less param polluting way to skip forbidden view check for reads
491
497
  if (!skipForbiddenViewCheck) _checkForForbiddenViews(target)
492
-
493
- if (!columns.length) {
494
- columns = _filterColumnsOnce(target, columns, force)
498
+ const targetStartsWithSrvName = service.namespace && target.name.startsWith(`${service.namespace}.`)
499
+ const persistenceTable = _isPersistenceTable(target)
500
+ columns = _queryColumns(target, columns, persistenceTable, service.name !== 'db' && !targetStartsWithSrvName)
501
+ if (persistenceTable && service.name === 'db') {
502
+ return { target, transitionColumns: columns }
495
503
  }
496
-
497
- if (_isProjection(target, force)) {
498
- columns = _renameColumns(target, columns, force)
504
+ // stop projection resolving if it starts with the service name prefix or if there is nothing to resolve anymore
505
+ if ((service.name !== 'db' && targetStartsWithSrvName) || !(target.query && target.query._target)) {
506
+ return { target, transitionColumns: columns }
507
+ }
508
+ // continue projection resolving if the target is a projection
509
+ if (target.query && target.query._target) {
499
510
  const newTarget = target.query._target
500
- return _getTransitionData(newTarget, columns, force, skipForbiddenViewCheck)
511
+ return _getTransitionData(newTarget, columns, service, skipForbiddenViewCheck)
501
512
  }
502
-
503
513
  return { target, transitionColumns: columns }
504
514
  }
505
515
 
@@ -507,16 +517,16 @@ const _getTransitionData = (target, columns, force, skipForbiddenViewCheck) => {
507
517
  * If no entity definition is found, no transition is done.
508
518
  *
509
519
  * @param queryTarget
510
- * @param force
520
+ * @param service
511
521
  * @param skipForbiddenViewCheck
512
522
  */
513
- const getTransition = (queryTarget, force, skipForbiddenViewCheck) => {
523
+ const getTransition = (queryTarget, service, skipForbiddenViewCheck) => {
514
524
  // Never resolve unknown targets (e.g. for drafts)
515
- if (!queryTarget || (_isPersistenceTable(queryTarget) && !force)) {
525
+ if (!queryTarget) {
516
526
  return { target: queryTarget, queryTarget, mapping: new Map() }
517
527
  }
518
528
 
519
- const { target: _target, transitionColumns } = _getTransitionData(queryTarget, [], force, skipForbiddenViewCheck)
529
+ const { target: _target, transitionColumns } = _getTransitionData(queryTarget, [], service, skipForbiddenViewCheck)
520
530
  const query = queryTarget.query
521
531
  const alias = query && query.SELECT && query.SELECT.from && query.SELECT.from.as
522
532
  const mappedColumns = transitionColumns.map(column => _mappedValue(column, alias))
@@ -524,11 +534,11 @@ const getTransition = (queryTarget, force, skipForbiddenViewCheck) => {
524
534
  return { target: _target, queryTarget, mapping }
525
535
  }
526
536
 
527
- const _entityTransitionsForTarget = (from, model, force) => {
537
+ const _entityTransitionsForTarget = (from, model, service) => {
528
538
  let previousEntity
529
539
 
530
540
  if (typeof from === 'string') {
531
- return model.definitions[from] && [getTransition(model.definitions[from], force)]
541
+ return model.definitions[from] && [getTransition(model.definitions[from], service)]
532
542
  }
533
543
 
534
544
  return from.ref.map((f, i) => {
@@ -538,22 +548,30 @@ const _entityTransitionsForTarget = (from, model, force) => {
538
548
  const entity = model.definitions[element]
539
549
  if (entity) {
540
550
  previousEntity = entity
541
- return getTransition(entity, force)
551
+ return getTransition(entity, service)
542
552
  }
543
553
  }
544
554
 
545
555
  if (previousEntity) {
546
556
  const entity = previousEntity.elements[element] && previousEntity.elements[element]._target
547
-
548
557
  if (entity) {
558
+ // > assoc
549
559
  previousEntity = entity
550
- return getTransition(entity, force)
560
+ return getTransition(entity, service)
561
+ } else {
562
+ // > struct
563
+ previousEntity = previousEntity.elements[element]
564
+ return {
565
+ target: previousEntity,
566
+ queryTarget: previousEntity,
567
+ mapping: new Map()
568
+ }
551
569
  }
552
570
  }
553
571
  })
554
572
  }
555
573
 
556
- const _newQuery = (query, event, model, force) => {
574
+ const _newQuery = (query, event, model, service) => {
557
575
  const [_prop, _func] = {
558
576
  SELECT: ['from', _newSelect],
559
577
  INSERT: ['into', _newInsert],
@@ -561,25 +579,25 @@ const _newQuery = (query, event, model, force) => {
561
579
  DELETE: ['from', _newDelete]
562
580
  }[event]
563
581
  const newQuery = Object.create(query)
564
- const transitions = _entityTransitionsForTarget(query[event][_prop], model, force)
565
- newQuery[event] = (transitions[0] && _func(newQuery, transitions, force)) || { ...query[event] }
582
+ const transitions = _entityTransitionsForTarget(query[event][_prop], model, service)
583
+ newQuery[event] = (transitions[0] && _func(newQuery, transitions, service)) || { ...query[event] }
566
584
  return newQuery
567
585
  }
568
586
 
569
- const resolveView = (query, model, component, force) => {
587
+ const resolveView = (query, model, service) => {
570
588
  // swap logger
571
589
  const _LOG = LOG
572
- LOG = cds.log(component)
590
+ LOG = cds.log(service.kind) // REVISIT: Avoid obtaining loggers per request!
573
591
 
574
592
  // If the query is a projection, one must follow it
575
593
  // to let the underlying service know its true entity.
594
+ if (query.cmd) _event = query.cmd
595
+ else if (query.SELECT) _event = 'SELECT'
596
+ else if (query.INSERT) _event = 'INSERT'
597
+ else if (query.UPDATE) _event = 'UPDATE'
598
+ else if (query.DELETE) _event = 'DELETE'
576
599
 
577
- if (query.SELECT) _event = 'SELECT'
578
- if (query.INSERT) _event = 'INSERT'
579
- if (query.UPDATE) _event = 'UPDATE'
580
- if (query.DELETE) _event = 'DELETE'
581
-
582
- const newQuery = _newQuery(query, _event, model, force)
600
+ const newQuery = _newQuery(query, _event, model, service)
583
601
 
584
602
  // restore logger and clear _event
585
603
  LOG = _LOG
@@ -588,9 +606,43 @@ const resolveView = (query, model, component, force) => {
588
606
  return newQuery
589
607
  }
590
608
 
609
+ /**
610
+ * Restores the link of req.data and req.query in case req.query was overwritten.
611
+ * Only applicable for UPDATEs and INSERTs.
612
+ *
613
+ * @param {*} req
614
+ */
615
+ const restoreLink = req => {
616
+ if (req.query.INSERT && req.query.INSERT.entries) {
617
+ if (Array.isArray(req.query.INSERT.entries)) req.data = req.query.INSERT.entries[0]
618
+ else req.data = req.query.INSERT.entries
619
+ } else if (req.query.UPDATE && req.query.UPDATE.data) {
620
+ req.data = req.query.UPDATE.data
621
+ }
622
+ }
623
+
624
+ /**
625
+ * Retrieves the actual query target by evaluating the created transitions.
626
+ * @param {*} q - the resolved query
627
+ * @returns {*} csn entity or undefined
628
+ */
629
+ const findQueryTarget = q => {
630
+ return q.SELECT
631
+ ? q.SELECT._transitions[q.SELECT._transitions.length - 1].target
632
+ : q.INSERT
633
+ ? q.INSERT._transitions[q.INSERT._transitions.length - 1].target
634
+ : q.UPDATE
635
+ ? q.UPDATE._transitions[q.UPDATE._transitions.length - 1].target
636
+ : q.DELETE
637
+ ? q.DELETE._transitions[q.DELETE._transitions.length - 1].target
638
+ : undefined
639
+ }
640
+
591
641
  module.exports = {
642
+ findQueryTarget,
592
643
  getDBTable,
593
644
  resolveView,
594
645
  getTransition,
646
+ restoreLink,
595
647
  revertData
596
648
  }