Description:
I have identified a logic regression in com.mysql.cj.protocol.a.BinaryResultsetReader that causes query failures when connecting to MySQL servers where CLIENT_DEPRECATE_EOF is NOT enabled (e.g., older MySQL versions like 5.6/5.7 or specific configurations).
Analysis:
In the read method of BinaryResultsetReader, after reading column definitions, the code attempts to handle the intermediate packet (EOF or OK) before reading rows.
Lines 78-95 (in source):
```
if (isCursorPossible || !this.protocol.getServerSession().isEOFDeprecated()) {
// Probe the next packet
NativePacketPayload rowPacket = this.protocol.probeMessage(this.protocol.getReusablePacket());
if (rowPacket.isResultSetOKPacket() || rowPacket.isEOFPacket()) {
this.protocol.readServerStatusForResultSets(rowPacket, true);
// PROBLEM HERE:
// The packet is ONLY consumed if cursorExists() is true.
if (this.protocol.getServerSession().cursorExists()) {
rowPacket = this.protocol.readMessage(this.protocol.getReusablePacket());
}
}
// ...
}
```
The Issue:
When isEOFDeprecated() is false (legacy protocol) and cursorExists() is false (standard binary protocol execution):
1. The code enters the if block because !isEOFDeprecated() is true.
2. It probes the next packet, which is the EOF Packet marking the end of Column Definitions.
3. It reads the server status.
4. However, it skips the readMessage() call because cursorExists() is false.
Consequence:
The intermediate EOF packet remains in the protocol reader buffer. When ResultsetRowReader subsequently attempts to read the first row of data, it reads this stale EOF packet instead. The reader misinterprets this as the end of the result set (end of rows), resulting in an empty ResultSet even when the query matches data. Furthermore, this desynchronizes the connection state for subsequent queries.
How to repeat:
1. Use MySQL Connector/J 9.5.0.
2. Connect to a MySQL Server version < 5.7.5 (or a server configured without CLIENT_DEPRECATE_EOF capability).
3. Execute a standard PreparedStatement query (Server-side prepared statement) that returns rows.
SELECT * FROM some_table
4. The returned ResultSet will be empty, or the connection may throw a packet sequence error on the next usage.
Suggested fix:
The logic should consume the packet if it is an EOF packet and EOF is not deprecated, regardless of the cursor state.
Modified logic suggestion:
```
if (rowPacket.isResultSetOKPacket() || rowPacket.isEOFPacket()) {
this.protocol.readServerStatusForResultSets(rowPacket, true);
// Fix: Consume packet if cursor exists OR if it's a legacy EOF packet
if (this.protocol.getServerSession().cursorExists() ||
(rowPacket.isEOFPacket() && !this.protocol.getServerSession().isEOFDeprecated())) {
rowPacket = this.protocol.readMessage(this.protocol.getReusablePacket());
}
}
```
Description: I have identified a logic regression in com.mysql.cj.protocol.a.BinaryResultsetReader that causes query failures when connecting to MySQL servers where CLIENT_DEPRECATE_EOF is NOT enabled (e.g., older MySQL versions like 5.6/5.7 or specific configurations). Analysis: In the read method of BinaryResultsetReader, after reading column definitions, the code attempts to handle the intermediate packet (EOF or OK) before reading rows. Lines 78-95 (in source): ``` if (isCursorPossible || !this.protocol.getServerSession().isEOFDeprecated()) { // Probe the next packet NativePacketPayload rowPacket = this.protocol.probeMessage(this.protocol.getReusablePacket()); if (rowPacket.isResultSetOKPacket() || rowPacket.isEOFPacket()) { this.protocol.readServerStatusForResultSets(rowPacket, true); // PROBLEM HERE: // The packet is ONLY consumed if cursorExists() is true. if (this.protocol.getServerSession().cursorExists()) { rowPacket = this.protocol.readMessage(this.protocol.getReusablePacket()); } } // ... } ``` The Issue: When isEOFDeprecated() is false (legacy protocol) and cursorExists() is false (standard binary protocol execution): 1. The code enters the if block because !isEOFDeprecated() is true. 2. It probes the next packet, which is the EOF Packet marking the end of Column Definitions. 3. It reads the server status. 4. However, it skips the readMessage() call because cursorExists() is false. Consequence: The intermediate EOF packet remains in the protocol reader buffer. When ResultsetRowReader subsequently attempts to read the first row of data, it reads this stale EOF packet instead. The reader misinterprets this as the end of the result set (end of rows), resulting in an empty ResultSet even when the query matches data. Furthermore, this desynchronizes the connection state for subsequent queries. How to repeat: 1. Use MySQL Connector/J 9.5.0. 2. Connect to a MySQL Server version < 5.7.5 (or a server configured without CLIENT_DEPRECATE_EOF capability). 3. Execute a standard PreparedStatement query (Server-side prepared statement) that returns rows. SELECT * FROM some_table 4. The returned ResultSet will be empty, or the connection may throw a packet sequence error on the next usage. Suggested fix: The logic should consume the packet if it is an EOF packet and EOF is not deprecated, regardless of the cursor state. Modified logic suggestion: ``` if (rowPacket.isResultSetOKPacket() || rowPacket.isEOFPacket()) { this.protocol.readServerStatusForResultSets(rowPacket, true); // Fix: Consume packet if cursor exists OR if it's a legacy EOF packet if (this.protocol.getServerSession().cursorExists() || (rowPacket.isEOFPacket() && !this.protocol.getServerSession().isEOFDeprecated())) { rowPacket = this.protocol.readMessage(this.protocol.getReusablePacket()); } } ```