Skip to content

Commit 889a597

Browse files
committed
sqlite: add fast api to DatabaseSync.isTransaction
1 parent 9cc0195 commit 889a597

File tree

3 files changed

+84
-9
lines changed

3 files changed

+84
-9
lines changed

src/node_sqlite.cc

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
#include "env-inl.h"
66
#include "memory_tracker-inl.h"
77
#include "node.h"
8+
#include "node_debug.h"
89
#include "node_errors.h"
910
#include "node_mem-inl.h"
1011
#include "node_url.h"
1112
#include "sqlite3.h"
1213
#include "threadpoolwork-inl.h"
1314
#include "util-inl.h"
15+
#include "v8-fast-api-calls.h"
1416

1517
#include <cinttypes>
1618

@@ -21,10 +23,12 @@ using v8::Array;
2123
using v8::ArrayBuffer;
2224
using v8::BigInt;
2325
using v8::Boolean;
26+
using v8::CFunction;
2427
using v8::ConstructorBehavior;
2528
using v8::Context;
2629
using v8::DontDelete;
2730
using v8::Exception;
31+
using v8::FastApiCallbackOptions;
2832
using v8::Function;
2933
using v8::FunctionCallback;
3034
using v8::FunctionCallbackInfo;
@@ -58,11 +62,11 @@ using v8::Value;
5862
} \
5963
} while (0)
6064

61-
#define THROW_AND_RETURN_ON_BAD_STATE(env, condition, msg) \
65+
#define THROW_AND_RETURN_ON_BAD_STATE(env, condition, msg, ...) \
6266
do { \
6367
if ((condition)) { \
6468
THROW_ERR_INVALID_STATE((env), (msg)); \
65-
return; \
69+
return __VA_ARGS__; \
6670
} \
6771
} while (0)
6872

@@ -979,7 +983,7 @@ void DatabaseSync::IsOpenGetter(const FunctionCallbackInfo<Value>& args) {
979983
args.GetReturnValue().Set(db->IsOpen());
980984
}
981985

