~ubuntu-branches/ubuntu/saucy/drizzle/saucy-proposed

« back to all changes in this revision

Viewing changes to plugin/innobase/handler/handler0alter.cc

  • Committer: Bazaar Package Importer
  • Author(s): Monty Taylor
  • Date: 2010-03-18 12:12:31 UTC
  • Revision ID: james.westby@ubuntu.com-20100318121231-k6g1xe6cshbwa0f8
Tags: upstream-2010.03.1347
ImportĀ upstreamĀ versionĀ 2010.03.1347

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*****************************************************************************
 
2
 
 
3
Copyright (c) 2005, 2009, Innobase Oy. All Rights Reserved.
 
4
 
 
5
This program is free software; you can redistribute it and/or modify it under
 
6
the terms of the GNU General Public License as published by the Free Software
 
7
Foundation; version 2 of the License.
 
8
 
 
9
This program is distributed in the hope that it will be useful, but WITHOUT
 
10
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 
11
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 
12
 
 
13
You should have received a copy of the GNU General Public License along with
 
14
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 
15
Place, Suite 330, Boston, MA 02111-1307 USA
 
16
 
 
17
*****************************************************************************/
 
18
 
 
19
/**************************************************//**
 
20
@file handler/handler0alter.cc
 
21
Smart ALTER TABLE
 
22
*******************************************************/
 
23
 
 
24
#include "config.h"
 
25
#include <drizzled/error.h>
 
26
#include "drizzled/charset_info.h"
 
27
#include <drizzled/field.h>
 
28
#include <drizzled/table.h>
 
29
#include <drizzled/field/varstring.h>
 
30
#include "drizzled/internal/my_sys.h"
 
31
 
 
32
extern "C" {
 
33
#include "log0log.h"
 
34
#include "row0merge.h"
 
35
#include "srv0srv.h"
 
36
#include "trx0trx.h"
 
37
#include "trx0roll.h"
 
38
#include "ha_prototypes.h"
 
39
#include "handler0alter.h"
 
40
}
 
41
 
 
42
#include "ha_innodb.h"
 
43
#include "handler0vars.h"
 
44
 
 
45
/*************************************************************//**
 
46
Copies an InnoDB column to a MySQL field.  This function is
 
47
adapted from row_sel_field_store_in_mysql_format(). */
 
48
static
 
49
void
 
50
innobase_col_to_mysql(
 
51
/*==================*/
 
52
        const dict_col_t*       col,    /*!< in: InnoDB column */
 
53
        const unsigned char*    data,   /*!< in: InnoDB column data */
 
54
        ulint                   len,    /*!< in: length of data, in bytes */
 
55
        Field*                  field)  /*!< in/out: MySQL field */
 
56
{
 
57
        unsigned char*  ptr;
 
58
        unsigned char*  dest    = field->ptr;
 
59
        ulint   flen    = field->pack_length();
 
60
 
 
61
        switch (col->mtype) {
 
62
        case DATA_INT:
 
63
                ut_ad(len == flen);
 
64
 
 
65
                /* Convert integer data from Innobase to little-endian
 
66
                format, sign bit restored to normal */
 
67
 
 
68
                for (ptr = dest + len; ptr != dest; ) {
 
69
                        *--ptr = *data++;
 
70
                }
 
71
 
 
72
                if (!(field->flags & UNSIGNED_FLAG)) {
 
73
                        ((byte*) dest)[len - 1] ^= 0x80;
 
74
                }
 
75
 
 
76
                break;
 
77
 
 
78
        case DATA_VARCHAR:
 
79
        case DATA_VARMYSQL:
 
80
        case DATA_BINARY:
 
81
                field->reset();
 
82
 
 
83
                if (field->type() == DRIZZLE_TYPE_VARCHAR) {
 
84
                        /* This is a >= 5.0.3 type true VARCHAR. Store the
 
85
                        length of the data to the first byte or the first
 
86
                        two bytes of dest. */
 
87
 
 
88
                        dest = row_mysql_store_true_var_len(
 
89
                                dest, len, flen - field->key_length());
 
90
                }
 
91
 
 
92
                /* Copy the actual data */
 
93
                memcpy(dest, data, len);
 
94
                break;
 
95
 
 
96
        case DATA_BLOB:
 
97
                /* Store a pointer to the BLOB buffer to dest: the BLOB was
 
98
                already copied to the buffer in row_sel_store_mysql_rec */
 
99
 
 
100
                row_mysql_store_blob_ref(dest, flen, data, len);
 
101
                break;
 
102
 
 
103
#ifdef UNIV_DEBUG
 
104
        case DATA_MYSQL:
 
105
                ut_ad(flen >= len);
 
106
                ut_ad(col->mbmaxlen >= col->mbminlen);
 
107
                ut_ad(col->mbmaxlen > col->mbminlen || flen == len);
 
108
                memcpy(dest, data, len);
 
109
                break;
 
110
 
 
111
        default:
 
112
        case DATA_SYS_CHILD:
 
113
        case DATA_SYS:
 
114
                /* These column types should never be shipped to MySQL. */
 
115
                ut_ad(0);
 
116
 
 
117
        case DATA_CHAR:
 
118
        case DATA_FIXBINARY:
 
119
        case DATA_FLOAT:
 
120
        case DATA_DOUBLE:
 
121
        case DATA_DECIMAL:
 
122
                /* Above are the valid column types for MySQL data. */
 
123
                ut_ad(flen == len);
 
124
#else /* UNIV_DEBUG */
 
125
        default:
 
126
#endif /* UNIV_DEBUG */
 
127
                memcpy(dest, data, len);
 
128
        }
 
129
}
 
130
 
 
131
/*************************************************************//**
 
132
Copies an InnoDB record to table->record[0]. */
 
133
extern "C" UNIV_INTERN
 
134
void
 
135
innobase_rec_to_mysql(
 
136
/*==================*/
 
137
        Table*                  table,          /*!< in/out: MySQL table */
 
138
        const rec_t*            rec,            /*!< in: record */
 
139
        const dict_index_t*     index,          /*!< in: index */
 
140
        const ulint*            offsets)        /*!< in: rec_get_offsets(
 
141
                                                rec, index, ...) */
 
