Bug #63959 Repeated query execution gives different column types (user variable)
Submitted: 7 Jan 2012 18:21 Modified: 13 Aug 2012 13:31
Reporter: Mike Pomraning Email Updates:
Status: Not a Bug Impact on me:
None 
Category:MySQL Server: Data Types Severity:S2 (Serious)
Version:5.1.61, 5.5.19 OS:Any (Linux, Mac OS X)
Assigned to: CPU Architecture:Any

[7 Jan 2012 18:21] Mike Pomraning
Description:
== Overview ==
In a session, the very first execution of certain queries will interpret a column derived from a numeric user variable as `mediumtext`, rather than as a numeric column.

**All subsequent executions in a session of the very same query, referencing the same user variable, will interpret the variable as a numeric type.**

Affects at least 5.5.19 and 5.5.17.

== Example ==
Run these under `mysql --column-type-info` in the same session:

    -- `uservar` is type `mediumtext`
    SELECT uservar FROM (SELECT @u AS uservar FROM (select @u := CAST(0 AS UNSIGNED) AS ignored) initializer) d;

    -- hereafter it is type `bigint`
    SELECT uservar FROM (SELECT @u AS uservar FROM (select @u := CAST(0 AS UNSIGNED) AS ignored) initializer) d;

Now disconnect and reconnect and observe the same behavior.  Change `@u` to some other variable not yet used in the session, and observe the same behavior:  first execution in the above query is BLOB-ish, subsequent executions are numeric.

== Impact ==
The exact same SELECT produces differently typed selections, and perhaps even different result sets since different types sort differently.

For example, certain complex queries emulating ROW_NUMBER()/RANK() with user variables will appear to sort incorrectly, but only the very first time they are run in a session.  (Is it sorted 1, 2, 10 or '1', '10', '2' ?)  This is how I discovered this issue.

Also, tables created from certain queries (CREATE TABLE t AS SELECT ...) will have an *unpredictable column type* if user doesn't know whether the query has been issued in this session or not.

How to repeat:
--
-- On my system:
--
--   $ mysql -N < path/to/this/sql
--   MySQL 5.5.19
--   1st run:  t.uservar is type `mediumtext`
--   2nd run:  t.uservar is type `bigint`
--

SELECT CONCAT('MySQL ', VERSION());

-- Initialize @u to an integral value in a nested derived table.
--
CREATE TABLE t AS
  SELECT uservar
   FROM (SELECT @u AS uservar
           FROM (select @u := CAST(0 AS UNSIGNED) AS ignored) initializer) d;

-- Now, what type is that `uservar` column?
--
SELECT CONCAT('1st run:  t.uservar is type `', DATA_TYPE, '`')
  FROM information_schema.columns
 WHERE TABLE_SCHEMA = SCHEMA()
       AND
       TABLE_NAME = 't';

DROP TABLE t;

-- Let's do it all over again
--
CREATE TABLE t AS
  SELECT uservar
   FROM (SELECT @u AS uservar
           FROM (select @u := CAST(0 AS UNSIGNED) AS ignored) initializer) d;

SELECT CONCAT('2nd run:  t.uservar is type `', DATA_TYPE, '`')
  FROM information_schema.columns
 WHERE TABLE_SCHEMA = SCHEMA()
       AND
       TABLE_NAME = 't';

DROP TABLE t;
[7 Jan 2012 18:24] Valeriy Kravchuk
Thank you for the bug report. Verified just as described with 5.1.61 on Mac OS X also:

macbook-pro:5.1 openxs$ bin/mysql -uroot --column-type-info test
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 7
Server version: 5.1.61-debug Source distribution

Copyright (c) 2000, 2011, 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 uservar FROM (SELECT @u AS uservar FROM (select @u := CAST(0
    -> AS UNSIGNED) AS ignored) initializer) d;
Field   1:  `uservar`
Catalog:    `def`
Database:   ``
Table:      `d`
Org_table:  ``
Type:       BLOB
Collation:  binary (63)
Length:     16777215
Max_length: 1
Decimals:   0
Flags:      BLOB BINARY 

+---------+
| uservar |
+---------+
| 0       |
+---------+
1 row in set (0.04 sec)

mysql> SELECT uservar FROM (SELECT @u AS uservar FROM (select @u := CAST(0 AS UNSIGNED) AS ignored) initializer) d;
Field   1:  `uservar`
Catalog:    `def`
Database:   ``
Table:      `d`
Org_table:  ``
Type:       LONGLONG
Collation:  binary (63)
Length:     20
Max_length: 1
Decimals:   0
Flags:      UNSIGNED NUM 

+---------+
| uservar |
+---------+
|       0 |
+---------+
1 row in set (0.00 sec)
[13 Aug 2012 13:31] Guilhem Bichot
SELECT uservar
   FROM (SELECT @u AS uservar
           FROM (select @u := CAST(0 AS UNSIGNED) AS ignored) initializer) d;

When query starts, @u does not exist. So, as
http://dev.mysql.com/doc/refman/5.6/en/user-variables.html
says, "If you refer to a variable that has not been initialized, it has a
value of NULL and a type of string. ".
Thus, when parser sees 'SELECT @u AS uservar', it checks the type of @u:
string; thus column 'uservar' will be string.
Query is executed, @u gets defined to a number (0).
When query starts for second time, @u is as the previous execution left it: a
number.
This is exactly what's documented in
http://dev.mysql.com/doc/refman/5.6/en/user-variables.html :

"Another issue with assigning a value to a variable and reading the value
within the same statement is that the default result type of a variable is
based on its type at the start of the statement.  The following example
illustrates this:

mysql> SET @a='test';
mysql> SELECT @a,(@a:=20) FROM tbl_name;

For this SELECT statement, MySQL reports to the client that column one is a
string and converts all accesses of @a to strings, even though @a is set to a
number for the second row. After the SELECT statement executes, @a is
regarded as a number for the next statement.

To avoid problems with this behavior, either do not assign a value to and
read the value of the same variable within a single statement, or else set
the variable to 0, 0.0, or '' to define its type before you use it. "

So it works as documented. Not a bug.