You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by ba...@apache.org on 2005/11/14 16:43:04 UTC

svn commit: r344147 - in /db/derby/code/trunk/java: client/org/apache/derby/client/am/ drda/org/apache/derby/impl/drda/ testing/org/apache/derbyTesting/functionTests/suites/ testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/

Author: bakksjo
Date: Mon Nov 14 07:42:40 2005
New Revision: 344147

URL: http://svn.apache.org/viewcvs?rev=344147&view=rev
Log:
DERBY-506

Implements Statement.setQueryTimeout in the client driver by
"piggybacking" an EXCSQLSET command on statement execution.


Modified:
    db/derby/code/trunk/java/client/org/apache/derby/client/am/PreparedStatement.java
    db/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java
    db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/suites/DerbyNetClient.exclude
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/SetQueryTimeoutTest.java

Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/PreparedStatement.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/client/org/apache/derby/client/am/PreparedStatement.java?rev=344147&r1=344146&r2=344147&view=diff
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/PreparedStatement.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/PreparedStatement.java Mon Nov 14 07:42:40 2005
@@ -1259,15 +1259,6 @@
             updateCount_ = -1;
         }
 
-        java.util.Timer queryTimer = null;
-        QueryTimerTask queryTimerTask = null;
-        if (timeout_ != 0) {
-            queryTimer = new java.util.Timer(); // A thread that ticks the seconds
-            queryTimerTask = new QueryTimerTask(this, queryTimer);
-            queryTimer.schedule(queryTimerTask, 1000 * timeout_);
-        }
-
-        try {
             agent_.beginWriteChain(this);
 
             boolean piggybackedAutocommit = super.writeCloseResultSets(true);  // true means permit auto-commits
@@ -1278,8 +1269,15 @@
             boolean chainAutoCommit = false;
             boolean commitSubstituted = false;
             boolean repositionedCursor = false;
+            boolean timeoutSent = false;
             ResultSet scrollableRS = null;
 
+            if (doWriteTimeout) {
+                timeoutArrayList.set(0, TIMEOUT_STATEMENT + timeout_);
+                writeSetSpecialRegister(timeoutArrayList);
+                doWriteTimeout = false;
+                timeoutSent = true;
+            }
             switch (sqlMode_) {
             case isUpdate__:
                 if (positionedUpdateCursorName_ != null) {
@@ -1369,6 +1367,10 @@
 
             super.markResultSetsClosed(true); // true means remove from list of commit and rollback listeners
 
+            if (timeoutSent) {
+                readSetSpecialRegister(); // Read response to the EXCSQLSET
+            }
+
             switch (sqlMode_) {
             case isUpdate__:
                 // do not need to reposition for a rowset cursor
@@ -1450,14 +1452,6 @@
                 throw new SqlException(agent_.logWriter_, "Unable to open resultSet with requested " +
                         "holdability " + resultSetHoldability_ + ".");
             }
-
-        } finally {
-            if (timeout_ != 0) { // query timers need to be cancelled.
-                queryTimer.cancel();
-                queryTimerTask.cancel();
-            }
-        }
-
     }
 
     public int[] executeBatchX(boolean supportsQueryBatchRequest) throws SqlException, BatchUpdateException {
@@ -1476,6 +1470,7 @@
         int[] updateCounts = new int[batchSize];
         int numInputColumns = parameterMetaData_ == null ? 0 : parameterMetaData_.getColumnCount();
         Object[] savedInputs = null;  // used to save/restore existing parameters
+        boolean timeoutSent = false;
 
         if (batchSize == 0) {
             return updateCounts;
@@ -1510,6 +1505,13 @@
         agent_.beginBatchedWriteChain(this);
         boolean chainAutoCommit = connection_.willAutoCommitGenerateFlow() && isAutoCommittableStatement_;
 
+        if (doWriteTimeout) {
+            timeoutArrayList.set(0, TIMEOUT_STATEMENT + timeout_);
+            writeSetSpecialRegister(timeoutArrayList);
+            doWriteTimeout = false;
+            timeoutSent = true;
+        }
+
         for (int i = 0; i < batchSize; i++) {
             parameters_ = (Object[]) batch_.get(i);
 
@@ -1553,6 +1555,10 @@
         }
 
         agent_.flowBatch(this, batchSize);
+
+        if (timeoutSent) {
+            readSetSpecialRegister(); // Read response to the EXCSQLSET
+        }
 
         try {
             for (int i = 0; i < batchSize; i++) {

Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java?rev=344147&r1=344146&r2=344147&view=diff
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/Statement.java Mon Nov 14 07:42:40 2005
@@ -131,7 +131,10 @@
     protected int indexOfCurrentResultSet_ = -1;
     ResultSet[] resultSetList_ = null;   // array of ResultSet objects
 
-    int timeout_ = 0; // for query timeout in seconds, multiplied by 1000 when passed to java.util.Timer
+    protected final static String TIMEOUT_STATEMENT = "SET STATEMENT_TIMEOUT ";
+    protected java.util.ArrayList timeoutArrayList = new java.util.ArrayList(1);
+    protected boolean doWriteTimeout = false;
+    int timeout_ = 0; // for query timeout in seconds
     int maxRows_ = 0;
     int maxFieldSize_ = 0; // zero means that there is no limit to the size of a column.
     boolean escapedProcedureCallWithResult_ = false;
@@ -204,6 +207,9 @@
         resultSetConcurrency_ = java.sql.ResultSet.CONCUR_READ_ONLY;
         resultSetHoldability_ = 0;
         cursorAttributesToSendOnPrepare_ = null;
+        if (timeoutArrayList.size() == 0) {
+            timeoutArrayList.add(null); // Make sure the list's length is 1
+        }
 
         initResetStatement();
     }
@@ -232,6 +238,7 @@
         indexOfCurrentResultSet_ = -1;
         resultSetList_ = null;
         timeout_ = 0;
+        doWriteTimeout = false;
         maxRows_ = 0;
         maxFieldSize_ = 0;
         escapedProcedureCallWithResult_ = false;
@@ -536,9 +543,14 @@
             }
             checkForClosedStatement(); // Per jdbc spec (see java.sql.Statement.close() javadoc)
             if (seconds < 0) {
-                throw new SqlException(agent_.logWriter_, "Attempt to set a negative query timeout");
+                throw new SqlException(agent_.logWriter_,
+                                       "Attempt to set a negative query timeout",
+                                       "XJ074.S");
+            }
+            if (seconds != timeout_) {
+                timeout_ = seconds;
+                doWriteTimeout = true;
             }
-            timeout_ = seconds; // java.util.Timer takes milliseconds
         }
     }
 
@@ -1446,17 +1458,8 @@
 
         checkForAppropriateSqlMode(executeType, sqlMode_);
 
-        java.util.Timer queryTimer = null;
-        QueryTimerTask queryTimerTask = null;
-        if (timeout_ != 0) {
-            queryTimer = new java.util.Timer(); // A thread that ticks the seconds
-            queryTimerTask = new QueryTimerTask(this, queryTimer);
-            queryTimer.schedule(queryTimerTask, 1000 * timeout_);
-        }
+        boolean timeoutSent = false;
 
-        // enclose the processing in a try finally block in order to make sure
-        // the query timeout is cancelled at the end of this method.
-        try {
             agent_.beginWriteChain(this);
             boolean piggybackedAutoCommit = writeCloseResultSets(true);  // true means permit auto-commits
 
@@ -1464,6 +1467,12 @@
             Section newSection = null;
             boolean repositionedCursor = false;
 
+            if (doWriteTimeout) {
+                timeoutArrayList.set(0, TIMEOUT_STATEMENT + timeout_);
+                writeSetSpecialRegister(timeoutArrayList);
+                doWriteTimeout = false;
+                timeoutSent = true;
+            }
             switch (sqlMode_) {
             case isQuery__:
                 newSection = agent_.sectionManager_.getDynamicSection(resultSetHoldability_);
@@ -1555,6 +1564,10 @@
 
             readCloseResultSets(true);  // true means permit auto-commits
 
+            if (timeoutSent) {
+                readSetSpecialRegister(); // Read response to the EXCSQLSET
+            }
+
             // turn inUnitOfWork_ flag back on and add statement
             // back on commitListeners_ list if they were off
             // by an autocommit chained to a close cursor.
@@ -1634,15 +1647,6 @@
                 throw new SqlException(agent_.logWriter_, "Unable to open resultSet with requested " +
                         "holdability " + resultSetHoldability_ + ".");
             }
-        } finally {
-            // We don't want to cancel immediately after flow since there is still incoming data on the wire and
-            // so logically we have not completed a full traversal from the client to the server and back.
-            // Cancelling the query timers needs to occur after endReadChain() in order to avoid deadlock conditions.
-            if (timeout_ != 0) { // query timers need to be cancelled.
-                queryTimer.cancel();
-                queryTimerTask.cancel();
-            }
-        }
 
         // In the case of executing a call to a stored procedure.
         if (sqlMode_ == isCall__) {

Modified: db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java?rev=344147&r1=344146&r2=344147&view=diff
==============================================================================
--- db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java (original)
+++ db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java Mon Nov 14 07:42:40 2005
@@ -139,6 +139,10 @@
 	private String pkgcnstknStr;
 	private int secnumber;
 
+    private final static String TIMEOUT_STATEMENT = "SET STATEMENT_TIMEOUT ";
+
+    private int pendingStatementTimeout; // < 0 means no pending timeout to set
+
 	// this flag is for an execute statement/procedure which actually returns a result set;
 	// do not commit the statement, otherwise result set is closed
 
@@ -171,6 +175,7 @@
 		this.server = server;
 		this.timeSlice = timeSlice;
 		this.logConnections = logConnections;
+        this.pendingStatementTimeout = -1;
 		initialize();
     }
 
@@ -706,6 +711,10 @@
 							stmt = database.getDRDAStatement(pkgnamcsn);
 							ps = stmt.getPreparedStatement();
 							ps.clearWarnings();
+                            if (pendingStatementTimeout >= 0) {
+                                ps.setQueryTimeout(pendingStatementTimeout);
+                                pendingStatementTimeout = -1;
+                            }
 							stmt.execute();
 							writeOPNQRYRM(false, stmt);
 							checkWarning(null, ps, null, 0, false, true);
@@ -3506,6 +3515,10 @@
  		stmt.maxrslcnt = maxrslcnt;
  		stmt.outovropt = outovropt;
  		stmt.rslsetflg = rslsetflg;
+        if (pendingStatementTimeout >= 0) {
+            stmt.getPreparedStatement().setQueryTimeout(pendingStatementTimeout);
+            pendingStatementTimeout = -1;
+        }
  
 	
 		// set the statement as the current statement
@@ -4323,8 +4336,13 @@
 		// initialize statement for reuse
 		drdaStmt.initialize();
 		String sqlStmt = parseEXECSQLIMMobjects();
-		drdaStmt.getStatement().clearWarnings();
-		int updCount = drdaStmt.getStatement().executeUpdate(sqlStmt);
+        Statement statement = drdaStmt.getStatement();
+        statement.clearWarnings();
+        if (pendingStatementTimeout >= 0) {
+            statement.setQueryTimeout(pendingStatementTimeout);
+            pendingStatementTimeout = -1;
+        }
+		int updCount = statement.executeUpdate(sqlStmt);
 		return updCount;
 	}
 
