Revision: 4579 http://sourceforge.net/p/jump-pilot/code/4579 Author: elnico Date: 2015-12-07 17:07:02 +0000 (Mon, 07 Dec 2015) Log Message: ----------- Integration of the latest version of spatialDatabases plugin into the core, for Postgis and Oracle Spatial support. Refactored methods: added a base package for spatialdatabases common code, each DB provider implementing custom operations. Uses JDBC Database Metadata information as much as possible (indexes, column names, etc.). Uses Ede introspection code to hide Oracle.* imports at compile time.
Modified Paths: -------------- core/trunk/src/com/vividsolutions/jump/datastore/OracleDataStoreExtension.java core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleDSConnection.java core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleDSMetadata.java core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleDataStoreDriver.java core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleFeatureInputStream.java core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleResultSetConverter.java core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleSQLBuilder.java core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleValueConverterFactory.java core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisDSConnection.java core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisDSMetadata.java core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisDataStoreDriver.java core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisFeatureInputStream.java core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisResultSetConverter.java core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisSQLBuilder.java core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisValueConverterFactory.java Added Paths: ----------- core/trunk/src/com/vividsolutions/jump/datastore/spatialdatabases/ core/trunk/src/com/vividsolutions/jump/datastore/spatialdatabases/SpatialDatabasesDSConnection.java core/trunk/src/com/vividsolutions/jump/datastore/spatialdatabases/SpatialDatabasesDSMetadata.java core/trunk/src/com/vividsolutions/jump/datastore/spatialdatabases/SpatialDatabasesDataStoreDriver.java core/trunk/src/com/vividsolutions/jump/datastore/spatialdatabases/SpatialDatabasesFeatureInputStream.java core/trunk/src/com/vividsolutions/jump/datastore/spatialdatabases/SpatialDatabasesResultSetConverter.java core/trunk/src/com/vividsolutions/jump/datastore/spatialdatabases/SpatialDatabasesSQLBuilder.java core/trunk/src/com/vividsolutions/jump/datastore/spatialdatabases/SpatialDatabasesValueConverterFactory.java Modified: core/trunk/src/com/vividsolutions/jump/datastore/OracleDataStoreExtension.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/OracleDataStoreExtension.java 2015-12-07 07:02:08 UTC (rev 4578) +++ core/trunk/src/com/vividsolutions/jump/datastore/OracleDataStoreExtension.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -7,11 +7,8 @@ import com.vividsolutions.jump.workbench.WorkbenchContext; import com.vividsolutions.jump.workbench.plugin.Extension; import com.vividsolutions.jump.workbench.plugin.PlugInContext; -import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.Statement; /** * Modified: core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleDSConnection.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleDSConnection.java 2015-12-07 07:02:08 UTC (rev 4578) +++ core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleDSConnection.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -1,46 +1,37 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ package com.vividsolutions.jump.datastore.oracle; -import com.vividsolutions.jump.I18N; -import com.vividsolutions.jump.datastore.*; +import com.vividsolutions.jump.datastore.FilterQuery; +import com.vividsolutions.jump.datastore.SpatialReferenceSystemID; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesDSConnection; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesDSMetadata; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesFeatureInputStream; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesSQLBuilder; import com.vividsolutions.jump.io.FeatureInputStream; - import java.sql.Connection; import java.sql.SQLException; /** + * + * @author nicolas */ -public class OracleDSConnection implements DataStoreConnection { +public class OracleDSConnection extends SpatialDatabasesDSConnection { - private OracleDSMetadata dbMetadata; - private Connection connection; - - public OracleDSConnection(Connection conn) { - connection = conn; - dbMetadata = new OracleDSMetadata(this); + public OracleDSConnection(Connection con) { + super(con); // ? + connection = con; + this.dbMetadata = new OracleDSMetadata(this); } - - public Connection getConnection() { - return connection; + + @Override + public SpatialDatabasesSQLBuilder getSqlBuilder(SpatialReferenceSystemID srid, String[] colNames) { + return new OracleSQLBuilder(this.dbMetadata, srid, colNames); } - public DataStoreMetadata getMetadata() { - return dbMetadata; - } - - public FeatureInputStream execute(Query query) throws Exception { - if (query instanceof FilterQuery) { - try { - return executeFilterQuery((FilterQuery) query); - } catch (SQLException e) { - throw new RuntimeException(e); - } - } - if (query instanceof AdhocQuery) { - return executeAdhocQuery((AdhocQuery) query); - } - throw new IllegalArgumentException(I18N.get(this.getClass().getName()+".unsupported-query-type")); - } - /** * Executes a filter query. * @@ -51,40 +42,10 @@ * @return the results of the query * @throws SQLException */ + @Override public FeatureInputStream executeFilterQuery(FilterQuery query) throws SQLException { - - SpatialReferenceSystemID srid = dbMetadata.getSRID(query.getDatasetName(), query.getGeometryAttributeName()); - String[] colNames = dbMetadata.getColumnNames(query.getDatasetName()); - - OracleSQLBuilder builder = new OracleSQLBuilder(srid, colNames, dbMetadata.getGeoIndexes()); - String queryString = builder.getSQL(query); - - return new OracleFeatureInputStream(connection, queryString, query.getPrimaryKey()); + SpatialDatabasesFeatureInputStream fis = (SpatialDatabasesFeatureInputStream)super.executeFilterQuery(query); + return new OracleFeatureInputStream(fis.getConnection(), fis.getQueryString(), query.getPrimaryKey()); } - - public FeatureInputStream executeAdhocQuery(AdhocQuery query) throws Exception { - String queryString = query.getQuery(); - OracleFeatureInputStream ifs = new OracleFeatureInputStream(connection, queryString, query.getPrimaryKey()); - if (ifs.getFeatureSchema().getGeometryIndex() < 0) { - throw new Exception(I18N.get(this.getClass().getName()+".resultset-must-have-a-geometry-column")); - } - return ifs; - } - - - public void close() throws DataStoreException { - try { - connection.close(); - } - catch (Exception ex) { throw new DataStoreException(ex); } - } - - public boolean isClosed() throws DataStoreException { - try { - return connection.isClosed(); - } catch (SQLException e) { - throw new DataStoreException(e); - } - } - -} \ No newline at end of file + +} Modified: core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleDSMetadata.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleDSMetadata.java 2015-12-07 07:02:08 UTC (rev 4578) +++ core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleDSMetadata.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -1,383 +1,84 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ package com.vividsolutions.jump.datastore.oracle; -import com.vividsolutions.jts.geom.Envelope; -import com.vividsolutions.jts.io.WKBReader; -import com.vividsolutions.jump.datastore.DataStoreMetadata; +import com.vividsolutions.jump.datastore.DataStoreConnection; +import com.vividsolutions.jump.datastore.spatialdatabases.*; import com.vividsolutions.jump.datastore.GeometryColumn; -import com.vividsolutions.jump.datastore.PrimaryKeyColumn; -import com.vividsolutions.jump.datastore.SpatialReferenceSystemID; -import com.vividsolutions.jump.datastore.jdbc.JDBCUtil; -import com.vividsolutions.jump.datastore.jdbc.ResultSetBlock; -import com.vividsolutions.jump.workbench.JUMPWorkbench; -import java.sql.DatabaseMetaData; - -import java.sql.ResultSet; import java.sql.SQLException; -import java.util.*; +import java.util.List; -public class OracleDSMetadata implements DataStoreMetadata { - private static final String SQL_QUERY_LAYERS = "select distinct owner, table_name from ALL_SDO_GEOM_METADATA"; +public class OracleDSMetadata extends SpatialDatabasesDSMetadata { - private final WKBReader reader = new WKBReader(); - - private OracleDSConnection conn; - // connection username, used a default schema - private String userSchema; - - private Map sridMap = new HashMap(); - - // map storing the geo column names and the name of their index - private HashMap<String, String> geoIndexes = new HashMap<String, String>(); - - public OracleDSMetadata(OracleDSConnection conn) { - this.conn = conn; - + public OracleDSMetadata(DataStoreConnection con) { + conn = con; + // TODO: use bind parameters to avoid SQL injection try { - this.userSchema = conn.getConnection().getMetaData().getUserName(); + this.defaultSchemaName = conn.getConnection().getMetaData().getUserName(); } catch (SQLException ex) { - System.out.println(ex.toString()); + System.err.println(ex.toString()); + defaultSchemaName = ""; } - } - - public HashMap<String, String> getGeoIndexes() { - return geoIndexes; - } - - public String[] getDatasetNames() { - final List datasetNames = new ArrayList(); - // Spatial tables only. - JUMPWorkbench.getInstance().getFrame().log( - "SQL query to get list of spatial data:\n\t" - + SQL_QUERY_LAYERS, this.getClass()); - - JDBCUtil.execute( - conn.getConnection(), - SQL_QUERY_LAYERS, - new ResultSetBlock() { - public void yield(ResultSet resultSet) throws SQLException { - while (resultSet.next()) { - String schema = resultSet.getString(1); - String table = resultSet.getString(2); - // on Oracle, user's schema is the default schema - if (!schema.equalsIgnoreCase(userSchema)) { - table = schema + "." + table; - } - datasetNames.add(table); - } - } - }); - return (String[]) datasetNames.toArray(new String[datasetNames.size()]); - } - - // TODO: check - public Envelope getExtents(String datasetName, String attributeName) { - - final Envelope[] e = new Envelope[]{null}; - // extent is taken from layer's metadata - String sql1 = "SELECT dim.* FROM ALL_SDO_GEOM_METADATA usgm, TABLE(usgm.diminfo) " - + "dim WHERE table_name = '" + getTableName(datasetName) + "' and owner='" + getSchemaName(datasetName) - + "' and column_name = '" + attributeName + "'"; + datasetNameQuery = "SELECT distinct asgm.OWNER, asgm.TABLE_NAME FROM ALL_SDO_GEOM_METADATA asgm"; + spatialDbName = "Oracle Spatial"; + spatialExtentQuery1 = "with tmp as (\n" + + " SELECT dim.*\n" + + " FROM ALL_SDO_GEOM_METADATA asgm, TABLE (asgm.diminfo) dim\n" + + " WHERE owner = '%s' and table_name = '%s' AND COLUMN_NAME='%s'\n" + + ") select sdo_util.to_wktgeometry(SDO_GEOMETRY(\n" + + " 2003,\n" + + " NULL,\n" + + " NULL,\n" + + " SDO_ELEM_INFO_ARRAY(1,1003,1),\n" + + " SDO_ORDINATE_ARRAY((select sdo_lb from tmp where sdo_dimname = 'X'),\n" + + " (select sdo_lb from tmp where sdo_dimname = 'Y'), \n" + + " (select sdo_ub from tmp where sdo_dimname = 'X'),\n" + + " (select sdo_lb from tmp where sdo_dimname = 'Y'),\n" + + " (select sdo_ub from tmp where sdo_dimname = 'X'),\n" + + " (select sdo_ub from tmp where sdo_dimname = 'Y'),\n" + + " (select sdo_ub from tmp where sdo_dimname = 'X'),\n" + + " (select sdo_lb from tmp where sdo_dimname = 'Y'),\n" + + " (select sdo_lb from tmp where sdo_dimname = 'X'),\n" + + " (select sdo_lb from tmp where sdo_dimname = 'Y'))\n" + + " )) as geom \n" + + "from dual"; - JUMPWorkbench.getInstance().getFrame().log("SQL query to get extent:\n\t" + sql1, this.getClass()); - - final ResultSetBlock resultSetBlock = new ResultSetBlock() { - public void yield(ResultSet resultSet) throws Exception { - // looks only for X-Y dimension, though oracle metadata can store more - double xmin = 0, ymin = 0, xmax = 0, ymax = 0; - while (resultSet.next()) { - if ("X".equalsIgnoreCase(resultSet.getString(1))) { - xmin = resultSet.getDouble(2); - xmax = resultSet.getDouble(3); - } - if ("Y".equalsIgnoreCase(resultSet.getString(1))) { - ymin = resultSet.getDouble(2); - ymax = resultSet.getDouble(3); - } - } - e[0] = new Envelope(xmin, ymin, xmax, ymax); - } - }; - try { - JDBCUtil.execute(conn.getConnection(), (sql1), resultSetBlock); - if (e[0] == null || e[0].isNull()) { - JDBCUtil.execute(conn.getConnection(), (sql1), resultSetBlock); - } - } catch (Exception ex1) { - // TODO: - ex1.printStackTrace(); - } - return e[0]; - } - - /** - * Returns the list of geometryColumns for the given name in Oracle, - * metadata does not store this info: queries the table to get distinct list - * of geometries: if several found: geometry type. Also retrieves geom srid - * from metadata and stores it in the map - * - * @param datasetName - * @return - */ - public List<GeometryColumn> getGeometryAttributes(final String datasetName) { - final List<GeometryColumn> geometryAttributes = new ArrayList<GeometryColumn>(); - String sql = "select column_name, srid from ALL_SDO_GEOM_METADATA " - + geomColumnMetadataWhereClause("owner", "table_name", datasetName); + spatialExtentQuery2 = "select sdo_util.to_wktgeometry(sdo_aggr_mbr(%s)) as geom from %s.%s"; - JUMPWorkbench.getInstance().getFrame().log( - "SQL query to get geometry attributes from a spatial table:\n\t" - + sql, this.getClass()); - - JDBCUtil.execute( - conn.getConnection(), sql, - new ResultSetBlock() { - public void yield(ResultSet resultSet) throws SQLException { - while (resultSet.next()) { - String colName = resultSet.getString(1); - int srid = resultSet.getInt(2); - sridMap.put(datasetName + "#" + colName, new SpatialReferenceSystemID(srid)); - geometryAttributes.add(new GeometryColumn( - colName, - srid - // ,resultSet.getString(3) - )); - } - } - }); - - // TODO: move MD retrieving in a JDBCUtil static method and use JDBC metadata - // as deep as we can ? - geoIndexes = new HashMap<String, String>(); - try { - // gets info in cnx metadata: - DatabaseMetaData md = conn.getConnection().getMetaData(); - ResultSet rs = md.getIndexInfo(null, getSchemaName(datasetName), getTableName(datasetName), false, true); - StringBuilder b = new StringBuilder(); - while (rs.next()) { - if (rs.getString(6) != null && rs.getString(9) != null) { - geoIndexes.put(rs.getString(9), rs.getString(6)); - } - } - } catch (SQLException ex) { - // TODO - } - - // gets the geometry column type for each geo col - for (final GeometryColumn gc : geometryAttributes) { - sql = "select distinct t." + gc.getName() + ".sdo_gtype from " + datasetName + " t"; - JUMPWorkbench.getInstance().getFrame().log( - "SQL query to get geometry column type :\n\t" - + sql, this.getClass()); - - JDBCUtil.execute( - conn.getConnection(), sql, - new ResultSetBlock() { - public void yield(ResultSet resultSet) throws SQLException { - int gtype = 0; - int i = 0; - String type = ""; - while (resultSet.next()) { - gtype = resultSet.getInt(1) % 10; - if (i == 0) { - switch (gtype) { - case 1: - type = "POINT"; - break; - case 2: - type = "LINESTRING"; - break; - case 3: - type = "POLYGON"; - break; - case 4: - type = "GEOMETRYCOLLECTION"; - break; - case 5: - type = "MULTIPOINT"; - break; - case 6: - type = "MULTILINESTRING"; - break; - case 7: - type = "MULTIPOLYGON"; - break; - default: - type = "GEOMETRY"; - } - } else if (i > 0) { - // more than one geo type: - type = "GEOMETRY"; - break; - } - i++; - } - gc.setType(type); - } - }); - // also gets info about index is column indexed: useful to avoid calling sdo_filter on - // a non-indexed column - gc.setIndexed(geoIndexes.containsKey(gc.getName())); - } - return geometryAttributes; + geoColumnsQuery = "select t.column_name, t.srid, 'SDO_GEOMETRY' as type from ALL_SDO_GEOM_METADATA t " + + "where t.owner = '%s' and t.table_name = '%s'"; + sridQuery = "select t.srid from ALL_SDO_GEOM_METADATA t " + + "where t.owner = '%s' and t.table_name = '%s' and t.COLUMN_NAME = '%s'"; } - /** - * Returns PRIMARY KEY columns of dataset names. // TODO: check STATUS PK: - * enabled/disabled - * - * @param datasetName name of the table (optionally prefixed by the schema - * name) - * @return the list of columns involved in the Primary Key (generally, a - * single column) - */ - public List<PrimaryKeyColumn> getPrimaryKeyColumns(String datasetName) { - final List<PrimaryKeyColumn> identifierColumns = new ArrayList<PrimaryKeyColumn>(); - // query taken from http://www.alberton.info/postgresql_meta_info.html#.UewsFG29b0Q - String sql - = "SELECT cols.table_name, cols.column_name \n" - + "FROM all_constraints cons, all_cons_columns cols \n" - + "WHERE cols.table_name = '" + getTableName(datasetName) + "' and cols.OWNER = '" - + getSchemaName(datasetName) + "' \n" - + "AND cons.constraint_type = 'P' \n" - + "AND cons.constraint_name = cols.constraint_name \n" - + "AND cons.owner = cols.owner \n" - + "ORDER BY cols.table_name, cols.position"; - - JUMPWorkbench.getInstance().getFrame().log( - "SQL query to get PK from a spatial table:\n\t" - + sql, this.getClass()); - - JDBCUtil.execute( - conn.getConnection(), sql, - new ResultSetBlock() { - public void yield(ResultSet resultSet) throws SQLException { - while (resultSet.next()) { - identifierColumns.add(new PrimaryKeyColumn( - resultSet.getString(1), - resultSet.getString(2))); - } - } - }); - return identifierColumns; + @Override + public String getSpatialExtentQuery1(String schema, String table, String attributeName) { + return String.format(this.spatialExtentQuery1, schema, table, attributeName); } - /** - * Fetch only not hidden columns - * - * @param datasetName - * @return - */ - public String[] getColumnNames(String datasetName) { - String sql = "SELECT cols.COLUMN_NAME, cols.DATA_TYPE \n" - + "FROM ALL_TAB_COLS cols \n" - + "WHERE hidden_column = 'NO' and cols.table_name = '" + getTableName(datasetName) + "' and cols.OWNER = '" - + getSchemaName(datasetName) + "' \n" - + "ORDER BY cols.table_name, cols.COLUMN_ID"; - - JUMPWorkbench.getInstance().getFrame().log( - "SQL query to get column names and types from a spatial table:\n\t" - + sql, this.getClass()); - - ColumnNameBlock block = new ColumnNameBlock(); - JDBCUtil.execute(conn.getConnection(), sql, block); - return block.colName; + @Override + public String getSpatialExtentQuery2(String schema, String table, String attributeName) { + return String.format(this.spatialExtentQuery2, attributeName, schema, table); } - /** - * Returns the schema name based on the given tableName: string before . if - * exists, else returns userName - * - * @param tableName - * @return - */ - private String getSchemaName(String tableName) { - int dotPos = tableName.indexOf("."); - String schema = this.userSchema; - if (dotPos != -1) { - schema = tableName.substring(0, dotPos); - } - return schema; + @Override + public String getGeoColumnsQuery(String datasetName) { + return String.format(this.geoColumnsQuery, getSchemaName(datasetName), getTableName(datasetName)); } - /** - * Returns the table name based on the given tableName: string after "." if - * exists, else returns userName - * - * @param tableName - * @return - */ - private String getTableName(String tableName) { - int dotPos = tableName.indexOf("."); - String ret = tableName; - if (dotPos != -1) { - ret = tableName.substring(0, dotPos); - } - return ret; + @Override + public String getSridQuery(String schemaName, String tableName, String colName) { + // TODO + return String.format(this.sridQuery, schemaName, tableName, colName); } - - @Deprecated - public SpatialReferenceSystemID getSRID(String tableName, String colName) - throws SQLException { - String key = tableName + "#" + colName; - if (!sridMap.containsKey(key)) { - // not in cache, so query it - String srid = querySRID(tableName, colName); - sridMap.put(key, new SpatialReferenceSystemID(srid)); - } - return (SpatialReferenceSystemID) sridMap.get(key); + + @Override + public List<GeometryColumn> getGeometryAttributes(String datasetName) { + String sql = this.getGeoColumnsQuery(datasetName); + return getGeometryAttributes(sql, datasetName); } - // TODO: should never be called: sridMap should be filled when getting geo columns - @Deprecated - private String querySRID(String tableName, String colName) { - final StringBuffer srid = new StringBuffer(); - // Changed by Michael Michaud 2010-05-26 (throwed exception for empty tableName) - // String sql = "SELECT getsrid(" + colName + ") FROM " + tableName + " LIMIT 1"; - String[] tokens = tableName.split("\\.", 2); - String schema = tokens.length == 2 ? tokens[0] : "public"; - String table = tokens.length == 2 ? tokens[1] : tableName; - String sql = "SELECT srid FROM geometry_columns where (f_table_schema = '" + schema + "' and f_table_name = '" + table + "')"; - - JUMPWorkbench.getInstance().getFrame().log( - "SQL query to get geometry column SRID:\n\t" - + sql, this.getClass()); - - // End of the fix - JDBCUtil.execute(conn.getConnection(), sql, new ResultSetBlock() { - public void yield(ResultSet resultSet) throws SQLException { - if (resultSet.next()) { - srid.append(resultSet.getString(1)); - } - } - }); - - return srid.toString(); - } - - private String geomColumnMetadataWhereClause(String schemaCol, String tableCol, String tableName) { - // [mmichaud 2011-07-24] Fixed a bug related to tables having common - // names in public schema and another schema - int dotPos = tableName.indexOf("."); - String schema = this.userSchema; - String table = tableName; - if (dotPos != -1) { - schema = tableName.substring(0, dotPos); - table = tableName.substring(dotPos + 1); - } - return "WHERE " + schemaCol + " = '" + schema + "'" - + " AND " + tableCol + " = '" + table + "'"; - } - - private static class ColumnNameBlock implements ResultSetBlock { - - List colList = new ArrayList(); - String[] colName; - - public void yield(ResultSet resultSet) throws SQLException { - while (resultSet.next()) { - colList.add(resultSet.getString(1)); - } - colName = (String[]) colList.toArray(new String[colList.size()]); - } - } - } Modified: core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleDataStoreDriver.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleDataStoreDriver.java 2015-12-07 07:02:08 UTC (rev 4578) +++ core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleDataStoreDriver.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -1,110 +1,40 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ package com.vividsolutions.jump.datastore.oracle; -import java.sql.*; - -import com.vividsolutions.jump.datastore.*; - +import com.vividsolutions.jump.datastore.DataStoreConnection; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesDataStoreDriver; import com.vividsolutions.jump.parameter.ParameterList; -import com.vividsolutions.jump.parameter.ParameterListSchema; -import com.vividsolutions.jump.util.Blackboard; -import com.vividsolutions.jump.workbench.JUMPWorkbench; /** - * * A driver for supplying {@link OracleDSConnection}s + * A driver for supplying {@link SpatialDatabaseDSConnection}s */ public class OracleDataStoreDriver - implements DataStoreDriver -{ - public static final String DRIVER_NAME = "Oracle Spatial"; - /** name of the class dealing with SDO geometries from Geotools jar. - * Oracle driver needs this class - */ - public static final String GT_SDO_CLASS_NAME = "org.geotools.data.oracle.sdo.SDO"; - public static final String JDBC_CLASS = "oracle.jdbc.driver.OracleDriver"; - // using new URL style: jdbc:oracle:thin:@//[HOST][:PORT]/SERVICE - public static final String URL_PREFIX = "jdbc:oracle:thin:@//"; + extends SpatialDatabasesDataStoreDriver { + // TODO: uniformize + public final static String JDBC_CLASS = "oracle.jdbc.driver.OracleDriver"; + public static final String GT_SDO_CLASS_NAME = "org.geotools.data.oracle.sdo.SDO"; - public static final String PARAM_Server = "Server"; - public static final String PARAM_Port = "Port"; - public static final String PARAM_Instance = "Database"; - public static final String PARAM_User = "User"; - public static final String PARAM_Password = "Password"; - private static final String[] paramNames = new String[] { - PARAM_Server, - PARAM_Port, - PARAM_Instance, - PARAM_User, - PARAM_Password - }; - private static final Class[] paramClasses = new Class[] - { - String.class, - Integer.class, - String.class, - String.class, - String.class - }; - private final ParameterListSchema schema = new ParameterListSchema(paramNames, paramClasses); - - /** The database driver */ - private Driver driver; - - public OracleDataStoreDriver(Driver driver) { - this.driver = driver; - } - - public OracleDataStoreDriver() { - this(null); - } - - public String getName() - { - return DRIVER_NAME; - } - public ParameterListSchema getParameterListSchema() - { - return schema; - } - public DataStoreConnection createConnection(ParameterList params) - throws Exception - { - String host = params.getParameterString(PARAM_Server); - int port = params.getParameterInt(PARAM_Port); - String database = params.getParameterString(PARAM_Instance); - String user = params.getParameterString(PARAM_User); - String password = params.getParameterString(PARAM_Password); - - String url - = String.valueOf(new StringBuffer(URL_PREFIX).append - (host).append - (":").append - (port).append - ("/").append(database)); - - // mmichaud 2013-08-27 workaround for ticket #330 - String savePreferIPv4Stack = System.getProperty("java.net.preferIPv4Stack"); - String savePreferIPv6Addresses = System.getProperty("java.net.preferIPv6Addresses"); - System.setProperty("java.net.preferIPv4Stack", "true"); - System.setProperty("java.net.preferIPv6Addresses", "false"); - - JUMPWorkbench.getInstance().getFrame().log("Url for oracle: " + url, this.getClass()); - Connection conn = DriverManager.getConnection(url, user, password); - - if (savePreferIPv4Stack == null) { - System.getProperties().remove("java.net.preferIPv4Stack"); - } else { - System.setProperty("java.net.preferIPv4Stack", savePreferIPv4Stack); + public OracleDataStoreDriver() { + this.driverName = "Oracle Spatial"; + this.jdbcClass = OracleDataStoreDriver.JDBC_CLASS; + this.urlPrefix = "jdbc:oracle:thin:@//"; } - if (savePreferIPv6Addresses == null) { - System.getProperties().remove("java.net.preferIPv6Addresses"); - } else { - System.setProperty("java.net.preferIPv6Addresses", savePreferIPv6Addresses); + + /** + * returns the right type of DataStoreConnection + * @param params + * @return + * @throws Exception + */ + @Override + public DataStoreConnection createConnection(ParameterList params) + throws Exception { + DataStoreConnection ret = super.createConnection(params); + return new OracleDSConnection(ret.getConnection()); } - return new OracleDSConnection(conn); - } - public boolean isAdHocQuerySupported() { - return true; - } - -} \ No newline at end of file +} Modified: core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleFeatureInputStream.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleFeatureInputStream.java 2015-12-07 07:02:08 UTC (rev 4578) +++ core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleFeatureInputStream.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -1,113 +1,35 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ package com.vividsolutions.jump.datastore.oracle; -import com.vividsolutions.jump.feature.Feature; -import com.vividsolutions.jump.feature.FeatureSchema; -import com.vividsolutions.jump.io.BaseFeatureInputStream; - +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesFeatureInputStream; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesResultSetConverter; import java.sql.Connection; import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; /** - * Reads features from a PostgreSQL/PostGIS database. + * + * @author nicolas */ -public class OracleFeatureInputStream extends BaseFeatureInputStream { - - private FeatureSchema featureSchema; - private Connection conn; - private String queryString; - private boolean initialized = false; - private Exception savedException; - - private Statement stmt = null; - private ResultSet rs = null; - private OracleResultSetConverter mapper; - - int geometryColIndex = -1; - String externalIdentifier = null; // added on 2013-08-07 - +public class OracleFeatureInputStream extends SpatialDatabasesFeatureInputStream { public OracleFeatureInputStream(Connection conn, String queryString) { - this.conn = conn; - this.queryString = queryString; + super(conn, queryString); } public OracleFeatureInputStream(Connection conn, String queryString, String externalIdentifier) { - this.conn = conn; - this.queryString = queryString; - this.externalIdentifier = externalIdentifier; + super(conn, queryString, externalIdentifier); } - + /** - * @return The underlaying {@link Connection}. + * Returns a PostgisResultSetConverter + * @param rs + * @return */ - public Connection getConnection(){return conn;} - - /** - * @return The underlaying {@link Statement}. - * Useful to cancel the query on the server if the PlugIn is interrupted - */ - public Statement getStatement(){return stmt;} - - private void init() throws SQLException { - if (initialized) { - return; - } - initialized = true; - - stmt = conn.createStatement(); - String parsedQuery = queryString; - try { - rs = stmt.executeQuery(parsedQuery); - } catch (SQLException e) { - SQLException sqle = new SQLException("Error : " + parsedQuery); - sqle.setNextException(e); - throw sqle; - } - mapper = new OracleResultSetConverter(conn, rs); - featureSchema = mapper.getFeatureSchema(); - if (externalIdentifier != null) { - featureSchema.setExternalPrimaryKeyIndex(featureSchema.getAttributeIndex(externalIdentifier)); - } + @Override + protected SpatialDatabasesResultSetConverter getResultSetConverter(ResultSet rs) { + return new OracleResultSetConverter(conn, rs); } - - protected Feature readNext() throws Exception { - if (savedException != null) throw savedException; - if (! initialized) init(); - if (rs == null) return null; - if (! rs.next()) return null; - return getFeature(); - } - - private Feature getFeature() throws Exception { - return mapper.getFeature(); - } - - public void close() throws SQLException { - if (rs != null) { - rs.close(); - } - if (stmt != null) { - stmt.close(); - } - } - - public FeatureSchema getFeatureSchema() { - if (featureSchema != null) { - return featureSchema; - } - try { - init(); - } - catch (SQLException ex) { - String message = ex.getLocalizedMessage(); - Throwable nextT = ex.getNextException(); - if (nextT != null) message = message + "\n" + nextT.getLocalizedMessage(); - throw new Error(message); - } - if (featureSchema == null) { - featureSchema = new FeatureSchema(); - } - return featureSchema; - } -} \ No newline at end of file +} Modified: core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleResultSetConverter.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleResultSetConverter.java 2015-12-07 07:02:08 UTC (rev 4578) +++ core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleResultSetConverter.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -1,94 +1,27 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ package com.vividsolutions.jump.datastore.oracle; -import com.vividsolutions.jump.datastore.jdbc.ValueConverter; -import com.vividsolutions.jump.feature.AttributeType; -import com.vividsolutions.jump.feature.BasicFeature; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesResultSetConverter; import com.vividsolutions.jump.feature.Feature; import com.vividsolutions.jump.feature.FeatureSchema; - import java.sql.Connection; import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; /** - * Implements the mapping between a result set and - * a {@link FeatureSchema} and {@link Feature} set. + * Implements the mapping between a result set and a {@link FeatureSchema} and + * {@link Feature} set. * - * This is a transient worker class, whose lifetime - * should be no longer than the lifetime of the - * provided ResultSet + * This is a transient worker class, whose lifetime should be no longer than the + * lifetime of the provided ResultSet */ -public class OracleResultSetConverter -{ - private ResultSet rs; - private FeatureSchema featureSchema; - private int geometryColIndex = -1; - private ValueConverter[] mapper; - private OracleValueConverterFactory odm; - private boolean isInitialized = false; +public class OracleResultSetConverter extends SpatialDatabasesResultSetConverter { - public OracleResultSetConverter(Connection conn, ResultSet rs) - { - odm = new OracleValueConverterFactory(conn); - this.rs = rs; - } - - public FeatureSchema getFeatureSchema() - throws SQLException - { - init(); - return featureSchema; - } - - public Feature getFeature() - throws Exception - { - init(); - Feature f = new BasicFeature(featureSchema); - for (int i = 0; i < mapper.length; i++) { - f.setAttribute(i, mapper[i].getValue(rs, i + 1)); + public OracleResultSetConverter(Connection conn, ResultSet rs) { + super(conn, rs); + this.odm = new OracleValueConverterFactory(conn); } - return f; - } - - private void init() - throws SQLException - { - if (isInitialized) return; - isInitialized = true; - - ResultSetMetaData rsmd = rs.getMetaData(); - int numberOfColumns = rsmd.getColumnCount(); - //String[] columnNames = new String[numberOfColumns]; - //String[] columnTypeNames = new String[numberOfColumns]; - //int[] columnPositions = new int[numberOfColumns]; - mapper = new ValueConverter[numberOfColumns]; - featureSchema = new FeatureSchema(); - - for (int i = 0; i < numberOfColumns; i++) - { - mapper[i] = odm.getConverter(rsmd, i + 1); - String colName = rsmd.getColumnName(i + 1); - // only handles one geometry col for now [MD ?] - // Convert the first geometry into AttributeType.GEOMETRY and the following ones - // into AttributeType.STRINGs [mmichaud 2007-05-13] - if (mapper[i].getType() == AttributeType.GEOMETRY) { - if (featureSchema.getGeometryIndex() == -1) { - // fixed by mmichaud using a patch from jaakko [2008-05-21] : - // use colName instead of "GEOMETRY" for attribute name - featureSchema.addAttribute(colName, mapper[i].getType()); - } - else { - // other oracle geom columns are treated as text: - //mapper[i] = odm.; - featureSchema.addAttribute(colName, AttributeType.STRING); - } - } - else { - featureSchema.addAttribute(colName, mapper[i].getType()); - } - } - } - -} \ No newline at end of file +} Modified: core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleSQLBuilder.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleSQLBuilder.java 2015-12-07 07:02:08 UTC (rev 4578) +++ core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleSQLBuilder.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -1,49 +1,52 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ package com.vividsolutions.jump.datastore.oracle; import com.vividsolutions.jts.geom.Envelope; -import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jump.datastore.FilterQuery; import com.vividsolutions.jump.datastore.SpatialReferenceSystemID; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesDSMetadata; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesSQLBuilder; import com.vividsolutions.jump.workbench.JUMPWorkbench; -import java.util.Map; +import java.sql.SQLException; /** - * Creates SQL query strings for a PostGIS database + * Creates SQL query strings for a Spatial database. To be overloaded by classes + * implementing a spatial database support. */ -public class OracleSQLBuilder { +public class OracleSQLBuilder extends SpatialDatabasesSQLBuilder { - private SpatialReferenceSystemID defaultSRID = null; - private String[] colNames = null; - private Map<String, String> geoIndexes = null; - - public OracleSQLBuilder(SpatialReferenceSystemID defaultSRID, String[] colNames) { - this.defaultSRID = defaultSRID; - this.colNames = colNames; + public OracleSQLBuilder(SpatialDatabasesDSMetadata dbMetadata, + SpatialReferenceSystemID defaultSRID, String[] colNames) { + + super(dbMetadata, defaultSRID, colNames); + } - public OracleSQLBuilder(SpatialReferenceSystemID defaultSRID, String[] colNames, Map<String, String> geoIndexes) { - this(defaultSRID, colNames); - this.geoIndexes = geoIndexes; - } - + /** + * Builds a valid SQL spatial query with the given spatial filter. + * + * @param query + * @return a SQL query to get column names + */ + @Override public String getSQL(FilterQuery query) { - return buildQueryString(query); - } - - private String buildQueryString(FilterQuery query) { StringBuilder qs = new StringBuilder(); //HACK // surrond query by a rownum clause, used for limit - qs.append("SELECT ").append(getColumnListSpecifier(colNames, query.getGeometryAttributeName(), false)); + qs.append("SELECT ROWNUM, ").append(getColumnListSpecifier(colNames, query.getGeometryAttributeName())); qs.append(" FROM ( "); qs.append("SELECT "); - qs.append(getColumnListSpecifier(colNames, query.getGeometryAttributeName(), true)); + qs.append(getColumnListSpecifier(colNames, query.getGeometryAttributeName())); qs.append(" FROM "); // fixed by mmichaud on 2010-05-27 for mixed case dataset names qs.append("\"").append(query.getDatasetName().replaceAll("\\.", "\".\"")).append("\""); qs.append(" t WHERE "); - qs.append(buildBoxFilter(query.getGeometryAttributeName(), query.getSRSName(), query.getFilterGeometry())); + qs.append(buildBoxFilter(query)); String whereCond = query.getCondition(); if (whereCond != null) { @@ -51,7 +54,7 @@ qs.append(whereCond); } qs.append(")"); - + int limit = query.getLimit(); if (limit != 0 && limit != Integer.MAX_VALUE) { qs.append(" where ROWNUM <= ").append(limit); @@ -64,59 +67,18 @@ return qs.toString(); } - /** - * Buuilds a bbox filter only if geo column is indexed with a valid spatial index - * @param geometryColName - * @param SRID - * @param geom - * @return - */ - private String buildBoxFilter(String geometryColName, SpatialReferenceSystemID SRID, Geometry geom) { - StringBuilder buf = new StringBuilder("1=1"); - - if (this.geoIndexes != null && this.geoIndexes.containsKey(geometryColName)) { - buf = new StringBuilder(); - Envelope env = geom.getEnvelopeInternal(); + ; - // Example of Postgis SQL: where sdo_filter (geom, SDO_geometry(2003,2154,NULL,SDO_elem_info_array(1,1003,3),SDO_ordinate_array(100225,1002375,6549646,6810524))) = 'TRUE' - String srid = getSRID(SRID) == null ? "null" : getSRID(SRID); - - // fixed by mmichaud on 2010-05-27 for mixed case geometryColName names - buf.append("sdo_filter(\"").append(geometryColName).append("\" , SDO_geometry("); - buf.append("2003,").append(srid).append(",NULL,SDO_elem_info_array(1,1003,3),SDO_ordinate_array("); - buf.append(env.getMinX()).append(", ").append(env.getMinY()).append(", ").append(env.getMaxX()).append(", ").append(env.getMaxY()); - buf.append(srid).append(")))='TRUE'"); - - JUMPWorkbench.getInstance().getFrame().log( - "SQL query fragment to get spatial table BBOX filter:\n\t" - + buf.toString(), this.getClass()); - } - - return buf.toString(); - } - - private String getSRID(SpatialReferenceSystemID querySRID) { - SpatialReferenceSystemID srid = defaultSRID; - if (!querySRID.isNull()) { - srid = querySRID; - } - - if (srid.isNull() || srid.getString().trim().length() == 0) { - return null; - } else { - return srid.getString(); - } - } - - private String getColumnListSpecifier(String[] colNames, String geomColName, boolean addRownum) { - // Added double quotes around each column name in order to read mixed case table names - // correctly [mmichaud 2007-05-13] + /** + * Returns the string representing a SQL column definition. + * Implementors should take care of column names (case, quotes) + * @param colNames + * @param geomColName + * @return column list + */ + @Override + protected String getColumnListSpecifier(String[] colNames, String geomColName) { StringBuilder buf = new StringBuilder(); - // fixed by mmichaud using a patch from jaakko [2008-05-21] - // query geomColName as geomColName instead of geomColName as geomColName + "_wkb" - if (addRownum) { - buf.append("ROWNUM, "); - } buf.append(geomColName).append(" as ").append("\"").append(geomColName).append("\""); for (String colName : colNames) { if (!geomColName.equalsIgnoreCase(colName)) { @@ -125,4 +87,41 @@ } return buf.toString(); } + + @Override + protected String buildBoxFilter(FilterQuery query) { + StringBuilder buf = new StringBuilder("1=1"); + + // spatial query in Oracle is only available if geom column is indexed. + // This information is found during datastoreDSMetadata init. + // todo names can contain dots ? + // todo: oracle always have a schema name ? + try { + if (super.getDbMetadata().isIndexed(query.getDatasetName(), query.getGeometryAttributeName())) { + buf = new StringBuilder(); + Envelope env = query.getFilterGeometry().getEnvelopeInternal(); + + // Example of Postgis SQL: where sdo_filter (geom, SDO_geometry(2003,2154,NULL,SDO_elem_info_array(1,1003,3),SDO_ordinate_array(100225,1002375,6549646,6810524))) = 'TRUE' + String srid = getSRID(query.getSRSName()) == null ? "null" : getSRID(query.getSRSName()); + + // fixed by mmichaud on 2010-05-27 for mixed case geometryColName names + buf.append("sdo_filter(\"").append(query.getGeometryAttributeName()).append("\" , SDO_geometry("); + buf.append("2003,").append(srid).append(",NULL,SDO_elem_info_array(1,1003,3),SDO_ordinate_array("); + // force min/max values to avoid a ORA-01426: numeric overflow with some extents OJ can generate + buf.append(env.getMinX()).append(", ").append(env.getMinY()).append(", ").append(env.getMaxX()).append(", ").append(env.getMaxY()); + buf.append(srid).append(")))='TRUE'"); + + JUMPWorkbench.getInstance().getFrame().log( + "SQL query fragment to get spatial table BBOX filter:\n\t" + + buf.toString(), this.getClass()); + } + } catch (SQLException ex) { + JUMPWorkbench.getInstance().getFrame().log( + "cannot guess if geo column is indexed, error: "+ ex.getMessage(), this.getClass()); + //TODO: remove ? + ex.printStackTrace(); + } + + return buf.toString(); + } } Modified: core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleValueConverterFactory.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleValueConverterFactory.java 2015-12-07 07:02:08 UTC (rev 4578) +++ core/trunk/src/com/vividsolutions/jump/datastore/oracle/OracleValueConverterFactory.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -1,131 +1,88 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ package com.vividsolutions.jump.datastore.oracle; -import static com.vividsolutions.jump.datastore.oracle.OracleDataStoreDriver.JDBC_CLASS; - -import java.sql.*; -import java.io.*; +import com.vividsolutions.jts.io.ParseException; +import com.vividsolutions.jump.datastore.jdbc.ValueConverter; +import com.vividsolutions.jump.datastore.jdbc.ValueConverterFactory; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesValueConverterFactory; +import com.vividsolutions.jump.feature.AttributeType; +import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; -import com.vividsolutions.jump.feature.*; -import com.vividsolutions.jts.geom.*; -import com.vividsolutions.jts.io.*; -import com.vividsolutions.jump.datastore.jdbc.*; -//import oracle.jdbc.OracleConnection; -//import oracle.sql.STRUCT; -//import org.geotools.data.oracle.sdo.GeometryConverter; - /** * */ -public class OracleValueConverterFactory -{ - // should lazily init these - private final ValueConverter ORA_STRUCT_GEOMETRY_MAPPER = new OracleStructGeometryValueConverter(); - public final ValueConverter WKB_OBJECT_MAPPER = new OracleValueConverterFactory.WKBObjectValueConverter(); +public class OracleValueConverterFactory extends SpatialDatabasesValueConverterFactory { - private final Connection conn; - private final WKBReader wkbReader = new WKBReader(); - private final WKTReader wktReader = new WKTReader(); + protected final ValueConverter ORA_STRUCT_GEOMETRY_MAPPER = new OracleStructGeometryValueConverter(); + class OracleStructGeometryValueConverter implements ValueConverter { + + public AttributeType getType() { + return AttributeType.GEOMETRY; + } + + public Object getValue(ResultSet rs, int columnIndex) throws IOException, + SQLException, ParseException, ClassNotFoundException, + NoSuchMethodException, SecurityException, InstantiationException, + IllegalAccessException, IllegalArgumentException, + InvocationTargetException { + Object geometryObject = rs.getObject(columnIndex); + Class converterClazz = Class + .forName("org.geotools.data.oracle.sdo.GeometryConverter"); + Class connectionClazz = Class.forName("oracle.jdbc.OracleConnection"); + Class structClazz = Class.forName("oracle.sql.STRUCT"); + Method converterMethod = converterClazz.getMethod("asGeometry", + new Class[]{structClazz}); + + Constructor constructor = converterClazz + .getDeclaredConstructor(connectionClazz); + Object converter = constructor.newInstance(connectionClazz.cast(rs + .getStatement().getConnection())); + + return converterMethod.invoke(converter, structClazz.cast(geometryObject)); + + //** this is original implementation w/o reflection **// + // org.geotools.data.oracle.sdo.GeometryConverter geometryConverter = + // new org.geotools.data.oracle.sdo.GeometryConverter((oracle.jdbc.OracleConnection) + // rs.getStatement().getConnection()); + // return geometryConverter.asGeometry((oracle.sql.STRUCT) + // geometryObject); + } + } + public OracleValueConverterFactory(Connection conn) { - this.conn = conn; + super(conn); } + @Override public ValueConverter getConverter(ResultSetMetaData rsm, int columnIndex) - throws SQLException - { + throws SQLException { String classname = rsm.getColumnClassName(columnIndex); String dbTypeName = rsm.getColumnTypeName(columnIndex); if (dbTypeName.equalsIgnoreCase("MDSYS.SDO_GEOMETRY")) { - // WKB is now the normal way to store geometry in PostGIS [mmichaud 2007-05-13] - return ORA_STRUCT_GEOMETRY_MAPPER; + return ORA_STRUCT_GEOMETRY_MAPPER; } // handle the standard types ValueConverter stdConverter = ValueConverterFactory.getConverter(rsm, columnIndex); - if (stdConverter != null) + if (stdConverter != null) { return stdConverter; + } // default - can always show it as a string! return ValueConverterFactory.STRING_MAPPER; } - - class OracleStructGeometryValueConverter implements ValueConverter { - public AttributeType getType() { - return AttributeType.GEOMETRY; - } - - public Object getValue(ResultSet rs, int columnIndex) throws IOException, - SQLException, ParseException, ClassNotFoundException, - NoSuchMethodException, SecurityException, InstantiationException, - IllegalAccessException, IllegalArgumentException, - InvocationTargetException - { - Object geometryObject = rs.getObject(columnIndex); - Class converterClazz = Class - .forName("org.geotools.data.oracle.sdo.GeometryConverter"); - Class connectionClazz = Class.forName("oracle.jdbc.OracleConnection"); - Class structClazz = Class.forName("oracle.sql.STRUCT"); - Method converterMethod = converterClazz.getMethod("asGeometry", - new Class[] { structClazz }); - - Constructor constructor = converterClazz - .getDeclaredConstructor(connectionClazz); - Object converter = constructor.newInstance(connectionClazz.cast(rs - .getStatement().getConnection())); - - return converterMethod.invoke(converter, structClazz.cast(geometryObject)); - - //** this is original implementation w/o reflection **// - // org.geotools.data.oracle.sdo.GeometryConverter geometryConverter = - // new org.geotools.data.oracle.sdo.GeometryConverter((oracle.jdbc.OracleConnection) - // rs.getStatement().getConnection()); - // return geometryConverter.asGeometry((oracle.sql.STRUCT) - // geometryObject); - } - } - - class WKBObjectValueConverter implements ValueConverter - { - public AttributeType getType() { return AttributeType.OBJECT; } - public Object getValue(ResultSet rs, int columnIndex) - throws IOException, SQLException, ParseException - { - byte[] bytes = rs.getBytes(columnIndex); - - //so rs.getBytes will be one of two things: - //1. The actual bytes of the WKB if someone did ST_AsBinary - //2. The bytes of hex representation of the WKB. - - //in the case of #1, according to the WKB spec, the byte value - //can only be 0 or 1. - //in the case of #2, it's a hex string, so values range from ascii 0-F - //use this logic to determine how to process the bytes. - - Geometry geometry = null; - if(bytes == null || bytes.length <= 0) - { - geometry = wktReader.read("GEOMETRYCOLLECTION EMPTY"); - } - else - { - //assume it's the actual bytes (from ST_AsBinary) - byte[] realWkbBytes = bytes; - if(bytes[0] >= '0') - { - //ok, it's hex, convert hex string to actual bytes - String hexString = new String(bytes); - realWkbBytes = WKBReader.hexToBytes(hexString); - } - - geometry = wkbReader.read(realWkbBytes); - } - - return geometry; - } - } -} \ No newline at end of file +} Modified: core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisDSConnection.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisDSConnection.java 2015-12-07 07:02:08 UTC (rev 4578) +++ core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisDSConnection.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -1,46 +1,37 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ package com.vividsolutions.jump.datastore.postgis; -import com.vividsolutions.jump.I18N; -import com.vividsolutions.jump.datastore.*; +import com.vividsolutions.jump.datastore.FilterQuery; +import com.vividsolutions.jump.datastore.SpatialReferenceSystemID; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesDSConnection; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesDSMetadata; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesFeatureInputStream; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesSQLBuilder; import com.vividsolutions.jump.io.FeatureInputStream; - import java.sql.Connection; import java.sql.SQLException; /** + * + * @author nicolas */ -public class PostgisDSConnection implements DataStoreConnection { +public class PostgisDSConnection extends SpatialDatabasesDSConnection { - private PostgisDSMetadata dbMetadata; - private Connection connection; - - public PostgisDSConnection(Connection conn) { - connection = conn; - dbMetadata = new PostgisDSMetadata(this); + public PostgisDSConnection(Connection con) { + super(con); // ? + connection = con; + this.dbMetadata = new PostgisDSMetadata(this); } - - public Connection getConnection() { - return connection; + + @Override + public SpatialDatabasesSQLBuilder getSqlBuilder(SpatialReferenceSystemID srid, String[] colNames) { + return new PostgisSQLBuilder(this.dbMetadata, srid, colNames); } - public DataStoreMetadata getMetadata() { - return dbMetadata; - } - - public FeatureInputStream execute(Query query) throws Exception { - if (query instanceof FilterQuery) { - try { - return executeFilterQuery((FilterQuery) query); - } catch (SQLException e) { - throw new RuntimeException(e); - } - } - if (query instanceof AdhocQuery) { - return executeAdhocQuery((AdhocQuery) query); - } - throw new IllegalArgumentException(I18N.get(this.getClass().getName()+".unsupported-query-type")); - } - /** * Executes a filter query. * @@ -51,41 +42,10 @@ * @return the results of the query * @throws SQLException */ + @Override public FeatureInputStream executeFilterQuery(FilterQuery query) throws SQLException { - - SpatialReferenceSystemID srid = dbMetadata.getSRID(query.getDatasetName(), query.getGeometryAttributeName()); - String[] colNames = dbMetadata.getColumnNames(query.getDatasetName()); - - PostgisSQLBuilder builder = new PostgisSQLBuilder(srid, colNames); - String queryString = builder.getSQL(query); - - // [mmichaud 2013-08-07] add a parameter for database primary key name - return new PostgisFeatureInputStream(connection, queryString, query.getPrimaryKey()); + SpatialDatabasesFeatureInputStream fis = (SpatialDatabasesFeatureInputStream)super.executeFilterQuery(query); + return new PostgisFeatureInputStream(fis.getConnection(), fis.getQueryString(), query.getPrimaryKey()); } - - public FeatureInputStream executeAdhocQuery(AdhocQuery query) throws Exception { - String queryString = query.getQuery(); - PostgisFeatureInputStream ifs = new PostgisFeatureInputStream(connection, queryString, query.getPrimaryKey()); - if (ifs.getFeatureSchema().getGeometryIndex() < 0) { - throw new Exception(I18N.get(this.getClass().getName()+".resultset-must-have-a-geometry-column")); - } - return ifs; - } - - - public void close() throws DataStoreException { - try { - connection.close(); - } - catch (Exception ex) { throw new DataStoreException(ex); } - } - - public boolean isClosed() throws DataStoreException { - try { - return connection.isClosed(); - } catch (SQLException e) { - throw new DataStoreException(e); - } - } - -} \ No newline at end of file + +} Modified: core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisDSMetadata.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisDSMetadata.java 2015-12-07 07:02:08 UTC (rev 4578) +++ core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisDSMetadata.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -1,294 +1,54 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ package com.vividsolutions.jump.datastore.postgis; -import com.vividsolutions.jts.geom.Envelope; -import com.vividsolutions.jts.geom.Geometry; -import com.vividsolutions.jts.io.WKBReader; -import com.vividsolutions.jump.datastore.DataStoreMetadata; +import com.vividsolutions.jump.datastore.spatialdatabases.*; +import com.vividsolutions.jump.datastore.DataStoreConnection; import com.vividsolutions.jump.datastore.GeometryColumn; -import com.vividsolutions.jump.datastore.PrimaryKeyColumn; -import com.vividsolutions.jump.datastore.SpatialReferenceSystemID; -import com.vividsolutions.jump.datastore.jdbc.JDBCUtil; -import com.vividsolutions.jump.datastore.jdbc.ResultSetBlock; +import java.util.List; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.*; +public class PostgisDSMetadata extends SpatialDatabasesDSMetadata { -public class PostgisDSMetadata implements DataStoreMetadata { - - private final WKBReader reader = new WKBReader(); - - private PostgisDSConnection conn; - - private Map sridMap = new HashMap(); - - public PostgisDSMetadata( PostgisDSConnection conn ) { - this.conn = conn; - } - - public String[] getDatasetNames() { - final List datasetNames = new ArrayList(); - // Spatial tables only. - try { - JDBCUtil.execute( - conn.getConnection(), - "SELECT DISTINCT f_table_schema, f_table_name FROM geometry_columns ORDER BY f_table_schema, f_table_name", - new ResultSetBlock() { - public void yield( ResultSet resultSet ) throws SQLException { - while ( resultSet.next() ) { - String schema = resultSet.getString( 1 ); - String table = resultSet.getString( 2 ); - if ( !schema.equalsIgnoreCase( "public" ) ) { - table = schema + "." + table; - } - datasetNames.add( table ); - } - } - }); - } catch (Exception e) { - if (! e.getMessage().contains("geometry_columns")) { - e.printStackTrace(); - } else { - System.out.println("not a postGIS db or bad search_path"); - } + public PostgisDSMetadata(DataStoreConnection con) { + conn = con; + // TODO: use bind parameters to avoid SQL injection + datasetNameQuery = "SELECT DISTINCT f_table_schema, f_table_name FROM geometry_columns"; + defaultSchemaName = "public"; + spatialDbName = "PostGIS"; + spatialExtentQuery1 = "SELECT ST_AsBinary(ST_Estimated_Extent( '%s', '%s', '%s' ))"; + spatialExtentQuery2 = "SELECT ST_AsBinary(ST_Envelope(ST_Extent(%s))) FROM %s.%s"; + geoColumnsQuery = "SELECT f_geometry_column, srid, type FROM geometry_columns where f_table_schema='%s' and f_table_name = '%s'"; + sridQuery = "SELECT srid FROM geometry_columns where f_table_schema = '%s' and f_table_name = '%s' and f_geometry_column = '%s'"; } - return (String[]) datasetNames.toArray(new String[datasetNames.size()]); - } - public Envelope getExtents(String datasetName, String attributeName) { - - final Envelope[] e = new Envelope[]{null}; - - String schema; - String table; - if (datasetName.indexOf('.') != -1) { - String[] parts = datasetName.split("\\.", 2); - schema = parts[0]; - table = parts[1]; - } else { - schema = "public"; - table = datasetName; + @Override + public String getSpatialExtentQuery1(String schema, String table, String attributeName) { + return String.format(this.spatialExtentQuery1, schema, table, attributeName); } - // There are two methods to compute the extent, - // ST_EstimatedExtent is fast but not precise (last entered data may be excluded - // until the next ANALYZE) - // ST_Extent is slow but precise (scan over the whole table) - String sql1 = "SELECT ST_AsBinary(ST_Estimated_Extent( '" + schema + "', '" + table + "', '" + attributeName + "' ));"; - String sql2 = "SELECT ST_AsBinary(ST_Envelope(ST_Extent(\"" + attributeName + "\"))) FROM \"" + schema + "\".\"" + table + "\";"; - final ResultSetBlock resultSetBlock = new ResultSetBlock() { - public void yield(ResultSet resultSet) throws Exception { - if (resultSet.next()) { - byte[] bytes = (byte[]) resultSet.getObject(1); - if (bytes != null) { - Geometry geom = reader.read(bytes); - if (geom != null) { - e[0] = geom.getEnvelopeInternal(); - } - } - } - } - }; - try { - JDBCUtil.execute(conn.getConnection(), (sql1), resultSetBlock); - if (e[0] == null || e[0].isNull()) { - JDBCUtil.execute(conn.getConnection(), (sql2), resultSetBlock); - } - } catch (Exception ex1) { - // If attributeName is indexed but indexed has not been initialized - // (no VACUUM ANALYZE) ST_Estimated_Extent may throw an exception. - // In this case, try the second method using ST_Extent - JDBCUtil.execute(conn.getConnection(), sql2, resultSetBlock); + @Override + public String getSpatialExtentQuery2(String schema, String table, String attributeName) { + return String.format(this.spatialExtentQuery2, attributeName, schema, table); } - return e[0]; - } - @Deprecated - public SpatialReferenceSystemID getSRID(String tableName, String colName) - throws SQLException { - String key = tableName + "#" + colName; - if (!sridMap.containsKey(key)) { - // not in cache, so query it - String srid = querySRID(tableName, colName); - sridMap.put(key, new SpatialReferenceSystemID(srid)); + @Override + public String getGeoColumnsQuery(String datasetName) { + return String.format(this.geoColumnsQuery, getSchemaName(datasetName), getTableName(datasetName)); } - return (SpatialReferenceSystemID) sridMap.get(key); - } - @Deprecated - private String querySRID(String tableName, String colName) { - final StringBuffer srid = new StringBuffer(); - // Changed by Michael Michaud 2010-05-26 (throwed exception for empty tableName) - // String sql = "SELECT getsrid(" + colName + ") FROM " + tableName + " LIMIT 1"; - String[] tokens = tableName.split("\\.", 2); - String schema = tokens.length == 2 ? tokens[0] : "public"; - String table = tokens.length == 2 ? tokens[1] : tableName; - String sql = "SELECT srid FROM geometry_columns where (f_table_schema = '" + schema + "' and f_table_name = '" + table + "')"; - // End of the fix - JDBCUtil.execute(conn.getConnection(), sql, new ResultSetBlock() { - public void yield(ResultSet resultSet) throws SQLException { - if (resultSet.next()) { - srid.append(resultSet.getString(1)); - } - } - }); - - return srid.toString(); - } - - @Deprecated - public String[] getGeometryAttributeNames(String datasetName) { - final List geometryAttributeNames = new ArrayList(); - String sql = "SELECT f_geometry_column FROM geometry_columns " - + geomColumnMetadataWhereClause("f_table_schema", "f_table_name", datasetName); - JDBCUtil.execute( - conn.getConnection(), sql, - new ResultSetBlock() { - public void yield(ResultSet resultSet) throws SQLException { - while (resultSet.next()) { - geometryAttributeNames.add(resultSet.getString(1)); - } - } - }); - return (String[]) geometryAttributeNames.toArray(new String[geometryAttributeNames.size()]); - } - - public List<GeometryColumn> getGeometryAttributes(String datasetName) { - final List<GeometryColumn> geometryAttributes = new ArrayList<GeometryColumn>(); - String sql = "SELECT f_geometry_column, srid, type FROM geometry_columns " - + geomColumnMetadataWhereClause("f_table_schema", "f_table_name", datasetName); - JDBCUtil.execute( - conn.getConnection(), sql, - new ResultSetBlock() { - public void yield(ResultSet resultSet) throws SQLException { - while (resultSet.next()) { - geometryAttributes.add(new GeometryColumn( - resultSet.getString(1), - resultSet.getInt(2), - resultSet.getString(3))); - } - } - }); - return geometryAttributes; - } - - /** - * Returns PRIMARY KEY columns of dataset names. - * - * @param datasetName name of the table (optionally prefixed by the schema - * name) - * @return the list of columns involved in the Primary Key (generally, a - * single column) - */ - public List<PrimaryKeyColumn> getPrimaryKeyColumns(String datasetName) { - final List<PrimaryKeyColumn> identifierColumns = new ArrayList<PrimaryKeyColumn>(); - // query taken from http://www.alberton.info/postgresql_meta_info.html#.UewsFG29b0Q - String sql - = "SELECT kcu.column_name, col.data_type" - + " FROM information_schema.table_constraints tc" - + " LEFT JOIN information_schema.key_column_usage kcu" - + " ON tc.constraint_catalog = kcu.constraint_catalog" - + " AND tc.constraint_schema = kcu.constraint_schema" - + " AND tc.constraint_name = kcu.constraint_name" - + " LEFT JOIN information_schema.referential_constraints rc" - + " ON tc.constraint_catalog = rc.constraint_catalog" - + " AND tc.constraint_schema = rc.constraint_schema" - + " AND tc.constraint_name = rc.constraint_name" - + " LEFT JOIN information_schema.constraint_column_usage ccu" - + " ON rc.unique_constraint_catalog = ccu.constraint_catalog" - + " AND rc.unique_constraint_schema = ccu.constraint_schema" - + " AND rc.unique_constraint_name = ccu.constraint_name" - + " LEFT JOIN information_schema.columns col" - + " ON kcu.table_catalog = col.table_catalog" - + " AND kcu.table_schema = col.table_schema" - + " AND kcu.table_name = col.table_name" - + " AND kcu.column_name = col.column_name " - + geomColumnMetadataWhereClause("col.table_schema", "col.table_name", datasetName) - + " AND tc.constraint_type = 'PRIMARY KEY';"; - JDBCUtil.execute( - conn.getConnection(), sql, - new ResultSetBlock() { - public void yield(ResultSet resultSet) throws SQLException { - while (resultSet.next()) { - identifierColumns.add(new PrimaryKeyColumn( - resultSet.getString(1), - resultSet.getString(2))); - } - } - }); - return identifierColumns; - } - - public String[] getColumnNames(String datasetName) { - String sql = "SELECT column_name FROM information_schema.columns " - + geomColumnMetadataWhereClause("table_schema", "table_name", datasetName); - ColumnNameBlock block = new ColumnNameBlock(); - JDBCUtil.execute(conn.getConnection(), sql, block); - return block.colName; - } - - private String geomColumnMetadataWhereClause(String schemaCol, String tableCol, String tableName) { - // [mmichaud 2011-07-24] Fixed a bug related to tables having common - // names in public schema and another schema - int dotPos = tableName.indexOf("."); - String schema = "public"; - String table = tableName; - if (dotPos != -1) { - schema = tableName.substring(0, dotPos); - table = tableName.substring(dotPos + 1); + @Override + public String getSridQuery(String schemaName, String tableName, String colName) { + // TODO + return String.format(this.sridQuery, schemaName, tableName, colName); } - return "WHERE " + schemaCol + " = '" + schema + "'" - + " AND " + tableCol + " = '" + table + "'"; - } - - /** - * Returns whether column is used by a spatial index (Gist) or not. - */ - public boolean isIndexed(final String schema, final String table, - final String column) throws SQLException { - String sql = getGeometryIndicesQuery(schema, table, column); - return conn.getConnection().createStatement().executeQuery(sql).next(); - } - - private static class ColumnNameBlock implements ResultSetBlock { - - List colList = new ArrayList(); - String[] colName; - - public void yield(ResultSet resultSet) throws SQLException { - while (resultSet.next()) { - colList.add(resultSet.getString(1)); - } - colName = (String[]) colList.toArray(new String[colList.size()]); + + @Override + public List<GeometryColumn> getGeometryAttributes(String datasetName) { + String sql = this.getGeoColumnsQuery(datasetName); + return getGeometryAttributes(sql, datasetName); } - } - /** - * Use PostgreSQL metadata to know if schema.table.column is spatially indexed - * (or involved in a spatial index). - * - * @deprecated initially thought that statistic used by ST_Estimated_Extent - * was depending on whether a spatial index exists or not. Finally, statistics - * and index seem to be two independant things - */ - @Deprecated - public String getGeometryIndicesQuery(String schema, String table, String column) { - return "SELECT n.nspname, t.relname, a.attname, c.relname\n" - + "FROM pg_index i\n" - + "-- join indexes in pg_class to filter gist indexes (783 and 4000)\n" - + "JOIN pg_class c ON i.indexrelid = c.oid\n" - + "-- join tables in pg_class to get the table name\n" - + "JOIN pg_class t ON i.indrelid = t.oid\n" - + "-- join tables in pg_tables to get the schema name\n" - + "JOIN pg_namespace n ON t.relnamespace = n.oid\n" - + "-- join attributes to check geometry involved in the index\n" - + "JOIN pg_attribute a ON (a.attrelid = t.oid AND a.attnum = ANY(i.indkey))\n" - + "-- limit the search in pg_class to index objects\n" - + "WHERE c.relkind = 'i'\n" - + "AND (c.relam = 783 OR c.relam = 4000)\n" - + "AND n.nspname = '" + schema + "'\n" - + "AND t.relname = '" + table + "'\n" - + "AND a.attname = '" + column + "';"; - } - } Modified: core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisDataStoreDriver.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisDataStoreDriver.java 2015-12-07 07:02:08 UTC (rev 4578) +++ core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisDataStoreDriver.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -1,97 +1,36 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ package com.vividsolutions.jump.datastore.postgis; -import java.sql.*; - -import com.vividsolutions.jump.datastore.*; - +import com.vividsolutions.jump.datastore.DataStoreConnection; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesDataStoreDriver; import com.vividsolutions.jump.parameter.ParameterList; -import com.vividsolutions.jump.parameter.ParameterListSchema; /** - * A driver for supplying {@link PostgisDSConnection}s + * A driver for supplying {@link SpatialDatabaseDSConnection}s */ public class PostgisDataStoreDriver - implements DataStoreDriver -{ - public static final String DRIVER_NAME = "PostGIS"; - public static final String JDBC_CLASS = "org.postgresql.Driver"; - public static final String URL_PREFIX = "jdbc:postgresql://"; + extends SpatialDatabasesDataStoreDriver { - public static final String PARAM_Server = "Server"; - public static final String PARAM_Port = "Port"; - public static final String PARAM_Instance = "Database"; - public static final String PARAM_User = "User"; - public static final String PARAM_Password = "Password"; - - private static final String[] paramNames = new String[] { - PARAM_Server, - PARAM_Port, - PARAM_Instance, - PARAM_User, - PARAM_Password - }; - private static final Class[] paramClasses = new Class[] - { - String.class, - Integer.class, - String.class, - String.class, - String.class - }; - private final ParameterListSchema schema = new ParameterListSchema(paramNames, paramClasses); - - public PostgisDataStoreDriver() { - } - - public String getName() - { - return DRIVER_NAME; - } - public ParameterListSchema getParameterListSchema() - { - return schema; - } - public DataStoreConnection createConnection(ParameterList params) - throws Exception - { - String host = params.getParameterString(PARAM_Server); - int port = params.getParameterInt(PARAM_Port); - String database = params.getParameterString(PARAM_Instance); - String user = params.getParameterString(PARAM_User); - String password = params.getParameterString(PARAM_Password); - - String url - = String.valueOf(new StringBuffer(URL_PREFIX).append - (host).append - (":").append - (port).append - ("/").append(database)); - - Driver driver = (Driver) Class.forName(JDBC_CLASS).newInstance(); - DriverManager.registerDriver(driver); - - // mmichaud 2013-08-27 workaround for ticket #330 - String savePreferIPv4Stack = System.getProperty("java.net.preferIPv4Stack"); - String savePreferIPv6Addresses = System.getProperty("java.net.preferIPv6Addresses"); - System.setProperty("java.net.preferIPv4Stack", "true"); - System.setProperty("java.net.preferIPv6Addresses", "false"); - - Connection conn = DriverManager.getConnection(url, user, password); - - if (savePreferIPv4Stack == null) { - System.getProperties().remove("java.net.preferIPv4Stack"); - } else { - System.setProperty("java.net.preferIPv4Stack", savePreferIPv4Stack); + public PostgisDataStoreDriver() { + this.driverName = "PostGIS"; + this.jdbcClass = "org.postgresql.Driver"; + this.urlPrefix = "jdbc:postgresql://"; } - if (savePreferIPv6Addresses == null) { - System.getProperties().remove("java.net.preferIPv6Addresses"); - } else { - System.setProperty("java.net.preferIPv6Addresses", savePreferIPv6Addresses); + + /** + * returns the right type of DataStoreConnection + * @param params + * @return + * @throws Exception + */ + @Override + public DataStoreConnection createConnection(ParameterList params) + throws Exception { + DataStoreConnection ret = super.createConnection(params); + return new PostgisDSConnection(ret.getConnection()); } - return new PostgisDSConnection(conn); - } - public boolean isAdHocQuerySupported() { - return true; - } - -} \ No newline at end of file +} Modified: core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisFeatureInputStream.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisFeatureInputStream.java 2015-12-07 07:02:08 UTC (rev 4578) +++ core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisFeatureInputStream.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -1,113 +1,35 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ package com.vividsolutions.jump.datastore.postgis; -import com.vividsolutions.jump.feature.Feature; -import com.vividsolutions.jump.feature.FeatureSchema; -import com.vividsolutions.jump.io.BaseFeatureInputStream; - +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesFeatureInputStream; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesResultSetConverter; import java.sql.Connection; import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; /** - * Reads features from a PostgreSQL/PostGIS database. + * + * @author nicolas */ -public class PostgisFeatureInputStream extends BaseFeatureInputStream { - - private FeatureSchema featureSchema; - private Connection conn; - private String queryString; - private boolean initialized = false; - private Exception savedException; - - private Statement stmt = null; - private ResultSet rs = null; - private PostgisResultSetConverter mapper; - - int geometryColIndex = -1; - String externalIdentifier = null; // added on 2013-08-07 - +public class PostgisFeatureInputStream extends SpatialDatabasesFeatureInputStream { public PostgisFeatureInputStream(Connection conn, String queryString) { - this.conn = conn; - this.queryString = queryString; + super(conn, queryString); } public PostgisFeatureInputStream(Connection conn, String queryString, String externalIdentifier) { - this.conn = conn; - this.queryString = queryString; - this.externalIdentifier = externalIdentifier; + super(conn, queryString, externalIdentifier); } - + /** - * @return The underlaying {@link Connection}. + * Returns a PostgisResultSetConverter + * @param rs + * @return */ - public Connection getConnection(){return conn;} - - /** - * @return The underlaying {@link Statement}. - * Useful to cancel the query on the server if the PlugIn is interrupted - */ - public Statement getStatement(){return stmt;} - - private void init() throws SQLException { - if (initialized) { - return; - } - initialized = true; - - stmt = conn.createStatement(); - String parsedQuery = queryString; - try { - rs = stmt.executeQuery(parsedQuery); - } catch (SQLException e) { - SQLException sqle = new SQLException("Error : " + parsedQuery); - sqle.setNextException(e); - throw sqle; - } - mapper = new PostgisResultSetConverter(conn, rs); - featureSchema = mapper.getFeatureSchema(); - if (externalIdentifier != null) { - featureSchema.setExternalPrimaryKeyIndex(featureSchema.getAttributeIndex(externalIdentifier)); - } + @Override + protected SpatialDatabasesResultSetConverter getResultSetConverter(ResultSet rs) { + return new PostgisResultSetConverter(conn, rs); } - - protected Feature readNext() throws Exception { - if (savedException != null) throw savedException; - if (! initialized) init(); - if (rs == null) return null; - if (! rs.next()) return null; - return getFeature(); - } - - private Feature getFeature() throws Exception { - return mapper.getFeature(); - } - - public void close() throws SQLException { - if (rs != null) { - rs.close(); - } - if (stmt != null) { - stmt.close(); - } - } - - public FeatureSchema getFeatureSchema() { - if (featureSchema != null) { - return featureSchema; - } - try { - init(); - } - catch (SQLException ex) { - String message = ex.getLocalizedMessage(); - Throwable nextT = ex.getNextException(); - if (nextT != null) message = message + "\n" + nextT.getLocalizedMessage(); - throw new Error(message); - } - if (featureSchema == null) { - featureSchema = new FeatureSchema(); - } - return featureSchema; - } -} \ No newline at end of file +} Modified: core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisResultSetConverter.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisResultSetConverter.java 2015-12-07 07:02:08 UTC (rev 4578) +++ core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisResultSetConverter.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -1,83 +1,27 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ package com.vividsolutions.jump.datastore.postgis; -import com.vividsolutions.jump.datastore.jdbc.ValueConverter; -import com.vividsolutions.jump.datastore.jdbc.ValueConverterFactory; -import com.vividsolutions.jump.feature.AttributeType; -import com.vividsolutions.jump.feature.BasicFeature; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesResultSetConverter; import com.vividsolutions.jump.feature.Feature; import com.vividsolutions.jump.feature.FeatureSchema; - import java.sql.Connection; import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; /** - * Implements the mapping between a result set and - * a {@link FeatureSchema} and {@link Feature} set. + * Implements the mapping between a result set and a {@link FeatureSchema} and + * {@link Feature} set. * - * This is a transient worker class, whose lifetime - * should be no longer than the lifetime of the - * provided ResultSet + * This is a transient worker class, whose lifetime should be no longer than the + * lifetime of the provided ResultSet */ -public class PostgisResultSetConverter { +public class PostgisResultSetConverter extends SpatialDatabasesResultSetConverter { - private ResultSet rs; - private FeatureSchema featureSchema; - private int geometryColIndex = -1; - private ValueConverter[] mapper; - private PostgisValueConverterFactory odm; - private boolean isInitialized = false; - public PostgisResultSetConverter(Connection conn, ResultSet rs) { - odm = new PostgisValueConverterFactory(conn); - this.rs = rs; + super(conn, rs); + this.odm = new PostgisValueConverterFactory(conn); } - - public FeatureSchema getFeatureSchema() throws SQLException { - init(); - return featureSchema; - } - - public Feature getFeature() throws Exception { - init(); - Feature f = new BasicFeature(featureSchema); - for (int i = 0; i < mapper.length; i++) { - f.setAttribute(i, mapper[i].getValue(rs, i + 1)); - } - return f; - } - - private void init() throws SQLException { - if (isInitialized) return; - isInitialized = true; - - ResultSetMetaData rsmd = rs.getMetaData(); - int numberOfColumns = rsmd.getColumnCount(); - mapper = new ValueConverter[numberOfColumns]; - featureSchema = new FeatureSchema(); - - for (int i = 0; i < numberOfColumns; i++) { - mapper[i] = odm.getConverter(rsmd, i + 1); - String colName = rsmd.getColumnName(i + 1); - // only handles one geometry col for now [MD ?] - // Convert the first geometry into AttributeType.GEOMETRY and the following ones - // into AttributeType.STRINGs [mmichaud 2007-05-13] - if (mapper[i].getType() == AttributeType.GEOMETRY) { - if (featureSchema.getGeometryIndex() == -1) { - // fixed by mmichaud using a patch from jaakko [2008-05-21] : - // use colName instead of "GEOMETRY" for attribute name - featureSchema.addAttribute(colName, AttributeType.GEOMETRY); - } - else { - mapper[i] = new ValueConverterFactory.ObjectConverter(); - featureSchema.addAttribute(colName, AttributeType.OBJECT); - } - } - else { - featureSchema.addAttribute(colName, mapper[i].getType()); - } - } - } - -} \ No newline at end of file +} Modified: core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisSQLBuilder.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisSQLBuilder.java 2015-12-07 07:02:08 UTC (rev 4578) +++ core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisSQLBuilder.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -1,28 +1,36 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ package com.vividsolutions.jump.datastore.postgis; import com.vividsolutions.jts.geom.Envelope; -import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jump.datastore.FilterQuery; import com.vividsolutions.jump.datastore.SpatialReferenceSystemID; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesDSMetadata; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesSQLBuilder; + /** - * Creates SQL query strings for a PostGIS database + * Creates SQL query strings for a Spatial database. + * To be overloaded by classes implementing a spatial database support. */ -public class PostgisSQLBuilder { +public class PostgisSQLBuilder extends SpatialDatabasesSQLBuilder { - private SpatialReferenceSystemID defaultSRID = null; - private String[] colNames = null; - - public PostgisSQLBuilder(SpatialReferenceSystemID defaultSRID, String[] colNames) { - this.defaultSRID = defaultSRID; - this.colNames = colNames; + public PostgisSQLBuilder(SpatialDatabasesDSMetadata dbMetadata, + SpatialReferenceSystemID defaultSRID, String[] colNames) { + super(dbMetadata, defaultSRID, colNames); } + /** + * Builds a valid SQL spatial query with the given spatial filter. + * @param query + * @return a SQL query to get column names + * //TODO: refactor like Oracle code: queries as variable placeholders: put it in base class. + */ + @Override public String getSQL(FilterQuery query) { - return buildQueryString(query); - } - - private String buildQueryString(FilterQuery query) { StringBuilder qs = new StringBuilder(); //HACK qs.append("SELECT "); @@ -31,7 +39,7 @@ // fixed by mmichaud on 2010-05-27 for mixed case dataset names qs.append("\"").append(query.getDatasetName().replaceAll("\\.","\".\"")).append("\""); qs.append(" t WHERE "); - qs.append(buildBoxFilter(query.getGeometryAttributeName(), query.getSRSName(), query.getFilterGeometry())); + qs.append(buildBoxFilter(query)); String whereCond = query.getCondition(); if (whereCond != null) { @@ -44,15 +52,39 @@ } //System.out.println(qs); return qs.toString(); + }; + + /** + * Returns the string representing a SQL column definition. + * Implementors should take care of column names (case, quotes) + * @param colNames + * @param geomColName + * @return column list + */ + @Override + protected String getColumnListSpecifier(String[] colNames, String geomColName) { + // Added double quotes around each column name in order to read mixed case table names + // correctly [mmichaud 2007-05-13] + StringBuilder buf = new StringBuilder(); + // fixed by mmichaud using a patch from jaakko [2008-05-21] + // query geomColName as geomColName instead of geomColName as geomColName + "_wkb" + buf.append("ST_AsEWKB(\"").append(geomColName).append("\") as ").append("\"").append(geomColName).append("\""); + for (String colName : colNames) { + if (! geomColName.equalsIgnoreCase(colName)) { + buf.append(",\"").append(colName).append("\""); + } + } + return buf.toString(); } - private String buildBoxFilter(String geometryColName, SpatialReferenceSystemID SRID, Geometry geom) { - Envelope env = geom.getEnvelopeInternal(); + @Override + protected String buildBoxFilter(FilterQuery query) { + Envelope env = query.getFilterGeometry().getEnvelopeInternal(); // Example of Postgis SQL: GEOM && SetSRID('BOX3D(191232 243117,191232 243119)'::box3d,-1); StringBuilder buf = new StringBuilder(); // fixed by mmichaud on 2010-05-27 for mixed case geometryColName names - buf.append("\"").append(geometryColName).append("\" && ST_SetSRID('BOX3D("); + buf.append("\"").append(query.getGeometryAttributeName()).append("\" && ST_SetSRID('BOX3D("); buf.append(env.getMinX() + " " + env.getMinY() + "," + env.getMaxX() @@ -61,35 +93,9 @@ buf.append(")'::box3d,"); // [mmichaud 2012-03-14] make windows srid homogeneous with geometry srid // in case it is not defined - String srid = getSRID(SRID); - srid = srid==null? "ST_SRID(\"" + geometryColName + "\")" : srid; + String srid = getSRID(query.getSRSName()); + srid = srid==null? "ST_SRID(\"" + query.getGeometryAttributeName() + "\")" : srid; buf.append(srid).append(")"); return buf.toString(); } - - private String getSRID(SpatialReferenceSystemID querySRID) { - SpatialReferenceSystemID srid = defaultSRID; - if (! querySRID.isNull()) - srid = querySRID; - - if (srid.isNull() || srid.getString().trim().length()==0) - return null; - else - return srid.getString(); - } - - private String getColumnListSpecifier(String[] colNames, String geomColName) { - // Added double quotes around each column name in order to read mixed case table names - // correctly [mmichaud 2007-05-13] - StringBuilder buf = new StringBuilder(); - // fixed by mmichaud using a patch from jaakko [2008-05-21] - // query geomColName as geomColName instead of geomColName as geomColName + "_wkb" - buf.append("ST_AsEWKB(\"").append(geomColName).append("\") as ").append("\"").append(geomColName).append("\""); - for (String colName : colNames) { - if (! geomColName.equalsIgnoreCase(colName)) { - buf.append(",\"").append(colName).append("\""); - } - } - return buf.toString(); - } } Modified: core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisValueConverterFactory.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisValueConverterFactory.java 2015-12-07 07:02:08 UTC (rev 4578) +++ core/trunk/src/com/vividsolutions/jump/datastore/postgis/PostgisValueConverterFactory.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -1,90 +1,50 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ package com.vividsolutions.jump.datastore.postgis; -import java.sql.*; -import java.io.*; -import com.vividsolutions.jump.feature.*; -import com.vividsolutions.jts.geom.*; -import com.vividsolutions.jts.io.*; -import com.vividsolutions.jump.datastore.jdbc.*; +import com.vividsolutions.jump.datastore.jdbc.ValueConverter; +import com.vividsolutions.jump.datastore.jdbc.ValueConverterFactory; +import com.vividsolutions.jump.datastore.spatialdatabases.SpatialDatabasesValueConverterFactory; +import java.sql.Connection; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; /** * */ -public class PostgisValueConverterFactory { +public class PostgisValueConverterFactory extends SpatialDatabasesValueConverterFactory { - // should lazily init these - private final ValueConverter WKB_GEOMETRY_MAPPER = new WKBGeometryValueConverter(); - - private final Connection conn; - private static final WKBReader wkbReader = new WKBReader(); - private static final WKTReader wktReader = new WKTReader(); - public PostgisValueConverterFactory(Connection conn) { - this.conn = conn; - } + super(conn); + } - public ValueConverter getConverter(ResultSetMetaData rsm, int columnIndex) throws SQLException { + @Override + public ValueConverter getConverter(ResultSetMetaData rsm, int columnIndex) + throws SQLException { String classname = rsm.getColumnClassName(columnIndex); String dbTypeName = rsm.getColumnTypeName(columnIndex); - ResultSet rs = conn.getMetaData().getColumns( - rsm.getCatalogName(columnIndex), - rsm.getSchemaName(columnIndex), - rsm.getTableName(columnIndex), - rsm.getColumnName(columnIndex)); - String udt = rs.next() ? rs.getString("TYPE_NAME") : ""; - //System.out.println(dbTypeName + " (" + classname + ") [" + udt + "]"); - // A geometry column created with addGeometryColumn has the ColumnTypeName = geometry - // but if column is created from a statement like "create table2 as select * from table1" - // its ColumnTypeName is bytea even if its UDT is geometry - if ("geometry".equals(dbTypeName) || "geometry".equals(udt)) + // MD - this is slow - is there a better way? + if (dbTypeName.equalsIgnoreCase("geometry")) // WKB is now the normal way to store geometry in PostGIS [mmichaud 2007-05-13] + { return WKB_GEOMETRY_MAPPER; + } + if (dbTypeName.equalsIgnoreCase("bytea")) { + return WKB_GEOMETRY_MAPPER; + } + // handle the standard types ValueConverter stdConverter = ValueConverterFactory.getConverter(rsm, columnIndex); - if (stdConverter != null) + if (stdConverter != null) { return stdConverter; + } // default - can always show it as a string! return ValueConverterFactory.STRING_MAPPER; } - - static class WKBGeometryValueConverter implements ValueConverter { - - public AttributeType getType() { - return AttributeType.GEOMETRY; - } - - public Object getValue(ResultSet rs, int columnIndex) throws IOException, SQLException, ParseException { - - byte[] bytes = rs.getBytes(columnIndex); - - //so rs.getBytes will be one of two things: - //1. The actual bytes of the WKB if someone did ST_AsBinary - //2. The bytes of hex representation of the WKB. - - //in the case of #1, according to the WKB spec, the byte value can only be 0 or 1. - //in the case of #2, it's a hex string, so values range from ascii 0-F - //use this logic to determine how to process the bytes. - - Geometry geometry = null; - if(bytes == null || bytes.length <= 0) { - geometry = wktReader.read("GEOMETRYCOLLECTION EMPTY"); - } - else { - //assume it's the actual bytes (from ST_AsBinary) - byte[] realWkbBytes = bytes; - if(bytes[0] >= '0') { - //ok, it's hex, convert hex string to actual bytes - String hexString = new String(bytes); - realWkbBytes = WKBReader.hexToBytes(hexString); - } - geometry = wkbReader.read(realWkbBytes); - } - - return geometry; - } - } - -} \ No newline at end of file +} Added: core/trunk/src/com/vividsolutions/jump/datastore/spatialdatabases/SpatialDatabasesDSConnection.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/datastore/spatialdatabases/SpatialDatabasesDSConnection.java (rev 0) +++ core/trunk/src/com/vividsolutions/jump/datastore/spatialdatabases/SpatialDatabasesDSConnection.java 2015-12-07 17:07:02 UTC (rev 4579) @@ -0,0 +1,114 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.vividsolutions.jump.datastore.spatialdatabases; + +import com.vividsolutions.jump.I18N; +import com.vividsolutions.jump.datastore.AdhocQuery; +import com.vividsolutions.jump.datastore.DataStoreConnection; +import com.vividsolutions.jump.datastore.DataStoreException; +import com.vividsolutions.jump.datastore.DataStoreMetadata; +import com.vividsolutions.jump.datastore.FilterQuery; @@ Diff output truncated at 100000 characters. @@ ------------------------------------------------------------------------------ Go from Idea to Many App Stores Faster with Intel(R) XDK Give your users amazing mobile app experiences with Intel(R) XDK. Use one codebase in this all-in-one HTML5 development environment. Design, debug & build mobile apps & 2D/3D high-impact games for multiple OSs. http://pubads.g.doubleclick.net/gampad/clk?id=254741911&iu=/4140 _______________________________________________ Jump-pilot-devel mailing list Jump-pilot-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel