diff --git a/README.md b/README.md index 90f97db..9028638 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # Introducing GeoDB -GeoDB is a spatial extension of [H2](http://h2database.com), the Java SQL database. GeoDB utilizes the [JTS](http://tsusiatsoftware.net/jts/main.html) library as its geometry engine and the [Hatbox](http://hatbox.sourceforge.net) library for spatial indexing support. +GeoDB is a spatial extension of [H2](http://h2database.com) and [Apache Derby](http://db.apache.org/derby), two Java SQL databases. GeoDB utilizes the [JTS](http://tsusiatsoftware.net/jts/main.html) library as its geometry engine and the [Hatbox](http://hatbox.sourceforge.net) library for spatial indexing support. # Quickstart +## H2 * Download [GeoDB](http://ares.boundlessgeo.com/geodb/0.8/geodb-0.8-app.zip) * Unzip the `geodb-0.8-app.zip` file * Update the `PATH` environment variable to include `geodb-0.8/bin` -* Run the @geodb@ command: +* Run the `geodb` command: % geodb foo @@ -34,7 +35,42 @@ GeoDB is a spatial extension of [H2](http://h2database.com), the Java SQL databa * Perform a spatial query - @h2> SELECT ST_AsText(geom) FROM spatial WHERE id IN (SELECT CAST(HATBOX_JOIN_ID AS INT) FROM HATBOX_MBR_INTERSECTS_ENV('PUBLIC', 'SPATIAL', -2, 2, -2, 2)); + @h2> SELECT ST_AsText(geom) FROM spatial WHERE id IN (SELECT CAST(HATBOX_JOIN_ID AS INT) FROM HATBOX_MBR_INTERSECTS_ENV('PUBLIC', 'SPATIAL', -2, 2, -2, 2)); + +## Apache Derby +* Download [GeoDB](http://repo.opengeo.org/org/opengeo/geodb), [Hatbox](http://repo.opengeo.org/net/sourceforge/hatbox/hatbox/) and [JTS](http://mvnrepository.com/artifact/com.vividsolutions/jts) +* Update the `CLASSPATH` environment variable to include the three `jar` files +* Download [Apache Derby](http://db.apache.org/derby/derby_downloads.html) 10.10.1.1 or newer +* Unzip the `db-derby-10.10.1.1-bin.zip` file +* Update the `PATH` environment variable to include `db-derby-10.10.1.1-bin/bin` +* Run the `ij` command: + + ij> connect 'jdbc:derby:foo;create=true'; + +* Initialize the spatial database: + + ij> CREATE PROCEDURE InitGeoDB () language java external name 'geodb.GeoDB.InitGeoDBProc' parameter style java modifies sql data; + ij> CALL InitGeoDB(); + +* Create a spatial table: + + ij> CREATE TABLE spatial (id INT NOT NULL GENERATED ALWAYS AS IDENTITY CONSTRAINT spatial_pk PRIMARY KEY, geom VARCHAR(32672) FOR BIT DATA); + +* Create some spatial data: + + ij> INSERT INTO spatial (geom) VALUES (ST_GeomFromText('POINT(-5 -5)', 4326)); + ij> INSERT INTO spatial (geom) VALUES (ST_GeomFromText('POINT(0 0)', 4326)); + ij> INSERT INTO spatial (geom) VALUES (ST_GeomFromText('POINT(5 5)', 4326)); + + ij> SELECT ST_AsText(ST_Buffer(geom, 10)) as buffer FROM spatial; + +* Create a spatial index + + ij> CALL CreateSpatialIndex(null, 'SPATIAL', 'GEOM', '4326'); + +* Perform a spatial query + + ij> SELECT ST_AsText(geom) FROM spatial s INNER JOIN table(HATBOX.MBR_INTERSECTS_ENV('APP', 'SPATIAL', -2, 2, -2, 2)) i on s.ID = i.HATBOX_JOIN_ID; # License diff --git a/app/pom.xml b/app/pom.xml index 014ca45..158edf1 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -5,10 +5,9 @@ org.opengeo geodb-parent - 0-SNAPSHOT + 0.9-SNAPSHOT - org.opengeo geodb-app jar GeoDB App diff --git a/core/pom.xml b/core/pom.xml index edd92bb..bf449cb 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -5,10 +5,9 @@ org.opengeo geodb-parent - 0-SNAPSHOT + 0.9-SNAPSHOT - org.opengeo geodb jar GeoDB Core @@ -35,6 +34,18 @@ 4.7 test + + org.apache.derby + derby + 10.10.1.1 + true + + + commons-io + commons-io + 2.4 + test + diff --git a/core/src/main/java/geodb/GeoDB.java b/core/src/main/java/geodb/GeoDB.java index 191a486..eb8409a 100644 --- a/core/src/main/java/geodb/GeoDB.java +++ b/core/src/main/java/geodb/GeoDB.java @@ -7,6 +7,7 @@ import java.io.InputStreamReader; import java.sql.Connection; import java.sql.DatabaseMetaData; +import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -89,16 +90,31 @@ public static String Version() { return "9"; } + // + // Database initializer + // + public static void InitGeoDBProc() throws SQLException { + final Connection connection = DriverManager.getConnection("jdbc:default:connection"); + InitGeoDB(connection); + } + // // Database initializer // public static void InitGeoDB(Connection cx) throws SQLException { try { + final String scriptSuffix; + final String geoDbTableName = getGeoDBTableName(cx); + if (isH2(cx)) { + scriptSuffix = "_h2"; + } else { + scriptSuffix = "_derby"; + } Statement st = cx.createStatement(); try { //first check if this database is already spatial and up to date try { - ResultSet rs = st.executeQuery("SELECT checksum FROM _GEODB"); + ResultSet rs = st.executeQuery("SELECT checksum FROM " + geoDbTableName); try { //table exists, check the checksum if (rs.next()) { @@ -117,23 +133,38 @@ public static void InitGeoDB(Connection cx) throws SQLException { catch (SQLException e) { //first time, continue with initialization } - - //load h2 functions + + //load h2/derby functions BufferedReader in = new BufferedReader( - new InputStreamReader(GeoDB.class.getResourceAsStream("geodb.sql"))); + new InputStreamReader(GeoDB.class.getResourceAsStream("geodb" + scriptSuffix + ".sql"))); String line = null; + String fullLine = ""; while((line = in.readLine()) != null) { - try { - st.execute(line); + line = line.trim(); + // Skip lines that start with a '#'. + if (line.startsWith("#") || line.length() == 0) { + continue; } - catch(SQLException e) { - //ignore + if (fullLine == null) { + fullLine = line; + } else { + fullLine += ' ' + line; + } + if (line.endsWith(";")) { + fullLine = fullLine.substring(0, fullLine.lastIndexOf(';')); + try { + CmdLine.LOGGER.log(Level.FINE, "Executing line : " + fullLine); + st.execute(fullLine); + } catch (SQLException e) { + // ignore + } + fullLine = null; } } in.close(); //load hatbox functions - List ddl = CmdLine.getddl("create_h2.sql"); + List ddl = CmdLine.getddl("create" + scriptSuffix + ".sql"); for (Iterator i = ddl.iterator(); i.hasNext(); ) { line = i.next(); try { @@ -144,15 +175,19 @@ public static void InitGeoDB(Connection cx) throws SQLException { } } - //create the _GEOH2 metadata table - st.execute("CREATE TABLE IF NOT EXISTS _GEODB (checksum VARCHAR)"); - st.execute("DELETE FROM _GEODB"); - st.execute("INSERT INTO _GEODB VALUES (" + Version() + ")" ); - + //create the GeoDB metadata table + if (!tableExists(cx, null, geoDbTableName, "TABLE")) { + st.execute("CREATE TABLE " + geoDbTableName + " (checksum VARCHAR(4))"); + } + st.execute("DELETE FROM " + geoDbTableName); + st.execute("INSERT INTO " + geoDbTableName + " VALUES ('" + Version() + "')" ); + //create the geometry columns table - st.execute("CREATE TABLE IF NOT EXISTS geometry_columns (f_table_schema VARCHAR, " + - "f_table_name VARCHAR, f_geometry_column VARCHAR, coord_dimension INT, " + - "srid INT, type VARCHAR(30))"); + if (!tableExists(cx, null, "geometry_columns", "TABLE")) { + st.execute("CREATE TABLE geometry_columns (f_table_schema VARCHAR(128), " + + "f_table_name VARCHAR(128), f_geometry_column VARCHAR(128), coord_dimension INT, " + + "srid INT, type VARCHAR(30))"); + } } finally { st.close(); @@ -162,10 +197,45 @@ public static void InitGeoDB(Connection cx) throws SQLException { throw (SQLException) new SQLException("Could not initialize database").initCause(e); } } - + + /** + * Returns the appropriate GeoDB table name for the given connection. + * + * @param cx + * the database connection. + * @return the GeoDB table name. + * @throws SQLException + * if unable to extract the database product name from the + * connection. + */ + public static String getGeoDBTableName(Connection cx) throws SQLException { + if (isH2(cx)) { + return "_GEODB"; + } else { + return "GEODB_"; + } + } + // // Management functions // + /** + * Adds a geometry column to a table. + * + * @param schema The table schema, may be null to specify default schema + * @param table The table name, not null + * @param column The geometry column name, not null + * @param srid The spatial reference system identifier + * @param type The geometry type, one of "POINT", "LINESTRING", "POLYGON", "MULTIPOINT", + * "MULTILINESTRING", "MULTIPOLYGON", "GEOMETRY", "GEOMETRYCOLLECTION" + * @param dim The geometry dimension + */ + public static void AddGeometryColumnProc(String schema, String table, + String column, int srid, String type, int dim) throws SQLException { + Connection cx = DriverManager.getConnection("jdbc:default:connection"); + AddGeometryColumn(cx, schema, table, column, srid, type, dim); + } + /** * Adds a geometry column to a table. * @@ -188,19 +258,35 @@ public static void AddGeometryColumn(Connection cx, String schema, String table, cx.getMetaData().getColumns(null, schema, table, column); try { if (!rs.next()) { - st.execute("ALTER TABLE " + tbl(schema, table) + " ADD " - + esc(column) + " " + type + " COMMENT '" + type + "'"); + StringBuilder sql = new StringBuilder(); + sql.append("ALTER TABLE ").append(tbl(schema, table)); + sql.append(" ADD COLUMN ").append(esc(column)).append(' ') + .append(getGeometryColumnType(cx, type)); + if (isH2(cx)) { + sql.append(" COMMENT '").append(getGeometryColumnType(cx, type)).append("'"); + } + st.execute(sql.toString()); } } finally { rs.close(); } - schema = schema != null ? schema : "PUBLIC"; + schema = schema != null ? schema : getDefaultSchema(cx); if (!"GEOMETRY".equals(type) && !"GEOMETRYCOLLECTION".equals(type)) { - st.execute("ALTER TABLE " + tbl(schema, table) + " ADD CONSTRAINT " + - esc(geotypeConstraint(schema,table,column)) + " CHECK " + esc(column) + - " IS NULL OR " + "GeometryType(" + esc(column) + ") = '" + type + "'"); + String sql = "ALTER TABLE " + tbl(schema, table) + + " ADD CONSTRAINT " + + esc(geotypeConstraint(schema, table, column)) + + " CHECK "; + if (!isH2(cx)) { + sql += '('; + } + sql += esc(column) + " IS NULL OR " + + "GeometryType(" + esc(column) + ") = '" + type + "'"; + if (!isH2(cx)) { + sql += ')'; + } + st.execute(sql); } st.execute("INSERT INTO geometry_columns VALUES (" + str(schema) + ", " + str(table) + ", " + str(column) + ", " + @@ -210,7 +296,27 @@ public static void AddGeometryColumn(Connection cx, String schema, String table, st.close(); } } - + + /** + * Returns the geometry column type that is appropriate for the current + * database. + * + * @param cx + * the database connection. + * @param type + * the default type. + * @return the database-specific type. + * @throws SQLException + * if unable to determine the current database. + */ + private static Object getGeometryColumnType(Connection cx, String type) + throws SQLException { + if (isH2(cx)) { + return type; + } + return "VARCHAR (32672) FOR BIT DATA"; + } + /** * Drops a geometry column from a table. * @@ -218,24 +324,56 @@ public static void AddGeometryColumn(Connection cx, String schema, String table, * @param table The table name, not null * @param column The geometry column name, not null */ + public static void DropGeometryColumnProc(String schema, String table, String column) + throws SQLException { + Connection cx = DriverManager.getConnection("jdbc:default:connection"); + DropGeometryColumn(cx, schema, table, column); + } + + /** + * Drops a geometry column from a table. + * + * @param cx The database connection + * @param schema The table schema, may be null to specify default schema + * @param table The table name, not null + * @param column The geometry column name, not null + */ public static void DropGeometryColumn(Connection cx, String schema, String table, String column) throws SQLException { Statement st = cx.createStatement(); try { //check the case of a view - boolean isView = false; - ResultSet tables = cx.getMetaData().getTables(null, schema, table, new String[]{"VIEW"}); - try { - isView = tables.next(); - } - finally { - tables.close(); + boolean isView = tableExists(cx, schema, table, "VIEW"); + + schema = schema != null ? schema : getDefaultSchema(cx); + final String constraintName = geotypeConstraint(schema,table,column); + boolean constraintExists = true; + String ifExistsClause = ""; + if (isH2(cx)) { + ifExistsClause = "IF EXISTS "; + } else { + // Query the Derby SYS schema to determine if the constraint already exists. + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM SYS.SYSSCHEMAS s INNER JOIN SYS.SYSTABLES t "); + sql.append("ON s.SCHEMANAME = ").append(str(schema)); + sql.append(" AND t.TABLENAME = ").append(str(table)); + sql.append(" AND s.SCHEMAID = t.SCHEMAID INNER JOIN SYS.SYSCONSTRAINTS c "); + sql.append("ON c.CONSTRAINTNAME = ").append(str(constraintName)); + sql.append(" AND c.TABLEID = t.TABLEID"); + ResultSet rs = st.executeQuery(sql.toString()); + try { + constraintExists = rs.next(); + } + finally { + rs.close(); + } } - schema = schema != null ? schema : "PUBLIC"; - st.execute("ALTER TABLE " + tbl(schema, table) + " DROP CONSTRAINT IF EXISTS " - + esc(geotypeConstraint(schema,table,column))); + if (constraintExists) { + st.execute("ALTER TABLE " + tbl(schema, table) + " DROP CONSTRAINT " + ifExistsClause + + esc(constraintName)); + } if (!isView) { st.execute("ALTER TABLE " + tbl(schema, table) + " DROP COLUMN " + esc(column)); @@ -255,10 +393,22 @@ public static void DropGeometryColumn(Connection cx, String schema, String table * @param schema The table schema, may be null to specify default schema * @param table The table name, not null */ + public static void DropGeometryColumnsProc(String schema, String table) throws SQLException { + Connection cx = DriverManager.getConnection("jdbc:default:connection"); + DropGeometryColumns(cx, schema, table); + } + + /** + * Drops all the geometry columns from a table. + * + * @param cx The database connection + * @param schema The table schema, may be null to specify default schema + * @param table The table name, not null + */ public static void DropGeometryColumns(Connection cx, String schema, String table) throws SQLException { Statement st = cx.createStatement(); try { - schema = schema != null ? schema : "PUBLIC"; + schema = schema != null ? schema : getDefaultSchema(cx); //look up the geometry column entries StringBuffer sql = new StringBuffer(); @@ -293,7 +443,7 @@ public static String ST_AsText( byte[] wkb ) { } /** - * Return the Well-Known Text (WKT) representation of the geometry with SRID meta data. + * Return the Extended Well-Known Text (EWKT) representation of the geometry with SRID meta data. */ public static String ST_AsEWKT( byte[] wkb ) { if ( wkb == null ) { @@ -303,9 +453,20 @@ public static String ST_AsEWKT( byte[] wkb ) { Geometry g = gFromWKB(wkb); return gToEWKT(g); } - + /** - * Return the Well-Known Binary (WKB) representation of the geometry with SRID meta data. + * Return the Well-Known Binary (WKB) representation of the geometry without SRID meta data. + */ + public static byte[] ST_AsBinary( byte[] wkb ) { + if (wkb == null) { + return null; + } + + return wkb; + } + + /** + * Return the Extended Well-Known Binary (EWKB) representation of the geometry with SRID meta data. */ public static byte[] ST_AsEWKB( byte[] wkb ) { return wkb; @@ -345,6 +506,10 @@ public static String ST_GeoHash( byte[] wkb ) { * Return a specified ST_Geometry value from Extended Well-Known Binary representation (EWKB). */ public static byte[] ST_GeomFromEWKB (byte[] wkb) { + if (wkb == null) { + return null; + } + return gToWKB(gFromWKB(wkb)); } @@ -1048,11 +1213,17 @@ public static byte[] ST_Union(byte[] wkb1, byte[] wkb2) { // // Management functions // + public static void CreateSpatialIndexProc( String schemaName, String tableName, + String columnName, String srid) throws SQLException { + final Connection connection = DriverManager.getConnection("jdbc:default:connection"); + CreateSpatialIndex(connection, schemaName, tableName, columnName, srid); + } + public static void CreateSpatialIndex( Connection cx, String schemaName, String tableName, String columnName, String srid) throws SQLException { HashMap args = new HashMap(); if (schemaName == null) { - schemaName = "PUBLIC"; + schemaName = getDefaultSchema(cx); } args.put("s", schemaName); @@ -1095,13 +1266,18 @@ public static void CreateSpatialIndex_GeoHash( Connection cx, String schemaName, st.close(); } } - + + public static void DropSpatialIndexProc( String schemaName, String tableName) throws SQLException { + final Connection connection = DriverManager.getConnection("jdbc:default:connection"); + DropSpatialIndex(connection, schemaName, tableName); + } + public static void DropSpatialIndex( Connection cx, String schemaName, String tableName) throws SQLException { HashMap args = new HashMap(); if (schemaName == null) { - schemaName = "PUBLIC"; + schemaName = getDefaultSchema(cx); } args.put("s", schemaName); @@ -1281,6 +1457,56 @@ public static int GetSRID( Connection cx, String schemaName, String tableName ) // // helper/utility functions // + + /** + * Determines if the current database is H2. + * @param cx the database connection. + * @return true if the database connection is to H2. + * @throws SQLException if unable to fetch the database metadata. + */ + public static boolean isH2(Connection cx) throws SQLException { + return "H2".equalsIgnoreCase(cx.getMetaData().getDatabaseProductName()); + } + + /** + * Returns the default schema name depending upon the type of database. + * @param cx the database connection. + * @return the default schema name. + * @throws SQLException if unable to fetch the database metadata. + */ + public static String getDefaultSchema(Connection cx) throws SQLException { + return isH2(cx) ? "PUBLIC" : cx.getMetaData().getUserName().toUpperCase(); + } + + /** + * Determines if the specified table exists. + * + * @param cx + * the database connection. + * @param schemaName + * the schema name or null. + * @param tableName + * the table name. + * @param types + * the array of table types (see + * {@link DatabaseMetaData#getTableTypes() getTableTypes()}) or + * null for all types. + * @return true if the table exists. + * @throws SQLException + * if unable to query the database. + */ + public static boolean tableExists(Connection cx, String schemaName, + String tableName, String... types) throws SQLException { + boolean exists = false; + ResultSet rs = cx.getMetaData().getTables(null, schemaName, tableName.toUpperCase(), types); + try { + exists = rs.next(); + } finally { + rs.close(); + } + return exists; + } + public static byte[] gToWKB( Geometry g ) { return wkbwriter().write( g ); } diff --git a/core/src/main/java/geodb/aggregate/GeoAggregateFunction.java b/core/src/main/java/geodb/aggregate/GeoAggregateFunction.java index 4ee509f..2489952 100644 --- a/core/src/main/java/geodb/aggregate/GeoAggregateFunction.java +++ b/core/src/main/java/geodb/aggregate/GeoAggregateFunction.java @@ -4,9 +4,12 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; +import java.sql.Connection; import java.sql.SQLException; import java.sql.Types; +import org.apache.derby.agg.Aggregator; import org.h2.api.AggregateFunction; import com.vividsolutions.jts.geom.Geometry; @@ -14,7 +17,7 @@ import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKBReader; -public abstract class GeoAggregateFunction implements AggregateFunction { +public abstract class GeoAggregateFunction implements AggregateFunction, Aggregator { private Geometry createGeometry(ByteArrayInputStream stream) { InputStreamInStream inputStreamInStream = new InputStreamInStream(stream); @@ -55,4 +58,44 @@ public final int getType(int[] arg0) throws SQLException { return Types.BLOB; } + /** + * @see org.apache.derby.agg.Aggregator#init() + */ + public void init() { + try { + init(null); + } catch (SQLException e) { + throw new IllegalStateException("Failed to initialize the function", e); + } + } + + /** + * @see org.apache.derby.agg.Aggregator#accumulate(java.lang.Object) + */ + public void accumulate(byte[] array) { + if (array != null) { + Geometry geometry = createGeometry(new ByteArrayInputStream(array)); + if (geometry != null) { + add(geometry); + } + } + } + + /** + * @see org.apache.derby.agg.Aggregator#merge(org.apache.derby.agg.Aggregator) + */ + public void merge(GeoAggregateFunction function) { + add(function.getGeometryResult()); + } + + /** + * @see org.apache.derby.agg.Aggregator#terminate() + */ + public byte[] terminate() { + try { + return (byte[])getResult(); + } catch (SQLException e) { + throw new IllegalStateException("Failed to get the function's result", e); + } + } } \ No newline at end of file diff --git a/core/src/main/resources/geodb/geodb.sql b/core/src/main/resources/geodb/geodb.sql deleted file mode 100644 index 5fea34a..0000000 --- a/core/src/main/resources/geodb/geodb.sql +++ /dev/null @@ -1,60 +0,0 @@ -CREATE ALIAS AddGeometryColumn for "geodb.GeoDB.AddGeometryColumn" -CREATE ALIAS CreateSpatialIndex for "geodb.GeoDB.CreateSpatialIndex" -CREATE ALIAS DropGeometryColumn for "geodb.GeoDB.DropGeometryColumn" -CREATE ALIAS DropGeometryColumns for "geodb.GeoDB.DropGeometryColumns" -CREATE ALIAS DropSpatialIndex for "geodb.GeoDB.DropSpatialIndex" -CREATE ALIAS EnvelopeAsText for "geodb.GeoDB.EnvelopeAsText" -CREATE ALIAS GeometryType for "geodb.GeoDB.GeometryType" -CREATE ALIAS ST_Area FOR "geodb.GeoDB.ST_Area" -CREATE ALIAS ST_AsEWKB FOR "geodb.GeoDB.ST_AsEWKB" -CREATE ALIAS ST_AsEWKT FOR "geodb.GeoDB.ST_AsEWKT" -CREATE ALIAS ST_AsHexEWKB FOR "geodb.GeoDB.ST_AsHexEWKB" -CREATE ALIAS ST_AsText FOR "geodb.GeoDB.ST_AsText" -CREATE ALIAS ST_BBOX FOR "geodb.GeoDB.ST_BBox" -CREATE ALIAS ST_Boundary FOR "geodb.GeoDB.ST_Boundary" -CREATE ALIAS ST_Buffer FOR "geodb.GeoDB.ST_Buffer" -CREATE ALIAS ST_Centroid FOR "geodb.GeoDB.ST_Centroid" -CREATE ALIAS ST_Crosses FOR "geodb.GeoDB.ST_Crosses" -CREATE ALIAS ST_Contains FOR "geodb.GeoDB.ST_Contains" -CREATE ALIAS ST_ConvexHull FOR "geodb.GeoDB.ST_ConvexHull" -CREATE ALIAS ST_DWithin FOR "geodb.GeoDB.ST_DWithin" -CREATE ALIAS ST_Disjoint FOR "geodb.GeoDB.ST_Disjoint" -CREATE ALIAS ST_Distance FOR "geodb.GeoDB.ST_Distance" -CREATE ALIAS ST_Difference FOR "geodb.GeoDB.ST_Difference" -CREATE ALIAS ST_Dimension FOR "geodb.GeoDB.ST_Dimension" -CREATE ALIAS ST_Envelope FOR "geodb.GeoDB.ST_Envelope" -CREATE ALIAS ST_Equals FOR "geodb.GeoDB.ST_Equals" -CREATE ALIAS ST_GeoHash FOR "geodb.GeoDB.ST_GeoHash" -CREATE ALIAS ST_GeomFromEWKB FOR "geodb.GeoDB.ST_GeomFromEWKB" -CREATE ALIAS ST_GeomFromEWKT FOR "geodb.GeoDB.ST_GeomFromEWKT" -CREATE ALIAS ST_GeomFromText FOR "geodb.GeoDB.ST_GeomFromText" -CREATE ALIAS ST_GeomFromWKB FOR "geodb.GeoDB.ST_GeomFromWKB" -CREATE ALIAS ST_Intersection FOR "geodb.GeoDB.ST_Intersection" -CREATE ALIAS ST_Intersects FOR "geodb.GeoDB.ST_Intersects" -CREATE ALIAS ST_IsEmpty FOR "geodb.GeoDB.ST_IsEmpty" -CREATE ALIAS ST_IsSimple FOR "geodb.GeoDB.ST_IsSimple" -CREATE ALIAS ST_IsValid FOR "geodb.GeoDB.ST_IsValid" -CREATE ALIAS ST_MakePoint FOR "geodb.GeoDB.ST_MakePoint" -CREATE ALIAS ST_MakeBox2D FOR "geodb.GeoDB.ST_MakeBox2D" -CREATE ALIAS ST_Overlaps FOR "geodb.GeoDB.ST_Overlaps" -CREATE ALIAS ST_Relate FOR "geodb.GeoDB.ST_Relate" -CREATE ALIAS ST_SRID FOR "geodb.GeoDB.ST_SRID" -CREATE ALIAS ST_X FOR "geodb.GeoDB.ST_X" -CREATE ALIAS ST_Y FOR "geodb.GeoDB.ST_Y" -CREATE ALIAS ST_SetSRID FOR "geodb.GeoDB.ST_SetSRID" -CREATE ALIAS ST_Simplify FOR "geodb.GeoDB.ST_Simplify" -CREATE ALIAS ST_SymDifference FOR "geodb.GeoDB.ST_SymDifference" -CREATE ALIAS ST_Touches FOR "geodb.GeoDB.ST_Touches" -CREATE ALIAS ST_Union FOR "geodb.GeoDB.ST_Union" -CREATE ALIAS ST_Within FOR "geodb.GeoDB.ST_Within" -CREATE ALIAS Version FOR "geodb.GeoDB.Version" -CREATE DOMAIN POINT AS BLOB -CREATE DOMAIN LINESTRING AS BLOB -CREATE DOMAIN POLYGON AS BLOB -CREATE DOMAIN MULTIPOINT AS BLOB -CREATE DOMAIN MULTILINESTRING AS BLOB -CREATE DOMAIN MULTIPOLYGON AS BLOB -CREATE DOMAIN GEOMETRYCOLLECTION AS BLOB -CREATE DOMAIN GEOMETRY AS BLOB -CREATE AGGREGATE ST_Extent FOR "geodb.aggregate.Extent" -CREATE AGGREGATE ST_Union_Aggregate FOR "geodb.aggregate.Union" diff --git a/core/src/main/resources/geodb/geodb_derby.sql b/core/src/main/resources/geodb/geodb_derby.sql new file mode 100644 index 0000000..f5f7cf4 --- /dev/null +++ b/core/src/main/resources/geodb/geodb_derby.sql @@ -0,0 +1,493 @@ +CREATE PROCEDURE AddGeometryColumn ( + IN SPATIAL_SCHEMA VARCHAR(128), + IN SPATIAL_TABLE VARCHAR(128), + IN SPATIAL_COLUMN VARCHAR(128), + IN SPATIAL_SRID INT, + IN SPATIAL_TYPE VARCHAR(128), + IN SPATIAL_DIM INT +) + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.AddGeometryColumnProc' + PARAMETER STYLE JAVA + MODIFIES SQL DATA; + +CREATE PROCEDURE CreateSpatialIndex ( + IN SPATIAL_SCHEMA VARCHAR(128), + IN SPATIAL_TABLE VARCHAR(128), + IN SPATIAL_COLUMN VARCHAR(128), + IN SPATIAL_SRID VARCHAR(16) +) + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.CreateSpatialIndexProc' + PARAMETER STYLE JAVA + MODIFIES SQL DATA; + +CREATE PROCEDURE DropGeometryColumn ( + IN SPATIAL_SCHEMA VARCHAR(128), + IN SPATIAL_TABLE VARCHAR(128), + IN SPATIAL_COLUMN VARCHAR(128) +) + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.DropGeometryColumnProc' + PARAMETER STYLE JAVA + MODIFIES SQL DATA; + +CREATE PROCEDURE DropGeometryColumns ( + IN SPATIAL_SCHEMA VARCHAR(128), + IN SPATIAL_TABLE VARCHAR(128) +) + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.DropGeometryColumnsProc' + PARAMETER STYLE JAVA + MODIFIES SQL DATA; + +CREATE PROCEDURE DropSpatialIndex ( + IN SPATIAL_SCHEMA VARCHAR(128), + IN SPATIAL_TABLE VARCHAR(128) +) + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.DropSpatialIndexProc' + PARAMETER STYLE JAVA + MODIFIES SQL DATA; + +CREATE FUNCTION EnvelopeAsText ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS VARCHAR(32672) + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.EnvelopeAsText' + PARAMETER STYLE JAVA; + +CREATE FUNCTION GeometryType ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS VARCHAR(32672) + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.GeometryType' + PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_Area ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS DOUBLE PRECISION + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Area' + PARAMETER STYLE JAVA + DETERMINISTIC; + +CREATE FUNCTION ST_AsEWKB ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_AsEWKB' + PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_AsEWKT ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS VARCHAR(32672) + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_AsEWKT' + PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_AsHexEWKB ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS VARCHAR(32672) + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_AsHexEWKB' + PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_AsText ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS VARCHAR(32672) + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_AsText' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_AsBinary ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_AsBinary' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_BBox ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS BOOLEAN + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_BBox' + PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_Boundary ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Boundary' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_Buffer ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA, + SPATIAL_DISTANCE DOUBLE PRECISION +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Buffer' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_Centroid ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Centroid' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_Crosses ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS BOOLEAN + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Crosses' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_Contains ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS BOOLEAN + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Contains' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_ConvexHull ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_ConvexHull' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_DWithin ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA, + SPATIAL_DISTANCE DOUBLE PRECISION +) RETURNS BOOLEAN + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_DWithin' + PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_Disjoint ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS BOOLEAN + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Disjoint' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_Distance ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS DOUBLE PRECISION + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Distance' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_Difference ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Difference' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_Dimension ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS SMALLINT + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Dimension' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_Envelope ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS VARCHAR(32672) + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Envelope' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_Equals ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS BOOLEAN + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Equals' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_GeoHash ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS VARCHAR(32672) + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_GeoHash' + PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_GeomFromEWKB ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_GeomFromEWKB' + PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_GeomFromEWKT ( + SPATIAL_WKT VARCHAR(32672) +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_GeomFromEWKT' + PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_GeomFromText ( + SPATIAL_WKT VARCHAR(32672), + SPATIAL_SRID INTEGER +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_GeomFromText' + PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_GeomFromWKB ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA, + SPATIAL_SRID INTEGER +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_GeomFromWKB' + PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_Intersection ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Intersection' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_Intersects ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS BOOLEAN + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Intersects' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_IsEmpty ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS BOOLEAN + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_IsEmpty' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_IsSimple ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS BOOLEAN + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_IsSimple' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_IsValid ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS BOOLEAN + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_IsValid' + PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_MakePoint ( + SPATIAL_X DOUBLE PRECISION, + SPATIAL_Y DOUBLE PRECISION +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_MakePoint' + PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_MakeBox2D ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_MakeBox2D' + PARAMETER STYLE JAVA; + +# Derby doesn't support function overloading. +#CREATE FUNCTION ST_MakeBox2D ( +# SPATIAL_X1 DOUBLE PRECISION, +# SPATIAL_Y1 DOUBLE PRECISION, +# SPATIAL_X2 DOUBLE PRECISION, +# SPATIAL_Y2 DOUBLE PRECISION +#) RETURNS LONG VARCHAR FOR BIT DATA +# LANGUAGE JAVA +# EXTERNAL NAME 'geodb.GeoDB.ST_MakeBox2D' +# PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_Overlaps ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS BOOLEAN + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Overlaps' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_Relate ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS VARCHAR(32672) + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Relate' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +# Derby doesn't support function overloading. +#CREATE FUNCTION ST_Relate ( +# SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, +# SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA, +# SPATIAL_INTERSECTION_PATTERN VARCHAR(32672) +#) RETURNS BOOLEAN +# LANGUAGE JAVA +# EXTERNAL NAME 'geodb.GeoDB.ST_Relate' +# PARAMETER STYLE JAVA +# DETERMINISTIC +# RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_SRID ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS INT + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_SRID' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_X ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS DOUBLE PRECISION + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_X' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_Y ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA +) RETURNS DOUBLE PRECISION + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Y' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_SetSRID ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA, + SPATIAL_SRID INT +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_SetSRID' + PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_Simplify ( + SPATIAL_WKB LONG VARCHAR FOR BIT DATA, + SPATIAL_DISTANCE DOUBLE PRECISION +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Simplify' + PARAMETER STYLE JAVA; + +CREATE FUNCTION ST_SymDifference ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_SymDifference' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_Touches ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS BOOLEAN + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Touches' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_Union ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS LONG VARCHAR FOR BIT DATA + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Union' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION ST_Within ( + SPATIAL_WKB1 LONG VARCHAR FOR BIT DATA, + SPATIAL_WKB2 LONG VARCHAR FOR BIT DATA +) RETURNS BOOLEAN + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.ST_Within' + PARAMETER STYLE JAVA + DETERMINISTIC + RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION Version () RETURNS VARCHAR(16) + LANGUAGE JAVA + EXTERNAL NAME 'geodb.GeoDB.Version' + PARAMETER STYLE JAVA; + +# Derby can't alias types. +#CREATE DOMAIN POINT AS BLOB +#CREATE DOMAIN LINESTRING AS BLOB +#CREATE DOMAIN POLYGON AS BLOB +#CREATE DOMAIN MULTIPOINT AS BLOB +#CREATE DOMAIN MULTILINESTRING AS BLOB +#CREATE DOMAIN MULTIPOLYGON AS BLOB +#CREATE DOMAIN GEOMETRYCOLLECTION AS BLOB +#CREATE DOMAIN GEOMETRY AS BLOB + +CREATE DERBY AGGREGATE ST_Extent + FOR LONG VARCHAR FOR BIT DATA + RETURNS LONG VARCHAR FOR BIT DATA + EXTERNAL NAME 'geodb.aggregate.Extent'; + +CREATE DERBY AGGREGATE ST_Union_Aggregate + FOR LONG VARCHAR FOR BIT DATA + RETURNS LONG VARCHAR FOR BIT DATA + EXTERNAL NAME 'geodb.aggregate.Union'; diff --git a/core/src/main/resources/geodb/geodb_h2.sql b/core/src/main/resources/geodb/geodb_h2.sql new file mode 100644 index 0000000..561dcf8 --- /dev/null +++ b/core/src/main/resources/geodb/geodb_h2.sql @@ -0,0 +1,61 @@ +CREATE ALIAS AddGeometryColumn for "geodb.GeoDB.AddGeometryColumn"; +CREATE ALIAS CreateSpatialIndex for "geodb.GeoDB.CreateSpatialIndex"; +CREATE ALIAS DropGeometryColumn for "geodb.GeoDB.DropGeometryColumn"; +CREATE ALIAS DropGeometryColumns for "geodb.GeoDB.DropGeometryColumns"; +CREATE ALIAS DropSpatialIndex for "geodb.GeoDB.DropSpatialIndex"; +CREATE ALIAS EnvelopeAsText for "geodb.GeoDB.EnvelopeAsText"; +CREATE ALIAS GeometryType for "geodb.GeoDB.GeometryType"; +CREATE ALIAS ST_Area FOR "geodb.GeoDB.ST_Area"; +CREATE ALIAS ST_AsEWKB FOR "geodb.GeoDB.ST_AsEWKB"; +CREATE ALIAS ST_AsEWKT FOR "geodb.GeoDB.ST_AsEWKT"; +CREATE ALIAS ST_AsHexEWKB FOR "geodb.GeoDB.ST_AsHexEWKB"; +CREATE ALIAS ST_AsText FOR "geodb.GeoDB.ST_AsText"; +CREATE ALIAS ST_AsBinary FOR "geodb.GeoDB.ST_AsBinary"; +CREATE ALIAS ST_BBOX FOR "geodb.GeoDB.ST_BBox"; +CREATE ALIAS ST_Boundary FOR "geodb.GeoDB.ST_Boundary"; +CREATE ALIAS ST_Buffer FOR "geodb.GeoDB.ST_Buffer"; +CREATE ALIAS ST_Centroid FOR "geodb.GeoDB.ST_Centroid"; +CREATE ALIAS ST_Crosses FOR "geodb.GeoDB.ST_Crosses"; +CREATE ALIAS ST_Contains FOR "geodb.GeoDB.ST_Contains"; +CREATE ALIAS ST_ConvexHull FOR "geodb.GeoDB.ST_ConvexHull"; +CREATE ALIAS ST_DWithin FOR "geodb.GeoDB.ST_DWithin"; +CREATE ALIAS ST_Disjoint FOR "geodb.GeoDB.ST_Disjoint"; +CREATE ALIAS ST_Distance FOR "geodb.GeoDB.ST_Distance"; +CREATE ALIAS ST_Difference FOR "geodb.GeoDB.ST_Difference"; +CREATE ALIAS ST_Dimension FOR "geodb.GeoDB.ST_Dimension"; +CREATE ALIAS ST_Envelope FOR "geodb.GeoDB.ST_Envelope"; +CREATE ALIAS ST_Equals FOR "geodb.GeoDB.ST_Equals"; +CREATE ALIAS ST_GeoHash FOR "geodb.GeoDB.ST_GeoHash"; +CREATE ALIAS ST_GeomFromEWKB FOR "geodb.GeoDB.ST_GeomFromEWKB"; +CREATE ALIAS ST_GeomFromEWKT FOR "geodb.GeoDB.ST_GeomFromEWKT"; +CREATE ALIAS ST_GeomFromText FOR "geodb.GeoDB.ST_GeomFromText"; +CREATE ALIAS ST_GeomFromWKB FOR "geodb.GeoDB.ST_GeomFromWKB"; +CREATE ALIAS ST_Intersection FOR "geodb.GeoDB.ST_Intersection"; +CREATE ALIAS ST_Intersects FOR "geodb.GeoDB.ST_Intersects"; +CREATE ALIAS ST_IsEmpty FOR "geodb.GeoDB.ST_IsEmpty"; +CREATE ALIAS ST_IsSimple FOR "geodb.GeoDB.ST_IsSimple"; +CREATE ALIAS ST_IsValid FOR "geodb.GeoDB.ST_IsValid"; +CREATE ALIAS ST_MakePoint FOR "geodb.GeoDB.ST_MakePoint"; +CREATE ALIAS ST_MakeBox2D FOR "geodb.GeoDB.ST_MakeBox2D"; +CREATE ALIAS ST_Overlaps FOR "geodb.GeoDB.ST_Overlaps"; +CREATE ALIAS ST_Relate FOR "geodb.GeoDB.ST_Relate"; +CREATE ALIAS ST_SRID FOR "geodb.GeoDB.ST_SRID"; +CREATE ALIAS ST_X FOR "geodb.GeoDB.ST_X"; +CREATE ALIAS ST_Y FOR "geodb.GeoDB.ST_Y"; +CREATE ALIAS ST_SetSRID FOR "geodb.GeoDB.ST_SetSRID"; +CREATE ALIAS ST_Simplify FOR "geodb.GeoDB.ST_Simplify"; +CREATE ALIAS ST_SymDifference FOR "geodb.GeoDB.ST_SymDifference"; +CREATE ALIAS ST_Touches FOR "geodb.GeoDB.ST_Touches"; +CREATE ALIAS ST_Union FOR "geodb.GeoDB.ST_Union"; +CREATE ALIAS ST_Within FOR "geodb.GeoDB.ST_Within"; +CREATE ALIAS Version FOR "geodb.GeoDB.Version"; +CREATE DOMAIN POINT AS BLOB; +CREATE DOMAIN LINESTRING AS BLOB; +CREATE DOMAIN POLYGON AS BLOB; +CREATE DOMAIN MULTIPOINT AS BLOB; +CREATE DOMAIN MULTILINESTRING AS BLOB; +CREATE DOMAIN MULTIPOLYGON AS BLOB; +CREATE DOMAIN GEOMETRYCOLLECTION AS BLOB; +CREATE DOMAIN GEOMETRY AS BLOB; +CREATE AGGREGATE ST_Extent FOR "geodb.aggregate.Extent"; +CREATE AGGREGATE ST_Union_Aggregate FOR "geodb.aggregate.Union"; diff --git a/core/src/test/java/geodb/DatabaseTestUtils.java b/core/src/test/java/geodb/DatabaseTestUtils.java new file mode 100644 index 0000000..2773a25 --- /dev/null +++ b/core/src/test/java/geodb/DatabaseTestUtils.java @@ -0,0 +1,72 @@ +package geodb; + +/** + * Implementations of DatabaseTestUtils provide the + * database-specific implementations of the methods in this interface. + */ +public interface DatabaseTestUtils { + /** + * Creates a new database with the given name. + * + * @param databaseName + * the database name. + * @throws Exception + * if anything goes wrong while creating the new database. + */ + void createDB(String databaseName) throws Exception; + + /** + * Destroys the database with the given name. The name is expected to be + * something like . + * + * @param databaseName + * the database name. + * @throws Exception + * if anything goes wrong while destroying the database. + */ + void destroyDB(String databaseName) throws Exception; + + /** + * Creates a test table with the given name. The ID column should be an + * integer type that is the primary key and have the + * auto-increment capability. + * + * @param tableName + * the table name. + * @param idColumnName + * the ID column name. + * @param geomColumnName + * the optional Geometry column name. + * @returns the CREATE TABLE SQL. + */ + String getCreateTestTableSql(String tableName, String idColumnName, + String geomColumnName); + + /** + * Drops the given table. + * + * @param tableName + * the table name. + * @return the DROP TABLE SQL. + */ + String getDropTableSql(String tableName); + + /** + * Indicates if a DROP TABLE statement drops the table if it + * exists. + * + * @return true if the {@link #dropTable(String)} SQL can be + * run even if the table does not exist. + */ + boolean isDropTableIfExistsSupported(); + + /** + * Creates the SQL to limit the number of results returned by a + * SELECT statement. + * + * @param limit + * the number of rows to fetch. + * @return the LIMIT clause to a SELECT statement. + */ + String getLimitClauseSql(int limit); +} \ No newline at end of file diff --git a/core/src/test/java/geodb/DerbyTestUtils.java b/core/src/test/java/geodb/DerbyTestUtils.java new file mode 100644 index 0000000..8b1448d --- /dev/null +++ b/core/src/test/java/geodb/DerbyTestUtils.java @@ -0,0 +1,72 @@ +package geodb; + +import java.io.File; +import java.sql.DriverManager; + +import org.apache.commons.io.FileUtils; + +/** + * DerbyTestUtils provides the Apache Derby-specific SQL and + * utility methods for unit testing. + */ +public class DerbyTestUtils implements DatabaseTestUtils { + /** + * @see geodb.DatabaseTestUtils#createDB(java.lang.String) + */ + public void createDB(String databaseName) throws Exception { + Class.forName("org.apache.derby.jdbc.EmbeddedDriver"); + DriverManager.getConnection( + "jdbc:derby:directory:target/" + databaseName + ";create=true") + .close(); + } + + /** + * @see geodb.DatabaseTestUtils#destroyDB(java.lang.String) + */ + public void destroyDB(String databaseName) throws Exception { + try { + DriverManager.getConnection( + "jdbc:derby:directory:target/" + databaseName + + ";shutdown=true").close(); + } catch (Exception ignore) { + } + FileUtils.deleteDirectory(new File("target/" + databaseName)); + } + + /** + * @see geodb.DatabaseTestUtils#getCreateTestTableSql(java.lang.String, + * java.lang.String, java.lang.String) + */ + public String getCreateTestTableSql(String tableName, String idColumnName, + String geomColumnName) { + StringBuilder sql = new StringBuilder("CREATE TABLE "); + sql.append(tableName).append(" ("); + sql.append(idColumnName) + .append(" INT NOT NULL GENERATED ALWAYS AS IDENTITY CONSTRAINT ") + .append(tableName.toUpperCase()).append("_PK PRIMARY KEY"); + if (geomColumnName != null) { + sql.append(", ").append(geomColumnName) + .append(" VARCHAR (32672) FOR BIT DATA"); + } + sql.append(')'); + return sql.toString(); + } + + /** + * @see geodb.DatabaseTestUtils#getDropTableSql(java.lang.String) + */ + public String getDropTableSql(String tableName) { + return "DROP TABLE " + tableName; + } + + /** + * @see geodb.DatabaseTestUtils#isDropTableIfExistsSupported() + */ + public boolean isDropTableIfExistsSupported() { + return false; + } + + public String getLimitClauseSql(int limit) { + return "FETCH FIRST " + limit + " ROWS ONLY"; + } +} diff --git a/core/src/test/java/geodb/GeoDBDerbyTest.java b/core/src/test/java/geodb/GeoDBDerbyTest.java new file mode 100644 index 0000000..d4642c2 --- /dev/null +++ b/core/src/test/java/geodb/GeoDBDerbyTest.java @@ -0,0 +1,65 @@ +package geodb; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +/** + * GeoDBDerbyTest runs the tests in {@link GeoDBTest} with Derby. + */ +public class GeoDBDerbyTest extends GeoDBTest { + /** The database connection instance. */ + protected Connection cx; + + /** The Derby test utilities. */ + private static DerbyTestUtils testUtils = new DerbyTestUtils(); + + @Override + protected Connection getConnection() { + return cx; + } + + @Override + protected DatabaseTestUtils getTestUtils() { + return testUtils; + } + + @Override + protected void createInitGeoDBProcedure(Statement st) throws SQLException { + // Ensure that the procedure does not already exist. + ResultSet rs = st.executeQuery("SELECT * FROM SYS.SYSALIASES a " + + "INNER JOIN SYS.SYSSCHEMAS s " + + "ON s.SCHEMANAME = CURRENT SCHEMA " + + "AND a.ALIAS = 'INITGEODB'"); + boolean procedureExists = rs.next(); + rs.close(); + if (!procedureExists) { + st.execute("CREATE PROCEDURE InitGeoDB () " + + "language java external name 'geodb.GeoDB.InitGeoDBProc' " + + "parameter style java modifies sql data"); + } + } + + @BeforeClass + public static void createDB() throws Exception { + testUtils.destroyDB(getDatabaseName()); + testUtils.createDB(getDatabaseName()); + } + + @AfterClass + public static void destroyDB() throws Exception { + testUtils.destroyDB(getDatabaseName()); + } + + @Before + public void setUp() throws Exception { + cx = DriverManager.getConnection("jdbc:derby:directory:target/" + getDatabaseName()); + super.setUp(); + } +} diff --git a/core/src/test/java/geodb/GeoDBExtraFunctionsDerbyTest.java b/core/src/test/java/geodb/GeoDBExtraFunctionsDerbyTest.java new file mode 100644 index 0000000..78b4bc6 --- /dev/null +++ b/core/src/test/java/geodb/GeoDBExtraFunctionsDerbyTest.java @@ -0,0 +1,48 @@ +package geodb; + +import java.sql.Connection; +import java.sql.DriverManager; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +/** + * GeoDBExtraFunctionsDerbyTest runs the tests in + * {@link GeoDBExtraFunctionsTest} with Derby. + */ +public class GeoDBExtraFunctionsDerbyTest extends GeoDBExtraFunctionsTest { + /** The database connection instance. */ + protected Connection cx; + + /** The Derby test utilities. */ + private static DerbyTestUtils testUtils = new DerbyTestUtils(); + + @Override + protected Connection getConnection() { + return cx; + } + + @Override + protected DatabaseTestUtils getTestUtils() { + return testUtils; + } + + @BeforeClass + public static void createDB() throws Exception { + testUtils.destroyDB(getDatabaseName()); + testUtils.createDB(getDatabaseName()); + } + + @AfterClass + public static void destroyDB() throws Exception { + testUtils.destroyDB(getDatabaseName()); + } + + @Before + public void setup() throws Exception { + cx = DriverManager.getConnection("jdbc:derby:directory:target/" + + getDatabaseName()); + super.setup(); + } +} diff --git a/core/src/test/java/geodb/GeoDBExtraFunctionsH2Test.java b/core/src/test/java/geodb/GeoDBExtraFunctionsH2Test.java new file mode 100644 index 0000000..fbe6bcc --- /dev/null +++ b/core/src/test/java/geodb/GeoDBExtraFunctionsH2Test.java @@ -0,0 +1,64 @@ +package geodb; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.vividsolutions.jts.io.ParseException; + +/** + * GeoDBExtraFunctionsH2Test runs the tests in + * {@link GeoDBExtraFunctionsTest} with H2. + */ +public class GeoDBExtraFunctionsH2Test extends GeoDBExtraFunctionsTest { + /** The H2 test utilities. */ + private static final H2TestUtils TEST_UTILS = new H2TestUtils(); + + /** The H2 database connection instance. */ + private Connection cx; + + @Override + protected Connection getConnection() { + return cx; + } + + @Override + protected DatabaseTestUtils getTestUtils() { + return TEST_UTILS; + } + + @BeforeClass + @AfterClass + public static void destroyDB() throws Exception { + TEST_UTILS.destroyDB(getDatabaseName()); + } + + @Before + public void setup() throws Exception { + Class.forName("org.h2.Driver"); + cx = DriverManager.getConnection("jdbc:h2:target/" + getDatabaseName()); + super.setup(); + } + + @Test + public void testRelateWithMatrix() throws SQLException, IOException, ParseException { + Connection cx = getConnection(); + Statement st = cx.createStatement(); + ResultSet rs = st.executeQuery("CALL ST_Relate(ST_GeomFromText('POINT(1 2)',4326), ST_Buffer(ST_GeomFromText('POINT(1 2)',4326),2), '0FFFFFFF2')"); + rs.next(); + boolean result = rs.getBoolean(1); + st.close(); + //I don't really understand this function so not sure if the result is correct. At least the result of both tests seems consistent. + assertTrue(result); + } +} diff --git a/core/src/test/java/geodb/GeoDBExtraFunctionsTest.java b/core/src/test/java/geodb/GeoDBExtraFunctionsTest.java index 60706b3..c38ee9c 100644 --- a/core/src/test/java/geodb/GeoDBExtraFunctionsTest.java +++ b/core/src/test/java/geodb/GeoDBExtraFunctionsTest.java @@ -1,33 +1,46 @@ package geodb; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; import java.io.IOException; import java.io.InputStream; +import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; +import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.Point; +import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.io.InputStreamInStream; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKBReader; +import com.vividsolutions.jts.io.WKTReader; -public class GeoDBExtraFunctionsTest extends GeoDBTestSupport { +public abstract class GeoDBExtraFunctionsTest extends GeoDBTestSupport { + /** + * The name of the test database. + * + * @return the database name. + */ + public static String getDatabaseName() { + return "geodb_extra_functions"; + } @Before public void setup() throws Exception { - super.setUp(); + Connection cx = getConnection(); Statement st = cx.createStatement(); - st.execute("DROP TABLE IF EXISTS _GEODB"); - st.execute("DROP TABLE IF EXISTS spatial"); - st.execute("DROP TABLE IF EXISTS spatial_hatbox"); - st.execute("DROP TABLE IF EXISTS noindex"); + dropTable(st, GeoDB.getGeoDBTableName(cx)); + dropTable(st, "spatial"); + dropTable(st, "spatial_hatbox"); + dropTable(st, "noindex"); st.close(); GeoDB.InitGeoDB(cx); } @@ -36,6 +49,7 @@ public void setup() throws Exception { @Test public void testDimension() throws SQLException, IOException, ParseException { insertThreePoints(); + Connection cx = getConnection(); Statement st = cx.createStatement(); ResultSet rs = st.executeQuery("select st_dimension(geom) from spatial"); rs.next(); @@ -47,6 +61,7 @@ public void testDimension() throws SQLException, IOException, ParseException { @Test public void testBoundary() throws SQLException, IOException, ParseException { insertThreePoints(); + Connection cx = getConnection(); Statement st = cx.createStatement(); ResultSet rs = st.executeQuery("select st_boundary(geom) from spatial"); rs.next(); @@ -58,8 +73,9 @@ public void testBoundary() throws SQLException, IOException, ParseException { @Test public void testRelate() throws SQLException, IOException, ParseException { + Connection cx = getConnection(); Statement st = cx.createStatement(); - ResultSet rs = st.executeQuery("SELECT ST_Relate(ST_GeomFromText('POINT(1 2)',4326), ST_Buffer(ST_GeomFromText('POINT(1 2)',4326),2))"); + ResultSet rs = st.executeQuery("VALUES ST_Relate(ST_GeomFromText('POINT(1 2)',4326), ST_Buffer(ST_GeomFromText('POINT(1 2)',4326),2))"); rs.next(); String result = rs.getString(1); st.close(); @@ -67,20 +83,10 @@ public void testRelate() throws SQLException, IOException, ParseException { assertThat(result, is("0FFFFFFF2")); } - @Test - public void testRelateWithMatrix() throws SQLException, IOException, ParseException { - Statement st = cx.createStatement(); - ResultSet rs = st.executeQuery("SELECT ST_Relate(ST_GeomFromText('POINT(1 2)',4326), ST_Buffer(ST_GeomFromText('POINT(1 2)',4326),2), '0FFFFFFF2')"); - rs.next(); - boolean result = rs.getBoolean(1); - st.close(); - //I don't really understand this function so not sure if the result is correct. At least the result of both tests seems consistent. - assertTrue(result); - } - @Test public void testConvexHull() throws SQLException, IOException, ParseException { insertThreePoints(); + Connection cx = getConnection(); Statement st = cx.createStatement(); ResultSet rs = st.executeQuery("select st_convexhull(geom) from spatial"); rs.next(); @@ -90,11 +96,11 @@ public void testConvexHull() throws SQLException, IOException, ParseException { assertThat(geometry.getArea(), is(0.0)); } - @Test public void testDifference() throws SQLException, IOException, ParseException { + Connection cx = getConnection(); Statement st = cx.createStatement(); - ResultSet rs = st.executeQuery("select ST_Difference(" + + ResultSet rs = st.executeQuery("VALUES ST_Difference(" + "ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 4326)," + "ST_GeomFromText('POLYGON((5 5, 5 10, 10 10, 10 5, 5 5))', 4326))"); rs.next(); @@ -106,8 +112,9 @@ public void testDifference() throws SQLException, IOException, ParseException { @Test public void testIntersection() throws SQLException, IOException, ParseException { + Connection cx = getConnection(); Statement st = cx.createStatement(); - ResultSet rs = st.executeQuery("select ST_Intersection(" + + ResultSet rs = st.executeQuery("VALUES ST_Intersection(" + "ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 4326)," + "ST_GeomFromText('POLYGON((5 5, 5 10, 10 10, 10 5, 5 5))', 4326))"); rs.next(); @@ -119,8 +126,9 @@ public void testIntersection() throws SQLException, IOException, ParseException @Test public void testSymdifference() throws SQLException, IOException, ParseException { + Connection cx = getConnection(); Statement st = cx.createStatement(); - ResultSet rs = st.executeQuery("select ST_SymDifference(" + + ResultSet rs = st.executeQuery("VALUES ST_SymDifference(" + "ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 4326)," + "ST_GeomFromText('POLYGON((5 5, 5 15, 10 15, 10 5, 5 5))', 4326))"); rs.next(); @@ -133,8 +141,9 @@ public void testSymdifference() throws SQLException, IOException, ParseException @Test public void testUnion() throws SQLException, IOException, ParseException { + Connection cx = getConnection(); Statement st = cx.createStatement(); - ResultSet rs = st.executeQuery("select ST_Union(" + + ResultSet rs = st.executeQuery("VALUES ST_Union(" + "ST_GeomFromText('POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))', 4326)," + "ST_GeomFromText('POLYGON((5 5, 5 15, 10 15, 10 5, 5 5))', 4326))"); rs.next(); @@ -145,14 +154,530 @@ public void testUnion() throws SQLException, IOException, ParseException { } private void insertThreePoints() throws SQLException{ + Connection cx = getConnection(); Statement st = cx.createStatement(); - st.execute("CREATE TABLE spatial (id INT AUTO_INCREMENT PRIMARY KEY, geom BLOB)"); + createTable(st, "spatial", "id", "geom"); st.execute("INSERT INTO spatial (geom) VALUES (ST_GeomFromText('POINT(0 0)', 4326))"); st.execute("INSERT INTO spatial (geom) VALUES (ST_GeomFromText('POINT(1 1)', 4326))"); st.execute("INSERT INTO spatial (geom) VALUES (ST_GeomFromText('POINT(2 2)', 4326))"); st.close(); } - - + @Test + public void testArea() throws SQLException, IOException, ParseException { + Connection cx = getConnection(); + Geometry original = GeoDB.gFromWKT( + "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))", 4326); + Statement st = cx.createStatement(); + ResultSet rs = st.executeQuery("VALUES ST_Area(ST_GeomFromText('" + + original.toText() + "', 4326))"); + rs.next(); + double area = rs.getDouble(1); + rs.close(); + assertEquals(original.getArea(), area, 0.0001); + + rs = st.executeQuery("VALUES ST_Area(null)"); + rs.next(); + area = rs.getDouble(1); + assertEquals(-1, area, 0.0001); + rs.close(); + + st.close(); + } + + @Test + public void testCentroid() throws SQLException, IOException, ParseException { + Connection cx = getConnection(); + Statement st = cx.createStatement(); + ResultSet rs = st + .executeQuery("VALUES ST_Centroid(" + + "ST_GeomFromText('MULTIPOINT ( -1 0, -1 2, -1 3, -1 4, -1 7, 0 1, 0 3, 1 1, 2 0, 6 0, 7 8, 9 8, 10 6 )', 4326))"); + rs.next(); + InputStream binaryStream = rs.getBinaryStream(1); + Geometry geometry = new WKBReader().read(new InputStreamInStream(binaryStream)); + rs.close(); + assertTrue(geometry instanceof Point); + Point point = (Point)geometry; + assertEquals(2.30769230769231, point.getX(), 0.00000000000001); + assertEquals(3.30769230769231, point.getY(), 0.00000000000001); + + rs = st.executeQuery("VALUES ST_Centroid(null)"); + rs.next(); + binaryStream = rs.getBinaryStream(1); + rs.close(); + assertNull(binaryStream); + + st.close(); + } + + @Test + public void testSimplify() throws SQLException, IOException, ParseException { + Connection cx = getConnection(); + Statement st = cx.createStatement(); + ResultSet rs = st + .executeQuery("VALUES ST_Simplify(" + + "ST_GeomFromText('POLYGON((0 0, 0 1, 1 1, 1 0, 0.9 0, 0 0))', 4326), .1)"); + rs.next(); + InputStream binaryStream = rs.getBinaryStream(1); + Geometry geometry = new WKBReader().read(new InputStreamInStream(binaryStream)); + rs.close(); + assertTrue(geometry instanceof Polygon); + Polygon polygon = (Polygon)geometry; + assertEquals(5, polygon.getExteriorRing().getNumPoints()); + + st.close(); + } + + @Test + public void testMakePoint() throws SQLException, IOException, ParseException { + Connection cx = getConnection(); + Statement st = cx.createStatement(); + ResultSet rs = st + .executeQuery("VALUES ST_MakePoint(1, 2)"); + rs.next(); + InputStream binaryStream = rs.getBinaryStream(1); + Geometry geometry = new WKBReader().read(new InputStreamInStream(binaryStream)); + rs.close(); + assertTrue(geometry instanceof Point); + Point point = (Point)geometry; + assertEquals(1, point.getX(), 0.00000000000001); + assertEquals(2, point.getY(), 0.00000000000001); + assertEquals(0, point.getSRID()); + + st.close(); + } + + // TODO: ST_MakeBox2D does not create a JTS-formatted WKB. + @Ignore + public void testMakeBox2D() throws SQLException, IOException, ParseException { + Connection cx = getConnection(); + Statement st = cx.createStatement(); + ResultSet rs = st + .executeQuery("VALUES ST_MakeBox2D(" + + "ST_GeomFromText('POINT(0 0)', 4326)," + + "ST_GeomFromText('POINT(1 1)', 4326))"); + rs.next(); + InputStream binaryStream = rs.getBinaryStream(1); + Geometry geometry = new WKBReader().read(new InputStreamInStream(binaryStream)); + rs.close(); + Envelope envelope = geometry.getEnvelopeInternal(); + assertEquals(0, envelope.getMinX(), 0.00000000000001); + assertEquals(0, envelope.getMinY(), 0.00000000000001); + assertEquals(1, envelope.getMaxX(), 0.00000000000001); + assertEquals(1, envelope.getMaxY(), 0.00000000000001); + + st.close(); + } + + @Test + public void testSetSRID() throws SQLException, IOException, ParseException { + Connection cx = getConnection(); + Statement st = cx.createStatement(); + ResultSet rs = st + .executeQuery("VALUES ST_SetSRID(" + + "ST_GeomFromText('POINT(-123.365556 48.428611)', 4326), 3785)"); + rs.next(); + InputStream binaryStream = rs.getBinaryStream(1); + Geometry geometry = new WKBReader().read(new InputStreamInStream(binaryStream)); + rs.close(); + assertEquals(3785, geometry.getSRID()); + + st.close(); + } + + @Test + public void testEmpty() throws SQLException, IOException, ParseException { + testBooleanPredicate("ST_IsEmpty", + "GEOMETRYCOLLECTION EMPTY", + "POLYGON((1 2, 3 4, 5 6, 1 2))"); + } + + @Test + public void testSimple() throws SQLException, IOException, ParseException { + testBooleanPredicate("ST_IsSimple", + "POLYGON((1 2, 3 4, 5 6, 1 2))", + "LINESTRING(1 1,2 2,2 3.5,1 3,1 2,2 1)"); + } + + @Test + public void testValid() throws SQLException, IOException, ParseException { + testBooleanPredicate("ST_IsValid", + "LINESTRING(0 0, 1 1)", + "POLYGON((0 0, 1 1, 1 2, 1 1, 0 0))"); + } + + /** + * Tests a predicate that takes one geometry as an argument and returns a + * boolean value. + * + * @param predicate + * the predicate to test. + * @param wktPass + * the WKT that should pass. + * @param wktFail + * the WKT that should fail. + * @throws SQLException + * if unable to run the test in the database. + */ + private void testBooleanPredicate(final String predicate, + final String wktPass, final String wktFail) throws SQLException { + Connection cx = getConnection(); + Statement st = cx.createStatement(); + + // Test the passing condition. + ResultSet rs = st.executeQuery("VALUES " + predicate + "(" + + "ST_GeomFromText('" + wktPass + "', 4326))"); + rs.next(); + boolean result = rs.getBoolean(1); + rs.close(); + assertTrue(result); + + // Test the failing condition. + rs = st.executeQuery("VALUES " + predicate + "(" + "ST_GeomFromText('" + + wktFail + "', 4326))"); + rs.next(); + result = rs.getBoolean(1); + rs.close(); + assertFalse(result); + + // Test the null argument. + rs = st.executeQuery("VALUES " + predicate + "(null)"); + rs.next(); + result = rs.getBoolean(1); + rs.close(); + assertFalse(result); + + st.close(); + } + + @Test + public void testContains() throws SQLException, IOException, ParseException { + testBooleanPredicate("ST_Contains", + "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))", + "POINT(5 5)", + "POINT(15 15)"); + } + + @Test + public void testCrosses() throws SQLException, IOException, ParseException { + testBooleanPredicate("ST_Crosses", + "LINESTRING(0 -2, 0 2)", + "LINESTRING(-2 0, 2 0)", + "POINT(1 1)"); + } + + @Test + public void testDisjoint() throws SQLException, IOException, ParseException { + testBooleanPredicate("ST_Disjoint", + "POINT(0 0)", + "LINESTRING ( 2 0, 0 2 )", + "LINESTRING ( 0 0, 0 2 )"); + } + + @Test + public void testEquals() throws SQLException, IOException, ParseException { + testBooleanPredicate("ST_Equals", + "LINESTRING(0 0, 10 10)", + "LINESTRING(0 0, 5 5, 10 10)", + "LINESTRING(0 0, 5 5, 11 11)"); + } + + @Test + public void testIntersects() throws SQLException, IOException, ParseException { + testBooleanPredicate("ST_Intersects", + "POINT(0 0)", + "LINESTRING ( 0 0, 0 2 )", + "LINESTRING ( 2 0, 0 2 )"); + } + + @Test + public void testOverlaps() throws SQLException, IOException, ParseException { + testBooleanPredicate("ST_Overlaps", + "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))", + "POLYGON((1 1, 1 11, 11 11, 11 1, 1 1))", + "POLYGON((1 1, 1 9, 9 9, 9 1, 1 1))"); + } + + @Test + public void testTouches() throws SQLException, IOException, ParseException { + testBooleanPredicate("ST_Touches", + "LINESTRING(0 0, 1 1, 0 2)", + "POINT(0 2)", + "POINT(1 1)"); + } + + @Test + public void testWithin() throws SQLException, IOException, ParseException { + testBooleanPredicate("ST_Within", + "POLYGON((1 1, 1 9, 9 9, 9 1, 1 1))", + "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))", + "POLYGON((2 2, 1 11, 11 11, 11 1, 2 2))"); + } + + /** + * Tests a predicate that takes two geometries as arguments and returns a + * boolean value. + * + * @param predicate + * the predicate to test. + * @param wktA + * the WKT of the first geometry. + * @param wktBPass + * the WKT of the second geometry that should pass. + * @param wktBFail + * the WKT of the second geometry that should fail. + * @throws SQLException + * if unable to run the test in the database. + */ + private void testBooleanPredicate(final String predicate, + final String wktA, final String wktBPass, final String wktBFail) + throws SQLException { + Connection cx = getConnection(); + Statement st = cx.createStatement(); + + // Test the passing condition. + ResultSet rs = st.executeQuery("VALUES " + predicate + "(" + + "ST_GeomFromText('" + wktA + "', 4326)," + + "ST_GeomFromText('" + wktBPass + "', 4326))"); + rs.next(); + boolean result = rs.getBoolean(1); + rs.close(); + assertTrue(result); + + // Test the failing condition. + rs = st.executeQuery("VALUES " + predicate + "(" + "ST_GeomFromText('" + + wktA + "', 4326)," + "ST_GeomFromText('" + wktBFail + + "', 4326))"); + rs.next(); + result = rs.getBoolean(1); + rs.close(); + assertFalse(result); + + // Test the first null. + rs = st.executeQuery("VALUES " + predicate + "(null," + + "ST_GeomFromText('" + wktBPass + "', 4326))"); + rs.next(); + result = rs.getBoolean(1); + rs.close(); + assertFalse(result); + + // Test the second null. + rs = st.executeQuery("VALUES " + predicate + "(" + "ST_GeomFromText('" + + wktA + "', 4326), null)"); + rs.next(); + result = rs.getBoolean(1); + rs.close(); + assertFalse(result); + + st.close(); + } + + @Test + public void testDWithin() throws SQLException, IOException, ParseException { + final String predicate = "ST_DWithin"; + final String wktA = "POINT(0 0)"; + final String wktBPass = "POINT(7 7)"; + final String wktBFail = "POINT(8 8)"; + + Connection cx = getConnection(); + Statement st = cx.createStatement(); + + // Test the passing condition. + ResultSet rs = st.executeQuery("VALUES " + predicate + "(" + + "ST_GeomFromText('" + wktA + "', 4326)," + + "ST_GeomFromText('" + wktBPass + "', 4326), 10)"); + rs.next(); + boolean result = rs.getBoolean(1); + rs.close(); + assertTrue(result); + + // Test the failing condition. + rs = st.executeQuery("VALUES " + predicate + "(" + "ST_GeomFromText('" + + wktA + "', 4326)," + "ST_GeomFromText('" + wktBFail + + "', 4326), 10)"); + rs.next(); + result = rs.getBoolean(1); + rs.close(); + assertFalse(result); + + // Test the first null. + rs = st.executeQuery("VALUES " + predicate + "(null," + + "ST_GeomFromText('" + wktBPass + "', 4326), 10)"); + rs.next(); + result = rs.getBoolean(1); + rs.close(); + assertFalse(result); + + // Test the second null. + rs = st.executeQuery("VALUES " + predicate + "(" + "ST_GeomFromText('" + + wktA + "', 4326), null, 10)"); + rs.next(); + result = rs.getBoolean(1); + rs.close(); + assertFalse(result); + + st.close(); + } + + @Test + public void testWKB() throws SQLException, IOException, ParseException { + String wkt = "POLYGON((743238 2967416,743238 2967450, 743265 2967450,743265.625 2967416,743238 2967416))"; + int srid = 2249; + Geometry original = new WKTReader().read(wkt); + original.setSRID(srid); + Connection cx = getConnection(); + Statement st = cx.createStatement(); + + ResultSet rs = st + .executeQuery("VALUES ST_GeomFromWKB(ST_AsBinary(ST_GeomFromText('" + + wkt + "', " + srid + ")), " + srid + ")"); + rs.next(); + InputStream binaryStream = rs.getBinaryStream(1); + Geometry geometry = new WKBReader().read(new InputStreamInStream( + binaryStream)); + binaryStream.close(); + rs.close(); + assertEquals(original, geometry); + assertEquals(original.getSRID(), geometry.getSRID()); + + rs = st.executeQuery("VALUES ST_AsBinary(null)"); + rs.next(); + binaryStream = rs.getBinaryStream(1); + rs.close(); + assertNull(binaryStream); + + rs = st.executeQuery("VALUES ST_GeomFromWKB(null, " + srid + ")"); + rs.next(); + binaryStream = rs.getBinaryStream(1); + rs.close(); + assertNull(binaryStream); + + st.close(); + } + + @Test + public void testEWKB() throws SQLException, IOException, ParseException { + String wkt = "POLYGON((743238 2967416,743238 2967450, 743265 2967450,743265.625 2967416,743238 2967416))"; + int srid = 2249; + Geometry original = new WKTReader().read(wkt); + original.setSRID(srid); + Connection cx = getConnection(); + Statement st = cx.createStatement(); + + ResultSet rs = st + .executeQuery("VALUES ST_GeomFromEWKB(ST_AsEWKB(ST_GeomFromText('" + + wkt + "', " + srid + ")))"); + rs.next(); + InputStream binaryStream = rs.getBinaryStream(1); + Geometry geometry = new WKBReader().read(new InputStreamInStream( + binaryStream)); + binaryStream.close(); + rs.close(); + assertEquals(original, geometry); + assertEquals(original.getSRID(), geometry.getSRID()); + + rs = st.executeQuery("VALUES ST_AsEWKB(null)"); + rs.next(); + binaryStream = rs.getBinaryStream(1); + rs.close(); + assertNull(binaryStream); + + rs = st.executeQuery("VALUES ST_GeomFromEWKB(null)"); + rs.next(); + binaryStream = rs.getBinaryStream(1); + rs.close(); + assertNull(binaryStream); + + st.close(); + } + + @Test + public void testWKT() throws SQLException, IOException, ParseException { + String wkt = "POLYGON((743238 2967416,743238 2967450, 743265 2967450,743265.625 2967416,743238 2967416))"; + int srid = 2249; + Geometry original = new WKTReader().read(wkt); + original.setSRID(srid); + Connection cx = getConnection(); + Statement st = cx.createStatement(); + + // Ensure that the EWKT matches the expected pattern. + ResultSet rs = st.executeQuery("VALUES ST_AsText(ST_GeomFromText('" + + wkt + "', " + srid + "))"); + rs.next(); + String ewkt = rs.getString(1); + rs.close(); + String pattern = "^POLYGON[\\s]*[(][(][\\d\\s.,]+[)][)]$"; + assertTrue("'" + ewkt + "' does not match the pattern " + pattern, + ewkt.matches(pattern)); + + rs = st.executeQuery("VALUES ST_GeomFromText(ST_AsText(ST_GeomFromText('" + + wkt + "', " + srid + ")), " + srid + ")"); + rs.next(); + InputStream binaryStream = rs.getBinaryStream(1); + Geometry geometry = new WKBReader().read(new InputStreamInStream( + binaryStream)); + binaryStream.close(); + rs.close(); + assertEquals(original, geometry); + assertEquals(original.getSRID(), geometry.getSRID()); + + rs = st.executeQuery("VALUES ST_AsText(null)"); + rs.next(); + wkt = rs.getString(1); + rs.close(); + assertNull(wkt); + + rs = st.executeQuery("VALUES ST_GeomFromText(null, " + srid + ")"); + rs.next(); + binaryStream = rs.getBinaryStream(1); + rs.close(); + assertNull(binaryStream); + + st.close(); + } + + @Test + public void testEWKT() throws SQLException, IOException, ParseException { + String wkt = "POLYGON((743238 2967416,743238 2967450, 743265 2967450,743265.625 2967416,743238 2967416))"; + int srid = 2249; + Geometry original = new WKTReader().read(wkt); + original.setSRID(srid); + Connection cx = getConnection(); + Statement st = cx.createStatement(); + + // Ensure that the EWKT matches the expected pattern. + ResultSet rs = st.executeQuery("VALUES ST_AsEWKT(ST_GeomFromText('" + + wkt + "', " + srid + "))"); + rs.next(); + String ewkt = rs.getString(1); + rs.close(); + String pattern = "^SRID=" + srid + + ";POLYGON[\\s]*[(][(][\\d\\s.,]+[)][)]$"; + assertTrue("'" + ewkt + "' does not match the pattern " + pattern, + ewkt.matches(pattern)); + + rs = st.executeQuery("VALUES ST_GeomFromEWKT(ST_AsEWKT(ST_GeomFromText('" + + wkt + "', " + srid + ")))"); + rs.next(); + InputStream binaryStream = rs.getBinaryStream(1); + Geometry geometry = new WKBReader().read(new InputStreamInStream( + binaryStream)); + rs.close(); + assertEquals(original, geometry); + assertEquals(original.getSRID(), geometry.getSRID()); + + rs = st.executeQuery("VALUES ST_AsEWKT(null)"); + rs.next(); + ewkt = rs.getString(1); + rs.close(); + assertNull(ewkt); + + rs = st.executeQuery("VALUES ST_GeomFromEWKT(null)"); + rs.next(); + binaryStream = rs.getBinaryStream(1); + rs.close(); + assertNull(binaryStream); + + st.close(); + } } diff --git a/core/src/test/java/geodb/GeoDBFunctionDerbyTest.java b/core/src/test/java/geodb/GeoDBFunctionDerbyTest.java new file mode 100644 index 0000000..6aac7b7 --- /dev/null +++ b/core/src/test/java/geodb/GeoDBFunctionDerbyTest.java @@ -0,0 +1,48 @@ +package geodb; + +import java.sql.Connection; +import java.sql.DriverManager; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +/** + * GeoDBFunctionDerbyTest runs the tests in + * {@link GeoDBFunctionTest} with Derby. + */ +public class GeoDBFunctionDerbyTest extends GeoDBFunctionTest { + /** The database connection instance. */ + protected Connection cx; + + /** The Derby test utilities. */ + private static DerbyTestUtils testUtils = new DerbyTestUtils(); + + @Override + protected Connection getConnection() { + return cx; + } + + @Override + protected DatabaseTestUtils getTestUtils() { + return testUtils; + } + + @BeforeClass + public static void createDB() throws Exception { + testUtils.destroyDB(getDatabaseName()); + testUtils.createDB(getDatabaseName()); + } + + @AfterClass + public static void destroyDB() throws Exception { + testUtils.destroyDB(getDatabaseName()); + } + + @Before + public void setUp() throws Exception { + cx = DriverManager.getConnection("jdbc:derby:directory:target/" + + getDatabaseName()); + super.setUp(); + } +} diff --git a/core/src/test/java/geodb/GeoDBFunctionH2Test.java b/core/src/test/java/geodb/GeoDBFunctionH2Test.java new file mode 100644 index 0000000..7b0ee5d --- /dev/null +++ b/core/src/test/java/geodb/GeoDBFunctionH2Test.java @@ -0,0 +1,43 @@ +package geodb; + +import java.sql.Connection; +import java.sql.DriverManager; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +/** + * GeoDBFunctionH2Test runs the tests in {@link GeoDBFunctionTest} + * with H2. + */ +public class GeoDBFunctionH2Test extends GeoDBFunctionTest { + /** The H2 test utilities. */ + private static final H2TestUtils TEST_UTILS = new H2TestUtils(); + + /** The H2 database connection instance. */ + private Connection cx; + + @Override + protected Connection getConnection() { + return cx; + } + + @Override + protected DatabaseTestUtils getTestUtils() { + return TEST_UTILS; + } + + @BeforeClass + @AfterClass + public static void destroyDB() throws Exception { + TEST_UTILS.destroyDB(getDatabaseName()); + } + + @Before + public void setUp() throws Exception { + Class.forName("org.h2.Driver"); + cx = DriverManager.getConnection("jdbc:h2:target/" + getDatabaseName()); + super.setUp(); + } +} diff --git a/core/src/test/java/geodb/GeoDBFunctionTest.java b/core/src/test/java/geodb/GeoDBFunctionTest.java index d398c78..5f077d9 100644 --- a/core/src/test/java/geodb/GeoDBFunctionTest.java +++ b/core/src/test/java/geodb/GeoDBFunctionTest.java @@ -1,10 +1,8 @@ package geodb; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; +import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; @@ -15,20 +13,27 @@ import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.io.WKTReader; -public class GeoDBFunctionTest extends GeoDBTestSupport { +public abstract class GeoDBFunctionTest extends GeoDBTestSupport { + /** + * The name of the test database. + * + * @return the database name. + */ + public static String getDatabaseName() { + return "geodb_function"; + } @Before public void setUp() throws Exception { - super.setUp(); - + Connection cx = getConnection(); GeoDB.InitGeoDB(cx); Statement st = cx.createStatement(); - st.execute("DROP TABLE IF EXISTS spatial"); - st.execute("DROP TABLE IF EXISTS spatial2"); + dropTable(st, "spatial"); + dropTable(st, "spatial2"); st.execute("DELETE FROM geometry_columns"); - - st.execute("CREATE TABLE spatial (id INT AUTO_INCREMENT PRIMARY KEY, geom BLOB)"); + + createTable(st, "spatial", "id", "geom"); st.execute("INSERT INTO spatial (geom) VALUES (ST_GeomFromText('POINT(0 0)', 4326))"); st.execute("INSERT INTO spatial (geom) VALUES (ST_GeomFromText('POINT(1 1)', 4326))"); st.execute("INSERT INTO spatial (geom) VALUES (ST_GeomFromText('POINT(2 2)', 4326))"); @@ -37,8 +42,9 @@ public void setUp() throws Exception { @Test public void testSRID() throws Exception { + Connection cx = getConnection(); Statement st = cx.createStatement(); - ResultSet rs = st.executeQuery("SELECT ST_SRID(geom) FROM spatial LIMIT 1"); + ResultSet rs = st.executeQuery("SELECT ST_SRID(geom) FROM spatial " + getTestUtils().getLimitClauseSql(1)); rs.next(); assertEquals(4326, rs.getInt(1)); @@ -48,6 +54,7 @@ public void testSRID() throws Exception { @Test public void testAddGeometryColumn() throws Exception { + Connection cx = getConnection(); Statement st = cx.createStatement(); st.execute("CALL AddGeometryColumn(NULL,'SPATIAL', 'FOO', -1, 'POINT', 2)"); @@ -74,8 +81,9 @@ public void testAddGeometryColumn() throws Exception { @Test public void testAddGeometryColumn2() throws Exception { + Connection cx = getConnection(); Statement st = cx.createStatement(); - st.execute("CREATE TABLE spatial2 (id INT AUTO_INCREMENT PRIMARY KEY)"); + createTable(st, "spatial2", "id", null); st.execute("CALL AddGeometryColumn(null, 'SPATIAL2', 'GEOM', 4326, 'POINT', 2)"); ResultSet rs = st.executeQuery("SELECT * from geometry_columns"); @@ -98,12 +106,13 @@ public void testAddGeometryColumn2() throws Exception { rs.close(); st.close(); - } + @Test public void testDropGeometryColumn() throws Exception { testAddGeometryColumn(); + Connection cx = getConnection(); Statement st = cx.createStatement(); st.executeQuery("SELECT foo FROM spatial"); @@ -121,6 +130,7 @@ public void testDropGeometryColumn() throws Exception { @Test public void testDropGeometryColumns() throws Exception { + Connection cx = getConnection(); Statement st = cx.createStatement(); st.execute("CALL AddGeometryColumn(NULL,'SPATIAL', 'FOO', -1, 'POINT', 2)"); @@ -152,10 +162,11 @@ public void testDistance() throws Exception { Geometry g2 = wkt.read("POINT(90711.7123 56791.89)"); double dist = g1.distance(g2); + Connection cx = getConnection(); Statement st = cx.createStatement(); ResultSet rs = st.executeQuery( - "CALL ST_Distance(ST_GeomFromText('POINT(12123.343 79586.125)',-1), " + - " ST_GeomFromText('POINT(90711.7123 56791.89)',-1));"); + "VALUES ST_Distance(ST_GeomFromText('POINT(12123.343 79586.125)',-1), " + + " ST_GeomFromText('POINT(90711.7123 56791.89)',-1))"); rs.next(); double result = rs.getDouble(1); assertEquals(dist, result, 0.00001); @@ -164,9 +175,10 @@ public void testDistance() throws Exception { @Test public void testST_X() throws Exception { + Connection cx = getConnection(); Statement st = cx.createStatement(); ResultSet rs = st.executeQuery( - "CALL ST_X(ST_GeomFromText('POINT(12123.343 79586.125)',-1));"); + "VALUES ST_X(ST_GeomFromText('POINT(12123.343 79586.125)',-1))"); rs.next(); double result = rs.getDouble(1); assertEquals(12123.343, result, 0.00001); @@ -174,9 +186,10 @@ public void testST_X() throws Exception { @Test public void testST_Y() throws Exception { + Connection cx = getConnection(); Statement st = cx.createStatement(); ResultSet rs = st.executeQuery( - "CALL ST_Y(ST_GeomFromText('POINT(12123.343 79586.125)',-1));"); + "VALUES ST_Y(ST_GeomFromText('POINT(12123.343 79586.125)',-1))"); rs.next(); double result = rs.getDouble(1); assertEquals(79586.125, result, 0.00001); diff --git a/core/src/test/java/geodb/GeoDBH2Test.java b/core/src/test/java/geodb/GeoDBH2Test.java new file mode 100644 index 0000000..739624c --- /dev/null +++ b/core/src/test/java/geodb/GeoDBH2Test.java @@ -0,0 +1,58 @@ +package geodb; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +/** + * GeoDBH2Test runs the tests in {@link GeoDBTest} with H2. + */ +public class GeoDBH2Test extends GeoDBTest { + /** The H2 test utilities. */ + private static final H2TestUtils TEST_UTILS = new H2TestUtils(); + + /** The H2 database connection instance. */ + private Connection cx; + + @Override + protected Connection getConnection() { + return cx; + } + + @Override + protected DatabaseTestUtils getTestUtils() { + return TEST_UTILS; + } + + @Override + protected void createInitGeoDBProcedure(Statement st) throws SQLException { + ResultSet rs = st + .executeQuery("SELECT * FROM INFORMATION_SCHEMA.FUNCTION_ALIASES " + + "WHERE alias_schema IN (schema(), 'PUBLIC') " + + "AND alias_name = 'INITGEODB'"); + boolean functionExists = rs.next(); + rs.close(); + if (!functionExists) { + st.execute("CREATE ALIAS InitGeoDB for \"geodb.GeoDB.InitGeoDB\""); + } + } + + @BeforeClass + @AfterClass + public static void destroyDB() throws Exception { + TEST_UTILS.destroyDB(getDatabaseName()); + } + + @Before + public void setUp() throws Exception { + Class.forName("org.h2.Driver"); + cx = DriverManager.getConnection("jdbc:h2:target/" + getDatabaseName()); + super.setUp(); + } +} diff --git a/core/src/test/java/geodb/GeoDBTest.java b/core/src/test/java/geodb/GeoDBTest.java index c17aa30..4e67635 100644 --- a/core/src/test/java/geodb/GeoDBTest.java +++ b/core/src/test/java/geodb/GeoDBTest.java @@ -4,69 +4,122 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.sql.Connection; import java.sql.ResultSet; +import java.sql.SQLException; import java.sql.Statement; +import org.junit.After; import org.junit.Before; import org.junit.Test; -public class GeoDBTest extends GeoDBTestSupport { +public abstract class GeoDBTest extends GeoDBTestSupport { + /** + * The name of the database. + * @return the database name. + */ + public static String getDatabaseName() { + return "geodb"; + } + + /** + * Creates the InitGeoDB procedure. + * + * @param st + * the statement to execute the SQL. + * @throws SQLException + * if unable to execute the SQL. + */ + protected abstract void createInitGeoDBProcedure(Statement st) throws SQLException; @Before public void setUp() throws Exception { - super.setUp(); - + final Connection cx = getConnection(); Statement st = cx.createStatement(); - st.execute("DROP TABLE IF EXISTS _GEODB"); - st.execute("DROP TABLE IF EXISTS spatial"); - st.execute("DROP TABLE IF EXISTS spatial_hatbox"); - st.execute("DROP TABLE IF EXISTS noindex"); + String tableName = GeoDB.getGeoDBTableName(cx); + dropTable(st, tableName); + dropTable(st, "spatial"); + dropTable(st, "spatial_hatbox"); + dropTable(st, "noindex"); st.close(); } - + @Test public void testInitDB() throws Exception { - ResultSet tables = cx.getMetaData().getTables(null, null, "_GEODB", new String[] {"TABLE"}); + final Connection cx = getConnection(); + ResultSet tables = cx.getMetaData().getTables(null, null, GeoDB.getGeoDBTableName(cx), new String[] {"TABLE"}); assertFalse(tables.next()); - - GeoDB.InitGeoDB(cx); - tables = cx.getMetaData().getTables(null, null, "_GEODB", new String[] {"TABLE"}); + + Statement st = cx.createStatement(); + createInitGeoDBProcedure(st); + st.execute("CALL InitGeoDB()"); + st.close(); + tables = cx.getMetaData().getTables(null, null, GeoDB.getGeoDBTableName(cx), new String[] {"TABLE"}); assertTrue(tables.next()); tables.close(); } - + @Test public void testCreateSpatialIndex() throws Exception { - GeoDB.InitGeoDB(cx); + final Connection cx = getConnection(); Statement st = cx.createStatement(); - - st.execute("CREATE TABLE spatial (id INT AUTO_INCREMENT PRIMARY KEY, geom BLOB)"); + + createInitGeoDBProcedure(st); + st.execute("CALL InitGeoDB()"); + + createTable(st, "spatial", "id", "geom"); st.execute("INSERT INTO spatial (geom) VALUES (ST_GeomFromText('POINT(0 0)', 4326))"); st.execute("INSERT INTO spatial (geom) VALUES (ST_GeomFromText('POINT(1 1)', 4326))"); st.execute("INSERT INTO spatial (geom) VALUES (ST_GeomFromText('POINT(2 2)', 4326))"); + + ResultSet tables = + cx.getMetaData().getTables(null, null, "SPATIAL_HATBOX", new String[] {"TABLE"}); + assertFalse(tables.next()); + st.execute("CALL CreateSpatialIndex(null, 'SPATIAL', 'GEOM', '4326')"); + + tables = + cx.getMetaData().getTables(null, null, "SPATIAL_HATBOX", new String[] {"TABLE"}); + assertTrue(tables.next()); st.close(); + } + + @Test + public void testDropSpatialIndex() throws Exception { + final Connection cx = getConnection(); + Statement st = cx.createStatement(); + + createInitGeoDBProcedure(st); + st.execute("CALL InitGeoDB()"); + + createTable(st, "spatial", "id", "geom"); ResultSet tables = cx.getMetaData().getTables(null, null, "SPATIAL_HATBOX", new String[] {"TABLE"}); assertFalse(tables.next()); - GeoDB.CreateSpatialIndex(cx, null, "SPATIAL", "GEOM", "4326"); + st.execute("CALL CreateSpatialIndex(null, 'SPATIAL', 'GEOM', '4326')"); tables = cx.getMetaData().getTables(null, null, "SPATIAL_HATBOX", new String[] {"TABLE"}); assertTrue(tables.next()); + + st.execute("CALL DropSpatialIndex(null, 'SPATIAL')"); + tables = + cx.getMetaData().getTables(null, null, "SPATIAL_HATBOX", new String[] {"TABLE"}); + assertFalse(tables.next()); + st.close(); } - + @Test public void testGetSRID() throws Exception { - Statement st = cx.createStatement(); - st.execute("CREATE TABLE spatial (id INT AUTO_INCREMENT PRIMARY KEY, geom BLOB)"); + Statement st = getConnection().createStatement(); + createTable(st, "spatial", "id", "geom"); st.execute("CALL CreateSpatialIndex(null, 'SPATIAL', 'GEOM', '4326')"); - assertEquals(4326, GeoDB.GetSRID(cx, null, "SPATIAL")); + assertEquals(4326, GeoDB.GetSRID(getConnection(), null, "SPATIAL")); - st.execute("CREATE TABLE noindex (id INT AUTO_INCREMENT PRIMARY KEY, geom BLOB)"); - assertEquals(-1, GeoDB.GetSRID(cx, null, "NOINDEX")); + createTable(st, "noindex", "id", "geom"); + assertEquals(-1, GeoDB.GetSRID(getConnection(), null, "NOINDEX")); st.close(); } diff --git a/core/src/test/java/geodb/GeoDBTestSupport.java b/core/src/test/java/geodb/GeoDBTestSupport.java index df32488..f044485 100644 --- a/core/src/test/java/geodb/GeoDBTestSupport.java +++ b/core/src/test/java/geodb/GeoDBTestSupport.java @@ -1,32 +1,68 @@ package geodb; import java.sql.Connection; -import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; -import org.h2.tools.DeleteDbFiles; import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; - -public class GeoDBTestSupport { - - protected Connection cx; - - @BeforeClass - @AfterClass - public static void destroyDB() throws Exception { - DeleteDbFiles.execute("target", "geodb", true); + +public abstract class GeoDBTestSupport { + + /** + * Returns the connection to the database under test. + * + * @return the connection instance. + */ + protected abstract Connection getConnection(); + + protected abstract DatabaseTestUtils getTestUtils(); + + /** + * Creates a test table with the given name. The ID column should be an + * integer type that is the primary key and have the + * auto-increment capability. + * + * @param st + * the statement to execute the SQL. + * @param tableName + * the table name. + * @param idColumnName + * the ID column name. + * @param geomColumnName + * the Geometry column name. + * @throws SQLException + * if unable to create the table. + */ + protected void createTable(Statement st, String tableName, + String idColumnName, String geomColumnName) throws SQLException { + String sql = getTestUtils().getCreateTestTableSql(tableName, + idColumnName, geomColumnName); + st.execute(sql); } - - @Before - public void setUp() throws Exception { - Class.forName("org.h2.Driver"); - cx = DriverManager.getConnection("jdbc:h2:target/geodb"); + + /** + * Drops the given table. + * + * @param st + * the statement to execute the SQL. + * @param tableName + * the table name. + * @throws SQLException + * if unable to drop the table. + */ + protected void dropTable(Statement st, String tableName) throws SQLException { + String sql = getTestUtils().getDropTableSql(tableName); + try { + st.execute(sql); + } catch (SQLException e) { + if (getTestUtils().isDropTableIfExistsSupported()) { + throw e; + } + } } - + @After public void tearDown() throws Exception { - cx.close(); + getConnection().close(); } } diff --git a/core/src/test/java/geodb/H2TestUtils.java b/core/src/test/java/geodb/H2TestUtils.java new file mode 100644 index 0000000..592017f --- /dev/null +++ b/core/src/test/java/geodb/H2TestUtils.java @@ -0,0 +1,60 @@ +package geodb; + +import java.sql.DriverManager; + +import org.h2.tools.DeleteDbFiles; + +/** + * H2TestUtils assists in generating H2-specific SQL and utility + * methods for unit testing. + */ +public class H2TestUtils implements DatabaseTestUtils { + /** + * @see geodb.DatabaseTestUtils#createDB(java.lang.String) + */ + public void createDB(String databaseName) throws Exception { + Class.forName("org.h2.Driver"); + DriverManager.getConnection("jdbc:h2:target/" + databaseName).close(); + } + + /** + * @see geodb.DatabaseTestUtils#destroyDB(java.lang.String) + */ + public void destroyDB(String databaseName) throws Exception { + DeleteDbFiles.execute("target", databaseName, true); + } + + /** + * @see geodb.DatabaseTestUtils#getCreateTestTableSql(java.lang.String, + * java.lang.String, java.lang.String) + */ + public String getCreateTestTableSql(String tableName, String idColumnName, + String geomColumnName) { + StringBuilder sql = new StringBuilder("CREATE TABLE "); + sql.append(tableName).append(" (").append(idColumnName) + .append(" INT AUTO_INCREMENT PRIMARY KEY"); + if (geomColumnName != null) { + sql.append(", ").append(geomColumnName).append(" BLOB"); + } + sql.append(')'); + return sql.toString(); + } + + /** + * @see geodb.DatabaseTestUtils#getDropTableSql(java.lang.String) + */ + public String getDropTableSql(String tableName) { + return "DROP TABLE IF EXISTS " + tableName; + } + + /** + * @see geodb.DatabaseTestUtils#isDropTableIfExistsSupported() + */ + public boolean isDropTableIfExistsSupported() { + return true; + } + + public String getLimitClauseSql(int limit) { + return "LIMIT " + limit; + } +} diff --git a/core/src/test/java/geodb/aggregate/AggregateFunctionDerbyTest.java b/core/src/test/java/geodb/aggregate/AggregateFunctionDerbyTest.java new file mode 100644 index 0000000..05d31e5 --- /dev/null +++ b/core/src/test/java/geodb/aggregate/AggregateFunctionDerbyTest.java @@ -0,0 +1,51 @@ +package geodb.aggregate; + +import geodb.DatabaseTestUtils; +import geodb.DerbyTestUtils; + +import java.sql.Connection; +import java.sql.DriverManager; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +/** + * AggregateFunctionDerbyTest runs the tests in + * {@link TestAggregateFunction} with Derby. + */ +public class AggregateFunctionDerbyTest extends TestAggregateFunction { + /** The database connection instance. */ + protected Connection cx; + + /** The Derby test utilities. */ + private static DerbyTestUtils testUtils = new DerbyTestUtils(); + + @Override + protected Connection getConnection() { + return cx; + } + + @Override + protected DatabaseTestUtils getTestUtils() { + return testUtils; + } + + @BeforeClass + public static void createDB() throws Exception { + testUtils.destroyDB(getDatabaseName()); + testUtils.createDB(getDatabaseName()); + } + + @AfterClass + public static void destroyDB() throws Exception { + testUtils.destroyDB(getDatabaseName()); + } + + @Before + public void setup() throws Exception { + cx = DriverManager.getConnection("jdbc:derby:directory:target/" + + getDatabaseName()); + super.setup(); + } +} diff --git a/core/src/test/java/geodb/aggregate/AggregateFunctionH2Test.java b/core/src/test/java/geodb/aggregate/AggregateFunctionH2Test.java new file mode 100644 index 0000000..2ea75c0 --- /dev/null +++ b/core/src/test/java/geodb/aggregate/AggregateFunctionH2Test.java @@ -0,0 +1,47 @@ +package geodb.aggregate; + +import geodb.DatabaseTestUtils; +import geodb.H2TestUtils; +import geodb.GeoDBFunctionTest; + +import java.sql.Connection; +import java.sql.DriverManager; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +/** + * AggregateFunction runs the tests in {@link GeoDBFunctionTest} + * with H2. + */ +public class AggregateFunctionH2Test extends TestAggregateFunction { + /** The H2 test utilities. */ + private static final H2TestUtils TEST_UTILS = new H2TestUtils(); + + /** The H2 database connection instance. */ + private Connection cx; + + @Override + protected Connection getConnection() { + return cx; + } + + @Override + protected DatabaseTestUtils getTestUtils() { + return TEST_UTILS; + } + + @BeforeClass + @AfterClass + public static void destroyDB() throws Exception { + TEST_UTILS.destroyDB(getDatabaseName()); + } + + @Before + public void setup() throws Exception { + Class.forName("org.h2.Driver"); + cx = DriverManager.getConnection("jdbc:h2:target/" + getDatabaseName()); + super.setup(); + } +} diff --git a/core/src/test/java/geodb/aggregate/AggregateFunctionTest.java b/core/src/test/java/geodb/aggregate/TestAggregateFunction.java similarity index 74% rename from core/src/test/java/geodb/aggregate/AggregateFunctionTest.java rename to core/src/test/java/geodb/aggregate/TestAggregateFunction.java index f43ca8c..7488261 100644 --- a/core/src/test/java/geodb/aggregate/AggregateFunctionTest.java +++ b/core/src/test/java/geodb/aggregate/TestAggregateFunction.java @@ -6,6 +6,7 @@ import geodb.GeoDBTestSupport; import java.io.InputStream; +import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; @@ -16,22 +17,29 @@ import com.vividsolutions.jts.io.InputStreamInStream; import com.vividsolutions.jts.io.WKBReader; -public class AggregateFunctionTest extends GeoDBTestSupport { +public abstract class TestAggregateFunction extends GeoDBTestSupport { + /** + * The name of the test database. + * + * @return the database name. + */ + public static String getDatabaseName() { + return "geodb_aggregate_function"; + } @Before public void setup() throws Exception { - super.setUp(); - + Connection cx = getConnection(); Statement st = cx.createStatement(); - st.execute("DROP TABLE IF EXISTS _GEODB"); - st.execute("DROP TABLE IF EXISTS spatial"); - st.execute("DROP TABLE IF EXISTS spatial_hatbox"); - st.execute("DROP TABLE IF EXISTS noindex"); + dropTable(st, GeoDB.getGeoDBTableName(cx)); + dropTable(st, "spatial"); + dropTable(st, "spatial_hatbox"); + dropTable(st, "noindex"); st.close(); GeoDB.InitGeoDB(cx); st = cx.createStatement(); - st.execute("CREATE TABLE spatial (id INT AUTO_INCREMENT PRIMARY KEY, geom BLOB)"); + createTable(st, "spatial", "id", "geom"); st.execute("INSERT INTO spatial (geom) VALUES (ST_GeomFromText('POINT(0 0)', 4326))"); st.execute("INSERT INTO spatial (geom) VALUES (ST_GeomFromText('POINT(1 1)', 4326))"); st.execute("INSERT INTO spatial (geom) VALUES (ST_GeomFromText('POINT(2 2)', 4326))"); @@ -40,6 +48,7 @@ public void setup() throws Exception { @Test public void testExtent() throws Exception { + Connection cx = getConnection(); Statement st = cx.createStatement(); ResultSet rs = st.executeQuery("select st_extent(geom) from spatial"); rs.next(); @@ -51,6 +60,7 @@ public void testExtent() throws Exception { @Test public void testUnion() throws Exception { + Connection cx = getConnection(); Statement st = cx.createStatement(); ResultSet rs = st.executeQuery("select st_union_aggregate(geom) from spatial"); rs.next(); @@ -59,6 +69,4 @@ public void testUnion() throws Exception { st.close(); assertThat(geometry.getArea(), is(0.0)); } - - } diff --git a/pom.xml b/pom.xml index 180f902..6128aa3 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.opengeo geodb-parent pom - 0-SNAPSHOT + 0.9-SNAPSHOT GeoDB