@cap-js/db-service 1.4.0 → 1.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.
- package/CHANGELOG.md +13 -0
- package/lib/SQLService.js +11 -12
- package/lib/common/DatabaseService.js +14 -1
- package/lib/cqn2sql.js +10 -9
- package/lib/cqn4sql.js +2 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@
|
|
|
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.5.0](https://github.com/cap-js/cds-dbs/compare/db-service-v1.4.0...db-service-v1.5.0) (2023-12-06)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
* Improved connection pool for HANAService ([#349](https://github.com/cap-js/cds-dbs/issues/349)) ([1c284e6](https://github.com/cap-js/cds-dbs/commit/1c284e69cccd76daad52249c0462bc62aa4d11a8))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
* **localized:** `ref`s in subqueries in `from` are translated ([#366](https://github.com/cap-js/cds-dbs/issues/366)) ([cfe4897](https://github.com/cap-js/cds-dbs/commit/cfe489715db0854d30b90b7f13c024e6e90be497))
|
|
18
|
+
* wrong odata count in filter with groupby ([#352](https://github.com/cap-js/cds-dbs/issues/352)) ([70690a1](https://github.com/cap-js/cds-dbs/commit/70690a1a13e72bfbb66f03bf315d3f2d48672bf6))
|
|
19
|
+
|
|
7
20
|
## [1.4.0](https://github.com/cap-js/cds-dbs/compare/db-service-v1.3.2...db-service-v1.4.0) (2023-11-20)
|
|
8
21
|
|
|
9
22
|
|
package/lib/SQLService.js
CHANGED
|
@@ -230,16 +230,15 @@ class SQLService extends DatabaseService {
|
|
|
230
230
|
if (max === undefined || (n < max && (n || !offset))) return n + offset
|
|
231
231
|
}
|
|
232
232
|
// REVISIT: made uppercase count because of HANA reserved word quoting
|
|
233
|
-
const cq =
|
|
234
|
-
|
|
233
|
+
const cq = SELECT.one([{ func: 'count', as: 'COUNT' }]).from(
|
|
234
|
+
cds.ql.clone(query, {
|
|
235
235
|
localized: false,
|
|
236
236
|
expand: false,
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
const { count, COUNT } = await ps.get(values)
|
|
237
|
+
limit: undefined,
|
|
238
|
+
orderBy: undefined,
|
|
239
|
+
}),
|
|
240
|
+
)
|
|
241
|
+
const { count, COUNT } = await this.onSELECT({ query: cq })
|
|
243
242
|
return count ?? COUNT
|
|
244
243
|
}
|
|
245
244
|
|
|
@@ -280,12 +279,12 @@ class SQLService extends DatabaseService {
|
|
|
280
279
|
*/
|
|
281
280
|
cqn2sql(query, values) {
|
|
282
281
|
let q = this.cqn4sql(query)
|
|
283
|
-
if (q.SELECT &&
|
|
282
|
+
if (q.SELECT && 'elements' in q) q.SELECT.expand ??= 'root'
|
|
284
283
|
|
|
285
|
-
let
|
|
286
|
-
if (
|
|
284
|
+
let kind = q.kind || Object.keys(q)[0]
|
|
285
|
+
if (kind in { INSERT: 1, DELETE: 1, UPSERT: 1, UPDATE: 1 } || q.STREAM?.into) {
|
|
287
286
|
q = resolveView(q, this.model, this) // REVISIT: before resolveView was called on flat cqn obtained from cqn4sql -> is it correct to call on original q instead?
|
|
288
|
-
let target = q[
|
|
287
|
+
let target = q[kind]._transitions?.[0].target
|
|
289
288
|
if (target) q.target = target // REVISIT: Why isn't that done in resolveView?
|
|
290
289
|
}
|
|
291
290
|
let cqn2sql = new this.class.CQN2SQL(this)
|
|
@@ -63,6 +63,7 @@ class DatabaseService extends cds.Service {
|
|
|
63
63
|
|
|
64
64
|
// Acquire a pooled connection
|
|
65
65
|
this.dbc = await this.acquire()
|
|
66
|
+
this.dbc.destroy = this.destroy.bind(this)
|
|
66
67
|
|
|
67
68
|
// Begin a session...
|
|
68
69
|
try {
|
|
@@ -110,8 +111,20 @@ class DatabaseService extends cds.Service {
|
|
|
110
111
|
*/
|
|
111
112
|
async release() {
|
|
112
113
|
if (!this.dbc) return
|
|
113
|
-
|
|
114
|
+
const dbc = this.dbc
|
|
114
115
|
this.dbc = undefined
|
|
116
|
+
await this.pool.release(dbc)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Destroys own connection, i.e. tix.dbc, from this.pool
|
|
121
|
+
* This is for subclasses to intercept, if required.
|
|
122
|
+
*/
|
|
123
|
+
async destroy() {
|
|
124
|
+
if (!this.dbc) return
|
|
125
|
+
const dbc = this.dbc
|
|
126
|
+
this.dbc = undefined
|
|
127
|
+
await this.pool.destroy(dbc)
|
|
115
128
|
}
|
|
116
129
|
|
|
117
130
|
// REVISIT: should happen automatically after a configurable time
|
package/lib/cqn2sql.js
CHANGED
|
@@ -62,14 +62,14 @@ class CQN2SQLRenderer {
|
|
|
62
62
|
* @returns {CQN2SQLRenderer|unknown}
|
|
63
63
|
*/
|
|
64
64
|
render(q, vars) {
|
|
65
|
-
const
|
|
65
|
+
const kind = q.kind || Object.keys(q)[0] // SELECT, INSERT, ...
|
|
66
66
|
/**
|
|
67
67
|
* @type {string} the rendered SQL string
|
|
68
68
|
*/
|
|
69
69
|
this.sql = '' // to have it as first property for debugging
|
|
70
70
|
/** @type {unknown[]} */
|
|
71
71
|
this.values = [] // prepare values, filled in by subroutines
|
|
72
|
-
this[
|
|
72
|
+
this[kind]((this.cqn = q)) // actual sql rendering happens here
|
|
73
73
|
if (vars?.length && !this.values?.length) this.values = vars
|
|
74
74
|
const sanitize_values = process.env.NODE_ENV === 'production' && cds.env.log.sanitize_values !== false
|
|
75
75
|
DEBUG?.(
|
|
@@ -208,9 +208,8 @@ class CQN2SQLRenderer {
|
|
|
208
208
|
if (limit) sql += ` LIMIT ${this.limit(limit)}`
|
|
209
209
|
// Expand cannot work without an inferred query
|
|
210
210
|
if (expand) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
sql = this.SELECT_expand(q, sql)
|
|
211
|
+
if ('elements' in q) sql = this.SELECT_expand (q,sql)
|
|
212
|
+
else cds.error`Query was not inferred and includes expand. For which the metadata is missing.`
|
|
214
213
|
}
|
|
215
214
|
return (this.sql = sql)
|
|
216
215
|
}
|
|
@@ -230,10 +229,12 @@ class CQN2SQLRenderer {
|
|
|
230
229
|
* @param {string} sql
|
|
231
230
|
* @returns {string} SQL
|
|
232
231
|
*/
|
|
233
|
-
SELECT_expand(
|
|
232
|
+
SELECT_expand(q, sql) {
|
|
233
|
+
if (!('elements' in q)) return sql
|
|
234
|
+
|
|
235
|
+
const SELECT = q.SELECT
|
|
234
236
|
if (!SELECT.columns) return sql
|
|
235
|
-
|
|
236
|
-
|
|
237
|
+
|
|
237
238
|
let cols = SELECT.columns.map(x => {
|
|
238
239
|
const name = this.column_name(x)
|
|
239
240
|
let col = `'${name}',${this.output_converter4(x.element, this.quote(name))}`
|
|
@@ -256,7 +257,7 @@ class CQN2SQLRenderer {
|
|
|
256
257
|
}
|
|
257
258
|
// REVISIT: json_merge is a user defined function, bad performance!
|
|
258
259
|
obj = `json_merge(${chunks})`
|
|
259
|
-
}
|
|
260
|
+
}
|
|
260
261
|
|
|
261
262
|
|
|
262
263
|
return `SELECT ${SELECT.one || SELECT.expand === 'root' ? obj : `json_group_array(${obj.includes('json_merge') ? `json_insert(${obj})` : obj})`} as _json_ FROM (${sql})`
|
package/lib/cqn4sql.js
CHANGED
|
@@ -48,7 +48,7 @@ function cqn4sql(originalQuery, model = cds.context?.model || cds.model) {
|
|
|
48
48
|
if (originalQuery.SELECT?.from.args && !originalQuery.joinTree) return inferred
|
|
49
49
|
|
|
50
50
|
let transformedQuery = cds.ql.clone(inferred)
|
|
51
|
-
const kind = inferred.
|
|
51
|
+
const kind = inferred.kind || Object.keys(inferred)[0]
|
|
52
52
|
|
|
53
53
|
if (inferred.INSERT || inferred.UPSERT) {
|
|
54
54
|
transformedQuery = transformQueryForInsertUpsert(kind)
|
|
@@ -415,7 +415,6 @@ function cqn4sql(originalQuery, model = cds.context?.model || cds.model) {
|
|
|
415
415
|
return transformedColumns
|
|
416
416
|
|
|
417
417
|
function handleSubquery(col) {
|
|
418
|
-
if (isLocalized(inferred.target)) col.SELECT.localized = true
|
|
419
418
|
if (!col.SELECT.from.as) {
|
|
420
419
|
const uniqueSubqueryAlias = inferred.joinTree.addNextAvailableTableAlias(
|
|
421
420
|
getLastStringSegment(col.SELECT.from.ref[col.SELECT.from.ref.length - 1]),
|
|
@@ -830,7 +829,6 @@ function cqn4sql(originalQuery, model = cds.context?.model || cds.model) {
|
|
|
830
829
|
one: column.$refLinks[column.$refLinks.length - 1].definition.is2one,
|
|
831
830
|
},
|
|
832
831
|
}
|
|
833
|
-
if (isLocalized(inferred.target)) subquery.SELECT.localized = true
|
|
834
832
|
const expanded = transformSubquery(subquery)
|
|
835
833
|
const correlated = _correlate({ ...expanded, as: columnAlias }, outerAlias)
|
|
836
834
|
Object.defineProperty(correlated, 'elements', { value: subquery.elements, writable: true })
|
|
@@ -946,6 +944,7 @@ function cqn4sql(originalQuery, model = cds.context?.model || cds.model) {
|
|
|
946
944
|
outerQueries.push(inferred)
|
|
947
945
|
Object.defineProperty(q, 'outerQueries', { value: outerQueries })
|
|
948
946
|
}
|
|
947
|
+
if (isLocalized(inferred.target)) q.SELECT.localized = true
|
|
949
948
|
return cqn4sql(q, model)
|
|
950
949
|
}
|
|
951
950
|
|
package/package.json
CHANGED