Skip to content

Commit ea75fcc

Browse files
committed
sqlite: avoid extra copy for large text binds
When binding UTF-8 strings to prepared statements, transfer ownership of the malloc-backed Utf8Value buffer to SQLite to avoid an extra copy. Also use sqlite3_bind_blob64() when binding BLOB parameters.
1 parent 37e4004 commit ea75fcc

File tree

1 file changed

+29
-8
lines changed

1 file changed

+29
-8
lines changed

src/node_sqlite.cc

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2260,20 +2260,41 @@ bool StatementSync::BindValue(const Local<Value>& value, const int index) {
22602260
// Dates could be supported by converting them to numbers. However, there
22612261
// would not be a good way to read the values back from SQLite with the
22622262
// original type.
2263+
Isolate* isolate = env()->isolate();
22632264
int r;
22642265
if (value->IsNumber()) {
2265-
double val = value.As<Number>()->Value();
2266+
const double val = value.As<Number>()->Value();
22662267
r = sqlite3_bind_double(statement_, index, val);
22672268
} else if (value->IsString()) {
2268-
Utf8Value val(env()->isolate(), value.As<String>());
2269-
r = sqlite3_bind_text(
2270-
statement_, index, *val, val.length(), SQLITE_TRANSIENT);
2269+
Utf8Value val(isolate, value.As<String>());
2270+
if (val.IsAllocated()) {
2271+
// Avoid an extra SQLite copy for large strings by transferring ownership
2272+
// of the malloc()'d buffer to SQLite.
2273+
char* data = *val;
2274+
const sqlite3_uint64 length =
2275+
static_cast<sqlite3_uint64>(val.length());
2276+
val.Release();
2277+
r = sqlite3_bind_text64(
2278+
statement_, index, data, length, std::free, SQLITE_UTF8);
2279+
} else {
2280+
// Stack-backed buffer: SQLite must make its own copy.
2281+
r = sqlite3_bind_text64(statement_,
2282+
index,
2283+
*val,
2284+
static_cast<sqlite3_uint64>(val.length()),
2285+
SQLITE_TRANSIENT,
2286+
SQLITE_UTF8);
2287+
}
22712288
} else if (value->IsNull()) {
22722289
r = sqlite3_bind_null(statement_, index);
22732290
} else if (value->IsArrayBufferView()) {
22742291
ArrayBufferViewContents<uint8_t> buf(value);
2275-
r = sqlite3_bind_blob(
2276-
statement_, index, buf.data(), buf.length(), SQLITE_TRANSIENT);
2292+
r = sqlite3_bind_blob64(
2293+
statement_,
2294+
index,
2295+
buf.data(),
2296+
static_cast<sqlite3_uint64>(buf.length()),
2297+
SQLITE_TRANSIENT);
22772298
} else if (value->IsBigInt()) {
22782299
bool lossless;
22792300
int64_t as_int = value.As<BigInt>()->Int64Value(&lossless);
@@ -2284,13 +2305,13 @@ bool StatementSync::BindValue(const Local<Value>& value, const int index) {
22842305
r = sqlite3_bind_int64(statement_, index, as_int);
22852306
} else {
22862307
THROW_ERR_INVALID_ARG_TYPE(
2287-
env()->isolate(),
2308+
isolate,
22882309
"Provided value cannot be bound to SQLite parameter %d.",
22892310
index);
22902311
return false;
22912312
}
22922313

2293-
CHECK_ERROR_OR_THROW(env()->isolate(), db_.get(), r, SQLITE_OK, false);
2314+
CHECK_ERROR_OR_THROW(isolate, db_.get(), r, SQLITE_OK, false);
22942315
return true;
22952316
}
22962317

0 commit comments

Comments
 (0)