@cap-js/sqlite 1.4.0 → 1.5.1

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,30 @@
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.5.1](https://github.com/cap-js/cds-dbs/compare/sqlite-v1.5.0...sqlite-v1.5.1) (2024-02-16)
8
+
9
+
10
+ ### Fixed
11
+
12
+ * **`sqlite`:** Retain Error object for unique constraint violation ([#446](https://github.com/cap-js/cds-dbs/issues/446)) ([d27ee79](https://github.com/cap-js/cds-dbs/commit/d27ee79b4c4eea8522bf5dd2a288638f54029567))
13
+
14
+ ## [1.5.0](https://github.com/cap-js/cds-dbs/compare/sqlite-v1.4.0...sqlite-v1.5.0) (2024-02-02)
15
+
16
+
17
+ ### Added
18
+
19
+ * 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))
20
+ * 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))
21
+
22
+
23
+ ### Fixed
24
+
25
+ * 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))
26
+ * 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))
27
+ * 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))
28
+ * 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)
29
+ * 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))
30
+
7
31
  ## [1.4.0](https://github.com/cap-js/cds-dbs/compare/sqlite-v1.3.1...sqlite-v1.4.0) (2023-11-20)
8
32
 
9
33
 
@@ -1,11 +1,15 @@
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 },
@@ -33,9 +37,6 @@ class SQLiteService extends SQLService {
33
37
  dbc.function('minute', deterministic, d => d === null ? null : toDate(d, true).getUTCMinutes())
34
38
  dbc.function('second', deterministic, d => d === null ? null : toDate(d, true).getUTCSeconds())
35
39
 
36
- dbc.function('json_merge', { varargs: true, deterministic: true }, (...args) =>
37
- args.join('').replace(/}{/g, ','),
38
- )
39
40
  if (!dbc.memory) dbc.pragma('journal_mode = WAL')
40
41
  return dbc
41
42
  },
@@ -80,13 +81,11 @@ class SQLiteService extends SQLService {
80
81
  async _run(stmt, binding_params) {
81
82
  for (let i = 0; i < binding_params.length; i++) {
82
83
  const val = binding_params[i]
84
+ if (val instanceof Readable) {
85
+ binding_params[i] = await convStrm[val.type === 'json' ? 'text' : 'buffer'](val)
86
+ }
83
87
  if (Buffer.isBuffer(val)) {
84
- binding_params[i] = Buffer.from(val.base64Slice())
85
- } else if (typeof val === 'object' && val && val.pipe) {
86
- // REVISIT: stream.setEncoding('base64') sometimes misses the last bytes
87
- // if (val.type === 'binary') val.setEncoding('base64')
88
- binding_params[i] = await convStrm.buffer(val)
89
- if (val.type === 'binary') binding_params[i] = Buffer.from(binding_params[i].toString('base64'))
88
+ binding_params[i] = Buffer.from(val.toString('base64'))
90
89
  }
91
90
  }
92
91
  return stmt.run(binding_params)
@@ -112,36 +111,25 @@ class SQLiteService extends SQLService {
112
111
  yield ']'
113
112
  }
114
113
 
115
- async _stream(stmt, binding_params, one) {
116
- const columns = stmt.columns()
117
- // Stream single blob column
118
- if (columns.length === 1 && columns[0].name !== '_json_') {
119
- // Setting result set to raw to keep better-sqlite from doing additional processing
120
- stmt.raw(true)
121
- const rows = stmt.all(binding_params)
122
- // REVISIT: return undefined when no rows are found
123
- if (rows.length === 0) return undefined
124
- if (rows[0][0] === null) return null
125
- // Buffer.from only applies encoding when the input is a string
126
- let raw = Buffer.from(rows[0][0].toString(), 'base64')
127
- stmt.raw(false)
128
- return new Readable({
129
- read(size) {
130
- if (raw.length === 0) return this.push(null)
131
- const chunk = raw.slice(0, size)
132
- raw = raw.slice(size)
133
- this.push(chunk)
134
- },
135
- })
136
- }
114
+ exec(sql) {
115
+ return this.dbc.exec(sql)
116
+ }
137
117
 
138
- stmt.raw(true)
139
- const rs = stmt.iterate(binding_params)
140
- 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
141
126
  }
142
127
 
143
- exec(sql) {
144
- 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
145
133
  }
146
134
 
147
135
  onPlainSQL({ query, data }, next) {
@@ -172,8 +160,14 @@ class SQLiteService extends SQLService {
172
160
  }
173
161
 
174
162
  val(v) {
163
+ if (Buffer.isBuffer(v.val)) v.val = v.val.toString('base64')
175
164
  // intercept DateTime values and convert to Date objects to compare ISO Strings
176
- 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
+ }
177
171
  return super.val(v)
178
172
  }
179
173
 
@@ -197,7 +191,8 @@ class SQLiteService extends SQLService {
197
191
 
198
192
  // Structs and arrays are stored as JSON strings; the ->'$' unwraps them.
199
193
  // Otherwise they would be added as strings to json_objects.
200
- struct: expr => `${expr}->'$'`, // Association + Composition inherits from struct
194
+ Association: expr => `${expr}->'$'`,
195
+ struct: expr => `${expr}->'$'`,
201
196
  array: expr => `${expr}->'$'`,
202
197
 
203
198
  // SQLite has no booleans so we need to convert 0 and 1
@@ -250,7 +245,7 @@ class SQLiteService extends SQLService {
250
245
  try {
251
246
  return await super.onINSERT(req)
252
247
  } catch (err) {
253
- throw _not_unique(err, 'ENTITY_ALREADY_EXISTS') || err
248
+ throw _not_unique(err, 'ENTITY_ALREADY_EXISTS')
254
249
  }
255
250
  }
256
251
 
@@ -258,13 +253,13 @@ class SQLiteService extends SQLService {
258
253
  try {
259
254
  return await super.onUPDATE(req)
260
255
  } catch (err) {
261
- throw _not_unique(err, 'UNIQUE_CONSTRAINT_VIOLATION') || err
256
+ throw _not_unique(err, 'UNIQUE_CONSTRAINT_VIOLATION')
262
257
  }
263
258
  }
264
259
  }
265
260
 
266
261
  // function _not_null (err) {
267
- // if (err.code === "SQLITE_CONSTRAINT_NOTNULL") return Object.assign ({
262
+ // if (err.code === "SQLITE_CONSTRAINT_NOTNULL") return Object.assign (err, {
268
263
  // code: 'MUST_NOT_BE_NULL',
269
264
  // target: /\.(.*?)$/.exec(err.message)[1], // here we are even constructing OData responses, with .target
270
265
  // message: 'Value is required',
@@ -273,11 +268,12 @@ class SQLiteService extends SQLService {
273
268
 
274
269
  function _not_unique(err, code) {
275
270
  if (err.message.match(/unique constraint/i))
276
- return Object.assign({
271
+ return Object.assign(err, {
277
272
  originalMessage: err.message, // FIXME: required because of next line
278
273
  message: code, // FIXME: misusing message as code
279
274
  code: 400, // FIXME: misusing code as (http) status
280
275
  })
276
+ return err
281
277
  }
282
278
 
283
279
  module.exports = SQLiteService
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js/sqlite",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
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": "^9"
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": {