Changeset: ab562d1c50f7 for MonetDB
URL: http://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=ab562d1c50f7
Modified Files:
        java/ChangeLog
        java/src/nl/cwi/monetdb/jdbc/MonetConnection.java
        java/src/nl/cwi/monetdb/jdbc/MonetStatement.java
Branch: default
Log Message:

jdbc: close PreparedStatements and ResultSets on schema updates

Since the SQL server throws away all handles for PreparedStatements and
ResultSets upon schema updates, we have to make this visible for the
JDBC user.  Drivers before this version simply get weird errors,
caused by missing, invalid or incompatible handles.


diffs (209 lines):

diff --git a/java/ChangeLog b/java/ChangeLog
--- a/java/ChangeLog
+++ b/java/ChangeLog
@@ -2,6 +2,9 @@
 # This file is updated with Maddlog
 
 * Fri Oct 14 2011 Fabian Groffen <[email protected]>
+- Schema updates now close PreparedStatements and ResultSets that still
+  need the server, to make behaviour of the server explicit to the client.
+  When this happens, a message is put on the Connection's warning stack.
 - The embedded monet instance capability of MonetConnection was removed.
 - Bump JDBC version to 2.0 (Liberica).  JDBC now implements JDBC4 which
   makes it possible to be built with Java 6 and up.  Java 5 and before
diff --git a/java/src/nl/cwi/monetdb/jdbc/MonetConnection.java 
b/java/src/nl/cwi/monetdb/jdbc/MonetConnection.java
--- a/java/src/nl/cwi/monetdb/jdbc/MonetConnection.java
+++ b/java/src/nl/cwi/monetdb/jdbc/MonetConnection.java
@@ -48,9 +48,6 @@ import nl.cwi.monetdb.mcl.parser.*;
  * <br /><br />
  * The current state of this connection is that it nearly implements the
  * whole Connection interface.<br />
- * Additionally, the static method getEmbeddedInstanceConnection()
- * provides a Connection for embedded situations, where an embedded
- * Mserver is started and used.
  *
  * @author Fabian Groffen <[email protected]>
  * @version 1.2
@@ -66,7 +63,7 @@ public class MonetConnection extends Mon
        private final String username;
        /** The password to use when authenticating */
        private final String password;
-       /** A connection to Mserver using a TCP socket */
+       /** A connection to mserver5 using a TCP socket */
        private final MapiSocket server;
        /** The Reader from the server */
        private final BufferedMCLReader in;
@@ -1785,6 +1782,31 @@ public class MonetConnection extends Mon
                }
 
                /**
+                * Close this Response if it still needs some data from the
+                * server.  This method is intended for the situation where the
+                * server invalidates all handles.
+                *
+                * @return true if this Response was closed, false otherwise
+                */
+               boolean closeIfOutstandingResults() {
+                       int block = ((tuplecount + 1) - blockOffset) / 
cacheSize;
+                       if (resultBlocks[block] == null &&
+                                       parent.rstype == 
ResultSet.TYPE_FORWARD_ONLY)
+                       {
+                               close();
+                               return(true);
+                       } else if (parent.rstype != 
ResultSet.TYPE_FORWARD_ONLY) {
+                               for (int i = block; i >= 0; i--) {
+                                       if (resultBlocks[i] == null) {
+                                               close();
+                                               return(true);
+                                       }
+                               }
+                       }
+                       return(false);
+               }
+
+               /**
                 * Closes this Response by sending an Xclose to the server 
indicating
                 * that the result can be closed at the server side as well.
                 */
@@ -2007,13 +2029,30 @@ public class MonetConnection extends Mon
         * <tt>&amp;4 (t|f)</tt>
         */
        // {{{ AutoCommitResponse class implementation
