Bug #70091 tinyint(1) column type returned by jdbc connector becomes Integer with ORDER BY

Submitted: 20 Aug 2013 12:30 Modified: 21 Aug 2013 17:16
Reporter: Stefan Thurnherr Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Data Types Severity:S2 (Serious)
Version:5.6.13 OS:Linux (Ubuntu x86_64)
Assigned to: CPU Architecture:Any
Tags: regression

[20 Aug 2013 12:30] Stefan Thurnherr
Description:
When fetching a value from a resultset through resultSet.getObject("column_name"), a Boolean object is normally returned for a tinyint(1) column.

However in the case below adding an ORDER BY clause makes the mysql-connector/J return an Integer object for a tinyint(1) column.

This bug is a regression introduced in 5.6.13. It does not occur in 5.6.12.

How to repeat:
Assume the following three tables:

delimiter $$

CREATE TABLE `test_category` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `important` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_spanish_ci$$

CREATE TABLE `test_entity` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `category_id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  CONSTRAINT `fk_category` FOREIGN KEY (`category_id`) REFERENCES `test_category` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_spanish_ci$$

CREATE TABLE `test_property` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `test_entity_id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_test_entity_idx` (`test_entity_id`),
  CONSTRAINT `fk_test_entity` FOREIGN KEY (`test_entity_id`) REFERENCES `test_entity` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_spanish_ci$$

Also assume the following populated data:

INSERT INTO `test_category` (`id`,`important`)
  VALUES (1,0), (2,1), (3,0)$$

INSERT INTO `test_entity` (`id`,`category_id`)
  VALUES (1,1), (2,2), (3,2)$$

INSERT INTO `test_entity` (`id`,`category_id`)
  VALUES (1,1), (2,1), (3,1), (4,2), (5,2)$$

When running the following Java program (mysql-connector-java v1.5.25 must be on the classpath), the variable categoryImportantClass is java.lang.Integer (not as expected). However, when commenting out the ORDER BY clause, the categoryImportantClass is java.lang.Boolean (as expected).

//------------TestQuery.java----------------
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class TestQuery {

	public static void main(String[] args) throws Exception {
		final Class<?> jdbcClass = Class.forName("com.mysql.jdbc.Driver");
		System.out.println("jdbc class: " + jdbcClass);

		String url = "JDBC-URL-TO-MYSQL-DATABASE";

		Connection connection = null;
		Statement statement = null;
		ResultSet resultSet = null;
		
		try {
			
			connection = DriverManager.getConnection(url, "user", "password");
			
	
			statement = connection.createStatement();
			
			
			StringBuilder query = new StringBuilder();
			
			query.append("SELECT * FROM test_entity");
			query.append("\n LEFT JOIN test_category ON (test_category.id = test_entity.category_id)");
			query.append("\n LEFT JOIN test_property ON (test_property.test_entity_id = test_entity.id)");
			query.append("\n WHERE test_entity.id = 1");
			query.append("\n ORDER BY test_property.id");

			
			System.out.println("the test query:\n" + query.toString());
			
			statement.execute(query.toString());
			
			resultSet = statement.getResultSet();
			
			while (resultSet.next()) {
				Object entityId = resultSet.getObject("test_entity.id");
				Object categoryId = resultSet.getObject("test_category.id");
				Object categoryBoolean = resultSet.getObject("test_category.important");
				Class<?> categoryImportantClass = categoryBoolean.getClass(); // comment out this line to get expected result
				
				System.out.println("hit: entityId:" + entityId + " categoryId:" + categoryId + " categoryImportant?" + categoryBoolean + "(class:" + categoryImportantClass + ")");
			}
			System.out.println("-----------end of resultSet---------------");
			
		} finally {
			if (resultSet != null) {
				try {
					resultSet.close();
				} catch (Throwable t) {
					// silently ignore
				} 
			}
			if (statement != null) {
				try {
					statement.close();
				} catch (Throwable t) {
					// silently ignore
				}
			}
			if (connection != null) {
				try {
					connection.close();
				} catch (Throwable t) {
					// silently ignore
				}
			}
		}
	}

}
[20 Aug 2013 13:13] Stefan Thurnherr
The INSERT statements above are wrong and should be like this to be able to reproduce:

INSERT INTO `test_category` (`id`,`important`)
  VALUES (1,0), (2,1), (3,0)$$

INSERT INTO `test_entity` (`id`,`category_id`)
  VALUES (1,1), (2,2), (3,2)$$

INSERT INTO `test_property` (`id`,`test_entity_id`)
  VALUES (1,1), (2,1), (3,1), (4,2), (5,2)$$
[20 Aug 2013 13:16] Stefan Thurnherr
And the error is not with the mysql-connector/J but with the MySQL server (using mysql-connector/J with MySQL 5.6.12 and also 5.5.29 works as expected).
[20 Aug 2013 16:02] MySQL Verification Team
Please provide the query/output using the mysql client. Thanks.
[21 Aug 2013 9:07] Stefan Thurnherr
Found some typos in the sql script above, and improved the java program to get a better console output, so here is the complete info to be able to reproduce (including resulting stdout):

----------sql script--------------

delimiter $$

CREATE TABLE `test_category` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `important` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_spanish_ci$$

CREATE TABLE `test_entity` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `category_id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  CONSTRAINT `fk_category` FOREIGN KEY (`category_id`) REFERENCES `test_category` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_spanish_ci$$

