Repository navigation
bindings/c: convert non-text values to text in sqlite3_value_text - #10852
Open
abrahamperz wants to merge 2 commits into
Open
abrahamperz wants to merge 2 commits into
abrahamperz wants to merge 2 commits into
Conversation
sqlite3_value_text must return a pointer to a NUL-terminated string (the SQLite API contract), but the C compat layer handed back the pointer of a Rust &str whose backing buffer (TextValue, built by new_boxed) was exactly `len` bytes with no terminator. A C caller running strlen on it read past the buffer. Allocate TextValue storage with a trailing NUL (len + 1 bytes); `len` still counts only the text, so to_text()/as_str() are unchanged. free() releases len + 1, and the error-message path (new_with_message) allocates the same way so its stored message is also a valid C string. Tests: text_value_pointer_is_nul_terminated checks the byte at `len` is NUL for several inputs. Without the fix that read runs past the buffer (SIGSEGV); with it, the test passes. Fixes tursodatabase#7982
sqlite3_value_text returned a null pointer for an INTEGER, REAL or BLOB value, while SQLite converts the value to its text form. A C function that ran strlen on the result, or printed it, saw nothing for a numeric or blob argument. This also reached sqlite3_value_dup copies and sqlite3_column_value values, which read through the same accessor. Coerce the value to text in place, as SQLite does, so the returned pointer stays valid: a number becomes its decimal text and a blob keeps its bytes verbatim, each NUL-terminated. from_text_bytes builds the text value for a blob without a UTF-8 round-trip. Any previous heap is freed before the text replaces it. Follow-up to tursodatabase#10728. Tests: test_value_text_coerces_non_text_like_sqlite registers a C function that returns strlen(sqlite3_value_text(argv[0])) and checks 42, 1.5, X'414243' and 'hello' give 2, 3, 3 and 5, matching SQLite. The numeric and blob cases return -1 without the change.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #10728, and stacked on it: the first commit here is that PR's NUL-termination, which drops out once that PR merges to main.
A C function registered with
sqlite3_create_function_v2that read an argument withsqlite3_value_textgot a null pointer when the argument was an INTEGER, REAL or BLOB. SQLite converts the value to text there, sostrlenorprintfon the result works for any type. The same accessor is used bysqlite3_value_dupcopies andsqlite3_column_value, so they had the gap too.Root cause
sqlite3_value_textreturned a pointer only when the value already held text;to_text()givesNonefor a number or a blob. Turso has the bytes but never converted them.Fix
Coerce the value to text in place, the way SQLite does, so the returned pointer stays valid: a number becomes its decimal text, a blob keeps its bytes verbatim, each NUL-terminated.
from_text_bytesbuilds the text value for a blob without a UTF-8 round-trip. Any previous heap is freed before the text replaces it.Replacing the value in place is safe because the
ExtValueis owned for the duration of the call and freed by type afterwards: a scalar function's arguments live in aVecthatfinish_callfrees,column_valuecaches owned values in the statement, and a dup'd value is owned by the caller. So the allocation and its free stay matched.One visible consequence, which matches SQLite: after
sqlite3_value_textreads a value, a latersqlite3_value_typeon it reports TEXT.Testing
test_value_text_coerces_non_text_like_sqliteregisters a C function that returnsstrlen(sqlite3_value_text(argv[0]))and checks that42,1.5,X'414243'and'hello'give2,3,3and5. It runs against both Turso and real SQLite. Without the change the numeric and blob cases return a null pointer, so-1from the test.