@sap/cds 5.6.4 → 5.7.4

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 (183) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/_i18n/i18n_fr.properties +4 -4
  3. package/apis/cds.d.ts +7 -10
  4. package/apis/connect.d.ts +3 -3
  5. package/apis/core.d.ts +2 -4
  6. package/apis/models.d.ts +2 -3
  7. package/apis/ql.d.ts +0 -1
  8. package/apis/services.d.ts +3 -3
  9. package/bin/build/buildTaskFactory.js +16 -10
  10. package/bin/build/buildTaskProviderFactory.js +3 -3
  11. package/bin/build/constants.js +2 -1
  12. package/bin/build/provider/buildTaskProviderInternal.js +14 -14
  13. package/bin/build/provider/hana/2migration.js +2 -3
  14. package/bin/build/provider/hana/index.js +34 -0
  15. package/bin/build/provider/hana/migrationtable.js +90 -22
  16. package/bin/build/provider/hana/template/undeploy.json +5 -0
  17. package/bin/build/provider/node-cf/index.js +9 -2
  18. package/bin/serve.js +16 -18
  19. package/lib/compile/cdsc.js +15 -5
  20. package/lib/compile/etc/_localized.js +4 -4
  21. package/lib/compile/extend.js +8 -0
  22. package/lib/compile/index.js +3 -1
  23. package/lib/compile/minify.js +61 -0
  24. package/lib/compile/resolve.js +4 -1
  25. package/lib/compile/to/gql.js +9 -0
  26. package/lib/compile/to/sql.js +26 -30
  27. package/lib/connect/index.js +1 -1
  28. package/lib/core/entities.js +0 -3
  29. package/lib/core/infer.js +1 -0
  30. package/lib/core/reflect.js +0 -34
  31. package/lib/deploy.js +25 -17
  32. package/lib/env/defaults.js +3 -1
  33. package/lib/env/index.js +8 -3
  34. package/lib/env/presets.js +38 -0
  35. package/lib/env/requires.js +16 -11
  36. package/lib/index.js +13 -11
  37. package/lib/log/format/kibana.js +3 -1
  38. package/lib/log/index.js +2 -2
  39. package/lib/req/cds-context.js +79 -0
  40. package/lib/req/context.js +5 -77
  41. package/lib/req/request.js +1 -1
  42. package/lib/serve/Service-api.js +8 -4
  43. package/lib/serve/Service-dispatch.js +0 -7
  44. package/lib/serve/Service-methods.js +6 -8
  45. package/lib/serve/Transaction.js +35 -30
  46. package/lib/serve/adapters.js +1 -4
  47. package/lib/utils/axios.js +1 -1
  48. package/libx/_runtime/audit/Service.js +44 -20
  49. package/libx/_runtime/audit/generic/personal/access.js +16 -11
  50. package/libx/_runtime/audit/generic/personal/modification.js +5 -5
  51. package/libx/_runtime/audit/generic/personal/utils.js +46 -37
  52. package/libx/_runtime/{common/auth → auth}/index.js +21 -7
  53. package/libx/_runtime/{common/auth → auth}/strategies/JWT.js +2 -2
  54. package/libx/_runtime/{common/auth → auth}/strategies/basic.js +2 -2
  55. package/libx/_runtime/{common/auth → auth}/strategies/dummy.js +1 -1
  56. package/libx/_runtime/{common/auth → auth}/strategies/mock.js +2 -2
  57. package/libx/_runtime/{common/auth → auth}/strategies/utils/uaa.js +1 -1
  58. package/libx/_runtime/{common/auth → auth}/strategies/utils/xssec.js +0 -0
  59. package/libx/_runtime/{common/auth → auth}/strategies/xsuaa.js +2 -2
  60. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +7 -2
  61. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +0 -7
  62. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +0 -8
  63. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +3 -4
  64. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +6 -7
  65. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +3 -4
  66. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +2 -11
  67. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +16 -6
  68. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +26 -69
  69. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +0 -7
  70. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +3 -66
  71. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +29 -1
  72. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +7 -1
  73. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +4 -0
  74. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +2 -2
  75. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +13 -10
  76. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/ConditionalRequestControlCommand.js +0 -7
  77. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/ConditionalRequestValidator.js +0 -8
  78. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +54 -76
  79. package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +0 -7
  80. package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +3 -6
  81. package/libx/_runtime/cds-services/adapter/rest/handlers/delete.js +3 -6
  82. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +3 -6
  83. package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +3 -6
  84. package/libx/_runtime/cds-services/adapter/rest/handlers/update.js +3 -6
  85. package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +8 -4
  86. package/libx/_runtime/cds-services/services/Service.js +1 -7
  87. package/libx/_runtime/cds-services/services/utils/columns.js +10 -3
  88. package/libx/_runtime/cds-services/services/utils/compareJson.js +3 -6
  89. package/libx/_runtime/cds-services/services/utils/differ.js +4 -1
  90. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +1 -41
  91. package/libx/_runtime/cds-services/util/assert.js +1 -262
  92. package/libx/_runtime/cds.js +6 -9
  93. package/libx/_runtime/common/aspects/entity.js +1 -1
  94. package/libx/_runtime/common/composition/delete.js +4 -2
  95. package/libx/_runtime/common/composition/update.js +22 -38
  96. package/libx/_runtime/common/composition/utils.js +3 -7
  97. package/libx/_runtime/common/error/standardError.js +11 -0
  98. package/libx/_runtime/common/generic/auth.js +63 -33
  99. package/libx/_runtime/common/generic/crud.js +11 -23
  100. package/libx/_runtime/common/generic/input.js +20 -0
  101. package/libx/_runtime/common/generic/put.js +4 -10
  102. package/libx/_runtime/common/generic/sorting.js +12 -30
  103. package/libx/_runtime/common/i18n/messages.properties +2 -0
  104. package/libx/_runtime/common/perf/index.js +24 -0
  105. package/libx/_runtime/common/utils/cqn.js +58 -1
  106. package/libx/_runtime/common/utils/cqn2cqn4sql.js +298 -121
  107. package/libx/_runtime/common/utils/csn.js +38 -56
  108. package/libx/_runtime/common/utils/entityFromCqn.js +6 -6
  109. package/libx/_runtime/common/utils/resolveView.js +4 -5
  110. package/libx/_runtime/common/utils/rewriteAsterisks.js +46 -5
  111. package/libx/_runtime/common/utils/search2cqn4sql.js +21 -9
  112. package/libx/_runtime/common/utils/structured.js +35 -25
  113. package/libx/_runtime/db/Service.js +0 -6
  114. package/libx/_runtime/db/data-conversion/post-processing.js +22 -22
  115. package/libx/_runtime/db/expand/expand-v2.js +130 -0
  116. package/libx/_runtime/db/expand/expandCQNToJoin.js +57 -75
  117. package/libx/_runtime/db/expand/index.js +3 -1
  118. package/libx/_runtime/db/generic/input.js +52 -10
  119. package/libx/_runtime/db/generic/integrity.js +367 -26
  120. package/libx/_runtime/db/generic/virtual.js +51 -13
  121. package/libx/_runtime/db/query/read.js +12 -8
  122. package/libx/_runtime/db/query/update.js +9 -3
  123. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +8 -9
  124. package/libx/_runtime/{common → db}/utils/propagateForeignKeys.js +11 -14
  125. package/libx/_runtime/fiori/generic/activate.js +1 -0
  126. package/libx/_runtime/fiori/generic/before.js +2 -1
  127. package/libx/_runtime/fiori/generic/edit.js +1 -0
  128. package/libx/_runtime/fiori/generic/patch.js +1 -1
  129. package/libx/_runtime/fiori/generic/read.js +128 -57
  130. package/libx/_runtime/fiori/uiflex/index.js +1 -1
  131. package/libx/_runtime/fiori/uiflex/{extensibility/index.js → service.js} +3 -3
  132. package/libx/_runtime/fiori/utils/delete.js +7 -1
  133. package/libx/_runtime/hana/Service.js +1 -8
  134. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -14
  135. package/libx/_runtime/hana/execute.js +10 -4
  136. package/libx/_runtime/hana/pool.js +55 -45
  137. package/libx/_runtime/hana/search.js +7 -6
  138. package/libx/_runtime/hana/search2cqn4sql.js +8 -5
  139. package/libx/_runtime/hana/searchToContains.js +3 -1
  140. package/libx/_runtime/index.js +5 -5
  141. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +3 -3
  142. package/libx/_runtime/messaging/Outbox.js +53 -0
  143. package/libx/_runtime/messaging/common-utils/AMQPClient.js +17 -10
  144. package/libx/_runtime/messaging/common-utils/connections.js +14 -9
  145. package/libx/_runtime/messaging/common-utils/waitingTime.js +2 -0
  146. package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -3
  147. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -2
  148. package/libx/_runtime/messaging/enterprise-messaging.js +21 -15
  149. package/libx/_runtime/messaging/file-based.js +5 -5
  150. package/libx/_runtime/messaging/message-queuing-utils/options-messaging.js +1 -0
  151. package/libx/_runtime/messaging/message-queuing.js +2 -3
  152. package/libx/_runtime/messaging/outbox/OutboxRunner.js +75 -0
  153. package/libx/_runtime/messaging/outbox/utils.js +192 -0
  154. package/libx/_runtime/messaging/service.js +16 -30
  155. package/libx/_runtime/remote/Service.js +15 -0
  156. package/libx/_runtime/remote/utils/client.js +15 -3
  157. package/libx/_runtime/remote/utils/{dataConversion.js → data.js} +12 -2
  158. package/libx/_runtime/sqlite/Service.js +7 -10
  159. package/libx/_runtime/sqlite/customBuilder/CustomExpressionBuilder.js +19 -0
  160. package/libx/_runtime/sqlite/execute.js +18 -12
  161. package/libx/_runtime/types/api.js +2 -1
  162. package/libx/gql/resolvers/parse/ast2cqn/columns.js +1 -1
  163. package/libx/odata/{odata2cqn/afterburner.js → afterburner.js} +19 -15
  164. package/libx/odata/{cqn2odata/index.js → cqn2odata.js} +1 -1
  165. package/libx/odata/{odata2cqn/grammar.pegjs → grammar.pegjs} +217 -148
  166. package/libx/odata/index.js +21 -13
  167. package/libx/odata/parser.js +1 -0
  168. package/libx/odata/utils.js +57 -0
  169. package/libx/rest/RestAdapter.js +2 -6
  170. package/libx/rest/utils/data.js +1 -6
  171. package/package.json +4 -3
  172. package/server.js +4 -5
  173. package/srv/audit-log.cds +87 -0
  174. package/{libx/_runtime/fiori/uiflex/extensibility/index.cds → srv/flex.cds} +0 -0
  175. package/srv/flex.js +1 -0
  176. package/srv/outbox.cds +11 -0
  177. package/srv/outbox.js +0 -0
  178. package/libx/_runtime/cds-services/adapter/perf/performance.js +0 -104
  179. package/libx/_runtime/cds-services/adapter/perf/performanceMeasurement.js +0 -33
  180. package/libx/odata/odata2cqn/index.js +0 -3
  181. package/libx/odata/odata2cqn/parser.js +0 -1
  182. package/libx/odata/readme.md +0 -1
  183. package/libx/odata/utils/index.js +0 -64
