Changeset: 5244af37a8e2 for monetdb-java
URL: https://dev.monetdb.org/hg/monetdb-java/rev/5244af37a8e2
Modified Files:
        src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java
        tests/JDBC_API_Tester.java
Branch: default
Log Message:

Counting only parameter markers to determine the fetchSize is not sufficiant 
for all Prepared statements.
For instance prepared queries which return many columns (larger than fetchSize) 
also need to work without error.
Extended test with prepared select statements with many columns and parameters 
and improved the implementation to work correctly.


diffs (270 lines):

diff --git a/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java 
b/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java
--- a/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java
+++ b/src/main/java/org/monetdb/jdbc/MonetPreparedStatement.java
@@ -125,27 +125,26 @@ public class MonetPreparedStatement
                if (prepareQuery == null)
                        throw new SQLException("Missing SQL statement", 
"M1M05");
 
-               // Count the number of parameter markers (question marks) in 
the SQL string.
-               int countParamMarkers = 0;
-               int pos = prepareQuery.indexOf('?');
-               while (pos >= 0) {
-                       countParamMarkers++;
-                       pos = prepareQuery.indexOf('?', pos+1);
-               }
-               // When it is larger than the current fetchsize, increase it
-               // so all the parameters can be read into one DataBlockResponse.
-               // see also: https://github.com/MonetDB/MonetDB/issues/7337
-               int currentFetchSize = super.getFetchSize();
-               if (currentFetchSize == 0) {
-                       currentFetchSize = connection.getDefaultFetchSize();
-               }
-               if (countParamMarkers > currentFetchSize) {
-                       super.setFetchSize(countParamMarkers);
-               }
+               /**
+                * For a PREPARE statement the server sends back a result set
+                * with info on all the parameters and/or result columns of a
+                * parameterized query. This result set however needs to be
+                * read in one DataBlockResponse due to protocol limitations.
+                * This requires the fetchSize needs to be set large enough
+                * to retrieve all rows in one go, else we get eror:
+                * <pre>resultBlocks[1] should have been fetched by now</pre>
+                * See also: https://github.com/MonetDB/MonetDB/issues/7337
+                */
+               final int originalFetchSize = getFetchSize();
+               // increase the fetchSize temporarily before sending the 
PREPARE statement
+               // we can not use -1 (unlimited), so use a high value.
+               setFetchSize(50*1000);
 
                if (!super.execute("PREPARE " + prepareQuery))
                        throw new SQLException("Unexpected server response", 
"M0M10");
 
+               setFetchSize(originalFetchSize);
+
                sqlStatement = prepareQuery;
                // cheat a bit to get the ID and the number of columns
                id = ((MonetConnection.ResultSetResponse)header).id;
