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