Bug #43196 Statement.getGeneratedKeys() use long but unsigned bigint(20) use BigInteger
Submitted: 25 Feb 2009 16:12 Modified: 18 Dec 2009 16:31
Reporter: cole dda Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / J Severity:S2 (Serious)
Version:5.1.31 OS:Any
Assigned to: CPU Architecture:Any
Tags: getgeneratedkeys, last_insert_id()

[25 Feb 2009 16:12] cole dda
Description:
about get auto_increment key:

CREATE TABLE  test1 (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`a` bigint(20) unsigned NOT NULL,
  PRIMARY KEY (`id`)
)
 ENGINE=InnoDB  DEFAULT CHARSET=latin1;

		Statement state = conn.createStatement();
			state.executeUpdate("insert into test1(a) values(1)", Statement.RETURN_GENERATED_KEYS);
			ResultSet rs = state.getGeneratedKeys();
			if(rs.next()){
				long id = rs.getObject(1);//use long
			}
			rs.close();
			rs = state.executeQuery("select id from test1");
			if(rs.next()){
				BigInteger id = rs.getObject(1);//use BigInteger
			}
			rs.close();
			//insert a id > Long.MAX_VALUE(9223372036854775807)
			state.executeUpdate("insert into test1(id,a) values(18446744073709551615,1)", Statement.RETURN_GENERATED_KEYS);
			//insert successfully
			rs = state.getGeneratedKeys();//or rs = state.executeQuery("select last_insert_id()");

			if(rs.next()){//false
				//can't return id
			}else{
				//goto to here
			}

How to repeat:
CREATE TABLE  test1 (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`a` bigint(20) unsigned NOT NULL,
  PRIMARY KEY (`id`)
)
 ENGINE=InnoDB  DEFAULT CHARSET=latin1;

		Statement state = conn.createStatement();
			state.executeUpdate("insert into test1(a) values(1)", Statement.RETURN_GENERATED_KEYS);
			ResultSet rs = state.getGeneratedKeys();
			if(rs.next()){
				long id = rs.getObject(1);//use long
			}
			rs.close();
			rs = state.executeQuery("select id from test1");
			if(rs.next()){
				BigInteger id = rs.getObject(1);//use BigInteger
			}
			rs.close();
			//insert a id > Long.MAX_VALUE(9223372036854775807)
			state.executeUpdate("insert into test1(id,a) values(18446744073709551615,1)", Statement.RETURN_GENERATED_KEYS);
			//insert successfully
			rs = state.getGeneratedKeys();//or rs = state.executeQuery("select last_insert_id()");

			if(rs.next()){//false
				//can't return id
			}else{
				//goto to here
			}

Suggested fix:
getGeneratedKeys() and "select last_insert_id()") use BigInteger for unsigned BIGINT(20)
[25 Feb 2009 17:50] Tonci Grgin
Test case for our test framework.

Attachment: TestBug43196.java (text/x-java), 2.70 KiB.

[25 Feb 2009 17:52] Tonci Grgin
Thank you Cole for your report.

Verified as described using attached test case on bzr source repo:
.Loading JDBC driver 'com.mysql.jdbc.Driver'
Done.

Done.

Connected to 5.1.31-log
java.vm.version         : 1.5.0_17-b04
java.vm.vendor          : Sun Microsystems Inc.
java.runtime.version    : 1.5.0_17-b04
os                      : Windows Server 2008, 6.0, x86
sun.management.compiler : HotSpot Client Compiler
-------------------------------------------------
F
Time: 120,614
There was 1 failure:
1) testBug43196(testsuite.simple.TestBug43196)junit.framework.AssertionFailedError: No rows returned
	at testsuite.simple.TestBug43196.testBug43196(TestBug43196.java:55)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at testsuite.simple.TestBug43196.main(TestBug43196.java:72)

FAILURES!!!
Tests run: 1,  Failures: 1,  Errors: 0

mysql> select * from bug43196;
+----------------------+---+
| id                   | a |
+----------------------+---+
|                    1 | 1 |
| 18446744073709551200 | 1 |
+----------------------+---+
2 rows in set (0.00 sec)
[28 May 2009 19:38] Mark Matthews
This is now fixed, but it's not optimal, because the server doesn't give clients enough information to be consistent.

Unfortunately, because the server doesn't tell clients what TYPE the auto increment value is, the driver can't consistently return BigIntegers for the result set returned from getGeneratedKeys(), it will only return them if the value is > Long.MAX_VALUE. 

If your application needs this consistency, it will need to check the class of the return value from .getObject() on the ResultSet returned by Statement.getGeneratedKeys() and if it's not a BigInteger, create one based on the java.lang.Long that is returned.
[18 Dec 2009 16:31] Tony Bedford
An entry has been added to the 5.2.8 changelog:

The method Statement.getGeneratedKeys() did not return values for UNSIGNED BIGINTS with values greater than Long.MAX_VALUE.

Unfortunately, because the server does not tell clients what TYPE the auto increment value is, the driver cannot consistently return BigIntegers for the result set returned from getGeneratedKeys(), it will only return them if the value is greater than Long.MAX_VALUE. If your application needs this consistency, it will need to check the class of the return value from .getObject() on the ResultSet returned by Statement.getGeneratedKeys() and if it is not a BigInteger, create one based on the java.lang.Long that is returned.