Use VarString for string parameter encoding in binary protocol#142
Use VarString for string parameter encoding in binary protocol#142
Conversation
String parameters in prepared statements were encoded with MYSQL_TYPE_LONG_BLOB (0xfb), which tells the server to treat the value as raw binary data. This breaks MariaDB's native UUID column type (introduced in 10.7), which requires the parameter to be declared as a string type to trigger string-to-UUID parsing. Changing to MYSQL_TYPE_VAR_STRING (0xfd) is more semantically correct for PHP string values and matches what MySQL's own C API uses for string binds. The wire encoding (length-prefixed bytes) is identical for both types, so this is a no-op for all standard column types on both MySQL and MariaDB.
Psalm 6.15.1 on PHP 8.4.19 incorrectly reports InvalidAttribute for #[\Override] attributes. This was green on PHP 8.4.18 but broke with the runner update. Suppress until Psalm is updated.
|
Note: The CI failures on PHP 8.2–8.5 are unrelated to this change — Psalm 6.15.1 started reporting |
Security: - Fix HTTP/2 ping flood on active streams (amphp/http-server#386) Bug fixes: - Use VarString for string params in binary protocol (amphp/mysql#142) - Decode BIT columns as int instead of string (amphp/mysql#138) - Close connections on pool destruct (amphp/http-client#396) - Fix duplicate keys in byte-stream split() (amphp/byte-stream#113) - Fix Closure type annotation for static analysis (amphp/amp#451) - Safely handle DisposedException on unsubscribe (amphp/redis#100) Features: - Add TLS support for Redis connections (amphp/redis#98) - Add disperse() for concurrent closure execution (amphp/amp#460)
|
I wonder if this could negatively affect a prepared statement providing data for a blob-type field via a prepared statement. I think this makes sense for string types by default, but I think this might need to be coupled with changes to the connection processor to set the value type based on the column type. |
Switching every string parameter to VarString broke binary payloads sent to BLOB columns: VarString carries connection-charset semantics, while LongBlob is treated as binary (charset 63). Non-UTF8 bytes flowing into a BLOB column on a utf8mb4 connection could trip "Incorrect string value" or transcode silently. The prepare response already exposes the target type per parameter, so ConnectionProcessor::execute now forwards it to MysqlEncodedValue. For PHP string values, the encoder picks LongBlob when the target is in the Blob family (TinyBlob, Blob, MediumBlob, LongBlob) and VarString otherwise. The same branch is applied to the prebound types entry, which previously hardcoded VarString and would have hit the same issue for COM_STMT_SEND_LONG_DATA writes to blob columns. Keeps the MariaDB UUID fix intact (string targets like Varchar, String, VarString fall through to VarString) while leaving binary blobs binary.
|
Hey @trowski, fair point and you're right. I'd been telling myself "wire format is identical, so no behavior change", but Pushed b603de7 with the column-type-aware version:
Added unit coverage in The MariaDB UUID fix still works since Adding MariaDB to the CI matrix in a follow-up still makes sense since the UUID side of this only exercises on MariaDB. Happy to take that on after this lands. |
Summary
MYSQL_TYPE_LONG_BLOB(0xfb), which tells the server to treat values as raw binary dataUUIDcolumn type (introduced in MariaDB 10.7), which requires string-typed parameters to trigger string→UUID parsingMYSQL_TYPE_VAR_STRING(0xfd), which is more semantically correct for PHP string values and matches MySQL's own C API behavior for string bindsWhy this matters
MariaDB 10.7+ introduced a native
UUIDcolumn type that stores UUIDs as 16-byte binary internally. When a prepared statement parameter is typed asLONG_BLOB, MariaDB interprets the 36-byte ASCII UUID string as raw bytes instead of parsing the human-readable format. WithVAR_STRING, MariaDB correctly converts the string representation to its internal format.Safety
The wire encoding (length-prefixed bytes) is identical for both
LongBlobandVarString— only the 2-byte type header inCOM_STMT_EXECUTEchanges. All existing tests pass (136/136).This is effectively a no-op for all standard MySQL/MariaDB column types (
VARCHAR,CHAR,TEXT,BLOB, etc.) since both types trigger the same implicit conversion paths. The difference only surfaces for MariaDB-specific types likeUUIDthat distinguish between string and binary parameter sources.Suggestion
The current test suite only runs against MySQL. Adding MariaDB to the CI matrix (related: #80) would help catch driver-specific issues like this. Happy to help with that in a follow-up PR if there's interest.