Skip to content

Commit

Permalink
Freeze results that come from the database
Browse files Browse the repository at this point in the history
Freeze strings and row records so that we can more easily share results
among Ractors
  • Loading branch information
tenderlove committed Jan 23, 2024
1 parent 6a5d21f commit cb9481e
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 4 deletions.
4 changes: 4 additions & 0 deletions ext/sqlite3/statement.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,15 @@ step(VALUE self)
if (internal_encoding) {
val = rb_str_export_to_enc(val, internal_encoding);
}
rb_obj_freeze(val);
}
break;
case SQLITE_BLOB: {
val = rb_str_new(
(const char *)sqlite3_column_blob(stmt, i),
(long)sqlite3_column_bytes(stmt, i)
);
rb_obj_freeze(val);
}
break;
case SQLITE_NULL:
Expand All @@ -192,6 +194,8 @@ step(VALUE self)
CHECK(sqlite3_db_handle(ctx->st), value);
}

rb_obj_freeze(list);

return list;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/sqlite3/database.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions test/test_encoding.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
17 changes: 17 additions & 0 deletions test/test_statement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)'

Expand Down

0 comments on commit cb9481e

Please sign in to comment.