Bug #119659 BinaryResultsetReader fails to consume EOF packet after column definitions when EOF is not deprecated
Submitted: 12 Jan 11:27
Reporter: zander zeng Email Updates:
Status: Open Impact on me:
None 
Category:Connector / J Severity:S2 (Serious)
Version:mysql-connector-j-9.5.0 OS:Any
Assigned to: CPU Architecture:Any

[12 Jan 11:27] zander zeng
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());
    }
} 
```