| Bug #82425 | Problem with stored function which generates BIGINT UNSIGNED used in WHERE claus | ||
|---|---|---|---|
| Submitted: | 3 Aug 2016 13:38 | Modified: | 18 Aug 2016 13:55 |
| Reporter: | Artur Banul | Email Updates: | |
| Status: | Closed | Impact on me: | |
| Category: | MySQL Server: Optimizer | Severity: | S3 (Non-critical) |
| Version: | 5.6.25-1~dotdeb+7.1-log | OS: | Any |
| Assigned to: | CPU Architecture: | Any | |
[3 Aug 2016 17:47]
Artur Banul
Example can be even simplied
# 1. adding function for calculating crc64 values based on some string
DROP FUNCTION test001;
DELIMITER ;;
CREATE DEFINER = 'root'@'localhost' FUNCTION test001(
_text TEXT
)
RETURNS BIGINT(20) UNSIGNED
DETERMINISTIC
NO SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
RETURN CONV(LEFT(MD5(_text),16),16,10);
END;;
DELIMITER ;
# 2. creating table with BIGINT field
DROP TABLE `test001`;
CREATE TABLE `test001` (
`id` bigint(20) UNSIGNED NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
# 3. inserting data - calculated crc64 values from some strings using test001 function
INSERT INTO `test001` (`id`) VALUES
(test001('photos'));
# 4. select query works and return records
SELECT *
FROM test001 AS `e`
WHERE e.id = test001('photos')\G
*************************** 1. row ***************************
id: 15460588431078774149
1 row in set (0.00 sec)
# 5. but when you add an index on t3 colemn something is not right
ALTER TABLE test001 ADD INDEX test001_idx1 (id) COMMENT '';
# 6. and the same select doesn't work !!!
SELECT *
FROM test001 AS `e`
WHERE e.id = test001('photos')
AND TRUE\G
Empty set (0.00 sec)
# 7. but little simplified works
SELECT *
FROM test001 AS `e`
WHERE e.id = test001('photos')\G
*************************** 1. row ***************************
id: 15460588431078774149
1 row in set (0.00 sec)
# 9. when you remove added index select queries workds again
ALTER TABLE test001 DROP INDEX test001_idx1;
# 10.
SELECT *
FROM test001 AS `e`
WHERE e.id = test001('photos')
AND TRUE\G
*************************** 1. row ***************************
id: 15460588431078774149
1 row in set (0.00 sec)
# 11.
SELECT *
FROM test001 AS `e`
WHERE e.id = test001('photos')\G
*************************** 1. row ***************************
id: 15460588431078774149
1 row in set (0.00 sec)
[3 Aug 2016 18:16]
MySQL Verification Team
Hi,
Thank you for your bug report.
It is a bug, but not that hard since there is a workaround with user variable.
Here is a full SQL test case:
DROP TABLE IF EXISTS `test001`;
DROP FUNCTION IF EXISTS test0001;
DROP FUNCTION IF EXISTS test001;
DELIMITER $$
CREATE DEFINER = 'root'@'localhost' FUNCTION test0001( _text TEXT ) RETURNS BIGINT(20) UNSIGNED DETERMINISTIC NO SQL SQL SECURITY DEFINER COMMENT ''
BEGIN
RETURN CONV(LEFT(MD5(_text),16),16,10);
END$$
DELIMITER ;
CREATE TABLE `test001` ( `id` bigint(20) unsigned NOT NULL DEFAULT '0', `t1` bigint(20) unsigned NOT NULL DEFAULT '0', `t3` bigint(20) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `test001` (`id`, `t1`, `t3`) VALUES (1, 0, test0001('photos')), (2, 0, test0001('photos')), (3, 0, test0001('photos'));
# 4. select query works and return records
SELECT "It works without index "; SELECT " ";
SELECT * FROM test001 AS `e` WHERE e.t3 = test0001('photos') AND e.t1 = 0;
ALTER TABLE test001 ADD INDEX test001_idx1 (t3) COMMENT '';
SELECT "It does not work with index"; SELECT " ";
SET @rez= test0001('photos');
SELECT *, @rez FROM test001 AS `e` WHERE e.t3 = @rez AND e.t1 = 0;
SELECT "But, it works without : AND et1 = 0"; SELECT " ";
SELECT * FROM test001 AS `e` WHERE e.t3 = test0001('photos');
DROP FUNCTION IF EXISTS test001;
DROP TABLE IF EXISTS `test001`;
[3 Aug 2016 18:24]
Artur Banul
Yes, I'm aware that this example works with user variable but still this is an unexplained bug/behaviour. Unfortunatelly, I our API there lot of queries with such inline function call. It would need lot of changes in many DB classes.
[3 Aug 2016 18:29]
Artur Banul
Little background: Before, we used UDF string2crc64 written in C++. Right now, we have requirement to remove UDF's. There were no problems with inline UDF function calls. So currently simple function replacement will require API rebuilding.
[4 Aug 2016 9:08]
Øystein Grøvlen
Posted by developer:
The EXPLAIN output indicates that the optimizer thinks that it is dealing with a const table:
mysql> EXPLAIN SELECT * FROM test001 AS `e` WHERE e.t3 = test001('photos') AND e.t1 = 0;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | no matching row in const table |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------+
1 row in set, 1 warning (0,00 sec)
Not sure how that could happen and why that should be dependent on the existence of an index.
For the simpler query one gets the following plan:
mysql> EXPLAIN SELECT * FROM test001 AS `e` WHERE e.t3 = test001('photos');
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | e | NULL | ALL | test001_idx1 | NULL | NULL | NULL | 3 | 100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0,00 sec)
Without index, one gets:
mysql> EXPLAIN SELECT * FROM test001 AS `e` WHERE e.t3 = test001('photos') AND e.t1 = 0; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | e | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0,00 sec)
[10 Aug 2016 14:42]
Steinar Gunderson
Posted by developer: I made an MTR test case, using FORCE/IGNORE INDEX. Note that this bug only triggers when the function returns a value that would be negative when converted to signed (as you can see from the EXPLAIN). IOW, it is likely to be an issue with signed/unsigned comparisons. This bug is reproducible in 5.7 but not 5.5.
[11 Aug 2016 13:28]
Steinar Gunderson
Posted by developer: I tried a bisect, but even with manual guidance, it looks to be very difficult because there are many worklogs here cross-merging from each other. It looks as if WL #4443 (“Remove scalability problem with many lock's”) is at least partially implicated; there's one commit there where this simply starts segfaulting, but it's hard to figure if that is the true cause or not. WL #6255 is also involved in this merge, but since it's an InnoDB worklog and this also happens with MyISAM, it's not likely to be the culprit.
[11 Aug 2016 14:43]
Steinar Gunderson
Posted by developer:
I finally found the offending commit:
commit 7491b2b86810a83d06c3467925136329ab6a4fe8
Author: Mattias Jonsson <mattias.jonsson@oracle.com>
Date: Mon May 7 23:43:48 2012 +0200
WL#4443
Fix for Item_func_sp::const_item.
And one test result fix (same as in trunk)
Reverting it fixes the test, but it's unclear what else it might break.
[12 Aug 2016 7:24]
Artur Banul
You wrote:
| it is likely to be an issue with signed/unsigned comparisons.
Seems so, but check the point 7.
# 7. but little simplified works
SELECT *
FROM test001 AS `e`
WHERE e.t3 = test001('photos');
When there is only one comparission in WHERE clause it works. But when you add simple AND 1=1 then it doesn't.
[12 Aug 2016 8:38]
Steinar Gunderson
Posted by developer: Sure, but that's probably just due to some optimization being inhibited. It's relevant for diagnosis, but not a huge smoking gun for anything in particular.
[18 Aug 2016 13:55]
Paul DuBois
Posted by developer: Noted in 8.0.1 changelog. Constant folding could produce incorrect results for large unsigned integers.

