Merge lp:~dshrews/drizzle/bug599582 into lp:~drizzle-trunk/drizzle/development

Proposed by David Shrewsbury
Status: Merged
Approved by: Monty Taylor
Approved revision: 1733
Merged at revision: 1734
Proposed branch: lp:~dshrews/drizzle/bug599582
Merge into: lp:~drizzle-trunk/drizzle/development
Diff against target: 297 lines (+215/-10)
5 files modified
drizzled/cursor.cc (+7/-2)
drizzled/transaction_services.cc (+22/-5)
drizzled/transaction_services.h (+4/-3)
plugin/transaction_log/tests/r/replace.result (+138/-0)
plugin/transaction_log/tests/t/replace.inc (+44/-0)
To merge this branch: bzr merge lp:~dshrews/drizzle/bug599582
Reviewer Review Type Date Requested Status
Jay Pipes (community) Approve
Drizzle Merge Team Pending
Review via email: mp+33815@code.launchpad.net

Description of the change

This modifies transaction services logging to use the row stored in table->record[1] (which will be the row causing the unique key conflict) to log any DELETEs resulting from a REPLACE statement. This is also the record sent to the storage engine for deleting. Note: This will happen only for REPLACE.

We don't have Field pointers for the update row (table->record[1]), only the insert row (table->record[0]), so I have to "borrow" the value parsing abilities from the insert row Field pointers to get at the value in the update row. This seemed to be least expensive way to do this, and the Field::val_str() calls don't appear to modify anything in Field, so this seems to be safe.

Also added some tests to make sure this functions properly.

To post a comment you must log in.
Revision history for this message
Jay Pipes (jaypipes) wrote :

