Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions ext/sqlite3/database.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,21 @@ allocate(VALUE klass)
static char *
utf16_string_value_ptr(VALUE str)
{
VALUE utf16str, codepoints;

StringValue(str);

utf16str = rb_str_dup(str);
if (!UTF16_LE_P(utf16str) && !UTF16_BE_P(utf16str)) {
/* an untagged string (the utf16 option) holds native-byte-order UTF-16 */
const union { uint16_t u16; uint8_t u8[2]; } native = { 1 };
rb_enc_associate_index(utf16str, rb_enc_find_index(native.u8[0] ? "UTF-16LE" : "UTF-16BE"));
}
codepoints = rb_funcall(utf16str, rb_intern("codepoints"), 0);
if (RTEST(rb_funcall(codepoints, rb_intern("include?"), 1, INT2FIX(0)))) {
rb_raise(rb_eArgError, "string contains null char");
}

rb_str_buf_cat(str, "\x00\x00", 2L);
return RSTRING_PTR(str);
}
Expand Down Expand Up @@ -200,10 +214,10 @@ rb_sqlite3_open_v2(VALUE self, VALUE file, VALUE mode, VALUE zvfs)

flags = NUM2INT(mode);
status = sqlite3_open_v2(
StringValuePtr(file),
StringValueCStr(file),
&ctx->db,
flags,
NIL_P(zvfs) ? NULL : StringValuePtr(zvfs)
NIL_P(zvfs) ? NULL : StringValueCStr(zvfs)
);

if (status != SQLITE_OK) {
Expand Down
48 changes: 44 additions & 4 deletions test/test_database.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ def teardown
@db.close unless @db.closed?
end

def native_utf16_encoding
([1].pack("I") == [1].pack("N")) ? Encoding::UTF_16BE : Encoding::UTF_16LE
end

def mock_database_load_extension_internal(db)
class << db
attr_reader :load_extension_internal_path
Expand Down Expand Up @@ -269,15 +273,51 @@ def test_open_yields_self
end

def test_new_with_options
# determine if Ruby is running on Big Endian platform
utf16 = ([1].pack("I") == [1].pack("N")) ? "UTF-16BE" : "UTF-16LE"

db = SQLite3::Database.new(":memory:".encode(utf16), utf16: true)
db = SQLite3::Database.new(":memory:".encode(native_utf16_encoding), utf16: true)
assert_instance_of(SQLite3::Database, db)
ensure
db&.close
end

def test_new_with_filename_containing_null_byte_raises_and_creates_no_file
Dir.mktmpdir do |dir|
assert_raises(ArgumentError) { SQLite3::Database.new("#{dir}/prefix\0suffix.db") }
assert_empty Dir.children(dir)
end
end

def test_new_with_utf16_filename_containing_null_char_raises_and_creates_no_file
Dir.mktmpdir do |dir|
assert_raises(ArgumentError) { SQLite3::Database.new("#{dir}/prefix\0suffix.db".encode(native_utf16_encoding)) }
assert_empty Dir.children(dir)
end
end

def test_new_with_untagged_utf16_filename_and_utf16_option
Dir.mktmpdir do |dir|
untagged = "#{dir}/test.db".encode(native_utf16_encoding).force_encoding(Encoding::BINARY)
db = SQLite3::Database.new(untagged, utf16: true)
assert_path_exists("#{dir}/test.db")
ensure
db&.close
end
end

def test_new_with_untagged_utf16_filename_containing_null_char_raises_and_creates_no_file
Dir.mktmpdir do |dir|
untagged = "#{dir}/prefix\0suffix.db".encode(native_utf16_encoding).force_encoding(Encoding::BINARY)
assert_raises(ArgumentError) { SQLite3::Database.new(untagged, utf16: true) }
assert_empty Dir.children(dir)
end
end

def test_new_with_vfs_name_containing_null_byte_raises
Dir.mktmpdir do |dir|
assert_raises(ArgumentError) { SQLite3::Database.new("#{dir}/test.db", {}, "prefix\0suffix") }
assert_empty Dir.children(dir)
end
end

def test_close
db = SQLite3::Database.new(":memory:")
db.close
Expand Down