Coverage Report - liquibase.executor.jvm.JdbcExecutor
 
Classes in this File Line Coverage Branch Coverage Complexity
JdbcExecutor
5%
4/75
0%
0/18
1.784
JdbcExecutor$1
0%
0/3
N/A
1.784
JdbcExecutor$1ExecuteStatementCallback
0%
0/13
0%
0/6
1.784
JdbcExecutor$1QueryStatementCallback
0%
0/11
0%
0/2
1.784
JdbcExecutor$1UpdateStatementCallback
0%
0/7
0%
0/2
1.784
JdbcExecutor$RowCallbackHandlerResultSetExtractor
0%
0/6
0%
0/2
1.784
 
 1  
 package liquibase.executor.jvm;
 2  
 
 3  
 import liquibase.database.Database;
 4  
 import liquibase.database.DatabaseConnection;
 5  
 import liquibase.database.core.OracleDatabase;
 6  
 import liquibase.database.jvm.JdbcConnection;
 7  
 import liquibase.exception.DatabaseException;
 8  
 import liquibase.executor.AbstractExecutor;
 9  
 import liquibase.executor.Executor;
 10  
 import liquibase.logging.LogFactory;
 11  
 import liquibase.logging.Logger;
 12  
 import liquibase.snapshot.jvm.JdbcDatabaseSnapshotGenerator;
 13  
 import liquibase.sql.visitor.SqlVisitor;
 14  
 import liquibase.statement.CallableSqlStatement;
 15  
 import liquibase.statement.SqlStatement;
 16  
 import liquibase.statement.StoredProcedureStatement;
 17  
 import liquibase.util.JdbcUtils;
 18  
 import liquibase.util.StringUtils;
 19  
 import liquibase.snapshot.DatabaseSnapshotGeneratorFactory;
 20  
 
 21  
 import java.sql.CallableStatement;
 22  
 import java.sql.ResultSet;
 23  
 import java.sql.SQLException;
 24  
 import java.sql.Statement;
 25  
 import java.util.ArrayList;
 26  
 import java.util.HashMap;
 27  
 import java.util.List;
 28  
 import java.util.Map;
 29  
 
 30  
 /**
 31  
  * Class to simplify execution of SqlStatements. Based heavily on <a
 32  
  * href="http://static.springframework.org/spring/docs/2.0.x/reference/jdbc.html">Spring's JdbcTemplate</a>. <br>
 33  
  * <br>
 34  
  * <b>Note: This class is currently intended for Liquibase-internal use only and may change without notice in the
 35  
  * future</b>
 36  
  */
 37  
 @SuppressWarnings({ "unchecked" })
 38  6
 public class JdbcExecutor extends AbstractExecutor implements Executor {
 39  
 
 40  6
     private Logger log = LogFactory.getLogger();
 41  
 
 42  
     public boolean updatesDatabase() {
 43  3
         return true;
 44  
     }
 45  
 
 46  
     // -------------------------------------------------------------------------
 47  
     // Methods dealing with static SQL (java.sql.Statement)
 48  
     // -------------------------------------------------------------------------
 49  
 
 50  
     public Object execute(StatementCallback action, List<SqlVisitor> sqlVisitors) throws DatabaseException {
 51  0
         DatabaseConnection con = database.getConnection();
 52  0
         Statement stmt = null;
 53  
         try {
 54  0
             stmt = ((JdbcConnection) con).getUnderlyingConnection().createStatement();
 55  0
             Statement stmtToUse = stmt;
 56  
 
 57  0
             return action.doInStatement(stmtToUse);
 58  0
         } catch (SQLException ex) {
 59  
             // Release Connection early, to avoid potential connection pool deadlock
 60  
             // in the case when the exception translator hasn't been initialized yet.
 61  0
             JdbcUtils.closeStatement(stmt);
 62  0
             stmt = null;
 63  0
             throw new DatabaseException("Error executing SQL "
 64  
                     + StringUtils.join(applyVisitors(action.getStatement(), sqlVisitors), "; on " + con.getURL())
 65  
                     + ": " + ex.getMessage(), ex);
 66  
         } finally {
 67  0
             JdbcUtils.closeStatement(stmt);
 68  
         }
 69  
     }
 70  
 
 71  
     public void execute(final SqlStatement sql) throws DatabaseException {
 72  0
         execute(sql, new ArrayList<SqlVisitor>());
 73  0
     }
 74  
 
 75  
     public void execute(final SqlStatement sql, final List<SqlVisitor> sqlVisitors) throws DatabaseException {
 76  0
         if (sql instanceof CallableSqlStatement) {
 77  0
             call(((CallableSqlStatement) sql), new ArrayList(), sqlVisitors);
 78  0
             return;
 79  
         }
 80  
 
 81  0
         class ExecuteStatementCallback implements StatementCallback {
 82  
             public Object doInStatement(Statement stmt) throws SQLException, DatabaseException {
 83  0
                 for (String statement : applyVisitors(sql, sqlVisitors)) {
 84  0
                     if (database instanceof OracleDatabase) {
 85  0
                         statement = statement.replaceFirst("/\\s*/\\s*$", ""); // remove duplicated /'s
 86  
                     }
 87  
 
 88  0
                     log.debug("Executing EXECUTE database command: " + statement);
 89  0
                     if (statement.contains("?")) {
 90  0
                         stmt.setEscapeProcessing(false);
 91  
                     }
 92  
                     try {
 93  0
                         stmt.execute(statement);
 94  0
                     } catch (SQLException e) {
 95  0
                         throw e;
 96  0
                     }
 97  
                 }
 98  0
                 return null;
 99  
             }
 100  
 
 101  
             public SqlStatement getStatement() {
 102  0
                 return sql;
 103  
             }
 104  
         }
 105  0
         execute(new ExecuteStatementCallback(), sqlVisitors);
 106  0
     }
 107  
 
 108  
     public Object query(final SqlStatement sql, final ResultSetExtractor rse) throws DatabaseException {
 109  0
         return query(sql, rse, new ArrayList<SqlVisitor>());
 110  
     }
 111  
 
 112  
     public Object query(final SqlStatement sql, final ResultSetExtractor rse, final List<SqlVisitor> sqlVisitors)
 113  
             throws DatabaseException {
 114  0
         if (sql instanceof CallableSqlStatement) {
 115  0
             throw new DatabaseException("Direct query using CallableSqlStatement not currently implemented");
 116  
         }
 117  
 
 118  0
         class QueryStatementCallback implements StatementCallback {
 119  
             public Object doInStatement(Statement stmt) throws SQLException, DatabaseException {
 120  0
                 ResultSet rs = null;
 121  
                 try {
 122  0
                     String[] sqlToExecute = applyVisitors(sql, sqlVisitors);
 123  
 
 124  0
                     if (sqlToExecute.length != 1) {
 125  0
                         throw new DatabaseException("Can only query with statements that return one sql statement");
 126  
                     }
 127  0
                     log.debug("Executing QUERY database command: " + sqlToExecute[0]);
 128  
 
 129  0
                     rs = stmt.executeQuery(sqlToExecute[0]);
 130  0
                     ResultSet rsToUse = rs;
 131  0
                     return rse.extractData(rsToUse);
 132  
                 } finally {
 133  0
                     JdbcUtils.closeResultSet(rs);
 134  
                 }
 135  
             }
 136  
 
 137  
             public SqlStatement getStatement() {
 138  0
                 return sql;
 139  
             }
 140  
         }
 141  0
         return execute(new QueryStatementCallback(), sqlVisitors);
 142  
     }
 143  
 
 144  
     public List query(SqlStatement sql, RowMapper rowMapper) throws DatabaseException {
 145  0
         return query(sql, rowMapper, new ArrayList());
 146  
     }
 147  
 
 148  
     public List query(SqlStatement sql, RowMapper rowMapper, List<SqlVisitor> sqlVisitors) throws DatabaseException {
 149  0
         return (List) query(sql, new RowMapperResultSetExtractor(rowMapper), sqlVisitors);
 150  
     }
 151  
 
 152  
     public Object queryForObject(SqlStatement sql, RowMapper rowMapper) throws DatabaseException {
 153  0
         return queryForObject(sql, rowMapper, new ArrayList());
 154  
     }
 155  
 
 156  
     public Object queryForObject(SqlStatement sql, RowMapper rowMapper, List<SqlVisitor> sqlVisitors)
 157  
             throws DatabaseException {
 158  0
         List results = query(sql, rowMapper, sqlVisitors);
 159  0
         return JdbcUtils.requiredSingleResult(results);
 160  
     }
 161  
 
 162  
     public Object queryForObject(SqlStatement sql, Class requiredType) throws DatabaseException {
 163  0
         return queryForObject(sql, requiredType, new ArrayList());
 164  
     }
 165  
 
 166  
     public Object queryForObject(SqlStatement sql, Class requiredType, List<SqlVisitor> sqlVisitors)
 167  
             throws DatabaseException {
 168  0
         return queryForObject(sql, getSingleColumnRowMapper(requiredType), sqlVisitors);
 169  
     }
 170  
 
 171  
     public long queryForLong(SqlStatement sql) throws DatabaseException {
 172  0
         return queryForLong(sql, new ArrayList());
 173  
     }
 174  
 
 175  
     public long queryForLong(SqlStatement sql, List<SqlVisitor> sqlVisitors) throws DatabaseException {
 176  0
         Number number = (Number) queryForObject(sql, Long.class, sqlVisitors);
 177  0
         return (number != null ? number.longValue() : 0);
 178  
     }
 179  
 
 180  
     public int queryForInt(SqlStatement sql) throws DatabaseException {
 181  0
         return queryForInt(sql, new ArrayList());
 182  
     }
 183  
 
 184  
     public int queryForInt(SqlStatement sql, List<SqlVisitor> sqlVisitors) throws DatabaseException {
 185  0
         Number number = (Number) queryForObject(sql, Integer.class, sqlVisitors);
 186  0
         return (number != null ? number.intValue() : 0);
 187  
     }
 188  
 
 189  
     public List queryForList(SqlStatement sql, Class elementType) throws DatabaseException {
 190  0
         return queryForList(sql, elementType, new ArrayList());
 191  
     }
 192  
 
 193  
     public List queryForList(SqlStatement sql, Class elementType, List<SqlVisitor> sqlVisitors)
 194  
             throws DatabaseException {
 195  0
         return query(sql, getSingleColumnRowMapper(elementType), sqlVisitors);
 196  
     }
 197  
 
 198  
     public List<Map> queryForList(SqlStatement sql) throws DatabaseException {
 199  0
         return queryForList(sql, new ArrayList());
 200  
     }
 201  
 
 202  
     public List<Map> queryForList(SqlStatement sql, List<SqlVisitor> sqlVisitors) throws DatabaseException {
 203  
         // noinspection unchecked
 204  0
         return (List<Map>) query(sql, getColumnMapRowMapper(), sqlVisitors);
 205  
     }
 206  
 
 207  
     public int update(final SqlStatement sql) throws DatabaseException {
 208  0
         return update(sql, new ArrayList());
 209  
     }
 210  
 
 211  
     public int update(final SqlStatement sql, final List<SqlVisitor> sqlVisitors) throws DatabaseException {
 212  0
         if (sql instanceof CallableSqlStatement) {
 213  0
             throw new DatabaseException("Direct update using CallableSqlStatement not currently implemented");
 214  
         }
 215  
 
 216  0
         class UpdateStatementCallback implements StatementCallback {
 217  
             public Object doInStatement(Statement stmt) throws SQLException, DatabaseException {
 218  0
                 String[] sqlToExecute = applyVisitors(sql, sqlVisitors);
 219  0
                 if (sqlToExecute.length != 1) {
 220  0
                     throw new DatabaseException(
 221  
                             "Cannot call update on Statement that returns back multiple Sql objects");
 222  
                 }
 223  0
                 log.debug("Executing UPDATE database command: " + sqlToExecute[0]);
 224  0
                 return stmt.executeUpdate(sqlToExecute[0]);
 225  
             }
 226  
 
 227  
             public SqlStatement getStatement() {
 228  0
                 return sql;
 229  
             }
 230  
         }
 231  0
         return (Integer) execute(new UpdateStatementCallback(), sqlVisitors);
 232  
     }
 233  
 
 234  
     // -------------------------------------------------------------------------
 235  
     // Methods dealing with callable statements
 236  
     // -------------------------------------------------------------------------
 237  
 
 238  
     public Object execute(CallableSqlStatement csc, CallableStatementCallback action, List<SqlVisitor> sqlVisitors)
 239  
             throws DatabaseException {
 240  0
         CallableStatement cs = null;
 241  
         try {
 242  0
             cs = createCallableStatement(((StoredProcedureStatement) csc), database);
 243  0
             CallableStatement csToUse = cs;
 244  0
             return action.doInCallableStatement(csToUse);
 245  0
         } catch (SQLException ex) {
 246  0
             throw new DatabaseException("Error executing callable statement", ex);
 247  
         } finally {
 248  0
             JdbcUtils.closeStatement(cs);
 249  
         }
 250  
 
 251  
     }
 252  
 
 253  
     public CallableStatement createCallableStatement(StoredProcedureStatement statement, Database database)
 254  
             throws SQLException {
 255  0
         StringBuffer sql = new StringBuffer("{call " + statement.getProcedureName());
 256  
 
 257  0
         List<String> parameters = statement.getParameters();
 258  0
         if (parameters.size() > 0) {
 259  0
             sql.append("(");
 260  
             // noinspection UnusedDeclaration
 261  0
             for (Object param : parameters) {
 262  0
                 sql.append("?,");
 263  
             }
 264  0
             sql.deleteCharAt(sql.lastIndexOf(","));
 265  0
             sql.append(")");
 266  
         }
 267  
 
 268  0
         sql.append("}");
 269  
 
 270  0
         CallableStatement pstmt = ((JdbcConnection) database.getConnection()).getUnderlyingConnection().prepareCall(
 271  
                 sql.toString());
 272  
 
 273  0
         for (int i = 0; i < parameters.size(); i++) {
 274  0
             String param = parameters.get(i);
 275  0
             int type = ((JdbcDatabaseSnapshotGenerator) DatabaseSnapshotGeneratorFactory.getInstance().getGenerator(
 276  
                     database)).getDatabaseType(statement.getParameterType(param), database);
 277  
 
 278  0
             if (param == null) {
 279  0
                 pstmt.setNull(i + 1, type);
 280  
             } else {
 281  0
                 pstmt.setObject(i + 1, param, type);
 282  
             }
 283  
         }
 284  
 
 285  0
         return pstmt;
 286  
     }
 287  
 
 288  
     public Map call(CallableSqlStatement csc, final List declaredParameters, List<SqlVisitor> sqlVisitors)
 289  
             throws DatabaseException {
 290  0
         return (Map) execute(csc, new CallableStatementCallback() {
 291  
             public Object doInCallableStatement(CallableStatement cs) throws SQLException {
 292  
                 // not currently doing anything with returned results
 293  
                 // boolean retVal = cs.execute();
 294  
                 // int updateCount = cs.getUpdateCount();
 295  
                 // Map returnedResults = new HashMap();
 296  
                 // if (retVal || updateCount != -1) {
 297  
                 // returnedResults.putAll(extractReturnedResultSets(cs, declaredParameters, updateCount));
 298  
                 // }
 299  
                 // returnedResults.putAll(extractOutputParameters(cs, declaredParameters));
 300  0
                 cs.execute();
 301  0
                 return new HashMap();
 302  
             }
 303  
         }, sqlVisitors);
 304  
     }
 305  
 
 306  
     /**
 307  
      * Create a new RowMapper for reading columns as key-value pairs.
 308  
      * 
 309  
      * @return the RowMapper to use
 310  
      * @see ColumnMapRowMapper
 311  
      */
 312  
     protected RowMapper getColumnMapRowMapper() {
 313  0
         return new ColumnMapRowMapper();
 314  
     }
 315  
 
 316  
     /**
 317  
      * Create a new RowMapper for reading result objects from a single column.
 318  
      * 
 319  
      * @param requiredType
 320  
      *            the type that each result object is expected to match
 321  
      * @return the RowMapper to use
 322  
      * @see SingleColumnRowMapper
 323  
      */
 324  
     protected RowMapper getSingleColumnRowMapper(Class requiredType) {
 325  0
         return new SingleColumnRowMapper(requiredType);
 326  
     }
 327  
 
 328  
     public void comment(String message) throws DatabaseException {
 329  0
         LogFactory.getLogger().debug(message);
 330  0
     }
 331  
 
 332  
     /**
 333  
      * Adapter to enable use of a RowCallbackHandler inside a ResultSetExtractor.
 334  
      * <p>
 335  
      * Uses a regular ResultSet, so we have to be careful when using it: We don't use it for navigating since this could
 336  
      * lead to unpredictable consequences.
 337  
      */
 338  6
     private static class RowCallbackHandlerResultSetExtractor implements ResultSetExtractor {
 339  
 
 340  
         private final RowCallbackHandler rch;
 341  
 
 342  0
         public RowCallbackHandlerResultSetExtractor(RowCallbackHandler rch) {
 343  0
             this.rch = rch;
 344  0
         }
 345  
 
 346  
         public Object extractData(ResultSet rs) throws SQLException {
 347  0
             while (rs.next()) {
 348  0
                 this.rch.processRow(rs);
 349  
             }
 350  0
             return null;
 351  
         }
 352  
     }
 353  
 }