View Javadoc

1   package liquibase.ext.spatial.sqlgenerator;
2   
3   import java.util.ArrayList;
4   import java.util.Arrays;
5   import java.util.Collection;
6   import java.util.Iterator;
7   
8   import liquibase.database.Database;
9   import liquibase.database.core.OracleDatabase;
10  import liquibase.ext.spatial.statement.CreateSpatialIndexStatement;
11  import liquibase.sql.Sql;
12  import liquibase.sql.UnparsedSql;
13  import liquibase.sqlgenerator.SqlGeneratorChain;
14  import liquibase.structure.core.Column;
15  import liquibase.structure.core.Table;
16  import liquibase.structure.core.View;
17  import liquibase.util.StringUtils;
18  
19  /**
20   * <code>CreateSpatialIndexGeneratorOracle</code> generates the SQL for creating a spatial index in
21   * Oracle.
22   */
23  public class CreateSpatialIndexGeneratorOracle extends AbstractCreateSpatialIndexGenerator {
24     @Override
25     public boolean supports(final CreateSpatialIndexStatement statement, final Database database) {
26        return database instanceof OracleDatabase;
27     }
28  
29     @Override
30     public Sql[] generateSql(final CreateSpatialIndexStatement statement, final Database database,
31           final SqlGeneratorChain sqlGeneratorChain) {
32        final View metadataView = new View().setName("USER_SDO_GEOM_METADATA");
33        final String deleteMetadataSql = generateDeleteMetadataSql(statement, database);
34        final Sql deleteMetadata = new UnparsedSql(deleteMetadataSql, metadataView);
35        final String insertMetadataSql = generateInsertMetadataSql(statement, database);
36        final Sql insertMetadata = new UnparsedSql(insertMetadataSql, metadataView);
37        final String createIndexSql = generateCreateIndexSql(statement, database);
38        final Sql createIndex = new UnparsedSql(createIndexSql, getAffectedIndex(statement));
39        return new Sql[] { deleteMetadata, insertMetadata, createIndex };
40     }
41  
42     /**
43      * Generates the SQL for deleting any existing record from the
44      * <code>USER_SDO_GEOM_METADATA</code> table. Typically this record shouldn't be present but we
45      * must ensure that it does not already exist.
46      * 
47      * @param statement
48      *           the create spatial index statement.
49      * @param database
50      *           the database instance.
51      * @return the SQL to delete any existing metadata record.
52      */
53     protected String generateDeleteMetadataSql(final CreateSpatialIndexStatement statement,
54           final Database database) {
55        final StringBuilder sql = new StringBuilder();
56        sql.append("DELETE FROM user_sdo_geom_metadata ");
57        final String tableName = statement.getTableName().trim();
58        sql.append("WHERE table_name = '").append(database.correctObjectName(tableName, Table.class));
59        final String columnName = statement.getColumns()[0].trim();
60        sql.append("' AND column_name = '").append(
61              database.correctObjectName(columnName, Column.class));
62        sql.append("'");
63        return sql.toString();
64     }
65  
66     /**
67      * Generates the SQL for inserting the necessary record into the
68      * <code>USER_SDO_GEOM_METADATA</code> table. This record must be present prior to creating the
69      * spatial index.
70      * 
71      * @param statement
72      *           the create spatial index statement.
73      * @param database
74      *           the database instance.
75      * @return the SQL to insert the metadata record.
76      */
77     protected String generateInsertMetadataSql(final CreateSpatialIndexStatement statement,
78           final Database database) {
79        final StringBuilder sql = new StringBuilder();
80        sql.append("INSERT INTO user_sdo_geom_metadata ");
81        sql.append("(table_name, column_name, diminfo, srid) ");
82        final String tableName = statement.getTableName().trim();
83        sql.append("VALUES ('").append(database.correctObjectName(tableName, Table.class));
84        final String columnName = statement.getColumns()[0].trim();
85        sql.append("', '").append(database.correctObjectName(columnName, Column.class));
86        sql.append("', SDO_DIM_ARRAY(");
87        sql.append("SDO_DIM_ELEMENT('Longitude', -180, 180, 0.005), ");
88        sql.append("SDO_DIM_ELEMENT('Latitude', -90, 90, 0.005))");
89        final Integer srid = statement.getSrid();
90        if (srid == null) {
91           sql.append(", NULL");
92        } else {
93           sql.append(", ").append(OracleSpatialUtils.EPSG_TO_ORACLE_FUNCTION).append("(")
94                 .append(srid).append(")");
95        }
96        sql.append(")");
97        return sql.toString();
98     }
99  
100    /**
101     * Generates the SQL for creating the spatial index.
102     * 
103     * @param statement
104     *           the create spatial index statement.
105     * @param database
106     *           the database instance.
107     * @return the SQL to create a spatial index.
108     */
109    protected String generateCreateIndexSql(final CreateSpatialIndexStatement statement,
110          final Database database) {
111       final StringBuilder sql = new StringBuilder();
112       sql.append("CREATE INDEX ");
113       final String schemaName = statement.getTableSchemaName();
114       final String catalogName = statement.getTableCatalogName();
115       final String indexName = statement.getIndexName();
116       sql.append(database.escapeIndexName(catalogName, schemaName, indexName));
117       sql.append(" ON ");
118       final String tableName = statement.getTableName();
119       sql.append(database.escapeTableName(catalogName, schemaName, tableName)).append(" (");
120       final Iterator<String> iterator = Arrays.asList(statement.getColumns()).iterator();
121       final String column = iterator.next();
122       sql.append(database.escapeColumnName(catalogName, statement.getTableSchemaName(), tableName,
123             column));
124       sql.append(") INDEXTYPE IS mdsys.spatial_index");
125 
126       // Generate and add the optional parameters.
127       final Collection<String> parameters = getParameters(statement);
128       if (parameters != null && !parameters.isEmpty()) {
129          sql.append(" PARAMETERS ('");
130          sql.append(StringUtils.join(parameters, " "));
131          sql.append("')");
132       }
133       return sql.toString();
134    }
135 
136    /**
137     * Creates the parameters to the spatial index creation statement.
138     * 
139     * @param statement
140     *           the statement.
141     * @return the optional parameters for the <code>CREATE INDEX</code> statement.
142     */
143    protected Collection<String> getParameters(final CreateSpatialIndexStatement statement) {
144       final Collection<String> parameters = new ArrayList<String>();
145       if (StringUtils.trimToNull(statement.getGeometryType()) != null) {
146          final String gType = getGtype(statement.getGeometryType().trim());
147          if (gType != null) {
148             parameters.add("layer_gtype=" + gType);
149          }
150       }
151       if (StringUtils.trimToNull(statement.getTablespace()) != null) {
152          parameters.add("tablespace=" + statement.getTablespace().trim());
153       }
154       return parameters;
155    }
156 
157    /**
158     * Converts the OGC geometry type to Oracle's <code>SDO_GTYPE</code>.
159     * 
160     * @param ogcGeometryType
161     *           the OGC geometry type.
162     * @return the corresponding Oracle <code>SDO_GTYPE</code>.
163     */
164    protected String getGtype(final String ogcGeometryType) {
165       final String gType;
166       if (ogcGeometryType == null) {
167          gType = null;
168       } else if ("LineString".equalsIgnoreCase(ogcGeometryType)) {
169          gType = "LINE";
170       } else if ("MultiLineString".equalsIgnoreCase(ogcGeometryType)) {
171          gType = "MULTILINE";
172       } else if ("Triangle".equalsIgnoreCase(ogcGeometryType)) {
173          gType = "POLYGON";
174       } else if ("Point".equalsIgnoreCase(ogcGeometryType)
175             || "MultiPoint".equalsIgnoreCase(ogcGeometryType)
176             || "Curve".equalsIgnoreCase(ogcGeometryType)
177             || "MultiCurve".equalsIgnoreCase(ogcGeometryType)
178             || "Polygon".equalsIgnoreCase(ogcGeometryType)
179             || "MultiPolygon".equalsIgnoreCase(ogcGeometryType)) {
180          gType = ogcGeometryType.toUpperCase();
181       } else {
182          gType = "COLLECTION";
183       }
184       return gType;
185    }
186 }