@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
@@ -4,6 +4,7 @@ const BaseBuilder = require('./BaseBuilder')
4
4
  const SelectBuilder = require('./SelectBuilder')
5
5
  const getAnnotatedColumns = require('./annotations')
6
6
  const dollar = require('./dollar')
7
+ const { stringifyIfArrayedElement, isArrayedElement } = require('./arrayed')
7
8
 
8
9
  /**
9
10
  * InsertBuilder is used to take a CQN object as an input and to build an object representing an insert operation
@@ -185,13 +186,13 @@ class InsertBuilder extends BaseBuilder {
185
186
  const annotatedInsertColumnValues = annotatedColumns ? this._getAnnotatedInsertColumnValues(annotatedColumns) : []
186
187
 
187
188
  if (this._obj.INSERT.values) {
188
- this._outputObj.values = this._obj.INSERT.values
189
+ this._outputObj.values = this._obj.INSERT.values.map(stringifyIfArrayedElement)
189
190
 
190
191
  placeholderNum = this._outputObj.values.length
191
192
 
192
193
  this._valuesAnnotatedValues(annotatedInsertColumnValues, this._outputObj.values)
193
194
  } else {
194
- this._outputObj.values = this._obj.INSERT.rows
195
+ this._outputObj.values = this._obj.INSERT.rows.map(r => r.map(stringifyIfArrayedElement))
195
196
 
196
197
  placeholderNum = this._outputObj.values[0].length
197
198
 
@@ -261,7 +262,7 @@ class InsertBuilder extends BaseBuilder {
261
262
  for (const column of columns) {
262
263
  const flattenColumn = flattenColumnMap.get(column)
263
264
  const val = this._getValue(column, { entry, flattenColumn, insertAnnotatedColumns })
264
- values.push(val)
265
+ values.push(isArrayedElement(val) ? JSON.stringify(val) : val)
265
266
  }
266
267
 
267
268
  // insert values for insert annotated columns
@@ -370,7 +371,7 @@ class InsertBuilder extends BaseBuilder {
370
371
  _getFlattenEntryColumnsRecursion(res, entry, prefix) {
371
372
  Object.keys(entry).forEach(key => {
372
373
  const prefixKey = prefix ? `${prefix}_${key}` : key
373
- if (this._isSubObject(key, entry)) {
374
+ if (this._isSubObject(key, entry) && !isArrayedElement(entry[key])) {
374
375
  this._getFlattenEntryColumnsRecursion(res, entry[key], prefixKey)
375
376
  } else {
376
377
  res.push(prefixKey)
@@ -384,7 +385,7 @@ class InsertBuilder extends BaseBuilder {
384
385
  entries.forEach(entry => {
385
386
  Object.keys(entry).forEach(key => {
386
387
  const prefixKey = prefix ? `${prefix}_${key}` : key
387
- if (this._isSubObject(key, entry)) {
388
+ if (this._isSubObject(key, entry) && !isArrayedElement(entry[key])) {
388
389
  const resInternal = this._getFlattenColumnMap([entry[key]], { prefix: prefixKey, annotatedColumns })
389
390
 
390
391
  Array.from(resInternal.keys()).forEach(keyInternal => {
@@ -45,6 +45,16 @@ class ReferenceBuilder extends BaseBuilder {
45
45
  this._outputObj.values.push(...values)
46
46
  } else if (this._obj.ref) {
47
47
  // reference
48
+
49
+ // REVISIT: compat for legacy ref: "['foo as bar']" -> remove with cds^6
50
+ if (this._obj.ref.length === 1 && typeof this._obj.ref[0] === 'string') {
51
+ const matched = this._obj.ref[0].match(/(\S*)\s{1,}as\s{1,}(\S*)/i)
52
+ if (matched) {
53
+ this._obj.ref = [matched[1]]
54
+ this._obj.as = matched[2].replace(/^"/, '').replace(/"$/, '')
55
+ }
56
+ }
57
+
48
58
  if (this._obj.param) {
49
59
  this._parseParamReference(this._obj.ref)
50
60
  } else {
@@ -1,3 +1,5 @@
1
+ const cds = require('../../cds')
2
+
1
3
  const BaseBuilder = require('./BaseBuilder')
2
4
  const { DRAFT_COLUMNS } = require('../../common/constants/draft')
3
5
 
@@ -67,6 +69,8 @@ class SelectBuilder extends BaseBuilder {
67
69
  * SQL string for prepared statement and array of values to replace the placeholders.
68
70
  */
