Skip to content

Commit cf604db

Browse files
committed
sqlite: add getter to detect transactions
This commit adds an isTransaction getter to the DatabaseSync class for determining if the database is currently within a transaction. Fixes: nodejs#57922
1 parent 52d95f5 commit cf604db

File tree

4 files changed

+62
-0
lines changed

4 files changed

+62
-0
lines changed

Diff for: doc/api/sqlite.md

+10
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,15 @@ added: v23.11.0
291291

292292
* {boolean} Whether the database is currently open or not.
293293

294+
### `database.isTransaction`
295+
296+
<!-- YAML
297+
added: REPLACEME
298+
-->
299+
300+
* {boolean} Whether the database is currently within a transaction. This method
301+
is a wrapper around [`sqlite3_get_autocommit()`][].
302+
294303
### `database.open()`
295304

296305
<!-- YAML
@@ -839,6 +848,7 @@ resolution handler passed to [`database.applyChangeset()`][]. See also
839848
[`sqlite3_create_window_function()`]: https://www.sqlite.org/c3ref/create_function.html
840849
[`sqlite3_exec()`]: https://www.sqlite.org/c3ref/exec.html
841850
[`sqlite3_expanded_sql()`]: https://www.sqlite.org/c3ref/expanded_sql.html
851+
[`sqlite3_get_autocommit()`]: https://sqlite.org/c3ref/get_autocommit.html
842852
[`sqlite3_last_insert_rowid()`]: https://www.sqlite.org/c3ref/last_insert_rowid.html
843853
[`sqlite3_load_extension()`]: https://www.sqlite.org/c3ref/load_extension.html
844854
[`sqlite3_prepare_v2()`]: https://www.sqlite.org/c3ref/prepare.html

Diff for: src/node_sqlite.cc

+13
Original file line numberDiff line numberDiff line change
@@ -979,6 +979,15 @@ void DatabaseSync::IsOpenGetter(const FunctionCallbackInfo<Value>& args) {
979979
args.GetReturnValue().Set(db->IsOpen());
980980
}
981981

982+
void DatabaseSync::IsTransactionGetter(
983+
const FunctionCallbackInfo<Value>& args) {
984+
DatabaseSync* db;
985+
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
986+
Environment* env = Environment::GetCurrent(args);
987+
THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open");
988+
args.GetReturnValue().Set(sqlite3_get_autocommit(db->connection_) == 0);
989+
}
990+
982991
void DatabaseSync::Close(const FunctionCallbackInfo<Value>& args) {
983992
DatabaseSync* db;
984993
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
@@ -2623,6 +2632,10 @@ static void Initialize(Local<Object> target,
26232632
db_tmpl,
26242633
FIXED_ONE_BYTE_STRING(isolate, "isOpen"),
26252634
DatabaseSync::IsOpenGetter);
2635+
SetSideEffectFreeGetter(isolate,
2636+
db_tmpl,
2637+
FIXED_ONE_BYTE_STRING(isolate, "isTransaction"),
2638+
DatabaseSync::IsTransactionGetter);
26262639
SetConstructorFunction(context, target, "DatabaseSync", db_tmpl);
26272640
SetConstructorFunction(context,
26282641
target,

Diff for: src/node_sqlite.h

+2
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ class DatabaseSync : public BaseObject {
6161
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
6262
static void Open(const v8::FunctionCallbackInfo<v8::Value>& args);
6363
static void IsOpenGetter(const v8::FunctionCallbackInfo<v8::Value>& args);
64+
static void IsTransactionGetter(
65+
const v8::FunctionCallbackInfo<v8::Value>& args);
6466
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
6567
static void Prepare(const v8::FunctionCallbackInfo<v8::Value>& args);
6668
static void Exec(const v8::FunctionCallbackInfo<v8::Value>& args);

Diff for: test/parallel/test-sqlite-database-sync.js

+37
Original file line numberDiff line numberDiff line change
@@ -324,3 +324,40 @@ suite('DatabaseSync.prototype.exec()', () => {
324324
});
325325
});
326326
});
327+
328+
suite('DatabaseSync.prototype.isTransaction', () => {
329+
test('correctly detects a committed transaction', (t) => {
330+
const db = new DatabaseSync(':memory:');
331+
332+
t.assert.strictEqual(db.isTransaction, false);
333+
db.exec('BEGIN');
334+
t.assert.strictEqual(db.isTransaction, true);
335+
db.exec('CREATE TABLE foo (id INTEGER PRIMARY KEY)');
336+
t.assert.strictEqual(db.isTransaction, true);
337+
db.exec('COMMIT');
338+
t.assert.strictEqual(db.isTransaction, false);
339+
});
340+
341+
test('correctly detects a rolled back transaction', (t) => {
342+
const db = new DatabaseSync(':memory:');
343+
344+
t.assert.strictEqual(db.isTransaction, false);
345+
db.exec('BEGIN');
346+
t.assert.strictEqual(db.isTransaction, true);
347+
db.exec('CREATE TABLE foo (id INTEGER PRIMARY KEY)');
348+
t.assert.strictEqual(db.isTransaction, true);
349+
db.exec('ROLLBACK');
350+
t.assert.strictEqual(db.isTransaction, false);
351+
});
352+
353+
test('throws if database is not open', (t) => {
354+
const db = new DatabaseSync(nextDb(), { open: false });
355+
356+
t.assert.throws(() => {
357+
return db.isTransaction;
358+
}, {
359+
code: 'ERR_INVALID_STATE',
360+
message: /database is not open/,
361+
});
362+
});
363+
});

0 commit comments

Comments
 (0)