Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions deps/defines.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
'SQLITE_ENABLE_MATH_FUNCTIONS',
'SQLITE_ENABLE_RTREE',
'SQLITE_ENABLE_STAT4',
'SQLITE_ENABLE_STMT_SCANSTATUS',
'SQLITE_ENABLE_UPDATE_DELETE_LIMIT',
'SQLITE_LIKE_DOESNT_MATCH_BLOBS',
'SQLITE_OMIT_DEPRECATED',
Expand Down
12 changes: 12 additions & 0 deletions lib/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,16 @@ Database.prototype.defaultSafeIntegers = wrappers.defaultSafeIntegers;
Database.prototype.unsafeMode = wrappers.unsafeMode;
Database.prototype[util.inspect] = require('./methods/inspect');

// Export SQLITE_SCANSTAT_* constants from native addon
const nativeAddon = DEFAULT_ADDON || require('bindings')('better_sqlite3.node');
Database.SQLITE_SCANSTAT_NLOOP = nativeAddon.SQLITE_SCANSTAT_NLOOP;
Database.SQLITE_SCANSTAT_NVISIT = nativeAddon.SQLITE_SCANSTAT_NVISIT;
Database.SQLITE_SCANSTAT_EST = nativeAddon.SQLITE_SCANSTAT_EST;
Database.SQLITE_SCANSTAT_NAME = nativeAddon.SQLITE_SCANSTAT_NAME;
Database.SQLITE_SCANSTAT_EXPLAIN = nativeAddon.SQLITE_SCANSTAT_EXPLAIN;
Database.SQLITE_SCANSTAT_SELECTID = nativeAddon.SQLITE_SCANSTAT_SELECTID;
Database.SQLITE_SCANSTAT_PARENTID = nativeAddon.SQLITE_SCANSTAT_PARENTID;
Database.SQLITE_SCANSTAT_NCYCLE = nativeAddon.SQLITE_SCANSTAT_NCYCLE;
Database.SQLITE_SCANSTAT_COMPLEX = nativeAddon.SQLITE_SCANSTAT_COMPLEX;

module.exports = Database;
30 changes: 30 additions & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ type VariableArgFunction = (...params: any[]) => unknown;
type ArgumentTypes<F extends VariableArgFunction> = F extends (...args: infer A) => unknown ? A : never;
type ElementOf<T> = T extends Array<infer E> ? E : T;

declare const enum ScanStatOpcode {
SQLITE_SCANSTAT_NLOOP = 0,
SQLITE_SCANSTAT_NVISIT = 1,
SQLITE_SCANSTAT_EST = 2,
SQLITE_SCANSTAT_NAME = 3,
SQLITE_SCANSTAT_EXPLAIN = 4,
SQLITE_SCANSTAT_SELECTID = 5,
SQLITE_SCANSTAT_PARENTID = 6,
SQLITE_SCANSTAT_NCYCLE = 7,
}

