@cap-js/db-service 1.11.0 → 1.12.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.
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.12.0](https://github.com/cap-js/cds-dbs/compare/db-service-v1.11.0...db-service-v1.12.0) (2024-07-25)
8
+
9
+
10
+ ### Fixed
11
+
12
+ *** add placeholder for string values ([#733](https://github.com/cap-js/cds-dbs/issues/733)) ([8136a45](https://github.com/cap-js/cds-dbs/commit/8136a4526f596b67932908b8ab1336cb052100f3))
13
+ *** for aggregated `expand` always set explicit alias ([#739](https://github.com/cap-js/cds-dbs/issues/739)) ([53a8075](https://github.com/cap-js/cds-dbs/commit/53a8075a609666a896296401a28b6183ff5aa487)), closes [#708](https://github.com/cap-js/cds-dbs/issues/708)
14
+ *** quotations in vals ([#754](https://github.com/cap-js/cds-dbs/issues/754)) ([94d8e97](https://github.com/cap-js/cds-dbs/commit/94d8e977ed00776ff494287ce505d6b7e8017d2e))
15
+
16
+
17
+ ### Changed
18
+
19
+ *** generic-pool as real dep ([#750](https://github.com/cap-js/cds-dbs/issues/750)) ([b50c907](https://github.com/cap-js/cds-dbs/commit/b50c907880455a41a73826a736bc17ca17e5b9ae))
20
+
21
+
7
22
  ## [1.11.0](https://github.com/cap-js/cds-dbs/compare/db-service-v1.10.3...db-service-v1.11.0) (2024-07-08)
8
23
 
9
24
 
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # CDS base database service
2
2
 
3
- Welcome to the new base database service for [SAP Cloud Application Programming Model](https://cap.cloud.sap) Node.js, based on new, streamlined database architecture.
3
+ Welcome to the base database service for [SAP Cloud Application Programming Model](https://cap.cloud.sap) Node.js. This service forms the core of all supported databases and is the base of our streamlined database architecture.
4
4
 
5
- Find documentation at https://cap.cloud.sap/docs/guides/databases
5
+ Find documentation at <https://cap.cloud.sap/docs/guides/databases>
6
6
 
7
7
  ## Support
8
8
 
@@ -23,4 +23,4 @@ We as members, contributors, and leaders pledge to make participation in our com
23
23
 
24
24
  ## Licensing
25
25
 
26
- Copyright 2023 SAP SE or an SAP affiliate company and cds-dbs contributors. Please see our [LICENSE](LICENSE) for copyright and license information. Detailed information including third-party components and their licensing/copyright information is available [via the REUSE tool](https://api.reuse.software/info/github.com/cap-js/cds-dbs).
26
+ Copyright 2024 SAP SE or an SAP affiliate company and cds-dbs contributors. Please see our [LICENSE](LICENSE) for copyright and license information. Detailed information including third-party components and their licensing/copyright information is available [via the REUSE tool](https://api.reuse.software/info/github.com/cap-js/cds-dbs).
package/lib/SQLService.js CHANGED
@@ -371,10 +371,8 @@ class SQLService extends DatabaseService {
371
371
  !q.SELECT?.from?.join &&
372
372
  !q.SELECT?.from?.SELECT &&
373
373
  !this.model?.definitions[_target_name4(q)]
374
- ) {
375
- return _unquirked(q)
376
- }
377
- return cqn4sql(q, this.model)
374
+ ) return q
375
+ else return cqn4sql(q, this.model)
378
376
  }
379
377
 
380
378
  /**
@@ -464,18 +462,6 @@ const _target_name4 = q => {
464
462
  return first.id || first
465
463
  }
466
464
 
467
- const _unquirked = !cds.env.ql.quirks_mode ? q => q : q => {
468
- if (!q) return q
469
- else if (typeof q.SELECT?.from === 'string') q.SELECT.from = { ref: [q.SELECT.from] }
470
- else if (typeof q.INSERT?.into === 'string') q.INSERT.into = { ref: [q.INSERT.into] }
471
- else if (typeof q.UPSERT?.into === 'string') q.UPSERT.into = { ref: [q.UPSERT.into] }
472
- else if (typeof q.UPDATE?.entity === 'string') q.UPDATE.entity = { ref: [q.UPDATE.entity] }
473
- else if (typeof q.DELETE?.from === 'string') q.DELETE.from = { ref: [q.DELETE.from] }
474
- else if (typeof q.CREATE?.entity === 'string') q.CREATE.entity = { ref: [q.CREATE.entity] }
475
- else if (typeof q.DROP?.entity === 'string') q.DROP.entity = { ref: [q.DROP.entity] }
476
- return q
477
- }
478
-
479
465
  const sqls = new (class extends SQLService {
480
466
  get factory() {
481
467
  return null
@@ -1,4 +1,4 @@
1
- const { createPool } = require('@sap/cds-foss').pool
1
+ const { createPool } = require('generic-pool')
2
2
 
3
3
  class ConnectionPool {
4
4
  constructor(factory, tenant) {
@@ -23,7 +23,8 @@ const StandardFunctions = {
23
23
  search: function (ref, arg) {
24
24
  if (!('val' in arg)) throw new Error(`Only single value arguments are allowed for $search`)
25
25
  // only apply first search term, rest is ignored
26
- arg.val = arg.__proto__.val = arg.val.split(' ')[0].replace(/"/g, '')
26
+ const sub= /("")|("(?:[^"]|\\")*(?:[^\\]|\\\\)")|(\S*)/.exec(arg.val)
27
+ arg.val = arg.__proto__.val = (sub[2] ? JSON.parse(sub[2]) : sub[3]) || ''
27
28
  const refs = ref.list || [ref],
28
29
  { toString } = ref
29
30
  return '(' + refs.map(ref2 => this.contains(this.tolower(toString(ref2)), this.tolower(arg))).join(' or ') + ')'
package/lib/cqn2sql.js CHANGED
@@ -85,7 +85,7 @@ class CQN2SQLRenderer {
85
85
  const sanitize_values = process.env.NODE_ENV === 'production' && cds.env.log.sanitize_values !== false
86
86
  DEBUG?.(
87
87
  this.sql,
88
- sanitize_values && (this.entries || this.values?.length > 0) ? ['***'] : this.entries || this.values,
88
+ ...(sanitize_values && (this.entries || this.values?.length > 0) ? ['***'] : this.entries || this.values || []),
89
89
  )
90
90
  return this
91
91
  }
@@ -311,8 +311,8 @@ class CQN2SQLRenderer {
311
311
  */
312
312
  column_expr(x, q) {
313
313
  if (x === '*') return '*'
314
-
315
- let sql = this.expr({ param: false, __proto__: x })
314
+
315
+ let sql = x.param !== true && typeof x.val === 'number' ? this.expr({ param: false, __proto__: x }): this.expr(x)
316
316
  let alias = this.column_alias4(x, q)
317
317
  if (alias) sql += ' as ' + this.quote(alias)
318
318
  return sql
@@ -508,6 +508,7 @@ class CQN2SQLRenderer {
508
508
  } else {
509
509
  const stream = Readable.from(this.INSERT_entries_stream(INSERT.entries), { objectMode: false })
510
510
  stream.type = 'json'
511
+ stream._raw = INSERT.entries
511
512
  this.entries = [[...this.values, stream]]
512
513
  }
513
514
 
@@ -652,6 +653,7 @@ class CQN2SQLRenderer {
652
653
  } else {
653
654
  const stream = Readable.from(this.INSERT_rows_stream(INSERT.rows), { objectMode: false })
654
655
  stream.type = 'json'
656
+ stream._raw = INSERT.rows
655
657
  this.entries = [[...this.values, stream]]
656
658
  }
657
659
 
@@ -1080,6 +1082,8 @@ Buffer.prototype.toJSON = function () {
1080
1082
  return this.toString('base64')
1081
1083
  }
1082
1084
 
1085
+ Readable.prototype[require('node:util').inspect.custom] = Readable.prototype.toJSON = function () { return this._raw || `[object ${this.constructor.name}]` }
1086
+
1083
1087
  const ObjectKeys = o => (o && [...ObjectKeys(o.__proto__), ...Object.keys(o)]) || []
1084
1088
  const _managed = {
1085
1089
  '$user.id': '$user.id',
package/lib/cqn4sql.js CHANGED
@@ -432,7 +432,7 @@ function cqn4sql(originalQuery, model) {
432
432
  return
433
433
  }
434
434
 
435
- const tableAlias = getQuerySourceName(col)
435
+ const tableAlias = getTableAlias(col)
436
436
  // re-adjust usage of implicit alias in subquery
437
437
  if (col.$refLinks[0].definition.kind === 'entity' && col.ref[0] !== tableAlias) {
438
438
  col.ref[0] = tableAlias
@@ -725,7 +725,7 @@ function cqn4sql(originalQuery, model) {
725
725
  res.push(...getColumnsForWildcard(exclude, replace, col.as))
726
726
  } else
727
727
  res.push(
728
- ...getFlatColumnsFor(col, { columnAlias: col.as, tableAlias: getQuerySourceName(col) }, [], {
728
+ ...getFlatColumnsFor(col, { columnAlias: col.as, tableAlias: getTableAlias(col) }, [], {
729
729
  exclude,
730
730
  replace,
731
731
  }),
@@ -895,11 +895,12 @@ function cqn4sql(originalQuery, model) {
895
895
  )
896
896
  }
897
897
 
898
- const columnCopy = Object.create(groupByRef)
899
- if (expand.as) {
900
- columnCopy.as = expand.as
901
- }
902
- const res = getFlatColumnsFor(columnCopy, { tableAlias: getQuerySourceName(columnCopy) })
898
+ const copy = Object.create(groupByRef)
899
+ // always alias for this special case, so that they nested element names match the expected result structure
900
+ // otherwise we'd get `author { <outer>.author_ID }`, but we need `author { <outer>.author_ID as ID }`
901
+ copy.as = expand.as || expand.ref.at(-1)
902
+ const tableAlias = getTableAlias(copy)
903
+ const res = getFlatColumnsFor(copy, { tableAlias })
903
904
  res.forEach(c => {
904
905
  elements[c.as || c.ref.at(-1)] = c.element
905
906
  })
@@ -961,7 +962,7 @@ function cqn4sql(originalQuery, model) {
961
962
  referredCol.nulls = col.nulls
962
963
  col = referredCol
963
964
  if (definition.kind === 'element') {
964
- tableAlias = getQuerySourceName(col)
965
+ tableAlias = getTableAlias(col)
965
966
  } else {
966
967
  // we must replace the reference with the underlying expression
967
968
  const { val, func, args, xpr } = col
@@ -973,7 +974,7 @@ function cqn4sql(originalQuery, model) {
973
974
  }
974
975
  }
975
976
  } else {
976
- tableAlias = getQuerySourceName(col) // do not prepend TA if orderBy column addresses element of query
977
+ tableAlias = getTableAlias(col) // do not prepend TA if orderBy column addresses element of query
977
978
  }
978
979
  const leaf = col.$refLinks[col.$refLinks.length - 1].definition
979
980
  if (leaf.virtual === true) continue // already in getFlatColumnForElement
@@ -992,12 +993,9 @@ function cqn4sql(originalQuery, model) {
992
993
  if (inOrderBy && flatColumns.length > 1)
993
994
  throw new Error(`"${getFullName(leaf)}" can't be used in order by as it expands to multiple fields`)
994
995
  flatColumns.forEach(fc => {
995
- if (col.nulls)
996
- fc.nulls = col.nulls
997
- if (col.sort)
998
- fc.sort = col.sort
999
- if (fc.as)
1000
- delete fc.as
996
+ if (col.nulls) fc.nulls = col.nulls
997
+ if (col.sort) fc.sort = col.sort
998
+ if (fc.as) delete fc.as
1001
999
  })
1002
1000
  res.push(...flatColumns)
1003
1001
  } else {
@@ -1156,7 +1154,7 @@ function cqn4sql(originalQuery, model) {
1156
1154
  if (column.val || column.func || column.SELECT) return [column]
1157
1155
 
1158
1156
  const structsAreUnfoldedAlready = model.meta.unfolded?.includes('structs')
1159
- let { baseName, columnAlias, tableAlias } = names
1157
+ let { baseName, columnAlias = column.as, tableAlias } = names
1160
1158
  const { exclude, replace } = excludeAndReplace || {}
1161
1159
  const { $refLinks, flatName, isJoinRelevant } = column
1162
1160
  let leafAssoc
@@ -1199,7 +1197,7 @@ function cqn4sql(originalQuery, model) {
1199
1197
  baseName = getFullName(replacedBy.$refLinks?.[replacedBy.$refLinks.length - 2].definition)
1200
1198
  if (replacedBy.isJoinRelevant)
1201
1199
  // we need to provide the correct table alias
1202
- tableAlias = getQuerySourceName(replacedBy)
1200
+ tableAlias = getTableAlias(replacedBy)
1203
1201
 
1204
1202
  if (replacedBy.expand) return [{ as: baseName }]
1205
1203
 
@@ -1488,7 +1486,7 @@ function cqn4sql(originalQuery, model) {
1488
1486
  // hence we need to ignore the alias of the `$baseLink`
1489
1487
  const lastAssoc =
1490
1488
  token.isJoinRelevant && [...token.$refLinks].reverse().find(l => l.definition.isAssociation)
1491
- const tableAlias = getQuerySourceName(token, (!lastAssoc?.onlyForeignKeyAccess && lastAssoc) || $baseLink)
1489
+ const tableAlias = getTableAlias(token, (!lastAssoc?.onlyForeignKeyAccess && lastAssoc) || $baseLink)
1492
1490
  if ((!$baseLink || lastAssoc) && token.isJoinRelevant) {
1493
1491
  let name = calculateElementName(token, getFullName)
1494
1492
  result.ref = [tableAlias, name]
@@ -1585,7 +1583,7 @@ function cqn4sql(originalQuery, model) {
1585
1583
  if (!def.$refLinks) return def
1586
1584
  const leaf = def.$refLinks[def.$refLinks.length - 1]
1587
1585
  const first = def.$refLinks[0]
1588
- const tableAlias = getQuerySourceName(
1586
+ const tableAlias = getTableAlias(
1589
1587
  def,
1590
1588
  def.ref.length > 1 && first.definition.isAssociation ? first : $baseLink,
1591
1589
  )
@@ -2194,7 +2192,7 @@ function cqn4sql(originalQuery, model) {
2194
2192
  * the combined elements of the query
2195
2193
  * @returns the source name which can be used to address the node
2196
2194
  */
2197
- function getQuerySourceName(node, $baseLink = null) {
2195
+ function getTableAlias(node, $baseLink = null) {
2198
2196
  if (!node || !node.$refLinks || !node.ref) {
2199
2197
  throw new Error('Invalid node')
2200
2198
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js/db-service",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
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": {
@@ -24,6 +24,9 @@
24
24
  "scripts": {
25
25
  "test": "jest --silent"
26
26
  },
27
+ "dependencies": {
28
+ "generic-pool": "^3.9.0"
29
+ },
27
30
  "peerDependencies": {
28
31
  "@sap/cds": ">=7.9"
29
32
  },