From: Sergey Glukhov
Date: June 9 2011 10:11am
Subject: Re: bzr commit into mysql-5.5 branch (jorgen.loland:3409) Bug#12561818
List-Archive: http://lists.mysql.com/commits/138956
Message-Id: <4DF09C4B.204@oracle.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Hi,
On 05/27/2011 04:04 PM, Jorgen Loland wrote:
> #At file:///export/home/jl208045/mysql/mysql-5.5-12561818/ based on revid:dmitry.shulga@stripped
>
> 3409 Jorgen Loland 2011-05-27
> BUG#12561818 - RERUN OF STORED FUNCTION GIVES ERROR 1172:
> RESULT CONSISTED OF MORE THAN ONE ROW
>
> MySQL converts incorrect DATEs and DATETIMEs to '0000-00-00' on
> insertion by default. This means that this sequence is possible:
>
> CREATE TABLE t1(date_notnull DATE NOT NULL);
> INSERT INTO t1 values (NULL);
> SELECT * FROM t1;
> 0000-00-00
>
> At the same time, ODBC drivers do not (or at least did not in the
> 90's) understand the DATE and DATETIME value '0000-00-00'. Thus,
> to be able to query for the value 0000-00-00 it was decided in
> MySQL 4.x (or maybe even before that) that for the special case
> of DATE/DATETIME NOT NULL columns, the query "SELECT ... WHERE
> date_notnull IS NULL" should return rows with date_notnull ==
> '0000-00-00'. This is documented misbehavior that we do not want
> to change.
>
> The hack used to make MySQL return these rows is to convert
> "date_notnull IS NULL" to "date_notnull = 0". This is, however,
> only done if the table date_notnull belongs to is not an inner
> table of an outer join. The rationale for this seems to be that
> if there is no join match for the row in the outer table,
> null-complemented rows would otherwise not be returned because
> the null-complemented DATE value is actually NULL. On the other
> hand, this means that the "return rows with 0000-00-00 when the
> query asks for IS NULL"-hack is not in effect for outer joins.
>
> In this bug, we have a LEFT JOIN that does not misbehave like
> the documentation says it should. The fix is to rewrite
>
> "date_notnull IS NULL" to "date_notnull IS NULL OR
> date_notnull = 0"
> instead of
> "date_notnull IS NULL" to "date_notnull = 0"
> and also do this rewrite for outer joins.
> @ mysql-test/r/ps.result
> Added test for BUG#12561818
> @ mysql-test/r/type_datetime.result
> "
IS NULL" for DATE NOT NULL column is now rewritten to
> " IS NULL OR = 0" and also done for outer joins.
> @ mysql-test/t/ps.test
> Added test for BUG#12561818
> @ sql/sql_select.cc
> Special handling of NULL for DATE/DATETIME NOT NULL columns:
> Instead of rewriting
> "date_notnull IS NULL" => "date_notnull = 0"
> it is now rewritten to
> "date_notnull IS NULL" => "date_notnull IS NULL OR date_notnull = 0"
>
> modified:
> mysql-test/r/ps.result
> mysql-test/r/type_datetime.result
> mysql-test/t/ps.test
> sql/sql_select.cc
>
> === modified file 'mysql-test/t/ps.test'
> --- a/mysql-test/t/ps.test 2011-03-22 11:48:56 +0000
> +++ b/mysql-test/t/ps.test 2011-05-27 12:04:16 +0000
> @@ -3357,6 +3357,36 @@ disconnect con1;
> SELECT *
> FROM (SELECT 1 UNION SELECT 2) t;
>
> +--echo #
> +--echo # BUG#12561818: RERUN OF STORED FUNCTION GIVES ERROR 1172:
> +--echo # RESULT CONSISTED OF MORE THAN ONE ROW
> +--echo #
> +
> +CREATE TABLE t1 (a DATE NOT NULL, b INT);
> +INSERT INTO t1 VALUES ('0000-00-00',1), ('1999-05-10',2);
> +
> +--echo
> +SELECT * FROM t1 WHERE a IS NULL;
> +--echo
^^^
I would prefer to remove 'echo' line,
result line could be treated as empty result row
which looks a bit confusing for me, but I don't insist :)
> +SELECT * FROM t1 LEFT JOIN t1 AS t1_2 ON 1 WHERE t1_2.a IS NULL;
> +--echo
^^^
same here
> +SELECT * FROM t1 JOIN t1 AS t1_2 ON 1 WHERE t1_2.a IS NULL;
> +
> +--echo
> +DELIMITER $;
> +CREATE PROCEDURE p1()
> +BEGIN
> + SELECT *
> + FROM t1 LEFT JOIN t1 AS t1_2 ON 1
> + WHERE t1_2.a IS NULL AND t1_2.b< 2;
> +END $
> +DELIMITER ;$
> +--echo
> +CALL p1();
> +CALL p1();
> +
> +DROP PROCEDURE p1;
> +DROP TABLE t1;
>
> --echo #
> --echo # End of 5.5 tests.
>
> === modified file 'sql/sql_select.cc'
> --- a/sql/sql_select.cc 2011-05-06 08:27:04 +0000
> +++ b/sql/sql_select.cc 2011-05-27 12:04:16 +0000
> @@ -9519,24 +9519,29 @@ internal_remove_eq_conds(THD *thd, COND
> Field *field=((Item_field*) args[0])->field;
> /* fix to replace 'NULL' dates with '0' (shreeve@stripped) */
> /*
> - datetime_field IS NULL has to be modified to
> - datetime_field == 0
> + See BUG#12594011
> + Documentation says that
> + SELECT datetime_notnull d FROM t1 WHERE d IS NULL
> + shall return rows where d=='0000-00-00'
> +
> + Thus, for DATE and DATETIME columns defined as NOT NULL,
> + "date_notnull IS NULL" has to be modified to
> + "date_notnull IS NULL OR date_notnull == 0"
> */
> if (((field->type() == MYSQL_TYPE_DATE) ||
> (field->type() == MYSQL_TYPE_DATETIME))&&
> - (field->flags& NOT_NULL_FLAG)&& !field->table->maybe_null)
> + (field->flags& NOT_NULL_FLAG))
> {
> - COND *new_cond;
> - if ((new_cond= new Item_func_eq(args[0],new Item_int("0", 0, 2))))
> - {
> - cond=new_cond;
> - /*
> - Item_func_eq can't be fixed after creation so we do not check
> - cond->fixed, also it do not need tables so we use 0 as second
> - argument.
> - */
> - cond->fix_fields(thd,&cond);
> - }
> + COND *eq_cond= new Item_func_eq(args[0],new Item_int("0", 0, 2));
> + if (!eq_cond)
> + return cond;
> +
^^^
Please put the code below under
if (field->table->pos_in_table_list->outer_join) condition.
We don't need additional 'IS NULL' condition if table does not
belong to outer join.
> + COND *or_cond= new Item_cond_or(eq_cond, cond);
> + if (!or_cond)
> + return cond;
> + cond= or_cond;
> +
> + cond->fix_fields(thd,&cond);
> }
> }
> if (cond->const_item())
>
Regards,
Gluh