feat: stmt.scanStats()
This commit is contained in:
parent
d1646af2f3
commit
9e85ee2e91
13
deps/sqlcipher/sqlcipher.gyp
vendored
13
deps/sqlcipher/sqlcipher.gyp
vendored
@ -73,6 +73,7 @@
|
||||
'SQLITE_EXTRA_SHUTDOWN=sqlcipher_extra_shutdown',
|
||||
],
|
||||
'conditions': [
|
||||
# Link with extension
|
||||
['OS == "win"', {
|
||||
'defines': [
|
||||
'WIN32'
|
||||
@ -96,6 +97,18 @@
|
||||
]
|
||||
},
|
||||
}],
|
||||
|
||||
# Profiling
|
||||
["\"<!(node -p \"require('../../package.json').version\")\".endswith(\"-profiling\")", {
|
||||
'defines': [
|
||||
'SQLITE_ENABLE_STMT_SCANSTATUS'
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'defines': [
|
||||
'SQLITE_ENABLE_STMT_SCANSTATUS'
|
||||
],
|
||||
},
|
||||
}],
|
||||
],
|
||||
'configurations': {
|
||||
'Debug': {
|
||||
|
||||
29
lib/index.ts
29
lib/index.ts
@ -39,6 +39,7 @@ const addon = bindings<{
|
||||
cache: Array<SqliteValue<Options>> | undefined,
|
||||
isGet: boolean,
|
||||
): Array<SqliteValue<Options>>;
|
||||
statementScanStats(stmt: NativeStatement): Array<ScanStats>;
|
||||
statementClose(stmt: NativeStatement): void;
|
||||
|
||||
databaseOpen(path: string): NativeDatabase;
|
||||
@ -225,6 +226,20 @@ class Statement<Options extends StatementOptions = object> {
|
||||
return result as unknown as Array<Row>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report collected performance statics for the statement.
|
||||
*
|
||||
* @returns A list of objects describing the performance of the query.
|
||||
*
|
||||
* @see {@link https://www.sqlite.org/profile.html}
|
||||
*/
|
||||
public scanStats(): Array<ScanStats> {
|
||||
if (this.#native === undefined) {
|
||||
throw new Error('Statement closed');
|
||||
}
|
||||
return addon.statementScanStats(this.#native);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the statement and release the used memory.
|
||||
*/
|
||||
@ -302,6 +317,20 @@ export type PragmaResult<Options extends PragmaOptions> = Options extends {
|
||||
? RowType<{ pluck: true }> | undefined
|
||||
: Array<RowType<object>>;
|
||||
|
||||
/**
|
||||
* An entry of result array of `stmt.scanStats()` method.
|
||||
*
|
||||
* Value of `-1` indicates that the field is not available for a given entry.
|
||||
*/
|
||||
export type ScanStats = Readonly<{
|
||||
id: number;
|
||||
parent: number;
|
||||
cycles: number;
|
||||
loops: number;
|
||||
rows: number;
|
||||
explain: string | null;
|
||||
}>;
|
||||
|
||||
/** @internal */
|
||||
type TransactionStatement = Statement<{ persistent: true; pluck: true }>;
|
||||
|
||||
|
||||
104
src/addon.cc
104
src/addon.cc
@ -321,6 +321,8 @@ Napi::Object Statement::Init(Napi::Env env, Napi::Object exports) {
|
||||
exports["statementClose"] = Napi::Function::New(env, &Statement::Close);
|
||||
exports["statementRun"] = Napi::Function::New(env, &Statement::Run);
|
||||
exports["statementStep"] = Napi::Function::New(env, &Statement::Step);
|
||||
exports["statementScanStats"] =
|
||||
Napi::Function::New(env, &Statement::ScanStats);
|
||||
return exports;
|
||||
}
|
||||
|
||||
@ -562,6 +564,108 @@ Napi::Value Statement::Step(const Napi::CallbackInfo& info) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Only enabled on `-profiling` npm package versions
|
||||
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
Napi::Value Statement::ScanStats(const Napi::CallbackInfo& info) {
|
||||
auto env = info.Env();
|
||||
|
||||
auto stmt = FromExternal(info[0]);
|
||||
if (stmt == nullptr) {
|
||||
return Napi::Value();
|
||||
}
|
||||
|
||||
sqlite3_int64 total_cycles = 0;
|
||||
int r = sqlite3_stmt_scanstatus_v2(stmt->handle_, -1, SQLITE_SCANSTAT_NCYCLE,
|
||||
SQLITE_SCANSTAT_COMPLEX, &total_cycles);
|
||||
|
||||
if (r != SQLITE_OK) {
|
||||
return stmt->db_->ThrowSqliteError(env, r);
|
||||
}
|
||||
|
||||
auto results = Napi::Array::New(env, 1);
|
||||
|
||||
auto root = Napi::Object::New(env);
|
||||
|
||||
root["id"] = 0;
|
||||
root["parent"] = -1;
|
||||
root["cycles"] = total_cycles;
|
||||
root["loops"] = -1;
|
||||
root["rows"] = -1;
|
||||
root["explain"] = env.Null();
|
||||
results[static_cast<uint32_t>(0)] = root;
|
||||
|
||||
for (int idx = 0; r == SQLITE_OK; idx++) {
|
||||
int id = 0;
|
||||
int parent = 0;
|
||||
sqlite3_int64 cycles = 0;
|
||||
sqlite3_int64 loops = 0;
|
||||
sqlite3_int64 rows = 0;
|
||||
const char* explain = nullptr;
|
||||
|
||||
r = sqlite3_stmt_scanstatus_v2(stmt->handle_, idx, SQLITE_SCANSTAT_SELECTID,
|
||||
SQLITE_SCANSTAT_COMPLEX, &id);
|
||||
if (r != SQLITE_OK) {
|
||||
break;
|
||||
}
|
||||
r = sqlite3_stmt_scanstatus_v2(stmt->handle_, idx, SQLITE_SCANSTAT_PARENTID,
|
||||
SQLITE_SCANSTAT_COMPLEX, &parent);
|
||||
if (r != SQLITE_OK) {
|
||||
break;
|
||||
}
|
||||
r = sqlite3_stmt_scanstatus_v2(stmt->handle_, idx, SQLITE_SCANSTAT_NCYCLE,
|
||||
SQLITE_SCANSTAT_COMPLEX, &cycles);
|
||||
if (r != SQLITE_OK) {
|
||||
break;
|
||||
}
|
||||
r = sqlite3_stmt_scanstatus_v2(stmt->handle_, idx, SQLITE_SCANSTAT_NLOOP,
|
||||
SQLITE_SCANSTAT_COMPLEX, &loops);
|
||||
if (r != SQLITE_OK) {
|
||||
break;
|
||||
}
|
||||
r = sqlite3_stmt_scanstatus_v2(stmt->handle_, idx, SQLITE_SCANSTAT_NVISIT,
|
||||
SQLITE_SCANSTAT_COMPLEX, &rows);
|
||||
if (r != SQLITE_OK) {
|
||||
break;
|
||||
}
|
||||
r = sqlite3_stmt_scanstatus_v2(stmt->handle_, idx, SQLITE_SCANSTAT_EXPLAIN,
|
||||
SQLITE_SCANSTAT_COMPLEX, &explain);
|
||||
if (r != SQLITE_OK) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto result = Napi::Object::New(env);
|
||||
|
||||
result["id"] = id;
|
||||
result["parent"] = parent;
|
||||
result["cycles"] = cycles;
|
||||
result["loops"] = loops;
|
||||
result["rows"] = rows;
|
||||
if (explain == nullptr) {
|
||||
result["explain"] = env.Null();
|
||||
} else {
|
||||
result["explain"] = explain;
|
||||
}
|
||||
|
||||
results[static_cast<uint32_t>(idx + 1)] = result;
|
||||
}
|
||||
|
||||
// SQLITE_ERROR is returned when `idx` is out of range
|
||||
if (r != SQLITE_ERROR) {
|
||||
return stmt->db_->ThrowSqliteError(env, r);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
#else // !SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
Napi::Value Statement::ScanStats(const Napi::CallbackInfo& info) {
|
||||
auto env = info.Env();
|
||||
|
||||
NAPI_THROW(Napi::Error::New(env, "Not available in production builds"),
|
||||
Napi::Value());
|
||||
}
|
||||
#endif // !SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
|
||||
bool Statement::BindParams(Napi::Env env, Napi::Value params) {
|
||||
int key_count = sqlite3_bind_parameter_count(handle_);
|
||||
|
||||
|
||||
@ -131,6 +131,7 @@ class Statement {
|
||||
static Napi::Value Close(const Napi::CallbackInfo& info);
|
||||
static Napi::Value Run(const Napi::CallbackInfo& info);
|
||||
static Napi::Value Step(const Napi::CallbackInfo& info);
|
||||
static Napi::Value ScanStats(const Napi::CallbackInfo& info);
|
||||
|
||||
bool BindParams(Napi::Env env, Napi::Value params);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user