Skip to content

Commit b9bbeaa

Browse files
committed
Replace separate methods with a single Statement#stat method that returns a hash
Works like GC.stat, and can also take a symbol to fetch only one stat
1 parent 17e6619 commit b9bbeaa

File tree

3 files changed

+158
-146
lines changed

3 files changed

+158
-146
lines changed

ext/sqlite3/statement.c

Lines changed: 99 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -411,125 +411,129 @@ bind_parameter_count(VALUE self)
411411
return INT2NUM(sqlite3_bind_parameter_count(ctx->st));
412412
}
413413

414-
/* call-seq: stmt.fullscan_steps
415-
*
416-
* Return the number of times that SQLite has stepped forward in a table as part of a full table scan
417-
*/
418-
static VALUE
419-
fullscan_steps(VALUE self)
420-
{
421-
sqlite3StmtRubyPtr ctx;
422-
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
423-
REQUIRE_OPEN_STMT(ctx);
414+
enum stmt_stat_sym {
415+
stmt_stat_sym_fullscan_steps,
416+
stmt_stat_sym_sorts,
417+
stmt_stat_sym_autoindexes,
418+
stmt_stat_sym_vm_steps,
419+
#ifdef SQLITE_STMTSTATUS_REPREPARE
420+
stmt_stat_sym_reprepares,
421+
#endif
422+
#ifdef SQLITE_STMTSTATUS_RUN
423+
stmt_stat_sym_runs,
424+
#endif
425+
#ifdef SQLITE_STMTSTATUS_FILTER_MISS
426+
stmt_stat_sym_filter_misses,
427+
#endif
428+
#ifdef SQLITE_STMTSTATUS_FILTER_HIT
429+
stmt_stat_sym_filter_hits,
430+
#endif
431+
stmt_stat_sym_last
432+
};
424433

425-
return INT2NUM(sqlite3_stmt_status(ctx->st, SQLITE_STMTSTATUS_FULLSCAN_STEP, 0));
426-
}
434+
static VALUE stmt_stat_symbols[stmt_stat_sym_last];
427435

428-
/* call-seq: stmt.sorts
429-
*
430-
* Return the number of sort operations that have occurred
431-
*/
432-
static VALUE
433-
sorts(VALUE self)
436+
static void
437+
setup_stmt_stat_symbols(void)
434438
{
435-
sqlite3StmtRubyPtr ctx;
436-
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
437-
REQUIRE_OPEN_STMT(ctx);
438-
439-
return INT2NUM(sqlite3_stmt_status(ctx->st, SQLITE_STMTSTATUS_SORT, 0));
439+
if (stmt_stat_symbols[0] == 0) {
440+
#define S(s) stmt_stat_symbols[stmt_stat_sym_##s] = ID2SYM(rb_intern_const(#s))
441+
S(fullscan_steps);
442+
S(sorts);
443+
S(autoindexes);
444+
S(vm_steps);
445+
#ifdef SQLITE_STMTSTATUS_REPREPARE
446+
S(reprepares);
447+
#endif
448+
#ifdef SQLITE_STMTSTATUS_RUN
449+
S(runs);
450+
#endif
451+
#ifdef SQLITE_STMTSTATUS_FILTER_MISS
452+
S(filter_misses);
453+
#endif
454+
#ifdef SQLITE_STMTSTATUS_FILTER_HIT
455+
S(filter_hits);
456+
#endif
457+
#undef S
458+
}
440459
}
441460

442-
/* call-seq: stmt.autoindexes
443-
*
444-
* Return the number of rows inserted into transient indices that were created automatically in order to help joins run faster
445-
*/
446-
static VALUE
447-
autoindexes(VALUE self)
461+
static size_t
462+
stmt_stat_internal(VALUE hash_or_sym, sqlite3_stmt *stmt)
448463
{
449-
sqlite3StmtRubyPtr ctx;
450-
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
451-
REQUIRE_OPEN_STMT(ctx);
464+
VALUE hash = Qnil, key = Qnil;
452465

453-
return INT2NUM(sqlite3_stmt_status(ctx->st, SQLITE_STMTSTATUS_AUTOINDEX, 0));
454-
}
466+
setup_stmt_stat_symbols();
455467

