sqlite,test,doc: allow Buffer and URL as database location · nodejs/node@227603d · GitHub
Skip to content

Commit 227603d

Browse files
geeksilva97RafaelGSS
authored andcommitted
sqlite,test,doc: allow Buffer and URL as database location
PR-URL: #56991 Backport-PR-URL: #57533 Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
1 parent be79f4a commit 227603d

5 files changed

Lines changed: 212 additions & 17 deletions

File tree

doc/api/sqlite.md

Lines changed: 8 additions & 4 deletions

src/env_properties.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@
186186
V(homedir_string, "homedir") \
187187
V(host_string, "host") \
188188
V(hostmaster_string, "hostmaster") \
189+
V(hostname_string, "hostname") \
190+
V(href_string, "href") \
189191
V(http_1_1_string, "http/1.1") \
190192
V(id_string, "id") \
191193
V(identity_string, "identity") \
@@ -295,6 +297,7 @@
295297
V(priority_string, "priority") \
296298
V(process_string, "process") \
297299
V(promise_string, "promise") \
300+
V(protocol_string, "protocol") \
298301
V(prototype_string, "prototype") \
299302
V(psk_string, "psk") \
300303
V(pubkey_string, "pubkey") \

src/node_sqlite.cc

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "node.h"
88
#include "node_errors.h"
99
#include "node_mem-inl.h"
10+
#include "node_url.h"
1011
#include "sqlite3.h"
1112
#include "util-inl.h"
1213

@@ -292,11 +293,14 @@ bool DatabaseSync::Open() {
292293
}
293294

294295
// TODO(cjihrig): Support additional flags.
296+
int default_flags = SQLITE_OPEN_URI;
295297
int flags = open_config_.get_read_only()
296298
? SQLITE_OPEN_READONLY
297299
: SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
298-
int r = sqlite3_open_v2(
299-
open_config_.location().c_str(), &connection_, flags, nullptr);
300+
int r = sqlite3_open_v2(open_config_.location().c_str(),
301+
&connection_,
302+
flags | default_flags,
303+
nullptr);
300304
CHECK_ERROR_OR_THROW(env()->isolate(), connection_, r, SQLITE_OK, false);
301305

