Пример #1
0
  @Override
  public void setConnection(final DatabaseConnection conn) {
    LogFactory.getLogger()
        .debug("Connected to " + conn.getConnectionUserName() + "@" + conn.getURL());
    this.connection = conn;
    try {
      boolean autoCommit = conn.getAutoCommit();
      if (autoCommit == getAutoCommitMode()) {
        // Don't adjust the auto-commit mode if it's already what the database wants it to be.
        LogFactory.getLogger()
            .debug("Not adjusting the auto commit mode; it is already " + autoCommit);
      } else {
        // Store the previous auto-commit mode, because the connection needs to be restored to it
        // when this
        // AbstractDatabase type is closed. This is important for systems which use connection
        // pools.
        previousAutoCommit = autoCommit;

        LogFactory.getLogger()
            .debug("Setting auto commit to " + getAutoCommitMode() + " from " + autoCommit);
        connection.setAutoCommit(getAutoCommitMode());
      }
    } catch (DatabaseException e) {
      LogFactory.getLogger()
          .warning("Cannot set auto commit to " + getAutoCommitMode() + " on connection");
    }

    this.connection.attached(this);
  }
Пример #2
0
  @Override
  public void setConnection(DatabaseConnection conn) {
    reservedWords.addAll(
        Arrays.asList(
            "GROUP",
            "USER",
            "SESSION",
            "PASSWORD",
            "RESOURCE",
            "START",
            "SIZE",
            "UID",
            "DESC")); // more reserved words not returned by driver

    Connection sqlConn = null;
    if (!(conn instanceof OfflineConnection)) {
      try {
        /**
         * Don't try to call getWrappedConnection if the conn instance is is not a JdbcConnection.
         * This happens for OfflineConnection.
         *
         * @see <a href="https://liquibase.jira.com/browse/CORE-2192">CORE-2192</a>
         */
        if (conn instanceof JdbcConnection) {
          Method wrappedConn = conn.getClass().getMethod("getWrappedConnection");
          wrappedConn.setAccessible(true);
          sqlConn = (Connection) wrappedConn.invoke(conn);
        }
      } catch (Exception e) {
        throw new UnexpectedLiquibaseException(e);
      }

      if (sqlConn != null) {
        try {
          reservedWords.addAll(
              Arrays.asList(sqlConn.getMetaData().getSQLKeywords().toUpperCase().split(",\\s*")));
        } catch (SQLException e) {
          LogFactory.getLogger()
              .info("Could get sql keywords on OracleDatabase: " + e.getMessage());
          // can not get keywords. Continue on
        }
        try {
          Method method = sqlConn.getClass().getMethod("setRemarksReporting", Boolean.TYPE);
          method.setAccessible(true);
          method.invoke(sqlConn, true);
        } catch (Exception e) {
          LogFactory.getLogger()
              .info("Could not set remarks reporting on OracleDatabase: " + e.getMessage());
          ; // cannot set it. That is OK
        }
      }
    }
    super.setConnection(conn);
  }
Пример #3
0
 @Override
 public void setConnection(DatabaseConnection conn) {
   LogFactory.getLogger()
       .debug("Connected to " + conn.getConnectionUserName() + "@" + conn.getURL());
   this.connection = conn;
   try {
     connection.setAutoCommit(getAutoCommitMode());
   } catch (DatabaseException sqle) {
     LogFactory.getLogger()
         .warning("Can not set auto commit to " + getAutoCommitMode() + " on connection");
   }
 }
Пример #4
0
  /** Returns the ChangeSets that have been run against the current database. */
  @Override
  public List<RanChangeSet> getRanChangeSetList() throws DatabaseException {
    if (this.ranChangeSetList != null) {
      return this.ranChangeSetList;
    }

    String databaseChangeLogTableName =
        escapeTableName(getLiquibaseSchemaName(), getDatabaseChangeLogTableName());
    ranChangeSetList = new ArrayList<RanChangeSet>();
    if (hasDatabaseChangeLogTable()) {
      LogFactory.getLogger().info("Reading from " + databaseChangeLogTableName);
      SqlStatement select =
          new SelectFromDatabaseChangeLogStatement(
                  "FILENAME",
                  "AUTHOR",
                  "ID",
                  "MD5SUM",
                  "DATEEXECUTED",
                  "ORDEREXECUTED",
                  "TAG",
                  "EXECTYPE")
              .setOrderBy("DATEEXECUTED ASC", "ORDEREXECUTED ASC");
      List<Map> results = ExecutorService.getInstance().getExecutor(this).queryForList(select);
      for (Map<?, ?> rs : results) {
        String fileName = rs.get("FILENAME").toString();
        String author = rs.get("AUTHOR").toString();
        String id = rs.get("ID").toString();
        String md5sum = rs.get("MD5SUM") == null ? null : rs.get("MD5SUM").toString();
        Date dateExecuted = (Date) rs.get("DATEEXECUTED");
        String tag = rs.get("TAG") == null ? null : rs.get("TAG").toString();
        String execType = rs.get("EXECTYPE") == null ? null : rs.get("EXECTYPE").toString();
        try {
          RanChangeSet ranChangeSet =
              new RanChangeSet(
                  fileName,
                  id,
                  author,
                  CheckSum.parse(md5sum),
                  dateExecuted,
                  tag,
                  ChangeSet.ExecType.valueOf(execType));
          ranChangeSetList.add(ranChangeSet);
        } catch (IllegalArgumentException e) {
          LogFactory.getLogger().severe("Unknown EXECTYPE from database: " + execType);
          throw e;
        }
      }
    }
    return ranChangeSetList;
  }
Пример #5
0
  @Override
  public void waitForLock() throws LockException {

    boolean locked = false;
    long timeToGiveUp = new Date().getTime() + (getChangeLogLockWaitTime() * 1000 * 60);
    while (!locked && new Date().getTime() < timeToGiveUp) {
      locked = acquireLock();
      if (!locked) {
        LogFactory.getLogger().info("Waiting for changelog lock....");
        try {
          Thread.sleep(getChangeLogLockRecheckTime() * 1000);
        } catch (InterruptedException e) {;
        }
      }
    }

    if (!locked) {
      DatabaseChangeLogLock[] locks = listLocks();
      String lockedBy;
      if (locks.length > 0) {
        DatabaseChangeLogLock lock = locks[0];
        lockedBy =
            lock.getLockedBy()
                + " since "
                + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT)
                    .format(lock.getLockGranted());
      } else {
        lockedBy = "UNKNOWN";
      }
      throw new LockException(
          "Could not acquire change log lock.  Currently locked by " + lockedBy);
    }
  }
Пример #6
0
  @Override
  public void finishInitialization() throws SetupException {
    if (path == null) {
      throw new SetupException("<sqlfile> - No path specified");
    }
    LogFactory.getLogger().debug("SQLFile file:" + path);
    boolean loaded = loadFromClasspath(path);
    if (!loaded) {
      loaded = loadFromFileSystem(path);
    }

    if (!loaded) {
      throw new SetupException("<sqlfile path=" + path + "> - Could not find file");
    }
    LogFactory.getLogger().debug("SQLFile file contents is:" + getSql());
  }
Пример #7
0
  @Override
  public void setConnection(DatabaseConnection conn) {
    try {
      Method wrappedConn = conn.getClass().getMethod("getWrappedConnection");
      wrappedConn.setAccessible(true);
      Connection sqlConn = (Connection) wrappedConn.invoke(conn);
      Method method = sqlConn.getClass().getMethod("setRemarksReporting", Boolean.TYPE);
      method.setAccessible(true);
      method.invoke(sqlConn, true);

      reservedWords.addAll(
          Arrays.asList(sqlConn.getMetaData().getSQLKeywords().toUpperCase().split(",\\s*")));
      reservedWords.addAll(
          Arrays.asList(
              "GROUP",
              "USER",
              "SESSION",
              "PASSWORD",
              "RESOURCE",
              "START",
              "SIZE",
              "UID")); // more reserved words not returned by driver
    } catch (Exception e) {
      LogFactory.getLogger()
          .info("Could not set remarks reporting on OracleDatabase: " + e.getMessage());
      ; // cannot set it. That is OK
    }
    super.setConnection(conn);
  }
Пример #8
0
  public void releaseLock() throws DatabaseException, LockException {
    Executor executor = ExecutorService.getInstance().getExecutor(database);
    try {
      if (database.hasDatabaseChangeLogLockTable()) {
        executor.comment("Release Database Lock");
        database.rollback();
        int updatedRows = executor.update(new UnlockDatabaseChangeLogStatement());
        if (updatedRows != 1) {
          throw new LockException(
              "Did not update change log lock correctly.\n\n"
                  + updatedRows
                  + " rows were updated instead of the expected 1 row using executor "
                  + executor.getClass().getName()
                  + " there are "
                  + executor.queryForInt(
                      new RawSqlStatement(
                          "select count(*) from " + database.getDatabaseChangeLogLockTableName()))
                  + " rows in the table");
        }
        database.commit();
        hasChangeLogLock = false;

        instances.remove(this.database);

        database.setCanCacheLiquibaseTableInfo(false);

        LogFactory.getLogger().info("Successfully released change log lock");
      }
    } finally {
      database.rollback();
    }
  }