982-
void DatabaseSync::IsTransactionGetter(
986+
void DatabaseSync::IsTransactionGetterSlow(
983987
const FunctionCallbackInfo<Value>& args) {
984988
DatabaseSync* db;
985989
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
@@ -988,6 +992,23 @@ void DatabaseSync::IsTransactionGetter(
988992
args.GetReturnValue().Set(sqlite3_get_autocommit(db->connection_) == 0);
989993
}
990994

995+
bool DatabaseSync::IsTransactionGetterFast(
996+
Local<Value> receiver,
997+
Local<Object> holder,
998+
// NOLINTNEXTLINE(runtime/references) This is V8 api.
999+
FastApiCallbackOptions& options) {
1000+
TRACK_V8_FAST_API_CALL("DatabaseSync.isTransaction");
1001+
DatabaseSync* db;
1002+
ASSIGN_OR_RETURN_UNWRAP(&db, holder, false);
1003+
Environment* env = Environment::GetCurrent(options.isolate);
1004+
THROW_AND_RETURN_ON_BAD_STATE(
1005+
env, !db->IsOpen(), "database is not open", false);
1006+
return sqlite3_get_autocommit(db->connection_) == 0;
1007+
}
1008+
1009+
CFunction DatabaseSync::is_transaction_getter_methods[] = {
1010+
CFunction::Make(IsTransactionGetterFast)};
1011+
9911012
void DatabaseSync::Close(const FunctionCallbackInfo<Value>& args) {
9921013
DatabaseSync* db;
9931014
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
@@ -2343,6 +2364,23 @@ static inline void SetSideEffectFreeGetter(
23432364
name, getter, Local<FunctionTemplate>(), DontDelete);
23442365
}
23452366

2367+
static inline void SetSideEffectFreeFastGetter(
2368+
Isolate* isolate,
2369+
Local<FunctionTemplate> class_template,
2370+
Local<String> name,
2371+
v8::FunctionCallback slow_callback,
2372+
const v8::CFunction* c_function) {
2373+
Local<v8::FunctionTemplate> getter =
2374+
NewFunctionTemplate(isolate,
2375+
slow_callback,
2376+
v8::Signature::New(isolate, class_template),
2377+
v8::ConstructorBehavior::kThrow,
2378+
v8::SideEffectType::kHasSideEffect,
2379+
c_function);
2380+
class_template->InstanceTemplate()->SetAccessorProperty(
2381+
name, getter, Local<FunctionTemplate>(), DontDelete);
2382+
}
2383+
23462384
Local<FunctionTemplate> StatementSync::GetConstructorTemplate(
23472385
Environment* env) {
23482386
Local<FunctionTemplate> tmpl =
@@ -2672,10 +2710,11 @@ static void Initialize(Local<Object> target,
26722710
db_tmpl,
26732711
FIXED_ONE_BYTE_STRING(isolate, "isOpen"),
26742712
DatabaseSync::IsOpenGetter);
2675-
SetSideEffectFreeGetter(isolate,
2676-
db_tmpl,
2677-
FIXED_ONE_BYTE_STRING(isolate, "isTransaction"),
2678-
DatabaseSync::IsTransactionGetter);
2713+
SetSideEffectFreeFastGetter(isolate,
2714+
db_tmpl,
2715+
FIXED_ONE_BYTE_STRING(isolate, "isTransaction"),
2716+
DatabaseSync::IsTransactionGetterSlow,
2717+
DatabaseSync::is_transaction_getter_methods);
26792718
SetConstructorFunction(context, target, "DatabaseSync", db_tmpl);
26802719
SetConstructorFunction(context,
26812720
target,

src/node_sqlite.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "node_mem.h"
88
#include "sqlite3.h"
99
#include "util.h"
10+
#include "v8-fast-api-calls.h"
1011

1112
#include <map>
1213
#include <unordered_set>
@@ -61,8 +62,11 @@ class DatabaseSync : public BaseObject {
6162
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
6263
static void Open(const v8::FunctionCallbackInfo<v8::Value>& args);
6364
static void IsOpenGetter(const v8::FunctionCallbackInfo<v8::Value>& args);
64-
static void IsTransactionGetter(
65+
static void IsTransactionGetterSlow(
6566
const v8::FunctionCallbackInfo<v8::Value>& args);
67+
static bool IsTransactionGetterFast(v8::Local<v8::Value> receiver,
68+
v8::Local<v8::Object> holder,
69+
v8::FastApiCallbackOptions& options);
6670
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
6771
static void Prepare(const v8::FunctionCallbackInfo<v8::Value>& args);
6872
static void Exec(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -93,6 +97,8 @@ class DatabaseSync : public BaseObject {
9397
SET_MEMORY_INFO_NAME(DatabaseSync)
9498
SET_SELF_SIZE(DatabaseSync)
9599

100+
static v8::CFunction is_transaction_getter_methods[];
101+
96102
private:
97103
bool Open();
98104
void DeleteSessions();

test/parallel/test-sqlite-database-sync.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
// Flags: --expose-internals --allow-natives-syntax
12
'use strict';
2-
require('../common');
3+
const { isDebug } = require('../common');
34
const tmpdir = require('../common/tmpdir');
45
const { existsSync } = require('node:fs');
56
const { join } = require('node:path');
67
const { DatabaseSync, StatementSync } = require('node:sqlite');
78
const { suite, test } = require('node:test');
9+
const assert = require('node:assert');
10+
const { internalBinding } = require('internal/test/binding');
811
let cnt = 0;
912

1013
tmpdir.refresh();
@@ -360,6 +363,33 @@ suite('DatabaseSync.prototype.isTransaction', () => {
360363
message: /database is not open/,
361364
});
362365
});
366+
367+
test('supports isTransaction fast path', (t) => {
368+
const db = new DatabaseSync(':memory:');
369+
370+
// Only javascript methods can be optimized through %OptimizeFunctionOnNextCall
371+
// This is why we surround the C++ method we want to optimize with a JS function.
372+
function isTransaction() {
373+
return db.isTransaction;
374+
}
375+
376+
t.assert.strictEqual(db.isTransaction, false);
377+
db.exec('BEGIN');
378+
eval('%PrepareFunctionForOptimization(isTransaction)');
379+
t.assert.strictEqual(isTransaction(), true);
380+
eval('%OptimizeFunctionOnNextCall(isTransaction)');
381+
t.assert.strictEqual(isTransaction(), true);
382+
383+
if (isDebug) {
384+
const { getV8FastApiCallCount } = internalBinding('debug');
385+
assert.strictEqual(getV8FastApiCallCount('DatabaseSync.isTransaction'), 1);
386+
}
387+
388+
db.exec('CREATE TABLE foo (id INTEGER PRIMARY KEY)');
389+
t.assert.strictEqual(db.isTransaction, true);
390+
db.exec('COMMIT');
391+
t.assert.strictEqual(db.isTransaction, false);
392+
});
363393
});
364394

365395
suite('DatabaseSync.prototype.location()', () => {

0 commit comments

Comments
 (0)