302306
r = sqlite3_db_config(connection_,
@@ -358,27 +362,85 @@ inline sqlite3* DatabaseSync::Connection() {
358362
return connection_;
359363
}
360364

365+
std::optional<std::string> ValidateDatabasePath(Environment* env,
366+
Local<Value> path,
367+
const std::string& field_name) {
368+
auto has_null_bytes = [](const std::string& str) {
369+
return str.find('\0') != std::string::npos;
370+
};
371+
std::string location;
372+
if (path->IsString()) {
373+
location = Utf8Value(env->isolate(), path.As<String>()).ToString();
374+
if (!has_null_bytes(location)) {
375+
return location;
376+
}
377+
}
378+
379+
if (path->IsUint8Array()) {
380+
Local<Uint8Array> buffer = path.As<Uint8Array>();
381+
size_t byteOffset = buffer->ByteOffset();
382+
size_t byteLength = buffer->ByteLength();
383+
auto data =
384+
static_cast<const uint8_t*>(buffer->Buffer()->Data()) + byteOffset;
385+
if (!(std::find(data, data + byteLength, 0) != data + byteLength)) {
386+
Local<Value> out;
387+
if (String::NewFromUtf8(env->isolate(),
388+
reinterpret_cast<const char*>(data),
389+
NewStringType::kNormal,
390+
static_cast<int>(byteLength))
391+
.ToLocal(&out)) {
392+
return Utf8Value(env->isolate(), out.As<String>()).ToString();
393+
}
394+
}
395+
}
396+
397+
// When is URL
398+
if (path->IsObject()) {
399+
Local<Object> url = path.As<Object>();
400+
Local<Value> href;
401+
Local<Value> protocol;
402+
if (url->Get(env->context(), env->href_string()).ToLocal(&href) &&
403+
href->IsString() &&
404+
url->Get(env->context(), env->protocol_string()).ToLocal(&protocol) &&
405+
protocol->IsString()) {
406+
location = Utf8Value(env->isolate(), href.As<String>()).ToString();
407+
if (!has_null_bytes(location)) {
408+
auto file_url = ada::parse(location);
409+
CHECK(file_url);
410+
if (file_url->type != ada::scheme::FILE) {
411+
THROW_ERR_INVALID_URL_SCHEME(env->isolate());
412+
return std::nullopt;
413+
}
414+
415+
return location;
416+
}
417+
}
418+
}
419+
420+
THROW_ERR_INVALID_ARG_TYPE(env->isolate(),
421+
"The \"%s\" argument must be a string, "
422+
"Uint8Array, or URL without null bytes.",
423+
field_name.c_str());
424+
425+
return std::nullopt;
426+
}
427+
361428
void DatabaseSync::New(const FunctionCallbackInfo<Value>& args) {
362429
Environment* env = Environment::GetCurrent(args);
363-
364430
if (!args.IsConstructCall()) {
365431
THROW_ERR_CONSTRUCT_CALL_REQUIRED(env);
366432
return;
367433
}
368434

369-
if (!args[0]->IsString()) {
370-
THROW_ERR_INVALID_ARG_TYPE(env->isolate(),
371-
"The \"path\" argument must be a string.");
435+
std::optional<std::string> location =
436+
ValidateDatabasePath(env, args[0], "path");
437+
if (!location.has_value()) {
372438
return;
373439
}
374440

375-
std::string location =
376-
Utf8Value(env->isolate(), args[0].As<String>()).ToString();
377-
DatabaseOpenConfiguration open_config(std::move(location));
378-
441+
DatabaseOpenConfiguration open_config(std::move(location.value()));
379442
bool open = true;
380443
bool allow_load_extension = false;
381-
382444
if (args.Length() > 1) {
383445
if (!args[1]->IsObject()) {
384446
THROW_ERR_INVALID_ARG_TYPE(env->isolate(),

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,30 @@ suite('DatabaseSync() constructor', () => {
2323
});
2424
});
2525

26-
test('throws if database path is not a string', (t) => {
26+
test('throws if database path is not a string, Uint8Array, or URL', (t) => {
2727
t.assert.throws(() => {
2828
new DatabaseSync();
2929
}, {
3030
code: 'ERR_INVALID_ARG_TYPE',
31-
message: /The "path" argument must be a string/,
31+
message: /The "path" argument must be a string, Uint8Array, or URL without null bytes/,
32+
});
33+
});
34+
35+
test('throws if the database location as Buffer contains null bytes', (t) => {
36+
t.assert.throws(() => {
37+
new DatabaseSync(Buffer.from('l\0cation'));
38+
}, {
39+
code: 'ERR_INVALID_ARG_TYPE',
40+
message: 'The "path" argument must be a string, Uint8Array, or URL without null bytes.',
41+
});
42+
});
43+
44+
test('throws if the database location as string contains null bytes', (t) => {
45+
t.assert.throws(() => {
46+
new DatabaseSync('l\0cation');
47+
}, {
48+
code: 'ERR_INVALID_ARG_TYPE',
49+
message: 'The "path" argument must be a string, Uint8Array, or URL without null bytes.',
3250
});
3351
});
3452

@@ -256,6 +274,15 @@ suite('DatabaseSync.prototype.exec()', () => {
256274
});
257275
});
258276

277+
test('throws if the URL does not have the file: scheme', (t) => {
278+
t.assert.throws(() => {
279+
new DatabaseSync(new URL('http://example.com'));
280+
}, {
281+
code: 'ERR_INVALID_URL_SCHEME',
282+
message: 'The URL must be of scheme file:',
283+
});
284+
});
285+
259286
test('throws if database is not open', (t) => {
260287
const db = new DatabaseSync(nextDb(), { open: false });
261288

test/parallel/test-sqlite.js

Lines changed: 99 additions & 0 deletions

0 commit comments

Comments
 (0)