Skip to content

Commit

Permalink
sqlite: add transactions support
Browse files Browse the repository at this point in the history
Fixes: #511
  • Loading branch information
saghul committed Jun 6, 2024
1 parent 1d93630 commit 09af900
Show file tree
Hide file tree
Showing 5 changed files with 608 additions and 149 deletions.
45 changes: 45 additions & 0 deletions docs/types/sqlite.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ declare module 'tjs:sqlite'{
readOnly: boolean;
}

export interface ITransaction extends Function {
deferred: Function;
immediate: Function;
exclusive: Function;
}

export class Database {
/**
* Opens a SQLite database.
Expand All @@ -73,6 +79,45 @@ declare module 'tjs:sqlite'{
*/
prepare(sql: string): IStatement;

/**
* Wrap the given function so it runs in a [transaction](https://sqlite.org/lang_transaction.html).
* When the (returned) function is invoked, it will start a new transaction. When the function returns,
* the transaction will be committed. If an exception is thrown, the transaction will be rolled back.
*
* ```js
* const ins = db.prepare('INSERT INTO test (txt, int) VALUES(?, ?)');
* const insMany = db.transaction(datas => {
* for (const data of datas) {
* ins.run(data);
* }
* });
*
* insMany([
* [ '1234', 1234 ],
* [ '4321', 4321 ],
* ]);
* ```
* Transaction functions can be called from inside other transaction functions. When doing so,
* the inner transaction becomes a [savepoint](https://www.sqlite.org/lang_savepoint.html). If an error
* is thrown inside of a nested transaction function, the nested transaction function will roll back
* to the state just before the savepoint. If the error is not caught in the outer transaction function,
* this will cause the outer transaction function to roll back as well.
*
* Transactions also come with deferred, immediate, and exclusive versions:
*
* ```js
* insertMany(datas); // uses "BEGIN"
* insertMany.deferred(datas); // uses "BEGIN DEFERRED"
* insertMany.immediate(datas); // uses "BEGIN IMMEDIATE"
* insertMany.exclusive(datas); // uses "BEGIN EXCLUSIVE"
* ```
*
* NOTE: This implementation was mostly taken from [better-sqlite3](https://github.com/WiseLibs/better-sqlite3/blob/6acc3fcebe469969aa29319714b187a53ada0934/docs/api.md#transactionfunction---function).
*
* @param fn - The function to be wrapped in a transaction.
*/
transaction(fn: Function): ITransaction;

/**
* Closes the database. No further operations can be performed afterwards.
*/
Expand Down
10 changes: 10 additions & 0 deletions src/_sqlite3.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,15 @@ static JSValue tjs_sqlite3_prepare(JSContext *ctx, JSValue this_val, int argc, J
return obj;
}

static JSValue tjs_sqlite3_in_transaction(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
TJSSqlite3Handle *h = tjs_sqlite3_get(ctx, argv[0]);

if (!h)
return JS_EXCEPTION;

return JS_NewBool(ctx, !sqlite3_get_autocommit(h->handle));
}

static JSValue tjs_sqlite3_stmt_finalize(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
TJSSqlite3Stmt *h = tjs_sqlite3_stmt_get(ctx, argv[0]);

Expand Down Expand Up @@ -512,6 +521,7 @@ static const JSCFunctionListEntry tjs_sqlite3_funcs[] = {
TJS_CFUNC_DEF("close", 1, tjs_sqlite3_close),
TJS_CFUNC_DEF("exec", 2, tjs_sqlite3_exec),
TJS_CFUNC_DEF("prepare", 2, tjs_sqlite3_prepare),
TJS_CFUNC_DEF("in_transaction", 1, tjs_sqlite3_in_transaction),
TJS_CFUNC_DEF("stmt_finalize", 1, tjs_sqlite3_stmt_finalize),
TJS_CFUNC_DEF("stmt_expand", 1, tjs_sqlite3_stmt_expand),
TJS_CFUNC_DEF("stmt_all", 2, tjs_sqlite3_stmt_all),
Expand Down
Loading

0 comments on commit 09af900

Please sign in to comment.