Very nice work, David. ++ for comments explaining code. :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'drizzled/cursor.cc'
2--- drizzled/cursor.cc 2010-08-09 00:00:11 +0000
3+++ drizzled/cursor.cc 2010-08-26 18:28:46 +0000
4@@ -1327,7 +1327,7 @@
5 * called. If it fails, then a call to deleteRecord()
6 * is called, followed by a repeat of the original
7 * call to insertRecord(). So, log_row_for_replication
8- * could be called either once or twice for a REPLACE
9+ * could be called multiple times for a REPLACE
10 * statement. The below looks at the values of before_record
11 * and after_record to determine which call to this
12 * function is for the delete or the insert, since NULL
13@@ -1340,7 +1340,12 @@
14 */
15 if (after_record == NULL)
16 {
17- transaction_services.deleteRecord(session, table);
18+ /*
19+ * The storage engine is passed the record in table->record[1]
20+ * as the row to delete (this is the conflicting row), so
21+ * we need to notify TransactionService to use that row.
22+ */
23+ transaction_services.deleteRecord(session, table, true);
24 /*
25 * We set the "current" statement message to NULL. This triggers
26 * the replication services component to generate a new statement
27
28=== modified file 'drizzled/transaction_services.cc'
29--- drizzled/transaction_services.cc 2010-08-09 00:00:11 +0000
30+++ drizzled/transaction_services.cc 2010-08-26 18:28:46 +0000
31@@ -1463,7 +1463,7 @@
32 }
33 }
34
35-void TransactionServices::deleteRecord(Session *in_session, Table *in_table)
36+void TransactionServices::deleteRecord(Session *in_session, Table *in_table, bool use_update_record)
37 {
38 ReplicationServices &replication_services= ReplicationServices::singleton();
39 if (! replication_services.isActive())
40@@ -1490,11 +1490,28 @@
41 */
42 if (in_table->getShare()->fieldInPrimaryKey(current_field))
43 {
44- string_value= current_field->val_str(string_value);
45+ if (use_update_record)
46+ {
47+ /*
48+ * Temporarily point to the update record to get its value.
49+ * This is pretty much a hack in order to get the PK value from
50+ * the update record rather than the insert record. Field::val_str()
51+ * should not change anything in Field::ptr, so this should be safe.
52+ * We are careful not to change anything in old_ptr.
53+ */
54+ const unsigned char *old_ptr= current_field->ptr;
55+ current_field->ptr= in_table->getUpdateRecord() + static_cast<ptrdiff_t>(old_ptr - in_table->getInsertRecord());
56+ string_value= current_field->val_str(string_value);
57+ current_field->ptr= const_cast<unsigned char *>(old_ptr);
58+ }
59+ else
60+ {
61+ string_value= current_field->val_str(string_value);
62+ /**
63+ * @TODO Store optional old record value in the before data member
64+ */
65+ }
66 record->add_key_value(string_value->c_ptr(), string_value->length());
67- /**
68- * @TODO Store optional old record value in the before data member
69- */
70 string_value->free();
71 }
72 }
73
74=== modified file 'drizzled/transaction_services.h'
75--- drizzled/transaction_services.h 2010-03-31 19:04:12 +0000
76+++ drizzled/transaction_services.h 2010-08-26 18:28:46 +0000
77@@ -239,10 +239,11 @@
78 * Creates a new DeleteRecord GPB message and pushes it to
79 * replicators.
80 *
81- * @param Pointer to the Session which has deleted a record
82- * @param Pointer to the Table containing delete information
83+ * @param in_session Pointer to the Session which has deleted a record
84+ * @param in_table Pointer to the Table containing delete information
85+ * @param use_update_record If true, uses the values from the update row instead
86 */
87- void deleteRecord(Session *in_session, Table *in_table);
88+ void deleteRecord(Session *in_session, Table *in_table, bool use_update_record= false);
89 /**
90 * Creates a CreateSchema Statement GPB message and adds it
91 * to the Session's active Transaction GPB message for pushing
92
93=== modified file 'plugin/transaction_log/tests/r/replace.result'
94--- plugin/transaction_log/tests/r/replace.result 2010-07-27 19:02:25 +0000
95+++ plugin/transaction_log/tests/r/replace.result 2010-08-26 18:28:46 +0000
96@@ -20,6 +20,48 @@
97 INSERT INTO t1 VALUES (2, "I hate testing.");
98 REPLACE INTO t1 VALUE (2, "I love testing.");
99 DROP TABLE t2, t1;
100+create table t1 (a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, b char(1), c INT, d INT, unique key(b), unique key(c));
101+insert into t1 values (1,"a",1,1),(2,"b",2,2),(3,"c",3,3);
102+replace into t1 values (1,"b",3,4);
103+select * from t1 order by a;
104+a b c d
105+1 b 3 4
106+drop table t1;
107+create table t1 (a CHAR(5) NOT NULL PRIMARY KEY, b char(1), c INT, d INT, unique key(b), unique key(c));
108+insert into t1 values ("a","a",1,1),("bb","b",2,2),("ccc","c",3,3);
109+replace into t1 values ("a","b",3,4);
110+select * from t1 order by a;
111+a b c d
112+a b 3 4
113+drop table t1;
114+create table t1 (a DATE NOT NULL PRIMARY KEY, b char(1), c INT, d INT, unique key(b), unique key(c));
115+insert into t1 values ("2010-01-01","a",1,1),("2010-02-02","b",2,2),("2010-03-03","c",3,3);
116+replace into t1 values ("2010-01-01","b",3,4);
117+select * from t1 order by a;
118+a b c d
119+2010-01-01 b 3 4
120+drop table t1;
121+create table t1 (a DOUBLE NOT NULL PRIMARY KEY, b char(1), c INT, d INT, unique key(b), unique key(c));
122+insert into t1 values (1.1,"a",1,1),(22.22,"b",2,2),(333.333,"c",3,3);
123+replace into t1 values (1.1,"b",3,4);
124+select * from t1 order by a;
125+a b c d
126+1.1 b 3 4
127+drop table t1;
128+create table t1 (a FLOAT NOT NULL PRIMARY KEY, b char(1), c INT, d INT, unique key(b), unique key(c));
129+insert into t1 values (1.1,"a",1,1),(22.22,"b",2,2),(333.333,"c",3,3);
130+replace into t1 values (1.1,"b",3,4);
131+select * from t1 order by a;
132+a b c d
133+1.1 b 3 4
134+drop table t1;
135+create table t1 (a ENUM("a","bb","ccc") NOT NULL PRIMARY KEY, b char(1), c INT, d INT, unique key(b), unique key(c));
136+insert into t1 values ("a","a",1,1),("bb","b",2,2),("ccc","c",3,3);
137+replace into t1 values ("a","b",3,4);
138+select * from t1 order by a;
139+a b c d
140+a b 3 4
141+drop table t1;
142 START TRANSACTION;
143 DROP TABLE IF EXISTS `test`.`t1`;
144 COMMIT;
145@@ -63,4 +105,100 @@
146 START TRANSACTION;
147 DROP TABLE `test`.`t1`;
148 COMMIT;
149+START TRANSACTION;
150+CREATE TABLE `test`.`t1` ( `a` int NOT NULL AUTO_INCREMENT, `b` varchar(1) DEFAULT NULL, `c` int DEFAULT NULL, `d` int DEFAULT NULL, PRIMARY KEY (`a`), UNIQUE KEY `b` (`b`), UNIQUE KEY `c` (`c`) ) ENGINE=InnoDB COLLATE = utf8_general_ci;
151+COMMIT;
152+START TRANSACTION;
153+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES (1,'a',1,1);
154+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES (2,'b',2,2);
155+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES (3,'c',3,3);
156+COMMIT;
157+START TRANSACTION;
158+DELETE FROM `test`.`t1` WHERE `a`=1;
159+DELETE FROM `test`.`t1` WHERE `a`=2;
160+UPDATE `test`.`t1` SET `a`=1,`b`='b',`d`=4 WHERE `a`=3;
161+COMMIT;
162+START TRANSACTION;
163+DROP TABLE `test`.`t1`;
164+COMMIT;
165+START TRANSACTION;
166+CREATE TABLE `test`.`t1` ( `a` varchar(5) NOT NULL, `b` varchar(1) DEFAULT NULL, `c` int DEFAULT NULL, `d` int DEFAULT NULL, PRIMARY KEY (`a`), UNIQUE KEY `b` (`b`), UNIQUE KEY `c` (`c`) ) ENGINE=InnoDB COLLATE = utf8_general_ci;
167+COMMIT;
168+START TRANSACTION;
169+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES ('a','a',1,1);
170+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES ('bb','b',2,2);
171+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES ('ccc','c',3,3);
172+COMMIT;
173+START TRANSACTION;
174+DELETE FROM `test`.`t1` WHERE `a`='a';
175+DELETE FROM `test`.`t1` WHERE `a`='bb';
176+UPDATE `test`.`t1` SET `a`='a',`b`='b',`d`=4 WHERE `a`='ccc';
177+COMMIT;
178+START TRANSACTION;
179+DROP TABLE `test`.`t1`;
180+COMMIT;
181+START TRANSACTION;
182+CREATE TABLE `test`.`t1` ( `a` date NOT NULL, `b` varchar(1) DEFAULT NULL, `c` int DEFAULT NULL, `d` int DEFAULT NULL, PRIMARY KEY (`a`), UNIQUE KEY `b` (`b`), UNIQUE KEY `c` (`c`) ) ENGINE=InnoDB COLLATE = utf8_general_ci;
183+COMMIT;
184+START TRANSACTION;
185+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES ('2010-01-01','a',1,1);
186+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES ('2010-02-02','b',2,2);
187+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES ('2010-03-03','c',3,3);
188+COMMIT;
189+START TRANSACTION;
190+DELETE FROM `test`.`t1` WHERE `a`='2010-01-01';
191+DELETE FROM `test`.`t1` WHERE `a`='2010-02-02';
192+UPDATE `test`.`t1` SET `a`='2010-01-01',`b`='b',`d`=4 WHERE `a`='2010-03-03';
193+COMMIT;
194+START TRANSACTION;
195+DROP TABLE `test`.`t1`;
196+COMMIT;
197+START TRANSACTION;
198+CREATE TABLE `test`.`t1` ( `a` double NOT NULL, `b` varchar(1) DEFAULT NULL, `c` int DEFAULT NULL, `d` int DEFAULT NULL, PRIMARY KEY (`a`), UNIQUE KEY `b` (`b`), UNIQUE KEY `c` (`c`) ) ENGINE=InnoDB COLLATE = utf8_general_ci;
199+COMMIT;
200+START TRANSACTION;
201+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES (1.1,'a',1,1);
202+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES (22.22,'b',2,2);
203+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES (333.333,'c',3,3);
204+COMMIT;
205+START TRANSACTION;
206+DELETE FROM `test`.`t1` WHERE `a`=1.1;
207+DELETE FROM `test`.`t1` WHERE `a`=22.22;
208+UPDATE `test`.`t1` SET `a`=1.1,`b`='b',`d`=4 WHERE `a`=333.333;
209+COMMIT;
210+START TRANSACTION;
211+DROP TABLE `test`.`t1`;
212+COMMIT;
213+START TRANSACTION;
214+CREATE TABLE `test`.`t1` ( `a` double NOT NULL, `b` varchar(1) DEFAULT NULL, `c` int DEFAULT NULL, `d` int DEFAULT NULL, PRIMARY KEY (`a`), UNIQUE KEY `b` (`b`), UNIQUE KEY `c` (`c`) ) ENGINE=InnoDB COLLATE = utf8_general_ci;
215+COMMIT;
216+START TRANSACTION;
217+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES (1.1,'a',1,1);
218+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES (22.22,'b',2,2);
219+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES (333.333,'c',3,3);
220+COMMIT;
221+START TRANSACTION;
222+DELETE FROM `test`.`t1` WHERE `a`=1.1;
223+DELETE FROM `test`.`t1` WHERE `a`=22.22;
224+UPDATE `test`.`t1` SET `a`=1.1,`b`='b',`d`=4 WHERE `a`=333.333;
225+COMMIT;
226+START TRANSACTION;
227+DROP TABLE `test`.`t1`;
228+COMMIT;
229+START TRANSACTION;
230+CREATE TABLE `test`.`t1` ( `a` enum('a','bb','ccc') NOT NULL, `b` varchar(1) DEFAULT NULL, `c` int DEFAULT NULL, `d` int DEFAULT NULL, PRIMARY KEY (`a`), UNIQUE KEY `b` (`b`), UNIQUE KEY `c` (`c`) ) ENGINE=InnoDB COLLATE = utf8_general_ci;
231+COMMIT;
232+START TRANSACTION;
233+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES (a,'a',1,1);
234+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES (bb,'b',2,2);
235+INSERT INTO `test`.`t1` (`a`,`b`,`c`,`d`) VALUES (ccc,'c',3,3);
236+COMMIT;
237+START TRANSACTION;
238+DELETE FROM `test`.`t1` WHERE `a`=a;
239+DELETE FROM `test`.`t1` WHERE `a`=bb;
240+UPDATE `test`.`t1` SET `a`=a,`b`='b',`d`=4 WHERE `a`=ccc;
241+COMMIT;
242+START TRANSACTION;
243+DROP TABLE `test`.`t1`;
244+COMMIT;
245 SET GLOBAL transaction_log_truncate_debug= true;
246
247=== modified file 'plugin/transaction_log/tests/t/replace.inc'
248--- plugin/transaction_log/tests/t/replace.inc 2009-10-23 16:30:06 +0000
249+++ plugin/transaction_log/tests/t/replace.inc 2010-08-26 18:28:46 +0000
250@@ -45,3 +45,47 @@
251 REPLACE INTO t1 VALUE (2, "I love testing.");
252
253 DROP TABLE t2, t1;
254+
255+#
256+# Test when using replace on table with multiple unique keys
257+# and a primary key of various data types. This tests code
258+# that, during a REPLACE, deletes multiple conflicting rows.
259+# (FYI, In the transaction log, there should be 2 DELETEs
260+# and 1 UPDATE for each of these REPLACE statements.)
261+#
262+
263+create table t1 (a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, b char(1), c INT, d INT, unique key(b), unique key(c));
264+insert into t1 values (1,"a",1,1),(2,"b",2,2),(3,"c",3,3);
265+replace into t1 values (1,"b",3,4);
266+select * from t1 order by a;
267+drop table t1;
268+
269+create table t1 (a CHAR(5) NOT NULL PRIMARY KEY, b char(1), c INT, d INT, unique key(b), unique key(c));
270+insert into t1 values ("a","a",1,1),("bb","b",2,2),("ccc","c",3,3);
271+replace into t1 values ("a","b",3,4);
272+select * from t1 order by a;
273+drop table t1;
274+
275+create table t1 (a DATE NOT NULL PRIMARY KEY, b char(1), c INT, d INT, unique key(b), unique key(c));
276+insert into t1 values ("2010-01-01","a",1,1),("2010-02-02","b",2,2),("2010-03-03","c",3,3);
277+replace into t1 values ("2010-01-01","b",3,4);
278+select * from t1 order by a;
279+drop table t1;
280+
281+create table t1 (a DOUBLE NOT NULL PRIMARY KEY, b char(1), c INT, d INT, unique key(b), unique key(c));
282+insert into t1 values (1.1,"a",1,1),(22.22,"b",2,2),(333.333,"c",3,3);
283+replace into t1 values (1.1,"b",3,4);
284+select * from t1 order by a;
285+drop table t1;
286+
287+create table t1 (a FLOAT NOT NULL PRIMARY KEY, b char(1), c INT, d INT, unique key(b), unique key(c));
288+insert into t1 values (1.1,"a",1,1),(22.22,"b",2,2),(333.333,"c",3,3);
289+replace into t1 values (1.1,"b",3,4);
290+select * from t1 order by a;
291+drop table t1;
292+
293+create table t1 (a ENUM("a","bb","ccc") NOT NULL PRIMARY KEY, b char(1), c INT, d INT, unique key(b), unique key(c));
294+insert into t1 values ("a","a",1,1),("bb","b",2,2),("ccc","c",3,3);
295+replace into t1 values ("a","b",3,4);
296+select * from t1 order by a;
297+drop table t1;