/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * * 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.geotools.jdbc; import java.io.IOException; import java.math.BigDecimal; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.Date; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.geotools.data.Query; import org.geotools.factory.Hints; import org.geotools.feature.visitor.CountVisitor; import org.geotools.feature.visitor.MaxVisitor; import org.geotools.feature.visitor.MinVisitor; import org.geotools.feature.visitor.SumVisitor; import org.geotools.feature.visitor.UniqueVisitor; import org.geotools.filter.FilterCapabilities; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; import org.geotools.util.logging.Logging; import org.opengis.feature.FeatureVisitor; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.feature.type.GeometryDescriptor; import org.opengis.filter.ExcludeFilter; import org.opengis.filter.Id; import org.opengis.filter.IncludeFilter; import org.opengis.filter.PropertyIsBetween; import org.opengis.filter.PropertyIsLike; import org.opengis.filter.PropertyIsNull; import org.opengis.filter.expression.Add; import org.opengis.filter.expression.Divide; import org.opengis.filter.expression.Multiply; import org.opengis.filter.expression.Subtract; import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; /** * The driver used by JDBCDataStore to directly communicate with the database. * <p> * This class encapsulates all the database specific operations that JDBCDataStore * needs to function. It is implemented on a per-database basis. * </p> * <p> * <h3>Type Mapping</h3> * One of the jobs of a dialect is to map sql types to java types and vice * versa. This abstract implementation provides default mappings for "primitive" * java types. The following mappings are provided. A '*' denotes that the * mapping is the default java to sql mapping as well. * <ul> * <li>VARCHAR -> String * * <li>CHAR -> String * <li>LONGVARCHAR -> String * <li>BIT -> Boolean * <li>BOOLEAN -> Boolean * * <li>SMALLINT -> Short * * <li>TINYINT -> Short * <li>INTEGER -> Integer * * <li>BIGINT -> Long * * <li>REAL -> Float * * <li>DOUBLE -> Double * * <li>FLOAT -> Double * <li>NUMERIC -> BigDecimal * * <li>DECIMAL -> BigDecimal * <li>DATE -> java.sql.Date * * <li>TIME -> java.sql.Time * * <li>TIMESTAMP -> java.sql.Timestmap * * </ul> * Subclasses should <b>extend</b> (not override) the following methods to * configure the mappings: * <ul> * <li>{@link #registerSqlTypeToClassMappings(Map)} * <li>{@link #registerSqlTypeNameToClassMappings(Map)} * <li>{@link #registerClassToSqlMappings(Map)} * </ul> * </p> * <p> * * </p> * <p> * This class is intended to be stateless, therefore subclasses should not * maintain any internal state. If for some reason a subclass must keep some * state around (not recommended), it must ensure that the state is accessed in * a thread safe manner. * </p> * @author Justin Deoliveira, The Open Planning Project * * * * @source $URL$ */ public abstract class SQLDialect { protected static final Logger LOGGER = Logging.getLogger(SQLDialect.class); /** * The basic filter capabilities all databases should have */ public static FilterCapabilities BASE_DBMS_CAPABILITIES = new FilterCapabilities() { { addAll(FilterCapabilities.LOGICAL_OPENGIS); addAll(FilterCapabilities.SIMPLE_COMPARISONS_OPENGIS); //simple arithmetic addType(Add.class); addType(Subtract.class); addType(Multiply.class); addType(Divide.class); //simple comparisons addType(PropertyIsNull.class); addType(PropertyIsBetween.class); addType(Id.class); addType(IncludeFilter.class); addType(ExcludeFilter.class); addType(PropertyIsLike.class); } }; /** * The datastore using the dialect */ protected JDBCDataStore dataStore; /** * Creates the dialect. * @param dataStore The dataStore using the dialect. */ protected SQLDialect( JDBCDataStore dataStore ) { this.dataStore = dataStore; } /** * Initializes a newly created database connection. * <p> * Subclasses should override this method if there is some additional action * that needs to be taken when a new connection to the database is created. The * default implementation does nothing. * </p> * @param cx The new database connection. */ public void initializeConnection( Connection cx ) throws SQLException { } /** * Determines if the specified table should be included in those published * by the datastore. * <p> * This method returns <code>true</code> if the table should be published as * a feature type, otherwise it returns <code>false</code>. Subclasses should * override this method, this default implementation returns <code>true<code>. * </p> * <p> * A database connection is provided to the dialect but it should not be closed. * However any statements objects or result sets that are instantiated from it * must be closed. * </p> * @param schemaName The schema of the table, might be <code>null</code>.. * @param tableName The name of the table. * @param cx Database connection. * */ public boolean includeTable(String schemaName, String tableName, Connection cx) throws SQLException { return true; } /** * Registers the sql type name to java type mappings that the dialect uses when * reading and writing objects to and from the database. * <p> * Subclasses should extend (not override) this method to provide additional * mappings, or to override mappings provided by this implementation. This * implementation provides the following mappings: * </p> */ public void registerSqlTypeNameToClassMappings(Map<String, Class<?>> mappings) { //TODO: do the normal types } /** * Determines the class mapping for a particular column of a table. * <p> * Implementing this method is optional. It is used to allow database to * perform custom type mappings based on various column metadata. It is called * before the mappings registered in {@link #registerSqlTypeToClassMappings(Map)} * and {@link #registerSqlTypeNameToClassMappings(Map) are used to determine * the mapping. Subclasses should implement as needed, this default implementation * returns <code>null</code>. * </p> * <p> * The <tt>columnMetaData</tt> argument is provided from * {@link DatabaseMetaData#getColumns(String, String, String, String)}. * </p> * @param columnMetaData The column metadata * @param The connection used to retrieve the metadata * @return The class mapped to the to column, or <code>null</code>. */ public Class<?> getMapping(ResultSet columnMetaData, Connection cx) throws SQLException { return null; } /** * Handles the mapping for a user defined type. * <p> * This method is called after {@link #getMapping(ResultSet, Connection)} but before the rest * of the type mapping heuristics are applied. * </p> * <p> * Implementing this method is optional. It is used to allow for handling user defined types * or "DOMAINS". Dialects that implement this method should set the appropriate information on * the <tt>metadata</tt> object to allow the column to be mapped via teh regular type mapping * heuristics. * </p> * * @param columnMetaData The column metdata. * @param metadata The column metadata object that collections mapping information. * @param cx The database connection, not to be closed. */ public void handleUserDefinedType(ResultSet columnMetaData, ColumnMetadata metadata, Connection cx) throws SQLException { } /** * Registers the sql type to java type mappings that the dialect uses when * reading and writing objects to and from the database. * <p> * Subclasses should extend (not override) this method to provide additional * mappings, or to override mappings provided by this implementation. This * implementation provides the following mappings: * </p> * */ public void registerSqlTypeToClassMappings(Map<Integer, Class<?>> mappings) { mappings.put(new Integer(Types.VARCHAR), String.class); mappings.put(new Integer(Types.CHAR), String.class); mappings.put(new Integer(Types.LONGVARCHAR), String.class); mappings.put(new Integer(Types.NVARCHAR), String.class); mappings.put(new Integer(Types.NCHAR), String.class); mappings.put(new Integer(Types.BIT), Boolean.class); mappings.put(new Integer(Types.BOOLEAN), Boolean.class); mappings.put(new Integer(Types.TINYINT), Short.class); mappings.put(new Integer(Types.SMALLINT), Short.class); mappings.put(new Integer(Types.INTEGER), Integer.class); mappings.put(new Integer(Types.BIGINT), Long.class); mappings.put(new Integer(Types.REAL), Float.class); mappings.put(new Integer(Types.FLOAT), Double.class); mappings.put(new Integer(Types.DOUBLE), Double.class); mappings.put(new Integer(Types.DECIMAL), BigDecimal.class); mappings.put(new Integer(Types.NUMERIC), BigDecimal.class); mappings.put(new Integer(Types.DATE), Date.class); mappings.put(new Integer(Types.TIME), Time.class); mappings.put(new Integer(Types.TIMESTAMP), Timestamp.class); mappings.put(new Integer(Types.BLOB), byte[].class); mappings.put(new Integer(Types.BINARY), byte[].class); mappings.put(new Integer(Types.CLOB), String.class); mappings.put(new Integer(Types.VARBINARY), byte[].class); //subclasses should extend to provide additional } /** * Registers the java type to sql type mappings that the datastore uses when * reading and writing objects to and from the database. * * <p> * Subclasses should extend (not override) this method to provide additional * mappings, or to override mappings provided by this implementation. This * implementation provides the following mappings: * </p> */ public void registerClassToSqlMappings(Map<Class<?>, Integer> mappings) { mappings.put(String.class, new Integer(Types.VARCHAR)); mappings.put(Boolean.class, new Integer(Types.BOOLEAN)); mappings.put(boolean.class, new Integer(Types.BOOLEAN)); mappings.put(Short.class, new Integer(Types.SMALLINT)); mappings.put(short.class, new Integer(Types.SMALLINT)); mappings.put(Integer.class, new Integer(Types.INTEGER)); mappings.put(int.class, new Integer(Types.INTEGER)); mappings.put(Long.class, new Integer(Types.BIGINT)); mappings.put(long.class, new Integer(Types.BIGINT)); mappings.put(Float.class, new Integer(Types.REAL)); mappings.put(float.class, new Integer(Types.REAL)); mappings.put(Double.class, new Integer(Types.DOUBLE)); mappings.put(double.class, new Integer(Types.DOUBLE)); mappings.put(BigDecimal.class, new Integer(Types.NUMERIC)); mappings.put(Date.class, new Integer(Types.DATE)); mappings.put(Time.class, new Integer(Types.TIME)); mappings.put(java.util.Date.class, new Integer(Types.TIMESTAMP)); mappings.put(Timestamp.class, new Integer(Types.TIMESTAMP)); mappings.put(byte[].class, new Integer(Types.BLOB)); //subclasses should extend and provide additional } /** * Registers any overrides that should occur when mapping an integer sql type * value to an underlying sql type name. * <p> * The default implementation of this method does nothing. Subclasses should override * in cases where: * <ul> * <li>database type metadata does not provide enough information to properly map * <li>to support custom types (those not in {@link Types}) * </ul> * </p> */ public void registerSqlTypeToSqlTypeNameOverrides(Map<Integer,String> overrides) { } /** * Registers the set of aggregate functions the dialect is capable of handling. * <p> * Aggregate functions are handled via visitors of special types. The <param>aggregates</param> * maps the class of the visitor to the associated function name. This base implementation * handles some of the well known mappings: * <ul> * <li>{@link UniqueVisitor} -> "unique" <li> * <li>{@link CountVisitor} -> "count" <li> * <li>{@link MaxVisitor} -> "max" <li> * <li>{@link MinVisitor} -> "min" <li> * <li>{@link SumVisitor} -> "sum" <li> * </ul> * Subclasses should extend (not override) to provide additional functions. * </p> */ public void registerAggregateFunctions(Map<Class<? extends FeatureVisitor>,String> aggregates) { //register the well known aggregates.put( UniqueVisitor.class, "distinct"); aggregates.put( CountVisitor.class, "count" ); aggregates.put( MinVisitor.class, "min"); aggregates.put( MaxVisitor.class, "max"); aggregates.put( SumVisitor.class, "sum"); } /** * Returns the java class mapping for a particular column. * <p> * This method is used as a "last resort" when the mappings specified by the * dialect in the {@link #registerSqlTypeToClassMappings(Map)}" method fail * to yield a java type. * </p> * <p> * The most common case is for databases which store all geometric values under * a single type, and use some secondary means to store the specific type * (like a metadata table). * </p> * * <p> * This method is given a direct connection to the database. The connection * must not be closed. However any statements or result sets instantiated * from the connection must be closed. * </p> * <p> * In the event that the mapping cannot be determined, this method should return * <code>null</code>. * </p> * @param schemaName The schema name, may be <code>null</code>. * @param tableName The table name. * @param columnName The column name. * @param type The data type from {@link Types}, reported by database metadata. * @param cx The database connection. * * @return The mapped type of the column, or <code>null</code> if it can not * be inferred. */ // public final Class getMapping( String schemaName, String tableName, String columnName, Integer type, Connection cx ) // throws SQLException { // return null; // } /** * Returns the string used to escape names. * <p> * This value is used to escape any name in a query. This includes columns, * tables, schemas, indexes, etc... If no escape is necessary this method * should return the empty string, and never return <code>null</code>. * </p> * <p> * This default implementation returns a single double quote ("), subclasses * must override to provide a different espcape. * </p> */ public String getNameEscape() { return "\""; } /** * Quick accessor for {@link #getNameEscape()}. */ protected final String ne() { return getNameEscape(); } /** * Encodes the name of a column in an SQL statement. * <p> * This method wraps <tt>raw</tt> in the character provided by * {@link #getNameEscape()}. Subclasses usually dont override this method * and instead override {@link #getNameEscape()}. * </p> */ public void encodeColumnName(String raw, StringBuffer sql) { sql.append(ne()).append(raw).append(ne()); } /** * Encodes the type of a column in an SQL CREATE TABLE statement. * <p> * The default implementation simply outputs the <tt>sqlTypeName</tt> argument * as is. Subclasses may override this method. Such cases might include: * <ul> * <li>A type definition requires some parameter, ex: size of a varchar * <li>The provided attribute (<tt>att</tt>) contains some additional * restrictions that can be encoded in the type, ex: field length * </ul> * </p> * @param sqlTypeName * @param sql */ public void encodeColumnType(String sqlTypeName, StringBuffer sql) { sql.append(sqlTypeName); } /** * Encodes the alias of a column in an sql query. * <p> * This default implementation uses the syntax: <pre>as "alias"</pre>. * Subclasses should override to provide a different syntax. * </p> */ public void encodeColumnAlias(String raw, StringBuffer sql) { sql.append(" as "); encodeColumnName(raw, sql); } /** * Encodes the alias of a table in an sql query. * <p> * This default implementation uses the syntax: <pre>as "alias"</pre>. * Subclasses should override to provide a different syntax. * </p> */ public void encodeTableAlias(String raw, StringBuffer sql) { sql.append(" as "); encodeColumnName(raw, sql); } /** * Encodes the name of a table in an SQL statement. * <p> * This method wraps <tt>raw</tt> in the character provided by * {@link #getNameEscape()}. Subclasses usually dont override this method * and instead override {@link #getNameEscape()}. * </p> */ public void encodeTableName(String raw, StringBuffer sql) { sql.append(ne()).append(raw).append(ne()); } /** * Encodes the name of a schema in an SQL statement. * <p> * This method wraps <tt>raw</tt> in the character provided by * {@link #getNameEscape()}. Subclasses usually dont override this method * and instead override {@link #getNameEscape()}. * </p> */ public void encodeSchemaName(String raw, StringBuffer sql) { sql.append(ne()).append(raw).append(ne()); } /** * Returns the name of a geometric type based on its integer constant. * <p> * The constant, <tt>type</tt>, is registered in {@link #registerSqlTypeNameToClassMappings(Map)}. * </p> * <p> * This default implementation returns <code>null</code>, subclasses should * override. * </p> */ public String getGeometryTypeName(Integer type) { return null; } /** * Returns the spatial reference system identifier (srid) for a particular * geometry column. * <p> * This method is given a direct connection to the database. The connection * must not be closed. However any statements or result sets instantiated * from the connection must be closed. * </p> * <p> * In the event that the srid cannot be determined, this method should return * <code>null</code>. * </p> * @param schemaName The database schema, could be <code>null</code>. * @param tableName The table, never <code>null</code>. * @param columnName The column name, never <code>null</code> * @param cx The database connection. */ public Integer getGeometrySRID(String schemaName, String tableName, String columnName, Connection cx) throws SQLException { return null; } /** * Turns the specified srid into a {@link CoordinateReferenceSystem}, or returns <code>null</code> if not possible. * <p> * The implementation might just use <code>CRS.decode("EPSG:" + srid)</code>, but most spatial databases will have * their own SRS database that can be queried as well.</p> * <p>As a rule of thumb you should override this method if your spatial database uses codes that are * not part of the EPSG standard database, of if for some reason you deem it preferable to use * your database definition instead of an official EPSG one.</p> * <p>Most overrides will try out to decode the official EPSG code first, and fall back on * the custom database definition otherwise</p> * @param srid * @return */ public CoordinateReferenceSystem createCRS(int srid, Connection cx) throws SQLException { try { return CRS.decode("EPSG:" + srid); } catch(Exception e) { if(LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Could not decode " + srid + " using the built-in EPSG database"); } return null; } } /** * Returns the bounds of all geometry columns in the layer using any approach that proves * to be faster than the plain bounds aggregation * (e.g., better than the "plain select extent(geom) from table" on PostGIS), * or null if none exists or the fast method has not been enabled (e.g., if the fast method is * just an estimate of the bounds you probably want the user to enable it manually) * * @param schema * The database schema, if any, or null * @param featureType * The feature type containing the geometry columns whose bounds need to computed. * Mind, it may be retyped and thus contain less geometry columns than the table * @param cx * @return a list of referenced envelopes (some of which may be null or empty) */ public List<ReferencedEnvelope> getOptimizedBounds(String schema, SimpleFeatureType featureType, Connection cx) throws SQLException, IOException { return null; } /** * Encodes the spatial extent function of a geometry column in a SELECT statement. * <p> * This method must also be sure to properly encode the name of the column * with the {@link #encodeColumnName(String, StringBuffer)} function. * </p> * @param tableName */ public abstract void encodeGeometryEnvelope(String tableName, String geometryColumn, StringBuffer sql); /** * Decodes the result of a spatial extent function in a SELECT statement. * <p> * This method is given direct access to a result set. The <tt>column</tt> * parameter is the index into the result set which contains the spatial * extent value. The query for this value is build with the {@link #encodeGeometryEnvelope(String, String, StringBuffer)} * method. * </p> * <p> * This method must not read any other objects from the result set other then * the one referenced by <tt>column</tt>. * </p> * @param rs A result set * @param column Index into the result set which points at the spatial extent * value. * @param The database connection. */ public abstract Envelope decodeGeometryEnvelope(ResultSet rs, int column, Connection cx ) throws SQLException, IOException; /** * Encodes the name of a geometry column in a SELECT statement. * <p> * This method should wrap the column name in any functions that are used to * retrieve its value. For instance, often it is necessary to use the function * <code>asText</code>, or <code>asWKB</code> when fetching a geometry. * </p> * <p> * This method must also be sure to properly encode the name of the column * with the {@link #encodeColumnName(String, StringBuffer)} function. * </p> * <p> * Example: * </p> * <pre> * <code> * sql.append( "asText(" ); * column( gatt.getLocalName(), sql ); * sql.append( ")" ); * </code> * </pre> * </p> * <p> * This default implementation simply uses the column name without any * wrapping function, subclasses must override. * </p> */ public void encodeGeometryColumn(GeometryDescriptor gatt, int srid, StringBuffer sql) { encodeColumnName(gatt.getLocalName(), sql); } /** * Encodes a generalized geometry using a DB provided SQL function if available * If not supported, subclasses should not implement * Only called if {@link Hints#GEOMETRY_GENERALIZATION is supported} * * Example: * </p> * <pre> * <code> * sql.append( "asText(generalize(" ); * column( gatt.getLocalName(), sql ); * sql.append( "," ); * sql.append(distance); * sql.append( "))" ); * </code> * </pre> * </p> * <p> * */ public void encodeGeometryColumnGeneralized(GeometryDescriptor gatt, int srid,StringBuffer sql, Double distance) { throw new UnsupportedOperationException("Geometry generalization not supported"); } /** * * Encodes a simplified geometry using a DB provided SQL function if available * If not supported, subclasses should not implement * Only called if {@link Hints#GEOMETRY_SIMPLIFICATION is supported} * @see SQLDialect#encodeGeometryColumnGeneralized(GeometryDescriptor, StringBuffer, Double) * */ public void encodeGeometryColumnSimplified(GeometryDescriptor gatt, int srid,StringBuffer sql, Double distance) { throw new UnsupportedOperationException("Geometry simplification not supported"); } /** * Decodes a geometry value from the result of a query. * <p> * This method is given direct access to a result set. The <tt>column</tt> * parameter is the index into the result set which contains the geometric * value. * </p> * <p> * An implementation should deserialize the value provided by the result * set into {@link Geometry} object. For example, consider an implementation * which deserializes from well known text: * <code> * <pre> * String wkt = rs.getString( column ); * if ( wkt == null ) { * return null; * } * return new WKTReader(factory).read( wkt ); * </pre> * </code> * Note that implementations must handle <code>null</code> values. * </p> * <p> * The <tt>factory</tt> parameter should be used to instantiate any geometry * objects. * </p> */ public abstract Geometry decodeGeometryValue(GeometryDescriptor descriptor, ResultSet rs, String column, GeometryFactory factory, Connection cx ) throws IOException, SQLException; /** * Decodes a geometry value from the result of a query specifying the column * as an index. * <p> * See {@link #decodeGeometryValue(GeometryDescriptor, ResultSet, String, GeometryFactory)} * for a more in depth description. * </p> * @see {@link #decodeGeometryValue(GeometryDescriptor, ResultSet, String, GeometryFactory)}. */ public Geometry decodeGeometryValue(GeometryDescriptor descriptor, ResultSet rs, int column, GeometryFactory factory, Connection cx ) throws IOException, SQLException { String columnName = rs.getMetaData().getColumnName( column ); return decodeGeometryValue(descriptor, rs, columnName, factory, cx); } /** * Encodes the primary key definition in a CREATE TABLE statement. * <p> * Subclasses should override this method if need be, the default implementation does the * following: * <pre> * <code> * encodeColumnName( column, sql ); * sql.append( " int PRIMARY KEY" ); * </code> * </pre> * </p> * */ public void encodePrimaryKey(String column, StringBuffer sql) { encodeColumnName( column, sql ); sql.append( " INTEGER PRIMARY KEY" ); } /** * Encodes anything post a column in a CREATE TABLE statement. * <p> * This is appended after the column name and type. Subclasses may choose to override * this method, the default implementation does nothing. * </p> * @param att The attribute corresponding to the column. */ public void encodePostColumnCreateTable(AttributeDescriptor att, StringBuffer sql) { } /** * Encodes anything post a CREATE TABLE statement. * <p> * This is appended to a CREATE TABLE statement after the column definitions. * This default implementation does nothing, subclasses should override as * need be. * </p> */ public void encodePostCreateTable(String tableName, StringBuffer sql) { } /** * Encodes anything after the SELECT clause and before the FROM clause. * <p> * This method does not nothing, subclass may override to add additional columns. * </p> * @param featureType The feature type being queried. */ public void encodePostSelect(SimpleFeatureType featureType, StringBuffer sql) { } /** * Callback to execute any additional sql statements post a create table * statement. * <p> * This method should be implemented by subclasses that need to do some post * processing on the database after a table has been created. Examples might * include: * <ul> * <li>Creating a sequence for a primary key * <li>Registering geometry column metadata * <li>Creating a spatial index * </ul> * </p> * <p> * A common case is creating an auto incrementing sequence for the primary * key of a table. It should be noted that all tables created through the * datastore use the column "fid" as the primary key. * </p> * <p> * A direct connection to the database is provided (<tt>cx</tt>). This * connection must not be closed, however any statements or result sets * instantiated from the connection must be closed. * </p> * @param schemaName The name of the schema, may be <code>null</code>. * @param featureType The feature type that has just been created on the database. * @param cx Database connection. * */ public void postCreateTable(String schemaName, SimpleFeatureType featureType, Connection cx) throws SQLException, IOException { } /** * Callback which executes after an attribute descriptor has been built from a table column. * <p> * The result set <tt>columnMetadata</tt> should not be modified in any way (including scrolling) * , it should only be read from. * </p> * <p> * This base implementation does nothing, subclasses should override as need be. * </p> * @param att The built attribute descriptor. * @param tableName The name of the table containing the column * @param schemaName The name of the database scheam containing the table containing the column * @param cx The database connection. */ public void postCreateAttribute(AttributeDescriptor att, String tableName, String schemaName, Connection cx ) throws SQLException { } /** * Callback which executes after a feature type has been built from a database table. * <p> * This base implementation does nothing, subclasses should override as need be. * </p> * @param featureType The build feature type. * @param metadata The database metadata. * @param schemaName The name of the database scheam containing the table containing the column * @param cx The database connection. */ public void postCreateFeatureType(SimpleFeatureType featureType, DatabaseMetaData metadata, String schemaName, Connection cx) throws SQLException { } /** * Controls whether keys are looked up post or pre insert. * <p> * When a row is inserted into a table, and a key is automatically generated * it can be looked up before the insert occurs, or after the insert has been made. * Returning <code>false</code> will cause the lookup to occur before the insert * via {@link #getNextAutoGeneratedValue(String, String, String, Connection)}. * Returning <code>true</code> will cause the lookup to occur after the insert via * {@link #getLastAutoGeneratedValue(String, String, String, Connection)}. * </p> * <p> * Subclasses returning false should implement: * <ul> * <li>{@link #getNextAutoGeneratedValue(String, String, String, Connection)} * </ul> * </p> * <p> * Subclasses returning true should implement: * <ul> * <li>{@link #getLastAutoGeneratedValue(String, String, String, Connection)} * </ul> * </p> */ public boolean lookupGeneratedValuesPostInsert() { return false; } /** * Obtains the next value of an auto generated column. * <p> * Implementations should determine the next value of a column for which * values are automatically generated by the database. * </p> * <p> * This method is given a direct connection to the database, but this connection * should never be closed. However any statements or result sets instantiated * from the connection must be closed. * </p> * <p> * Implementations should handle the case where <tt>schemaName</tt> is <code>null</code>. * </p> * @param schemaName The schema name, this might be <code>null</code>. * @param tableName The name of the table. * @param columnName The column. * @param cx The database connection. * * @return The next value of the column, or <code>null</code>. */ public Object getNextAutoGeneratedValue(String schemaName, String tableName, String columnName, Connection cx) throws SQLException { return null; } /** * Obtains the last value of an auto generated column. * <p> * This method is only called when {@link #lookupGeneratedValuesPostInsert()} returns true. * Implementations should determine the previous value of a column for which was automatically * generated by the database. * </p> * <p> * This method is given a direct connection to the database, but this connection * should never be closed. However any statements or result sets instantiated * from the connection must be closed. * </p> * <p> * Implementations should handle the case where <tt>schemaName</tt> is <code>null</code>. * </p> * @param schemaName The schema name, this might be <code>null</code>. * @param tableName The name of the table. * @param columnName The column. * @param cx The database connection. * * @return The previous value of the column, or <code>null</code>. */ public Object getLastAutoGeneratedValue(String schemaName, String tableName, String columnName, Connection cx) throws SQLException { return null; } /** * Determines the name of the sequence (if any) which is used to increment * generate values for a table column. * <p> * This method should return null if no such sequence exists. * </p> * <p> * This method is given a direct connection to the database, but this connection * should never be closed. However any statements or result sets instantiated * from the connection must be closed. * </p> * @param schemaName The schema name, this might be <code>null</code>. * @param tableName The table name. * @param columnName The column name. * @param cx The database connection. * */ public String getSequenceForColumn(String schemaName, String tableName, String columnName, Connection cx) throws SQLException { return null; } /** * Obtains the next value of a sequence, incrementing the sequence to the next state in the * process. * <p> * Implementations should determine the next value of a column for which * values are automatically generated by the database. * </p> * <p> * This method is given a direct connection to the database, but this connection * should never be closed. However any statements or result sets instantiated * from the connection must be closed. * </p> * <p> * Implementations should handle the case where <tt>schemaName</tt> is <code>null</code>. * </p> * @param schemaName The schema name, this might be <code>null</code>. * @param sequenceName The name of the sequence. * @param cx The database connection. * * @return The next value of the sequence, or <code>null</code>. */ public Object getNextSequenceValue(String schemaName, String sequenceName, Connection cx ) throws SQLException { return null; } /** * Returns true if this dialect can encode both {@linkplain Query#getStartIndex()} * and {@linkplain Query#getMaxFeatures()} into native SQL. * @return */ public boolean isLimitOffsetSupported() { return false; } /** * Alters the query provided so that limit and offset are natively dealt with. This might mean * simply appending some extra directive to the query, or wrapping it into a bigger one. * @param sql * @param limit * @param offset */ public void applyLimitOffset(StringBuffer sql, int limit, int offset) { throw new UnsupportedOperationException("Ovveride this method when isLimitOffsetSupported returns true"); } /** * Add hints to the JDBC Feature Source. A subclass * can override * * possible hints (but not limited to) * * {@link Hints#GEOMETRY_GENERALIZATION} * {@link Hints#GEOMETRY_SIMPLIFICATION} * * @param hints */ protected void addSupportedHints(Set<Hints.Key> hints) { } /** * Determines the default length that a varchar field should be when creating * datastore tables from feature types. * <p> * Some dialects allow no length to be specified for varchar fields (PostGIS * for example) however others require a maximum length to be set. * </p> * <p> * Subclasses can override this method and either return -1 to specify that * no length is required, or otherwise return an appropriate default length * for varchars of that dialect. * </p> */ public int getDefaultVarcharSize(){ return 255; } /** * Determine if a read query should be set to autocommit. * <p> * Some databases (like postgres) want this enabled to respect fetch size. * The default implementation is to return false. * </p> * @return true if read queries should remain autocommit, false otherwise */ public boolean isAutoCommitQuery() { return false; } }