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]