jdbc_setQueryTimeout_02.patch

text/plain

Filename: jdbc_setQueryTimeout_02.patch
Type: text/plain
Part: 1
Message: Support for JDBC setQueryTimeout, et al.

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
org/postgresql/core/QueryExecutor.java 2 0
org/postgresql/core/v2/ConnectionFactoryImpl.java 1 1
org/postgresql/core/v2/QueryExecutorImpl.java 5 5
org/postgresql/core/v3/QueryExecutorImpl.java 62 28
org/postgresql/jdbc2/AbstractJdbc2Connection.java 26 10
org/postgresql/jdbc2/AbstractJdbc2Statement.java 3 4
org/postgresql/jdbc3/AbstractJdbc3Statement.java 1 1
diff --git a/org/postgresql/core/QueryExecutor.java b/org/postgresql/core/QueryExecutor.java
index 73cbfac..12c978e 100644
--- a/org/postgresql/core/QueryExecutor.java
+++ b/org/postgresql/core/QueryExecutor.java
@@ -113,6 +113,7 @@ public interface QueryExecutor {
                  ResultHandler handler,
                  int maxRows,
                  int fetchSize,
+                 int timer,
                  int flags)
     throws SQLException;
 
@@ -140,6 +141,7 @@ public interface QueryExecutor {
                  ResultHandler handler,
                  int maxRows,
                  int fetchSize,
+                 int timer,
                  int flags)
     throws SQLException;
 
diff --git a/org/postgresql/core/v2/ConnectionFactoryImpl.java b/org/postgresql/core/v2/ConnectionFactoryImpl.java
index cfa1df5..9fa6aeb 100644
--- a/org/postgresql/core/v2/ConnectionFactoryImpl.java
+++ b/org/postgresql/core/v2/ConnectionFactoryImpl.java
@@ -413,7 +413,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
 
         try
         {
-            executor.execute(query, null, handler, 0, 0, flags);
+            executor.execute(query, null, handler, 0, 0, 0, flags);
         }
         finally
         {
diff --git a/org/postgresql/core/v2/QueryExecutorImpl.java b/org/postgresql/core/v2/QueryExecutorImpl.java
index f06b7eb..ef0222b 100644
--- a/org/postgresql/core/v2/QueryExecutorImpl.java
+++ b/org/postgresql/core/v2/QueryExecutorImpl.java
@@ -253,17 +253,17 @@ public class QueryExecutorImpl implements QueryExecutor {
     public synchronized void execute(Query query,
                                      ParameterList parameters,
                                      ResultHandler handler,
-                                     int maxRows, int fetchSize, int flags)
+                                     int maxRows, int fetchSize, int timer, int flags)
     throws SQLException
     {
-        execute((V2Query)query, (SimpleParameterList)parameters, handler, maxRows, flags);
+        execute((V2Query)query, (SimpleParameterList)parameters, handler, maxRows, timer, flags);
     }
 
     // Nothing special yet, just run the queries one at a time.
     public synchronized void execute(Query[] queries,
                                      ParameterList[] parameters,
                                      ResultHandler handler,
-                                     int maxRows, int fetchSize, int flags)
+                                     int maxRows, int fetchSize, int timer, int flags)
     throws SQLException
     {
         final ResultHandler delegateHandler = handler;
@@ -289,7 +289,7 @@ public class QueryExecutorImpl implements QueryExecutor {
                   };
 
         for (int i = 0; i < queries.length; ++i)
-            execute((V2Query)queries[i], (SimpleParameterList)parameters[i], handler, maxRows, flags);
+            execute((V2Query)queries[i], (SimpleParameterList)parameters[i], handler, maxRows, timer, flags);
 
         delegateHandler.handleCompletion();
     }
