Bug #121646 Wrong result: EXISTS predicate lost in semijoin materialization inside nested outer joins

Submitted: 1 Nov 14:36 Modified: 3 Nov 8:23
Reporter: cl hl Email Updates:
Status: Duplicate Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S2 (Serious)
Version:9.7.1 OS:Any
Assigned to: CPU Architecture:Any

[1 Nov 14:36] cl hl
Description:
MySQL Community Server 9.7.1 returns incorrect results after the `replace_where_with_true` mutation relaxes the outer WHERE clause of a SELECT containing nested outer joins. The original returns 5,184 NULL values, but the mutated query returns 5,184 copies of `1`. Relaxing WHERE must retain every original result row, including multiplicities; the observed results violate that requirement.

The reduced reproduction uses two single-column tables and 73 input rows. The only query change is `WHERE a.c IS NOT NULL` to `WHERE 1 = 1`, emitted by the project's existing mutation operator. Both queries use default optimizer settings without hints.

The mutated execution plan uses semijoin materialization and omits the false subquery predicate `s.c > 1`. The original uses FirstMatch and retains that filter. These observations identify a failing optimization path; the precise internal cause has not been established.

## Expected behaviour

Both queries: 5,184 rows, each containing NULL.

## Actual behaviour

| Query | Rows | Value |
|---|---|---|
| Original | 5,184 | NULL |
| Mutated | 5,184 | `1` |

How to repeat:
-- Run in a fresh test database.
SET SESSION optimizer_switch = DEFAULT;

CREATE TABLE valscope_exists_outer (c BLOB);
CREATE TABLE valscope_exists_inner (c INT);

INSERT INTO valscope_exists_outer (c)
WITH RECURSIVE seq(n) AS (
    SELECT 1
    UNION ALL
    SELECT n + 1 FROM seq WHERE n < 72
)
SELECT 'x' FROM seq;
INSERT INTO valscope_exists_inner VALUES (1);

ANALYZE TABLE valscope_exists_outer, valscope_exists_inner;

-- Original: expected and actual 5,184 NULL values.
SELECT b.c
FROM valscope_exists_outer AS a
LEFT JOIN valscope_exists_inner AS b
    ON EXISTS (
        SELECT 1
        FROM valscope_exists_inner AS s
        WHERE s.c > 1
    )
RIGHT JOIN valscope_exists_outer AS d
    ON a.c IS NOT NULL
WHERE a.c IS NOT NULL;

-- Mutated by replace_where_with_true: expected 5,184 NULL values,
-- actual 5,184 copies of 1.
SELECT b.c
FROM valscope_exists_outer AS a
LEFT JOIN valscope_exists_inner AS b
    ON EXISTS (
        SELECT 1
        FROM valscope_exists_inner AS s
        WHERE s.c > 1
    )
RIGHT JOIN valscope_exists_outer AS d
    ON a.c IS NOT NULL
WHERE 1 = 1;
[3 Nov 8:23] Chaithra Marsur Gopala Reddy
Hi cl hl,

Thank you for the test case. Verified as described. However, we think this is a duplicate of Bug#113718. So we are closing this report.

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

Page generated in 0.049 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.