@cap-js/sqlite 1.3.1 → 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 CHANGED
@@ -1,10 +1,45 @@
1
- # Change Log
1
+ # Changelog
2
2
 
3
3
  - All notable changes to this project are documented in this file.
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.3.1 - 2023-10-10
7
+ ## [1.5.0](https://github.com/cap-js/cds-dbs/compare/sqlite-v1.4.0...sqlite-v1.5.0) (2024-02-02)
8
+
9
+
10
+ ### Added
11
+
12
+ * SELECT returns LargeBinaries as streams unless feature flag "stream_compat" is set ([#251](https://github.com/cap-js/cds-dbs/issues/251)) ([8165a4a](https://github.com/cap-js/cds-dbs/commit/8165a4a3f6bb21c970668c8873f9d9c662b43780))
13
+ * Support Readable Streams inside INSERT.entries ([#343](https://github.com/cap-js/cds-dbs/issues/343)) ([f6faf89](https://github.com/cap-js/cds-dbs/commit/f6faf8955b7888479c66f1727ade65b382611c2f))
14
+
15
+
16
+ ### Fixed
17
+
18
+ * config in streaming test with compat flag ([#412](https://github.com/cap-js/cds-dbs/issues/412)) ([335a178](https://github.com/cap-js/cds-dbs/commit/335a1785e216b581759f75154fef7b1b43e6ca17))
19
+ * Do not generate UUIDs for association keys ([#398](https://github.com/cap-js/cds-dbs/issues/398)) ([9970e14](https://github.com/cap-js/cds-dbs/commit/9970e14352679711a9c60807608becff05151fc4))
20
+ * make @cap-js/sqlite work with better-sqlite3@9.3.0 ([#422](https://github.com/cap-js/cds-dbs/issues/422)) ([44c0a59](https://github.com/cap-js/cds-dbs/commit/44c0a59277b14be0b81b7f80555e18377ddbfe3c))
21
+ * sqlite date string compatibility parsing only for valid dates ([#410](https://github.com/cap-js/cds-dbs/issues/410)) ([2a8bb2d](https://github.com/cap-js/cds-dbs/commit/2a8bb2d60940760c6280d8cc06100cb9087194b5)), closes [#409](https://github.com/cap-js/cds-dbs/issues/409)
22
+ * UPSERT for @cap-js/hana for entities with multiple keys ([#418](https://github.com/cap-js/cds-dbs/issues/418)) ([9bbac6e](https://github.com/cap-js/cds-dbs/commit/9bbac6ebbbddfa2f620833ce195eedeb0a79f43e))
23
+
24
+ ## [1.4.0](https://github.com/cap-js/cds-dbs/compare/sqlite-v1.3.1...sqlite-v1.4.0) (2023-11-20)
25
+
26
+
27
+ ### Added
28
+
29
+ * **temporal data:** add time slice key to conflict clause ([#249](https://github.com/cap-js/cds-dbs/issues/249)) ([67b8edf](https://github.com/cap-js/cds-dbs/commit/67b8edf9b7f6b0fbab0010d7c93ed03a01e103ed))
30
+
31
+
32
+ ### Fixed
33
+
34
+ * align time function behavior ([#322](https://github.com/cap-js/cds-dbs/issues/322)) ([c3ab40a](https://github.com/cap-js/cds-dbs/commit/c3ab40a007c105465349dd2f612178367b8e713a))
35
+ * date functions with null value ([#347](https://github.com/cap-js/cds-dbs/issues/347)) ([bdc8967](https://github.com/cap-js/cds-dbs/commit/bdc8967f07276acdb249dec42231d432e132e0d4))
36
+
37
+
38
+ ### Changed
39
+
40
+ * upgrade to better-sqlite@9 ([#334](https://github.com/cap-js/cds-dbs/issues/334)) ([5184e41](https://github.com/cap-js/cds-dbs/commit/5184e4155ccd1a2945a1fc033204e24425d70341))
41
+
42
+ ## [1.3.1](https://github.com/cap-js/cds-dbs/compare/v1.3.0...v1.3.1) (2023-10-10)
8
43
 
9
44
  ### Changed
10
45
 
@@ -12,7 +47,7 @@
12
47
 
13
48
  ## Version 1.3.0 - 2023-10-06
14
49
 
15
- ### Fixed
50
+ ### Fixed
16
51
 
17
52
  - `CURRENT_TIMESTAMP` in view definition preserves the timezone. #254
18
53
 
package/README.md CHANGED
@@ -14,7 +14,6 @@ npm add @cap-js/sqlite -D
14
14
 
15
15
  Learn more about setup and usage in the [respective database guide](https://cap.cloud.sap/docs/guides/databases-sqlite).
16
16
 
17
-
18
17
  ## Support
19
18
 
20
19
  This project is open to feature requests/suggestions, bug reports etc. via [GitHub issues](https://github.com/cap-js/cds-dbs/issues).
@@ -23,6 +22,11 @@ This project is open to feature requests/suggestions, bug reports etc. via [GitH
23
22
 
24
23
  Contribution and feedback are encouraged and always welcome. For more information about how to contribute, the project structure, as well as additional contribution information, see our [Contribution Guidelines](CONTRIBUTING.md).
25
24
 
25
+ ## Versioning
26
+
27
+ This library follows [Semantic Versioning](https://semver.org/).
28
+ All notable changes are documented in [CHANGELOG.md](CHANGELOG.md).
29
+
26
30
  ## Code of Conduct
27
31
 
28
32
  We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone. By participating in this project, you agree to abide by its [Code of Conduct](CODE_OF_CONDUCT.md) at all times.
@@ -1,23 +1,42 @@
1
1
  const { SQLService } = require('@cap-js/db-service')
2
- const { Readable } = require('stream')
3
2
  const cds = require('@sap/cds/lib')
4
3
  const sqlite = require('better-sqlite3')
5
4
  const $session = Symbol('dbc.session')
6
5
  const convStrm = require('stream/consumers')
6
+ const { Readable } = require('stream')
7
7
 
8
8
  class SQLiteService extends SQLService {
9
+ init() {
10
+ return super.init(...arguments)
11
+ }
12
+
9
13
  get factory() {
10
14
  return {
11
15
  options: { max: 1, ...this.options.pool },
12
16
  create: tenant => {
13
17
  const database = this.url4(tenant)
14
18
  const dbc = new sqlite(database)
19
+
20
+ const deterministic = { deterministic: true }
15
21
  dbc.function('session_context', key => dbc[$session][key])
16
- dbc.function('regexp', { deterministic: true }, (re, x) => (RegExp(re).test(x) ? 1 : 0))
17
- dbc.function('ISO', { deterministic: true }, d => d && new Date(d).toISOString())
18
- dbc.function('json_merge', { varargs: true, deterministic: true }, (...args) =>
19
- args.join('').replace(/}{/g, ','),
20
- )
22
+ dbc.function('regexp', deterministic, (re, x) => (RegExp(re).test(x) ? 1 : 0))
23
+ dbc.function('ISO', deterministic, d => d && new Date(d).toISOString())
24
+
25
+ // define date and time functions in js to allow for throwing errors
26
+ const isTime = /^\d{1,2}:\d{1,2}:\d{1,2}$/
27
+ const hasTimezone = /([+-]\d{1,2}:?\d{0,2}|Z)$/
28
+ const toDate = (d, allowTime = false) => {
29
+ const date = new Date(allowTime && isTime.test(d) ? `1970-01-01T${d}Z` : hasTimezone.test(d) ? d : d + 'Z')
30
+ if (Number.isNaN(date.getTime())) throw new Error(`Value does not contain a valid ${allowTime ? 'time' : 'date'} "${d}"`)
31
+ return date
32
+ }
33
+ dbc.function('year', deterministic, d => d === null ? null : toDate(d).getUTCFullYear())
34
+ dbc.function('month', deterministic, d => d === null ? null : toDate(d).getUTCMonth() + 1)
35
+ dbc.function('day', deterministic, d => d === null ? null : toDate(d).getUTCDate())
36
+ dbc.function('hour', deterministic, d => d === null ? null : toDate(d, true).getUTCHours())
37
+ dbc.function('minute', deterministic, d => d === null ? null : toDate(d, true).getUTCMinutes())
38
+ dbc.function('second', deterministic, d => d === null ? null : toDate(d, true).getUTCSeconds())
39
+
21
40
  if (!dbc.memory) dbc.pragma('journal_mode = WAL')
22
41
  return dbc
23
42
  },
@@ -62,13 +81,11 @@ class SQLiteService extends SQLService {
62
81
  async _run(stmt, binding_params) {
63
82
  for (let i = 0; i < binding_params.length; i++) {
64
83
  const val = binding_params[i]
84
+ if (val instanceof Readable) {
85
+ binding_params[i] = await convStrm[val.type === 'json' ? 'text' : 'buffer'](val)
86
+ }
65
87
  if (Buffer.isBuffer(val)) {
66
- binding_params[i] = Buffer.from(val.base64Slice())
67
- } else if (typeof val === 'object' && val && val.pipe) {
68
- // REVISIT: stream.setEncoding('base64') sometimes misses the last bytes
69
- // if (val.type === 'binary') val.setEncoding('base64')
70
- binding_params[i] = await convStrm.buffer(val)
71
- if (val.type === 'binary') binding_params[i] = Buffer.from(binding_params[i].toString('base64'))
88
+ binding_params[i] = Buffer.from(val.toString('base64'))
72
89
  }
73
90
  }
74
91
  return stmt.run(binding_params)
@@ -94,36 +111,25 @@ class SQLiteService extends SQLService {
94
111
  yield ']'
95
112
  }
96
113
 
97
- async _stream(stmt, binding_params, one) {
98
- const columns = stmt.columns()
99
- // Stream single blob column
100
- if (columns.length === 1 && columns[0].name !== '_json_') {
101
- // Setting result set to raw to keep better-sqlite from doing additional processing
102
- stmt.raw(true)
103
- const rows = stmt.all(binding_params)
104
- // REVISIT: return undefined when no rows are found
105
- if (rows.length === 0) return undefined
106
- if (rows[0][0] === null) return null
107
- // Buffer.from only applies encoding when the input is a string
108
- let raw = Buffer.from(rows[0][0].toString(), 'base64')
109
- stmt.raw(false)
110
- return new Readable({
111
- read(size) {
112
- if (raw.length === 0) return this.push(null)
113
- const chunk = raw.slice(0, size)
114
- raw = raw.slice(size)
115
- this.push(chunk)
116
- },
117
- })
118
- }
114
+ exec(sql) {
115
+ return this.dbc.exec(sql)
116
+ }
119
117
 
120
- stmt.raw(true)
121
- const rs = stmt.iterate(binding_params)
122
- return Readable.from(this._iterator(rs, one))
118
+ _prepareStreams(values) {
119
+ let any
120
+ values.forEach((v, i) => {
121
+ if (v instanceof Readable) {
122
+ any = values[i] = convStrm.buffer(v)
123
+ }
124
+ })
125
+ return any ? Promise.all(values) : values
123
126
  }
124
127
 
125
- exec(sql) {
126
- return this.dbc.exec(sql)
128
+ async onSIMPLE({ query, data }) {
129
+ const { sql, values } = this.cqn2sql(query, data)
130
+ let ps = await this.prepare(sql)
131
+ const vals = await this._prepareStreams(values)
132
+ return (await ps.run(vals)).changes
127
133
  }
128
134
 
129
135
  onPlainSQL({ query, data }, next) {
@@ -154,8 +160,14 @@ class SQLiteService extends SQLService {
154
160
  }
155
161
 
156
162
  val(v) {
163
+ if (Buffer.isBuffer(v.val)) v.val = v.val.toString('base64')
157
164
  // intercept DateTime values and convert to Date objects to compare ISO Strings
158
- if (/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[Z+-]/.test(v.val)) v.val = new Date(v.val)
165
+ else if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(.\d{1,9})?(Z|[+-]\d{2}(:?\d{2})?)$/.test(v.val)) {
166
+ const date = new Date(v.val)
167
+ if (!Number.isNaN(date.getTime())) {
168
+ v.val = date
169
+ }
170
+ }
159
171
  return super.val(v)
160
172
  }
161
173
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js/sqlite",
3
- "version": "1.3.1",
3
+ "version": "1.5.0",
4
4
  "description": "CDS database service for SQLite",
5
5
  "homepage": "https://github.com/cap-js/cds-dbs/tree/main/sqlite#cds-database-service-for-sqlite",
6
6
  "repository": {
@@ -30,11 +30,11 @@
30
30
  "test": "jest --silent"
31
31
  },
32
32
  "dependencies": {
33
- "@cap-js/db-service": "^1.3.1",
34
- "better-sqlite3": "^8"
33
+ "@cap-js/db-service": "^1.6.0",
34
+ "better-sqlite3": "^9.3.0"
35
35
  },
36
36
  "peerDependencies": {
37
- "@sap/cds": ">=7"
37
+ "@sap/cds": ">=7.6"
38
38
  },
39
39
  "cds": {
40
40
  "requires": {