removed db.checkpoint() method in favor of wal_checkpoint(RESTART) pragma

This commit is contained in:
Joshua Wise 2020-04-22 12:05:52 -05:00
parent 32f0347e00
commit 94521dd185
8 changed files with 134 additions and 242 deletions

View File

@ -9,7 +9,6 @@
- [Database#prepare()](#preparestring---statement) (see [`Statement`](#class-statement))
- [Database#transaction()](#transactionfunction---function)
- [Database#pragma()](#pragmastring-options---results)
- [Database#checkpoint()](#checkpointdatabasename---this)
- [Database#backup()](#backupdestination-options---promise)
- [Database#function()](#functionname-options-function---this)
- [Database#aggregate()](#aggregatename-options---this)
@ -117,20 +116,6 @@ If execution of the PRAGMA fails, an `Error` is thrown.
It's better to use this method instead of normal [prepared statements](#preparestring---statement) when executing PRAGMA, because this method normalizes some odd behavior that may otherwise be experienced. The documentation on SQLite3 PRAGMA can be found [here](https://www.sqlite.org/pragma.html).
### .checkpoint([*databaseName*]) -> *this*
Runs a [WAL mode checkpoint](https://www.sqlite.org/wal.html) on all attached databases (including the main database).
Unlike [automatic checkpoints](https://www.sqlite.org/wal.html#automatic_checkpoint), this method executes a checkpoint in "RESTART" mode, which ensures a complete checkpoint operation even if other processes are using the database at the same time. You only need to use this method if you are accessing the database from multiple processes at the same time.
```js
setInterval(() => db.checkpoint(), 30000).unref();
```
If `databaseName` is provided, it should be the name of an attached database (or `"main"`). This causes only that database to be checkpointed.
If the checkpoint fails, an `Error` is thrown.
### .backup(*destination*, [*options*]) -> *promise*
Initiates a [backup](https://www.sqlite.org/backup.html) of the database, returning a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) for when the backup is complete. If the backup fails, the promise will be rejected with an `Error`. You can optionally backup an attached database by setting the `attached` option to the name of the desired attached database.

View File

@ -18,16 +18,16 @@ However, you trade those disadvantages for extremely fast performance in most we
Checkpoint starvation is when SQLite3 is unable to recycle the [WAL file](https://www.sqlite.org/wal.html) due to everlasting concurrent reads to the database. If this happens, the WAL file will grow without bound, leading to unacceptable amounts of disk usage and deteriorating performance.
If you don't access the database from multiple processes simultaneously, you'll never encounter this issue.
If you don't access the database from multiple processes or threads simultaneously, you'll never encounter this issue.
If you do access the database from multiple processes simultaneously, just use the [`db.checkpoint()`](./api.md#checkpointdatabasename---this) method when the WAL file gets too big.
If you do access the database from multiple processes or threads simultaneously, just use the [`wal_checkpoint(RESTART)`](https://www.sqlite.org/pragma.html#pragma_wal_checkpoint) pragma when the WAL file gets too big.
```js
setInterval(fs.stat.bind(null, 'foobar.db-wal', (err, stat) => {
if (err) {
if (err.code !== 'ENOENT') throw err;
} else if (stat.size > someUnacceptableSize) {
db.checkpoint();
db.pragma('wal_checkpoint(RESTART)');
}
}), 5000).unref();
```

View File

@ -315,7 +315,6 @@ v8::Local <v8 :: Function> Database::Init (v8::Isolate * isolate, v8::Local <v8
SetPrototypeMethod(isolate, data, t, "prepare", JS_prepare);
SetPrototypeMethod(isolate, data, t, "exec", JS_exec);
SetPrototypeMethod(isolate, data, t, "pragma", JS_pragma);
SetPrototypeMethod(isolate, data, t, "checkpoint", JS_checkpoint);
SetPrototypeMethod(isolate, data, t, "backup", JS_backup);
SetPrototypeMethod(isolate, data, t, "function", JS_function);
SetPrototypeMethod(isolate, data, t, "aggregate", JS_aggregate);
@ -326,41 +325,41 @@ v8::Local <v8 :: Function> Database::Init (v8::Isolate * isolate, v8::Local <v8
SetPrototypeGetter(isolate, data, t, "inTransaction", JS_inTransaction);
return t->GetFunction( isolate -> GetCurrentContext ( ) ).ToLocalChecked();
}
#line 23 "./src/objects/database.lzz"
#line 22 "./src/objects/database.lzz"
bool Database::CompareDatabase::operator () (Database const * const a, Database const * const b) const
#line 23 "./src/objects/database.lzz"
#line 22 "./src/objects/database.lzz"
{
return a < b;
}
#line 28 "./src/objects/database.lzz"
#line 27 "./src/objects/database.lzz"
bool Database::CompareStatement::operator () (Statement const * const a, Statement const * const b) const
#line 28 "./src/objects/database.lzz"
#line 27 "./src/objects/database.lzz"
{
return Statement::Compare(a, b);
}
#line 33 "./src/objects/database.lzz"
#line 32 "./src/objects/database.lzz"
bool Database::CompareBackup::operator () (Backup const * const a, Backup const * const b) const
#line 33 "./src/objects/database.lzz"
#line 32 "./src/objects/database.lzz"
{
return Backup::Compare(a, b);
}
#line 39 "./src/objects/database.lzz"
#line 38 "./src/objects/database.lzz"
void Database::ThrowDatabaseError ()
#line 39 "./src/objects/database.lzz"
#line 38 "./src/objects/database.lzz"
{
if (was_js_error) was_js_error = false;
else ThrowSqliteError(addon, db_handle);
}
#line 43 "./src/objects/database.lzz"
#line 42 "./src/objects/database.lzz"
void Database::ThrowSqliteError (Addon * addon, sqlite3 * db_handle)
#line 43 "./src/objects/database.lzz"
#line 42 "./src/objects/database.lzz"
{
assert(db_handle != NULL);
ThrowSqliteError(addon, sqlite3_errmsg(db_handle), sqlite3_extended_errcode(db_handle));
}
#line 47 "./src/objects/database.lzz"
#line 46 "./src/objects/database.lzz"
void Database::ThrowSqliteError (Addon * addon, char const * message, int code)
#line 47 "./src/objects/database.lzz"
#line 46 "./src/objects/database.lzz"
{
assert(message != NULL);
assert((code & 0xff) != SQLITE_OK);
@ -370,9 +369,9 @@ void Database::ThrowSqliteError (Addon * addon, char const * message, int code)
v8::Local<v8::Value> args[2] = { StringFromUtf8(isolate, message, -1), addon->cs.Code(isolate, code) };
isolate->ThrowException(v8::Local<v8::Function>::New(isolate, addon->SqliteError)->NewInstance( isolate -> GetCurrentContext ( ) , 2, args).ToLocalChecked());
}
#line 58 "./src/objects/database.lzz"
#line 57 "./src/objects/database.lzz"
bool Database::Log (v8::Isolate * isolate, v8::Local <v8::Value> data)
#line 58 "./src/objects/database.lzz"
#line 57 "./src/objects/database.lzz"
{
if (!has_logger) return false;
assert(was_js_error == false);
@ -380,9 +379,9 @@ bool Database::Log (v8::Isolate * isolate, v8::Local <v8::Value> data)
->Call( isolate -> GetCurrentContext ( ) , v8::Undefined(isolate), 1, &data)
.IsEmpty();
}
#line 65 "./src/objects/database.lzz"
#line 64 "./src/objects/database.lzz"
bool Database::Log (v8::Isolate * isolate, sqlite3_stmt * handle)
#line 65 "./src/objects/database.lzz"
#line 64 "./src/objects/database.lzz"
{
if (!has_logger) return false;
char* expanded = sqlite3_expanded_sql(handle);
@ -390,9 +389,9 @@ bool Database::Log (v8::Isolate * isolate, sqlite3_stmt * handle)
if (expanded) sqlite3_free(expanded);
return failed;
}
#line 103 "./src/objects/database.lzz"
#line 102 "./src/objects/database.lzz"
void Database::CloseHandles ()
#line 103 "./src/objects/database.lzz"
#line 102 "./src/objects/database.lzz"
{
if (open) {
open = false;
@ -404,25 +403,25 @@ void Database::CloseHandles ()
assert(status == SQLITE_OK); ((void)status);
}
}
#line 115 "./src/objects/database.lzz"
#line 114 "./src/objects/database.lzz"
Database::~ Database ()
#line 115 "./src/objects/database.lzz"
#line 114 "./src/objects/database.lzz"
{
if (open) addon->dbs.erase(this);
CloseHandles();
}
#line 122 "./src/objects/database.lzz"
#line 121 "./src/objects/database.lzz"
Database::Database (sqlite3 * _db_handle, v8::Isolate * isolate, Addon * _addon, v8::Local <v8::Value> _logger)
#line 122 "./src/objects/database.lzz"
#line 121 "./src/objects/database.lzz"
: node::ObjectWrap (), db_handle (_db_handle), open (true), busy (false), pragma_mode (false), safe_ints (false), was_js_error (false), has_logger (_logger->IsFunction()), iterators (0), addon (_addon), logger (isolate, _logger), stmts (), backups ()
#line 134 "./src/objects/database.lzz"
#line 133 "./src/objects/database.lzz"
{
assert(_db_handle != NULL);
addon->dbs.insert(this);
}
#line 139 "./src/objects/database.lzz"
#line 138 "./src/objects/database.lzz"
void Database::JS_new (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 139 "./src/objects/database.lzz"
#line 138 "./src/objects/database.lzz"
{
assert(info.IsConstructCall());
if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsString ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a string" ) ; v8 :: Local < v8 :: String > filename = v8 :: Local < v8 :: String > :: Cast ( info [ 0 ] ) ;
@ -467,9 +466,9 @@ void Database::JS_new (v8::FunctionCallbackInfo <v8 :: Value> const & info)
info.GetReturnValue().Set(info.This());
}
#line 184 "./src/objects/database.lzz"
#line 183 "./src/objects/database.lzz"
void Database::JS_prepare (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 184 "./src/objects/database.lzz"
#line 183 "./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 = v8 :: Local < v8 :: String > :: Cast ( info [ 0 ] ) ;
(void)source;
@ -481,9 +480,9 @@ void Database::JS_prepare (v8::FunctionCallbackInfo <v8 :: Value> const & info)
addon->privileged_info = NULL;
if (!maybe_statement.IsEmpty()) info.GetReturnValue().Set(maybe_statement.ToLocalChecked());
}
#line 196 "./src/objects/database.lzz"
#line 195 "./src/objects/database.lzz"
void Database::JS_exec (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 196 "./src/objects/database.lzz"
#line 195 "./src/objects/database.lzz"
{
Database* db = node :: ObjectWrap :: Unwrap <Database>(info.This());
if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsString ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a string" ) ; v8 :: Local < v8 :: String > source = v8 :: Local < v8 :: String > :: Cast ( info [ 0 ] ) ;
@ -503,60 +502,15 @@ void Database::JS_exec (v8::FunctionCallbackInfo <v8 :: Value> const & info)
if (status == SQLITE_OK) info.GetReturnValue().Set(info.This());
else db->ThrowDatabaseError();
}
#line 216 "./src/objects/database.lzz"
#line 215 "./src/objects/database.lzz"
void Database::JS_pragma (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 216 "./src/objects/database.lzz"
#line 215 "./src/objects/database.lzz"
{
if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsBoolean ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a boolean" ) ; node :: ObjectWrap :: Unwrap < Database > ( info . This ( ) ) -> pragma_mode = v8 :: Local < v8 :: Boolean > :: Cast ( info [ 0 ] ) -> Value ( ) ;
}
#line 220 "./src/objects/database.lzz"
void Database::JS_checkpoint (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 220 "./src/objects/database.lzz"
{
Database* db = node :: ObjectWrap :: Unwrap <Database>(info.This());
if ( ! db -> open ) return ThrowTypeError ( "The database connection is not open" ) ;
if ( db -> busy ) return ThrowTypeError ( "This database connection is busy executing a query" ) ;
if ( db -> iterators ) return ThrowTypeError ( "This database connection is busy executing a query" ) ;
sqlite3* db_handle = db->db_handle;
if (info.Length()) {
if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsString ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a string" ) ; v8 :: Local < v8 :: String > onlyDatabase = v8 :: Local < v8 :: String > :: Cast ( info [ 0 ] ) ;
v8::String::Utf8Value only_database( info . GetIsolate ( ) , onlyDatabase);
if (only_database.length() == 0) {
return ThrowTypeError("Invalid database name (empty string)");
}
if (sqlite3_wal_checkpoint_v2(db_handle, *only_database, SQLITE_CHECKPOINT_RESTART, NULL, NULL) != SQLITE_OK) {
return db->ThrowDatabaseError();
}
return info.GetReturnValue().Set(info.This());
}
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db_handle, "PRAGMA database_list", -1, &stmt, NULL) != SQLITE_OK) {
return db->ThrowDatabaseError();
}
std::vector<std::string> database_names;
while (sqlite3_step(stmt) == SQLITE_ROW) {
database_names.emplace_back(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1)), sqlite3_column_bytes(stmt, 1));
}
if (sqlite3_finalize(stmt) != SQLITE_OK) {
return db->ThrowDatabaseError();
}
bool threw_error = false;
for (std::string const &name : database_names) {
if (sqlite3_wal_checkpoint_v2(db_handle, name.c_str(), SQLITE_CHECKPOINT_RESTART, NULL, NULL) != SQLITE_OK) {
if (!threw_error) {
db->ThrowDatabaseError();
threw_error = true;
}
}
}
if (!threw_error) info.GetReturnValue().Set(info.This());
}
#line 263 "./src/objects/database.lzz"
#line 219 "./src/objects/database.lzz"
void Database::JS_backup (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 263 "./src/objects/database.lzz"
#line 219 "./src/objects/database.lzz"
{
if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsString ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a string" ) ; v8 :: Local < v8 :: String > attachedName = v8 :: Local < v8 :: String > :: Cast ( info [ 0 ] ) ;
if ( info . Length ( ) <= ( 1 ) || ! info [ 1 ] -> IsString ( ) ) return ThrowTypeError ( "Expected " "second" " argument to be " "a string" ) ; v8 :: Local < v8 :: String > destFile = v8 :: Local < v8 :: String > :: Cast ( info [ 1 ] ) ;
@ -572,9 +526,9 @@ void Database::JS_backup (v8::FunctionCallbackInfo <v8 :: Value> const & info)
addon->privileged_info = NULL;
if (!maybe_backup.IsEmpty()) info.GetReturnValue().Set(maybe_backup.ToLocalChecked());
}
#line 279 "./src/objects/database.lzz"
#line 235 "./src/objects/database.lzz"
void Database::JS_function (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 279 "./src/objects/database.lzz"
#line 235 "./src/objects/database.lzz"
{
Database* db = node :: ObjectWrap :: Unwrap <Database>(info.This());
if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsFunction ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a function" ) ; v8 :: Local < v8 :: Function > fn = v8 :: Local < v8 :: Function > :: Cast ( info [ 0 ] ) ;
@ -596,9 +550,9 @@ void Database::JS_function (v8::FunctionCallbackInfo <v8 :: Value> const & info)
}
info.GetReturnValue().Set(info.This());
}
#line 301 "./src/objects/database.lzz"
#line 257 "./src/objects/database.lzz"
void Database::JS_aggregate (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 301 "./src/objects/database.lzz"
#line 257 "./src/objects/database.lzz"
{
Database* db = node :: ObjectWrap :: Unwrap <Database>(info.This());
if ( info . Length ( ) <= ( 0 ) ) return ThrowTypeError ( "Expected a " "first" " argument" ) ; v8 :: Local < v8 :: Value > start = info [ 0 ] ;
@ -625,9 +579,9 @@ void Database::JS_aggregate (v8::FunctionCallbackInfo <v8 :: Value> const & info
}
info.GetReturnValue().Set(info.This());
}
#line 328 "./src/objects/database.lzz"
#line 284 "./src/objects/database.lzz"
void Database::JS_loadExtension (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 328 "./src/objects/database.lzz"
#line 284 "./src/objects/database.lzz"
{
Database* db = node :: ObjectWrap :: Unwrap <Database>(info.This());
if ( info . Length ( ) <= ( 0 ) || ! info [ 0 ] -> IsString ( ) ) return ThrowTypeError ( "Expected " "first" " argument to be " "a string" ) ; v8 :: Local < v8 :: String > filenameString = v8 :: Local < v8 :: String > :: Cast ( info [ 0 ] ) ;
@ -641,9 +595,9 @@ void Database::JS_loadExtension (v8::FunctionCallbackInfo <v8 :: Value> const &
else ThrowSqliteError(db->addon, error, status);
sqlite3_free(error);
}
#line 342 "./src/objects/database.lzz"
#line 298 "./src/objects/database.lzz"
void Database::JS_close (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 342 "./src/objects/database.lzz"
#line 298 "./src/objects/database.lzz"
{
Database* db = node :: ObjectWrap :: Unwrap <Database>(info.This());
if (db->open) {
@ -654,31 +608,31 @@ void Database::JS_close (v8::FunctionCallbackInfo <v8 :: Value> const & info)
}
info.GetReturnValue().Set(info.This());
}
#line 353 "./src/objects/database.lzz"
#line 309 "./src/objects/database.lzz"
void Database::JS_defaultSafeIntegers (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 353 "./src/objects/database.lzz"
#line 309 "./src/objects/database.lzz"
{
Database* db = node :: ObjectWrap :: Unwrap <Database>(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 = v8 :: Local < v8 :: Boolean > :: Cast ( info [ 0 ] ) -> Value ( ) ; }
info.GetReturnValue().Set(info.This());
}
#line 360 "./src/objects/database.lzz"
#line 316 "./src/objects/database.lzz"
void Database::JS_open (v8::Local <v8 :: String> _, v8::PropertyCallbackInfo <v8 :: Value> const & info)
#line 360 "./src/objects/database.lzz"
#line 316 "./src/objects/database.lzz"
{
info.GetReturnValue().Set( node :: ObjectWrap :: Unwrap <Database>(info.This())->open);
}
#line 364 "./src/objects/database.lzz"
#line 320 "./src/objects/database.lzz"
void Database::JS_inTransaction (v8::Local <v8 :: String> _, v8::PropertyCallbackInfo <v8 :: Value> const & info)
#line 364 "./src/objects/database.lzz"
#line 320 "./src/objects/database.lzz"
{
Database* db = node :: ObjectWrap :: Unwrap <Database>(info.This());
info.GetReturnValue().Set(db->open && !static_cast<bool>(sqlite3_get_autocommit(db->db_handle)));
}
#line 369 "./src/objects/database.lzz"
#line 325 "./src/objects/database.lzz"
int const Database::MAX_BUFFER_SIZE;
#line 370 "./src/objects/database.lzz"
#line 326 "./src/objects/database.lzz"
int const Database::MAX_STRING_SIZE;
#line 5 "./src/objects/statement.lzz"
v8::Local <v8 :: Function> Statement::Init (v8::Isolate * isolate, v8::Local <v8 :: External> data)

View File

@ -171,133 +171,131 @@ class Database : public node::ObjectWrap
public:
#line 4 "./src/objects/database.lzz"
static v8::Local <v8 :: Function> Init (v8::Isolate * isolate, v8::Local <v8 :: External> data);
#line 22 "./src/objects/database.lzz"
#line 21 "./src/objects/database.lzz"
class CompareDatabase
{
#line 22 "./src/objects/database.lzz"
#line 21 "./src/objects/database.lzz"
public:
#line 23 "./src/objects/database.lzz"
#line 22 "./src/objects/database.lzz"
bool operator () (Database const * const a, Database const * const b) const;
};
#line 27 "./src/objects/database.lzz"
#line 26 "./src/objects/database.lzz"
class CompareStatement
{
#line 27 "./src/objects/database.lzz"
#line 26 "./src/objects/database.lzz"
public:
#line 28 "./src/objects/database.lzz"
#line 27 "./src/objects/database.lzz"
bool operator () (Statement const * const a, Statement const * const b) const;
};
#line 32 "./src/objects/database.lzz"
#line 31 "./src/objects/database.lzz"
class CompareBackup
{
#line 32 "./src/objects/database.lzz"
#line 31 "./src/objects/database.lzz"
public:
#line 33 "./src/objects/database.lzz"
#line 32 "./src/objects/database.lzz"
bool operator () (Backup const * const a, Backup const * const b) const;
};
#line 39 "./src/objects/database.lzz"
#line 38 "./src/objects/database.lzz"
void ThrowDatabaseError ();
#line 43 "./src/objects/database.lzz"
#line 42 "./src/objects/database.lzz"
static void ThrowSqliteError (Addon * addon, sqlite3 * db_handle);
#line 47 "./src/objects/database.lzz"
#line 46 "./src/objects/database.lzz"
static void ThrowSqliteError (Addon * addon, char const * message, int code);
#line 58 "./src/objects/database.lzz"
#line 57 "./src/objects/database.lzz"
bool Log (v8::Isolate * isolate, v8::Local <v8::Value> data);
#line 65 "./src/objects/database.lzz"
#line 64 "./src/objects/database.lzz"
bool Log (v8::Isolate * isolate, sqlite3_stmt * handle);
#line 74 "./src/objects/database.lzz"
#line 73 "./src/objects/database.lzz"
void AddStatement (Statement * stmt);
#line 75 "./src/objects/database.lzz"
#line 74 "./src/objects/database.lzz"
void RemoveStatement (Statement * stmt);
#line 78 "./src/objects/database.lzz"
#line 77 "./src/objects/database.lzz"
void AddBackup (Backup * backup);
#line 79 "./src/objects/database.lzz"
#line 78 "./src/objects/database.lzz"
void RemoveBackup (Backup * backup);
#line 83 "./src/objects/database.lzz"
#line 82 "./src/objects/database.lzz"
struct State
{
#line 84 "./src/objects/database.lzz"
#line 83 "./src/objects/database.lzz"
bool const open;
#line 85 "./src/objects/database.lzz"
#line 84 "./src/objects/database.lzz"
bool busy;
#line 86 "./src/objects/database.lzz"
#line 85 "./src/objects/database.lzz"
bool const pragma_mode;
#line 87 "./src/objects/database.lzz"
#line 86 "./src/objects/database.lzz"
bool const safe_ints;
#line 88 "./src/objects/database.lzz"
#line 87 "./src/objects/database.lzz"
bool was_js_error;
#line 89 "./src/objects/database.lzz"
#line 88 "./src/objects/database.lzz"
unsigned short int iterators;
#line 90 "./src/objects/database.lzz"
#line 89 "./src/objects/database.lzz"
Addon * const addon;
};
#line 92 "./src/objects/database.lzz"
#line 91 "./src/objects/database.lzz"
State * GetState ();
#line 95 "./src/objects/database.lzz"
#line 94 "./src/objects/database.lzz"
sqlite3 * GetHandle ();
#line 98 "./src/objects/database.lzz"
#line 97 "./src/objects/database.lzz"
Addon * GetAddon ();
#line 103 "./src/objects/database.lzz"
#line 102 "./src/objects/database.lzz"
void CloseHandles ();
#line 115 "./src/objects/database.lzz"
#line 114 "./src/objects/database.lzz"
~ Database ();
#line 120 "./src/objects/database.lzz"
#line 119 "./src/objects/database.lzz"
private:
#line 122 "./src/objects/database.lzz"
#line 121 "./src/objects/database.lzz"
explicit Database (sqlite3 * _db_handle, v8::Isolate * isolate, Addon * _addon, v8::Local <v8::Value> _logger);
#line 139 "./src/objects/database.lzz"
#line 138 "./src/objects/database.lzz"
static void JS_new (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 184 "./src/objects/database.lzz"
#line 183 "./src/objects/database.lzz"
static void JS_prepare (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 196 "./src/objects/database.lzz"
#line 195 "./src/objects/database.lzz"
static void JS_exec (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 216 "./src/objects/database.lzz"
#line 215 "./src/objects/database.lzz"
static void JS_pragma (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 220 "./src/objects/database.lzz"
static void JS_checkpoint (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 263 "./src/objects/database.lzz"
#line 219 "./src/objects/database.lzz"
static void JS_backup (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 279 "./src/objects/database.lzz"
#line 235 "./src/objects/database.lzz"
static void JS_function (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 301 "./src/objects/database.lzz"
#line 257 "./src/objects/database.lzz"
static void JS_aggregate (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 328 "./src/objects/database.lzz"
#line 284 "./src/objects/database.lzz"
static void JS_loadExtension (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 342 "./src/objects/database.lzz"
#line 298 "./src/objects/database.lzz"
static void JS_close (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 353 "./src/objects/database.lzz"
#line 309 "./src/objects/database.lzz"
static void JS_defaultSafeIntegers (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 360 "./src/objects/database.lzz"
#line 316 "./src/objects/database.lzz"
static void JS_open (v8::Local <v8 :: String> _, v8::PropertyCallbackInfo <v8 :: Value> const & info);
#line 364 "./src/objects/database.lzz"
#line 320 "./src/objects/database.lzz"
static void JS_inTransaction (v8::Local <v8 :: String> _, v8::PropertyCallbackInfo <v8 :: Value> const & info);
#line 369 "./src/objects/database.lzz"
#line 325 "./src/objects/database.lzz"
static int const MAX_BUFFER_SIZE = node::Buffer::kMaxLength > INT_MAX ? INT_MAX : static_cast<int>(node::Buffer::kMaxLength);
#line 370 "./src/objects/database.lzz"
#line 326 "./src/objects/database.lzz"
static int const MAX_STRING_SIZE = v8::String::kMaxLength > INT_MAX ? INT_MAX : static_cast<int>(v8::String::kMaxLength);
#line 372 "./src/objects/database.lzz"
#line 328 "./src/objects/database.lzz"
sqlite3 * const db_handle;
#line 373 "./src/objects/database.lzz"
#line 329 "./src/objects/database.lzz"
bool open;
#line 374 "./src/objects/database.lzz"
#line 330 "./src/objects/database.lzz"
bool busy;
#line 375 "./src/objects/database.lzz"
#line 331 "./src/objects/database.lzz"
bool pragma_mode;
#line 376 "./src/objects/database.lzz"
#line 332 "./src/objects/database.lzz"
bool safe_ints;
#line 377 "./src/objects/database.lzz"
#line 333 "./src/objects/database.lzz"
bool was_js_error;
#line 378 "./src/objects/database.lzz"
#line 334 "./src/objects/database.lzz"
bool const has_logger;
#line 379 "./src/objects/database.lzz"
#line 335 "./src/objects/database.lzz"
unsigned short int iterators;
#line 380 "./src/objects/database.lzz"
#line 336 "./src/objects/database.lzz"
Addon * const addon;
#line 381 "./src/objects/database.lzz"
#line 337 "./src/objects/database.lzz"
CopyablePersistent <v8::Value> const logger;
#line 382 "./src/objects/database.lzz"
#line 338 "./src/objects/database.lzz"
std::set <Statement*, CompareStatement> stmts;
#line 383 "./src/objects/database.lzz"
#line 339 "./src/objects/database.lzz"
std::set <Backup*, CompareBackup> backups;
};
#line 1 "./src/objects/statement.lzz"
@ -743,41 +741,41 @@ LZZ_INLINE int BindMap::GetSize ()
#line 42 "./src/util/bind-map.lzz"
{ return length;
}
#line 74 "./src/objects/database.lzz"
#line 73 "./src/objects/database.lzz"
LZZ_INLINE void Database::AddStatement (Statement * stmt)
#line 74 "./src/objects/database.lzz"
#line 73 "./src/objects/database.lzz"
{ stmts.insert(stmts.end(), stmt);
}
#line 75 "./src/objects/database.lzz"
#line 74 "./src/objects/database.lzz"
LZZ_INLINE void Database::RemoveStatement (Statement * stmt)
#line 75 "./src/objects/database.lzz"
#line 74 "./src/objects/database.lzz"
{ stmts.erase(stmt);
}
#line 78 "./src/objects/database.lzz"
#line 77 "./src/objects/database.lzz"
LZZ_INLINE void Database::AddBackup (Backup * backup)
#line 78 "./src/objects/database.lzz"
#line 77 "./src/objects/database.lzz"
{ backups.insert(backups.end(), backup);
}
#line 79 "./src/objects/database.lzz"
#line 78 "./src/objects/database.lzz"
LZZ_INLINE void Database::RemoveBackup (Backup * backup)
#line 79 "./src/objects/database.lzz"
#line 78 "./src/objects/database.lzz"
{ backups.erase(backup);
}
#line 92 "./src/objects/database.lzz"
#line 91 "./src/objects/database.lzz"
LZZ_INLINE Database::State * Database::GetState ()
#line 92 "./src/objects/database.lzz"
#line 91 "./src/objects/database.lzz"
{
return reinterpret_cast<State*>(&open);
}
#line 95 "./src/objects/database.lzz"
#line 94 "./src/objects/database.lzz"
LZZ_INLINE sqlite3 * Database::GetHandle ()
#line 95 "./src/objects/database.lzz"
#line 94 "./src/objects/database.lzz"
{
return db_handle;
}
#line 98 "./src/objects/database.lzz"
#line 97 "./src/objects/database.lzz"
LZZ_INLINE Addon * Database::GetAddon ()
#line 98 "./src/objects/database.lzz"
#line 97 "./src/objects/database.lzz"
{
return addon;
}

View File

@ -6,7 +6,6 @@ public:
SetPrototypeMethod(isolate, data, t, "prepare", JS_prepare);
SetPrototypeMethod(isolate, data, t, "exec", JS_exec);
SetPrototypeMethod(isolate, data, t, "pragma", JS_pragma);
SetPrototypeMethod(isolate, data, t, "checkpoint", JS_checkpoint);
SetPrototypeMethod(isolate, data, t, "backup", JS_backup);
SetPrototypeMethod(isolate, data, t, "function", JS_function);
SetPrototypeMethod(isolate, data, t, "aggregate", JS_aggregate);
@ -217,49 +216,6 @@ private:
REQUIRE_ARGUMENT_BOOLEAN(first, Unwrap<Database>(info.This())->pragma_mode);
}
NODE_METHOD(JS_checkpoint) {
Database* db = Unwrap<Database>(info.This());
REQUIRE_DATABASE_OPEN(db);
REQUIRE_DATABASE_NOT_BUSY(db);
REQUIRE_DATABASE_NO_ITERATORS(db);
sqlite3* db_handle = db->db_handle;
if (info.Length()) {
REQUIRE_ARGUMENT_STRING(first, v8::Local<v8::String> onlyDatabase);
v8::String::Utf8Value only_database(OnlyIsolate, onlyDatabase);
if (only_database.length() == 0) {
return ThrowTypeError("Invalid database name (empty string)");
}
if (sqlite3_wal_checkpoint_v2(db_handle, *only_database, SQLITE_CHECKPOINT_RESTART, NULL, NULL) != SQLITE_OK) {
return db->ThrowDatabaseError();
}
return info.GetReturnValue().Set(info.This());
}
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db_handle, "PRAGMA database_list", -1, &stmt, NULL) != SQLITE_OK) {
return db->ThrowDatabaseError();
}
std::vector<std::string> database_names;
while (sqlite3_step(stmt) == SQLITE_ROW) {
database_names.emplace_back(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1)), sqlite3_column_bytes(stmt, 1));
}
if (sqlite3_finalize(stmt) != SQLITE_OK) {
return db->ThrowDatabaseError();
}
bool threw_error = false;
for (std::string const &name : database_names) {
if (sqlite3_wal_checkpoint_v2(db_handle, name.c_str(), SQLITE_CHECKPOINT_RESTART, NULL, NULL) != SQLITE_OK) {
if (!threw_error) {
db->ThrowDatabaseError();
threw_error = true;
}
}
}
if (!threw_error) info.GetReturnValue().Set(info.This());
}
NODE_METHOD(JS_backup) {
REQUIRE_ARGUMENT_STRING(first, v8::Local<v8::String> attachedName);
REQUIRE_ARGUMENT_STRING(second, v8::Local<v8::String> destFile);

View File

@ -28,7 +28,6 @@ describe('Database#close()', function () {
expect(() => this.db.prepare('CREATE TABLE cats (name TEXT)')).to.throw(TypeError);
expect(() => this.db.transaction(() => {})).to.throw(TypeError);
expect(() => this.db.pragma('cache_size')).to.throw(TypeError);
expect(() => this.db.checkpoint()).to.throw(TypeError);
expect(() => this.db.function('foo', () => {})).to.throw(TypeError);
expect(() => this.db.aggregate('foo', { step: () => {} })).to.throw(TypeError);
});

View File

@ -2,7 +2,7 @@
const fs = require('fs');
const Database = require('../.');
describe('Database#checkpoint()', function () {
describe('Database#pragma(\'wal_checkpoint(RESTART)\')', function () {
let db1, db2;
before(function () {
db1 = new Database(util.next());
@ -35,7 +35,7 @@ describe('Database#checkpoint()', function () {
});
specify('inserts after a checkpoint should NOT increase the size of the WAL file', function () {
db1.prepare(`ATTACH '${db2.name}' AS foobar`).run();
expect(db1.checkpoint()).to.deep.equal(db1);
db1.pragma('wal_checkpoint(RESTART)');
fillWall(10, (b, a) => expect(b).to.equal(a));
});
});
@ -52,7 +52,7 @@ describe('Database#checkpoint()', function () {
});
specify('inserts after a checkpoint should NOT increase the size of the WAL file', function () {
db1.prepare(`ATTACH '${db2.name}' AS bazqux`).run();
expect(db1.checkpoint('bazqux')).to.equal(db1);
db1.pragma('bazqux.wal_checkpoint(RESTART)');
fillWall(10, (b, a, db) => {
if (db === db1) expect(b).to.be.above(a);
else expect(b).to.be.equal(a);

View File

@ -90,17 +90,17 @@ describe('integrity checks', function () {
});
});
describe('Database#checkpoint()', function () {
describe('Database#pragma(\'wal_checkpoint(RESTART)\')', function () {
specify('while iterating (blocked)', function () {
whileIterating(this, blocked(() => this.db.checkpoint()));
normally(allowed(() => this.db.checkpoint()));
whileIterating(this, blocked(() => this.db.pragma('wal_checkpoint(RESTART)')));
normally(allowed(() => this.db.pragma('wal_checkpoint(RESTART)')));
});
specify('while busy (blocked)', function () {
whileBusy(this, blocked(() => this.db.checkpoint()));
normally(allowed(() => this.db.checkpoint()));
whileBusy(this, blocked(() => this.db.pragma('wal_checkpoint(RESTART)')));
normally(allowed(() => this.db.pragma('wal_checkpoint(RESTART)')));
});
specify('while closed (blocked)', function () {
whileClosed(this, blocked(() => this.db.checkpoint()));
whileClosed(this, blocked(() => this.db.pragma('wal_checkpoint(RESTART)')));
});
});