/**
   * Implementation delegates logic to the {@link
   * liquibase.sqlgenerator.SqlGenerator#warn(liquibase.statement.SqlStatement,
   * liquibase.database.Database, liquibase.sqlgenerator.SqlGeneratorChain)} method on the {@link
   * SqlStatement} objects returned by {@link #generateStatements }. If a generated statement is not
   * supported for the given database, no warning will be added since that is a validation error. If
   * no or null SqlStatements are returned by generateStatements then this method returns no
   * warnings.
   */
  @Override
  public Warnings warn(Database database) {
    Warnings warnings = new Warnings();
    if (generateStatementsVolatile(database)) {
      return warnings;
    }

    SqlStatement[] statements = generateStatements(database);
    if (statements == null) {
      return warnings;
    }
    for (SqlStatement statement : statements) {
      if (SqlGeneratorFactory.getInstance().supports(statement, database)) {
        warnings.addAll(SqlGeneratorFactory.getInstance().warn(statement, database));
      } else if (statement.skipOnUnsupported()) {
        warnings.addWarning(
            statement.getClass().getName()
                + " is not supported on "
                + database.getShortName()
                + ", but "
                + ChangeFactory.getInstance().getChangeMetaData(this).getName()
                + " will still execute");
      }
    }

    return warnings;
  }
  public Warnings warn(Database database) {
    Warnings warnings = new Warnings();
    for (SqlStatement statement : generateStatements(database)) {
      if (SqlGeneratorFactory.getInstance().supports(statement, database)) {
        warnings.addAll(SqlGeneratorFactory.getInstance().warn(statement, database));
      }
    }

    return warnings;
  }
  public void printNonRenames(DatabaseSnapshot reference, DatabaseSnapshot target, StringBuilder sb)
      throws Exception {
    CompareControl compareControl =
        new CompareControl(reference.getSnapshotControl().getTypesToInclude());
    DiffResult diffResult =
        DiffGeneratorFactory.getInstance().compare(reference, target, compareControl);

    DiffToChangeLog diffToChangeLog =
        new DiffToChangeLog(diffResult, new DiffOutputControl(false, false, false));

    SqlGeneratorFactory generatorFactory = SqlGeneratorFactory.getInstance();
    for (ChangeSet changeSet : diffToChangeLog.generateChangeSets()) {
      for (Change change : changeSet.getChanges()) {
        for (SqlStatement sqlStatement :
            change.generateStatements(LiquibaseModelFactory.DATABASE)) {
          for (Sql sql :
              generatorFactory.generateSql(sqlStatement, LiquibaseModelFactory.DATABASE)) {

            final String sqlString = sql.toSql();
            if (sqlString.endsWith("DROP INDEX")) {
              sb.append(StringUtils.substringBefore(sqlString, " DROP INDEX")).append(";\n");
            } else {
              sb.append(sqlString).append(";\n");
            }
          }
        }
      }
    }
  }
  @Override
  public Sql[] generateSql(
      CreateDatabaseChangeLogTableStatement statement,
      Database database,
      SqlGeneratorChain sqlGeneratorChain) {
    /* Because Informix has some limitations on the primary key column cumulative size (same is for unique indices),
     * the ID's column has been made the sole primary key, and also has a reduced size.
     *
     * The original configuration provided by the base class causes the database changelog table not to be created.
     */

    CreateTableStatement createTableStatement =
        new CreateTableStatement(
                database.getLiquibaseCatalogName(),
                database.getLiquibaseSchemaName(),
                database.getDatabaseChangeLogTableName())
            .addPrimaryKeyColumn(
                "ID",
                DataTypeFactory.getInstance().fromDescription("VARCHAR(" + getIdColumnSize() + ")"),
                null,
                null,
                null,
                new NotNullConstraint())
            .addColumn(
                "AUTHOR",
                DataTypeFactory.getInstance()
                    .fromDescription("VARCHAR(" + getAuthorColumnSize() + ")"),
                null,
                null,
                null,
                new NotNullConstraint())
            .addColumn(
                "FILENAME",
                DataTypeFactory.getInstance()
                    .fromDescription("VARCHAR(" + getFilenameColumnSize() + ")"),
                null,
                null,
                null,
                new NotNullConstraint())
            .addColumn(
                "DATEEXECUTED",
                DataTypeFactory.getInstance().fromDescription("datetime"),
                null,
                new NotNullConstraint())
            .addColumn(
                "ORDEREXECUTED",
                DataTypeFactory.getInstance().fromDescription("INT"),
                new NotNullConstraint())
            .addColumn(
                "EXECTYPE",
                DataTypeFactory.getInstance().fromDescription("VARCHAR(10)"),
                new NotNullConstraint())
            .addColumn("MD5SUM", DataTypeFactory.getInstance().fromDescription("VARCHAR(35)"))
            .addColumn("DESCRIPTION", DataTypeFactory.getInstance().fromDescription("VARCHAR(255)"))
            .addColumn("COMMENTS", DataTypeFactory.getInstance().fromDescription("VARCHAR(255)"))
            .addColumn("TAG", DataTypeFactory.getInstance().fromDescription("VARCHAR(255)"))
            .addColumn("LIQUIBASE", DataTypeFactory.getInstance().fromDescription("VARCHAR(20)"));

    return SqlGeneratorFactory.getInstance().generateSql(createTableStatement, database);
  }
  /**
   * Implementation checks the ChangeParameterMetaData for declared required fields and also
   * delegates logic to the {@link
   * liquibase.sqlgenerator.SqlGenerator#validate(liquibase.statement.SqlStatement,
   * liquibase.database.Database, liquibase.sqlgenerator.SqlGeneratorChain)} method on the {@link
   * SqlStatement} objects returned by {@link #generateStatements }. If no or null SqlStatements are
   * returned by generateStatements then this method returns no errors. If there are no parameters
   * than this method returns no errors
   */
  @Override
  public ValidationErrors validate(Database database) {
    ValidationErrors changeValidationErrors = new ValidationErrors();

    for (ChangeParameterMetaData param :
        ChangeFactory.getInstance().getChangeMetaData(this).getParameters().values()) {
      if (param.isRequiredFor(database) && param.getCurrentValue(this) == null) {
        changeValidationErrors.addError(
            param.getParameterName()
                + " is required for "
                + ChangeFactory.getInstance().getChangeMetaData(this).getName()
                + " on "
                + database.getShortName());
      }
    }
    if (changeValidationErrors.hasErrors()) {
      return changeValidationErrors;
    }

    String unsupportedWarning =
        ChangeFactory.getInstance().getChangeMetaData(this).getName()
            + " is not supported on "
            + database.getShortName();
    if (!this.supports(database)) {
      changeValidationErrors.addError(unsupportedWarning);
    } else if (!generateStatementsVolatile(database)) {
      boolean sawUnsupportedError = false;
      SqlStatement[] statements;
      statements = generateStatements(database);
      if (statements != null) {
        for (SqlStatement statement : statements) {
          boolean supported = SqlGeneratorFactory.getInstance().supports(statement, database);
          if (!supported && !sawUnsupportedError) {
            if (!statement.skipOnUnsupported()) {
              changeValidationErrors.addError(unsupportedWarning);
              sawUnsupportedError = true;
            }
          } else {
            changeValidationErrors.addAll(
                SqlGeneratorFactory.getInstance().validate(statement, database));
          }
        }
      }
    }

    return changeValidationErrors;
  }
 public boolean supports(Database database) {
   for (SqlStatement statement : generateStatements(database)) {
     if (!SqlGeneratorFactory.getInstance().supports(statement, database)) {
       return false;
     }
   }
   return true;
 }
 public boolean requiresUpdatedDatabaseMetadata(Database database) {
   for (SqlStatement statement : generateStatements(database)) {
     if (SqlGeneratorFactory.getInstance().requiresCurrentDatabaseMetadata(statement, database)) {
       return true;
     }
   }
   return false;
 }
  public Set<DatabaseObject> getAffectedDatabaseObjects(Database database) {
    Set<DatabaseObject> affectedObjects = new HashSet<DatabaseObject>();
    for (SqlStatement statement : generateStatements(database)) {
      affectedObjects.addAll(
          SqlGeneratorFactory.getInstance().getAffectedDatabaseObjects(statement, database));
    }

    return affectedObjects;
  }
 @Override
 public void saveRollbackStatement(Change change, List<SqlVisitor> sqlVisitors, Writer writer)
     throws IOException, UnsupportedChangeException, RollbackImpossibleException,
         StatementNotSupportedOnDatabaseException, LiquibaseException {
   SqlStatement[] statements = change.generateRollbackStatements(this);
   for (SqlStatement statement : statements) {
     for (Sql sql : SqlGeneratorFactory.getInstance().generateSql(statement, this)) {
       writer.append(sql.toSql()).append(sql.getEndDelimiter()).append("\n\n");
     }
   }
 }
  @Override
  public Sql[] generateSql(
      final AddAutoIncrementStatement statement,
      Database database,
      SqlGeneratorChain sqlGeneratorChain) {
    List<Sql> statements = new ArrayList<Sql>();

    // define alter table logic
    SQLiteDatabase.AlterTableVisitor rename_alter_visitor =
        new SQLiteDatabase.AlterTableVisitor() {
          @Override
          public ColumnConfig[] getColumnsToAdd() {
            return new ColumnConfig[0];
          }

          @Override
          public boolean copyThisColumn(ColumnConfig column) {
            return true;
          }

          @Override
          public boolean createThisColumn(ColumnConfig column) {
            if (column.getName().equals(statement.getColumnName())) {
              column.setAutoIncrement(true);
              column.setType("INTEGER");
            }
            return true;
          }

          @Override
          public boolean createThisIndex(Index index) {
            return true;
          }
        };

    try {
      // alter table
      for (SqlStatement generatedStatement :
          SQLiteDatabase.getAlterTableStatements(
              rename_alter_visitor,
              database,
              statement.getCatalogName(),
              statement.getSchemaName(),
              statement.getTableName())) {
        statements.addAll(
            Arrays.asList(
                SqlGeneratorFactory.getInstance().generateSql(generatedStatement, database)));
      }
    } catch (DatabaseException e) {
      e.printStackTrace();
    }

    return statements.toArray(new Sql[statements.size()]);
  }
 /*
  * 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);
   }
 }
 /**
  * Implementation delegates logic to the {@link
  * liquibase.sqlgenerator.SqlGenerator#generateStatementsIsVolatile(Database) } method on the
  * {@link SqlStatement} objects returned by {@link #generateStatements }. If zero or null
  * SqlStatements are returned by generateStatements then this method returns false.
  */
 @Override
 public boolean generateStatementsVolatile(Database database) {
   SqlStatement[] statements = generateStatements(database);
   if (statements == null) {
     return false;
   }
   for (SqlStatement statement : statements) {
     if (SqlGeneratorFactory.getInstance().generateStatementsVolatile(statement, database)) {
       return true;
     }
   }
   return false;
 }
 protected void addUniqueConstrantStatements(
     AddColumnStatement statement, Database database, List<Sql> returnSql) {
   if (statement.isUnique()) {
     AddUniqueConstraintStatement addConstraintStmt =
         new AddUniqueConstraintStatement(
             statement.getCatalogName(),
             statement.getSchemaName(),
             statement.getTableName(),
             ColumnConfig.arrayFromNames(statement.getColumnName()),
             statement.getUniqueStatementName());
     returnSql.addAll(
         Arrays.asList(
             SqlGeneratorFactory.getInstance().generateSql(addConstraintStmt, database)));
   }
 }
  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;
  }
 @Override
 public void saveStatements(
     final Change change, final List<SqlVisitor> sqlVisitors, final Writer writer)
     throws IOException, StatementNotSupportedOnDatabaseException, LiquibaseException {
   SqlStatement[] statements = change.generateStatements(this);
   for (SqlStatement statement : statements) {
     for (Sql sql : SqlGeneratorFactory.getInstance().generateSql(statement, this)) {
       writer
           .append(sql.toSql())
           .append(sql.getEndDelimiter())
           .append(StreamUtil.getLineSeparator())
           .append(StreamUtil.getLineSeparator());
     }
   }
 }
  /**
   * Implementation delegates logic to the {@link
   * liquibase.sqlgenerator.SqlGeneratorFactory#getAffectedDatabaseObjects(liquibase.statement.SqlStatement,
   * liquibase.database.Database)} method on the {@link SqlStatement} objects returned by {@link
   * #generateStatements } Returns empty set if change is not supported for the passed database
   */
  @Override
  public Set<DatabaseObject> getAffectedDatabaseObjects(Database database) {
    if (this.generateStatementsVolatile(database)) {
      return new HashSet<DatabaseObject>();
    }
    Set<DatabaseObject> affectedObjects = new HashSet<DatabaseObject>();
    SqlStatement[] statements = generateStatements(database);

    if (statements != null) {
      for (SqlStatement statement : statements) {
        affectedObjects.addAll(
            SqlGeneratorFactory.getInstance().getAffectedDatabaseObjects(statement, database));
      }
    }

    return affectedObjects;
  }
  protected void addForeignKeyStatements(
      AddColumnStatement statement, Database database, List<Sql> returnSql) {
    for (ColumnConstraint constraint : statement.getConstraints()) {
      if (constraint instanceof ForeignKeyConstraint) {
        ForeignKeyConstraint fkConstraint = (ForeignKeyConstraint) constraint;
        String refSchemaName = null;
        String refTableName;
        String refColName;
        if (fkConstraint.getReferences() != null) {
          Matcher referencesMatcher =
              Pattern.compile("([\\w\\._]+)\\(([\\w_]+)\\)").matcher(fkConstraint.getReferences());
          if (!referencesMatcher.matches()) {
            throw new UnexpectedLiquibaseException(
                "Don't know how to find table and column names from "
                    + fkConstraint.getReferences());
          }
          refTableName = referencesMatcher.group(1);
          refColName = referencesMatcher.group(2);
        } else {
          refTableName = ((ForeignKeyConstraint) constraint).getReferencedTableName();
          refColName = ((ForeignKeyConstraint) constraint).getReferencedColumnNames();
        }

        if (refTableName.indexOf(".") > 0) {
          refSchemaName = refTableName.split("\\.")[0];
          refTableName = refTableName.split("\\.")[1];
        }

        AddForeignKeyConstraintStatement addForeignKeyConstraintStatement =
            new AddForeignKeyConstraintStatement(
                fkConstraint.getForeignKeyName(),
                statement.getCatalogName(),
                statement.getSchemaName(),
                statement.getTableName(),
                ColumnConfig.arrayFromNames(statement.getColumnName()),
                null,
                refSchemaName,
                refTableName,
                ColumnConfig.arrayFromNames(refColName));
        returnSql.addAll(
            Arrays.asList(
                SqlGeneratorFactory.getInstance()
                    .generateSql(addForeignKeyConstraintStatement, database)));
      }
    }
  }
  @Test
  public void parseAndGenerate() throws Exception {
    Database database = liquiBase.getDatabase();
    ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor();

    ChangeLogParameters changeLogParameters = new ChangeLogParameters();

    DatabaseChangeLog changeLog =
        ChangeLogParserFactory.getInstance()
            .getParser(changeLogFile, resourceAccessor)
            .parse(changeLogFile, changeLogParameters, resourceAccessor);

    database.checkDatabaseChangeLogTable(false, changeLog, null);
    changeLog.validate(database);

    List<ChangeSet> changeSets = changeLog.getChangeSets();

    List<String> expectedQuery = new ArrayList<String>();

    expectedQuery.add(
        "MERGE INTO LIQUIBASE.myTable2 m "
            + "USING LIQUIBASE.myTable d "
            + "ON (m.pid=d.pid) "
            + "WHEN MATCHED THEN UPDATE SET m.sales=m.sales+d.sales,m.status=d.status "
            + "DELETE WHERE (m.status='OBS') "
            + "WHEN NOT MATCHED THEN INSERT VALUES(d.pid,d.sales,'OLD')");

    int i = 0;

    for (ChangeSet changeSet : changeSets) {
      for (Change change : changeSet.getChanges()) {
        Sql[] sql =
            SqlGeneratorFactory.getInstance()
                .generateSql(change.generateStatements(database)[0], database);
        if (i == 3) {
          assertEquals(expectedQuery.get(0), sql[0].toSql());
        }
      }
      i++;
    }
  }
