Skip to content

Commit 97fcafb

Browse files
committed
MDEV-32837 long unique does not work like unique key when using replace
write_record() when performing REPLACE has an optimization: - if the unique violation happened in the last unique key, then do UPDATE - otherwise, do DELETE+INSERT This patch changes the way of detecting if this optimization can be applied if the table has long (hash based) unique (i.e. UNIQUE..USING HASH) constraints. Problem: The old condition did not take into account that TABLE_SHARE and TABLE see long uniques differently: - TABLE_SHARE sees as HA_KEY_ALG_LONG_HASH and HA_NOSAME - TABLE sees as usual non-unique indexes So the old condition could erroneously decide that the UPDATE optimization is possible when there are still some unique hash constraints in the table. Fix: - If the current key is a long unique, it now works as follows: UPDATE can be done if the current long unique is the last long unique, and there are no in-engine (normal) uniques. - For in-engine uniques nothing changes, it still works as before: If the current key is an in-engine (normal) unique: UPDATE can be done if it is the last normal unique.
1 parent a7ee3bc commit 97fcafb

File tree

5 files changed

+213
-3
lines changed

5 files changed

+213
-3
lines changed

mysql-test/main/long_unique_bugs.result

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,5 +660,20 @@ Table Op Msg_type Msg_text
660660
test.t1 check status OK
661661
drop table t1;
662662
#
663+
# MDEV-32837 long unique does not work like unique key when using replace
664+
#
665+
CREATE TABLE t1 (a INT PRIMARY KEY, b INT, c INT, UNIQUE KEY `test` (b,c) USING HASH) ENGINE=MyISAM;
666+
INSERT INTO t1 VALUES (1,1,1),(2,2,2);
667+
REPLACE INTO t1 VALUES (3,1,1);
668+
SELECT * FROM t1 ORDER BY a;
669+
a b c
670+
2 2 2
671+
3 1 1
672+
REPLACE INTO t1 VALUES (3,2,2);
673+
SELECT * FROM t1;
674+
a b c
675+
3 2 2
676+
DROP TABLE t1;
677+
#
663678
# End of 10.5 tests
664679
#

mysql-test/main/long_unique_bugs.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,19 @@ insert into t1 values (0);
642642
check table t1 extended;
643643
drop table t1;
644644

