postgresql-jdbc.git / summary / log / commit / refs
commit a77c82871511c53d0ea16febce4c46a911eaac46
Author: Kris Jurka <books@ejurka.com>
Date: Tue Jul 27 05:19:40 2004 +0000
Don't make the driver begin a transaction for certain calls.
Notably we don't want Connection.getTransactionIsolation() to start
a transaction because this is often a check put in before trying to
change it and you can't change it in mid-transaction.
Oliver Jowett
org/postgresql/core/BaseConnection.java | 2 +
org/postgresql/core/BaseStatement.java | 9 +++
org/postgresql/jdbc2/AbstractJdbc2Connection.java | 73 +++++++++++++----------
org/postgresql/jdbc2/AbstractJdbc2ResultSet.java | 8 +++
org/postgresql/jdbc2/AbstractJdbc2Statement.java | 2 +-
org/postgresql/test/jdbc2/ConnectionTest.java | 21 ++++++-
6 files changed, 80 insertions(+), 35 deletions(-)
diff --git a/org/postgresql/core/BaseConnection.java b/org/postgresql/core/BaseConnection.java
index c5ce9f41..9b1d05b1 100644
--- a/org/postgresql/core/BaseConnection.java
+++ b/org/postgresql/core/BaseConnection.java
@@ -31,6 +31,7 @@ public interface BaseConnection extends PGConnection, Connection
/**
* Execute a SQL query that returns a single resultset.
+ * Never causes a new transaction to be started regardless of the autocommit setting.
*
* @param s the query to execute
* @return the (non-null) returned resultset
@@ -40,6 +41,7 @@ public interface BaseConnection extends PGConnection, Connection
/**
* Execute a SQL query that does not return results.
+ * Never causes a new transaction to be started regardless of the autocommit setting.
*
* @param s the query to execute
* @throws SQLException if something goes wrong.
diff --git a/org/postgresql/core/BaseStatement.java b/org/postgresql/core/BaseStatement.java
index e6eaf994..9dfba379 100644
--- a/org/postgresql/core/BaseStatement.java
+++ b/org/postgresql/core/BaseStatement.java
@@ -53,4 +53,13 @@ public interface BaseStatement extends PGStatement, Statement
* @throws SQLException if something goes wrong.
*/
public boolean executeWithFlags(String p_sql, int flags) throws SQLException;
+
+ /**
+ * Execute a prepared query, passing additional query flags.
+ *
+ * @param flags additional {@link QueryExecutor} flags for execution; these
+ * are bitwise-ORed into the default flags.
+ * @throws SQLException if something goes wrong.
+ */
+ public boolean executeWithFlags(int flags) throws SQLException;
}
diff --git a/org/postgresql/jdbc2/AbstractJdbc2Connection.java b/org/postgresql/jdbc2/AbstractJdbc2Connection.java
index f064d9b8..8769b8be 100644
--- a/org/postgresql/jdbc2/AbstractJdbc2Connection.java
+++ b/org/postgresql/jdbc2/AbstractJdbc2Connection.java
@@ -175,10 +175,15 @@ public abstract class AbstractJdbc2Connection implements BaseConnection
/**
* Simple query execution.
*/
- public ResultSet execSQLQuery(String s) throws SQLException
- {
- Statement stat = (Statement) createStatement();
- boolean hasResultSet = stat.execute(s);
+ public ResultSet execSQLQuery(String s) throws SQLException {
+ BaseStatement stat = (BaseStatement) createStatement();
+ boolean hasResultSet = stat.executeWithFlags(s, QueryExecutor.QUERY_SUPPRESS_BEGIN);
+
+ while (!hasResultSet && stat.getUpdateCount() != -1)
+ hasResultSet = stat.getMoreResults();
+
+ if (!hasResultSet)
+ throw new PSQLException("postgresql.stat.noresult", PSQLState.NO_DATA);
// Transfer warnings to the connection, since the user never
// has a chance to see the statement itself.
@@ -186,21 +191,13 @@ public abstract class AbstractJdbc2Connection implements BaseConnection
if (warnings != null)
addWarning(warnings);
- while (!hasResultSet && stat.getUpdateCount() != -1)
- hasResultSet = stat.getMoreResults();
-
return stat.getResultSet();
}
- public void execSQLUpdate(String s) throws SQLException
- {
- execSQLUpdate(s, 0);
- }
-
- private void execSQLUpdate(String s, int flags) throws SQLException
- {
+ public void execSQLUpdate(String s) throws SQLException {
BaseStatement stmt = (BaseStatement) createStatement();
- stmt.executeWithFlags(s, QueryExecutor.QUERY_NO_METADATA | QueryExecutor.QUERY_NO_RESULTS | flags);
+ if (stmt.executeWithFlags(s, QueryExecutor.QUERY_NO_METADATA | QueryExecutor.QUERY_NO_RESULTS | QueryExecutor.QUERY_SUPPRESS_BEGIN))
+ throw new PSQLException("postgresql.stat.result");
// Transfer warnings to the connection, since the user never
// has a chance to see the statement itself.
@@ -526,7 +523,7 @@ public abstract class AbstractJdbc2Connection implements BaseConnection
if (haveMinimumServerVersion("7.4") && readOnly != this.readOnly) {
String readOnlySql = "SET SESSION CHARACTERISTICS AS TRANSACTION " + (readOnly ? "READ ONLY" : "READ WRITE");
- execSQLUpdate(readOnlySql, QueryExecutor.QUERY_SUPPRESS_BEGIN); // No BEGIN regardles of our autocommit setting
+ execSQLUpdate(readOnlySql); // nb: no BEGIN triggered.
}
this.readOnly = readOnly;
@@ -633,26 +630,32 @@ public abstract class AbstractJdbc2Connection implements BaseConnection
public int getTransactionIsolation() throws SQLException
{
String level = null;
- Statement stmt = createStatement();
- if (haveMinimumServerVersion("7.3")) {
- ResultSet rs = stmt.executeQuery("SHOW TRANSACTION ISOLATION LEVEL");
+ if (haveMinimumServerVersion("7.3")) {
+ // 7.3+ returns the level as a query result.
+ ResultSet rs = execSQLQuery("SHOW TRANSACTION ISOLATION LEVEL"); // nb: no BEGIN triggered
if (rs.next())
level = rs.getString(1);
-
- // Transfer warnings to the connection, since the user never
- // has a chance to see the statement itself.
- SQLWarning warnings = stmt.getWarnings();
- if (warnings != null)
- addWarning(warnings);
+ rs.close();
} else {
- stmt.executeUpdate("SHOW TRANSACTION ISOLATION LEVEL");
- SQLWarning warning = stmt.getWarnings();
+ // 7.2 returns the level as an INFO message. Ew.
+ // We juggle the warning chains a bit here.
+
+ // Swap out current warnings.
+ SQLWarning saveWarnings = getWarnings();
+ clearWarnings();
+
+ // Run the query any examine any resulting warnings.
+ execSQLUpdate("SHOW TRANSACTION ISOLATION LEVEL"); // nb: no BEGIN triggered
+ SQLWarning warning = getWarnings();
if (warning != null)
level = warning.getMessage();
- }
- stmt.close();
+ // Swap original warnings back.
+ clearWarnings();
+ if (saveWarnings != null)
+ addWarning(saveWarnings);
+ }
// XXX revisit: throw exception instead of silently eating the error in unkwon cases?
if (level == null)
@@ -694,7 +697,7 @@ public abstract class AbstractJdbc2Connection implements BaseConnection
throw new PSQLException("postgresql.con.isolevel", PSQLState.TRANSACTION_STATE_INVALID, new Integer(level));
String isolationLevelSQL = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL " + isolationLevelName;
- execSQLUpdate(isolationLevelSQL, QueryExecutor.QUERY_SUPPRESS_BEGIN); // No BEGIN regardles of our autocommit setting
+ execSQLUpdate(isolationLevelSQL); // nb: no BEGIN triggered
}
protected String getIsolationLevelName(int level)
@@ -887,7 +890,10 @@ public abstract class AbstractJdbc2Connection implements BaseConnection
query.setString(1, typeName);
- BaseResultSet result = (BaseResultSet)query.executeQuery();
+ if (! ((BaseStatement)query).executeWithFlags(QueryExecutor.QUERY_SUPPRESS_BEGIN) )
+ throw new PSQLException("postgresql.stat.noresult", PSQLState.NO_DATA);
+
+ ResultSet result = query.getResultSet();
if (result.next()) {
oid = result.getInt(1);
oidTypeCache.put(new Integer(oid), typeName);
@@ -926,7 +932,10 @@ public abstract class AbstractJdbc2Connection implements BaseConnection
query.setInt(1, oid);
- ResultSet result = query.executeQuery();
+ if (! ((BaseStatement)query).executeWithFlags(QueryExecutor.QUERY_SUPPRESS_BEGIN) )
+ throw new PSQLException("postgresql.stat.noresult", PSQLState.NO_DATA);
+
+ ResultSet result = query.getResultSet();
if (result.next()) {
typeName = result.getString(1);
typeOidCache.put(typeName, new Integer(oid));
diff --git a/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java b/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java
index 8fc3b3b0..78eecdeb 100644
--- a/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java
+++ b/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java
@@ -158,6 +158,14 @@ public abstract class AbstractJdbc2ResultSet implements BaseResultSet, org.postg
// Fetch all results.
String cursorName = getString(columnIndex);
String fetchSql = "FETCH ALL IN \"" + cursorName + "\"";
+
+ // nb: no BEGIN triggered here. This is fine. If someone
+ // committed, and the cursor was not holdable (closing the
+ // cursor), we avoid starting a new xact and promptly causing
+ // it to fail. If the cursor *was* holdable, we don't want a
+ // new xact anyway since holdable cursor state isn't affected
+ // by xact boundaries. If our caller didn't commit at all, or
+ // autocommit was on, then we wouldn't issue a BEGIN anyway.
ResultSet rs = connection.execSQLQuery(fetchSql);
((AbstractJdbc2ResultSet)rs).setRefCursor(cursorName);
return rs;
diff --git a/org/postgresql/jdbc2/AbstractJdbc2Statement.java b/org/postgresql/jdbc2/AbstractJdbc2Statement.java
index 1e142de3..b1555135 100644
--- a/org/postgresql/jdbc2/AbstractJdbc2Statement.java
+++ b/org/postgresql/jdbc2/AbstractJdbc2Statement.java
@@ -282,7 +282,7 @@ public abstract class AbstractJdbc2Statement implements BaseStatement
return executeWithFlags(0);
}
- private boolean executeWithFlags(int flags) throws SQLException
+ public boolean executeWithFlags(int flags) throws SQLException
{
checkClosed();
if (isFunction && !returnTypeSet)
diff --git a/org/postgresql/test/jdbc2/ConnectionTest.java b/org/postgresql/test/jdbc2/ConnectionTest.java
index 2fee5325..b1189fad 100644
--- a/org/postgresql/test/jdbc2/ConnectionTest.java
+++ b/org/postgresql/test/jdbc2/ConnectionTest.java
@@ -242,8 +242,6 @@ public class ConnectionTest extends TestCase
assertEquals(Connection.TRANSACTION_READ_COMMITTED,
con.getTransactionIsolation());
-
- Statement stmt = con.createStatement();
// Now run some tests with autocommit enabled.
con.setAutoCommit(true);
@@ -274,7 +272,26 @@ public class ConnectionTest extends TestCase
con.setAutoCommit(false);
assertEquals(Connection.TRANSACTION_READ_COMMITTED,
con.getTransactionIsolation());
+ con.commit();
+ // Test that getTransactionIsolation() does not actually start a new txn.
+ con.getTransactionIsolation(); // Shouldn't start a new transaction.
+ con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); // Should be ok -- we're not in a transaction.
+ con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); // Should still be ok.
+
+ // Test that we can't change isolation mid-transaction
+ Statement stmt = con.createStatement();
+ stmt.executeQuery("SELECT 1"); // Start transaction.
+ stmt.close();
+
+ try {
+ con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
+ fail("Expected an exception when changing transaction isolation mid-transaction");
+ } catch (SQLException e) {
+ // Ok.
+ }
+
+ con.rollback();
TestUtil.closeDB(con);
}
[parent: da8f789989b2]