142
{
 
143
        uint    n_fields        = table->s->fields;
 
144
        uint    i;
 
145
 
 
146
        ut_ad(n_fields == dict_table_get_n_user_cols(index->table));
 
147
 
 
148
        for (i = 0; i < n_fields; i++) {
 
149
                Field*          field   = table->field[i];
 
150
                ulint           ipos;
 
151
                ulint           ilen;
 
152
                const unsigned char*    ifield;
 
153
 
 
154
                field->reset();
 
155
 
 
156
                ipos = dict_index_get_nth_col_pos(index, i);
 
157
 
 
158
                if (UNIV_UNLIKELY(ipos == ULINT_UNDEFINED)) {
 
159
null_field:
 
160
                        field->set_null();
 
161
                        continue;
 
162
                }
 
163
 
 
164
                ifield = rec_get_nth_field(rec, offsets, ipos, &ilen);
 
165
 
 
166
                /* Assign the NULL flag */
 
167
                if (ilen == UNIV_SQL_NULL) {
 
168
                        ut_ad(field->real_maybe_null());
 
169
                        goto null_field;
 
170
                }
 
171
 
 
172
                field->set_notnull();
 
173
 
 
174
                innobase_col_to_mysql(
 
175
                        dict_field_get_col(
 
176
                                dict_index_get_nth_field(index, ipos)),
 
177
                        ifield, ilen, field);
 
178
        }
 
179
}
 
180
 
 
181
/*************************************************************//**
 
182
Resets table->record[0]. */
 
183
extern "C" UNIV_INTERN
 
184
void
 
185
innobase_rec_reset(
 
186
/*===============*/
 
187
        Table*                  table)          /*!< in/out: MySQL table */
 
188
{
 
189
        uint    n_fields        = table->s->fields;
 
190
        uint    i;
 
191
 
 
192
        for (i = 0; i < n_fields; i++) {
 
193
                table->field[i]->set_default();
 
194
        }
 
195
}
 
196
 
 
197
/******************************************************************//**
 
198
Removes the filename encoding of a database and table name. */
 
199
static
 
200
void
 
201
innobase_convert_tablename(
 
202
/*=======================*/
 
203
        char*   s)      /*!< in: identifier; out: decoded identifier */
 
204
{
 
205
 
 
206
        char*   slash = strchr(s, '/');
 
207
 
 
208
        if (slash) {
 
209
                char*   t;
 
210
                /* Temporarily replace the '/' with NUL. */
 
211
                *slash = 0;
 
212
                strncpy(s, s, slash - s + 1);
 
213
 
 
214
                t = s + strlen(s);
 
215
                ut_ad(slash >= t);
 
216
                /* Append a  '.' after the database name. */
 
217
                *t++ = '.';
 
218
                slash++;
 
219
                /* Convert the table name. */
 
220
                strncpy(t, slash, slash - t + strlen(slash));
 
221
        }
 
222
}
 
223
 
 
224
/*******************************************************************//**
 
225
This function checks that index keys are sensible.
 
226
@return 0 or error number */
 
227
static
 
228
int
 
229
innobase_check_index_keys(
 
230
/*======================*/
 
231
        const KEY*      key_info,       /*!< in: Indexes to be created */
 
232
        ulint           num_of_keys)    /*!< in: Number of indexes to
 
233
                                        be created */
 
234
{
 
235
        ulint           key_num;
 
236
 
 
237
        ut_ad(key_info);
 
238
        ut_ad(num_of_keys);
 
239
 
 
240
        for (key_num = 0; key_num < num_of_keys; key_num++) {
 
241
                const KEY&      key = key_info[key_num];
 
242
 
 
243
                /* Check that the same index name does not appear
 
244
                twice in indexes to be created. */
 
245
 
 
246
                for (ulint i = 0; i < key_num; i++) {
 
247
                        const KEY&      key2 = key_info[i];
 
248
 
 
249
                        if (0 == strcmp(key.name, key2.name)) {
 
250
                                errmsg_printf(ERRMSG_LVL_ERROR, "InnoDB: key name `%s` appears"
 
251
                                                " twice in CREATE INDEX\n",
 
252
                                                key.name);
 
253
 
 
254
                                return(ER_WRONG_NAME_FOR_INDEX);
 
255
                        }
 
256
                }
 
257
 
 
258
                /* Check that MySQL does not try to create a column
 
259
                prefix index field on an inappropriate data type and
 
260
                that the same colum does not appear twice in the index. */
 
261
 
 
262
                for (ulint i = 0; i < key.key_parts; i++) {
 
263
                        const KEY_PART_INFO&    key_part1
 
264
                                = key.key_part[i];
 
265
                        const Field*            field
 
266
                                = key_part1.field;
 
267
                        ibool                   is_unsigned;
 
268
 
 
269
                        switch (get_innobase_type_from_mysql_type(
 
270
                                        &is_unsigned, field)) {
 
271
                        default:
 
272
                                break;
 
273
                        case DATA_INT:
 
274
                        case DATA_FLOAT:
 
275
                        case DATA_DOUBLE:
 
276
                        case DATA_DECIMAL:
 
277
                                if (field->type() == DRIZZLE_TYPE_VARCHAR) {
 
278
                                        if (key_part1.length
 
279
                                            >= field->pack_length()
 
280
                                            - ((Field_varstring*) field)
 
281
                                            ->length_bytes) {
 
282
                                                break;
 
283
                                        }
 
284
                                } else {
 
285
                                        if (key_part1.length
 
286
                                            >= field->pack_length()) {
 
287
                                                break;
 
288
                                        }
 
289
                                }
 
290
 
 
291
                                errmsg_printf(ERRMSG_LVL_ERROR, "InnoDB: MySQL is trying to"
 
292
                                                " create a column prefix"
 
293
                                                " index field on an"
 
294
                                                " inappropriate data type."
 
295
                                                " column `%s`,"
 
296
                                                " index `%s`.\n",
 
297
                                                field->field_name,
 
298
                                                key.name);
 
299
                                return(ER_WRONG_KEY_COLUMN);
 
300
                        }
 
301
 
 
302
                        for (ulint j = 0; j < i; j++) {
 
303
                                const KEY_PART_INFO&    key_part2
 
304
                                        = key.key_part[j];
 
305
 
 
306
                                if (strcmp(key_part1.field->field_name,
 
307
                                           key_part2.field->field_name)) {
 
308
                                        continue;
 
309
                                }
 
310
 
 
311
                                errmsg_printf(ERRMSG_LVL_ERROR, "InnoDB: column `%s`"
 
312
                                                " is not allowed to occur"
 
313
                                                " twice in index `%s`.\n",
 
314
                                                key_part1.field->field_name,
 
315
                                                key.name);
 
316
                                return(ER_WRONG_KEY_COLUMN);
 
317
                        }
 
318
                }
 
319
        }
 
320
 
 
321
        return(0);
 
322
}
 
