Skip to content

Commit bd2abc6

Browse files
committed
Tests, fixes, cleanup.
1 parent 80e0b51 commit bd2abc6

11 files changed

+281
-23
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules/
2+
test-db/
3+
*.db

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
{
2+
"scripts": {
3+
"test": "vitest"
4+
},
25
"dependencies": {
36
"better-sqlite3": "^9.5.0",
47
"event-iterator": "^2.0.0"

src/driver-api.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { SqliteArguments, SqliteValue } from "./common.js";
22

33
export interface SqliteDriverConnection {
44
prepare(query: string): SqliteDriverStatement;
5+
close(): Promise<void>;
56
}
67

78
export interface SqliteDriverConnectionPool {
@@ -14,6 +15,8 @@ export interface SqliteDriverConnectionPool {
1415
reserveConnection(
1516
options?: ReserveConnectionOptions
1617
): Promise<ReservedConnection>;
18+
19+
close(): Promise<void>;
1720
}
1821

1922
export interface ReservedConnection {
@@ -27,16 +30,27 @@ export interface ReserveConnectionOptions {
2730
}
2831

2932
export interface SqliteDriverStatement {
30-
execute(args?: SqliteArguments): Promise<void>;
33+
run(args?: SqliteArguments): Promise<void>;
34+
runWithResults(args?: SqliteArguments): Promise<RunResults>;
3135

32-
stream(
36+
selectStreamed(
3337
args?: SqliteArguments,
3438
options?: ExecuteOptions
3539
): AsyncIterable<ResultSet>;
3640

41+
selectAll(
42+
args?: SqliteArguments,
43+
options?: ExecuteOptions
44+
): Promise<ResultSet>;
45+
3746
dispose(): void;
3847
}
3948

49+
export interface RunResults {
50+
changes: number;
51+
lastInsertRowId: bigint;
52+
}
53+
4054
export interface ResultSet {
4155
columns: string[];
4256
rows: SqliteValue[][];

src/driver-util.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ export class SingleConnectionPool implements SqliteDriverConnectionPool {
1717

1818
constructor(private connection: SqliteDriverConnection) {}
1919

20+
async close() {
21+
await this.connection.close();
22+
}
23+
2024
reserveConnection(
2125
options?: ReserveConnectionOptions
2226
): Promise<ReservedConnection> {
@@ -150,6 +154,13 @@ class MultiConnectionPool implements SqliteDriverConnectionPool {
150154
},
151155
});
152156
}
157+
158+
async close() {
159+
// TODO: Wait for statements to finish
160+
for (let con of this._allConnections) {
161+
await con.close();
162+
}
163+
}
153164
}
154165

155166
export class ReadWriteConnectionPool implements SqliteDriverConnectionPool {
@@ -177,4 +188,9 @@ export class ReadWriteConnectionPool implements SqliteDriverConnectionPool {
177188
return this.writePool!.reserveConnection(options);
178189
}
179190
}
191+
192+
async close() {
193+
await this.readPool.close();
194+
await this.writePool?.close();
195+
}
180196
}

src/drivers/better-sqlite3-async-driver.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import type * as bsqlite from "better-sqlite3";
22
import * as worker_threads from "worker_threads";
33
import {
4+
ResultSet,
5+
RunResults,
46
SqliteDriverConnection,
57
SqliteDriverConnectionPool,
68
SqliteDriverStatement,
@@ -37,19 +39,37 @@ export class BetterSqliteAsyncConnection implements SqliteDriverConnection {
3739
this.worker = worker;
3840
}
3941

42+
async close() {
43+
this.worker.postMessage(["close"]);
44+
await new Promise<void>((resolve, reject) => {
45+
this.worker.once("message", (value) => {
46+
resolve();
47+
});
48+
});
49+
await this.worker.terminate();
50+
}
51+
4052
prepare(query: string): SqliteDriverStatement {
4153
const worker = this.worker;
4254
return {
4355
dispose() {},
44-
execute: async (args) => {
45-
worker.postMessage(["execute", { query, args }]);
56+
run: async (args) => {
57+
worker.postMessage(["run", { query, args }]);
4658
return new Promise<void>((resolve, reject) => {
4759
worker.once("message", (value) => {
4860
resolve();
4961
});
5062
});
5163
},
52-
async *stream(args, options) {
64+
runWithResults: async (args) => {
65+
worker.postMessage(["run", { query, args }]);
66+
return new Promise<RunResults>((resolve, reject) => {
67+
worker.once("message", (value) => {
68+
resolve(value);
69+
});
70+
});
71+
},
72+
async *selectStreamed(args, options) {
5373
worker.postMessage(["stream", { query, args, options }]);
5474
const iter = new EventIterator(({ push }) => {
5575
worker.addListener("message", push);
@@ -67,6 +87,18 @@ export class BetterSqliteAsyncConnection implements SqliteDriverConnection {
6787
}
6888
}
6989
},
90+
91+
async selectAll(args, options): Promise<ResultSet> {
92+
let results: ResultSet | undefined = undefined;
93+
for await (let rs of this.selectStreamed(args, options)) {
94+
if (results == null) {
95+
results = rs;
96+
} else {
97+
results!.rows.push(...rs.rows);
98+
}
99+
}
100+
return results!;
101+
},
70102
};
71103
}
72104
}

src/drivers/better-sqlite3-driver.ts

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { SqliteArguments, SqliteValue } from "../common.js";
22
import {
33
ExecuteOptions,
44
ResultSet,
5+
RunResults,
56
SqliteDriverConnection,
67
SqliteDriverConnectionPool,
78
SqliteDriverStatement,
@@ -29,20 +30,44 @@ export class BetterSqliteConnection implements SqliteDriverConnection {
2930
con: bsqlite.Database;
3031

3132
constructor(path: string, options?: bsqlite.Options) {
32-
console.log("options", options);
3333
this.con = new Database(path, options);
3434
}
3535

3636
prepare(query: string): SqliteDriverStatement {
3737
const stmt = this.con.prepare(query);
3838
return new BetterSqliteStatement(stmt);
3939
}
40+
41+
async close() {
42+
this.con.close();
43+
}
4044
}
4145

4246
export class BetterSqliteStatement implements SqliteDriverStatement {
4347
constructor(private statement: bsqlite.Statement) {}
4448

45-
async *stream(
49+
async selectAll(
50+
args?: SqliteArguments | undefined,
51+
options?: ExecuteOptions | undefined
52+
): Promise<ResultSet> {
53+
const bindArgs = args == undefined ? [] : [args];
54+
if (!this.statement.reader) {
55+
this.statement.run(...bindArgs);
56+
return { columns: [], rows: [] };
57+
}
58+
this.statement.raw();
59+
if (options?.bigint) {
60+
this.statement.safeIntegers();
61+
}
62+
const columns = this.statement.columns().map((c) => c.name);
63+
const rows = this.statement.all(...bindArgs) as SqliteValue[][];
64+
return {
65+
columns,
66+
rows,
67+
};
68+
}
69+
70+
async *selectStreamed(
4671
args?: SqliteArguments,
4772
options?: ExecuteOptions
4873
): AsyncGenerator<ResultSet, any, undefined> {
@@ -58,7 +83,7 @@ export class BetterSqliteStatement implements SqliteDriverStatement {
5883
const columns = this.statement.columns().map((c) => c.name);
5984
let buffer: SqliteValue[][] = [];
6085
let didYield = false;
61-
for (let row of this.statement.all(...bindArgs)) {
86+
for (let row of this.statement.iterate(...bindArgs)) {
6287
buffer.push(row as SqliteValue[]);
6388
if (buffer.length > (options?.chunkSize ?? 10)) {
6489
yield {
@@ -77,8 +102,18 @@ export class BetterSqliteStatement implements SqliteDriverStatement {
77102
}
78103
}
79104

80-
async execute(args?: SqliteArguments): Promise<void> {
81-
this.statement.run();
105+
async run(args?: SqliteArguments): Promise<void> {
106+
const bindArgs = args == undefined ? [] : [args];
107+
this.statement.run(...bindArgs);
108+
}
109+
110+
async runWithResults(args?: SqliteArguments): Promise<RunResults> {
111+
const bindArgs = args == undefined ? [] : [args];
112+
const r = this.statement.run(...bindArgs);
113+
return {
114+
changes: r.changes,
115+
lastInsertRowId: BigInt(r.lastInsertRowid),
116+
};
82117
}
83118

84119
dispose(): void {

src/drivers/better-sqlite3-worker.js

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,20 @@ worker_threads.parentPort.addListener("message", (value) => {
88
const [message, args] = value;
99
if (message == "open") {
1010
db = new Database(args.path);
11-
} else if (message == "execute") {
12-
if (args.args == null) {
13-
const rs = db.prepare(args.query).all();
14-
worker_threads.parentPort.postMessage(rs);
15-
} else {
16-
const rs = db.prepare(args.query).all(args.args);
17-
worker_threads.parentPort.postMessage(rs);
18-
}
11+
} else if (message == "close") {
12+
db?.close();
13+
worker_threads.parentPort.postMessage(["closed"]);
14+
} else if (message == "run") {
15+
const bindArgs = args.args == undefined ? [] : [args.args];
16+
const rs = db.prepare(args.query).run(...bindArgs);
17+
worker_threads.parentPort.postMessage({
18+
changes: rs.changes,
19+
lastInsertRowId: BigInt(rs.lastInsertRowid),
20+
});
21+
} else if (message == "selectAll") {
22+
const bindArgs = args.args == undefined ? [] : [args.args];
23+
const rs = db.prepare(args.query).all(...bindArgs);
24+
worker_threads.parentPort.postMessage(rs);
1925
} else if (message == "stream") {
2026
const bindArgs = args.args == undefined ? [] : [args.args];
2127
const statement = db.prepare(args.query);

src/impl.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,15 @@ export class ConnectionImpl implements SqliteConnection {
8585
callback: (tx: SqliteTransaction) => Promise<T>,
8686
options: TransactionOptions
8787
): Promise<T> {
88-
await this.driver.prepare("BEGIN").execute();
88+
await this.driver.prepare("BEGIN").run();
8989
try {
9090
const tx = new TransactionImpl(this);
9191
const result = await callback(tx);
9292

93-
await this.driver.prepare("COMMIT").execute();
93+
await this.driver.prepare("COMMIT").run();
9494
return result;
9595
} catch (e) {
96-
await this.driver.prepare("ROLLBACK").execute();
96+
await this.driver.prepare("ROLLBACK").run();
9797
throw e;
9898
}
9999
}
@@ -135,7 +135,7 @@ export class ConnectionImpl implements SqliteConnection {
135135

136136
const q = this.driver.prepare(query as string);
137137

138-
for await (let rs of q.stream(args)) {
138+
for await (let rs of q.selectStreamed(args)) {
139139
if (result == null) {
140140
result = new ResultSetImpl(rs.columns, [...rs.rows]);
141141
} else {
@@ -152,7 +152,7 @@ export class ConnectionImpl implements SqliteConnection {
152152
): AsyncGenerator<ResultSet<T>, void, unknown> {
153153
const q = this.driver.prepare(query as string);
154154

155-
for await (let rs of q.stream(args, options)) {
155+
for await (let rs of q.selectStreamed(args, options)) {
156156
yield new ResultSetImpl(rs.columns, rs.rows);
157157
}
158158
}

test/better-sqlite3-async.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { betterSqliteAsyncPool } from "../src/drivers/better-sqlite3-async-driver.js";
2+
import { describeDriverTests } from "./tests/drivers.js";
3+
4+
describeDriverTests("better-sqlite3-async-pool", betterSqliteAsyncPool);

test/better-sqlite3.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { betterSqlitePool } from "../src/drivers/better-sqlite3-driver.js";
2+
import { describeDriverTests } from "./tests/drivers.js";
3+
4+
describeDriverTests("better-sqlite3", betterSqlitePool);

0 commit comments

Comments
 (0)