@cap-js/db-service 1.17.2 → 1.19.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,35 @@
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.19.0](https://github.com/cap-js/cds-dbs/compare/db-service-v1.18.0...db-service-v1.19.0) (2025-03-31)
8
+
9
+
10
+ ### Added
11
+
12
+ * **forUpdate:** ignore locked ([#1074](https://github.com/cap-js/cds-dbs/issues/1074)) ([163480b](https://github.com/cap-js/cds-dbs/commit/163480b245b18a2829cd871c2f053c82bcc1abef))
13
+ * support recursive cqn queries ([#1089](https://github.com/cap-js/cds-dbs/issues/1089)) ([f09b0f8](https://github.com/cap-js/cds-dbs/commit/f09b0f815c3788349f3d39419990cd1c00963b7d))
14
+
15
+
16
+ ### Fixed
17
+
18
+ * assign more technical, implicit table aliases ([#1082](https://github.com/cap-js/cds-dbs/issues/1082)) ([1f8925a](https://github.com/cap-js/cds-dbs/commit/1f8925a5d4bcc0123f3abbee2c65ed77877da591))
19
+ * consider `nulls first | last` on `orderBy` ([#1064](https://github.com/cap-js/cds-dbs/issues/1064)) ([c6bed60](https://github.com/cap-js/cds-dbs/commit/c6bed60f0d93b9f4a73c976727f30172707c60d9)), closes [#1062](https://github.com/cap-js/cds-dbs/issues/1062)
20
+
21
+ ## [1.18.0](https://github.com/cap-js/cds-dbs/compare/db-service-v1.17.2...db-service-v1.18.0) (2025-03-04)
22
+
23
+
24
+ ### Added
25
+
26
+ * query modifiers on expand `ref` are propagated to subquery ([#1049](https://github.com/cap-js/cds-dbs/issues/1049)) ([39fbadf](https://github.com/cap-js/cds-dbs/commit/39fbadf25a874f810ac2795f2e6b0a46c3678058))
27
+ * support query modifiers at leaf of from ref ([#1050](https://github.com/cap-js/cds-dbs/issues/1050)) ([500a666](https://github.com/cap-js/cds-dbs/commit/500a666a9a054dd72d6ec8ccba0c6a6ddc263cd3))
28
+
29
+
30
+ ### Fixed
31
+
32
+ * `<expand>[@odata](https://github.com/odata).count` queries ([#966](https://github.com/cap-js/cds-dbs/issues/966)) ([6607a84](https://github.com/cap-js/cds-dbs/commit/6607a8404aa70f2f3f7c6c65c7e9b1c324a5230b))
33
+ * align debug log format of stmt values ([#1052](https://github.com/cap-js/cds-dbs/issues/1052)) ([93af0fe](https://github.com/cap-js/cds-dbs/commit/93af0fe5f93a0c1b91f592417b31fdb6266fdd79))
34
+ * expand + groupby may return null, dont attach `.element` ([#1042](https://github.com/cap-js/cds-dbs/issues/1042)) ([cf2e0a2](https://github.com/cap-js/cds-dbs/commit/cf2e0a215e89f9055e28d9f0984adf292e220aee))
35
+
7
36
  ## [1.17.2](https://github.com/cap-js/cds-dbs/compare/db-service-v1.17.1...db-service-v1.17.2) (2025-02-09)
8
37
 
9
38
 
@@ -34,7 +34,7 @@ module.exports = class InsertResult {
34
34
  })
35
35
  }
36
36
 
37
- const { target } = this.query
37
+ const target = this.query._target
38
38
  if (!target?.keys) return (super[iterator] = this.results[iterator])
39
39
  const keys = Object.keys(target.keys),
40
40
  [k1] = keys
package/lib/SQLService.js CHANGED
@@ -25,7 +25,7 @@ class SQLService extends DatabaseService {
25
25
  this.on(['INSERT', 'UPSERT', 'UPDATE'], require('./deep-queries').onDeep)
26
26
  if (cds.env.features.db_strict) {
27
27
  this.before(['INSERT', 'UPSERT', 'UPDATE'], ({ query }) => {
28
- const elements = query.target?.elements
28
+ const elements = query._target?.elements
29
29
  if (!elements) return
30
30
  const kind = query.kind || Object.keys(query)[0]
31
31
  const operation = query[kind]
@@ -120,10 +120,10 @@ class SQLService extends DatabaseService {
120
120
  async onSELECT({ query, data }) {
121
121
  // REVISIT: for custom joins, infer is called twice, which is bad
122
122
  // --> make cds.infer properly work with custom joins and remove this
123
- if (!query.target) {
123
+ if (!(query._target instanceof cds.entity)) {
124
124
  try { this.infer(query) } catch { /**/ }
125
125
  }
126
- if (query.target && !query.target._unresolved) {
126
+ if (!query._target?._unresolved) { // REVISIT: use query._target instead
127
127
  // Will return multiple rows with objects inside
128
128
  query.SELECT.expand = 'root'
129
129
  }
@@ -267,7 +267,7 @@ class SQLService extends DatabaseService {
267
267
  )
268
268
  // Prepare and run deep query, à la CQL`DELETE from Foo[pred]:comp1.comp2...`
269
269
  const query = DELETE.from({ ref: [...from.ref, c.name] })
270
- query.target = c._target
270
+ query._target = c._target
271
271
  return this.onDELETE({ query, depth, visited: [...visited], target: c._target })
272
272
  }),
273
273
  )
@@ -382,7 +382,7 @@ class SQLService extends DatabaseService {
382
382
  if (kind in { INSERT: 1, DELETE: 1, UPSERT: 1, UPDATE: 1 }) {
383
383
  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?
384
384
  let target = q[kind]._transitions?.[0].target
385
- if (target) q.target = target // REVISIT: Why isn't that done in resolveView?
385
+ if (target) q._target = target // REVISIT: Why isn't that done in resolveView?
386
386
  }
387
387
  let cqn2sql = new this.class.CQN2SQL(this)
388
388
  return cqn2sql.render(q, values)
@@ -23,8 +23,8 @@ class TemporalSessionContext extends SessionContext {
23
23
  get '$valid.to'() {
24
24
  return (super['$valid.to'] =
25
25
  this.ctx._?.['VALID-TO'] ??
26
- this.ctx._?.['VALID-AT']?.replace(/(\dZ?)$/, d => parseInt(d[0]) + 1 + d[1] || '') ??
27
- new Date().toISOString().replace(/(\dZ?)$/, d => parseInt(d[0]) + 1 + d[1] || ''))
26
+ this.ctx._?.['VALID-AT']?.replace(/\.(\d*)(Z?)$/, (_, d, z) => `.${parseInt(d) + 1}${z}`) ??
27
+ (new Date(Date.now() + 1)).toISOString())
28
28
  }
29
29
  }
30
30
 
@@ -1,30 +1,16 @@
1
- const cds = require("@sap/cds")
1
+ 'use strict'
2
2
 
3
+ // OData: https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#sec_CanonicalFunctions
3
4
  const StandardFunctions = {
4
- // OData: https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#sec_CanonicalFunctions
5
-
6
- // String and Collection Functions
7
- /**
8
- * Generates SQL statement that produces the length of a given string
9
- * @param {string} x
10
- * @returns {string}
11
- */
12
- length: x => `length(${x})`,
13
- /**
14
- * Generates SQL statement that produces the average of a given expression
15
- * @param {string} x
16
- * @returns {string}
17
- */
18
- average: x => `avg(${x})`,
19
5
  /**
20
6
  * Generates SQL statement that produces a boolean value indicating whether the search term is contained in the given columns
21
- * @param {string} ref
22
- * @param {string} arg
23
- * @returns {string}
7
+ * @param {string} ref - The reference object containing column information
8
+ * @param {string} arg - The argument object containing the search value
9
+ * @returns {string} - SQL statement
24
10
  */
25
11
  search: function (ref, arg) {
26
- if (!('val' in arg)) throw new Error(`Only single value arguments are allowed for $search`)
27
- // only apply first search term, rest is ignored
12
+ if (!('val' in arg)) throw new Error('Only single value arguments are allowed for $search')
13
+ // Only apply first search term, rest is ignored
28
14
  const sub = /("")|("(?:[^"]|\\")*(?:[^\\]|\\\\)")|(\S*)/.exec(arg.val)
29
15
  let val
30
16
  try {
@@ -37,53 +23,50 @@ const StandardFunctions = {
37
23
  const { toString } = ref
38
24
  return '(' + refs.map(ref2 => this.contains(this.tolower(toString(ref2)), this.tolower(arg))).join(' or ') + ')'
39
25
  },
40
- /**
41
- * Generates SQL statement that produces a string with all provided strings concatenated
42
- * @param {...string} args
43
- * @returns {string}
44
- */
45
- concat: (...args) => args.map(a => (a.xpr ? `(${a})` : a)).join(' || '),
26
+
27
+ // ==============================
28
+ // Aggregation Functions
29
+ // ==============================
46
30
 
47
31
  /**
48
- * Generates SQL statement that produces a boolean value indicating whether the first string contains the second string
49
- * @param {...string} args
50
- * @returns {string}
32
+ * Generates SQL statement that produces the average of a given expression
33
+ * @param {string} x - The expression to average
34
+ * @returns {string} - SQL statement
51
35
  */
52
- contains: (...args) => `(ifnull(instr(${args}),0) > 0)`,
36
+ average: x => `avg(${x})`,
37
+
53
38
  /**
54
39
  * Generates SQL statement that produces the number of elements in a given collection
55
- * @param {string} x
56
- * @returns {string}
40
+ * @param {string} x - The collection input
41
+ * @returns {string} - SQL statement
57
42
  */
58
- count: x => `count(${x || '*'})`,
43
+ count: x => `count(${x?.val || x || '*'})`,
44
+
59
45
  /**
60
46
  * Generates SQL statement that produces the number of distinct values of a given expression
61
- * @param {string} x
62
- * @returns {string}
63
- */
64
- countdistinct: x => `count(distinct ${x || cds.error`countdistinct requires a ref to be counted`})`,
65
- /**
66
- * Generates SQL statement that produces the index of the first occurrence of the second string in the first string
67
- * @param {string} x
68
- * @param {string} y
69
- * @returns {string}
47
+ * @param {string} x - The expression input
48
+ * @returns {string} - SQL statement
70
49
  */
71
- indexof: (x, y) => `instr(${x},${y}) - 1`, // sqlite instr is 1 indexed
50
+ countdistinct: x => `count(distinct ${x.val || x || '*'})`,
51
+
52
+ // ==============================
53
+ // String Functions
54
+ // ==============================
55
+
72
56
  /**
73
- * Generates SQL statement that produces a boolean value indicating whether the first string starts with the second string
74
- * @param {string} x
75
- * @param {string} y
76
- * @returns {string}
57
+ * Generates SQL statement that produces the length of a given string
58
+ * @param {string} x - The string input
59
+ * @returns {string} - SQL statement
77
60
  */
78
- startswith: (x, y) => `coalesce(instr(${x},${y}) = 1,false)`, // sqlite instr is 1 indexed
79
- // takes the end of the string of the size of the target and compares it with the target
61
+ length: x => `length(${x})`,
62
+
80
63
  /**
81
- * Generates SQL statement that produces a boolean value indicating whether the first string ends with the second string
82
- * @param {string} x
83
- * @param {string} y
84
- * @returns {string}
64
+ * Generates SQL statement that produces a string with all provided strings concatenated
65
+ * @param {...string} args - The strings to concatenate
66
+ * @returns {string} - SQL statement
85
67
  */
86
- endswith: (x, y) => `coalesce(substr(${x}, length(${x}) + 1 - length(${y})) = ${y},false)`,
68
+ concat: (...args) => args.map(a => (a.xpr ? `(${a})` : a)).join(' || '),
69
+
87
70
  /**
88
71
  * Generates SQL statement that produces the substring of a given string
89
72
  * @example
@@ -92,266 +75,115 @@ const StandardFunctions = {
92
75
  * @example
93
76
  * // returns 'b'
94
77
  * {func:'substring',args:[{val:'abc'},{val:1},{val:1}]}
95
- * @param {string} x
96
- * @param {string} y
97
- * @param {string} z
98
- * @returns {string}
78
+ * @param {string} x - The string input
79
+ * @param {string} y - The starting position
80
+ * @param {string} [z] - Optional length of the substring
81
+ * @returns {string} - SQL statement
99
82
  */
100
83
  substring: (x, y, z) =>
101
84
  z
102
- ? `substr( ${x}, case when ${y} < 0 then length(${x}) + ${y} + 1 else ${y} + 1 end, ${z} )`
103
- : `substr( ${x}, case when ${y} < 0 then length(${x}) + ${y} + 1 else ${y} + 1 end )`,
85
+ ? `substr(${x}, case when ${y} < 0 then length(${x}) + ${y} + 1 else ${y} + 1 end, ${z})`
86
+ : `substr(${x}, case when ${y} < 0 then length(${x}) + ${y} + 1 else ${y} + 1 end)`,
104
87
 
105
- // String Functions
106
- /**
107
- * Generates SQL statement that matches the given string against a regular expression
108
- * @param {string} x
109
- * @param {string} y
110
- * @returns {string}
111
- */
112
- matchesPattern: (x, y) => `(${x} regexp ${y})`,
113
- /**
114
- * Generates SQL statement that matches the given string against a regular expression
115
- * @param {string} x
116
- * @param {string} y
117
- * @returns {string}
118
- */
119
- matchespattern: (x, y) => `(${x} regexp ${y})`,
120
88
  /**
121
89
  * Generates SQL statement that produces the lower case value of a given string
122
- * @param {string} x
123
- * @returns {string}
90
+ * @param {string} x - The string input
91
+ * @returns {string} - SQL statement
124
92
  */
125
93
  tolower: x => `lower(${x})`,
94
+
126
95
  /**
127
96
  * Generates SQL statement that produces the upper case value of a given string
128
- * @param {string} x
129
- * @returns {string}
97
+ * @param {string} x - The string input
98
+ * @returns {string} - SQL statement
130
99
  */
131
100
  toupper: x => `upper(${x})`,
101
+
132
102
  /**
133
103
  * Generates SQL statement that produces the trimmed value of a given string
134
- * @param {string} x
135
- * @returns {string}
104
+ * @param {string} x - The string input
105
+ * @returns {string} - SQL statement
136
106
  */
137
107
  trim: x => `trim(${x})`,
138
108
 
109
+ // ==============================
139
110
  // Arithmetic Functions
111
+ // ==============================
112
+
140
113
  /**
141
114
  * Generates SQL statement that produces the rounded up value of a given number
142
- * @param {string} x
143
- * @returns {string}
115
+ * @param {string} x - The number input
116
+ * @returns {string} - SQL statement
144
117
  */
145
118
  ceiling: x => `ceil(${x})`,
119
+
146
120
  /**
147
121
  * Generates SQL statement that produces the rounded down value of a given number
148
- * @param {string} x
149
- * @returns {string}
122
+ * @param {string} x - The number input
123
+ * @returns {string} - SQL statement
150
124
  */
151
125
  floor: x => `floor(${x})`,
126
+
152
127
  /**
153
128
  * Generates SQL statement that produces the rounded value of a given number
154
- * @param {string} x
155
- * @param {string} p precision
156
- * @returns {string}
129
+ * @param {string} x - The number input
130
+ * @param {string} p - The precision
131
+ * @returns {string} - SQL statement
157
132
  */
158
133
  round: (x, p) => `round(${x}${p ? `,${p}` : ''})`,
159
134
 
135
+ // ==============================
160
136
  // Date and Time Functions
137
+ // ==============================
161
138
 
162
139
  /**
163
140
  * Generates SQL statement that produces current point in time (date and time with time zone)
164
- * @returns {string}
141
+ * @returns {string} - SQL statement
165
142
  */
166
143
  now: function () {
167
144
  return this.session_context({ val: '$now' })
168
145
  },
169
- /**
170
- * Generates SQL statement that produces the year of a given timestamp
171
- * @param {string} x
172
- * @returns {string}
173
- * /
174
- year: x => `cast( strftime('%Y',${x}) as Integer )`,
175
- /**
176
- * Generates SQL statement that produces the month of a given timestamp
177
- * @param {string} x
178
- * @returns {string}
179
- * /
180
- month: x => `cast( strftime('%m',${x}) as Integer )`,
181
- /**
182
- * Generates SQL statement that produces the day of a given timestamp
183
- * @param {string} x
184
- * @returns {string}
185
- * /
186
- day: x => `cast( strftime('%d',${x}) as Integer )`,
187
- /**
188
- * Generates SQL statement that produces the hours of a given timestamp
189
- * @param {string} x
190
- * @returns {string}
191
- * /
192
- hour: x => `cast( strftime('%H',${x}) as Integer )`,
193
- /**
194
- * Generates SQL statement that produces the minutes of a given timestamp
195
- * @param {string} x
196
- * @returns {string}
197
- * /
198
- minute: x => `cast( strftime('%M',${x}) as Integer )`,
199
- /**
200
- * Generates SQL statement that produces the seconds of a given timestamp
201
- * @param {string} x
202
- * @returns {string}
203
- * /
204
- second: x => `cast( strftime('%S',${x}) as Integer )`,
205
146
 
206
- // REVISIT: make precision configurable
207
147
  /**
208
- * Generates SQL statement that produces the fractional seconds of a given timestamp
209
- * @param {string} x
210
- * @returns {string}
148
+ * Maximum date time value
149
+ * @returns {string} - SQL statement
211
150
  */
212
- fractionalseconds: x => `cast( substr( strftime('%f', ${x}), length(strftime('%f', ${x})) - 3) as REAL)`,
151
+ maxdatetime: () => `'9999-12-31T23:59:59.999Z'`,
213
152
 
214
153
  /**
215
- * maximum date time value
216
- * @returns {string}
217
- */
218
- maxdatetime: () => "'9999-12-31T23:59:59.999Z'",
219
- /**
220
- * minimum date time value
221
- * @returns {string}
154
+ * Minimum date time value
155
+ * @returns {string} - SQL statement
222
156
  */
223
- mindatetime: () => "'0001-01-01T00:00:00.000Z'",
224
-
225
- // odata spec defines the value format for totalseconds as a duration like: P12DT23H59M59.999999999999S
226
- // P -> duration indicator
227
- // D -> days, T -> Time seperator, H -> hours, M -> minutes, S -> fractional seconds
228
- // By splitting the DT and calculating the seconds of the time separate from the day
229
- // it possible to determine the full amount of seconds by adding them together as fractionals and multiplying
230
- // the number of seconds in a day
231
- // As sqlite is most accurate with juliandays it is better to do then then using actual second function
232
- // while the odata specification states that the seconds has to be fractional which only julianday allows
233
- /**
234
- * Generates SQL statement that produces an OData compliant duration string like: P12DT23H59M59.999999999999S
235
- * @param {string} x
236
- * @returns {string}
237
- */
238
- totalseconds: x => `(
239
- (
240
- (
241
- cast(substr(${x},2,instr(${x},'DT') - 2) as Integer)
242
- ) + (
243
- julianday(
244
- '-4713-11-25T' ||
245
- replace(
246
- replace(
247
- replace(
248
- substr(${x},instr(${x},'DT') + 2),
249
- 'H',':'
250
- ),'M',':'
251
- ),'S','Z'
252
- )
253
- ) - 0.5
254
- )
255
- ) * 86400
256
- )`
157
+ mindatetime: () => `'0001-01-01T00:00:00.000Z'`,
257
158
  }
258
159
 
259
160
  const HANAFunctions = {
260
- // https://help.sap.com/docs/SAP_HANA_PLATFORM/4fe29514fd584807ac9f2a04f6754767/f12b86a6284c4aeeb449e57eb5dd3ebd.html
261
-
262
161
  /**
263
162
  * Generates SQL statement that calls the session_context function with the given parameter
264
- * @param {string} x session variable name or SQL expression
265
- * @returns {string}
163
+ * @param {string} x - The session variable name or SQL expression
164
+ * @returns {string} - SQL statement
266
165
  */
267
- session_context: x => `session_context('${x.val}')`,
166
+ session_context: x => `session_context('${x.val}')`,
268
167
 
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'),
273
- /**
274
- * Generates SQL statement that calculates the difference in 100nanoseconds between two timestamps
275
- * @param {string} x left timestamp
276
- * @param {string} y right timestamp
277
- * @returns {string}
278
- */
279
- nano100_between: (x, y) => `(julianday(${y}) - julianday(${x})) * 864000000000`,
280
168
  /**
281
- * Generates SQL statement that calculates the difference in seconds between two timestamps
282
- * @param {string} x left timestamp
283
- * @param {string} y right timestamp
284
- * @returns {string}
169
+ * Generates SQL statement for the current date
170
+ * @returns {string} - SQL statement
285
171
  */
286
- seconds_between: (x, y) => `(julianday(${y}) - julianday(${x})) * 86400`,
287
- // Calculates the difference in full days using julian day
288
- // Using the exact time of the day to determine whether 24 hours have passed or not to add the final day
289
- // When just comparing the julianday values with each other there are leap seconds included
290
- // Which on the day resolution are included as the individual days therefor ignoring them to match HANA
291
- /**
292
- * Generates SQL statement that calculates the difference in days between two timestamps
293
- * @param {string} x left timestamp
294
- * @param {string} y right timestamp
295
- * @returns {string}
296
- */
297
- days_between: (x, y) => `(
298
- cast ( julianday(${y}) as Integer ) - cast ( julianday(${x}) as Integer )
299
- ) + (
300
- case
301
- when ( julianday(${y}) < julianday(${x}) ) then
302
- (cast( strftime('%H%M%S%f0000', ${y}) as Integer ) < cast( strftime('%H%M%S%f0000', ${x}) as Integer ))
303
- else
304
- (cast( strftime('%H%M%S%f0000', ${y}) as Integer ) > cast( strftime('%H%M%S%f0000', ${x}) as Integer )) * -1
305
- end
306
- )`,
172
+ current_date: () => 'current_date',
307
173
 
308
- // (y1 - y0) * 12 + (m1 - m0) + (t1 < t0) * -1
309
- /* '%d%H%M%S%f' returns as a number like which results in an equal check to:
310
- (
311
- d1 < d0 ||
312
- (d1 = d0 && h1 < h0) ||
313
- (d1 = d0 && h1 = h0 && m1 < m0) ||
314
- (d1 = d0 && h1 = h0 && m1 = m0 && s1 < s0) ||
315
- (d1 = d0 && h1 = h0 && m1 = m0 && s1 = s0 && ms1 < ms0)
316
- )
317
- Which will remove the current month if the time of the month is below the time of the month of the start date
318
- It should not matter that the number of days in the month is different as for a month to have passed
319
- the time of the month would have to be higher then the time of the month of the start date
320
-
321
- Also check whether the result will be positive or negative to make sure to not subtract an extra month
322
- */
323
174
  /**
324
- * Generates SQL statement that calculates the difference in months between two timestamps
325
- * @param {string} x left timestamp
326
- * @param {string} y right timestamp
327
- * @returns {string}
175
+ * Generates SQL statement for the current time
176
+ * @param {string} [p] - Optional precision parameter
177
+ * @returns {string} - SQL statement
328
178
  */
329
- months_between: (x, y) => `
330
- (
331
- (
332
- ( cast( strftime('%Y', ${y}) as Integer ) - cast( strftime('%Y', ${x}) as Integer ) ) * 12
333
- ) + (
334
- cast( strftime('%m', ${y}) as Integer ) - cast( strftime('%m', ${x}) as Integer )
335
- ) + (
336
- (
337
- case
338
- when ( cast( strftime('%Y%m', ${y}) as Integer ) < cast( strftime('%Y%m', ${x}) as Integer ) ) then
339
- (cast( strftime('%d%H%M%S%f0000', ${y}) as Integer ) > cast( strftime('%d%H%M%S%f0000', ${x}) as Integer ))
340
- else
341
- (cast( strftime('%d%H%M%S%f0000', ${y}) as Integer ) < cast( strftime('%d%H%M%S%f0000', ${x}) as Integer )) * -1
342
- end
343
- )
344
- )
345
- )`,
179
+ current_time: p => (p ? `current_time(${p})` : 'current_time'),
180
+
346
181
  /**
347
- * Generates SQL statement that calculates the difference in years between two timestamps
348
- * @param {string} x left timestamp
349
- * @param {string} y right timestamp
350
- * @returns {string}
182
+ * Generates SQL statement for the current timestamp
183
+ * @param {string} [p] - Optional precision parameter
184
+ * @returns {string} - SQL statement
351
185
  */
352
- years_between(x, y) {
353
- return `floor(${this.months_between(x, y)} / 12)`
354
- },
186
+ current_timestamp: p => (p ? `current_timestamp(${p})` : 'current_timestamp'),
355
187
  }
356
188
 
357
189
  for (let each in HANAFunctions) HANAFunctions[each.toUpperCase()] = HANAFunctions[each]