Skip to content

Commit e65dae0

Browse files
authored
Merge pull request #155 from tursodatabase/interrupt
Add support for Database.interrupt()
2 parents 8f364e3 + 753fda9 commit e65dae0

12 files changed

+130
-50
lines changed

Cargo.lock

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ crate-type = ["cdylib"]
1212

1313
[dependencies]
1414
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
15-
libsql = { git = "https://github.com/tursodatabase/libsql/", rev = "f8954db1302a97d435f7fcd7bf0d155ee38d632d", features = ["encryption"] }
15+
libsql = { git = "https://github.com/tursodatabase/libsql/", rev = "58b016ab72eea7fc0118009fb03195449f202edc", features = ["encryption"] }
1616
tracing = "0.1"
1717
once_cell = "1.18.0"
1818
tokio = { version = "1.29.1", features = [ "rt-multi-thread" ] }

docs/api.md

+7
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ This function is currently not supported.
6969
### loadExtension(path, [entryPoint]) ⇒ this
7070

7171
Loads a SQLite3 extension
72+
7273
### exec(sql) ⇒ this
7374

7475
Executes a SQL statement.
@@ -77,6 +78,12 @@ Executes a SQL statement.
7778
| ------ | ------------------- | ------------------------------------ |
7879
| sql | <code>string</code> | The SQL statement string to execute. |
7980

81+
### interrupt() ⇒ this
82+
83+
Cancel ongoing operations and make them return at earliest opportunity.
84+
85+
**Note:** This is an extension in libSQL and not available in `better-sqlite3`.
86+
8087
### close() ⇒ this
8188

8289
Closes the database connection.

index.js

+38-18
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const {
2626
databaseOpen,
2727
databaseOpenWithSync,
2828
databaseInTransaction,
29+
databaseInterrupt,
2930
databaseClose,
3031
databaseSyncSync,
3132
databaseSyncUntilSync,
@@ -245,6 +246,13 @@ class Database {
245246
}
246247
}
247248