CREATE TABLE `test_property` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `test_entity_id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_test_entity_idx` (`test_entity_id`),
  CONSTRAINT `fk_test_entity` FOREIGN KEY (`test_entity_id`) REFERENCES `test_entity` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_spanish_ci$$

INSERT INTO `test_category` (`id`,`important`)
  VALUES (1,0), (2,1), (3,0)$$

INSERT INTO `test_entity` (`id`,`category_id`)
  VALUES (1,1), (2,2), (3,2)$$

INSERT INTO `test_property` (`id`,`test_entity_id`)
  VALUES (1,1), (2,1), (3,1), (4,2), (5,2)$$

----------TestQuery.java--------------

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class TestQuery {

	public static void main(String[] args) throws Exception {
		final Class<?> jdbcClass = Class.forName("com.mysql.jdbc.Driver");
		System.out.println("jdbc class: " + jdbcClass);

		String jdbcUrl = "jdbc url";
		
		String mysqlUsername = "username";
		String mysqlPassword = "password";

		Connection connection = null;
		Statement statement = null;
		ResultSet resultSet = null;

		try {
			connection = DriverManager.getConnection(jdbcUrl, mysqlUsername, mysqlPassword);

			DatabaseMetaData dbMetaData = connection.getMetaData();
			System.out.println(" MySQL server version: " + dbMetaData.getDatabaseProductVersion());
			System.out.println(" Driver Name: " + dbMetaData.getDriverName());
			System.out.println(" Driver Version: " + dbMetaData.getDriverVersion());

			System.out.println("url: " + jdbcUrl);

			statement = connection.createStatement();

			StringBuilder query = new StringBuilder();

			query.append("SELECT * FROM test_entity");
			query.append("\n LEFT JOIN test_category ON (test_category.id = test_entity.category_id)");
			query.append("\n LEFT JOIN test_property ON (test_property.test_entity_id = test_entity.id)");
			query.append("\n WHERE test_entity.id = 1");
			query.append("\n ORDER BY test_property.id"); // comment this line to get expected result

			System.out.println("the test query:\n" + query.toString());

			statement.execute(query.toString());

			resultSet = statement.getResultSet();

			while (resultSet.next()) {
				Object entityId = resultSet.getObject("test_entity.id");
				Object categoryId = resultSet.getObject("test_category.id");
				Object categoryBoolean = resultSet.getObject("test_category.important");
				Class<?> categoryImportantClass = categoryBoolean.getClass();

				System.out.println("hit: entityId:" + entityId + " categoryId:" + categoryId + " categoryImportant?" + categoryBoolean + "(class:" + categoryImportantClass
						+ ")");
			}
			System.out.println("-----------end of resultSet---------------");

		} finally {
			if (resultSet != null) {
				try {
					resultSet.close();
				} catch (Throwable t) {
					// silently ignore
				}
			}
			if (statement != null) {
				try {
					statement.close();
				} catch (Throwable t) {
					// silently ignore
				}
			}
			if (connection != null) {
				try {
					connection.close();
				} catch (Throwable t) {
					// silently ignore
				}
			}
		}
	}

}

