Skip to content

Commit c24a3f2

Browse files
committed
Make sure we close all database file descriptors.
Iterate through the names returned by sqlite3_db_name which will include names given via "ATTACH DATABASE ... AS" and any temp databases.
1 parent 8a08461 commit c24a3f2

File tree

2 files changed

+19
-11
lines changed

2 files changed

+19
-11
lines changed

ext/sqlite3/database.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,20 @@ close_or_discard_db(sqlite3RubyPtr ctx)
2222
} else {
2323
// This is an open connection carried across a fork().
2424
// "Discard" it. See adr/2024-09-fork-safety.md
25+
const char *db_name;
2526
sqlite3_file *sfile;
26-
int status;
27+
int status, j_db = 0;
2728

2829
rb_warning("An open sqlite database connection was inherited from a forked process and "
2930
"is being discarded. This is a memory leak. If possible, please close all sqlite "
3031
"database connections before forking.");
3132

32-
// close the open file descriptors
33-
status = sqlite3_file_control(ctx->db, NULL, SQLITE_FCNTL_FILE_POINTER, &sfile);
34-
if (status == 0 && sfile->pMethods != NULL) {
35-
sfile->pMethods->xClose(sfile);
33+
while ((db_name = sqlite3_db_name(ctx->db, j_db)) != NULL) {
34+
status = sqlite3_file_control(ctx->db, db_name, SQLITE_FCNTL_FILE_POINTER, &sfile);
35+
if (status == 0 && sfile->pMethods != NULL) {
36+
sfile->pMethods->xClose(sfile);
37+
}
38+
j_db++;
3639
}
3740

3841
status = sqlite3_file_control(ctx->db, NULL, SQLITE_FCNTL_JOURNAL_POINTER, &sfile);

test/test_database.rb

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,9 @@ def test_discard_a_connection
730730
read, write = IO.pipe
731731

732732
db = SQLite3::Database.new("test.db")
733+
db.execute("attach database 'test.db' as 'foo';") # exercise sqlite3_db_name()
733734
Process.fork do
735+
read.close
734736
$stderr = StringIO.new
735737

736738
result = db.close
@@ -740,20 +742,23 @@ def test_discard_a_connection
740742
write.write($stderr.string)
741743

742744
write.close
743-
read.close
744745
exit!
745746
end
747+
write.close
748+
749+
assert1, assert2, *stderr = *read.readlines
750+
read.close
751+
752+
assert_equal("ok", assert1.chomp, "return value was not the database")
753+
assert_equal("ok", assert2.chomp, "closed? did not return true")
746754

747-
assert_equal("ok", read.readline.chomp, "return value was not the database")
748-
assert_equal("ok", read.readline.chomp, "closed? did not return true")
755+
assert_equal(1, stderr.count, "unexpected output on stderr: #{stderr.inspect}")
749756
assert_match(
750757
/warning: An open sqlite database connection was inherited from a forked process/,
751-
read.readline,
758+
stderr.first,
752759
"expected warning was not emitted"
753760
)
754761

755-
write.close
756-
read.close
757762
ensure
758763
db.close
759764
FileUtils.rm_f("test.db")

0 commit comments

Comments
 (0)