blob: 734ebcc2b231290a3c5a82f15d579a58cf8ba36d [file] [log] [blame]
/*
* 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.commons.dbcp2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* TestSuite for BasicDataSource with prepared statement pooling enabled
*/
public class TestPStmtPoolingBasicDataSource extends TestBasicDataSource {
@Override
@BeforeEach
public void setUp() throws Exception {
super.setUp();
// PoolPreparedStatements enabled, should not affect the basic tests
ds.setPoolPreparedStatements(true);
ds.setMaxOpenPreparedStatements(2);
}
@Test
public void testPreparedStatementPooling() throws Exception {
final Connection conn = getConnection();
assertNotNull(conn);
final PreparedStatement stmt1 = conn.prepareStatement("select 'a' from dual");
assertNotNull(stmt1);
final PreparedStatement stmt2 = conn.prepareStatement("select 'b' from dual");
assertNotNull(stmt2);
assertTrue(stmt1 != stmt2);
// go over the maxOpen limit
PreparedStatement stmt3 = null;
try (PreparedStatement ps = conn.prepareStatement("select 'c' from dual")) {
fail("expected SQLException");
}
catch (final SQLException e) {}
// make idle
stmt2.close();
// test cleanup the 'b' statement
stmt3 = conn.prepareStatement("select 'c' from dual");
assertNotNull(stmt3);
assertTrue(stmt3 != stmt1);
assertTrue(stmt3 != stmt2);
// normal reuse of statement
stmt1.close();
try (final PreparedStatement stmt4 = conn.prepareStatement("select 'a' from dual")) {
assertNotNull(stmt4);
}
}
/**
* Verifies that the prepared statement pool behaves as an LRU cache,
* closing least-recently-used statements idle in the pool to make room
* for new ones if necessary.
*/
@Test
public void testLRUBehavior() throws Exception {
ds.setMaxOpenPreparedStatements(3);
final Connection conn = getConnection();
assertNotNull(conn);
// Open 3 statements and then close them into the pool
final PreparedStatement stmt1 = conn.prepareStatement("select 'a' from dual");
final PreparedStatement inner1 = (PreparedStatement) ((DelegatingPreparedStatement) stmt1).getInnermostDelegate();
final PreparedStatement stmt2 = conn.prepareStatement("select 'b' from dual");
final PreparedStatement inner2 = (PreparedStatement) ((DelegatingPreparedStatement) stmt2).getInnermostDelegate();
final PreparedStatement stmt3 = conn.prepareStatement("select 'c' from dual");
final PreparedStatement inner3 = (PreparedStatement) ((DelegatingPreparedStatement) stmt3).getInnermostDelegate();
stmt1.close();
Thread.sleep(100); // Make sure return timestamps are different
stmt2.close();
Thread.sleep(100);
stmt3.close();
// Pool now has three idle statements, getting another one will force oldest (stmt1) out
final PreparedStatement stmt4 = conn.prepareStatement("select 'd' from dual");
assertNotNull(stmt4);
// Verify that inner1 has been closed
try {
inner1.clearParameters();
fail("expecting SQLExcption - statement should be closed");
} catch (final SQLException ex) {
//Expected
}
// But others are still open
inner2.clearParameters();
inner3.clearParameters();
// Now make sure stmt1 does not come back from the dead
final PreparedStatement stmt5 = conn.prepareStatement("select 'a' from dual");
final PreparedStatement inner5 = (PreparedStatement) ((DelegatingPreparedStatement) stmt5).getInnermostDelegate();
assertNotSame(inner5, inner1);
// inner2 should be closed now
try {
inner2.clearParameters();
fail("expecting SQLExcption - statement should be closed");
} catch (final SQLException ex) {
//Expected
}
// But inner3 should still be open
inner3.clearParameters();
}
// Bugzilla Bug 27246
// PreparedStatement cache should be different depending on the Catalog
@Test
public void testPStmtCatalog() throws Exception {
final Connection conn = getConnection();
conn.setCatalog("catalog1");
final DelegatingPreparedStatement stmt1 = (DelegatingPreparedStatement) conn.prepareStatement("select 'a' from dual");
final TesterPreparedStatement inner1 = (TesterPreparedStatement) stmt1.getInnermostDelegate();
assertEquals("catalog1", inner1.getCatalog());
stmt1.close();
conn.setCatalog("catalog2");
final DelegatingPreparedStatement stmt2 = (DelegatingPreparedStatement) conn.prepareStatement("select 'a' from dual");
final TesterPreparedStatement inner2 = (TesterPreparedStatement) stmt2.getInnermostDelegate();
assertEquals("catalog2", inner2.getCatalog());
stmt2.close();
conn.setCatalog("catalog1");
final DelegatingPreparedStatement stmt3 = (DelegatingPreparedStatement) conn.prepareStatement("select 'a' from dual");
final TesterPreparedStatement inner3 = (TesterPreparedStatement) stmt3.getInnermostDelegate();
assertEquals("catalog1", inner3.getCatalog());
stmt3.close();
assertNotSame(inner1, inner2);
assertSame(inner1, inner3);
}
@Test
public void testPStmtPoolingWithNoClose() throws Exception {
ds.setMaxTotal(1); // only one connection in pool needed
ds.setMaxIdle(1);
ds.setAccessToUnderlyingConnectionAllowed(true);
final Connection conn1 = getConnection();
assertNotNull(conn1);
assertEquals(1, ds.getNumActive());
assertEquals(0, ds.getNumIdle());
final PreparedStatement stmt1 = conn1.prepareStatement("select 'a' from dual");
assertNotNull(stmt1);
final Statement inner1 = ((DelegatingPreparedStatement) stmt1).getInnermostDelegate();
assertNotNull(inner1);
stmt1.close();
final Connection conn2 = conn1;
assertNotNull(conn2);
assertEquals(1, ds.getNumActive());
assertEquals(0, ds.getNumIdle());
final PreparedStatement stmt2 = conn2.prepareStatement("select 'a' from dual");
assertNotNull(stmt2);
final Statement inner2 = ((DelegatingPreparedStatement) stmt2).getInnermostDelegate();
assertNotNull(inner2);
assertSame(inner1, inner2);
}
@Test
public void testPStmtPoolingAcrossClose() throws Exception {
ds.setMaxTotal(1); // only one connection in pool needed
ds.setMaxIdle(1);
ds.setAccessToUnderlyingConnectionAllowed(true);
final Connection conn1 = getConnection();
assertNotNull(conn1);
assertEquals(1, ds.getNumActive());
assertEquals(0, ds.getNumIdle());
final PreparedStatement stmt1 = conn1.prepareStatement("select 'a' from dual");
assertNotNull(stmt1);
final Statement inner1 = ((DelegatingPreparedStatement) stmt1).getInnermostDelegate();
assertNotNull(inner1);
stmt1.close();
conn1.close();
assertEquals(0, ds.getNumActive());
assertEquals(1, ds.getNumIdle());
final Connection conn2 = getConnection();
assertNotNull(conn2);
assertEquals(1, ds.getNumActive());
assertEquals(0, ds.getNumIdle());
final PreparedStatement stmt2 = conn2.prepareStatement("select 'a' from dual");
assertNotNull(stmt2);
final Statement inner2 = ((DelegatingPreparedStatement) stmt2).getInnermostDelegate();
assertNotNull(inner2);
assertSame(inner1, inner2);
}
/**
* Tests high-concurrency contention for connections and pooled prepared statements.
* DBCP-414
*/
@Test
public void testMultipleThreads1() throws Exception {
ds.setMaxWaitMillis(-1);
ds.setMaxTotal(5);
ds.setMaxOpenPreparedStatements(-1);
multipleThreads(5, false, false, -1, 3, 100, 10000);
}
}