Description:
MySQL 26.10.0-trunk, commit `561753af986c3c006c4b6c7caa8359748fb55b22`
With strict SQL mode enabled (`STRICT_TRANS_TABLES`, on by default), a data-conversion error raised
while evaluating the left operand of `IN` is **silently swallowed** when the `IN` list is a
**constant list** (the `In_vector_*` fast path). The expression returns `0` with no error.
The semantically equivalent `OR` expansion, which goes through the per-item comparison loop,
**does** raise the error:
```
ERROR 1525 (HY000): Incorrect DATE value: '1'
```
So the same data error is reported or hidden depending only on how the predicate is written.
A systematic type matrix (31 left-operand literals × 14 constant lists = 448 combinations)
reproduced this inconsistency in **84 combinations**.
This matters operationally: `WHERE v IN (DATE'...')` silently classifies rows containing invalid
temporal text as "no match" instead of failing, so data-quality problems go unnoticed, and the
behaviour is unpredictable because rewriting the predicate to an equivalent form suddenly errors out.
How to repeat:
SELECT @@sql_mode LIKE '%STRICT_TRANS_TABLES%' AS strict_on; -- 1
-- IN (constant list): silent, no error
SELECT '1' IN (DATE'2024-01-01', DATE'2025-01-01') AS in_res; -- 0 <-- no error raised
-- Equivalent OR expansion: correctly raises the error
SELECT ('1' = DATE'2024-01-01' OR '1' = DATE'2025-01-01') AS or_res;
-- ERROR 1525 (HY000): Incorrect DATE value: '1'
```
Suggested fix:
## Analysis
`Item_func_in::val_int()` only inspects the **boolean** result of `find_item()`:
```cpp
const bool tmp = m_const_array->find_item(args[0]);
null_value = args[0]->null_value || (!tmp && have_null);
return (longlong)(!null_value && tmp != negated);
```
and every `In_vector_*::find_item()` returns `false` on a conversion failure **without propagating
`current_thd->is_error()`**:
```cpp
bool In_vector_date::find_item(Item *item) {
if (m_used_size == 0) return false;
Date_val date;
if (item->val_date(&date, 0)) return false; // conversion failed -> just returns false
return std::binary_search(...);
}
```
By contrast, the non-`In_vector` branch of the very same function does check:
```cpp
const int rc = in_item->cmp(args[i]);
if (current_thd->is_error()) return error_int(); // <-- error is propagated here
```
So **only the constant-list fast path** loses the error.
Description: MySQL 26.10.0-trunk, commit `561753af986c3c006c4b6c7caa8359748fb55b22` With strict SQL mode enabled (`STRICT_TRANS_TABLES`, on by default), a data-conversion error raised while evaluating the left operand of `IN` is **silently swallowed** when the `IN` list is a **constant list** (the `In_vector_*` fast path). The expression returns `0` with no error. The semantically equivalent `OR` expansion, which goes through the per-item comparison loop, **does** raise the error: ``` ERROR 1525 (HY000): Incorrect DATE value: '1' ``` So the same data error is reported or hidden depending only on how the predicate is written. A systematic type matrix (31 left-operand literals × 14 constant lists = 448 combinations) reproduced this inconsistency in **84 combinations**. This matters operationally: `WHERE v IN (DATE'...')` silently classifies rows containing invalid temporal text as "no match" instead of failing, so data-quality problems go unnoticed, and the behaviour is unpredictable because rewriting the predicate to an equivalent form suddenly errors out. How to repeat: SELECT @@sql_mode LIKE '%STRICT_TRANS_TABLES%' AS strict_on; -- 1 -- IN (constant list): silent, no error SELECT '1' IN (DATE'2024-01-01', DATE'2025-01-01') AS in_res; -- 0 <-- no error raised -- Equivalent OR expansion: correctly raises the error SELECT ('1' = DATE'2024-01-01' OR '1' = DATE'2025-01-01') AS or_res; -- ERROR 1525 (HY000): Incorrect DATE value: '1' ``` Suggested fix: ## Analysis `Item_func_in::val_int()` only inspects the **boolean** result of `find_item()`: ```cpp const bool tmp = m_const_array->find_item(args[0]); null_value = args[0]->null_value || (!tmp && have_null); return (longlong)(!null_value && tmp != negated); ``` and every `In_vector_*::find_item()` returns `false` on a conversion failure **without propagating `current_thd->is_error()`**: ```cpp bool In_vector_date::find_item(Item *item) { if (m_used_size == 0) return false; Date_val date; if (item->val_date(&date, 0)) return false; // conversion failed -> just returns false return std::binary_search(...); } ``` By contrast, the non-`In_vector` branch of the very same function does check: ```cpp const int rc = in_item->cmp(args[i]); if (current_thd->is_error()) return error_int(); // <-- error is propagated here ``` So **only the constant-list fast path** loses the error.