645+
646+
--echo #
647+
--echo # MDEV-32837 long unique does not work like unique key when using replace
648+
--echo #
649+
650+
CREATE TABLE t1 (a INT PRIMARY KEY, b INT, c INT, UNIQUE KEY `test` (b,c) USING HASH) ENGINE=MyISAM;
651+
INSERT INTO t1 VALUES (1,1,1),(2,2,2);
652+
REPLACE INTO t1 VALUES (3,1,1);
653+
SELECT * FROM t1 ORDER BY a;
654+
REPLACE INTO t1 VALUES (3,2,2);
655+
SELECT * FROM t1;
656+
DROP TABLE t1;
657+
645658
--echo #
646659
--echo # End of 10.5 tests
647660
--echo #
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#
2+
# Start of 10.5 tests
3+
#
4+
#
5+
# MDEV-32837 long unique does not work like unique key when using replace
6+
#
7+
#
8+
# Normal unique key + long unique key
9+
#
10+
CREATE TABLE t1 (a INT PRIMARY KEY, b INT, c INT, UNIQUE KEY `test` (b,c) USING HASH) ENGINE=MyISAM;
11+
INSERT INTO t1 VALUES (1,1,1),(2,2,2);
12+
FLUSH STATUS;
13+
REPLACE INTO t1 VALUES (3,1,1);
14+
SHOW STATUS WHERE Variable_name LIKE 'handler%' AND Value>0;
15+
Variable_name Value
16+
Handler_delete 1
17+
Handler_read_key 2
18+
Handler_read_rnd 1
19+
Handler_write 1
20+
SELECT * FROM t1 ORDER BY a;
21+
a b c
22+
2 2 2
23+
3 1 1
24+
FLUSH STATUS;
25+
REPLACE INTO t1 VALUES (3,2,2);
26+
SHOW STATUS WHERE Variable_name LIKE 'handler%' AND Value>0;
27+
Variable_name Value
28+
Handler_delete 1
29+
Handler_read_key 3
30+
Handler_read_rnd 2
31+
Handler_update 1
32+
Handler_write 1
33+
SELECT * FROM t1;
34+
a b c
35+
3 2 2
36+
DROP TABLE t1;
37+
#
38+
# Two long unique keys
39+
#
40+
CREATE TABLE t1 (a INT, b INT, c INT, UNIQUE KEY a (a) USING HASH,UNIQUE KEY `test` (b,c) USING HASH) ENGINE=MyISAM;
41+
INSERT INTO t1 VALUES (1,1,1),(2,2,2);
42+
FLUSH STATUS;
43+
REPLACE INTO t1 VALUES (3,1,1);
44+
SHOW STATUS WHERE Variable_name LIKE 'handler%' AND Value>0;
45+
Variable_name Value
46+
Handler_read_key 3
47+
Handler_read_rnd 1
48+
Handler_update 1
49+
SELECT * FROM t1 ORDER BY a;
50+
a b c
51+
2 2 2
52+
3 1 1
53+
FLUSH STATUS;
54+
REPLACE INTO t1 VALUES (3,2,2);
55+
SHOW STATUS WHERE Variable_name LIKE 'handler%' AND Value>0;
56+
Variable_name Value
57+
Handler_delete 1
58+
Handler_read_key 4
59+
Handler_read_rnd 2
60+
Handler_update 1
61+
SELECT * FROM t1;
62+
a b c
63+
3 2 2
64+
DROP TABLE t1;
65+
#
66+
# One long unique key
67+
#
68+
CREATE TABLE t1 (a INT, b INT, c INT, UNIQUE KEY `test` (b,c) USING HASH) ENGINE=MyISAM;
69+
INSERT INTO t1 VALUES (1,1,1),(2,2,2);
70+
FLUSH STATUS;
71+
REPLACE INTO t1 VALUES (3,1,1);
72+
SHOW STATUS WHERE Variable_name LIKE 'handler%' AND Value>0;
73+
Variable_name Value
74+
Handler_read_key 1
75+
Handler_read_rnd 1
76+
Handler_update 1
77+
SELECT * FROM t1 ORDER BY a;
78+
a b c
79+
2 2 2
80+
3 1 1
81+
FLUSH STATUS;
82+
REPLACE INTO t1 VALUES (3,2,2);
83+
SHOW STATUS WHERE Variable_name LIKE 'handler%' AND Value>0;
84+
Variable_name Value
85+
Handler_read_key 1
86+
Handler_read_rnd 1
87+
Handler_update 1
88+
SELECT * FROM t1;
89+
a b c
90+
3 1 1
91+
3 2 2
92+
DROP TABLE t1;
93+
#
94+
# End of 10.5 tests
95+
#
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
if (`SELECT $SP_PROTOCOL > 0`)
2+
{
3+
--skip Test requires: sp-protocol disabled
4+
}
5+
6+
7+
--echo #
8+
--echo # Start of 10.5 tests
9+
--echo #
10+
11+
--echo #
12+
--echo # MDEV-32837 long unique does not work like unique key when using replace
13+
--echo #
14+
15+
# This test produces different Handler commands in the SHOW STATUS output
16+
# with --sp-protocol. So it's here, in this *.test file with --sp-protocol disabled.
17+
18+
--echo #
19+
--echo # Normal unique key + long unique key
20+
--echo #
21+
22+
CREATE TABLE t1 (a INT PRIMARY KEY, b INT, c INT, UNIQUE KEY `test` (b,c) USING HASH) ENGINE=MyISAM;
23+
INSERT INTO t1 VALUES (1,1,1),(2,2,2);
24+
FLUSH STATUS;
25+
REPLACE INTO t1 VALUES (3,1,1);
26+
SHOW STATUS WHERE Variable_name LIKE 'handler%' AND Value>0;
27+
SELECT * FROM t1 ORDER BY a;
28+
FLUSH STATUS;
29+
REPLACE INTO t1 VALUES (3,2,2);
30+
SHOW STATUS WHERE Variable_name LIKE 'handler%' AND Value>0;
31+
SELECT * FROM t1;
32+
DROP TABLE t1;
33+
34+
--echo #
35+
--echo # Two long unique keys
36+
--echo #
37+
38+
CREATE TABLE t1 (a INT, b INT, c INT, UNIQUE KEY a (a) USING HASH,UNIQUE KEY `test` (b,c) USING HASH) ENGINE=MyISAM;
39+
INSERT INTO t1 VALUES (1,1,1),(2,2,2);
40+
FLUSH STATUS;
41+
REPLACE INTO t1 VALUES (3,1,1);
42+
SHOW STATUS WHERE Variable_name LIKE 'handler%' AND Value>0;
43+
SELECT * FROM t1 ORDER BY a;
44+
FLUSH STATUS;
45+
REPLACE INTO t1 VALUES (3,2,2);
46+
SHOW STATUS WHERE Variable_name LIKE 'handler%' AND Value>0;
47+
SELECT * FROM t1;
48+
DROP TABLE t1;
49+
50+
--echo #
51+
--echo # One long unique key
52+
--echo #
53+
54+
CREATE TABLE t1 (a INT, b INT, c INT, UNIQUE KEY `test` (b,c) USING HASH) ENGINE=MyISAM;
55+
INSERT INTO t1 VALUES (1,1,1),(2,2,2);
56+
FLUSH STATUS;
57+
REPLACE INTO t1 VALUES (3,1,1);
58+
SHOW STATUS WHERE Variable_name LIKE 'handler%' AND Value>0;
59+
SELECT * FROM t1 ORDER BY a;
60+
FLUSH STATUS;
61+
REPLACE INTO t1 VALUES (3,2,2);
62+
SHOW STATUS WHERE Variable_name LIKE 'handler%' AND Value>0;
63+
SELECT * FROM t1;
64+
DROP TABLE t1;
65+
66+
--echo #
67+
--echo # End of 10.5 tests
68+
--echo #