declare namespace BetterSqlite3 {
interface Statement<BindParameters extends unknown[], Result = unknown> {
database: Database;
Expand All @@ -23,6 +34,15 @@ declare namespace BetterSqlite3 {
bind(...params: BindParameters): this;
columns(): ColumnDefinition[];
safeIntegers(toggleState?: boolean): this;
scanStatusV2(idx: number, opcode: ScanStatOpcode.SQLITE_SCANSTAT_NAME, resetFlag: number): string | undefined;
scanStatusV2(idx: number, opcode: ScanStatOpcode.SQLITE_SCANSTAT_EXPLAIN, resetFlag: number): string | undefined;
scanStatusV2(idx: number, opcode: ScanStatOpcode.SQLITE_SCANSTAT_NLOOP, resetFlag: number): number | undefined;
scanStatusV2(idx: number, opcode: ScanStatOpcode.SQLITE_SCANSTAT_NVISIT, resetFlag: number): number | undefined;
scanStatusV2(idx: number, opcode: ScanStatOpcode.SQLITE_SCANSTAT_EST, resetFlag: number): number | undefined;
scanStatusV2(idx: number, opcode: ScanStatOpcode.SQLITE_SCANSTAT_SELECTID, resetFlag: number): number | undefined;
scanStatusV2(idx: number, opcode: ScanStatOpcode.SQLITE_SCANSTAT_PARENTID, resetFlag: number): number | undefined;
scanStatusV2(idx: number, opcode: ScanStatOpcode.SQLITE_SCANSTAT_NCYCLE, resetFlag: number): number | undefined;
scanStatusV2(idx: number, opcode: ScanStatOpcode, resetFlag: number): number | string | undefined;
}

interface ColumnDefinition {
Expand Down Expand Up @@ -89,6 +109,16 @@ declare namespace BetterSqlite3 {
prototype: Database;

SqliteError: typeof SqliteError;

// scanstatus constants
SQLITE_SCANSTAT_NLOOP: ScanStatOpcode.SQLITE_SCANSTAT_NLOOP;
SQLITE_SCANSTAT_NVISIT: ScanStatOpcode.SQLITE_SCANSTAT_NVISIT;
SQLITE_SCANSTAT_EST: ScanStatOpcode.SQLITE_SCANSTAT_EST;
SQLITE_SCANSTAT_NAME: ScanStatOpcode.SQLITE_SCANSTAT_NAME;
SQLITE_SCANSTAT_EXPLAIN: ScanStatOpcode.SQLITE_SCANSTAT_EXPLAIN;
SQLITE_SCANSTAT_SELECTID: ScanStatOpcode.SQLITE_SCANSTAT_SELECTID;
SQLITE_SCANSTAT_PARENTID: ScanStatOpcode.SQLITE_SCANSTAT_PARENTID;
SQLITE_SCANSTAT_NCYCLE: ScanStatOpcode.SQLITE_SCANSTAT_NCYCLE;
}
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rocicorp/zero-sqlite3",
"version": "1.0.8",
"version": "1.0.9",
"description": "better-sqlite3 on bedrock",
"homepage": "https://github.com/rocicorp/zero-sqlite3",
"author": "Rocicorp",
Expand Down
66 changes: 64 additions & 2 deletions src/better_sqlite3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,17 @@ NODE_MODULE_INIT(/* exports, context */) {
exports->Set(context, InternalizedFromLatin1(isolate, "Backup"), Backup::Init(isolate, data)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "setErrorConstructor"), v8::FunctionTemplate::New(isolate, Addon::JS_setErrorConstructor, data)->GetFunction(context).ToLocalChecked()).FromJust();

// Export SQLITE_SCANSTAT_* constants
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_NLOOP"), v8::Int32::New(isolate, SQLITE_SCANSTAT_NLOOP)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_NVISIT"), v8::Int32::New(isolate, SQLITE_SCANSTAT_NVISIT)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_EST"), v8::Int32::New(isolate, SQLITE_SCANSTAT_EST)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_NAME"), v8::Int32::New(isolate, SQLITE_SCANSTAT_NAME)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_EXPLAIN"), v8::Int32::New(isolate, SQLITE_SCANSTAT_EXPLAIN)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_SELECTID"), v8::Int32::New(isolate, SQLITE_SCANSTAT_SELECTID)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_PARENTID"), v8::Int32::New(isolate, SQLITE_SCANSTAT_PARENTID)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_NCYCLE"), v8::Int32::New(isolate, SQLITE_SCANSTAT_NCYCLE)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_COMPLEX"), v8::Int32::New(isolate, SQLITE_SCANSTAT_COMPLEX)).FromJust();

// Store addon instance data.
addon->Statement.Reset(isolate, exports->Get(context, InternalizedFromLatin1(isolate, "Statement")).ToLocalChecked().As<v8::Function>());
addon->StatementIterator.Reset(isolate, exports->Get(context, InternalizedFromLatin1(isolate, "StatementIterator")).ToLocalChecked().As<v8::Function>());
Expand Down Expand Up @@ -773,6 +784,7 @@ v8::Local <v8 :: Function> Statement::Init (v8::Isolate * isolate, v8::Local <v8
SetPrototypeMethod(isolate, data, t, "raw", JS_raw);
SetPrototypeMethod(isolate, data, t, "safeIntegers", JS_safeIntegers);
SetPrototypeMethod(isolate, data, t, "columns", JS_columns);
SetPrototypeMethod(isolate, data, t, "scanStatusV2", JS_scanStatusV2);
SetPrototypeGetter(isolate, data, t, "busy", JS_busy);
return t->GetFunction( isolate -> GetCurrentContext ( ) ).ToLocalChecked();
}
Expand Down Expand Up @@ -1083,9 +1095,59 @@ void Statement::JS_columns (v8::FunctionCallbackInfo <v8 :: Value> const & info)

