Modified: db/derby/code/trunk/java/client/org/apache/derby/client/net/ClientJDBCObjectFactoryImpl.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/net/ClientJDBCObjectFactoryImpl.java?rev=629734&r1=629733&r2=629734&view=diff ============================================================================== --- db/derby/code/trunk/java/client/org/apache/derby/client/net/ClientJDBCObjectFactoryImpl.java (original) +++ db/derby/code/trunk/java/client/org/apache/derby/client/net/ClientJDBCObjectFactoryImpl.java Thu Feb 21 02:26:31 2008 @@ -26,18 +26,23 @@ import java.sql.SQLException; import org.apache.derby.client.ClientPooledConnection; import org.apache.derby.client.ClientXAConnection; +import org.apache.derby.client.am.CachingLogicalConnection; import org.apache.derby.client.am.CallableStatement; import org.apache.derby.client.am.ClientJDBCObjectFactory; import org.apache.derby.client.am.LogicalConnection; import org.apache.derby.client.am.ParameterMetaData; import org.apache.derby.client.am.PreparedStatement; import org.apache.derby.client.am.Configuration; +import org.apache.derby.client.am.LogicalCallableStatement; +import org.apache.derby.client.am.LogicalPreparedStatement; import org.apache.derby.client.am.LogWriter; import org.apache.derby.client.am.Agent; import org.apache.derby.client.am.Section; import org.apache.derby.client.am.Statement; import org.apache.derby.client.am.SqlException; import org.apache.derby.client.am.Cursor; +import org.apache.derby.client.am.stmtcache.JDBCStatementCache; +import org.apache.derby.client.am.stmtcache.StatementKey; import org.apache.derby.client.net.NetLogWriter; import org.apache.derby.jdbc.ClientBaseDataSource; import org.apache.derby.jdbc.ClientXADataSource; @@ -112,6 +117,26 @@ return new LogicalConnection(physicalConnection, pooledConnection); } + /** + * Returns an instance of a {@code CachingLogicalConnection}, which + * provides caching of prepared statements. + * + * @param physicalConnection the underlying physical connection + * @param pooledConnection the pooled connection + * @param stmtCache statement cache + * @return A logical connection with statement caching capabilities. + * + * @throws SqlException if creation of the logical connection fails + */ + public LogicalConnection newCachingLogicalConnection( + org.apache.derby.client.am.Connection physicalConnection, + ClientPooledConnection pooledConnection, + JDBCStatementCache stmtCache) throws SqlException { + return new CachingLogicalConnection(physicalConnection, + pooledConnection, + stmtCache); + } + /** * This method returns an instance of PreparedStatement * which implements java.sql.PreparedStatement. It has the @@ -180,6 +205,36 @@ throws SqlException { return new PreparedStatement(agent,connection,sql,type,concurrency, holdability,autoGeneratedKeys,columnNames, columnIndexes, cpc); + } + + /** + * Returns a new logcial prepared statement object. + * + * @param ps underlying physical prepared statement + * @param stmtKey key for the underlying physical prepared statement + * @param stmtCache the statement cache + * @return A logical prepared statement. + */ + public java.sql.PreparedStatement newLogicalPreparedStatement( + java.sql.PreparedStatement ps, + StatementKey stmtKey, + JDBCStatementCache stmtCache) { + return new LogicalPreparedStatement(ps, stmtKey, stmtCache); + } + + /** + * Returns a new logical callable statement object. + * + * @param cs underlying physical callable statement + * @param stmtKey key for the underlying physical callable statement + * @param stmtCache the statement cache + * @return A logical callable statement. + */ + public java.sql.CallableStatement newLogicalCallableStatement( + java.sql.CallableStatement cs, + StatementKey stmtKey, + JDBCStatementCache stmtCache) { + return new LogicalCallableStatement(cs, stmtKey, stmtCache); } /** Modified: db/derby/code/trunk/java/client/org/apache/derby/client/net/ClientJDBCObjectFactoryImpl40.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/net/ClientJDBCObjectFactoryImpl40.java?rev=629734&r1=629733&r2=629734&view=diff ============================================================================== --- db/derby/code/trunk/java/client/org/apache/derby/client/net/ClientJDBCObjectFactoryImpl40.java (original) +++ db/derby/code/trunk/java/client/org/apache/derby/client/net/ClientJDBCObjectFactoryImpl40.java Thu Feb 21 02:26:31 2008 @@ -25,6 +25,7 @@ import org.apache.derby.client.ClientPooledConnection40; import org.apache.derby.client.ClientXAConnection; import org.apache.derby.client.ClientXAConnection40; +import org.apache.derby.client.am.CachingLogicalConnection40; import org.apache.derby.client.am.CallableStatement; import org.apache.derby.client.am.CallableStatement40; import org.apache.derby.client.am.ColumnMetaData; @@ -36,6 +37,8 @@ import org.apache.derby.client.am.PreparedStatement40; import org.apache.derby.client.am.ParameterMetaData; import org.apache.derby.client.am.ParameterMetaData40; +import org.apache.derby.client.am.LogicalCallableStatement40; +import org.apache.derby.client.am.LogicalPreparedStatement40; import org.apache.derby.client.am.LogWriter; import org.apache.derby.client.am.Agent; import org.apache.derby.client.am.SQLExceptionFactory40; @@ -44,6 +47,8 @@ import org.apache.derby.client.am.Statement40; import org.apache.derby.client.am.SqlException; import org.apache.derby.client.am.Cursor; +import org.apache.derby.client.am.stmtcache.JDBCStatementCache; +import org.apache.derby.client.am.stmtcache.StatementKey; import org.apache.derby.client.net.NetLogWriter; import org.apache.derby.jdbc.ClientDataSource; import org.apache.derby.jdbc.ClientBaseDataSource; @@ -127,6 +132,26 @@ return new LogicalConnection40(physicalConnection, pooledConnection); } + /** + * Returns an instance of a {@code CachingLogicalConnection}, which + * provides caching of prepared statements. + * + * @param physicalConnection the underlying physical connection + * @param pooledConnection the pooled connection + * @param stmtCache statement cache + * @return A logical connection with statement caching capabilities. + * + * @throws SqlException if creation of the logical connection fails + */ + public LogicalConnection newCachingLogicalConnection( + org.apache.derby.client.am.Connection physicalConnection, + ClientPooledConnection pooledConnection, + JDBCStatementCache stmtCache) throws SqlException { + return new CachingLogicalConnection40(physicalConnection, + pooledConnection, + stmtCache); + } + /** * Returns an instance of org.apache.derby.client.am.CallableStatement40 */ @@ -180,7 +205,36 @@ holdability,autoGeneratedKeys,columnNames,columnIndexes, cpc); } - + /** + * Returns a new logcial prepared statement object. + * + * @param ps underlying physical prepared statement + * @param stmtKey key for the underlying physical prepared statement + * @param stmtCache the statement cache + * @return A logical prepared statement. + */ + public java.sql.PreparedStatement newLogicalPreparedStatement( + java.sql.PreparedStatement ps, + StatementKey stmtKey, + JDBCStatementCache stmtCache) { + return new LogicalPreparedStatement40(ps, stmtKey, stmtCache); + } + + /** + * Returns a new logical callable statement object. + * + * @param cs underlying physical callable statement + * @param stmtKey key for the underlying physical callable statement + * @param stmtCache the statement cache + * @return A logical callable statement. + */ + public java.sql.CallableStatement newLogicalCallableStatement( + java.sql.CallableStatement cs, + StatementKey stmtKey, + JDBCStatementCache stmtCache) { + return new LogicalCallableStatement40(cs, stmtKey, stmtCache); + } + /** * returns an instance of org.apache.derby.client.net.NetConnection40 */ Added: db/derby/code/trunk/java/testing/org/apache/derby/client/am/LogicalStatementEntityTest.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derby/client/am/LogicalStatementEntityTest.java?rev=629734&view=auto ============================================================================== --- db/derby/code/trunk/java/testing/org/apache/derby/client/am/LogicalStatementEntityTest.java (added) +++ db/derby/code/trunk/java/testing/org/apache/derby/client/am/LogicalStatementEntityTest.java Thu Feb 21 02:26:31 2008 @@ -0,0 +1,298 @@ +/* + + Derby - Class org.apache.derby.client.am.LogicalStatementEntityTest + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ +package org.apache.derby.client.am; + +import java.sql.SQLException; + +import junit.framework.Test; + +import org.apache.derby.client.am.stmtcache.JDBCStatementCache; +import org.apache.derby.client.am.stmtcache.StatementKey; +import org.apache.derby.client.am.stmtcache.StatementKeyFactory; + +import org.apache.derby.jdbc.ClientDriver; +import org.apache.derbyTesting.junit.BaseJDBCTestCase; +import org.apache.derbyTesting.junit.JDBC; +import org.apache.derbyTesting.junit.TestConfiguration; + +/** + * Tests for the handling of logical prepared statements. + */ +public class LogicalStatementEntityTest + extends BaseJDBCTestCase { + + public LogicalStatementEntityTest(String name) { + super(name); + } + + /** + * Verifies that the logical statement representing a prepared statement + * behaves correctly when it has been closed. + * + * @throws SQLException if a JDBC operation fails + */ + public void testCloseBehaviorExternalPs() + throws SQLException { + final String sql = "values 7"; + final String schema = "APP"; + java.sql.PreparedStatement ps = prepareStatement(sql); + StatementKey stmtKey = StatementKeyFactory.newPrepared( + sql, schema, getConnection().getHoldability()); + JDBCStatementCache cache = new JDBCStatementCache(10); + LogicalStatementEntity logic = + new LogicalStatementEntityClass(ps, stmtKey, cache); + assertSame(ps, logic.getPhysPs()); + assertFalse(logic.isLogicalEntityClosed()); + logic.close(); + assertTrue(logic.isLogicalEntityClosed()); + logic.close(); + logic.close(); + assertTrue(logic.isLogicalEntityClosed()); + try { + logic.getPhysPs(); + fail("Should have thrown exception"); + } catch (SQLException sqle) { + assertSQLState("XJ012", sqle); + } + } + + /** + * Verifies that the logical statement representing a callable statement + * behaves correctly when it has been closed. + * + * @throws SQLException if a JDBC operation fails + */ + public void testCloseBehaviorExternalCs() + throws SQLException { + final String sql = "values 3"; + final String schema = "APP"; + java.sql.CallableStatement cs = prepareCall(sql); + StatementKey stmtKey = StatementKeyFactory.newCallable( + sql, schema, getConnection().getHoldability()); + JDBCStatementCache cache = new JDBCStatementCache(10); + LogicalStatementEntity logic = + new LogicalStatementEntityClass(cs, stmtKey, cache); + assertSame(cs, logic.getPhysCs()); + assertFalse(logic.isLogicalEntityClosed()); + logic.close(); + assertTrue(logic.isLogicalEntityClosed()); + logic.close(); + logic.close(); + assertTrue(logic.isLogicalEntityClosed()); + try { + logic.getPhysCs(); + fail("Should have thrown exception"); + } catch (SQLException sqle) { + assertSQLState("XJ012", sqle); + } + } + + /** + * Tests that a statement equal to one in the cache is not cached when + * closing the logical statement, and that the physical statement is closed. + * + * @throws SQLException if a JDBC operation fails + */ + public void testCloseOnDuplicateStatement() + throws SQLException { + // Initial setup. + final String sql = "values 7"; + final String schema = "APP"; + java.sql.PreparedStatement ps = prepareStatement(sql); + StatementKey stmtKey = StatementKeyFactory.newPrepared( + sql, schema, getConnection().getHoldability()); + JDBCStatementCache cache = new JDBCStatementCache(10); + LogicalStatementEntity logic = + new LogicalStatementEntityClass(ps, stmtKey, cache); + assertSame(ps, logic.getPhysPs()); + assertFalse(logic.isLogicalEntityClosed()); + + // Put a statement into the cache. + assertTrue(cache.cacheStatement(stmtKey, ps)); + // Create a second statement, equal to the first. + java.sql.PreparedStatement psDupe = prepareStatement(sql); + LogicalStatementEntity logicDupe = + new LogicalStatementEntityClass(psDupe, stmtKey, cache); + // When we ask the logical entity to close the statement now, the + // underlying physical prepared statement should actually be closed. + logicDupe.close(); + assertTrue(logicDupe.isLogicalEntityClosed()); + // Since we are possibly running in pre-JDBC 4, try do do something to + // provoke exception. + try { + psDupe.execute(); + fail("Statement should have been closed and throw an exception"); + } catch (SQLException sqle) { + assertSQLState("XJ012", sqle); + } + + // The cached statement should still be open. + java.sql.PreparedStatement psCached = cache.getCached(stmtKey); + assertSame(ps, psCached); + java.sql.ResultSet rs = psCached.executeQuery(); + JDBC.assertSingleValueResultSet(rs, "7"); + } + + /** + * Asserts that closing the logical statement and caching the physical one + * does close the logical one but not the physical one. + * + * @throws SQLException if a JDBC operation fails + */ + public void testCloseWhenStatementShallBeCached() + throws SQLException { + // Initial setup. + final String sql = "values 9"; + final String schema = "APP"; + java.sql.PreparedStatement ps = prepareStatement(sql); + StatementKey stmtKey = StatementKeyFactory.newPrepared( + sql, schema, getConnection().getHoldability()); + JDBCStatementCache cache = new JDBCStatementCache(10); + LogicalStatementEntity logic = + new LogicalStatementEntityClass(ps, stmtKey, cache); + assertSame(ps, logic.getPhysPs()); + assertFalse(logic.isLogicalEntityClosed()); + + // Close the statement, it should go into the cache. + logic.close(); + assertTrue(logic.isLogicalEntityClosed()); + // Use the physical statement. + java.sql.ResultSet rs = ps.executeQuery(); + JDBC.assertSingleValueResultSet(rs, "9"); + // Get the statement from the cache. + assertSame(ps, cache.getCached(stmtKey)); + } + + /** + * Tries to execute a method on a logical statement when the underlying + * physical statement has been closed without the logical connection + * knowing. + * + * @throws SQLException if something goes wrong... + */ + public void testClosedUnderlyingStatement() + throws SQLException { + // Initial setup. + final String sql = "values 19"; + final String schema = "APP"; + java.sql.PreparedStatement ps = prepareStatement(sql); + StatementKey stmtKey = StatementKeyFactory.newPrepared( + sql, schema, getConnection().getHoldability()); + JDBCStatementCache cache = new JDBCStatementCache(10); + LogicalStatementEntity logic = + new LogicalStatementEntityClass(ps, stmtKey, cache); + assertSame(ps, logic.getPhysPs()); + assertFalse(logic.isLogicalEntityClosed()); + java.sql.PreparedStatement logicalPs = ClientDriver.getFactory(). + newLogicalPreparedStatement(ps, stmtKey, cache); + assertNotNull(logicalPs.getMetaData()); + ps.close(); + try { + logicalPs.getMetaData(); + fail("Getting meta data on a closed connection should fail"); + } catch (SQLException sqle) { + assertSQLState("XJ012", sqle); + } + logicalPs.close(); + } + + /** + * Tests that the cache throws out the least frequently used statement when + * it reaches its maximum capacity, and that the thrown out statement is + * closed in the process. + *
+ * Note: This test assumes things about the replacement policy. + * + * @throws SQLException if a JDBC operation fails + */ + public void testEvictionFromCache() + throws SQLException { + // Initial setup. + JDBCStatementCache cache = new JDBCStatementCache(2); + final String schema = "APP"; + java.sql.PreparedStatement ps1 = prepareStatement("values 1"); + java.sql.PreparedStatement ps2 = prepareStatement("values 2"); + java.sql.PreparedStatement ps3 = prepareStatement("values 3"); + StatementKey stmtKey1 = StatementKeyFactory.newPrepared( + "values 1", schema, getConnection().getHoldability()); + StatementKey stmtKey2 = StatementKeyFactory.newPrepared( + "values 2", schema, getConnection().getHoldability()); + StatementKey stmtKey3 = StatementKeyFactory.newPrepared( + "values 3", schema, getConnection().getHoldability()); + LogicalStatementEntity logic1 = + new LogicalStatementEntityClass(ps1, stmtKey1, cache); + LogicalStatementEntity logic2 = + new LogicalStatementEntityClass(ps2, stmtKey2, cache); + LogicalStatementEntity logic3 = + new LogicalStatementEntityClass(ps3, stmtKey3, cache); + + // Close the two first logical statements, putting them into the cache. + logic1.close(); + logic2.close(); + // Assert both of the statements are open. + JDBC.assertSingleValueResultSet(ps1.executeQuery(), "1"); + JDBC.assertSingleValueResultSet(ps2.executeQuery(), "2"); + // Close the third statement. It should be cached, but since the cache + // will exceed its maximum capacity, the first statement will be thrown + // out and it should be closed in the process. + logic3.close(); + JDBC.assertSingleValueResultSet(ps3.executeQuery(), "3"); + assertNull("ps1 still in the cache", cache.getCached(stmtKey1)); + try { + ps1.executeQuery(); + fail("ps1 should have been closed by the cache"); + } catch (SQLException sqle) { + assertSQLState("XJ012", sqle); + } + // Make sure the right statements are returned from the cache. + assertSame(ps2, cache.getCached(stmtKey2)); + assertSame(ps3, cache.getCached(stmtKey3)); + } + + /** + * Returns a suite of tests running in a client-server environment. + * + * @return A test suite. + */ + public static Test suite() { + return TestConfiguration.clientServerSuite( + LogicalStatementEntityTest.class); + } + + /** + * Class used to represent a logical statement. + */ + private static class LogicalStatementEntityClass + extends LogicalStatementEntity { + + /** + * Constructor creating an object handling closing of a logical + * prepared / callable statement. + * + * @param ps underlying physical prepared / callable statement + */ + public LogicalStatementEntityClass(java.sql.PreparedStatement ps, + StatementKey key, + JDBCStatementCache cache) { + super(ps, key, cache); + } + } +} Propchange: db/derby/code/trunk/java/testing/org/apache/derby/client/am/LogicalStatementEntityTest.java ------------------------------------------------------------------------------ svn:eol-style = native