diff -r 3e78c87bb673 modules/library/data/src/main/java/org/geotools/data/store/ContentFeatureStore.java --- a/modules/library/data/src/main/java/org/geotools/data/store/ContentFeatureStore.java Sun Jun 28 18:35:44 2009 +0800 +++ b/modules/library/data/src/main/java/org/geotools/data/store/ContentFeatureStore.java Tue Jun 30 01:13:13 2009 +1000 @@ -210,6 +210,9 @@ //add the id to the set of inserted ids.add( toWrite.getIdentifier() ); + + //copy any metadata from teh feature that was actually written + feature.getUserData().putAll( toWrite.getUserData() ); } } finally { diff -r 3e78c87bb673 modules/unsupported/jdbc-ng/jdbc-core/src/main/java/org/geotools/jdbc/JDBCDataStore.java --- a/modules/unsupported/jdbc-ng/jdbc-core/src/main/java/org/geotools/jdbc/JDBCDataStore.java Sun Jun 28 18:35:44 2009 +0800 +++ b/modules/unsupported/jdbc-ng/jdbc-core/src/main/java/org/geotools/jdbc/JDBCDataStore.java Tue Jun 30 01:13:13 2009 +1000 @@ -990,14 +990,19 @@ st = cx.createStatement(); } - //figure out what the next fid will be - List nextKeyValues = getNextValues( key, cx ); + //figure out if we should determine what the fid is pre or post insert + boolean postInsert = dialect.lookupGeneratedValuesPostInsert() && isGenerated(key); + + List keyValues = null; + if ( !postInsert ) { + keyValues = getNextValues( key, cx ); + } for (Iterator f = features.iterator(); f.hasNext();) { SimpleFeature feature = (SimpleFeature) f.next(); if ( dialect instanceof PreparedStatementSQLDialect ) { - PreparedStatement ps = insertSQLPS( featureType, feature, nextKeyValues, cx ); + PreparedStatement ps = insertSQLPS( featureType, feature, keyValues, cx ); try { ps.execute(); } @@ -1006,15 +1011,20 @@ } } else { - String sql = insertSQL(featureType, feature, nextKeyValues, cx); + String sql = insertSQL(featureType, feature, keyValues, cx); LOGGER.log(Level.FINE, "Inserting new feature: {0}", sql); //TODO: execute in batch to improve performance? st.execute(sql); } + if ( keyValues == null ) { + //grab the key values post insert + keyValues = getLastValues(key,cx); + } + //report the feature id as user data since we cant set the fid - String fid = featureType.getTypeName() + "." + encodeFID(nextKeyValues); + String fid = featureType.getTypeName() + "." + encodeFID(keyValues); feature.getUserData().put("fid", fid); } @@ -1289,6 +1299,20 @@ } /** + * Determines if a primary key is made up entirely of column which are generated + * via an auto-generating column or a sequence. + */ + protected boolean isGenerated( PrimaryKey pkey ) { + for ( PrimaryKeyColumn col : pkey.getColumns() ) { + if ( !(col instanceof AutoGeneratedPrimaryKeyColumn ) ) { + return false; + } + } + + return true; + } + + /** * Gets the next value of a primary key. */ protected List getNextValues( PrimaryKey pkey, Connection cx ) throws SQLException, IOException { @@ -1366,17 +1390,32 @@ return next; } + + /** + * Gets the last value of a generated primary key. + */ + protected List getLastValues( PrimaryKey pkey, Connection cx ) throws SQLException, IOException { + ArrayList last = new ArrayList(); + for( PrimaryKeyColumn col : pkey.getColumns() ) { + last.add( getLastValue( col, pkey, cx ) ); + } + return last; + } /** - * Determines the next FID of a feature to be instered based on the primary key. + * Gets the last value of a generated primary key column. */ - protected String getNextFID( PrimaryKey pkey, Connection cx ) throws SQLException, IOException { - StringBuffer fid = new StringBuffer(); - for ( PrimaryKeyColumn col : pkey.getColumns() ) { - Object next = getNextValue( col, pkey, cx ); - fid.append( next ); + protected Object getLastValue( PrimaryKeyColumn col, PrimaryKey pkey, Connection cx ) throws SQLException, IOException { + Object last = null; + + if ( col instanceof AutoGeneratedPrimaryKeyColumn ) { + last = dialect.getLastAutoGeneratedValue(databaseSchema, pkey.getTableName(), col.getName(), cx ); } - return fid.toString(); + else { + throw new IllegalArgumentException("Column " + col.getName() + " is not generated." ); + } + + return last; } // diff -r 3e78c87bb673 modules/unsupported/jdbc-ng/jdbc-core/src/main/java/org/geotools/jdbc/SQLDialect.java --- a/modules/unsupported/jdbc-ng/jdbc-core/src/main/java/org/geotools/jdbc/SQLDialect.java Sun Jun 28 18:35:44 2009 +0800 +++ b/modules/unsupported/jdbc-ng/jdbc-core/src/main/java/org/geotools/jdbc/SQLDialect.java Tue Jun 30 01:13:13 2009 +1000 @@ -721,7 +721,34 @@ } /** - * Obtains the next value of the primary key of a column. + * Controls whether keys are looked up post or pre insert. + *

