@cap-js/db-service 1.0.0 → 1.1.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 +19 -1
- package/index.js +16 -2
- package/lib/InsertResults.js +20 -3
- package/lib/SQLService.js +112 -28
- package/lib/common/DatabaseService.js +55 -4
- package/lib/common/factory.d.ts +5 -0
- package/lib/converters.d.ts +24 -0
- package/lib/cql-functions.js +192 -4
- package/lib/cqn2sql.js +270 -5
- package/lib/cqn4sql.js +172 -38
- package/lib/deep-queries.js +27 -0
- package/lib/fill-in-keys.js +10 -0
- package/lib/infer/cqn.d.ts +45 -0
- package/lib/infer/index.js +178 -43
- package/lib/infer/join-tree.js +62 -17
- package/package.json +18 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,24 @@
|
|
|
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
|
+
## Version 1.1.0 - 2023-08-01
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- `UPDATE` with path expressions do not end up in a dump anymore. Instead, a proper error message is emitted.
|
|
12
|
+
- `UPDATE` is only noop if it does not include an element annotated with `@cds.on.update`.
|
|
13
|
+
- `SELECT` with `'*'` that is not expanded creates now a clearer error when the column name is required.
|
|
14
|
+
- `SELECT` with plain SQL statements will return correct result regardless of casing.
|
|
15
|
+
- View resolving for streams.
|
|
16
|
+
|
|
17
|
+
## Version 1.0.1 - 2023-07-03
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Paths addressing a column of the query via `$self.<column>` in `group by` / `order by`, `having` or `where`
|
|
22
|
+
are now correctly substituted.
|
|
23
|
+
- Mapping for OData `average` function to ANSI SQL compliant `avg` function.
|
|
24
|
+
|
|
7
25
|
## Version 1.0.0 - 2023-06-23
|
|
8
26
|
|
|
9
|
-
- Initial Release
|
|
27
|
+
- Initial Release
|
package/index.js
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
|
+
const DatabaseService = require('./lib/common/DatabaseService')
|
|
2
|
+
const SQLService = require('./lib/SQLService')
|
|
3
|
+
const CQN2SQL = require('./lib/cqn2sql').classDefinition
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @template T
|
|
7
|
+
* @typedef {import('./lib/common/factory').Factory<T>} Factory
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {import('./lib/SQLService').prototype.PreparedStatement} PreparedStatement
|
|
12
|
+
*/
|
|
13
|
+
|
|
1
14
|
module.exports = {
|
|
2
|
-
DatabaseService
|
|
3
|
-
SQLService
|
|
15
|
+
DatabaseService,
|
|
16
|
+
SQLService,
|
|
17
|
+
CQN2SQL,
|
|
4
18
|
}
|
package/lib/InsertResults.js
CHANGED
|
@@ -12,12 +12,16 @@ const USAGE_SAMPLE = async () => {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
module.exports = class InsertResult {
|
|
15
|
+
/**
|
|
16
|
+
* @param {import('@sap/cds/apis/cqn').INSERT} query
|
|
17
|
+
* @param {unknown[]} results
|
|
18
|
+
*/
|
|
15
19
|
constructor(query, results) {
|
|
16
20
|
this.query = query
|
|
17
21
|
this.results = results
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
/**
|
|
21
25
|
* Lazy access to auto-generated keys.
|
|
22
26
|
*/
|
|
23
27
|
get [iterator]() {
|
|
@@ -70,8 +74,9 @@ module.exports = class InsertResult {
|
|
|
70
74
|
})
|
|
71
75
|
}
|
|
72
76
|
|
|
73
|
-
|
|
77
|
+
/**
|
|
74
78
|
* the number of inserted (root) entries or the number of affectedRows in case of INSERT into SELECT
|
|
79
|
+
* @return {number}
|
|
75
80
|
*/
|
|
76
81
|
get affectedRows() {
|
|
77
82
|
const { INSERT: _ } = this.query
|
|
@@ -79,16 +84,28 @@ module.exports = class InsertResult {
|
|
|
79
84
|
else return (super.affectedRows = _.entries?.length || _.rows?.length || this.results.length || 1)
|
|
80
85
|
}
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
/**
|
|
83
88
|
* for checks such as res > 2
|
|
89
|
+
* @return {number}
|
|
84
90
|
*/
|
|
85
91
|
valueOf() {
|
|
86
92
|
return this.affectedRows
|
|
87
93
|
}
|
|
88
94
|
|
|
95
|
+
/**
|
|
96
|
+
* The last id of the auto incremented key column
|
|
97
|
+
* @param {unknown[]} result
|
|
98
|
+
* @returns {number}
|
|
99
|
+
*/
|
|
89
100
|
insertedRowId4(result) {
|
|
90
101
|
return result.lastID
|
|
91
102
|
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Number of affected rows
|
|
106
|
+
* @param {unknown[]} result
|
|
107
|
+
* @returns {number}
|
|
108
|
+
*/
|
|
92
109
|
affectedRows4(result) {
|
|
93
110
|
return result.changes
|
|
94
111
|
}
|
package/lib/SQLService.js
CHANGED
|
@@ -4,6 +4,19 @@ const { resolveView } = require('@sap/cds/libx/_runtime/common/utils/resolveView
|
|
|
4
4
|
const DatabaseService = require('./common/DatabaseService')
|
|
5
5
|
const cqn4sql = require('./cqn4sql')
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @callback next
|
|
9
|
+
* @param {Error} param0
|
|
10
|
+
* @returns {Promise<unknown>}
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @callback Handler
|
|
15
|
+
* @param {import('@sap/cds/apis/services').Request} param0
|
|
16
|
+
* @param {next} param1
|
|
17
|
+
* @returns {Promise<unknown>}
|
|
18
|
+
*/
|
|
19
|
+
|
|
7
20
|
class SQLService extends DatabaseService {
|
|
8
21
|
init() {
|
|
9
22
|
this.on(['SELECT'], this.transformStreamFromCQN)
|
|
@@ -21,6 +34,7 @@ class SQLService extends DatabaseService {
|
|
|
21
34
|
return super.init()
|
|
22
35
|
}
|
|
23
36
|
|
|
37
|
+
/** @type {Handler} */
|
|
24
38
|
async transformStreamFromCQN({ query }, next) {
|
|
25
39
|
if (!query._streaming) return next()
|
|
26
40
|
const cqn = STREAM.from(query.SELECT.from).column(query.SELECT.columns[0].ref[0])
|
|
@@ -29,6 +43,7 @@ class SQLService extends DatabaseService {
|
|
|
29
43
|
return stream && { value: stream }
|
|
30
44
|
}
|
|
31
45
|
|
|
46
|
+
/** @type {Handler} */
|
|
32
47
|
async transformStreamIntoCQN({ query, data, target }, next) {
|
|
33
48
|
let col, type, etag
|
|
34
49
|
const elements = query._target?.elements || target?.elements
|
|
@@ -56,11 +71,11 @@ class SQLService extends DatabaseService {
|
|
|
56
71
|
return result
|
|
57
72
|
}
|
|
58
73
|
|
|
59
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Handler for SELECT
|
|
76
|
+
* @type {Handler}
|
|
77
|
+
*/
|
|
60
78
|
async onSELECT({ query, data }) {
|
|
61
|
-
// REVISIT: disable this for queries like (SELECT 1)
|
|
62
|
-
// Will return multiple rows with objects inside
|
|
63
|
-
query.SELECT.expand = 'root'
|
|
64
79
|
const { sql, values, cqn } = this.cqn2sql(query, data)
|
|
65
80
|
let ps = await this.prepare(sql)
|
|
66
81
|
let rows = await ps.all(values)
|
|
@@ -70,6 +85,10 @@ class SQLService extends DatabaseService {
|
|
|
70
85
|
return cqn.SELECT.one || query.SELECT.from.ref?.[0].cardinality?.max === 1 ? rows[0] : rows
|
|
71
86
|
}
|
|
72
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Handler for INSERT
|
|
90
|
+
* @type {Handler}
|
|
91
|
+
*/
|
|
73
92
|
async onINSERT({ query, data }) {
|
|
74
93
|
const { sql, entries, cqn } = this.cqn2sql(query, data)
|
|
75
94
|
if (!sql) return // Do nothing when there is nothing to be done
|
|
@@ -78,6 +97,10 @@ class SQLService extends DatabaseService {
|
|
|
78
97
|
return new this.class.InsertResults(cqn, results)
|
|
79
98
|
}
|
|
80
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Handler for UPSERT
|
|
102
|
+
* @type {Handler}
|
|
103
|
+
*/
|
|
81
104
|
async onUPSERT({ query, data }) {
|
|
82
105
|
const { sql, entries } = this.cqn2sql(query, data)
|
|
83
106
|
if (!sql) return // Do nothing when there is nothing to be done
|
|
@@ -86,13 +109,25 @@ class SQLService extends DatabaseService {
|
|
|
86
109
|
return results.reduce((lastValue, currentValue) => (lastValue += currentValue.changes), 0)
|
|
87
110
|
}
|
|
88
111
|
|
|
89
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* Handler for UPDATE
|
|
114
|
+
* @type {Handler}
|
|
115
|
+
*/
|
|
90
116
|
async onUPDATE(req) {
|
|
91
|
-
if
|
|
117
|
+
// noop if not a touch for @cds.on.update
|
|
118
|
+
if (
|
|
119
|
+
!req.query.UPDATE.data &&
|
|
120
|
+
!req.query.UPDATE.with &&
|
|
121
|
+
!Object.values(req.target?.elements || {}).some(e => e['@cds.on.update'])
|
|
122
|
+
)
|
|
123
|
+
return 0
|
|
92
124
|
return this.onSIMPLE(req)
|
|
93
125
|
}
|
|
94
126
|
|
|
95
|
-
/**
|
|
127
|
+
/**
|
|
128
|
+
* Handler for Stream
|
|
129
|
+
* @type {Handler}
|
|
130
|
+
*/
|
|
96
131
|
async onSTREAM(req) {
|
|
97
132
|
const { sql, values, entries } = this.cqn2sql(req.query)
|
|
98
133
|
// writing stream
|
|
@@ -111,20 +146,29 @@ class SQLService extends DatabaseService {
|
|
|
111
146
|
return Object.values(result[0])[0]
|
|
112
147
|
}
|
|
113
148
|
|
|
114
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* Handler for CREATE, DROP, UPDATE, DELETE, with simple CQN
|
|
151
|
+
* @type {Handler}
|
|
152
|
+
*/
|
|
115
153
|
async onSIMPLE({ query, data }) {
|
|
116
154
|
const { sql, values } = this.cqn2sql(query, data)
|
|
117
155
|
let ps = await this.prepare(sql)
|
|
118
156
|
return (await ps.run(values)).changes
|
|
119
157
|
}
|
|
120
158
|
|
|
121
|
-
/**
|
|
159
|
+
/**
|
|
160
|
+
* Handler for BEGIN, COMMIT, ROLLBACK, which don't have any CQN
|
|
161
|
+
* @type {Handler}
|
|
162
|
+
*/
|
|
122
163
|
async onEVENT({ event }) {
|
|
123
164
|
DEBUG?.(event) // in the other cases above DEBUG happens in cqn2sql
|
|
124
165
|
return await this.exec(event)
|
|
125
166
|
}
|
|
126
167
|
|
|
127
|
-
/**
|
|
168
|
+
/**
|
|
169
|
+
* Handler for SQL statements which don't have any CQN
|
|
170
|
+
* @type {Handler}
|
|
171
|
+
*/
|
|
128
172
|
async onPlainSQL({ query, data }, next) {
|
|
129
173
|
if (typeof query === 'string') {
|
|
130
174
|
DEBUG?.(query)
|
|
@@ -135,12 +179,20 @@ class SQLService extends DatabaseService {
|
|
|
135
179
|
} else return next()
|
|
136
180
|
}
|
|
137
181
|
|
|
138
|
-
/**
|
|
182
|
+
/**
|
|
183
|
+
* Override in subclasses to detect more statements to be called with ps.all()
|
|
184
|
+
* @param {string} sql
|
|
185
|
+
*/
|
|
139
186
|
hasResults(sql) {
|
|
140
|
-
return /^(SELECT|WITH|CALL|PRAGMA table_info)
|
|
187
|
+
return /^(SELECT|WITH|CALL|PRAGMA table_info)/i.test(sql)
|
|
141
188
|
}
|
|
142
189
|
|
|
143
|
-
/**
|
|
190
|
+
/**
|
|
191
|
+
* Derives and executes a query to fill in `$count` for given query
|
|
192
|
+
* @param {import('@sap/cds/apis/cqn').SELECT} query - SELECT CQN
|
|
193
|
+
* @param {unknown[]} ret - Results of the original query
|
|
194
|
+
* @returns {Promise<number>}
|
|
195
|
+
*/
|
|
144
196
|
async count(query, ret) {
|
|
145
197
|
if (ret) {
|
|
146
198
|
const { one, limit: _ } = query.SELECT,
|
|
@@ -168,15 +220,29 @@ class SQLService extends DatabaseService {
|
|
|
168
220
|
* Subclasses commonly override this.
|
|
169
221
|
*/
|
|
170
222
|
static CQN2SQL = require('./cqn2sql').class
|
|
171
|
-
|
|
172
|
-
|
|
223
|
+
|
|
224
|
+
/** @param {unknown[]} args */
|
|
225
|
+
constructor(...args) {
|
|
226
|
+
super(...args)
|
|
227
|
+
/** @type {unknown} */
|
|
173
228
|
this.class = new.target // for IntelliSense
|
|
174
229
|
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @param {import('@sap/cds/apis/cqn').Query} q
|
|
233
|
+
* @param {unknown} values
|
|
234
|
+
* @returns {typeof SQLService.CQN2SQL}
|
|
235
|
+
*/
|
|
175
236
|
cqn2sql(q, values) {
|
|
176
237
|
const cqn = this.cqn4sql(q)
|
|
177
238
|
|
|
239
|
+
// REVISIT: disable this for queries like (SELECT 1)
|
|
240
|
+
// Will return multiple rows with objects inside
|
|
241
|
+
// Only enable expand when the query is inferred
|
|
242
|
+
if (cqn.SELECT && cqn.elements) cqn.SELECT.expand = cqn.SELECT.expand ?? 'root'
|
|
243
|
+
|
|
178
244
|
const cmd = cqn.cmd || Object.keys(cqn)[0]
|
|
179
|
-
if (cmd in { INSERT: 1, DELETE: 1, UPSERT: 1, UPDATE: 1 }) {
|
|
245
|
+
if (cmd in { INSERT: 1, DELETE: 1, UPSERT: 1, UPDATE: 1 } || cqn.STREAM?.into) {
|
|
180
246
|
let resolvedCqn = resolveView(cqn, this.model, this)
|
|
181
247
|
if (resolvedCqn && resolvedCqn[cmd]._transitions?.[0].target) {
|
|
182
248
|
resolvedCqn = resolvedCqn || cqn
|
|
@@ -186,6 +252,11 @@ class SQLService extends DatabaseService {
|
|
|
186
252
|
}
|
|
187
253
|
return new this.class.CQN2SQL(this.context).render(cqn, values)
|
|
188
254
|
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* @param {import('@sap/cds/apis/cqn').Query} q
|
|
258
|
+
* @returns {import('./infer/cqn').Query}
|
|
259
|
+
*/
|
|
189
260
|
cqn4sql(q) {
|
|
190
261
|
// REVISIT: move this check to cqn4sql?
|
|
191
262
|
if (!q.SELECT?.from?.join && !this.model?.definitions[_target_name4(q)]) return _unquirked(q)
|
|
@@ -204,35 +275,47 @@ class SQLService extends DatabaseService {
|
|
|
204
275
|
|
|
205
276
|
/**
|
|
206
277
|
* Used to execute simple SQL statement like BEGIN, COMMIT, ROLLBACK
|
|
278
|
+
* @param {string} sql
|
|
279
|
+
* @returns {Promise<unknown>} The result of the query
|
|
207
280
|
*/
|
|
208
|
-
// eslint-disable-next-line no-unused-vars
|
|
209
281
|
async exec(sql) {
|
|
282
|
+
sql
|
|
210
283
|
throw '2b overridden by subclass'
|
|
211
284
|
}
|
|
212
285
|
}
|
|
213
286
|
|
|
214
|
-
/**
|
|
287
|
+
/**
|
|
288
|
+
* Interface of prepared statement objects as returned by {@link SQLService#prepare}
|
|
289
|
+
* @class
|
|
290
|
+
* @interface
|
|
291
|
+
*/
|
|
215
292
|
class PreparedStatement {
|
|
216
|
-
// eslint-disable-line no-unused-vars
|
|
217
293
|
/**
|
|
218
294
|
* Executes a prepared DML query, i.e., INSERT, UPDATE, DELETE, CREATE, DROP
|
|
219
|
-
* @param {[]
|
|
295
|
+
* @param {unknown|unknown[]} binding_params
|
|
220
296
|
*/
|
|
221
|
-
async run(
|
|
297
|
+
async run(binding_params) {
|
|
298
|
+
binding_params
|
|
299
|
+
return 0
|
|
300
|
+
}
|
|
222
301
|
/**
|
|
223
302
|
* Executes a prepared SELECT query and returns a single/first row only
|
|
224
|
-
* @param {[]
|
|
303
|
+
* @param {unknown|unknown[]} binding_params
|
|
304
|
+
* @returns {Promise<unknown>}
|
|
225
305
|
*/
|
|
226
|
-
async get(
|
|
306
|
+
async get(binding_params) {
|
|
307
|
+
binding_params
|
|
227
308
|
return {}
|
|
228
|
-
}
|
|
309
|
+
}
|
|
229
310
|
/**
|
|
230
311
|
* Executes a prepared SELECT query and returns an array of all rows
|
|
231
|
-
* @param {[]
|
|
312
|
+
* @param {unknown|unknown[]} binding_params
|
|
313
|
+
* @returns {Promise<unknown[]>}
|
|
232
314
|
*/
|
|
233
|
-
async all(
|
|
315
|
+
async all(binding_params) {
|
|
316
|
+
binding_params
|
|
234
317
|
return [{}]
|
|
235
|
-
}
|
|
318
|
+
}
|
|
236
319
|
}
|
|
237
320
|
SQLService.prototype.PreparedStatement = PreparedStatement
|
|
238
321
|
|
|
@@ -285,4 +368,5 @@ cds.extend(cds.ql.Query).with(
|
|
|
285
368
|
},
|
|
286
369
|
)
|
|
287
370
|
|
|
288
|
-
|
|
371
|
+
Object.assign(SQLService, { _target_name4 })
|
|
372
|
+
module.exports = SQLService
|
|
@@ -8,22 +8,38 @@ function Pool(factory, tenant) {
|
|
|
8
8
|
}
|
|
9
9
|
const { createPool } = require('@sap/cds-foss').pool
|
|
10
10
|
|
|
11
|
+
/** @typedef {unknown} DatabaseDriver */
|
|
12
|
+
|
|
11
13
|
class DatabaseService extends cds.Service {
|
|
12
14
|
/**
|
|
13
15
|
* Return a pool factory + options property as expected by
|
|
14
16
|
* https://github.com/coopernurse/node-pool#createpool.
|
|
17
|
+
* @abstract
|
|
18
|
+
* @type {import('./factory').Factory<DatabaseDriver>}
|
|
15
19
|
*/
|
|
16
20
|
get factory() {
|
|
17
21
|
throw '2b overriden in subclass'
|
|
18
22
|
}
|
|
19
23
|
pools = { _factory: this.factory }
|
|
20
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @returns {boolean} whether this service is multi tenant enabled
|
|
27
|
+
*/
|
|
21
28
|
get isMultitenant() {
|
|
22
29
|
return 'multiTenant' in this.options ? this.options.multiTenant : cds.env.requires.multitenancy
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
/**
|
|
26
|
-
*
|
|
33
|
+
* @typedef {Object} DefaultSessionVariables
|
|
34
|
+
* @property {string} '$user.id'
|
|
35
|
+
* @property {string} '$user.locale'
|
|
36
|
+
* @property {string} '$valid.from'
|
|
37
|
+
* @property {string} '$valid.to'
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Set one or more session context variables
|
|
42
|
+
* @example
|
|
27
43
|
* ```js
|
|
28
44
|
* const tx = cds.db.tx()
|
|
29
45
|
* tx.set({
|
|
@@ -31,16 +47,25 @@ class DatabaseService extends cds.Service {
|
|
|
31
47
|
* '$user.role': 'admin'
|
|
32
48
|
* })
|
|
33
49
|
* ```
|
|
50
|
+
* @param {unknown|DefaultSessionVariables} variables
|
|
34
51
|
*/
|
|
35
|
-
// eslint-disable-next-line no-unused-vars
|
|
36
52
|
set(variables) {
|
|
53
|
+
variables
|
|
37
54
|
throw '2b overridden by subclass'
|
|
38
55
|
}
|
|
39
56
|
|
|
57
|
+
/**
|
|
58
|
+
* @param {import('@sap/cds/apis/cqn').Query} q
|
|
59
|
+
* @param {import('@sap/cds/apis/csn').CSN} m
|
|
60
|
+
* @returns {import('../infer/cqn').Query}
|
|
61
|
+
*/
|
|
40
62
|
infer(q, m = this.model) {
|
|
41
63
|
return infer(q, m)
|
|
42
64
|
}
|
|
43
65
|
|
|
66
|
+
/**
|
|
67
|
+
* @returns {Promise<DatabaseService>}
|
|
68
|
+
*/
|
|
44
69
|
async begin() {
|
|
45
70
|
const ctx = this.context
|
|
46
71
|
if (!ctx) return this.tx().begin()
|
|
@@ -49,6 +74,7 @@ class DatabaseService extends cds.Service {
|
|
|
49
74
|
const connections = pool._trackedConnections
|
|
50
75
|
let dbc
|
|
51
76
|
try {
|
|
77
|
+
/** @type {DatabaseDriver} */
|
|
52
78
|
dbc = this.dbc = await pool.acquire()
|
|
53
79
|
} catch (err) {
|
|
54
80
|
// TODO: add acquire timeout error check
|
|
@@ -57,6 +83,9 @@ class DatabaseService extends cds.Service {
|
|
|
57
83
|
}
|
|
58
84
|
this._beginStack = new Error('begin called from:')
|
|
59
85
|
connections.push(this)
|
|
86
|
+
/**
|
|
87
|
+
* @param {DatabaseDriver} dbc
|
|
88
|
+
*/
|
|
60
89
|
this._release = async dbc => {
|
|
61
90
|
await pool.release(dbc)
|
|
62
91
|
connections.splice(connections.indexOf(this), 1)
|
|
@@ -108,6 +137,9 @@ class DatabaseService extends cds.Service {
|
|
|
108
137
|
}
|
|
109
138
|
|
|
110
139
|
// REVISIT: should happen automatically after a configurable time
|
|
140
|
+
/**
|
|
141
|
+
* @param {string} tenant
|
|
142
|
+
*/
|
|
111
143
|
async disconnect(tenant) {
|
|
112
144
|
const pool = this.pools[tenant]
|
|
113
145
|
if (pool) delete this.pools[tenant]
|
|
@@ -116,17 +148,36 @@ class DatabaseService extends cds.Service {
|
|
|
116
148
|
await pool.clear()
|
|
117
149
|
}
|
|
118
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Runs a Query on the database service
|
|
153
|
+
* @param {import("@sap/cds/apis/cqn").Query} query
|
|
154
|
+
* @param {unknown} data
|
|
155
|
+
* @param {...unknown} etc
|
|
156
|
+
* @returns {Promise<unknown>}
|
|
157
|
+
*/
|
|
119
158
|
run(query, data, ...etc) {
|
|
120
159
|
// Allow db.run('...',1,2,3,4)
|
|
121
160
|
if (data !== undefined && typeof query === 'string' && typeof data !== 'object') data = [data, ...etc]
|
|
122
161
|
return super.run(query, data)
|
|
123
162
|
}
|
|
124
163
|
|
|
125
|
-
|
|
126
|
-
|
|
164
|
+
/**
|
|
165
|
+
* Generated the database url for the given tenant
|
|
166
|
+
* @param {string} tenant
|
|
167
|
+
* @returns {string}
|
|
168
|
+
*/
|
|
169
|
+
url4(tenant) {
|
|
170
|
+
tenant
|
|
127
171
|
let { url } = this.options?.credentials || this.options || {}
|
|
128
172
|
return url
|
|
129
173
|
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Old name of url4
|
|
177
|
+
* @deprecated
|
|
178
|
+
* @param {string} tenant
|
|
179
|
+
* @returns {string}
|
|
180
|
+
*/
|
|
130
181
|
getDbUrl(tenant) {
|
|
131
182
|
return this.url4(tenant)
|
|
132
183
|
} // REVISIT: Remove after cds v6.7
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
declare function ConverterFunction(expression: string): string
|
|
2
|
+
export type Converter = typeof ConverterFunction
|
|
3
|
+
|
|
4
|
+
export type Converters = {
|
|
5
|
+
UUID: Converter
|
|
6
|
+
String: Converter
|
|
7
|
+
LargeString: Converter
|
|
8
|
+
Binary: Converter
|
|
9
|
+
LargeBinary: Converter
|
|
10
|
+
Boolean: Converter
|
|
11
|
+
Integer: Converter
|
|
12
|
+
UInt8: Converter
|
|
13
|
+
Int16: Converter
|
|
14
|
+
Int32: Converter
|
|
15
|
+
Int64: Converter
|
|
16
|
+
Float: Converter
|
|
17
|
+
Double: Converter
|
|
18
|
+
Decimal: Converter
|
|
19
|
+
DecimalFloat: Converter
|
|
20
|
+
Date: Converter
|
|
21
|
+
Time: Converter
|
|
22
|
+
DateTime: Converter
|
|
23
|
+
Timestamp: Converter
|
|
24
|
+
}
|