Description:
This issue can be described using a single table and a logically impossible
predicate.
After creating the table and inserting several rows via `INSERT IGNORE`, the
stored `DATE NOT NULL` column `c5` is displayed as `NULL` in the result set:
------------------------------------
mysql> SELECT c1, c4, c5,
-> c5 IS NULL AS is_null,
-> c5 IS NOT NULL AS is_not_null
-> FROM t
-> ORDER BY c1;
+----+------+------+---------+-------------+
| c1 | c4 | c5 | is_null | is_not_null |
+----+------+------+---------+-------------+
| 1 | 573 | NULL | 0 | 1 |
| 2 | -2 | NULL | 0 | 1 |
| 3 | -6 | NULL | 0 | 1 |
| 4 | -552 | NULL | 0 | 1 |
+----+------+------+---------+-------------+
------------------------------------
So the displayed value is `NULL`, but MySQL still evaluates `c5 IS NULL` as
false and `c5 IS NOT NULL` as true.
Then the following query is executed:
------------------------------------
mysql> SELECT c1, c4, c5
-> FROM t
-> WHERE c6 >= 'not-a-date'
-> AND c5 IS NOT NULL
-> AND c5 IS NULL
-> ORDER BY c4 DESC;
+----+------+------+
| c1 | c4 | c5 |
+----+------+------+
| 1 | 573 | NULL |
| 2 | -2 | NULL |
| 3 | -6 | NULL |
| 4 | -552 | NULL |
+----+------+------+
------------------------------------
This predicate is a contradiction and should be equivalent to `FALSE`, so the
query result must be empty. Instead, MySQL returns all four rows.
The problem is also visible in the execution plan. Rather than recognizing the
predicate as impossible, MySQL executes a full table scan:
------------------------------------
mysql> EXPLAIN FORMAT=TRADITIONAL
-> SELECT c1, c4, c5
-> FROM t
-> WHERE c6 >= 'not-a-date'
-> AND c5 IS NOT NULL
-> AND c5 IS NULL
-> ORDER BY c4 DESC;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
| 1 | SIMPLE | t | NULL | ALL | NULL | NULL | NULL | NULL | 4 | 25.0 | Using where; Using filesort |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
------------------------------------
This is a wrong-result bug. A contradiction of the form
------------------------------------
c5 IS NOT NULL AND c5 IS NULL
------------------------------------
must never return rows.
How to repeat:
SET SESSION sql_mode =
'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
DROP DATABASE IF EXISTS repro_null_contradiction;
CREATE DATABASE repro_null_contradiction;
USE repro_null_contradiction;
CREATE TABLE t (
c1 INT NOT NULL PRIMARY KEY,
c4 INT NULL,
c5 DATE NOT NULL,
c6 VARCHAR(10) NOT NULL
);
-- INSERT IGNORE is important here.
INSERT IGNORE INTO t (c1, c4, c5, c6) VALUES
(1, 573, '0000-00-00', 'sd d7j5nr6'),
(2, -2, 'not-a-date', 'zzzzzzzzzz'),
(3, -6, '0000-00-00', 'zzzzzzzzzz'),
(4, -552, 'not-a-date', 'zzzzzzzzzz');
ANALYZE TABLE t;
-- Control: inspect how MySQL treats c5 on these rows.
SELECT c1, c4, c5,
c5 IS NULL AS is_null,
c5 IS NOT NULL AS is_not_null
FROM t
ORDER BY c1;
-- Bug: this predicate is logically impossible, so the result must be empty.
SELECT c1, c4, c5
FROM t
WHERE c6 >= 'not-a-date'
AND c5 IS NOT NULL
AND c5 IS NULL
ORDER BY c4 DESC;
-- Optional: MySQL should normally reduce this to Impossible WHERE.
EXPLAIN FORMAT=TRADITIONAL
SELECT c1, c4, c5
FROM t
WHERE c6 >= 'not-a-date'
AND c5 IS NOT NULL
AND c5 IS NULL
ORDER BY c4 DESC;
Description: This issue can be described using a single table and a logically impossible predicate. After creating the table and inserting several rows via `INSERT IGNORE`, the stored `DATE NOT NULL` column `c5` is displayed as `NULL` in the result set: ------------------------------------ mysql> SELECT c1, c4, c5, -> c5 IS NULL AS is_null, -> c5 IS NOT NULL AS is_not_null -> FROM t -> ORDER BY c1; +----+------+------+---------+-------------+ | c1 | c4 | c5 | is_null | is_not_null | +----+------+------+---------+-------------+ | 1 | 573 | NULL | 0 | 1 | | 2 | -2 | NULL | 0 | 1 | | 3 | -6 | NULL | 0 | 1 | | 4 | -552 | NULL | 0 | 1 | +----+------+------+---------+-------------+ ------------------------------------ So the displayed value is `NULL`, but MySQL still evaluates `c5 IS NULL` as false and `c5 IS NOT NULL` as true. Then the following query is executed: ------------------------------------ mysql> SELECT c1, c4, c5 -> FROM t -> WHERE c6 >= 'not-a-date' -> AND c5 IS NOT NULL -> AND c5 IS NULL -> ORDER BY c4 DESC; +----+------+------+ | c1 | c4 | c5 | +----+------+------+ | 1 | 573 | NULL | | 2 | -2 | NULL | | 3 | -6 | NULL | | 4 | -552 | NULL | +----+------+------+ ------------------------------------ This predicate is a contradiction and should be equivalent to `FALSE`, so the query result must be empty. Instead, MySQL returns all four rows. The problem is also visible in the execution plan. Rather than recognizing the predicate as impossible, MySQL executes a full table scan: ------------------------------------ mysql> EXPLAIN FORMAT=TRADITIONAL -> SELECT c1, c4, c5 -> FROM t -> WHERE c6 >= 'not-a-date' -> AND c5 IS NOT NULL -> AND c5 IS NULL -> ORDER BY c4 DESC; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+ | 1 | SIMPLE | t | NULL | ALL | NULL | NULL | NULL | NULL | 4 | 25.0 | Using where; Using filesort | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+ ------------------------------------ This is a wrong-result bug. A contradiction of the form ------------------------------------ c5 IS NOT NULL AND c5 IS NULL ------------------------------------ must never return rows. How to repeat: SET SESSION sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'; DROP DATABASE IF EXISTS repro_null_contradiction; CREATE DATABASE repro_null_contradiction; USE repro_null_contradiction; CREATE TABLE t ( c1 INT NOT NULL PRIMARY KEY, c4 INT NULL, c5 DATE NOT NULL, c6 VARCHAR(10) NOT NULL ); -- INSERT IGNORE is important here. INSERT IGNORE INTO t (c1, c4, c5, c6) VALUES (1, 573, '0000-00-00', 'sd d7j5nr6'), (2, -2, 'not-a-date', 'zzzzzzzzzz'), (3, -6, '0000-00-00', 'zzzzzzzzzz'), (4, -552, 'not-a-date', 'zzzzzzzzzz'); ANALYZE TABLE t; -- Control: inspect how MySQL treats c5 on these rows. SELECT c1, c4, c5, c5 IS NULL AS is_null, c5 IS NOT NULL AS is_not_null FROM t ORDER BY c1; -- Bug: this predicate is logically impossible, so the result must be empty. SELECT c1, c4, c5 FROM t WHERE c6 >= 'not-a-date' AND c5 IS NOT NULL AND c5 IS NULL ORDER BY c4 DESC; -- Optional: MySQL should normally reduce this to Impossible WHERE. EXPLAIN FORMAT=TRADITIONAL SELECT c1, c4, c5 FROM t WHERE c6 >= 'not-a-date' AND c5 IS NOT NULL AND c5 IS NULL ORDER BY c4 DESC;