package liquibase.ext.spatial.sqlgenerator; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import liquibase.database.Database; import liquibase.database.core.DerbyDatabase; import liquibase.database.core.H2Database; import liquibase.datatype.DataTypeFactory; import liquibase.datatype.LiquibaseDataType; import liquibase.exception.ValidationErrors; import liquibase.ext.spatial.datatype.GeometryType; import liquibase.sql.Sql; import liquibase.sql.UnparsedSql; import liquibase.sqlgenerator.SqlGenerator; import liquibase.sqlgenerator.SqlGeneratorChain; import liquibase.sqlgenerator.core.AbstractSqlGenerator; import liquibase.statement.core.AddColumnStatement; import liquibase.util.StringUtils; /** * <code>AddGeometryColumnGeneratorGeoDB</code> is a SQL generator that * specializes in GeoDB. Regardless of the column type, the next SQL generator * in the chain is invoked to handle the normal column addition. If the column * to be added has a geometry type, the <code>AddGeometryColumn</code> stored * procedure is invoked to ensure that the necessary metadata is created in the * database. */ public class AddGeometryColumnGeneratorGeoDB extends AbstractSqlGenerator<AddColumnStatement> { /** * @see liquibase.sqlgenerator.core.AbstractSqlGenerator#supports(liquibase.statement.SqlStatement, * liquibase.database.Database) */ @Override public boolean supports(final AddColumnStatement statement, final Database database) { return database instanceof DerbyDatabase || database instanceof H2Database; } /** * @see liquibase.sqlgenerator.core.AbstractSqlGenerator#getPriority() */ @Override public int getPriority() { return SqlGenerator.PRIORITY_DATABASE + 1; } @Override public ValidationErrors validate(final AddColumnStatement statement, final Database database, final SqlGeneratorChain sqlGeneratorChain) { final ValidationErrors errors = new ValidationErrors(); final LiquibaseDataType dataType = DataTypeFactory.getInstance() .fromDescription(statement.getColumnType(), database); // Ensure that the SRID parameter is provided. if (dataType instanceof GeometryType) { final GeometryType geometryType = (GeometryType) dataType; if (geometryType.getSRID() == null) { errors.addError("The SRID parameter is required on the geometry type"); } } final ValidationErrors chainErrors = sqlGeneratorChain.validate( statement, database); if (chainErrors != null) { errors.addAll(chainErrors); } return errors; } @Override public Sql[] generateSql(final AddColumnStatement statement, final Database database, final SqlGeneratorChain sqlGeneratorChain) { GeometryType geometryType = null; final LiquibaseDataType dataType = DataTypeFactory.getInstance() .fromDescription(statement.getColumnType(), database); if (dataType instanceof GeometryType) { geometryType = (GeometryType) dataType; } final boolean isGeometryColumn = geometryType != null; // The AddGeometryColumn procedure handles the column already being // present, so let a // downstream SQL generator handle the typical column addition logic (e.g. // placement in the // table) then invoke the procedure. final List<Sql> list = new ArrayList<Sql>(); list.addAll(Arrays.asList(sqlGeneratorChain.generateSql(statement, database))); if (isGeometryColumn) { String schemaName = statement.getSchemaName(); if (schemaName == null) { schemaName = database.getDefaultSchemaName(); } final String tableName = statement.getTableName(); final String columnName = statement.getColumnName(); final int srid = geometryType.getSRID(); final String geomType = StringUtils.trimToNull(geometryType .getGeometryType()) == null ? "'Geometry'" : "'" + database.escapeStringForDatabase(geometryType .getGeometryType()) + "'"; final String sql = "CALL AddGeometryColumn('" + schemaName + "', '" + tableName + "', '" + columnName + "', " + srid + ", " + geomType + ", 2)"; final Sql addGeometryColumn = new UnparsedSql(sql); list.add(addGeometryColumn); } return list.toArray(new Sql[list.size()]); } }