@cap-js/db-service 1.15.0 → 1.16.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 +23 -0
- package/lib/cql-functions.js +10 -11
- package/lib/cqn2sql.js +15 -16
- package/lib/cqn4sql.js +6 -1
- package/lib/fill-in-keys.js +1 -1
- package/lib/infer/index.js +14 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@
|
|
|
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.16.0](https://github.com/cap-js/cds-dbs/compare/db-service-v1.15.1...db-service-v1.16.0) (2024-11-25)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
* single column in `search` function is also treated as CQN `list` ([#898](https://github.com/cap-js/cds-dbs/issues/898)) ([f6593e6](https://github.com/cap-js/cds-dbs/commit/f6593e69de6df3e85a39c048794a56c7eb842c4c))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
* foreignKeys aspect on association may not be set ([#903](https://github.com/cap-js/cds-dbs/issues/903)) ([732a2f3](https://github.com/cap-js/cds-dbs/commit/732a2f385074f50b87ff9715b8bdf48d28a36309))
|
|
18
|
+
* insert on uuid with default value ([#911](https://github.com/cap-js/cds-dbs/issues/911)) ([545e489](https://github.com/cap-js/cds-dbs/commit/545e489ecd07b5a3ece9441d95804fb2f3d436fa))
|
|
19
|
+
* `session_context`, `current_date`, `current_time` and `current_timestamp` are treated as SAP HANA functions and are callable in upper case ([#910](https://github.com/cap-js/cds-dbs/issues/910)) ([50ebd10](https://github.com/cap-js/cds-dbs/commit/50ebd106b9ee5bf7e1026658b89401e904ffe051))
|
|
20
|
+
* wrap values in array if it is object, so it can be spreaded ([#882](https://github.com/cap-js/cds-dbs/issues/882)) ([11f3e8b](https://github.com/cap-js/cds-dbs/commit/11f3e8bdf37d57295c1f2ffb40e217f86ec7d423))
|
|
21
|
+
|
|
22
|
+
## [1.15.1](https://github.com/cap-js/cds-dbs/compare/db-service-v1.15.0...db-service-v1.15.1) (2024-11-18)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
* cross joins without on-condition ([#899](https://github.com/cap-js/cds-dbs/issues/899)) ([c61a04a](https://github.com/cap-js/cds-dbs/commit/c61a04aa4394511100f97cfebd362a2298221d96))
|
|
28
|
+
* pseudo paths in expands ([#896](https://github.com/cap-js/cds-dbs/issues/896)) ([014c50c](https://github.com/cap-js/cds-dbs/commit/014c50cec9c2de1ee3dfdf1861940ae0e2520c16))
|
|
29
|
+
|
|
7
30
|
## [1.15.0](https://github.com/cap-js/cds-dbs/compare/db-service-v1.14.1...db-service-v1.15.0) (2024-11-14)
|
|
8
31
|
|
|
9
32
|
|
package/lib/cql-functions.js
CHANGED
|
@@ -33,7 +33,7 @@ const StandardFunctions = {
|
|
|
33
33
|
val = sub[2] || sub[3] || ''
|
|
34
34
|
}
|
|
35
35
|
arg.val = arg.__proto__.val = val
|
|
36
|
-
const refs = ref.list
|
|
36
|
+
const refs = ref.list
|
|
37
37
|
const { toString } = ref
|
|
38
38
|
return '(' + refs.map(ref2 => this.contains(this.tolower(toString(ref2)), this.tolower(arg))).join(' or ') + ')'
|
|
39
39
|
},
|
|
@@ -159,10 +159,6 @@ const StandardFunctions = {
|
|
|
159
159
|
|
|
160
160
|
// Date and Time Functions
|
|
161
161
|
|
|
162
|
-
current_date: p => (p ? `current_date(${p})` : 'current_date'),
|
|
163
|
-
current_time: p => (p ? `current_time(${p})` : 'current_time'),
|
|
164
|
-
current_timestamp: p => (p ? `current_timestamp(${p})` : 'current_timestamp'),
|
|
165
|
-
|
|
166
162
|
/**
|
|
167
163
|
* Generates SQL statement that produces current point in time (date and time with time zone)
|
|
168
164
|
* @returns {string}
|
|
@@ -257,20 +253,23 @@ const StandardFunctions = {
|
|
|
257
253
|
) - 0.5
|
|
258
254
|
)
|
|
259
255
|
) * 86400
|
|
260
|
-
)
|
|
256
|
+
)`
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const HANAFunctions = {
|
|
260
|
+
// https://help.sap.com/docs/SAP_HANA_PLATFORM/4fe29514fd584807ac9f2a04f6754767/f12b86a6284c4aeeb449e57eb5dd3ebd.html
|
|
261
261
|
|
|
262
262
|
/**
|
|
263
263
|
* Generates SQL statement that calls the session_context function with the given parameter
|
|
264
264
|
* @param {string} x session variable name or SQL expression
|
|
265
265
|
* @returns {string}
|
|
266
266
|
*/
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const HANAFunctions = {
|
|
271
|
-
// https://help.sap.com/docs/SAP_HANA_PLATFORM/4fe29514fd584807ac9f2a04f6754767/f12b86a6284c4aeeb449e57eb5dd3ebd.html
|
|
267
|
+
session_context: x => `session_context('${x.val}')`,
|
|
272
268
|
|
|
273
269
|
// Time functions
|
|
270
|
+
current_date: p => (p ? `current_date(${p})` : 'current_date'),
|
|
271
|
+
current_time: p => (p ? `current_time(${p})` : 'current_time'),
|
|
272
|
+
current_timestamp: p => (p ? `current_timestamp(${p})` : 'current_timestamp'),
|
|
274
273
|
/**
|
|
275
274
|
* Generates SQL statement that calculates the difference in 100nanoseconds between two timestamps
|
|
276
275
|
* @param {string} x left timestamp
|
package/lib/cqn2sql.js
CHANGED
|
@@ -6,16 +6,9 @@ const _strict_booleans = _simple_queries < 2
|
|
|
6
6
|
|
|
7
7
|
const { Readable } = require('stream')
|
|
8
8
|
|
|
9
|
-
const DEBUG = (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return cds.debug('sql|sqlite')
|
|
13
|
-
//if (DEBUG) {
|
|
14
|
-
// return DEBUG
|
|
15
|
-
// (sql, ...more) => DEBUG (sql.replace(/(?:SELECT[\n\r\s]+(json_group_array\()?[\n\r\s]*json_insert\((\n|\r|.)*?\)[\n\r\s]*\)?[\n\r\s]+as[\n\r\s]+_json_[\n\r\s]+FROM[\n\r\s]*\(|\)[\n\r\s]*(\)[\n\r\s]+AS )|\)$)/gim,(a,b,c,d) => d || ''), ...more)
|
|
16
|
-
// FIXME: looses closing ) on INSERT queries
|
|
17
|
-
//}
|
|
18
|
-
})()
|
|
9
|
+
const DEBUG = cds.debug('sql|sqlite')
|
|
10
|
+
const LOG_SQL = cds.log('sql')
|
|
11
|
+
const LOG_SQLITE = cds.log('sqlite')
|
|
19
12
|
|
|
20
13
|
class CQN2SQLRenderer {
|
|
21
14
|
/**
|
|
@@ -90,10 +83,17 @@ class CQN2SQLRenderer {
|
|
|
90
83
|
if (vars?.length && !this.values?.length) this.values = vars
|
|
91
84
|
if (vars && Object.keys(vars).length && !this.values?.length) this.values = vars
|
|
92
85
|
const sanitize_values = process.env.NODE_ENV === 'production' && cds.env.log.sanitize_values !== false
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
if (LOG_SQL._debug || LOG_SQLITE._debug) {
|
|
89
|
+
let values = sanitize_values && (this.entries || this.values?.length > 0) ? ['***'] : this.entries || this.values || []
|
|
90
|
+
if (values && !Array.isArray(values)) {
|
|
91
|
+
values = [values]
|
|
92
|
+
}
|
|
93
|
+
DEBUG(this.sql, ...values)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
97
|
return this
|
|
98
98
|
}
|
|
99
99
|
|
|
@@ -363,8 +363,7 @@ class CQN2SQLRenderer {
|
|
|
363
363
|
return _aliased(this.quote(this.name(z)))
|
|
364
364
|
}
|
|
365
365
|
if (from.SELECT) return _aliased(`(${this.SELECT(from)})`)
|
|
366
|
-
if (from.join)
|
|
367
|
-
return `${this.from(from.args[0])} ${from.join} JOIN ${this.from(from.args[1])} ON ${this.where(from.on)}`
|
|
366
|
+
if (from.join) return `${this.from(from.args[0])} ${from.join} JOIN ${this.from(from.args[1])}${from.on ? ` ON ${this.where(from.on)}` : ''}`
|
|
368
367
|
}
|
|
369
368
|
|
|
370
369
|
/**
|
package/lib/cqn4sql.js
CHANGED
|
@@ -1868,13 +1868,18 @@ function cqn4sql(originalQuery, model) {
|
|
|
1868
1868
|
value: [],
|
|
1869
1869
|
writable: true,
|
|
1870
1870
|
})
|
|
1871
|
+
let pseudoPath = false
|
|
1871
1872
|
ref.reduce((prev, res, i) => {
|
|
1872
1873
|
if (res === '$self')
|
|
1873
1874
|
// next is resolvable in entity
|
|
1874
1875
|
return prev
|
|
1875
1876
|
if (res in pseudos.elements) {
|
|
1877
|
+
pseudoPath = true
|
|
1876
1878
|
thing.$refLinks.push({ definition: pseudos.elements[res], target: pseudos })
|
|
1877
1879
|
return pseudos.elements[res]
|
|
1880
|
+
} else if (pseudoPath) {
|
|
1881
|
+
thing.$refLinks.push({ definition: {}, target: pseudos })
|
|
1882
|
+
return prev?.elements[res]
|
|
1878
1883
|
}
|
|
1879
1884
|
const definition =
|
|
1880
1885
|
prev?.elements?.[res] || getDefinition(prev?.target)?.elements[res] || pseudos.elements[res]
|
|
@@ -2198,7 +2203,7 @@ function cqn4sql(originalQuery, model) {
|
|
|
2198
2203
|
const searchFunc = {
|
|
2199
2204
|
func: 'search',
|
|
2200
2205
|
args: [
|
|
2201
|
-
|
|
2206
|
+
{ list: searchIn },
|
|
2202
2207
|
xpr.length === 1 && 'val' in xpr[0] ? xpr[0] : { xpr },
|
|
2203
2208
|
],
|
|
2204
2209
|
}
|
package/lib/fill-in-keys.js
CHANGED
|
@@ -35,7 +35,7 @@ const generateUUIDandPropagateKeys = (entity, data, event) => {
|
|
|
35
35
|
if (event === 'CREATE') {
|
|
36
36
|
const keys = entity.keys
|
|
37
37
|
for (const k in keys)
|
|
38
|
-
if (keys[k].isUUID && !data[k] && !assoc4(keys[k])) //> skip key assocs, and foreign keys thereof
|
|
38
|
+
if (keys[k].isUUID && !data[k] && !assoc4(keys[k]) && !keys[k].default) //> skip key assocs, and foreign keys thereof
|
|
39
39
|
data[k] = cds.utils.uuid()
|
|
40
40
|
}
|
|
41
41
|
for (const each in entity.elements) {
|
package/lib/infer/index.js
CHANGED
|
@@ -172,12 +172,17 @@ function infer(originalQuery, model) {
|
|
|
172
172
|
if (!ref) return
|
|
173
173
|
init$refLinks(arg)
|
|
174
174
|
let i = 0
|
|
175
|
+
let pseudoPath = false
|
|
175
176
|
for (const step of ref) {
|
|
176
177
|
const id = step.id || step
|
|
177
178
|
if (i === 0) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
if (id in pseudos.elements) {
|
|
180
|
+
// pseudo path
|
|
181
|
+
arg.$refLinks.push({ definition: pseudos.elements[id], target: pseudos })
|
|
182
|
+
pseudoPath = true // only first path step must be well defined
|
|
183
|
+
} else if ($baseLink) {
|
|
184
|
+
// infix filter never have table alias
|
|
185
|
+
// we need to search for first step in ´model.definitions[infixAlias]`
|
|
181
186
|
const { definition } = $baseLink
|
|
182
187
|
const elements = getDefinition(definition.target)?.elements || definition.elements
|
|
183
188
|
const e = elements?.[id] || cds.error`"${id}" not found in the elements of "${definition.name}"`
|
|
@@ -201,11 +206,15 @@ function infer(originalQuery, model) {
|
|
|
201
206
|
const definition = getDefinition(id) || cds.error`"${id}" not found in the definitions of your model`
|
|
202
207
|
arg.$refLinks[0] = { definition, target: definition }
|
|
203
208
|
}
|
|
209
|
+
} else if (arg.ref[0] === '$user' && pseudoPath) {
|
|
210
|
+
// `$user.some.unknown.element` -> no error
|
|
211
|
+
arg.$refLinks.push({ definition: {}, target: pseudos })
|
|
204
212
|
} else {
|
|
205
213
|
const recent = arg.$refLinks[i - 1]
|
|
206
214
|
const { elements } = getDefinition(recent.definition.target) || recent.definition
|
|
207
215
|
const e = elements[id]
|
|
208
|
-
|
|
216
|
+
const notFoundIn = pseudoPath ? arg.ref[i - 1] : getFullPathForLinkedArg(arg)
|
|
217
|
+
if (!e) throw new Error(`"${id}" not found in the elements of "${notFoundIn}"`)
|
|
209
218
|
arg.$refLinks.push({ definition: e, target: getDefinition(e.target) || e })
|
|
210
219
|
}
|
|
211
220
|
arg.$refLinks[i].alias = !ref[i + 1] && arg.as ? arg.as : id.split('.').pop()
|
|
@@ -1238,7 +1247,7 @@ function rejectNonFkNavigation(assoc, additionalInfo) {
|
|
|
1238
1247
|
*/
|
|
1239
1248
|
function isForeignKeyOf(e, assoc) {
|
|
1240
1249
|
if (!assoc.isAssociation) return false
|
|
1241
|
-
return e in (assoc.elements || assoc.foreignKeys)
|
|
1250
|
+
return e in (assoc.elements || assoc.foreignKeys || {})
|
|
1242
1251
|
}
|
|
1243
1252
|
const idOnly = ref => ref.id || ref
|
|
1244
1253
|
|
package/package.json
CHANGED