Author: tomdz Date: Sun Jun 18 02:06:16 2006 New Revision: 415112 URL: http://svn.apache.org/viewvc?rev=415112&view=rev Log: Added generic method for unescaping special characters in the default value Improved auto-increment handling
Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/PlatformInfo.java db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/PlatformImplBase.java db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/SqlBuilder.java Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/PlatformInfo.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/PlatformInfo.java?rev=415112&r1=415111&r2=415112&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/PlatformInfo.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/PlatformInfo.java Sun Jun 18 02:06:16 2006 @@ -74,15 +74,11 @@ /** Whether the database returns a synthetic default value for non-identity required columns. */ private boolean _syntheticDefaultValueForRequiredReturned = false; - - /** Whether the platform allows for the explicit specification of values for identity columns in INSERT - and UPDATE statements. */ - private boolean _identityOverrideAllowed = true; /** Whether the platform is able to determine auto increment status from an existing database. */ private boolean _identityStatusReadingSupported = true; - // other ddl properties + // other DDL/DML properties /** Whether comments are supported. */ private boolean _sqlCommentsSupported = true; @@ -93,6 +89,17 @@ /** Whether an ALTER TABLE is needed to drop indexes. */ private boolean _alterTableForDropUsed = false; + /** Whether the platform allows for the explicit specification of values for identity columns in INSERT + and UPDATE statements. */ + private boolean _identityOverrideAllowed = true; + + /** Whether the values of identity columns can be read back from the database after insertion. */ + private boolean _lastIdentityValueReadable = true; + + /** Whether auto-commit mode for the reading of the values of identity columns after insertion + shall be used. */ + private boolean _autoCommitModeForLastIdentityValueReading = true; + /** Specifies the maximum length that an identifier (name of a table, column, constraint etc.) can have for this database; use -1 if there is no limit. */ private int _maxIdentifierLength = -1; @@ -401,28 +408,6 @@ } /** - * Determines whether the platform is allows the explicit specification of values for - * identity columns in INSERT/UPDATE statements. - * - * @return <code>true</code> if values for identity columns can be specified - */ - public boolean isIdentityOverrideAllowed() - { - return _identityOverrideAllowed; - } - - /** - * Specifies whether the platform is allows the explicit specification of values for - * identity columns in INSERT/UPDATE statements. - * - * @param identityOverrideAllowed <code>true</code> if values for identity columns can be specified - */ - public void setIdentityOverrideAllowed(boolean identityOverrideAllowed) - { - _identityOverrideAllowed = identityOverrideAllowed; - } - - /** * Determines whether the platform is able to read the auto-increment status for columns * from an existing database. * @@ -508,6 +493,75 @@ public void setAlterTableForDropUsed(boolean useAlterTableForDrop) { _alterTableForDropUsed = useAlterTableForDrop; + } + + /** + * Determines whether the platform is allows the explicit specification of values for + * identity columns in INSERT/UPDATE statements. + * + * @return <code>true</code> if values for identity columns can be specified + */ + public boolean isIdentityOverrideAllowed() + { + return _identityOverrideAllowed; + } + + /** + * Specifies whether the platform is allows the explicit specification of values for + * identity columns in INSERT/UPDATE statements. + * + * @param identityOverrideAllowed <code>true</code> if values for identity columns can be specified + */ + public void setIdentityOverrideAllowed(boolean identityOverrideAllowed) + { + _identityOverrideAllowed = identityOverrideAllowed; + } + + /** + * Determines whether the values of identity columns can be read back from the + * database after insertion of a row. + * + * @return <code>true</code> if the identity column(s) can be read back + */ + public boolean isLastIdentityValueReadable() + { + return _lastIdentityValueReadable; + } + + /** + * Specifies whether the values of identity columns can be read back from the + * database after insertion of a row. + * + * @param lastIdentityValueReadable <code>true</code> if the identity column(s) can be read back + */ + public void setLastIdentityValueReadable(boolean lastIdentityValueReadable) + { + _lastIdentityValueReadable = lastIdentityValueReadable; + } + + /** + * Determines whether auto-commit mode for the reading of the values of identity columns + * after insertion shall be used, i.e. whether between the insertion of the row and the + * reading of the database-generated identity value a commit is issued. + * + * @return <code>true</code> if auto-commit mode is used + */ + public boolean isAutoCommitModeForLastIdentityValueReading() + { + return _autoCommitModeForLastIdentityValueReading; + } + + /** + * Determines whether auto-commit mode for the reading of the values of identity columns + * after insertion shall be used, i.e. whether between the insertion of the row and the + * reading of the database-generated identity value a commit is issued. + * + * @param autoCommitModeForLastIdentityValueReading <code>true</code> if auto-commit mode + * shall be used + */ + public void setAutoCommitModeForLastIdentityValueReading(boolean autoCommitModeForLastIdentityValueReading) + { + _autoCommitModeForLastIdentityValueReading = autoCommitModeForLastIdentityValueReading; } /** Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java?rev=415112&r1=415111&r2=415112&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java Sun Jun 18 02:06:16 2006 @@ -31,6 +31,7 @@ import java.util.Map; import org.apache.commons.collections.map.ListOrderedMap; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ddlutils.Platform; @@ -1042,5 +1043,37 @@ stmt.close(); } } + } + + /** + * Replaces a specific character sequence in the given text with the character sequence + * whose escaped version it is. + * + * @param text The text + * @param unescaped The unescaped string, e.g. "'" + * @param escaped The escaped version, e.g. "''" + * @return The resulting text + */ + protected String unescape(String text, String unescaped, String escaped) + { + String result = text; + + // we need special handling if the single quote is escaped via a double single quote + if (escaped.equals("''")) + { + if ((result.length() >= 2) && result.startsWith("'") && result.endsWith("'")) + { + result = "'" + StringUtils.replace(result.substring(1, result.length() - 1), escaped, unescaped) + "'"; + } + else + { + result = StringUtils.replace(result, escaped, unescaped); + } + } + else + { + result = StringUtils.replace(result, escaped, unescaped); + } + return result; } } Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/PlatformImplBase.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/PlatformImplBase.java?rev=415112&r1=415111&r2=415112&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/PlatformImplBase.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/PlatformImplBase.java Sun Jun 18 02:06:16 2006 @@ -1030,7 +1030,7 @@ { Table table = model.findTable(dynaClass.getTableName()); - return _builder.getSelectLastInsertId(table); + return _builder.getSelectLastIdentityValues(table); } /** @@ -1138,21 +1138,37 @@ return; } - String insertSql = createInsertSql(model, dynaClass, properties, null); - String queryIdSql = autoIncrColumns.length > 0 ? createSelectLastInsertIdSql(model, dynaClass) : null; - PreparedStatement statement = null; + String insertSql = createInsertSql(model, dynaClass, properties, null); + String queryIdentitySql = null; if (_log.isDebugEnabled()) { _log.debug("About to execute SQL: " + insertSql); } - if ((autoIncrColumns.length > 0) && (queryIdSql == null)) + + if (autoIncrColumns.length > 0) { - _log.warn("The database does not support querying for auto-generated column values"); + if (!getPlatformInfo().isLastIdentityValueReadable()) + { + _log.warn("The database does not support querying for auto-generated column values"); + } + else + { + queryIdentitySql = createSelectLastInsertIdSql(model, dynaClass); + } } + boolean autoCommitMode = false; + PreparedStatement statement = null; + try { + if (!getPlatformInfo().isAutoCommitModeForLastIdentityValueReading()) + { + autoCommitMode = connection.getAutoCommit(); + connection.setAutoCommit(false); + } + statement = connection.prepareStatement(insertSql); for (int idx = 0; idx < properties.length; idx++ ) @@ -1165,8 +1181,8 @@ if (count != 1) { _log.warn("Attempted to insert a single row " + dynaBean + - " in table " + dynaClass.getTableName() + - " but changed " + count + " row(s)"); + " in table " + dynaClass.getTableName() + + " but changed " + count + " row(s)"); } } catch (SQLException ex) @@ -1177,23 +1193,27 @@ { closeStatement(statement); } - if (queryIdSql != null) + if (queryIdentitySql != null) { Statement queryStmt = null; ResultSet lastInsertedIds = null; try { - // we'll have to commit the statement(s) because otherwise most likely - // the auto increment hasn't happened yet (the db didn't actually - // perform the insert yet so no triggering of sequences did occur) - if (!connection.getAutoCommit()) + if (getPlatformInfo().isAutoCommitModeForLastIdentityValueReading()) { - connection.commit(); + // we'll commit the statement(s) if no auto-commit is enabled because + // otherwise it is possible that the auto increment hasn't happened yet + // (the db didn't actually perform the insert yet so no triggering of + // sequences did occur) + if (!connection.getAutoCommit()) + { + connection.commit(); + } } queryStmt = connection.createStatement(); - lastInsertedIds = queryStmt.executeQuery(queryIdSql); + lastInsertedIds = queryStmt.executeQuery(queryIdentitySql); lastInsertedIds.next(); @@ -1202,7 +1222,7 @@ // we're using the index rather than the name because we cannot know how // the SQL statement looks like; rather we assume that we get the values // back in the same order as the auto increment columns - Object value = lastInsertedIds.getObject(idx + 1); + Object value = getObjectFromResultSet(lastInsertedIds, autoIncrColumns[idx], idx + 1); PropertyUtils.setProperty(dynaBean, autoIncrColumns[idx].getName(), value); } @@ -1221,7 +1241,7 @@ } catch (SQLException ex) { - throw new DynaSqlException("Error while retrieving the auto-generated primary key from the database", ex); + throw new DynaSqlException("Error while retrieving the identity column value(s) from the database", ex); } finally { @@ -1239,6 +1259,19 @@ closeStatement(statement); } } + if (!getPlatformInfo().isAutoCommitModeForLastIdentityValueReading()) + { + try + { + // we need to do a manual commit now + connection.commit(); + connection.setAutoCommit(autoCommitMode); + } + catch (SQLException ex) + { + throw new DynaSqlException(ex); + } + } } /** @@ -1882,7 +1915,7 @@ // we should not use the Clob interface if the database doesn't map to this type jdbcType = targetJdbcType; } - value = extractColumnValue(resultSet, columnName, jdbcType); + value = extractColumnValue(resultSet, columnName, 0, jdbcType); } else { @@ -1891,66 +1924,106 @@ return resultSet.wasNull() ? null : value; } + /** + * Helper method esp. for the [EMAIL PROTECTED] ModelBasedResultSetIterator} class that retrieves + * the value for a column from the given result set. If a table was specified, + * and it contains the column, then the jdbc type defined for the column is used for extracting + * the value, otherwise the object directly retrieved from the result set is returned.<br/> + * The method is defined here rather than in the [EMAIL PROTECTED] ModelBasedResultSetIterator} class + * so that concrete platforms can modify its behavior. + * + * @param resultSet The result set + * @param columnName The name of the column + * @param table The table + * @return The value + */ + protected Object getObjectFromResultSet(ResultSet resultSet, Column column, int idx) throws SQLException + { + int originalJdbcType = column.getTypeCode(); + int targetJdbcType = getPlatformInfo().getTargetJdbcType(originalJdbcType); + int jdbcType = originalJdbcType; + Object value = null; + + // in general we're trying to retrieve the value using the original type + // but sometimes we also need the target type: + if ((originalJdbcType == Types.BLOB) && (targetJdbcType != Types.BLOB)) + { + // we should not use the Blob interface if the database doesn't map to this type + jdbcType = targetJdbcType; + } + if ((originalJdbcType == Types.CLOB) && (targetJdbcType != Types.CLOB)) + { + // we should not use the Clob interface if the database doesn't map to this type + jdbcType = targetJdbcType; + } + value = extractColumnValue(resultSet, null, idx, jdbcType); + return resultSet.wasNull() ? null : value; + } + /** * This is the core method to retrieve a value for a column from a result set. Its primary * purpose is to call the appropriate method on the result set, and to provide an extension * point where database-specific implementations can change this behavior. * * @param resultSet The result set to extract the value from - * @param columnName The name of the column + * @param columnName The name of the column; can be <code>null</code> in which case the + * <code>columnIdx</code> will be used instead + * @param columnIdx The index of the column's value in the result set; is only used if + * <code>columnName</code> is <code>null</code> * @param jdbcType The jdbc type to extract * @return The value * @throws SQLException If an error occurred while accessing the result set */ - protected Object extractColumnValue(ResultSet resultSet, String columnName, int jdbcType) throws SQLException + protected Object extractColumnValue(ResultSet resultSet, String columnName, int columnIdx, int jdbcType) throws SQLException { - Object value; + boolean useIdx = (columnName == null); + Object value; switch (jdbcType) { case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: - value = resultSet.getString(columnName); + value = useIdx ? resultSet.getString(columnIdx) : resultSet.getString(columnName); break; case Types.NUMERIC: case Types.DECIMAL: - value = resultSet.getBigDecimal(columnName); + value = useIdx ? resultSet.getBigDecimal(columnIdx) : resultSet.getBigDecimal(columnName); break; case Types.BIT: - value = new Boolean(resultSet.getBoolean(columnName)); + value = new Boolean(useIdx ? resultSet.getBoolean(columnIdx) : resultSet.getBoolean(columnName)); break; case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: - value = new Integer(resultSet.getInt(columnName)); + value = new Integer(useIdx ? resultSet.getInt(columnIdx) : resultSet.getInt(columnName)); break; case Types.BIGINT: - value = new Long(resultSet.getLong(columnName)); + value = new Long(useIdx ? resultSet.getLong(columnIdx) : resultSet.getLong(columnName)); break; case Types.REAL: - value = new Float(resultSet.getFloat(columnName)); + value = new Float(useIdx ? resultSet.getFloat(columnIdx) : resultSet.getFloat(columnName)); break; case Types.FLOAT: case Types.DOUBLE: - value = new Double(resultSet.getDouble(columnName)); + value = new Double(useIdx ? resultSet.getDouble(columnIdx) : resultSet.getDouble(columnName)); break; case Types.BINARY: case Types.VARBINARY: case Types.LONGVARBINARY: - value = resultSet.getBytes(columnName); + value = useIdx ? resultSet.getBytes(columnIdx) : resultSet.getBytes(columnName); break; case Types.DATE: - value = resultSet.getDate(columnName); + value = useIdx ? resultSet.getDate(columnIdx) : resultSet.getDate(columnName); break; case Types.TIME: - value = resultSet.getTime(columnName); + value = useIdx ? resultSet.getTime(columnIdx) : resultSet.getTime(columnName); break; case Types.TIMESTAMP: - value = resultSet.getTimestamp(columnName); + value = useIdx ? resultSet.getTimestamp(columnIdx) : resultSet.getTimestamp(columnName); break; case Types.CLOB: - Clob clob = resultSet.getClob(columnName); + Clob clob = useIdx ? resultSet.getClob(columnIdx) : resultSet.getClob(columnName); if (clob == null) { @@ -1978,7 +2051,7 @@ } break; case Types.BLOB: - Blob blob = resultSet.getBlob(columnName); + Blob blob = useIdx ? resultSet.getBlob(columnIdx) : resultSet.getBlob(columnName); if (blob == null) { @@ -2006,21 +2079,21 @@ } break; case Types.ARRAY: - value = resultSet.getArray(columnName); + value = useIdx ? resultSet.getArray(columnIdx) : resultSet.getArray(columnName); break; case Types.REF: - value = resultSet.getRef(columnName); + value = useIdx ? resultSet.getRef(columnIdx) : resultSet.getRef(columnName); break; default: // special handling for Java 1.4/JDBC 3 types if (Jdbc3Utils.supportsJava14JdbcTypes() && (jdbcType == Jdbc3Utils.determineBooleanTypeCode())) { - value = new Boolean(resultSet.getBoolean(columnName)); + value = new Boolean(useIdx ? resultSet.getBoolean(columnIdx) : resultSet.getBoolean(columnName)); } else { - value = resultSet.getObject(columnName); + value = useIdx ? resultSet.getObject(columnIdx) : resultSet.getObject(columnName); } break; } Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/SqlBuilder.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/SqlBuilder.java?rev=415112&r1=415111&r2=415112&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/SqlBuilder.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/SqlBuilder.java Sun Jun 18 02:06:16 2006 @@ -1805,7 +1805,7 @@ * @param table The table * @return The sql, or <code>null</code> if the database does not support this */ - public String getSelectLastInsertId(Table table) + public String getSelectLastIdentityValues(Table table) { // No default possible as the databases are quite different in this respect return null;