sqlite: add serialize() and deserialize() · nodejs/node@3c96ae1
@@ -23,6 +23,7 @@ namespace sqlite {
2323using v8::Array;
2424using v8::ArrayBuffer;
2525using v8::BackingStoreInitializationMode;
26+using v8::BackingStoreOnFailureMode;
2627using v8::BigInt;
2728using v8::Boolean;
2829using 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+17221853void DatabaseSync::AggregateFunction(const FunctionCallbackInfo<Value>& args) {
17231854 DatabaseSync* db;
17241855ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
@@ -3766,6 +3897,8 @@ static void Initialize(Local<Object> target,
37663897 isolate, db_tmpl, "enableDefensive", DatabaseSync::EnableDefensive);
37673898SetProtoMethod(
37683899 isolate, db_tmpl, "loadExtension", DatabaseSync::LoadExtension);
3900+SetProtoMethod(isolate, db_tmpl, "serialize", DatabaseSync::Serialize);
3901+SetProtoMethod(isolate, db_tmpl, "deserialize", DatabaseSync::Deserialize);
37693902SetProtoMethod(
37703903 isolate, db_tmpl, "setAuthorizer", DatabaseSync::SetAuthorizer);
37713904SetSideEffectFreeGetter(isolate,