Skip to content

Commit

Permalink
Fix encoding for values passed to custom functions
Browse files Browse the repository at this point in the history
Fixes: 218
  • Loading branch information
tenderlove committed Jan 25, 2024
1 parent 44ab2aa commit be3a0bb
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 12 deletions.
26 changes: 14 additions & 12 deletions ext/sqlite3/database.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,32 +277,34 @@ last_insert_row_id(VALUE self)
VALUE
sqlite3val2rb(sqlite3_value *val)
{
VALUE rb_val;

switch (sqlite3_value_type(val)) {
case SQLITE_INTEGER:
return LL2NUM(sqlite3_value_int64(val));
rb_val = LL2NUM(sqlite3_value_int64(val));
break;
case SQLITE_FLOAT:
return rb_float_new(sqlite3_value_double(val));
rb_val = rb_float_new(sqlite3_value_double(val));
break;
case SQLITE_TEXT:
return rb_str_new2((const char *)sqlite3_value_text(val));
case SQLITE_TEXT: {
rb_val = rb_utf8_str_new_cstr((const char *)sqlite3_value_text(val));
rb_obj_freeze(rb_val);
break;
}
case SQLITE_BLOB: {
/* Sqlite warns calling sqlite3_value_bytes may invalidate pointer from sqlite3_value_blob,
so we explicitly get the length before getting blob pointer.
Note that rb_str_new apparently create string with ASCII-8BIT (BINARY) encoding,
which is what we want, as blobs are binary
*/
int len = sqlite3_value_bytes(val);
return rb_str_new((const char *)sqlite3_value_blob(val), len);
rb_val = rb_str_new((const char *)sqlite3_value_blob(val), len);
rb_obj_freeze(rb_val);
break;
}
case SQLITE_NULL:
return Qnil;
rb_val = Qnil;
break;
default:
rb_raise(rb_eRuntimeError, "bad type"); /* FIXME */
rb_raise(rb_eRuntimeError, "bad type");
}

return rb_val;
}

void
Expand Down
30 changes: 30 additions & 0 deletions test/test_database.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,36 @@ def teardown
@db.close unless @db.closed?
end

def test_custom_function_encoding
@db.execute("CREATE TABLE
sourceTable(
sourceData TEXT);")
@db.execute("INSERT INTO sourceTable
VALUES ('abcde');")

@db.create_function("GetCopy", 1) { |func, value|
func.result = value
}

@db.transaction { |t|
t.execute("CREATE TABLE
afterTable(
beforeData TEXT,
afterData TEXT);".squeeze(" "))

t.execute("INSERT INTO afterTable
SELECT
sourceData,
GetCopy(sourceData)
FROM sourceTable;")
}

assert_equal(1, @db.get_first_value("SELECT 1
FROM afterTable
WHERE beforeData = afterData
LIMIT 1;"))
end

def test_segv
assert_raises { SQLite3::Database.new 1 } # rubocop:disable Minitest/UnspecifiedException
end
Expand Down

0 comments on commit be3a0bb

Please sign in to comment.