@cap-js/db-service 1.17.0 → 1.17.2

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,21 @@
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.2](https://github.com/cap-js/cds-dbs/compare/db-service-v1.17.1...db-service-v1.17.2) (2025-02-09)
8
+
9
+
10
+ ### Fixed
11
+
12
+ * replace polynomial regex with simple string op ([#1015](https://github.com/cap-js/cds-dbs/issues/1015)) ([3fe6e6b](https://github.com/cap-js/cds-dbs/commit/3fe6e6b7f7aaf5aafb811acf2838cd1da30052a8))
13
+
14
+ ## [1.17.1](https://github.com/cap-js/cds-dbs/compare/db-service-v1.17.0...db-service-v1.17.1) (2025-02-04)
15
+
16
+
17
+ ### Fixed
18
+
19
+ * 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))
20
+ * 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))
21
+
7
22
  ## [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
23
 
9
24
 
@@ -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)
@@ -116,9 +116,11 @@ function infer(originalQuery, model) {
116
116
 
117
117
  inferArg(from, null, null, { inFrom: true })
118
118
  const alias =
119
- from.uniqueSubqueryAlias ||
120
- from.as ||
121
- (ref.length === 1 ? first.match(/[^.]+$/)[0] : ref[ref.length - 1].id || ref[ref.length - 1])
119
+ from.uniqueSubqueryAlias ||
120
+ from.as ||
121
+ (ref.length === 1
122
+ ? first.substring(first.lastIndexOf('.') + 1)
123
+ : (ref.at(-1).id || ref.at(-1)));
122
124
  if (alias in querySources) throw new Error(`Duplicate alias "${alias}"`)
123
125
  querySources[alias] = { definition: target, args }
124
126
  const last = from.$refLinks.at(-1)
@@ -134,7 +136,7 @@ function infer(originalQuery, model) {
134
136
  } else if (typeof from === 'string') {
135
137
  // TODO: Create unique alias, what about duplicates?
136
138
  const definition = getDefinition(from) || cds.error`"${from}" not found in the definitions of your model`
137
- querySources[/([^.]*)$/.exec(from)[0]] = { definition }
139
+ querySources[from.substring(from.lastIndexOf('.') + 1)] = { definition }
138
140
  } else if (from.SET) {
139
141
  infer(from, model)
140
142
  }
@@ -207,12 +209,12 @@ function infer(originalQuery, model) {
207
209
  if (as === undefined) cds.error`Expecting expression to have an alias name`
208
210
  if (queryElements[as]) cds.error`Duplicate definition of element “${as}”`
209
211
  if (col.xpr || col.SELECT) {
210
- queryElements[as] = getElementForXprOrSubquery(col, queryElements)
212
+ queryElements[as] = getElementForXprOrSubquery(col, queryElements, dollarSelfRefs)
211
213
  }
212
214
  if (col.func) {
213
215
  if (col.args) {
214
216
  // {func}.args are optional
215
- applyToFunctionArgs(col.args, inferArg, [false])
217
+ applyToFunctionArgs(col.args, inferArg, [false, null, {dollarSelfRefs}])
216
218
  }
217
219
  queryElements[as] = getElementForCast(col)
218
220
  }
@@ -287,7 +289,7 @@ function infer(originalQuery, model) {
287
289
  if (having) walkTokenStream(having)
288
290
  if (_.with)
289
291
  // consider UPDATE.with
290
- Object.values(_.with).forEach(val => inferArg(val, queryElements, null, { inExpr: true }))
292
+ Object.values(_.with).forEach(val => inferArg(val, queryElements, null, { inXpr: true }))
291
293
 
292
294
  return queryElements
293
295
 
@@ -299,7 +301,7 @@ function infer(originalQuery, model) {
299
301
  *
300
302
  * @param {array} tokenStream
301
303
  */
302
- function walkTokenStream(tokenStream, inExpr = false) {
304
+ function walkTokenStream(tokenStream, inXpr = false) {
303
305
  let skipJoins
304
306
  const processToken = t => {
305
307
  if (t === 'exists') {
@@ -309,7 +311,7 @@ function infer(originalQuery, model) {
309
311
  // don't miss an exists within an expression
310
312
  t.xpr.forEach(processToken)
311
313
  } else {
312
- inferArg(t, queryElements, null, { inExists: skipJoins, inExpr, inQueryModifier: true })
314
+ inferArg(t, queryElements, null, { inExists: skipJoins, inXpr, inQueryModifier: true })
313
315
  skipJoins = false
314
316
  }
315
317
  }
@@ -329,11 +331,12 @@ function infer(originalQuery, model) {
329
331
  const unprocessedColumns = []
330
332
 
331
333
  for (const currentDollarSelfColumn of dollarSelfColumns) {
332
- const { ref } = currentDollarSelfColumn
334
+ const { ref, inXpr } = currentDollarSelfColumn
333
335
  const stepToFind = ref[1]
334
336
 
335
337
  const referencesOtherDollarSelfColumn = dollarSelfColumns.find(
336
338
  otherDollarSelfCol =>
339
+ !(stepToFind in queryElements) &&
337
340
  otherDollarSelfCol !== currentDollarSelfColumn &&
338
341
  (otherDollarSelfCol.as
339
342
  ? stepToFind === otherDollarSelfCol.as
@@ -343,7 +346,7 @@ function infer(originalQuery, model) {
343
346
  if (referencesOtherDollarSelfColumn) {
344
347
  unprocessedColumns.push(currentDollarSelfColumn)
345
348
  } else {
346
- handleRef(currentDollarSelfColumn)
349
+ handleRef(currentDollarSelfColumn, inXpr)
347
350
  }
348
351
  }
349
352
 
@@ -351,8 +354,8 @@ function infer(originalQuery, model) {
351
354
  } while (dollarSelfColumns.length > 0)
352
355
  }
353
356
 
354
- function handleRef(col) {
355
- inferArg(col, queryElements)
357
+ function handleRef(col, inXpr) {
358
+ inferArg(col, queryElements, null, { inXpr })
356
359
  const { definition } = col.$refLinks[col.$refLinks.length - 1]
357
360
  if (col.cast)
358
361
  // final type overwritten -> element not visible anymore
@@ -379,7 +382,7 @@ function infer(originalQuery, model) {
379
382
  * @param {object} [context={}] - Contextual information for element inference.
380
383
  * @param {boolean} [context.inExists=false] - Flag to control the creation of joins for non-association path traversals.
381
384
  * 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.
385
+ * @param {boolean} [context.inXpr=false] - Flag to signal whether the element is part of an expression.
383
386
  * Used to ignore non-persisted elements.
384
387
  * @param {boolean} [context.inNestedProjection=false] - Flag to signal whether the element is part of a nested projection.
385
388
  *
@@ -401,11 +404,11 @@ function infer(originalQuery, model) {
401
404
  */
402
405
 
403
406
  function inferArg(arg, queryElements = null, $baseLink = null, context = {}) {
404
- const { inExists, inExpr, inCalcElement, baseColumn, inInfixFilter, inQueryModifier, inFrom } = context
407
+ const { inExists, inXpr, inCalcElement, baseColumn, inInfixFilter, inQueryModifier, inFrom, dollarSelfRefs } = context
405
408
  if (arg.param || arg.SELECT) return // parameter references are only resolved into values on execution e.g. :val, :1 or ?
406
409
  if (arg.args) applyToFunctionArgs(arg.args, inferArg, [null, $baseLink, context])
407
410
  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
411
+ if (arg.xpr) arg.xpr.forEach(token => inferArg(token, queryElements, $baseLink, { ...context, inXpr: true })) // e.g. function in expression
409
412
 
410
413
  if (!arg.ref) {
411
414
  if (arg.expand && queryElements) queryElements[arg.as] = resolveExpand(arg)
@@ -426,6 +429,11 @@ function infer(originalQuery, model) {
426
429
  firstStepIsSelf = !firstStepIsTableAlias && arg.ref.length > 1 && ['$self', '$projection'].includes(arg.ref[0])
427
430
  expandOnTableAlias = arg.ref.length === 1 && arg.ref[0] in sources && (arg.expand || arg.inline)
428
431
  }
432
+ if(dollarSelfRefs && firstStepIsSelf) {
433
+ Object.defineProperty(arg, 'inXpr', { value: true, writable: true })
434
+ dollarSelfRefs.push(arg)
435
+ return
436
+ }
429
437
  const nameSegments = []
430
438
  // if a (segment) of a (structured) foreign key is renamed, we must not include
431
439
  // the aliased ref segments into the name of the final foreign key which is e.g. used in
@@ -564,7 +572,7 @@ function infer(originalQuery, model) {
564
572
  } else if (token.ref || token.xpr || token.list) {
565
573
  inferArg(token, false, arg.$refLinks[i], {
566
574
  inExists: skipJoinsForFilter || inExists,
567
- inExpr: !!token.xpr,
575
+ inXpr: !!token.xpr,
568
576
  inInfixFilter: true,
569
577
  inFrom,
570
578
  })
@@ -573,7 +581,7 @@ function infer(originalQuery, model) {
573
581
  applyToFunctionArgs(token.args, inferArg, [
574
582
  false,
575
583
  arg.$refLinks[i],
576
- { inExists: skipJoinsForFilter || inExists, inExpr: true, inInfixFilter: true, inFrom },
584
+ { inExists: skipJoinsForFilter || inExists, inXpr: true, inInfixFilter: true, inFrom },
577
585
  ])
578
586
  }
579
587
  }
@@ -641,7 +649,7 @@ function infer(originalQuery, model) {
641
649
  }
642
650
  }
643
651
  const leafArt = arg.$refLinks[arg.$refLinks.length - 1].definition
644
- const virtual = (leafArt.virtual || !isPersisted) && !inExpr
652
+ const virtual = (leafArt.virtual || !isPersisted) && !inXpr
645
653
  // check if we need to merge the column `ref` into the join tree of the query
646
654
  if (!inFrom && !inExists && !virtual && !inCalcElement) {
647
655
  // for a ref inside an `inline` we need to consider the column `ref` which has the `inline` prop
@@ -658,7 +666,7 @@ function infer(originalQuery, model) {
658
666
  }
659
667
 
660
668
  function insertIntoQueryElements() {
661
- return queryElements && !inExpr && !inInfixFilter && !inQueryModifier
669
+ return queryElements && !inXpr && !inInfixFilter && !inQueryModifier
662
670
  }
663
671
 
664
672
  /**
@@ -686,7 +694,7 @@ function infer(originalQuery, model) {
686
694
  }
687
695
  let elements = {}
688
696
  inline.forEach(inlineCol => {
689
- inferArg(inlineCol, null, $leafLink, { inExpr: true, baseColumn: col })
697
+ inferArg(inlineCol, null, $leafLink, { inXpr: true, baseColumn: col })
690
698
  if (inlineCol === '*') {
691
699
  const wildCardElements = {}
692
700
  // either the `.elements´ of the struct or the `.elements` of the assoc target
@@ -762,7 +770,7 @@ function infer(originalQuery, model) {
762
770
  if (e === '*') {
763
771
  elements = { ...elements, ...$leafLink.definition.elements }
764
772
  } else {
765
- inferArg(e, false, $leafLink, { inExpr: true })
773
+ inferArg(e, false, $leafLink, { inXpr: true })
766
774
  if (e.expand) elements[e.as || e.flatName] = resolveExpand(e)
767
775
  if (e.inline) elements = { ...elements, ...resolveInline(e) }
768
776
  else elements[e.as || e.flatName] = e.$refLinks ? e.$refLinks[e.$refLinks.length - 1].definition : e
@@ -1008,7 +1016,7 @@ function infer(originalQuery, model) {
1008
1016
  * @param {object} col
1009
1017
  * @returns object
1010
1018
  */
1011
- function getElementForXprOrSubquery(col, queryElements) {
1019
+ function getElementForXprOrSubquery(col, queryElements, dollarSelfRefs) {
1012
1020
  const { xpr } = col
1013
1021
  let skipJoins = false
1014
1022
  xpr?.forEach(token => {
@@ -1016,7 +1024,7 @@ function infer(originalQuery, model) {
1016
1024
  // no joins for infix filters along `exists <path>`
1017
1025
  skipJoins = true
1018
1026
  } else {
1019
- inferArg(token, queryElements, null, { inExists: skipJoins, inExpr: true })
1027
+ inferArg(token, queryElements, null, { inExists: skipJoins, inXpr: true, dollarSelfRefs })
1020
1028
  skipJoins = false
1021
1029
  }
1022
1030
  })
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.2",
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": {