]> git.pld-linux.org Git - packages/mysql.git/blob - innodb_expand_fast_index_creation.patch
- mention innodb_file_per_table
[packages/mysql.git] / innodb_expand_fast_index_creation.patch
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 #
7 #
8 --- a/client/client_priv.h
9 +++ b/client/client_priv.h
10 @@ -92,6 +92,7 @@
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  
18 --- a/client/mysqldump.c
19 +++ b/client/mysqldump.c
20 @@ -47,6 +47,7 @@
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"
28 @@ -143,6 +144,8 @@
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
37 @@ -188,6 +191,8 @@
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',
46 @@ -351,6 +356,11 @@
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},
58 @@ -2238,6 +2248,77 @@
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.
136 @@ -2478,6 +2559,9 @@
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"
146 @@ -3572,6 +3656,27 @@
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      {
174 --- /dev/null
175 +++ b/mysql-test/r/percona_mysqldump_innodb_optimize_keys.result
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;
286 --- a/mysql-test/suite/innodb/r/innodb.result
287 +++ b/mysql-test/suite/innodb/r/innodb.result
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
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 @@
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);
311 --- a/mysql-test/suite/innodb/t/innodb.test
312 +++ b/mysql-test/suite/innodb/t/innodb.test
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
326 --- /dev/null
327 +++ b/mysql-test/t/percona_mysqldump_innodb_optimize_keys.test
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
391 --- a/sql/sql_lex.cc
392 +++ b/sql/sql_lex.cc
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  
411 --- a/sql/sql_lex.h
412 +++ b/sql/sql_lex.h
413 @@ -1009,6 +1009,9 @@
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;
423 @@ -1020,6 +1023,8 @@
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),
432 @@ -1035,6 +1040,9 @@
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;
442 --- a/sql/sql_table.cc
443 +++ b/sql/sql_table.cc
444 @@ -2776,7 +2776,7 @@
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.
453 @@ -3122,7 +3122,6 @@
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;
461 @@ -3220,6 +3219,14 @@
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++)
476 @@ -3638,6 +3645,22 @@
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 &&
499 @@ -5256,6 +5279,10 @@
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);
510 @@ -5268,6 +5295,7 @@
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  
518 @@ -5457,7 +5485,23 @@
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    {
543 @@ -5574,6 +5618,8 @@
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    {
552 @@ -5581,7 +5627,21 @@
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        {
574 @@ -5630,12 +5690,104 @@
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 +  }
652 +  handler_add_index *add;
653 +  if ((error= table->file->add_index(table, alter_info->delayed_key_info,
654 +                                     alter_info->delayed_key_count, &add)))
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 +  }
667 +  if ((error= table->file->final_add_index(add, true)))
668 +  {
669 +    table->file->print_error(error, MYF(0));
670 +  }
671 +
672 +  DBUG_RETURN(error);
673 +}
674 +
675 +/*
676    Alter table
677  
678    SYNOPSIS
679 @@ -6428,19 +6580,38 @@
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.089922 seconds and 3 git commands to generate.