323
 
 
324
/*******************************************************************//**
 
325
Create index field definition for key part */
 
326
static
 
327
void
 
328
innobase_create_index_field_def(
 
329
/*============================*/
 
330
        KEY_PART_INFO*          key_part,       /*!< in: MySQL key definition */
 
331
        mem_heap_t*             heap,           /*!< in: memory heap */
 
332
        merge_index_field_t*    index_field)    /*!< out: index field
 
333
                                                definition for key_part */
 
334
{
 
335
        Field*          field;
 
336
        ibool           is_unsigned;
 
337
        ulint           col_type;
 
338
 
 
339
        ut_ad(key_part);
 
340
        ut_ad(index_field);
 
341
 
 
342
        field = key_part->field;
 
343
        ut_a(field);
 
344
 
 
345
        col_type = get_innobase_type_from_mysql_type(&is_unsigned, field);
 
346
 
 
347
        if (DATA_BLOB == col_type
 
348
            || (key_part->length < field->pack_length()
 
349
                && field->type() != DRIZZLE_TYPE_VARCHAR)
 
350
            || (field->type() == DRIZZLE_TYPE_VARCHAR
 
351
                && key_part->length < field->pack_length()
 
352
                        - ((Field_varstring*)field)->length_bytes)) {
 
353
 
 
354
                index_field->prefix_len = key_part->length;
 
355
        } else {
 
356
                index_field->prefix_len = 0;
 
357
        }
 
358
 
 
359
        index_field->field_name = mem_heap_strdup(heap, field->field_name);
 
360
 
 
361
        return;
 
362
}
 
363
 
 
364
/*******************************************************************//**
 
365
Create index definition for key */
 
366
static
 
367
void
 
368
innobase_create_index_def(
 
369
/*======================*/
 
370
        KEY*                    key,            /*!< in: key definition */
 
371
        bool                    new_primary,    /*!< in: TRUE=generating
 
372
                                                a new primary key
 
373
                                                on the table */
 
374
        bool                    key_primary,    /*!< in: TRUE if this key
 
375
                                                is a primary key */
 
376
        merge_index_def_t*      index,          /*!< out: index definition */
 
377
        mem_heap_t*             heap)           /*!< in: heap where memory
 
378
                                                is allocated */
 
379
{
 
380
        ulint   i;
 
381
        ulint   len;
 
382
        ulint   n_fields = key->key_parts;
 
383
        char*   index_name;
 
384
 
 
385
        index->fields = (merge_index_field_t*) mem_heap_alloc(
 
386
                heap, n_fields * sizeof *index->fields);
 
387
 
 
388
        index->ind_type = 0;
 
389
        index->n_fields = n_fields;
 
390
        len = strlen(key->name) + 1;
 
391
        index->name = index_name = (char*) mem_heap_alloc(heap,
 
392
                                                          len + !new_primary);
 
393
 
 
394
        if (UNIV_LIKELY(!new_primary)) {
 
395
                *index_name++ = TEMP_INDEX_PREFIX;
 
396
        }
 
397
 
 
398
        memcpy(index_name, key->name, len);
 
399
 
 
400
        if (key->flags & HA_NOSAME) {
 
401
                index->ind_type |= DICT_UNIQUE;
 
402
        }
 
403
 
 
404
        if (key_primary) {
 
405
                index->ind_type |= DICT_CLUSTERED;
 
406
        }
 
407
 
 
408
        for (i = 0; i < n_fields; i++) {
 
409
                innobase_create_index_field_def(&key->key_part[i], heap,
 
410
                                                &index->fields[i]);
 
411
        }
 
412
 
 
413
        return;
 
414
}
 
415
 
 
416
/*******************************************************************//**
 
417
Copy index field definition */
 
418
static
 
419
void
 
420
innobase_copy_index_field_def(
 
421
/*==========================*/
 
422
        const dict_field_t*     field,          /*!< in: definition to copy */
 
423
        merge_index_field_t*    index_field)    /*!< out: copied definition */
 
424
{
 
425
        assert(field != NULL);
 
426
        assert(index_field != NULL);
 
427
 
 
428
        index_field->field_name = field->name;
 
429
        index_field->prefix_len = field->prefix_len;
 
430
 
 
431
        return;
 
432
}
 
433
 
 
434
/*******************************************************************//**
 
435
Copy index definition for the index */
 
436
static
 
437
void
 
438
innobase_copy_index_def(
 
439
/*====================*/
 
440
        const dict_index_t*     index,  /*!< in: index definition to copy */
 
441
        merge_index_def_t*      new_index,/*!< out: Index definition */
 
442
        mem_heap_t*             heap)   /*!< in: heap where allocated */
 
443
{
 
444
        ulint   n_fields;
 
445
        ulint   i;
 
446
 
 
447
        /* Note that we take only those fields that user defined to be
 
448
        in the index.  In the internal representation more colums were
 
449
        added and those colums are not copied .*/
 
450
 
 
451
        n_fields = index->n_user_defined_cols;
 
452
 
 
453
        new_index->fields = (merge_index_field_t*) mem_heap_alloc(
 
454
                heap, n_fields * sizeof *new_index->fields);
 
455
 
 
456
        /* When adding a PRIMARY KEY, we may convert a previous
 
457
        clustered index to a secondary index (UNIQUE NOT NULL). */
 
458
        new_index->ind_type = index->type & ~DICT_CLUSTERED;
 
459
        new_index->n_fields = n_fields;
 
460
        new_index->name = index->name;
 
461
 
 
462
        for (i = 0; i < n_fields; i++) {
 
463
                innobase_copy_index_field_def(&index->fields[i],
 
464
                                              &new_index->fields[i]);
 
465
        }
 
466
 
 
467
        return;
 
468
}
 