----------Console output MySQL 5.6.12--------------

jdbc class: class com.mysql.jdbc.Driver
 MySQL server version: 5.6.12-log
 Driver Name: MySQL Connector Java
 Driver Version: mysql-connector-java-5.1.25 ( Revision: ${bzr.revision-id} )
url: <jdbc-url>
the test query:
SELECT * FROM test_entity
 LEFT JOIN test_category ON (test_category.id = test_entity.category_id)
 LEFT JOIN test_property ON (test_property.test_entity_id = test_entity.id)
 WHERE test_entity.id = 1
 ORDER BY test_property.id
hit: entityId:1 categoryId:1 categoryImportant?false(class:class java.lang.Boolean)
hit: entityId:1 categoryId:1 categoryImportant?false(class:class java.lang.Boolean)
hit: entityId:1 categoryId:1 categoryImportant?false(class:class java.lang.Boolean)
-----------end of resultSet---------------

----------Console output MySQL 5.6.13--------------

jdbc class: class com.mysql.jdbc.Driver
 MySQL server version: 5.6.13-log
 Driver Name: MySQL Connector Java
 Driver Version: mysql-connector-java-5.1.25 ( Revision: ${bzr.revision-id} )
url: <jdbc-url>
the test query:
SELECT * FROM test_entity
 LEFT JOIN test_category ON (test_category.id = test_entity.category_id)
 LEFT JOIN test_property ON (test_property.test_entity_id = test_entity.id)
 WHERE test_entity.id = 1
 ORDER BY test_property.id
hit: entityId:1 categoryId:1 categoryImportant?0(class:class java.lang.Integer)
hit: entityId:1 categoryId:1 categoryImportant?0(class:class java.lang.Integer)
hit: entityId:1 categoryId:1 categoryImportant?0(class:class java.lang.Integer)
-----------end of resultSet---------------

And for the reference, here is the link to the specification: Link to the specification: http://dev.mysql.com/doc/refman/5.6/en/connector-j-reference-type-conversions.html#idp1741...
[21 Aug 2013 17:16] MySQL Verification Team
Thank you for the feedback.

c:\Program Files\MySQL\MySQL Server 5.6\bin>mysql -uroot  --column-type-info -p d90
Enter password: ******
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 17
Server version: 5.6.13 MySQL Community Server (GPL)

Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

<CUT>

mysql> SELECT * FROM test_entity
    ->  LEFT JOIN test_category ON (test_category.id = test_entity.category_id)
    ->  LEFT JOIN test_property ON (test_property.test_entity_id = test_entity.id)
    ->  WHERE test_entity.id = 1
    ->  ORDER BY test_property.id;

<cut>

Field   4:  `important`
Catalog:    `def`
Database:   `d90`
Table:      `test_category`
Org_table:  ``
Type:       LONG
Collation:  binary (63)
Length:     4
Max_length: 1
Decimals:   0
Flags:      NUM

<cut>

+----+-------------+------+-----------+------+----------------+
| id | category_id | id   | important | id   | test_entity_id |
+----+-------------+------+-----------+------+----------------+
|  1 |           1 |    1 |         0 |    1 |              1 |
|  1 |           1 |    1 |         0 |    2 |              1 |
|  1 |           1 |    1 |         0 |    3 |              1 |
+----+-------------+------+-----------+------+----------------+
3 rows in set (0.02 sec)

