Bug #21438 Failed to upd. rec. using ADDDATE in server-side PS with jdbcCompliantTruncation
Submitted: 4 Aug 2006 5:03 Modified: 3 Dec 2007 7:14
Reporter: Tanya Levshina Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / J Severity:S2 (Serious)
Version:5.0.26 OS:Linux (Linux Redhat)
Assigned to: Mark Matthews CPU Architecture:Any

[4 Aug 2006 5:03] Tanya Levshina
Description:
Environment Linux Redhat
java 1.4.2_10, mysql server 4.1.11

My code stops working correctly when I've upgraded to mysql connector 3.1.8,the result is even worse with 3.1.13. The problem is described below, java code is provided in #4.

The problem occurrs only if PreparedStatment setTimestamp method is used, otherwise everything works fine (#5 contains working code for all three connectors)

How to repeat:

1. Create and populate table
Table:
mysql create table test_table( t_id int(10), test_date timestamp(30) NOT NULL, 
primary key t_pk (t_id) );

mysql> describe test_table;
+-----------+-----------+------+-----+-------------------+-------+
| Field     | Type      | Null | Key | Default           | Extra |
+-----------+-----------+------+-----+-------------------+-------+
| t_id      | int(10)   |      | PRI | 0                 |       |
| test_date | timestamp | YES  |     | CURRENT_TIMESTAMP |       |
+-----------+-----------+------+-----+-------------------+-------+
2 rows in set (0.00 sec)

insert into test_table values (1,NOW());

mysql> select * from test_table;
+------+---------------------+
| t_id | test_date           |
+------+---------------------+
|    1 | 2007-08-03 23:28:01 |
+------+---------------------+
1 row in set (0.00 sec)

2. Java MysqlTest1.java is compiled and ran with 3.0.11 connector produces the correct results:

output:
executedQuery: com.mysql.jdbc.PreparedStatement@60420f: UPDATE test_table SET te
st_date=ADDDATE('2006-08-03 23:33:51',INTERVAL 1 YEAR) WHERE t_id=1
committing 
closing connections

in database:
mysql> select * from test_table;
+------+---------------------+
| t_id | test_date           |
+------+---------------------+
|    1 | 2007-08-03 23:33:51 |
+------+---------------------+
1 row in set (0.00 sec)

3. Java MysqlTest1.java is compiled and ran with 3.1.8 connector does not generate any error but produces wrong results
output:
executedQuery: com.mysql.jdbc.ServerPreparedStatement[1] - UPDATE test_table SET
 test_date=ADDDATE('2006-08-03 23:35:40',INTERVAL 1 YEAR) WHERE t_id=1
committing 
closing connections

but in database:
mysql> select * from test_table;
+------+---------------------+
| t_id | test_date           |
+------+---------------------+
|    1 | 0000-00-00 00:00:00 |
+------+---------------------+

4.Java MysqlTest1.java is compiled and ran with 3.1.13 connector throws MysqlDataTruncation

output:
java MysqlTest1
executedQuery: com.mysql.jdbc.ServerPreparedStatement[1] - UPDATE test_table SET
 test_date=ADDDATE('2006-08-03 23:38:18',INTERVAL 1 YEAR) WHERE t_id=1
com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data truncated for column '
test_date' at row 1
        at com.mysql.jdbc.SQLError.convertShowWarningsToSQLWarnings(SQLError.jav
a:709)
        at com.mysql.jdbc.MysqlIO.scanForAndThrowDataTruncation(MysqlIO.java:346
1)
        at com.mysql.jdbc.ServerPreparedStatement.serverExecute(ServerPreparedSt
atement.java:1198)
        at com.mysql.jdbc.ServerPreparedStatement.executeInternal(ServerPrepared
Statement.java:677)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java
:1357)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java
:1274)
        at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java
:1259)
        at MysqlTest1.execute(MysqlTest1.java:29)
        at MysqlTest1.main(MysqlTest1.java:52)
Data truncation: Data truncated for column 'test_date' at row 1
rollback 

5. Java code (MysqlTest1.java):

*************************************
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.Timestamp;
import java.util.Date;