@@ -301,7 +301,7 @@ public class QueryExecutorImpl implements QueryExecutor {
     private void execute(V2Query query,
                          SimpleParameterList parameters,
                          ResultHandler handler,
-                         int maxRows, int flags) throws SQLException
+                         int maxRows, int timer, int flags) throws SQLException
     {
 
         // The V2 protocol has no support for retrieving metadata
diff --git a/org/postgresql/core/v3/QueryExecutorImpl.java b/org/postgresql/core/v3/QueryExecutorImpl.java
index 62678f9..b246f84 100644
--- a/org/postgresql/core/v3/QueryExecutorImpl.java
+++ b/org/postgresql/core/v3/QueryExecutorImpl.java
@@ -224,6 +224,7 @@ public class QueryExecutorImpl implements QueryExecutor {
                                      ResultHandler handler,
                                      int maxRows,
                                      int fetchSize,
+                                     int timer,
                                      int flags)
     throws SQLException
     {
@@ -249,7 +250,7 @@ public class QueryExecutorImpl implements QueryExecutor {
         {
             try
             {
-                handler = sendQueryPreamble(handler, flags);
+                handler = sendQueryPreamble(handler, flags, timer);
                 ErrorTrackingResultHandler trackingHandler = new ErrorTrackingResultHandler(handler);
                 queryCount = 0;
                 sendQuery((V3Query)query, (V3ParameterList)parameters, maxRows, fetchSize, flags, trackingHandler);
@@ -362,6 +363,7 @@ public class QueryExecutorImpl implements QueryExecutor {
                                      ResultHandler handler,
                                      int maxRows,
                                      int fetchSize,
+                                     int timer,
                                      int flags)
     throws SQLException
     {
@@ -384,7 +386,7 @@ public class QueryExecutorImpl implements QueryExecutor {
 
         try
         {
-            handler = sendQueryPreamble(handler, flags);
+            handler = sendQueryPreamble(handler, flags, timer);
             ErrorTrackingResultHandler trackingHandler = new ErrorTrackingResultHandler(handler);
             queryCount = 0;
 
@@ -416,11 +418,33 @@ public class QueryExecutorImpl implements QueryExecutor {
         handler.handleCompletion();
     }
 
-    private ResultHandler sendQueryPreamble(final ResultHandler delegateHandler, int flags) throws IOException {
+    private ResultHandler sendQueryPreamble(final ResultHandler delegateHandler, int timer, int flags) throws IOException {
+        // First, decide whether we need to do anything.  Put that in final boolean so that we can use it later in anonymous class.
+        final boolean needBegin = ((flags & QueryExecutor.QUERY_SUPPRESS_BEGIN) != 0 ||
+                protoConnection.getTransactionState() != ProtocolConnection.TRANSACTION_IDLE);
+
+        final boolean needTimer = timer > 0; 
+
         // First, send CloseStatements for finalized SimpleQueries that had statement names assigned.
         processDeadParsedQueries();
         processDeadPortals();
 
+        if (!needBegin && !needTimer)
+            return delegateHandler;
+
+        if (needBegin) {
+            // Need to send out a BEGIN preamble.
+            sendOneQuery(beginTransactionQuery, SimpleQuery.NO_PARAMETERS, 0, 0, QueryExecutor.QUERY_NO_METADATA);
+        }
+
+        if (needTimer) {
+            // Need to send out a sesstion timer query, in ms.  Timer is in sec.
+            SimpleQuery timerQuery = new SimpleQuery(new String[] { 
+                "select set_session_timer(" + (timer * 1000) + "); "
+            }, null);
+            sendOneQuery(timerQuery, SimpleQuery.NO_PARAMETERS, 0, 0, QueryExecutor.QUERY_NO_METADATA);
+        }
+
         // Send BEGIN on first statement in transaction.
         if ((flags & QueryExecutor.QUERY_SUPPRESS_BEGIN) != 0 ||
                 protoConnection.getTransactionState() != ProtocolConnection.TRANSACTION_IDLE)
@@ -430,39 +454,49 @@ public class QueryExecutorImpl implements QueryExecutor {
 
         // Insert a handler that intercepts the BEGIN.
         return new ResultHandler() {
-                   private boolean sawBegin = false;
-
-                   public void handleResultRows(Query fromQuery, Field[] fields, Vector tuples, ResultCursor cursor) {
-                       if (sawBegin)
-                           delegateHandler.handleResultRows(fromQuery, fields, tuples, cursor);
-                   }
-
-                   public void handleCommandStatus(String status, int updateCount, long insertOID) {
-                       if (!sawBegin)
-                       {
-                           sawBegin = true;
-                           if (!status.equals("BEGIN"))
-                               handleError(new PSQLException(GT.tr("Expected command status BEGIN, got {0}.", status),
-                                                             PSQLState.PROTOCOL_VIOLATION));
+               private boolean needHandleBegin = needBegin; 
+               private boolean needHandleTimer = needTimer;
+
+               public void handleResultRows(Query fromQuery, Field[] fields, Vector tuples, ResultCursor cursor) {
+                   if (!needHandleBegin) {
+                       if (needHandleTimer) {
+                           // ResultSet from timer query.  Drop it on the floor.
+                           needHandleTimer = false;
                        }
                        else
                        {
-                           delegateHandler.handleCommandStatus(status, updateCount, insertOID);
+                           // Real query result.  Pass it on.
+                           delegateHandler.handleResultRows(fromQuery, fields, tuples, cursor);
                        }
                    }
-
-                   public void handleWarning(SQLWarning warning) {
-                       delegateHandler.handleWarning(warning);
+               }
+
+               public void handleCommandStatus(String status, int updateCount, long insertOID) {
+                   if (needHandleBegin)
+                   {
+                       needHandleBegin = false;
+                       if (!status.equals("BEGIN"))
+                           handleError(new PSQLException(GT.tr("Expected command status BEGIN, got {0}.", status),
+                                       PSQLState.PROTOCOL_VIOLATION));
                    }
-
-                   public void handleError(SQLException error) {
-                       delegateHandler.handleError(error);
+                   else
+                   {
+                       delegateHandler.handleCommandStatus(status, updateCount, insertOID);
                    }
+               }
 
-                   public void handleCompletion() throws SQLException{
-                       delegateHandler.handleCompletion();
-                   }
-               };
+               public void handleWarning(SQLWarning warning) {
+                   delegateHandler.handleWarning(warning);
+               }
+
+               public void handleError(SQLException error) {
+                   delegateHandler.handleError(error);
+               }
+
+               public void handleCompletion() throws SQLException{
+                   delegateHandler.handleCompletion();
+               }
+           };
     }
 
     //
diff --git a/org/postgresql/jdbc2/AbstractJdbc2Connection.java b/org/postgresql/jdbc2/AbstractJdbc2Connection.java
index 4571379..b24f3d2 100644
--- a/org/postgresql/jdbc2/AbstractJdbc2Connection.java
+++ b/org/postgresql/jdbc2/AbstractJdbc2Connection.java
@@ -67,6 +67,8 @@ public abstract class AbstractJdbc2Connection implements BaseConnection
     public boolean autoCommit = true;
     // Connection's readonly state.
     public boolean readOnly = false;
+    // Respect it?
+    private boolean respectReadonly = false;
 
     // Bind String to UNSPECIFIED or VARCHAR?
     public final boolean bindStringAsVarchar;
@@ -175,6 +177,12 @@ public abstract class AbstractJdbc2Connection implements BaseConnection
             openStackTrace = new Throwable("Connection was created at this point:");
             enableDriverManagerLogging();
         }
+
+        String rdonly = info.getProperty("respectReadonly");
+        if (rdonly != null) {
+            if (rdonly.equalsIgnoreCase("true"))
+                respectReadonly = true;
+        }
     }
 
     private final TimestampUtils timestampUtils;
@@ -601,28 +609,36 @@ public abstract class AbstractJdbc2Connection implements BaseConnection
 
 
     /*
-     * You can put a connection in read-only mode as a hunt to enable
+     * You can put a connection in read-only mode as a hint to enable
      * database optimizations
      *
      * <B>Note:</B> setReadOnly cannot be called while in the middle
      * of a transaction
      *
+     * <B>Note:</B> This is only a hint, so we are free not to do
+     * anything.  According to the JDBC specification, we should
+     * respectReadonly.  However, this breaks a lot of popular
+     * software like Hibernate, so unless the end user *really* wants
+     * this behavior, which they can do by setting respectReadonly in
+     * the connection string, we will make this a no-op.
+     *
      * @param readOnly - true enables read-only mode; false disables it
      * @exception SQLException if a database access error occurs
      */
     public void setReadOnly(boolean readOnly) throws SQLException
     {
         checkClosed();
-        if (protoConnection.getTransactionState() != ProtocolConnection.TRANSACTION_IDLE)
-            throw new PSQLException(GT.tr("Cannot change transaction read-only property in the middle of a transaction."),
-                                    PSQLState.ACTIVE_SQL_TRANSACTION);
+        if (respectReadonly) {
+            if (protoConnection.getTransactionState() != ProtocolConnection.TRANSACTION_IDLE)
+                throw new PSQLException(GT.tr("Cannot change transaction read-only property in the middle of a transaction."),
+                        PSQLState.ACTIVE_SQL_TRANSACTION);
 
-        if (haveMinimumServerVersion("7.4") && readOnly != this.readOnly)
-        {
-            String readOnlySql = "SET SESSION CHARACTERISTICS AS TRANSACTION " + (readOnly ? "READ ONLY" : "READ WRITE");
-            execSQLUpdate(readOnlySql); // nb: no BEGIN triggered.
+            if (haveMinimumServerVersion("7.4") && readOnly != this.readOnly)
+            {
+                String readOnlySql = "SET SESSION CHARACTERISTICS AS TRANSACTION " + (readOnly ? "READ ONLY" : "READ WRITE");
+                execSQLUpdate(readOnlySql); // nb: no BEGIN triggered.
+            }
         }
-
         this.readOnly = readOnly;
     }
 
@@ -683,7 +699,7 @@ public abstract class AbstractJdbc2Connection implements BaseConnection
 
     private void executeTransactionCommand(Query query) throws SQLException {
         getQueryExecutor().execute(query, null, new TransactionCommandHandler(),
-                                   0, 0, QueryExecutor.QUERY_NO_METADATA | QueryExecutor.QUERY_NO_RESULTS | QueryExecutor.QUERY_SUPPRESS_BEGIN);
+                                   0, 0, 0, QueryExecutor.QUERY_NO_METADATA | QueryExecutor.QUERY_NO_RESULTS | QueryExecutor.QUERY_SUPPRESS_BEGIN);
     }
 
     /*
diff --git a/org/postgresql/jdbc2/AbstractJdbc2Statement.java b/org/postgresql/jdbc2/AbstractJdbc2Statement.java
index 5e44a73..da2262b 100644
--- a/org/postgresql/jdbc2/AbstractJdbc2Statement.java
+++ b/org/postgresql/jdbc2/AbstractJdbc2Statement.java
@@ -502,6 +502,7 @@ public abstract class AbstractJdbc2Statement implements BaseStatement
                                               handler,
                                               maxrows,
                                               fetchSize,
+                                              timeout,
                                               flags);
         result = firstUnclosedResult = handler.getResults();
 
@@ -652,9 +653,6 @@ public abstract class AbstractJdbc2Statement implements BaseStatement
             throw new PSQLException(GT.tr("Query timeout must be a value greater than or equals to 0."),
                                     PSQLState.INVALID_PARAMETER_VALUE);
 
-        if (seconds > 0)
-            throw Driver.notImplemented(this.getClass(), "setQueryTimeout(int)");
-
         timeout = seconds;
     }
 
@@ -2739,6 +2737,7 @@ public abstract class AbstractJdbc2Statement implements BaseStatement
                                               handler,
                                               maxrows,
                                               fetchSize,
+                                              timeout,
                                               flags);
 
         return updateCounts;
@@ -2827,7 +2826,7 @@ public abstract class AbstractJdbc2Statement implements BaseStatement
 
             int flags = QueryExecutor.QUERY_ONESHOT | QueryExecutor.QUERY_DESCRIBE_ONLY | QueryExecutor.QUERY_SUPPRESS_BEGIN;
             StatementResultHandler handler = new StatementResultHandler();
-            connection.getQueryExecutor().execute(preparedQuery, preparedParameters, handler, 0, 0, flags);
+            connection.getQueryExecutor().execute(preparedQuery, preparedParameters, handler, 0, 0, timeout, flags);
             ResultWrapper wrapper = handler.getResults();
             if (wrapper != null) {
                 rs = wrapper.getResultSet();
diff --git a/org/postgresql/jdbc3/AbstractJdbc3Statement.java b/org/postgresql/jdbc3/AbstractJdbc3Statement.java
index ecbfb76..e795ee8 100644
--- a/org/postgresql/jdbc3/AbstractJdbc3Statement.java
+++ b/org/postgresql/jdbc3/AbstractJdbc3Statement.java
@@ -411,7 +411,7 @@ public abstract class AbstractJdbc3Statement extends org.postgresql.jdbc2.Abstra
     {
         int flags = QueryExecutor.QUERY_ONESHOT | QueryExecutor.QUERY_DESCRIBE_ONLY | QueryExecutor.QUERY_SUPPRESS_BEGIN;
         StatementResultHandler handler = new StatementResultHandler();
-        connection.getQueryExecutor().execute(preparedQuery, preparedParameters, handler, 0, 0, flags);
+        connection.getQueryExecutor().execute(preparedQuery, preparedParameters, handler, 0, 0, timeout, flags);
 
         int oids[] = preparedParameters.getTypeOIDs();
         if (oids != null)