This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new d4cc96a5cf5 [fix](protocol) Support CLIENT_DEPRECATE_EOF to fix empty
result with MySQL driver 9.5.0 (#61050)
d4cc96a5cf5 is described below
commit d4cc96a5cf5be0f3a106fd1dc524e34243b58c8c
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Wed Mar 4 22:45:58 2026 -0800
[fix](protocol) Support CLIENT_DEPRECATE_EOF to fix empty result with MySQL
driver 9.5.0 (#61050)
### What problem does this PR solve?
Issue Number: close #60634
Problem Summary:
When using MySQL Connector/J 9.5.0+ with useServerPrepStmts=true,
queries return empty result sets. This is caused by a behavior change in
the driver's BinaryResultsetReader that no longer consumes the
intermediate EOF packet after column definitions when no cursor exists.
The fix properly implements the CLIENT_DEPRECATE_EOF capability:
1. Advertise CLIENT_DEPRECATE_EOF in server handshake capability flags
(MysqlCapability.java), so the driver negotiates it and uses the
isEOFDeprecated()=true code path, bypassing the buggy 9.5.0 logic.
2. Skip intermediate EOF packets after column definitions when
CLIENT_DEPRECATE_EOF is negotiated (StmtExecutor.sendMetaData,
sendFields, sendStmtPrepareOK).
3. Send a 'ResultSet OK' packet (0xFE header, payload > 5 bytes) as the
final end-of-result-set marker instead of the traditional EOF packet, so
the driver's isResultSetOKPacket() correctly identifies it
(MysqlResultSetEndPacket.java, ConnectProcessor.getResultPacket).
4. Always write the info field in MysqlOkPacket (even when empty) to
prevent ArrayIndexOutOfBoundsException when the driver parses OK packets
via OkPacket.parse() which unconditionally reads STRING_LENENC.
---
.../org/apache/doris/mysql/MysqlCapability.java | 7 +-
.../java/org/apache/doris/mysql/MysqlOkPacket.java | 9 +-
.../doris/mysql/MysqlResultSetEndPacket.java | 61 +++++++++++
.../java/org/apache/doris/qe/ConnectProcessor.java | 19 +++-
.../java/org/apache/doris/qe/FEOpExecutor.java | 4 +
.../java/org/apache/doris/qe/StmtExecutor.java | 49 +++++----
.../apache/doris/mysql/MysqlCapabilityTest.java | 3 +-
.../org/apache/doris/mysql/MysqlOkPacketTest.java | 23 +++-
.../doris/mysql/MysqlResultSetEndPacketTest.java | 120 +++++++++++++++++++++
gensrc/thrift/FrontendService.thrift | 2 +
10 files changed, 266 insertions(+), 31 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlCapability.java
b/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlCapability.java
index cbfa88038be..d58f046eb53 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlCapability.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlCapability.java
@@ -74,13 +74,14 @@ public class MysqlCapability {
private static final int DEFAULT_FLAGS =
Flag.CLIENT_PROTOCOL_41.getFlagBit()
| Flag.CLIENT_CONNECT_WITH_DB.getFlagBit() |
Flag.CLIENT_SECURE_CONNECTION.getFlagBit()
- | Flag.CLIENT_PLUGIN_AUTH.getFlagBit() |
Flag.CLIENT_LOCAL_FILES.getFlagBit() | Flag.CLIENT_LONG_FLAG
- .getFlagBit();
+ | Flag.CLIENT_PLUGIN_AUTH.getFlagBit() |
Flag.CLIENT_LOCAL_FILES.getFlagBit()
+ | Flag.CLIENT_LONG_FLAG.getFlagBit() |
Flag.CLIENT_DEPRECATE_EOF.getFlagBit();
private static final int SSL_FLAGS = Flag.CLIENT_PROTOCOL_41.getFlagBit()
| Flag.CLIENT_CONNECT_WITH_DB.getFlagBit() |
Flag.CLIENT_SECURE_CONNECTION.getFlagBit()
| Flag.CLIENT_PLUGIN_AUTH.getFlagBit() |
Flag.CLIENT_LOCAL_FILES.getFlagBit()
- | Flag.CLIENT_LONG_FLAG.getFlagBit() |
Flag.CLIENT_SSL.getFlagBit();
+ | Flag.CLIENT_LONG_FLAG.getFlagBit() | Flag.CLIENT_SSL.getFlagBit()
+ | Flag.CLIENT_DEPRECATE_EOF.getFlagBit();
public static final MysqlCapability DEFAULT_CAPABILITY = new
MysqlCapability(DEFAULT_FLAGS);
public static final MysqlCapability SSL_CAPABILITY = new
MysqlCapability(SSL_FLAGS);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlOkPacket.java
b/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlOkPacket.java
index 778dfd8c3c4..4fa80102317 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlOkPacket.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlOkPacket.java
@@ -59,8 +59,13 @@ public class MysqlOkPacket extends MysqlPacket {
// if ((STATUS_FLAGS &
MysqlStatusFlag.SERVER_SESSION_STATE_CHANGED) != 0) {
// }
} else {
- if (!Strings.isNullOrEmpty(infoMessage)) {
- // NOTE: in datasheet, use EOF string, but in the code, mysql
use length encoded string
+ // Always write the info field as a length-encoded string.
+ // When CLIENT_DEPRECATE_EOF is negotiated, the driver's
OkPacket.parse()
+ // unconditionally reads STRING_LENENC for info, so an empty
string must
+ // still be written (as a single 0x00 byte representing length 0).
+ if (Strings.isNullOrEmpty(infoMessage)) {
+ serializer.writeVInt(0);
+ } else {
serializer.writeLenEncodedString(infoMessage);
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlResultSetEndPacket.java
b/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlResultSetEndPacket.java
new file mode 100644
index 00000000000..5543b85c361
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/mysql/MysqlResultSetEndPacket.java
@@ -0,0 +1,61 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.mysql;
+
+import org.apache.doris.qe.QueryState;
+
+/**
+ * MySQL OK packet with 0xFE header, used as the end-of-result-set marker
+ * when CLIENT_DEPRECATE_EOF is set.
+ *
+ * When CLIENT_DEPRECATE_EOF capability is negotiated, the traditional EOF
packet (0xFE, ≤5 bytes)
+ * is replaced by an OK packet that also uses 0xFE as its header byte but has
a payload larger
+ * than 5 bytes. This is called a "ResultSet OK" packet in the MySQL protocol
documentation.
+ *
+ * See:
https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_ok_packet.html
+ */
+public class MysqlResultSetEndPacket extends MysqlPacket {
+ // 0xFE header to distinguish from regular OK (0x00)
+ private static final int RESULTSET_OK_INDICATOR = 0xFE;
+ private static final long AFFECTED_ROWS = 0;
+ private static final long LAST_INSERT_ID = 0;
+
+ private int serverStatus = 0;
+ private int warningCount = 0;
+
+ public MysqlResultSetEndPacket(QueryState state) {
+ this.serverStatus = state.serverStatus;
+ }
+
+ @Override
+ public void writeTo(MysqlSerializer serializer) {
+ // header: 0xFE (same as EOF, but the payload length > 5 distinguishes
it)
+ serializer.writeInt1(RESULTSET_OK_INDICATOR);
+ // affected_rows: int<lenenc>
+ serializer.writeVInt(AFFECTED_ROWS);
+ // last_insert_id: int<lenenc>
+ serializer.writeVInt(LAST_INSERT_ID);
+ // status_flags: int<2>
+ serializer.writeInt2(serverStatus);
+ // warnings: int<2>
+ serializer.writeInt2(warningCount);
+ // info: string<lenenc> (empty string, written as a single 0x00 byte
for length 0)
+ // The driver's OkPacket.parse() unconditionally reads this field.
+ serializer.writeVInt(0);
+ }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java
index d3e5a4652a2..571ce990663 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectProcessor.java
@@ -44,6 +44,7 @@ import org.apache.doris.metric.MetricRepo;
import org.apache.doris.mysql.MysqlChannel;
import org.apache.doris.mysql.MysqlCommand;
import org.apache.doris.mysql.MysqlPacket;
+import org.apache.doris.mysql.MysqlResultSetEndPacket;
import org.apache.doris.mysql.MysqlSerializer;
import org.apache.doris.mysql.MysqlServerStatusFlag;
import org.apache.doris.nereids.SqlCacheContext;
@@ -553,7 +554,17 @@ public abstract class ConnectProcessor {
// only Mysql protocol
protected ByteBuffer getResultPacket() {
Preconditions.checkState(connectType.equals(ConnectType.MYSQL));
- MysqlPacket packet = ctx.getState().toResponsePacket();
+ MysqlPacket packet;
+ // When CLIENT_DEPRECATE_EOF is set and the state is EOF (end of
result set),
+ // we need to send a "ResultSet OK" packet (0xFE header with payload >
5 bytes)
+ // instead of the traditional EOF packet. This is required by the
MySQL protocol
+ // and expected by MySQL Connector/J 9.5.0+.
+ if (ctx.getState().getStateType() == QueryState.MysqlStateType.EOF
+ && ctx.getMysqlChannel().clientDeprecatedEOF()) {
+ packet = new MysqlResultSetEndPacket(ctx.getState());
+ } else {
+ packet = ctx.getState().toResponsePacket();
+ }
if (packet == null) {
// possible two cases:
// 1. handler has send request
@@ -643,6 +654,12 @@ public abstract class ConnectProcessor {
// set compute group
ctx.setComputeGroup(Env.getCurrentEnv().getAuth().getComputeGroup(ctx.getQualifiedUser()));
+ // Propagate the client's CLIENT_DEPRECATE_EOF capability to the proxy
channel.
+ // This ensures the master generates packets matching the original
client's protocol.
+ if (request.isSetClientDeprecatedEOF() &&
request.isClientDeprecatedEOF()) {
+ ctx.getMysqlChannel().setClientDeprecatedEOF();
+ }
+
ctx.setThreadLocalInfo();
StmtExecutor executor = null;
try {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/FEOpExecutor.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/FEOpExecutor.java
index 9dcd379f913..6a461cb5c94 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/FEOpExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/FEOpExecutor.java
@@ -206,6 +206,10 @@ public class FEOpExecutor {
}
}
+ // Propagate the client's CLIENT_DEPRECATE_EOF capability so the
master FE
+ // generates packets matching the original client's protocol
expectations.
+
params.setClientDeprecatedEOF(ctx.getMysqlChannel().clientDeprecatedEOF());
+
return params;
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
index f2afbc47085..5480c2933b1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
@@ -67,7 +67,6 @@ import org.apache.doris.mysql.FieldInfo;
import org.apache.doris.mysql.MysqlChannel;
import org.apache.doris.mysql.MysqlCommand;
import org.apache.doris.mysql.MysqlEofPacket;
-import org.apache.doris.mysql.MysqlOkPacket;
import org.apache.doris.mysql.MysqlSerializer;
import org.apache.doris.mysql.ProxyMysqlChannel;
import org.apache.doris.nereids.NereidsPlanner;
@@ -1595,11 +1594,15 @@ public class StmtExecutor {
}
context.getMysqlChannel().sendOnePacket(serializer.toByteBuffer());
}
- // send EOF
- serializer.reset();
- MysqlEofPacket eofPacket = new MysqlEofPacket(context.getState());
- eofPacket.writeTo(serializer);
- context.getMysqlChannel().sendOnePacket(serializer.toByteBuffer());
+ // When CLIENT_DEPRECATE_EOF is set, the server should not send the
intermediate
+ // EOF packet after column definitions. The client will go directly
from column
+ // definitions to reading data rows.
+ if (!context.getMysqlChannel().clientDeprecatedEOF()) {
+ serializer.reset();
+ MysqlEofPacket eofPacket = new MysqlEofPacket(context.getState());
+ eofPacket.writeTo(serializer);
+ context.getMysqlChannel().sendOnePacket(serializer.toByteBuffer());
+ }
}
private List<PrimitiveType> exprToStringType(List<Expr> exprs) {
@@ -1641,15 +1644,15 @@ public class StmtExecutor {
serializer.writeField(colNames.get(i), Type.STRING);
context.getMysqlChannel().sendOnePacket(serializer.toByteBuffer());
}
- serializer.reset();
+ // When CLIENT_DEPRECATE_EOF is set, no EOF/OK packet should be
sent after
+ // parameter definitions. The driver knows how many params to
expect from the
+ // prepare OK packet and simply stops reading after that count.
if (!context.getMysqlChannel().clientDeprecatedEOF()) {
+ serializer.reset();
MysqlEofPacket eofPacket = new
MysqlEofPacket(context.getState());
eofPacket.writeTo(serializer);
- } else {
- MysqlOkPacket okPacket = new MysqlOkPacket(context.getState());
- okPacket.writeTo(serializer);
+
context.getMysqlChannel().sendOnePacket(serializer.toByteBuffer());
}
- context.getMysqlChannel().sendOnePacket(serializer.toByteBuffer());
}
if (numColumns > 0) {
for (Slot slot : output) {
@@ -1668,15 +1671,15 @@ public class StmtExecutor {
}
context.getMysqlChannel().sendOnePacket(serializer.toByteBuffer());
}
- serializer.reset();
+ // When CLIENT_DEPRECATE_EOF is set, no EOF/OK packet should be
sent after
+ // column definitions. The driver knows how many columns to expect
from the
+ // prepare OK packet and simply stops reading after that count.
if (!context.getMysqlChannel().clientDeprecatedEOF()) {
+ serializer.reset();
MysqlEofPacket eofPacket = new
MysqlEofPacket(context.getState());
eofPacket.writeTo(serializer);
- } else {
- MysqlOkPacket okPacket = new MysqlOkPacket(context.getState());
- okPacket.writeTo(serializer);
+
context.getMysqlChannel().sendOnePacket(serializer.toByteBuffer());
}
- context.getMysqlChannel().sendOnePacket(serializer.toByteBuffer());
}
context.getMysqlChannel().flush();
context.getState().setNoop();
@@ -1726,11 +1729,15 @@ public class StmtExecutor {
context.getMysqlChannel().sendOnePacket(serializer.toByteBuffer());
}
}
- // send EOF
- serializer.reset();
- MysqlEofPacket eofPacket = new MysqlEofPacket(context.getState());
- eofPacket.writeTo(serializer);
- context.getMysqlChannel().sendOnePacket(serializer.toByteBuffer());
+ // When CLIENT_DEPRECATE_EOF is set, the server should not send the
intermediate
+ // EOF packet after column definitions. The client will go directly
from column
+ // definitions to reading data rows.
+ if (!context.getMysqlChannel().clientDeprecatedEOF()) {
+ serializer.reset();
+ MysqlEofPacket eofPacket = new MysqlEofPacket(context.getState());
+ eofPacket.writeTo(serializer);
+ context.getMysqlChannel().sendOnePacket(serializer.toByteBuffer());
+ }
}
public void sendResultSet(ResultSet resultSet) throws IOException {
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/mysql/MysqlCapabilityTest.java
b/fe/fe-core/src/test/java/org/apache/doris/mysql/MysqlCapabilityTest.java
index f3b4385b960..78197804b63 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/mysql/MysqlCapabilityTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/mysql/MysqlCapabilityTest.java
@@ -44,8 +44,9 @@ public class MysqlCapabilityTest {
public void testDefaultFlags() {
MysqlCapability capability = MysqlCapability.DEFAULT_CAPABILITY;
Assert.assertEquals("CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB |
CLIENT_LOCAL_FILES | CLIENT_PROTOCOL_41"
- + " | CLIENT_SECURE_CONNECTION | CLIENT_PLUGIN_AUTH",
+ + " | CLIENT_SECURE_CONNECTION | CLIENT_PLUGIN_AUTH |
CLIENT_DEPRECATE_EOF",
capability.toString());
Assert.assertTrue(capability.supportClientLocalFile());
+ Assert.assertTrue(capability.isDeprecatedEOF());
}
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/mysql/MysqlOkPacketTest.java
b/fe/fe-core/src/test/java/org/apache/doris/mysql/MysqlOkPacketTest.java
index 6cb157ee252..9fe47adbf5e 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/mysql/MysqlOkPacketTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/mysql/MysqlOkPacketTest.java
@@ -56,10 +56,27 @@ public class MysqlOkPacketTest {
// assert warnings, int2: 0
Assert.assertEquals(0x00, MysqlProto.readInt2(buffer));
- // assert info, eof string: "OK"
- // Assert.assertEquals("OK", new
String(MysqlProto.readEofString(buffer)));
-
+ // When infoMessage is empty, an empty len-encoded string (0x00)
should still be written.
+ // This is required because OkPacket.parse() in MySQL Connector/J
unconditionally reads
+ // STRING_LENENC for info. Without this byte, the driver throws
+ // ArrayIndexOutOfBoundsException when CLIENT_DEPRECATE_EOF is
negotiated.
+ Assert.assertEquals(0x00, MysqlProto.readVInt(buffer));
Assert.assertEquals(0, buffer.remaining());
}
+ @Test
+ public void testWritePayloadSizeGreaterThan5() {
+ // When CLIENT_DEPRECATE_EOF is negotiated, the driver distinguishes
between
+ // EOF packets (payload <= 5) and ResultSet OK packets (payload > 5).
+ // MysqlOkPacket payload must be > 5 to avoid being misidentified as
EOF.
+ // Payload: 0x00(1) + affected_rows(1) + last_insert_id(1) + status(2)
+ warnings(2) + info_len(1) = 8
+ MysqlOkPacket packet = new MysqlOkPacket(new QueryState());
+ MysqlSerializer serializer = MysqlSerializer.newInstance(capability);
+ packet.writeTo(serializer);
+
+ ByteBuffer buffer = serializer.toByteBuffer();
+ int payloadLength = buffer.remaining();
+ Assert.assertTrue("OK packet payload should be > 5 for
CLIENT_DEPRECATE_EOF compatibility, got: "
+ + payloadLength, payloadLength > 5);
+ }
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/mysql/MysqlResultSetEndPacketTest.java
b/fe/fe-core/src/test/java/org/apache/doris/mysql/MysqlResultSetEndPacketTest.java
new file mode 100644
index 00000000000..8dc0d18877f
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/mysql/MysqlResultSetEndPacketTest.java
@@ -0,0 +1,120 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.mysql;
+
+import org.apache.doris.qe.QueryState;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+
+public class MysqlResultSetEndPacketTest {
+ private MysqlCapability capability;
+
+ @Before
+ public void setUp() {
+ capability = new
MysqlCapability(MysqlCapability.Flag.CLIENT_PROTOCOL_41.getFlagBit());
+ }
+
+ @Test
+ public void testWriteHeader() {
+ // The ResultSet OK packet must start with 0xFE (same as EOF)
+ MysqlResultSetEndPacket packet = new MysqlResultSetEndPacket(new
QueryState());
+ MysqlSerializer serializer = MysqlSerializer.newInstance(capability);
+ packet.writeTo(serializer);
+
+ ByteBuffer buffer = serializer.toByteBuffer();
+
+ // assert header: 0xFE
+ Assert.assertEquals(0xFE, MysqlProto.readInt1(buffer));
+ }
+
+ @Test
+ public void testWriteFields() {
+ // Verify all fields are written correctly
+ MysqlResultSetEndPacket packet = new MysqlResultSetEndPacket(new
QueryState());
+ MysqlSerializer serializer = MysqlSerializer.newInstance(capability);
+ packet.writeTo(serializer);
+
+ ByteBuffer buffer = serializer.toByteBuffer();
+
+ // header: 0xFE
+ Assert.assertEquals(0xFE, MysqlProto.readInt1(buffer));
+
+ // affected_rows: int<lenenc> = 0
+ Assert.assertEquals(0, MysqlProto.readVInt(buffer));
+
+ // last_insert_id: int<lenenc> = 0
+ Assert.assertEquals(0, MysqlProto.readVInt(buffer));
+
+ // status_flags: int<2> = 0
+ Assert.assertEquals(0, MysqlProto.readInt2(buffer));
+
+ // warnings: int<2> = 0
+ Assert.assertEquals(0, MysqlProto.readInt2(buffer));
+
+ // info: string<lenenc> (empty, length = 0)
+ Assert.assertEquals(0, MysqlProto.readVInt(buffer));
+
+ // no remaining bytes
+ Assert.assertEquals(0, buffer.remaining());
+ }
+
+ @Test
+ public void testPayloadSizeGreaterThan5() {
+ // When CLIENT_DEPRECATE_EOF is negotiated, the MySQL driver uses
isResultSetOKPacket()
+ // to detect end-of-result-set. This method requires:
+ // (byteBuffer[0] & 0xff) == 0xFE && payloadLength > 5
+ // The traditional EOF packet has payloadLength == 5, so the ResultSet
OK packet
+ // MUST have payloadLength > 5 to be correctly identified.
+ MysqlResultSetEndPacket packet = new MysqlResultSetEndPacket(new
QueryState());
+ MysqlSerializer serializer = MysqlSerializer.newInstance(capability);
+ packet.writeTo(serializer);
+
+ ByteBuffer buffer = serializer.toByteBuffer();
+ int payloadLength = buffer.remaining();
+
+ // Payload: 0xFE(1) + affected_rows(1) + last_insert_id(1) + status(2)
+ warnings(2) + info(1) = 8
+ Assert.assertTrue("ResultSet OK packet payload must be > 5 for
isResultSetOKPacket(), got: "
+ + payloadLength, payloadLength > 5);
+ }
+
+ @Test
+ public void testDiffersFromEofPacket() {
+ // A traditional EOF packet has exactly 5 bytes payload.
+ // The ResultSet OK packet must have > 5 bytes to be distinguishable.
+ MysqlEofPacket eofPacket = new MysqlEofPacket(new QueryState());
+ MysqlSerializer eofSerializer =
MysqlSerializer.newInstance(capability);
+ eofPacket.writeTo(eofSerializer);
+ int eofPayloadLength = eofSerializer.toByteBuffer().remaining();
+
+ MysqlResultSetEndPacket rsEndPacket = new MysqlResultSetEndPacket(new
QueryState());
+ MysqlSerializer rsEndSerializer =
MysqlSerializer.newInstance(capability);
+ rsEndPacket.writeTo(rsEndSerializer);
+ int rsEndPayloadLength = rsEndSerializer.toByteBuffer().remaining();
+
+ // EOF payload should be <= 5
+ Assert.assertTrue("EOF packet payload should be <= 5, got: " +
eofPayloadLength,
+ eofPayloadLength <= 5);
+ // ResultSet OK payload should be > 5
+ Assert.assertTrue("ResultSet OK packet payload should be > 5, got: " +
rsEndPayloadLength,
+ rsEndPayloadLength > 5);
+ }
+}
diff --git a/gensrc/thrift/FrontendService.thrift
b/gensrc/thrift/FrontendService.thrift
index 82a2d40454f..7e520751ee1 100644
--- a/gensrc/thrift/FrontendService.thrift
+++ b/gensrc/thrift/FrontendService.thrift
@@ -402,6 +402,8 @@ struct TMasterOpRequest {
// temporary table
1002: optional string sessionId
+ // propagate client's CLIENT_DEPRECATE_EOF capability for proxy forwarding
+ 1003: optional bool clientDeprecatedEOF
}
struct TColumnDefinition {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]