001package org.avaje.dbmigration.ddl; 002 003import org.slf4j.Logger; 004import org.slf4j.LoggerFactory; 005 006import java.io.StringReader; 007import java.sql.Connection; 008import java.sql.PreparedStatement; 009import java.sql.SQLException; 010import java.util.ArrayList; 011import java.util.List; 012 013/** 014 * Runs DDL scripts. 015 */ 016public class DdlRunner { 017 018 protected static final Logger logger = LoggerFactory.getLogger(DdlRunner.class); 019 020 private DdlParser ddlParser = new DdlParser(); 021 022 private final String scriptName; 023 024 private final boolean expectErrors; 025 026 /** 027 * Construct with a script name (for logging) and flag indicating if errors are expected. 028 */ 029 public DdlRunner(boolean expectErrors, String scriptName) { 030 this.expectErrors = expectErrors; 031 this.scriptName = scriptName; 032 } 033 034 /** 035 * Parse the content into sql statements and execute them in a transaction. 036 */ 037 public int runAll(String content, Connection connection) throws SQLException { 038 039 List<String> statements = ddlParser.parse(new StringReader(content)); 040 return runStatements(statements, connection); 041 } 042 043 /** 044 * Execute all the statements in a single transaction. 045 */ 046 private int runStatements(List<String> statements, Connection connection) throws SQLException { 047 048 List<String> noDuplicates = new ArrayList<String>(); 049 050 for (String statement : statements) { 051 if (!noDuplicates.contains(statement)) { 052 noDuplicates.add(statement); 053 } 054 } 055 056 logger.info("Executing {} - {} statements", scriptName, noDuplicates.size()); 057 058 for (int i = 0; i < noDuplicates.size(); i++) { 059 String xOfy = (i + 1) + " of " + noDuplicates.size(); 060 runStatement(expectErrors, xOfy, noDuplicates.get(i), connection); 061 } 062 063 return noDuplicates.size(); 064 } 065 066 /** 067 * Execute the statement. 068 */ 069 private void runStatement(boolean expectErrors, String oneOf, String stmt, Connection c) throws SQLException { 070 071 PreparedStatement pstmt = null; 072 try { 073 074 // trim and remove trailing ; or / 075 stmt = stmt.trim(); 076 if (stmt.endsWith(";")) { 077 stmt = stmt.substring(0, stmt.length() - 1); 078 } else if (stmt.endsWith("/")) { 079 stmt = stmt.substring(0, stmt.length() - 1); 080 } 081 082 if (logger.isDebugEnabled()) { 083 logger.debug("executing " + oneOf + " " + getSummary(stmt)); 084 } 085 086 pstmt = c.prepareStatement(stmt); 087 pstmt.execute(); 088 089 } catch (SQLException e) { 090 if (expectErrors) { 091 logger.debug(" ... ignoring error executing " + getSummary(stmt) + " error: " + e.getMessage()); 092 } else { 093 String msg = "Error executing stmt[" + stmt + "] error[" + e.getMessage() + "]"; 094 throw new SQLException(msg, e); 095 } 096 097 } finally { 098 if (pstmt != null) { 099 try { 100 pstmt.close(); 101 } catch (SQLException e) { 102 logger.error("Error closing pstmt", e); 103 } 104 } 105 } 106 } 107 108 private String getSummary(String s) { 109 if (s.length() > 80) { 110 return s.substring(0, 80).trim() + "..."; 111 } 112 return s; 113 } 114 115}