@@ -51,12 +51,8 @@ class JoinCQNFromExpanded {
51
51
  * @returns {this}
52
52
  */
53
53
  buildJoinQueries() {
54
- const unionTableRef = this._getUnionTable(this._SELECT)
55
54
  // side effect: this_aliases is set
56
- const aliases = this._getTableAlias(this._SELECT, [], unionTableRef && unionTableRef.table)
57
-
58
- // Add table aliases to all refs in where part obtained from annotations
59
- this._adaptAliasForWhere(this._SELECT.where)
55
+ const aliases = this._getTableAlias(this._SELECT, [])
60
56
 
61
57
  // Update elements at WHERE, so there are no issues with ambiguity
62
58
  this._adaptWhereOrderBy(this._SELECT, aliases)
@@ -108,7 +104,7 @@ class JoinCQNFromExpanded {
108
104
 
109
105
  const unionTableRef = this._getUnionTable(SELECT)
110
106
  const unionTable = unionTableRef && unionTableRef.table
111
- const tableAlias = this._getTableAlias(SELECT, toManyTree, unionTable)
107
+ const tableAlias = this._getTableAlias(SELECT, toManyTree)
112
108
 
113
109
  const readToOneCQN = this._getReadToOneCQN(SELECT, isJoinOfTwoSelects ? 'filterExpand' : tableAlias)
114
110
 
@@ -161,11 +157,14 @@ class JoinCQNFromExpanded {
161
157
  * @returns {string}
162
158
  * @private
163
159
  */
164
- _getTableAlias(SELECT, toManyTree, unionTable) {
165
- return this._createAlias(toManyTree.length === 0 ? unionTable || this._getRef(SELECT).table : toManyTree.join(':'))
160
+ _getTableAlias(SELECT, toManyTree) {
161
+ return this._createAlias(toManyTree.length === 0 ? this._getRef(SELECT).table : toManyTree.join(':'))
166
162
  }
167
163
 
168
164
  _getRef(SELECT) {
165
+ const unionTableRef = this._getUnionTable(SELECT)
166
+ if (unionTableRef) return unionTableRef
167
+
169
168
  const table = Object.prototype.hasOwnProperty.call(SELECT.from, 'join')
170
169
  ? this._getRefFromJoin(SELECT.from.args)
171
170
  : SELECT.from
@@ -256,10 +255,18 @@ class JoinCQNFromExpanded {
256
255
  : column
257
256
  }
258
257
 
259
- _adaptJoin(tableAlias, cqn, from) {
258
+ _adaptJoin(tableAlias, cqn, from, mapping = {}) {
260
259
  from.args = from.args.slice(0)
261
260
  if (Object.prototype.hasOwnProperty.call(from.args[0], 'join')) {
262
- this._adaptJoin(tableAlias, cqn, from.args[0])
261
+ this._adaptJoin(tableAlias, cqn, from.args[0], mapping)
262
+ if (from.on) {
263
+ // we come here for SiblingEntity with expand
264
+ for (const originalIdentifier in mapping) {
265
+ from.on = from.on.map(column =>
266
+ this._adaptTableNameInColumn(column, originalIdentifier, mapping[originalIdentifier])
267
+ )
268
+ }
269
+ }
263
270
  } else {
264
271
  const index = from.args[0].ref ? 0 : from.args.length - 1
265
272
  const target = Object.assign({}, from.args[index], { as: tableAlias })
@@ -268,6 +275,7 @@ class JoinCQNFromExpanded {
268
275
  from.args[index] = target
269
276
  from.on = from.on.map(column => this._adaptTableNameInColumn(column, originalIdentifier, tableAlias))
270
277
  cqn.columns = cqn.columns.map(column => this._adaptTableNameInColumn(column, originalIdentifier, tableAlias))
278
+ mapping[originalIdentifier] = tableAlias
271
279
  }
272
280
  }
273
281
 
@@ -331,32 +339,6 @@ class JoinCQNFromExpanded {
331
339
  whereElement.ref && whereElement.ref.splice(0, 1, Object.values(this._aliases)[0])
332
340
  }
333
341
 
334
- _adaptAliasForFrom(from) {
335
- if (from.args) {
336
- from.args.forEach(arg => {
337
- this._adaptAliasForFrom(arg)
338
- })
339
- } else if (from.SELECT) {
340
- this._adaptAliasForFrom(from.SELECT.from)
341
- if (from.SELECT.where) {
342
- this._adaptAliasForWhere(from.SELECT.where)
343
- }
344
- }
345
- }
346
-
347
- _adaptAliasForWhere(where) {
348
- if (where) {
349
- for (const whereElement of where) {
350
- if (whereElement.SELECT) {
351
- if (whereElement.SELECT.where) {
352
- this._adaptAliasForWhere(whereElement.SELECT.where)
353
- }
354
- this._adaptAliasForFrom(whereElement.SELECT.from)
355
- }
356
- }
357
- }
358
- }
359
-
360
342
  _navigationNeedsAlias(element, { table } = {}) {
361
343
  const entity = this._csn.definitions[table]
362
344
  if (entity) {
@@ -377,9 +359,9 @@ class JoinCQNFromExpanded {
377
359
 
378
360
  if (element.ref.length === 1) {
379
361
  element.ref.unshift(tableAlias)
380
- } else if (this._elementAliasNeedsReplacement(element, this._getUnionTable(cqn) || this._getRef(cqn))) {
362
+ } else if (this._elementAliasNeedsReplacement(element, this._getRef(cqn))) {
381
363
  element.ref[0] = tableAlias
382
- } else if (this._navigationNeedsAlias(element, this._getUnionTable(cqn) || this._getRef(cqn))) {
364
+ } else if (this._navigationNeedsAlias(element, this._getRef(cqn))) {
383
365
  element.ref.unshift(tableAlias)
384
366
  }
385
367
 
@@ -392,7 +374,7 @@ class JoinCQNFromExpanded {
392
374
  } else if (element.SELECT && element.SELECT.where) {
393
375
  element = {
394
376
  SELECT: Object.assign({}, element.SELECT, {
395
- where: this._adaptWhereSELECT(this._getUnionTable(cqn) || this._getRef(cqn), element.SELECT.where, tableAlias)
377
+ where: this._adaptWhereSELECT(this._getRef(cqn), element.SELECT.where, tableAlias)
396
378
  })
397
379
  }
398
380
  }
@@ -588,13 +570,15 @@ class JoinCQNFromExpanded {
588
570
  const on = []
589
571
  for (const key in entity._target.keys) {
590
572
  if (key !== 'IsActiveEntity') {
573
+ if (on.length) on.push('AND')
591
574
  on.push({ ref: [`${tableAlias}_drafts`, key] }, '=', { ref: [tableAlias, key] })
592
575
  }
593
576
  }
577
+
594
578
  return {
595
579
  args: [cqn, { ref: [draftTable], as: `${tableAlias}_drafts` }],
596
580
  join: 'left',
597
- on: on
581
+ on: ['(', ...on, ')']
598
582
  }
599
583
  }
600
584
 
@@ -613,8 +597,8 @@ class JoinCQNFromExpanded {
613
597
  )
614
598
  }
615
599
 
616
- _getTarget(entity, column) {
617
- const navigation = getNavigationIfStruct(entity, column.ref)
600
+ _getTarget(entity, column, parentAlias) {
601
+ const navigation = getNavigationIfStruct(entity, column.ref[0] === parentAlias ? column.ref.slice(1) : column.ref)
618
602
  return (navigation && navigation.target) || column.ref[0]
619
603
  }
620
604
 
@@ -632,17 +616,19 @@ class JoinCQNFromExpanded {
632
616
  */
633
617
  // eslint-disable-next-line complexity
634
618
  _addJoinAndElements({ column, entity, readToOneCQN, toManyTree, parentAlias, defaultLanguage }) {
635
- const extendedToManyTree = toManyTree.concat(column.ref)
619
+ const extendedToManyTree = toManyTree.concat(column.ref[0] === parentAlias ? column.ref.slice(1) : column.ref)
636
620
  const tableAlias = this._createAlias(extendedToManyTree.join(':'))
637
- const target = this._getTarget(entity, column)
621
+ const target = this._getTarget(entity, column, parentAlias)
622
+
623
+ const name = column.ref[column.ref.length - 1]
624
+ const element = name && entity.elements[name]
638
625
 
639
626
  // if union always only expand with active, otherwise evaluate flag
640
627
  // if flag shows false, we check entity for associations to non draft
641
628
  const activeTableRequired =
642
- readToOneCQN[IS_UNION_DRAFT] ||
629
+ readToOneCQN[IS_UNION_DRAFT] || // > REVISIT: blocks expanding comp2one
643
630
  readToOneCQN[IS_ACTIVE] ||
644
- (entity.elements[column.ref[0]].type === 'cds.Association' &&
645
- !entity.elements[column.ref[0]]['@odata.draft.enclosed']) ||
631
+ (element && element.type === 'cds.Association' && !element['@odata.draft.enclosed']) ||
646
632
  !this._csn.definitions[target]._isDraftEnabled
647
633
 
648
634
  const colTarget = target && ensureUnlocalized(target)
@@ -652,9 +638,7 @@ class JoinCQNFromExpanded {
652
638
  (colTarget && this._csn.definitions[colTarget] && this._csn.definitions[colTarget]['@cds.localized'] === false)
653
639
 
654
640
  const join =
655
- column.ref[0] === 'DraftAdministrativeData' || !entity.elements[column.ref[0]].notNull || this._isDraft
656
- ? 'left'
657
- : 'inner'
641
+ column.ref[0] === 'DraftAdministrativeData' || !(element && element.notNull) || this._isDraft ? 'left' : 'inner'
658
642
 
659
643
  const args = [
660
644
  readToOneCQN.from.SET ? this._unionToSubQuery(readToOneCQN) : readToOneCQN.from,
@@ -681,9 +665,9 @@ class JoinCQNFromExpanded {
681
665
  readToOneCQN.from.args[1] = {
682
666
  SELECT: {
683
667
  columns: cols,
684
- from: unionFrom,
685
- as: tableAlias
686
- }
668
+ from: unionFrom
669
+ },
670
+ as: tableAlias
687
671
  }
688
672
  }
689
673
 
@@ -695,11 +679,11 @@ class JoinCQNFromExpanded {
695
679
  }
696
680
 
697
681
  // special case of navigation to one requires additional LEFT JOIN and CASE for HasDraftEntity
698
- const compToOne = this._isNavigationToOne(readToOneCQN[IS_ACTIVE], entity.elements[column.ref[0]])
682
+ const compToOne = this._isNavigationToOne(readToOneCQN[IS_ACTIVE], element)
699
683
  const index = column.expand.findIndex(col => col.ref && col.ref[col.ref.length - 1] === 'HasDraftEntity')
700
684
 
701
685
  if (compToOne && index !== -1) {
702
- readToOneCQN.from = this._addJoinCompToOne(readToOneCQN.from, entity.elements[column.ref[0]], tableAlias)
686
+ readToOneCQN.from = this._addJoinCompToOne(readToOneCQN.from, element, tableAlias)
703
687
  if (activeTableRequired) {
704
688
  column.expand[index] = {
705
689
  xpr: [
@@ -771,9 +755,9 @@ class JoinCQNFromExpanded {
771
755
  return {
772
756
  SELECT: {
773
757
  columns: Array.from(readToOneCQN.columns),
774
- from: readToOneCQN.from,
775
- as: readToOneCQN.from.as
776
- }
758
+ from: readToOneCQN.from
759
+ },
760
+ as: readToOneCQN.from.as
777
761
  }
778
762
  }
779
763
 
@@ -849,7 +833,9 @@ class JoinCQNFromExpanded {
849
833
  const subSelectColumns = this._getSubSelectColumns(readToOneCQN)
850
834
 
851
835
  if (subSelectColumns.length === 0) {
852
- return entity._relations[tableAlias === columns[0] ? columns.slice(1) : columns].join(tableAlias, parentAlias)
836
+ return entity._relations[
837
+ tableAlias === columns[0] || parentAlias === columns[0] ? columns.slice(1) : columns
838
+ ].join(tableAlias, parentAlias)
853
839
  }
854
840
 
855
841
  const aliases = this._getAliases(subSelectColumns)
@@ -872,18 +858,18 @@ class JoinCQNFromExpanded {
872
858
 
873
859
  if (arg.args) {
874
860
  this._addJoinKeyColumnsToUnion(arg.args, on, parentAlias)
875
- } else if (arg.SELECT.from.SET && arg.SELECT.as === parentAlias) {
876
- this._addColumns(arg.SELECT.from.SET.args, on, parentAlias)
861
+ } else if (arg.SELECT.from.SET && (arg.as === parentAlias || arg.SELECT.from.as === parentAlias)) {
862
+ for (const _arg of arg.SELECT.from.SET.args) {
863
+ this._addColumns(_arg.SELECT.columns, on, parentAlias)
864
+ }
865
+ if (arg.SELECT.columns) {
866
+ this._addColumns(arg.SELECT.columns, on, parentAlias, true)
867
+ }
877
868
  }
878
869
  }
879
870
  }
880
871
 
881
- _addColumns(args, on, parentAlias) {
882
- const [
883
- {
884
- SELECT: { columns }
885
- }
886
- ] = args
872
+ _addColumns(columns, on, parentAlias, withAlias = false) {
887
873
  const keyColumns = on
888
874
  .filter(entry => {
889
875
  return (
@@ -892,15 +878,11 @@ class JoinCQNFromExpanded {
892
878
  !columns.some(column => column.ref && column.ref[column.ref.length - 1] === entry.ref[1])
893
879
  )
894
880
  })
895
- .map(entry => ({ ref: [entry.ref[1]] }))
896
-
881
+ .map(entry =>
882
+ withAlias ? { ref: [parentAlias, entry.ref[1]], as: `${parentAlias}_${entry.ref[1]}` } : { ref: [entry.ref[1]] }
883
+ )
897
884
  if (keyColumns.length === 0) return
898
-
899
- for (const {
900
- SELECT: { columns }
901
- } of args) {
902
- columns.push(...keyColumns)
903
- }
885
+ columns.push(...keyColumns)
904
886
  }
905
887
 
906
888
  /**
@@ -1,8 +1,10 @@
1
1
  const { hasExpand, createJoinCQNFromExpanded } = require('./expandCQNToJoin')
2
2
  const rawToExpanded = require('./rawToExpanded')
3
+ const expandV2 = require('./expand-v2')
3
4
 
4
5
  module.exports = {
5
6
  hasExpand,
6
7
  createJoinCQNFromExpanded,
7
- rawToExpanded
8
+ rawToExpanded,
9
+ expandV2
8
10
  }
@@ -14,7 +14,7 @@ const cds = require('../../cds')
14
14
  const normalizeTimeData = require('../utils/normalizeTimeData')
15
15
 
16
16
  const { enrichDataWithKeysFromWhere } = require('../../common/utils/keys')
17
- const { propagateForeignKeys } = require('../../common/utils/propagateForeignKeys')
17
+ const propagateForeignKeys = require('../utils/propagateForeignKeys')
18
18
  const getTemplate = require('../../common/utils/template')
19
19
  const templateProcessor = require('../../common/utils/templateProcessor')
20
20
 
@@ -25,12 +25,14 @@ const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
25
25
  const _isManaged = (category, event) =>
26
26
  (category === '@cds.on.insert' && event === 'CREATE') || (category === '@cds.on.update' && event === 'UPDATE')
27
27
 
28
+ const _shouldGenerateUUID = element => element.key && !DRAFT_COLUMNS_MAP[element.name] && element.type === 'cds.UUID'
29
+
28
30
  const _processComplexCategory = ({ row, key, val, category, req, element }) => {
29
31
  const categoryArgs = category.args
30
32
  category = category.category
31
33
 
32
34
  // propagate keys
33
- if (category === 'propagateForeignKeys') {
35
+ if (category === 'propagateForeignKeys' && row[key]) {
34
36
  propagateForeignKeys(key, row, element._foreignKeys, element._isCompositionEffective)
35
37
  return
36
38
  }
@@ -73,6 +75,20 @@ const _processCategory = ({ category, row, key, element, val, req }) => {
73
75
  return
74
76
  }
75
77
 
78
+ if (category === 'comp2one') {
79
+ if (!val) return
80
+
81
+ for (const keyName in element._target.keys) {
82
+ const k = element._target.keys[keyName]
83
+ if (k.type === 'cds.Association' || k.name === 'IsActiveEntity') continue
84
+
85
+ if (k.type !== 'cds.UUID' && !(k.name in val)) {
86
+ req.error(400, 'ASSERT_NOT_NULL', key + '[' + k + ']', [key + '[' + k + ']'])
87
+ return
88
+ }
89
+ }
90
+ }
91
+
76
92
  // not null without default (for better error message)
77
93
  if (category === '!default' && val == null && req.event === 'CREATE') {
78
94
  req.error(400, 'ASSERT_NOT_NULL', key, [key])
@@ -101,12 +117,24 @@ const processorFn =
101
117
  }
102
118
  }
103
119
 
120
+ const _isVirtualOrCalculated = element => {
121
+ if (element.virtual) return true
122
+ if (
123
+ element.parent &&
124
+ element.parent.projection &&
125
+ element.parent.projection.columns &&
126
+ element.parent.projection.columns.find(c => c.as === element.name && (c.xpr || c.val || c.func))
127
+ ) {
128
+ return true
129
+ }
130
+ }
131
+
104
132
  // params: element, target, parent, templateElements
105
- const _pick = element => {
133
+ const _pickCRUD = element => {
106
134
  // collect actions to apply
107
135
  const categories = []
108
136
 
109
- if (element.virtual) {
137
+ if (_isVirtualOrCalculated(element)) {
110
138
  categories.push('virtual')
111
139
  return { categories } // > no need to continue
112
140
  }
@@ -136,22 +164,37 @@ const _pick = element => {
136
164
  categories.push('associationEffective')
137
165
  }
138
166
 
167
+ // REVISIT: element._foreignKeys.length seems to be a very broad check
139
168
  if (element.isAssociation && element._foreignKeys.length) {
140
169
  categories.push({ category: 'propagateForeignKeys' })
141
170
  }
142
171
 
143
- // generate uuid
144
- if (element.key && !DRAFT_COLUMNS_MAP[element.name] && element.type === 'cds.UUID') {
172
+ if (_shouldGenerateUUID(element)) {
145
173
  categories.push('uuid')
146
174
  }
147
175
 
176
+ if (element.isComposition && element.is2one) {
177
+ categories.push('comp2one')
178
+ }
179
+
148
180
  if (categories.length) return { categories }
149
181
  }
150
182
 
151
- const _pickVirtual = element => {
183
+ const _pickDraft = element => {
152
184
  // collect actions to apply
153
185
  const categories = []
186
+
154
187
  if (element.virtual) categories.push('virtual')
188
+
189
+ // REVISIT: element._foreignKeys.length seems to be a very broad check
190
+ if (element.isAssociation && element._foreignKeys.length) {
191
+ categories.push({ category: 'propagateForeignKeys' })
192
+ }
193
+
194
+ if (_shouldGenerateUUID(element)) {
195
+ categories.push('uuid')
196
+ }
197
+
155
198
  if (categories.length) return { categories }
156
199
  }
157
200
 
@@ -174,10 +217,9 @@ function _handler(req) {
174
217
 
175
218
  let template
176
219
  if (draft) {
177
- // draft -> filter virtual only
178
- template = getTemplate('db-virtual', this, target, { pick: _pickVirtual })
220
+ template = getTemplate('db-input-draft', this, target, { pick: _pickDraft })
179
221
  } else {
180
- template = getTemplate('db-input', this, target, { pick: _pick })
222
+ template = getTemplate('db-input-crud', this, target, { pick: _pickCRUD })
181
223
  }
182
224
 
183
225
  if (template.elements.size === 0) return