Bug #110474 Functional Index is not work like generated column and index
Submitted: 22 Mar 2023 23:09 Modified: 27 Mar 2023 7:02
Reporter: Shinya Sugiyama Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S5 (Performance)
Version:8.0, 8.0.32 OS:Any
Assigned to: CPU Architecture:Any

[22 Mar 2023 23:09] Shinya Sugiyama
Description:
Functional Index is not correclty working.
It should be work as generated column.

Functional Index
---------------
alter table t_funcindx add index idx_truncate((truncate(col1/10,0)));

Generated Column
---------------
alter table t_funcindx add COLUMN generated0 int GENERATED ALWAYS AS (truncate((col1/10),0)) VIRTUAL;
alter table t_funcindx add index idx_generated0(generated0);

Both of them should be work with "where truncate((col1/10),0) = 200" but functional index work with "where truncate((col1/10),0) = 200.000;"

How to repeat:
## Create table

root@localhost [poc]> CREATE TABLE `t_funcindx` (
  `id` int NOT NULL AUTO_INCREMENT,
    ->   `id` int NOT NULL AUTO_INCREMENT,
    ->   `col1` int DEFAULT NULL,
    ->   `col2` int DEFAULT NULL,
    ->   `generated0` int GENERATED ALWAYS AS (truncate((`col1` / 10),0)) VIRTUAL,
    ->   PRIMARY KEY (`id`),
    ->   KEY `indx_sum` (((`col1` + `col2`))),
    ->   KEY `idx_trunc` (((`col1` / 10))),
    ->   KEY `idx_trunc_0` ((truncate((`col1` / 10),0))),
    ->   KEY `idx_generated0` (`generated0`)
    -> ) ENGINE=InnoDB AUTO_INCREMENT=2001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Query OK, 0 rows affected (0.05 sec)

## Insert Data

insert into t_funcindx(col1,col2) values(2000,900);
insert into t_funcindx(col1,col2) values(9000,800);
insert into t_funcindx(col1,col2) values(3000,100);
insert into t_funcindx(col1,col2) values(1000,1000);
insert into t_funcindx(col1,col2) values(4000,700);
insert into t_funcindx(col1,col2) values(7000,400);
insert into t_funcindx(col1,col2) values(6000,200);
insert into t_funcindx(col1,col2) values(3000,500);
insert into t_funcindx(col1,col2) values(7000,200);
insert into t_funcindx(col1,col2) values(4000,400);

## Confirmation

root@localhost [poc]> explain select count(*) from t_funcindx where generated0 = 200;
+----+-------------+------------+------------+------+----------------+----------------+---------+-------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys  | key            | key_len | ref   | rows | filtered | Extra       |
+----+-------------+------------+------------+------+----------------+----------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | t_funcindx | NULL       | ref  | idx_generated0 | idx_generated0 | 5       | const |    1 |   100.00 | Using index |
+----+-------------+------------+------------+------+----------------+----------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

root@localhost [poc]> explain select * from t_funcindx where truncate((col1/10),0) = 200;
+----+-------------+------------+------------+------+----------------+----------------+---------+-------+------+----------+-------+
| id | select_type | table      | partitions | type | possible_keys  | key            | key_len | ref   | rows | filtered | Extra |
+----+-------------+------------+------------+------+----------------+----------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t_funcindx | NULL       | ref  | idx_generated0 | idx_generated0 | 5       | const |    1 |   100.00 | NULL  |
+----+-------------+------------+------------+------+----------------+----------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

root@localhost [poc]> explain select * from t_funcindx where truncate((col1/10),0) = 200.0000;
+----+-------------+------------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
| id | select_type | table      | partitions | type | possible_keys | key         | key_len | ref   | rows | filtered | Extra |
+----+-------------+------------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t_funcindx | NULL       | ref  | idx_trunc_0   | idx_trunc_0 | 6       | const |    1 |   100.00 | NULL  |
+----+-------------+------------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

## Drop Generated Column and Index on it.

root@localhost [poc]> alter table t_funcindx drop index idx_generated0;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

root@localhost [poc]> alter table t_funcindx drop column generated0;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

root@localhost [poc]> show create table t_funcindx\G
*************************** 1. row ***************************
       Table: t_funcindx
Create Table: CREATE TABLE `t_funcindx` (
  `id` int NOT NULL AUTO_INCREMENT,
  `col1` int DEFAULT NULL,
  `col2` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `indx_sum` (((`col1` + `col2`))),
  KEY `idx_trunc` (((`col1` / 10))),
  KEY `idx_trunc_0` ((truncate((`col1` / 10),0)))
) ENGINE=InnoDB AUTO_INCREMENT=2011 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

## Explain

root@localhost [poc]> explain select * from t_funcindx where truncate((col1/10),0) = 200;
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table      | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t_funcindx | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   10 |   100.00 | Using where |
+----+-------------+------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

root@localhost [poc]> explain select * from t_funcindx where truncate((col1/10),0) = 200.000;
+----+-------------+------------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
| id | select_type | table      | partitions | type | possible_keys | key         | key_len | ref   | rows | filtered | Extra |
+----+-------------+------------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t_funcindx | NULL       | ref  | idx_trunc_0   | idx_trunc_0 | 6       | const |    1 |   100.00 | NULL  |
+----+-------------+------------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

root@localhost [poc]>

## Expectation

"KEY `idx_trunc_0` ((truncate((`col1` / 10),0)))" is index that generated with truncate function, so truncate((col1/10),0) = 200 should be work like generated column.

Suggested fix:
It supporse to be work like generated column.

Functional Key Parts
https://dev.mysql.com/doc/refman/8.0/en/create-index.html#create-index-functional-key-part...
[27 Mar 2023 7:02] MySQL Verification Team
Hello Shinya Sugiyama,

Thank you for the report and feedback.

regards,
Umesh