]>
Commit | Line | Data |
---|---|---|
11822e22 AM |
1 | # name : innodb_expand_fast_index_creation.patch |
2 | # maintainer : Alexey | |
3 | # | |
4 | # Expands the applicability of InnoDB fast index creation to mysqldump, | |
5 | # ALTER TABLE and OPTIMIZE TABLE. | |
6 | # | |
adf0fb13 | 7 | # |
db82db79 AM |
8 | --- a/client/client_priv.h |
9 | +++ b/client/client_priv.h | |
10 | @@ -92,6 +92,7 @@ | |
11822e22 AM |
11 | OPT_NO_REMOVE_EOL_CARRET, |
12 | OPT_DEFAULT_AUTH, | |
13 | OPT_DEFAULT_PLUGIN, | |
14 | + OPT_INNODB_OPTIMIZE_KEYS, | |
15 | OPT_MAX_CLIENT_OPTION | |
16 | }; | |
17 | ||
db82db79 AM |
18 | --- a/client/mysqldump.c |
19 | +++ b/client/mysqldump.c | |
20 | @@ -47,6 +47,7 @@ | |
11822e22 AM |
21 | #include <m_ctype.h> |
22 | #include <hash.h> | |
23 | #include <stdarg.h> | |
24 | +#include <my_list.h> | |
25 | ||
26 | #include "client_priv.h" | |
27 | #include "mysql.h" | |
db82db79 | 28 | @@ -143,6 +144,8 @@ |
11822e22 AM |
29 | |
30 | static my_bool server_supports_sql_no_fcache= FALSE; | |
31 | ||
32 | +static my_bool opt_innodb_optimize_keys= FALSE; | |
33 | + | |
34 | /* | |
35 | Dynamic_string wrapper functions. In this file use these | |
36 | wrappers, they will terminate the process if there is | |
db82db79 | 37 | @@ -188,6 +191,8 @@ |
11822e22 AM |
38 | |
39 | HASH ignore_table; | |
40 | ||
41 | +LIST *skipped_keys_list; | |
42 | + | |
43 | static struct my_option my_long_options[] = | |
44 | { | |
45 | {"all-databases", 'A', | |
db82db79 | 46 | @@ -351,6 +356,11 @@ |
11822e22 AM |
47 | "in dump produced with --dump-slave.", &opt_include_master_host_port, |
48 | &opt_include_master_host_port, 0, GET_BOOL, NO_ARG, | |
49 | 0, 0, 0, 0, 0, 0}, | |
50 | + {"innodb-optimize-keys", OPT_INNODB_OPTIMIZE_KEYS, | |
51 | + "Use InnoDB fast index creation by creating secondary indexes after " | |
52 | + "dumping the data.", | |
53 | + &opt_innodb_optimize_keys, &opt_innodb_optimize_keys, 0, GET_BOOL, NO_ARG, | |
54 | + 0, 0, 0, 0, 0, 0}, | |
55 | {"insert-ignore", OPT_INSERT_IGNORE, "Insert rows with INSERT IGNORE.", | |
56 | &opt_ignore, &opt_ignore, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, | |
57 | 0, 0}, | |
db82db79 | 58 | @@ -2238,6 +2248,77 @@ |
11822e22 AM |
59 | } |
60 | ||
61 | /* | |
62 | + Remove secondary/foreign key definitions from a given SHOW CREATE TABLE string | |
63 | + and store them into a temporary list to be used later. | |
64 | + | |
65 | + SYNOPSIS | |
66 | + skip_secondary_keys() | |
67 | + create_str SHOW CREATE TABLE output | |
68 | + | |
69 | + | |
70 | + DESCRIPTION | |
71 | + | |
72 | + Stores all lines starting with "KEY" or "UNIQUE KEY" or "CONSTRAINT" | |
73 | + into skipped_keys_list and removes them from the input string. | |
74 | + Ignoring FOREIGN KEYS constraints when creating the table is ok, because | |
75 | + mysqldump sets foreign_key_checks to 0 anyway. | |
76 | +*/ | |
77 | + | |
78 | +static void skip_secondary_keys(char *create_str) | |
79 | +{ | |
80 | + char *ptr, *strend; | |
81 | + char *last_comma = NULL; | |
82 | + | |
83 | + strend= create_str + strlen(create_str); | |
84 | + | |
85 | + ptr= create_str; | |
86 | + while (*ptr) | |
87 | + { | |
88 | + char *tmp, *orig_ptr; | |
89 | + | |
90 | + orig_ptr= ptr; | |
91 | + /* Skip leading whitespace */ | |
92 | + while (*ptr && my_isspace(charset_info, *ptr)) | |
93 | + ptr++; | |
94 | + | |
95 | + /* Read the next line */ | |
96 | + for (tmp= ptr; *tmp != '\n' && *tmp != '\0'; tmp++); | |
97 | + | |
98 | + /* Is it a secondary index definition? */ | |
99 | + if (*tmp == '\n' && | |
100 | + (!strncmp(ptr, "UNIQUE KEY ", sizeof("UNIQUE KEY ") - 1) || | |
101 | + !strncmp(ptr, "KEY ", sizeof("KEY ") - 1) || | |
102 | + !strncmp(ptr, "CONSTRAINT ", sizeof("CONSTRAINT ") - 1))) | |
103 | + { | |
104 | + char *data, *end= tmp - 1; | |
105 | + | |
106 | + /* Remove the trailing comma */ | |
107 | + if (*end == ',') | |
108 | + end--; | |
109 | + data= my_strndup(ptr, end - ptr + 1, MYF(MY_FAE)); | |
110 | + skipped_keys_list= list_cons(data, skipped_keys_list); | |
111 | + | |
112 | + memmove(orig_ptr, tmp + 1, strend - tmp); | |
113 | + ptr= orig_ptr; | |
114 | + strend-= tmp + 1 - ptr; | |
115 | + | |
116 | + /* Remove the comma on the previos line */ | |
117 | + if (last_comma != NULL) | |
118 | + { | |
119 | + *last_comma= ' '; | |
120 | + last_comma = NULL; | |
121 | + } | |
122 | + } | |
123 | + else | |
124 | + { | |
125 | + if (tmp[-1] == ',') | |
126 | + last_comma= tmp - 1; | |
127 | + ptr= (*tmp == '\0') ? tmp : tmp + 1; | |
128 | + } | |
129 | + } | |
130 | +} | |
131 | + | |
132 | +/* | |
133 | get_table_structure -- retrievs database structure, prints out corresponding | |
134 | CREATE statement and fills out insert_pat if the table is the type we will | |
135 | be dumping. | |
db82db79 | 136 | @@ -2478,6 +2559,9 @@ |
11822e22 AM |
137 | |
138 | row= mysql_fetch_row(result); | |
139 | ||
140 | + if (opt_innodb_optimize_keys && !strcmp(table_type, "InnoDB")) | |
141 | + skip_secondary_keys(row[1]); | |
142 | + | |
143 | fprintf(sql_file, (opt_compatible_mode & 3) ? "%s;\n" : | |
144 | "/*!40101 SET @saved_cs_client = @@character_set_client */;\n" | |
145 | "/*!40101 SET character_set_client = utf8 */;\n" | |
db82db79 | 146 | @@ -3572,6 +3656,27 @@ |
11822e22 AM |
147 | goto err; |
148 | } | |
149 | ||
150 | + /* Perform delayed secondary index creation for --innodb-optimize-keys */ | |
151 | + if (skipped_keys_list) | |
152 | + { | |
153 | + uint keys; | |
154 | + skipped_keys_list= list_reverse(skipped_keys_list); | |
155 | + fprintf(md_result_file, "ALTER TABLE %s ", opt_quoted_table); | |
156 | + for (keys= list_length(skipped_keys_list); keys > 0; keys--) | |
157 | + { | |
158 | + LIST *node= skipped_keys_list; | |
159 | + char *def= node->data; | |
160 | + | |
161 | + fprintf(md_result_file, "ADD %s%s", def, (keys > 1) ? ", " : ";\n"); | |
162 | + | |
163 | + skipped_keys_list= list_delete(skipped_keys_list, node); | |
164 | + my_free(def); | |
165 | + my_free(node); | |
166 | + } | |
167 | + | |
168 | + DBUG_ASSERT(skipped_keys_list == NULL); | |
169 | + } | |
170 | + | |
171 | /* Moved enable keys to before unlock per bug 15977 */ | |
172 | if (opt_disable_keys) | |
173 | { | |
db82db79 AM |
174 | --- /dev/null |
175 | +++ b/mysql-test/r/percona_mysqldump_innodb_optimize_keys.result | |
11822e22 AM |
176 | @@ -0,0 +1,109 @@ |
177 | +# | |
178 | +# Test the --innodb-optimize-keys option. | |
179 | +# | |
180 | +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT, KEY(b)) ENGINE=MyISAM; | |
181 | +###################################### | |
182 | + | |
183 | +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; | |
184 | +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; | |
185 | +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; | |
186 | +/*!40101 SET NAMES utf8 */; | |
187 | +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; | |
188 | +/*!40103 SET TIME_ZONE='+00:00' */; | |
189 | +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; | |
190 | +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; | |
191 | +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; | |
192 | +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; | |
193 | +DROP TABLE IF EXISTS `t1`; | |
194 | +/*!40101 SET @saved_cs_client = @@character_set_client */; | |
195 | +/*!40101 SET character_set_client = utf8 */; | |
196 | +CREATE TABLE `t1` ( | |
197 | + `a` int(11) NOT NULL, | |
198 | + `b` int(11) DEFAULT NULL, | |
199 | + PRIMARY KEY (`a`), | |
200 | + KEY `b` (`b`) | |
201 | +) ENGINE=MyISAM DEFAULT CHARSET=latin1; | |
202 | +/*!40101 SET character_set_client = @saved_cs_client */; | |
203 | + | |
204 | +LOCK TABLES `t1` WRITE; | |
205 | +/*!40000 ALTER TABLE `t1` DISABLE KEYS */; | |
206 | +/*!40000 ALTER TABLE `t1` ENABLE KEYS */; | |
207 | +UNLOCK TABLES; | |
208 | +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; | |
209 | + | |
210 | +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; | |
211 | +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; | |
212 | +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; | |
213 | +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; | |
214 | +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; | |
215 | +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; | |
216 | +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; | |
217 | + | |
218 | +###################################### | |
219 | +DROP TABLE t1; | |
220 | +CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB; | |
221 | +INSERT INTO t2 VALUES (0), (1), (2); | |
222 | +CREATE TABLE t1 ( | |
223 | +id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, | |
224 | +a INT, b VARCHAR(255), c DECIMAL(10,3), | |
225 | +KEY (b), | |
226 | +UNIQUE KEY uniq(c,a), | |
227 | +FOREIGN KEY (a) REFERENCES t2(a) ON DELETE CASCADE | |
228 | +) ENGINE=InnoDB; | |
229 | +INSERT INTO t1(a,b,c) VALUES (0, "0", 0.0), (1, "1", 1.1), (2, "2", 2.2); | |
230 | +###################################### | |
231 | + | |
232 | +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; | |
233 | +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; | |
234 | +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; | |
235 | +/*!40101 SET NAMES utf8 */; | |
236 | +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; | |
237 | +/*!40103 SET TIME_ZONE='+00:00' */; | |
238 | +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; | |
239 | +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; | |
240 | +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; | |
241 | +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; | |
242 | +DROP TABLE IF EXISTS `t1`; | |
243 | +/*!40101 SET @saved_cs_client = @@character_set_client */; | |
244 | +/*!40101 SET character_set_client = utf8 */; | |
245 | +CREATE TABLE `t1` ( | |
246 | + `id` int(11) NOT NULL AUTO_INCREMENT, | |
247 | + `a` int(11) DEFAULT NULL, | |
248 | + `b` varchar(255) DEFAULT NULL, | |
249 | + `c` decimal(10,3) DEFAULT NULL, | |
250 | + PRIMARY KEY (`id`) | |
251 | +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; | |
252 | +/*!40101 SET character_set_client = @saved_cs_client */; | |
253 | + | |
254 | +LOCK TABLES `t1` WRITE; | |
255 | +/*!40000 ALTER TABLE `t1` DISABLE KEYS */; | |
256 | +INSERT INTO `t1` VALUES (1,0,'0',0.000),(2,1,'1',1.100),(3,2,'2',2.200); | |
257 | +ALTER TABLE `t1` ADD UNIQUE KEY `uniq` (`c`,`a`), ADD KEY `b` (`b`), ADD KEY `a` (`a`), ADD CONSTRAINT `t1_ibfk_1` FOREIGN KEY (`a`) REFERENCES `t2` (`a`) ON DELETE CASCADE; | |
258 | +/*!40000 ALTER TABLE `t1` ENABLE KEYS */; | |
259 | +UNLOCK TABLES; | |
260 | +DROP TABLE IF EXISTS `t2`; | |
261 | +/*!40101 SET @saved_cs_client = @@character_set_client */; | |
262 | +/*!40101 SET character_set_client = utf8 */; | |
263 | +CREATE TABLE `t2` ( | |
264 | + `a` int(11) NOT NULL, | |
265 | + PRIMARY KEY (`a`) | |
266 | +) ENGINE=InnoDB DEFAULT CHARSET=latin1; | |
267 | +/*!40101 SET character_set_client = @saved_cs_client */; | |
268 | + | |
269 | +LOCK TABLES `t2` WRITE; | |
270 | +/*!40000 ALTER TABLE `t2` DISABLE KEYS */; | |
271 | +INSERT INTO `t2` VALUES (0),(1),(2); | |
272 | +/*!40000 ALTER TABLE `t2` ENABLE KEYS */; | |
273 | +UNLOCK TABLES; | |
274 | +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; | |
275 | + | |
276 | +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; | |
277 | +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; | |
278 | +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; | |
279 | +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; | |
280 | +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; | |
281 | +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; | |
282 | +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; | |
283 | + | |
284 | +###################################### | |
285 | +DROP TABLE t1, t2; | |
db82db79 AM |
286 | --- a/mysql-test/suite/innodb/r/innodb.result |
287 | +++ b/mysql-test/suite/innodb/r/innodb.result | |
11822e22 AM |
288 | @@ -1673,7 +1673,7 @@ |
289 | 71 | |
290 | SELECT variable_value - @innodb_rows_inserted_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_rows_inserted'; | |
291 | variable_value - @innodb_rows_inserted_orig | |
292 | -1066 | |
293 | +1108 | |
294 | SELECT variable_value - @innodb_rows_updated_orig FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_rows_updated'; | |
295 | variable_value - @innodb_rows_updated_orig | |
296 | 866 | |
db82db79 AM |
297 | --- a/mysql-test/suite/innodb/t/innodb-index.test |
298 | +++ b/mysql-test/suite/innodb/t/innodb-index.test | |
299 | @@ -87,6 +87,11 @@ | |
11822e22 AM |
300 | show create table t1; |
301 | --error ER_MULTIPLE_PRI_KEY | |
302 | alter table t1 add primary key (c); | |
303 | +# Suppress the error log messages occuring on duplicate key error | |
304 | +# during ALTER TABLE when using fast index creation | |
305 | +--disable_query_log | |
306 | +call mtr.add_suppression("Cannot find index PRIMARY in InnoDB index translation table."); | |
307 | +--enable_query_log | |
308 | --error ER_DUP_ENTRY | |
309 | alter table t1 drop primary key, add primary key (b); | |
310 | create unique index c on t1 (c); | |
db82db79 AM |
311 | --- a/mysql-test/suite/innodb/t/innodb.test |
312 | +++ b/mysql-test/suite/innodb/t/innodb.test | |
11822e22 AM |
313 | @@ -21,6 +21,12 @@ |
314 | ||
315 | -- source include/have_innodb.inc | |
316 | ||
317 | +# Suppress the error log message occuring on duplicate key error | |
318 | +# during ALTER TABLE when using fast index creation | |
319 | +--disable_query_log | |
320 | +call mtr.add_suppression("Cannot find index v_2 in InnoDB index translation table."); | |
321 | +--enable_query_log | |
322 | + | |
323 | let $MYSQLD_DATADIR= `select @@datadir`; | |
324 | ||
325 | # Save the original values of some variables in order to be able to | |
db82db79 AM |
326 | --- /dev/null |
327 | +++ b/mysql-test/t/percona_mysqldump_innodb_optimize_keys.test | |
11822e22 AM |
328 | @@ -0,0 +1,62 @@ |
329 | +# Embedded server doesn't support external clients | |
330 | +--source include/not_embedded.inc | |
331 | + | |
332 | +# Fast index creation is only available in InnoDB plugin | |
333 | +--source include/have_innodb.inc | |
334 | + | |
335 | +# Save the initial number of concurrent sessions | |
336 | +--source include/count_sessions.inc | |
337 | + | |
338 | +--echo # | |
339 | +--echo # Test the --innodb-optimize-keys option. | |
340 | +--echo # | |
341 | + | |
342 | +--let $file=$MYSQLTEST_VARDIR/tmp/t1.sql | |
343 | + | |
344 | +# First test that the option has no effect on non-InnoDB tables | |
345 | + | |
346 | +CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY, b INT, KEY(b)) ENGINE=MyISAM; | |
347 | + | |
348 | +--exec $MYSQL_DUMP --skip-comments --innodb-optimize-keys test t1 >$file | |
349 | + | |
350 | +--echo ###################################### | |
351 | +--cat_file $file | |
352 | +--echo ###################################### | |
353 | + | |
354 | +--remove_file $file | |
355 | + | |
356 | +DROP TABLE t1; | |
357 | + | |
358 | + | |
359 | +# Check that for InnoDB tables secondary and foreign keys are created | |
360 | +# after the data is dumped | |
361 | + | |
362 | +CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB; | |
363 | +INSERT INTO t2 VALUES (0), (1), (2); | |
364 | + | |
365 | +CREATE TABLE t1 ( | |
366 | + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, | |
367 | + a INT, b VARCHAR(255), c DECIMAL(10,3), | |
368 | + KEY (b), | |
369 | + UNIQUE KEY uniq(c,a), | |
370 | + FOREIGN KEY (a) REFERENCES t2(a) ON DELETE CASCADE | |
371 | +) ENGINE=InnoDB; | |
372 | + | |
373 | +INSERT INTO t1(a,b,c) VALUES (0, "0", 0.0), (1, "1", 1.1), (2, "2", 2.2); | |
374 | + | |
375 | +--exec $MYSQL_DUMP --skip-comments --innodb-optimize-keys test t1 t2 >$file | |
376 | + | |
377 | +--echo ###################################### | |
378 | +--cat_file $file | |
379 | +--echo ###################################### | |
380 | + | |
381 | +# Check that the resulting dump can be imported back | |
382 | + | |
383 | +--exec $MYSQL test < $file | |
384 | + | |
385 | +--remove_file $file | |
386 | + | |
387 | +DROP TABLE t1, t2; | |
388 | + | |
389 | +# Wait till we reached the initial number of concurrent sessions | |
390 | +--source include/wait_until_count_sessions.inc | |
db82db79 AM |
391 | --- a/sql/sql_lex.cc |
392 | +++ b/sql/sql_lex.cc | |
11822e22 AM |
393 | @@ -1630,6 +1630,9 @@ |
394 | alter_list(rhs.alter_list, mem_root), | |
395 | key_list(rhs.key_list, mem_root), | |
396 | create_list(rhs.create_list, mem_root), | |
397 | + delayed_key_list(rhs.delayed_key_list, mem_root), | |
398 | + delayed_key_info(rhs.delayed_key_info), | |
399 | + delayed_key_count(rhs.delayed_key_count), | |
400 | flags(rhs.flags), | |
401 | keys_onoff(rhs.keys_onoff), | |
402 | tablespace_op(rhs.tablespace_op), | |
403 | @@ -1652,6 +1655,7 @@ | |
404 | list_copy_and_replace_each_value(alter_list, mem_root); | |
405 | list_copy_and_replace_each_value(key_list, mem_root); | |
406 | list_copy_and_replace_each_value(create_list, mem_root); | |
407 | + list_copy_and_replace_each_value(delayed_key_list, mem_root); | |
408 | /* partition_names are not deeply copied currently */ | |
409 | } | |
410 | ||
db82db79 AM |
411 | --- a/sql/sql_lex.h |
412 | +++ b/sql/sql_lex.h | |
413 | @@ -1009,6 +1009,9 @@ | |
11822e22 AM |
414 | List<Alter_column> alter_list; |
415 | List<Key> key_list; | |
416 | List<Create_field> create_list; | |
417 | + List<Key> delayed_key_list; | |
418 | + KEY *delayed_key_info; | |
419 | + uint delayed_key_count; | |
420 | uint flags; | |
421 | enum enum_enable_or_disable keys_onoff; | |
422 | enum tablespace_op_type tablespace_op; | |
db82db79 | 423 | @@ -1020,6 +1023,8 @@ |
11822e22 AM |
424 | |
425 | ||
426 | Alter_info() : | |
427 | + delayed_key_info(NULL), | |
428 | + delayed_key_count(0), | |
429 | flags(0), | |
430 | keys_onoff(LEAVE_AS_IS), | |
431 | tablespace_op(NO_TABLESPACE_OP), | |
db82db79 | 432 | @@ -1035,6 +1040,9 @@ |
11822e22 AM |
433 | alter_list.empty(); |
434 | key_list.empty(); | |
435 | create_list.empty(); | |
436 | + delayed_key_list.empty(); | |
437 | + delayed_key_info= NULL; | |
438 | + delayed_key_count= 0; | |
439 | flags= 0; | |
440 | keys_onoff= LEAVE_AS_IS; | |
441 | tablespace_op= NO_TABLESPACE_OP; | |
db82db79 AM |
442 | --- a/sql/sql_table.cc |
443 | +++ b/sql/sql_table.cc | |
444 | @@ -2776,7 +2776,7 @@ | |
11822e22 AM |
445 | file The handler for the new table. |
446 | key_info_buffer OUT An array of KEY structs for the indexes. | |
447 | key_count OUT The number of elements in the array. | |
448 | - select_field_count The number of fields coming from a select table. | |
449 | + select_field_count The number of fields coming from a select table. | |
450 | ||
451 | DESCRIPTION | |
452 | Prepares the table and key structures for table creation. | |
db82db79 | 453 | @@ -3122,7 +3122,6 @@ |
11822e22 AM |
454 | } |
455 | ||
456 | /* Create keys */ | |
457 | - | |
458 | List_iterator<Key> key_iterator(alter_info->key_list); | |
459 | List_iterator<Key> key_iterator2(alter_info->key_list); | |
460 | uint key_parts=0, fk_key_count=0; | |
db82db79 | 461 | @@ -3220,6 +3219,14 @@ |
11822e22 AM |
462 | if (!*key_info_buffer || ! key_part_info) |
463 | DBUG_RETURN(TRUE); // Out of memory | |
464 | ||
465 | + List_iterator<Key> delayed_key_iterator(alter_info->delayed_key_list); | |
466 | + alter_info->delayed_key_count= 0; | |
467 | + if (alter_info->delayed_key_list.elements > 0) | |
468 | + { | |
469 | + alter_info->delayed_key_info= (KEY *) sql_calloc(sizeof(KEY) * | |
470 | + (*key_count)); | |
471 | + } | |
472 | + | |
473 | key_iterator.rewind(); | |
474 | key_number=0; | |
475 | for (; (key=key_iterator++) ; key_number++) | |
db82db79 | 476 | @@ -3638,6 +3645,22 @@ |
11822e22 AM |
477 | key_info->comment.str= key->key_create_info.comment.str; |
478 | } | |
479 | ||
480 | + if (alter_info->delayed_key_list.elements > 0) | |
481 | + { | |
482 | + Key *delayed_key; | |
483 | + | |
484 | + delayed_key_iterator.rewind(); | |
485 | + while ((delayed_key= delayed_key_iterator++)) | |
486 | + { | |
487 | + if (delayed_key == key) | |
488 | + { | |
489 | + alter_info->delayed_key_info[alter_info->delayed_key_count++]= | |
490 | + *key_info; | |
491 | + break; | |
492 | + } | |
493 | + } | |
494 | + } | |
495 | + | |
496 | key_info++; | |
497 | } | |
498 | if (!unique_key && !primary_key && | |
db82db79 | 499 | @@ -5256,6 +5279,10 @@ |
11822e22 AM |
500 | List<Create_field> new_create_list; |
501 | /* New key definitions are added here */ | |
502 | List<Key> new_key_list; | |
503 | + /* List with secondary keys which should be created after copying the data */ | |
504 | + List<Key> delayed_key_list; | |
505 | + /* Foreign key list returned by handler::get_foreign_key_list() */ | |
506 | + List<FOREIGN_KEY_INFO> f_key_list; | |
507 | List_iterator<Alter_drop> drop_it(alter_info->drop_list); | |
508 | List_iterator<Create_field> def_it(alter_info->create_list); | |
509 | List_iterator<Alter_column> alter_it(alter_info->alter_list); | |
db82db79 | 510 | @@ -5268,6 +5295,7 @@ |
11822e22 AM |
511 | uint used_fields= create_info->used_fields; |
512 | KEY *key_info=table->key_info; | |
513 | bool rc= TRUE; | |
514 | + bool skip_secondary; | |
515 | ||
516 | DBUG_ENTER("mysql_prepare_alter_table"); | |
517 | ||
db82db79 | 518 | @@ -5457,7 +5485,23 @@ |
11822e22 AM |
519 | /* |
520 | Collect all keys which isn't in drop list. Add only those | |
521 | for which some fields exists. | |
522 | - */ | |
523 | + | |
524 | + We also store secondary keys in delayed_key_list to make use of | |
525 | + the InnoDB fast index creation. The following conditions must be | |
526 | + met: | |
527 | + | |
528 | + - we are going to create an InnoDB table (this is checked later when the | |
529 | + target engine is known); | |
530 | + - the key most be a non-UNIQUE one; | |
531 | + - there are no foreign keys. This can be optimized later to exclude only | |
532 | + those keys which are a part of foreign key constraints. Currently we | |
533 | + simply disable this optimization for all keys if there are any foreign | |
534 | + key constraints in the table. | |
535 | + */ | |
536 | + | |
537 | + skip_secondary= | |
538 | + !table->file->get_foreign_key_list(thd, &f_key_list) && | |
539 | + f_key_list.elements == 0; | |
540 | ||
541 | for (uint i=0 ; i < table->s->keys ; i++,key_info++) | |
542 | { | |
db82db79 | 543 | @@ -5574,6 +5618,8 @@ |
11822e22 AM |
544 | test(key_info->flags & HA_GENERATED_KEY), |
545 | key_parts); | |
546 | new_key_list.push_back(key); | |
547 | + if (skip_secondary && key_type == Key::MULTIPLE) | |
548 | + delayed_key_list.push_back(key); | |
549 | } | |
550 | } | |
551 | { | |
db82db79 | 552 | @@ -5581,7 +5627,21 @@ |
11822e22 AM |
553 | while ((key=key_it++)) // Add new keys |
554 | { | |
555 | if (key->type != Key::FOREIGN_KEY) | |
556 | + { | |
557 | new_key_list.push_back(key); | |
558 | + if (skip_secondary && key->type == Key::MULTIPLE) | |
559 | + delayed_key_list.push_back(key); | |
560 | + } | |
561 | + else if (skip_secondary) | |
562 | + { | |
563 | + /* | |
564 | + We are adding a foreign key so disable the secondary keys | |
565 | + optimization. | |
566 | + */ | |
567 | + skip_secondary= FALSE; | |
568 | + delayed_key_list.empty(); | |
569 | + } | |
570 | + | |
571 | if (key->name.str && | |
572 | !my_strcasecmp(system_charset_info, key->name.str, primary_key_name)) | |
573 | { | |
db82db79 | 574 | @@ -5630,12 +5690,104 @@ |
11822e22 AM |
575 | rc= FALSE; |
576 | alter_info->create_list.swap(new_create_list); | |
577 | alter_info->key_list.swap(new_key_list); | |
578 | + alter_info->delayed_key_list.swap(delayed_key_list); | |
579 | err: | |
580 | DBUG_RETURN(rc); | |
581 | } | |
582 | ||
583 | ||
584 | /* | |
585 | + Temporarily remove secondary keys previously stored in | |
586 | + alter_info->delayed_key_info. | |
587 | +*/ | |
588 | +static int | |
589 | +remove_secondary_keys(THD *thd, TABLE *table, Alter_info *alter_info) | |
590 | +{ | |
591 | + uint *key_numbers; | |
592 | + uint key_counter= 0; | |
593 | + uint i; | |
594 | + int error; | |
595 | + DBUG_ENTER("remove_secondary_keys"); | |
596 | + DBUG_ASSERT(alter_info->delayed_key_count > 0); | |
597 | + | |
598 | + key_numbers= (uint *) thd->alloc(sizeof(uint) * | |
599 | + alter_info->delayed_key_count); | |
600 | + for (i= 0; i < alter_info->delayed_key_count; i++) | |
601 | + { | |
602 | + KEY *key= alter_info->delayed_key_info + i; | |
603 | + uint j; | |
604 | + | |
605 | + for (j= 0; j < table->s->keys; j++) | |
606 | + { | |
607 | + if (!strcmp(table->key_info[j].name, key->name)) | |
608 | + { | |
609 | + key_numbers[key_counter++]= j; | |
610 | + break; | |
611 | + } | |
612 | + } | |
613 | + } | |
614 | + | |
615 | + DBUG_ASSERT(key_counter == alter_info->delayed_key_count); | |
616 | + | |
617 | + if ((error= table->file->prepare_drop_index(table, key_numbers, | |
618 | + key_counter)) || | |
619 | + (error= table->file->final_drop_index(table))) | |
620 | + { | |
621 | + table->file->print_error(error, MYF(0)); | |
622 | + } | |
623 | + | |
624 | + DBUG_RETURN(error); | |
625 | +} | |
626 | + | |
627 | +/* | |
628 | + Restore secondary keys previously removed in remove_secondary_keys. | |
629 | +*/ | |
630 | + | |
631 | +static int | |
632 | +restore_secondary_keys(THD *thd, TABLE *table, Alter_info *alter_info) | |
633 | +{ | |
634 | + uint i; | |
635 | + int error; | |
636 | + DBUG_ENTER("restore_secondary_keys"); | |
637 | + DBUG_ASSERT(alter_info->delayed_key_count > 0); | |
638 | + | |
639 | + thd_proc_info(thd, "restoring secondary keys"); | |
640 | + | |
641 | + /* Fix the key parts */ | |
642 | + for (i= 0; i < alter_info->delayed_key_count; i++) | |
643 | + { | |
644 | + KEY *key = alter_info->delayed_key_info + i; | |
645 | + KEY_PART_INFO *key_part; | |
646 | + KEY_PART_INFO *part_end; | |
647 | + | |
648 | + part_end= key->key_part + key->key_parts; | |
649 | + for (key_part= key->key_part; key_part < part_end; key_part++) | |
650 | + key_part->field= table->field[key_part->fieldnr]; | |
651 | + } | |
db82db79 | 652 | + handler_add_index *add; |
11822e22 | 653 | + if ((error= table->file->add_index(table, alter_info->delayed_key_info, |
db82db79 | 654 | + alter_info->delayed_key_count, &add))) |
11822e22 AM |
655 | + { |
656 | + /* | |
657 | + Exchange the key_info for the error message. If we exchange | |
658 | + key number by key name in the message later, we need correct info. | |
659 | + */ | |
660 | + KEY *save_key_info= table->key_info; | |
661 | + table->key_info= alter_info->delayed_key_info; | |
662 | + table->file->print_error(error, MYF(0)); | |
663 | + table->key_info= save_key_info; | |
664 | + | |
665 | + DBUG_RETURN(error); | |
666 | + } | |
db82db79 AM |
667 | + if ((error= table->file->final_add_index(add, true))) |
668 | + { | |
669 | + table->file->print_error(error, MYF(0)); | |
670 | + } | |
11822e22 | 671 | + |
db82db79 | 672 | + DBUG_RETURN(error); |
11822e22 AM |
673 | +} |
674 | + | |
675 | +/* | |
676 | Alter table | |
677 | ||
678 | SYNOPSIS | |
db82db79 | 679 | @@ -6428,19 +6580,38 @@ |
11822e22 AM |
680 | */ |
681 | if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER)) | |
682 | { | |
683 | + /* | |
684 | + Check if we can temporarily remove secondary indexes from the table | |
685 | + before copying the data and recreate them later to utilize InnoDB fast | |
686 | + index creation. | |
687 | + TODO: is there a better way to check for InnoDB? | |
688 | + */ | |
689 | + bool optimize_keys= (alter_info->delayed_key_count > 0) && | |
690 | + !my_strcasecmp(system_charset_info, | |
691 | + new_table->file->table_type(), "InnoDB"); | |
692 | /* We don't want update TIMESTAMP fields during ALTER TABLE. */ | |
693 | new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; | |
694 | new_table->next_number_field=new_table->found_next_number_field; | |
695 | + | |
696 | thd_proc_info(thd, "copy to tmp table"); | |
697 | DBUG_EXECUTE_IF("abort_copy_table", { | |
698 | my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); | |
699 | goto err_new_table_cleanup; | |
700 | }); | |
701 | + | |
702 | + if (optimize_keys) | |
703 | + { | |
704 | + /* ignore the error */ | |
705 | + error= remove_secondary_keys(thd, new_table, alter_info); | |
706 | + } | |
707 | + | |
708 | error= copy_data_between_tables(table, new_table, | |
709 | alter_info->create_list, ignore, | |
710 | order_num, order, &copied, &deleted, | |
711 | alter_info->keys_onoff, | |
712 | alter_info->error_if_not_empty); | |
713 | + if (!error && optimize_keys) | |
714 | + error= restore_secondary_keys(thd, new_table, alter_info); | |
715 | } | |
716 | else | |
717 | { |