sql/sql_insert.cc

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,7 +1728,7 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
17281728

17291729
/* Check if there is more uniq keys after field */
17301730

1731-
static int last_uniq_key(TABLE *table,uint keynr)
1731+
static int last_uniq_key(TABLE *table, const KEY *key, uint keynr)
17321732
{
17331733
/*
17341734
When an underlying storage engine informs that the unique key
@@ -1748,7 +1748,7 @@ static int last_uniq_key(TABLE *table,uint keynr)
17481748
return 0;
17491749

17501750
while (++keynr < table->s->keys)
1751-
if (table->key_info[keynr].flags & HA_NOSAME)
1751+
if (key[keynr].flags & HA_NOSAME)
17521752
return 0;
17531753
return 1;
17541754
}
@@ -2064,8 +2064,27 @@ int write_record(THD *thd, TABLE *table, COPY_INFO *info, select_result *sink)
20642064
tables which have ON UPDATE but have no ON DELETE triggers,
20652065
we just should not expose this fact to users by invoking
20662066
ON UPDATE triggers.
2067+
2068+
Note, TABLE_SHARE and TABLE see long uniques differently:
2069+
- TABLE_SHARE sees as HA_KEY_ALG_LONG_HASH and HA_NOSAME
2070+
- TABLE sees as usual non-unique indexes
20672071
*/
2068-
if (last_uniq_key(table,key_nr) &&
2072+
bool is_long_unique= table->s->key_info &&
2073+
table->s->key_info[key_nr].algorithm ==
2074+
HA_KEY_ALG_LONG_HASH;
2075+
if ((is_long_unique ?
2076+
/*
2077+
We have a long unique. Test that there are no in-engine
2078+
uniques and the current long unique is the last long unique.
2079+
*/
2080+
!(table->key_info[0].flags & HA_NOSAME) &&
2081+
last_uniq_key(table, table->s->key_info, key_nr) :
2082+
/*
2083+
We have a normal key - not a long unique.
2084+
Test is the current normal key is unique and
2085+
it is the last normal unique.
2086+
*/
2087+
last_uniq_key(table, table->key_info, key_nr)) &&
20692088
!table->file->referenced_by_foreign_key() &&
20702089
(!table->triggers || !table->triggers->has_delete_triggers()))
20712090
{

0 commit comments

Comments
 (0)