469
 
 
470
/*******************************************************************//**
 
471
Create an index table where indexes are ordered as follows:
 
472
 
 
473
IF a new primary key is defined for the table THEN
 
474
 
 
475
        1) New primary key
 
476
        2) Original secondary indexes
 
477
        3) New secondary indexes
 
478
 
 
479
ELSE
 
480
 
 
481
        1) All new indexes in the order they arrive from MySQL
 
482
 
 
483
ENDIF
 
484
 
 
485
 
 
486
@return key definitions or NULL */
 
487
static
 
488
merge_index_def_t*
 
489
innobase_create_key_def(
 
490
/*====================*/
 
491
        trx_t*          trx,            /*!< in: trx */
 
492
        const dict_table_t*table,               /*!< in: table definition */
 
493
        mem_heap_t*     heap,           /*!< in: heap where space for key
 
494
                                        definitions are allocated */
 
495
        KEY*            key_info,       /*!< in: Indexes to be created */
 
496
        ulint&          n_keys)         /*!< in/out: Number of indexes to
 
497
                                        be created */
 
498
{
 
499
        ulint                   i = 0;
 
500
        merge_index_def_t*      indexdef;
 
501
        merge_index_def_t*      indexdefs;
 
502
        bool                    new_primary;
 
503
 
 
504
        indexdef = indexdefs = (merge_index_def_t*)
 
505
                mem_heap_alloc(heap, sizeof *indexdef
 
506
                               * (n_keys + UT_LIST_GET_LEN(table->indexes)));
 
507
 
 
508
        /* If there is a primary key, it is always the first index
 
509
        defined for the table. */
 
510
 
 
511
        new_primary = !my_strcasecmp(system_charset_info,
 
512
                                     key_info->name, "PRIMARY");
 
513
 
 
514
        /* If there is a UNIQUE INDEX consisting entirely of NOT NULL
 
515
        columns, MySQL will treat it as a PRIMARY KEY unless the
 
516
        table already has one. */
 
517
 
 
518
        if (!new_primary && (key_info->flags & HA_NOSAME)
 
519
            && row_table_got_default_clust_index(table)) {
 
520
                uint    key_part = key_info->key_parts;
 
521
 
 
522
                new_primary = TRUE;
 
523
 
 
524
                while (key_part--) {
 
525
                        if (key_info->key_part[key_part].null_bit == 0) {
 
526
                                new_primary = FALSE;
 
527
                                break;
 
528
                        }
 
529
                }
 
530
        }
 
531
 
 
532
        if (new_primary) {
 
533
                const dict_index_t*     index;
 
534
 
 
535
                /* Create the PRIMARY key index definition */
 
536
                innobase_create_index_def(&key_info[i++], TRUE, TRUE,
 
537
                                          indexdef++, heap);
 
538
 
 
539
                row_mysql_lock_data_dictionary(trx);
 
540
 
 
541
                index = dict_table_get_first_index(table);
 
542
 
 
543
                /* Copy the index definitions of the old table.  Skip
 
544
                the old clustered index if it is a generated clustered
 
545
                index or a PRIMARY KEY.  If the clustered index is a
 
546
                UNIQUE INDEX, it must be converted to a secondary index. */
 
547
 
 
548
                if (dict_index_get_nth_col(index, 0)->mtype == DATA_SYS
 
549
                    || !my_strcasecmp(system_charset_info,
 
550
                                      index->name, "PRIMARY")) {
 
551
                        index = dict_table_get_next_index(index);
 
552
                }
 
553
 
 
554
                while (index) {
 
555
                        innobase_copy_index_def(index, indexdef++, heap);
 
556
                        index = dict_table_get_next_index(index);
 
557
                }
 
558
 
 
559
                row_mysql_unlock_data_dictionary(trx);
 
560
        }
 
561
 
 
562
        /* Create definitions for added secondary indexes. */
 
563
 
 
564
        while (i < n_keys) {
 
565
                innobase_create_index_def(&key_info[i++], new_primary, FALSE,
 
566
                                          indexdef++, heap);
 
567
        }
 
568
 
 
569
        n_keys = indexdef - indexdefs;
 
570
 
 
571
        return(indexdefs);
 
572
}
 
573
 
 
574
/*******************************************************************//**
 
575
Create a temporary tablename using query id, thread id, and id
 
576
@return temporary tablename */
 
577
static
 
578
char*
 
579
innobase_create_temporary_tablename(
 
580
/*================================*/
 
581
        mem_heap_t*     heap,           /*!< in: memory heap */
 
582
        char            id,             /*!< in: identifier [0-9a-zA-Z] */
 
583
        const char*     table_name)     /*!< in: table name */
 
584
{
 
585
        char*                   name;
 
586
        ulint                   len;
 
587
        static const char       suffix[] = "@0023 "; /* "# " */
 
588
 
 
589
        len = strlen(table_name);
 
590
 
 
591
        name = (char*) mem_heap_alloc(heap, len + sizeof suffix);
 
592
        memcpy(name, table_name, len);
 
593
        memcpy(name + len, suffix, sizeof suffix);
 
594
        name[len + (sizeof suffix - 2)] = id;
 
595
 
 
596
        return(name);
 
597
}
 
598
 
 
599
/*******************************************************************//**
 
600
Create indexes.
 
601
@return 0 or error number */
 
602
UNIV_INTERN
 
603
int
 
604
ha_innobase::add_index(
 
605
/*===================*/
 
606
        Table*  i_table,        /*!< in: Table where indexes are created */
 
607
        KEY*    key_info,       /*!< in: Indexes to be created */
 
608
        uint    num_of_keys)    /*!< in: Number of indexes to be created */
 