69
71
  build(noQuoting = false) {
72
+ if (this._obj.SELECT._4odata) this._options._4odata = true
73
+
70
74
  this._outputObj = {
71
75
  sql: ['SELECT'],
72
76
  values: []
@@ -250,8 +254,7 @@ class SelectBuilder extends BaseBuilder {
250
254
  res.sql = `( ${res.sql} )`
251
255
  } else {
252
256
  // val
253
- res.sql = this._val(col)
254
- res.values = []
257
+ res = this._val(col)
255
258
  }
256
259
 
257
260
  if (col.as) {
@@ -288,7 +291,7 @@ class SelectBuilder extends BaseBuilder {
288
291
  const aliases = columns.map(col => this._getAlias(col)).filter(element => element !== undefined)
289
292
  const findDuplicates = aliases.filter((item, index) => aliases.indexOf(item) !== index)
290
293
 
291
- if (findDuplicates.length !== 0) {
294
+ if (findDuplicates.length > 0) {
292
295
  throw new Error(
293
296
  `Duplicate column names ${JSON.stringify(findDuplicates)} detected in SELECT statement. Please use aliases.`
294
297
  )
@@ -296,11 +299,8 @@ class SelectBuilder extends BaseBuilder {
296
299
  }
297
300
 
298
301
  _getAlias(col) {
299
- if (col.as) {
300
- return col.as
301
- } else if (col.ref) {
302
- return col.ref[col.ref.length - 1]
303
- }
302
+ if (col.as) return col.as
303
+ if (col.ref) return col.ref[col.ref.length - 1]
304
304
  }
305
305
 
306
306
  _columns(noQuoting) {
@@ -368,16 +368,29 @@ class SelectBuilder extends BaseBuilder {
368
368
  }
369
369
 
370
370
  _having() {
371
- const having = new this.ExpressionBuilder(this._obj.SELECT.having, this._options, this._csn).build()
372
- this._outputObj.sql.push('HAVING', having.sql)
373
- this._outputObj.values.push(...having.values)
371
+ const { sql, values } = new this.ExpressionBuilder(this._obj.SELECT.having, this._options, this._csn).build()
372
+ this._outputObj.sql.push('HAVING', sql)
373
+ this._outputObj.values.push(...values)
374
374
  }
375
375
 
376
376
  _orderBy() {
377
377
  const sqls = []
378
378
  this._outputObj.sql.push('ORDER BY')
379
379
  for (const element of this._obj.SELECT.orderBy) {
380
- this._quoteAliases(element)
380
+ // REVISIT: should not be necessary
381
+ // if columns doesn't have matching ref but matching as, then use that as
382
+ const { columns } = this._obj.SELECT
383
+ if (columns) {
384
+ const serialized = JSON.stringify(element.ref)
385
+ if (!columns.find(c => JSON.stringify(c.ref) === serialized)) {
386
+ const toMatch = element.as || (element.ref && element.ref.length === 1 && element.ref[0])
387
+ if (toMatch && columns.find(c => c.as === toMatch)) {
388
+ sqls.push(this._quote(toMatch) + ' ' + (element.sort || 'asc').toUpperCase())
389
+ continue
390
+ }
391
+ }
392
+ }
393
+
381
394
  const { sql, values } = new this.ReferenceBuilder(element, this._options, this._csn).build()
382
395
  sqls.push(sql)
383
396
  this._outputObj.values.push(...values)
@@ -394,17 +407,8 @@ class SelectBuilder extends BaseBuilder {
394
407
  * offset will still use placeholders, as it'll change during the paging queries.
395
408
  */
396
409
  _limit() {
397
- // limit
398
- if (this._obj.SELECT.one) {
399
- this._outputObj.sql.push('LIMIT', 1)
400
- } else {
401
- if (typeof this._obj.SELECT.limit.rows.val === 'number' && !this._parameterizedNumbers) {
402
- this._outputObj.sql.push('LIMIT', this._obj.SELECT.limit.rows.val)
403
- } else {
404
- this._outputObj.sql.push('LIMIT', '?')
405
- this._outputObj.values.push(this._obj.SELECT.limit.rows.val)
406
- }
407
- }
410
+ // limit (no placeholder for statement caching)
411
+ this._outputObj.sql.push('LIMIT', this._obj.SELECT.one ? 1 : this._obj.SELECT.limit.rows.val)
408
412
  // offset
409
413
  if (this._obj.SELECT.limit && this._obj.SELECT.limit.offset) {
410
414
  if (typeof this._obj.SELECT.limit.offset.val === 'number' && !this._parameterizedNumbers) {
@@ -425,7 +429,14 @@ class SelectBuilder extends BaseBuilder {
425
429
  }
426
430
 
427
431
  _val(obj) {
428
- return obj.val
432
+ let _val = obj.val
433
+ if (typeof _val === 'number') return { sql: _val, values: [] }
434
+ // REVISIT: remove with cds^6
435
+ if (typeof _val === 'string' && cds.env.features.extract_vals !== false) {
436
+ const match = _val.match(/^'(.*)'$/)
437
+ if (match) _val = match[1]
438
+ }
439
+ return { sql: '?', values: [_val] }
429
440
  }
430
441
 
431
442
  getDefaultOptions() {
@@ -1,6 +1,7 @@
1
1
  const BaseBuilder = require('./BaseBuilder')
2
2
  const getAnnotatedColumns = require('./annotations')
3
3
  const dollar = require('./dollar')
4
+ const { stringifyIfArrayedElement, isArrayedElement } = require('./arrayed')
4
5
 
5
6
  /**
6
7
  * UpdateBuilder is used to take a CQN object as an input and to build an object representing an update operation
@@ -69,7 +70,8 @@ class UpdateBuilder extends BaseBuilder {
69
70
  const entityName = this._entity()
70
71
  this._options.entityName = entityName
71
72
 
72
- this._data(getAnnotatedColumns(entityName, this._csn))
73
+ const entity = this._csn && this._csn.definitions && this._csn.definitions[entityName]
74
+ this._data(getAnnotatedColumns(entityName, this._csn), entity)
73
75
  if (Array.isArray(this._obj.UPDATE.where) && this._obj.UPDATE.where.length > 0) {
74
76
  this._where()
75
77
  }
@@ -101,7 +103,7 @@ class UpdateBuilder extends BaseBuilder {
101
103
  }
102
104
  }
103
105
 
104
- _data(annotatedColumns) {
106
+ _data(annotatedColumns, entity) {
105
107
  const sql = []
106
108
  const data = this._obj.UPDATE.data || {}
107
109
  const withObj = this._obj.UPDATE.with || {}
@@ -111,13 +113,19 @@ class UpdateBuilder extends BaseBuilder {
111
113
 
112
114
  this._addAnnotatedUpdateColumns(resMap, annotatedColumns)
113
115
 
116
+ if (entity && entity.keys) {
117
+ resMap.forEach((value, key, map) => {
118
+ if (key in entity.keys) map.delete(key)
119
+ })
120
+ }
121
+
114
122
  resMap.forEach((value, key, map) => {
115
123
  if (value && value.sql) {
116
124
  sql.push(`${this._quoteElement(key)} = ${value.sql}`)
117
125
  this._outputObj.values.push(...value.values)
118
126
  } else {
119
127
  sql.push(`${this._quoteElement(key)} = ?`)
120
- this._outputObj.values.push(value)
128
+ this._outputObj.values.push(stringifyIfArrayedElement(value))
121
129
  }
122
130
  })
123
131
 
@@ -142,7 +150,9 @@ class UpdateBuilder extends BaseBuilder {
142
150
  const prefixKey = prefix ? `${prefix}_${key}` : key
143
151
  const value = data[key]
144
152
  if (typeof value === 'object' && !Buffer.isBuffer(value) && value !== null && typeof value.pipe !== 'function') {
145
- if ('xpr' in value && Array.isArray(value.xpr)) {
153
+ if (isArrayedElement(value)) {
154
+ res.set(key, JSON.stringify(value))
155
+ } else if ('xpr' in value && Array.isArray(value.xpr)) {
146
156
  const xpr = new this.ExpressionBuilder(value.xpr, this._options, this._csn).build()
147
157
  res.set(key, xpr)
148
158
  } else if ('ref' in value && Array.isArray(value.ref)) {
@@ -0,0 +1,4 @@
1
+ const isArrayedElement = v => Array.isArray(v) && !Buffer.isBuffer(v)
2
+ const stringifyIfArrayedElement = v => (isArrayedElement(v) ? JSON.stringify(v) : v)
3
+
4
+ module.exports = { isArrayedElement, stringifyIfArrayedElement }
@@ -30,12 +30,20 @@ async function processCQNs(processFn, cqns, model, dbc, user, locale, ts, chunks
30
30
  const results = new Array(cqns.length)
31
31
 
32
32
  const deletes = []
33
+ const updatesBeforeDelete = []
33
34
  const others = []
34
35
  for (let i = 0; i < cqns.length; i++) {
35
36
  if (cqns[i].DELETE) deletes.push(i)
37
+ else if (cqns[i].UPDATE && cqns[i].UPDATE._beforeDelete) updatesBeforeDelete.push(i)
36
38
  else others.push(i)
37
39
  }
38
40
 
41
+ // UPDATEs to SET null parent's foreign keys of one compositions
42
+ // which are otherwise violate foreign key constraints
43
+ if (updatesBeforeDelete.length) {
44
+ await _processChunk(processFn, model, dbc, cqns, user, locale, ts, updatesBeforeDelete, results)
45
+ }
46
+
39
47
  if (deletes.length > 0) {
40
48
  if (chunks) {
41
49
  let offset = 0
@@ -52,6 +52,7 @@ const _generateAliases = (partialCqn, aliasMap = new Map()) => {
52
52
  _redirectXpr(partialCqn.SELECT.where, selectMap)
53
53
  _redirectXpr(partialCqn.SELECT.having, selectMap)
54
54
  _redirectXpr(partialCqn.SELECT.columns, selectMap)
55
+ _redirectXpr(partialCqn.SELECT.groupBy, selectMap)
55
56
  return
56
57
  }
57
58
 
@@ -65,7 +66,7 @@ const _generateAliases = (partialCqn, aliasMap = new Map()) => {
65
66
  return
66
67
  }
67
68
 
68
- if (partialCqn.join) {
69
+ if (Object.prototype.hasOwnProperty.call(partialCqn, 'join')) {
69
70
  partialCqn.args.forEach(arg => {
70
71
  if (arg.ref) {
71
72
  _redirectRef(arg, aliasMap)
@@ -1,19 +1,19 @@
1
1
  const cds = require('../../cds')
2
2
  const { INSERT, SELECT, UPDATE, DELETE } = cds.ql
3
3
 
4
- const { ensureNoDraftsSuffix, ensureDraftsSuffix, filterKeys } = require('../utils/handler')
4
+ const {
5
+ ensureNoDraftsSuffix,
6
+ ensureDraftsSuffix,
7
+ filterKeys,
8
+ getDeleteDraftAdminCqn,
9
+ getCompositionTargets
10
+ } = require('../utils/handler')
5
11
  const { readAndDeleteKeywords, isActiveEntityRequested, getKeyData } = require('../utils/where')
6
12
  const { isDraftRootEntity } = require('../../fiori/utils/csn')
7
- const { getVirtuals, postProcessVirtuals } = require('../../db/generic/virtual')
8
13
  const { getColumns } = require('../../cds-services/services/utils/columns')
9
14
 
10
15
  const { DRAFT_COLUMNS } = require('../../common/constants/draft')
11
16
 
12
- const _getDeleteDraftAdminCqn = draftUUID =>
13
- DELETE.from('DRAFT.DraftAdministrativeData').where([{ ref: ['DraftUUID'] }, '=', { val: draftUUID }])
14
-
15
- const _getDeleteRootDraftCqn = (targetName, rootWhere) => DELETE.from(targetName).where(rootWhere)
16
-
17
17
  const _getRootCQN = (context, requestActiveData) => {
18
18
  const keys = filterKeys(context.target.keys)
19
19
  const keyData = getKeyData(keys, context.query.SELECT.from.ref[0].where)
@@ -147,12 +147,10 @@ const _handler = async function (req) {
147
147
  r.getUrlObject = () => req.getUrlObject()
148
148
  r._.params = req.params
149
149
 
150
- const virtuals = getVirtuals(req, this.model)
151
150
  // use finally to preserve r.messages in success or error case
152
151
  let result
153
152
  try {
154
153
  result = await this.dispatch(r)
155
- postProcessVirtuals(virtuals, result)
156
154
  } finally {
157
155
  // REVISIT: should not be necessary
158
156
  if (r.messages) for (const m of r.messages) req.info(m)
@@ -161,12 +159,18 @@ const _handler = async function (req) {
161
159
  /*
162
160
  * delete draft data
163
161
  */
164
- const deleteDraftAdminCqn = _getDeleteDraftAdminCqn(adminData.DraftUUID)
165
- const deleteRootDraftCqn = _getDeleteRootDraftCqn(
166
- ensureDraftsSuffix(req.target.name),
167
- req.query.SELECT.from.ref[0].where
168
- )
169
- await cds.db.tx(req).run([deleteDraftAdminCqn, deleteRootDraftCqn])
162
+ const deleteDraftAdminCqn = getDeleteDraftAdminCqn(adminData.DraftUUID)
163
+ const draftTablesToDeleteFrom = [req.target.name + '_drafts']
164
+ for (const [entity] of getCompositionTargets(req.target, this).entries())
165
+ draftTablesToDeleteFrom.push(entity + '_drafts')
166
+ await cds.db.tx(req).run([
167
+ deleteDraftAdminCqn,
168
+ ...draftTablesToDeleteFrom.map(dt => {
169
+ const d = DELETE.from(dt).where({ DraftAdministrativeData_DraftUUID: adminData.DraftUUID })
170
+ d._suppressDeepDelete = true // hidden flag to tell db layer that no deep delete is required
171
+ return d
172
+ })
173
+ ])
170
174
 
171
175
  return result
172
176
  }
@@ -3,8 +3,8 @@ const cds = require('../../cds')
3
3
  const { SELECT } = cds.ql
4
4
 
5
5
  const { isNavigationToMany } = require('../utils/req')
6
- const { isDraftActivateAction } = require('../utils/handler')
7
- const { ensureNoDraftsSuffix, ensureDraftsSuffix, draftIsLocked } = require('../utils/handler')
6
+ const { getKeysCondition } = require('../utils/where')
7
+ const { isDraftActivateAction, ensureNoDraftsSuffix, ensureDraftsSuffix, draftIsLocked } = require('../utils/handler')
8
8
 
9
9
  const { isCustomOperation } = require('../../cds-services/adapter/odata-v4/utils/request')
10
10
 
@@ -128,15 +128,7 @@ const _addDraftDataFromExistingDraft = async (req, service) => {
128
128
  }
129
129
 
130
130
  if (!parent) {
131
- const keys = Object.keys(req.target.keys)
132
- const rootWhere = keys.reduce((res, key) => {
133
- if (key === 'IsActiveEntity') {
134
- return res
135
- }
136
- res[key] = req.data[key]
137
- return res
138
- }, {})
139
-
131
+ const rootWhere = getKeysCondition(req.target, req.data)
140
132
  result = await cds.tx(req).run(_getSelectDraftDataCqn(ensureNoDraftsSuffix(req.target.name), rootWhere))
141
133
  if (result && result.length > 0) {
142
134
  _addDraftDataToContext(req, result)
@@ -8,7 +8,7 @@ const { deleteDraft } = require('../utils/delete')
8
8
  * @param req
9
9
  */
10
10
  const _handler = function (req) {
11
- return deleteDraft(req, this.model.definitions, false)
11
+ return deleteDraft(req, this)
12
12
  }
13
13
 
14
14
  module.exports = function () {
@@ -8,7 +8,9 @@ const { deleteDraft } = require('../utils/delete')
8
8
  * @param req
9
9
  */
10
10
  const _handler = function (req) {
11
- return deleteDraft(req, this.model.definitions, true)
11
+ // we should call deleteDraft only for draft tables and call next
12
+ // to pass delete of active tables to our general implementation
13
+ return deleteDraft(req, this, true)
12
14
  }
13
15
 
14
16
  module.exports = function () {
@@ -92,7 +92,9 @@ const _handler = async function (req) {
92
92
  res[key] = data[key]
93
93
  return res
94
94
  }, {})
95
- const transition = getTransition(req.target)
95
+
96
+ // cds.db and not "this" as we want to resolve as db here
97
+ const transition = getTransition(req.target, cds.db)
96
98
  const lockWhere = _getLockWhere(rootWhere, transition.mapping)
97
99
 
98
100
  // gets the underlying target entity, as record locking can't be
@@ -110,13 +112,21 @@ const _handler = async function (req) {
110
112
  const rootCQN = SELECT.from(req.target, columnNames).where(rootWhere)
111
113
  const subCQNs = getSubCQNs({
112
114
  definitions,
113
- context: req,
114
115
  rootCQN,
115
116
  compositionTree: getCompositionTree({ definitions, rootEntityName: ensureNoDraftsSuffix(req.target.name) })
116
117
  })
117
118
  const rootDraftName = ensureDraftsSuffix(req.target.name)
118
119
  const draftExistsCQN = SELECT.from(rootDraftName, ['DraftAdministrativeData_DraftUUID as DraftUUID']).where(rootWhere)
119
120
  const selectCQNs = [rootCQN, ...subCQNs.map(obj => obj.cqn)]
121
+
122
+ // fetch unlocalized data if not a texts entity
123
+ for (const q of selectCQNs) {
124
+ const entity = definitions[q.SELECT.from.ref[0]]
125
+ if (entity && !entity.name.match(/\.texts$/)) {
126
+ Object.defineProperty(q, '_suppressLocalization', { value: true })
127
+ }
128
+ }
129
+
120
130
  const lockAndSelectCQNs = [lockRecordCQN, draftExistsCQN, ...selectCQNs]
121
131
 
122
132
  const dbtx = cds.tx(req)
@@ -6,7 +6,6 @@ const { isNavigationToMany } = require('../utils/req')
6
6
  const { getKeysCondition } = require('../utils/where')
7
7
  const { removeDraftUUIDIfNecessary, ensureDraftsSuffix } = require('../utils/handler')
8
8
  const { DRAFT_COLUMNS } = require('../../common/constants/draft')
9
- const { getVirtuals, postProcessVirtuals } = require('../../db/generic/virtual')
10
9
 
11
10
  const _getUpdateDraftAdminCQN = ({ user, timestamp }, draftUUID) => {
12
11
  return UPDATE('DRAFT.DraftAdministrativeData')
@@ -76,10 +75,13 @@ const _handler = async function (req, next) {
76
75
  const insertDataCQN = _getInsertDataCQN(req, req.data.DraftAdministrativeData_DraftUUID)
77
76
 
78
77
  // read data as on db and return
79
- const readInsertDataCQN = SELECT.from(insertDataCQN.INSERT.into)
78
+ const columns = Object.keys(req.target.elements)
79
+ .map(e => req.target.elements[e])
80
+ .filter(e => !e.isAssociation)
81
+ .map(e => e.name)
82
+ const readInsertDataCQN = SELECT.from(insertDataCQN.INSERT.into).columns(columns)
80
83
  readInsertDataCQN.where(getKeysCondition(req.target, req.data))
81
84
 
82
- const virtuals = getVirtuals({ query: readInsertDataCQN, target: req.target }, this.model)
83
85
  const dbtx = cds.tx(req)
84
86
 
85
87
  await Promise.all([dbtx.run(adminDataCQN), dbtx.run(insertDataCQN)])
@@ -89,8 +91,6 @@ const _handler = async function (req, next) {
89
91
  req.reject(404)
90
92
  }
91
93
 
92
- postProcessVirtuals(virtuals, result)
93
-
94
94
  removeDraftUUIDIfNecessary(result[0], req)
95
95
 
96
96
  return result[0]
@@ -53,21 +53,6 @@ const _getUpdateDraftCQN = ({ query, target: { name } }, keysCondition) => {
53
53
  return UPDATE(ensureDraftsSuffix(name)).data(set).where(keysCondition)
54
54
  }
55
55
 
56
- const _deleteDraftAdminProperties = oldData => {
57
- for (const toBeDeletedProperty of [
58
- 'DraftIsCreatedByMe',
59
- 'DraftIsProcessedByMe',
60
- 'InProcessByUser',
61
- 'LastChangeDateTime',
62
- 'LastChangedByUser',
63
- 'CreatedByUser',
64
- 'CreationDateTime',
65
- 'DraftUUID'
66
- ]) {
67
- delete oldData[toBeDeletedProperty]
68
- }
69
- }
70
-
71
56
  /**
72
57
  * Generic Handler for PATCH requests in the context of draft.
73
58
  * In case of success it returns the updated entry.
@@ -91,9 +76,6 @@ const _handler = async function (req) {
91
76
  req.reject(403)
92
77
  }
93
78
 
94
- _deleteDraftAdminProperties(result[0])
95
- req._oldData = result[0]
96
-
97
79
  const updateDraftCQN = _getUpdateDraftCQN(req, keysCondition)
98
80
  const updateDraftAdminCQN = getUpdateDraftAdminCQN(req, result[0].DraftAdministrativeData_DraftUUID)
99
81