001 //$HeadURL: http://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/io/datastore/sql/AbstractSQLDatastore.java $
002 /*----------------------------------------------------------------------------
003 This file is part of deegree, http://deegree.org/
004 Copyright (C) 2001-2009 by:
005 Department of Geography, University of Bonn
006 and
007 lat/lon GmbH
008
009 This library is free software; you can redistribute it and/or modify it under
010 the terms of the GNU Lesser General Public License as published by the Free
011 Software Foundation; either version 2.1 of the License, or (at your option)
012 any later version.
013 This library is distributed in the hope that it will be useful, but WITHOUT
014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016 details.
017 You should have received a copy of the GNU Lesser General Public License
018 along with this library; if not, write to the Free Software Foundation, Inc.,
019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020
021 Contact information:
022
023 lat/lon GmbH
024 Aennchenstr. 19, 53177 Bonn
025 Germany
026 http://lat-lon.de/
027
028 Department of Geography, University of Bonn
029 Prof. Dr. Klaus Greve
030 Postfach 1147, 53001 Bonn
031 Germany
032 http://www.geographie.uni-bonn.de/deegree/
033
034 e-mail: info@deegree.org
035 ----------------------------------------------------------------------------*/
036 package org.deegree.io.datastore.sql;
037
038 import java.sql.Connection;
039 import java.sql.PreparedStatement;
040 import java.sql.ResultSet;
041 import java.sql.SQLException;
042 import java.sql.Statement;
043 import java.sql.Timestamp;
044 import java.util.Date;
045 import java.util.Iterator;
046 import java.util.List;
047 import java.util.Set;
048
049 import org.deegree.datatypes.Types;
050 import org.deegree.datatypes.UnknownTypeException;
051 import org.deegree.framework.log.ILogger;
052 import org.deegree.framework.log.LoggerFactory;
053 import org.deegree.framework.util.TimeTools;
054 import org.deegree.i18n.Messages;
055 import org.deegree.io.DBConnectionPool;
056 import org.deegree.io.JDBCConnection;
057 import org.deegree.io.datastore.AnnotationDocument;
058 import org.deegree.io.datastore.Datastore;
059 import org.deegree.io.datastore.DatastoreConfiguration;
060 import org.deegree.io.datastore.DatastoreException;
061 import org.deegree.io.datastore.DatastoreTransaction;
062 import org.deegree.io.datastore.FeatureId;
063 import org.deegree.io.datastore.TransactionException;
064 import org.deegree.io.datastore.idgenerator.IdGenerationException;
065 import org.deegree.io.datastore.schema.MappedFeatureType;
066 import org.deegree.io.datastore.schema.MappedGeometryPropertyType;
067 import org.deegree.io.datastore.schema.content.SQLFunctionCall;
068 import org.deegree.io.datastore.sql.StatementBuffer.StatementArgument;
069 import org.deegree.io.datastore.sql.transaction.SQLTransaction;
070 import org.deegree.io.datastore.sql.wherebuilder.WhereBuilder;
071 import org.deegree.model.crs.CoordinateSystem;
072 import org.deegree.model.crs.UnknownCRSException;
073 import org.deegree.model.feature.FeatureCollection;
074 import org.deegree.model.feature.GMLFeatureDocument;
075 import org.deegree.model.filterencoding.Filter;
076 import org.deegree.model.spatialschema.Geometry;
077 import org.deegree.ogcbase.SortProperty;
078 import org.deegree.ogcwebservices.wfs.operation.Lock;
079 import org.deegree.ogcwebservices.wfs.operation.Query;
080
081 /**
082 * This abstract class implements the common functionality of {@link Datastore} implementations that use SQL databases
083 * as backend.
084 *
085 * @see QueryHandler
086 *
087 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
088 * @author last edited by: $Author: aschmitz $
089 *
090 * @version $Revision: 20115 $, $Date: 2009-10-14 10:57:39 +0200 (Wed, 14 Oct 2009) $
091 */
092 public abstract class AbstractSQLDatastore extends Datastore {
093
094 private static final ILogger LOG = LoggerFactory.getLogger( AbstractSQLDatastore.class );
095
096 /** Database specific SRS code for an unspecified SRS. */
097 protected static final int SRS_UNDEFINED = -1;
098
099 /** Pool of database connections. */
100 protected DBConnectionPool pool;
101
102 private DatastoreTransaction activeTransaction;
103
104 private Thread transactionHolder;
105
106 @Override
107 public AnnotationDocument getAnnotationParser() {
108 return new SQLAnnotationDocument( this.getClass() );
109 }
110
111 @Override
112 public void configure( DatastoreConfiguration datastoreConfiguration )
113 throws DatastoreException {
114 super.configure( datastoreConfiguration );
115 this.pool = DBConnectionPool.getInstance();
116 }
117
118 @Override
119 public void close()
120 throws DatastoreException {
121 LOG.logInfo( "close() does not do nothing for AbstractSQLDatastore because no resources must be released" );
122 }
123
124 /**
125 * Overwrite this to return a database specific (spatial capable) {@link WhereBuilder} implementation.
126 *
127 * @param rootFts
128 * involved (requested) feature types
129 * @param aliases
130 * aliases for the feature types, may be null
131 * @param filter
132 * filter that restricts the matched features
133 * @param sortProperties
134 * sort criteria for the result, may be null or empty
135 * @param aliasGenerator
136 * used to generate unique table aliases
137 * @param vcProvider
138 * @return <code>WhereBuilder</code> implementation suitable for this datastore
139 * @throws DatastoreException
140 */
141 public WhereBuilder getWhereBuilder( MappedFeatureType[] rootFts, String[] aliases, Filter filter,
142 SortProperty[] sortProperties, TableAliasGenerator aliasGenerator,
143 VirtualContentProvider vcProvider )
144 throws DatastoreException {
145 return new WhereBuilder( rootFts, aliases, filter, sortProperties, aliasGenerator, vcProvider );
146 }
147
148 @Override
149 public FeatureCollection performQuery( Query query, MappedFeatureType[] rootFts )
150 throws DatastoreException, UnknownCRSException {
151 Connection conn = acquireConnection();
152 FeatureCollection result;
153 try {
154 result = performQuery( query, rootFts, conn );
155 } finally {
156 releaseConnection( conn );
157 }
158 return result;
159 }
160
161 @Override
162 public FeatureCollection performQuery( Query query, MappedFeatureType[] rootFts, DatastoreTransaction context )
163 throws DatastoreException, UnknownCRSException {
164 return performQuery( query, rootFts, ( (SQLTransaction) context ).getConnection() );
165 }
166
167 /**
168 * Performs a {@link Query} against the datastore.
169 * <p>
170 * Note that this method is responsible for the coordinate system tranformation of the input {@link Query} and the
171 * output {@link FeatureCollection}.
172 *
173 * @param query
174 * query to be performed
175 * @param rootFts
176 * the root feature types that are queried, more than one type means that the types are joined
177 * @param conn
178 * JDBC connection to use
179 * @return requested feature instances
180 * @throws DatastoreException
181 * @throws UnknownCRSException
182 */
183 protected FeatureCollection performQuery( Query query, MappedFeatureType[] rootFts, Connection conn )
184 throws DatastoreException, UnknownCRSException {
185
186 Query transformedQuery = transformQuery( query );
187
188 FeatureCollection result = null;
189 try {
190 QueryHandler queryHandler = new QueryHandler( this, new TableAliasGenerator(), conn, rootFts, query );
191 result = queryHandler.performQuery();
192 } catch ( SQLException e ) {
193 String msg = "SQL error while performing query: " + e.getMessage();
194 LOG.logError( msg, e );
195 throw new DatastoreException( msg, e );
196 }
197
198 // transform result to queried srs (only if necessary)
199 String targetSrs = transformedQuery.getSrsName();
200 if ( targetSrs != null && !this.canTransformTo( targetSrs ) ) {
201 result = transformResult( result, targetSrs );
202 }
203 return result;
204 }
205
206 /**
207 * Acquires transactional access to the datastore. There's only one active transaction per datastore instance
208 * allowed at a time.
209 *
210 * @return transaction object that allows to perform transaction operations on the datastore
211 * @throws DatastoreException
212 */
213 @Override
214 public DatastoreTransaction acquireTransaction()
215 throws DatastoreException {
216
217 while ( this.activeTransaction != null ) {
218 Thread holder = this.transactionHolder;
219 // check if transaction holder variable has (just) been cleared or if the other thread
220 // has been killed (avoid deadlocks)
221 if ( holder == null || !holder.isAlive() ) {
222 this.activeTransaction = null;
223 this.transactionHolder = null;
224 break;
225 }
226 }
227
228 this.activeTransaction = createTransaction();
229 this.transactionHolder = Thread.currentThread();
230 return this.activeTransaction;
231 }
232
233 /**
234 * Creates a new {@link SQLTransaction} that provides transactional access.
235 *
236 * @return new {@link SQLTransaction} instance
237 * @throws DatastoreException
238 */
239 protected SQLTransaction createTransaction()
240 throws DatastoreException {
241 return new SQLTransaction( this, new TableAliasGenerator(), acquireConnection() );
242 }
243
244 /**
245 * Returns the transaction to the datastore. This makes the transaction available to other clients again (via
246 * <code>acquireTransaction</code>). Underlying resources (such as JDBCConnections are freed).
247 * <p>
248 * The transaction should be terminated, i.e. commit() or rollback() must have been called before.
249 *
250 * @param ta
251 * the DatastoreTransaction to be returned
252 * @throws DatastoreException
253 */
254 @Override
255 public void releaseTransaction( DatastoreTransaction ta )
256 throws DatastoreException {
257 if ( ta.getDatastore() != this ) {
258 String msg = Messages.getMessage( "DATASTORE_TA_NOT_OWNER" );
259 throw new TransactionException( msg );
260 }
261 if ( ta != this.activeTransaction ) {
262 String msg = Messages.getMessage( "DATASTORE_TA_NOT_ACTIVE" );
263 throw new TransactionException( msg );
264 }
265 releaseConnection( ( (SQLTransaction) ta ).getConnection() );
266
267 this.activeTransaction = null;
268 this.transactionHolder = null;
269 }
270
271 @Override
272 public Set<FeatureId> determineFidsToLock( List<Lock> requestParts )
273 throws DatastoreException {
274
275 Set<FeatureId> lockedFids = null;
276 Connection conn = acquireConnection();
277 LockHandler handler = new LockHandler( this, new TableAliasGenerator(), conn, requestParts );
278 try {
279 lockedFids = handler.determineFidsToLock();
280 } finally {
281 releaseConnection( conn );
282 }
283 return lockedFids;
284 }
285
286 /**
287 * Converts a database specific geometry <code>Object</code> from the <code>ResultSet</code> to a deegree
288 * <code>Geometry</code>.
289 *
290 * @param value
291 * @param targetSRS
292 * @param conn
293 * @return corresponding deegree geometry
294 * @throws SQLException
295 */
296 public abstract Geometry convertDBToDeegreeGeometry( Object value, CoordinateSystem targetSRS, Connection conn )
297 throws SQLException;
298
299 /**
300 * Converts a deegree <code>Geometry</code> to a database specific geometry <code>Object</code>.
301 *
302 * @param geometry
303 * @param nativeSRSCode
304 * @param conn
305 * @return corresponding database specific geometry object
306 * @throws DatastoreException
307 */
308 public abstract Object convertDeegreeToDBGeometry( Geometry geometry, int nativeSRSCode, Connection conn )
309 throws DatastoreException;
310
311 /**
312 * Returns the database connection requested for.
313 *
314 * @return Connection
315 * @throws DatastoreException
316 */
317 protected Connection acquireConnection()
318 throws DatastoreException {
319 JDBCConnection jdbcConnection = ( (SQLDatastoreConfiguration) this.getConfiguration() ).getJDBCConnection();
320 Connection conn = null;
321 try {
322 conn = pool.acquireConnection( jdbcConnection.getDriver(), jdbcConnection.getURL(),
323 jdbcConnection.getUser(), jdbcConnection.getPassword() );
324 } catch ( Exception e ) {
325 String msg = "Cannot acquire database connection: " + e.getMessage();
326 LOG.logError( msg, e );
327 throw new DatastoreException( msg, e );
328 }
329 return conn;
330 }
331
332 /**
333 * Releases the connection.
334 *
335 * @param conn
336 * connection to be released.
337 * @throws DatastoreException
338 */
339 public void releaseConnection( Connection conn )
340 throws DatastoreException {
341 LOG.logDebug( "Releasing JDBCConnection." );
342 JDBCConnection jdbcConnection = ( (SQLDatastoreConfiguration) this.getConfiguration() ).getJDBCConnection();
343 try {
344 pool.releaseConnection( conn, jdbcConnection.getDriver(), jdbcConnection.getURL(),
345 jdbcConnection.getUser(), jdbcConnection.getPassword() );
346 } catch ( Exception e ) {
347 String msg = "Cannot release database connection: " + e.getMessage();
348 LOG.logError( msg, e );
349 throw new DatastoreException( msg, e );
350 }
351 }
352
353 /**
354 * Converts the <code>StatementBuffer</code> into a <code>PreparedStatement</code>, which is initialized and ready
355 * to be performed.
356 *
357 * @param conn
358 * connection to be used to create the <code>PreparedStatement</code>
359 * @param statementBuffer
360 * @return the <code>PreparedStatment</code>, ready to be performed
361 * @throws SQLException
362 * if a JDBC related error occurs
363 * @throws DatastoreException
364 */
365 public PreparedStatement prepareStatement( Connection conn, StatementBuffer statementBuffer )
366 throws SQLException, DatastoreException {
367
368 LOG.logDebug( "Preparing statement: " + statementBuffer.getQueryString() );
369 PreparedStatement preparedStatement = conn.prepareStatement( statementBuffer.getQueryString() );
370
371 Iterator<StatementArgument> argumentIter = statementBuffer.getArgumentsIterator();
372 int i = 1;
373 while ( argumentIter.hasNext() ) {
374 StatementArgument argument = argumentIter.next();
375 int targetSqlType = argument.getTypeCode();
376 Object obj = argument.getArgument();
377 Object sqlObject = obj != null ? convertToDBType( obj, targetSqlType ) : null;
378
379 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
380 try {
381 String typeName = Types.getTypeNameForSQLTypeCode( targetSqlType );
382 LOG.logDebug( "Setting argument " + i + ": type=" + typeName + ", value class="
383 + ( sqlObject == null ? "none (null object)" : sqlObject.getClass() ) );
384 if ( sqlObject instanceof String ) {
385 String s = (String) sqlObject;
386 if ( s.length() <= 100 ) {
387 LOG.logDebug( "Value: '" + s + "'" );
388 } else {
389 LOG.logDebug( "Value: '" + s.substring( 0, 100 ) + "...'" );
390 }
391 }
392 if ( sqlObject instanceof Number ) {
393 LOG.logDebug( "Value: '" + sqlObject + "'" );
394 }
395 } catch ( UnknownTypeException e ) {
396 LOG.logError( e.getMessage(), e );
397 throw new SQLException( e.getMessage() );
398 }
399 }
400 if ( sqlObject == null ) {
401 preparedStatement.setNull( i, targetSqlType );
402 } else {
403 preparedStatement.setObject( i, sqlObject, targetSqlType );
404 }
405 i++;
406 }
407 return preparedStatement;
408 }
409
410 /**
411 * Converts the given object into an object that is suitable for a table column of the specified SQL type.
412 * <p>
413 * The return value is used in a java.sql.PreparedStatement#setObject() call.
414 * <p>
415 * Please note that this implementation is subject to change. There are missing type cases, and it is preferable to
416 * use the original string representation of the input object (except for geometries).
417 * <p>
418 * NOTE: Almost identical functionality exists in {@link GMLFeatureDocument}. This is subject to change.
419 *
420 * @see java.sql.PreparedStatement#setObject(int, Object, int)
421 * @param o
422 * @param sqlTypeCode
423 * @return an object that is suitable for a table column of the specified SQL type
424 * @throws DatastoreException
425 */
426 private Object convertToDBType( Object o, int sqlTypeCode )
427 throws DatastoreException {
428
429 Object sqlType = null;
430
431 switch ( sqlTypeCode ) {
432 case Types.VARCHAR: {
433 sqlType = o.toString();
434 break;
435 }
436 case Types.INTEGER:
437 case Types.SMALLINT: {
438 try {
439 sqlType = new Integer( o.toString().trim() );
440 } catch ( NumberFormatException e ) {
441 throw new DatastoreException( "'" + o + "' does not denote a valid Integer value." );
442 }
443 break;
444 }
445 case Types.NUMERIC:
446 case Types.REAL:
447 case Types.DOUBLE: {
448 try {
449 sqlType = new Double( o.toString() );
450 } catch ( NumberFormatException e ) {
451 throw new DatastoreException( "'" + o + "' does not denote a valid Double value." );
452 }
453 break;
454 }
455 case Types.DECIMAL:
456 case Types.FLOAT: {
457 try {
458 sqlType = new Float( o.toString() );
459 } catch ( NumberFormatException e ) {
460 throw new DatastoreException( "'" + o + "' does not denote a valid Double value." );
461 }
462 break;
463 }
464 case Types.BOOLEAN: {
465 sqlType = new Boolean( o.toString() );
466 break;
467 }
468 case Types.DATE: {
469 if ( o instanceof Date ) {
470 sqlType = new java.sql.Date( ( (Date) o ).getTime() );
471 } else {
472 String s = o.toString();
473 int idx = s.indexOf( " " ); // Datestring like "2005-04-21 00:00:00"
474 if ( -1 != idx )
475 s = s.substring( 0, idx );
476 sqlType = new java.sql.Date( TimeTools.createCalendar( s ).getTimeInMillis() );
477 }
478 break;
479 }
480 case Types.TIME: {
481 if ( o instanceof Date ) {
482 sqlType = new java.sql.Time( ( (Date) o ).getTime() );
483 } else {
484 sqlType = new java.sql.Time( TimeTools.createCalendar( o.toString() ).getTimeInMillis() );
485 }
486 break;
487 }
488 case Types.TIMESTAMP: {
489 if ( o instanceof Date ) {
490 sqlType = new Timestamp( ( (Date) o ).getTime() );
491 } else {
492 sqlType = new java.sql.Timestamp( TimeTools.createCalendar( o.toString() ).getTimeInMillis() );
493 }
494 break;
495 }
496 default: {
497 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
498 String sqlTypeName = "" + sqlTypeCode;
499 try {
500 sqlTypeName = Types.getTypeNameForSQLTypeCode( sqlTypeCode );
501 } catch ( UnknownTypeException e ) {
502 LOG.logError( e.getMessage(), e );
503 }
504 LOG.logDebug( "No type conversion for sql type '" + sqlTypeName
505 + "' defined. Passing argument of type '" + o.getClass().getName() + "'." );
506 }
507 sqlType = o;
508 }
509 }
510 return sqlType;
511 }
512
513 /**
514 * Converts the given object from a <code>java.sql.ResultSet</code> column to the common type to be used as a
515 * feature property.
516 *
517 * @param rsObject
518 * @param sqlTypeCode
519 * @return an object that is suitable for a table column of the specified SQL type
520 * @throws DatastoreException
521 */
522 @SuppressWarnings("unused")
523 public Object convertFromDBType( Object rsObject, int sqlTypeCode )
524 throws DatastoreException {
525 return rsObject;
526 }
527
528 /**
529 * Overwrite this to enable the datastore to fetch the next value of a SQL sequence.
530 *
531 * @param conn
532 * JDBC connection to be used.
533 * @param sequence
534 * name of the SQL sequence.
535 * @return next value of the given SQL sequence
536 * @throws DatastoreException
537 * if the value could not be retrieved
538 */
539 public Object getSequenceNextVal( Connection conn, String sequence )
540 throws DatastoreException {
541 String msg = Messages.getMessage( "DATASTORE_SEQ_NOT_SUPPORTED", this.getClass().getName() );
542 throw new DatastoreException( msg );
543 }
544
545 /**
546 * Overwrite this to enable the datastore to fetch the current value (plus an offset) of a SQL sequence.
547 *
548 * @param conn
549 * JDBC connection to be used.
550 * @param sequence
551 * name of the SQL sequence
552 * @param offset
553 * offset added to the sequence value
554 * @return next value of the given SQL sequence
555 * @throws DatastoreException
556 * if the value could not be retrieved
557 */
558 public Object getSequenceCurrValPlusOffset( Connection conn, String sequence, int offset )
559 throws DatastoreException {
560 String msg = Messages.getMessage( "DATASTORE_SEQ_NOT_SUPPORTED", this.getClass().getName() );
561 throw new DatastoreException( msg );
562 }
563
564 /**
565 * Returns the maximum (integer) value stored in a certain table column.
566 *
567 * @param conn
568 * JDBC connection to be used
569 * @param tableName
570 * name of the table
571 * @param columnName
572 * name of the column
573 * @return the maximum value
574 * @throws IdGenerationException
575 * if the value could not be retrieved
576 */
577 public int getMaxValue( Connection conn, String tableName, String columnName )
578 throws IdGenerationException {
579
580 int max = 0;
581 Statement stmt = null;
582 ResultSet rs = null;
583
584 LOG.logDebug( "Retrieving max value in " + tableName + "." + columnName + "..." );
585
586 try {
587 try {
588 stmt = conn.createStatement();
589 rs = stmt.executeQuery( "SELECT MAX(" + columnName + ") FROM " + tableName );
590 if ( rs.next() ) {
591 Object columnMax = rs.getObject( 1 );
592 if ( columnMax != null ) {
593 if ( columnMax instanceof Integer ) {
594 max = ( (Integer) columnMax ).intValue();
595 } else {
596 max = Integer.parseInt( columnMax.toString() );
597 }
598 }
599 }
600 } finally {
601 try {
602 if ( rs != null ) {
603 rs.close();
604 }
605 } finally {
606 if ( stmt != null ) {
607 stmt.close();
608 }
609 }
610 }
611 } catch ( SQLException e ) {
612 String msg = "Could not retrieve max value for table column '" + tableName + "." + columnName + "': "
613 + e.getMessage();
614 LOG.logError( msg, e );
615 throw new IdGenerationException( msg, e );
616 } catch ( NumberFormatException e ) {
617 String msg = "Could not convert selected value to integer: " + e.getMessage();
618 LOG.logError( msg, e );
619 throw new IdGenerationException( msg, e );
620 }
621 LOG.logDebug( "max value: " + max );
622 return max;
623 }
624
625 /**
626 * Returns an {@link SQLFunctionCall} that refers to the given {@link MappedGeometryPropertyType} in the specified
627 * target SRS using a database specific SQL function.
628 *
629 * @param geoProperty
630 * geometry property
631 * @param targetSRS
632 * target spatial reference system (usually "EPSG:XYZ")
633 * @return an {@link SQLFunctionCall} that refers to the geometry in the specified srs
634 * @throws DatastoreException
635 */
636 public SQLFunctionCall buildSRSTransformCall( @SuppressWarnings("unused") MappedGeometryPropertyType geoProperty,
637 @SuppressWarnings("unused") String targetSRS )
638 throws DatastoreException {
639 String msg = Messages.getMessage( "DATASTORE_SQL_NATIVE_CT_UNSUPPORTED", this.getClass().getName() );
640 throw new DatastoreException( msg );
641 }
642
643 /**
644 * Builds an SQL fragment that converts the given geometry to the specified SRS.
645 *
646 * @param geomIdentifier
647 * @param nativeSRSCode
648 * @return an SQL fragment that converts the given geometry to the specified SRS
649 * @throws DatastoreException
650 */
651 public String buildSRSTransformCall( @SuppressWarnings("unused") String geomIdentifier,
652 @SuppressWarnings("unused") int nativeSRSCode )
653 throws DatastoreException {
654 String msg = Messages.getMessage( "DATASTORE_SQL_NATIVE_CT_UNSUPPORTED", this.getClass().getName() );
655 throw new DatastoreException( msg );
656 }
657
658 /**
659 * Returns the database specific code for the given SRS name.
660 *
661 * @param srsName
662 * spatial reference system name (usually "EPSG:XYZ")
663 * @return the database specific SRS code, or -1 if no corresponding native code is known
664 * @throws DatastoreException
665 */
666 public int getNativeSRSCode( @SuppressWarnings("unused") String srsName )
667 throws DatastoreException {
668 String msg = Messages.getMessage( "DATASTORE_SQL_NATIVE_CT_UNSUPPORTED", this.getClass().getName() );
669 throw new DatastoreException( msg );
670 }
671
672 /**
673 * Checks whether the (native) coordinate transformation of the specified geometry property to the given SRS is
674 * possible (and necessary), i.e.
675 * <ul>
676 * <li>the internal srs of the property is specified (and not -1)
677 * <li>or the requested SRS is null or equal to the property's srs
678 * </ul>
679 * If this is not the case, a {@link DatastoreException} is thrown to indicate the problem.
680 *
681 * @param pt
682 * @param queriedSrs
683 * @return the srs to transform to, or null, if transformation is unnecessary
684 * @throws DatastoreException
685 */
686 String checkTransformation( MappedGeometryPropertyType pt, String queriedSrs )
687 throws DatastoreException {
688
689 String targetSrs = null;
690 int internalSrs = pt.getMappingField().getSRS();
691 String propertySrs = pt.getCS().getIdentifier();
692
693 if ( queriedSrs != null && !propertySrs.equals( queriedSrs ) ) {
694 if ( internalSrs == SRS_UNDEFINED ) {
695 String msg = Messages.getMessage( "DATASTORE_SRS_NOT_SPECIFIED", pt.getName(), queriedSrs, propertySrs );
696 throw new DatastoreException( msg );
697 }
698 targetSrs = queriedSrs;
699 }
700 return targetSrs;
701 }
702
703 public void appendGeometryColumnGet( StatementBuffer query, String tableAlias, String column ) {
704 query.append( tableAlias );
705 query.append( '.' );
706 query.append( column );
707 }
708 }