Пример #9
0
  @Override
  public void init() throws DatabaseException {

    boolean createdTable = false;
    Executor executor = ExecutorService.getInstance().getExecutor(database);
    if (!hasDatabaseChangeLogLockTable()) {

      executor.comment("Create Database Lock Table");
      executor.execute(new CreateDatabaseChangeLogLockTableStatement());
      database.commit();
      LogFactory.getLogger()
          .debug(
              "Created database lock table with name: "
                  + database.escapeTableName(
                      database.getLiquibaseCatalogName(),
                      database.getLiquibaseSchemaName(),
                      database.getDatabaseChangeLogLockTableName()));
      this.hasDatabaseChangeLogLockTable = true;
      createdTable = true;
    }

    if (!isDatabaseChangeLogLockTableInitialized(createdTable)) {
      executor.comment("Initialize Database Lock Table");
      executor.execute(new InitializeDatabaseChangeLogLockTableStatement());
      database.commit();
    }

    if (database instanceof DerbyDatabase
        && ((DerbyDatabase) database)
            .supportsBooleanDataType()) { // check if the changelog table is of an old smallint vs.
                                          // boolean format
      String lockTable =
          database.escapeTableName(
              database.getLiquibaseCatalogName(),
              database.getLiquibaseSchemaName(),
              database.getDatabaseChangeLogLockTableName());
      Object obj =
          executor.queryForObject(
              new RawSqlStatement(
                  "select min(locked) as test from " + lockTable + " fetch first row only"),
              Object.class);
      if (!(obj instanceof Boolean)) { // wrong type, need to recreate table
        executor.execute(
            new DropTableStatement(
                database.getLiquibaseCatalogName(),
                database.getLiquibaseSchemaName(),
                database.getDatabaseChangeLogLockTableName(),
                false));
        executor.execute(new CreateDatabaseChangeLogLockTableStatement());
        executor.execute(new InitializeDatabaseChangeLogLockTableStatement());
      }
    }
  }
Пример #10
0
  /** Returns the run status for the given ChangeSet */
  @Override
  public ChangeSet.RunStatus getRunStatus(ChangeSet changeSet)
      throws DatabaseException, DatabaseHistoryException {
    if (!hasDatabaseChangeLogTable()) {
      return ChangeSet.RunStatus.NOT_RAN;
    }

    RanChangeSet foundRan = getRanChangeSet(changeSet);

    if (foundRan == null) {
      return ChangeSet.RunStatus.NOT_RAN;
    } else {
      if (foundRan.getLastCheckSum() == null) {
        try {
          LogFactory.getLogger().info("Updating NULL md5sum for " + changeSet.toString());
          ExecutorService.getInstance()
              .getExecutor(this)
              .execute(
                  new RawSqlStatement(
                      "UPDATE "
                          + escapeTableName(
                              getLiquibaseSchemaName(), getDatabaseChangeLogTableName())
                          + " SET MD5SUM='"
                          + changeSet.generateCheckSum().toString()
                          + "' WHERE ID='"
                          + changeSet.getId()
                          + "' AND AUTHOR='"
                          + changeSet.getAuthor()
                          + "' AND FILENAME='"
                          + changeSet.getFilePath()
                          + "'"));

          this.commit();
        } catch (DatabaseException e) {
          throw new DatabaseException(e);
        }

        return ChangeSet.RunStatus.ALREADY_RAN;
      } else {
        if (foundRan.getLastCheckSum().equals(changeSet.generateCheckSum())) {
          return ChangeSet.RunStatus.ALREADY_RAN;
        } else {
          if (changeSet.shouldRunOnChange()) {
            return ChangeSet.RunStatus.RUN_AGAIN;
          } else {
            return ChangeSet.RunStatus.INVALID_MD5SUM;
            // throw new DatabaseHistoryException("MD5 Check for " + changeSet.toString() + "
            // failed");
          }
        }
      }
    }
  }
Пример #11
0
 /*
  * Executes the statements passed
  *
  * @param statements an array containing the SQL statements to be issued
  * @param sqlVisitors a list of {@link SqlVisitor} objects to be applied to the executed statements
  * @throws DatabaseException if there were problems issuing the statements
  */
 @Override
 public void execute(final SqlStatement[] statements, final List<SqlVisitor> sqlVisitors)
     throws LiquibaseException {
   for (SqlStatement statement : statements) {
     if (statement.skipOnUnsupported()
         && !SqlGeneratorFactory.getInstance().supports(statement, this)) {
       continue;
     }
     LogFactory.getLogger().debug("Executing Statement: " + statement);
     ExecutorService.getInstance().getExecutor(this).execute(statement, sqlVisitors);
   }
 }
Пример #12
0
  /**
   * Overwrite this method to get the default schema name for the connection.
   *
   * @return
   */
  protected String getConnectionSchemaName() {
    if (connection == null || connection instanceof OfflineConnection) {
      return null;
    }
    try {
      return ExecutorService.getInstance()
          .getExecutor(this)
          .queryForObject(new RawCallStatement("call current_schema"), String.class);

    } catch (Exception e) {
      LogFactory.getLogger().info("Error getting default schema", e);
    }
    return null;
  }
Пример #13
0
  @Override
  public boolean acquireLock() throws LockException {
    if (hasChangeLogLock) {
      return true;
    }

    Executor executor = ExecutorService.getInstance().getExecutor(database);

    try {
      database.rollback();
      this.init();

      Boolean locked =
          (Boolean)
              ExecutorService.getInstance()
                  .getExecutor(database)
                  .queryForObject(
                      new SelectFromDatabaseChangeLogLockStatement("LOCKED"), Boolean.class);

      if (locked) {
        return false;
      } else {

        executor.comment("Lock Database");
        int rowsUpdated = executor.update(new LockDatabaseChangeLogStatement());
        if (rowsUpdated > 1) {
          throw new LockException("Did not update change log lock correctly");
        }
        if (rowsUpdated == 0) {
          // another node was faster
          return false;
        }
        database.commit();
        LogFactory.getLogger().info("Successfully acquired change log lock");

        hasChangeLogLock = true;

        database.setCanCacheLiquibaseTableInfo(true);
        return true;
      }
    } catch (Exception e) {
      throw new LockException(e);
    } finally {
      try {
        database.rollback();
      } catch (DatabaseException e) {;
      }
    }
  }
Пример #14
0
  /**
   * This method will check the database ChangeLogLock table used to keep track of if a machine is
   * updating the database. If the table does not exist it will create one otherwise it will not do
   * anything besides outputting a log message.
   */
  @Override
  public void checkDatabaseChangeLogLockTable() throws DatabaseException {

    Executor executor = ExecutorService.getInstance().getExecutor(this);
    if (!hasDatabaseChangeLogLockTable()) {

      executor.comment("Create Database Lock Table");
      executor.execute(new CreateDatabaseChangeLogLockTableStatement());
      this.commit();
      LogFactory.getLogger()
          .debug(
              "Created database lock table with name: "
                  + escapeTableName(getLiquibaseSchemaName(), getDatabaseChangeLogLockTableName()));
      this.hasDatabaseChangeLogLockTable = true;
    }
  }
Пример #15
0
 public void setUp(String contexts) {
   try {
     ResourceAccessor resourceAccessor = new FileSystemResourceAccessor();
     Class.forName("org.hsqldb.jdbcDriver");
     holdingConnection = getConnectionImpl();
     HsqlConnection hsconn = new HsqlConnection(holdingConnection);
     LogFactory.getLogger().setLogLevel("severe");
     liquibase = new Liquibase(CHANGE_LOG, resourceAccessor, hsconn);
     liquibase.dropAll();
     liquibase.update(contexts);
     hsconn.close();
   } catch (Exception e) {
     LOG.error("Error during database initialization", e);
     throw new RuntimeException("Error during database initialization", e);
   }
 }
Пример #16
0
 @Override
 protected String getConnectionCatalogName() throws DatabaseException {
   if (getConnection() instanceof OfflineConnection) {
     return getConnection().getCatalog();
   }
   try {
     return ExecutorService.getInstance()
         .getExecutor(this)
         .queryForObject(
             new RawCallStatement("select sys_context( 'userenv', 'current_schema' ) from dual"),
             String.class);
   } catch (Exception e) {
     LogFactory.getLogger().info("Error getting default schema", e);
   }
   return null;
 }
Пример #17
0
  @Override
  public String getDefaultCatalogName() {
    if (defaultCatalogName == null) {
      if (defaultSchemaName != null && !this.supportsSchemas()) {
        return defaultSchemaName;
      }

      if (connection != null) {
        try {
          defaultCatalogName = getConnectionCatalogName();
        } catch (DatabaseException e) {
          LogFactory.getLogger().info("Error getting default catalog", e);
        }
      }
    }
    return defaultCatalogName;
  }
