]> git.pld-linux.org Git - packages/mysql.git/blame - innodb_expand_fast_index_creation.patch
- mention innodb_file_per_table
[packages/mysql.git] / innodb_expand_fast_index_creation.patch
CommitLineData
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 {
This page took 0.105483 seconds and 4 git commands to generate.