249+
/**
250+
* Interrupts the database connection.
251+
*/
252+
interrupt() {
253+
databaseInterrupt.call(this.db);
254+
}
255+
248256
/**
249257
* Closes the database connection.
250258
*/
@@ -309,10 +317,14 @@ class Statement {
309317
* @param bindParameters - The bind parameters for executing the statement.
310318
*/
311319
get(...bindParameters) {
312-
if (bindParameters.length == 1 && typeof bindParameters[0] === "object") {
313-
return statementGet.call(this.stmt, bindParameters[0]);
314-
} else {
315-
return statementGet.call(this.stmt, bindParameters.flat());
320+
try {
321+
if (bindParameters.length == 1 && typeof bindParameters[0] === "object") {
322+
return statementGet.call(this.stmt, bindParameters[0]);
323+
} else {
324+
return statementGet.call(this.stmt, bindParameters.flat());
325+
}
326+
} catch (err) {
327+
throw convertError(err);
316328
}
317329
}
318330

@@ -332,17 +344,21 @@ class Statement {
332344
nextRows: Array(100),
333345
nextRowIndex: 100,
334346
next() {
335-
if (this.nextRowIndex === 100) {
336-
rowsNext.call(rows, this.nextRows);
337-
this.nextRowIndex = 0;
338-
}
339-
const row = this.nextRows[this.nextRowIndex];
340-
this.nextRows[this.nextRowIndex] = undefined;
341-
if (!row) {
342-
return { done: true };
343-
}
347+
try {
348+
if (this.nextRowIndex === 100) {
349+
rowsNext.call(rows, this.nextRows);
350+
this.nextRowIndex = 0;
351+
}
352+
const row = this.nextRows[this.nextRowIndex];
353+
this.nextRows[this.nextRowIndex] = undefined;
354+
if (!row) {
355+
return { done: true };
356+
}
344357
this.nextRowIndex++;
345-
return { value: row, done: false };
358+
return { value: row, done: false };
359+
} catch (err) {
360+
throw convertError(err);
361+
}
346362
},
347363
[Symbol.iterator]() {
348364
return this;
@@ -357,11 +373,15 @@ class Statement {
357373
* @param bindParameters - The bind parameters for executing the statement.
358374
*/
359375
all(...bindParameters) {
360-
const result = [];
361-
for (const row of this.iterate(...bindParameters)) {
362-
result.push(row);
376+
try {
377+
const result = [];
378+
for (const row of this.iterate(...bindParameters)) {
379+
result.push(row);
380+
}
381+
return result;
382+
} catch (err) {
383+
throw convertError(err);
363384
}
364-
return result;
365385
}
366386

367387
/**

integration-tests/package-lock.json

+5-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

integration-tests/tests/async.test.js

+14
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,20 @@ test.serial("Database.exec() after close()", async (t) => {
314314
});
315315
});
316316

317+
test.serial("Database.interrupt()", async (t) => {
318+
const db = t.context.db;
319+
const stmt = await db.prepare("WITH RECURSIVE infinite_loop(n) AS (SELECT 1 UNION ALL SELECT n + 1 FROM infinite_loop) SELECT * FROM infinite_loop;");
320+
const fut = stmt.all();
321+
db.interrupt();
322+
await t.throwsAsync(async () => {
323+
await fut;
324+
}, {
325+
instanceOf: t.context.errorType,
326+
message: 'interrupted',
327+
code: 'SQLITE_INTERRUPT'
328+
});
329+
});
330+
317331
const connect = async (path_opt) => {
318332
const path = path_opt ?? "hello.db";
319333
const provider = process.env.PROVIDER;

promise.js

+40-20
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const {
4040
databaseOpen,
4141
databaseOpenWithSync,
4242
databaseInTransaction,
43+
databaseInterrupt,
4344
databaseClose,
4445
databaseSyncAsync,
4546
databaseSyncUntilAsync,
@@ -246,6 +247,13 @@ class Database {
246247
});
247248
}
248249

250+
/**
251+
* Interrupts the database connection.
252+
*/
253+
interrupt() {
254+
databaseInterrupt.call(this.db);
255+
}
256+
249257
/**
250258
* Closes the database connection.
251259
*/
@@ -309,10 +317,14 @@ class Statement {
309317
* @param bindParameters - The bind parameters for executing the statement.
310318
*/
311319
get(...bindParameters) {
312-
if (bindParameters.length == 1 && typeof bindParameters[0] === "object") {
313-
return statementGet.call(this.stmt, bindParameters[0]);
314-
} else {
315-
return statementGet.call(this.stmt, bindParameters.flat());
320+
try {
321+
if (bindParameters.length == 1 && typeof bindParameters[0] === "object") {
322+
return statementGet.call(this.stmt, bindParameters[0]);
323+
} else {
324+
return statementGet.call(this.stmt, bindParameters.flat());
325+
}
326+
} catch (e) {
327+
throw convertError(e);
316328
}
317329
}
318330

@@ -332,18 +344,22 @@ class Statement {
332344
nextRows: Array(100),
333345
nextRowIndex: 100,
334346
next() {
335-
if (this.nextRowIndex === 100) {
336-
this.nextRows.fill(null);
337-
rowsNext.call(rows, this.nextRows);
338-
this.nextRowIndex = 0;
339-
}
340-
const row = this.nextRows[this.nextRowIndex];
341-
this.nextRows[this.nextRowIndex] = null;
342-
if (!row) {
343-
return { done: true };
347+
try {
348+
if (this.nextRowIndex === 100) {
349+
this.nextRows.fill(null);
350+
rowsNext.call(rows, this.nextRows);
351+
this.nextRowIndex = 0;
352+
}
353+
const row = this.nextRows[this.nextRowIndex];
354+
this.nextRows[this.nextRowIndex] = null;
355+
if (!row) {
356+
return { done: true };
357+
}
358+
this.nextRowIndex++;
359+
return { value: row, done: false };
360+
} catch (e) {
361+
throw convertError(e);
344362
}
345-
this.nextRowIndex++;
346-
return { value: row, done: false };
347363
},
348364
[Symbol.iterator]() {
349365
return this;
@@ -358,12 +374,16 @@ class Statement {
358374
* @param bindParameters - The bind parameters for executing the statement.
359375
*/
360376
async all(...bindParameters) {
361-
const result = [];
362-
const it = await this.iterate(...bindParameters);
363-
for (const row of it) {
364-
result.push(row);
377+
try {
378+
const result = [];
379+
const it = await this.iterate(...bindParameters);
380+
for (const row of it) {
381+
result.push(row);
382+
}
383+
return result;
384+
} catch (e) {
385+
throw convertError(e);
365386
}
366-
return result;
367387
}
368388

369389
/**

src/database.rs

+11
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@ impl Database {
126126
Ok(cx.boolean(result).upcast())
127127
}
128128

129+
pub fn js_interrupt(mut cx: FunctionContext) -> JsResult<JsUndefined> {
130+
let db: Handle<'_, JsBox<Database>> = cx.this()?;
131+
let conn = db.conn.borrow();
132+
let conn = conn.as_ref().unwrap().clone();
133+
conn.blocking_lock().interrupt().or_else(|err| {
134+
throw_libsql_error(&mut cx, err)?;
135+
Ok(())
136+
})?;
137+
Ok(cx.undefined())
138+
}
139+
129140
pub fn js_close(mut cx: FunctionContext) -> JsResult<JsUndefined> {
130141
// the conn will be closed when the last statement in discarded. In most situation that
131142
// means immediately because you don't want to hold on a statement for longer that its

src/errors.rs

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub fn throw_libsql_error<'a, C: Context<'a>, T>(cx: &mut C, err: libsql::Error)
1919
cx.throw(err)?
2020
}
2121
_ => {
22+
todo!("err = {:?}", err);
2223
let err = format!("{:?}", err);
2324
let err = JsError::error(cx, err).unwrap();
2425
let code = cx.string("");

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
3030
cx.export_function("databaseOpen", Database::js_open)?;
3131
cx.export_function("databaseOpenWithSync", Database::js_open_with_sync)?;
3232
cx.export_function("databaseInTransaction", Database::js_in_transaction)?;
33+
cx.export_function("databaseInterrupt", Database::js_interrupt)?;
3334
cx.export_function("databaseClose", Database::js_close)?;
3435
cx.export_function("databaseSyncSync", Database::js_sync_sync)?;
3536
cx.export_function("databaseSyncAsync", Database::js_sync_async)?;

types/promise.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ declare class Database {
4343
* @param {string} sql - The SQL statement string to execute.
4444
*/
4545
exec(sql: string): any;
46+
/**
47+
* Interrupts the database connection.
48+
*/
49+
interrupt(): void;
4650
/**
4751
* Closes the database connection.
4852
*/

types/promise.d.ts.map

+1-1
Original file line numberDiff line numberDiff line change

0 commit comments

Comments
 (0)