-
Notifications
You must be signed in to change notification settings - Fork 25
feat: support for node:sqlite and sql.js
#614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 14 commits
59bc177
f456747
3a56fe1
e44a447
9b1281a
3ebf2f4
1f5d2c7
c4e2562
6546a7d
8bc8319
19f4043
54c4b27
4235bf9
8f4a3e0
15aa62b
2123ecf
6644bfe
a3367fa
7c00fa9
5c17a7c
51fa585
12be223
94d2df2
2f8ce65
03a4dcb
d67fd65
0055a0d
149df0b
8764066
f8d2604
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,6 +1,18 @@ | ||||||
| const { SQLService } = require('@cap-js/db-service') | ||||||
| const cds = require('@sap/cds') | ||||||
| const sqlite = require('better-sqlite3') | ||||||
| const cds = require('@sap/cds/lib') | ||||||
| let sqlite | ||||||
|
|
||||||
| try { | ||||||
| sqlite = require('./node-sqlite') | ||||||
| } catch { | ||||||
| try { | ||||||
| sqlite = require('better-sqlite3') | ||||||
| } catch (err) { | ||||||
| // When failing to load better-sqlite3 it fallsback to sql.js (wasm version of sqlite) | ||||||
| sqlite = require('./sql.js.js') | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| const $session = Symbol('dbc.session') | ||||||
| const sessionVariableMap = require('./session.json') // Adjust the path as necessary for your project | ||||||
| const convStrm = require('stream/consumers') | ||||||
|
|
@@ -28,9 +40,11 @@ | |||||
| get factory() { | ||||||
| return { | ||||||
| options: this.options.pool || {}, | ||||||
| create: tenant => { | ||||||
| create: async tenant => { | ||||||
| const database = this.url4(tenant) | ||||||
| const dbc = new sqlite(database, this.options.client) | ||||||
| const dbc = new sqlite(database, this.options.client || {}) | ||||||
| await dbc.ready | ||||||
|
|
||||||
| const deterministic = { deterministic: true } | ||||||
| dbc.function('session_context', key => dbc[$session][key]) | ||||||
| dbc.function('regexp', deterministic, (re, x) => (RegExp(re).test(x) ? 1 : 0)) | ||||||
|
|
@@ -41,7 +55,7 @@ | |||||
| dbc.function('hour', deterministic, d => d === null ? null : toDate(d, true).getUTCHours()) | ||||||
| dbc.function('minute', deterministic, d => d === null ? null : toDate(d, true).getUTCMinutes()) | ||||||
| dbc.function('second', deterministic, d => d === null ? null : toDate(d, true).getUTCSeconds()) | ||||||
| if (!dbc.memory) dbc.pragma('journal_mode = WAL') | ||||||
| if (!dbc.memory) dbc.pragma?.('journal_mode = WAL') | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
| return dbc | ||||||
| }, | ||||||
| destroy: dbc => dbc.close(), | ||||||
|
|
@@ -134,10 +148,8 @@ | |||||
| } | ||||||
|
|
||||||
| async _allStream(stmt, binding_params, one, objectMode) { | ||||||
| stmt = stmt.constructor.name === 'Statement' ? stmt : stmt.__proto__ | ||||||
| stmt.raw(true) | ||||||
| const get = stmt.get(binding_params) | ||||||
| if (!get) return [] | ||||||
| stmt = stmt.iterate ? stmt : stmt.__proto__ | ||||||
| stmt.raw?.(true) | ||||||
| const rs = stmt.iterate(binding_params) | ||||||
| const stream = Readable.from(objectMode ? this._iteratorObjectMode(rs) : this._iteratorRaw(rs, one), { objectMode }) | ||||||
| const close = () => rs.return() // finish result set when closed early | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| const { DatabaseSync } = require('node:sqlite'); | ||
|
|
||
| class NodeSqlite extends DatabaseSync { | ||
| prepare(sql) { | ||
| const stmt = super.prepare(sql) | ||
| const ret = { | ||
| run(params) { | ||
| try { | ||
| params = Array.isArray(params) ? params : [params] | ||
| return stmt.run(...params) | ||
| } catch (err) { | ||
| if (err.message.indexOf('NOT NULL constraint failed:') === 0) { | ||
| err.code = 'SQLITE_CONSTRAINT_NOTNULL' | ||
| } | ||
| throw err | ||
| } | ||
| }, | ||
| get(params) { | ||
| params = Array.isArray(params) ? params : [params] | ||
| return stmt.get(...params) | ||
| }, | ||
| all(params) { | ||
| params = Array.isArray(params) ? params : [params] | ||
| return stmt.all(...params) | ||
| }, | ||
| iterate(params) { | ||
| stmt.setReturnArrays(true) | ||
| params = Array.isArray(params) ? params : [params] | ||
| return stmt.iterate(...params) | ||
| } | ||
| } | ||
| return ret | ||
| } | ||
| } | ||
|
|
||
| module.exports = NodeSqlite |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| const initSqlJs = require('sql.js'); | ||
|
|
||
| const init = initSqlJs({}) | ||
|
|
||
| class WasmSqlite { | ||
| constructor(/*database*/) { | ||
| // TODO: load / store database file contents | ||
| this.ready = init | ||
| .then(SQL => { this.db = new SQL.Database() }) | ||
|
|
||
| this.memory = true | ||
| this.gc = new FinalizationRegistry(stmt => { stmt.free() }) | ||
| } | ||
|
|
||
| prepare(sql) { | ||
| const stmt = this.db.prepare(sql) | ||
| const ret = { | ||
| run(params) { | ||
| try { | ||
| stmt.bind(params) | ||
| stmt.step() | ||
| return { changes: stmt.db.getRowsModified(stmt) } | ||
| } catch (err) { | ||
| if (err.message.indexOf('NOT NULL constraint failed:') === 0) { | ||
| err.code = 'SQLITE_CONSTRAINT_NOTNULL' | ||
| } | ||
| throw err | ||
| } | ||
| }, | ||
| get(params) { | ||
| const columns = stmt.getColumnNames() | ||
| stmt.bind(params) | ||
| stmt.step() | ||
| const row = stmt.get() | ||
| const ret = {} | ||
| for (let i = 0; i < columns.length; i++) { | ||
| ret[columns[i]] = row[i] | ||
| } | ||
| return ret | ||
| }, | ||
| all(params) { | ||
| const columns = stmt.getColumnNames() | ||
| const ret = [] | ||
| stmt.bind(params) | ||
| while (stmt.step()) { | ||
| const row = stmt.get() | ||
| const obj = {} | ||
| for (let i = 0; i < columns.length; i++) { | ||
| obj[columns[i]] = row[i] | ||
| } | ||
| ret.push(obj) | ||
| } | ||
| return ret | ||
| }, | ||
| *iterate(params) { | ||
| stmt.bind(params) | ||
| while (stmt.step()) { | ||
| yield stmt.get() | ||
| } | ||
| } | ||
| } | ||
| this.gc.register(ret, stmt) | ||
| return ret | ||
| } | ||
|
|
||
| exec(sql) { | ||
| try { | ||
| const { columns, values } = this.db.exec(sql) | ||
| return !Array.isArray(values) ? values : values.map(val => { | ||
| const ret = {} | ||
| for (let i = 0; i < columns.length; i++) { | ||
| ret[columns[i]] = val[i] | ||
| } | ||
| return ret | ||
| }) | ||
| } catch (err) { | ||
| // REVISIT: address transaction errors | ||
| if (sql === 'BEGIN' || sql === 'ROLLBACK') { return } | ||
| throw err | ||
| } | ||
| } | ||
|
|
||
| function(name, config, func) { | ||
| this.db.create_function(name, func || config) | ||
| } | ||
|
|
||
| close() { this.db.close() } | ||
| } | ||
|
|
||
| module.exports = WasmSqlite |
Uh oh!
There was an error while loading. Please reload this page.