@cap-js/db-service 1.17.0 → 1.17.1

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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,14 @@
4
4
  - The format is based on [Keep a Changelog](http://keepachangelog.com/).
5
5
  - This project adheres to [Semantic Versioning](http://semver.org/).
6
6
 
7
+ ## [1.17.1](https://github.com/cap-js/cds-dbs/compare/db-service-v1.17.0...db-service-v1.17.1) (2025-02-04)
8
+
9
+
10
+ ### Fixed
11
+
12
+ * deep update resulting in deep delete of sub-element ([#1006](https://github.com/cap-js/cds-dbs/issues/1006)) ([ef2f817](https://github.com/cap-js/cds-dbs/commit/ef2f8175df6fc7076fa8a9290e1863f44d267d8d))
13
+ * nested $self reference to other column ([#1009](https://github.com/cap-js/cds-dbs/issues/1009)) ([41a76d8](https://github.com/cap-js/cds-dbs/commit/41a76d89a884ac8266ccbd2d087af435e8f26ccb))
14
+
7
15
  ## [1.17.0](https://github.com/cap-js/cds-dbs/compare/db-service-v1.16.2...db-service-v1.17.0) (2025-01-28)
8
16
 
9
17
 
@@ -51,7 +51,7 @@ async function onDeep(req, next) {
51
51
  // - deletes never trigger unique constraints, but can prevent them -> execute first
52
52
  // - updates can trigger and prevent unique constraints -> execute second
53
53
  // - inserts can only trigger unique constraints -> execute last
54
- await Promise.all(Array.from(queries.deletes.values()).map(query => this.onSIMPLE({ query })))
54
+ await Promise.all(Array.from(queries.deletes.values()).map(query => this.onDELETE({ query, target: query._target })))
55
55
  await Promise.all(queries.updates.map(query => this.onUPDATE({ query })))
56
56
 
57
57
  const rootQuery = queries.inserts.get(ROOT)
@@ -207,12 +207,12 @@ function infer(originalQuery, model) {
207
207
  if (as === undefined) cds.error`Expecting expression to have an alias name`
208
208
  if (queryElements[as]) cds.error`Duplicate definition of element “${as}”`
209
209
  if (col.xpr || col.SELECT) {
210
- queryElements[as] = getElementForXprOrSubquery(col, queryElements)
210
+ queryElements[as] = getElementForXprOrSubquery(col, queryElements, dollarSelfRefs)
211
211
  }
212
212
  if (col.func) {
213
213
  if (col.args) {
214
214
  // {func}.args are optional
215
- applyToFunctionArgs(col.args, inferArg, [false])
215
+ applyToFunctionArgs(col.args, inferArg, [false, null, {dollarSelfRefs}])
216
216
  }
217
217
  queryElements[as] = getElementForCast(col)
218
218
  }
@@ -287,7 +287,7 @@ function infer(originalQuery, model) {
287
287
  if (having) walkTokenStream(having)
288
288
  if (_.with)
289
289
  // consider UPDATE.with
290
- Object.values(_.with).forEach(val => inferArg(val, queryElements, null, { inExpr: true }))
290
+ Object.values(_.with).forEach(val => inferArg(val, queryElements, null, { inXpr: true }))
291
291
 
292
292
  return queryElements
293
293
 
@@ -299,7 +299,7 @@ function infer(originalQuery, model) {
299
299
  *
300
300
  * @param {array} tokenStream
301
301
  */
302
- function walkTokenStream(tokenStream, inExpr = false) {
302
+ function walkTokenStream(tokenStream, inXpr = false) {
303
303
  let skipJoins
304
304
  const processToken = t => {
305
305
  if (t === 'exists') {
@@ -309,7 +309,7 @@ function infer(originalQuery, model) {
309
309
  // don't miss an exists within an expression
310
310
  t.xpr.forEach(processToken)
311
311
  } else {
312
- inferArg(t, queryElements, null, { inExists: skipJoins, inExpr, inQueryModifier: true })
312
+ inferArg(t, queryElements, null, { inExists: skipJoins, inXpr, inQueryModifier: true })
313
313
  skipJoins = false
314
314
  }
315
315
  }
@@ -329,11 +329,12 @@ function infer(originalQuery, model) {
329
329
  const unprocessedColumns = []
330
330
 
331
331
  for (const currentDollarSelfColumn of dollarSelfColumns) {
332
- const { ref } = currentDollarSelfColumn
332
+ const { ref, inXpr } = currentDollarSelfColumn
333
333
  const stepToFind = ref[1]
334
334
 
335
335
  const referencesOtherDollarSelfColumn = dollarSelfColumns.find(
336
336
  otherDollarSelfCol =>
337
+ !(stepToFind in queryElements) &&
337
338
  otherDollarSelfCol !== currentDollarSelfColumn &&
338
339
  (otherDollarSelfCol.as
339
340
  ? stepToFind === otherDollarSelfCol.as
@@ -343,7 +344,7 @@ function infer(originalQuery, model) {
343
344
  if (referencesOtherDollarSelfColumn) {
344
345
  unprocessedColumns.push(currentDollarSelfColumn)
345
346
  } else {
346
- handleRef(currentDollarSelfColumn)
347
+ handleRef(currentDollarSelfColumn, inXpr)
347
348
  }
348
349
  }
349
350
 
@@ -351,8 +352,8 @@ function infer(originalQuery, model) {
351
352
  } while (dollarSelfColumns.length > 0)
352
353
  }
353
354
 
354
- function handleRef(col) {
355
- inferArg(col, queryElements)
355
+ function handleRef(col, inXpr) {
356
+ inferArg(col, queryElements, null, { inXpr })
356
357
  const { definition } = col.$refLinks[col.$refLinks.length - 1]
357
358
  if (col.cast)
358
359
  // final type overwritten -> element not visible anymore
@@ -379,7 +380,7 @@ function infer(originalQuery, model) {
379
380
  * @param {object} [context={}] - Contextual information for element inference.
380
381
  * @param {boolean} [context.inExists=false] - Flag to control the creation of joins for non-association path traversals.
381
382
  * for `exists <assoc>` paths we do not need to create joins for path expressions as they are part of the semi-joined subquery.
382
- * @param {boolean} [context.inExpr=false] - Flag to signal whether the element is part of an expression.
383
+ * @param {boolean} [context.inXpr=false] - Flag to signal whether the element is part of an expression.
383
384
  * Used to ignore non-persisted elements.
384
385
  * @param {boolean} [context.inNestedProjection=false] - Flag to signal whether the element is part of a nested projection.
385
386
  *
@@ -401,11 +402,11 @@ function infer(originalQuery, model) {
401
402
  */
402
403
 
403
404
  function inferArg(arg, queryElements = null, $baseLink = null, context = {}) {
404
- const { inExists, inExpr, inCalcElement, baseColumn, inInfixFilter, inQueryModifier, inFrom } = context
405
+ const { inExists, inXpr, inCalcElement, baseColumn, inInfixFilter, inQueryModifier, inFrom, dollarSelfRefs } = context
405
406
  if (arg.param || arg.SELECT) return // parameter references are only resolved into values on execution e.g. :val, :1 or ?
406
407
  if (arg.args) applyToFunctionArgs(arg.args, inferArg, [null, $baseLink, context])
407
408
  if (arg.list) arg.list.forEach(arg => inferArg(arg, null, $baseLink, context))
408
- if (arg.xpr) arg.xpr.forEach(token => inferArg(token, queryElements, $baseLink, { ...context, inExpr: true })) // e.g. function in expression
409
+ if (arg.xpr) arg.xpr.forEach(token => inferArg(token, queryElements, $baseLink, { ...context, inXpr: true })) // e.g. function in expression
409
410
 
410
411
  if (!arg.ref) {
411
412
  if (arg.expand && queryElements) queryElements[arg.as] = resolveExpand(arg)
@@ -426,6 +427,11 @@ function infer(originalQuery, model) {
426
427
  firstStepIsSelf = !firstStepIsTableAlias && arg.ref.length > 1 && ['$self', '$projection'].includes(arg.ref[0])
427
428
  expandOnTableAlias = arg.ref.length === 1 && arg.ref[0] in sources && (arg.expand || arg.inline)
428
429
  }
430
+ if(dollarSelfRefs && firstStepIsSelf) {
431
+ Object.defineProperty(arg, 'inXpr', { value: true, writable: true })
432
+ dollarSelfRefs.push(arg)
433
+ return
434
+ }
429
435
  const nameSegments = []
430
436
  // if a (segment) of a (structured) foreign key is renamed, we must not include
431
437
  // the aliased ref segments into the name of the final foreign key which is e.g. used in
@@ -564,7 +570,7 @@ function infer(originalQuery, model) {
564
570
  } else if (token.ref || token.xpr || token.list) {
565
571
  inferArg(token, false, arg.$refLinks[i], {
566
572
  inExists: skipJoinsForFilter || inExists,
567
- inExpr: !!token.xpr,
573
+ inXpr: !!token.xpr,
568
574
  inInfixFilter: true,
569
575
  inFrom,
570
576
  })
@@ -573,7 +579,7 @@ function infer(originalQuery, model) {
573
579
  applyToFunctionArgs(token.args, inferArg, [
574
580
  false,
575
581
  arg.$refLinks[i],
576
- { inExists: skipJoinsForFilter || inExists, inExpr: true, inInfixFilter: true, inFrom },
582
+ { inExists: skipJoinsForFilter || inExists, inXpr: true, inInfixFilter: true, inFrom },
577
583
  ])
578
584
  }
579
585
  }
@@ -641,7 +647,7 @@ function infer(originalQuery, model) {
641
647
  }
642
648
  }
643
649
  const leafArt = arg.$refLinks[arg.$refLinks.length - 1].definition
644
- const virtual = (leafArt.virtual || !isPersisted) && !inExpr
650
+ const virtual = (leafArt.virtual || !isPersisted) && !inXpr
645
651
  // check if we need to merge the column `ref` into the join tree of the query
646
652
  if (!inFrom && !inExists && !virtual && !inCalcElement) {
647
653
  // for a ref inside an `inline` we need to consider the column `ref` which has the `inline` prop
@@ -658,7 +664,7 @@ function infer(originalQuery, model) {
658
664
  }
659
665
 
660
666
  function insertIntoQueryElements() {
661
- return queryElements && !inExpr && !inInfixFilter && !inQueryModifier
667
+ return queryElements && !inXpr && !inInfixFilter && !inQueryModifier
662
668
  }
663
669
 
664
670
  /**
@@ -686,7 +692,7 @@ function infer(originalQuery, model) {
686
692
  }
687
693
  let elements = {}
688
694
  inline.forEach(inlineCol => {
689
- inferArg(inlineCol, null, $leafLink, { inExpr: true, baseColumn: col })
695
+ inferArg(inlineCol, null, $leafLink, { inXpr: true, baseColumn: col })
690
696
  if (inlineCol === '*') {
691
697
  const wildCardElements = {}
692
698
  // either the `.elements´ of the struct or the `.elements` of the assoc target
@@ -762,7 +768,7 @@ function infer(originalQuery, model) {
762
768
  if (e === '*') {
763
769
  elements = { ...elements, ...$leafLink.definition.elements }
764
770
  } else {
765
- inferArg(e, false, $leafLink, { inExpr: true })
771
+ inferArg(e, false, $leafLink, { inXpr: true })
766
772
  if (e.expand) elements[e.as || e.flatName] = resolveExpand(e)
767
773
  if (e.inline) elements = { ...elements, ...resolveInline(e) }
768
774
  else elements[e.as || e.flatName] = e.$refLinks ? e.$refLinks[e.$refLinks.length - 1].definition : e
@@ -1008,7 +1014,7 @@ function infer(originalQuery, model) {
1008
1014
  * @param {object} col
1009
1015
  * @returns object
1010
1016
  */
1011
- function getElementForXprOrSubquery(col, queryElements) {
1017
+ function getElementForXprOrSubquery(col, queryElements, dollarSelfRefs) {
1012
1018
  const { xpr } = col
1013
1019
  let skipJoins = false
1014
1020
  xpr?.forEach(token => {
@@ -1016,7 +1022,7 @@ function infer(originalQuery, model) {
1016
1022
  // no joins for infix filters along `exists <path>`
1017
1023
  skipJoins = true
1018
1024
  } else {
1019
- inferArg(token, queryElements, null, { inExists: skipJoins, inExpr: true })
1025
+ inferArg(token, queryElements, null, { inExists: skipJoins, inXpr: true, dollarSelfRefs })
1020
1026
  skipJoins = false
1021
1027
  }
1022
1028
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js/db-service",
3
- "version": "1.17.0",
3
+ "version": "1.17.1",
4
4
  "description": "CDS base database service",
5
5
  "homepage": "https://github.com/cap-js/cds-dbs/tree/main/db-service#cds-base-database-service",
6
6
  "repository": {