Bug #77480 Wrong results for truncated column and aggregation

Submitted: 25 Jun 2015 9:14 Modified: 2 Nov 2015 16:32
Reporter: Daniël van Eeden Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: DML Severity:S3 (Non-critical)
Version:5.7.7, 5.6.25 OS:Any
Assigned to: CPU Architecture:Any

[25 Jun 2015 9:14] Daniël van Eeden
Description:
Aggregated results with a c1='something' can return wrong results if 'something' is longer that c1.

How to repeat:
mysql> CREATE TEMPORARY TABLE `PerformanceX` (
    ->   `method` varchar(64),
    ->   `service` varchar(16),
    ->   `period` datetime NOT NULL,
    ->   `count` int unsigned NOT NULL,
    ->   PRIMARY KEY (`service`, `period`, `method`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPRESSED;
Query OK, 0 rows affected (0.02 sec)

mysql> SET SESSION sql_mode='';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> INSERT INTO PerformanceX VALUES ('usersettings', 'useraccountserver', NOW() - INTERVAL 10 MINUTE, 1);
Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> show warnings;
+---------+------+----------------------------------------------+
| Level   | Code | Message                                      |
+---------+------+----------------------------------------------+
| Warning | 1265 | Data truncated for column 'service' at row 1 |
+---------+------+----------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT MAX(period) FROM PerformanceX WHERE service = 'useraccountserver';
+---------------------+
| MAX(period)         |
+---------------------+
| 2015-06-25 10:57:11 |
+---------------------+
1 row in set (0.00 sec)

mysql> SELECT MAX(period), service FROM PerformanceX WHERE service =  'useraccountserver';
+-------------+---------+
| MAX(period) | service |
+-------------+---------+
| NULL        | NULL    |
+-------------+---------+
1 row in set (0.01 sec)

mysql> SELECT MAX(period) FROM PerformanceX WHERE service = 'useraccountserveXXXXX';
+---------------------+
| MAX(period)         |
+---------------------+
| 2015-06-25 10:57:11 |
+---------------------+
1 row in set (0.00 sec)

mysql> SELECT * FROM PerformanceX WHERE service = 'useraccountserveXXXXX';
Empty set (0.00 sec)
[25 Jun 2015 9:19] Daniël van Eeden
Here the truncation is also shown:

mysql> SELECT MAX(period),MAX(service) FROM PerformanceX WHERE service = 'useraccountserveXXXXX';
+---------------------+------------------+
| MAX(period)         | MAX(service)     |
+---------------------+------------------+
| 2015-06-25 10:57:11 | useraccountserve |
+---------------------+------------------+
1 row in set (0.00 sec)
[25 Jun 2015 9:40] Shlomi Noach
To simplify the queries: this is not an issue of sql_mode which adds confusion to the problem. Here are two queries that do not rely on sql_mode and which present the same effect:

mysql> SELECT MAX(period) FROM PerformanceX WHERE service = 'useraccountserver';
+---------------------+
| MAX(period)         |
+---------------------+
| 2015-06-25 11:26:50 |
+---------------------+
1 row in set (0,00 sec)

mysql> SELECT MAX(period) FROM PerformanceX IGNORE INDEX(PRIMARY) WHERE service = 'useraccountserver';
+-------------+
| MAX(period) |
+-------------+
| NULL        |
+-------------+

This shows that the value is being picked up during iteration on the PRIMARY KEY.
[25 Jun 2015 9:46] Xin Wu
just a bit extra info, only happens it is the first column of combined index
root@> show create table PerformanceX\G
*************************** 1. row ***************************
       Table: PerformanceX
Create Table: CREATE TABLE `PerformanceX` (
  `method` varchar(64) NOT NULL DEFAULT '',
  `service` varchar(16) NOT NULL DEFAULT '',
  `period` datetime NOT NULL,
  `count` int(10) unsigned NOT NULL,
  PRIMARY KEY (`service`,`period`,`method`),
  KEY `service` (`service`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

root@> SELECT MAX(period) FROM PerformanceX WHERE service = 'useraccountserver';
+---------------------+
| MAX(period)         |
+---------------------+
| 2015-06-25 11:13:30 |
+---------------------+
1 row in set (0.00 sec)

root@> alter table PerformanceX drop primary key ;
Query OK, 1 row affected (0.21 sec)
Records: 1  Duplicates: 0  Warnings: 0

root@> SELECT MAX(period) FROM PerformanceX WHERE service = 'useraccountserver';
+-------------+
| MAX(period) |
+-------------+
| NULL        |
+-------------+
1 row in set (0.00 sec)

root@> alter table PerformanceX add index (service) ;
Query OK, 1 row affected, 0 (0.08 sec)
Records: 1  Duplicates: 0  Warnings: 0

root@> SELECT MAX(period) FROM PerformanceX WHERE service = 'useraccountserver';
+-------------+
| MAX(period) |
+-------------+
| NULL        |
+-------------+
1 row in set (0.01 sec)

root@> alter table PerformanceX drop index service ;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

root@> alter table PerformanceX add index (service, `period`,`method`) ;
Query OK, 0 rows affected (0.08 sec)
Records: 0  Duplicates: 0  Warnings: 0

root@> SELECT MAX(period) FROM PerformanceX WHERE service = 'useraccountserver';
+---------------------+
| MAX(period)         |
+---------------------+
| 2015-06-25 11:13:30 |
+---------------------+
1 row in set (0.00 sec)

root@ alter table PerformanceX drop index service;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

root@> alter table PerformanceX add index (`period`,`method`, service) ;Query OK, 0 rows affected (0.19 sec)
Records: 0  Duplicates: 0  Warnings: 0

root@> SELECT MAX(period) FROM PerformanceX WHERE service = 'useraccountserver';
+-------------+
| MAX(period) |
+-------------+
| NULL        |
+-------------+
[25 Jun 2015 10:07] MySQL Verification Team
Hello Daniel,

Thank you for the report and test case.

Thanks,
Umesh
[26 Jun 2015 8:22] Daniël van Eeden
Fixed typo in Synopsis
[29 Jun 2015 11:24] Shane Bester
---
drop table if exists t;
create table t(a int, b char(1), primary key(b,a))engine=innodb;
insert into t(a,b) values(1,'a'),(3,'c');
select max(a) from t force  index(primary) where b='aa';  <---returns 1
select max(a) from t ignore index(primary) where b='aa';  <---returns NULL
---
[2 Nov 2015 16:32] Paul DuBois
Noted in 5.7.10, 5.8.0 changelogs.

For queries with implicit grouping; an index with a string column as
its first part; and a WHERE clause with an equality comparison
comparing the column to a string with trailing characters in addition
to the column value, an aggregate function that should return NULL
returned non-NULL.

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.058 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.