Description: During my development I found some problem related with index on BIGINT's. I don't even know how to define it but checkout the example. How to repeat: # 1. adding function for calculating crc64 values based on some string DELIMITER ;; CREATE DEFINER = 'root'@'localhost' FUNCTION test001( _text TEXT ) RETURNS BIGINT(20) UNSIGNED DETERMINISTIC NO SQL SQL SECURITY DEFINER COMMENT '' BEGIN RETURN CONV(LEFT(MD5(_text),16),16,10); END;; DELIMITER ; # 2. creating table with BIGINT's fields CREATE TABLE `test001` ( `id` bigint(20) unsigned NOT NULL DEFAULT '0', `t1` bigint(20) unsigned NOT NULL DEFAULT '0', `t3` bigint(20) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; # 3. inserting data - calculated crc64 values from some strings using test001 function INSERT INTO `test001` (`id`, `t1`, `t3`) VALUES (1, 0, test001('photos')), (2, 0, test001('photos')), (3, 0, test001('photos')); # 4. select query works and return records SELECT * FROM test001 AS `e` WHERE e.t3 = test001('photos') AND e.t1 = 0; *************************** 1. row *************************** id: 1 t1: 0 t3: 15460588431078774149 *************************** 2. row *************************** id: 2 t1: 0 t3: 15460588431078774149 *************************** 3. row *************************** id: 3 t1: 0 t3: 15460588431078774149 3 rows in set (0.00 sec) # 5. but when you add an index on t3 colemn something is not right ALTER TABLE test001 ADD INDEX test001_idx1 (t3) COMMENT ''; # 6. and the same select doesn't work !!! SELECT * FROM test001 AS `e` WHERE e.t3 = test001('photos') AND e.t1 = 0; Empty set (0.00 sec) # 7. but little simplified works SELECT * FROM test001 AS `e` WHERE e.t3 = test001('photos'); *************************** 1. row *************************** id: 1 t1: 0 t3: 15460588431078774149 *************************** 2. row *************************** id: 2 t1: 0 t3: 15460588431078774149 *************************** 3. row *************************** id: 3 t1: 0 t3: 15460588431078774149 3 rows in set (0.00 sec) # 8. also when you pass crc64 value it works SELECT * FROM test001 AS `e` WHERE e.t3 = 15460588431078774149 AND e.t1 = 0\G *************************** 1. row *************************** id: 1 t1: 0 t3: 15460588431078774149 *************************** 2. row *************************** id: 2 t1: 0 t3: 15460588431078774149 *************************** 3. row *************************** id: 3 t1: 0 t3: 15460588431078774149 3 rows in set (0.00 sec) # 9. when you remove added index select queries workds again ALTER TABLE test001 DROP INDEX test001_idx1; # 10. SELECT * FROM test001 AS `e` WHERE e.t3 = test001('photos') AND e.t1 = 0; # 11. SELECT * FROM test001 AS `e` WHERE e.t3 = test001('photos'); Suggested fix: Somehow it doesn't work for strings which generates BIGINT values in higher range then maximum for BIGINT SIGNED (>9223372036854775807) - so for example 'photos' generates crc64 value = 15460588431078774149. But test001 function returns proper BIGINT UNSIGNED values as you can see in simplfied query at #7. But for 'test' string crc64 value is: 688887797400064883 And for this above example works fine.