info.GetReturnValue().Set(columns);
}
#line 321 "./src/objects/statement.lzz"
#line 322 "./src/objects/statement.lzz"
void Statement::JS_scanStatusV2 (v8::FunctionCallbackInfo <v8 :: Value> const & info)
#line 322 "./src/objects/statement.lzz"
{
Statement* stmt = node :: ObjectWrap :: Unwrap <Statement>(info.This());
if (!stmt->db->GetState()->open) return ThrowTypeError("The database connection is not open");
if (!stmt->alive) return ThrowTypeError("The statement has been finalized and can no longer be used");

int idx; if (info.Length() < (0 + 1) || !info[0]->IsInt32()) return ThrowTypeError("Expected " "first" " argument to be " "a 32-bit signed integer"); idx = (info[0].As<v8::Int32>()->Value());;
int iScanStatusOp; if (info.Length() < (1 + 1) || !info[1]->IsInt32()) return ThrowTypeError("Expected " "second" " argument to be " "a 32-bit signed integer"); iScanStatusOp = (info[1].As<v8::Int32>()->Value());;
int flags; if (info.Length() < (2 + 1) || !info[2]->IsInt32()) return ThrowTypeError("Expected " "third" " argument to be " "a 32-bit signed integer"); flags = (info[2].As<v8::Int32>()->Value());;

v8::Isolate* isolate = info.GetIsolate();;

// Based on iScanStatusOp, we know what type of output to expect
int rc;
if (iScanStatusOp == 0 || iScanStatusOp == 1 || iScanStatusOp == 5 || iScanStatusOp == 6 || iScanStatusOp == 7) {
// NLOOP, NVISIT, SELECTID, PARENTID, NCYCLE - return sqlite3_int64
sqlite3_int64 iOut;
rc = sqlite3_stmt_scanstatus_v2(stmt->handle, idx, iScanStatusOp, flags, (void*)&iOut);
if (rc == 0) {
info.GetReturnValue().Set(stmt->safe_ints
? v8::BigInt::New(isolate, iOut).As<v8::Value>()
: v8::Number::New(isolate, (double)iOut).As<v8::Value>());
return;
}
} else if (iScanStatusOp == 2) {
// EST - return double
double dOut;
rc = sqlite3_stmt_scanstatus_v2(stmt->handle, idx, iScanStatusOp, flags, (void*)&dOut);
if (rc == 0) {
info.GetReturnValue().Set(v8::Number::New(isolate, dOut));
return;
}
} else if (iScanStatusOp == 3 || iScanStatusOp == 4) {
// NAME, EXPLAIN - return const char*
const char* zOut;
rc = sqlite3_stmt_scanstatus_v2(stmt->handle, idx, iScanStatusOp, flags, (void*)&zOut);
if (rc == 0 && zOut != NULL) {
info.GetReturnValue().Set(StringFromUtf8(isolate, zOut, -1));
return;
} else if (rc == 0) {
info.GetReturnValue().Set(v8::Null(isolate));
return;
}
}

// Error or invalid operation - return undefined
info.GetReturnValue().Set(v8::Undefined(isolate));
}
#line 370 "./src/objects/statement.lzz"
void Statement::JS_busy (v8::Local <v8 :: Name> _, v8::PropertyCallbackInfo <v8 :: Value> const & info)
#line 321 "./src/objects/statement.lzz"
#line 370 "./src/objects/statement.lzz"
{
Statement* stmt = node :: ObjectWrap :: Unwrap <Statement>(info.This());
info.GetReturnValue().Set(stmt->alive && stmt->locked);
Expand Down
4 changes: 3 additions & 1 deletion src/better_sqlite3.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,9 @@ class Statement : public node::ObjectWrap
static void JS_safeIntegers (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 278 "./src/objects/statement.lzz"
static void JS_columns (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 321 "./src/objects/statement.lzz"
#line 322 "./src/objects/statement.lzz"
static void JS_scanStatusV2 (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 370 "./src/objects/statement.lzz"
static void JS_busy (v8::Local <v8 :: Name> _, v8::PropertyCallbackInfo <v8 :: Value> const & info);
#line 326 "./src/objects/statement.lzz"
Database * const db;
Expand Down
11 changes: 11 additions & 0 deletions src/better_sqlite3.lzz
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ NODE_MODULE_INIT(/* exports, context */) {
exports->Set(context, InternalizedFromLatin1(isolate, "Backup"), Backup::Init(isolate, data)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "setErrorConstructor"), v8::FunctionTemplate::New(isolate, Addon::JS_setErrorConstructor, data)->GetFunction(context).ToLocalChecked()).FromJust();

// Export SQLITE_SCANSTAT_* constants
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_NLOOP"), v8::Int32::New(isolate, SQLITE_SCANSTAT_NLOOP)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_NVISIT"), v8::Int32::New(isolate, SQLITE_SCANSTAT_NVISIT)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_EST"), v8::Int32::New(isolate, SQLITE_SCANSTAT_EST)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_NAME"), v8::Int32::New(isolate, SQLITE_SCANSTAT_NAME)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_EXPLAIN"), v8::Int32::New(isolate, SQLITE_SCANSTAT_EXPLAIN)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_SELECTID"), v8::Int32::New(isolate, SQLITE_SCANSTAT_SELECTID)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_PARENTID"), v8::Int32::New(isolate, SQLITE_SCANSTAT_PARENTID)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_NCYCLE"), v8::Int32::New(isolate, SQLITE_SCANSTAT_NCYCLE)).FromJust();
exports->Set(context, InternalizedFromLatin1(isolate, "SQLITE_SCANSTAT_COMPLEX"), v8::Int32::New(isolate, SQLITE_SCANSTAT_COMPLEX)).FromJust();

