postgresql-jdbc.git / summary / log / commit / refs

commit    17c8afb11c319255aeffc93617545181d20400a3
Author:   Vladimir Sitnikov <sitnikov.vladimir@gmail.com>
Date:     Sun Aug 14 09:34:51 2016 +0000

    fix: invalidate prepared statement cache when "set search_path=..." query is detected
    
    fixes #496


.../org/postgresql/core/v3/QueryExecutorImpl.java | 19 ++++++++++ .../postgresql/test/jdbc4/jdbc41/SchemaTest.java | 41 ++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java b/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java index 3abc2bde..45143df0 100644 --- a/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java +++ b/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java @@ -95,6 +95,13 @@ public class QueryExecutorImpl extends QueryExecutorBase { private short deallocateEpoch; + /** + * This caches the latest observed {@code set search_path} query so the reset of prepared + * statement cache can be skipped if using repeated calls for the same {@code set search_path} + * value. + */ + private String lastSetSearchPathQuery; + public QueryExecutorImpl(PGStream pgStream, String user, String database, int cancelSignalTimeout, Properties info, Logger logger) throws SQLException, IOException { super(logger, pgStream, user, database, cancelSignalTimeout, info); @@ -2042,6 +2049,18 @@ public class QueryExecutorImpl extends QueryExecutorBase { SimpleQuery currentQuery = executeData.query; Portal currentPortal = executeData.portal; + if (status.startsWith("SET")) { + String nativeSql = currentQuery.getNativeQuery().nativeSql; + // Scan only the first 1024 characters to + // avoid big overhead for long queries. + if (nativeSql.lastIndexOf("search_path", 1024) != -1 + && !nativeSql.equals(lastSetSearchPathQuery)) { + // Search path was changed, invalidate prepared statement cache + lastSetSearchPathQuery = nativeSql; + deallocateEpoch++; + } + } + if (!executeData.asSimple) { pendingExecuteQueue.removeFirst(); } else { diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/SchemaTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/SchemaTest.java index 975a32fa..25d5daa5 100644 --- a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/SchemaTest.java +++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/SchemaTest.java @@ -13,8 +13,12 @@ import org.postgresql.test.TestUtil; import junit.framework.TestCase; import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; +import java.sql.Types; import java.util.Properties; public class SchemaTest extends TestCase { @@ -46,9 +50,12 @@ public class SchemaTest extends TestCase { TestUtil.createTable(_conn, "schema1.table1", "id integer"); TestUtil.createTable(_conn, "schema2.table2", "id integer"); TestUtil.createTable(_conn, "\"UpperCase\".table3", "id integer"); + TestUtil.createTable(_conn, "schema1.sptest", "id integer"); + TestUtil.createTable(_conn, "schema2.sptest", "id varchar"); } protected void tearDown() throws SQLException { + _conn.setAutoCommit(true); _conn.setSchema(null); Statement stmt = _conn.createStatement(); if (dropUserSchema) { @@ -179,4 +186,38 @@ public class SchemaTest extends TestCase { } } + public void testSearchPathPreparedStatementAutoCommitFalse() throws SQLException { + _conn.setAutoCommit(false); + testSearchPathPreparedStatement(); + } + + public void testSearchPathPreparedStatementAutoCommitTrue() throws SQLException { + testSearchPathPreparedStatement(); + } + + public void testSearchPathPreparedStatement() throws SQLException { + execute("set search_path to schema1,public"); + PreparedStatement ps = _conn.prepareStatement("select * from sptest"); + for (int i = 0; i < 10; i++) { + ps.execute(); + } + assertColType(ps, "sptest should point to schema1.sptest, thus column type should be INT", + Types.INTEGER); + ps.close(); + execute("set search_path to schema2,public"); + ps = _conn.prepareStatement("select * from sptest"); + assertColType(ps, "sptest should point to schema2.sptest, thus column type should be VARCHAR", + Types.VARCHAR); + ps.close(); + } + + private void assertColType(PreparedStatement ps, String message, int expected) throws SQLException { + ResultSet rs = ps.executeQuery(); + ResultSetMetaData md = rs.getMetaData(); + int columnType = md.getColumnType(1); + assertEquals(message, + expected, columnType); + rs.close(); + } + } [parent: aa53fbae604a]