@cutils/sqlite 0.1.0 → 0.2.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/README.md +21 -5
- package/build/libsqlite3-shim.a +0 -0
- package/dist/esm/node-binding.mjs +120 -0
- package/package.json +13 -4
- package/vendor/shim/sqlite_shim.c +8 -1
package/README.md
CHANGED
|
@@ -112,11 +112,27 @@ no perf claims until the bench cell lands):
|
|
|
112
112
|
## Node usage
|
|
113
113
|
|
|
114
114
|
The npm package ships `dist/esm`. Under Node the emitted `shim_*` calls resolve
|
|
115
|
-
on `globalThis` —
|
|
116
|
-
`build/libcutils_sqlite.so`
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
on `globalThis` — an official [koffi](https://koffi.dev) binding loads
|
|
116
|
+
`build/libcutils_sqlite.so` for you:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
# one-time, inside the installed package (needs a C toolchain):
|
|
120
|
+
npm run build:shared --prefix node_modules/@cutils/sqlite
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import "koffi"; // ensure the optional peer is installed
|
|
125
|
+
import "@cutils/sqlite/node-binding"; // installs the shim_* globals
|
|
126
|
+
import { Database } from "@cutils/sqlite";
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`koffi` is an **optional peer dependency** — install it when you run under
|
|
130
|
+
Node; scriptc builds never see it. The `.so` is linked `-Wl,-Bsymbolic` so the
|
|
131
|
+
vendored amalgamation's `sqlite3_*` symbols can't interpose against a host's
|
|
132
|
+
sqlite — this is load-bearing, Node ≥ 22 exports its own sqlite3 symbols.
|
|
133
|
+
|
|
134
|
+
Node ≥ 22 is the runtime floor; the test suite also runs there (no
|
|
135
|
+
`using`-syntax — dispose paths are exercised via `[Symbol.dispose]()`).
|
|
120
136
|
|
|
121
137
|
## Subset quirks
|
|
122
138
|
|
package/build/libsqlite3-shim.a
CHANGED
|
Binary file
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// Installs the `shim_*` globals the emitted dist/esm/internal/ffi.js calls,
|
|
2
|
+
// backed by the shared build (build/libcutils_sqlite.so) through koffi.
|
|
3
|
+
// The scriptc span ABI means string/bytes params cross as (ptr,len) pairs —
|
|
4
|
+
// koffi can't produce that layout from a JS string, so each binding is
|
|
5
|
+
// wrapped in a JS adapter that encodes the span explicitly. Handles cross
|
|
6
|
+
// as uint64 bigints (pointer-sized on x86_64/aarch64).
|
|
7
|
+
|
|
8
|
+
import koffi from "koffi";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
|
|
12
|
+
const dir = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const lib = koffi.load(path.join(dir, "..", "..", "build", "libcutils_sqlite.so"));
|
|
14
|
+
|
|
15
|
+
const g = globalThis;
|
|
16
|
+
const enc = new TextEncoder();
|
|
17
|
+
|
|
18
|
+
const f = {
|
|
19
|
+
open: lib.func("uint64 shim_open(const uint8_t *path, uint64 plen, int32 flags)"),
|
|
20
|
+
close: lib.func("int32 shim_close(uint64 db)"),
|
|
21
|
+
errcode: lib.func("int32 shim_errcode(uint64 db)"),
|
|
22
|
+
errmsg: lib.func("int32 shim_errmsg(uint64 db, uint8_t *buf, uint64 len)"),
|
|
23
|
+
inTransaction: lib.func("int32 shim_in_transaction(uint64 db)"),
|
|
24
|
+
exec: lib.func("int32 shim_exec(uint64 db, const uint8_t *sql, uint64 slen)"),
|
|
25
|
+
begin: lib.func("int32 shim_begin(uint64 db)"),
|
|
26
|
+
commit: lib.func("int32 shim_commit(uint64 db)"),
|
|
27
|
+
rollback: lib.func("int32 shim_rollback(uint64 db)"),
|
|
28
|
+
savepoint: lib.func(
|
|
29
|
+
"int32 shim_savepoint(uint64 db, const uint8_t *name, uint64 nlen, int32 op)",
|
|
30
|
+
),
|
|
31
|
+
prepare: lib.func("uint64 shim_prepare(uint64 db, const uint8_t *sql, uint64 slen)"),
|
|
32
|
+
step: lib.func("int32 shim_step(uint64 stmt)"),
|
|
33
|
+
reset: lib.func("int32 shim_reset(uint64 stmt)"),
|
|
34
|
+
finalize: lib.func("int32 shim_finalize(uint64 stmt)"),
|
|
35
|
+
clearBindings: lib.func("int32 shim_clear_bindings(uint64 stmt)"),
|
|
36
|
+
stmtReadonly: lib.func("int32 shim_stmt_readonly(uint64 stmt)"),
|
|
37
|
+
expandedSql: lib.func("int32 shim_expanded_sql(uint64 stmt, uint8_t *buf, uint64 len)"),
|
|
38
|
+
bindCount: lib.func("int32 shim_bind_count(uint64 stmt)"),
|
|
39
|
+
bindIndex: lib.func("int32 shim_bind_index(uint64 stmt, const uint8_t *name, uint64 nlen)"),
|
|
40
|
+
bindInt: lib.func("int32 shim_bind_int(uint64 stmt, int32 idx, int64 val)"),
|
|
41
|
+
bindDbl: lib.func("int32 shim_bind_dbl(uint64 stmt, int32 idx, double val)"),
|
|
42
|
+
bindText: lib.func(
|
|
43
|
+
"int32 shim_bind_text(uint64 stmt, int32 idx, const uint8_t *val, uint64 vlen)",
|
|
44
|
+
),
|
|
45
|
+
bindBlob: lib.func(
|
|
46
|
+
"int32 shim_bind_blob(uint64 stmt, int32 idx, const uint8_t *val, uint64 vlen)",
|
|
47
|
+
),
|
|
48
|
+
bindNull: lib.func("int32 shim_bind_null(uint64 stmt, int32 idx)"),
|
|
49
|
+
columnCount: lib.func("int32 shim_column_count(uint64 stmt)"),
|
|
50
|
+
columnName: lib.func("int32 shim_column_name(uint64 stmt, int32 idx, uint8_t *buf, uint64 len)"),
|
|
51
|
+
columnMeta: lib.func(
|
|
52
|
+
"int32 shim_column_meta(uint64 stmt, int32 idx, int32 which, uint8_t *buf, uint64 len)",
|
|
53
|
+
),
|
|
54
|
+
columnType: lib.func("int32 shim_column_type(uint64 stmt, int32 idx)"),
|
|
55
|
+
columnInt64: lib.func("int64 shim_column_int64(uint64 stmt, int32 idx)"),
|
|
56
|
+
columnDbl: lib.func("double shim_column_dbl(uint64 stmt, int32 idx)"),
|
|
57
|
+
columnText: lib.func("int32 shim_column_text(uint64 stmt, int32 idx, uint8_t *buf, uint64 len)"),
|
|
58
|
+
columnBlob: lib.func("int32 shim_column_blob(uint64 stmt, int32 idx, uint8_t *buf, uint64 len)"),
|
|
59
|
+
lastInsertRowid: lib.func("int64 shim_last_insert_rowid(uint64 db)"),
|
|
60
|
+
changes: lib.func("int32 shim_changes(uint64 db)"),
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
function span(s) {
|
|
64
|
+
const b = enc.encode(s);
|
|
65
|
+
return [b, BigInt(b.length)];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
g.shim_open = (path, flags) => {
|
|
69
|
+
const [b, n] = span(path);
|
|
70
|
+
return f.open(b, n, flags);
|
|
71
|
+
};
|
|
72
|
+
g.shim_close = (db) => f.close(db);
|
|
73
|
+
g.shim_errcode = (db) => f.errcode(db);
|
|
74
|
+
g.shim_errmsg = (db, buf) => f.errmsg(db, buf, BigInt(buf.length));
|
|
75
|
+
g.shim_in_transaction = (db) => f.inTransaction(db);
|
|
76
|
+
g.shim_exec = (db, sql) => {
|
|
77
|
+
const [b, n] = span(sql);
|
|
78
|
+
return f.exec(db, b, n);
|
|
79
|
+
};
|
|
80
|
+
g.shim_begin = (db) => f.begin(db);
|
|
81
|
+
g.shim_commit = (db) => f.commit(db);
|
|
82
|
+
g.shim_rollback = (db) => f.rollback(db);
|
|
83
|
+
g.shim_savepoint = (db, name, op) => {
|
|
84
|
+
const [b, n] = span(name);
|
|
85
|
+
return f.savepoint(db, b, n, op);
|
|
86
|
+
};
|
|
87
|
+
g.shim_prepare = (db, sql) => {
|
|
88
|
+
const [b, n] = span(sql);
|
|
89
|
+
return f.prepare(db, b, n);
|
|
90
|
+
};
|
|
91
|
+
g.shim_step = (stmt) => f.step(stmt);
|
|
92
|
+
g.shim_reset = (stmt) => f.reset(stmt);
|
|
93
|
+
g.shim_finalize = (stmt) => f.finalize(stmt);
|
|
94
|
+
g.shim_clear_bindings = (stmt) => f.clearBindings(stmt);
|
|
95
|
+
g.shim_stmt_readonly = (stmt) => f.stmtReadonly(stmt);
|
|
96
|
+
g.shim_expanded_sql = (stmt, buf) => f.expandedSql(stmt, buf, BigInt(buf.length));
|
|
97
|
+
g.shim_bind_count = (stmt) => f.bindCount(stmt);
|
|
98
|
+
g.shim_bind_index = (stmt, name) => {
|
|
99
|
+
const [b, n] = span(name);
|
|
100
|
+
return f.bindIndex(stmt, b, n);
|
|
101
|
+
};
|
|
102
|
+
g.shim_bind_int = (stmt, idx, val) => f.bindInt(stmt, idx, val);
|
|
103
|
+
g.shim_bind_dbl = (stmt, idx, val) => f.bindDbl(stmt, idx, val);
|
|
104
|
+
g.shim_bind_text = (stmt, idx, val) => {
|
|
105
|
+
const [b, n] = span(val);
|
|
106
|
+
return f.bindText(stmt, idx, b, n);
|
|
107
|
+
};
|
|
108
|
+
g.shim_bind_blob = (stmt, idx, val) => f.bindBlob(stmt, idx, val, BigInt(val.length));
|
|
109
|
+
g.shim_bind_null = (stmt, idx) => f.bindNull(stmt, idx);
|
|
110
|
+
g.shim_column_count = (stmt) => f.columnCount(stmt);
|
|
111
|
+
g.shim_column_name = (stmt, idx, buf) => f.columnName(stmt, idx, buf, BigInt(buf.length));
|
|
112
|
+
g.shim_column_meta = (stmt, idx, which, buf) =>
|
|
113
|
+
f.columnMeta(stmt, idx, which, buf, BigInt(buf.length));
|
|
114
|
+
g.shim_column_type = (stmt, idx) => f.columnType(stmt, idx);
|
|
115
|
+
g.shim_column_int64 = (stmt, idx) => f.columnInt64(stmt, idx);
|
|
116
|
+
g.shim_column_dbl = (stmt, idx) => f.columnDbl(stmt, idx);
|
|
117
|
+
g.shim_column_text = (stmt, idx, buf) => f.columnText(stmt, idx, buf, BigInt(buf.length));
|
|
118
|
+
g.shim_column_blob = (stmt, idx, buf) => f.columnBlob(stmt, idx, buf, BigInt(buf.length));
|
|
119
|
+
g.shim_last_insert_rowid = (db) => f.lastInsertRowid(db);
|
|
120
|
+
g.shim_changes = (db) => f.changes(db);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cutils/sqlite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Embedded SQLite for chttp/scriptc apps — vendored amalgamation, FFI shim, Standard-Schema row seam. Compiles to a single static ELF.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"types": "./dist/esm/index.d.ts",
|
|
23
23
|
"import": "./dist/esm/index.js"
|
|
24
24
|
},
|
|
25
|
-
"./package.json": "./package.json"
|
|
25
|
+
"./package.json": "./package.json",
|
|
26
|
+
"./node-binding": "./dist/esm/node-binding.mjs"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
29
|
"@chttp/val": "^0.2.1",
|
|
@@ -35,6 +36,14 @@
|
|
|
35
36
|
"engines": {
|
|
36
37
|
"node": ">=22"
|
|
37
38
|
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"koffi": "^2.14.1"
|
|
41
|
+
},
|
|
42
|
+
"peerDependenciesMeta": {
|
|
43
|
+
"koffi": {
|
|
44
|
+
"optional": true
|
|
45
|
+
}
|
|
46
|
+
},
|
|
38
47
|
"author": "Joaquin Terrasa & contributors",
|
|
39
48
|
"keywords": [
|
|
40
49
|
"sqlite",
|
|
@@ -56,9 +65,9 @@
|
|
|
56
65
|
"build:lib": "ranlib build/libsqlite3-shim.a",
|
|
57
66
|
"build:shared": "cc -O2 -fPIC -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_ENABLE_COLUMN_METADATA -Wl,-Bsymbolic -shared vendor/shim/sqlite_shim.c vendor/sqlite-amalgamation/sqlite3.c -o build/libcutils_sqlite.so",
|
|
58
67
|
"build:ts": "tsc -p tsconfig.json",
|
|
59
|
-
"build": "mkdir -p build && npm run build:c && npm run build:sqlite && npm run build:archive && npm run build:lib && npm run build:ts",
|
|
68
|
+
"build": "mkdir -p build && npm run build:c && npm run build:sqlite && npm run build:archive && npm run build:lib && npm run build:ts && sed 's|\"\\.\\.\", \"build\"|\"..\", \"..\", \"build\"|' test/koffi-binding.mjs > dist/esm/node-binding.mjs",
|
|
60
69
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
61
|
-
"test": "npm run build && npm run build:shared && npm run test:c && npm run test:node",
|
|
70
|
+
"test": "npm run build && npm run build:shared && npm run test:c && npm run test:node && bash test/elf.test.sh",
|
|
62
71
|
"test:c": "cc -O2 -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_ENABLE_COLUMN_METADATA test/shim.test.c build/libsqlite3-shim.a -o build/shim_test -lm && ./build/shim_test",
|
|
63
72
|
"test:node": "node --test test/*.test.js test/*.test.mjs"
|
|
64
73
|
}
|
|
@@ -235,9 +235,14 @@ int32_t shim_bind_dbl(void *stmt, int32_t idx, double val) {
|
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
/* The text span is length-delimited; SQLITE_TRANSIENT copies the bytes
|
|
238
|
-
* inside the call — required because the borrow is call-scoped.
|
|
238
|
+
* inside the call — required because the borrow is call-scoped.
|
|
239
|
+
* An EMPTY span may arrive as a NULL pointer (koffi marshals an empty
|
|
240
|
+
* Uint8Array as NULL; the ELF span for "" can also carry address 0) and
|
|
241
|
+
* sqlite3_bind_text(NULL, n) binds SQL NULL — substitute a non-NULL
|
|
242
|
+
* literal so '' stays ''. */
|
|
239
243
|
int32_t shim_bind_text(void *stmt, int32_t idx, const uint8_t *val,
|
|
240
244
|
size_t vlen) {
|
|
245
|
+
if (val == NULL) val = (const uint8_t *)"";
|
|
241
246
|
return (int32_t)sqlite3_bind_text((sqlite3_stmt *)stmt, (int)idx,
|
|
242
247
|
(const char *)val, (int)vlen,
|
|
243
248
|
SQLITE_TRANSIENT);
|
|
@@ -245,6 +250,8 @@ int32_t shim_bind_text(void *stmt, int32_t idx, const uint8_t *val,
|
|
|
245
250
|
|
|
246
251
|
int32_t shim_bind_blob(void *stmt, int32_t idx, const uint8_t *val,
|
|
247
252
|
size_t vlen) {
|
|
253
|
+
/* Same empty-span NULL trap as shim_bind_text. */
|
|
254
|
+
if (val == NULL) val = (const uint8_t *)"";
|
|
248
255
|
return (int32_t)sqlite3_bind_blob((sqlite3_stmt *)stmt, (int)idx, val,
|
|
249
256
|
(int)vlen, SQLITE_TRANSIENT);
|
|
250
257
|
}
|