Пример #18
0
  @Override
  public void close() throws DatabaseException {
    DatabaseConnection connection = getConnection();
    if (connection != null) {
      if (previousAutoCommit != null) {
        try {
          connection.setAutoCommit(previousAutoCommit);
        } catch (DatabaseException e) {
          LogFactory.getLogger()
              .warning("Failed to restore the auto commit to " + previousAutoCommit);

          throw e;
        }
      }
      connection.close();
    }
    ExecutorService.getInstance().clearExecutor(this);
  }
Пример #19
0
 static {
   try {
     Class<?> scanner = Class.forName("Liquibase.ServiceLocator.ClrServiceLocator, Liquibase");
     instance = (ServiceLocator) scanner.newInstance();
   } catch (Exception e) {
     try {
       if (OSGiUtil.isLiquibaseLoadedAsOSGiBundle()) {
         Bundle liquibaseBundle = FrameworkUtil.getBundle(ServiceLocator.class);
         instance =
             new ServiceLocator(
                 new OSGiPackageScanClassResolver(liquibaseBundle),
                 new OSGiResourceAccessor(liquibaseBundle));
       } else {
         instance = new ServiceLocator();
       }
     } catch (Throwable e1) {
       LogFactory.getInstance().getLog().severe("Cannot build ServiceLocator", e1);
     }
   }
 }
  private static class IOThread extends Thread {
    private Logger log = LogFactory.getInstance().getLog();
    private InputStream from;
    private OutputStream to;

    public IOThread(InputStream from, OutputStream to) {
      super();
      setDaemon(true);
      this.from = from;
      this.to = to;
    }

    @Override
    public void run() {
      try {
        StreamUtil.copy(from, to);
      } catch (IOException e) {
        log.debug("While copying streams", e);
      }
    }
  }
Пример #21
0
  @Override
  public boolean isCaseSensitive() {
    if (caseSensitive == null) {
      if (connection != null && connection instanceof JdbcConnection) {
        try {
          caseSensitive =
              ((JdbcConnection) connection)
                  .getUnderlyingConnection()
                  .getMetaData()
                  .supportsMixedCaseIdentifiers();
        } catch (SQLException e) {
          LogFactory.getLogger().warning("Cannot determine case sensitivity from JDBC driver", e);
        }
      }
    }

    if (caseSensitive == null) {
      return false;
    } else {
      return caseSensitive.booleanValue();
    }
  }
  public ValidationErrors validate(Database database) {
    ValidationErrors changeValidationErrors = new ValidationErrors();
    for (SqlStatement statement : generateStatements(database)) {
      boolean supported = SqlGeneratorFactory.getInstance().supports(statement, database);
      if (!supported) {
        if (statement.skipOnUnsupported()) {
          LogFactory.getLogger()
              .info(
                  getChangeMetaData().getName()
                      + " is not supported on "
                      + database.getTypeName()
                      + " but will continue");
        } else {
          changeValidationErrors.addError(
              getChangeMetaData().getName() + " is not supported on " + database.getTypeName());
        }
      } else {
        changeValidationErrors.addAll(
            SqlGeneratorFactory.getInstance().validate(statement, database));
      }
    }

    return changeValidationErrors;
  }
Пример #23
0
  protected DataType readDataType(
      CachedRow columnMetadataResultSet, Column column, Database database) throws SQLException {

    if (database instanceof OracleDatabase) {
      String dataType = columnMetadataResultSet.getString("DATA_TYPE");
      dataType = dataType.replace("VARCHAR2", "VARCHAR");
      dataType = dataType.replace("NVARCHAR2", "NVARCHAR");

      DataType type = new DataType(dataType);
      //            type.setDataTypeId(dataType);
      if (dataType.equalsIgnoreCase("NUMBER")) {
        type.setColumnSize(columnMetadataResultSet.getInt("DATA_PRECISION"));
        //                if (type.getColumnSize() == null) {
        //                    type.setColumnSize(38);
        //                }
        type.setDecimalDigits(columnMetadataResultSet.getInt("DATA_SCALE"));
        //                if (type.getDecimalDigits() == null) {
        //                    type.setDecimalDigits(0);
        //                }
        //            type.setRadix(10);
      } else {
        type.setColumnSize(columnMetadataResultSet.getInt("DATA_LENGTH"));

        if (dataType.equalsIgnoreCase("NCLOB")
            || dataType.equalsIgnoreCase("BLOB")
            || dataType.equalsIgnoreCase("CLOB")) {
          type.setColumnSize(null);
        } else if (dataType.equalsIgnoreCase("NVARCHAR") || dataType.equalsIgnoreCase("NCHAR")) {
          type.setColumnSize(columnMetadataResultSet.getInt("CHAR_LENGTH"));
          type.setColumnSizeUnit(DataType.ColumnSizeUnit.CHAR);
        } else {
          String charUsed = columnMetadataResultSet.getString("CHAR_USED");
          DataType.ColumnSizeUnit unit = null;
          if ("C".equals(charUsed)) {
            unit = DataType.ColumnSizeUnit.CHAR;
            type.setColumnSize(columnMetadataResultSet.getInt("CHAR_LENGTH"));
          }
          type.setColumnSizeUnit(unit);
        }
      }

      return type;
    }

    String columnTypeName = (String) columnMetadataResultSet.get("TYPE_NAME");

    if (database instanceof FirebirdDatabase) {
      if (columnTypeName.equals("BLOB SUB_TYPE 0")) {
        columnTypeName = "BLOB";
      }
      if (columnTypeName.equals("BLOB SUB_TYPE 1")) {
        columnTypeName = "CLOB";
      }
    }

    if (database instanceof MySQLDatabase
        && (columnTypeName.equalsIgnoreCase("ENUM") || columnTypeName.equalsIgnoreCase("SET"))) {
      try {
        String boilerLength;
        if (columnTypeName.equalsIgnoreCase("ENUM")) boilerLength = "7";
        else // SET
        boilerLength = "6";
        List<String> enumValues =
            ExecutorService.getInstance()
                .getExecutor(database)
                .queryForList(
                    new RawSqlStatement(
                        "SELECT DISTINCT SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING(COLUMN_TYPE, "
                            + boilerLength
                            + ", LENGTH(COLUMN_TYPE) - "
                            + boilerLength
                            + " - 1 ), \"','\", 1 + units.i + tens.i * 10) , \"','\", -1)\n"
                            + "FROM INFORMATION_SCHEMA.COLUMNS\n"
                            + "CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units\n"
                            + "CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) tens\n"
                            + "WHERE TABLE_NAME = '"
                            + column.getRelation().getName()
                            + "' \n"
                            + "AND COLUMN_NAME = '"
                            + column.getName()
                            + "'"),
                    String.class);
        String enumClause = "";
        for (String enumValue : enumValues) {
          enumClause += "'" + enumValue + "', ";
        }
        enumClause = enumClause.replaceFirst(", $", "");
        return new DataType(columnTypeName + "(" + enumClause + ")");
      } catch (DatabaseException e) {
        LogFactory.getLogger().warning("Error fetching enum values", e);
      }
    }
    DataType.ColumnSizeUnit columnSizeUnit = DataType.ColumnSizeUnit.BYTE;

    int dataType = columnMetadataResultSet.getInt("DATA_TYPE");
    Integer columnSize = null;
    Integer decimalDigits = null;
    if (!database.dataTypeIsNotModifiable(
        columnTypeName)) { // don't set size for types like int4, int8 etc
      columnSize = columnMetadataResultSet.getInt("COLUMN_SIZE");
      decimalDigits = columnMetadataResultSet.getInt("DECIMAL_DIGITS");
      if (decimalDigits != null && decimalDigits.equals(0)) {
        decimalDigits = null;
      }
    }

    Integer radix = columnMetadataResultSet.getInt("NUM_PREC_RADIX");

    Integer characterOctetLength = columnMetadataResultSet.getInt("CHAR_OCTET_LENGTH");

    if (database instanceof DB2Database) {
      String typeName = columnMetadataResultSet.getString("TYPE_NAME");
      if (typeName.equalsIgnoreCase("DBCLOB")
          || typeName.equalsIgnoreCase("GRAPHIC")
          || typeName.equalsIgnoreCase("VARGRAPHIC")) {
        if (columnSize != null) {
          columnSize = columnSize / 2; // Stored as double length chars
        }
      }
    }

    DataType type = new DataType(columnTypeName);
    type.setDataTypeId(dataType);
    type.setColumnSize(columnSize);
    type.setDecimalDigits(decimalDigits);
    type.setRadix(radix);
    type.setCharacterOctetLength(characterOctetLength);
    type.setColumnSizeUnit(columnSizeUnit);

    return type;
  }
/** Statement to run {@code pt-online-schema-change} in order to alter a database table. */
public class PTOnlineSchemaChangeStatement extends RuntimeStatement {
  public static final String COMMAND = "pt-online-schema-change";
  private static String perconaToolkitVersion = null;
  static Boolean available = null;

  private static Logger log = LogFactory.getInstance().getLog();

  private String tableName;
  private String alterStatement;

  public PTOnlineSchemaChangeStatement(String tableName, String alterStatement) {
    this.tableName = tableName;
    this.alterStatement = alterStatement;
  }