public class MysqlTest1 {
        private Connection connection=null;
        public MysqlTest1() throws Exception{

                String username="user"; //change it !!
                String password="password"; //change it !!!
                String url="jdbc:mysql://localhost.localdomain:49151/test";
                String driver="org.gjt.mm.mysql.Driver";

                Class.forName(driver);
                connection=DriverManager.getConnection(url,username,password);
                connection.setAutoCommit (false);
        }       
        public void execute(String message) throws Exception{
                PreparedStatement stmt =  connection.prepareStatement(message);
                stmt.setTimestamp(1,new Timestamp(new Date().getTime()));
                System.out.println("executedQuery: "+stmt.toString());
                stmt.executeUpdate();
                stmt.close();
                System.out.println("committing ");
                connection.commit();
        }
        public void close() throws Exception{
                connection.close();
                System.out.println("closing connections");
        }
        public void rollback() throws Exception{
                connection.rollback();
                System.out.println("rollback ");
        }
        public static void main(String[] args) throws Exception{
                MysqlTest1 test=null;
                String sql="UPDATE test_table SET test_date=ADDDATE(?,INTERVAL 1
 YEAR) WHERE t_id=1";
                try {
                        test=new MysqlTest1();
                        test.execute(sql);
                        test.close();

                } catch (Exception e) {
                        e.printStackTrace();
                        System.out.println(e.getMessage());
                        if (test!=null) {
                                test.rollback();
                                test.close();
                        }

                }
        }

}
*****************************************************************************

#5 working code for all 3 connectors:

*************************************
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.Timestamp;
import java.util.Date;

public class MysqlTest1 {
        private Connection connection=null;
        public MysqlTest1() throws Exception{

                String username="user"; //change it !!
                String password="password"; //change it !!!
                String url="jdbc:mysql://localhost.localdomain:49151/test";
                String driver="org.gjt.mm.mysql.Driver";

                Class.forName(driver);
                connection=DriverManager.getConnection(url,username,password);
                connection.setAutoCommit (false);
        }       
        public void execute(String message) throws Exception{
                PreparedStatement stmt =  connection.prepareStatement(message);
                //stmt.setTimestamp(1,new Timestamp(new Date().getTime()));
                //Commented out from #4
                System.out.println("executedQuery: "+stmt.toString());
                stmt.executeUpdate();
                stmt.close();
                System.out.println("committing ");
                connection.commit();
        }
        public void close() throws Exception{
                connection.close();
                System.out.println("closing connections");
        }
        public void rollback() throws Exception{
                connection.rollback();
                System.out.println("rollback ");
        }
        public static void main(String[] args) throws Exception{
                MysqlTest1 test=null;
                //String sql="UPDATE test_table SET test_date=ADDDATE(?,INTERVAL 1 YEAR) WHERE t_id=1";
                String sql="UPDATE test_table SET test_date=ADDDATE('"+
                new Timestamp(new Date().getTime())+"',INTERVAL 1
 YEAR) WHERE t_id=1";

                try {
                        test=new MysqlTest1();
                        test.execute(sql);
                        test.close();

                } catch (Exception e) {
                        e.printStackTrace();
                        System.out.println(e.getMessage());
                        if (test!=null) {
                                test.rollback();
                                test.close();
                        }

                }
        }

}
*****************************************************************************
[4 Aug 2006 5:39] Tanya Levshina
updated synopsis
[12 Sep 2006 8:45] Tonci Grgin
Changing synopsis to more appropriate one
[12 Sep 2006 9:42] Tonci Grgin
Hi Tanya and thanks for your problem report. After version 3.1 connector/J uses server side prepared statements when available. You should either put &useServerPrepStmts=false in your connection string or turn off jdbcCompliantTruncation (&jdbcCompliantTruncation=false) to make your code work. Otherwise, you should format TIMESTAMP value in the manner your MySQL server accepts because the timestamp function has added milliseconds to the internal date.
Also, this report shold be set to no more than "S3" since convinient and documented workaround(s) exist.
[12 Sep 2006 10:02] Tonci Grgin
Test case, details inside.

Attachment: TestBug21438.java (text/x-java), 1.65 KiB.

[9 Jan 2007 22:47] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/17816
[14 Nov 2007 4:58] Jeffrey Pugh
Changed component as per commit and comments
[3 Dec 2007 7:14] MC Brown
A note has been added to the 5.0.5 changelog: 

When using a server-side prepared statement the driver would send timestamps to the server using nanoseconds instead of milliseconds.