terrymanu commented on issue #34289:
URL:
https://github.com/apache/shardingsphere/issues/34289#issuecomment-3677368199
## Problem Understanding
- On Proxy 5.5.1, a Golang client executes a prepared query (e.g., ...
limit ?, ?) and hits an NPE in MySQLComStmtExecuteExecutor; deeper inspection
shows the failure starts with a ClassCastException during parameter binding.
- Trigger: the client sends parameters via MySQL binary protocol with
LIMIT arguments marked as string-like types; the protocol layer returns raw
byte[]/String, while downstream expects numeric types and fails to cast.
## Root Cause
- In MySQL protocol execution (MySQLComStmtExecutePacket#readParameters),
string-type parameters (e.g., MYSQL_TYPE_STRING/VAR_STRING) are returned as-is
(byte[]/String) with no type normalization.
- Binding logic (e.g., PaginationContext#getValue and any path that needs
numbers) only accepts Number. Receiving byte[]/String triggers a
ClassCastException, which bubbles up and shows as an NPE in
MySQLComStmtExecuteExecutor.
- Essentially, the protocol layer doesn’t normalize binary-protocol
parameters to Java types consistent with their declared types/semantics; it
assumes parameters are already correct numerics.
## Problem Analysis
- For non-INSERT statements, prepare phase defaults parameter column
definitions to VAR_STRING, so clients may send all params as strings.
- MySQLBinaryProtocolValueFactory returns byte[]/String for string/byte
types and doesn’t align with SQL semantics (e.g., numeric usage).
- Binding cannot distinguish “string param” vs “numeric-meaning string”;
any numeric-expected path (not just pagination) will fail if the param comes
back as a string.
- Relevant official protocol doc (COM_STMT_EXECUTE and data types):
https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_stmt_execute.html
Conclusion (Code-Design-Level Fix Suggestion, Protocol-Layer General
Handling)
- Fix at the protocol layer by normalizing all parameters once after
reading, before binding, using MySQL parameter types (and unsigned flags) plus
column definition flags:
// Design sketch: after readParameters and before bindParameters
List<Object> params = packet.readParameters(...);
List<MySQLPreparedStatementParameterType> types =
preparedStatement.getParameterTypes();
List<Integer> flags =
preparedStatement.getParameterColumnDefinitionFlags();
List<Object> normalized = new ArrayList<>(params.size());
for (int i = 0; i < params.size(); i++) {
Object val = params.get(i);
MySQLPreparedStatementParameterType type = types.get(i);
int flag = flags.isEmpty() ? 0 : flags.get(i);
normalized.add(normalizeValue(type, flag, val)); // Number stays
Number; byte[]/CharSequence parsed to Number when the type is numeric;
non-numeric text throws a clear SQLException
}
// then bindParameters(normalized)
- Use MySQL type + unsigned flag + column definition flag to decide
the target Java type; keep true character types as-is, but for numeric types
(even if sent as VAR_STRING) parse safely to Long/BigDecimal, throwing a
readable error on invalid format.
- Do the normalization once in the protocol execution path to avoid
scattering type assumptions in higher layers.
- Tests:
- Numeric params (including unsigned) come back as correct Number.
- VAR_STRING/VARBINARY marked as numeric columns: numeric text in
byte[]/String parses successfully; non-numeric text throws a clear exception.
- Pure string types remain unchanged.
- Paths that need numerics (including LIMIT) no longer throw
type-cast errors.
We warmly invite community contributors to submit a PR implementing this
protocol-layer normalization and adding the above tests, especially to improve
robustness for Golang clients using prepared statements. Your help is highly
appreciated!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]