  /**
   * Tokenizes the given options into separate arguments, so that it can be fed into the {@link
   * ProcessBuilder}'s commands.
   *
   * @param options the options as one single string
   * @return the list of arguments
   */
  private List<String> tokenize(String options) {
    StringTokenizer stringTokenizer = new StringTokenizer(options);
    List<String> result = new LinkedList<String>();
    while (stringTokenizer.hasMoreTokens()) {
      result.add(stringTokenizer.nextToken());
    }
    return joinQuotedArguments(result);
  }

  /**
   * Very simplistic approach to join together any quoted arguments. Only double quotes are
   * supported and the join character is a space.
   *
   * @param tokenizedArguments the arguments tokenized by space
   * @return the filtered arguments, maybe joined
   */
  private List<String> joinQuotedArguments(List<String> tokenizedArguments) {
    final String joinCharacters = " ";
    List<String> filtered = new LinkedList<String>();
    boolean inQuotes = false;
    for (int i = 0; i < tokenizedArguments.size(); i++) {
      String arg = tokenizedArguments.get(i);
      if (!inQuotes) {
        if (arg.startsWith("\"")) {
          inQuotes = true;
          arg = arg.substring(1);
        }
        if (arg.endsWith("\"")) {
          inQuotes = false;
          arg = arg.substring(0, arg.length() - 1);
        }
        filtered.add(arg);
      } else {
        if (arg.endsWith("\"")) {
          inQuotes = false;
          arg = arg.substring(0, arg.length() - 1);
        }
        String last = filtered.get(filtered.size() - 1);
        filtered.set(filtered.size() - 1, last + joinCharacters + arg);
      }
    }
    return filtered;
  }