609
{
 
610
        dict_index_t**  index;          /*!< Index to be created */
 
611
        dict_table_t*   innodb_table;   /*!< InnoDB table in dictionary */
 
612
        dict_table_t*   indexed_table;  /*!< Table where indexes are created */
 
613
        merge_index_def_t* index_defs;  /*!< Index definitions */
 
614
        mem_heap_t*     heap;           /*!< Heap for index definitions */
 
615
        trx_t*          trx;            /*!< Transaction */
 
616
        ulint           num_of_idx;
 
617
        ulint           num_created     = 0;
 
618
        ibool           dict_locked     = FALSE;
 
619
        ulint           new_primary;
 
620
        ulint           error;
 
621
 
 
622
        ut_a(i_table);
 
623
        ut_a(key_info);
 
624
        ut_a(num_of_keys);
 
625
 
 
626
        if (srv_created_new_raw || srv_force_recovery) {
 
627
                return(HA_ERR_WRONG_COMMAND);
 
628
        }
 
629
 
 
630
        update_session();
 
631
 
 
632
        heap = mem_heap_create(1024);
 
633
 
 
634
        /* In case MySQL calls this in the middle of a SELECT query, release
 
635
        possible adaptive hash latch to avoid deadlocks of threads. */
 
636
        trx_search_latch_release_if_reserved(prebuilt->trx);
 
637
        trx_start_if_not_started(prebuilt->trx);
 
638
 
 
639
        /* Create a background transaction for the operations on
 
640
        the data dictionary tables. */
 
641
        trx = innobase_trx_allocate(user_session);
 
642
        trx_start_if_not_started(trx);
 
643
 
 
644
        innodb_table = indexed_table
 
645
                = dict_table_get(prebuilt->table->name, FALSE);
 
646
 
 
647
        /* Check that index keys are sensible */
 
648
 
 
649
        error = innobase_check_index_keys(key_info, num_of_keys);
 
650
 
 
651
        if (UNIV_UNLIKELY(error)) {
 
652
err_exit:
 
653
                mem_heap_free(heap);
 
654
                trx_general_rollback_for_mysql(trx, FALSE, NULL);
 
655
                trx_free_for_mysql(trx);
 
656
                trx_commit_for_mysql(prebuilt->trx);
 
657
                return(error);
 
658
        }
 
659
 
 
660
        /* Create table containing all indexes to be built in this
 
661
        alter table add index so that they are in the correct order
 
662
        in the table. */
 
663
 
 
664
        num_of_idx = num_of_keys;
 
665
 
 
666
        index_defs = innobase_create_key_def(
 
667
                trx, innodb_table, heap, key_info, num_of_idx);
 
668
 
 
669
        new_primary = DICT_CLUSTERED & index_defs[0].ind_type;
 
670
 
 
671
        /* Allocate memory for dictionary index definitions */
 
672
 
 
673
        index = (dict_index_t**) mem_heap_alloc(
 
674
                heap, num_of_idx * sizeof *index);
 
675
 
 
676
        /* Flag this transaction as a dictionary operation, so that
 
677
        the data dictionary will be locked in crash recovery. */
 
678
        trx_set_dict_operation(trx, TRX_DICT_OP_INDEX);
 
679
 
 
680
        /* Acquire a lock on the table before creating any indexes. */
 
681
        error = row_merge_lock_table(prebuilt->trx, innodb_table,
 
682
                                     new_primary ? LOCK_X : LOCK_S);
 
683
 
 
684
        if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
 
685
 
 
686
                goto error_handling;
 
687
        }
 
688
 
 
689
        /* Latch the InnoDB data dictionary exclusively so that no deadlocks
 
690
        or lock waits can happen in it during an index create operation. */
 
691
 
 
692
        row_mysql_lock_data_dictionary(trx);
 
693
        dict_locked = TRUE;
 
694
 
 
695
        /* If a new primary key is defined for the table we need
 
696
        to drop the original table and rebuild all indexes. */
 
697
 
 
698
        if (UNIV_UNLIKELY(new_primary)) {
 
699
                /* This transaction should be the only one
 
700
                operating on the table. */
 
701
                ut_a(innodb_table->n_mysql_handles_opened == 1);
 
702
 
 
703
                char*   new_table_name = innobase_create_temporary_tablename(
 
704
                        heap, '1', innodb_table->name);
 
705
 
 
706
                /* Clone the table. */
 
707
                trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
 
708
                indexed_table = row_merge_create_temporary_table(
 
709
                        new_table_name, index_defs, innodb_table, trx);
 
710
 
 
711
                if (!indexed_table) {
 
712
 
 
713
                        switch (trx->error_state) {
 
714
                        case DB_TABLESPACE_ALREADY_EXISTS:
 
715
                        case DB_DUPLICATE_KEY:
 
716
                                innobase_convert_tablename(new_table_name);
 
717
                                my_error(HA_ERR_TABLE_EXIST, MYF(0),
 
718
                                         new_table_name);
 
719
                                error = HA_ERR_TABLE_EXIST;
 
720
                                break;
 
721
                        default:
 
722
                                error = convert_error_code_to_mysql(
 
723
                                        trx->error_state, innodb_table->flags,
 
724
                                        user_session);
 
725
                        }
 
726
 
 
727
                        row_mysql_unlock_data_dictionary(trx);
 
728
                        goto err_exit;
 
729
                }
 
730
 
 
731
                trx->table_id = indexed_table->id;
 
732
        }
 
733
 
 
734
        /* Create the indexes in SYS_INDEXES and load into dictionary. */
 
735
 
 
736
        for (ulint i = 0; i < num_of_idx; i++) {
 
737
 
 
738
                index[i] = row_merge_create_index(trx, indexed_table,
 
739
                                                  &index_defs[i]);
 
740
 
 
741
                if (!index[i]) {
 
742
                        error = trx->error_state;
 
743
                        goto error_handling;
 
744
                }
 
745
 
 
746
                num_created++;
 
747
        }
 
748
 
 
749
        ut_ad(error == DB_SUCCESS);
 
