@sap/cds 5.4.3 → 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 +239 -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 +66 -63
  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 +12 -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 +53 -31
  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 +123 -108
  127. package/libx/_runtime/common/utils/csn.js +56 -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 +227 -173
  138. package/libx/_runtime/common/utils/rewriteAsterisk.js +46 -26
  139. package/libx/_runtime/common/utils/structured.js +13 -13
  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 +28 -72
  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 +21 -8
  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 +261 -205
  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 +3 -3
  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 +33 -27
  205. package/libx/_runtime/sqlite/localized.js +12 -7
  206. package/libx/_runtime/types/api.js +10 -0
  207. package/package.json +2 -2
  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)
@@ -30,7 +32,8 @@ const _inverseTransition = transition => {
30
32
 
31
33
  const ref0 = value.ref[0]
32
34
  if (value.ref.length > 1) {
33
- const nested = inverseTransition.mapping.get(ref0) || { transition: { mapping: new Map() } }
35
+ const nested = inverseTransition.mapping.get(ref0) || {}
36
+ if (!nested.transition) nested.transition = { mapping: new Map() }
34
37
  let current = nested.transition.mapping
35
38
  for (let i = 1; i < value.ref.length; i++) {
36
39
  const last = i === value.ref.length - 1
@@ -49,15 +52,15 @@ const _inverseTransition = transition => {
49
52
  return inverseTransition
50
53
  }
51
54
 
52
- const revertData = (data, transition) => {
55
+ const revertData = (data, transition, service) => {
53
56
  if (!transition || !transition.mapping.size) return data
54
57
  const inverseTransition = _inverseTransition(transition)
55
58
  return Array.isArray(data)
56
- ? data.map(entry => _newData(entry, inverseTransition, true))
57
- : _newData(data, inverseTransition, true)
59
+ ? data.map(entry => _newData(entry, inverseTransition, true, service))
60
+ : _newData(data, inverseTransition, true, service)
58
61
  }
59
62
 
60
- const _newSubData = (newData, key, transition, el, inverse) => {
63
+ const _newSubData = (newData, key, transition, el, inverse, service) => {
61
64
  const val = newData[key]
62
65
  if ((!Array.isArray(val) && typeof val === 'object') || (Array.isArray(val) && val.length !== 0)) {
63
66
  let mapped = transition.mapping.get(key)
@@ -66,13 +69,13 @@ const _newSubData = (newData, key, transition, el, inverse) => {
66
69
  transition.mapping.set(key, mapped)
67
70
  }
68
71
  if (!mapped.transition) {
69
- const subTransition = getTransition(el._target)
72
+ const subTransition = getTransition(el._target, service)
70
73
  mapped.transition = inverse ? _inverseTransition(subTransition) : subTransition
71
74
  }
72
75
  if (Array.isArray(val)) {
73
- newData[key] = val.map(singleVal => _newData(singleVal, mapped.transition, inverse))
76
+ newData[key] = val.map(singleVal => _newData(singleVal, mapped.transition, inverse, service))
74
77
  } else {
75
- newData[key] = _newData(val, mapped.transition, inverse)
78
+ newData[key] = _newData(val, mapped.transition, inverse, service)
76
79
  }
77
80
  }
78
81
  }
@@ -94,7 +97,7 @@ const _newNestedData = (queryTarget, newData, ref, value) => {
94
97
  }
95
98
  }
96
99
 
97
- const _newData = (data, transition, inverse) => {
100
+ const _newData = (data, transition, inverse, service) => {
98
101
  // no transition -> nothing to do
99
102
  if (transition.target && transition.target.name === transition.queryTarget.name) return data
100
103
 
@@ -108,12 +111,17 @@ const _newData = (data, transition, inverse) => {
108
111
  for (const key in newData) {
109
112
  const el = queryTarget && queryTarget.elements && queryTarget.elements[key]
110
113
  const isAssoc = el && el.isAssociation
111
- 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
+ }
112
119
 
113
120
  const mapped = transition.mapping.get(key)
114
121
  if (!mapped) {
115
122
  // if there is no mapping and no element with the same name in the target, then we don't need the data
116
- if (typeof newData[key] !== 'object' && !transition.target.elements[key]) delete newData[key]
123
+ if ((typeof newData[key] !== 'object' || newData[key] === null) && !transition.target.elements[key])
124
+ delete newData[key]
117
125
  continue
118
126
  }
119
127
 
@@ -140,7 +148,7 @@ const _newData = (data, transition, inverse) => {
140
148
  return newData
141
149
  }
142
150
 
143
- const _newColumns = (columns = [], transition, force = false, withAlias = false) => {
151
+ const _newColumns = (columns = [], transition, service, withAlias = false) => {
144
152
  const newColumns = []
145
153
 
146
154
  columns.forEach(column => {
@@ -165,11 +173,21 @@ const _newColumns = (columns = [], transition, force = false, withAlias = false)
165
173
 
166
174
  // ensure that renaming of a redirected assoc are also respected
167
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
+
168
186
  // reuse _newColumns with new transition
169
- const expandTarget = transition.queryTarget.elements[column.ref[column.ref.length - 1]]._target
170
- const subtransition = getTransition(expandTarget, force)
187
+ const expandTarget = def._target
188
+ const subtransition = getTransition(expandTarget, service)
171
189
 
172
- newColumn.expand = _newColumns(column.expand, subtransition, force, withAlias)
190
+ newColumn.expand = _newColumns(column.expand, subtransition, service, withAlias)
173
191
  }
174
192
  newColumns.push(newColumn)
175
193
  })
@@ -192,19 +210,44 @@ const _newInsertColumns = (columns = [], transition) => {
192
210
  return newColumns
193
211
  }
194
212
 
195
- 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
+ }
196
233
 
197
- const _newWhere = (where = [], transition) => {
198
- const newWhere = []
234
+ const _newEntries = (entries = [], transition, service) =>
235
+ entries.map(entry => _newData(entry, transition, false, service))
199
236
 
200
- where.forEach(whereElement => {
201
- const mapped = whereElement.ref && transition.mapping.get(whereElement.ref[0])
202
- if (mapped && mapped.ref) {
203
- const newWhereElement = { ...whereElement }
204
- newWhereElement.ref = [...mapped.ref, ...newWhereElement.ref.slice(mapped.ref.length)]
205
- newWhere.push(newWhereElement)
206
- } else if (!mapped) {
207
- 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
+ }
208
251
  }
209
252
  })
210
253
 
@@ -237,7 +280,7 @@ const _rewriteQueryPath = (path, transitions) => {
237
280
  if (f.id) {
238
281
  return {
239
282
  id: target.name,
240
- where: _newWhere(f.where, transitions[0])
283
+ where: _newWhere(f.where, transitions[0], f.id)
241
284
  }
242
285
  }
243
286
  } else {
@@ -250,14 +293,14 @@ const _rewriteQueryPath = (path, transitions) => {
250
293
  const transitionMapping = transitions[i - 1].mapping.get(f.id)
251
294
  return {
252
295
  id: (transitionMapping && transitionMapping.ref && transitionMapping.ref[0]) || f.id,
253
- where: _newWhere(f.where, transitions[i])
296
+ where: _newWhere(f.where, transitions[i], f.id)
254
297
  }
255
298
  }
256
299
  }
257
300
  })
258
301
  }
259
302
 
260
- const _newUpdate = (query, transitions) => {
303
+ const _newUpdate = (query, transitions, service) => {
261
304
  const targetTransition = transitions[transitions.length - 1]
262
305
  const targetName = targetTransition.target.name
263
306
  const newUpdate = { ...query.UPDATE }
@@ -267,9 +310,16 @@ const _newUpdate = (query, transitions) => {
267
310
  ref: _rewriteQueryPath(query.UPDATE.entity, transitions)
268
311
  }
269
312
  : targetName
270
- if (newUpdate.data) newUpdate.data = _newData(newUpdate.data, targetTransition)
271
- if (newUpdate.with) newUpdate.with = _newData(newUpdate.with, targetTransition)
272
- 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
+ }
273
323
  Object.defineProperty(newUpdate, '_transitions', {
274
324
  enumerable: false,
275
325
  value: transitions
@@ -277,7 +327,7 @@ const _newUpdate = (query, transitions) => {
277
327
  return newUpdate
278
328
  }
279
329
 
280
- const _newSelect = (query, transitions, force) => {
330
+ const _newSelect = (query, transitions, service) => {
281
331
  const targetTransition = transitions[transitions.length - 1]
282
332
  const newSelect = { ...query.SELECT }
283
333
  newSelect.from = {
@@ -285,11 +335,19 @@ const _newSelect = (query, transitions, force) => {
285
335
  ref: _rewriteQueryPath(query.SELECT.from, transitions)
286
336
  }
287
337
  if (!newSelect.columns && targetTransition.mapping.size) newSelect.columns = _initialColumns(targetTransition)
288
- 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')
289
340
  if (newSelect.having) newSelect.having = _newColumns(newSelect.having, targetTransition)
290
341
  if (newSelect.groupBy) newSelect.groupBy = _newColumns(newSelect.groupBy, targetTransition)
291
342
  if (newSelect.orderBy) newSelect.orderBy = _newColumns(newSelect.orderBy, targetTransition)
292
- 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
+ }
293
351
  Object.defineProperty(newSelect, '_transitions', {
294
352
  enumerable: false,
295
353
  value: transitions
@@ -297,7 +355,7 @@ const _newSelect = (query, transitions, force) => {
297
355
  return newSelect
298
356
  }
299
357
 
300
- const _newInsert = (query, transitions) => {
358
+ const _newInsert = (query, transitions, service) => {
301
359
  const targetTransition = transitions[transitions.length - 1]
302
360
  const targetName = targetTransition.target.name
303
361
  const newInsert = { ...query.INSERT }
@@ -308,7 +366,7 @@ const _newInsert = (query, transitions) => {
308
366
  }
309
367
  : targetName
310
368
  if (newInsert.columns) newInsert.columns = _newInsertColumns(newInsert.columns, targetTransition)
311
- if (newInsert.entries) newInsert.entries = _newEntries(newInsert.entries, targetTransition)
369
+ if (newInsert.entries) newInsert.entries = _newEntries(newInsert.entries, targetTransition, service)
312
370
  Object.defineProperty(newInsert, '_transitions', {
313
371
  enumerable: false,
314
372
  value: transitions
@@ -326,7 +384,14 @@ const _newDelete = (query, transitions) => {
326
384
  ref: _rewriteQueryPath(query.DELETE.from, transitions)
327
385
  }
328
386
  : targetName
329
- 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
+ }
330
395
  Object.defineProperty(newDelete, '_transitions', {
331
396
  enumerable: false,
332
397
  value: transitions
@@ -334,73 +399,41 @@ const _newDelete = (query, transitions) => {
334
399
  return newDelete
335
400
  }
336
401
 
337
- const _isPersistenceTable = target => {
338
- const persistenceTableTerm = '@cds.persistence.table'
339
- const hasOwnProperty = Object.prototype.hasOwnProperty
340
- const isPersistenceTable = hasOwnProperty.call(target, persistenceTableTerm) && target[persistenceTableTerm]
341
- return isPersistenceTable
342
- }
343
-
344
- const _queryColumns = (target, force = false) => {
345
- const columns = (target && target.query && target.query.SELECT && target.query.SELECT.columns) || []
346
- if (force) return columns
347
-
348
- // If the entity is annotated with the annotation `@cds.persistence.table`
349
- // and elements aliases exist, the aliases must be used as column references.
350
- // The reason is that in this scenario, the cds compiler generate a table
351
- // instead of a view.
352
- if (_isPersistenceTable(target)) {
353
- return columns.map(column => {
354
- return { ref: [column.as || column.ref[0]] }
355
- })
356
- }
402
+ const _isPersistenceTable = target =>
403
+ Object.prototype.hasOwnProperty.call(target, PERSISTENCE_TABLE) && target[PERSISTENCE_TABLE]
357
404
 
358
- return columns
359
- }
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
+ )
360
412
 
361
- // Find aliased column from the projection and set it as ref
362
- const _renameColumns = (target, columns, force = false) => {
363
- const queryColumns = _queryColumns(target, force)
364
-
365
- const renamedColumns = columns.map(column => {
366
- const queryColumn =
367
- column.ref &&
368
- queryColumns.find(
369
- queryColumn =>
370
- ((queryColumn.ref && queryColumn.as && queryColumn.ref[queryColumn.ref.length - 1] !== queryColumn.as) ||
371
- queryColumn.val) &&
372
- column.ref[0] === queryColumn.as
373
- )
374
-
375
- if (queryColumn) {
376
- const renamedColumn = { as: column.ref[0] }
377
- if (column.as) renamedColumn.as = column.as
378
- if (queryColumn.ref) {
379
- const aliasOfTargetEntity = target.query.SELECT && target.query.SELECT.from && target.query.SELECT.from.as
380
- renamedColumn.ref =
381
- aliasOfTargetEntity && queryColumn.ref[0] === aliasOfTargetEntity ? queryColumn.ref.slice(1) : queryColumn.ref
382
- }
383
- if (queryColumn.val) renamedColumn.val = queryColumn.val
384
- 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]
385
431
  }
386
-
387
- return column
388
- })
389
-
390
- return renamedColumns
432
+ res.push(column)
433
+ return _appendForeignKeys(res, target, columns, column)
434
+ }, [])
391
435
  }
392
436
 
393
- const _merge = (aliased, inherited) =>
394
- inherited.reduce(
395
- (res, el1) =>
396
- (!aliased.some(el2 => el2.ref && ((el1.as && el2.ref[0] === el1.as) || (el1.ref && el2.ref[0] === el1.ref[0]))) &&
397
- res.concat(el1)) ||
398
- res,
399
- []
400
- )
401
-
402
- const _starColumns = elements => Object.keys(elements).map(element => ({ ref: [element] }))
403
-
404
437
  const _mappedValue = (col, alias) => {
405
438
  const key = col.as || col.ref[0]
406
439
 
@@ -412,35 +445,33 @@ const _mappedValue = (col, alias) => {
412
445
  return [key, { val: col.val }]
413
446
  }
414
447
 
415
- // Don't check for table in case of external services (-> force)
416
- // Reason for hasOwnProperty: All projections have target in their prototype
417
- // Revisit: Check if this is really intended
418
- const _isProjection = (target, force) => target.query && target.query._target && (force || !_isPersistenceTable(target))
419
-
420
- const getDBTable = (target, force) => {
421
- if (_isProjection(target, force)) {
422
- 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)
423
451
  }
424
452
  return target
425
453
  }
426
454
 
427
- const _includeForeignKeys = (columns, target, force) => {
428
- for (const column of _queryColumns(target, force)) {
429
- const columnName = column.ref && column.ref[0]
430
- const el = column.as && target.elements && target.elements[column.as]
431
- if (columnName && el && el.name && el.type === 'cds.Association' && el.keys) {
432
- for (const rootKeys of el.keys) {
433
- // REVISIT: Check if this also works for deeply nested ones
434
- // .as and .ref has a different meaning here
435
- // .as means the original property name, if the foreign key is renamed
436
- const rootKeyName = rootKeys.as || (rootKeys.ref && rootKeys.ref[0])
437
- columns.push({
438
- ref: [`${columnName}_${rootKeyName}`],
439
- 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}`
440
470
  })
441
471
  }
442
472
  }
443
473
  }
474
+ return newColumns
444
475
  }
445
476
 
446
477
  const _checkForForbiddenViews = queryTarget => {
@@ -461,43 +492,24 @@ const _checkForForbiddenViews = queryTarget => {
461
492
  }
462
493
  }
463
494
 
464
- const _filterColumnsOnce = (target, columns = [], force) => {
465
- if (columns.length > 0) return columns
466
-
467
- const cqnColumns = _queryColumns(target, force)
468
-
469
- const plainColumns = cqnColumns
470
- // There could be some `where` clause inside `ref` which we don't support yet
471
- .filter(c => c !== '*' && !(c.ref && c.ref.some(ele => typeof ele !== 'string')) && !c.xpr)
472
- .map(c => ({ ref: (c.as && [c.as]) || c.ref }))
473
- _includeForeignKeys(columns, target, force)
474
-
475
- const _target = target.query && target.query._target
476
- if (_target && cqnColumns.some(c => c === '*')) {
477
- // 'alias' is required to properly perform mapping
478
- const starColumns = _starColumns(_target.elements).map(c => (c.as && c) || { as: c.ref[0], ref: c.ref })
479
- columns.push(..._merge(plainColumns, starColumns))
480
- }
481
-
482
- columns.push(...plainColumns)
483
-
484
- return columns
485
- }
486
-
487
- const _getTransitionData = (target, columns, force, skipForbiddenViewCheck) => {
495
+ const _getTransitionData = (target, columns, service, skipForbiddenViewCheck) => {
488
496
  // REVISIT: Find less param polluting way to skip forbidden view check for reads
489
497
  if (!skipForbiddenViewCheck) _checkForForbiddenViews(target)
490
-
491
- if (!columns.length) {
492
- 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 }
493
503
  }
494
-
495
- if (_isProjection(target, force)) {
496
- 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) {
497
510
  const newTarget = target.query._target
498
- return _getTransitionData(newTarget, columns, force, skipForbiddenViewCheck)
511
+ return _getTransitionData(newTarget, columns, service, skipForbiddenViewCheck)
499
512
  }
500
-
501
513
  return { target, transitionColumns: columns }
502
514
  }
503
515
 
@@ -505,16 +517,16 @@ const _getTransitionData = (target, columns, force, skipForbiddenViewCheck) => {
505
517
  * If no entity definition is found, no transition is done.
506
518
  *
507
519
  * @param queryTarget
508
- * @param force
520
+ * @param service
509
521
  * @param skipForbiddenViewCheck
510
522
  */
511
- const getTransition = (queryTarget, force, skipForbiddenViewCheck) => {
523
+ const getTransition = (queryTarget, service, skipForbiddenViewCheck) => {
512
524
  // Never resolve unknown targets (e.g. for drafts)
513
- if (!queryTarget || (_isPersistenceTable(queryTarget) && !force)) {
525
+ if (!queryTarget) {
514
526
  return { target: queryTarget, queryTarget, mapping: new Map() }
515
527
  }
516
528
 
517
- const { target: _target, transitionColumns } = _getTransitionData(queryTarget, [], force, skipForbiddenViewCheck)
529
+ const { target: _target, transitionColumns } = _getTransitionData(queryTarget, [], service, skipForbiddenViewCheck)
518
530
  const query = queryTarget.query
519
531
  const alias = query && query.SELECT && query.SELECT.from && query.SELECT.from.as
520
532
  const mappedColumns = transitionColumns.map(column => _mappedValue(column, alias))
@@ -522,11 +534,11 @@ const getTransition = (queryTarget, force, skipForbiddenViewCheck) => {
522
534
  return { target: _target, queryTarget, mapping }
523
535
  }
524
536
 
525
- const _entityTransitionsForTarget = (from, model, force) => {
537
+ const _entityTransitionsForTarget = (from, model, service) => {
526
538
  let previousEntity
527
539
 
528
540
  if (typeof from === 'string') {
529
- return model.definitions[from] && [getTransition(model.definitions[from], force)]
541
+ return model.definitions[from] && [getTransition(model.definitions[from], service)]
530
542
  }
531
543
 
532
544
  return from.ref.map((f, i) => {
@@ -536,22 +548,30 @@ const _entityTransitionsForTarget = (from, model, force) => {
536
548
  const entity = model.definitions[element]
537
549
  if (entity) {
538
550
  previousEntity = entity
539
- return getTransition(entity, force)
551
+ return getTransition(entity, service)
540
552
  }
541
553
  }
542
554
 
543
555
  if (previousEntity) {
544
556
  const entity = previousEntity.elements[element] && previousEntity.elements[element]._target
545
-
546
557
  if (entity) {
558
+ // > assoc
547
559
  previousEntity = entity
548
- 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
+ }
549
569
  }
550
570
  }
551
571
  })
552
572
  }
553
573
 
554
- const _newQuery = (query, event, model, force) => {
574
+ const _newQuery = (query, event, model, service) => {
555
575
  const [_prop, _func] = {
556
576
  SELECT: ['from', _newSelect],
557
577
  INSERT: ['into', _newInsert],
@@ -559,25 +579,25 @@ const _newQuery = (query, event, model, force) => {
559
579
  DELETE: ['from', _newDelete]
560
580
  }[event]
561
581
  const newQuery = Object.create(query)
562
- const transitions = _entityTransitionsForTarget(query[event][_prop], model, force)
563
- 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] }
564
584
  return newQuery
565
585
  }
566
586
 
567
- const resolveView = (query, model, component, force) => {
587
+ const resolveView = (query, model, service) => {
568
588
  // swap logger
569
589
  const _LOG = LOG
570
- LOG = cds.log(component)
590
+ LOG = cds.log(service.kind) // REVISIT: Avoid obtaining loggers per request!
571
591
 
572
592
  // If the query is a projection, one must follow it
573
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'
574
599
 
575
- if (query.SELECT) _event = 'SELECT'
576
- if (query.INSERT) _event = 'INSERT'
577
- if (query.UPDATE) _event = 'UPDATE'
578
- if (query.DELETE) _event = 'DELETE'
579
-
580
- const newQuery = _newQuery(query, _event, model, force)
600
+ const newQuery = _newQuery(query, _event, model, service)
581
601
 
582
602
  // restore logger and clear _event
583
603
  LOG = _LOG
@@ -586,9 +606,43 @@ const resolveView = (query, model, component, force) => {
586
606
  return newQuery
587
607
  }
588
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
+
589
641
  module.exports = {
642
+ findQueryTarget,
590
643
  getDBTable,
591
644
  resolveView,
592
645
  getTransition,
646
+ restoreLink,
593
647
  revertData
594
648
  }