+ * 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 false will cause the lookup to occur before the insert + * via {@link #getNextAutoGeneratedValue(String, String, String, Connection)}. + * Returning true will cause the lookup to occur after the insert via + * {@link #getLastAutoGeneratedValue(String, String, String, Connection)}. + *

+ *

+ * Subclasses returning false should implement: + *

    + *
  • {@link #getNextAutoGeneratedValue(String, String, String, Connection)} + *
+ *

+ *

+ * Subclasses returning true should implement: + *

    + *
  • {@link #getLastAutoGeneratedValue(String, String, String, Connection)} + *
+ *

+ */ + public boolean lookupGeneratedValuesPostInsert() { + return false; + } + + /** + * Obtains the next value of an auto generated column. *

* Implementations should determine the next value of a column for which * values are automatically generated by the database. @@ -747,6 +774,32 @@ } /** + * Obtains the last value of an auto generated column. + *

+ * Implementations should determine the previous value of a column for which + * was automatically generated by the database. + *

+ *

+ * 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. + *

+ *

+ * Implementations should handle the case where schemaName is null. + *

+ * @param schemaName The schema name, this might be null. + * @param tableName The name of the table. + * @param columnName The column. + * @param cx The database connection. + * + * @return The previous value of the column, or null. + */ + 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. *

diff -r 3e78c87bb673 modules/unsupported/jdbc-ng/jdbc-core/src/test/java/org/geotools/jdbc/JDBCPrimaryKeyTest.java --- a/modules/unsupported/jdbc-ng/jdbc-core/src/test/java/org/geotools/jdbc/JDBCPrimaryKeyTest.java Sun Jun 28 18:35:44 2009 +0800 +++ b/modules/unsupported/jdbc-ng/jdbc-core/src/test/java/org/geotools/jdbc/JDBCPrimaryKeyTest.java Tue Jun 30 01:13:13 2009 +1000 @@ -51,12 +51,7 @@ FeatureCollection features = fs.getFeatures(); assertPrimaryKeyValues(features, 3); - - SimpleFeatureBuilder b = new SimpleFeatureBuilder( (SimpleFeatureType) fs.getSchema() ); - b.add("four"); - b.add(new GeometryFactory().createPoint( new Coordinate(4,4) )); - features.add( b.buildFeature(null) ); - + addFeature(fs.getSchema(),features); assertPrimaryKeyValues(features,4); } @@ -68,12 +63,7 @@ FeatureCollection features = fs.getFeatures(); assertPrimaryKeyValues(features, 3); - - SimpleFeatureBuilder b = new SimpleFeatureBuilder( (SimpleFeatureType) fs.getSchema() ); - b.add("four"); - b.add(new GeometryFactory().createPoint( new Coordinate(4,4) )); - features.add( b.buildFeature(null) ); - + addFeature(fs.getSchema(),features); assertPrimaryKeyValues(features,4); } @@ -85,13 +75,20 @@ FeatureCollection features = fs.getFeatures(); assertPrimaryKeyValues(features, 3); - - SimpleFeatureBuilder b = new SimpleFeatureBuilder( (SimpleFeatureType) fs.getSchema() ); + addFeature(fs.getSchema(),features); + assertPrimaryKeyValues(features,4); + } + + void addFeature( SimpleFeatureType featureType, FeatureCollection features ) throws Exception { + SimpleFeatureBuilder b = new SimpleFeatureBuilder( featureType ); b.add("four"); b.add( new GeometryFactory().createPoint( new Coordinate(4,4) ) ); - features.add( b.buildFeature(null) ); - assertPrimaryKeyValues(features,4); + SimpleFeature f = b.buildFeature(null); + features.add( f ); + + //pattern match to handle the multi primary key case + assertTrue(((String)f.getUserData().get( "fid" )).matches( tname(featureType.getTypeName()) + ".4(\\..*)?")); } void assertPrimaryKeyValues( FeatureCollection features, int count ) throws Exception { @@ -125,10 +122,7 @@ } features.close( i ); - SimpleFeatureBuilder b = new SimpleFeatureBuilder( (SimpleFeatureType) fs.getSchema() ); - b.add("four"); - b.add(new GeometryFactory().createPoint(new Coordinate(4,4))); - features.add( b.buildFeature(null) ); + addFeature(fs.getSchema(),features); i = features.features(); for ( int j = 0; j < 3; j++ ) { @@ -163,12 +157,7 @@ FeatureCollection features = fs.getFeatures(); assertPrimaryKeyValues(features, 3); - - SimpleFeatureBuilder b = new SimpleFeatureBuilder( (SimpleFeatureType) fs.getSchema() ); - b.add("four"); - b.add(new GeometryFactory().createPoint( new Coordinate(4,4) )); - features.add( b.buildFeature(null) ); - + addFeature(fs.getSchema(),features); assertPrimaryKeyValues(features,4); } } diff -r 3e78c87bb673 modules/unsupported/jdbc-ng/jdbc-db2/src/main/java/org/geotools/data/db2/DB2SQLDialectBasic.java --- a/modules/unsupported/jdbc-ng/jdbc-db2/src/main/java/org/geotools/data/db2/DB2SQLDialectBasic.java Sun Jun 28 18:35:44 2009 +0800 +++ b/modules/unsupported/jdbc-ng/jdbc-db2/src/main/java/org/geotools/data/db2/DB2SQLDialectBasic.java Tue Jun 30 01:13:13 2009 +1000 @@ -155,12 +155,23 @@ } @Override + public boolean lookupGeneratedValuesPostInsert() { + return delegate.lookupGeneratedValuesPostInsert(); + } + + @Override public Object getNextAutoGeneratedValue(String schemaName, String tableName, String columnName, Connection cx) throws SQLException { return delegate.getNextAutoGeneratedValue(schemaName, tableName, columnName, cx); } + @Override + public Object getLastAutoGeneratedValue(String schemaName, String tableName, String columnName, + Connection cx) throws SQLException { + return delegate.getLastAutoGeneratedValue(schemaName, tableName, columnName, cx); + } + public boolean isLimitOffsetSupported() { return delegate.isLimitOffsetSupported(); } diff -r 3e78c87bb673 modules/unsupported/jdbc-ng/jdbc-db2/src/main/java/org/geotools/data/db2/DB2SQLDialectPrepared.java --- a/modules/unsupported/jdbc-ng/jdbc-db2/src/main/java/org/geotools/data/db2/DB2SQLDialectPrepared.java Sun Jun 28 18:35:44 2009 +0800 +++ b/modules/unsupported/jdbc-ng/jdbc-db2/src/main/java/org/geotools/data/db2/DB2SQLDialectPrepared.java Tue Jun 30 01:13:13 2009 +1000 @@ -174,13 +174,23 @@ } @Override + public boolean lookupGeneratedValuesPostInsert() { + return delegate.lookupGeneratedValuesPostInsert(); + } + + @Override public Object getNextAutoGeneratedValue(String schemaName, String tableName, String columnName, Connection cx) throws SQLException { return delegate.getNextAutoGeneratedValue(schemaName, tableName, columnName, cx); } - + @Override + public Object getLastAutoGeneratedValue(String schemaName, String tableName, String columnName, + Connection cx) throws SQLException { + return delegate.getLastAutoGeneratedValue(schemaName, tableName, columnName, cx); + } + @Override public void prepareGeometryValue(Geometry geom, int srid, Class binding, StringBuffer sql) { DB2Util.prepareGeometryValue(geom, srid, binding, sql); diff -r 3e78c87bb673 modules/unsupported/jdbc-ng/jdbc-h2/src/main/java/org/geotools/data/h2/H2DialectBasic.java --- a/modules/unsupported/jdbc-ng/jdbc-h2/src/main/java/org/geotools/data/h2/H2DialectBasic.java Sun Jun 28 18:35:44 2009 +0800 +++ b/modules/unsupported/jdbc-ng/jdbc-h2/src/main/java/org/geotools/data/h2/H2DialectBasic.java Tue Jun 30 01:13:13 2009 +1000 @@ -118,13 +118,23 @@ } @Override + public boolean lookupGeneratedValuesPostInsert() { + return delegate.lookupGeneratedValuesPostInsert(); + } + + @Override public Object getNextAutoGeneratedValue(String schemaName, String tableName, String columnName, Connection cx) throws SQLException { return delegate.getNextAutoGeneratedValue(schemaName, tableName, columnName, cx); } - + @Override + public Object getLastAutoGeneratedValue(String schemaName, String tableName, String columnName, + Connection cx) throws SQLException { + return delegate.getLastAutoGeneratedValue(schemaName, tableName, columnName, cx); + } + @Override public void encodeGeometryEnvelope(String tableName, String geometryColumn, StringBuffer sql) { diff -r 3e78c87bb673 modules/unsupported/jdbc-ng/jdbc-h2/src/main/java/org/geotools/data/h2/H2DialectPrepared.java --- a/modules/unsupported/jdbc-ng/jdbc-h2/src/main/java/org/geotools/data/h2/H2DialectPrepared.java Sun Jun 28 18:35:44 2009 +0800 +++ b/modules/unsupported/jdbc-ng/jdbc-h2/src/main/java/org/geotools/data/h2/H2DialectPrepared.java Tue Jun 30 01:13:13 2009 +1000 @@ -120,6 +120,11 @@ } @Override + public boolean lookupGeneratedValuesPostInsert() { + return delegate.lookupGeneratedValuesPostInsert(); + } + + @Override public Object getNextAutoGeneratedValue(String schemaName, String tableName, String columnName, Connection cx) throws SQLException { @@ -127,6 +132,12 @@ } @Override + public Object getLastAutoGeneratedValue(String schemaName, String tableName, String columnName, + Connection cx) throws SQLException { + return delegate.getLastAutoGeneratedValue(schemaName, tableName, columnName, cx); + } + + @Override public void encodeGeometryEnvelope(String tableName, String geometryColumn, StringBuffer sql) { delegate.encodeGeometryEnvelope(tableName, geometryColumn, sql); diff -r 3e78c87bb673 modules/unsupported/jdbc-ng/jdbc-mysql/src/main/java/org/geotools/data/mysql/MySQLDialect.java --- a/modules/unsupported/jdbc-ng/jdbc-mysql/src/main/java/org/geotools/data/mysql/MySQLDialect.java Sun Jun 28 18:35:44 2009 +0800 +++ b/modules/unsupported/jdbc-ng/jdbc-mysql/src/main/java/org/geotools/data/mysql/MySQLDialect.java Tue Jun 30 01:13:13 2009 +1000 @@ -239,31 +239,61 @@ } @Override - public Object getNextAutoGeneratedValue(String schemaName, - String tableName, String columnName, Connection cx) - throws SQLException { - //TODO: we need to find some way to make use of mysql's last_insert_id() function - // but that only works if we do it post-insert...and this method is supposed to get - // the next value pre-insert + public boolean lookupGeneratedValuesPostInsert() { + return true; + } + + @Override + public Object getLastAutoGeneratedValue(String schemaName, String tableName, String columnName, + Connection cx) throws SQLException { Statement st = cx.createStatement(); try { - String sql = "SELECT max( " + columnName + ")+1" + " FROM " + tableName; - dataStore.getLogger().fine(sql); - - ResultSet rs = st.executeQuery(sql); - + String sql = "SELECT last_insert_id()"; + dataStore.getLogger().fine( sql); + + ResultSet rs = st.executeQuery( sql); try { - rs.next(); - - return new Integer(rs.getInt(1)); - } finally { + if ( rs.next() ) { + return rs.getLong(1); + } + } + finally { dataStore.closeSafe(rs); } - } finally { + } + finally { dataStore.closeSafe(st); } + + return null; } - +// +// @Override +// public Object getNextAutoGeneratedValue(String schemaName, +// String tableName, String columnName, Connection cx) +// throws SQLException { +// //TODO: we need to find some way to make use of mysql's last_insert_id() function +// // but that only works if we do it post-insert...and this method is supposed to get +// // the next value pre-insert +// Statement st = cx.createStatement(); +// try { +// String sql = "SELECT max( " + columnName + ")+1" + " FROM " + tableName; +// dataStore.getLogger().fine(sql); +// +// ResultSet rs = st.executeQuery(sql); +// +// try { +// rs.next(); +// +// return new Integer(rs.getInt(1)); +// } finally { +// dataStore.closeSafe(rs); +// } +// } finally { +// dataStore.closeSafe(st); +// } +// } +// @Override public boolean isLimitOffsetSupported() { return true; diff -r 3e78c87bb673 modules/unsupported/jdbc-ng/jdbc-mysql/src/main/java/org/geotools/data/mysql/MySQLDialectBasic.java --- a/modules/unsupported/jdbc-ng/jdbc-mysql/src/main/java/org/geotools/data/mysql/MySQLDialectBasic.java Sun Jun 28 18:35:44 2009 +0800 +++ b/modules/unsupported/jdbc-ng/jdbc-mysql/src/main/java/org/geotools/data/mysql/MySQLDialectBasic.java Tue Jun 30 01:13:13 2009 +1000 @@ -107,6 +107,11 @@ } @Override + public boolean lookupGeneratedValuesPostInsert() { + return delegate.lookupGeneratedValuesPostInsert(); + } + + @Override public Object getNextAutoGeneratedValue(String schemaName, String tableName, String columnName, Connection cx) throws SQLException { @@ -114,6 +119,12 @@ } @Override + public Object getLastAutoGeneratedValue(String schemaName, String tableName, String columnName, + Connection cx) throws SQLException { + return delegate.getLastAutoGeneratedValue(schemaName, tableName, columnName, cx); + } + + @Override public void encodeGeometryValue(Geometry value, int srid, StringBuffer sql) throws IOException { sql.append("GeomFromText('"); diff -r 3e78c87bb673 modules/unsupported/jdbc-ng/jdbc-mysql/src/main/java/org/geotools/data/mysql/MySQLDialectPrepared.java --- a/modules/unsupported/jdbc-ng/jdbc-mysql/src/main/java/org/geotools/data/mysql/MySQLDialectPrepared.java Sun Jun 28 18:35:44 2009 +0800 +++ b/modules/unsupported/jdbc-ng/jdbc-mysql/src/main/java/org/geotools/data/mysql/MySQLDialectPrepared.java Tue Jun 30 01:13:13 2009 +1000 @@ -112,6 +112,11 @@ } @Override + public boolean lookupGeneratedValuesPostInsert() { + return delegate.lookupGeneratedValuesPostInsert(); + } + + @Override public Object getNextAutoGeneratedValue(String schemaName, String tableName, String columnName, Connection cx) throws SQLException { @@ -119,6 +124,12 @@ } @Override + public Object getLastAutoGeneratedValue(String schemaName, String tableName, String columnName, + Connection cx) throws SQLException { + return delegate.getLastAutoGeneratedValue(schemaName, tableName, columnName, cx); + } + + @Override public void encodeGeometryEnvelope(String tableName, String geometryColumn, StringBuffer sql) { delegate.encodeGeometryEnvelope(tableName, geometryColumn, sql); diff -r 3e78c87bb673 modules/unsupported/jdbc-ng/jdbc-postgis/src/main/java/org/geotools/data/postgis/PostGISDialect.java --- a/modules/unsupported/jdbc-ng/jdbc-postgis/src/main/java/org/geotools/data/postgis/PostGISDialect.java Sun Jun 28 18:35:44 2009 +0800 +++ b/modules/unsupported/jdbc-ng/jdbc-postgis/src/main/java/org/geotools/data/postgis/PostGISDialect.java Tue Jun 30 01:13:13 2009 +1000 @@ -367,34 +367,34 @@ } @Override - public Object getNextAutoGeneratedValue(String schemaName, - String tableName, String columnName, Connection cx) - throws SQLException { + public boolean lookupGeneratedValuesPostInsert() { + return true; + } + + @Override + public Object getLastAutoGeneratedValue(String schemaName, String tableName, String columnName, + Connection cx) throws SQLException { + + Statement st = cx.createStatement(); + try { + String sql = "SELECT lastval()"; + dataStore.getLogger().fine( sql); + + ResultSet rs = st.executeQuery( sql); + try { + if ( rs.next() ) { + return rs.getLong(1); + } + } + finally { + dataStore.closeSafe(rs); + } + } + finally { + dataStore.closeSafe(st); + } + return null; - - // the code to grab the current sequence value is here, - // but it will work only _after_ the insert occurred - - // Statement st = cx.createStatement(); - // try { - // String sql = "SELECT currval(pg_get_serial_sequence('" + tableName + - // "', '" + columnName + "'))"; - // - // dataStore.getLogger().fine( sql); - // ResultSet rs = st.executeQuery( sql); - // try { - // if ( rs.next() ) { - // return rs.getLong(1); - // } - // } finally { - // dataStore.closeSafe(rs); - // } - // } - // finally { - // dataStore.closeSafe(st); - // } - // - // return null; } @Override diff -r 3e78c87bb673 modules/unsupported/jdbc-ng/jdbc-postgis/src/main/java/org/geotools/data/postgis/PostGISPSDialect.java --- a/modules/unsupported/jdbc-ng/jdbc-postgis/src/main/java/org/geotools/data/postgis/PostGISPSDialect.java Sun Jun 28 18:35:44 2009 +0800 +++ b/modules/unsupported/jdbc-ng/jdbc-postgis/src/main/java/org/geotools/data/postgis/PostGISPSDialect.java Tue Jun 30 01:13:13 2009 +1000 @@ -78,6 +78,10 @@ return delegate.getMapping(columnMetaData, cx); } + @Override + public boolean lookupGeneratedValuesPostInsert() { + return delegate.lookupGeneratedValuesPostInsert(); + } public Object getNextAutoGeneratedValue(String schemaName, String tableName, String columnName, Connection cx) @@ -86,7 +90,12 @@ columnName, cx); } - + @Override + public Object getLastAutoGeneratedValue(String schemaName, String tableName, String columnName, + Connection cx) throws SQLException { + return delegate.getLastAutoGeneratedValue(schemaName, tableName, columnName, cx); + } + public Object getNextSequenceValue(String schemaName, String sequenceName, Connection cx) throws SQLException { return delegate.getNextSequenceValue(schemaName, sequenceName, cx);