750
 
 
751
        /* Commit the data dictionary transaction in order to release
 
752
        the table locks on the system tables.  Unfortunately, this
 
753
        means that if MySQL crashes while creating a new primary key
 
754
        inside row_merge_build_indexes(), indexed_table will not be
 
755
        dropped on crash recovery.  Thus, it will become orphaned. */
 
756
        trx_commit_for_mysql(trx);
 
757
 
 
758
        row_mysql_unlock_data_dictionary(trx);
 
759
        dict_locked = FALSE;
 
760
 
 
761
        ut_a(trx->n_active_thrs == 0);
 
762
        ut_a(UT_LIST_GET_LEN(trx->signals) == 0);
 
763
 
 
764
        if (UNIV_UNLIKELY(new_primary)) {
 
765
                /* A primary key is to be built.  Acquire an exclusive
 
766
                table lock also on the table that is being created. */
 
767
                ut_ad(indexed_table != innodb_table);
 
768
 
 
769
                error = row_merge_lock_table(prebuilt->trx, indexed_table,
 
770
                                             LOCK_X);
 
771
 
 
772
                if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
 
773
 
 
774
                        goto error_handling;
 
775
                }
 
776
        }
 
777
 
 
778
        /* Read the clustered index of the table and build indexes
 
779
        based on this information using temporary files and merge sort. */
 
780
        error = row_merge_build_indexes(prebuilt->trx,
 
781
                                        innodb_table, indexed_table,
 
782
                                        index, num_of_idx, i_table);
 
783
 
 
784
error_handling:
 
785
#ifdef UNIV_DEBUG
 
786
        /* TODO: At the moment we can't handle the following statement
 
787
        in our debugging code below:
 
788
 
 
789
        alter table t drop index b, add index (b);
 
790
 
 
791
        The fix will have to parse the SQL and note that the index
 
792
        being added has the same name as the the one being dropped and
 
793
        ignore that in the dup index check.*/
 
794
        //dict_table_check_for_dup_indexes(prebuilt->table);
 
795
#endif
 
796
 
 
797
        /* After an error, remove all those index definitions from the
 
798
        dictionary which were defined. */
 
799
 
 
800
        switch (error) {
 
801
                const char*     old_name;
 
802
                char*           tmp_name;
 
803
        case DB_SUCCESS:
 
804
                ut_a(!dict_locked);
 
805
                row_mysql_lock_data_dictionary(trx);
 
806
                dict_locked = TRUE;
 
807
 
 
808
                if (!new_primary) {
 
809
                        error = row_merge_rename_indexes(trx, indexed_table);
 
810
 
 
811
                        if (error != DB_SUCCESS) {
 
812
                                row_merge_drop_indexes(trx, indexed_table,
 
813
                                                       index, num_created);
 
814
                        }
 
815
 
 
816
                        goto convert_error;
 
817
                }
 
818
 
 
819
                /* If a new primary key was defined for the table and
 
820
                there was no error at this point, we can now rename
 
821
                the old table as a temporary table, rename the new
 
822
                temporary table as the old table and drop the old table. */
 
823
                old_name = innodb_table->name;
 
824
                tmp_name = innobase_create_temporary_tablename(heap, '2',
 
825
                                                               old_name);
 
826
 
 
827
                error = row_merge_rename_tables(innodb_table, indexed_table,
 
828
                                                tmp_name, trx);
 
829
 
 
830
                if (error != DB_SUCCESS) {
 
831
 
 
832
                        row_merge_drop_table(trx, indexed_table);
 
833
 
 
834
                        switch (error) {
 
835
                        case DB_TABLESPACE_ALREADY_EXISTS:
 
836
                        case DB_DUPLICATE_KEY:
 
837
                                innobase_convert_tablename(tmp_name);
 
838
                                my_error(HA_ERR_TABLE_EXIST, MYF(0), tmp_name);
 
839
                                error = HA_ERR_TABLE_EXIST;
 
840
                                break;
 
841
                        default:
 
842
                                goto convert_error;
 
843
                        }
 
844
                        break;
 
845
                }
 
846
 
 
847
                trx_commit_for_mysql(prebuilt->trx);
 
848
                row_prebuilt_free(prebuilt, TRUE);
 
849
                prebuilt = row_create_prebuilt(indexed_table);
 
850
 
 
851
                indexed_table->n_mysql_handles_opened++;
 
852
 
 
853
                error = row_merge_drop_table(trx, innodb_table);
 
854
                goto convert_error;
 
855
 
 
856
        case DB_TOO_BIG_RECORD:
 
857
                my_error(HA_ERR_TO_BIG_ROW, MYF(0));
 
858
                goto error;
 
859
        case DB_PRIMARY_KEY_IS_NULL:
 
860
                my_error(ER_PRIMARY_CANT_HAVE_NULL, MYF(0));
 
861
                /* fall through */
 
862
        case DB_DUPLICATE_KEY:
 
863
error:
 
864
                prebuilt->trx->error_info = NULL;
 
865
                /* fall through */
 
866
        default:
 
867
                if (new_primary) {
 
868
                        row_merge_drop_table(trx, indexed_table);
 
869
                } else {
 
870
                        if (!dict_locked) {
 
871
                                row_mysql_lock_data_dictionary(trx);
 
872
                                dict_locked = TRUE;
 
873
                        }
 
874
 
 
875
                        row_merge_drop_indexes(trx, indexed_table,
 
876
                                               index, num_created);
 
877
                }
 
878
 
 
879
convert_error:
 
880
                error = convert_error_code_to_mysql(error,
 
881
                                                    innodb_table->flags,
 
882
                                                    user_session);
 
883
        }
 
884
 
 
885
        mem_heap_free(heap);
 
886
        trx_commit_for_mysql(trx);
 
887
        if (prebuilt->trx) {
 
888
                trx_commit_for_mysql(prebuilt->trx);
 
889
        }
 
890
 
 
891
        if (dict_locked) {
 
892
                row_mysql_unlock_data_dictionary(trx);
 
893
        }
 
894
 
 
895
        trx_free_for_mysql(trx);
 
896
 
 
897
        /* There might be work for utility threads.*/
 
898
        srv_active_wake_master_thread();
 
899
 
 
900
        return(error);
 
901
}
 
