001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/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 (Mi, 14 Okt 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 }