package liquibase.sqlgenerator.core; import liquibase.database.Database; import liquibase.database.core.InformixDatabase; import liquibase.database.core.MSSQLDatabase; import liquibase.database.core.SQLiteDatabase; import liquibase.database.core.OracleDatabase; import liquibase.exception.ValidationErrors; import liquibase.sql.Sql; import liquibase.sql.UnparsedSql; import liquibase.sqlgenerator.SqlGenerator; import liquibase.sqlgenerator.SqlGeneratorChain; import liquibase.statement.core.AddForeignKeyConstraintStatement; public class AddForeignKeyConstraintGenerator extends AbstractSqlGenerator<AddForeignKeyConstraintStatement> { @Override @SuppressWarnings({"SimplifiableIfStatement"}) public boolean supports(AddForeignKeyConstraintStatement statement, Database database) { if (statement.getReferencesUniqueColumn() && !(database instanceof OracleDatabase)) { return false; } return (!(database instanceof SQLiteDatabase)); } public ValidationErrors validate(AddForeignKeyConstraintStatement addForeignKeyConstraintStatement, Database database, SqlGeneratorChain sqlGeneratorChain) { ValidationErrors validationErrors = new ValidationErrors(); if ((addForeignKeyConstraintStatement.isInitiallyDeferred() || addForeignKeyConstraintStatement.isDeferrable()) && !database.supportsInitiallyDeferrableColumns()) { validationErrors.checkDisallowedField("initiallyDeferred", addForeignKeyConstraintStatement.isInitiallyDeferred(), database); validationErrors.checkDisallowedField("deferrable", addForeignKeyConstraintStatement.isDeferrable(), database); } validationErrors.checkRequiredField("baseColumnNames", addForeignKeyConstraintStatement.getBaseColumnNames()); validationErrors.checkRequiredField("baseTableNames", addForeignKeyConstraintStatement.getBaseTableName()); validationErrors.checkRequiredField("referencedColumnNames", addForeignKeyConstraintStatement.getReferencedColumnNames()); validationErrors.checkRequiredField("referencedTableName", addForeignKeyConstraintStatement.getReferencedTableName()); return validationErrors; } public Sql[] generateSql(AddForeignKeyConstraintStatement statement, Database database, SqlGeneratorChain sqlGeneratorChain) { StringBuilder sb = new StringBuilder(); sb.append("ALTER TABLE ") .append(database.escapeTableName(statement.getBaseTableSchemaName(), statement.getBaseTableName())) .append(" ADD CONSTRAINT "); if (!(database instanceof InformixDatabase)) { sb.append(database.escapeConstraintName(statement.getConstraintName())); } sb.append(" FOREIGN KEY (") .append(database.escapeColumnNameList(statement.getBaseColumnNames())) .append(") REFERENCES ") .append(database.escapeTableName(statement.getReferencedTableSchemaName(), statement.getReferencedTableName())) .append(" (") .append(database.escapeColumnNameList(statement.getReferencedColumnNames())) .append(")"); if (statement.getOnUpdate() != null) { if (database instanceof OracleDatabase) { //don't use } else if (database instanceof InformixDatabase) { //TODO don't know if correct } else { sb.append(" ON UPDATE ").append(statement.getOnUpdate()); } } if (statement.getOnDelete() != null) { if ((database instanceof OracleDatabase) && (statement.getOnDelete().equalsIgnoreCase("RESTRICT") || statement.getOnDelete().equalsIgnoreCase("NO ACTION"))) { //don't use } else if ((database instanceof MSSQLDatabase) && statement.getOnDelete().equalsIgnoreCase("RESTRICT")) { //don't use } else if (database instanceof InformixDatabase && !(statement.getOnDelete().equalsIgnoreCase("CASCADE"))) { //TODO Informix can handle ON DELETE CASCADE only, but I don't know if this is really correct // see "REFERENCES Clause" in manual } else { sb.append(" ON DELETE ").append(statement.getOnDelete()); } } if (statement.isDeferrable() || statement.isInitiallyDeferred()) { if (statement.isDeferrable()) { sb.append(" DEFERRABLE"); } if (statement.isInitiallyDeferred()) { sb.append(" INITIALLY DEFERRED"); } } if (database instanceof InformixDatabase) { sb.append(" CONSTRAINT "); sb.append(database.escapeConstraintName(statement.getConstraintName())); } return new Sql[]{ new UnparsedSql(sb.toString()) }; } }