Skip to content

Commit

Permalink
always call sqlite3_finalize in deallocate func
Browse files Browse the repository at this point in the history
prevents memory leak when `close` is not called before a ResultSet is
garbage collected

also add coverage for statement resource cleanup

Co-authored-by: Mike Dalessio <mike.dalessio@gmail.com>
  • Loading branch information
haileys and flavorjones committed Jan 3, 2024
1 parent 870eecc commit 0c24631
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 1 deletion.
14 changes: 13 additions & 1 deletion ext/sqlite3/statement.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@

VALUE cSqlite3Statement;

static void
statement_deallocate(void *data)
{
sqlite3StmtRubyPtr s = (sqlite3StmtRubyPtr)data;

if (s->st) {
sqlite3_finalize(s->st);
}

xfree(data);
}

static size_t
statement_memsize(const void *data)
{
Expand All @@ -18,7 +30,7 @@ static const rb_data_type_t statement_type = {
"SQLite3::Backup",
{
NULL,
RUBY_TYPED_DEFAULT_FREE,
statement_deallocate,
statement_memsize,
},
0,
Expand Down
1 change: 1 addition & 0 deletions sqlite3.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ Gem::Specification.new do |s|
"test/test_integration_resultset.rb",
"test/test_integration_statement.rb",
"test/test_pragmas.rb",
"test/test_resource_cleanup.rb",
"test/test_result_set.rb",
"test/test_sqlite3.rb",
"test/test_statement.rb",
Expand Down
27 changes: 27 additions & 0 deletions test/test_resource_cleanup.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require "helper"

module SQLite3
# these tests will cause ruby_memcheck to report a leak if we're not cleaning up resources
class TestResourceCleanup < SQLite3::TestCase
def test_cleanup_unclosed_database_object
100.times do
SQLite3::Database.new(':memory:')
end
end

def test_cleanup_unclosed_statement_object
100.times do
db = SQLite3::Database.new(':memory:')
db.execute('create table foo(text BLOB)')
db.prepare('select * from foo')
end
end

# def test_cleanup_unclosed_resultset_object
# db = SQLite3::Database.new(':memory:')
# db.execute('create table foo(text BLOB)')
# stmt = db.prepare('select * from foo')
# stmt.execute
# end
end
end

0 comments on commit 0c24631

Please sign in to comment.