Skip to content

Commit 10aa4c9

Browse files
committed
Fix for Bug#97269 (30438500), POSSIBLE BUG IN
COM.MYSQL.CJ.XDEVAPI.STREAMINGDOCRESULTBUILDER.
1 parent 1198c8c commit 10aa4c9

File tree

6 files changed

+123
-23
lines changed

6 files changed

+123
-23
lines changed

CHANGES

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
Version 8.0.26
55

6+
- Fix for Bug#97269 (30438500), POSSIBLE BUG IN COM.MYSQL.CJ.XDEVAPI.STREAMINGDOCRESULTBUILDER.
7+
68
- Fix for Bug#103303 (32766143), JAVA.LANG.CLASSCASTEXCEPTION WHEN INSERTING BLOB WITH SERVER PREPARED STATEMENT.
79

810
- WL#14205, Support query attributes.

src/main/protocol-impl/java/com/mysql/cj/protocol/x/Notice.java

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2020, Oracle and/or its affiliates.
2+
* Copyright (c) 2018, 2021, Oracle and/or its affiliates.
33
*
44
* This program is free software; you can redistribute it and/or modify it under
55
* the terms of the GNU General Public License, version 2.0, as published by the
@@ -50,23 +50,21 @@ public class Notice implements ProtocolEntity {
5050

5151
public static Notice getInstance(XMessage message) {
5252
Frame notice = (Frame) message.getMessage();
53-
if (notice.getScope() != Frame.Scope.GLOBAL) { // TODO should we handle global notices somehow? What frame types are applicable there?
54-
switch (notice.getType()) {
55-
case Frame.Type.WARNING_VALUE:
56-
return new XWarning(notice);
57-
58-
case Frame.Type.SESSION_VARIABLE_CHANGED_VALUE:
59-
return new XSessionVariableChanged(notice);
60-
61-
case Frame.Type.SESSION_STATE_CHANGED_VALUE:
62-
return new XSessionStateChanged(notice);
63-
64-
case Frame.Type.GROUP_REPLICATION_STATE_CHANGED_VALUE:
65-
// TODO
66-
break;
67-
default:
68-
break;
69-
}
53+
switch (notice.getType()) {
54+
case Frame.Type.WARNING_VALUE:
55+
return new XWarning(notice);
56+
57+
case Frame.Type.SESSION_VARIABLE_CHANGED_VALUE:
58+
return new XSessionVariableChanged(notice);
59+
60+
case Frame.Type.SESSION_STATE_CHANGED_VALUE:
61+
return new XSessionStateChanged(notice);
62+
63+
case Frame.Type.GROUP_REPLICATION_STATE_CHANGED_VALUE:
64+
// TODO
65+
break;
66+
default:
67+
break;
7068
}
7169
return new Notice(notice);
7270
}

src/main/protocol-impl/java/com/mysql/cj/protocol/x/XProtocol.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -698,11 +698,19 @@ public void drainRows() {
698698
}
699699

700700
public ColumnDefinition readMetadata() {
701+
return readMetadata(null);
702+
}
703+
704+
public ColumnDefinition readMetadata(Consumer<Notice> noticeConsumer) {
701705
try {
706+
List<Notice> notices;
702707
List<ColumnMetaData> fromServer = new LinkedList<>();
703708
do { // use this construct to read at least one
704-
fromServer.add((ColumnMetaData) this.reader.readMessage(null, ServerMessages.Type.RESULTSET_COLUMN_META_DATA_VALUE).getMessage());
705-
// TODO put notices somewhere like it's done eg. in readStatementExecuteOk(): builder.addNotice(this.reader.read(Frame.class));
709+
XMessage mess = this.reader.readMessage(null, ServerMessages.Type.RESULTSET_COLUMN_META_DATA_VALUE);
710+
if (noticeConsumer != null && (notices = mess.getNotices()) != null) {
711+
notices.stream().forEach(noticeConsumer::accept);
712+
}
713+
fromServer.add((ColumnMetaData) mess.getMessage());
706714
} while (((SyncMessageReader) this.reader).getNextNonNoticeMessageType() == ServerMessages.Type.RESULTSET_COLUMN_META_DATA_VALUE);
707715
ArrayList<Field> metadata = new ArrayList<>(fromServer.size());
708716
@SuppressWarnings("unchecked")

src/main/user-impl/java/com/mysql/cj/xdevapi/StreamingDocResultBuilder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2020, Oracle and/or its affiliates.
2+
* Copyright (c) 2019, 2021, Oracle and/or its affiliates.
33
*
44
* This program is free software; you can redistribute it and/or modify it under
55
* the terms of the GNU General Public License, version 2.0, as published by the
@@ -70,6 +70,7 @@ public boolean addProtocolEntity(ProtocolEntity entity) {
7070

7171
} else if (entity instanceof Notice) {
7272
this.statementExecuteOkBuilder.addProtocolEntity(entity);
73+
return false;
7374
}
7475

7576
if (this.metadata == null) {

src/main/user-impl/java/com/mysql/cj/xdevapi/StreamingSqlResultBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2020, Oracle and/or its affiliates.
2+
* Copyright (c) 2019, 2021, Oracle and/or its affiliates.
33
*
44
* This program is free software; you can redistribute it and/or modify it under
55
* the terms of the GNU General Public License, version 2.0, as published by the
@@ -102,7 +102,7 @@ public boolean addProtocolEntity(ProtocolEntity entity) {
102102
});
103103
this.lastEntity = null;
104104
} else {
105-
cd = this.protocol.readMetadata();
105+
cd = this.protocol.readMetadata(this.statementExecuteOkBuilder::addProtocolEntity);
106106
}
107107
return new SqlSingleResult(cd, this.protocol.getServerSession().getDefaultTimeZone(), new XProtocolRowInputStream(cd, this.protocol, (n) -> {
108108
this.statementExecuteOkBuilder.addProtocolEntity(n);

src/test/java/testsuite/x/devapi/SessionTest.java

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,12 @@
8888
import com.mysql.cj.xdevapi.ClientFactory;
8989
import com.mysql.cj.xdevapi.ClientImpl;
9090
import com.mysql.cj.xdevapi.ClientImpl.PooledXProtocol;
91+
import com.mysql.cj.xdevapi.Collection;
92+
import com.mysql.cj.xdevapi.DbDoc;
93+
import com.mysql.cj.xdevapi.DocResult;
9194
import com.mysql.cj.xdevapi.FindStatement;
95+
import com.mysql.cj.xdevapi.JsonNumber;
96+
import com.mysql.cj.xdevapi.JsonString;
9297
import com.mysql.cj.xdevapi.Row;
9398
import com.mysql.cj.xdevapi.RowResult;
9499
import com.mysql.cj.xdevapi.Schema;
@@ -2566,4 +2571,90 @@ public void testExecAsyncNegative() throws Exception {
25662571
sqlUpdate("drop table if exists testExecAsyncNegative");
25672572
}
25682573
}
2574+
2575+
/**
2576+
* Test fix for Bug#97269 (30438500), POSSIBLE BUG IN COM.MYSQL.CJ.XDEVAPI.STREAMINGDOCRESULTBUILDER.
2577+
*
2578+
* @throws Exception
2579+
*/
2580+
@Test
2581+
public void testBug97269() throws Exception {
2582+
assumeTrue(this.isSetForXTests);
2583+
2584+
Session sess = null;
2585+
try {
2586+
String message1 = "W1";
2587+
String message2 = "W2";
2588+
2589+
// create notice message buffers
2590+
Frame.Builder notice1 = Frame.newBuilder().setScope(Frame.Scope.LOCAL).setType(Frame.Type.WARNING_VALUE)
2591+
.setPayload(com.mysql.cj.x.protobuf.MysqlxNotice.Warning.newBuilder().setCode(MysqlErrorNumbers.ER_BAD_DB_ERROR).setMsg(message1).build()
2592+
.toByteString());
2593+
Frame.Builder notice2 = Frame.newBuilder().setScope(Frame.Scope.GLOBAL).setType(Frame.Type.WARNING_VALUE)
2594+
.setPayload(com.mysql.cj.x.protobuf.MysqlxNotice.Warning.newBuilder().setCode(MysqlErrorNumbers.ER_BAD_DB_ERROR).setMsg(message2).build()
2595+
.toByteString());
2596+
2597+
byte[] notice1Bytes = makeNoticeBytes(notice1.build());
2598+
byte[] notice2Bytes = makeNoticeBytes(notice2.build());
2599+
int size = notice1Bytes.length + notice2Bytes.length;
2600+
byte[] noticesBytes = new byte[size];
2601+
System.arraycopy(notice1Bytes, 0, noticesBytes, 0, notice1Bytes.length);
2602+
System.arraycopy(notice2Bytes, 0, noticesBytes, notice1Bytes.length, notice2Bytes.length);
2603+
2604+
InjectedSocketFactory.flushAllStaticData();
2605+
2606+
String url = this.baseUrl + (this.baseUrl.contains("?") ? "" : "?")
2607+
+ makeParam(PropertyKey.socketFactory, InjectedSocketFactory.class.getName(), !this.baseUrl.contains("?") || this.baseUrl.endsWith("?"))
2608+
+ makeParam(PropertyKey.xdevapiSslMode, XdevapiSslMode.DISABLED.toString())
2609+
+ makeParam(PropertyKey.xdevapiCompression, Compression.DISABLED.toString())
2610+
// to allow injection between result rows
2611+
+ makeParam(PropertyKey.useReadAheadInput, "false");
2612+
2613+
sess = this.fact.getSession(url);
2614+
SocketFactory sf = ((SessionImpl) sess).getSession().getProtocol().getSocketConnection().getSocketFactory();
2615+
assertTrue(InjectedSocketFactory.class.isAssignableFrom(sf.getClass()));
2616+
2617+
Collection collection = sess.getDefaultSchema().createCollection("testBug97269");
2618+
collection.add("{\"_id\":\"the_id\",\"g\":1}").execute();
2619+
2620+
// StreamingDocResultBuilder
2621+
InjectedSocketFactory.injectedBuffer = noticesBytes;
2622+
DocResult docs = collection.find().fields("$._id as _id, $.g as g, 1 + 1 as q").execute();
2623+
DbDoc doc = docs.next();
2624+
assertEquals("the_id", ((JsonString) doc.get("_id")).getString());
2625+
assertEquals(new Integer(1), ((JsonNumber) doc.get("g")).getInteger());
2626+
assertEquals(new Integer(2), ((JsonNumber) doc.get("q")).getInteger());
2627+
2628+
int cnt = 0;
2629+
for (Iterator<Warning> warn = docs.getWarnings(); warn.hasNext();) {
2630+
Warning w = warn.next();
2631+
if (w.getMessage().equals(message1) || w.getMessage().equals(message2)) {
2632+
cnt++;
2633+
}
2634+
}
2635+
assertEquals(2, cnt);
2636+
2637+
InjectedSocketFactory.flushAllStaticData();
2638+
InjectedSocketFactory.injectedBuffer = noticesBytes;
2639+
2640+
SqlResult rs1 = sess.sql("select 1").execute();
2641+
assertEquals(1, rs1.fetchOne().getInt(0));
2642+
cnt = 0;
2643+
for (Iterator<Warning> warn = rs1.getWarnings(); warn.hasNext();) {
2644+
Warning w = warn.next();
2645+
if (w.getMessage().equals(message1) || w.getMessage().equals(message2)) {
2646+
cnt++;
2647+
}
2648+
}
2649+
assertEquals(2, cnt);
2650+
2651+
} finally {
2652+
InjectedSocketFactory.flushAllStaticData();
2653+
dropCollection("testBug97269");
2654+
if (sess != null) {
2655+
sess.close();
2656+
}
2657+
}
2658+
2659+
}
25692660
}

0 commit comments

Comments
 (0)