/* * Copyright 2015-2016 the original author or authors. * * Licensed 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.glowroot.agent.plugin.jdbc; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Iterator; import java.util.List; import org.apache.commons.dbcp.DelegatingConnection; import org.apache.commons.dbcp.DelegatingStatement; import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.glowroot.agent.it.harness.AppUnderTest; import org.glowroot.agent.it.harness.Container; import org.glowroot.agent.it.harness.Containers; import org.glowroot.agent.it.harness.TransactionMarker; import org.glowroot.wire.api.model.TraceOuterClass.Trace; import static org.assertj.core.api.Assertions.assertThat; public class StatementIT { private static Container container; @BeforeClass public static void setUp() throws Exception { container = Containers.create(); } @AfterClass public static void tearDown() throws Exception { container.close(); } @After public void afterEachTest() throws Exception { container.checkAndReset(); } @Test public void testStatement() throws Exception { // when Trace trace = container.execute(ExecuteStatementAndIterateOverResults.class); // then Iterator<Trace.Entry> i = trace.getEntryList().iterator(); List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList(); Trace.Entry entry = i.next(); assertThat(entry.getDepth()).isEqualTo(0); assertThat(entry.getMessage()).isEmpty(); assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex()) .getFullText()).isEqualTo("select * from employee"); assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: "); assertThat(entry.getQueryEntryMessage().getSuffix()).isEqualTo(" => 3 rows"); assertThat(i.hasNext()).isFalse(); } @Test public void testStatementQuery() throws Exception { // when Trace trace = container.execute(ExecuteStatementQueryAndIterateOverResults.class); // then Iterator<Trace.Entry> i = trace.getEntryList().iterator(); List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList(); Trace.Entry entry = i.next(); assertThat(entry.getDepth()).isEqualTo(0); assertThat(entry.getMessage()).isEmpty(); assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex()) .getFullText()).isEqualTo("select * from employee"); assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: "); assertThat(entry.getQueryEntryMessage().getSuffix()).isEqualTo(" => 3 rows"); assertThat(i.hasNext()).isFalse(); } @Test public void testStatementUpdate() throws Exception { // when Trace trace = container.execute(ExecuteStatementUpdate.class); // then Iterator<Trace.Entry> i = trace.getEntryList().iterator(); List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList(); Trace.Entry entry = i.next(); assertThat(entry.getDepth()).isEqualTo(0); assertThat(entry.getMessage()).isEmpty(); assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex()) .getFullText()).isEqualTo("update employee set name = 'nobody'"); assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: "); assertThat(entry.getQueryEntryMessage().getSuffix()).isEqualTo(" => 3 rows"); assertThat(i.hasNext()).isFalse(); } @Test public void testNullStatement() throws Exception { // when Trace trace = container.execute(ExecuteNullStatement.class); // then assertThat(trace.getHeader().getEntryCount()).isZero(); } @Test public void testStatementThrowing() throws Exception { // when Trace trace = container.execute(ExecuteStatementThrowing.class); // then Iterator<Trace.Entry> i = trace.getEntryList().iterator(); List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList(); for (int j = 0; j < 2000; j++) { i.next(); } Trace.Entry entry = i.next(); assertThat(entry.getDepth()).isEqualTo(0); assertThat(entry.getMessage()).isEmpty(); assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex()) .getFullText()).isEqualTo("select * from employee"); assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: "); assertThat(entry.getQueryEntryMessage().getSuffix()).isEmpty(); assertThat(entry.getError().getMessage()).isEqualTo("An execute failure"); assertThat(i.hasNext()).isFalse(); } @Test public void testStatementUsingPrevious() throws Exception { // when Trace trace = container.execute(ExecuteStatementAndUsePrevious.class); // then Iterator<Trace.Entry> i = trace.getEntryList().iterator(); List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList(); Trace.Entry entry = i.next(); assertThat(entry.getDepth()).isEqualTo(0); assertThat(entry.getMessage()).isEmpty(); assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex()) .getFullText()).isEqualTo("select * from employee"); assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: "); assertThat(entry.getQueryEntryMessage().getSuffix()).isEqualTo(" => 3 rows"); assertThat(i.hasNext()).isFalse(); } @Test public void testStatementUsingRelativeForward() throws Exception { // when Trace trace = container.execute(ExecuteStatementAndUseRelativeForward.class); // then Iterator<Trace.Entry> i = trace.getEntryList().iterator(); List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList(); Trace.Entry entry = i.next(); assertThat(entry.getDepth()).isEqualTo(0); assertThat(entry.getMessage()).isEmpty(); assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex()) .getFullText()).isEqualTo("select * from employee"); assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: "); assertThat(entry.getQueryEntryMessage().getSuffix()).isEqualTo(" => 3 rows"); assertThat(i.hasNext()).isFalse(); } @Test public void testStatementUsingRelativeBackward() throws Exception { // when Trace trace = container.execute(ExecuteStatementAndUseRelativeBackward.class); // then Iterator<Trace.Entry> i = trace.getEntryList().iterator(); List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList(); Trace.Entry entry = i.next(); assertThat(entry.getDepth()).isEqualTo(0); assertThat(entry.getMessage()).isEmpty(); assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex()) .getFullText()).isEqualTo("select * from employee"); assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: "); assertThat(entry.getQueryEntryMessage().getSuffix()).isEqualTo(" => 3 rows"); assertThat(i.hasNext()).isFalse(); } @Test public void testStatementUsingAbsolute() throws Exception { // when Trace trace = container.execute(ExecuteStatementAndUseAbsolute.class); // then Iterator<Trace.Entry> i = trace.getEntryList().iterator(); List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList(); Trace.Entry entry = i.next(); assertThat(entry.getDepth()).isEqualTo(0); assertThat(entry.getMessage()).isEmpty(); assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex()) .getFullText()).isEqualTo("select * from employee"); assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: "); assertThat(entry.getQueryEntryMessage().getSuffix()).isEqualTo(" => 2 rows"); assertThat(i.hasNext()).isFalse(); } @Test public void testStatementUsingFirst() throws Exception { // when Trace trace = container.execute(ExecuteStatementAndUseFirst.class); // then Iterator<Trace.Entry> i = trace.getEntryList().iterator(); List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList(); Trace.Entry entry = i.next(); assertThat(entry.getDepth()).isEqualTo(0); assertThat(entry.getMessage()).isEmpty(); assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex()) .getFullText()).isEqualTo("select * from employee"); assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: "); assertThat(entry.getQueryEntryMessage().getSuffix()).isEqualTo(" => 1 row"); assertThat(i.hasNext()).isFalse(); } @Test public void testStatementUsingLast() throws Exception { // when Trace trace = container.execute(ExecuteStatementAndUseLast.class); // then Iterator<Trace.Entry> i = trace.getEntryList().iterator(); List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList(); Trace.Entry entry = i.next(); assertThat(entry.getDepth()).isEqualTo(0); assertThat(entry.getMessage()).isEmpty(); assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex()) .getFullText()).isEqualTo("select * from employee"); assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: "); assertThat(entry.getQueryEntryMessage().getSuffix()).isEqualTo(" => 3 rows"); assertThat(i.hasNext()).isFalse(); } public static class ExecuteStatementAndIterateOverResults implements AppUnderTest, TransactionMarker { private Connection connection; @Override public void executeApp() throws Exception { connection = Connections.createConnection(); try { transactionMarker(); } finally { Connections.closeConnection(connection); } } @Override public void transactionMarker() throws Exception { Statement statement = connection.createStatement(); try { statement.execute("select * from employee"); ResultSet rs = statement.getResultSet(); while (rs.next()) { rs.getString(1); } } finally { statement.close(); } } } public static class ExecuteStatementQueryAndIterateOverResults implements AppUnderTest, TransactionMarker { private Connection connection; @Override public void executeApp() throws Exception { connection = Connections.createConnection(); try { transactionMarker(); } finally { Connections.closeConnection(connection); } } @Override public void transactionMarker() throws Exception { Statement statement = connection.createStatement(); try { ResultSet rs = statement.executeQuery("select * from employee"); while (rs.next()) { rs.getString(1); } } finally { statement.close(); } } } public static class ExecuteStatementUpdate implements AppUnderTest, TransactionMarker { private Connection connection; @Override public void executeApp() throws Exception { connection = Connections.createConnection(); try { transactionMarker(); } finally { Connections.closeConnection(connection); } } @Override public void transactionMarker() throws Exception { Statement statement = connection.createStatement(); try { statement.executeUpdate("update employee set name = 'nobody'"); } finally { statement.close(); } } } public static class ExecuteNullStatement implements AppUnderTest, TransactionMarker { private Connection delegatingConnection; @Override public void executeApp() throws Exception { Connection connection = Connections.createConnection(); delegatingConnection = new DelegatingConnection(connection) { @Override public Statement createStatement() throws SQLException { return new DelegatingStatement(this, super.createStatement()) { @Override public boolean execute(String sql) throws SQLException { return super.execute("select 1 from employee"); } }; } }; try { transactionMarker(); } finally { Connections.closeConnection(connection); } } @Override public void transactionMarker() throws Exception { Statement statement = delegatingConnection.createStatement(); try { statement.execute(null); } finally { statement.close(); } } } public static class ExecuteStatementThrowing implements AppUnderTest, TransactionMarker { private Connection connection; private Connection delegatingConnection; @Override public void executeApp() throws Exception { connection = Connections.createConnection(); delegatingConnection = new DelegatingConnection(connection) { @Override public Statement createStatement() throws SQLException { return new DelegatingStatement(this, super.createStatement()) { @Override public boolean execute(String sql) throws SQLException { throw new SQLException("An execute failure"); } }; } }; try { transactionMarker(); } finally { Connections.closeConnection(connection); } } @Override public void transactionMarker() throws Exception { // exceed the limit for distinct aggregated queries in a single trace for (int i = 0; i < 5000; i++) { Statement statement = connection.createStatement(); try { statement.execute("select " + i + " from employee"); } finally { statement.close(); } } Statement statement = delegatingConnection.createStatement(); try { statement.execute("select * from employee"); } catch (SQLException e) { } finally { statement.close(); } } } public static class ExecuteStatementAndUsePrevious implements AppUnderTest, TransactionMarker { private Connection connection; @Override public void executeApp() throws Exception { connection = Connections.createConnection(); try { transactionMarker(); } finally { Connections.closeConnection(connection); } } @Override public void transactionMarker() throws Exception { Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); try { statement.execute("select * from employee"); ResultSet rs = statement.getResultSet(); rs.afterLast(); while (rs.previous()) { rs.getString(1); } } finally { statement.close(); } } } public static class ExecuteStatementAndUseRelativeForward implements AppUnderTest, TransactionMarker { private Connection connection; @Override public void executeApp() throws Exception { connection = Connections.createConnection(); try { transactionMarker(); } finally { Connections.closeConnection(connection); } } @Override public void transactionMarker() throws Exception { Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); try { statement.execute("select * from employee"); ResultSet rs = statement.getResultSet(); // need to position cursor on a valid row before calling relative(), at least for // sqlserver jdbc driver rs.next(); rs.getString(1); while (rs.relative(1)) { rs.getString(1); } } finally { statement.close(); } } } public static class ExecuteStatementAndUseRelativeBackward implements AppUnderTest, TransactionMarker { private Connection connection; @Override public void executeApp() throws Exception { connection = Connections.createConnection(); try { transactionMarker(); } finally { Connections.closeConnection(connection); } } @Override public void transactionMarker() throws Exception { Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); try { statement.execute("select * from employee"); ResultSet rs = statement.getResultSet(); rs.afterLast(); // need to position cursor on a valid row before calling relative(), at least for // sqlserver jdbc driver rs.previous(); rs.getString(1); while (rs.relative(-1)) { rs.getString(1); } } finally { statement.close(); } } } public static class ExecuteStatementAndUseAbsolute implements AppUnderTest, TransactionMarker { private Connection connection; @Override public void executeApp() throws Exception { connection = Connections.createConnection(); try { transactionMarker(); } finally { Connections.closeConnection(connection); } } @Override public void transactionMarker() throws Exception { Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); try { statement.execute("select * from employee"); ResultSet rs = statement.getResultSet(); rs.absolute(2); } finally { statement.close(); } } } public static class ExecuteStatementAndUseFirst implements AppUnderTest, TransactionMarker { private Connection connection; @Override public void executeApp() throws Exception { connection = Connections.createConnection(); try { transactionMarker(); } finally { Connections.closeConnection(connection); } } @Override public void transactionMarker() throws Exception { Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); try { statement.execute("select * from employee"); ResultSet rs = statement.getResultSet(); rs.first(); } finally { statement.close(); } } } public static class ExecuteStatementAndUseLast implements AppUnderTest, TransactionMarker { private Connection connection; @Override public void executeApp() throws Exception { connection = Connections.createConnection(); try { transactionMarker(); } finally { Connections.closeConnection(connection); } } @Override public void transactionMarker() throws Exception { Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); try { statement.execute("select * from employee"); ResultSet rs = statement.getResultSet(); rs.last(); } finally { statement.close(); } } } }