diff --git a/tests/JDBC_API_Tester.java b/tests/JDBC_API_Tester.java
--- a/tests/JDBC_API_Tester.java
+++ b/tests/JDBC_API_Tester.java
@@ -5970,10 +5970,11 @@ final public class JDBC_API_Tester {
                final int NR_COLUMNS = nrParams;
                final StringBuilder sql = new StringBuilder(100 + (NR_COLUMNS * 
25));
 
+               int col;
                Statement stmt = null;
-               PreparedStatement pstmt = null;
                try {
-                       int col;
+                       stmt = con.createStatement();
+                       sb.append("0. fetch size of new statement: 
").append(stmt.getFetchSize()).append("\n");
 
                        // construct the Create Table SQL text
                        sql.append("CREATE TABLE t7337 (ID BIGINT 
AUTO_INCREMENT PRIMARY KEY, ");
@@ -5983,12 +5984,18 @@ final public class JDBC_API_Tester {
                        sql.append("column").append(col).append(" TIMESTAMP);");
 
                        sb.append("1. create table with 
").append(NR_COLUMNS+2).append(" columns, sql has length: 
").append(sql.length()).append("\n");
-                       stmt = con.createStatement();
                        int ret = stmt.executeUpdate(sql.toString());
                        sb.append("2. table created. ret = 
").append(ret).append("\n");
                        stmt.close();
                        stmt = null;
-
+               } catch (SQLException e) {
+                       sb.append("FAILED: 
").append(e.getMessage()).append("\n");
+               }
+               closeStmtResSet(stmt, null);
+
+               // test: PREPARE INSERT INTO with many parameters
+               PreparedStatement pstmt = null;
+               try {
                        // construct the Insert Into Table SQL text, first 
without any parameter makers
                        sql.setLength(0);       // clear the sql buffer
                        sql.append("INSERT INTO t7337 \n(");
@@ -6005,6 +6012,7 @@ final public class JDBC_API_Tester {
                        sb.append("3. prepare insert statement (no params), sql 
has length: ").append(sql.length()).append("\n");
                        pstmt = con.prepareStatement(sql.toString());
                        if (pstmt != null) {
+                               sb.append("   fetch size after prepare 1: 
").append(pstmt.getFetchSize()).append("\n");
                                ParameterMetaData pmd = 
pstmt.getParameterMetaData();
                                sb.append("   pmd. 
").append(pmd.getParameterCount()).append(" parameters\n");
                                sb.append("4. execute prepared insert\n");
@@ -6028,6 +6036,7 @@ final public class JDBC_API_Tester {
                        sb.append("7. prepare insert statement (with params), 
sql has length: ").append(sql.length()).append("\n");
                        pstmt = con.prepareStatement(sql.toString());
                        if (pstmt != null) {
+                               sb.append("   fetch size after prepare 2: 
").append(pstmt.getFetchSize()).append("\n");
                                ParameterMetaData pmd = 
pstmt.getParameterMetaData();
                                sb.append("   pmd. 
").append(pmd.getParameterCount()).append(" parameters\n");
                                sb.append("8. bind parameters\n");
@@ -6049,31 +6058,157 @@ final public class JDBC_API_Tester {
                }
                closeStmtResSet(pstmt, null);
 
-               // cleanup table
-               try {
-                       stmt = con.createStatement();
-                       stmt.executeUpdate("DROP TABLE IF EXISTS t7337;");
-               } catch (SQLException e) {
-                       sb.append("FAILED: 
").append(e.getMessage()).append("\n");
-               }
-               closeStmtResSet(stmt, null);
-
-               compareExpectedOutput("Bug_PrepStmtManyParams_7337(" + 
(nrParams) + ")",
+               compareExpectedOutput("Bug_PrepStmtManyParams_7337(" + nrParams 
+ ")",
+                       "0. fetch size of new statement: 250\n" +
                        "1. create table with " + (NR_COLUMNS+2) + " columns, 
sql has length: " + ((NR_COLUMNS * 23) -29) + "\n" +
                        "2. table created. ret = -2\n" +
                        "3. prepare insert statement (no params), sql has 
length: " + ((NR_COLUMNS * 25) -53) + "\n" +
+                       "   fetch size after prepare 1: 250\n" +
                        "   pmd. 0 parameters\n" +
                        "4. execute prepared insert\n" +
                        "5. first execute returned: 1\n" +
                        "5. second execute returned: 1\n" +
                        "6. inserted data committed\n" +
                        "7. prepare insert statement (with params), sql has 
length: " + ((nrParams * 12) -53) + "\n" +
-                       "   pmd. " + (nrParams) + " parameters\n" +
+                       "   fetch size after prepare 2: 250\n" +
+                       "   pmd. " + nrParams + " parameters\n" +
                        "8. bind parameters\n" +
                        "9. execute prepared insert with parameters\n" +
                        "10. first execute returned: 1\n" +
                        "10. second execute returned: 1\n" +
                        "11. inserted data committed\n");
+
+               // test also: PREPARE SELECT * FROM .. without and with many 
parameters
+               sb.setLength(0);        // clear the output log buffer
+               ResultSet rs = null;
+               try {
+                       // construct the Select SQL text, first without any 
parameter makers
+                       sql.setLength(0);       // clear the sql buffer
+                       sql.append("SELECT * FROM t7337");
+
+                       sb.append("12. prepare select statement (no params), 
sql has length: ").append(sql.length()).append("\n");
+                       pstmt = con.prepareStatement(sql.toString());
+                       if (pstmt != null) {
+                               sb.append("   fetch size after prepare 3: 
").append(pstmt.getFetchSize()).append("\n");
+                               ParameterMetaData pmd = 
pstmt.getParameterMetaData();
+                               sb.append("   pmd. 
").append(pmd.getParameterCount()).append(" parameters\n");
+                               ResultSetMetaData rsmd = pstmt.getMetaData();
+                               sb.append("   rsmd. 
").append(rsmd.getColumnCount()).append(" result columns\n");
+                               sb.append("13. execute prepared select\n");
+                               rs = pstmt.executeQuery();
+                               if (rs != null) {
+                                       rsmd = rs.getMetaData();
+                                       sb.append("14. first query execute 
succeeded. it has ").append(rsmd.getColumnCount()).append(" result columns\n");
+                                       rs.close();
+                                       rs = null;
+                               } else {
+                                       sb.append("14. first query execute 
failed to return a result\n");
+                               }
+                               // do it one more time
+                               rs = pstmt.executeQuery();
+                               if (rs != null) {
+                                       rsmd = rs.getMetaData();
+                                       sb.append("15. second query execute 
succeeded. it has ").append(rsmd.getColumnCount()).append(" result columns\n");
+                                       rs.close();
+                                       rs = null;
+                               } else {
+                                       sb.append("15. second query execute 
failed to return a result\n");
+                               }
+                               pstmt.close();
+                               pstmt = null;
+                       }
+
+                       // add the WHERE part with many parameter makers
+                       sql.append(" WHERE ");
+                       for (col = 1; col <= NR_COLUMNS; col++) {
+                               sql.append("column").append(col).append(" = ? 
AND ");
+                       }
+                       sql.append("column").append(col).append(" = 
'2022-11-16'");
+
+                       sb.append("16. prepare select statement (with params), 
sql has length: ").append(sql.length()).append("\n");
+                       pstmt = con.prepareStatement(sql.toString());
+                       if (pstmt != null) {
+                               sb.append("   fetch size after prepare 4: 
").append(pstmt.getFetchSize()).append("\n");
+                               ParameterMetaData pmd = 
pstmt.getParameterMetaData();
+                               sb.append("   pmd. 
").append(pmd.getParameterCount()).append(" parameters\n");
+                               ResultSetMetaData rsmd = pstmt.getMetaData();
+                               sb.append("   rsmd. 
").append(rsmd.getColumnCount()).append(" result columns\n");
+                               sb.append("17. bind parameters\n");
+                               for (col = 1; col <= nrParams; col++) {
+                                       pstmt.setString(col, "someMoreText");
+                               }
+                               sb.append("18. execute prepared select\n");
+                               rs = pstmt.executeQuery();
+                               if (rs != null) {
+                                       rsmd = rs.getMetaData();
+                                       sb.append("19. first query execute 
succeeded. it has ")
+                                         
.append(rsmd.getColumnCount()).append(" result columns and ");
+                                       int rows = 0;
+                                       while (rs.next())
+                                               rows++;
+                                       sb.append(rows).append(" rows\n");
+                                       rs.close();
+                                       rs = null;
+                               } else {
+                                       sb.append("19. first query execute 
failed to return a result\n");
+                               }
+                               // do it one more time
+                               sb.append("20. bind parameters\n");
+                               for (col = 1; col <= nrParams; col++) {
+                                       pstmt.setString(col, "someMoreText");
+                               }
+                               sb.append("21. execute prepared select 
again\n");
+                               rs = pstmt.executeQuery();
+                               if (rs != null) {
+                                       rsmd = rs.getMetaData();
+                                       sb.append("22. second query execute 
succeeded. it has ")
+                                         
.append(rsmd.getColumnCount()).append(" result columns and ");
+                                       int rows = 0;
+                                       while (rs.next())
+                                               rows++;
+                                       sb.append(rows).append(" rows\n");
+                                       rs.close();
+                                       rs = null;
+                               } else {
+                                       sb.append("22. second query execute 
failed to return a result\n");
+                               }
+                               pstmt.close();
+                               pstmt = null;
+                       }
+               } catch (SQLException e) {
+                       sb.append("FAILED: 
").append(e.getMessage()).append("\n");
+               }
+               closeStmtResSet(pstmt, rs);
+
+               // cleanup table
+               try {
+                       stmt = con.createStatement();
+                       stmt.executeUpdate("DROP TABLE IF EXISTS t7337;");
+                       stmt.close();
+                       stmt = null;
+               } catch (SQLException e) {
+                       sb.append("FAILED: 
").append(e.getMessage()).append("\n");
+               }
+               closeStmtResSet(stmt, null);
+
+               compareExpectedOutput("Bug_PrepStmtManyParams_7337(" + nrParams 
+ ")",
+                       "12. prepare select statement (no params), sql has 
length: 19\n" +
+                       "   fetch size after prepare 3: 250\n" +
+                       "   pmd. 0 parameters\n" +
+                       "   rsmd. " + (NR_COLUMNS+2) + " result columns\n" +
+                       "13. execute prepared select\n" +
+                       "14. first query execute succeeded. it has " + 
(NR_COLUMNS+2) + " result columns\n" +
+                       "15. second query execute succeeded. it has " + 
(NR_COLUMNS+2) + " result columns\n" +
+                       "16. prepare select statement (with params), sql has 
length: " + ((NR_COLUMNS * 18) -58) + "\n" +
+                       "   fetch size after prepare 4: 250\n" +
+                       "   pmd. " + nrParams + " parameters\n" +
+                       "   rsmd. " + (NR_COLUMNS+2) + " result columns\n" +
+                       "17. bind parameters\n" +
+                       "18. execute prepared select\n" +
+                       "19. first query execute succeeded. it has " + 
(NR_COLUMNS+2) + " result columns and 2 rows\n" +
+                       "20. bind parameters\n" +
+                       "21. execute prepared select again\n" +
+                       "22. second query execute succeeded. it has " + 
(NR_COLUMNS+2) + " result columns and 2 rows\n");
        }
 
        /**
_______________________________________________
checkin-list mailing list -- checkin-list@monetdb.org
To unsubscribe send an email to checkin-list-le...@monetdb.org

Reply via email to