Bug #112876 ICP (Index condition pushdown) causing BIGINT UNSIGNED value out of range error
Submitted: 28 Oct 2023 16:43 Modified: 10 Nov 2023 11:34
Reporter: senthil nathan Email Updates:
Status: Not a Bug Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S3 (Non-critical)
Version:8.0.33 OS:Any
Assigned to: CPU Architecture:Any
Tags: ICP, IndexConditionPushDown, Optimizer

[28 Oct 2023 16:43] senthil nathan
Description:
Filter containing left shift operator is throwing "ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(32 - `authservice`.`route`.`networkLen`)'".

The table structure I am using is as follows 
CREATE TABLE `routes` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `fieldId` bigint NOT NULL,
  `ip` varchar(255) NOT NULL,
  `networkLen` int unsigned NOT NULL DEFAULT '24',
  `addressType` enum('IPv4','IPv6') NOT NULL DEFAULT 'IPv4',
  PRIMARY KEY (`id`),
  KEY `fieldId` (`fieldId`,`ip`,`networkLen`)
) ENGINE=InnoDB AUTO_INCREMENT=49772538 DEFAULT CHARSET=utf8mb3;

On such a table, a query with filter containing the left shift operator is failing.

mysql> SELECT   DISTINCT route.id routeId FROM routes route WHERE route.fieldId = 1 AND route.addressType='IPv4' AND (-1 << (32-networkLen)) & INET_ATON('183.115.195.215') = INET_ATON(ip);
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(32 - `authservice`.`route`.`networkLen`)'

When I use no NO_ICP hint, the error goes away.

mysql> SELECT /*+ NO_ICP(route) */  DISTINCT route.id routeId FROM routes route     WHERE route.fieldId = 1 AND route.addressType='IPv4' AND (-1 << (32-networkLen)) & INET_ATON('183.115.195.215') = INET_ATON(ip);
+----------+
| routeId  |
+----------+
| 49672547 |
+----------+
1 row in set (0.07 sec)

The explain plan looks like after disabling ICP.

mysql> EXPLAIN SELECT /*+ NO_ICP(route) */  DISTINCT route.id routeId FROM routes route WHERE route.fieldId = 1 AND route.addressType=
'IPv4' AND (-1 << (32-networkLen)) & INET_ATON('183.115.195.215') = INET_ATON(ip);
+----+-------------+-------+------------+------+-----------------+---------+---------+-------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys   | key     | key_len | ref   | rows  | filtered | Extra       |
+----+-------------+-------+------------+------+-----------------+---------+---------+-------+-------+----------+-------------+
|  1 | SIMPLE      | route | NULL       | ref  | PRIMARY,fieldId | fieldId | 8       | const | 49999 |    50.00 | Using where |
+----+-------------+-------+------------+------+-----------------+---------+---------+-------+-------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

When ICP is not disabled, the plan looks like this

mysql> EXPLAIN SELECT DISTINCT route.id routeId FROM routes route WHERE route.fieldId = 1 AND route.addressType='IPv4' AND (
-1 << (32-networkLen)) & INET_ATON('183.115.195.215') = INET_ATON(ip);
+----+-------------+-------+------------+------+-----------------+---------+---------+-------+-------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys   | key     | key_len | ref   | rows  | filtered | Extra                              |
+----+-------------+-------+------------+------+-----------------+---------+---------+-------+-------+----------+------------------------------------+
|  1 | SIMPLE      | route | NULL       | ref  | PRIMARY,fieldId | fieldId | 8       | const | 49999 |    50.00 | Using index condition; Using where |
+----+-------------+-------+------------+------+-----------------+---------+---------+-------+-------+----------+------------------------------------+
1 row in set, 1 warning (0.00 sec)

Please note that this was not throwing error in previous versions. AFAIK I know It definitely works in 5.7. Even after we upgraded to 8.x, we did not face the issue until recently. Currently we are in 8.0.33 and we face the issue.

Please note that the same filter predicate used in select does not throw any error.
mysql> SELECT (-1 << (32-networkLen)) & INET_ATON('183.115.195.215') = INET_ATON(ip)  FROM routes route WHERE route.fieldId = 1 AND route.addressType='IPv4';
+------------------------------------------------------------------------+
| (-1 << (32-networkLen)) & INET_ATON('183.115.195.215') = INET_ATON(ip) |
+------------------------------------------------------------------------+
|                                                                      0 |
|                                                                      0 |
|                                                                      0 |
|                                                                      0 |
.......
|                                                                      0 |
|                                                                      0 |
|                                                                      0 |
|                                                                      0 |
|                                                                      0 |
+------------------------------------------------------------------------+
16691 rows in set (0.05 sec)

