Bug #113843 prepared json query can not use index
Submitted: 1 Feb 2024 2:49 Modified: 1 Feb 2024 8:08
Reporter: yuxiang jiang (OCA) Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Prepared statements Severity:S3 (Non-critical)
Version:8.0.32, 8.0.36 OS:Any
Assigned to: CPU Architecture:Any

[1 Feb 2024 2:49] yuxiang jiang
Description:
Prepared statement show different phenomena compared to direct execution SQL.

Direct execution use the expected index whether prepared use another.

How to repeat:
CREATE TABLE `test_json` (
  `a` int NOT NULL,
  `b` varchar(10) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `c` json DEFAULT NULL,
  PRIMARY KEY (`a`),
  KEY `key_idx_euip` (((cast(json_unquote(json_extract(`c`,_utf8mb4'$.euip')) as char(30) charset utf8mb4) collate utf8mb4_bin))));

insert into test_json (a,b,c) value (  1 , 'name'  , '{"euip": "1"}' );
insert into test_json (a,b,c) value (  2 , 'name1' , '{"euip": "2"}' );
insert into test_json (a,b,c) value (  3 , 'name2' , '{"euip": "3"}' );
insert into test_json (a,b,c) value (  4 , 'name4' , '{"euip": "4"}' );
insert into test_json (a,b,c) value (  5 , 'name5' , '{"euip": "5"}' );
insert into test_json (a,b,c) value ( 10 , 'name'  , '{"erip": "1"}' );
insert into test_json (a,b,c) value ( 11 , 'name'  , '{"erip": "2"}' );
insert into test_json (a,b,c) value ( 12 , 'name'  , '{"erip": "3"}' );
insert into test_json (a,b,c) value ( 13 , 'name4' , '{"erip": "4"}' );

---------------direct execut
explain select * from test_json where json_extract(c,"$.euip")='2' order by a limit 10;
+----+-------------+-----------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
| id | select_type | table     | partitions | type | possible_keys | key          | key_len | ref   | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | test_json | NULL       | ref  | key_idx_euip  | key_idx_euip | 123     | const |    1 |   100.00 | NULL  |
+----+-------------+-----------+------------+------+---------------+--------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

---------------prepare unexpected 1
PREPARE stmt1 FROM 'explain analyze select * from test_json where json_extract(c,?)=? order by a limit ?';
set @a="$.euip";set @b="2";set @c=10;
EXECUTE stmt1 USING @a,@b,@c;

+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                                                                                                                                                                                   |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Limit: 10 row(s)  (cost=1.15 rows=9) (actual time=0.049..0.061 rows=1 loops=1)
    -> Filter: (json_extract(test_json.c,'$.euip') = '2')  (cost=1.15 rows=9) (actual time=0.048..0.059 rows=1 loops=1)
        -> Index scan on test_json using PRIMARY  (cost=1.15 rows=9) (actual time=0.027..0.038 rows=9 loops=1)
 |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

---------------prepare unexpected 2
PREPARE stmt1 FROM 'explain analyze select * from test_json where json_extract(c,"$.euip")=? order by a limit ?';
set @b= '2';set @c=10;
EXECUTE stmt1 USING @b, @c;
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                                                                                                                                                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Limit: 10 row(s)  (cost=0.35 rows=1) (actual time=0.051..0.066 rows=1 loops=1)
    -> Filter: ((cast(json_unquote(json_extract(c,_utf8mb4'$.euip')) as char(30) charset utf8mb4) collate utf8mb4_bin) = '2')  (cost=0.35 rows=1) (actual time=0.049..0.064 rows=1 loops=1)
        -> Index scan on test_json using PRIMARY  (cost=0.35 rows=9) (actual time=0.038..0.054 rows=9 loops=1)
 |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set, 2 warnings (0.00 sec)

---------------prepare expected
PREPARE stmt1 FROM 'explain analyze select * from test_json where json_extract(c,"$.euip")="2" order by a limit ?';
set @c=10;
EXECUTE stmt1 USING @c;
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                                                                                                                                                                           |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Limit: 10 row(s)  (cost=0.35 rows=1) (actual time=0.038..0.041 rows=1 loops=1)
    -> Index lookup on test_json using key_idx_euip ((cast(json_unquote(json_extract(c,_utf8mb4'$.euip')) as char(30) charset utf8mb4) collate utf8mb4_bin)='2')  (cost=0.35 rows=1) (actual time=0.036..0.039 rows=1 loops=1)
 |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
[1 Feb 2024 8:08] MySQL Verification Team
Hello yuxiang jiang,

Thank you for the report and feedback.

regards,
Umesh