  /**
   * Builds the command line arguments that will be executed.
   *
   * @param database the database - needed to get the connection info.
   * @return the command line arguments including {@link #COMMAND}
   */
  List<String> buildCommand(Database database) {
    List<String> commands = new ArrayList<String>();
    commands.add(PTOnlineSchemaChangeStatement.COMMAND);

    if (!Configuration.getAdditionalOptions().isEmpty()) {
      commands.addAll(tokenize(Configuration.getAdditionalOptions()));
    }

    commands.add("--alter=" + alterStatement);
    commands.add("--alter-foreign-keys-method=auto");

    if (database.getConnection() != null) {
      DatabaseConnectionUtil connection = new DatabaseConnectionUtil(database.getConnection());
      commands.add("--host=" + connection.getHost());
      commands.add("--port=" + connection.getPort());
      commands.add("--user="******"--password="******"--execute");
    commands.add("D=" + database.getLiquibaseSchemaName() + ",t=" + tableName);
    return commands;
  }

  /**
   * Generates the command line that would be executed and return it as a single string. The
   * password will be masked.
   *
   * @param database the database - needed to get the connection info
   * @return the string
   */
  public String printCommand(Database database) {
    List<String> command = buildCommand(database);
    return filterCommands(command);
  }

  /**
   * Converts the given command list into a single string and mask the password
   *
   * @param command the command line arguments that would be used for pt-online-schema-change
   * @return the string with masked password
   */
  private String filterCommands(List<String> command) {
    StringBuilder sb = new StringBuilder();
    for (String s : command) {
      sb.append(" ");
      if (s.startsWith("--password")) {
        sb.append("--password=***");
      } else if (s.contains(" ")) {
        sb.append(s.substring(0, s.indexOf('=') + 1))
            .append("\"")
            .append(s.substring(s.indexOf('=') + 1))
            .append("\"");
      } else {
        sb.append(s);
      }
    }
    return sb.substring(1).toString();
  }

  /**
   * Actually executes pt-online-schema change. Does not generate any Sql.
   *
   * @return always <code>null</code>
   */
  @Override
  public Sql[] generate(Database database) {
    List<String> cmndline = buildCommand(database);
    log.info("Executing: " + filterCommands(cmndline));

    ProcessBuilder pb = new ProcessBuilder(cmndline);
    pb.redirectErrorStream(true);
    Process p = null;
    final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    final OutputStream tee =
        new FilterOutputStream(outputStream) {
          @Override
          public void write(int b) throws IOException {
            if (b == '\n') {
              log.info(outputStream.toString(Charset.defaultCharset().toString()));
              outputStream.reset();
            } else {
              super.write(b);
            }
          }
        };
    try {
      p = pb.start();
      final InputStream in = p.getInputStream();
      final InputStream err = p.getErrorStream();

      IOThread reader = new IOThread(in, tee);
      IOThread reader2 = new IOThread(err, tee);
      reader.start();
      reader2.start();

      int exitCode = p.waitFor();
      reader.join(5000);
      reader2.join(5000);
      // log the remaining output
      log.info(outputStream.toString(Charset.defaultCharset().toString()));

      if (exitCode != 0) {
        throw new RuntimeException("Percona exited with " + exitCode);
      }
    } catch (IOException e) {
      throw new UnexpectedLiquibaseException(e);
    } catch (InterruptedException e) {
      throw new UnexpectedLiquibaseException(e);
    } finally {
      if (p != null) {
        StreamUtil.closeQuietly(p.getErrorStream());
        StreamUtil.closeQuietly(p.getInputStream());
        StreamUtil.closeQuietly(p.getOutputStream());
        p.destroy();
      }
      StreamUtil.closeQuietly(outputStream);
    }
    return null;
  }

  @Override
  public String toString() {
    return PTOnlineSchemaChangeStatement.class.getSimpleName()
        + "[table: "
        + tableName
        + ", alterStatement: "
        + alterStatement
        + "]";
  }

  private static class IOThread extends Thread {
    private Logger log = LogFactory.getInstance().getLog();
    private InputStream from;
    private OutputStream to;

    public IOThread(InputStream from, OutputStream to) {
      super();
      setDaemon(true);
      this.from = from;
      this.to = to;
    }

    @Override
    public void run() {
      try {
        StreamUtil.copy(from, to);
      } catch (IOException e) {
        log.debug("While copying streams", e);
      }
    }
  }

  public static String getVersion() {
    checkIsAvailableAndGetVersion();
    return perconaToolkitVersion;
  }

  /**
   * Checks whether the command is available and can be started.
   *
   * <p><em>Implementation detail:</em> This is lazily detected once and then cached.
   *
   * @return <code>true</code> if it is available and executable, <code>false</code> otherwise
   * @see #COMMAND
   */
  public static synchronized boolean isAvailable() {
    if (available != null) {
      return available.booleanValue();
    }
    checkIsAvailableAndGetVersion();
    return available.booleanValue();
  }

  private static void checkIsAvailableAndGetVersion() {
    ProcessBuilder pb = new ProcessBuilder(COMMAND, "--version");
    pb.redirectErrorStream(true);
    Process p = null;
    try {
      p = pb.start();
      p.waitFor();

      perconaToolkitVersion = StreamUtil.getStreamContents(p.getInputStream());
      if (perconaToolkitVersion != null) {
        perconaToolkitVersion = perconaToolkitVersion.replaceAll("\n|\r", "");
      }
      available = true;
      log.info("Using percona toolkit: " + perconaToolkitVersion);
    } catch (IOException e) {
      available = false;
    } catch (InterruptedException e) {
      available = false;
    } finally {
      if (p != null) {
        StreamUtil.closeQuietly(p.getErrorStream());
        StreamUtil.closeQuietly(p.getInputStream());
        StreamUtil.closeQuietly(p.getOutputStream());
        p.destroy();
      }
    }
  }
}
Пример #25
0
  protected Column readColumn(CachedRow columnMetadataResultSet, Relation table, Database database)
      throws SQLException, DatabaseException {
    String rawTableName = (String) columnMetadataResultSet.get("TABLE_NAME");
    String rawColumnName = (String) columnMetadataResultSet.get("COLUMN_NAME");
    String rawSchemaName =
        StringUtils.trimToNull((String) columnMetadataResultSet.get("TABLE_SCHEM"));
    String rawCatalogName =
        StringUtils.trimToNull((String) columnMetadataResultSet.get("TABLE_CAT"));
    String remarks = StringUtils.trimToNull((String) columnMetadataResultSet.get("REMARKS"));
    if (remarks != null) {
      remarks = remarks.replace("''", "'"); // come back escaped sometimes
    }

    Column column = new Column();
    column.setName(StringUtils.trimToNull(rawColumnName));
    column.setRelation(table);
    column.setRemarks(remarks);

    if (database instanceof OracleDatabase) {
      String nullable = columnMetadataResultSet.getString("NULLABLE");
      if (nullable.equals("Y")) {
        column.setNullable(true);
      } else {
        column.setNullable(false);
      }
    } else {
      int nullable = columnMetadataResultSet.getInt("NULLABLE");
      if (nullable == DatabaseMetaData.columnNoNulls) {
        column.setNullable(false);
      } else if (nullable == DatabaseMetaData.columnNullable) {
        column.setNullable(true);
      } else if (nullable == DatabaseMetaData.columnNullableUnknown) {
        LogFactory.getLogger()
            .info("Unknown nullable state for column " + column.toString() + ". Assuming nullable");
        column.setNullable(true);
      }
    }

    if (database.supportsAutoIncrement()) {
      if (table instanceof Table) {
        if (columnMetadataResultSet.containsColumn("IS_AUTOINCREMENT")) {
          String isAutoincrement = (String) columnMetadataResultSet.get("IS_AUTOINCREMENT");
          isAutoincrement = StringUtils.trimToNull(isAutoincrement);
          if (isAutoincrement == null) {
            column.setAutoIncrementInformation(null);
          } else if (isAutoincrement.equals("YES")) {
            column.setAutoIncrementInformation(new Column.AutoIncrementInformation());
          } else if (isAutoincrement.equals("NO")) {
            column.setAutoIncrementInformation(null);
          } else if (isAutoincrement.equals("")) {
            LogFactory.getLogger()
                .info(
                    "Unknown auto increment state for column "
                        + column.toString()
                        + ". Assuming not auto increment");
            column.setAutoIncrementInformation(null);
          } else {
            throw new UnexpectedLiquibaseException(
                "Unknown is_autoincrement value: '" + isAutoincrement + "'");
          }
        } else {
          // probably older version of java, need to select from the column to find out if it is
          // auto-increment
          String selectStatement;
          if (database.getDatabaseProductName().startsWith("DB2 UDB for AS/400")) {
            selectStatement =
                "select "
                    + database.escapeColumnName(
                        rawCatalogName, rawSchemaName, rawTableName, rawColumnName)
                    + " from "
                    + rawSchemaName
                    + "."
                    + rawTableName
                    + " where 0=1";
            LogFactory.getLogger().debug("rawCatalogName : <" + rawCatalogName + ">");
            LogFactory.getLogger().debug("rawSchemaName : <" + rawSchemaName + ">");
            LogFactory.getLogger().debug("rawTableName : <" + rawTableName + ">");
            LogFactory.getLogger().debug("raw selectStatement : <" + selectStatement + ">");

          } else {
            selectStatement =
                "select "
                    + database.escapeColumnName(
                        rawCatalogName, rawSchemaName, rawTableName, rawColumnName)
                    + " from "
                    + database.escapeTableName(rawCatalogName, rawSchemaName, rawTableName)
                    + " where 0=1";
          }
          LogFactory.getLogger()
              .debug(
                  "Checking "
                      + rawTableName
                      + "."
                      + rawCatalogName
                      + " for auto-increment with SQL: '"
                      + selectStatement
                      + "'");
          Connection underlyingConnection =
              ((JdbcConnection) database.getConnection()).getUnderlyingConnection();
          Statement statement = null;
          ResultSet columnSelectRS = null;

          try {
            statement = underlyingConnection.createStatement();
            columnSelectRS = statement.executeQuery(selectStatement);
            if (columnSelectRS.getMetaData().isAutoIncrement(1)) {
              column.setAutoIncrementInformation(new Column.AutoIncrementInformation());
            } else {
              column.setAutoIncrementInformation(null);
            }
          } finally {
            try {
              if (statement != null) {
                statement.close();
              }
            } catch (SQLException ignore) {
            }
            if (columnSelectRS != null) {
              columnSelectRS.close();
            }
          }
        }
      }
    }

    DataType type = readDataType(columnMetadataResultSet, column, database);
    column.setType(type);

    column.setDefaultValue(readDefaultValue(columnMetadataResultSet, column, database));

    return column;
  }
  @Override
  public Sql[] generateSql(
      CreateTempTableStatementVertica statement,
      Database database,
      SqlGeneratorChain sqlGeneratorChain) {
    StringBuilder sql = new StringBuilder();

    sql.append("CREATE ");
    if (statement.getIsGlobal() != null) sql.append(" GLOBAL ");
    else sql.append(" LOCAL ");
    sql.append(" TEMPORARY TABLE ");
    if (statement.getSchemaName() != null) sql.append(statement.getSchemaName()).append(".");
    else sql.append(database.getDefaultSchemaName()).append(".");
    if (statement.getTableName() != null) {
      sql.append(statement.getTableName()).append(" ");
    }

    boolean isSinglePrimaryKeyColumn =
        statement.getPrimaryKeyConstraint() != null
            && statement.getPrimaryKeyConstraint().getColumns().size() == 1;

    boolean isPrimaryKeyAutoIncrement = false;

    sql.append("( ");
    Iterator<String> columnIterator = statement.getColumns().iterator();
    List<String> primaryKeyColumns = new LinkedList<String>();
    while (columnIterator.hasNext()) {
      String column = columnIterator.next();
      DatabaseDataType columnType =
          statement.getColumnTypes().get(column).toDatabaseDataType(database);
      sql.append(
          database.escapeColumnName(
              statement.getCatalogName(),
              statement.getSchemaName(),
              statement.getTableName(),
              column));

      // This is the difference between vertica & other RDBMS - the encoding part.

      AutoIncrementConstraint autoIncrementConstraint = null;

      for (AutoIncrementConstraint currentAutoIncrementConstraint :
          statement.getAutoIncrementConstraints()) {
        if (column.equals(currentAutoIncrementConstraint.getColumnName())) {
          autoIncrementConstraint = currentAutoIncrementConstraint;
          break;
        }
      }

      boolean isAutoIncrementColumn = autoIncrementConstraint != null;
      boolean isPrimaryKeyColumn =
          statement.getPrimaryKeyConstraint() != null
              && statement.getPrimaryKeyConstraint().getColumns().contains(column);
      isPrimaryKeyAutoIncrement =
          isPrimaryKeyAutoIncrement || isPrimaryKeyColumn && isAutoIncrementColumn;

      if (isPrimaryKeyColumn) {
        primaryKeyColumns.add(column);
      }
      if (!isAutoIncrementColumn) {
        sql.append(" ").append(columnType);
      }

      // for the serial data type in postgres, there should be no default value
      if (!columnType.isAutoIncrement() && statement.getDefaultValue(column) != null) {
        Object defaultValue = statement.getDefaultValue(column);
        sql.append(" DEFAULT ");
        sql.append(statement.getColumnTypes().get(column).objectToSql(defaultValue, database));
      }

      // TODO: Change this - vertica supports both auto incremental & identity fields.
      if (isAutoIncrementColumn) {
        // TODO: check if database supports auto increment on non primary key column
        if (database.supportsAutoIncrement()) {
          String autoIncrementClause =
              database.getAutoIncrementClause(
                  autoIncrementConstraint.getStartWith(), autoIncrementConstraint.getIncrementBy());

          if (!"".equals(autoIncrementClause)) {
            sql.append(" ").append(autoIncrementClause);
          }

          if (database instanceof PostgresDatabase
              && autoIncrementConstraint.getStartWith() != null) {
            String sequenceName = statement.getTableName() + "_" + column + "_seq";
            //                        additionalSql.add(new UnparsedSql("alter sequence
            // "+database.escapeSequenceName(statement.getCatalogName(), statement.getSchemaName(),
            // sequenceName)+" start with "+autoIncrementConstraint.getStartWith(), new
            // Sequence().setName(sequenceName).setSchema(statement.getCatalogName(),
            // statement.getSchemaName())));
          }
        } else {
          LogFactory.getLogger()
              .warning(
                  database.getShortName()
                      + " does not support autoincrement columns as request for "
                      + (database.escapeTableName(
                          statement.getCatalogName(),
                          statement.getSchemaName(),
                          statement.getTableName())));
        }
      }

      if (isPrimaryKeyColumn) {

        String pkName =
            StringUtils.trimToNull(statement.getPrimaryKeyConstraint().getConstraintName());
        if (pkName != null) {
          sql.append(" CONSTRAINT ");
          sql.append(database.escapeConstraintName(pkName));
        }

        sql.append(" PRIMARY KEY ");
      }

      if (statement.getNotNullColumns().contains(column)) {
        sql.append(" NOT NULL");
      }

      if (statement.getColumnEncoding(column) != null) {
        sql.append(" ENCODING ").append(statement.getColumnEncoding(column));
      }

      if (statement.getColumnAccessrank(column) != null)
        sql.append(" ACCESSRANK  ").append(statement.getColumnAccessrank(column));

      if (columnIterator.hasNext()) {
        sql.append(", ");
      }
    }
    sql.append(" )");
    sql.append(" ON COMMIT ");
    if (statement.getIsPreserve()) sql.append(" PRESERVE ");
    else sql.append(" DELETE");
    sql.append("  ROWS ");

    if (statement.getOrderby() != null) sql.append(" ORDER BY ").append(statement.getOrderby());

    if (statement.getSegmentation() != null) {
      Segmentation seg = statement.getSegmentation();
      if (seg.getUnsegmented() == true) {
        sql.append(" UNSEGMENTED ");
      } else {
        sql.append(" SEGMENTED BY ");

        sql.append(seg.getExpression());
      }
      if (seg.getAllNodes()) {
        sql.append(" ALL NODES ");
      } else {
        sql.append(" NODES ").append(seg.getNodes());
        if (seg.getOffset() != null) sql.append(" OFFSET ").append(seg.getOffset().toString());
      }
    }

    if (statement.getKsafe() != null) sql.append(" KSAFE ").append(statement.getKsafe());

    if (statement.getPartitionby() != null)
      sql.append(" PARTITION BY ").append(statement.getPartitionby());

    System.out.println(sql.toString());
    return new Sql[] {new UnparsedSql(sql.toString())};
  }
Пример #27
0
  /**
   * This method will actually execute each of the changes in the list against the specified
   * database.
   *
   * @return should change set be marked as ran
   */
  public ExecType execute(
      DatabaseChangeLog databaseChangeLog, ChangeExecListener listener, Database database)
      throws MigrationFailedException {
    if (validationFailed) {
      return ExecType.MARK_RAN;
    }

    long startTime = new Date().getTime();

    ExecType execType = null;

    boolean skipChange = false;

    Executor executor = ExecutorService.getInstance().getExecutor(database);
    try {
      // set object quoting strategy
      database.setObjectQuotingStrategy(objectQuotingStrategy);

      // set auto-commit based on runInTransaction if database supports DDL in transactions
      if (database.supportsDDLInTransaction()) {
        database.setAutoCommit(!runInTransaction);
      }

      executor.comment("Changeset " + toString(false));
      if (StringUtils.trimToNull(getComments()) != null) {
        String comments = getComments();
        String[] lines = comments.split("\\n");
        for (int i = 0; i < lines.length; i++) {
          if (i > 0) {
            lines[i] = database.getLineComment() + " " + lines[i];
          }
        }
        executor.comment(StringUtils.join(Arrays.asList(lines), "\n"));
      }

      try {
        if (preconditions != null) {
          preconditions.check(database, databaseChangeLog, this);
        }
      } catch (PreconditionFailedException e) {
        if (listener != null) {
          listener.preconditionFailed(e, preconditions.getOnFail());
        }
        StringBuffer message = new StringBuffer();
        message.append(StreamUtil.getLineSeparator());
        for (FailedPrecondition invalid : e.getFailedPreconditions()) {
          message.append("          ").append(invalid.toString());
          message.append(StreamUtil.getLineSeparator());
        }

        if (preconditions.getOnFail().equals(PreconditionContainer.FailOption.HALT)) {
          throw new MigrationFailedException(this, message.toString(), e);
        } else if (preconditions.getOnFail().equals(PreconditionContainer.FailOption.CONTINUE)) {
          skipChange = true;
          execType = ExecType.SKIPPED;

          LogFactory.getLogger()
              .info(
                  "Continuing past: "
                      + toString()
                      + " despite precondition failure due to onFail='CONTINUE': "
                      + message);
        } else if (preconditions.getOnFail().equals(PreconditionContainer.FailOption.MARK_RAN)) {
          execType = ExecType.MARK_RAN;
          skipChange = true;

          log.info(
              "Marking ChangeSet: "
                  + toString()
                  + " ran despite precondition failure due to onFail='MARK_RAN': "
                  + message);
        } else if (preconditions.getOnFail().equals(PreconditionContainer.FailOption.WARN)) {
          execType = null; // already warned
        } else {
          throw new UnexpectedLiquibaseException(
              "Unexpected precondition onFail attribute: " + preconditions.getOnFail(), e);
        }
      } catch (PreconditionErrorException e) {
        if (listener != null) {
          listener.preconditionErrored(e, preconditions.getOnError());
        }

        StringBuffer message = new StringBuffer();
        message.append(StreamUtil.getLineSeparator());
        for (ErrorPrecondition invalid : e.getErrorPreconditions()) {
          message.append("          ").append(invalid.toString());
          message.append(StreamUtil.getLineSeparator());
        }

        if (preconditions.getOnError().equals(PreconditionContainer.ErrorOption.HALT)) {
          throw new MigrationFailedException(this, message.toString(), e);
        } else if (preconditions.getOnError().equals(PreconditionContainer.ErrorOption.CONTINUE)) {
          skipChange = true;
          execType = ExecType.SKIPPED;

        } else if (preconditions.getOnError().equals(PreconditionContainer.ErrorOption.MARK_RAN)) {
          execType = ExecType.MARK_RAN;
          skipChange = true;

          log.info(
              "Marking ChangeSet: " + toString() + " ran despite precondition error: " + message);
        } else if (preconditions.getOnError().equals(PreconditionContainer.ErrorOption.WARN)) {
          execType = null; // already logged
        } else {
          throw new UnexpectedLiquibaseException(
              "Unexpected precondition onError attribute: " + preconditions.getOnError(), e);
        }

        database.rollback();
      } finally {
        database.rollback();
      }

      if (!skipChange) {
        for (Change change : changes) {
          try {
            change.finishInitialization();
          } catch (SetupException se) {
            throw new MigrationFailedException(this, se);
          }
        }

        log.debug("Reading ChangeSet: " + toString());
        for (Change change : getChanges()) {
          if ((!(change instanceof DbmsTargetedChange))
              || DatabaseList.definitionMatches(
                  ((DbmsTargetedChange) change).getDbms(), database, true)) {
            if (listener != null) {
              listener.willRun(change, this, changeLog, database);
            }
            if (change.generateStatementsVolatile(database)) {
              executor.comment(
                  "WARNING The following SQL is possibly incorrect, invalid, and/or may change on each run:");
            }

            database.executeStatements(change, databaseChangeLog, sqlVisitors);
            log.info(change.getConfirmationMessage());
            if (listener != null) {
              listener.ran(change, this, changeLog, database);
            }
          } else {
            log.debug(
                "Change "
                    + change.getSerializedObjectName()
                    + " not included for database "
                    + database.getShortName());
          }
        }

        if (runInTransaction) {
          database.commit();
        }
        log.info(
            "ChangeSet "
                + toString(false)
                + " ran successfully in "
                + (new Date().getTime() - startTime + "ms"));
        if (execType == null) {
          execType = ExecType.EXECUTED;
        }
      } else {
        log.debug("Skipping ChangeSet: " + toString());
      }

    } catch (Exception e) {
      try {
        database.rollback();
      } catch (Exception e1) {
        throw new MigrationFailedException(this, e);
      }
      if (getFailOnError() != null && !getFailOnError()) {
        log.info(
            "Change set "
                + toString(false)
                + " failed, but failOnError was false.  Error: "
                + e.getMessage());
        log.debug("Failure Stacktrace", e);
        execType = ExecType.FAILED;
      } else {
        // just log the message, dont log the stacktrace by appending exception. Its logged anyway
        // to stdout
        log.severe("Change Set " + toString(false) + " failed.  Error: " + e.getMessage());
        if (e instanceof MigrationFailedException) {
          throw ((MigrationFailedException) e);
        } else {
          throw new MigrationFailedException(this, e);
        }
      }
    } finally {
      // restore auto-commit to false if this ChangeSet was not run in a transaction,
      // but only if the database supports DDL in transactions
      if (!runInTransaction && database.supportsDDLInTransaction()) {
        try {
          database.setAutoCommit(false);
        } catch (DatabaseException e) {
          throw new MigrationFailedException(this, "Could not resetInternalState autocommit", e);
        }
      }
    }
    return execType;
  }
Пример #28
0
  /** Drops all objects owned by the connected user. */
  @Override
  public void dropDatabaseObjects(final CatalogAndSchema schemaToDrop) throws LiquibaseException {
    ObjectQuotingStrategy currentStrategy = this.getObjectQuotingStrategy();
    this.setObjectQuotingStrategy(ObjectQuotingStrategy.QUOTE_ALL_OBJECTS);
    try {
      DatabaseSnapshot snapshot;
      try {
        final SnapshotControl snapshotControl = new SnapshotControl(this);
        final Set<Class<? extends DatabaseObject>> typesToInclude =
            snapshotControl.getTypesToInclude();

        // We do not need to remove indexes and primary/unique keys explicitly. They should be
        // removed
        // as part of tables.
        typesToInclude.remove(Index.class);
        typesToInclude.remove(PrimaryKey.class);
        typesToInclude.remove(UniqueConstraint.class);

        if (supportsForeignKeyDisable()) {
          // We do not remove ForeignKey because they will be disabled and removed as parts of
          // tables.
          typesToInclude.remove(ForeignKey.class);
        }

        final long createSnapshotStarted = System.currentTimeMillis();
        snapshot =
            SnapshotGeneratorFactory.getInstance()
                .createSnapshot(schemaToDrop, this, snapshotControl);
        LogFactory.getLogger()
            .debug(
                String.format(
                    "Database snapshot generated in %d ms. Snapshot includes: %s",
                    System.currentTimeMillis() - createSnapshotStarted, typesToInclude));
      } catch (LiquibaseException e) {
        throw new UnexpectedLiquibaseException(e);
      }

      final long changeSetStarted = System.currentTimeMillis();
      DiffResult diffResult =
          DiffGeneratorFactory.getInstance()
              .compare(
                  new EmptyDatabaseSnapshot(this),
                  snapshot,
                  new CompareControl(snapshot.getSnapshotControl().getTypesToInclude()));
      List<ChangeSet> changeSets =
          new DiffToChangeLog(
                  diffResult,
                  new DiffOutputControl(true, true, false).addIncludedSchema(schemaToDrop))
              .generateChangeSets();
      LogFactory.getLogger()
          .debug(
              String.format(
                  "ChangeSet to Remove Database Objects generated in %d ms.",
                  System.currentTimeMillis() - changeSetStarted));

      boolean previousAutoCommit = this.getAutoCommitMode();
      this.commit(); // clear out currently executed statements
      this.setAutoCommit(false); // some DDL doesn't work in autocommit mode
      final boolean reEnableFK = supportsForeignKeyDisable() && disableForeignKeyChecks();
      try {
        for (ChangeSet changeSet : changeSets) {
          changeSet.setFailOnError(false);
          for (Change change : changeSet.getChanges()) {
            if (change instanceof DropTableChange) {
              ((DropTableChange) change).setCascadeConstraints(true);
            }
            SqlStatement[] sqlStatements = change.generateStatements(this);
            for (SqlStatement statement : sqlStatements) {
              ExecutorService.getInstance().getExecutor(this).execute(statement);
            }
          }
          this.commit();
        }
      } finally {
        if (reEnableFK) {
          enableForeignKeyChecks();
        }
      }

      ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(this).destroy();
      LockServiceFactory.getInstance().getLockService(this).destroy();

      this.setAutoCommit(previousAutoCommit);

    } finally {
      this.setObjectQuotingStrategy(currentStrategy);
      this.commit();
    }
  }
Пример #29
0
  @Override
  public void checkDatabaseChangeLogTable(
      final boolean updateExistingNullChecksums,
      final DatabaseChangeLog databaseChangeLog,
      final Contexts contexts)
      throws DatabaseException {
    if (updateExistingNullChecksums && databaseChangeLog == null) {
      throw new DatabaseException("changeLog parameter is required if updating existing checksums");
    }

    Executor executor = ExecutorService.getInstance().getExecutor(this);

    Table changeLogTable =
        SnapshotGeneratorFactory.getInstance()
            .getDatabaseChangeLogTable(new SnapshotControl(this, Table.class, Column.class), this);

    List<SqlStatement> statementsToExecute = new ArrayList<SqlStatement>();

    boolean changeLogCreateAttempted = false;
    if (changeLogTable != null) {
      boolean hasDescription = changeLogTable.getColumn("DESCRIPTION") != null;
      boolean hasComments = changeLogTable.getColumn("COMMENTS") != null;
      boolean hasTag = changeLogTable.getColumn("TAG") != null;
      boolean hasLiquibase = changeLogTable.getColumn("LIQUIBASE") != null;
      boolean liquibaseColumnNotRightSize = false;
      if (!getConnection().getDatabaseProductName().equals("SQLite")) {
        liquibaseColumnNotRightSize =
            changeLogTable.getColumn("LIQUIBASE").getType().getColumnSize() != 20;
      }
      boolean hasOrderExecuted = changeLogTable.getColumn("ORDEREXECUTED") != null;
      boolean checksumNotRightSize = false;
      boolean hasExecTypeColumn = changeLogTable.getColumn("EXECTYPE") != null;

      if (!hasDescription) {
        executor.comment("Adding missing databasechangelog.description column");
        statementsToExecute.add(
            new AddColumnStatement(
                getLiquibaseCatalogName(),
                getLiquibaseSchemaName(),
                getDatabaseChangeLogTableName(),
                "DESCRIPTION",
                "VARCHAR(255)",
                null));
      }
      if (!hasTag) {
        executor.comment("Adding missing databasechangelog.tag column");
        statementsToExecute.add(
            new AddColumnStatement(
                getLiquibaseCatalogName(),
                getLiquibaseSchemaName(),
                getDatabaseChangeLogTableName(),
                "TAG",
                "VARCHAR(255)",
                null));
      }
      if (!hasComments) {
        executor.comment("Adding missing databasechangelog.comments column");
        statementsToExecute.add(
            new AddColumnStatement(
                getLiquibaseCatalogName(),
                getLiquibaseSchemaName(),
                getDatabaseChangeLogTableName(),
                "COMMENTS",
                "VARCHAR(255)",
                null));
      }
      if (!hasLiquibase) {
        executor.comment("Adding missing databasechangelog.liquibase column");
        statementsToExecute.add(
            new AddColumnStatement(
                getLiquibaseCatalogName(),
                getLiquibaseSchemaName(),
                getDatabaseChangeLogTableName(),
                "LIQUIBASE",
                "VARCHAR(255)",
                null));
      }
      if (!hasOrderExecuted) {
        executor.comment("Adding missing databasechangelog.orderexecuted column");
        statementsToExecute.add(
            new AddColumnStatement(
                getLiquibaseCatalogName(),
                getLiquibaseSchemaName(),
                getDatabaseChangeLogTableName(),
                "ORDEREXECUTED",
                "INT",
                null));
        statementsToExecute.add(
            new UpdateStatement(
                    getLiquibaseCatalogName(),
                    getLiquibaseSchemaName(),
                    getDatabaseChangeLogTableName())
                .addNewColumnValue("ORDEREXECUTED", -1));
        statementsToExecute.add(
            new SetNullableStatement(
                getLiquibaseCatalogName(),
                getLiquibaseSchemaName(),
                getDatabaseChangeLogTableName(),
                "ORDEREXECUTED",
                "INT",
                false));
      }
      if (checksumNotRightSize) {
        executor.comment("Modifying size of databasechangelog.md5sum column");

        statementsToExecute.add(
            new ModifyDataTypeStatement(
                getLiquibaseCatalogName(),
                getLiquibaseSchemaName(),
                getDatabaseChangeLogTableName(),
                "MD5SUM",
                "VARCHAR(35)"));
      }
      if (liquibaseColumnNotRightSize) {
        executor.comment("Modifying size of databasechangelog.liquibase column");

        statementsToExecute.add(
            new ModifyDataTypeStatement(
                getLiquibaseCatalogName(),
                getLiquibaseSchemaName(),
                getDatabaseChangeLogTableName(),
                "LIQUIBASE",
                "VARCHAR(20)"));
      }
      if (!hasExecTypeColumn) {
        executor.comment("Adding missing databasechangelog.exectype column");
        statementsToExecute.add(
            new AddColumnStatement(
                getLiquibaseCatalogName(),
                getLiquibaseSchemaName(),
                getDatabaseChangeLogTableName(),
                "EXECTYPE",
                "VARCHAR(10)",
                null));
        statementsToExecute.add(
            new UpdateStatement(
                    getLiquibaseCatalogName(),
                    getLiquibaseSchemaName(),
                    getDatabaseChangeLogTableName())
                .addNewColumnValue("EXECTYPE", "EXECUTED"));
        statementsToExecute.add(
            new SetNullableStatement(
                getLiquibaseCatalogName(),
                getLiquibaseSchemaName(),
                getDatabaseChangeLogTableName(),
                "EXECTYPE",
                "VARCHAR(10)",
                false));
      }

      List<Map> md5sumRS =
          ExecutorService.getInstance()
              .getExecutor(this)
              .queryForList(
                  new SelectFromDatabaseChangeLogStatement(
                      new SelectFromDatabaseChangeLogStatement.ByNotNullCheckSum(), "MD5SUM"));
      if (md5sumRS.size() > 0) {
        String md5sum = md5sumRS.get(0).get("MD5SUM").toString();
        if (!md5sum.startsWith(CheckSum.getCurrentVersion() + ":")) {
          executor.comment(
              "DatabaseChangeLog checksums are an incompatible version.  Setting them to null so they will be updated on next database update");
          statementsToExecute.add(
              new RawSqlStatement(
                  "UPDATE "
                      + escapeTableName(
                          getLiquibaseCatalogName(),
                          getLiquibaseSchemaName(),
                          getDatabaseChangeLogTableName())
                      + " SET MD5SUM=null"));
        }
      }

    } else if (!changeLogCreateAttempted) {
      executor.comment("Create Database Change Log Table");
      SqlStatement createTableStatement = new CreateDatabaseChangeLogTableStatement();
      if (!canCreateChangeLogTable()) {
        throw new DatabaseException(
            "Cannot create "
                + escapeTableName(
                    getLiquibaseCatalogName(),
                    getLiquibaseSchemaName(),
                    getDatabaseChangeLogTableName())
                + " table for your database.\n\n"
                + "Please construct it manually using the following SQL as a base and re-run Liquibase:\n\n"
                + createTableStatement);
      }
      // If there is no table in the database for recording change history create one.
      statementsToExecute.add(createTableStatement);
      LogFactory.getLogger()
          .info(
              "Creating database history table with name: "
                  + escapeTableName(
                      getLiquibaseCatalogName(),
                      getLiquibaseSchemaName(),
                      getDatabaseChangeLogTableName()));
      //                }
    }

    for (SqlStatement sql : statementsToExecute) {
      executor.execute(sql);
      this.commit();
    }

    if (updateExistingNullChecksums) {
      for (RanChangeSet ranChangeSet : this.getRanChangeSetList()) {
        if (ranChangeSet.getLastCheckSum() == null) {
          ChangeSet changeSet = databaseChangeLog.getChangeSet(ranChangeSet);
          if (changeSet != null
              && new ContextChangeSetFilter(contexts).accepts(changeSet)
              && new DbmsChangeSetFilter(this).accepts(changeSet)) {
            LogFactory.getLogger()
                .info(
                    "Updating null or out of date checksum on changeSet "
                        + changeSet
                        + " to correct value");
            executor.execute(new UpdateChangeSetChecksumStatement(changeSet));
          }
        }
      }
      commit();
      resetRanChangeSetList();
    }
  }
Пример #30
0
  public static Object parseValue(Database database, Object val, DataType type) {
    if (!(val instanceof String)) {
      return val;
    }

    int typeId = Integer.MIN_VALUE;
    if (type.getDataTypeId() != null) {
      typeId = type.getDataTypeId();
    }
    String typeName = type.getTypeName();

    LiquibaseDataType liquibaseDataType = DataTypeFactory.getInstance().from(type, database);

    String stringVal = (String) val;
    if (stringVal.isEmpty()) {
      if (liquibaseDataType instanceof CharType) {
        return "";
      } else {
        return null;
      }
    }

    if (database instanceof OracleDatabase
        && !stringVal.startsWith("'")
        && !stringVal.endsWith("'")) {
      // oracle returns functions without quotes
      Object maybeDate = null;

      if (liquibaseDataType instanceof DateType || typeId == Types.DATE) {
        if (stringVal.endsWith("'HH24:MI:SS')")) {
          maybeDate =
              DataTypeFactory.getInstance()
                  .fromDescription("time", database)
                  .sqlToObject(stringVal, database);
        } else {
          maybeDate =
              DataTypeFactory.getInstance()
                  .fromDescription("date", database)
                  .sqlToObject(stringVal, database);
        }
      } else if (liquibaseDataType instanceof DateTimeType || typeId == Types.TIMESTAMP) {
        maybeDate =
            DataTypeFactory.getInstance()
                .fromDescription("datetime", database)
                .sqlToObject(stringVal, database);
      } else {
        return new DatabaseFunction(stringVal);
      }
      if (maybeDate != null) {
        if (maybeDate instanceof java.util.Date) {
          return maybeDate;
        } else {
          return new DatabaseFunction(stringVal);
        }
      }
    }

    if (stringVal.startsWith("'") && stringVal.endsWith("'")) {
      stringVal = stringVal.substring(1, stringVal.length() - 1);
    } else if (stringVal.startsWith("((") && stringVal.endsWith("))")) {
      stringVal = stringVal.substring(2, stringVal.length() - 2);
    } else if (stringVal.startsWith("('") && stringVal.endsWith("')")) {
      stringVal = stringVal.substring(2, stringVal.length() - 2);
    } else if (stringVal.startsWith("(") && stringVal.endsWith(")")) {
      return new DatabaseFunction(stringVal.substring(1, stringVal.length() - 1));
    }

    Scanner scanner = new Scanner(stringVal.trim());
    if (typeId == Types.ARRAY) {
      return new DatabaseFunction(stringVal);
    } else if ((liquibaseDataType instanceof BigIntType || typeId == Types.BIGINT)) {
      if (scanner.hasNextBigInteger()) {
        return scanner.nextBigInteger();
      } else {
        return new DatabaseFunction(stringVal);
      }
    } else if (typeId == Types.BINARY) {
      return new DatabaseFunction(stringVal.trim());
    } else if (typeId == Types.BIT) {
      if (stringVal.startsWith("b'")) { // mysql returns boolean values as b'0' and b'1'
        stringVal = stringVal.replaceFirst("b'", "").replaceFirst("'$", "");
      }
      stringVal = stringVal.trim();
      if (scanner.hasNextBoolean()) {
        return scanner.nextBoolean();
      } else {
        return new Integer(stringVal);
      }
    } else if (liquibaseDataType instanceof BlobType || typeId == Types.BLOB) {
      return new DatabaseFunction(stringVal);
    } else if ((liquibaseDataType instanceof BooleanType || typeId == Types.BOOLEAN)) {
      if (scanner.hasNextBoolean()) {
        return scanner.nextBoolean();
      } else {
        return new DatabaseFunction(stringVal);
      }
    } else if (liquibaseDataType instanceof CharType || typeId == Types.CHAR) {
      return stringVal;
    } else if (liquibaseDataType instanceof ClobType || typeId == Types.CLOB) {
      return stringVal;
    } else if (typeId == Types.DATALINK) {
      return new DatabaseFunction(stringVal);
    } else if (liquibaseDataType instanceof DateType || typeId == Types.DATE) {
      if (typeName.equalsIgnoreCase("year")) {
        return stringVal.trim();
      }
      return DataTypeFactory.getInstance()
          .fromDescription("date", database)
          .sqlToObject(stringVal, database);
    } else if ((liquibaseDataType instanceof DecimalType || typeId == Types.DECIMAL)) {
      if (scanner.hasNextBigDecimal()) {
        return scanner.nextBigDecimal();
      } else {
        return new DatabaseFunction(stringVal);
      }
    } else if (typeId == Types.DISTINCT) {
      return new DatabaseFunction(stringVal);
    } else if ((liquibaseDataType instanceof DoubleType || typeId == Types.DOUBLE)) {
      if (scanner.hasNextDouble()) {
        return scanner.nextDouble();
      } else {
        return new DatabaseFunction(stringVal);
      }
    } else if ((liquibaseDataType instanceof FloatType || typeId == Types.FLOAT)) {
      if (scanner.hasNextFloat()) {
        return scanner.nextFloat();
      } else {
        return new DatabaseFunction(stringVal);
      }
    } else if ((liquibaseDataType instanceof IntType || typeId == Types.INTEGER)) {
      if (scanner.hasNextInt()) {
        return scanner.nextInt();
      } else {
        return new DatabaseFunction(stringVal);
      }
    } else if (typeId == Types.JAVA_OBJECT) {
      return new DatabaseFunction(stringVal);
    } else if (typeId == Types.LONGNVARCHAR) {
      return stringVal;
    } else if (typeId == Types.LONGVARBINARY) {
      return new DatabaseFunction(stringVal);
    } else if (typeId == Types.LONGVARCHAR) {
      return stringVal;
    } else if (liquibaseDataType instanceof NCharType || typeId == Types.NCHAR) {
      return stringVal;
    } else if (typeId == Types.NCLOB) {
      return stringVal;
    } else if (typeId == Types.NULL) {
      return null;
    } else if ((liquibaseDataType instanceof NumberType || typeId == Types.NUMERIC)) {
      if (scanner.hasNextBigDecimal()) {
        return scanner.nextBigDecimal();
      } else {
        return new DatabaseFunction(stringVal);
      }
    } else if (liquibaseDataType instanceof NVarcharType || typeId == Types.NVARCHAR) {
      return stringVal;
    } else if (typeId == Types.OTHER) {
      if (database instanceof DB2Database && typeName.equalsIgnoreCase("DECFLOAT")) {
        return new BigDecimal(stringVal);
      }
      return new DatabaseFunction(stringVal);
    } else if (typeId == Types.REAL) {
      return new BigDecimal(stringVal.trim());
    } else if (typeId == Types.REF) {
      return new DatabaseFunction(stringVal);
    } else if (typeId == Types.ROWID) {
      return new DatabaseFunction(stringVal);
    } else if ((liquibaseDataType instanceof SmallIntType || typeId == Types.SMALLINT)) {
      if (scanner.hasNextInt()) {
        return scanner.nextInt();
      } else {
        return new DatabaseFunction(stringVal);
      }
    } else if (typeId == Types.SQLXML) {
      return new DatabaseFunction(stringVal);
    } else if (typeId == Types.STRUCT) {
      return new DatabaseFunction(stringVal);
    } else if (liquibaseDataType instanceof TimeType || typeId == Types.TIME) {
      return DataTypeFactory.getInstance()
          .fromDescription("time", database)
          .sqlToObject(stringVal, database);
    } else if (liquibaseDataType instanceof DateTimeType
        || liquibaseDataType instanceof TimestampType
        || typeId == Types.TIMESTAMP) {
      return DataTypeFactory.getInstance()
          .fromDescription("datetime", database)
          .sqlToObject(stringVal, database);
    } else if ((liquibaseDataType instanceof TinyIntType || typeId == Types.TINYINT)) {
      if (scanner.hasNextInt()) {
        return scanner.nextInt();
      } else {
        return new DatabaseFunction(stringVal);
      }
    } else if (typeId == Types.VARBINARY) {
      return new DatabaseFunction(stringVal);
    } else if (liquibaseDataType instanceof VarcharType || typeId == Types.VARCHAR) {
      return stringVal;
    } else if (database instanceof MySQLDatabase && typeName.toLowerCase().startsWith("enum")) {
      return stringVal;
    } else {
      LogFactory.getLogger()
          .info(
              "Unknown default value: value '"
                  + stringVal
                  + "' type "
                  + typeName
                  + " ("
                  + type
                  + "), assuming it is a function");
      return new DatabaseFunction(stringVal);
    }
  }