Bug #105857 ResultSetMetaData return different column type in two calls
Submitted: 10 Dec 2021 7:39 Modified: 11 Jan 2022 7:56
Reporter: Yu Ma Ma Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S3 (Non-critical)
Version:8.0.23, 8.0.27 OS:Linux
Assigned to: CPU Architecture:Any
Tags: Java connector

[10 Dec 2021 7:39] Yu Ma Ma
Description:

Try to get column type through ResultSetMetaData.getColumnTypeName(colIndex),
1st,create a connection and call stmt.executeQuery, getColumnTypeName,it will return DOUBLE.
2nd,close the stmt and create,call stmt,executeQuery again , it will return BIGINT.
But if you close the connection and create the connection ,run stmt.executeQuery,then, it will return DOUBLE again.
It seems like the 1st call to ResultSetMetaData.getColumnTypeName(colIndex) in the newly created connection ,will return DOUBLE. But if you close the stmt and create it again in the same connection (do not close connection itselt),it will return BIGINT.

How to repeat:
import java.sql.*;

public class MySQLJDBCTestSingle {
    public static String sqlStatement="select * from (SELECT @rownum := @rownum+1 AS rownum,1_test.* FROM (SELECT @rownum:=0) r,1_test) T1639101751247 where 1=0" ;

    public static void main(String args[]) {
        Connection con ;
        ResultSetMetaData resultSetMetaData;
        ResultSet rs ;
        Statement stmt;

        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            con = DriverManager
                    .getConnection("jdbc:mysql://172.16.15.128:3306/avatar?useUnicode=true&autoReconnect=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8",
                            "avatar4", "avatar4");

            stmt=con.createStatement();;
            rs = stmt.executeQuery(sqlStatement);
            System.out.println("_____________________");
            resultSetMetaData = rs.getMetaData();
            System.out.println(resultSetMetaData.getColumnTypeName(1));
            System.out.println(resultSetMetaData.getColumnName(1));
            System.out.println(resultSetMetaData.getColumnClassName(1));
            System.out.println(resultSetMetaData.getPrecision(1));
            System.out.println(resultSetMetaData.getScale(1));
            System.out.println("_____________________");
            rs.close();
            stmt.close();

            stmt=con.createStatement();;
            rs = stmt.executeQuery(sqlStatement);

            resultSetMetaData = rs.getMetaData();
            System.out.println(resultSetMetaData.getColumnTypeName(1));
            System.out.println(resultSetMetaData.getColumnName(1));
            System.out.println(resultSetMetaData.getColumnClassName(1));
            System.out.println(resultSetMetaData.getPrecision(1));
            System.out.println(resultSetMetaData.getScale(1));
            System.out.println("_____________________");
            rs.close();
            stmt.close();

            stmt=con.createStatement();;
            rs = stmt.executeQuery(sqlStatement);

            resultSetMetaData = rs.getMetaData();
            System.out.println(resultSetMetaData.getColumnTypeName(1));
            System.out.println(resultSetMetaData.getColumnName(1));
            System.out.println(resultSetMetaData.getColumnClassName(1));
            System.out.println(resultSetMetaData.getPrecision(1));
            System.out.println(resultSetMetaData.getScale(1));
            System.out.println("_____________________");
            rs.close();
            stmt.close();

            System.out.println("Close connection and create it again:");
            con.close();
            con = DriverManager
                    .getConnection("jdbc:mysql://172.16.15.128:3306/avatar?useUnicode=true&autoReconnect=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8",
                            "avatar4", "avatar4");

            stmt=con.createStatement();;
            rs = stmt.executeQuery(sqlStatement);
            System.out.println("_____________________");
            resultSetMetaData = rs.getMetaData();
            System.out.println(resultSetMetaData.getColumnTypeName(1));
            System.out.println(resultSetMetaData.getColumnName(1));
            System.out.println(resultSetMetaData.getColumnClassName(1));
            System.out.println(resultSetMetaData.getPrecision(1));
            System.out.println(resultSetMetaData.getScale(1));
            System.out.println("_____________________");
            rs.close();
            stmt.close();
            con.close();

        } catch (Exception e) {
            e.printStackTrace();
            System.err.println(e.getClass().getName() + ": " + e.getMessage());
        }
    }
}

These codes will produce the result which is hard to understand:

_____________________
DOUBLE
rownum
java.lang.Double
92
0
_____________________
BIGINT
rownum
java.lang.Long
19
0
_____________________
BIGINT
rownum
java.lang.Long
19
0
_____________________
Close connection and create it again:
_____________________
DOUBLE
rownum
java.lang.Double
92
0
_____________________

Process finished with exit code 0

Thanks in advance.
[10 Dec 2021 7:41] Yu Ma Ma
The driver I tried with 8.0.23 and 8.0.27:
  <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.27</version>
      </dependency>
[10 Dec 2021 8:04] MySQL Verification Team
Hello Yu Ma Ma,

Thank you for the report and test case.

regards,
Umesh
[11 Jan 2022 7:56] Yu Ma Ma
No one follow this problem?
[15 Nov 2022 19:34] Filipe Silva
MySQL server returns the metadata as it is shown by Connector/J, so this is actually a server bug.

The same behavior can be observed using mysql client.

Changing bug category.
[16 Nov 2022 12:55] Roy Lyseng
A reasonable workaround for this problem is to assign an integer value to the variable just before executing the actual SELECT statement.

Notice also that this statement is quite vulnerable due to the dynamic typing
rules for user variables:
If some other statement has assigned a decimal value or a float value to that specific variable, the SELECT statement will actually pick that type as the type for the user variable. And since the statement contains both assignments and references to the same variable, it is not possible to determine the sequence of these statements, and thus the reference must use the current type of the variable.

That said, it should be possible to determine the correct type (i.e. INTEGER) based on the expression type when the variable does not exist.