Exemple #19
0
  private void outputStatement(SqlStatement sql, List<SqlVisitor> sqlVisitors)
      throws DatabaseException {
    try {
      if (SqlGeneratorFactory.getInstance().generateStatementsVolatile(sql, database)) {
        throw new DatabaseException(
            sql.getClass().getSimpleName()
                + " requires access to up to date database metadata which is not available in SQL output mode");
      }
      for (String statement : applyVisitors(sql, sqlVisitors)) {
        if (statement == null) {
          continue;
        }
        output.write(statement);

        if (database instanceof MSSQLDatabase
            || database instanceof SybaseDatabase
            || database instanceof SybaseASADatabase) {
          output.write(StreamUtil.getLineSeparator());
          output.write("GO");
          //            } else if (database instanceof OracleDatabase) {
          //                output.write(StreamUtil.getLineSeparator());
          //                output.write("/");
        } else {
          String endDelimiter = ";";
          if (sql instanceof RawSqlStatement) {
            endDelimiter = ((RawSqlStatement) sql).getEndDelimiter();
          }
          if (!statement.endsWith(endDelimiter)) {
            output.write(endDelimiter);
          }
        }
        output.write(StreamUtil.getLineSeparator());
        output.write(StreamUtil.getLineSeparator());
      }
    } catch (IOException e) {
      throw new DatabaseException(e);
    }
  }
  @Override
  public Sql[] generateSql(
      MarkChangeSetRanStatement statement, Database database, SqlGeneratorChain sqlGeneratorChain) {
    String dateValue = database.getCurrentDateTimeFunction();

    ChangeSet changeSet = statement.getChangeSet();

    SqlStatement runStatement;
    try {
      if (statement.getExecType().equals(ChangeSet.ExecType.FAILED)
          || statement.getExecType().equals(ChangeSet.ExecType.SKIPPED)) {
        return new Sql[0]; // don't mark
      } else if (statement.getExecType().ranBefore) {
        runStatement =
            new UpdateStatement(
                    database.getLiquibaseCatalogName(),
                    database.getLiquibaseSchemaName(),
                    database.getDatabaseChangeLogTableName())
                .addNewColumnValue("DATEEXECUTED", new DatabaseFunction(dateValue))
                .addNewColumnValue("MD5SUM", changeSet.generateCheckSum().toString())
                .addNewColumnValue("EXECTYPE", statement.getExecType().value)
                .setWhereClause("ID=? AND AUTHOR=? AND FILENAME=?")
                .addWhereParameters(
                    changeSet.getId(), changeSet.getAuthor(), changeSet.getFilePath());
      } else {
        runStatement =
            new InsertStatement(
                    database.getLiquibaseCatalogName(),
                    database.getLiquibaseSchemaName(),
                    database.getDatabaseChangeLogTableName())
                .addColumnValue("ID", changeSet.getId())
                .addColumnValue("AUTHOR", changeSet.getAuthor())
                .addColumnValue("FILENAME", changeSet.getFilePath())
                .addColumnValue("DATEEXECUTED", new DatabaseFunction(dateValue))
                .addColumnValue(
                    "ORDEREXECUTED",
                    ChangeLogHistoryServiceFactory.getInstance()
                        .getChangeLogService(database)
                        .getNextSequenceValue())
                .addColumnValue("MD5SUM", changeSet.generateCheckSum().toString())
                .addColumnValue("DESCRIPTION", limitSize(changeSet.getDescription()))
                .addColumnValue(
                    "COMMENTS",
                    limitSize(
                        database.escapeStringForDatabase(
                            StringUtils.trimToEmpty(changeSet.getComments()))))
                .addColumnValue("EXECTYPE", statement.getExecType().value)
                .addColumnValue(
                    "LIQUIBASE", LiquibaseUtil.getBuildVersion().replaceAll("SNAPSHOT", "SNP"));

        String tag = null;
        List<Change> changes = changeSet.getChanges();
        if (changes != null && changes.size() == 1) {
          Change change = changes.get(0);
          if (change instanceof TagDatabaseChange) {
            TagDatabaseChange tagChange = (TagDatabaseChange) change;
            tag = tagChange.getTag();
          }
        }
        if (tag != null) {
          ((InsertStatement) runStatement).addColumnValue("TAG", tag);
        }
      }
    } catch (LiquibaseException e) {
      throw new UnexpectedLiquibaseException(e);
    }

    return SqlGeneratorFactory.getInstance().generateSql(runStatement, database);
  }
  @Test
  public void minimumRequiredIsValidSql() throws Exception {
    ChangeFactory changeFactory = ChangeFactory.getInstance();
    for (String changeName : changeFactory.getDefinedChanges()) {
      if (changeName.equals("addDefaultValue")) {
        continue; // need to better handle strange "one of defaultValue* is required" logic
      }
      if (changeName.equals("changeWithNestedTags") || changeName.equals("sampleChange")) {
        continue; // not a real change
      }
      for (Database database : DatabaseFactory.getInstance().getImplementedDatabases()) {
        if (database.getShortName() == null) {
          continue;
        }

        TestState state =
            new TestState(
                name.getMethodName(), changeName, database.getShortName(), TestState.Type.SQL);
        state.addComment("Database: " + database.getShortName());

        Change change = changeFactory.create(changeName);
        if (!change.supports(database)) {
          continue;
        }
        if (change.generateStatementsVolatile(database)) {
          continue;
        }
        ChangeMetaData changeMetaData = ChangeFactory.getInstance().getChangeMetaData(change);

        change.setResourceAccessor(new JUnitResourceAccessor());

        for (String paramName :
            new TreeSet<String>(changeMetaData.getRequiredParameters(database).keySet())) {
          ChangeParameterMetaData param = changeMetaData.getParameters().get(paramName);
          Object paramValue = param.getExampleValue();
          String serializedValue;
          serializedValue = formatParameter(paramValue);
          state.addComment("Change Parameter: " + param.getParameterName() + "=" + serializedValue);
          param.setValue(change, paramValue);
        }

        ValidationErrors errors = change.validate(database);
        assertFalse(
            "Validation errors for "
                + changeMetaData.getName()
                + " on "
                + database.getShortName()
                + ": "
                + errors.toString(),
            errors.hasErrors());

        SqlStatement[] sqlStatements = change.generateStatements(database);
        for (SqlStatement statement : sqlStatements) {
          Sql[] sql = SqlGeneratorFactory.getInstance().generateSql(statement, database);
          if (sql == null) {
            System.out.println("Null sql for " + statement + " on " + database.getShortName());
          } else {
            for (Sql line : sql) {
              String sqlLine = line.toSql();
              assertFalse(
                  "Change "
                      + changeMetaData.getName()
                      + " contains 'null' for "
                      + database.getShortName()
                      + ": "
                      + sqlLine,
                  sqlLine.contains(" null "));

              state.addValue(sqlLine + ";");
            }
          }
        }
        state.test();
      }
    }
  }