From 9f1bbb8ebf5c11a290552c861f1df61206bd4834 Mon Sep 17 00:00:00 2001
From: Filipe Silva <filipe.silva@oracle.com>
Date: Sat, 4 Feb 2023 00:42:22 +0000
Subject: [PATCH] Fix for Bug#109864 (Bug#35034666), Connector/J 8.0.32 hangs
 on MySQL 5.5 with prepared statements.

Change-Id: I1830f3b459f16052868bc669e2bac29ebf6d239e
---
 CHANGES                                       |  2 +
 .../com/mysql/cj/ServerPreparedQuery.java     |  9 +--
 .../regression/StatementRegressionTest.java   | 73 +++++++++++++++----
 3 files changed, 66 insertions(+), 18 deletions(-)

Index: mysql-connector-j-8.0.32/src/main/core-impl/java/com/mysql/cj/ServerPreparedQuery.java
===================================================================
--- mysql-connector-j-8.0.32.orig/src/main/core-impl/java/com/mysql/cj/ServerPreparedQuery.java
+++ mysql-connector-j-8.0.32/src/main/core-impl/java/com/mysql/cj/ServerPreparedQuery.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2022, Oracle and/or its affiliates.
+ * Copyright (c) 2017, 2023, Oracle and/or its affiliates.
  *
  * This program is free software; you can redistribute it and/or modify it under
  * the terms of the GNU General Public License, version 2.0, as published by the
@@ -113,7 +113,6 @@ public class ServerPreparedQuery extends
     }
 
     /**
-     * 
      * @param sql
      *            query string
      * @throws IOException
@@ -160,9 +159,9 @@ public class ServerPreparedQuery extends
             // Read in the result set column information
             if (fieldCount > 0) {
                 this.resultFields = this.session.getProtocol().read(ColumnDefinition.class, new ColumnDefinitionFactory(fieldCount, null));
-            }
-            if (checkEOF && this.session.getProtocol().probeMessage(null).isEOFPacket()) { // Skip the following EOF packet.
-                this.session.getProtocol().skipPacket();
+                if (checkEOF && this.session.getProtocol().probeMessage(null).isEOFPacket()) { // Skip the following EOF packet.
+                    this.session.getProtocol().skipPacket();
+                }
             }
         }
     }
Index: mysql-connector-j-8.0.32/src/test/java/testsuite/regression/StatementRegressionTest.java
===================================================================
--- mysql-connector-j-8.0.32.orig/src/test/java/testsuite/regression/StatementRegressionTest.java
+++ mysql-connector-j-8.0.32/src/test/java/testsuite/regression/StatementRegressionTest.java
@@ -8360,14 +8360,14 @@ public class StatementRegressionTest ext
             // A. Test Statement.execute() results
             createTable("testBug71672", tableDDL);
             for (int i = 0; i < queries.length; i++) {
-                testBug71672Statement(testStep, testConn, queries[i], -1, expectedGenKeys[i]);
+                checkStatementForTestBug71672(testStep, testConn, queries[i], -1, expectedGenKeys[i]);
             }
             dropTable("testBug71672");
 
             // B. Test Statement.executeUpdate() results
             createTable("testBug71672", tableDDL);
             for (int i = 0; i < queries.length; i++) {
-                testBug71672Statement(testStep, testConn, queries[i], expectedUpdCount[i], expectedGenKeys[i]);
+                checkStatementForTestBug71672(testStep, testConn, queries[i], expectedUpdCount[i], expectedGenKeys[i]);
             }
             dropTable("testBug71672");
 
@@ -8397,14 +8397,14 @@ public class StatementRegressionTest ext
             // D. Test PreparedStatement.execute() results
             createTable("testBug71672", tableDDL);
             for (int i = 0; i < queries.length; i++) {
-                testBug71672PreparedStatement(testStep, testConn, queries[i], -1, expectedGenKeys[i]);
+                checkPreparedStatementForTestBug71672(testStep, testConn, queries[i], -1, expectedGenKeys[i]);
             }
             dropTable("testBug71672");
 
             // E. Test PreparedStatement.executeUpdate() results
             createTable("testBug71672", tableDDL);
             for (int i = 0; i < queries.length; i++) {
-                testBug71672PreparedStatement(testStep, testConn, queries[i], expectedUpdCount[i], expectedGenKeys[i]);
+                checkPreparedStatementForTestBug71672(testStep, testConn, queries[i], expectedUpdCount[i], expectedGenKeys[i]);
             }
             dropTable("testBug71672");
 
@@ -8462,22 +8462,22 @@ public class StatementRegressionTest ext
 
             // A. Test Statement.execute() results
             createTable("testBug71672", tableDDL);
-            testBug71672Statement(testStep, testConn, allQueries, -1, expectedGenKeysMultiQueries);
+            checkStatementForTestBug71672(testStep, testConn, allQueries, -1, expectedGenKeysMultiQueries);
             dropTable("testBug71672");
 
             // B. Test Statement.executeUpdate() results
             createTable("testBug71672", tableDDL);
-            testBug71672Statement(testStep, testConn, allQueries, 3, expectedGenKeysMultiQueries);
+            checkStatementForTestBug71672(testStep, testConn, allQueries, 3, expectedGenKeysMultiQueries);
             dropTable("testBug71672");
 
             // C. Test PreparedStatement.execute() results
             createTable("testBug71672", tableDDL);
-            testBug71672PreparedStatement(testStep, testConn, allQueries, -1, expectedGenKeysMultiQueries);
+            checkPreparedStatementForTestBug71672(testStep, testConn, allQueries, -1, expectedGenKeysMultiQueries);
             dropTable("testBug71672");
 
             // D. Test PreparedStatement.executeUpdate() results
             createTable("testBug71672", tableDDL);
-            testBug71672PreparedStatement(testStep, testConn, allQueries, 3, expectedGenKeysMultiQueries);
+            checkPreparedStatementForTestBug71672(testStep, testConn, allQueries, 3, expectedGenKeysMultiQueries);
             dropTable("testBug71672");
 
             testConn.close();
@@ -8495,7 +8495,8 @@ public class StatementRegressionTest ext
      * @param expectedKeys
      * @throws SQLException
      */