-       class AutoCommitResponse extends SchemaResponse {
+       class AutoCommitResponse implements Response {
+               public final int state = Statement.SUCCESS_NO_INFO;
                public final boolean autocommit;
                
                public AutoCommitResponse(boolean ac) {
                        // fill the blank final
                        this.autocommit = ac;
                }
+
+               public String addLine(String line, int linetype) {
+                       return("Header lines are not supported for a 
AutoCommitResponse");
+               }
+
+               public boolean wantsMore() {
+                       return(false);
+               }
+
+               public void complete() {
+                       // empty, because there is nothing to check
+               }
+
+               public void close() {
+                       // nothing to do here...
+               }
        }
        // }}}
 
@@ -2123,6 +2162,27 @@ public class MonetConnection extends Mon
                }
 
                /**
+                * Close all ResultSetResponses that still need the server for
+                * obtaining all results.  This is intended for when the server
+                * invalidates all open handles.
+                *
+                * @return true when ResultSets were closed, false otherwise
+                */
+               boolean closeUnfinishedResultSets() {
+                       // optimal case, no ResultSets at all to check
+                       if (rsresponses == null)
+                               return(false);
+
+                       boolean didClose = false;
+                       Iterator it = rsresponses.keySet().iterator();
+                       while (it.hasNext()) {
+                               ResultSetResponse rsr = 
(ResultSetResponse)(it.next());
+                               didClose |= rsr.closeIfOutstandingResults();
+                       }
+                       return(didClose);
+               }
+
+               /**
                 * Closes this ResponseList by closing all the Responses in this
                 * ResponseList.
                 */
@@ -2255,6 +2315,11 @@ public class MonetConnection extends Mon
                                                                                
                );
                                                                        break;
                                                                        case 
StartOfHeaderParser.Q_SCHEMA:
+                                                                               
// schema updates invalidate all
+                                                                               
// open handles for result sets
+                                                                               
// as well as prepared
+                                                                               
// statements
+                                                                               
invalidateHandles();
                                                                                
res = new SchemaResponse();
                                                                        break;
                                                                        case 
StartOfHeaderParser.Q_TRANS:
@@ -2389,6 +2454,39 @@ public class MonetConnection extends Mon
                }
        }
        // }}}
+       
+       /**
+        * Invalidate all outstanding ResultSet and PreparedStatement
+        * handles.  Whenever a schema update occurs, the server discards
+        * all outstanding handles, since they might have become
+        * incompatible with the current situation.  Hence, we have to close
+        * all open handles, such that the user knows the (most notably
+        * PreparedStatement) handles cannot be used any longer.  We emit a
+        * warning in case anything is invalidated.
+        */
+       private void invalidateHandles() {
+               boolean didClose = false;
+               Iterator it = statements.keySet().iterator();
+               while (it.hasNext()) {
+                       try {
+                               Object o = it.next();
+                               if (o instanceof PreparedStatement) {
+                                       // we can always close this, since it 
always keeps a
+                                       // handle on the server
+                                       ((PreparedStatement)o).close();
+                                       didClose = true;
+                               } else if (o instanceof Statement) {
+                                       // don't close the entire Statement, 
only the
+                                       // ResultSets that really have to be 
closed
+                                       didClose |= 
((MonetStatement)o).closeUnfinishedResultSets();
+                               }
+                       } catch (SQLException e) {
+                               // better luck next time!
+                       }
+               }
+               if (didClose)
+                       addWarning("Invalidated ResultSets and 
PreparedStatements due to schema update");
+       }
 
        /**
         * A thread to send a query to the server.  When sending large
diff --git a/java/src/nl/cwi/monetdb/jdbc/MonetStatement.java 
b/java/src/nl/cwi/monetdb/jdbc/MonetStatement.java
--- a/java/src/nl/cwi/monetdb/jdbc/MonetStatement.java
+++ b/java/src/nl/cwi/monetdb/jdbc/MonetStatement.java
@@ -315,6 +315,17 @@ public class MonetStatement extends Mone
        }
 
        /**
+        * Closes all ResultSets that might still need the server for more
+        * data.  This method is intended for the situation where the server
+        * invalidates all handles, and hence makes it impossible to
+        * retrieve more data for ResultSets that haven't yet received all
+        * data they need.
+        */
+       boolean closeUnfinishedResultSets() {
+               return(lastResponseList.closeUnfinishedResultSets());
+       }
+
+       /**
         * Retrieves whether this Statement object has been closed. A
         * Statement is closed if the method close has been called on it, or
         * if it is automatically closed.
@@ -899,6 +910,8 @@ public class MonetStatement extends Mone
                        ret = ((MonetConnection.UpdateResponse)header).count;
                } else if (header instanceof MonetConnection.SchemaResponse) {
                        ret = ((MonetConnection.SchemaResponse)header).state;
+               } else if (header instanceof 
MonetConnection.AutoCommitResponse) {
+                       ret = 
((MonetConnection.AutoCommitResponse)header).state;
                }
 
                return(ret);
_______________________________________________
Checkin-list mailing list
[email protected]
http://mail.monetdb.org/mailman/listinfo/checkin-list

Reply via email to