mysql> SELECT * FROM test_entity
    ->  LEFT JOIN test_category ON (test_category.id = test_entity.category_id)
    ->  LEFT JOIN test_property ON (test_property.test_entity_id = test_entity.id)
    ->  WHERE test_entity.id = 1;

<CUT>

Field   4:  `important`
Catalog:    `def`
Database:   `d90`
Table:      `test_category`
Org_table:  `test_category`
Type:       TINY
Collation:  binary (63)
Length:     1
Max_length: 1
Decimals:   0
Flags:      NOT_NULL NUM

<CUT>

+----+-------------+----+-----------+------+----------------+
| id | category_id | id | important | id   | test_entity_id |
+----+-------------+----+-----------+------+----------------+
|  1 |           1 |  1 |         0 |    1 |              1 |
|  1 |           1 |  1 |         0 |    2 |              1 |
|  1 |           1 |  1 |         0 |    3 |              1 |
+----+-------------+----+-----------+------+----------------+
3 rows in set (0.00 sec)
[21 Aug 2013 17:25] MySQL Verification Team
C:\dbs\5.5>bin\mysql -uroot --port=3550 --column-type-info d90
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.5.34 Source distribution

Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> SELECT * FROM test_entity
    ->  LEFT JOIN test_category ON (test_category.id = test_entity.category_id)
    ->  LEFT JOIN test_property ON (test_property.test_entity_id = test_entity.id)
    ->  WHERE test_entity.id = 1
    ->  ORDER BY test_property.id;
Field   1:  `id`
Catalog:    `def`
Database:   `d90`
Table:      `test_entity`
Org_table:  `test_entity`
Type:       LONG
Collation:  binary (63)
Length:     10
Max_length: 1
Decimals:   0
Flags:      NOT_NULL PRI_KEY UNSIGNED AUTO_INCREMENT NUM PART_KEY

Field   2:  `category_id`
Catalog:    `def`
Database:   `d90`
Table:      `test_entity`
Org_table:  `test_entity`
Type:       LONG
Collation:  binary (63)
Length:     11
Max_length: 1
Decimals:   0
Flags:      NOT_NULL MULTIPLE_KEY UNSIGNED NO_DEFAULT_VALUE NUM PART_KEY

Field   3:  `id`
Catalog:    `def`
Database:   `d90`
Table:      `test_category`
Org_table:  `test_category`
Type:       LONG
Collation:  binary (63)
Length:     10
Max_length: 1
Decimals:   0
Flags:      NOT_NULL PRI_KEY UNSIGNED AUTO_INCREMENT NUM PART_KEY

Field   4:  `important`
Catalog:    `def`
Database:   `d90`
Table:      `test_category`
Org_table:  `test_category`
Type:       TINY
Collation:  binary (63)
Length:     1
Max_length: 1
Decimals:   0
Flags:      NOT_NULL NUM

Field   5:  `id`
Catalog:    `def`
Database:   `d90`
Table:      `test_property`
Org_table:  `test_property`
Type:       LONG
Collation:  binary (63)
Length:     10
Max_length: 1
Decimals:   0
Flags:      UNSIGNED NUM

Field   6:  `test_entity_id`
Catalog:    `def`
Database:   `d90`
Table:      `test_property`
Org_table:  `test_property`
Type:       LONG
Collation:  binary (63)
Length:     11
Max_length: 1
Decimals:   0
Flags:      UNSIGNED NO_DEFAULT_VALUE NUM

+----+-------------+----+-----------+------+----------------+
| id | category_id | id | important | id   | test_entity_id |
+----+-------------+----+-----------+------+----------------+
|  1 |           1 |  1 |         0 |    1 |              1 |
|  1 |           1 |  1 |         0 |    2 |              1 |
|  1 |           1 |  1 |         0 |    3 |              1 |
+----+-------------+----+-----------+------+----------------+
3 rows in set (0.00 sec)

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.045 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.