From 8efd495100d50c1a54c5b083313158b6244937e5 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Tue, 11 Aug 2026 14:22:53 -0400 Subject: [PATCH] Pass TEXT values containing embedded NULs to UDFs intact TEXT values containing an embedded NUL byte were stored whole, but functions created with `Database#define_function` received them truncated at the first NUL, so any validation or content-policy decision the function made ran on only a prefix of the stored value. `sqlite3val2rb` will now build the Ruby string with the explicit byte length reported by sqlite, matching the BLOB branch, so function blocks will receive the complete value. --- ext/sqlite3/database.c | 4 +++- test/test_database.rb | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ext/sqlite3/database.c b/ext/sqlite3/database.c index 2c949b30..91550db0 100644 --- a/ext/sqlite3/database.c +++ b/ext/sqlite3/database.c @@ -456,7 +456,9 @@ sqlite3val2rb(sqlite3_value *val) rb_val = rb_float_new(sqlite3_value_double(val)); break; case SQLITE_TEXT: { - rb_val = rb_utf8_str_new_cstr((const char *)sqlite3_value_text(val)); + const char *text = (const char *)sqlite3_value_text(val); + int len = sqlite3_value_bytes(val); + rb_val = rb_utf8_str_new(text, len); rb_obj_freeze(rb_val); break; } diff --git a/test/test_database.rb b/test/test_database.rb index 4b02ee6e..718fce8b 100644 --- a/test/test_database.rb +++ b/test/test_database.rb @@ -508,6 +508,19 @@ def test_call_func_blob assert_equal [blob, blob.length, 21], called_with end + def test_call_func_text_with_embedded_nul + called_with = nil + @db.define_function("hello") do |a| + called_with = a + nil + end + @db.execute("create table texts ( text_value text )") + @db.execute("insert into texts ( text_value ) values ( ? )", ["abc\0def"]) + @db.execute("select hello(text_value) from texts") + assert_equal "abc\0def", called_with + assert_equal 7, called_with.bytesize + end + def test_function_return @db.define_function("hello") { |a| 10 } assert_equal [10], @db.execute("select hello('world')").first