package liquibase.executor.jvm; import java.io.StringWriter; import java.math.BigDecimal; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Types; import java.util.Calendar; /** * Utility methods for PreparedStatementSetter/Creator and CallableStatementCreator * implementations, providing sophisticated parameter management (including support * for LOB values). * <p/> * <p>Used by PreparedStatementCreatorFactory and CallableStatementCreatorFactory, * but also available for direct use in custom setter/creator implementations. * * @author Spring Framework * @see PreparedStatementSetter * @see SqlParameter */ abstract class StatementCreatorUtils { /** * Set the value for a parameter. The method used is based on the SQL type * of the parameter and we can handle complex types like arrays and LOBs. * * @param ps the prepared statement or callable statement * @param paramIndex index of the parameter we are setting * @param param the parameter as it is declared including type * @param inValue the value to set * @throws SQLException if thrown by PreparedStatement methods */ public static void setParameterValue( PreparedStatement ps, int paramIndex, SqlParameter param, Object inValue) throws SQLException { setParameterValueInternal(ps, paramIndex, param.getSqlType(), param.getTypeName(), param.getScale(), inValue); } /** * Set the value for a parameter. The method used is based on the SQL type * of the parameter and we can handle complex types like arrays and LOBs. * * @param ps the prepared statement or callable statement * @param paramIndex index of the parameter we are setting * @param sqlType the SQL type of the parameter * @param inValue the value to set (plain value or a SqlTypeValue) * @throws SQLException if thrown by PreparedStatement methods */ public static void setParameterValue( PreparedStatement ps, int paramIndex, int sqlType, Object inValue) throws SQLException { setParameterValueInternal(ps, paramIndex, sqlType, null, null, inValue); } /** * Set the value for a parameter. The method used is based on the SQL type * of the parameter and we can handle complex types like arrays and LOBs. * * @param ps the prepared statement or callable statement * @param paramIndex index of the parameter we are setting * @param sqlType the SQL type of the parameter * @param typeName the type name of the parameter * (optional, only used for SQL NULL and SqlTypeValue) * @param scale the number of digits after the decimal point * (for DECIMAL and NUMERIC types) * @param inValue the value to set (plain value or a SqlTypeValue) * @throws SQLException if thrown by PreparedStatement methods */ private static void setParameterValueInternal( PreparedStatement ps, int paramIndex, int sqlType, String typeName, Integer scale, Object inValue) throws SQLException { if (inValue == null) { if (sqlType == SqlTypeValue.TYPE_UNKNOWN) { boolean useSetObject = false; try { useSetObject = (ps.getConnection().getMetaData().getDatabaseProductName().indexOf("Informix") != -1); } catch (Throwable ex) { // logger.debug("Could not check database product name", ex); } if (useSetObject) { ps.setObject(paramIndex, null); } else { ps.setNull(paramIndex, Types.NULL); } } else if (typeName != null) { ps.setNull(paramIndex, sqlType, typeName); } else { ps.setNull(paramIndex, sqlType); } } else { // inValue != null if (inValue instanceof SqlTypeValue) { ((SqlTypeValue) inValue).setTypeValue(ps, paramIndex, sqlType, typeName); } else if (sqlType == Types.VARCHAR || sqlType == -9 ) { //-9 is Types.NVARCHAR in java 1.6 ps.setString(paramIndex, inValue.toString()); } else if (sqlType == Types.DECIMAL || sqlType == Types.NUMERIC) { if (inValue instanceof BigDecimal) { ps.setBigDecimal(paramIndex, (BigDecimal) inValue); } else if (scale != null) { ps.setObject(paramIndex, inValue, sqlType, scale); } else { ps.setObject(paramIndex, inValue, sqlType); } } else if (sqlType == Types.DATE) { if (inValue instanceof java.util.Date) { if (inValue instanceof java.sql.Date) { ps.setDate(paramIndex, (java.sql.Date) inValue); } else { ps.setDate(paramIndex, new java.sql.Date(((java.util.Date) inValue).getTime())); } } else if (inValue instanceof Calendar) { Calendar cal = (Calendar) inValue; ps.setDate(paramIndex, new java.sql.Date(cal.getTime().getTime()), cal); } else { ps.setObject(paramIndex, inValue, Types.DATE); } } else if (sqlType == Types.TIME) { if (inValue instanceof java.util.Date) { if (inValue instanceof java.sql.Time) { ps.setTime(paramIndex, (java.sql.Time) inValue); } else { ps.setTime(paramIndex, new java.sql.Time(((java.util.Date) inValue).getTime())); } } else if (inValue instanceof Calendar) { Calendar cal = (Calendar) inValue; ps.setTime(paramIndex, new java.sql.Time(cal.getTime().getTime()), cal); } else { ps.setObject(paramIndex, inValue, Types.TIME); } } else if (sqlType == Types.TIMESTAMP) { if (inValue instanceof java.util.Date) { if (inValue instanceof java.sql.Timestamp) { ps.setTimestamp(paramIndex, (java.sql.Timestamp) inValue); } else { ps.setTimestamp(paramIndex, new java.sql.Timestamp(((java.util.Date) inValue).getTime())); } } else if (inValue instanceof Calendar) { Calendar cal = (Calendar) inValue; ps.setTimestamp(paramIndex, new java.sql.Timestamp(cal.getTime().getTime()), cal); } else { ps.setObject(paramIndex, inValue, Types.TIMESTAMP); } } else if (sqlType == SqlTypeValue.TYPE_UNKNOWN) { if (isStringValue(inValue)) { ps.setString(paramIndex, inValue.toString()); } else if (isDateValue(inValue)) { ps.setTimestamp(paramIndex, new java.sql.Timestamp(((java.util.Date) inValue).getTime())); } else if (inValue instanceof Calendar) { Calendar cal = (Calendar) inValue; ps.setTimestamp(paramIndex, new java.sql.Timestamp(cal.getTime().getTime())); } else { // Fall back to generic setObject call without SQL type specified. ps.setObject(paramIndex, inValue); } } else { // Fall back to generic setObject call with SQL type specified. ps.setObject(paramIndex, inValue, sqlType); } } } /** * Check whether the given value can be treated as a String value. */ private static boolean isStringValue(Object inValue) { return (inValue instanceof CharSequence || inValue instanceof StringWriter); } /** * Check whether the given value is a <code>java.util.Date</code> * (but not one of the JDBC-specific subclasses). */ private static boolean isDateValue(Object inValue) { return (inValue instanceof java.util.Date && !(inValue instanceof java.sql.Date || inValue instanceof java.sql.Time || inValue instanceof java.sql.Timestamp)); } }