-    public void testBug71672Statement(int testStep, Connection testConn, String query, int expectedUpdateCount, int[] expectedKeys) throws SQLException {
+    private void checkStatementForTestBug71672(int testStep, Connection testConn, String query, int expectedUpdateCount, int[] expectedKeys)
+            throws SQLException {
         Statement testStmt = testConn.createStatement();
 
         if (expectedUpdateCount < 0) {
@@ -8525,7 +8526,7 @@ public class StatementRegressionTest ext
      * @param expectedKeys
      * @throws SQLException
      */
-    public void testBug71672PreparedStatement(int testStep, Connection testConn, String query, int expectedUpdateCount, int[] expectedKeys)
+    private void checkPreparedStatementForTestBug71672(int testStep, Connection testConn, String query, int expectedUpdateCount, int[] expectedKeys)
             throws SQLException {
         PreparedStatement testPStmt = testConn.prepareStatement(query);
 
@@ -12847,7 +12848,7 @@ public class StatementRegressionTest ext
     }
 
     /**
-     * Tests for Bug#99604 (Bug#31612628), Add support to row alias on INSERT... ON DUPLICATE KEY UPDATE on batch mode.
+     * Tests fix for Bug#99604 (Bug#31612628), Add support to row alias on INSERT... ON DUPLICATE KEY UPDATE on batch mode.
      * Resolved by fix for Bug#106240 (33781440), StringIndexOutOfBoundsException when VALUE is at the end of the query.
      * 
      * @throws Exception
@@ -12945,4 +12946,50 @@ public class StatementRegressionTest ext
             testConn.close();
         } while ((useSPS = !useSPS) || (cachePS = !cachePS));
     }
+
+    /**
+     * Tests fix for Bug#109864 (Bug#35034666), Connector/J 8.0.32 hangs on MySQL 5.5 with prepared statements.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testBug109864() throws Exception {
+        createTable("testBug109864", "(id INT)");
+
+        boolean useSPS = false;
+
+        do {
+            Properties props = new Properties();
+            props.setProperty(PropertyKey.sslMode.getKeyName(), SslMode.DISABLED.name());
+            props.setProperty(PropertyKey.allowPublicKeyRetrieval.getKeyName(), "true");
+            props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), Boolean.toString(useSPS));
+            props.setProperty(PropertyKey.socketTimeout.getKeyName(), "5000");
+
+            Connection testConn = getConnectionWithProps(props);
+
+            this.pstmt = testConn.prepareStatement("SELECT ?");  // Both column and parameter definition blocks in Prepare response.
+            this.pstmt.setInt(1, 1);
+            this.rs = this.pstmt.executeQuery();
+            assertTrue(this.rs.next());
+            assertFalse(this.rs.next());
+            this.pstmt.close();
+
+            this.pstmt = testConn.prepareStatement("SELECT 1");  // No parameter definition block in Stmt Prepare response.
+            this.rs = this.pstmt.executeQuery();
+            assertTrue(this.rs.next());
+            assertFalse(this.rs.next());
+
+            // Prior to this fix preparing this statement would hang indefinitely with server-prepared statements.
+            this.pstmt = testConn.prepareStatement("INSERT INTO testBug109864 VALUES (?)"); // No column definition block in Prepare response.
+            this.pstmt.setInt(1, 1);
+            assertEquals(1, this.pstmt.executeUpdate());
+            this.pstmt.close();
+
+            this.pstmt = testConn.prepareStatement("INSERT INTO testBug109864 VALUES (2)");  // No column or parameter definition blocks in Prepare response.
+            assertEquals(1, this.pstmt.executeUpdate());
+            this.pstmt.close();
+
+            testConn.close();
+        } while (useSPS = !useSPS);
+    }
 }
