Modified: 
tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java?rev=1837746&r1=1837745&r2=1837746&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java 
(original)
+++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java Thu 
Aug  9 16:50:30 2018
@@ -42,6 +42,8 @@ public class DelegatingStatement extends
     /** The connection that created me. **/
     private DelegatingConnection<?> connection;
 
+    private boolean closed = false;
+
     /**
      * Create a wrapper for the Statement which traces this Statement to the 
Connection which created it and the code
      * which created it.
@@ -58,65 +60,60 @@ public class DelegatingStatement extends
     }
 
     /**
-     * Returns my underlying {@link Statement}.
      *
-     * @return my underlying {@link Statement}.
-     * @see #getInnermostDelegate
+     * @throws SQLException
+     *             thrown by the delegating statement.
+     * @since 2.4.0 made public, was protected in 2.3.0.
      */
-    public Statement getDelegate() {
-        return statement;
+    public void activate() throws SQLException {
+        if (statement instanceof DelegatingStatement) {
+            ((DelegatingStatement) statement).activate();
+        }
     }
 
-    /**
-     * If my underlying {@link Statement} is not a {@code 
DelegatingStatement}, returns it, otherwise recursively
-     * invokes this method on my delegate.
-     * <p>
-     * Hence this method will return the first delegate that is not a {@code 
DelegatingStatement} or {@code null} when
-     * no non-{@code DelegatingStatement} delegate can be found by traversing 
this chain.
-     * </p>
-     * <p>
-     * This method is useful when you may have nested {@code 
DelegatingStatement}s, and you want to make sure to obtain
-     * a "genuine" {@link Statement}.
-     * </p>
-     *
-     * @return The innermost delegate.
-     *
-     * @see #getDelegate
-     */
-    public Statement getInnermostDelegate() {
-        Statement s = statement;
-        while (s != null && s instanceof DelegatingStatement) {
-            s = ((DelegatingStatement) s).getDelegate();
-            if (this == s) {
-                return null;
-            }
+    @Override
+    public void addBatch(final String sql) throws SQLException {
+        checkOpen();
+        try {
+            statement.addBatch(sql);
+        } catch (final SQLException e) {
+            handleException(e);
         }
-        return s;
     }
 
-    /**
-     * Sets my delegate.
-     *
-     * @param statement
-     *            my delegate.
-     */
-    public void setDelegate(final Statement statement) {
-        this.statement = statement;
+    @Override
+    public void cancel() throws SQLException {
+        checkOpen();
+        try {
+            statement.cancel();
+        } catch (final SQLException e) {
+            handleException(e);
+        }
     }
 
-    private boolean closed = false;
-
-    protected boolean isClosedInternal() {
-        return closed;
+    protected void checkOpen() throws SQLException {
+        if (isClosed()) {
+            throw new SQLException(this.getClass().getName() + " with address: 
\"" + this.toString() + "\" is closed.");
+        }
     }
 
-    protected void setClosedInternal(final boolean closed) {
-        this.closed = closed;
+    @Override
+    public void clearBatch() throws SQLException {
+        checkOpen();
+        try {
+            statement.clearBatch();
+        } catch (final SQLException e) {
+            handleException(e);
+        }
     }
 
-    protected void checkOpen() throws SQLException {
-        if (isClosed()) {
-            throw new SQLException(this.getClass().getName() + " with address: 
\"" + this.toString() + "\" is closed.");
+    @Override
+    public void clearWarnings() throws SQLException {
+        checkOpen();
+        try {
+            statement.clearWarnings();
+        } catch (final SQLException e) {
+            handleException(e);
         }
     }
 
@@ -160,113 +157,169 @@ public class DelegatingStatement extends
         }
     }
 
-    protected void handleException(final SQLException e) throws SQLException {
-        if (connection != null) {
-            connection.handleException(e);
-        } else {
-            throw e;
+    @Override
+    public void closeOnCompletion() throws SQLException {
+        checkOpen();
+        try {
+            statement.closeOnCompletion();
+        } catch (final SQLException e) {
+            handleException(e);
         }
     }
 
-    /**
-     *
-     * @throws SQLException
-     *             thrown by the delegating statement.
-     * @since 2.4.0 made public, was protected in 2.3.0.
-     */
-    public void activate() throws SQLException {
-        if (statement instanceof DelegatingStatement) {
-            ((DelegatingStatement) statement).activate();
+    @Override
+    public boolean execute(final String sql) throws SQLException {
+        checkOpen();
+        setLastUsedInParent();
+        try {
+            return statement.execute(sql);
+        } catch (final SQLException e) {
+            handleException(e);
+            return false;
         }
     }
 
-    /**
-     *
-     * @throws SQLException
-     *             thrown by the delegating statement.
-     * @since 2.4.0 made public, was protected in 2.3.0.
-     */
-    public void passivate() throws SQLException {
-        if (statement instanceof DelegatingStatement) {
-            ((DelegatingStatement) statement).passivate();
+    @Override
+    public boolean execute(final String sql, final int autoGeneratedKeys) 
throws SQLException {
+        checkOpen();
+        setLastUsedInParent();
+        try {
+            return statement.execute(sql, autoGeneratedKeys);
+        } catch (final SQLException e) {
+            handleException(e);
+            return false;
         }
     }
 
     @Override
-    public Connection getConnection() throws SQLException {
+    public boolean execute(final String sql, final int columnIndexes[]) throws 
SQLException {
         checkOpen();
-        return getConnectionInternal(); // return the delegating connection 
that created this
+        setLastUsedInParent();
+        try {
+            return statement.execute(sql, columnIndexes);
+        } catch (final SQLException e) {
+            handleException(e);
+            return false;
+        }
     }
 
-    protected DelegatingConnection<?> getConnectionInternal() {
-        return connection;
+    @Override
+    public boolean execute(final String sql, final String columnNames[]) 
throws SQLException {
+        checkOpen();
+        setLastUsedInParent();
+        try {
+            return statement.execute(sql, columnNames);
+        } catch (final SQLException e) {
+            handleException(e);
+            return false;
+        }
     }
 
     @Override
-    public ResultSet executeQuery(final String sql) throws SQLException {
+    public int[] executeBatch() throws SQLException {
         checkOpen();
-        if (connection != null) {
-            connection.setLastUsed();
-        }
+        setLastUsedInParent();
         try {
-            return DelegatingResultSet.wrapResultSet(this, 
statement.executeQuery(sql));
+            return statement.executeBatch();
         } catch (final SQLException e) {
             handleException(e);
             throw new AssertionError();
         }
     }
 
+    /**
+     * @since 2.5.0
+     */
     @Override
-    public ResultSet getResultSet() throws SQLException {
+    public long[] executeLargeBatch() throws SQLException {
         checkOpen();
+        setLastUsedInParent();
         try {
-            return DelegatingResultSet.wrapResultSet(this, 
statement.getResultSet());
+            return statement.executeLargeBatch();
         } catch (final SQLException e) {
             handleException(e);
-            throw new AssertionError();
+            return null;
         }
     }
 
+    /**
+     * @since 2.5.0
+     */
     @Override
-    public int executeUpdate(final String sql) throws SQLException {
+    public long executeLargeUpdate(final String sql) throws SQLException {
         checkOpen();
-        if (connection != null) {
-            connection.setLastUsed();
+        setLastUsedInParent();
+        try {
+            return statement.executeLargeUpdate(sql);
+        } catch (final SQLException e) {
+            handleException(e);
+            return 0;
         }
+    }
+
+    /**
+     * @since 2.5.0
+     */
+    @Override
+    public long executeLargeUpdate(final String sql, final int 
autoGeneratedKeys) throws SQLException {
+        checkOpen();
+        setLastUsedInParent();
         try {
-            return statement.executeUpdate(sql);
+            return statement.executeLargeUpdate(sql, autoGeneratedKeys);
         } catch (final SQLException e) {
             handleException(e);
             return 0;
         }
     }
 
+    /**
+     * @since 2.5.0
+     */
     @Override
-    public int getMaxFieldSize() throws SQLException {
+    public long executeLargeUpdate(final String sql, final int[] 
columnIndexes) throws SQLException {
         checkOpen();
+        setLastUsedInParent();
         try {
-            return statement.getMaxFieldSize();
+            return statement.executeLargeUpdate(sql, columnIndexes);
         } catch (final SQLException e) {
             handleException(e);
             return 0;
         }
     }
 
+    /**
+     * @since 2.5.0
+     */
     @Override
-    public void setMaxFieldSize(final int max) throws SQLException {
+    public long executeLargeUpdate(final String sql, final String[] 
columnNames) throws SQLException {
         checkOpen();
+        setLastUsedInParent();
         try {
-            statement.setMaxFieldSize(max);
+            return statement.executeLargeUpdate(sql, columnNames);
         } catch (final SQLException e) {
             handleException(e);
+            return 0;
         }
     }
 
     @Override
-    public int getMaxRows() throws SQLException {
+    public ResultSet executeQuery(final String sql) throws SQLException {
         checkOpen();
+        setLastUsedInParent();
         try {
-            return statement.getMaxRows();
+            return DelegatingResultSet.wrapResultSet(this, 
statement.executeQuery(sql));
+        } catch (final SQLException e) {
+            handleException(e);
+            throw new AssertionError();
+        }
+    }
+
+    @Override
+    public int executeUpdate(final String sql) throws SQLException {
+        checkOpen();
+        setLastUsedInParent();
+        try {
+            return statement.executeUpdate(sql);
         } catch (final SQLException e) {
             handleException(e);
             return 0;
@@ -274,30 +327,35 @@ public class DelegatingStatement extends
     }
 
     @Override
-    public void setMaxRows(final int max) throws SQLException {
+    public int executeUpdate(final String sql, final int autoGeneratedKeys) 
throws SQLException {
         checkOpen();
+        setLastUsedInParent();
         try {
-            statement.setMaxRows(max);
+            return statement.executeUpdate(sql, autoGeneratedKeys);
         } catch (final SQLException e) {
             handleException(e);
+            return 0;
         }
     }
 
     @Override
-    public void setEscapeProcessing(final boolean enable) throws SQLException {
+    public int executeUpdate(final String sql, final int columnIndexes[]) 
throws SQLException {
         checkOpen();
+        setLastUsedInParent();
         try {
-            statement.setEscapeProcessing(enable);
+            return statement.executeUpdate(sql, columnIndexes);
         } catch (final SQLException e) {
             handleException(e);
+            return 0;
         }
     }
 
     @Override
-    public int getQueryTimeout() throws SQLException {
+    public int executeUpdate(final String sql, final String columnNames[]) 
throws SQLException {
         checkOpen();
+        setLastUsedInParent();
         try {
-            return statement.getQueryTimeout();
+            return statement.executeUpdate(sql, columnNames);
         } catch (final SQLException e) {
             handleException(e);
             return 0;
@@ -305,75 +363,144 @@ public class DelegatingStatement extends
     }
 
     @Override
-    public void setQueryTimeout(final int seconds) throws SQLException {
+    protected void finalize() throws Throwable {
+        // This is required because of statement pooling. The poolable
+        // statements will always be strongly held by the statement pool. If 
the
+        // delegating statements that wrap the poolable statement are not
+        // strongly held they will be garbage collected but at that point the
+        // poolable statements need to be returned to the pool else there will
+        // be a leak of statements from the pool. Closing this statement will
+        // close all the wrapped statements and return any poolable statements
+        // to the pool.
+        close();
+        super.finalize();
+    }
+
+    @Override
+    public Connection getConnection() throws SQLException {
+        checkOpen();
+        return getConnectionInternal(); // return the delegating connection 
that created this
+    }
+
+    protected DelegatingConnection<?> getConnectionInternal() {
+        return connection;
+    }
+
+    /**
+     * Returns my underlying {@link Statement}.
+     *
+     * @return my underlying {@link Statement}.
+     * @see #getInnermostDelegate
+     */
+    public Statement getDelegate() {
+        return statement;
+    }
+
+    @Override
+    public int getFetchDirection() throws SQLException {
         checkOpen();
         try {
-            statement.setQueryTimeout(seconds);
+            return statement.getFetchDirection();
         } catch (final SQLException e) {
             handleException(e);
+            return 0;
         }
     }
 
     @Override
-    public void cancel() throws SQLException {
+    public int getFetchSize() throws SQLException {
         checkOpen();
         try {
-            statement.cancel();
+            return statement.getFetchSize();
         } catch (final SQLException e) {
             handleException(e);
+            return 0;
         }
     }
 
     @Override
-    public SQLWarning getWarnings() throws SQLException {
+    public ResultSet getGeneratedKeys() throws SQLException {
         checkOpen();
         try {
-            return statement.getWarnings();
+            return DelegatingResultSet.wrapResultSet(this, 
statement.getGeneratedKeys());
         } catch (final SQLException e) {
             handleException(e);
             throw new AssertionError();
         }
     }
 
+    /**
+     * If my underlying {@link Statement} is not a {@code 
DelegatingStatement}, returns it, otherwise recursively
+     * invokes this method on my delegate.
+     * <p>
+     * Hence this method will return the first delegate that is not a {@code 
DelegatingStatement} or {@code null} when
+     * no non-{@code DelegatingStatement} delegate can be found by traversing 
this chain.
+     * </p>
+     * <p>
+     * This method is useful when you may have nested {@code 
DelegatingStatement}s, and you want to make sure to obtain
+     * a "genuine" {@link Statement}.
+     * </p>
+     *
+     * @return The innermost delegate.
+     *
+     * @see #getDelegate
+     */
+    @SuppressWarnings("resource")
+    public Statement getInnermostDelegate() {
+        Statement s = statement;
+        while (s != null && s instanceof DelegatingStatement) {
+            s = ((DelegatingStatement) s).getDelegate();
+            if (this == s) {
+                return null;
+            }
+        }
+        return s;
+    }
+
+    /**
+     * @since 2.5.0
+     */
     @Override
-    public void clearWarnings() throws SQLException {
+    public long getLargeMaxRows() throws SQLException {
         checkOpen();
         try {
-            statement.clearWarnings();
+            return statement.getLargeMaxRows();
         } catch (final SQLException e) {
             handleException(e);
+            return 0;
         }
     }
 
+    /**
+     * @since 2.5.0
+     */
     @Override
-    public void setCursorName(final String name) throws SQLException {
+    public long getLargeUpdateCount() throws SQLException {
         checkOpen();
         try {
-            statement.setCursorName(name);
+            return statement.getLargeUpdateCount();
         } catch (final SQLException e) {
             handleException(e);
+            return 0;
         }
     }
 
     @Override
-    public boolean execute(final String sql) throws SQLException {
+    public int getMaxFieldSize() throws SQLException {
         checkOpen();
-        if (connection != null) {
-            connection.setLastUsed();
-        }
         try {
-            return statement.execute(sql);
+            return statement.getMaxFieldSize();
         } catch (final SQLException e) {
             handleException(e);
-            return false;
+            return 0;
         }
     }
 
     @Override
-    public int getUpdateCount() throws SQLException {
+    public int getMaxRows() throws SQLException {
         checkOpen();
         try {
-            return statement.getUpdateCount();
+            return statement.getMaxRows();
         } catch (final SQLException e) {
             handleException(e);
             return 0;
@@ -392,20 +519,21 @@ public class DelegatingStatement extends
     }
 
     @Override
-    public void setFetchDirection(final int direction) throws SQLException {
+    public boolean getMoreResults(final int current) throws SQLException {
         checkOpen();
         try {
-            statement.setFetchDirection(direction);
+            return statement.getMoreResults(current);
         } catch (final SQLException e) {
             handleException(e);
+            return false;
         }
     }
 
     @Override
-    public int getFetchDirection() throws SQLException {
+    public int getQueryTimeout() throws SQLException {
         checkOpen();
         try {
-            return statement.getFetchDirection();
+            return statement.getQueryTimeout();
         } catch (final SQLException e) {
             handleException(e);
             return 0;
@@ -413,20 +541,21 @@ public class DelegatingStatement extends
     }
 
     @Override
-    public void setFetchSize(final int rows) throws SQLException {
+    public ResultSet getResultSet() throws SQLException {
         checkOpen();
         try {
-            statement.setFetchSize(rows);
+            return DelegatingResultSet.wrapResultSet(this, 
statement.getResultSet());
         } catch (final SQLException e) {
             handleException(e);
+            throw new AssertionError();
         }
     }
 
     @Override
-    public int getFetchSize() throws SQLException {
+    public int getResultSetConcurrency() throws SQLException {
         checkOpen();
         try {
-            return statement.getFetchSize();
+            return statement.getResultSetConcurrency();
         } catch (final SQLException e) {
             handleException(e);
             return 0;
@@ -434,10 +563,10 @@ public class DelegatingStatement extends
     }
 
     @Override
-    public int getResultSetConcurrency() throws SQLException {
+    public int getResultSetHoldability() throws SQLException {
         checkOpen();
         try {
-            return statement.getResultSetConcurrency();
+            return statement.getResultSetHoldability();
         } catch (final SQLException e) {
             handleException(e);
             return 0;
@@ -456,54 +585,52 @@ public class DelegatingStatement extends
     }
 
     @Override
-    public void addBatch(final String sql) throws SQLException {
+    public int getUpdateCount() throws SQLException {
         checkOpen();
         try {
-            statement.addBatch(sql);
+            return statement.getUpdateCount();
         } catch (final SQLException e) {
             handleException(e);
+            return 0;
         }
     }
 
     @Override
-    public void clearBatch() throws SQLException {
+    public SQLWarning getWarnings() throws SQLException {
         checkOpen();
         try {
-            statement.clearBatch();
+            return statement.getWarnings();
         } catch (final SQLException e) {
             handleException(e);
+            throw new AssertionError();
         }
     }
 
-    @Override
-    public int[] executeBatch() throws SQLException {
-        checkOpen();
+    protected void handleException(final SQLException e) throws SQLException {
         if (connection != null) {
-            connection.setLastUsed();
-        }
-        try {
-            return statement.executeBatch();
-        } catch (final SQLException e) {
-            handleException(e);
-            throw new AssertionError();
+            connection.handleException(e);
+        } else {
+            throw e;
         }
     }
 
-    /**
-     * Returns a String representation of this object.
-     *
-     * @return String
+    /*
+     * Note was protected prior to JDBC 4
      */
     @Override
-    public String toString() {
-        return statement == null ? "NULL" : statement.toString();
+    public boolean isClosed() throws SQLException {
+        return closed;
+    }
+
+    protected boolean isClosedInternal() {
+        return closed;
     }
 
     @Override
-    public boolean getMoreResults(final int current) throws SQLException {
+    public boolean isCloseOnCompletion() throws SQLException {
         checkOpen();
         try {
-            return statement.getMoreResults(current);
+            return statement.isCloseOnCompletion();
         } catch (final SQLException e) {
             handleException(e);
             return false;
@@ -511,194 +638,170 @@ public class DelegatingStatement extends
     }
 
     @Override
-    public ResultSet getGeneratedKeys() throws SQLException {
+    public boolean isPoolable() throws SQLException {
         checkOpen();
         try {
-            return DelegatingResultSet.wrapResultSet(this, 
statement.getGeneratedKeys());
+            return statement.isPoolable();
         } catch (final SQLException e) {
             handleException(e);
-            throw new AssertionError();
+            return false;
         }
     }
 
     @Override
-    public int executeUpdate(final String sql, final int autoGeneratedKeys) 
throws SQLException {
-        checkOpen();
-        if (connection != null) {
-            connection.setLastUsed();
-        }
-        try {
-            return statement.executeUpdate(sql, autoGeneratedKeys);
-        } catch (final SQLException e) {
-            handleException(e);
-            return 0;
+    public boolean isWrapperFor(final Class<?> iface) throws SQLException {
+        if (iface.isAssignableFrom(getClass())) {
+            return true;
+        } else if (iface.isAssignableFrom(statement.getClass())) {
+            return true;
+        } else {
+            return statement.isWrapperFor(iface);
         }
     }
 
-    @Override
-    public int executeUpdate(final String sql, final int columnIndexes[]) 
throws SQLException {
-        checkOpen();
-        if (connection != null) {
-            connection.setLastUsed();
-        }
-        try {
-            return statement.executeUpdate(sql, columnIndexes);
-        } catch (final SQLException e) {
-            handleException(e);
-            return 0;
+    /**
+     *
+     * @throws SQLException
+     *             thrown by the delegating statement.
+     * @since 2.4.0 made public, was protected in 2.3.0.
+     */
+    public void passivate() throws SQLException {
+        if (statement instanceof DelegatingStatement) {
+            ((DelegatingStatement) statement).passivate();
         }
     }
 
+    protected void setClosedInternal(final boolean closed) {
+        this.closed = closed;
+    }
+
     @Override
-    public int executeUpdate(final String sql, final String columnNames[]) 
throws SQLException {
+    public void setCursorName(final String name) throws SQLException {
         checkOpen();
-        if (connection != null) {
-            connection.setLastUsed();
-        }
         try {
-            return statement.executeUpdate(sql, columnNames);
+            statement.setCursorName(name);
         } catch (final SQLException e) {
             handleException(e);
-            return 0;
         }
     }
 
+    /**
+     * Sets my delegate.
+     *
+     * @param statement
+     *            my delegate.
+     */
+    public void setDelegate(final Statement statement) {
+        this.statement = statement;
+    }
+
     @Override
-    public boolean execute(final String sql, final int autoGeneratedKeys) 
throws SQLException {
+    public void setEscapeProcessing(final boolean enable) throws SQLException {
         checkOpen();
-        if (connection != null) {
-            connection.setLastUsed();
-        }
         try {
-            return statement.execute(sql, autoGeneratedKeys);
+            statement.setEscapeProcessing(enable);
         } catch (final SQLException e) {
             handleException(e);
-            return false;
         }
     }
 
     @Override
-    public boolean execute(final String sql, final int columnIndexes[]) throws 
SQLException {
+    public void setFetchDirection(final int direction) throws SQLException {
         checkOpen();
-        if (connection != null) {
-            connection.setLastUsed();
-        }
         try {
-            return statement.execute(sql, columnIndexes);
+            statement.setFetchDirection(direction);
         } catch (final SQLException e) {
             handleException(e);
-            return false;
         }
     }
 
     @Override
-    public boolean execute(final String sql, final String columnNames[]) 
throws SQLException {
+    public void setFetchSize(final int rows) throws SQLException {
         checkOpen();
-        if (connection != null) {
-            connection.setLastUsed();
-        }
         try {
-            return statement.execute(sql, columnNames);
+            statement.setFetchSize(rows);
         } catch (final SQLException e) {
             handleException(e);
-            return false;
         }
     }
 
+    /**
+     * @since 2.5.0
+     */
     @Override
-    public int getResultSetHoldability() throws SQLException {
+    public void setLargeMaxRows(final long max) throws SQLException {
         checkOpen();
         try {
-            return statement.getResultSetHoldability();
+            statement.setLargeMaxRows(max);
         } catch (final SQLException e) {
             handleException(e);
-            return 0;
-        }
-    }
-
-    /*
-     * Note was protected prior to JDBC 4
-     */
-    @Override
-    public boolean isClosed() throws SQLException {
-        return closed;
-    }
-
-    @Override
-    public boolean isWrapperFor(final Class<?> iface) throws SQLException {
-        if (iface.isAssignableFrom(getClass())) {
-            return true;
-        } else if (iface.isAssignableFrom(statement.getClass())) {
-            return true;
-        } else {
-            return statement.isWrapperFor(iface);
         }
     }
 
-    @Override
-    public <T> T unwrap(final Class<T> iface) throws SQLException {
-        if (iface.isAssignableFrom(getClass())) {
-            return iface.cast(this);
-        } else if (iface.isAssignableFrom(statement.getClass())) {
-            return iface.cast(statement);
-        } else {
-            return statement.unwrap(iface);
+    private void setLastUsedInParent() {
+        if (connection != null) {
+            connection.setLastUsed();
         }
     }
 
     @Override
-    public void setPoolable(final boolean poolable) throws SQLException {
+    public void setMaxFieldSize(final int max) throws SQLException {
         checkOpen();
         try {
-            statement.setPoolable(poolable);
+            statement.setMaxFieldSize(max);
         } catch (final SQLException e) {
             handleException(e);
         }
     }
 
     @Override
-    public boolean isPoolable() throws SQLException {
+    public void setMaxRows(final int max) throws SQLException {
         checkOpen();
         try {
-            return statement.isPoolable();
+            statement.setMaxRows(max);
         } catch (final SQLException e) {
             handleException(e);
-            return false;
         }
     }
 
     @Override
-    public void closeOnCompletion() throws SQLException {
+    public void setPoolable(final boolean poolable) throws SQLException {
         checkOpen();
         try {
-            statement.closeOnCompletion();
+            statement.setPoolable(poolable);
         } catch (final SQLException e) {
             handleException(e);
         }
     }
 
     @Override
-    public boolean isCloseOnCompletion() throws SQLException {
+    public void setQueryTimeout(final int seconds) throws SQLException {
         checkOpen();
         try {
-            return statement.isCloseOnCompletion();
+            statement.setQueryTimeout(seconds);
         } catch (final SQLException e) {
             handleException(e);
-            return false;
         }
     }
 
+    /**
+     * Returns a String representation of this object.
+     *
+     * @return String
+     */
     @Override
-    protected void finalize() throws Throwable {
-        // This is required because of statement pooling. The poolable
-        // statements will always be strongly held by the statement pool. If 
the
-        // delegating statements that wrap the poolable statement are not
-        // strongly held they will be garbage collected but at that point the
-        // poolable statements need to be returned to the pool else there will
-        // be a leak of statements from the pool. Closing this statement will
-        // close all the wrapped statements and return any poolable statements
-        // to the pool.
-        close();
-        super.finalize();
+    public String toString() {
+        return statement == null ? "NULL" : statement.toString();
+    }
+
+    @Override
+    public <T> T unwrap(final Class<T> iface) throws SQLException {
+        if (iface.isAssignableFrom(getClass())) {
+            return iface.cast(this);
+        } else if (iface.isAssignableFrom(statement.getClass())) {
+            return iface.cast(statement);
+        } else {
+            return statement.unwrap(iface);
+        }
     }
 }

Modified: 
tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/DriverConnectionFactory.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/DriverConnectionFactory.java?rev=1837746&r1=1837745&r2=1837746&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/DriverConnectionFactory.java 
(original)
+++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/DriverConnectionFactory.java 
Thu Aug  9 16:50:30 2018
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 package org.apache.tomcat.dbcp.dbcp2;
+
 import java.sql.Connection;
 import java.sql.Driver;
 import java.sql.SQLException;

Modified: 
tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/DriverManagerConnectionFactory.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/DriverManagerConnectionFactory.java?rev=1837746&r1=1837745&r2=1837746&view=diff
==============================================================================
--- 
tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/DriverManagerConnectionFactory.java
 (original)
+++ 
tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/DriverManagerConnectionFactory.java
 Thu Aug  9 16:50:30 2018
@@ -16,6 +16,7 @@
  */
 
 package org.apache.tomcat.dbcp.dbcp2;
+
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.SQLException;

Modified: tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/PStmtKey.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/PStmtKey.java?rev=1837746&r1=1837745&r2=1837746&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/PStmtKey.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/PStmtKey.java Thu Aug  9 
16:50:30 2018
@@ -17,7 +17,6 @@
 package org.apache.tomcat.dbcp.dbcp2;
 
 import java.sql.Connection;
-import java.sql.PreparedStatement;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.util.Arrays;
@@ -32,6 +31,105 @@ import org.apache.tomcat.dbcp.dbcp2.Pool
 public class PStmtKey {
 
     /**
+     * Builder for prepareCall(String sql).
+     */
+    private class PreparedCallSQL implements StatementBuilder {
+        @Override
+        public Statement createStatement(final Connection connection) throws 
SQLException {
+            return connection.prepareCall(sql);
+        }
+    }
+
+    /**
+     * Builder for prepareCall(String sql, int resultSetType, int 
resultSetConcurrency).
+     */
+    private class PreparedCallWithResultSetConcurrency implements 
StatementBuilder {
+        @Override
+        public Statement createStatement(final Connection connection) throws 
SQLException {
+            return connection.prepareCall(sql, resultSetType.intValue(), 
resultSetConcurrency.intValue());
+        }
+    }
+
+    /**
+     * Builder for prepareCall(String sql, int resultSetType, int 
resultSetConcurrency, int resultSetHoldability).
+     */
+    private class PreparedCallWithResultSetHoldability implements 
StatementBuilder {
+        @Override
+        public Statement createStatement(final Connection connection) throws 
SQLException {
+            return connection.prepareCall(sql, resultSetType.intValue(), 
resultSetConcurrency.intValue(),
+                    resultSetHoldability.intValue());
+        }
+    }
+
+    /**
+     * Builder for prepareStatement(String sql).
+     */
+    private class PreparedStatementSQL implements StatementBuilder {
+        @Override
+        public Statement createStatement(final Connection connection) throws 
SQLException {
+            return connection.prepareStatement(sql);
+        }
+    }
+
+    /**
+     * Builder for prepareStatement(String sql, int autoGeneratedKeys).
+     */
+    private class PreparedStatementWithAutoGeneratedKeys implements 
StatementBuilder {
+        @Override
+        public Statement createStatement(final Connection connection) throws 
SQLException {
+            return connection.prepareStatement(sql, 
autoGeneratedKeys.intValue());
+        }
+    }
+
+    /**
+     * Builder for prepareStatement(String sql, int[] columnIndexes).
+     */
+    private class PreparedStatementWithColumnIndexes implements 
StatementBuilder {
+        @Override
+        public Statement createStatement(final Connection connection) throws 
SQLException {
+            return connection.prepareStatement(sql, columnIndexes);
+        }
+    }
+
+    /**
+     * Builder for prepareStatement(String sql, String[] columnNames).
+     */
+    private class PreparedStatementWithColumnNames implements StatementBuilder 
{
+        @Override
+        public Statement createStatement(final Connection connection) throws 
SQLException {
+            return connection.prepareStatement(sql, columnNames);
+        }
+    }
+
+    /**
+     * Builder for prepareStatement(String sql, int resultSetType, int 
resultSetConcurrency).
+     */
+    private class PreparedStatementWithResultSetConcurrency implements 
StatementBuilder {
+        @Override
+        public Statement createStatement(final Connection connection) throws 
SQLException {
+            return connection.prepareStatement(sql, resultSetType.intValue(), 
resultSetConcurrency.intValue());
+        }
+    }
+
+    /**
+     * Builder for prepareStatement(String sql, int resultSetType, int 
resultSetConcurrency, int resultSetHoldability).
+     */
+    private class PreparedStatementWithResultSetHoldability implements 
StatementBuilder {
+        @Override
+        public Statement createStatement(final Connection connection) throws 
SQLException {
+            return connection.prepareStatement(sql, resultSetType.intValue(), 
resultSetConcurrency.intValue(),
+                    resultSetHoldability.intValue());
+        }
+    }
+
+    /**
+     * Interface for Prepared or Callable Statement.
+     */
+    private interface StatementBuilder {
+        Statement createStatement(Connection connection) throws SQLException;
+    }
+
+    /**
      * SQL defining Prepared or Callable Statement
      */
     private final String sql;
@@ -54,9 +152,12 @@ public class PStmtKey {
      */
     private final Integer resultSetHoldability;
 
-    /** Database catalog */
+    /** Database catalog. */
     private final String catalog;
 
+    /** Database schema. */
+    private final String schema;
+
     /**
      * A flag indicating whether auto-generated keys should be returned; one of
      * <code>Statement.RETURN_GENERATED_KEYS</code> or 
<code>Statement.NO_GENERATED_KEYS</code>.
@@ -79,14 +180,16 @@ public class PStmtKey {
     private final StatementType statementType;
 
     /** Statement builder */
-    private StatementBuilder builder;
+    private transient StatementBuilder builder;
 
     /**
      * Constructs a key to uniquely identify a prepared statement.
      *
      * @param sql
      *            The SQL statement.
+     * @deprecated Use {@link #PStmtKey(String, String, String)}.
      */
+    @Deprecated
     public PStmtKey(final String sql) {
         this(sql, null, StatementType.PREPARED_STATEMENT);
     }
@@ -96,11 +199,17 @@ public class PStmtKey {
      *
      * @param sql
      *            The SQL statement.
-     * @param catalog
-     *            The catalog.
+     * @param resultSetType
+     *            A result set type; one of 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
+     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
+     * @param resultSetConcurrency
+     *            A concurrency type; one of 
<code>ResultSet.CONCUR_READ_ONLY</code> or
+     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
+     * @deprecated Use {@link #PStmtKey(String, String, String, int, int)}.
      */
-    public PStmtKey(final String sql, final String catalog) {
-        this(sql, catalog, StatementType.PREPARED_STATEMENT);
+    @Deprecated
+    public PStmtKey(final String sql, final int resultSetType, final int 
resultSetConcurrency) {
+        this(sql, null, resultSetType, resultSetConcurrency, 
StatementType.PREPARED_STATEMENT);
     }
 
     /**
@@ -110,25 +219,11 @@ public class PStmtKey {
      *            The SQL statement.
      * @param catalog
      *            The catalog.
-     * @param statementType
-     *            The SQL statement type, prepared or callable.
+     * @deprecated Use {@link #PStmtKey(String, String, String)}.
      */
-    public PStmtKey(final String sql, final String catalog, final 
StatementType statementType) {
-        this.sql = sql;
-        this.catalog = catalog;
-        this.statementType = statementType;
-        this.autoGeneratedKeys = null;
-        this.columnIndexes = null;
-        this.columnNames = null;
-        this.resultSetType = null;
-        this.resultSetConcurrency = null;
-        this.resultSetHoldability = null;
-        // create builder
-        if (statementType == StatementType.PREPARED_STATEMENT) {
-            this.builder = new PreparedStatementSQL();
-        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
-            this.builder = new PreparedCallSQL();
-        }
+    @Deprecated
+    public PStmtKey(final String sql, final String catalog) {
+        this(sql, catalog, StatementType.PREPARED_STATEMENT);
     }
 
     /**
@@ -141,7 +236,9 @@ public class PStmtKey {
      * @param autoGeneratedKeys
      *            A flag indicating whether auto-generated keys should be 
returned; one of
      *            <code>Statement.RETURN_GENERATED_KEYS</code> or 
<code>Statement.NO_GENERATED_KEYS</code>.
+     * @deprecated Use {@link #PStmtKey(String, String, String, int)}.
      */
+    @Deprecated
     public PStmtKey(final String sql, final String catalog, final int 
autoGeneratedKeys) {
         this(sql, catalog, StatementType.PREPARED_STATEMENT, 
Integer.valueOf(autoGeneratedKeys));
     }
@@ -153,54 +250,17 @@ public class PStmtKey {
      *            The SQL statement.
      * @param catalog
      *            The catalog.
-     * @param statementType
-     *            The SQL statement type, prepared or callable.
-     * @param autoGeneratedKeys
-     *            A flag indicating whether auto-generated keys should be 
returned; one of
-     *            <code>Statement.RETURN_GENERATED_KEYS</code> or 
<code>Statement.NO_GENERATED_KEYS</code>.
-     */
-    public PStmtKey(final String sql, final String catalog, final 
StatementType statementType,
-            final Integer autoGeneratedKeys) {
-        this.sql = sql;
-        this.catalog = catalog;
-        this.statementType = statementType;
-        this.autoGeneratedKeys = autoGeneratedKeys;
-        this.columnIndexes = null;
-        this.columnNames = null;
-        this.resultSetType = null;
-        this.resultSetConcurrency = null;
-        this.resultSetHoldability = null;
-        // create builder
-        if (statementType == StatementType.PREPARED_STATEMENT) {
-            this.builder = new PreparedStatementWithAutoGeneratedKeys();
-        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
-            this.builder = new PreparedCallSQL();
-        }
-    }
-
-    /**
-     * Constructs a key to uniquely identify a prepared statement.
-     *
-     * @param sql
-     *            The SQL statement.
-     * @param catalog
-     *            The catalog.
-     * @param columnIndexes
-     *            An array of column indexes indicating the columns that 
should be returned from the inserted row or
-     *            rows.
+     * @param resultSetType
+     *            A result set type; one of 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
+     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
+     * @param resultSetConcurrency
+     *            A concurrency type; one of 
<code>ResultSet.CONCUR_READ_ONLY</code> or
+     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
+     * @deprecated Use @link {@link #PStmtKey(String, String, String, int, 
int)}.
      */
-    public PStmtKey(final String sql, final String catalog, final int[] 
columnIndexes) {
-        this.sql = sql;
-        this.catalog = catalog;
-        this.statementType = StatementType.PREPARED_STATEMENT;
-        this.autoGeneratedKeys = null;
-        this.columnIndexes = columnIndexes == null ? null : 
Arrays.copyOf(columnIndexes, columnIndexes.length);
-        this.columnNames = null;
-        this.resultSetType = null;
-        this.resultSetConcurrency = null;
-        this.resultSetHoldability = null;
-        // create builder
-        this.builder = new PreparedStatementWithColumnIndexes();
+    @Deprecated
+    public PStmtKey(final String sql, final String catalog, final int 
resultSetType, final int resultSetConcurrency) {
+        this(sql, catalog, resultSetType, resultSetConcurrency, 
StatementType.PREPARED_STATEMENT);
     }
 
     /**
@@ -210,37 +270,21 @@ public class PStmtKey {
      *            The SQL statement.
      * @param catalog
      *            The catalog.
-     * @param columnNames
-     *            An array of column names indicating the columns that should 
be returned from the inserted row or rows.
-     */
-    public PStmtKey(final String sql, final String catalog, final String[] 
columnNames) {
-        this.sql = sql;
-        this.catalog = catalog;
-        this.statementType = StatementType.PREPARED_STATEMENT;
-        this.autoGeneratedKeys = null;
-        this.columnIndexes = null;
-        this.columnNames = columnNames == null ? null : 
Arrays.copyOf(columnNames, columnNames.length);
-        this.resultSetType = null;
-        this.resultSetConcurrency = null;
-        this.resultSetHoldability = null;
-        // create builder
-        builder = new PreparedStatementWithColumnNames();
-    }
-
-    /**
-     * Constructs a key to uniquely identify a prepared statement.
-     *
-     * @param sql
-     *            The SQL statement.
      * @param resultSetType
-     *            A result set type; one of 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
+     *            a result set type; one of 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
      *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
      * @param resultSetConcurrency
      *            A concurrency type; one of 
<code>ResultSet.CONCUR_READ_ONLY</code> or
-     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
+     *            <code>ResultSet.CONCUR_UPDATABLE</code>
+     * @param resultSetHoldability
+     *            One of the following <code>ResultSet</code> constants: 
<code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
+     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
+     * @deprecated Use {@link #PStmtKey(String, String, String, int, int, 
int)}.
      */
-    public PStmtKey(final String sql, final int resultSetType, final int 
resultSetConcurrency) {
-        this(sql, null, resultSetType, resultSetConcurrency, 
StatementType.PREPARED_STATEMENT);
+    @Deprecated
+    public PStmtKey(final String sql, final String catalog, final int 
resultSetType, final int resultSetConcurrency,
+            final int resultSetHoldability) {
+        this(sql, catalog, resultSetType, resultSetConcurrency, 
resultSetHoldability, StatementType.PREPARED_STATEMENT);
     }
 
     /**
@@ -251,14 +295,37 @@ public class PStmtKey {
      * @param catalog
      *            The catalog.
      * @param resultSetType
-     *            A result set type; one of 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
-     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
+     *            a result set type; one of 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
+     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
      * @param resultSetConcurrency
      *            A concurrency type; one of 
<code>ResultSet.CONCUR_READ_ONLY</code> or
      *            <code>ResultSet.CONCUR_UPDATABLE</code>.
+     * @param resultSetHoldability
+     *            One of the following <code>ResultSet</code> constants: 
<code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
+     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
+     * @param statementType
+     *            The SQL statement type, prepared or callable.
+     * @deprecated Use {@link #PStmtKey(String, String, String, int, int, int, 
PoolingConnection.StatementType)}
      */
-    public PStmtKey(final String sql, final String catalog, final int 
resultSetType, final int resultSetConcurrency) {
-        this(sql, catalog, resultSetType, resultSetConcurrency, 
StatementType.PREPARED_STATEMENT);
+    @Deprecated
+    public PStmtKey(final String sql, final String catalog, final int 
resultSetType, final int resultSetConcurrency,
+            final int resultSetHoldability, final StatementType statementType) 
{
+        this.sql = sql;
+        this.catalog = catalog;
+        this.schema = null;
+        this.resultSetType = Integer.valueOf(resultSetType);
+        this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
+        this.resultSetHoldability = Integer.valueOf(resultSetHoldability);
+        this.statementType = statementType;
+        this.autoGeneratedKeys = null;
+        this.columnIndexes = null;
+        this.columnNames = null;
+        // create builder
+        if (statementType == StatementType.PREPARED_STATEMENT) {
+            this.builder = new PreparedStatementWithResultSetHoldability();
+        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
+            this.builder = new PreparedCallWithResultSetHoldability();
+        }
     }
 
     /**
@@ -276,11 +343,14 @@ public class PStmtKey {
      *            <code>ResultSet.CONCUR_UPDATABLE</code>.
      * @param statementType
      *            The SQL statement type, prepared or callable.
+     * @deprecated Use {@link #PStmtKey(String, String, String, int, int, 
PoolingConnection.StatementType)}.
      */
+    @Deprecated
     public PStmtKey(final String sql, final String catalog, final int 
resultSetType, final int resultSetConcurrency,
             final StatementType statementType) {
         this.sql = sql;
         this.catalog = catalog;
+        this.schema = null;
         this.resultSetType = Integer.valueOf(resultSetType);
         this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
         this.resultSetHoldability = null;
@@ -303,28 +373,180 @@ public class PStmtKey {
      *            The SQL statement.
      * @param catalog
      *            The catalog.
-     * @param resultSetType
-     *            a result set type; one of 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
-     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
-     * @param resultSetConcurrency
-     *            A concurrency type; one of 
<code>ResultSet.CONCUR_READ_ONLY</code> or
-     *            <code>ResultSet.CONCUR_UPDATABLE</code>
-     * @param resultSetHoldability
-     *            One of the following <code>ResultSet</code> constants: 
<code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
-     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
+     * @param columnIndexes
+     *            An array of column indexes indicating the columns that 
should be returned from the inserted row or
+     *            rows.
+     * @deprecated Use {@link #PStmtKey(String, String, String, int[])}.
      */
-    public PStmtKey(final String sql, final String catalog, final int 
resultSetType, final int resultSetConcurrency,
-            final int resultSetHoldability) {
-        this(sql, catalog, resultSetType, resultSetConcurrency, 
resultSetHoldability, StatementType.PREPARED_STATEMENT);
-    }
-
-    /**
-     * Constructs a key to uniquely identify a prepared statement.
+    @Deprecated
+    public PStmtKey(final String sql, final String catalog, final int[] 
columnIndexes) {
+        this.sql = sql;
+        this.catalog = catalog;
+        this.schema = null;
+        this.statementType = StatementType.PREPARED_STATEMENT;
+        this.autoGeneratedKeys = null;
+        this.columnIndexes = columnIndexes == null ? null : 
Arrays.copyOf(columnIndexes, columnIndexes.length);
+        this.columnNames = null;
+        this.resultSetType = null;
+        this.resultSetConcurrency = null;
+        this.resultSetHoldability = null;
+        // create builder
+        this.builder = new PreparedStatementWithColumnIndexes();
+    }
+
+    /**
+     * Constructs a key to uniquely identify a prepared statement.
      *
      * @param sql
      *            The SQL statement.
      * @param catalog
      *            The catalog.
+     * @param statementType
+     *            The SQL statement type, prepared or callable.
+     * @deprecated Use {@link #PStmtKey(String, String, String, 
PoolingConnection.StatementType)}.
+     */
+    @Deprecated
+    public PStmtKey(final String sql, final String catalog, final 
StatementType statementType) {
+        this.sql = sql;
+        this.catalog = catalog;
+        this.schema = null;
+        this.statementType = statementType;
+        this.autoGeneratedKeys = null;
+        this.columnIndexes = null;
+        this.columnNames = null;
+        this.resultSetType = null;
+        this.resultSetConcurrency = null;
+        this.resultSetHoldability = null;
+        // create builder
+        if (statementType == StatementType.PREPARED_STATEMENT) {
+            this.builder = new PreparedStatementSQL();
+        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
+            this.builder = new PreparedCallSQL();
+        }
+    }
+
+    /**
+     * Constructs a key to uniquely identify a prepared statement.
+     *
+     * @param sql
+     *            The SQL statement.
+     * @param catalog
+     *            The catalog.
+     * @param statementType
+     *            The SQL statement type, prepared or callable.
+     * @param autoGeneratedKeys
+     *            A flag indicating whether auto-generated keys should be 
returned; one of
+     *            <code>Statement.RETURN_GENERATED_KEYS</code> or 
<code>Statement.NO_GENERATED_KEYS</code>.
+     * @deprecated Use {@link #PStmtKey(String, String, String, 
PoolingConnection.StatementType, Integer)}
+     */
+    @Deprecated
+    public PStmtKey(final String sql, final String catalog, final 
StatementType statementType,
+            final Integer autoGeneratedKeys) {
+        this.sql = sql;
+        this.catalog = catalog;
+        this.schema = null;
+        this.statementType = statementType;
+        this.autoGeneratedKeys = autoGeneratedKeys;
+        this.columnIndexes = null;
+        this.columnNames = null;
+        this.resultSetType = null;
+        this.resultSetConcurrency = null;
+        this.resultSetHoldability = null;
+        // create builder
+        if (statementType == StatementType.PREPARED_STATEMENT) {
+            this.builder = new PreparedStatementWithAutoGeneratedKeys();
+        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
+            this.builder = new PreparedCallSQL();
+        }
+    }
+
+    /**
+     * Constructs a key to uniquely identify a prepared statement.
+     *
+     * @param sql
+     *            The SQL statement.
+     * @param catalog
+     *            The catalog.
+     * @param schema
+     *            The schema
+     * @since 2.5.0
+     */
+    public PStmtKey(final String sql, final String catalog, final String 
schema) {
+        this(sql, catalog, schema, StatementType.PREPARED_STATEMENT);
+    }
+
+    /**
+     * Constructs a key to uniquely identify a prepared statement.
+     *
+     * @param sql
+     *            The SQL statement.
+     * @param catalog
+     *            The catalog.
+     * @param schema
+     *            The schema
+     * @param autoGeneratedKeys
+     *            A flag indicating whether auto-generated keys should be 
returned; one of
+     *            <code>Statement.RETURN_GENERATED_KEYS</code> or 
<code>Statement.NO_GENERATED_KEYS</code>.
+     * @since 2.5.0
+     */
+    public PStmtKey(final String sql, final String catalog, final String 
schema, final int autoGeneratedKeys) {
+        this(sql, catalog, schema, StatementType.PREPARED_STATEMENT, 
Integer.valueOf(autoGeneratedKeys));
+    }
+
+    /**
+     * Constructs a key to uniquely identify a prepared statement.
+     *
+     * @param sql
+     *            The SQL statement.
+     * @param catalog
+     *            The catalog.
+     * @param schema
+     *            The schema
+     * @param resultSetType
+     *            A result set type; one of 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
+     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
+     * @param resultSetConcurrency
+     *            A concurrency type; one of 
<code>ResultSet.CONCUR_READ_ONLY</code> or
+     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
+     */
+    public PStmtKey(final String sql, final String catalog, final String 
schema, final int resultSetType, final int resultSetConcurrency) {
+        this(sql, catalog, schema, resultSetType, resultSetConcurrency, 
StatementType.PREPARED_STATEMENT);
+    }
+
+    /**
+     * Constructs a key to uniquely identify a prepared statement.
+     *
+     * @param sql
+     *            The SQL statement.
+     * @param catalog
+     *            The catalog.
+     * @param schema
+     *            The schema
+     * @param resultSetType
+     *            a result set type; one of 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
+     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
+     * @param resultSetConcurrency
+     *            A concurrency type; one of 
<code>ResultSet.CONCUR_READ_ONLY</code> or
+     *            <code>ResultSet.CONCUR_UPDATABLE</code>
+     * @param resultSetHoldability
+     *            One of the following <code>ResultSet</code> constants: 
<code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code>
+     *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
+     * @since 2.5.0
+     */
+    public PStmtKey(final String sql, final String catalog, final String 
schema, final int resultSetType, final int resultSetConcurrency,
+            final int resultSetHoldability) {
+        this(sql, catalog, schema, resultSetType, resultSetConcurrency, 
resultSetHoldability, StatementType.PREPARED_STATEMENT);
+    }
+
+    /**
+     * Constructs a key to uniquely identify a prepared statement.
+     *
+     * @param sql
+     *            The SQL statement.
+     * @param catalog
+     *            The catalog.
+     * @param schema
+     *            The schema.
      * @param resultSetType
      *            a result set type; one of 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
      *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
@@ -336,11 +558,13 @@ public class PStmtKey {
      *            or <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
      * @param statementType
      *            The SQL statement type, prepared or callable.
+     * @since 2.5.0
      */
-    public PStmtKey(final String sql, final String catalog, final int 
resultSetType, final int resultSetConcurrency,
+    public PStmtKey(final String sql, final String catalog, final String 
schema, final int resultSetType, final int resultSetConcurrency,
             final int resultSetHoldability, final StatementType statementType) 
{
         this.sql = sql;
         this.catalog = catalog;
+        this.schema = schema;
         this.resultSetType = Integer.valueOf(resultSetType);
         this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
         this.resultSetHoldability = Integer.valueOf(resultSetHoldability);
@@ -357,88 +581,209 @@ public class PStmtKey {
     }
 
     /**
-     * Gets the SQL statement.
-     *
-     * @return the SQL statement.
-     */
-    public String getSql() {
-        return sql;
-    }
-
-    /**
-     * Gets the result set type, one of 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
-     * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
-     *
-     * @return the result set type.
-     */
-    public Integer getResultSetType() {
-        return resultSetType;
-    }
-
-    /**
-     * Gets the result set concurrency type; one of 
<code>ResultSet.CONCUR_READ_ONLY</code> or
-     * <code>ResultSet.CONCUR_UPDATABLE</code>.
+     * Constructs a key to uniquely identify a prepared statement.
      *
-     * @return The result set concurrency type.
+     * @param sql
+     *            The SQL statement.
+     * @param catalog
+     *            The catalog.
+     * @param schema
+     *            The schema.
+     * @param resultSetType
+     *            A result set type; one of 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
+     *            <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
+     * @param resultSetConcurrency
+     *            A concurrency type; one of 
<code>ResultSet.CONCUR_READ_ONLY</code> or
+     *            <code>ResultSet.CONCUR_UPDATABLE</code>.
+     * @param statementType
+     *            The SQL statement type, prepared or callable.
+     * @since 2.5.0
      */
-    public Integer getResultSetConcurrency() {
-        return resultSetConcurrency;
+    public PStmtKey(final String sql, final String catalog, final String 
schema, final int resultSetType, final int resultSetConcurrency,
+            final StatementType statementType) {
+        this.sql = sql;
+        this.catalog = catalog;
+        this.schema = schema;
+        this.resultSetType = Integer.valueOf(resultSetType);
+        this.resultSetConcurrency = Integer.valueOf(resultSetConcurrency);
+        this.resultSetHoldability = null;
+        this.statementType = statementType;
+        this.autoGeneratedKeys = null;
+        this.columnIndexes = null;
+        this.columnNames = null;
+        // create builder
+        if (statementType == StatementType.PREPARED_STATEMENT) {
+            this.builder = new PreparedStatementWithResultSetConcurrency();
+        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
+            this.builder = new PreparedCallWithResultSetConcurrency();
+        }
     }
 
     /**
-     * Gets the result set holdability, one of the following 
<code>ResultSet</code> constants:
-     * <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or 
<code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
+     * Constructs a key to uniquely identify a prepared statement.
      *
-     * @return The result set holdability.
+     * @param sql
+     *            The SQL statement.
+     * @param catalog
+     *            The catalog.
+     * @param schema
+     *            The schema.
+     * @param columnIndexes
+     *            An array of column indexes indicating the columns that 
should be returned from the inserted row or
+     *            rows.
      */
-    public Integer getResultSetHoldability() {
-        return resultSetHoldability;
+    public PStmtKey(final String sql, final String catalog, final String 
schema, final int[] columnIndexes) {
+        this.sql = sql;
+        this.catalog = catalog;
+        this.schema = schema;
+        this.statementType = StatementType.PREPARED_STATEMENT;
+        this.autoGeneratedKeys = null;
+        this.columnIndexes = columnIndexes == null ? null : 
Arrays.copyOf(columnIndexes, columnIndexes.length);
+        this.columnNames = null;
+        this.resultSetType = null;
+        this.resultSetConcurrency = null;
+        this.resultSetHoldability = null;
+        // create builder
+        this.builder = new PreparedStatementWithColumnIndexes();
     }
 
     /**
-     * Gets a flag indicating whether auto-generated keys should be returned; 
one of
-     * <code>Statement.RETURN_GENERATED_KEYS</code> or 
<code>Statement.NO_GENERATED_KEYS</code>.
+     * Constructs a key to uniquely identify a prepared statement.
      *
-     * @return a flag indicating whether auto-generated keys should be 
returned.
+     * @param sql
+     *            The SQL statement.
+     * @param catalog
+     *            The catalog.
+     * @param schema
+     *            The schema.
+     * @param statementType
+     *            The SQL statement type, prepared or callable.
+     * @since 2.5.0
      */
-    public Integer getAutoGeneratedKeys() {
-        return autoGeneratedKeys;
+    public PStmtKey(final String sql, final String catalog, final String 
schema, final StatementType statementType) {
+        this.sql = sql;
+        this.catalog = catalog;
+        this.schema = schema;
+        this.statementType = statementType;
+        this.autoGeneratedKeys = null;
+        this.columnIndexes = null;
+        this.columnNames = null;
+        this.resultSetType = null;
+        this.resultSetConcurrency = null;
+        this.resultSetHoldability = null;
+        // create builder
+        if (statementType == StatementType.PREPARED_STATEMENT) {
+            this.builder = new PreparedStatementSQL();
+        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
+            this.builder = new PreparedCallSQL();
+        }
     }
 
     /**
-     * Gets an array of column indexes indicating the columns that should be 
returned from the inserted row or rows.
+     * Constructs a key to uniquely identify a prepared statement.
      *
-     * @return An array of column indexes.
+     * @param sql
+     *            The SQL statement.
+     * @param catalog
+     *            The catalog.
+     * @param schema
+     *            The schema.
+     * @param statementType
+     *            The SQL statement type, prepared or callable.
+     * @param autoGeneratedKeys
+     *            A flag indicating whether auto-generated keys should be 
returned; one of
+     *            <code>Statement.RETURN_GENERATED_KEYS</code> or 
<code>Statement.NO_GENERATED_KEYS</code>.
+     * @since 2.5.0
      */
-    public int[] getColumnIndexes() {
-        return columnIndexes;
+    public PStmtKey(final String sql, final String catalog, final String 
schema, final StatementType statementType,
+            final Integer autoGeneratedKeys) {
+        this.sql = sql;
+        this.catalog = catalog;
+        this.schema = schema;
+        this.statementType = statementType;
+        this.autoGeneratedKeys = autoGeneratedKeys;
+        this.columnIndexes = null;
+        this.columnNames = null;
+        this.resultSetType = null;
+        this.resultSetConcurrency = null;
+        this.resultSetHoldability = null;
+        // create builder
+        if (statementType == StatementType.PREPARED_STATEMENT) {
+            this.builder = new PreparedStatementWithAutoGeneratedKeys();
+        } else if (statementType == StatementType.CALLABLE_STATEMENT) {
+            this.builder = new PreparedCallSQL();
+        }
     }
 
     /**
-     * Gets an array of column names indicating the columns that should be 
returned from the inserted row or rows.
+     * Constructs a key to uniquely identify a prepared statement.
      *
-     * @return An array of column names.
-     */
-    public String[] getColumnNames() {
-        return columnNames;
+     * @param sql
+     *            The SQL statement.
+     * @param catalog
+     *            The catalog.
+     * @param schema
+     *            The schema.
+     * @param columnNames
+     *            An array of column names indicating the columns that should 
be returned from the inserted row or rows.
+     * @since 2.5.0
+     */
+    public PStmtKey(final String sql, final String catalog, final String 
schema, final String[] columnNames) {
+        this.sql = sql;
+        this.catalog = catalog;
+        this.schema = schema;
+        this.statementType = StatementType.PREPARED_STATEMENT;
+        this.autoGeneratedKeys = null;
+        this.columnIndexes = null;
+        this.columnNames = columnNames == null ? null : 
Arrays.copyOf(columnNames, columnNames.length);
+        this.resultSetType = null;
+        this.resultSetConcurrency = null;
+        this.resultSetHoldability = null;
+        // create builder
+        builder = new PreparedStatementWithColumnNames();
     }
 
     /**
-     * The catalog.
+     * Constructs a key to uniquely identify a prepared statement.
      *
-     * @return The catalog.
+     * @param sql
+     *            The SQL statement.
+     * @param catalog
+     *            The catalog.
+     * @param columnNames
+     *            An array of column names indicating the columns that should 
be returned from the inserted row or rows.
+     * @deprecated Use {@link #PStmtKey(String, String, String, String[])}.
      */
-    public String getCatalog() {
-        return catalog;
+    @Deprecated
+    public PStmtKey(final String sql, final String catalog, final String[] 
columnNames) {
+        this.sql = sql;
+        this.catalog = catalog;
+        this.schema = null;
+        this.statementType = StatementType.PREPARED_STATEMENT;
+        this.autoGeneratedKeys = null;
+        this.columnIndexes = null;
+        this.columnNames = columnNames == null ? null : 
Arrays.copyOf(columnNames, columnNames.length);
+        this.resultSetType = null;
+        this.resultSetConcurrency = null;
+        this.resultSetHoldability = null;
+        // create builder
+        builder = new PreparedStatementWithColumnNames();
     }
 
     /**
-     * The SQL statement type.
+     * Creates a new Statement from the given Connection.
      *
-     * @return The SQL statement type.
+     * @param connection
+     *            The Connection to use to create the statement.
+     * @return The statement.
+     * @throws SQLException
+     *             Thrown when there is a problem creating the statement.
      */
-    public StatementType getStmtType() {
-        return statementType;
+    public Statement createStatement(final Connection connection) throws 
SQLException {
+        if (builder == null) {
+            throw new IllegalStateException("Prepared statement key is 
invalid.");
+        }
+        return builder.createStatement(connection);
     }
 
     @Override
@@ -453,6 +798,13 @@ public class PStmtKey {
             return false;
         }
         final PStmtKey other = (PStmtKey) obj;
+        if (autoGeneratedKeys == null) {
+            if (other.autoGeneratedKeys != null) {
+                return false;
+            }
+        } else if (!autoGeneratedKeys.equals(other.autoGeneratedKeys)) {
+            return false;
+        }
         if (catalog == null) {
             if (other.catalog != null) {
                 return false;
@@ -460,6 +812,12 @@ public class PStmtKey {
         } else if (!catalog.equals(other.catalog)) {
             return false;
         }
+        if (!Arrays.equals(columnIndexes, other.columnIndexes)) {
+            return false;
+        }
+        if (!Arrays.equals(columnNames, other.columnNames)) {
+            return false;
+        }
         if (resultSetConcurrency == null) {
             if (other.resultSetConcurrency != null) {
                 return false;
@@ -467,13 +825,6 @@ public class PStmtKey {
         } else if (!resultSetConcurrency.equals(other.resultSetConcurrency)) {
             return false;
         }
-        if (resultSetType == null) {
-            if (other.resultSetType != null) {
-                return false;
-            }
-        } else if (!resultSetType.equals(other.resultSetType)) {
-            return false;
-        }
         if (resultSetHoldability == null) {
             if (other.resultSetHoldability != null) {
                 return false;
@@ -481,17 +832,18 @@ public class PStmtKey {
         } else if (!resultSetHoldability.equals(other.resultSetHoldability)) {
             return false;
         }
-        if (autoGeneratedKeys == null) {
-            if (other.autoGeneratedKeys != null) {
+        if (resultSetType == null) {
+            if (other.resultSetType != null) {
                 return false;
             }
-        } else if (!autoGeneratedKeys.equals(other.autoGeneratedKeys)) {
-            return false;
-        }
-        if (!Arrays.equals(columnIndexes, other.columnIndexes)) {
+        } else if (!resultSetType.equals(other.resultSetType)) {
             return false;
         }
-        if (!Arrays.equals(columnNames, other.columnNames)) {
+        if (schema == null) {
+            if (other.schema != null) {
+                return false;
+            }
+        } else if (!schema.equals(other.schema)) {
             return false;
         }
         if (sql == null) {
@@ -507,169 +859,140 @@ public class PStmtKey {
         return true;
     }
 
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + (catalog == null ? 0 : catalog.hashCode());
-        result = prime * result + (resultSetConcurrency == null ? 0 : 
resultSetConcurrency.hashCode());
-        result = prime * result + (resultSetType == null ? 0 : 
resultSetType.hashCode());
-        result = prime * result + (resultSetHoldability == null ? 0 : 
resultSetHoldability.hashCode());
-        result = prime * result + (sql == null ? 0 : sql.hashCode());
-        result = prime * result + (autoGeneratedKeys == null ? 0 : 
autoGeneratedKeys.hashCode());
-        result = prime * result + Arrays.hashCode(columnIndexes);
-        result = prime * result + Arrays.hashCode(columnNames);
-        result = prime * result + statementType.hashCode();
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        final StringBuffer buf = new StringBuffer();
-        buf.append("PStmtKey: sql=");
-        buf.append(sql);
-        buf.append(", catalog=");
-        buf.append(catalog);
-        buf.append(", resultSetType=");
-        buf.append(resultSetType);
-        buf.append(", resultSetConcurrency=");
-        buf.append(resultSetConcurrency);
-        buf.append(", resultSetHoldability=");
-        buf.append(resultSetHoldability);
-        buf.append(", autoGeneratedKeys=");
-        buf.append(autoGeneratedKeys);
-        buf.append(", columnIndexes=");
-        buf.append(Arrays.toString(columnIndexes));
-        buf.append(", columnNames=");
-        buf.append(Arrays.toString(columnNames));
-        buf.append(", statementType=");
-        buf.append(statementType);
-        return buf.toString();
-    }
-
     /**
-     * Creates a new Statement from the given Connection.
+     * Gets a flag indicating whether auto-generated keys should be returned; 
one of
+     * <code>Statement.RETURN_GENERATED_KEYS</code> or 
<code>Statement.NO_GENERATED_KEYS</code>.
      *
-     * @param connection
-     *            The Connection to use to create the statement.
-     * @return The statement.
-     * @throws SQLException
-     *             Thrown when there is a problem creating the statement.
+     * @return a flag indicating whether auto-generated keys should be 
returned.
      */
-    public Statement createStatement(final Connection connection) throws 
SQLException {
-        if (builder == null) {
-            throw new IllegalStateException("Prepared statement key is 
invalid.");
-        }
-        return builder.createStatement(connection);
+    public Integer getAutoGeneratedKeys() {
+        return autoGeneratedKeys;
     }
 
     /**
-     * Interface for Prepared or Callable Statement.
+     * The catalog.
+     *
+     * @return The catalog.
      */
-    private interface StatementBuilder {
-        public Statement createStatement(Connection connection) throws 
SQLException;
+    public String getCatalog() {
+        return catalog;
     }
 
     /**
-     * Builder for prepareStatement(String sql).
+     * Gets an array of column indexes indicating the columns that should be 
returned from the inserted row or rows.
+     *
+     * @return An array of column indexes.
      */
-    private class PreparedStatementSQL implements StatementBuilder {
-        @Override
-        public Statement createStatement(final Connection connection) throws 
SQLException {
-            final PreparedStatement statement = 
connection.prepareStatement(sql);
-            return statement;
-        }
+    public int[] getColumnIndexes() {
+        return columnIndexes;
     }
 
     /**
-     * Builder for prepareStatement(String sql, int autoGeneratedKeys).
+     * Gets an array of column names indicating the columns that should be 
returned from the inserted row or rows.
+     *
+     * @return An array of column names.
      */
-    private class PreparedStatementWithAutoGeneratedKeys implements 
StatementBuilder {
-        @Override
-        public Statement createStatement(final Connection connection) throws 
SQLException {
-            final PreparedStatement statement = 
connection.prepareStatement(sql, autoGeneratedKeys.intValue());
-            return statement;
-        }
+    public String[] getColumnNames() {
+        return columnNames;
     }
 
     /**
-     * Builder for prepareStatement(String sql, int[] columnIndexes).
+     * Gets the result set concurrency type; one of 
<code>ResultSet.CONCUR_READ_ONLY</code> or
+     * <code>ResultSet.CONCUR_UPDATABLE</code>.
+     *
+     * @return The result set concurrency type.
      */
-    private class PreparedStatementWithColumnIndexes implements 
StatementBuilder {
-        @Override
-        public Statement createStatement(final Connection connection) throws 
SQLException {
-            final PreparedStatement statement = 
connection.prepareStatement(sql, columnIndexes);
-            return statement;
-        }
+    public Integer getResultSetConcurrency() {
+        return resultSetConcurrency;
     }
 
     /**
-     * Builder for prepareStatement(String sql, int resultSetType, int 
resultSetConcurrency).
+     * Gets the result set holdability, one of the following 
<code>ResultSet</code> constants:
+     * <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or 
<code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>.
+     *
+     * @return The result set holdability.
      */
-    private class PreparedStatementWithResultSetConcurrency implements 
StatementBuilder {
-        @Override
-        public Statement createStatement(final Connection connection) throws 
SQLException {
-            final PreparedStatement statement = 
connection.prepareStatement(sql, resultSetType.intValue(),
-                    resultSetConcurrency.intValue());
-            return statement;
-        }
+    public Integer getResultSetHoldability() {
+        return resultSetHoldability;
     }
 
     /**
-     * Builder for prepareStatement(String sql, int resultSetType, int 
resultSetConcurrency, int resultSetHoldability).
+     * Gets the result set type, one of 
<code>ResultSet.TYPE_FORWARD_ONLY</code>,
+     * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or 
<code>ResultSet.TYPE_SCROLL_SENSITIVE</code>.
+     *
+     * @return the result set type.
      */
-    private class PreparedStatementWithResultSetHoldability implements 
StatementBuilder {
-        @Override
-        public Statement createStatement(final Connection connection) throws 
SQLException {
-            final PreparedStatement statement = 
connection.prepareStatement(sql, resultSetType.intValue(),
-                    resultSetConcurrency.intValue(), 
resultSetHoldability.intValue());
-            return statement;
-        }
+    public Integer getResultSetType() {
+        return resultSetType;
     }
 
     /**
-     * Builder for prepareStatement(String sql, String[] columnNames).
+     * The schema.
+     *
+     * @return The catalog.
      */
-    private class PreparedStatementWithColumnNames implements StatementBuilder 
{
-        @Override
-        public Statement createStatement(final Connection connection) throws 
SQLException {
-            final PreparedStatement statement = 
connection.prepareStatement(sql, columnNames);
-            return statement;
-        }
+    public String getSchema() {
+        return schema;
     }
 
     /**
-     * Builder for prepareCall(String sql).
+     * Gets the SQL statement.
+     *
+     * @return the SQL statement.
      */
-    private class PreparedCallSQL implements StatementBuilder {
-        @Override
-        public Statement createStatement(final Connection connection) throws 
SQLException {
-            final PreparedStatement statement = connection.prepareCall(sql);
-            return statement;
-        }
+    public String getSql() {
+        return sql;
     }
 
     /**
-     * Builder for prepareCall(String sql, int resultSetType, int 
resultSetConcurrency).
+     * The SQL statement type.
+     *
+     * @return The SQL statement type.
      */
-    private class PreparedCallWithResultSetConcurrency implements 
StatementBuilder {
-        @Override
-        public Statement createStatement(final Connection connection) throws 
SQLException {
-            final PreparedStatement statement = connection.prepareCall(sql, 
resultSetType.intValue(),
-                    resultSetConcurrency.intValue());
-            return statement;
-        }
+    public StatementType getStmtType() {
+        return statementType;
     }
 
-    /**
-     * Builder for prepareCall(String sql, int resultSetType, int 
resultSetConcurrency, int resultSetHoldability).
-     */
-    private class PreparedCallWithResultSetHoldability implements 
StatementBuilder {
-        @Override
-        public Statement createStatement(final Connection connection) throws 
SQLException {
-            final PreparedStatement statement = connection.prepareCall(sql, 
resultSetType.intValue(),
-                    resultSetConcurrency.intValue(), 
resultSetHoldability.intValue());
-            return statement;
-        }
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((autoGeneratedKeys == null) ? 0 : 
autoGeneratedKeys.hashCode());
+        result = prime * result + ((catalog == null) ? 0 : catalog.hashCode());
+        result = prime * result + Arrays.hashCode(columnIndexes);
+        result = prime * result + Arrays.hashCode(columnNames);
+        result = prime * result + ((resultSetConcurrency == null) ? 0 : 
resultSetConcurrency.hashCode());
+        result = prime * result + ((resultSetHoldability == null) ? 0 : 
resultSetHoldability.hashCode());
+        result = prime * result + ((resultSetType == null) ? 0 : 
resultSetType.hashCode());
+        result = prime * result + ((schema == null) ? 0 : schema.hashCode());
+        result = prime * result + ((sql == null) ? 0 : sql.hashCode());
+        result = prime * result + ((statementType == null) ? 0 : 
statementType.hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuffer buf = new StringBuffer();
+        buf.append("PStmtKey: sql=");
+        buf.append(sql);
+        buf.append(", catalog=");
+        buf.append(catalog);
+        buf.append(", schema=");
+        buf.append(schema);
+        buf.append(", resultSetType=");
+        buf.append(resultSetType);
+        buf.append(", resultSetConcurrency=");
+        buf.append(resultSetConcurrency);
+        buf.append(", resultSetHoldability=");
+        buf.append(resultSetHoldability);
+        buf.append(", autoGeneratedKeys=");
+        buf.append(autoGeneratedKeys);
+        buf.append(", columnIndexes=");
+        buf.append(Arrays.toString(columnIndexes));
+        buf.append(", columnNames=");
+        buf.append(Arrays.toString(columnNames));
+        buf.append(", statementType=");
+        buf.append(statementType);
+        return buf.toString();
     }
 }

Modified: tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java?rev=1837746&r1=1837745&r2=1837746&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java 
(original)
+++ tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java Thu 
Aug  9 16:50:30 2018
@@ -117,7 +117,7 @@ public class PoolableConnection extends
      */
     public PoolableConnection(final Connection conn, final 
ObjectPool<PoolableConnection> pool,
             final ObjectName jmxName) {
-        this(conn, pool, jmxName, null, false);
+        this(conn, pool, jmxName, null, true);
     }
 
     @Override

Modified: 
tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java?rev=1837746&r1=1837745&r2=1837746&view=diff
==============================================================================
--- 
tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java 
(original)
+++ 
tomcat/trunk/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java 
Thu Aug  9 16:50:30 2018
@@ -157,6 +157,17 @@ public class PoolableConnectionFactory i
         this.defaultCatalog = defaultCatalog;
     }
 
+    /**
+     * Sets the default "schema" setting for borrowed {@link Connection}s
+     *
+     * @param defaultSchema
+     *            the default "schema" setting for borrowed {@link Connection}s
+     * @since 2.5.0
+     */
+    public void setDefaultSchema(final String defaultSchema) {
+        this.defaultSchema = defaultSchema;
+    }
+
     public void setCacheState(final boolean cacheState) {
         this.cacheState = cacheState;
     }
@@ -259,6 +270,7 @@ public class PoolableConnectionFactory i
      * @return true if connections created by this factory will fast fail 
validation.
      * @see #setDisconnectionSqlCodes(Collection)
      * @since 2.1
+     * @since 2.5.0 Defaults to true, previous versions defaulted to false.
      */
     public boolean isFastFailValidation() {
         return fastFailValidation;
@@ -312,10 +324,11 @@ public class PoolableConnectionFactory i
             } else {
                 config.setJmxEnabled(false);
             }
+            final PoolingConnection poolingConn = (PoolingConnection) conn;
             final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> 
stmtPool = new GenericKeyedObjectPool<>(
-                    (PoolingConnection) conn, config);
-            ((PoolingConnection) conn).setStatementPool(stmtPool);
-            ((PoolingConnection) conn).setCacheState(cacheState);
+                    poolingConn, config);
+            poolingConn.setStatementPool(stmtPool);
+            poolingConn.setCacheState(cacheState);
         }
 
         // Register this connection with JMX
@@ -427,6 +440,9 @@ public class PoolableConnectionFactory i
         if (defaultCatalog != null && 
!defaultCatalog.equals(conn.getCatalog())) {
             conn.setCatalog(defaultCatalog);
         }
+        if (defaultSchema != null && !defaultSchema.equals(conn.getSchema())) {
+            conn.setSchema(defaultSchema);
+        }
         conn.setDefaultQueryTimeout(defaultQueryTimeoutSeconds);
     }
 
@@ -470,7 +486,7 @@ public class PoolableConnectionFactory i
     private volatile int validationQueryTimeoutSeconds = -1;
     private Collection<String> connectionInitSqls;
     private Collection<String> disconnectionSqlCodes;
-    private boolean fastFailValidation;
+    private boolean fastFailValidation = true;
     private volatile ObjectPool<PoolableConnection> pool;
     private Boolean defaultReadOnly;
     private Boolean defaultAutoCommit;
@@ -478,6 +494,7 @@ public class PoolableConnectionFactory i
     private boolean rollbackOnReturn = true;
     private int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION;
     private String defaultCatalog;
+    private String defaultSchema;
     private boolean cacheState;
     private boolean poolStatements;
     private int maxOpenPreparedStatements = 
GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY;



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to