From e2b15ee9f77a1a6ce4bcd3f23d0c2cdea2928fff Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 1 Feb 2024 09:35:50 -0800 Subject: [PATCH] Add `sql` and `expanded_sql` methods to the statement object These methods allow you to retrieve the SQL statement that was used to create the statement object. The `sql` method just returns the sql statement, where the `expanded_sql` method returns the statement but with bind parameters substituted. This should aid with debugging and other tasks. Fixes #293 --- ext/sqlite3/statement.c | 31 +++++++++++++++++++++++++++++++ test/test_statement.rb | 17 +++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/ext/sqlite3/statement.c b/ext/sqlite3/statement.c index a3fc7abd..4e5987e2 100644 --- a/ext/sqlite3/statement.c +++ b/ext/sqlite3/statement.c @@ -598,6 +598,35 @@ database_name(VALUE self, VALUE index) #endif +/* call-seq: stmt.sql + * + * Returns the SQL statement used to create this prepared statement + */ +static VALUE +get_sql(VALUE self) +{ + sqlite3StmtRubyPtr ctx; + TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx); + REQUIRE_OPEN_STMT(ctx); + + return rb_obj_freeze(SQLITE3_UTF8_STR_NEW2(sqlite3_sql(ctx->st))); +} + +/* call-seq: stmt.expanded_sql + * + * Returns the SQL statement used to create this prepared statement, but + * with bind parameters substituted in to the statement. + */ +static VALUE +get_expanded_sql(VALUE self) +{ + sqlite3StmtRubyPtr ctx; + TypedData_Get_Struct(self, sqlite3StmtRuby, &statement_type, ctx); + REQUIRE_OPEN_STMT(ctx); + + return rb_obj_freeze(SQLITE3_UTF8_STR_NEW2(sqlite3_expanded_sql(ctx->st))); +} + void init_sqlite3_statement(void) { @@ -615,6 +644,8 @@ init_sqlite3_statement(void) rb_define_method(cSqlite3Statement, "column_name", column_name, 1); rb_define_method(cSqlite3Statement, "column_decltype", column_decltype, 1); rb_define_method(cSqlite3Statement, "bind_parameter_count", bind_parameter_count, 0); + rb_define_method(cSqlite3Statement, "sql", get_sql, 0); + rb_define_method(cSqlite3Statement, "expanded_sql", get_expanded_sql, 0); #ifdef HAVE_SQLITE3_COLUMN_DATABASE_NAME rb_define_method(cSqlite3Statement, "database_name", database_name, 1); #endif diff --git a/test/test_statement.rb b/test/test_statement.rb index 0b74fdb7..675f8bb1 100644 --- a/test/test_statement.rb +++ b/test/test_statement.rb @@ -65,6 +65,23 @@ def test_column_names_are_deduped stmt&.close end + def test_sql_method + sql = "SELECT 1234" + stmt = @db.prepare sql + assert_equal sql, stmt.sql + ensure + stmt.close + end + + def test_expanded_sql_method + sql = "SELECT ?" + stmt = @db.prepare sql + stmt.bind_params 1234 + assert_equal "SELECT 1234", stmt.expanded_sql + ensure + stmt.close + end + def test_insert_duplicate_records @db.execute 'CREATE TABLE "things" ("name" varchar(20) CONSTRAINT "index_things_on_name" UNIQUE)' stmt = @db.prepare("INSERT INTO things(name) VALUES(?)")