@@ -4493,6 +4511,12 @@
 						if (sqlStmt != null)
 						// then we have at least one SQL Statement.
 							gotSqlStt = true;
+
+                        if (sqlStmt.startsWith(TIMEOUT_STATEMENT)) {
+                            String timeoutString = sqlStmt.substring(TIMEOUT_STATEMENT.length());
+                            pendingStatementTimeout = Integer.valueOf(timeoutString).intValue();
+                            break;
+                        }
 
 						if (canIgnoreStmt(sqlStmt)) {
 						// We _know_ Cloudscape doesn't recognize this

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/suites/DerbyNetClient.exclude
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/suites/DerbyNetClient.exclude?rev=344147&r1=344146&r2=344147&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/suites/DerbyNetClient.exclude (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/suites/DerbyNetClient.exclude Mon Nov 14 07:42:40 2005
@@ -3,7 +3,6 @@
 # excluding batchUpdate.java for it hits a problem in networkserver ('beetle' 5561)
 # excluding statementJdbc20.java because this tests fetch_reverse throughout the test
 # excluding jdbcapi/testRelative.java because it is a new test that requires debugging with the IBM Driver
-# excluding jdbcapi/SetQueryTimeoutTest.java because neither the JCC driver nor the ClientDriver support setQueryTimeout() yet.
 # excluding jdbcapi/resultsetJdbc30.java because the features tested are not implemented by Derby Client
 # excluding jdbcapi/checkDataSource30.java - Client behaves differently. Need to look into this
 # excluding jdbcapi/statementJdbc30.java - Client behaves differently. Need to look into this
@@ -12,7 +11,6 @@
 jdbcapi/batchUpdate.java
 jdbcapi/statementJdbc20.java
 jdbcapi/testRelative.java
-jdbcapi/SetQueryTimeoutTest.java
 jdbcapi/resultsetJdbc30.java
 jdbcapi/checkDataSource30.java
 jdbcapi/statementJdbc30.java

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/SetQueryTimeoutTest.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/SetQueryTimeoutTest.java?rev=344147&r1=344146&r2=344147&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/SetQueryTimeoutTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/SetQueryTimeoutTest.java Mon Nov 14 07:42:40 2005
@@ -70,7 +70,6 @@
  * 3. Sets an invalid (negative) timeout. Verifies that the correct
  *    exception is thrown.
  *
- * @author oyvind.bakksjo@sun.com
  */
 public class SetQueryTimeoutTest
 {
@@ -242,6 +241,12 @@
         } catch (SQLException e) {
             throw new TestFailedException("Should not happen", e);
         }
+        
+        try {
+            exec(conn, "DROP FUNCTION DELAY");
+        } catch (Exception e) {
+            // Ignore
+        }
 
         exec(conn, "CREATE FUNCTION DELAY(SECONDS INTEGER, VALUE INTEGER) RETURNS INTEGER PARAMETER STYLE JAVA NO SQL LANGUAGE JAVA EXTERNAL NAME 'org.apache.derbyTesting.functionTests.tests.jdbcapi.SetQueryTimeoutTest.delay'");
 
@@ -427,9 +432,11 @@
         // B - different stmt on the same connection; should NOT time out
         // C - different stmt on different connection; should NOT time out
         // D - here just to create equal contention on conn1 and conn2
-        
+
+        // FIXME: Should have used conn1 for statementB below, but
+        // this is blocked by DERBY-694
         PreparedStatement statementA = prepare(conn1, getFetchQuery("t"));
-        PreparedStatement statementB = prepare(conn1, getFetchQuery("t"));
+        PreparedStatement statementB = prepare(conn2, getFetchQuery("t"));
         PreparedStatement statementC = prepare(conn2, getFetchQuery("t"));
         PreparedStatement statementD = prepare(conn2, getFetchQuery("t"));