Skip to content

Commit 1355702

Browse files
authored
Various async fixes (#197)
2 parents e594032 + cca242d commit 1355702

File tree

8 files changed

+216
-215
lines changed

8 files changed

+216
-215
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ edition = "2021"
1010
crate-type = ["cdylib"]
1111

1212
[dependencies]
13-
libsql = { version = "0.9.18", features = ["encryption"] }
13+
libsql = { version = "0.9.20", features = ["encryption"] }
1414
napi = { version = "2", default-features = false, features = ["napi6", "tokio_rt", "async"] }
1515
napi-derive = "2"
1616
once_cell = "1.18.0"

index.d.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ export interface SyncResult {
2525
export declare function databasePrepareSync(db: Database, sql: string): Statement
2626
/** Syncs the database in blocking mode. */
2727
export declare function databaseSyncSync(db: Database): SyncResult
28+
/** Executes SQL in blocking mode. */
29+
export declare function databaseExecSync(db: Database, sql: string): void
30+
/** Gets first row from statement in blocking mode. */
31+
export declare function statementGetSync(stmt: Statement, params?: unknown | undefined | null): unknown
32+
/** Runs a statement in blocking mode. */
33+
export declare function statementRunSync(stmt: Statement, params?: unknown | undefined | null): RunResult
2834
export declare function statementIterateSync(stmt: Statement, params?: unknown | undefined | null): RowsIterator
2935
/** SQLite `run()` result object */
3036
export interface RunResult {
@@ -109,7 +115,7 @@ export declare class Database {
109115
* * `env` - The environment.
110116
* * `sql` - The SQL statement to execute.
111117
*/
112-
exec(sql: string): void
118+
exec(sql: string): Promise<void>
113119
/**
114120
* Syncs the database.
115121
*
@@ -146,7 +152,7 @@ export declare class Statement {
146152
*
147153
* * `params` - The parameters to bind to the statement.
148154
*/
149-
run(params?: unknown | undefined | null): RunResult
155+
run(params?: unknown | undefined | null): object
150156
/**
151157
* Executes a SQL statement and returns the first row.
152158
*
@@ -155,7 +161,7 @@ export declare class Statement {
155161
* * `env` - The environment.
156162
* * `params` - The parameters to bind to the statement.
157163
*/
158-
get(params?: unknown | undefined | null): unknown
164+
get(params?: unknown | undefined | null): object
159165
/**
160166
* Create an iterator over the rows of a statement.
161167
*

index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,12 +310,15 @@ if (!nativeBinding) {
310310
throw new Error(`Failed to load native binding`)
311311
}
312312

313-
const { Database, databasePrepareSync, databaseSyncSync, Statement, statementIterateSync, RowsIterator, iteratorNextSync, Record } = nativeBinding
313+
const { Database, databasePrepareSync, databaseSyncSync, databaseExecSync, Statement, statementGetSync, statementRunSync, statementIterateSync, RowsIterator, iteratorNextSync, Record } = nativeBinding
314314

315315
module.exports.Database = Database
316316
module.exports.databasePrepareSync = databasePrepareSync
317317
module.exports.databaseSyncSync = databaseSyncSync
318+
module.exports.databaseExecSync = databaseExecSync
318319
module.exports.Statement = Statement
320+
module.exports.statementGetSync = statementGetSync
321+
module.exports.statementRunSync = statementRunSync
319322
module.exports.statementIterateSync = statementIterateSync
320323
module.exports.RowsIterator = RowsIterator
321324
module.exports.iteratorNextSync = iteratorNextSync

integration-tests/tests/async.test.js

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -47,21 +47,21 @@ test.serial("Statement.run() [positional]", async (t) => {
4747
const db = t.context.db;
4848

4949
const stmt = await db.prepare("INSERT INTO users(name, email) VALUES (?, ?)");
50-
const info = stmt.run(["Carol", "carol@example.net"]);
50+
const info = await stmt.run(["Carol", "carol@example.net"]);
5151
t.is(info.changes, 1);
5252
t.is(info.lastInsertRowid, 3);
5353

5454
// Verify that the data is inserted
5555
const stmt2 = await db.prepare("SELECT * FROM users WHERE id = 3");
56-
t.is(stmt2.get().name, "Carol");
57-
t.is(stmt2.get().email, "carol@example.net");
56+
t.is((await stmt2.get()).name, "Carol");
57+
t.is((await stmt2.get()).email, "carol@example.net");
5858
});
5959

6060
test.serial("Statement.get() returns no rows", async (t) => {
6161
const db = t.context.db;
6262

6363
const stmt = await db.prepare("SELECT * FROM users WHERE id = 0");
64-
t.is(stmt.get(), undefined);
64+
t.is((await stmt.get()), undefined);
6565
});
6666

6767
test.serial("Statement.get() [no parameters]", async (t) => {
@@ -70,7 +70,7 @@ test.serial("Statement.get() [no parameters]", async (t) => {
7070
var stmt = 0;
7171

7272
stmt = await db.prepare("SELECT * FROM users");
73-
t.is(stmt.get().name, "Alice");
73+
t.is((await stmt.get()).name, "Alice");
7474
t.deepEqual(await stmt.raw().get(), [1, 'Alice', 'alice@example.org']);
7575
});
7676

@@ -80,15 +80,15 @@ test.serial("Statement.get() [positional]", async (t) => {
8080
var stmt = 0;
8181

8282
stmt = await db.prepare("SELECT * FROM users WHERE id = ?");
83-
t.is(stmt.get(0), undefined);
84-
t.is(stmt.get([0]), undefined);
85-
t.is(stmt.get(1).name, "Alice");
86-
t.is(stmt.get(2).name, "Bob");
83+
t.is((await stmt.get(0)), undefined);
84+
t.is((await stmt.get([0])), undefined);
85+
t.is((await stmt.get(1)).name, "Alice");
86+
t.is((await stmt.get(2)).name, "Bob");
8787

8888
stmt = await db.prepare("SELECT * FROM users WHERE id = ?1");
89-
t.is(stmt.get({1: 0}), undefined);
90-
t.is(stmt.get({1: 1}).name, "Alice");
91-
t.is(stmt.get({1: 2}).name, "Bob");
89+
t.is((await stmt.get({1: 0})), undefined);
90+
t.is((await stmt.get({1: 1})).name, "Alice");
91+
t.is((await stmt.get({1: 2})).name, "Bob");
9292
});
9393

9494
test.serial("Statement.get() [named]", async (t) => {
@@ -97,27 +97,27 @@ test.serial("Statement.get() [named]", async (t) => {
9797
var stmt = undefined;
9898

9999
stmt = await db.prepare("SELECT * FROM users WHERE id = :id");
100-
t.is(stmt.get({ id: 0 }), undefined);
101-
t.is(stmt.get({ id: 1 }).name, "Alice");
102-
t.is(stmt.get({ id: 2 }).name, "Bob");
100+
t.is((await stmt.get({ id: 0 })), undefined);
101+
t.is((await stmt.get({ id: 1 })).name, "Alice");
102+
t.is((await stmt.get({ id: 2 })).name, "Bob");
103103

104104
stmt = await db.prepare("SELECT * FROM users WHERE id = @id");
105-
t.is(stmt.get({ id: 0 }), undefined);
106-
t.is(stmt.get({ id: 1 }).name, "Alice");
107-
t.is(stmt.get({ id: 2 }).name, "Bob");
105+
t.is((await stmt.get({ id: 0 })), undefined);
106+
t.is((await stmt.get({ id: 1 })).name, "Alice");
107+
t.is((await stmt.get({ id: 2 })).name, "Bob");
108108

109109
stmt = await db.prepare("SELECT * FROM users WHERE id = $id");
110-
t.is(stmt.get({ id: 0 }), undefined);
111-
t.is(stmt.get({ id: 1 }).name, "Alice");
112-
t.is(stmt.get({ id: 2 }).name, "Bob");
110+
t.is((await stmt.get({ id: 0 })), undefined);
111+
t.is((await stmt.get({ id: 1 })).name, "Alice");
112+
t.is((await stmt.get({ id: 2 })).name, "Bob");
113113
});
114114

115115

116116
test.serial("Statement.get() [raw]", async (t) => {
117117
const db = t.context.db;
118118

119119
const stmt = await db.prepare("SELECT * FROM users WHERE id = ?");
120-
t.deepEqual(stmt.raw().get(1), [1, "Alice", "alice@example.org"]);
120+
t.deepEqual(await stmt.raw().get(1), [1, "Alice", "alice@example.org"]);
121121
});
122122

123123
test.serial("Statement.iterate() [empty]", async (t) => {
@@ -253,9 +253,9 @@ test.serial("Database.transaction()", async (t) => {
253253
"INSERT INTO users(name, email) VALUES (:name, :email)"
254254
);
255255

256-
const insertMany = db.transaction((users) => {
256+
const insertMany = db.transaction(async (users) => {
257257
t.is(db.inTransaction, true);
258-
for (const user of users) insert.run(user);
258+
for (const user of users) await insert.run(user);
259259
});
260260

261261
t.is(db.inTransaction, false);
@@ -267,19 +267,19 @@ test.serial("Database.transaction()", async (t) => {
267267
t.is(db.inTransaction, false);
268268

269269
const stmt = await db.prepare("SELECT * FROM users WHERE id = ?");
270-
t.is(stmt.get(3).name, "Joey");
271-
t.is(stmt.get(4).name, "Sally");
272-
t.is(stmt.get(5).name, "Junior");
270+
t.is((await stmt.get(3)).name, "Joey");
271+
t.is((await stmt.get(4)).name, "Sally");
272+
t.is((await stmt.get(5)).name, "Junior");
273273
});
274274

275275
test.serial("Database.transaction().immediate()", async (t) => {
276276
const db = t.context.db;
277277
const insert = await db.prepare(
278278
"INSERT INTO users(name, email) VALUES (:name, :email)"
279279
);
280-
const insertMany = db.transaction((users) => {
280+
const insertMany = db.transaction(async (users) => {
281281
t.is(db.inTransaction, true);
282-
for (const user of users) insert.run(user);
282+
for (const user of users) await insert.run(user);
283283
});
284284
t.is(db.inTransaction, false);
285285
await insertMany.immediate([

integration-tests/tests/concurrency.test.js

Lines changed: 4 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ test("Concurrent reads", async (t) => {
3232

3333
const promises = [];
3434
for (let i = 0; i < 100; i++) {
35-
promises.push(stmt.get(t.context.aliceId));
36-
promises.push(stmt.get(t.context.bobId));
35+
promises.push(await stmt.get(t.context.aliceId));
36+
promises.push(await stmt.get(t.context.bobId));
3737
}
3838

3939
const results = await Promise.all(promises);
@@ -63,7 +63,7 @@ test("Concurrent writes", async (t) => {
6363

6464
const promises = [];
6565
for (let i = 0; i < 50; i++) {
66-
promises.push(stmt.run({
66+
promises.push(await stmt.run({
6767
id: generateUUID(),
6868
name: `User${i}`,
6969
email: `user${i}@example.com`
@@ -79,49 +79,6 @@ test("Concurrent writes", async (t) => {
7979
cleanup(t.context);
8080
});
8181

82-
test("Concurrent transaction isolation", async (t) => {
83-
const db = t.context.db;
84-
85-
await db.exec(`
86-
DROP TABLE IF EXISTS transaction_users;
87-
CREATE TABLE transaction_users (
88-
id TEXT PRIMARY KEY,
89-
name TEXT,
90-
email TEXT
91-
)
92-
`);
93-
94-
const aliceId = generateUUID();
95-
const bobId = generateUUID();
96-
97-
await db.exec(`
98-
INSERT INTO transaction_users (id, name, email) VALUES
99-
('${aliceId}', 'Alice', 'alice@example.org'),
100-
('${bobId}', 'Bob', 'bob@example.com')
101-
`);
102-
103-
const updateUser = db.transaction(async (id, name, email) => {
104-
const stmt = await db.prepare("UPDATE transaction_users SET name = :name, email = :email WHERE id = :id");
105-
await stmt.run({ id, name, email });
106-
});
107-
108-
const promises = [];
109-
for (let i = 0; i < 10; i++) {
110-
promises.push(updateUser(aliceId, `Alice${i}`, `alice${i}@example.org`));
111-
promises.push(updateUser(bobId, `Bob${i}`, `bob${i}@example.com`));
112-
}
113-
114-
await Promise.all(promises);
115-
116-
const stmt = await db.prepare("SELECT * FROM transaction_users ORDER BY name");
117-
const results = await stmt.all();
118-
t.is(results.length, 2);
119-
t.truthy(results[0].name.startsWith('Alice'));
120-
t.truthy(results[1].name.startsWith('Bob'));
121-
122-
cleanup(t.context);
123-
});
124-
12582
test("Concurrent reads and writes", async (t) => {
12683
const db = t.context.db;
12784

@@ -146,7 +103,7 @@ test("Concurrent reads and writes", async (t) => {
146103
const promises = [];
147104
for (let i = 0; i < 20; i++) {
148105
promises.push(readStmt.get(aliceId));
149-
writeStmt.run({
106+
await writeStmt.run({
150107
id: generateUUID(),
151108
name: `User${i}`,
152109
email: `user${i}@example.com`

promise.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,14 @@ class Database {
8888

8989
const db = this;
9090
const wrapTxn = (mode) => {
91-
return (...bindParameters) => {
92-
db.exec("BEGIN " + mode);
91+
return async (...bindParameters) => {
92+
await db.exec("BEGIN " + mode);
9393
try {
94-
const result = fn(...bindParameters);
95-
db.exec("COMMIT");
94+
const result = await fn(...bindParameters);
95+
await db.exec("COMMIT");
9696
return result;
9797
} catch (err) {
98-
db.exec("ROLLBACK");
98+
await db.exec("ROLLBACK");
9999
throw err;
100100
}
101101
};
@@ -172,9 +172,9 @@ class Database {
172172
*
173173
* @param {string} sql - The SQL statement string to execute.
174174
*/
175-
exec(sql) {
175+
async exec(sql) {
176176
try {
177-
this.db.exec(sql);
177+
await this.db.exec(sql);
178178
} catch (err) {
179179
throw convertError(err);
180180
}
@@ -257,9 +257,9 @@ class Statement {
257257
/**
258258
* Executes the SQL statement and returns an info object.
259259
*/
260-
run(...bindParameters) {
260+
async run(...bindParameters) {
261261
try {
262-
return this.stmt.run(...bindParameters);
262+
return await this.stmt.run(...bindParameters);
263263
} catch (err) {
264264
throw convertError(err);
265265
}
@@ -270,9 +270,9 @@ class Statement {
270270
*
271271
* @param bindParameters - The bind parameters for executing the statement.
272272
*/
273-
get(...bindParameters) {
273+
async get(...bindParameters) {
274274
try {
275-
return this.stmt.get(...bindParameters);
275+
return await this.stmt.get(...bindParameters);
276276
} catch (err) {
277277
throw convertError(err);
278278
}

0 commit comments

Comments
 (0)