Skip to content

Commit f8ed177

Browse files
author
Maheedhar PV
committed
Bug#32552332 - CAN'T WRITE; DUPLICATE KEY IN TABLE '/TMP/#SQL...'
In the case of a query that uses temporary table for aggregation, the group-by Item is used as the unique constraint of the temporary table. If the group-by Item value is already present, the row is updated. Otherwise, a new row is inserted into the temporary table. If the group-by Item is an Item with a result field(like Item_func) or a reference Item, it gets evaluated twice. Once to check if the result exists in the temporary table and if not, again while constructing the row to insert. If the group-by item is non-deterministic, the result value used to check for existence would be different from the value with which an insert is tried. This causes the insert to fail if the value already exists in the table. Fix: If any of the group-by Items are non-deterministic, use the hash of the group-by Items as the unique constraint of the temporary table as the hash gets evaluated only once. Change-Id: I7004f298dc0ea84d760903c30af8f08e072a3f2d
1 parent ac79aa1 commit f8ed177

File tree

4 files changed

+86
-2
lines changed

4 files changed

+86
-2
lines changed

mysql-test/r/func_group.result

+35
Original file line numberDiff line numberDiff line change
@@ -1941,3 +1941,38 @@ FROM t1 t GROUP BY t1.a)
19411941
0
19421942
0
19431943
DROP TABLE t1;
1944+
#
1945+
# Bug#32552332 - CAN'T WRITE; DUPLICATE KEY IN TABLE '/TMP/#SQL...'
1946+
#
1947+
CREATE TABLE tr (c1 INT);
1948+
INSERT INTO tr VALUES (1);
1949+
CREATE FUNCTION seq_1_to_2() RETURNS INT
1950+
BEGIN
1951+
DECLARE limit_value, return_value INT;
1952+
SET limit_value = 2;
1953+
SELECT c1 INTO return_value FROM tr;
1954+
IF (return_value < limit_value) THEN
1955+
UPDATE tr SET c1 = c1 + 1;
1956+
ELSE
1957+
UPDATE tr SET c1 = 1;
1958+
END IF;
1959+
RETURN (return_value);
1960+
END|
1961+
CREATE TABLE t1 (c1 INT);
1962+
INSERT INTO t1 VALUES (10);
1963+
INSERT INTO t1 VALUES (11);
1964+
SELECT seq_1_to_2() as seq, COUNT(*) FROM t1 GROUP BY seq;
1965+
seq COUNT(*)
1966+
1 1
1967+
2 1
1968+
SELECT FLOOR(seq_1_to_2() * 2) AS val, COUNT(*) FROM t1 GROUP BY val;
1969+
val COUNT(*)
1970+
2 1
1971+
4 1
1972+
SELECT val, COUNT(*) FROM (SELECT FLOOR(seq_1_to_2())+1 val FROM t1) x
1973+
GROUP BY x.val;
1974+
val COUNT(*)
1975+
2 1
1976+
3 1
1977+
DROP TABLE t1, tr;
1978+
DROP FUNCTION seq_1_to_2;

mysql-test/t/func_group.test

+35
Original file line numberDiff line numberDiff line change
@@ -1240,3 +1240,38 @@ WHERE 0 = (SELECT group_concat(b)
12401240
FROM t1 t GROUP BY t1.a)
12411241
;
12421242
DROP TABLE t1;
1243+
1244+
--echo #
1245+
--echo # Bug#32552332 - CAN'T WRITE; DUPLICATE KEY IN TABLE '/TMP/#SQL...'
1246+
--echo #
1247+
1248+
CREATE TABLE tr (c1 INT);
1249+
INSERT INTO tr VALUES (1);
1250+
1251+
# Generates a integer sequence from 1 to 2 and repeats
1252+
DELIMITER |;
1253+
CREATE FUNCTION seq_1_to_2() RETURNS INT
1254+
BEGIN
1255+
DECLARE limit_value, return_value INT;
1256+
SET limit_value = 2;
1257+
SELECT c1 INTO return_value FROM tr;
1258+
IF (return_value < limit_value) THEN
1259+
UPDATE tr SET c1 = c1 + 1;
1260+
ELSE
1261+
UPDATE tr SET c1 = 1;
1262+
END IF;
1263+
RETURN (return_value);
1264+
END|
1265+
DELIMITER ;|
1266+
1267+
CREATE TABLE t1 (c1 INT);
1268+
INSERT INTO t1 VALUES (10);
1269+
INSERT INTO t1 VALUES (11);
1270+
1271+
SELECT seq_1_to_2() as seq, COUNT(*) FROM t1 GROUP BY seq;
1272+
SELECT FLOOR(seq_1_to_2() * 2) AS val, COUNT(*) FROM t1 GROUP BY val;
1273+
SELECT val, COUNT(*) FROM (SELECT FLOOR(seq_1_to_2())+1 val FROM t1) x
1274+
GROUP BY x.val;
1275+
1276+
DROP TABLE t1, tr;
1277+
DROP FUNCTION seq_1_to_2;

sql/item.h

+8-1
Original file line numberDiff line numberDiff line change
@@ -1663,9 +1663,16 @@ class Item : public Parse_tree_node
16631663
Returns true if this is constant but its value may be not known yet.
16641664
(Can be used for parameters of prep. stmts or of stored procedures.)
16651665
*/
1666-
virtual bool const_during_execution() const
1666+
virtual bool const_during_execution() const
16671667
{ return (used_tables() & ~PARAM_TABLE_BIT) == 0; }
16681668

1669+
/**
1670+
@returns true if this item is non-deterministic, which means that a
1671+
has a component that must be evaluated once per row in
1672+
execution of a JOIN query.
1673+
*/
1674+
bool is_non_deterministic() const { return used_tables() & RAND_TABLE_BIT; }
1675+
16691676
/**
16701677
This method is used for to:
16711678
- to generate a view definition query (SELECT-statement);

sql/sql_tmp_table.cc

+8-1
Original file line numberDiff line numberDiff line change
@@ -745,8 +745,15 @@ create_tmp_table(THD *thd, Temp_table_param *param, List<Item> &fields,
745745
(*tmp->item)->marker= 4;
746746
const uint char_len=
747747
(*tmp->item)->max_length / (*tmp->item)->collation.collation->mbmaxlen;
748-
if (char_len > CONVERT_IF_BIGGER_TO_BLOB)
748+
/*
749+
Use hash key as the unique constraint if the group-by key is
750+
big or if it is non-deterministic. Group-by items get evaluated
751+
twice and a non-deterministic function would cause a discrepancy.
752+
*/
753+
if (char_len > CONVERT_IF_BIGGER_TO_BLOB ||
754+
(*tmp->item)->is_non_deterministic()) {
749755
using_unique_constraint= true;
756+
}
750757
}
751758
if (group)
752759
{

0 commit comments

Comments
 (0)