// Store addon instance data.
addon->Statement.Reset(isolate, exports->Get(context, InternalizedFromLatin1(isolate, "Statement")).ToLocalChecked().As<v8::Function>());
addon->StatementIterator.Reset(isolate, exports->Get(context, InternalizedFromLatin1(isolate, "StatementIterator")).ToLocalChecked().As<v8::Function>());
Expand Down
49 changes: 49 additions & 0 deletions src/objects/statement.lzz
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public:
SetPrototypeMethod(isolate, data, t, "raw", JS_raw);
SetPrototypeMethod(isolate, data, t, "safeIntegers", JS_safeIntegers);
SetPrototypeMethod(isolate, data, t, "columns", JS_columns);
SetPrototypeMethod(isolate, data, t, "scanStatusV2", JS_scanStatusV2);
SetPrototypeGetter(isolate, data, t, "busy", JS_busy);
return t->GetFunction(OnlyContext).ToLocalChecked();
}
Expand Down Expand Up @@ -318,6 +319,54 @@ private:
info.GetReturnValue().Set(columns);
}

NODE_METHOD(JS_scanStatusV2) {
Statement* stmt = Unwrap<Statement>(info.This());
REQUIRE_DATABASE_OPEN(stmt->db->GetState());
if (!stmt->alive) return ThrowTypeError("The statement has been finalized and can no longer be used");

REQUIRE_ARGUMENT_INT32(first, int idx);
REQUIRE_ARGUMENT_INT32(second, int iScanStatusOp);
REQUIRE_ARGUMENT_INT32(third, int flags);

UseIsolate;

// Based on iScanStatusOp, we know what type of output to expect
int rc;
if (iScanStatusOp == 0 || iScanStatusOp == 1 || iScanStatusOp == 5 || iScanStatusOp == 6 || iScanStatusOp == 7) {
// NLOOP, NVISIT, SELECTID, PARENTID, NCYCLE - return sqlite3_int64
sqlite3_int64 iOut;
rc = sqlite3_stmt_scanstatus_v2(stmt->handle, idx, iScanStatusOp, flags, (void*)&iOut);
if (rc == 0) {
info.GetReturnValue().Set(stmt->safe_ints
? v8::BigInt::New(isolate, iOut).As<v8::Value>()
: v8::Number::New(isolate, (double)iOut).As<v8::Value>());
return;
}
} else if (iScanStatusOp == 2) {
// EST - return double
double dOut;
rc = sqlite3_stmt_scanstatus_v2(stmt->handle, idx, iScanStatusOp, flags, (void*)&dOut);
if (rc == 0) {
info.GetReturnValue().Set(v8::Number::New(isolate, dOut));
return;
}
} else if (iScanStatusOp == 3 || iScanStatusOp == 4) {
// NAME, EXPLAIN - return const char*
const char* zOut;
rc = sqlite3_stmt_scanstatus_v2(stmt->handle, idx, iScanStatusOp, flags, (void*)&zOut);
if (rc == 0 && zOut != NULL) {
info.GetReturnValue().Set(StringFromUtf8(isolate, zOut, -1));
return;
} else if (rc == 0) {
info.GetReturnValue().Set(v8::Null(isolate));
return;
}
}

// Error or invalid operation - return undefined
info.GetReturnValue().Set(v8::Undefined(isolate));
}

