From cb9481e8a47e82b4cda38a51e8a9f714ad7a537c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Jan 2024 15:47:09 -0800 Subject: [PATCH 1/2] Freeze results that come from the database Freeze strings and row records so that we can more easily share results among Ractors --- ext/sqlite3/statement.c | 4 ++++ lib/sqlite3/database.rb | 2 +- test/test_encoding.rb | 6 +++--- test/test_statement.rb | 17 +++++++++++++++++ 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/ext/sqlite3/statement.c b/ext/sqlite3/statement.c index f6d257a8..ad1765f2 100644 --- a/ext/sqlite3/statement.c +++ b/ext/sqlite3/statement.c @@ -162,6 +162,7 @@ step(VALUE self) if (internal_encoding) { val = rb_str_export_to_enc(val, internal_encoding); } + rb_obj_freeze(val); } break; case SQLITE_BLOB: { @@ -169,6 +170,7 @@ step(VALUE self) (const char *)sqlite3_column_blob(stmt, i), (long)sqlite3_column_bytes(stmt, i) ); + rb_obj_freeze(val); } break; case SQLITE_NULL: @@ -192,6 +194,8 @@ step(VALUE self) CHECK(sqlite3_db_handle(ctx->st), value); } + rb_obj_freeze(list); + return list; } diff --git a/lib/sqlite3/database.rb b/lib/sqlite3/database.rb index e8977896..87f9112f 100644 --- a/lib/sqlite3/database.rb +++ b/lib/sqlite3/database.rb @@ -208,7 +208,7 @@ def execute sql, bind_vars = [], *args, &block yield row end else - stmt.to_a + stmt.to_a.freeze end end end diff --git a/test/test_encoding.rb b/test/test_encoding.rb index 33c72c67..2b0ea712 100644 --- a/test/test_encoding.rb +++ b/test/test_encoding.rb @@ -82,7 +82,7 @@ def test_blob_is_binary string = @db.execute("select data from foo").first.first assert_equal Encoding.find("ASCII-8BIT"), string.encoding - assert_equal str, string.force_encoding("UTF-8") + assert_equal str, string.dup.force_encoding("UTF-8") end def test_blob_is_ascii8bit @@ -95,7 +95,7 @@ def test_blob_is_ascii8bit string = @db.execute("select data from foo").first.first assert_equal Encoding.find("ASCII-8BIT"), string.encoding - assert_equal str, string.force_encoding("UTF-8") + assert_equal str, string.dup.force_encoding("UTF-8") end def test_blob_with_eucjp @@ -108,7 +108,7 @@ def test_blob_with_eucjp string = @db.execute("select data from foo").first.first assert_equal Encoding.find("ASCII-8BIT"), string.encoding - assert_equal str, string.force_encoding("EUC-JP") + assert_equal str, string.dup.force_encoding("EUC-JP") end def test_db_with_eucjp diff --git a/test/test_statement.rb b/test/test_statement.rb index df3c6c20..b868deff 100644 --- a/test/test_statement.rb +++ b/test/test_statement.rb @@ -12,6 +12,23 @@ def teardown @db.close end + def test_rows_should_be_frozen + @db.execute 'CREATE TABLE "things" ("float" float, "int" int, "text" blob, "string" string, "nil" string)' + stmt = @db.prepare "INSERT INTO things (float, int, text, string, nil) VALUES (?, ?, ?, ?, ?)" + stmt.execute(1.2, 2, "blob", "string", nil) + stmt.close + + rows = @db.execute "SELECT float, int, text, string, nil FROM things" + assert_predicate rows, :frozen? + assert_equal 1, rows.length + row = rows[0] + assert_predicate row, :frozen? + row.each { |item| assert_predicate item, :frozen? } + + assert Ractor.shareable?(rows) + assert Ractor.shareable?(row) + end + def test_double_close_does_not_segv @db.execute 'CREATE TABLE "things" ("number" float NOT NULL)' From 839c288bf24ef959578df251d12a4d80b734d9c3 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Jan 2024 16:12:51 -0800 Subject: [PATCH 2/2] Only test Ractor sharability if Ractors are defined --- test/test_statement.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/test_statement.rb b/test/test_statement.rb index b868deff..c4a22662 100644 --- a/test/test_statement.rb +++ b/test/test_statement.rb @@ -25,8 +25,10 @@ def test_rows_should_be_frozen assert_predicate row, :frozen? row.each { |item| assert_predicate item, :frozen? } - assert Ractor.shareable?(rows) - assert Ractor.shareable?(row) + if defined?(Ractor) + assert Ractor.shareable?(rows) + assert Ractor.shareable?(row) + end end def test_double_close_does_not_segv