@@ -321,6 +321,8 @@ Napi::Object Statement::Init(Napi::Env env, Napi::Object exports) {
321321 exports[" statementClose" ] = Napi::Function::New (env, &Statement::Close);
322322 exports[" statementRun" ] = Napi::Function::New (env, &Statement::Run);
323323 exports[" statementStep" ] = Napi::Function::New (env, &Statement::Step);
324+ exports[" statementScanStats" ] =
325+ Napi::Function::New (env, &Statement::ScanStats);
324326 return exports;
325327}
326328
@@ -562,6 +564,108 @@ Napi::Value Statement::Step(const Napi::CallbackInfo& info) {
562564 return result;
563565}
564566
567+ // Only enabled on `-profiling` npm package versions
568+
569+ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS
570+ Napi::Value Statement::ScanStats (const Napi::CallbackInfo& info) {
571+ auto env = info.Env ();
572+
573+ auto stmt = FromExternal (info[0 ]);
574+ if (stmt == nullptr ) {
575+ return Napi::Value ();
576+ }
577+
578+ sqlite3_int64 total_cycles = 0 ;
579+ int r = sqlite3_stmt_scanstatus_v2 (stmt->handle_ , -1 , SQLITE_SCANSTAT_NCYCLE,
580+ SQLITE_SCANSTAT_COMPLEX, &total_cycles);
581+
582+ if (r != SQLITE_OK) {
583+ return stmt->db_ ->ThrowSqliteError (env, r);
584+ }
585+
586+ auto results = Napi::Array::New (env, 1 );
587+
588+ auto root = Napi::Object::New (env);
589+
590+ root[" id" ] = 0 ;
591+ root[" parent" ] = -1 ;
592+ root[" cycles" ] = total_cycles;
593+ root[" loops" ] = -1 ;
594+ root[" rows" ] = -1 ;
595+ root[" explain" ] = env.Null ();
596+ results[static_cast <uint32_t >(0 )] = root;
597+
598+ for (int idx = 0 ; r == SQLITE_OK; idx++) {
599+ int id = 0 ;
600+ int parent = 0 ;
601+ sqlite3_int64 cycles = 0 ;
602+ sqlite3_int64 loops = 0 ;
603+ sqlite3_int64 rows = 0 ;
604+ const char * explain = nullptr ;
605+
606+ r = sqlite3_stmt_scanstatus_v2 (stmt->handle_ , idx, SQLITE_SCANSTAT_SELECTID,
607+ SQLITE_SCANSTAT_COMPLEX, &id);
608+ if (r != SQLITE_OK) {
609+ break ;
610+ }
611+ r = sqlite3_stmt_scanstatus_v2 (stmt->handle_ , idx, SQLITE_SCANSTAT_PARENTID,
612+ SQLITE_SCANSTAT_COMPLEX, &parent);
613+ if (r != SQLITE_OK) {
614+ break ;
615+ }
616+ r = sqlite3_stmt_scanstatus_v2 (stmt->handle_ , idx, SQLITE_SCANSTAT_NCYCLE,
617+ SQLITE_SCANSTAT_COMPLEX, &cycles);
618+ if (r != SQLITE_OK) {
619+ break ;
620+ }
621+ r = sqlite3_stmt_scanstatus_v2 (stmt->handle_ , idx, SQLITE_SCANSTAT_NLOOP,
622+ SQLITE_SCANSTAT_COMPLEX, &loops);
623+ if (r != SQLITE_OK) {
624+ break ;
625+ }
626+ r = sqlite3_stmt_scanstatus_v2 (stmt->handle_ , idx, SQLITE_SCANSTAT_NVISIT,
627+ SQLITE_SCANSTAT_COMPLEX, &rows);
628+ if (r != SQLITE_OK) {
629+ break ;
630+ }
631+ r = sqlite3_stmt_scanstatus_v2 (stmt->handle_ , idx, SQLITE_SCANSTAT_EXPLAIN,
632+ SQLITE_SCANSTAT_COMPLEX, &explain);
633+ if (r != SQLITE_OK) {
634+ break ;
635+ }
636+
637+ auto result = Napi::Object::New (env);
638+
639+ result[" id" ] = id;
640+ result[" parent" ] = parent;
641+ result[" cycles" ] = cycles;
642+ result[" loops" ] = loops;
643+ result[" rows" ] = rows;
644+ if (explain == nullptr ) {
645+ result[" explain" ] = env.Null ();
646+ } else {
647+ result[" explain" ] = explain;
648+ }
649+
650+ results[static_cast <uint32_t >(idx + 1 )] = result;
651+ }
652+
653+ // SQLITE_ERROR is returned when `idx` is out of range
654+ if (r != SQLITE_ERROR) {
655+ return stmt->db_ ->ThrowSqliteError (env, r);
656+ }
657+
658+ return results;
659+ }
660+ #else // !SQLITE_ENABLE_STMT_SCANSTATUS
661+ Napi::Value Statement::ScanStats (const Napi::CallbackInfo& info) {
662+ auto env = info.Env ();
663+
664+ NAPI_THROW (Napi::Error::New (env, " Not available in production builds" ),
665+ Napi::Value ());
666+ }
667+ #endif // !SQLITE_ENABLE_STMT_SCANSTATUS
668+
565669bool Statement::BindParams (Napi::Env env, Napi::Value params) {
566670 int key_count = sqlite3_bind_parameter_count (handle_);
567671
0 commit comments