| Bug #121017 | subquery_to_derived drops LIMIT 1 from non-equality scalar subqueries | ||
|---|---|---|---|
| Submitted: | 28 Jul 5:06 | Modified: | 28 Jul 5:42 |
| Reporter: | Jacob Ding | Email Updates: | |
| Status: | Open | Impact on me: | |
| Category: | MySQL Server: Optimizer | Severity: | S3 (Non-critical) |
| Version: | 9.7.1 | OS: | MacOS |
| Assigned to: | CPU Architecture: | ARM | |
| Tags: | logical-bug | ||
[28 Jul 5:06]
Jacob Ding
[28 Jul 5:11]
Jacob Ding
When `subquery_to_derived=on`, a correlated scalar subquery with `LIMIT 1` in its WHERE clause is rewritten as a LATERAL derived table join. During this rewrite, the `LIMIT 1` clause is **dropped** from the LATERAL derived table — but **only when the WHERE condition uses a non-equality operator**. The `=` (equality) operator is the **sole exception**: for `=` conditions, the LATERAL rewrite correctly preserves `LIMIT 1`. All other comparison operators lose the `LIMIT 1` constraint, causing the LATERAL join to produce multiple rows per outer row when more than one row matches, triggering error 1242 "Subquery returns more than 1 row". ### Operator-specific test results All tested with identical data (t0.c0=20 matching 3 rows in t1 via the operator), `LIMIT 1`, and `subquery_to_derived=on`: | Operator | Multiple match? | LIMIT 1 preserved? | Bug? | |---|---|---|---| | `=` | Yes (3 rows) | **Yes** | No | | `<=>` (NULL-safe eq) | Yes | No | **Yes (1242)** | | `!=` / `<>` | Yes | No | **Yes (1242)** | | `<` | Yes | No | **Yes (1242)** | | `<=` | Yes | No | **Yes (1242)** | | `>=` | Yes | No | **Yes (1242)** | | `LIKE` | Yes | No | **Yes (1242)** | | `BETWEEN` | Yes | No | **Yes (1242)** | | `IN` | Yes | Yes | No (different path) | | `>` | Yes | No | **Yes (1242)** | **Conclusion**: `=` is special-cased in the LATERAL rewrite to preserve `LIMIT 1`. All other operators that can produce multiple matching rows trigger the bug. This is likely because `=` is the most common correlated subquery join condition and has a dedicated fast-path in the rewrite code, while other operators fall through to a generic path that fails to propagate the `LIMIT` clause. ### Additional trigger conditions - The bug does NOT require ROLLUP, NULL values, or window functions - The bug does NOT require NULL-safe equal (`<=>`) specifically — any non-`=` operator with multiple matches triggers it - Using `LIMIT 2` instead of `LIMIT 1`: **no bug** (only `LIMIT 1` is dropped) - Using `ORDER BY ... LIMIT 1`: **no bug** (ORDER BY changes the rewrite path) - Using `IS NOT DISTINCT FROM` (SQL standard equivalent of `<=>`): **no bug** (MySQL handles this via a different code path — syntactically rejected on some versions) - Only `subquery_to_derived=on` triggers the bug; `semijoin=off`, `materialization=off`, `derived_condition_pushdown=on` do NOT trigger it ### Query context generality The bug manifests in all tested query contexts: SELECT list, WHERE, HAVING, ORDER BY, CASE expressions, COALESCE, DISTINCT, derived tables, multi-table JOINs — indicating a systemic issue in the LATERAL rewrite pass rather than a context-specific edge case.
[28 Jul 5:19]
Jacob Ding
Change the synopsis..
[28 Jul 5:42]
Jacob Ding
change the category to optimizer...