NODE_GETTER(JS_busy) {
Statement* stmt = Unwrap<Statement>(info.This());
info.GetReturnValue().Set(stmt->alive && stmt->locked);
Expand Down
100 changes: 100 additions & 0 deletions test/26.statement.scanstatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
'use strict';
const Database = require('../.');

describe('Statement#scanStatusV2()', function () {
beforeEach(function () {
this.db = new Database(util.next());
this.db.prepare('CREATE TABLE entries (id INTEGER PRIMARY KEY, name TEXT, value INTEGER)').run();
this.db.prepare('INSERT INTO entries (name, value) VALUES (?, ?)').run('foo', 1);
this.db.prepare('INSERT INTO entries (name, value) VALUES (?, ?)').run('bar', 2);
this.db.prepare('INSERT INTO entries (name, value) VALUES (?, ?)').run('baz', 3);
});
afterEach(function () {
this.db.close();
});

it('should return scan status information for a query', function () {
const stmt = this.db.prepare('SELECT * FROM entries WHERE value > ?');

// Execute the statement to populate scan status
const rows = stmt.all(1);
expect(rows).to.have.lengthOf(2);

// Get scan status for the first loop (idx=0)
const explain = stmt.scanStatusV2(0, Database.SQLITE_SCANSTAT_EXPLAIN, 0);
expect(explain).to.be.a('string');

const nLoop = stmt.scanStatusV2(0, Database.SQLITE_SCANSTAT_NLOOP, 0);
expect(nLoop).to.be.a('number');

const nVisit = stmt.scanStatusV2(0, Database.SQLITE_SCANSTAT_NVISIT, 0);
expect(nVisit).to.be.a('number');
});

it('should return undefined for invalid index', function () {
const stmt = this.db.prepare('SELECT * FROM entries');
stmt.all();

// Try to get scan status for a non-existent loop
const result = stmt.scanStatusV2(999, Database.SQLITE_SCANSTAT_EXPLAIN, 0);
expect(result).to.be.undefined;
});

it('should work with SQLITE_SCANSTAT_COMPLEX flag', function () {
const stmt = this.db.prepare('SELECT * FROM entries WHERE value > ?');
stmt.all(1);

// Use COMPLEX flag to get more detailed information
const explain = stmt.scanStatusV2(0, Database.SQLITE_SCANSTAT_EXPLAIN, Database.SQLITE_SCANSTAT_COMPLEX);
expect(explain).to.be.a('string');

const selectId = stmt.scanStatusV2(0, Database.SQLITE_SCANSTAT_SELECTID, Database.SQLITE_SCANSTAT_COMPLEX);
expect(selectId).to.be.a('number');
});

it('should respect safeIntegers setting', function () {
const stmt = this.db.prepare('SELECT * FROM entries').safeIntegers(true);
stmt.all();

const nLoop = stmt.scanStatusV2(0, Database.SQLITE_SCANSTAT_NLOOP, 0);
expect(nLoop).to.be.a('bigint');
});

it('should return EST as a double', function () {
const stmt = this.db.prepare('SELECT * FROM entries WHERE value > ?');
stmt.all(1);

const est = stmt.scanStatusV2(0, Database.SQLITE_SCANSTAT_EST, 0);
expect(est).to.be.a('number');
expect(est).to.be.greaterThan(0);
});

it('should return NAME for table/index name', function () {
const stmt = this.db.prepare('SELECT * FROM entries');
stmt.all();

const name = stmt.scanStatusV2(0, Database.SQLITE_SCANSTAT_NAME, 0);
// Name could be null for some query plans or a string for table/index name
expect(name === null || typeof name === 'string').to.be.true;
});

it('should throw when database is closed', function () {
const stmt = this.db.prepare('SELECT * FROM entries');
this.db.close();

expect(() => stmt.scanStatusV2(0, Database.SQLITE_SCANSTAT_EXPLAIN, 0))
.to.throw(TypeError);
});

it('should verify constants are exported', function () {
expect(Database.SQLITE_SCANSTAT_NLOOP).to.equal(0);
expect(Database.SQLITE_SCANSTAT_NVISIT).to.equal(1);
expect(Database.SQLITE_SCANSTAT_EST).to.equal(2);
expect(Database.SQLITE_SCANSTAT_NAME).to.equal(3);
expect(Database.SQLITE_SCANSTAT_EXPLAIN).to.equal(4);
expect(Database.SQLITE_SCANSTAT_SELECTID).to.equal(5);
expect(Database.SQLITE_SCANSTAT_PARENTID).to.equal(6);
expect(Database.SQLITE_SCANSTAT_NCYCLE).to.equal(7);
expect(Database.SQLITE_SCANSTAT_COMPLEX).to.equal(0x0001);
});
});