How to repeat:
Create table as

CREATE TABLE `routes` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `fieldId` bigint NOT NULL,
  `ip` varchar(255) NOT NULL,
  `networkLen` int unsigned NOT NULL DEFAULT '24',
  `addressType` enum('IPv4','IPv6') NOT NULL DEFAULT 'IPv4',
  PRIMARY KEY (`id`),
  KEY `fieldId` (`fieldId`,`ip`,`networkLen`)
) ENGINE=InnoDB AUTO_INCREMENT=49772538 DEFAULT CHARSET=utf8mb3;

Insert random data like this.(I have about 100000 rows in my table)
fielId column is one of 1, 2,3, 4
ip is either ipv4 or ipv6 address
networkLen is 0-32 for ipv4 and 0-128 for ipv6.

INSERT INTO `routes` VALUES
    (49672537,3,'61.184.111.131',22,'IPv4'),
    (49672538,3,'4a2:3066:12a4:be77:bcbd:df91:517c:251a',48,'IPv6'),
    (49672539,2,'169.206.212.22',9,'IPv4'),
    (49672540,3,'685d:46de:30e0:e7a4:c313:d6cf:c760:4db4',69,'IPv6'),
    (49672541,1,'e391:c972:f038:7032:7e8a:c72a:cbf3:a859',108,'IPv6'),
    (49672542,1,'7a09:c286:911d:a023:ed36:1f3:108c:9bdd',102,'IPv6'),
    (49672543,3,'3715:1ea6:151d:2e6:4de1:570:5d00:64e8',84,'IPv6'),
    (49672544,3,'88.73.103.218',27,'IPv4'),
    (49672545,1,'138.10.64.0',4,'IPv4'),
    (49672546,1,'179.159.7.109',1,'IPv4'),
    (49672547,1,'183.115.195.215',16,'IPv4'),
    (49672548,3,'9c3b:3cde:dd48:9d0b:caf1:1635:9c53:37aa',0,'IPv6'),
    (49672549,3,'103.14.225.128',15,'IPv4');

After this, run the queries given in the description to reproduce this issue.
[28 Oct 2023 16:56] senthil nathan
mysqldump of the table

Attachment: 1.sql (application/octet-stream, text), 4.94 MiB.

[30 Oct 2023 12:07] MySQL Verification Team
Hi Mr. nathan,

Thank you for your bug report.

However, we fail to see why are you reporting this problem as a bug.

Because, when `authservice`.`route`.`networkLen` is larger then 32, then you are out of the range for the unsigned integer.

Not a bug.
[31 Oct 2023 2:52] senthil nathan
It is a bug because there is no way to run the query. It fails even when using GREATEST function and removing all negative values.

mysql> SELECT   DISTINCT route.id routeId FROM routes route     WHERE route.fieldId = 1 AND  (-1 << GREATEST(32-networkLen, 0)) & INET_ATON('183.115.195.215') = INET_ATON(ip)       ;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(32 - `authservice`.`route`.`networkLen`)'

I am using Greatest of 32-networkLen and 0, which will cast negative values to 0. Even in this case, it fails.

It only runs fine, if I remove networkLen from the fieldId index.
[31 Oct 2023 12:53] MySQL Verification Team
HI Mr. nathan,

This seems like a possible bug with Index Condition Pushdown.

However, we need a table with sufficient rows, so that we can try to repeat the problem.

We are waiting on your full feedback, including rows and precisely the query that is failing and the one that is succeeding.

Waiting on your feedback.
[10 Nov 2023 5:00] senthil nathan
I have already added the mysqldump for the table while creating the bug and I have provided the query that fails. Are you expecting something else? can you provide details? 

If you create the table using the provided dump and run the query provided, it reproduces directly.
[10 Nov 2023 11:34] MySQL Verification Team
Hi Mr. nathan,

We have reproduced your test case.

However, it is not a bug.

The problem is in your query, notable the expression:

-1 << GREATEST(32-networkLen, 0)

The rules of expression evaluation require that the type of the expression 32 - networkLen is UNSIGNED BIGINT , due to the type of the column.

Hence, you can either change networkLen to SIGNED or try to use CAST() in order to have your query working.

Not a bug.