902
 
 
903
/*******************************************************************//**
 
904
Prepare to drop some indexes of a table.
 
905
@return 0 or error number */
 
906
UNIV_INTERN
 
907
int
 
908
ha_innobase::prepare_drop_index(
 
909
/*============================*/
 
910
        Table*  i_table,        /*!< in: Table where indexes are dropped */
 
911
        uint*   key_num,        /*!< in: Key nums to be dropped */
 
912
        uint    num_of_keys)    /*!< in: Number of keys to be dropped */
 
913
{
 
914
        trx_t*          trx;
 
915
        int             err = 0;
 
916
        uint            n_key;
 
917
 
 
918
        ut_ad(i_table);
 
919
        ut_ad(key_num);
 
920
        ut_ad(num_of_keys);
 
921
        if (srv_created_new_raw || srv_force_recovery) {
 
922
                return(HA_ERR_WRONG_COMMAND);
 
923
        }
 
924
 
 
925
        update_session();
 
926
 
 
927
        trx_search_latch_release_if_reserved(prebuilt->trx);
 
928
        trx = prebuilt->trx;
 
929
 
 
930
        /* Test and mark all the indexes to be dropped */
 
931
 
 
932
        row_mysql_lock_data_dictionary(trx);
 
933
 
 
934
        /* Check that none of the indexes have previously been flagged
 
935
        for deletion. */
 
936
        {
 
937
                const dict_index_t*     index
 
938
                        = dict_table_get_first_index(prebuilt->table);
 
939
                do {
 
940
                        ut_a(!index->to_be_dropped);
 
941
                        index = dict_table_get_next_index(index);
 
942
                } while (index);
 
943
        }
 
944
 
 
945
        for (n_key = 0; n_key < num_of_keys; n_key++) {
 
946
                const KEY*      key;
 
947
                dict_index_t*   index;
 
948
 
 
949
                key = i_table->key_info + key_num[n_key];
 
950
                index = dict_table_get_index_on_name_and_min_id(
 
951
                        prebuilt->table, key->name);
 
952
 
 
953
                if (!index) {
 
954
                        errmsg_printf(ERRMSG_LVL_ERROR, "InnoDB could not find key n:o %u "
 
955
                                        "with name %s for table %s",
 
956
                                        key_num[n_key],
 
957
                                        key ? key->name : "NULL",
 
958
                                        prebuilt->table->name);
 
959
 
 
960
                        err = HA_ERR_KEY_NOT_FOUND;
 
961
                        goto func_exit;
 
962
                }
 
963
 
 
964
                /* Refuse to drop the clustered index.  It would be
 
965
                better to automatically generate a clustered index,
 
966
                but drizzled::alter_table() will call this method only
 
967
                after ha_innobase::add_index(). */
 
968
 
 
969
                if (dict_index_is_clust(index)) {
 
970
                        my_error(ER_REQUIRES_PRIMARY_KEY, MYF(0));
 
971
                        err = -1;
 
972
                        goto func_exit;
 
973
                }
 
974
 
 
975
                index->to_be_dropped = TRUE;
 
976
        }
 
977
 
 
978
        /* If FOREIGN_KEY_CHECK = 1 you may not drop an index defined
 
979
        for a foreign key constraint because InnoDB requires that both
 
980
        tables contain indexes for the constraint.  Note that CREATE
 
981
        INDEX id ON table does a CREATE INDEX and DROP INDEX, and we
 
982
        can ignore here foreign keys because a new index for the
 
983
        foreign key has already been created.
 
984
 
 
985
        We check for the foreign key constraints after marking the
 
986
        candidate indexes for deletion, because when we check for an
 
987
        equivalent foreign index we don't want to select an index that
 
988
        is later deleted. */
 
989
 
 
990
        if (trx->check_foreigns
 
991
            && session_sql_command(user_session) != SQLCOM_CREATE_INDEX) {
 
992
                dict_index_t*   index;
 
993
 
 
994
                for (index = dict_table_get_first_index(prebuilt->table);
 
995
                     index;
 
996
                     index = dict_table_get_next_index(index)) {
 
997
                        dict_foreign_t* foreign;
 
998
 
 
999
                        if (!index->to_be_dropped) {
 
1000
 
 
1001
                                continue;
 
1002
                        }
 
1003
 
 
1004
                        /* Check if the index is referenced. */
 
1005
                        foreign = dict_table_get_referenced_constraint(
 
1006
                                prebuilt->table, index);
 
1007
 
 
1008
                        if (foreign) {
 
1009
index_needed:
 
1010
                                trx_set_detailed_error(
 
1011
                                        trx,
 
1012
                                        "Index needed in foreign key "
 
1013
                                        "constraint");
 
1014
 
 
1015
                                trx->error_info = index;
 
1016
 
 
1017
                                err = HA_ERR_DROP_INDEX_FK;
 
1018
                                break;
 
1019
                        } else {
 
1020
                                /* Check if this index references some
 
1021
                                other table */
 
1022
                                foreign = dict_table_get_foreign_constraint(
 
1023
                                        prebuilt->table, index);
 
1024
 
 
1025
                                if (foreign) {
 
1026
                                        ut_a(foreign->foreign_index == index);
 
1027
 
 
1028
                                        /* Search for an equivalent index that
 
1029
                                        the foreign key constraint could use
 
1030
                                        if this index were to be deleted. */
 
1031
                                        if (!dict_foreign_find_equiv_index(
 
1032
                                                foreign)) {
 
1033
 
 
1034
                                                goto index_needed;
 
1035
                                        }
 
1036
                                }
 
1037
                        }
 
1038
                }
 
1039
        } else if (session_sql_command(user_session) == SQLCOM_CREATE_INDEX) {
 
1040
                /* This is a drop of a foreign key constraint index that
 
1041
                was created by MySQL when the constraint was added.  MySQL
 
1042
                does this when the user creates an index explicitly which
 
1043
                can be used in place of the automatically generated index. */
 
1044
 
 
1045
                dict_index_t*   index;
 
1046
 
 
1047
                for (index = dict_table_get_first_index(prebuilt->table);
 
1048
                     index;
 
1049
                     index = dict_table_get_next_index(index)) {
 
1050
                        dict_foreign_t* foreign;
 
1051
 
 
1052
                        if (!index->to_be_dropped) {
 
1053
 
 
1054
                                continue;
 
1055
                        }
 
1056
 
 
1057
                        /* Check if this index references some other table */
 
1058
                        foreign = dict_table_get_foreign_constraint(
 
1059
                                prebuilt->table, index);
 
1060
 
 
1061
                        if (foreign == NULL) {
 
1062
 
 
1063
                                continue;
 
1064
                        }
 
1065
 
 
1066
                        ut_a(foreign->foreign_index == index);
 
1067
 
 
1068
                        /* Search for an equivalent index that the
 
1069
                        foreign key constraint could use if this index
 
1070
                        were to be deleted. */
 
1071
 
 
1072
                        if (!dict_foreign_find_equiv_index(foreign)) {
 
1073
                                trx_set_detailed_error(
 
1074
                                        trx,
 
1075
                                        "Index needed in foreign key "
 
1076
                                        "constraint");
 
1077
 
 
1078
                                trx->error_info = foreign->foreign_index;
 
1079
 
 
1080
                                err = HA_ERR_DROP_INDEX_FK;
 
1081
                                break;
 
1082
                        }
 
1083
                }
 
1084
        }
 
1085
 
 
1086
func_exit:
 
1087
        if (err) {
 
1088
                /* Undo our changes since there was some sort of error. */
 
1089
                dict_index_t*   index
 
1090
                        = dict_table_get_first_index(prebuilt->table);
 
1091
 
 
1092
                do {
 
1093
                        index->to_be_dropped = FALSE;
 
1094
                        index = dict_table_get_next_index(index);
 
1095
                } while (index);
 
1096
        }
 
1097
 
 
1098
        row_mysql_unlock_data_dictionary(trx);
 
1099
 
 
1100
        return(err);
 
1101
}
 
