package liquibase.ext.spatial.sqlgenerator; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import liquibase.database.Database; import liquibase.database.core.OracleDatabase; import liquibase.ext.spatial.statement.CreateSpatialIndexStatement; import liquibase.sql.Sql; import liquibase.sql.UnparsedSql; import liquibase.sqlgenerator.SqlGeneratorChain; import liquibase.structure.core.Column; import liquibase.structure.core.Table; import liquibase.structure.core.View; import liquibase.util.StringUtils; /** * <code>CreateSpatialIndexGeneratorOracle</code> generates the SQL for creating a spatial index in * Oracle. */ public class CreateSpatialIndexGeneratorOracle extends AbstractCreateSpatialIndexGenerator { @Override public boolean supports(final CreateSpatialIndexStatement statement, final Database database) { return database instanceof OracleDatabase; } @Override public Sql[] generateSql(final CreateSpatialIndexStatement statement, final Database database, final SqlGeneratorChain sqlGeneratorChain) { final View metadataView = new View().setName("USER_SDO_GEOM_METADATA"); final String deleteMetadataSql = generateDeleteMetadataSql(statement, database); final Sql deleteMetadata = new UnparsedSql(deleteMetadataSql, metadataView); final String insertMetadataSql = generateInsertMetadataSql(statement, database); final Sql insertMetadata = new UnparsedSql(insertMetadataSql, metadataView); final String createIndexSql = generateCreateIndexSql(statement, database); final Sql createIndex = new UnparsedSql(createIndexSql, getAffectedIndex(statement)); return new Sql[] { deleteMetadata, insertMetadata, createIndex }; } /** * Generates the SQL for deleting any existing record from the * <code>USER_SDO_GEOM_METADATA</code> table. Typically this record shouldn't be present but we * must ensure that it does not already exist. * * @param statement * the create spatial index statement. * @param database * the database instance. * @return the SQL to delete any existing metadata record. */ protected String generateDeleteMetadataSql(final CreateSpatialIndexStatement statement, final Database database) { final StringBuilder sql = new StringBuilder(); sql.append("DELETE FROM user_sdo_geom_metadata "); final String tableName = statement.getTableName().trim(); sql.append("WHERE table_name = '").append(database.correctObjectName(tableName, Table.class)); final String columnName = statement.getColumns()[0].trim(); sql.append("' AND column_name = '").append( database.correctObjectName(columnName, Column.class)); sql.append("'"); return sql.toString(); } /** * Generates the SQL for inserting the necessary record into the * <code>USER_SDO_GEOM_METADATA</code> table. This record must be present prior to creating the * spatial index. * * @param statement * the create spatial index statement. * @param database * the database instance. * @return the SQL to insert the metadata record. */ protected String generateInsertMetadataSql(final CreateSpatialIndexStatement statement, final Database database) { final StringBuilder sql = new StringBuilder(); sql.append("INSERT INTO user_sdo_geom_metadata "); sql.append("(table_name, column_name, diminfo, srid) "); final String tableName = statement.getTableName().trim(); sql.append("VALUES ('").append(database.correctObjectName(tableName, Table.class)); final String columnName = statement.getColumns()[0].trim(); sql.append("', '").append(database.correctObjectName(columnName, Column.class)); sql.append("', SDO_DIM_ARRAY("); sql.append("SDO_DIM_ELEMENT('Longitude', -180, 180, 0.005), "); sql.append("SDO_DIM_ELEMENT('Latitude', -90, 90, 0.005))"); final Integer srid = statement.getSrid(); if (srid == null) { sql.append(", NULL"); } else { sql.append(", ").append(OracleSpatialUtils.EPSG_TO_ORACLE_FUNCTION).append("(") .append(srid).append(")"); } sql.append(")"); return sql.toString(); } /** * Generates the SQL for creating the spatial index. * * @param statement * the create spatial index statement. * @param database * the database instance. * @return the SQL to create a spatial index. */ protected String generateCreateIndexSql(final CreateSpatialIndexStatement statement, final Database database) { final StringBuilder sql = new StringBuilder(); sql.append("CREATE INDEX "); final String schemaName = statement.getTableSchemaName(); final String catalogName = statement.getTableCatalogName(); final String indexName = statement.getIndexName(); sql.append(database.escapeIndexName(catalogName, schemaName, indexName)); sql.append(" ON "); final String tableName = statement.getTableName(); sql.append(database.escapeTableName(catalogName, schemaName, tableName)).append(" ("); final Iterator<String> iterator = Arrays.asList(statement.getColumns()).iterator(); final String column = iterator.next(); sql.append(database.escapeColumnName(catalogName, statement.getTableSchemaName(), tableName, column)); sql.append(") INDEXTYPE IS mdsys.spatial_index"); // Generate and add the optional parameters. final Collection<String> parameters = getParameters(statement); if (parameters != null && !parameters.isEmpty()) { sql.append(" PARAMETERS ('"); sql.append(StringUtils.join(parameters, " ")); sql.append("')"); } return sql.toString(); } /** * Creates the parameters to the spatial index creation statement. * * @param statement * the statement. * @return the optional parameters for the <code>CREATE INDEX</code> statement. */ protected Collection<String> getParameters(final CreateSpatialIndexStatement statement) { final Collection<String> parameters = new ArrayList<String>(); if (StringUtils.trimToNull(statement.getGeometryType()) != null) { final String gType = getGtype(statement.getGeometryType().trim()); if (gType != null) { parameters.add("layer_gtype=" + gType); } } if (StringUtils.trimToNull(statement.getTablespace()) != null) { parameters.add("tablespace=" + statement.getTablespace().trim()); } return parameters; } /** * Converts the OGC geometry type to Oracle's <code>SDO_GTYPE</code>. * * @param ogcGeometryType * the OGC geometry type. * @return the corresponding Oracle <code>SDO_GTYPE</code>. */ protected String getGtype(final String ogcGeometryType) { final String gType; if (ogcGeometryType == null) { gType = null; } else if ("LineString".equalsIgnoreCase(ogcGeometryType)) { gType = "LINE"; } else if ("MultiLineString".equalsIgnoreCase(ogcGeometryType)) { gType = "MULTILINE"; } else if ("Triangle".equalsIgnoreCase(ogcGeometryType)) { gType = "POLYGON"; } else if ("Point".equalsIgnoreCase(ogcGeometryType) || "MultiPoint".equalsIgnoreCase(ogcGeometryType) || "Curve".equalsIgnoreCase(ogcGeometryType) || "MultiCurve".equalsIgnoreCase(ogcGeometryType) || "Polygon".equalsIgnoreCase(ogcGeometryType) || "MultiPolygon".equalsIgnoreCase(ogcGeometryType)) { gType = ogcGeometryType.toUpperCase(); } else { gType = "COLLECTION"; } return gType; } }