Coverage Report - liquibase.database.AbstractDatabase
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractDatabase
11%
53/457
9%
27/292
3.17
 
 1  
 package liquibase.database;
 2  
 
 3  
 import java.io.IOException;
 4  
 import java.io.Writer;
 5  
 import java.text.ParseException;
 6  
 import java.text.SimpleDateFormat;
 7  
 import java.util.ArrayList;
 8  
 import java.util.Date;
 9  
 import java.util.HashSet;
 10  
 import java.util.List;
 11  
 import java.util.Map;
 12  
 import java.util.Set;
 13  
 import java.util.regex.Pattern;
 14  
 
 15  
 import liquibase.change.Change;
 16  
 import liquibase.change.CheckSum;
 17  
 import liquibase.change.core.AnonymousChange;
 18  
 import liquibase.change.core.DropForeignKeyConstraintChange;
 19  
 import liquibase.change.core.DropSequenceChange;
 20  
 import liquibase.change.core.DropTableChange;
 21  
 import liquibase.change.core.DropViewChange;
 22  
 import liquibase.changelog.ChangeSet;
 23  
 import liquibase.changelog.DatabaseChangeLog;
 24  
 import liquibase.changelog.RanChangeSet;
 25  
 import liquibase.changelog.filter.ContextChangeSetFilter;
 26  
 import liquibase.changelog.filter.DbmsChangeSetFilter;
 27  
 import liquibase.database.core.DB2Database;
 28  
 import liquibase.database.core.DerbyDatabase;
 29  
 import liquibase.database.core.FirebirdDatabase;
 30  
 import liquibase.database.core.MSSQLDatabase;
 31  
 import liquibase.database.core.SQLiteDatabase;
 32  
 import liquibase.database.core.SybaseASADatabase;
 33  
 import liquibase.database.core.SybaseDatabase;
 34  
 import liquibase.database.structure.DatabaseObject;
 35  
 import liquibase.database.structure.ForeignKey;
 36  
 import liquibase.database.structure.Sequence;
 37  
 import liquibase.database.structure.Table;
 38  
 import liquibase.database.structure.View;
 39  
 import liquibase.diff.DiffStatusListener;
 40  
 import liquibase.exception.DatabaseException;
 41  
 import liquibase.exception.DatabaseHistoryException;
 42  
 import liquibase.exception.DateParseException;
 43  
 import liquibase.exception.LiquibaseException;
 44  
 import liquibase.exception.RollbackImpossibleException;
 45  
 import liquibase.exception.StatementNotSupportedOnDatabaseException;
 46  
 import liquibase.exception.UnsupportedChangeException;
 47  
 import liquibase.executor.Executor;
 48  
 import liquibase.executor.ExecutorService;
 49  
 import liquibase.logging.LogFactory;
 50  
 import liquibase.snapshot.DatabaseSnapshot;
 51  
 import liquibase.snapshot.DatabaseSnapshotGeneratorFactory;
 52  
 import liquibase.sql.Sql;
 53  
 import liquibase.sql.visitor.SqlVisitor;
 54  
 import liquibase.sqlgenerator.SqlGeneratorFactory;
 55  
 import liquibase.statement.DatabaseFunction;
 56  
 import liquibase.statement.SqlStatement;
 57  
 import liquibase.statement.core.AddColumnStatement;
 58  
 import liquibase.statement.core.ClearDatabaseChangeLogTableStatement;
 59  
 import liquibase.statement.core.CreateDatabaseChangeLogLockTableStatement;
 60  
 import liquibase.statement.core.CreateDatabaseChangeLogTableStatement;
 61  
 import liquibase.statement.core.GetNextChangeSetSequenceValueStatement;
 62  
 import liquibase.statement.core.GetViewDefinitionStatement;
 63  
 import liquibase.statement.core.MarkChangeSetRanStatement;
 64  
 import liquibase.statement.core.ModifyDataTypeStatement;
 65  
 import liquibase.statement.core.RawSqlStatement;
 66  
 import liquibase.statement.core.RemoveChangeSetRanStatusStatement;
 67  
 import liquibase.statement.core.SelectFromDatabaseChangeLogStatement;
 68  
 import liquibase.statement.core.SetNullableStatement;
 69  
 import liquibase.statement.core.TagDatabaseStatement;
 70  
 import liquibase.statement.core.UpdateChangeSetChecksumStatement;
 71  
 import liquibase.statement.core.UpdateStatement;
 72  
 import liquibase.util.ISODateFormat;
 73  
 import liquibase.util.StreamUtil;
 74  
 import liquibase.util.StringUtils;
 75  
 
 76  
 /**
 77  
  * AbstractDatabase is extended by all supported databases as a facade to the underlying database. The physical
 78  
  * connection can be retrieved from the AbstractDatabase implementation, as well as any database-specific
 79  
  * characteristics such as the datatype for "boolean" fields.
 80  
  */
 81  
 public abstract class AbstractDatabase implements Database {
 82  
 
 83  
     private DatabaseConnection connection;
 84  
     private String defaultSchemaName;
 85  
 
 86  
     protected String currentDateTimeFunction;
 87  
 
 88  
     // List of Database native functions.
 89  124
     protected List<DatabaseFunction> databaseFunctions = new ArrayList<DatabaseFunction>();
 90  
 
 91  
     private List<RanChangeSet> ranChangeSetList;
 92  
 
 93  1
     private static Pattern CREATE_VIEW_AS_PATTERN = Pattern.compile("^CREATE\\s+.*?VIEW\\s+.*?AS\\s+",
 94  
             Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
 95  
 
 96  124
     private String databaseChangeLogTableName = System.getProperty("liquibase.databaseChangeLogTableName") == null ? "DatabaseChangeLog"
 97  
             .toUpperCase() : System.getProperty("liquibase.databaseChangeLogTableName");
 98  124
     private String databaseChangeLogLockTableName = System.getProperty("liquibase.databaseChangeLogLockTableName") == null ? "DatabaseChangeLogLock"
 99  
             .toUpperCase() : System.getProperty("liquibase.databaseChangeLogLockTableName");
 100  124
     private String liquibaseSchemaName = System.getProperty("liquibase.schemaName") == null ? null : System
 101  
             .getProperty("liquibase.schemaName");
 102  
 
 103  
     private Integer lastChangeSetSequenceValue;
 104  
 
 105  124
     private boolean canCacheLiquibaseTableInfo = false;
 106  124
     private boolean hasDatabaseChangeLogTable = false;
 107  124
     private boolean hasDatabaseChangeLogLockTable = false;
 108  
 
 109  124
     protected AbstractDatabase() {
 110  124
     }
 111  
 
 112  
     @Override
 113  
     public boolean requiresPassword() {
 114  0
         return true;
 115  
     }
 116  
 
 117  
     @Override
 118  
     public boolean requiresUsername() {
 119  0
         return true;
 120  
     }
 121  
 
 122  
     @Override
 123  
     public DatabaseObject[] getContainingObjects() {
 124  0
         return null;
 125  
     }
 126  
 
 127  
     // ------- DATABASE INFORMATION METHODS ---- //
 128  
 
 129  
     @Override
 130  
     public DatabaseConnection getConnection() {
 131  176
         return connection;
 132  
     }
 133  
 
 134  
     @Override
 135  
     public void setConnection(DatabaseConnection conn) {
 136  4
         LogFactory.getLogger().debug("Connected to " + conn.getConnectionUserName() + "@" + conn.getURL());
 137  4
         this.connection = conn;
 138  
         try {
 139  4
             connection.setAutoCommit(getAutoCommitMode());
 140  0
         } catch (DatabaseException sqle) {
 141  0
             LogFactory.getLogger().warning("Can not set auto commit to " + getAutoCommitMode() + " on connection");
 142  4
         }
 143  4
     }
 144  
 
 145  
     /**
 146  
      * Auto-commit mode to run in
 147  
      */
 148  
     @Override
 149  
     public boolean getAutoCommitMode() {
 150  4
         return !supportsDDLInTransaction();
 151  
     }
 152  
 
 153  
     /**
 154  
      * By default databases should support DDL within a transaction.
 155  
      */
 156  
     @Override
 157  
     public boolean supportsDDLInTransaction() {
 158  0
         return true;
 159  
     }
 160  
 
 161  
     /**
 162  
      * Returns the name of the database product according to the underlying database.
 163  
      */
 164  
     @Override
 165  
     public String getDatabaseProductName() {
 166  0
         if (connection == null) {
 167  0
             return null;
 168  
         }
 169  
 
 170  
         try {
 171  0
             return connection.getDatabaseProductName();
 172  0
         } catch (DatabaseException e) {
 173  0
             throw new RuntimeException("Cannot get database name");
 174  
         }
 175  
     }
 176  
 
 177  
     @Override
 178  
     public String getDatabaseProductVersion() throws DatabaseException {
 179  0
         if (connection == null) {
 180  0
             return null;
 181  
         }
 182  
 
 183  
         try {
 184  0
             return connection.getDatabaseProductVersion();
 185  0
         } catch (DatabaseException e) {
 186  0
             throw new DatabaseException(e);
 187  
         }
 188  
     }
 189  
 
 190  
     @Override
 191  
     public int getDatabaseMajorVersion() throws DatabaseException {
 192  3
         if (connection == null) {
 193  3
             return -1;
 194  
         }
 195  
         try {
 196  0
             return connection.getDatabaseMajorVersion();
 197  0
         } catch (DatabaseException e) {
 198  0
             throw new DatabaseException(e);
 199  
         }
 200  
     }
 201  
 
 202  
     @Override
 203  
     public int getDatabaseMinorVersion() throws DatabaseException {
 204  0
         if (connection == null) {
 205  0
             return -1;
 206  
         }
 207  
         try {
 208  0
             return connection.getDatabaseMinorVersion();
 209  0
         } catch (DatabaseException e) {
 210  0
             throw new DatabaseException(e);
 211  
         }
 212  
     }
 213  
 
 214  
     @Override
 215  
     public String getDefaultCatalogName() throws DatabaseException {
 216  0
         return null;
 217  
     }
 218  
 
 219  
     protected String getDefaultDatabaseSchemaName() throws DatabaseException {
 220  0
         return getConnection().getConnectionUserName();
 221  
     }
 222  
 
 223  
     @Override
 224  
     public String getDefaultSchemaName() {
 225  1419
         return defaultSchemaName;
 226  
     }
 227  
 
 228  
     @Override
 229  
     public void setDefaultSchemaName(String schemaName) throws DatabaseException {
 230  1
         this.defaultSchemaName = schemaName;
 231  1
     }
 232  
 
 233  
     /**
 234  
      * Returns system (undroppable) tables and views.
 235  
      */
 236  
     protected Set<String> getSystemTablesAndViews() {
 237  0
         return new HashSet<String>();
 238  
     }
 239  
 
 240  
     // ------- DATABASE FEATURE INFORMATION METHODS ---- //
 241  
 
 242  
     /**
 243  
      * Does the database type support sequence.
 244  
      */
 245  
     @Override
 246  
     public boolean supportsSequences() {
 247  45
         return true;
 248  
     }
 249  
 
 250  
     @Override
 251  
     public boolean supportsAutoIncrement() {
 252  82
         return true;
 253  
     }
 254  
 
 255  
     // ------- DATABASE-SPECIFIC SQL METHODS ---- //
 256  
 
 257  
     @Override
 258  
     public void setCurrentDateTimeFunction(String function) {
 259  0
         if (function != null) {
 260  0
             this.currentDateTimeFunction = function;
 261  
         }
 262  0
     }
 263  
 
 264  
     /**
 265  
      * Return a date literal with the same value as a string formatted using ISO 8601.
 266  
      * <p/>
 267  
      * Note: many databases accept date literals in ISO8601 format with the 'T' replaced with a space. Only databases
 268  
      * which do not accept these strings should need to override this method.
 269  
      * <p/>
 270  
      * Implementation restriction: Currently, only the following subsets of ISO8601 are supported: yyyy-MM-dd hh:mm:ss
 271  
      * yyyy-MM-ddThh:mm:ss
 272  
      */
 273  
     @Override
 274  
     public String getDateLiteral(String isoDate) {
 275  7
         if (isDateOnly(isoDate) || isTimeOnly(isoDate)) {
 276  2
             return "'" + isoDate + "'";
 277  5
         } else if (isDateTime(isoDate)) {
 278  
             // StringBuffer val = new StringBuffer();
 279  
             // val.append("'");
 280  
             // val.append(isoDate.substring(0, 10));
 281  
             // val.append(" ");
 282  
             // //noinspection MagicNumber
 283  
             // val.append(isoDate.substring(11));
 284  
             // val.append("'");
 285  
             // return val.toString();
 286  5
             return "'" + isoDate.replace('T', ' ') + "'";
 287  
         } else {
 288  0
             return "BAD_DATE_FORMAT:" + isoDate;
 289  
         }
 290  
     }
 291  
 
 292  
     @Override
 293  
     public String getDateTimeLiteral(java.sql.Timestamp date) {
 294  0
         return getDateLiteral(new ISODateFormat().format(date).replaceFirst("^'", "").replaceFirst("'$", ""));
 295  
     }
 296  
 
 297  
     @Override
 298  
     public String getDateLiteral(java.sql.Date date) {
 299  0
         return getDateLiteral(new ISODateFormat().format(date).replaceFirst("^'", "").replaceFirst("'$", ""));
 300  
     }
 301  
 
 302  
     @Override
 303  
     public String getTimeLiteral(java.sql.Time date) {
 304  0
         return getDateLiteral(new ISODateFormat().format(date).replaceFirst("^'", "").replaceFirst("'$", ""));
 305  
     }
 306  
 
 307  
     @Override
 308  
     public String getDateLiteral(Date date) {
 309  0
         if (date instanceof java.sql.Date) {
 310  0
             return getDateLiteral(((java.sql.Date) date));
 311  0
         } else if (date instanceof java.sql.Time) {
 312  0
             return getTimeLiteral(((java.sql.Time) date));
 313  0
         } else if (date instanceof java.sql.Timestamp) {
 314  0
             return getDateTimeLiteral(((java.sql.Timestamp) date));
 315  
         } else {
 316  0
             throw new RuntimeException("Unexpected type: " + date.getClass().getName());
 317  
         }
 318  
     }
 319  
 
 320  
     @Override
 321  
     public Date parseDate(String dateAsString) throws DateParseException {
 322  
         try {
 323  0
             if (dateAsString.indexOf(" ") > 0) {
 324  0
                 return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateAsString);
 325  0
             } else if (dateAsString.indexOf("T") > 0) {
 326  0
                 return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(dateAsString);
 327  
             } else {
 328  0
                 if (dateAsString.indexOf(":") > 0) {
 329  0
                     return new SimpleDateFormat("HH:mm:ss").parse(dateAsString);
 330  
                 } else {
 331  0
                     return new SimpleDateFormat("yyyy-MM-dd").parse(dateAsString);
 332  
                 }
 333  
             }
 334  0
         } catch (ParseException e) {
 335  0
             throw new DateParseException(dateAsString);
 336  
         }
 337  
     }
 338  
 
 339  
     protected boolean isDateOnly(String isoDate) {
 340  13
         return isoDate.length() == "yyyy-MM-dd".length();
 341  
     }
 342  
 
 343  
     protected boolean isDateTime(String isoDate) {
 344  5
         return isoDate.length() >= "yyyy-MM-ddThh:mm:ss".length();
 345  
     }
 346  
 
 347  
     protected boolean isTimeOnly(String isoDate) {
 348  13
         return isoDate.length() == "hh:mm:ss".length();
 349  
     }
 350  
 
 351  
     /**
 352  
      * Returns database-specific line comment string.
 353  
      */
 354  
     @Override
 355  
     public String getLineComment() {
 356  0
         return "--";
 357  
     }
 358  
 
 359  
     /**
 360  
      * Returns database-specific auto-increment DDL clause.
 361  
      */
 362  
     @Override
 363  
     public String getAutoIncrementClause() {
 364  0
         if (!supportsAutoIncrement()) {
 365  0
             return "";
 366  
         }
 367  0
         return "AUTO_INCREMENT";
 368  
     }
 369  
 
 370  
     @Override
 371  
     public String getConcatSql(String... values) {
 372  0
         StringBuffer returnString = new StringBuffer();
 373  0
         for (String value : values) {
 374  0
             returnString.append(value).append(" || ");
 375  
         }
 376  
 
 377  0
         return returnString.toString().replaceFirst(" \\|\\| $", "");
 378  
     }
 379  
 
 380  
     // ------- DATABASECHANGELOG / DATABASECHANGELOGLOCK METHODS ---- //
 381  
 
 382  
     /**
 383  
      * @see liquibase.database.Database#getDatabaseChangeLogTableName()
 384  
      */
 385  
     @Override
 386  
     public String getDatabaseChangeLogTableName() {
 387  0
         return databaseChangeLogTableName;
 388  
     }
 389  
 
 390  
     /**
 391  
      * @see liquibase.database.Database#getDatabaseChangeLogLockTableName()
 392  
      */
 393  
     @Override
 394  
     public String getDatabaseChangeLogLockTableName() {
 395  0
         return databaseChangeLogLockTableName;
 396  
     }
 397  
 
 398  
     /**
 399  
      * @see liquibase.database.Database#setDatabaseChangeLogTableName(java.lang.String)
 400  
      */
 401  
     @Override
 402  
     public void setDatabaseChangeLogTableName(String tableName) {
 403  0
         this.databaseChangeLogTableName = tableName;
 404  0
     }
 405  
 
 406  
     /**
 407  
      * @see liquibase.database.Database#setDatabaseChangeLogLockTableName(java.lang.String)
 408  
      */
 409  
     @Override
 410  
     public void setDatabaseChangeLogLockTableName(String tableName) {
 411  0
         this.databaseChangeLogLockTableName = tableName;
 412  0
     }
 413  
 
 414  
     /**
 415  
      * This method will check the database ChangeLog table used to keep track of the changes in the file. If the table
 416  
      * does not exist it will create one otherwise it will not do anything besides outputting a log message.
 417  
      * 
 418  
      * @param updateExistingNullChecksums
 419  
      * @param contexts
 420  
      */
 421  
     @Override
 422  
     public void checkDatabaseChangeLogTable(boolean updateExistingNullChecksums, DatabaseChangeLog databaseChangeLog,
 423  
             String... contexts) throws DatabaseException {
 424  0
         Executor executor = ExecutorService.getInstance().getExecutor(this);
 425  
 
 426  0
         Table changeLogTable = DatabaseSnapshotGeneratorFactory.getInstance().getGenerator(this)
 427  
                 .getDatabaseChangeLogTable(this);
 428  
 
 429  0
         List<SqlStatement> statementsToExecute = new ArrayList<SqlStatement>();
 430  
 
 431  0
         boolean changeLogCreateAttempted = false;
 432  0
         if (changeLogTable != null) {
 433  0
             boolean hasDescription = changeLogTable.getColumn("DESCRIPTION") != null;
 434  0
             boolean hasComments = changeLogTable.getColumn("COMMENTS") != null;
 435  0
             boolean hasTag = changeLogTable.getColumn("TAG") != null;
 436  0
             boolean hasLiquibase = changeLogTable.getColumn("LIQUIBASE") != null;
 437  0
             boolean liquibaseColumnNotRightSize = changeLogTable.getColumn("LIQUIBASE").getColumnSize() != 20;
 438  0
             boolean hasOrderExecuted = changeLogTable.getColumn("ORDEREXECUTED") != null;
 439  0
             boolean checksumNotRightSize = changeLogTable.getColumn("MD5SUM").getColumnSize() != 35;
 440  0
             boolean hasExecTypeColumn = changeLogTable.getColumn("EXECTYPE") != null;
 441  
 
 442  0
             if (!hasDescription) {
 443  0
                 executor.comment("Adding missing databasechangelog.description column");
 444  0
                 statementsToExecute.add(new AddColumnStatement(getLiquibaseSchemaName(),
 445  
                         getDatabaseChangeLogTableName(), "DESCRIPTION", "VARCHAR(255)", null));
 446  
             }
 447  0
             if (!hasTag) {
 448  0
                 executor.comment("Adding missing databasechangelog.tag column");
 449  0
                 statementsToExecute.add(new AddColumnStatement(getLiquibaseSchemaName(),
 450  
                         getDatabaseChangeLogTableName(), "TAG", "VARCHAR(255)", null));
 451  
             }
 452  0
             if (!hasComments) {
 453  0
                 executor.comment("Adding missing databasechangelog.comments column");
 454  0
                 statementsToExecute.add(new AddColumnStatement(getLiquibaseSchemaName(),
 455  
                         getDatabaseChangeLogTableName(), "COMMENTS", "VARCHAR(255)", null));
 456  
             }
 457  0
             if (!hasLiquibase) {
 458  0
                 executor.comment("Adding missing databasechangelog.liquibase column");
 459  0
                 statementsToExecute.add(new AddColumnStatement(getLiquibaseSchemaName(),
 460  
                         getDatabaseChangeLogTableName(), "LIQUIBASE", "VARCHAR(255)", null));
 461  
             }
 462  0
             if (!hasOrderExecuted) {
 463  0
                 executor.comment("Adding missing databasechangelog.orderexecuted column");
 464  0
                 statementsToExecute.add(new AddColumnStatement(getLiquibaseSchemaName(),
 465  
                         getDatabaseChangeLogTableName(), "ORDEREXECUTED", "INT", null));
 466  0
                 statementsToExecute.add(new UpdateStatement(getLiquibaseSchemaName(), getDatabaseChangeLogTableName())
 467  
                         .addNewColumnValue("ORDEREXECUTED", -1));
 468  0
                 statementsToExecute.add(new SetNullableStatement(getLiquibaseSchemaName(),
 469  
                         getDatabaseChangeLogTableName(), "ORDEREXECUTED", "INT", false));
 470  
             }
 471  0
             if (checksumNotRightSize) {
 472  0
                 executor.comment("Modifying size of databasechangelog.md5sum column");
 473  
 
 474  0
                 statementsToExecute.add(new ModifyDataTypeStatement(getLiquibaseSchemaName(),
 475  
                         getDatabaseChangeLogTableName(), "MD5SUM", "VARCHAR(35)"));
 476  
             }
 477  0
             if (liquibaseColumnNotRightSize) {
 478  0
                 executor.comment("Modifying size of databasechangelog.liquibase column");
 479  
 
 480  0
                 statementsToExecute.add(new ModifyDataTypeStatement(getLiquibaseSchemaName(),
 481  
                         getDatabaseChangeLogTableName(), "LIQUIBASE", "VARCHAR(20)"));
 482  
             }
 483  0
             if (!hasExecTypeColumn) {
 484  0
                 executor.comment("Adding missing databasechangelog.exectype column");
 485  0
                 statementsToExecute.add(new AddColumnStatement(getLiquibaseSchemaName(),
 486  
                         getDatabaseChangeLogTableName(), "EXECTYPE", "VARCHAR(10)", null));
 487  0
                 statementsToExecute.add(new UpdateStatement(getLiquibaseSchemaName(), getDatabaseChangeLogTableName())
 488  
                         .addNewColumnValue("EXECTYPE", "EXECUTED"));
 489  0
                 statementsToExecute.add(new SetNullableStatement(getLiquibaseSchemaName(),
 490  
                         getDatabaseChangeLogTableName(), "EXECTYPE", "VARCHAR(10)", false));
 491  
             }
 492  
 
 493  0
             List<Map> md5sumRS = ExecutorService
 494  
                     .getInstance()
 495  
                     .getExecutor(this)
 496  
                     .queryForList(
 497  
                             new SelectFromDatabaseChangeLogStatement(
 498  
                                     new SelectFromDatabaseChangeLogStatement.ByNotNullCheckSum(), "MD5SUM"));
 499  0
             if (md5sumRS.size() > 0) {
 500  0
                 String md5sum = md5sumRS.get(0).get("MD5SUM").toString();
 501  0
                 if (!md5sum.startsWith(CheckSum.getCurrentVersion() + ":")) {
 502  0
                     executor.comment("DatabaseChangeLog checksums are an incompatible version.  Setting them to null so they will be updated on next database update");
 503  0
                     statementsToExecute.add(new RawSqlStatement("UPDATE "
 504  
                             + escapeTableName(getLiquibaseSchemaName(), getDatabaseChangeLogTableName())
 505  
                             + " SET MD5SUM=null"));
 506  
                 }
 507  
             }
 508  
 
 509  0
         } else if (!changeLogCreateAttempted) {
 510  0
             executor.comment("Create Database Change Log Table");
 511  0
             SqlStatement createTableStatement = new CreateDatabaseChangeLogTableStatement();
 512  0
             if (!canCreateChangeLogTable()) {
 513  0
                 throw new DatabaseException("Cannot create "
 514  
                         + escapeTableName(getDefaultSchemaName(), getDatabaseChangeLogTableName())
 515  
                         + " table for your database.\n\n"
 516  
                         + "Please construct it manually using the following SQL as a base and re-run Liquibase:\n\n"
 517  
                         + createTableStatement);
 518  
             }
 519  
             // If there is no table in the database for recording change history create one.
 520  0
             statementsToExecute.add(createTableStatement);
 521  0
             LogFactory.getLogger().info(
 522  
                     "Creating database history table with name: "
 523  
                             + escapeTableName(getDefaultSchemaName(), getDatabaseChangeLogTableName()));
 524  
             // }
 525  
         }
 526  
 
 527  0
         for (SqlStatement sql : statementsToExecute) {
 528  0
             executor.execute(sql);
 529  0
             this.commit();
 530  
         }
 531  
 
 532  0
         if (updateExistingNullChecksums) {
 533  0
             for (RanChangeSet ranChangeSet : this.getRanChangeSetList()) {
 534  0
                 if (ranChangeSet.getLastCheckSum() == null) {
 535  0
                     ChangeSet changeSet = databaseChangeLog.getChangeSet(ranChangeSet);
 536  0
                     if (changeSet != null && new ContextChangeSetFilter(contexts).accepts(changeSet)
 537  
                             && new DbmsChangeSetFilter(this).accepts(changeSet)) {
 538  0
                         LogFactory.getLogger()
 539  
                                 .info("Updating null or out of date checksum on changeSet " + changeSet
 540  
                                         + " to correct value");
 541  0
                         executor.execute(new UpdateChangeSetChecksumStatement(changeSet));
 542  
                     }
 543  0
                 }
 544  
             }
 545  0
             commit();
 546  0
             this.ranChangeSetList = null;
 547  
         }
 548  0
     }
 549  
 
 550  
     protected boolean canCreateChangeLogTable() throws DatabaseException {
 551  0
         return true;
 552  
     }
 553  
 
 554  
     @Override
 555  
     public void setCanCacheLiquibaseTableInfo(boolean canCacheLiquibaseTableInfo) {
 556  16
         this.canCacheLiquibaseTableInfo = canCacheLiquibaseTableInfo;
 557  16
         hasDatabaseChangeLogTable = false;
 558  16
         hasDatabaseChangeLogLockTable = false;
 559  16
     }
 560  
 
 561  
     @Override
 562  
     public boolean hasDatabaseChangeLogTable() throws DatabaseException {
 563  0
         if (hasDatabaseChangeLogTable) {
 564  0
             return true;
 565  
         }
 566  0
         boolean hasTable = DatabaseSnapshotGeneratorFactory.getInstance().getGenerator(this)
 567  
                 .hasDatabaseChangeLogTable(this);
 568  0
         if (canCacheLiquibaseTableInfo) {
 569  0
             hasDatabaseChangeLogTable = hasTable;
 570  
         }
 571  0
         return hasTable;
 572  
     }
 573  
 
 574  
     @Override
 575  
     public boolean hasDatabaseChangeLogLockTable() throws DatabaseException {
 576  0
         if (canCacheLiquibaseTableInfo && hasDatabaseChangeLogLockTable) {
 577  0
             return true;
 578  
         }
 579  0
         boolean hasTable = DatabaseSnapshotGeneratorFactory.getInstance().getGenerator(this)
 580  
                 .hasDatabaseChangeLogLockTable(this);
 581  0
         if (canCacheLiquibaseTableInfo) {
 582  0
             hasDatabaseChangeLogLockTable = hasTable;
 583  
         }
 584  0
         return hasTable;
 585  
     }
 586  
 
 587  
     @Override
 588  
     public String getLiquibaseSchemaName() {
 589  0
         return liquibaseSchemaName == null ? getDefaultSchemaName() : liquibaseSchemaName;
 590  
     }
 591  
 
 592  
     /**
 593  
      * This method will check the database ChangeLogLock table used to keep track of if a machine is updating the
 594  
      * database. If the table does not exist it will create one otherwise it will not do anything besides outputting a
 595  
      * log message.
 596  
      */
 597  
     @Override
 598  
     public void checkDatabaseChangeLogLockTable() throws DatabaseException {
 599  
 
 600  0
         Executor executor = ExecutorService.getInstance().getExecutor(this);
 601  0
         if (!hasDatabaseChangeLogLockTable()) {
 602  
 
 603  0
             executor.comment("Create Database Lock Table");
 604  0
             executor.execute(new CreateDatabaseChangeLogLockTableStatement());
 605  0
             this.commit();
 606  0
             LogFactory.getLogger().debug(
 607  
                     "Created database lock table with name: "
 608  
                             + escapeTableName(getLiquibaseSchemaName(), getDatabaseChangeLogLockTableName()));
 609  0
             this.hasDatabaseChangeLogLockTable = true;
 610  
         }
 611  0
     }
 612  
 
 613  
     @Override
 614  
     public boolean isReservedWord(String string) {
 615  0
         return false;
 616  
     }
 617  
 
 618  
     // ------- DATABASE OBJECT DROPPING METHODS ---- //
 619  
 
 620  
     /**
 621  
      * Drops all objects owned by the connected user.
 622  
      * 
 623  
      * @param schema
 624  
      */
 625  
     @Override
 626  
     public void dropDatabaseObjects(String schema) throws DatabaseException {
 627  
         try {
 628  0
             DatabaseSnapshot snapshot = DatabaseSnapshotGeneratorFactory.getInstance().createSnapshot(this, schema,
 629  
                     new HashSet<DiffStatusListener>());
 630  
 
 631  0
             List<Change> dropChanges = new ArrayList<Change>();
 632  
 
 633  0
             for (View view : snapshot.getViews()) {
 634  0
                 DropViewChange dropChange = new DropViewChange();
 635  0
                 dropChange.setViewName(view.getName());
 636  0
                 dropChange.setSchemaName(schema);
 637  
 
 638  0
                 dropChanges.add(dropChange);
 639  0
             }
 640  
 
 641  0
             if (!supportsForeignKeyDisable()) {
 642  0
                 for (ForeignKey fk : snapshot.getForeignKeys()) {
 643  0
                     DropForeignKeyConstraintChange dropFK = new DropForeignKeyConstraintChange();
 644  0
                     dropFK.setBaseTableSchemaName(schema);
 645  0
                     dropFK.setBaseTableName(fk.getForeignKeyTable().getName());
 646  0
                     dropFK.setConstraintName(fk.getName());
 647  
 
 648  0
                     dropChanges.add(dropFK);
 649  0
                 }
 650  
             }
 651  
 
 652  
             // for (Index index : snapshotGenerator.getIndexes()) {
 653  
             // DropIndexChange dropChange = new DropIndexChange();
 654  
             // dropChange.setIndexName(index.getName());
 655  
             // dropChange.setSchemaName(schema);
 656  
             // dropChange.setTableName(index.getTableName());
 657  
             //
 658  
             // dropChanges.add(dropChange);
 659  
             // }
 660  
 
 661  0
             for (Table table : snapshot.getTables()) {
 662  0
                 DropTableChange dropChange = new DropTableChange();
 663  0
                 dropChange.setSchemaName(schema);
 664  0
                 dropChange.setTableName(table.getName());
 665  0
                 if (supportsDropTableCascadeConstraints()) {
 666  0
                     dropChange.setCascadeConstraints(true);
 667  
                 }
 668  
 
 669  0
                 dropChanges.add(dropChange);
 670  0
             }
 671  
 
 672  0
             if (this.supportsSequences()) {
 673  0
                 for (Sequence seq : snapshot.getSequences()) {
 674  0
                     DropSequenceChange dropChange = new DropSequenceChange();
 675  0
                     dropChange.setSequenceName(seq.getName());
 676  0
                     dropChange.setSchemaName(schema);
 677  
 
 678  0
                     dropChanges.add(dropChange);
 679  0
                 }
 680  
             }
 681  
 
 682  0
             if (snapshot.hasDatabaseChangeLogTable()) {
 683  0
                 dropChanges.add(new AnonymousChange(new ClearDatabaseChangeLogTableStatement(schema)));
 684  
             }
 685  
 
 686  0
             final boolean reEnableFK = supportsForeignKeyDisable() && disableForeignKeyChecks();
 687  
             try {
 688  0
                 for (Change change : dropChanges) {
 689  0
                     for (SqlStatement statement : change.generateStatements(this)) {
 690  0
                         ExecutorService.getInstance().getExecutor(this).execute(statement);
 691  
                     }
 692  
                 }
 693  
             } finally {
 694  0
                 if (reEnableFK) {
 695  0
                     enableForeignKeyChecks();
 696  
                 }
 697  
             }
 698  
 
 699  
         } finally {
 700  0
             this.commit();
 701  0
         }
 702  0
     }
 703  
 
 704  
     @Override
 705  
     public boolean supportsDropTableCascadeConstraints() {
 706  0
         return (this instanceof DerbyDatabase || this instanceof DB2Database || this instanceof MSSQLDatabase
 707  
                 || this instanceof FirebirdDatabase || this instanceof SQLiteDatabase || this instanceof SybaseDatabase || this instanceof SybaseASADatabase);
 708  
     }
 709  
 
 710  
     @Override
 711  
     public boolean isSystemTable(String catalogName, String schemaName, String tableName) {
 712  0
         if ("information_schema".equalsIgnoreCase(schemaName)) {
 713  0
             return true;
 714  0
         } else if (tableName.equalsIgnoreCase(getDatabaseChangeLogLockTableName())) {
 715  0
             return true;
 716  0
         } else if (getSystemTablesAndViews().contains(tableName)) {
 717  0
             return true;
 718  
         }
 719  0
         return false;
 720  
     }
 721  
 
 722  
     @Override
 723  
     public boolean isSystemView(String catalogName, String schemaName, String viewName) {
 724  0
         if ("information_schema".equalsIgnoreCase(schemaName)) {
 725  0
             return true;
 726  0
         } else if (getSystemTablesAndViews().contains(viewName)) {
 727  0
             return true;
 728  
         }
 729  0
         return false;
 730  
     }
 731  
 
 732  
     @Override
 733  
     public boolean isLiquibaseTable(String tableName) {
 734  0
         return tableName.equalsIgnoreCase(this.getDatabaseChangeLogTableName())
 735  
                 || tableName.equalsIgnoreCase(this.getDatabaseChangeLogLockTableName());
 736  
     }
 737  
 
 738  
     // ------- DATABASE TAGGING METHODS ---- //
 739  
 
 740  
     /**
 741  
      * Tags the database changelog with the given string.
 742  
      */
 743  
     @Override
 744  
     public void tag(String tagString) throws DatabaseException {
 745  0
         Executor executor = ExecutorService.getInstance().getExecutor(this);
 746  
         try {
 747  0
             int totalRows = ExecutorService.getInstance().getExecutor(this)
 748  
                     .queryForInt(new SelectFromDatabaseChangeLogStatement("COUNT(*)"));
 749  0
             if (totalRows == 0) {
 750  0
                 ChangeSet emptyChangeSet = new ChangeSet(String.valueOf(new Date().getTime()), "liquibase", false,
 751  
                         false, "liquibase-internal", null, null);
 752  0
                 this.markChangeSetExecStatus(emptyChangeSet, ChangeSet.ExecType.EXECUTED);
 753  
             }
 754  
 
 755  
             // Timestamp lastExecutedDate = (Timestamp) this.getExecutor().queryForObject(createChangeToTagSQL(),
 756  
             // Timestamp.class);
 757  0
             executor.execute(new TagDatabaseStatement(tagString));
 758  0
             this.commit();
 759  
 
 760  0
             getRanChangeSetList().get(getRanChangeSetList().size() - 1).setTag(tagString);
 761  0
         } catch (Exception e) {
 762  0
             throw new DatabaseException(e);
 763  0
         }
 764  0
     }
 765  
 
 766  
     @Override
 767  
     public boolean doesTagExist(String tag) throws DatabaseException {
 768  0
         int count = ExecutorService
 769  
                 .getInstance()
 770  
                 .getExecutor(this)
 771  
                 .queryForInt(
 772  
                         new SelectFromDatabaseChangeLogStatement(new SelectFromDatabaseChangeLogStatement.ByTag("tag"),
 773  
                                 "COUNT(*)"));
 774  0
         return count > 0;
 775  
     }
 776  
 
 777  
     @Override
 778  
     public String toString() {
 779  154
         if (getConnection() == null) {
 780  154
             return getTypeName() + " Database";
 781  
         }
 782  
 
 783  0
         return getConnection().getConnectionUserName() + " @ " + getConnection().getURL()
 784  
                 + (getDefaultSchemaName() == null ? "" : " (Default Schema: " + getDefaultSchemaName() + ")");
 785  
     }
 786  
 
 787  
     @Override
 788  
     public boolean shouldQuoteValue(String value) {
 789  7
         return true;
 790  
     }
 791  
 
 792  
     @Override
 793  
     public String getViewDefinition(String schemaName, String viewName) throws DatabaseException {
 794  0
         if (schemaName == null) {
 795  0
             schemaName = convertRequestedSchemaToSchema(null);
 796  
         }
 797  0
         String definition = (String) ExecutorService.getInstance().getExecutor(this)
 798  
                 .queryForObject(new GetViewDefinitionStatement(schemaName, viewName), String.class);
 799  0
         if (definition == null) {
 800  0
             return null;
 801  
         }
 802  0
         return CREATE_VIEW_AS_PATTERN.matcher(definition).replaceFirst("");
 803  
     }
 804  
 
 805  
     @Override
 806  
     public String escapeTableName(String schemaName, String tableName) {
 807  183
         if (schemaName == null) {
 808  116
             schemaName = getDefaultSchemaName();
 809  
         }
 810  
 
 811  183
         if (StringUtils.trimToNull(schemaName) == null || !supportsSchemas()) {
 812  122
             return escapeDatabaseObject(tableName);
 813  
         } else {
 814  61
             return escapeDatabaseObject(schemaName) + "." + escapeDatabaseObject(tableName);
 815  
         }
 816  
     }
 817  
 
 818  
     @Override
 819  
     public String escapeDatabaseObject(String objectName) {
 820  103
         return objectName;
 821  
     }
 822  
 
 823  
     @Override
 824  
     public String escapeIndexName(String schemaName, String indexName) {
 825  0
         if (StringUtils.trimToNull(schemaName) == null || !supportsSchemas()) {
 826  0
             return escapeDatabaseObject(indexName);
 827  
         } else {
 828  0
             return escapeDatabaseObject(schemaName) + "." + escapeDatabaseObject(indexName);
 829  
         }
 830  
     }
 831  
 
 832  
     @Override
 833  
     public String escapeSequenceName(String schemaName, String sequenceName) {
 834  0
         if (schemaName == null) {
 835  0
             schemaName = getDefaultSchemaName();
 836  
         }
 837  
 
 838  0
         if (StringUtils.trimToNull(schemaName) == null || !supportsSchemas()) {
 839  0
             return escapeDatabaseObject(sequenceName);
 840  
         } else {
 841  0
             return escapeDatabaseObject(schemaName) + "." + escapeDatabaseObject(sequenceName);
 842  
         }
 843  
     }
 844  
 
 845  
     @Override
 846  
     public String escapeConstraintName(String constraintName) {
 847  0
         return escapeDatabaseObject(constraintName);
 848  
     }
 849  
 
 850  
     @Override
 851  
     public String escapeColumnName(String schemaName, String tableName, String columnName) {
 852  9
         if (columnName.contains("(")) {
 853  0
             return columnName;
 854  
         }
 855  
 
 856  9
         if (schemaName == null) {
 857  0
             schemaName = getDefaultSchemaName();
 858  
         }
 859  
 
 860  9
         return escapeDatabaseObject(columnName);
 861  
     }
 862  
 
 863  
     @Override
 864  
     public String escapeColumnNameList(String columnNames) {
 865  0
         StringBuffer sb = new StringBuffer();
 866  0
         for (String columnName : columnNames.split(",")) {
 867  0
             if (sb.length() > 0) {
 868  0
                 sb.append(", ");
 869  
             }
 870  0
             sb.append(escapeDatabaseObject(columnName.trim()));
 871  
         }
 872  0
         return sb.toString();
 873  
 
 874  
     }
 875  
 
 876  
     @Override
 877  
     public String convertRequestedSchemaToCatalog(String requestedSchema) throws DatabaseException {
 878  0
         if (getDefaultCatalogName() == null) {
 879  0
             return null;
 880  
         } else {
 881  0
             if (requestedSchema == null) {
 882  0
                 return getDefaultCatalogName();
 883  
             }
 884  0
             return StringUtils.trimToNull(requestedSchema);
 885  
         }
 886  
     }
 887  
 
 888  
     @Override
 889  
     public String convertRequestedSchemaToSchema(String requestedSchema) throws DatabaseException {
 890  0
         String returnSchema = requestedSchema;
 891  0
         if (returnSchema == null) {
 892  0
             returnSchema = getDefaultDatabaseSchemaName();
 893  
         }
 894  
 
 895  0
         if (returnSchema != null) {
 896  0
             returnSchema = returnSchema.toUpperCase();
 897  
         }
 898  0
         return returnSchema;
 899  
     }
 900  
 
 901  
     @Override
 902  
     public boolean supportsSchemas() {
 903  62
         return true;
 904  
     }
 905  
 
 906  
     @Override
 907  
     public String generatePrimaryKeyName(String tableName) {
 908  0
         return "PK_" + tableName.toUpperCase();
 909  
     }
 910  
 
 911  
     @Override
 912  
     public String escapeViewName(String schemaName, String viewName) {
 913  0
         return escapeTableName(schemaName, viewName);
 914  
     }
 915  
 
 916  
     /**
 917  
      * Returns the run status for the given ChangeSet
 918  
      */
 919  
     @Override
 920  
     public ChangeSet.RunStatus getRunStatus(ChangeSet changeSet) throws DatabaseException, DatabaseHistoryException {
 921  0
         if (!hasDatabaseChangeLogTable()) {
 922  0
             return ChangeSet.RunStatus.NOT_RAN;
 923  
         }
 924  
 
 925  0
         RanChangeSet foundRan = getRanChangeSet(changeSet);
 926  
 
 927  0
         if (foundRan == null) {
 928  0
             return ChangeSet.RunStatus.NOT_RAN;
 929  
         } else {
 930  0
             if (foundRan.getLastCheckSum() == null) {
 931  
                 try {
 932  0
                     LogFactory.getLogger().info("Updating NULL md5sum for " + changeSet.toString());
 933  0
                     ExecutorService
 934  
                             .getInstance()
 935  
                             .getExecutor(this)
 936  
                             .execute(
 937  
                                     new RawSqlStatement(
 938  
                                             "UPDATE "
 939  
                                                     + escapeTableName(getLiquibaseSchemaName(),
 940  
                                                             getDatabaseChangeLogTableName()) + " SET MD5SUM='"
 941  
                                                     + changeSet.generateCheckSum().toString() + "' WHERE ID='"
 942  
                                                     + changeSet.getId() + "' AND AUTHOR='" + changeSet.getAuthor()
 943  
                                                     + "' AND FILENAME='" + changeSet.getFilePath() + "'"));
 944  
 
 945  0
                     this.commit();
 946  0
                 } catch (DatabaseException e) {
 947  0
                     throw new DatabaseException(e);
 948  0
                 }
 949  
 
 950  0
                 return ChangeSet.RunStatus.ALREADY_RAN;
 951  
             } else {
 952  0
                 if (foundRan.getLastCheckSum().equals(changeSet.generateCheckSum())) {
 953  0
                     return ChangeSet.RunStatus.ALREADY_RAN;
 954  
                 } else {
 955  0
                     if (changeSet.shouldRunOnChange()) {
 956  0
                         return ChangeSet.RunStatus.RUN_AGAIN;
 957  
                     } else {
 958  0
                         return ChangeSet.RunStatus.INVALID_MD5SUM;
 959  
                         // throw new DatabaseHistoryException("MD5 Check for " + changeSet.toString() + " failed");
 960  
                     }
 961  
                 }
 962  
             }
 963  
         }
 964  
     }
 965  
 
 966  
     @Override
 967  
     public RanChangeSet getRanChangeSet(ChangeSet changeSet) throws DatabaseException, DatabaseHistoryException {
 968  0
         if (!hasDatabaseChangeLogTable()) {
 969  0
             return null;
 970  
         }
 971  
 
 972  0
         RanChangeSet foundRan = null;
 973  0
         for (RanChangeSet ranChange : getRanChangeSetList()) {
 974  0
             if (ranChange.isSameAs(changeSet)) {
 975  0
                 foundRan = ranChange;
 976  0
                 break;
 977  
             }
 978  
         }
 979  0
         return foundRan;
 980  
     }
 981  
 
 982  
     /**
 983  
      * Returns the ChangeSets that have been run against the current database.
 984  
      */
 985  
     @Override
 986  
     public List<RanChangeSet> getRanChangeSetList() throws DatabaseException {
 987  0
         if (this.ranChangeSetList != null) {
 988  0
             return this.ranChangeSetList;
 989  
         }
 990  
 
 991  0
         String databaseChangeLogTableName = escapeTableName(getLiquibaseSchemaName(), getDatabaseChangeLogTableName());
 992  0
         ranChangeSetList = new ArrayList<RanChangeSet>();
 993  0
         if (hasDatabaseChangeLogTable()) {
 994  0
             LogFactory.getLogger().info("Reading from " + databaseChangeLogTableName);
 995  0
             SqlStatement select = new SelectFromDatabaseChangeLogStatement("FILENAME", "AUTHOR", "ID", "MD5SUM",
 996  
                     "DATEEXECUTED", "ORDEREXECUTED", "TAG", "EXECTYPE").setOrderBy("DATEEXECUTED ASC",
 997  
                     "ORDEREXECUTED ASC");
 998  0
             List<Map> results = ExecutorService.getInstance().getExecutor(this).queryForList(select);
 999  0
             for (Map rs : results) {
 1000  0
                 String fileName = rs.get("FILENAME").toString();
 1001  0
                 String author = rs.get("AUTHOR").toString();
 1002  0
                 String id = rs.get("ID").toString();
 1003  0
                 String md5sum = rs.get("MD5SUM") == null ? null : rs.get("MD5SUM").toString();
 1004  0
                 Date dateExecuted = (Date) rs.get("DATEEXECUTED");
 1005  0
                 String tag = rs.get("TAG") == null ? null : rs.get("TAG").toString();
 1006  0
                 String execType = rs.get("EXECTYPE") == null ? null : rs.get("EXECTYPE").toString();
 1007  
                 try {
 1008  0
                     RanChangeSet ranChangeSet = new RanChangeSet(fileName, id, author, CheckSum.parse(md5sum),
 1009  
                             dateExecuted, tag, ChangeSet.ExecType.valueOf(execType));
 1010  0
                     ranChangeSetList.add(ranChangeSet);
 1011  0
                 } catch (IllegalArgumentException e) {
 1012  0
                     LogFactory.getLogger().severe("Unknown EXECTYPE from database: " + execType);
 1013  0
                     throw e;
 1014  0
                 }
 1015  0
             }
 1016  
         }
 1017  0
         return ranChangeSetList;
 1018  
     }
 1019  
 
 1020  
     @Override
 1021  
     public Date getRanDate(ChangeSet changeSet) throws DatabaseException, DatabaseHistoryException {
 1022  0
         RanChangeSet ranChange = getRanChangeSet(changeSet);
 1023  0
         if (ranChange == null) {
 1024  0
             return null;
 1025  
         } else {
 1026  0
             return ranChange.getDateExecuted();
 1027  
         }
 1028  
     }
 1029  
 
 1030  
     /**
 1031  
      * After the change set has been ran against the database this method will update the change log table with the
 1032  
      * information.
 1033  
      */
 1034  
     @Override
 1035  
     public void markChangeSetExecStatus(ChangeSet changeSet, ChangeSet.ExecType execType) throws DatabaseException {
 1036  
 
 1037  0
         ExecutorService.getInstance().getExecutor(this).execute(new MarkChangeSetRanStatement(changeSet, execType));
 1038  0
         commit();
 1039  0
         getRanChangeSetList().add(new RanChangeSet(changeSet, execType));
 1040  0
     }
 1041  
 
 1042  
     @Override
 1043  
     public void removeRanStatus(ChangeSet changeSet) throws DatabaseException {
 1044  
 
 1045  0
         ExecutorService.getInstance().getExecutor(this).execute(new RemoveChangeSetRanStatusStatement(changeSet));
 1046  0
         commit();
 1047  
 
 1048  0
         getRanChangeSetList().remove(new RanChangeSet(changeSet));
 1049  0
     }
 1050  
 
 1051  
     @Override
 1052  
     public String escapeStringForDatabase(String string) {
 1053  7
         if (string == null) {
 1054  0
             return null;
 1055  
         }
 1056  7
         return string.replaceAll("'", "''");
 1057  
     }
 1058  
 
 1059  
     @Override
 1060  
     public void commit() throws DatabaseException {
 1061  
         try {
 1062  0
             getConnection().commit();
 1063  0
         } catch (DatabaseException e) {
 1064  0
             throw new DatabaseException(e);
 1065  0
         }
 1066  0
     }
 1067  
 
 1068  
     @Override
 1069  
     public void rollback() throws DatabaseException {
 1070  
         try {
 1071  0
             getConnection().rollback();
 1072  0
         } catch (DatabaseException e) {
 1073  0
             throw new DatabaseException(e);
 1074  0
         }
 1075  0
     }
 1076  
 
 1077  
     @Override
 1078  
     public boolean equals(Object o) {
 1079  9
         if (this == o)
 1080  9
             return true;
 1081  0
         if (o == null || getClass() != o.getClass())
 1082  0
             return false;
 1083  
 
 1084  0
         AbstractDatabase that = (AbstractDatabase) o;
 1085  
 
 1086  0
         if (connection == null) {
 1087  0
             if (that.connection == null) {
 1088  0
                 return this == that;
 1089  
             } else {
 1090  0
                 return false;
 1091  
             }
 1092  
         } else {
 1093  0
             return connection.equals(that.connection);
 1094  
         }
 1095  
     }
 1096  
 
 1097  
     @Override
 1098  
     public int hashCode() {
 1099  93
         return (connection != null ? connection.hashCode() : super.hashCode());
 1100  
     }
 1101  
 
 1102  
     @Override
 1103  
     public void close() throws DatabaseException {
 1104  
         try {
 1105  0
             DatabaseConnection connection = getConnection();
 1106  0
             if (connection != null) {
 1107  0
                 connection.close();
 1108  
             }
 1109  0
         } catch (DatabaseException e) {
 1110  0
             throw new DatabaseException(e);
 1111  0
         }
 1112  0
     }
 1113  
 
 1114  
     @Override
 1115  
     public boolean supportsRestrictForeignKeys() {
 1116  0
         return true;
 1117  
     }
 1118  
 
 1119  
     @Override
 1120  
     public boolean isAutoCommit() throws DatabaseException {
 1121  
         try {
 1122  0
             return getConnection().getAutoCommit();
 1123  0
         } catch (DatabaseException e) {
 1124  0
             throw new DatabaseException(e);
 1125  
         }
 1126  
     }
 1127  
 
 1128  
     @Override
 1129  
     public void setAutoCommit(boolean b) throws DatabaseException {
 1130  
         try {
 1131  0
             getConnection().setAutoCommit(b);
 1132  0
         } catch (DatabaseException e) {
 1133  0
             throw new DatabaseException(e);
 1134  0
         }
 1135  0
     }
 1136  
 
 1137  
     /**
 1138  
      * Default implementation, just look for "local" IPs
 1139  
      * 
 1140  
      * @throws liquibase.exception.DatabaseException
 1141  
      * 
 1142  
      */
 1143  
     @Override
 1144  
     public boolean isLocalDatabase() throws DatabaseException {
 1145  0
         DatabaseConnection connection = getConnection();
 1146  0
         if (connection == null) {
 1147  0
             return true;
 1148  
         }
 1149  0
         String url = connection.getURL();
 1150  0
         return (url.contains("localhost")) || (url.contains("127.0.0.1"));
 1151  
     }
 1152  
 
 1153  
     @Override
 1154  
     public void executeStatements(Change change, DatabaseChangeLog changeLog, List<SqlVisitor> sqlVisitors)
 1155  
             throws LiquibaseException, UnsupportedChangeException {
 1156  0
         SqlStatement[] statements = change.generateStatements(this);
 1157  
 
 1158  0
         execute(statements, sqlVisitors);
 1159  0
     }
 1160  
 
 1161  
     /*
 1162  
      * Executes the statements passed as argument to a target {@link Database}
 1163  
      * 
 1164  
      * @param statements an array containing the SQL statements to be issued
 1165  
      * 
 1166  
      * @param database the target {@link Database}
 1167  
      * 
 1168  
      * @throws DatabaseException if there were problems issuing the statements
 1169  
      */
 1170  
     @Override
 1171  
     public void execute(SqlStatement[] statements, List<SqlVisitor> sqlVisitors) throws LiquibaseException {
 1172  0
         for (SqlStatement statement : statements) {
 1173  0
             if (statement.skipOnUnsupported() && !SqlGeneratorFactory.getInstance().supports(statement, this)) {
 1174  0
                 continue;
 1175  
             }
 1176  0
             LogFactory.getLogger().debug("Executing Statement: " + statement);
 1177  0
             ExecutorService.getInstance().getExecutor(this).execute(statement, sqlVisitors);
 1178  
         }
 1179  0
     }
 1180  
 
 1181  
     @Override
 1182  
     public void saveStatements(Change change, List<SqlVisitor> sqlVisitors, Writer writer) throws IOException,
 1183  
             UnsupportedChangeException, StatementNotSupportedOnDatabaseException, LiquibaseException {
 1184  0
         SqlStatement[] statements = change.generateStatements(this);
 1185  0
         for (SqlStatement statement : statements) {
 1186  0
             for (Sql sql : SqlGeneratorFactory.getInstance().generateSql(statement, this)) {
 1187  0
                 writer.append(sql.toSql()).append(sql.getEndDelimiter()).append(StreamUtil.getLineSeparator())
 1188  
                         .append(StreamUtil.getLineSeparator());
 1189  
             }
 1190  
         }
 1191  0
     }
 1192  
 
 1193  
     @Override
 1194  
     public void executeRollbackStatements(Change change, List<SqlVisitor> sqlVisitors) throws LiquibaseException,
 1195  
             UnsupportedChangeException, RollbackImpossibleException {
 1196  0
         SqlStatement[] statements = change.generateRollbackStatements(this);
 1197  0
         List<SqlVisitor> rollbackVisitors = new ArrayList<SqlVisitor>();
 1198  0
         if (sqlVisitors != null) {
 1199  0
             for (SqlVisitor visitor : sqlVisitors) {
 1200  0
                 if (visitor.isApplyToRollback()) {
 1201  0
                     rollbackVisitors.add(visitor);
 1202  
                 }
 1203  
             }
 1204  
         }
 1205  0
         execute(statements, rollbackVisitors);
 1206  0
     }
 1207  
 
 1208  
     @Override
 1209  
     public void saveRollbackStatement(Change change, List<SqlVisitor> sqlVisitors, Writer writer) throws IOException,
 1210  
             UnsupportedChangeException, RollbackImpossibleException, StatementNotSupportedOnDatabaseException,
 1211  
             LiquibaseException {
 1212  0
         SqlStatement[] statements = change.generateRollbackStatements(this);
 1213  0
         for (SqlStatement statement : statements) {
 1214  0
             for (Sql sql : SqlGeneratorFactory.getInstance().generateSql(statement, this)) {
 1215  0
                 writer.append(sql.toSql()).append(sql.getEndDelimiter()).append("\n\n");
 1216  
             }
 1217  
         }
 1218  0
     }
 1219  
 
 1220  
     @Override
 1221  
     public int getNextChangeSetSequenceValue() throws LiquibaseException {
 1222  0
         if (lastChangeSetSequenceValue == null) {
 1223  0
             if (getConnection() == null) {
 1224  0
                 lastChangeSetSequenceValue = 0;
 1225  
             } else {
 1226  0
                 lastChangeSetSequenceValue = ExecutorService.getInstance().getExecutor(this)
 1227  
                         .queryForInt(new GetNextChangeSetSequenceValueStatement());
 1228  
             }
 1229  
         }
 1230  
 
 1231  0
         return ++lastChangeSetSequenceValue;
 1232  
     }
 1233  
 
 1234  
     public Table getTable(String schemaName, String tableName) throws DatabaseException {
 1235  0
         return DatabaseSnapshotGeneratorFactory.getInstance().getGenerator(this).getTable(schemaName, tableName, this);
 1236  
     }
 1237  
 
 1238  
     @Override
 1239  
     public List<DatabaseFunction> getDatabaseFunctions() {
 1240  0
         return databaseFunctions;
 1241  
     }
 1242  
 
 1243  
     @Override
 1244  
     public void reset() {
 1245  0
         this.ranChangeSetList = null;
 1246  0
         this.hasDatabaseChangeLogLockTable = false;
 1247  0
     }
 1248  
 
 1249  
     @Override
 1250  
     public boolean supportsForeignKeyDisable() {
 1251  0
         return false;
 1252  
     }
 1253  
 
 1254  
     @Override
 1255  
     public boolean disableForeignKeyChecks() throws DatabaseException {
 1256  0
         throw new DatabaseException("ForeignKeyChecks Management not supported");
 1257  
     }
 1258  
 
 1259  
     @Override
 1260  
     public void enableForeignKeyChecks() throws DatabaseException {
 1261  0
         throw new DatabaseException("ForeignKeyChecks Management not supported");
 1262  
     }
 1263  
 }