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
*
* Note: setReadOnly cannot be called while in the middle
* of a transaction
*
+ * Note: 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)