Fix SQL_C_GUID parameter binding sending corrupted data on the wire (#295)#296
Fix SQL_C_GUID parameter binding sending corrupted data on the wire (#295)#296fdcastel wants to merge 1 commit into
Conversation
|
Honestly, I studied this PR and didn't understand a thing. Could you describe it more clearly—where the error was, how to reproduce it, and what the purpose of these changes was?.. |
|
I understand the code switching for string/binary, it's okay. But what's the purpose of total rewriting of the convGuidToString() foo? And why the convGuidToStringW() is left untouched?.. |
a32a022 to
538a720
Compare
When an ODBC application called SQLBindParameter with C type SQL_C_GUID and SQL type SQL_GUID, the driver accepted the call but did not convert the 16-byte UUID into Firebird's wire format. Symptoms reported in FirebirdSQL#295: * BINARY(16) / CHAR(16) CHARACTER SET OCTETS targets received the ASCII bytes of the canonical UUID string truncated to 16 chars. * Untyped VARCHAR parameters (e.g. inside CHAR_TO_UUID(?)) received a wide-char buffer interpreted as narrow text, so Firebird raised "Human readable UUID argument for CHAR_TO_UUID must have hex digit at position 2 instead of ''". Add two wire-only conversion functions and route SQL_C_GUID input parameters to them: * convGuidToBinary writes 16 raw bytes in canonical UUID byte order (Data1/2/3 swapped from x86 little-endian to big-endian, Data4 as is). Used for BINARY(16) / CHAR(16) OCTETS / FB4+ BINARY targets. * convGuidToVarString stages the 36-char canonical UUID in the DescRecord local buffer and redirects the wire's sqldata via setSqlData(), matching the idiom transferStringToAllowedType already uses. This sidesteps the SQLDA-allocated buffer being only 2 bytes wide for an untyped `?` (VARCHAR(0)) placeholder. The pre-existing convGuidToString / convGuidToStringW remain unchanged; they handle the (currently unreachable) app-side fetch path that will become live with the column-side SQL_GUID mapping work tracked under FirebirdSQL#287 T5-5. Expose getSqlSubtype() and getSqlLen() as read-only accessors on HeadSqlVar so the dispatch in getAdressFunction can distinguish sqlsubtype == 1 + sqllen == 16 (BINARY/CHAR(16) OCTETS, raw bytes) from text wires of any other charset (canonical UUID string). Add three GuidParamBindingTest acceptance tests covering the issue's exact reproducers: SQL_C_GUID into CHAR(16) OCTETS, into VARCHAR via CHAR_TO_UUID(?), and into BINARY(16) via UUID_TO_CHAR(?). Closes FirebirdSQL#295.
|
Thanks for the review — fair feedback. I rewrote the commit so the diff is easier to read; here's the walkthrough. The bug. An ODBC app calls
Issue #295 has the full empirical trace including the two reproducers above ( The fix shape, after the rewrite. The wire-side fix lives in two new functions:
The dispatch in
History is squashed to one commit; force-pushed with |
What this fixes
When an ODBC application calls
SQLBindParameter(SQL_C_GUID, SQL_GUID, …, ptr, 16, &len)and hands the driver a 16-byte UUID, the driver returnsSQL_SUCCESSbut Firebird never sees the 16 UUID bytes:30 33 30 36 43 31 37 36 2D 45 34 30 31 2D 31 31instead of the 16 raw UUID bytes). The driver was stringifying to 36 chars and then truncating to fit the column.CHAR_TO_UUID(?)) — Firebird returnsexpression evaluation not supported — Human readable UUID argument for CHAR_TO_UUID must have hex digit at position 2 instead of "". The driver was writing a wide-char (UTF-16) buffer into a narrow-char wire slot; the embedded NULs made the string look empty.Reproducers, both from issue #295:
Real-world impact: duckdb/odbc-scanner binds DuckDB
::UUIDvalues exactly this way (seesrc/types/uuid_type.cppL33-44). Their Firebird UUID round-trip test (duckdb/odbc-scanner#169) is parked on this.Closes #295.
How the fix works
Two new wire-only conversion functions, picked up by
OdbcConvert::getAdressFunctionwhen binding an SQL_C_GUID input parameter:convGuidToBinary— writes 16 raw bytes in canonical UUID byte order (Data1/Data2/Data3 swapped from x86 little-endian to big-endian, Data4 unchanged). Used when the IPD describes the slot as BINARY(16) / CHAR(16) CHARACTER SET OCTETS (sqlsubtype == 1 && sqllen == 16) or as FB4+ BINARY/VARBINARY (SQL_C_BINARY).convGuidToVarString— stages the 36-char canonical UUID in the DescRecord's local buffer and redirects the wire'ssqldataviasetSqlData(), mirroring the idiomtransferStringToAllowedTypeuses. Necessary because an untyped?placeholder is described by Firebird as VARCHAR(0): its wire buffer only holds the 2-byte length prefix — nowhere near enough for 36 chars without the redirection.setTypeText()converts the wire from SQL_VARYING to SQL_TEXT so there's no length prefix to maintain.The pre-existing
convGuidToString/convGuidToStringWare intentionally untouched. They handle the app-side fetch direction (SQLGetData(…, SQL_C_CHAR/WCHAR, …)from a column described as SQL_GUID). That path is currently dead code because the column-side SQL_GUID mapping is open task T5-5 (#287), but the functions remain correct for when T5-5 lands.getSqlSubtype()/getSqlLen()onHeadSqlVarare pure read-only accessors so the dispatch can distinguish OCTETS-subtype + sqllen 16 from any other text charset.Tests
Three new
GuidParamBindingTestcases intests/test_guid_and_binary.cppcover each reproducer:BindGuidToCharOctets16— SQL_C_GUID into CHAR(16) CHARACTER SET OCTETS, round-tripped viaUUID_TO_CHAR.BindGuidToVarcharViaCharToUuid— SQL_C_GUID into VARCHAR viaCHAR_TO_UUID(?)(reproducer B).BindGuidToUuidToCharRoundtrip— SQL_C_GUID into BINARY(16) viaUUID_TO_CHAR(?)(reproducer A).All three pass on the full FB-master matrix (windows-x64/x86/ARM64, ubuntu-x64/ARM64). Existing suite shows zero regressions locally (202 passed, 0 failed, same skip count as before this PR).
CI status
All five
master(Firebird 6) matrix jobs pass with this change. The still-failing5.0.3jobs error out at the database-provisioning step withCould not find Firebird release for version 5.0.3 on GitHubfromPSFirebird/1.2.2/Get-FirebirdReleaseUrl.ps1— a pre-existing infra issue that surfaces before any code from this branch runs, unrelated to this PR.Test plan
BindGuidToCharOctets16,BindGuidToVarcharViaCharToUuid,BindGuidToUuidToCharRoundtrippass on the master CI matrix.