~drizzle-trunk/drizzle/development

« back to all changes in this revision

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

Merged in latest plugin-slot-reorg.

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