456-
/* call-seq: stmt.vm_steps
457-
*
458-
* Return the number of virtual machine operations executed by the prepared statement
459-
*/
460-
static VALUE
461-
vm_steps(VALUE self)
462-
{
463-
sqlite3StmtRubyPtr ctx;
464-
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
465-
REQUIRE_OPEN_STMT(ctx);
468+
if (RB_TYPE_P(hash_or_sym, T_HASH)) {
469+
hash = hash_or_sym;
470+
}
471+
else if (SYMBOL_P(hash_or_sym)) {
472+
key = hash_or_sym;
473+
}
474+
else {
475+
rb_raise(rb_eTypeError, "non-hash or symbol argument");
476+
}
466477

467-
return INT2NUM(sqlite3_stmt_status(ctx->st, SQLITE_STMTSTATUS_VM_STEP, 0));
468-
}
478+
#define SET(name, stat_type) \
479+
if (key == stmt_stat_symbols[stmt_stat_sym_##name]) \
480+
return sqlite3_stmt_status(stmt, stat_type, 0); \
481+
else if (hash != Qnil) \
482+
rb_hash_aset(hash, stmt_stat_symbols[stmt_stat_sym_##name], SIZET2NUM(sqlite3_stmt_status(stmt, stat_type, 0)));
469483

484+
SET(fullscan_steps, SQLITE_STMTSTATUS_FULLSCAN_STEP);
485+
SET(sorts, SQLITE_STMTSTATUS_SORT);
486+
SET(autoindexes, SQLITE_STMTSTATUS_AUTOINDEX);
487+
SET(vm_steps, SQLITE_STMTSTATUS_VM_STEP);
470488
#ifdef SQLITE_STMTSTATUS_REPREPARE
471-
/* call-seq: stmt.reprepares
472-
*
473-
* Return the number of times that the prepare statement has been automatically regenerated due to schema changes or changes to bound parameters that might affect the query plan.
474-
*/
475-
static VALUE
476-
reprepares(VALUE self)
477-
{
478-
sqlite3StmtRubyPtr ctx;
479-
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
480-
REQUIRE_OPEN_STMT(ctx);
481-
482-
return INT2NUM(sqlite3_stmt_status(ctx->st, SQLITE_STMTSTATUS_REPREPARE, 0));
483-
}
489+
SET(reprepares, SQLITE_STMTSTATUS_REPREPARE);
484490
#endif
485-
486491
#ifdef SQLITE_STMTSTATUS_RUN
487-
/* call-seq: stmt.runs
488-
*
489-
* Return the number of times that the prepared statement has been run
490-
*/
491-
static VALUE
492-
runs(VALUE self)
493-
{
494-
sqlite3StmtRubyPtr ctx;
495-
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
496-
REQUIRE_OPEN_STMT(ctx);
497-
498-
return INT2NUM(sqlite3_stmt_status(ctx->st, SQLITE_STMTSTATUS_RUN, 0));
499-
}
492+
SET(runs, SQLITE_STMTSTATUS_RUN);
500493
#endif
501-
502494
#ifdef SQLITE_STMTSTATUS_FILTER_MISS
503-
/* call-seq: stmt.filter_misses
504-
*
505-
* Return the number of times that the Bloom filter returned a find, and thus the join step had to be processed as normal.
506-
*/
507-
static VALUE
508-
filter_misses(VALUE self)
509-
{
510-
sqlite3StmtRubyPtr ctx;
511-
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
512-
REQUIRE_OPEN_STMT(ctx);
495+
SET(filter_misses, SQLITE_STMTSTATUS_FILTER_MISS);
496+
#endif
497+
#ifdef SQLITE_STMTSTATUS_FILTER_HIT
498+
SET(filter_hits, SQLITE_STMTSTATUS_FILTER_HIT);
499+
#endif
500+
#undef SET
501+
502+
if (!NIL_P(key)) { /* matched key should return above */
503+
rb_raise(rb_eArgError, "unknown key: %"PRIsVALUE, rb_sym2str(key));
504+
}
513505

514-
return INT2NUM(sqlite3_stmt_status(ctx->st, SQLITE_STMTSTATUS_FILTER_MISS, 0));
506+
return 0;
515507
}
516-
#endif
517508

518-
#ifdef SQLITE_STMTSTATUS_FILTER_HIT
519-
/* call-seq: stmt.filter_hits
509+
/* call-seq: stmt.stmt_stat(hash_or_key)
520510
*
521-
* Return the number of times that a join step was bypassed because a Bloom filter returned not-found
511+
* Returns a Hash containing information about the statement.
522512
*/
523513
static VALUE
524-
filter_hits(VALUE self)
514+
stmt_stat(VALUE self, VALUE arg) // arg is (nil || hash || symbol)
525515
{
526516
sqlite3StmtRubyPtr ctx;
527517
TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx);
528518
REQUIRE_OPEN_STMT(ctx);
529519

530-
return INT2NUM(sqlite3_stmt_status(ctx->st, SQLITE_STMTSTATUS_FILTER_HIT, 0));
520+
if (NIL_P(arg)) {
521+
arg = rb_hash_new();
522+
}
523+
else if (SYMBOL_P(arg)) {
524+
size_t value = stmt_stat_internal(arg, ctx->st);
525+
return SIZET2NUM(value);
526+
}
527+
else if (RB_TYPE_P(arg, T_HASH)) {
528+
// ok
529+
}
530+
else {
531+
rb_raise(rb_eTypeError, "non-hash or symbol given");
532+
}
533+
534+
stmt_stat_internal(arg, ctx->st);
535+
return arg;
531536
}
532-
#endif
533537

534538
#ifdef SQLITE_STMTSTATUS_MEMUSED
535539
/* call-seq: stmt.memory_used
@@ -583,33 +587,10 @@ init_sqlite3_statement(void)
583587
rb_define_method(cSqlite3Statement, "column_name", column_name, 1);
584588
rb_define_method(cSqlite3Statement, "column_decltype", column_decltype, 1);
585589
rb_define_method(cSqlite3Statement, "bind_parameter_count", bind_parameter_count, 0);
586-
rb_define_method(cSqlite3Statement, "fullscan_steps", fullscan_steps, 0);
587-
rb_define_method(cSqlite3Statement, "sorts", sorts, 0);
588-
rb_define_method(cSqlite3Statement, "autoindexes", autoindexes, 0);
589-
rb_define_method(cSqlite3Statement, "vm_steps", vm_steps, 0);
590-
rb_define_private_method(cSqlite3Statement, "prepare", prepare, 2);
591-
592-
#ifdef HAVE_SQLITE3_COLUMN_DATABASE_NAME
593-
rb_define_method(cSqlite3Statement, "database_name", database_name, 1);
594-
#endif
595-
596-
#ifdef SQLITE_STMTSTATUS_REPREPARE
597-
rb_define_method(cSqlite3Statement, "reprepares", reprepares, 0);
598-
#endif
599-
600-
#ifdef SQLITE_STMTSTATUS_RUN
601-
rb_define_method(cSqlite3Statement, "runs", runs, 0);
602-
#endif
603-
604-
#ifdef SQLITE_STMTSTATUS_FILTER_MISS
605-
rb_define_method(cSqlite3Statement, "filter_misses", filter_misses, 0);
606-
#endif
607-
608-
#ifdef SQLITE_STMTSTATUS_FILTER_HIT
609-
rb_define_method(cSqlite3Statement, "filter_hits", filter_hits, 0);
610-
#endif
611-
590+
rb_define_method(cSqlite3Statement, "stmt_stat", stmt_stat, 1);
612591
#ifdef SQLITE_STMTSTATUS_MEMUSED
613592
rb_define_method(cSqlite3Statement, "memused", memused, 0);
614593
#endif
594+
595+
rb_define_private_method(cSqlite3Statement, "prepare", prepare, 2);
615596
}

lib/sqlite3/statement.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,29 @@ def must_be_open! # :nodoc:
145145
end
146146
end
147147

148+
# Returns a Hash containing information about the statement.
149+
# The contents of the hash are implementation specific and may change in
150+
# the future without notice. The hash includes information about internal
151+
# statistics about the statement such as:
152+
# - +fullscan_steps+: the number of times that SQLite has stepped forward
153+
# in a table as part of a full table scan
154+
# - +sorts+: the number of sort operations that have occurred
155+
# - +autoindexes+: the number of rows inserted into transient indices
156+
# that were created automatically in order to help joins run faster
157+
# - +vm_steps+: the number of virtual machine operations executed by the
158+
# prepared statement
159+
# - +reprepares+: the number of times that the prepare statement has been
160+
# automatically regenerated due to schema changes or changes to bound
161+
# parameters that might affect the query plan
162+
# - +runs+: the number of times that the prepared statement has been run
163+
# - +filter_misses+: the number of times that the Bloom filter returned
164+
# a find, and thus the join step had to be processed as normal
165+
# - +filter_hits+: the number of times that a join step was bypassed
166+
# because a Bloom filter returned not-found
167+
def stat hash_or_key = nil
168+
stmt_stat hash_or_key
169+
end
170+
148171
private
149172
# A convenience method for obtaining the metadata about the query. Note
150173
# that this will actually execute the SQL, which means it can be a

0 commit comments

Comments
 (0)