/* * #%L * P6Spy * %% * Copyright (C) 2013 P6Spy * %% * 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. * #L% */ package com.p6spy.engine.spy; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import org.apache.log4j.Logger; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import com.p6spy.engine.logging.P6LogOptions; import com.p6spy.engine.test.P6TestFramework; @RunWith(Parameterized.class) public class P6TestCallableStatement extends P6TestFramework { private static final int TEST_IMG_ID = 2000; private static final Logger log = Logger.getLogger(P6TestCallableStatement.class); private boolean originalExcludeBinaryFlag; @Before public void before() { this.originalExcludeBinaryFlag = P6LogOptions.getActiveInstance().getExcludebinary(); } @After public void after() { P6LogOptions.getActiveInstance().setExcludebinary(this.originalExcludeBinaryFlag); } @Parameterized.Parameters(name = "{index}: {0}") public static Collection<Object[]> dbs() { Collection<Object[]> result; String dbList = (System.getProperty("DB") == null ? "HSQLDB" : System.getProperty("DB")); Object[] dbs = dbList.split(","); List<Object[]> dbsToTest = new ArrayList<Object[]>(); for (int i = 0; i < dbs.length; i++) { // Check against list of databases with stored procs // As procs become available for other databases, enable them here. if( Arrays.asList("Oracle","MySQL","HSQLDB").contains(dbs[i])) { dbsToTest.add(new Object[]{dbs[i]}); } else { log.info("Skipping "+dbs[i]+" because stored procedures have not been created for testing"); } } result = dbsToTest; return result; } public P6TestCallableStatement(String db) throws SQLException, IOException { super(db); } @Test public void testStoredProcedureNoResultSet() throws SQLException { this.clearLogEntries(); // execute the statement String query = "{call test_proc(?,?,?)}"; CallableStatement call = connection.prepareCall(query); call.registerOutParameter(3, Types.INTEGER); call.setInt(1, 1); call.setString(2, "hi"); call.execute(); int retVal = call.getInt(3); assertEquals(2, retVal); call.close(); // the last log message should have the original query assertTrue(getLastLogEntry().contains(query)); // verify that the bind parameters are resolved in the log message assertTrue(getLastLogEntry().contains("1,'hi'")); } @Test public void testStoredProcedureResultSet() throws SQLException { if( "Oracle".equals(db)) { // Oracle does not support returning a resultset from a store proc via CallableStatement.getResultSet() return; } P6LogOptions.getActiveInstance().setExcludecategories("debug,info,result"); this.clearLogEntries(); // execute the statement String query = "{call test_proc_rs(?)}"; CallableStatement call = connection.prepareCall(query); call.setString(1, "a"); call.execute(); ResultSet rs = call.getResultSet(); if( rs == null ) { // HSQLDB requires you to call ResultSet.getMoreResults() before accessing the resultset. call.getMoreResults(); rs = call.getResultSet(); } while(rs.next()) { rs.getString("name"); rs.getInt("id"); } rs.close(); call.close(); // verify that the result set was logged assertTrue(getLastLogEntry().contains("resultset")); } @Test public void testNamedParameters() throws SQLException { this.clearLogEntries(); String param1Name = "param1"; String param2Name = "param2"; String resultParamName = "result_param"; if( "HSQLDB".equals(db) ) { // HSQLDB uses @p1, @p2, etc... as the "names" of the parameters param1Name = "@p1"; param2Name = "@p2"; resultParamName = "@p3"; } // execute the statement String query = "{call test_proc(?,?,?)}"; CallableStatement call = connection.prepareCall(query); call.setInt(param1Name, 1); call.setString(param2Name, "hi"); call.registerOutParameter(resultParamName, Types.INTEGER); call.execute(); int retVal = call.getInt(resultParamName); assertEquals(2, retVal); call.close(); // the last log message should have the original query assertTrue(getLastLogEntry().contains(query)); assertTrue(getLastLogEntry().contains("{call test_proc(?,?,?)}")); assertTrue(getLastLogEntry().contains(param1Name+":"+"1")); assertTrue(getLastLogEntry().contains(param2Name+":"+"'hi'")); } @Test public void testStoredProcedureWithNullInputParameter() throws SQLException { this.clearLogEntries(); // execute the statement String query = "{call test_proc(?,?,?)}"; CallableStatement stmt = connection.prepareCall(query); stmt.registerOutParameter(3, Types.INTEGER); stmt.setInt(1, 1); stmt.setNull(2, Types.VARCHAR); stmt.execute(); int retVal = stmt.getInt(3); assertEquals(2, retVal); stmt.close(); // verify that the third parameter is NULL assertTrue(getLastLogEntry().contains("1,NULL")); } @Test public void binaryExcludedTrue() throws SQLException { // given P6LogOptions.getActiveInstance().setExcludebinary(true); // when String paramValName = "param_val"; String paramIntName = "param_id"; String resultParamName = "result_param"; if( "HSQLDB".equals(db) ) { // HSQLDB uses @p1, @p2, etc... as the "names" of the parameters paramValName = "@p1"; paramIntName = "@p2"; resultParamName = "@p3"; } // execute the statement String query = "{call test_proc_binary(?,?,?)}"; CallableStatement call = connection.prepareCall(query); call.setBytes(paramValName, "foo".getBytes(StandardCharsets.UTF_8)); call.setInt(paramIntName, TEST_IMG_ID); call.registerOutParameter(resultParamName, Types.INTEGER); call.execute(); // out vals not logged anyway https://github.com/p6spy/p6spy/issues/133 // byte[] retVal = call.getBytes(resultParamName); // assertEquals("foo", retVal); call.close(); // then assertTrue( getLastLogEntry().contains("{call test_proc_binary(?,?,?)} " + paramIntName + ":2000, " + paramValName + ":'[binary]'") // || getLastLogEntry().contains("{call test_proc_binary(?,?,?)} " + paramValName + ":'[binary]', " + paramIntName + ":2000")); } @Test public void binaryExcludedFalse() throws SQLException { // given P6LogOptions.getActiveInstance().setExcludebinary(false); // when String paramValName = "param_val"; String paramIntName = "param_id"; String resultParamName = "result_param"; if( "HSQLDB".equals(db) ) { // HSQLDB uses @p1, @p2, etc... as the "names" of the parameters paramValName = "@p1"; paramIntName = "@p2"; resultParamName = "@p3"; } // execute the statement String query = "{call test_proc_binary(?,?,?)}"; CallableStatement call = connection.prepareCall(query); call.setBytes(paramValName, "foo".getBytes(StandardCharsets.UTF_8)); call.setInt(paramIntName, TEST_IMG_ID); call.registerOutParameter(resultParamName, Types.INTEGER); call.execute(); // out vals not logged anyway https://github.com/p6spy/p6spy/issues/133 // byte[] retVal = call.getBytes(resultParamName); // assertEquals("foo", retVal); call.close(); // then assertTrue( getLastLogEntry().contains("{call test_proc_binary(?,?,?)} " + paramIntName + ":2000, " + paramValName + ":'666F6F'") // || getLastLogEntry().contains("{call test_proc_binary(?,?,?)} " + paramValName + ":'666F6F', " + paramIntName + ":2000")); } protected PreparedStatement getPreparedStatement(String query) throws SQLException { return connection.prepareStatement(query); } }