/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2015, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.db.oracle;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.io.WKBReader;
import java.io.IOException;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import org.apache.sis.feature.SingleAttributeTypeBuilder;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.util.ObjectConverters;
import org.apache.sis.util.Version;
import org.geotoolkit.db.DefaultJDBCFeatureStore;
import org.geotoolkit.db.FilterToSQL;
import org.geotoolkit.db.JDBCFeatureStoreUtilities;
import org.geotoolkit.db.dialect.AbstractSQLDialect;
import org.geotoolkit.db.reverse.ColumnMetaModel;
import org.geotoolkit.db.reverse.PrimaryKey;
import org.geotoolkit.factory.Hints;
import org.geotoolkit.filter.capability.DefaultArithmeticOperators;
import org.geotoolkit.filter.capability.DefaultComparisonOperators;
import org.geotoolkit.filter.capability.DefaultFilterCapabilities;
import org.geotoolkit.filter.capability.DefaultFunctions;
import org.geotoolkit.filter.capability.DefaultIdCapabilities;
import org.geotoolkit.filter.capability.DefaultOperator;
import org.geotoolkit.filter.capability.DefaultScalarCapabilities;
import org.geotoolkit.filter.capability.DefaultSpatialCapabilities;
import org.geotoolkit.filter.capability.DefaultSpatialOperator;
import org.geotoolkit.filter.capability.DefaultSpatialOperators;
import org.geotoolkit.filter.capability.DefaultTemporalCapabilities;
import org.geotoolkit.filter.capability.DefaultTemporalOperators;
import org.apache.sis.referencing.CRS;
import org.opengis.coverage.Coverage;
import org.opengis.filter.Filter;
import org.opengis.filter.PropertyIsBetween;
import org.opengis.filter.PropertyIsEqualTo;
import org.opengis.filter.PropertyIsGreaterThan;
import org.opengis.filter.PropertyIsGreaterThanOrEqualTo;
import org.opengis.filter.PropertyIsLessThan;
import org.opengis.filter.PropertyIsLessThanOrEqualTo;
import org.opengis.filter.PropertyIsLike;
import org.opengis.filter.PropertyIsNotEqualTo;
import org.opengis.filter.PropertyIsNull;
import org.opengis.filter.capability.ArithmeticOperators;
import org.opengis.filter.capability.ComparisonOperators;
import org.opengis.filter.capability.FilterCapabilities;
import org.opengis.filter.capability.FunctionName;
import org.opengis.filter.capability.Functions;
import org.opengis.filter.capability.GeometryOperand;
import org.opengis.filter.capability.Operator;
import org.opengis.filter.capability.ScalarCapabilities;
import org.opengis.filter.capability.SpatialCapabilities;
import org.opengis.filter.capability.SpatialOperator;
import org.opengis.filter.capability.SpatialOperators;
import org.opengis.filter.capability.TemporalCapabilities;
import org.opengis.filter.capability.TemporalOperand;
import org.opengis.filter.capability.TemporalOperator;
import org.opengis.filter.capability.TemporalOperators;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.spatial.BBOX;
import org.opengis.filter.spatial.Beyond;
import org.opengis.filter.spatial.Contains;
import org.opengis.filter.spatial.Crosses;
import org.opengis.filter.spatial.DWithin;
import org.opengis.filter.spatial.Disjoint;
import org.opengis.filter.spatial.Equals;
import org.opengis.filter.spatial.Intersects;
import org.opengis.filter.spatial.Overlaps;
import org.opengis.filter.spatial.Touches;
import org.opengis.filter.spatial.Within;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.referencing.crs.AbstractCRS;
import org.apache.sis.referencing.cs.AxesConvention;
import org.opengis.feature.AttributeType;
import org.opengis.feature.FeatureType;
/**
*
* @author Johann Sorel (Geomatys)
*/
public class OracleDialect extends AbstractSQLDialect{
protected final Map<Integer, CoordinateReferenceSystem> CRS_CACHE = new HashMap<>();
private static final Map<Integer,Class> TYPE_TO_CLASS = new HashMap<>();
private static final Map<String,Class> TYPENAME_TO_CLASS = new HashMap<>();
private static final Map<Class,String> CLASS_TO_TYPENAME = new HashMap<>();
private static final Map<String, String> TYPE_TO_ST_TYPE_MAP = new HashMap<>();
private static final Set<String> IGNORE_TABLES = new HashSet<>();
private static final FilterCapabilities FILTER_CAPABILITIES;
static {
//fill base types
TYPE_TO_CLASS.put(Types.VARCHAR, String.class);
TYPE_TO_CLASS.put(Types.CHAR, String.class);
TYPE_TO_CLASS.put(Types.LONGVARCHAR, String.class);
TYPE_TO_CLASS.put(Types.NVARCHAR, String.class);
TYPE_TO_CLASS.put(Types.NCHAR, String.class);
TYPE_TO_CLASS.put(Types.BIT, Boolean.class);
TYPE_TO_CLASS.put(Types.BOOLEAN, Boolean.class);
TYPE_TO_CLASS.put(Types.TINYINT, Short.class);
TYPE_TO_CLASS.put(Types.SMALLINT, Short.class);
TYPE_TO_CLASS.put(Types.INTEGER, Integer.class);
TYPE_TO_CLASS.put(Types.BIGINT, Long.class);
TYPE_TO_CLASS.put(Types.REAL, Float.class);
TYPE_TO_CLASS.put(Types.FLOAT, Double.class);
TYPE_TO_CLASS.put(Types.DOUBLE, Double.class);
TYPE_TO_CLASS.put(Types.DECIMAL, BigDecimal.class);
TYPE_TO_CLASS.put(Types.NUMERIC, BigDecimal.class);
TYPE_TO_CLASS.put(Types.DATE, Date.class);
TYPE_TO_CLASS.put(Types.TIME, Time.class);
TYPE_TO_CLASS.put(Types.TIMESTAMP, Timestamp.class);
TYPE_TO_CLASS.put(Types.BLOB, byte[].class);
TYPE_TO_CLASS.put(Types.BINARY, byte[].class);
TYPE_TO_CLASS.put(Types.CLOB, String.class);
TYPE_TO_CLASS.put(Types.VARBINARY, byte[].class);
TYPE_TO_CLASS.put(Types.ARRAY, Array.class);
//NAME IN CREATE QUERY SQL TYPE SQL TPE NAME
/*serial 4 serial */ TYPENAME_TO_CLASS.put("serial", Integer.class);
/*bigserial -5 bigserial */ TYPENAME_TO_CLASS.put("bigserial", Long.class);
/*abstime 1111 abstime */ TYPENAME_TO_CLASS.put("abstime", Object.class);
/*aclitem 1111 aclitem */ TYPENAME_TO_CLASS.put("aclitem", Object.class);
/*bigint -5 int8 */ TYPENAME_TO_CLASS.put("int8", Long.class);
/*bit(1) -7 bit */ TYPENAME_TO_CLASS.put("bit", Boolean.class);
/*bit varying 1111 varbit */ TYPENAME_TO_CLASS.put("varbit", Object.class);
/*boolean -7 bool */ TYPENAME_TO_CLASS.put("bool", Boolean.class);
/*box 1111 box */ TYPENAME_TO_CLASS.put("box", Object.class);
/*byte -2 bytea */ TYPENAME_TO_CLASS.put("bytea", Object.class);
/*char 1 char */ TYPENAME_TO_CLASS.put("char", String.class);
/*character(1) 1 bpchar */ TYPENAME_TO_CLASS.put("bpchar", String.class);
/*character varying 12 varchar */ TYPENAME_TO_CLASS.put("varchar", String.class);
/*cid 1111 cid */ TYPENAME_TO_CLASS.put("cid", Object.class);
/*cidr 1111 cidr */ TYPENAME_TO_CLASS.put("cidr", Object.class);
/*circle 1111 circle */ TYPENAME_TO_CLASS.put("circle", Object.class);
/*date 91 date */ TYPENAME_TO_CLASS.put("date", Date.class);
/*double precision 8 float8 */ TYPENAME_TO_CLASS.put("float8", Double.class);
/*gtsvector 1111 gtsvector */ TYPENAME_TO_CLASS.put("gtsvector", Object.class);
/*inet 1111 inet */ TYPENAME_TO_CLASS.put("inet", Object.class);
/*int2vector 1111 int2vector */ TYPENAME_TO_CLASS.put("int2vector", Object.class);
/*integer 4 int4 */ TYPENAME_TO_CLASS.put("int4", Integer.class);
/*interval 1111 interval */ TYPENAME_TO_CLASS.put("interval", Object.class);
/*line 1111 line */ TYPENAME_TO_CLASS.put("line", Object.class);
/*lseg 1111 lseg */ TYPENAME_TO_CLASS.put("lseg", Object.class);
/*macaddr 1111 macaddr */ TYPENAME_TO_CLASS.put("macaddr", Object.class);
/*money 8 money */ TYPENAME_TO_CLASS.put("money", Double.class);
/*name 12 name */ TYPENAME_TO_CLASS.put("name", String.class);
/*numeric 2 numeric */ TYPENAME_TO_CLASS.put("numeric", BigDecimal.class);
/*oid -5 oid */ TYPENAME_TO_CLASS.put("oid", Long.class);
/*oidvector 1111 oidvector */ TYPENAME_TO_CLASS.put("oidvector", Object.class);
/*path 1111 path */ TYPENAME_TO_CLASS.put("path", Object.class);
/*pg_node_tree 1111 pg_node_tree*/ TYPENAME_TO_CLASS.put("pg_node_tree", Object.class);
/*point 1111 point */ TYPENAME_TO_CLASS.put("point", Object.class);
/*polygon 1111 polygon */ TYPENAME_TO_CLASS.put("polygon", Object.class);
/*real 7 float4 */ TYPENAME_TO_CLASS.put("float4", Float.class);
/*refcursor 1111 refcursor */ TYPENAME_TO_CLASS.put("refcursor", Object.class);
/*regclass 1111 regclass */ TYPENAME_TO_CLASS.put("regclass", Object.class);
/*regconfig 1111 regconfig */ TYPENAME_TO_CLASS.put("regconfig", Object.class);
/*regdictionary 1111 regdictionary*/ TYPENAME_TO_CLASS.put("regdictionary", Object.class);
/*regoper 1111 regoper */ TYPENAME_TO_CLASS.put("regoper", Object.class);
/*regoperator 1111 regoperator */ TYPENAME_TO_CLASS.put("regoperator", Object.class);
/*regproc 1111 regproc */ TYPENAME_TO_CLASS.put("regproc", Object.class);
/*regprocedure 1111 regprocedure*/ TYPENAME_TO_CLASS.put("regprocedure", Object.class);
/*regtype 1111 regtype */ TYPENAME_TO_CLASS.put("regtype", Object.class);
/*reltime 1111 reltime */ TYPENAME_TO_CLASS.put("reltime", Object.class);
/*smallint 8 int2 */ TYPENAME_TO_CLASS.put("int2", Short.class);
/*smgr 1111 smgr */ TYPENAME_TO_CLASS.put("smgr", Object.class);
/*text 12 text */ TYPENAME_TO_CLASS.put("text", String.class);
/*tid 1111 tid */ TYPENAME_TO_CLASS.put("tid", Object.class);
/*timestamp without time zone 93 timestamp */ TYPENAME_TO_CLASS.put("timestamp", Timestamp.class);
/*timestamp with time zone 93 timestamptz */ TYPENAME_TO_CLASS.put("timestamptz", Timestamp.class);
/*time without time zone 92 time */ TYPENAME_TO_CLASS.put("time", Time.class);
/*time with time zone 92 timetz */ TYPENAME_TO_CLASS.put("timetz", Time.class);
/*tinterval 1111 tinterval */ TYPENAME_TO_CLASS.put("tinterval", Object.class);
/*tsquery 1111 tsquery */ TYPENAME_TO_CLASS.put("tsquery", Object.class);
/*tsvector 1111 tsvector */ TYPENAME_TO_CLASS.put("tsvector", Object.class);
/*txid_snapshot 1111 txid_snapshot*/ TYPENAME_TO_CLASS.put("txid_snapshot", Object.class);
/*uuid 1111 uuid */ TYPENAME_TO_CLASS.put("uuid", Object.class);
/*xid 1111 xid */ TYPENAME_TO_CLASS.put("xid", Object.class);
/*xml 2009 xml */ TYPENAME_TO_CLASS.put("xml", String.class);
/*box2d 1111 box2d */ TYPENAME_TO_CLASS.put("box2d", Object.class);
/*box3d 1111 box3d */ TYPENAME_TO_CLASS.put("box3d", Object.class);
/*box3d_extent 1111 box3d_extent*/ TYPENAME_TO_CLASS.put("box3d_extent", Object.class);
/*chip 1111 chip */ TYPENAME_TO_CLASS.put("chip", Object.class);
/*geography 1111 geography */ TYPENAME_TO_CLASS.put("geography", Object.class);
/*geometry_dump 2002 geometry_dump*/ TYPENAME_TO_CLASS.put("geometry_dump", Object.class);
/*gidx 1111 gidx */ TYPENAME_TO_CLASS.put("gidx", Object.class);
/*pgis_abs 1111 pgis_abs */ TYPENAME_TO_CLASS.put("pgis_abs", Object.class);
/*spheroid 1111 spheroid */ TYPENAME_TO_CLASS.put("spheroid", Object.class);
CLASS_TO_TYPENAME.put(String.class, "varchar");
CLASS_TO_TYPENAME.put(Boolean.class, "bool");
CLASS_TO_TYPENAME.put(boolean.class, "bool");
CLASS_TO_TYPENAME.put(Byte.class, "smallint");
CLASS_TO_TYPENAME.put(byte.class, "smallint");
CLASS_TO_TYPENAME.put(Short.class, "int2");
CLASS_TO_TYPENAME.put(short.class, "int2");
CLASS_TO_TYPENAME.put(Integer.class, "int4");
CLASS_TO_TYPENAME.put(int.class, "int4");
CLASS_TO_TYPENAME.put(Long.class, "int8");
CLASS_TO_TYPENAME.put(long.class, "int8");
CLASS_TO_TYPENAME.put(Float.class, "float4");
CLASS_TO_TYPENAME.put(float.class, "float4");
CLASS_TO_TYPENAME.put(Double.class, "float8");
CLASS_TO_TYPENAME.put(double.class, "float8");
CLASS_TO_TYPENAME.put(BigDecimal.class, "");
CLASS_TO_TYPENAME.put(Date.class, "date");
CLASS_TO_TYPENAME.put(Time.class, "time");
CLASS_TO_TYPENAME.put(java.util.Date.class, "timestamp");
CLASS_TO_TYPENAME.put(Timestamp.class, "timestamp");
CLASS_TO_TYPENAME.put(byte[].class, "blob");
CLASS_TO_TYPENAME.put(Coverage.class, "raster");
//H2GIS extension
TYPENAME_TO_CLASS.put("GEOMETRY", Geometry.class);
TYPENAME_TO_CLASS.put("GEOGRAPHY", Geometry.class);
TYPENAME_TO_CLASS.put("POINT", Point.class);
TYPENAME_TO_CLASS.put("POINTM", Point.class);
TYPENAME_TO_CLASS.put("LINESTRING", LineString.class);
TYPENAME_TO_CLASS.put("LINESTRINGM", LineString.class);
TYPENAME_TO_CLASS.put("POLYGON", Polygon.class);
TYPENAME_TO_CLASS.put("POLYGONM", Polygon.class);
TYPENAME_TO_CLASS.put("MULTIPOINT", MultiPoint.class);
TYPENAME_TO_CLASS.put("MULTIPOINTM", MultiPoint.class);
TYPENAME_TO_CLASS.put("MULTILINESTRING", MultiLineString.class);
TYPENAME_TO_CLASS.put("MULTILINESTRINGM", MultiLineString.class);
TYPENAME_TO_CLASS.put("MULTIPOLYGON", MultiPolygon.class);
TYPENAME_TO_CLASS.put("MULTIPOLYGONM", MultiPolygon.class);
TYPENAME_TO_CLASS.put("GEOMETRYCOLLECTION", GeometryCollection.class);
TYPENAME_TO_CLASS.put("GEOMETRYCOLLECTIONM", GeometryCollection.class);
CLASS_TO_TYPENAME.put(Geometry.class, "GEOMETRY");
CLASS_TO_TYPENAME.put(Point.class, "POINT");
CLASS_TO_TYPENAME.put(LineString.class, "LINESTRING");
CLASS_TO_TYPENAME.put(Polygon.class, "POLYGON");
CLASS_TO_TYPENAME.put(MultiPoint.class, "MULTIPOINT");
CLASS_TO_TYPENAME.put(MultiLineString.class, "MULTILINESTRING");
CLASS_TO_TYPENAME.put(MultiPolygon.class, "MULTIPOLYGON");
CLASS_TO_TYPENAME.put(GeometryCollection.class, "GEOMETRYCOLLECTION");
TYPE_TO_ST_TYPE_MAP.put("GEOMETRY","ST_Geometry");
TYPE_TO_ST_TYPE_MAP.put("POINT","ST_Point");
TYPE_TO_ST_TYPE_MAP.put("LINESTRING","ST_LineString");
TYPE_TO_ST_TYPE_MAP.put("POLYGON","ST_Polygon");
TYPE_TO_ST_TYPE_MAP.put("MULTIPOINT","ST_MultiPoint");
TYPE_TO_ST_TYPE_MAP.put("MULTILINESTRING","ST_MultiLineString");
TYPE_TO_ST_TYPE_MAP.put("MULTIPOLYGON","ST_MultiPolygon");
TYPE_TO_ST_TYPE_MAP.put("GEOMETRYCOLLECTION","ST_GeometryCollection");
//postgis 1+ geometry and referencing
IGNORE_TABLES.add("spatial_ref_sys");
IGNORE_TABLES.add("geometry_columns");
IGNORE_TABLES.add("geography_columns");
//filter capabilities
final String version = null;
//ID capabilities, support : EID, FID
final DefaultIdCapabilities idCapa = new DefaultIdCapabilities(true, true);
//Spatial capabilities
final GeometryOperand[] geometryOperands = new GeometryOperand[]{
GeometryOperand.Point,
GeometryOperand.LineString,
GeometryOperand.Polygon,
GeometryOperand.Envelope
};
final SpatialOperator[] spatialOperatrs = new SpatialOperator[]{
new DefaultSpatialOperator(BBOX.NAME , geometryOperands),
new DefaultSpatialOperator(Beyond.NAME , geometryOperands),
new DefaultSpatialOperator(Contains.NAME , geometryOperands),
new DefaultSpatialOperator(Crosses.NAME , geometryOperands),
new DefaultSpatialOperator(Disjoint.NAME , geometryOperands),
new DefaultSpatialOperator(DWithin.NAME , geometryOperands),
new DefaultSpatialOperator(Equals.NAME , geometryOperands),
new DefaultSpatialOperator(Intersects.NAME, geometryOperands),
new DefaultSpatialOperator(Overlaps.NAME , geometryOperands),
new DefaultSpatialOperator(Touches.NAME , geometryOperands),
new DefaultSpatialOperator(Within.NAME , geometryOperands)
};
final SpatialOperators spatialOperators = new DefaultSpatialOperators(spatialOperatrs);
final SpatialCapabilities spatialCapa = new DefaultSpatialCapabilities(geometryOperands, spatialOperators);
//scalar capabilities
//support : AND, OR, NOT
final boolean logical = true;
//support : =, <>, <, <=, >, >=, LIKE, BEETWEN, NULL
final Operator[] comparaisonOps = new Operator[]{
new DefaultOperator(PropertyIsEqualTo.NAME),
new DefaultOperator(PropertyIsNotEqualTo.NAME),
new DefaultOperator(PropertyIsLessThan.NAME),
new DefaultOperator(PropertyIsLessThanOrEqualTo.NAME),
new DefaultOperator(PropertyIsGreaterThan.NAME),
new DefaultOperator(PropertyIsGreaterThanOrEqualTo.NAME),
new DefaultOperator(PropertyIsLike.NAME),
new DefaultOperator(PropertyIsBetween.NAME),
new DefaultOperator(PropertyIsNull.NAME)
};
final ComparisonOperators comparisonOperators = new DefaultComparisonOperators(comparaisonOps);
//support : +, -, *, /
final boolean arithmeticSimple = true;
//support various functions
final FunctionName[] functionNames = new FunctionName[0];
final Functions functions = new DefaultFunctions(functionNames);
final ArithmeticOperators arithmeticOperators = new DefaultArithmeticOperators(arithmeticSimple, functions);
final ScalarCapabilities scalarCapa = new DefaultScalarCapabilities(logical, comparisonOperators, arithmeticOperators);
//temporal capabilities
final TemporalOperand[] temporalOperands = new TemporalOperand[0];
final TemporalOperator[] temporalOperatrs = new TemporalOperator[0];
final TemporalOperators temporalOperators = new DefaultTemporalOperators(temporalOperatrs);
final TemporalCapabilities temporalCapa = new DefaultTemporalCapabilities(temporalOperands, temporalOperators);
FILTER_CAPABILITIES = new DefaultFilterCapabilities(version, idCapa, spatialCapa, scalarCapa, temporalCapa);
}
private final DefaultJDBCFeatureStore featurestore;
//readers
private final ThreadLocal<WKBReader> wkbReader = new ThreadLocal<WKBReader>();
//cache
private Version version = null;
OracleDialect(DefaultJDBCFeatureStore datastore) {
this.featurestore = datastore;
}
DefaultJDBCFeatureStore getFeaturestore() {
return featurestore;
}
@Override
public boolean supportGlobalMetadata() {
return true;
}
@Override
public FilterCapabilities getFilterCapabilities() {
return FILTER_CAPABILITIES;
}
@Override
public FilterToSQL getFilterToSQL(FeatureType featureType) {
try{
PrimaryKey pk = null;
if(featureType!=null){
pk = featurestore.getDatabaseModel().getPrimaryKey(featureType.getName().toString());
}
return new OracleFilterToSQL(featureType, pk);
}catch(DataStoreException ex){
throw new RuntimeException(ex.getMessage(),ex);
}
}
@Override
public String getTableEscape() {
return "\"";
}
@Override
public Class getJavaType(int sqlType, String sqlTypeName) {
Class c = null;
sqlTypeName = sqlTypeName.toLowerCase();
if(sqlType == Types.ARRAY){
//special case for array types
if(sqlTypeName.startsWith("_")){
sqlTypeName = sqlTypeName.substring(1);
}
c = TYPENAME_TO_CLASS.get(sqlTypeName);
if(c==null) c = TYPENAME_TO_CLASS.get(sqlTypeName.toUpperCase());
if(c == null){
c = Object.class;
}
c = Array.newInstance(c, 0).getClass();
}else{
c = TYPENAME_TO_CLASS.get(sqlTypeName);
if(c==null) c = TYPENAME_TO_CLASS.get(sqlTypeName.toUpperCase());
if(c == null){
//try relying on base type.
c = TYPE_TO_CLASS.get(sqlType);
}
}
if(c == null){
featurestore.getLogger().log(Level.INFO, "No definied mapping for type : {0} {1}", new Object[]{sqlType, sqlTypeName});
c = Object.class;
}
return c;
}
@Override
public String getSQLType(Class javaType) throws SQLException{
String sqlName = CLASS_TO_TYPENAME.get(javaType);
if(javaType.isArray()){
sqlName = getSQLType(javaType.getComponentType());
if(sqlName == null) throw new SQLException("No database mapping for type "+ javaType);
sqlName = sqlName+"[]";
}
if(sqlName == null) throw new SQLException("No database mapping for type "+ javaType);
return sqlName;
}
@Override
public String getColumnSequence(Connection cx, String schemaName, String tableName, String columnName) throws SQLException {
//TODO
return null;
}
@Override
public boolean ignoreTable(String name) {
name = name.toLowerCase();
//ignore the versioning tables
if(name.contains("$")){
return true;
}
return IGNORE_TABLES.contains(name.toLowerCase());
}
@Override
public Version getVersion(String schema){
if(version != null){
return version;
}
//TODO
version = new Version("1.0.0");
return version;
}
////////////////////////////////////////////////////////////////////////////
// METHODS TO CREATE SQL QUERIES ///////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
@Override
public String encodeFilter(Filter filter, FeatureType type) {
final FilterToSQL fts = getFilterToSQL(type);
final StringBuilder sb = (StringBuilder)filter.accept(fts, new StringBuilder());
return sb.toString();
}
@Override
public void encodeColumnType(StringBuilder sql, String sqlTypeName, Integer length) {
if(TYPE_TO_ST_TYPE_MAP.containsKey(sqlTypeName)){
//geometry type, will be added as a constraint in the postcreate method
sqlTypeName = "GEOMETRY";
}
if(length == null){
sql.append(sqlTypeName);
}else{
final int arrayindex = sqlTypeName.indexOf("[]");
if(arrayindex>0){
sql.append(sqlTypeName.substring(0, arrayindex));
sql.append('(').append(length).append(')');
sql.append(sqlTypeName.substring(arrayindex));
}else{
sql.append(sqlTypeName);
sql.append('(').append(length).append(')');
}
}
}
@Override
public void encodeGeometryColumn(StringBuilder sql, AttributeType gatt, int srid, Hints hints) {
throw new RuntimeException("Not supported yet.");
}
@Override
public void encodeLimitOffset(StringBuilder sql, Integer limit, int offset) {
if (limit != null && limit > 0 && limit < Integer.MAX_VALUE) {
sql.append(" LIMIT ").append(limit);
}
if (offset > 0) {
sql.append(" OFFSET ").append(offset);
}
}
@Override
public void encodeValue(StringBuilder sql, Object value, Class type) {
//turn the value into a literal and use FilterToSQL to encode it
final Literal literal = featurestore.getFilterFactory().literal(value);
literal.accept(getFilterToSQL(null), sql);
}
@Override
public void encodeGeometryValue(StringBuilder sql, Geometry value, int srid) throws DataStoreException {
throw new DataStoreException("Not supported yet.");
}
@Override
public void encodeCoverageValue(StringBuilder sql, Coverage value) throws DataStoreException {
throw new DataStoreException("Coverage type not supported.");
}
@Override
public void encodePrimaryKey(StringBuilder sql, Class binding, String sqlType) {
if(Integer.class.isAssignableFrom(binding) || Short.class.isAssignableFrom(binding)){
sql.append(" SERIAL ");
}else if(Long.class.isAssignableFrom(binding)){
sql.append(" BIGSERIAL ");
}else{
sql.append(' ').append(sqlType).append(' ');
}
sql.append("PRIMARY KEY");
}
@Override
public void postCreateTable(String schemaName, final FeatureType featureType,
final Connection cx) throws SQLException{
}
////////////////////////////////////////////////////////////////////////////
// PRIMARY KEY CALCULATION METHOS //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
@Override
public Object nextValue(final ColumnMetaModel column, final Connection cx) throws SQLException, DataStoreException {
if(column.getType() == ColumnMetaModel.Type.SEQUENCED){
final Statement st = cx.createStatement();
ResultSet rs = null;
try {
final String sql = "SELECT nextval('" + column.getSequenceName() + "')";
rs = st.executeQuery(sql);
if (rs.next()) {
return rs.getLong(1);
}
} finally {
JDBCFeatureStoreUtilities.closeSafe(featurestore.getLogger(), null,st,rs);
}
return null;
}
return null;
}
////////////////////////////////////////////////////////////////////////////
// METHODS TO READ FROM RESULTSET //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
@Override
public void decodeColumnType(final SingleAttributeTypeBuilder atb, final Connection cx,
String typeName, final int datatype, final String schemaName,
final String tableName, final String columnName) throws SQLException {
super.decodeColumnType(atb, cx, typeName, datatype,
schemaName, tableName,columnName);
}
@Override
public void decodeGeometryColumnType(final SingleAttributeTypeBuilder atb, final Connection cx,
final ResultSet rs, final int columnIndex, boolean customQuery) throws SQLException {
throw new SQLException("Not supported yet.");
}
@Override
public Integer getGeometrySRID(String schemaName, final String tableName, final String columnName,
Map metas, final Connection cx) throws SQLException{
throw new SQLException("Not supported yet.");
}
@Override
public CoordinateReferenceSystem createCRS(int srid, Connection cx) throws SQLException {
CoordinateReferenceSystem crs = CRS_CACHE.get(srid);
if (crs == null && srid!=0) {
try {
crs = AbstractCRS.castOrCopy(CRS.forCode("EPSG:" + srid)).forConvention(AxesConvention.RIGHT_HANDED);
CRS_CACHE.put(srid, crs);
} catch(Exception e) {
if(featurestore.getLogger().isLoggable(Level.FINE)) {
featurestore.getLogger().log(Level.FINE, "Could not decode " + srid + " using the built-in EPSG database", e);
}
return null;
}
}
return crs;
}
@Override
public Object decodeAttributeValue(AttributeType descriptor, ResultSet rs, int i) throws SQLException {
final Class binding = descriptor.getValueClass();
if(binding.isArray()){
if(byte.class.equals(binding.getComponentType())){
//blob or binary field type
final Blob blob = rs.getBlob(i);
return blob.getBytes(1, (int)blob.length());
}else if(rs.getArray(i) != null) {
Object baseArray = rs.getArray(i).getArray();
final Class c = binding.getComponentType();
if(!baseArray.getClass().getComponentType().equals(c)){
int nbdim=1;
Class base = baseArray.getClass().getComponentType();
while(base.isArray()){
base = base.getComponentType();
nbdim++;
}
baseArray = rebuildArray(baseArray, c, nbdim);
}
return baseArray;
} else {
return null;
}
}else{
if(String.class.equals(binding)){
//solve nclob string
return rs.getString(i);
}else{
return rs.getObject(i);
}
}
}
private Object rebuildArray(Object candidate, Class componentType, int depth){
if(candidate==null) return null;
if(candidate.getClass().isArray()){
final int size = Array.getLength(candidate);
final int[] dims = new int[depth];
dims[0] = size;
final Object rarray = Array.newInstance(componentType, dims);
depth--;
for(int k=0; k<size; k++){
Array.set(rarray, k, rebuildArray(Array.get(candidate, k), componentType, depth));
}
return rarray;
}else{
return ObjectConverters.convert(candidate, componentType);
}
}
@Override
public Geometry decodeGeometryValue(AttributeType descriptor, ResultSet rs,
String column) throws IOException, SQLException {
throw new IOException("Not supported yet.");
}
@Override
public Geometry decodeGeometryValue(AttributeType descriptor, ResultSet rs,
int column) throws IOException, SQLException {
throw new IOException("Not supported yet.");
}
@Override
public Coverage decodeCoverageValue(AttributeType descriptor, ResultSet rs, String column) throws IOException, SQLException {
throw new IOException("Coverage type not supported.");
}
@Override
public Coverage decodeCoverageValue(AttributeType descriptor, ResultSet rs, int column) throws IOException, SQLException {
throw new IOException("Coverage type not supported.");
}
}