1102
 
 
1103
/*******************************************************************//**
 
1104
Drop the indexes that were passed to a successful prepare_drop_index().
 
1105
@return 0 or error number */
 
1106
UNIV_INTERN
 
1107
int
 
1108
ha_innobase::final_drop_index(
 
1109
/*==========================*/
 
1110
        Table*  )               /*!< in: Table where indexes are dropped */
 
1111
{
 
1112
        dict_index_t*   index;          /*!< Index to be dropped */
 
1113
        trx_t*          trx;            /*!< Transaction */
 
1114
        int             err;
 
1115
 
 
1116
        if (srv_created_new_raw || srv_force_recovery) {
 
1117
                return(HA_ERR_WRONG_COMMAND);
 
1118
        }
 
1119
 
 
1120
        update_session();
 
1121
 
 
1122
        trx_search_latch_release_if_reserved(prebuilt->trx);
 
1123
        trx_start_if_not_started(prebuilt->trx);
 
1124
 
 
1125
        /* Create a background transaction for the operations on
 
1126
        the data dictionary tables. */
 
1127
        trx = innobase_trx_allocate(user_session);
 
1128
        trx_start_if_not_started(trx);
 
1129
 
 
1130
        /* Flag this transaction as a dictionary operation, so that
 
1131
        the data dictionary will be locked in crash recovery. */
 
1132
        trx_set_dict_operation(trx, TRX_DICT_OP_INDEX);
 
1133
 
 
1134
        /* Lock the table exclusively, to ensure that no active
 
1135
        transaction depends on an index that is being dropped. */
 
1136
        err = convert_error_code_to_mysql(
 
1137
                row_merge_lock_table(prebuilt->trx, prebuilt->table, LOCK_X),
 
1138
                prebuilt->table->flags, user_session);
 
1139
 
 
1140
        row_mysql_lock_data_dictionary(trx);
 
1141
 
 
1142
        if (UNIV_UNLIKELY(err)) {
 
1143
 
 
1144
                /* Unmark the indexes to be dropped. */
 
1145
                for (index = dict_table_get_first_index(prebuilt->table);
 
1146
                     index; index = dict_table_get_next_index(index)) {
 
1147
 
 
1148
                        index->to_be_dropped = FALSE;
 
1149
                }
 
1150
 
 
1151
                goto func_exit;
 
1152
        }
 
1153
 
 
1154
        /* Drop indexes marked to be dropped */
 
1155
 
 
1156
        index = dict_table_get_first_index(prebuilt->table);
 
1157
 
 
1158
        while (index) {
 
1159
                dict_index_t*   next_index;
 
1160
 
 
1161
                next_index = dict_table_get_next_index(index);
 
1162
 
 
1163
                if (index->to_be_dropped) {
 
1164
 
 
1165
                        row_merge_drop_index(index, prebuilt->table, trx);
 
1166
                }
 
1167
 
 
1168
                index = next_index;
 
1169
        }
 
1170
 
 
1171
        /* Check that all flagged indexes were dropped. */
 
1172
        for (index = dict_table_get_first_index(prebuilt->table);
 
1173
             index; index = dict_table_get_next_index(index)) {
 
1174
                ut_a(!index->to_be_dropped);
 
1175
        }
 
1176
 
 
1177
#ifdef UNIV_DEBUG
 
1178
        dict_table_check_for_dup_indexes(prebuilt->table);
 
1179
#endif
 
1180
 
 
1181
func_exit:
 
1182
        trx_commit_for_mysql(trx);
 
1183
        trx_commit_for_mysql(prebuilt->trx);
 
1184
        row_mysql_unlock_data_dictionary(trx);
 
1185
 
 
1186
        /* Flush the log to reduce probability that the .frm files and
 
1187
        the InnoDB data dictionary get out-of-sync if the user runs
 
1188
        with innodb_flush_log_at_trx_commit = 0 */
 
1189
 
 
1190
        log_buffer_flush_to_disk();
 
1191
 
 
1192
        trx_free_for_mysql(trx);
 
1193
 
 
1194
        /* Tell the InnoDB server that there might be work for
 
1195
        utility threads: */
 
1196
 
 
1197
        srv_active_wake_master_thread();
 
1198
 
 
1199
        return(err);
 
1200
}