◐ Shell
clean mode source ↗

sqlite: add serialize() and deserialize() · nodejs/node@3c96ae1

@@ -23,6 +23,7 @@ namespace sqlite {

2323

using v8::Array;

2424

using v8::ArrayBuffer;

2525

using v8::BackingStoreInitializationMode;

26+

using v8::BackingStoreOnFailureMode;

2627

using v8::BigInt;

2728

using v8::Boolean;

2829

using v8::ConstructorBehavior;

@@ -1719,6 +1720,136 @@ void DatabaseSync::Location(const FunctionCallbackInfo<Value>& args) {

17191720

}

17201721

}

172117221723+

void DatabaseSync::Serialize(const FunctionCallbackInfo<Value>& args) {

1724+

DatabaseSync* db;

1725+

ASSIGN_OR_RETURN_UNWRAP(&db, args.This());

1726+

Environment* env = Environment::GetCurrent(args);

1727+

THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open");

1728+1729+

std::string db_name = "main";

1730+

if (!args[0]->IsUndefined()) {

1731+

if (!args[0]->IsString()) {

1732+

THROW_ERR_INVALID_ARG_TYPE(env->isolate(),

1733+

"The \"dbName\" argument must be a string.");

1734+

return;

1735+

}

1736+

db_name = Utf8Value(env->isolate(), args[0].As<String>()).ToString();

1737+

}

1738+1739+

sqlite3_int64 size = 0;

1740+

unsigned char* data =

1741+

sqlite3_serialize(db->connection_, db_name.c_str(), &size, 0);

1742+1743+

if (data == nullptr) {

1744+

if (size == 0) {

1745+

Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), 0);

1746+

args.GetReturnValue().Set(Uint8Array::New(ab, 0, 0));

1747+

return;

1748+

}

1749+

THROW_ERR_SQLITE_ERROR(env->isolate(), db);

1750+

return;

1751+

}

1752+1753+

// V8 sandbox forbids external backing stores so allocate inside the

1754+

// sandbox and copy. Without sandbox wrap the output directly using

1755+

// sqlite3_free as the destructor to avoid the copy.

1756+

#ifdef V8_ENABLE_SANDBOX

1757+

auto free_data = OnScopeLeave([&] { sqlite3_free(data); });

1758+

auto store = ArrayBuffer::NewBackingStore(

1759+

env->isolate(),

1760+

size,

1761+

BackingStoreInitializationMode::kUninitialized,

1762+

BackingStoreOnFailureMode::kReturnNull);

1763+

if (!store) {

1764+

THROW_ERR_MEMORY_ALLOCATION_FAILED(env);

1765+

return;

1766+

}

1767+

memcpy(store->Data(), data, size);

1768+

Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(store));

1769+

#else

1770+

auto store = ArrayBuffer::NewBackingStore(

1771+

data, size, [](void* ptr, size_t, void*) { sqlite3_free(ptr); }, nullptr);

1772+

Local<ArrayBuffer> ab = ArrayBuffer::New(env->isolate(), std::move(store));

1773+

#endif

1774+1775+

args.GetReturnValue().Set(Uint8Array::New(ab, 0, size));

1776+

}

1777+1778+

void DatabaseSync::Deserialize(const FunctionCallbackInfo<Value>& args) {

1779+

DatabaseSync* db;

1780+

ASSIGN_OR_RETURN_UNWRAP(&db, args.This());

1781+

Environment* env = Environment::GetCurrent(args);

1782+

THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open");

1783+1784+

if (!args[0]->IsUint8Array()) {

1785+

THROW_ERR_INVALID_ARG_TYPE(env->isolate(),

1786+

"The \"buffer\" argument must be a Uint8Array.");

1787+

return;

1788+

}

1789+1790+

Local<Uint8Array> input = args[0].As<Uint8Array>();

1791+

size_t byte_length = input->ByteLength();

1792+1793+

if (byte_length == 0) {

1794+

THROW_ERR_INVALID_ARG_VALUE(env,

1795+

"The \"buffer\" argument must not be empty.");

1796+

return;

1797+

}

1798+1799+

std::string db_name = "main";

1800+

if (args.Length() > 1 && !args[1]->IsUndefined()) {

1801+

if (!args[1]->IsObject()) {

1802+

THROW_ERR_INVALID_ARG_TYPE(env->isolate(),

1803+

"The \"options\" argument must be an object.");

1804+

return;

1805+

}

1806+1807+

Local<Object> options = args[1].As<Object>();

1808+

Local<String> db_name_key = FIXED_ONE_BYTE_STRING(env->isolate(), "dbName");

1809+

Local<Value> db_name_value;

1810+

if (!options->Get(env->context(), db_name_key).ToLocal(&db_name_value)) {

1811+

return;

1812+

}

1813+1814+

if (!db_name_value->IsUndefined()) {

1815+

if (!db_name_value->IsString()) {

1816+

THROW_ERR_INVALID_ARG_TYPE(

1817+

env->isolate(),

1818+

"The \"options.dbName\" argument must be a string.");

1819+

return;

1820+

}

1821+

db_name =

1822+

Utf8Value(env->isolate(), db_name_value.As<String>()).ToString();

1823+

}

1824+

}

1825+1826+

// sqlite3_malloc64 is required because SQLITE_DESERIALIZE_FREEONCLOSE

1827+

// transfers ownership to SQLite, which calls sqlite3_free() on close.

1828+

// See: https://www.sqlite.org/c3ref/deserialize.html

1829+

unsigned char* buf =

1830+

static_cast<unsigned char*>(sqlite3_malloc64(byte_length));

1831+

if (buf == nullptr) {

1832+

THROW_ERR_MEMORY_ALLOCATION_FAILED(env);

1833+

return;

1834+

}

1835+1836+

input->CopyContents(buf, byte_length);

1837+1838+

db->FinalizeStatements();

1839+1840+

int r = sqlite3_deserialize(

1841+

db->connection_,

1842+

db_name.c_str(),

1843+

buf,

1844+

byte_length,

1845+

byte_length,

1846+

SQLITE_DESERIALIZE_FREEONCLOSE | SQLITE_DESERIALIZE_RESIZEABLE);

1847+

if (r != SQLITE_OK) {

1848+

THROW_ERR_SQLITE_ERROR(env->isolate(), db);

1849+

return;

1850+

}

1851+

}

1852+17221853

void DatabaseSync::AggregateFunction(const FunctionCallbackInfo<Value>& args) {

17231854

DatabaseSync* db;

17241855

ASSIGN_OR_RETURN_UNWRAP(&db, args.This());

@@ -3766,6 +3897,8 @@ static void Initialize(Local<Object> target,

37663897

isolate, db_tmpl, "enableDefensive", DatabaseSync::EnableDefensive);

37673898

SetProtoMethod(

37683899

isolate, db_tmpl, "loadExtension", DatabaseSync::LoadExtension);

3900+

SetProtoMethod(isolate, db_tmpl, "serialize", DatabaseSync::Serialize);

3901+

SetProtoMethod(isolate, db_tmpl, "deserialize", DatabaseSync::Deserialize);

37693902

SetProtoMethod(

37703903

isolate, db_tmpl, "setAuthorizer", DatabaseSync::SetAuthorizer);

37713904

SetSideEffectFreeGetter(isolate,