ojdbc can send invalid timestamp values to Oracle via prepared statement parameters.
Oracle can't convert such values, raising ORA-01877. But Oracle can insert them into a table and return without conversion.
This happens when I create a java.sql.Timestamp value 23:00 <= x < 0:00 and < '2010-03-28'.
The value can be created on client or returned from Oracle, it doesn't matter.
Client:
OS: Windows 7 64
Timezone: "Europe/Moscow"
Java:
reproduced on: 1.6.0_31, 1.7.0_07
not reproduced on: 1.5.0_22, 1.4.2_16
driver:
reproduced on: ojdbc6-11.2.0.jar, ojdbc14-10.2.0.3.0.jar
not reproduced on: ojdbc5-11.2.0.1.0.jar
import java.sql.*;
public class Main
{
private static final String driver = "oracle.jdbc.driver.OracleDriver";
private static final String url = "jdbc:oracle:thin:@adcserv2:1521:PFMS";
public static final String username = "scott";
public static final String password = "tiger";
public static void main(String[] args)
throws Exception
{
Class.forName(driver);
Connection cnn = DriverManager.getConnection(url, username, password);
PreparedStatement st;
ResultSet rs;
Timestamp ts;
st = cnn.prepareStatement("select to_timestamp('2010-03-26 23:00:00', 'yyyy-mm-dd HH24:MI:SS') from dual");
rs = st.executeQuery();
rs.next();
ts = rs.getTimestamp(1);
st = cnn.prepareStatement("select to_char(?, 'yyyy-mm-dd HH24:MI:SS.FF9') from dual");
st.setTimestamp(1, ts);
rs = st.executeQuery(); // <- ORA-01877: string is too long for internal buffer
rs.next();
System.out.println(rs.getString(1));
}
}
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production
PL/SQL Release 11.2.0.2.0 - Production
CORE 11.2.0.2.0 Production
TNS for 64-bit Windows: Version 11.2.0.2.0 - Production
NLSRTL Version 11.2.0.2.0 - Production
same with
Oracle Database 10g Enterprise Edition Release 10.2.0.2.0 - Prod
The valid timestamp CAN be sent to Oracle using the overloaded method:
setTimestamp(1, ts, null);
but it's not an option, because there's existing software that can't be changed. The only solution I can think of is patching the ojdbc.jar:
class OraclePreparedStatement {
public void setTimestamp(int paramInt, Timestamp paramTimestamp)
throws SQLException
{
setTimestampInternal(paramInt, paramTimestamp, null);
}
to call the overloaded method with null calendar.
Here's the dump of valid and invalid values. As you can see, the date part of invalid value is next day.
ALTER session SET NLS_TIMESTAMP_FORMAT="yyyy-mm-dd HH24:MI:SS.FF9";
select a, dump(a), s from testtstamp order by 1;
A DUMP(A) S
------------------------------ --------------------------------------- -------
2001-02-28 23:00:00.000000000 Typ=180 Len=7: 120,101,2,28,24,1,1 valid
2001-02-28 23:00:00.000000000 Typ=180 Len=7: 120,101,3,1,0,1,1 invalid
2001-03-01 00:00:00.000000000 Typ=180 Len=7: 120,101,3,1,1,1,1 valid