Details
-
Bug
-
Status: Confirmed (View Workflow)
-
Major
-
Resolution: Unresolved
-
10.11, 11.4, 11.8, 12.3, 13.0, 12.3.2
-
Operating system:
- Ubuntu 20.04.6 LTS (Focal Fossa)
- Linux kernel: 5.4.0-204-generic
- Architecture: x86_64
Hardware:
- CPU: 2 Ă— Intel Xeon Gold 5218R @ 2.10 GHz
- 40 physical cores / 80 logical CPUs
Description
-
-
- Problem Description
In MariaDB, when a `BIT(8)` indexed column is compared with a string constant, a full table scan implicitly converts the string to a number at the SQL layer, while the index `ref` lookup path does not perform the same numeric conversion. This causes the same query to return different results under different execution plans. Constants of correct types (integer or bit literal) are not affected.
- Problem Description
-
-
-
- How to Reproduce
Execute the following SQL:
- How to Reproduce
-
```sql
DROP DATABASE IF EXISTS repro_mariadb809_db16_min;
CREATE DATABASE repro_mariadb809_db16_min;
USE repro_mariadb809_db16_min;
CREATE TABLE base_rows (
c0 BIT(8) NOT NULL
) ENGINE=MyISAM;
INSERT INTO base_rows(c0)
VALUES (1),(2),(3),(4),(5),(6),(7),(8);
CREATE TABLE vp_source (
vp_rowid BIGINT NOT NULL,
c0 BIT(8) NOT NULL,
PRIMARY KEY (vp_rowid)
) ENGINE=InnoDB;
INSERT INTO vp_source(vp_rowid, c0)
SELECT ROW_NUMBER() OVER (ORDER BY c0), c0
FROM base_rows;
CREATE TABLE vp_left (
vp_rowid BIGINT NOT NULL PRIMARY KEY,
c0 BIT(8) NOT NULL
) ENGINE=InnoDB;
CREATE TABLE vp_right (
vp_rowid BIGINT NOT NULL PRIMARY KEY,
c0 BIT(8) NOT NULL
) ENGINE=InnoDB;
INSERT INTO vp_left
SELECT vp_rowid, c0 FROM vp_source;
INSERT INTO vp_right
SELECT vp_rowid, c0 FROM vp_source;
CREATE INDEX idx_left_c0 ON vp_left(c0);
```
*Single-table query:*
```sql
SELECT DISTINCT
CAST(LENGTHB(c0) AS UNSIGNED) AS result_value
FROM vp_source
WHERE c0 IN ('3bf0a4fa-72ff-433c-9a26-eecf4d741c6c');
```
Actual result:
```
result_value
1
```
*Reconstructed query, forcing vp_left(c0) index:*
```sql
SELECT DISTINCT
CAST(LENGTHB(c0) AS UNSIGNED) AS result_value
FROM (
SELECT
l.vp_rowid,
l.c0 AS c0
FROM vp_left AS l FORCE INDEX (idx_left_c0)
JOIN (
SELECT
vp_rowid,
JSON_OBJECT('vp', vp_rowid) AS payload
FROM vp_right
) AS p
ON p.vp_rowid = l.vp_rowid
JOIN vp_right AS r
ON r.vp_rowid = p.vp_rowid
) AS reconstructed
WHERE c0 IN ('3bf0a4fa-72ff-433c-9a26-eecf4d741c6c');
```
Actual result:
```
Empty set
```
*Direct query execution plan:*
```
id select_type table type possible_keys key rows Extra
1 SIMPLE vp_source ALL NULL NULL 8 Using where; Using temporary
```
*Forced-index reconstructed query execution plan:*
```
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE l ref idx_left_c0 idx_left_c0 1 const 0 Using where; Using index; Using temporary
1 SIMPLE vp_right
eq_ref PRIMARY PRIMARY 8 repro_mariadb809_db16_min.l.vp_rowid 1 Distinct
1 SIMPLE r eq_ref PRIMARY PRIMARY 8 repro_mariadb809_db16_min.l.vp_rowid 1 Distinct
```
*Additionally, when the index is disabled:*
```sql
SELECT DISTINCT
CAST(LENGTHB(c0) AS UNSIGNED) AS result_value
FROM (
SELECT
l.vp_rowid,
l.c0 AS c0
FROM vp_left AS l IGNORE INDEX (idx_left_c0)
JOIN (
SELECT
vp_rowid,
JSON_OBJECT('vp', vp_rowid) AS payload
FROM vp_right
) AS p
ON p.vp_rowid = l.vp_rowid
JOIN vp_right AS r
ON r.vp_rowid = p.vp_rowid
) AS reconstructed
WHERE c0 IN ('3bf0a4fa-72ff-433c-9a26-eecf4d741c6c');
```
The result recovers to:
```
result_value
1
```
-
-
- Expected Behavior
The single-table query and the forced-index reconstructed query should return the same result. Whether a full table scan or an index `ref` lookup is used, the query result for the same string constant should be consistent.
- Expected Behavior
-
-
-
- Actual Behavior
-
- Single-table full scan: returns `result_value = 1`
- Forced `idx_left_c0` index reconstructed query: returns `Empty set`
- After disabling the index (`IGNORE INDEX`), full scan is restored and returns `result_value = 1` again
-
-
- Additional Analysis
-
The `BIT(8)` column is compared with the string UUID `'3bf0a4fa-...'`. The numeric prefix of the string is `3`.
- During a full table scan, the SQL layer implicitly converts the string to the number `3`, matches `c0=3`, `LENGTHB(c0)=1`, and therefore returns `1`.
- When forcing the `idx_left_c0` index, the index `ref` lookup does not perform the same numeric conversion and may compare in binary/byte form, resulting in an empty set.
- After `IGNORE INDEX`, full scan is restored and the result changes back to `1`.
Attachments
Issue Links
- relates to
-
MDEV-29259 Comparison semantic of int = string changes with creation of an index
-
- Confirmed
-