@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 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
 
@@ -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 || [ref]
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
- session_context: x => `session_context('${x.val}')`,
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
- const LOG = cds.log('sql-json')
11
- if (LOG._debug) return cds.debug('sql-json')
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
- DEBUG?.(
94
- this.sql,
95
- ...(sanitize_values && (this.entries || this.values?.length > 0) ? ['***'] : this.entries || this.values || []),
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
- searchIn.length > 1 ? { list: searchIn } : { ...searchIn[0] },
2206
+ { list: searchIn },
2202
2207
  xpr.length === 1 && 'val' in xpr[0] ? xpr[0] : { xpr },
2203
2208
  ],
2204
2209
  }
@@ -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) {
@@ -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
- // infix filter never have table alias
179
- // we need to search for first step in ´model.definitions[infixAlias]`
180
- if ($baseLink) {
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
- if (!e) throw new Error(`"${id}" not found in the elements of "${arg.$refLinks[i - 1].definition.name}"`)
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js/db-service",
3
- "version": "1.15.0",
3
+ "version": "1.16.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": {