From d32daa5fac269e45a819cc273f8c2a416f9395e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20S=2E=20Ga=C3=9Fmann?= Date: Sun, 27 Mar 2022 08:18:23 +0200 Subject: [PATCH 1/2] Wrap sqlite3 OPEN_URI option --- docs/api.md | 2 ++ lib/database.js | 5 +-- src/better_sqlite3.cpp | 74 +++++++++++++++++++++------------------- src/better_sqlite3.hpp | 58 +++++++++++++++---------------- src/objects/database.lzz | 10 ++++-- 5 files changed, 80 insertions(+), 69 deletions(-) diff --git a/docs/api.md b/docs/api.md index 8c074d1a8..f09114ca4 100644 --- a/docs/api.md +++ b/docs/api.md @@ -33,6 +33,8 @@ Various options are accepted: - `options.fileMustExist`: if the database does not exist, an `Error` will be thrown instead of creating a new file. This option is ignored for in-memory, temporary, or readonly database connections (default: `false`). +- `options.uriPath`: The filename can be interpreted as an URI if this flag is set (passes `SQLITE_OPEN_URI` to the underlying `sqlite3_open_v2()`). See also: [`sqlite3_open_v2()` documentation](https://sqlite.org/c3ref/open.html) + - `options.timeout`: the number of milliseconds to wait when executing queries on a locked database, before throwing a `SQLITE_BUSY` error (default: `5000`). - `options.verbose`: provide a function that gets called with every SQL string executed by the database connection (default: `null`). diff --git a/lib/database.js b/lib/database.js index aea774d37..5392fa8fb 100644 --- a/lib/database.js +++ b/lib/database.js @@ -31,6 +31,7 @@ function Database(filenameGiven, options) { const anonymous = filename === '' || filename === ':memory:'; const readonly = util.getBooleanOption(options, 'readonly'); const fileMustExist = util.getBooleanOption(options, 'fileMustExist'); + const uriPath = util.getBooleanOption(options, 'uriPath'); const timeout = 'timeout' in options ? options.timeout : 5000; const verbose = 'verbose' in options ? options.verbose : null; const nativeBindingPath = 'nativeBinding' in options ? options.nativeBinding : null; @@ -55,12 +56,12 @@ function Database(filenameGiven, options) { } // Make sure the specified directory exists - if (!anonymous && !fs.existsSync(path.dirname(filename))) { + if (!anonymous && !uriPath && !fs.existsSync(path.dirname(filename))) { throw new TypeError('Cannot open database because the directory does not exist'); } Object.defineProperties(this, { - [util.cppdb]: { value: new addon.Database(filename, filenameGiven, anonymous, readonly, fileMustExist, timeout, verbose || null, buffer || null) }, + [util.cppdb]: { value: new addon.Database(filename, filenameGiven, anonymous, readonly, fileMustExist, uriPath, timeout, verbose || null, buffer || null) }, ...wrappers.getters, }); } diff --git a/src/better_sqlite3.cpp b/src/better_sqlite3.cpp index 777c8b82c..727dd1e1c 100644 --- a/src/better_sqlite3.cpp +++ b/src/better_sqlite3.cpp @@ -414,9 +414,10 @@ void Database::JS_new (v8::FunctionCallbackInfo const & info) if ( info . Length ( ) <= ( 2 ) || ! info [ 2 ] -> IsBoolean ( ) ) return ThrowTypeError ( "Expected " "third" " argument to be " "a boolean" ) ; bool in_memory = ( info [ 2 ] . As < v8 :: Boolean > ( ) ) -> Value ( ) ; if ( info . Length ( ) <= ( 3 ) || ! info [ 3 ] -> IsBoolean ( ) ) return ThrowTypeError ( "Expected " "fourth" " argument to be " "a boolean" ) ; bool readonly = ( info [ 3 ] . As < v8 :: Boolean > ( ) ) -> Value ( ) ; if ( info . Length ( ) <= ( 4 ) || ! info [ 4 ] -> IsBoolean ( ) ) return ThrowTypeError ( "Expected " "fifth" " argument to be " "a boolean" ) ; bool must_exist = ( info [ 4 ] . As < v8 :: Boolean > ( ) ) -> Value ( ) ; - if ( info . Length ( ) <= ( 5 ) || ! info [ 5 ] -> IsInt32 ( ) ) return ThrowTypeError ( "Expected " "sixth" " argument to be " "a 32-bit signed integer" ) ; int timeout = ( info [ 5 ] . As < v8 :: Int32 > ( ) ) -> Value ( ) ; - if ( info . Length ( ) <= ( 6 ) ) return ThrowTypeError ( "Expected a " "seventh" " argument" ) ; v8 :: Local < v8 :: Value > logger = info [ 6 ] ; - if ( info . Length ( ) <= ( 7 ) ) return ThrowTypeError ( "Expected a " "eighth" " argument" ) ; v8 :: Local < v8 :: Value > buffer = info [ 7 ] ; + if ( info . Length ( ) <= ( 5 ) || ! info [ 5 ] -> IsBoolean ( ) ) return ThrowTypeError ( "Expected " "sixth" " argument to be " "a boolean" ) ; bool uri_path = ( info [ 5 ] . As < v8 :: Boolean > ( ) ) -> Value ( ) ; + if ( info . Length ( ) <= ( 6 ) || ! info [ 6 ] -> IsInt32 ( ) ) return ThrowTypeError ( "Expected " "seventh" " argument to be " "a 32-bit signed integer" ) ; int timeout = ( info [ 6 ] . As < v8 :: Int32 > ( ) ) -> Value ( ) ; + if ( info . Length ( ) <= ( 7 ) ) return ThrowTypeError ( "Expected a " "eighth" " argument" ) ; v8 :: Local < v8 :: Value > logger = info [ 7 ] ; + if ( info . Length ( ) <= ( 8 ) ) return ThrowTypeError ( "Expected a " "ninth" " argument" ) ; v8 :: Local < v8 :: Value > buffer = info [ 8 ] ; Addon * addon = static_cast < Addon * > ( info . Data ( ) . As < v8 :: External > ( ) -> Value ( ) ) ; v8 :: Isolate * isolate = info . GetIsolate ( ) ; @@ -425,6 +426,9 @@ void Database::JS_new (v8::FunctionCallbackInfo const & info) int mask = readonly ? SQLITE_OPEN_READONLY : must_exist ? SQLITE_OPEN_READWRITE : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + if (uri_path) { + mask |= SQLITE_OPEN_URI; + } if (sqlite3_open_v2(*utf8, &db_handle, mask, NULL) != SQLITE_OK) { ThrowSqliteError(addon, db_handle); @@ -458,9 +462,9 @@ void Database::JS_new (v8::FunctionCallbackInfo const & info) info.GetReturnValue().Set(info.This()); } -#line 201 "./src/objects/database.lzz" +#line 205 "./src/objects/database.lzz" void Database::JS_prepare (v8::FunctionCallbackInfo const & info) -#line 201 "./src/objects/database.lzz" +#line 205 "./src/objects/database.lzz" { if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsString ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a string" ) ; v8 :: Local < v8 :: String > source = ( info [ 0 ] . As < v8 :: String > ( ) ) ; if ( info . Length ( ) <= ( 1 ) || ! info [ 1 ] -> IsObject ( ) ) return ThrowTypeError ( "Expected " "second" " argument to be " "an object" ) ; v8 :: Local < v8 :: Object > database = ( info [ 1 ] . As < v8 :: Object > ( ) ) ; @@ -476,9 +480,9 @@ void Database::JS_prepare (v8::FunctionCallbackInfo const & info) addon->privileged_info = NULL; if (!maybeStatement.IsEmpty()) info.GetReturnValue().Set(maybeStatement.ToLocalChecked()); } -#line 217 "./src/objects/database.lzz" +#line 221 "./src/objects/database.lzz" void Database::JS_exec (v8::FunctionCallbackInfo const & info) -#line 217 "./src/objects/database.lzz" +#line 221 "./src/objects/database.lzz" { Database* db = node :: ObjectWrap :: Unwrap (info.This()); if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsString ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a string" ) ; v8 :: Local < v8 :: String > source = ( info [ 0 ] . As < v8 :: String > ( ) ) ; @@ -518,9 +522,9 @@ void Database::JS_exec (v8::FunctionCallbackInfo const & info) db->ThrowDatabaseError(); } } -#line 257 "./src/objects/database.lzz" +#line 261 "./src/objects/database.lzz" void Database::JS_backup (v8::FunctionCallbackInfo const & info) -#line 257 "./src/objects/database.lzz" +#line 261 "./src/objects/database.lzz" { if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsObject ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "an object" ) ; v8 :: Local < v8 :: Object > database = ( info [ 0 ] . As < v8 :: Object > ( ) ) ; if ( info . Length ( ) <= ( 1 ) || ! info [ 1 ] -> IsString ( ) ) return ThrowTypeError ( "Expected " "second" " argument to be " "a string" ) ; v8 :: Local < v8 :: String > attachedName = ( info [ 1 ] . As < v8 :: String > ( ) ) ; @@ -538,9 +542,9 @@ void Database::JS_backup (v8::FunctionCallbackInfo const & info) addon->privileged_info = NULL; if (!maybeBackup.IsEmpty()) info.GetReturnValue().Set(maybeBackup.ToLocalChecked()); } -#line 275 "./src/objects/database.lzz" +#line 279 "./src/objects/database.lzz" void Database::JS_serialize (v8::FunctionCallbackInfo const & info) -#line 275 "./src/objects/database.lzz" +#line 279 "./src/objects/database.lzz" { Database* db = node :: ObjectWrap :: Unwrap (info.This()); if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsString ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a string" ) ; v8 :: Local < v8 :: String > attachedName = ( info [ 0 ] . As < v8 :: String > ( ) ) ; @@ -562,9 +566,9 @@ void Database::JS_serialize (v8::FunctionCallbackInfo const & info node::Buffer::New(isolate, reinterpret_cast(data), length, FreeSerialization, NULL).ToLocalChecked() ); } -#line 297 "./src/objects/database.lzz" +#line 301 "./src/objects/database.lzz" void Database::JS_function (v8::FunctionCallbackInfo const & info) -#line 297 "./src/objects/database.lzz" +#line 301 "./src/objects/database.lzz" { Database* db = node :: ObjectWrap :: Unwrap (info.This()); if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsFunction ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a function" ) ; v8 :: Local < v8 :: Function > fn = ( info [ 0 ] . As < v8 :: Function > ( ) ) ; @@ -588,9 +592,9 @@ void Database::JS_function (v8::FunctionCallbackInfo const & info) db->ThrowDatabaseError(); } } -#line 321 "./src/objects/database.lzz" +#line 325 "./src/objects/database.lzz" void Database::JS_aggregate (v8::FunctionCallbackInfo const & info) -#line 321 "./src/objects/database.lzz" +#line 325 "./src/objects/database.lzz" { Database* db = node :: ObjectWrap :: Unwrap (info.This()); if ( info . Length ( ) <= ( 0 ) ) return ThrowTypeError ( "Expected a " "first" " argument" ) ; v8 :: Local < v8 :: Value > start = info [ 0 ] ; @@ -619,9 +623,9 @@ void Database::JS_aggregate (v8::FunctionCallbackInfo const & info db->ThrowDatabaseError(); } } -#line 350 "./src/objects/database.lzz" +#line 354 "./src/objects/database.lzz" void Database::JS_table (v8::FunctionCallbackInfo const & info) -#line 350 "./src/objects/database.lzz" +#line 354 "./src/objects/database.lzz" { Database* db = node :: ObjectWrap :: Unwrap (info.This()); if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsFunction ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a function" ) ; v8 :: Local < v8 :: Function > factory = ( info [ 0 ] . As < v8 :: Function > ( ) ) ; @@ -641,9 +645,9 @@ void Database::JS_table (v8::FunctionCallbackInfo const & info) } db->busy = false; } -#line 370 "./src/objects/database.lzz" +#line 374 "./src/objects/database.lzz" void Database::JS_loadExtension (v8::FunctionCallbackInfo const & info) -#line 370 "./src/objects/database.lzz" +#line 374 "./src/objects/database.lzz" { Database* db = node :: ObjectWrap :: Unwrap (info.This()); v8::Local entryPoint; @@ -665,9 +669,9 @@ void Database::JS_loadExtension (v8::FunctionCallbackInfo const & } sqlite3_free(error); } -#line 392 "./src/objects/database.lzz" +#line 396 "./src/objects/database.lzz" void Database::JS_close (v8::FunctionCallbackInfo const & info) -#line 392 "./src/objects/database.lzz" +#line 396 "./src/objects/database.lzz" { Database* db = node :: ObjectWrap :: Unwrap (info.This()); if (db->open) { @@ -677,39 +681,39 @@ void Database::JS_close (v8::FunctionCallbackInfo const & info) db->CloseHandles(); } } -#line 402 "./src/objects/database.lzz" +#line 406 "./src/objects/database.lzz" void Database::JS_defaultSafeIntegers (v8::FunctionCallbackInfo const & info) -#line 402 "./src/objects/database.lzz" +#line 406 "./src/objects/database.lzz" { Database* db = node :: ObjectWrap :: Unwrap (info.This()); if (info.Length() == 0) db->safe_ints = true; else { if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsBoolean ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a boolean" ) ; db -> safe_ints = ( info [ 0 ] . As < v8 :: Boolean > ( ) ) -> Value ( ) ; } } -#line 408 "./src/objects/database.lzz" +#line 412 "./src/objects/database.lzz" void Database::JS_unsafeMode (v8::FunctionCallbackInfo const & info) -#line 408 "./src/objects/database.lzz" +#line 412 "./src/objects/database.lzz" { Database* db = node :: ObjectWrap :: Unwrap (info.This()); if (info.Length() == 0) db->unsafe_mode = true; else { if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsBoolean ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a boolean" ) ; db -> unsafe_mode = ( info [ 0 ] . As < v8 :: Boolean > ( ) ) -> Value ( ) ; } sqlite3_db_config(db->db_handle, SQLITE_DBCONFIG_DEFENSIVE, static_cast(!db->unsafe_mode), NULL); } -#line 415 "./src/objects/database.lzz" +#line 419 "./src/objects/database.lzz" void Database::JS_open (v8::Local _, v8::PropertyCallbackInfo const & info) -#line 415 "./src/objects/database.lzz" +#line 419 "./src/objects/database.lzz" { info.GetReturnValue().Set( node :: ObjectWrap :: Unwrap (info.This())->open); } -#line 419 "./src/objects/database.lzz" +#line 423 "./src/objects/database.lzz" void Database::JS_inTransaction (v8::Local _, v8::PropertyCallbackInfo const & info) -#line 419 "./src/objects/database.lzz" +#line 423 "./src/objects/database.lzz" { Database* db = node :: ObjectWrap :: Unwrap (info.This()); info.GetReturnValue().Set(db->open && !static_cast(sqlite3_get_autocommit(db->db_handle))); } -#line 424 "./src/objects/database.lzz" +#line 428 "./src/objects/database.lzz" bool Database::Deserialize (v8::Local buffer, Addon * addon, sqlite3 * db_handle, bool readonly) -#line 424 "./src/objects/database.lzz" +#line 428 "./src/objects/database.lzz" { size_t length = node::Buffer::Length(buffer); unsigned char* data = (unsigned char*)sqlite3_malloc64(length); @@ -734,15 +738,15 @@ bool Database::Deserialize (v8::Local buffer, Addon * addon, sqlite return true; } -#line 449 "./src/objects/database.lzz" +#line 453 "./src/objects/database.lzz" void Database::FreeSerialization (char * data, void * _) -#line 449 "./src/objects/database.lzz" +#line 453 "./src/objects/database.lzz" { sqlite3_free(data); } -#line 453 "./src/objects/database.lzz" +#line 457 "./src/objects/database.lzz" int const Database::MAX_BUFFER_SIZE; -#line 454 "./src/objects/database.lzz" +#line 458 "./src/objects/database.lzz" int const Database::MAX_STRING_SIZE; #line 4 "./src/objects/statement.lzz" v8::Local Statement::Init (v8::Isolate * isolate, v8::Local data) diff --git a/src/better_sqlite3.hpp b/src/better_sqlite3.hpp index 5d25ee970..94d3ba6a7 100644 --- a/src/better_sqlite3.hpp +++ b/src/better_sqlite3.hpp @@ -244,63 +244,63 @@ class Database : public node::ObjectWrap explicit Database (v8::Isolate * isolate, Addon * addon, sqlite3 * db_handle, v8::Local logger); #line 149 "./src/objects/database.lzz" static void JS_new (v8::FunctionCallbackInfo const & info); -#line 201 "./src/objects/database.lzz" +#line 205 "./src/objects/database.lzz" static void JS_prepare (v8::FunctionCallbackInfo const & info); -#line 217 "./src/objects/database.lzz" +#line 221 "./src/objects/database.lzz" static void JS_exec (v8::FunctionCallbackInfo const & info); -#line 257 "./src/objects/database.lzz" +#line 261 "./src/objects/database.lzz" static void JS_backup (v8::FunctionCallbackInfo const & info); -#line 275 "./src/objects/database.lzz" +#line 279 "./src/objects/database.lzz" static void JS_serialize (v8::FunctionCallbackInfo const & info); -#line 297 "./src/objects/database.lzz" +#line 301 "./src/objects/database.lzz" static void JS_function (v8::FunctionCallbackInfo const & info); -#line 321 "./src/objects/database.lzz" +#line 325 "./src/objects/database.lzz" static void JS_aggregate (v8::FunctionCallbackInfo const & info); -#line 350 "./src/objects/database.lzz" +#line 354 "./src/objects/database.lzz" static void JS_table (v8::FunctionCallbackInfo const & info); -#line 370 "./src/objects/database.lzz" +#line 374 "./src/objects/database.lzz" static void JS_loadExtension (v8::FunctionCallbackInfo const & info); -#line 392 "./src/objects/database.lzz" +#line 396 "./src/objects/database.lzz" static void JS_close (v8::FunctionCallbackInfo const & info); -#line 402 "./src/objects/database.lzz" +#line 406 "./src/objects/database.lzz" static void JS_defaultSafeIntegers (v8::FunctionCallbackInfo const & info); -#line 408 "./src/objects/database.lzz" +#line 412 "./src/objects/database.lzz" static void JS_unsafeMode (v8::FunctionCallbackInfo const & info); -#line 415 "./src/objects/database.lzz" - static void JS_open (v8::Local _, v8::PropertyCallbackInfo const & info); #line 419 "./src/objects/database.lzz" + static void JS_open (v8::Local _, v8::PropertyCallbackInfo const & info); +#line 423 "./src/objects/database.lzz" static void JS_inTransaction (v8::Local _, v8::PropertyCallbackInfo const & info); -#line 424 "./src/objects/database.lzz" +#line 428 "./src/objects/database.lzz" static bool Deserialize (v8::Local buffer, Addon * addon, sqlite3 * db_handle, bool readonly); -#line 449 "./src/objects/database.lzz" - static void FreeSerialization (char * data, void * _); #line 453 "./src/objects/database.lzz" + static void FreeSerialization (char * data, void * _); +#line 457 "./src/objects/database.lzz" static int const MAX_BUFFER_SIZE = node::Buffer::kMaxLength > INT_MAX ? INT_MAX : static_cast(node::Buffer::kMaxLength); -#line 454 "./src/objects/database.lzz" +#line 458 "./src/objects/database.lzz" static int const MAX_STRING_SIZE = v8::String::kMaxLength > INT_MAX ? INT_MAX : static_cast(v8::String::kMaxLength); -#line 456 "./src/objects/database.lzz" +#line 460 "./src/objects/database.lzz" sqlite3 * const db_handle; -#line 457 "./src/objects/database.lzz" +#line 461 "./src/objects/database.lzz" bool open; -#line 458 "./src/objects/database.lzz" +#line 462 "./src/objects/database.lzz" bool busy; -#line 459 "./src/objects/database.lzz" +#line 463 "./src/objects/database.lzz" bool safe_ints; -#line 460 "./src/objects/database.lzz" +#line 464 "./src/objects/database.lzz" bool unsafe_mode; -#line 461 "./src/objects/database.lzz" +#line 465 "./src/objects/database.lzz" bool was_js_error; -#line 462 "./src/objects/database.lzz" +#line 466 "./src/objects/database.lzz" bool const has_logger; -#line 463 "./src/objects/database.lzz" +#line 467 "./src/objects/database.lzz" unsigned short int iterators; -#line 464 "./src/objects/database.lzz" +#line 468 "./src/objects/database.lzz" Addon * const addon; -#line 465 "./src/objects/database.lzz" +#line 469 "./src/objects/database.lzz" CopyablePersistent const logger; -#line 466 "./src/objects/database.lzz" +#line 470 "./src/objects/database.lzz" std::set stmts; -#line 467 "./src/objects/database.lzz" +#line 471 "./src/objects/database.lzz" std::set backups; }; #line 1 "./src/objects/statement.lzz" diff --git a/src/objects/database.lzz b/src/objects/database.lzz index 94b1d455e..c7b40a5ca 100644 --- a/src/objects/database.lzz +++ b/src/objects/database.lzz @@ -153,9 +153,10 @@ private: REQUIRE_ARGUMENT_BOOLEAN(third, bool in_memory); REQUIRE_ARGUMENT_BOOLEAN(fourth, bool readonly); REQUIRE_ARGUMENT_BOOLEAN(fifth, bool must_exist); - REQUIRE_ARGUMENT_INT32(sixth, int timeout); - REQUIRE_ARGUMENT_ANY(seventh, v8::Local logger); - REQUIRE_ARGUMENT_ANY(eighth, v8::Local buffer); + REQUIRE_ARGUMENT_BOOLEAN(sixth, bool uri_path); + REQUIRE_ARGUMENT_INT32(seventh, int timeout); + REQUIRE_ARGUMENT_ANY(eighth, v8::Local logger); + REQUIRE_ARGUMENT_ANY(ninth, v8::Local buffer); UseAddon; UseIsolate; @@ -164,6 +165,9 @@ private: int mask = readonly ? SQLITE_OPEN_READONLY : must_exist ? SQLITE_OPEN_READWRITE : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + if (uri_path) { + mask |= SQLITE_OPEN_URI; + } if (sqlite3_open_v2(*utf8, &db_handle, mask, NULL) != SQLITE_OK) { ThrowSqliteError(addon, db_handle); From e19ec371971516fcd7f194f0f1b88b50396467f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20S=2E=20Ga=C3=9Fmann?= Date: Mon, 23 May 2022 15:32:30 +0200 Subject: [PATCH 2/2] Add a test for opening dbs by file URI --- test/10.database.open.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/10.database.open.js b/test/10.database.open.js index 465088bfb..23a77cd95 100644 --- a/test/10.database.open.js +++ b/test/10.database.open.js @@ -1,6 +1,7 @@ 'use strict'; const fs = require('fs'); const path = require('path'); +const url = require('url') const Database = require('../.'); describe('new Database()', function () { @@ -90,6 +91,17 @@ describe('new Database()', function () { expect(db.inTransaction).to.be.false; expect(fs.existsSync(util.current())).to.be.true; }); + it('should open databases with URI paths', function () { + expect(fs.existsSync(util.next())).to.be.false; + const uri = url.pathToFileURL(util.current()).toString(); + const db = this.db = new Database(uri, { uriPath: true }); + expect(db.name).to.equal(uri); + expect(db.memory).to.be.false; + expect(db.readonly).to.be.false; + expect(db.open).to.be.true; + expect(db.inTransaction).to.be.false; + expect(fs.existsSync(util.current())).to.be.true; + }); util.itUnix('should accept the "timeout" option', function () { this.slow(4000); const testTimeout = (timeout) => {