~drizzle-trunk/drizzle/development

« back to all changes in this revision

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

  • Committer: Brian Aker
  • Date: 2009-03-22 21:27:04 UTC
  • mfrom: (960.2.22 mordred)
  • Revision ID: brian@tangent.org-20090322212704-ysn4mkkjg2u9kv22
Merge Monty

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/******************************************************
2
 
Smart ALTER TABLE
3
 
 
4
 
(c) 2005-2008 Innobase Oy
5
 
*******************************************************/
6
 
 
7
 
#include <mysql_priv.h>
8
 
#include <mysqld_error.h>
9
 
 
10
 
extern "C" {
11
 
#include "log0log.h"
12
 
#include "row0merge.h"
13
 
#include "srv0srv.h"
14
 
#include "trx0trx.h"
15
 
#include "trx0roll.h"
16
 
#include "ha_prototypes.h"
17
 
#include "handler0alter.h"
18
 
}
19
 
 
20
 
#include "ha_innodb.h"
21
 
#include "handler0vars.h"
22
 
 
23
 
/*****************************************************************
24
 
Copies an InnoDB column to a MySQL field.  This function is
25
 
adapted from row_sel_field_store_in_mysql_format(). */
26
 
static
27
 
void
28
 
innobase_col_to_mysql(
29
 
/*==================*/
30
 
        const dict_col_t*       col,    /* in: InnoDB column */
31
 
        const uchar*            data,   /* in: InnoDB column data */
32
 
        ulint                   len,    /* in: length of data, in bytes */
33
 
        Field*                  field)  /* in/out: MySQL field */
34
 
{
35
 
        uchar*  ptr;
36
 
        uchar*  dest    = field->ptr;
37
 
        ulint   flen    = field->pack_length();
38
 
 
39
 
        switch (col->mtype) {
40
 
        case DATA_INT:
41
 
                ut_ad(len == flen);
42
 
 
43
 
                /* Convert integer data from Innobase to little-endian
44
 
                format, sign bit restored to normal */
45
 
 
46
 
                for (ptr = dest + len; ptr != dest; ) {
47
 
                        *--ptr = *data++;
48
 
                }
49
 
 
50
 
                if (!(field->flags & UNSIGNED_FLAG)) {
51
 
                        ((byte*) dest)[len - 1] ^= 0x80;
52
 
                }
53
 
 
54
 
                break;
55
 
 
56
 
        case DATA_VARCHAR:
57
 
        case DATA_VARMYSQL:
58
 
        case DATA_BINARY:
59
 
                field->reset();
60
 
 
61
 
                if (field->type() == MYSQL_TYPE_VARCHAR) {
62
 
                        /* This is a >= 5.0.3 type true VARCHAR. Store the
63
 
                        length of the data to the first byte or the first
64
 
                        two bytes of dest. */
65
 
 
66
 
                        dest = row_mysql_store_true_var_len(
67
 
                                dest, len, flen - field->key_length());
68
 
                }
69
 
 
70
 
                /* Copy the actual data */
71
 
                memcpy(dest, data, len);
72
 
                break;
73
 
 
74
 
        case DATA_BLOB:
75
 
                /* Store a pointer to the BLOB buffer to dest: the BLOB was
76
 
                already copied to the buffer in row_sel_store_mysql_rec */
77
 
 
78
 
                row_mysql_store_blob_ref(dest, flen, data, len);
79
 
                break;
80
 
 
81
 
#ifdef UNIV_DEBUG
82
 
        case DATA_MYSQL:
83
 
                ut_ad(flen >= len);
84
 
                ut_ad(col->mbmaxlen >= col->mbminlen);
85
 
                ut_ad(col->mbmaxlen > col->mbminlen || flen == len);
86
 
                memcpy(dest, data, len);
87
 
                break;
88
 
 
89
 
        default:
90
 
        case DATA_SYS_CHILD:
91
 
        case DATA_SYS:
92
 
                /* These column types should never be shipped to MySQL. */
93
 
                ut_ad(0);
94
 
 
95
 
        case DATA_CHAR:
96
 
        case DATA_FIXBINARY:
97
 
        case DATA_FLOAT:
98
 
        case DATA_DOUBLE:
99
 
        case DATA_DECIMAL:
100
 
                /* Above are the valid column types for MySQL data. */
101
 
                ut_ad(flen == len);
102
 
#else /* UNIV_DEBUG */
103
 
        default:
104
 
#endif /* UNIV_DEBUG */
105
 
                memcpy(dest, data, len);
106
 
        }
107
 
}
108
 
 
109
 
/*****************************************************************
110
 
Copies an InnoDB record to table->record[0]. */
111
 
extern "C" UNIV_INTERN
112
 
void
113
 
innobase_rec_to_mysql(
114
 
/*==================*/
115
 
        TABLE*                  table,          /* in/out: MySQL table */
116
 
        const rec_t*            rec,            /* in: record */
117
 
        const dict_index_t*     index,          /* in: index */
118
 
        const ulint*            offsets)        /* in: rec_get_offsets(
119
 
                                                rec, index, ...) */
120
 
{
121
 
        uint    n_fields        = table->s->fields;
122
 
        uint    i;
123
 
 
124
 
        ut_ad(n_fields == dict_table_get_n_user_cols(index->table));
125
 
 
126
 
        for (i = 0; i < n_fields; i++) {
127
 
                Field*          field   = table->field[i];
128
 
                ulint           ipos;
129
 
                ulint           ilen;
130
 
                const uchar*    ifield;
131
 
 
132
 
                field->reset();
133
 
 
134
 
                ipos = dict_index_get_nth_col_pos(index, i);
135
 
 
136
 
                if (UNIV_UNLIKELY(ipos == ULINT_UNDEFINED)) {
137
 
null_field:
138
 
                        field->set_null();
139
 
                        continue;
140
 
                }
141
 
 
142
 
                ifield = rec_get_nth_field(rec, offsets, ipos, &ilen);
143
 
 
144
 
                /* Assign the NULL flag */
145
 
                if (ilen == UNIV_SQL_NULL) {
146
 
                        ut_ad(field->real_maybe_null());
147
 
                        goto null_field;
148
 
                }
149
 
 
150
 
                field->set_notnull();
151
 
 
152
 
                innobase_col_to_mysql(
153
 
                        dict_field_get_col(
154
 
                                dict_index_get_nth_field(index, ipos)),
155
 
                        ifield, ilen, field);
156
 
        }
157
 
}
158
 
 
159
 
/*****************************************************************
160
 
Resets table->record[0]. */
161
 
extern "C" UNIV_INTERN
162
 
void
163
 
innobase_rec_reset(
164
 
/*===============*/
165
 
        TABLE*                  table)          /* in/out: MySQL table */
166
 
{
167
 
        uint    n_fields        = table->s->fields;
168
 
        uint    i;
169
 
 
170
 
        for (i = 0; i < n_fields; i++) {
171
 
                table->field[i]->set_default();
172
 
        }
173
 
}
174
 
 
175
 
/**********************************************************************
176
 
Removes the filename encoding of a database and table name. */
177
 
static
178
 
void
179
 
innobase_convert_tablename(
180
 
/*=======================*/
181
 
        char*   s)      /* in: identifier; out: decoded identifier */
182
 
{
183
 
        uint    errors;
184
 
 
185
 
        char*   slash = strchr(s, '/');
186
 
 
187
 
        if (slash) {
188
 
                char*   t;
189
 
                /* Temporarily replace the '/' with NUL. */
190
 
                *slash = 0;
191
 
                /* Convert the database name. */
192
 
                strconvert(&my_charset_filename, s, system_charset_info,
193
 
                           s, slash - s + 1, &errors);
194
 
 
195
 
                t = s + strlen(s);
196
 
                ut_ad(slash >= t);
197
 
                /* Append a  '.' after the database name. */
198
 
                *t++ = '.';
199
 
                slash++;
200
 
                /* Convert the table name. */
201
 
                strconvert(&my_charset_filename, slash, system_charset_info,
202
 
                           t, slash - t + strlen(slash), &errors);
203
 
        } else {
204
 
                strconvert(&my_charset_filename, s,
205
 
                           system_charset_info, s, strlen(s), &errors);
206
 
        }
207
 
}
208
 
 
209
 
/***********************************************************************
210
 
This function checks that index keys are sensible. */
211
 
static
212
 
int
213
 
innobase_check_index_keys(
214
 
/*======================*/
215
 
                                        /* out: 0 or error number */
216
 
        const KEY*      key_info,       /* in: Indexes to be created */
217
 
        ulint           num_of_keys)    /* in: Number of indexes to
218
 
                                        be created */
219
 
{
220
 
        ulint           key_num;
221
 
 
222
 
        ut_ad(key_info);
223
 
        ut_ad(num_of_keys);
224
 
 
225
 
        for (key_num = 0; key_num < num_of_keys; key_num++) {
226
 
                const KEY&      key = key_info[key_num];
227
 
 
228
 
                /* Check that the same index name does not appear
229
 
                twice in indexes to be created. */
230
 
 
231
 
                for (ulint i = 0; i < key_num; i++) {
232
 
                        const KEY&      key2 = key_info[i];
233
 
 
234
 
                        if (0 == strcmp(key.name, key2.name)) {
235
 
                                errmsg_printf(ERRMSG_LVL_ERROR, "InnoDB: key name `%s` appears"
236
 
                                                " twice in CREATE INDEX\n",
237
 
                                                key.name);
238
 
 
239
 
                                return(ER_WRONG_NAME_FOR_INDEX);
240
 
                        }
241
 
                }
242
 
 
243
 
                /* Check that MySQL does not try to create a column
244
 
                prefix index field on an inappropriate data type and
245
 
                that the same colum does not appear twice in the index. */
246
 
 
247
 
                for (ulint i = 0; i < key.key_parts; i++) {
248
 
                        const KEY_PART_INFO&    key_part1
249
 
                                = key.key_part[i];
250
 
                        const Field*            field
251
 
                                = key_part1.field;
252
 
                        ibool                   is_unsigned;
253
 
 
254
 
                        switch (get_innobase_type_from_mysql_type(
255
 
                                        &is_unsigned, field)) {
256
 
                        default:
257
 
                                break;
258
 
                        case DATA_INT:
259
 
                        case DATA_FLOAT:
260
 
                        case DATA_DOUBLE:
261
 
                        case DATA_DECIMAL:
262
 
                                if (field->type() == MYSQL_TYPE_VARCHAR) {
263
 
                                        if (key_part1.length
264
 
                                            >= field->pack_length()
265
 
                                            - ((Field_varstring*) field)
266
 
                                            ->length_bytes) {
267
 
                                                break;
268
 
                                        }
269
 
                                } else {
270
 
                                        if (key_part1.length
271
 
                                            >= field->pack_length()) {
272
 
                                                break;
273
 
                                        }
274
 
                                }
275
 
 
276
 
                                errmsg_printf(ERRMSG_LVL_ERROR, "InnoDB: MySQL is trying to"
277
 
                                                " create a column prefix"
278
 
                                                " index field on an"
279
 
                                                " inappropriate data type."
280
 
                                                " column `%s`,"
281
 
                                                " index `%s`.\n",
282
 
                                                field->field_name,
283
 
                                                key.name);
284
 
                                return(ER_WRONG_KEY_COLUMN);
285
 
                        }
286
 
 
287
 
                        for (ulint j = 0; j < i; j++) {
288
 
                                const KEY_PART_INFO&    key_part2
289
 
                                        = key.key_part[j];
290
 
 
291
 
                                if (strcmp(key_part1.field->field_name,
292
 
                                           key_part2.field->field_name)) {
293
 
                                        continue;
294
 
                                }
295
 
 
296
 
                                errmsg_printf(ERRMSG_LVL_ERROR, "InnoDB: column `%s`"
297
 
                                                " is not allowed to occur"
298
 
                                                " twice in index `%s`.\n",
299
 
                                                key_part1.field->field_name,
300
 
                                                key.name);
301
 
                                return(ER_WRONG_KEY_COLUMN);
302
 
                        }
303
 
                }
304
 
        }
305
 
 
306
 
        return(0);
307
 
}
308
 
 
309
 
/***********************************************************************
310
 
Create index field definition for key part */
311
 
static
312
 
void
313
 
innobase_create_index_field_def(
314
 
/*============================*/
315
 
        KEY_PART_INFO*          key_part,       /* in: MySQL key definition */
316
 
        mem_heap_t*             heap,           /* in: memory heap */
317
 
        merge_index_field_t*    index_field)    /* out: index field
318
 
                                                definition for key_part */
319
 
{
320
 
        Field*          field;
321
 
        ibool           is_unsigned;
322
 
        ulint           col_type;
323
 
 
324
 
        DBUG_ENTER("innobase_create_index_field_def");
325
 
 
326
 
        ut_ad(key_part);
327
 
        ut_ad(index_field);
328
 
 
329
 
        field = key_part->field;
330
 
        ut_a(field);
331
 
 
332
 
        col_type = get_innobase_type_from_mysql_type(&is_unsigned, field);
333
 
 
334
 
        if (DATA_BLOB == col_type
335
 
            || (key_part->length < field->pack_length()
336
 
                && field->type() != MYSQL_TYPE_VARCHAR)
337
 
            || (field->type() == MYSQL_TYPE_VARCHAR
338
 
                && key_part->length < field->pack_length()
339
 
                        - ((Field_varstring*)field)->length_bytes)) {
340
 
 
341
 
                index_field->prefix_len = key_part->length;
342
 
        } else {
343
 
                index_field->prefix_len = 0;
344
 
        }
345
 
 
346
 
        index_field->field_name = mem_heap_strdup(heap, field->field_name);
347
 
 
348
 
        DBUG_VOID_RETURN;
349
 
}
350
 
 
351
 
/***********************************************************************
352
 
Create index definition for key */
353
 
static
354
 
void
355
 
innobase_create_index_def(
356
 
/*======================*/
357
 
        KEY*                    key,            /* in: key definition */
358
 
        bool                    new_primary,    /* in: TRUE=generating
359
 
                                                a new primary key
360
 
                                                on the table */
361
 
        bool                    key_primary,    /* in: TRUE if this key
362
 
                                                is a primary key */
363
 
        merge_index_def_t*      index,          /* out: index definition */
364
 
        mem_heap_t*             heap)           /* in: heap where memory
365
 
                                                is allocated */
366
 
{
367
 
        ulint   i;
368
 
        ulint   len;
369
 
        ulint   n_fields = key->key_parts;
370
 
        char*   index_name;
371
 
 
372
 
        DBUG_ENTER("innobase_create_index_def");
373
 
 
374
 
        index->fields = (merge_index_field_t*) mem_heap_alloc(
375
 
                heap, n_fields * sizeof *index->fields);
376
 
 
377
 
        index->ind_type = 0;
378
 
        index->n_fields = n_fields;
379
 
        len = strlen(key->name) + 1;
380
 
        index->name = index_name = (char*) mem_heap_alloc(heap,
381
 
                                                          len + !new_primary);
382
 
 
383
 
        if (UNIV_LIKELY(!new_primary)) {
384
 
                *index_name++ = TEMP_INDEX_PREFIX;
385
 
        }
386
 
 
387
 
        memcpy(index_name, key->name, len);
388
 
 
389
 
        if (key->flags & HA_NOSAME) {
390
 
                index->ind_type |= DICT_UNIQUE;
391
 
        }
392
 
 
393
 
        if (key_primary) {
394
 
                index->ind_type |= DICT_CLUSTERED;
395
 
        }
396
 
 
397
 
        for (i = 0; i < n_fields; i++) {
398
 
                innobase_create_index_field_def(&key->key_part[i], heap,
399
 
                                                &index->fields[i]);
400
 
        }
401
 
 
402
 
        DBUG_VOID_RETURN;
403
 
}
404
 
 
405
 
/***********************************************************************
406
 
Copy index field definition */
407
 
static
408
 
void
409
 
innobase_copy_index_field_def(
410
 
/*==========================*/
411
 
        const dict_field_t*     field,          /* in: definition to copy */
412
 
        merge_index_field_t*    index_field)    /* out: copied definition */
413
 
{
414
 
        DBUG_ENTER("innobase_copy_index_field_def");
415
 
        DBUG_ASSERT(field != NULL);
416
 
        DBUG_ASSERT(index_field != NULL);
417
 
 
418
 
        index_field->field_name = field->name;
419
 
        index_field->prefix_len = field->prefix_len;
420
 
 
421
 
        DBUG_VOID_RETURN;
422
 
}
423
 
 
424
 
/***********************************************************************
425
 
Copy index definition for the index */
426
 
static
427
 
void
428
 
innobase_copy_index_def(
429
 
/*====================*/
430
 
        const dict_index_t*     index,  /* in: index definition to copy */
431
 
        merge_index_def_t*      new_index,/* out: Index definition */
432
 
        mem_heap_t*             heap)   /* in: heap where allocated */
433
 
{
434
 
        ulint   n_fields;
435
 
        ulint   i;
436
 
 
437
 
        DBUG_ENTER("innobase_copy_index_def");
438
 
 
439
 
        /* Note that we take only those fields that user defined to be
440
 
        in the index.  In the internal representation more colums were
441
 
        added and those colums are not copied .*/
442
 
 
443
 
        n_fields = index->n_user_defined_cols;
444
 
 
445
 
        new_index->fields = (merge_index_field_t*) mem_heap_alloc(
446
 
                heap, n_fields * sizeof *new_index->fields);
447
 
 
448
 
        /* When adding a PRIMARY KEY, we may convert a previous
449
 
        clustered index to a secondary index (UNIQUE NOT NULL). */
450
 
        new_index->ind_type = index->type & ~DICT_CLUSTERED;
451
 
        new_index->n_fields = n_fields;
452
 
        new_index->name = index->name;
453
 
 
454
 
        for (i = 0; i < n_fields; i++) {
455
 
                innobase_copy_index_field_def(&index->fields[i],
456
 
                                              &new_index->fields[i]);
457
 
        }
458
 
 
459
 
        DBUG_VOID_RETURN;
460
 
}
461
 
 
462
 
/***********************************************************************
463
 
Create an index table where indexes are ordered as follows:
464
 
 
465
 
IF a new primary key is defined for the table THEN
466
 
 
467
 
        1) New primary key
468
 
        2) Original secondary indexes
469
 
        3) New secondary indexes
470
 
 
471
 
ELSE
472
 
 
473
 
        1) All new indexes in the order they arrive from MySQL
474
 
 
475
 
ENDIF
476
 
 
477
 
*/
478
 
static
479
 
merge_index_def_t*
480
 
innobase_create_key_def(
481
 
/*====================*/
482
 
                                        /* out: key definitions or NULL */
483
 
        trx_t*          trx,            /* in: trx */
484
 
        const dict_table_t*table,               /* in: table definition */
485
 
        mem_heap_t*     heap,           /* in: heap where space for key
486
 
                                        definitions are allocated */
487
 
        KEY*            key_info,       /* in: Indexes to be created */
488
 
        ulint&          n_keys)         /* in/out: Number of indexes to
489
 
                                        be created */
490
 
{
491
 
        ulint                   i = 0;
492
 
        merge_index_def_t*      indexdef;
493
 
        merge_index_def_t*      indexdefs;
494
 
        bool                    new_primary;
495
 
 
496
 
        DBUG_ENTER("innobase_create_key_def");
497
 
 
498
 
        indexdef = indexdefs = (merge_index_def_t*)
499
 
                mem_heap_alloc(heap, sizeof *indexdef
500
 
                               * (n_keys + UT_LIST_GET_LEN(table->indexes)));
501
 
 
502
 
        /* If there is a primary key, it is always the first index
503
 
        defined for the table. */
504
 
 
505
 
        new_primary = !my_strcasecmp(system_charset_info,
506
 
                                     key_info->name, "PRIMARY");
507
 
 
508
 
        /* If there is a UNIQUE INDEX consisting entirely of NOT NULL
509
 
        columns, MySQL will treat it as a PRIMARY KEY unless the
510
 
        table already has one. */
511
 
 
512
 
        if (!new_primary && (key_info->flags & HA_NOSAME)
513
 
            && row_table_got_default_clust_index(table)) {
514
 
                uint    key_part = key_info->key_parts;
515
 
 
516
 
                new_primary = TRUE;
517
 
 
518
 
                while (key_part--) {
519
 
                        if (key_info->key_part[key_part].key_type
520
 
                            & FIELDFLAG_MAYBE_NULL) {
521
 
                                new_primary = FALSE;
522
 
                                break;
523
 
                        }
524
 
                }
525
 
        }
526
 
 
527
 
        if (new_primary) {
528
 
                const dict_index_t*     index;
529
 
 
530
 
                /* Create the PRIMARY key index definition */
531
 
                innobase_create_index_def(&key_info[i++], TRUE, TRUE,
532
 
                                          indexdef++, heap);
533
 
 
534
 
                row_mysql_lock_data_dictionary(trx);
535
 
 
536
 
                index = dict_table_get_first_index(table);
537
 
 
538
 
                /* Copy the index definitions of the old table.  Skip
539
 
                the old clustered index if it is a generated clustered
540
 
                index or a PRIMARY KEY.  If the clustered index is a
541
 
                UNIQUE INDEX, it must be converted to a secondary index. */
542
 
 
543
 
                if (dict_index_get_nth_col(index, 0)->mtype == DATA_SYS
544
 
                    || !my_strcasecmp(system_charset_info,
545
 
                                      index->name, "PRIMARY")) {
546
 
                        index = dict_table_get_next_index(index);
547
 
                }
548
 
 
549
 
                while (index) {
550
 
                        innobase_copy_index_def(index, indexdef++, heap);
551
 
                        index = dict_table_get_next_index(index);
552
 
                }
553
 
 
554
 
                row_mysql_unlock_data_dictionary(trx);
555
 
        }
556
 
 
557
 
        /* Create definitions for added secondary indexes. */
558
 
 
559
 
        while (i < n_keys) {
560
 
                innobase_create_index_def(&key_info[i++], new_primary, FALSE,
561
 
                                          indexdef++, heap);
562
 
        }
563
 
 
564
 
        n_keys = indexdef - indexdefs;
565
 
 
566
 
        DBUG_RETURN(indexdefs);
567
 
}
568
 
 
569
 
/***********************************************************************
570
 
Create a temporary tablename using query id, thread id, and id */
571
 
static
572
 
char*
573
 
innobase_create_temporary_tablename(
574
 
/*================================*/
575
 
                                        /* out: temporary tablename */
576
 
        mem_heap_t*     heap,           /* in: memory heap */
577
 
        char            id,             /* in: identifier [0-9a-zA-Z] */
578
 
        const char*     table_name)     /* in: table name */
579
 
{
580
 
        char*                   name;
581
 
        ulint                   len;
582
 
        static const char       suffix[] = "@0023 "; /* "# " */
583
 
 
584
 
        len = strlen(table_name);
585
 
 
586
 
        name = (char*) mem_heap_alloc(heap, len + sizeof suffix);
587
 
        memcpy(name, table_name, len);
588
 
        memcpy(name + len, suffix, sizeof suffix);
589
 
        name[len + (sizeof suffix - 2)] = id;
590
 
 
591
 
        return(name);
592
 
}
593
 
 
594
 
/***********************************************************************
595
 
Create indexes. */
596
 
UNIV_INTERN
597
 
int
598
 
ha_innobase::add_index(
599
 
/*===================*/
600
 
                                /* out: 0 or error number */
601
 
        TABLE*  table,          /* in: Table where indexes are created */
602
 
        KEY*    key_info,       /* in: Indexes to be created */
603
 
        uint    num_of_keys)    /* in: Number of indexes to be created */
604
 
{
605
 
        dict_index_t**  index;          /* Index to be created */
606
 
        dict_table_t*   innodb_table;   /* InnoDB table in dictionary */
607
 
        dict_table_t*   indexed_table;  /* Table where indexes are created */
608
 
        merge_index_def_t* index_defs;  /* Index definitions */
609
 
        mem_heap_t*     heap;           /* Heap for index definitions */
610
 
        trx_t*          trx;            /* Transaction */
611
 
        ulint           num_of_idx;
612
 
        ulint           num_created     = 0;
613
 
        ibool           dict_locked     = FALSE;
614
 
        ulint           new_primary;
615
 
        ulint           error;
616
 
 
617
 
        DBUG_ENTER("ha_innobase::add_index");
618
 
        ut_a(table);
619
 
        ut_a(key_info);
620
 
        ut_a(num_of_keys);
621
 
 
622
 
        if (srv_created_new_raw || srv_force_recovery) {
623
 
                DBUG_RETURN(HA_ERR_WRONG_COMMAND);
624
 
        }
625
 
 
626
 
        update_thd();
627
 
 
628
 
        heap = mem_heap_create(1024);
629
 
 
630
 
        /* In case MySQL calls this in the middle of a SELECT query, release
631
 
        possible adaptive hash latch to avoid deadlocks of threads. */
632
 
        trx_search_latch_release_if_reserved(prebuilt->trx);
633
 
 
634
 
        /* Create a background transaction for the operations on
635
 
        the data dictionary tables. */
636
 
        trx = trx_allocate_for_mysql();
637
 
        trx_start_if_not_started(trx);
638
 
 
639
 
        trx->mysql_thd = user_thd;
640
 
        trx->mysql_query_str = thd_query(user_thd);
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
 
                DBUG_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_thd);
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, 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_thd);
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
 
        DBUG_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*  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
 
        DBUG_ENTER("ha_innobase::prepare_drop_index");
917
 
        ut_ad(table);
918
 
        ut_ad(key_num);
919
 
        ut_ad(num_of_keys);
920
 
        if (srv_created_new_raw || srv_force_recovery) {
921
 
                DBUG_RETURN(HA_ERR_WRONG_COMMAND);
922
 
        }
923
 
 
924
 
        update_thd();
925
 
 
926
 
        trx_search_latch_release_if_reserved(prebuilt->trx);
927
 
        trx = prebuilt->trx;
928
 
 
929
 
        /* Test and mark all the indexes to be dropped */
930
 
 
931
 
        row_mysql_lock_data_dictionary(trx);
932
 
 
933
 
        /* Check that none of the indexes have previously been flagged
934
 
        for deletion. */
935
 
        {
936
 
                const dict_index_t*     index
937
 
                        = dict_table_get_first_index(prebuilt->table);
938
 
                do {
939
 
                        ut_a(!index->to_be_dropped);
940
 
                        index = dict_table_get_next_index(index);
941
 
                } while (index);
942
 
        }
943
 
 
944
 
        for (n_key = 0; n_key < num_of_keys; n_key++) {
945
 
                const KEY*      key;
946
 
                dict_index_t*   index;
947
 
 
948
 
                key = table->key_info + key_num[n_key];
949
 
                index = dict_table_get_index_on_name_and_min_id(
950
 
                        prebuilt->table, key->name);
951
 
 
952
 
                if (!index) {
953
 
                        errmsg_printf(ERRMSG_LVL_ERROR, "InnoDB could not find key n:o %u "
954
 
                                        "with name %s for table %s",
955
 
                                        key_num[n_key],
956
 
                                        key ? key->name : "NULL",
957
 
                                        prebuilt->table->name);
958
 
 
959
 
                        err = HA_ERR_KEY_NOT_FOUND;
960
 
                        goto func_exit;
961
 
                }
962
 
 
963
 
                /* Refuse to drop the clustered index.  It would be
964
 
                better to automatically generate a clustered index,
965
 
                but mysql_alter_table() will call this method only
966
 
                after ha_innobase::add_index(). */
967
 
 
968
 
                if (dict_index_is_clust(index)) {
969
 
                        my_error(ER_REQUIRES_PRIMARY_KEY, MYF(0));
970
 
                        err = -1;
971
 
                        goto func_exit;
972
 
                }
973
 
 
974
 
                index->to_be_dropped = TRUE;
975
 
        }
976
 
 
977
 
        /* If FOREIGN_KEY_CHECK = 1 you may not drop an index defined
978
 
        for a foreign key constraint because InnoDB requires that both
979
 
        tables contain indexes for the constraint.  Note that CREATE
980
 
        INDEX id ON table does a CREATE INDEX and DROP INDEX, and we
981
 
        can ignore here foreign keys because a new index for the
982
 
        foreign key has already been created.
983
 
 
984
 
        We check for the foreign key constraints after marking the
985
 
        candidate indexes for deletion, because when we check for an
986
 
        equivalent foreign index we don't want to select an index that
987
 
        is later deleted. */
988
 
 
989
 
        if (trx->check_foreigns
990
 
            && thd_sql_command(user_thd) != SQLCOM_CREATE_INDEX) {
991
 
                dict_index_t*   index;
992
 
 
993
 
                for (index = dict_table_get_first_index(prebuilt->table);
994
 
                     index;
995
 
                     index = dict_table_get_next_index(index)) {
996
 
                        dict_foreign_t* foreign;
997
 
 
998
 
                        if (!index->to_be_dropped) {
999
 
 
1000
 
                                continue;
1001
 
                        }
1002
 
 
1003
 
                        /* Check if the index is referenced. */
1004
 
                        foreign = dict_table_get_referenced_constraint(
1005
 
                                prebuilt->table, index);
1006
 
 
1007
 
                        if (foreign) {
1008
 
index_needed:
1009
 
                                trx_set_detailed_error(
1010
 
                                        trx,
1011
 
                                        "Index needed in foreign key "
1012
 
                                        "constraint");
1013
 
 
1014
 
                                trx->error_info = index;
1015
 
 
1016
 
                                err = HA_ERR_DROP_INDEX_FK;
1017
 
                                break;
1018
 
                        } else {
1019
 
                                /* Check if this index references some
1020
 
                                other table */
1021
 
                                foreign = dict_table_get_foreign_constraint(
1022
 
                                        prebuilt->table, index);
1023
 
 
1024
 
                                if (foreign) {
1025
 
                                        ut_a(foreign->foreign_index == index);
1026
 
 
1027
 
                                        /* Search for an equivalent index that
1028
 
                                        the foreign key constraint could use
1029
 
                                        if this index were to be deleted. */
1030
 
                                        if (!dict_foreign_find_equiv_index(
1031
 
                                                foreign)) {
1032
 
 
1033
 
                                                goto index_needed;
1034
 
                                        }
1035
 
                                }
1036
 
                        }
1037
 
                }
1038
 
        } else if (thd_sql_command(user_thd) == SQLCOM_CREATE_INDEX) {
1039
 
                /* This is a drop of a foreign key constraint index that
1040
 
                was created by MySQL when the constraint was added.  MySQL
1041
 
                does this when the user creates an index explicitly which
1042
 
                can be used in place of the automatically generated index. */
1043
 
 
1044
 
                dict_index_t*   index;
1045
 
 
1046
 
                for (index = dict_table_get_first_index(prebuilt->table);
1047
 
                     index;
1048
 
                     index = dict_table_get_next_index(index)) {
1049
 
                        dict_foreign_t* foreign;
1050
 
 
1051
 
                        if (!index->to_be_dropped) {
1052
 
 
1053
 
                                continue;
1054
 
                        }
1055
 
 
1056
 
                        /* Check if this index references some other table */
1057
 
                        foreign = dict_table_get_foreign_constraint(
1058
 
                                prebuilt->table, index);
1059
 
 
1060
 
                        if (foreign == NULL) {
1061
 
 
1062
 
                                continue;
1063
 
                        }
1064
 
 
1065
 
                        ut_a(foreign->foreign_index == index);
1066
 
 
1067
 
                        /* Search for an equivalent index that the
1068
 
                        foreign key constraint could use if this index
1069
 
                        were to be deleted. */
1070
 
 
1071
 
                        if (!dict_foreign_find_equiv_index(foreign)) {
1072
 
                                trx_set_detailed_error(
1073
 
                                        trx,
1074
 
                                        "Index needed in foreign key "
1075
 
                                        "constraint");
1076
 
 
1077
 
                                trx->error_info = foreign->foreign_index;
1078
 
 
1079
 
                                err = HA_ERR_DROP_INDEX_FK;
1080
 
                                break;
1081
 
                        }
1082
 
                }
1083
 
        }
1084
 
 
1085
 
func_exit:
1086
 
        if (err) {
1087
 
                /* Undo our changes since there was some sort of error. */
1088
 
                dict_index_t*   index
1089
 
                        = dict_table_get_first_index(prebuilt->table);
1090
 
 
1091
 
                do {
1092
 
                        index->to_be_dropped = FALSE;
1093
 
                        index = dict_table_get_next_index(index);
1094
 
                } while (index);
1095
 
        }
1096
 
 
1097
 
        row_mysql_unlock_data_dictionary(trx);
1098
 
 
1099
 
        DBUG_RETURN(err);
1100
 
}
1101
 
 
1102
 
/***********************************************************************
1103
 
Drop the indexes that were passed to a successful prepare_drop_index(). */
1104
 
UNIV_INTERN
1105
 
int
1106
 
ha_innobase::final_drop_index(
1107
 
/*==========================*/
1108
 
                                /* out: 0 or error number */
1109
 
        TABLE*  table)          /* in: Table where indexes are dropped */
1110
 
{
1111
 
        dict_index_t*   index;          /* Index to be dropped */
1112
 
        trx_t*          trx;            /* Transaction */
1113
 
        int             err;
1114
 
 
1115
 
        DBUG_ENTER("ha_innobase::final_drop_index");
1116
 
        ut_ad(table);
1117
 
 
1118
 
        if (srv_created_new_raw || srv_force_recovery) {
1119
 
                DBUG_RETURN(HA_ERR_WRONG_COMMAND);
1120
 
        }
1121
 
 
1122
 
        update_thd();
1123
 
 
1124
 
        trx_search_latch_release_if_reserved(prebuilt->trx);
1125
 
 
1126
 
        /* Create a background transaction for the operations on
1127
 
        the data dictionary tables. */
1128
 
        trx = trx_allocate_for_mysql();
1129
 
        trx_start_if_not_started(trx);
1130
 
 
1131
 
        trx->mysql_thd = user_thd;
1132
 
        trx->mysql_query_str = thd_query(user_thd);
1133
 
 
1134
 
        /* Flag this transaction as a dictionary operation, so that
1135
 
        the data dictionary will be locked in crash recovery. */
1136
 
        trx_set_dict_operation(trx, TRX_DICT_OP_INDEX);
1137
 
 
1138
 
        /* Lock the table exclusively, to ensure that no active
1139
 
        transaction depends on an index that is being dropped. */
1140
 
        err = convert_error_code_to_mysql(
1141
 
                row_merge_lock_table(prebuilt->trx, prebuilt->table, LOCK_X),
1142
 
                prebuilt->table->flags, user_thd);
1143
 
 
1144
 
        row_mysql_lock_data_dictionary(trx);
1145
 
 
1146
 
        if (UNIV_UNLIKELY(err)) {
1147
 
 
1148
 
                /* Unmark the indexes to be dropped. */
1149
 
                for (index = dict_table_get_first_index(prebuilt->table);
1150
 
                     index; index = dict_table_get_next_index(index)) {
1151
 
 
1152
 
                        index->to_be_dropped = FALSE;
1153
 
                }
1154
 
 
1155
 
                goto func_exit;
1156
 
        }
1157
 
 
1158
 
        /* Drop indexes marked to be dropped */
1159
 
 
1160
 
        index = dict_table_get_first_index(prebuilt->table);
1161
 
 
1162
 
        while (index) {
1163
 
                dict_index_t*   next_index;
1164
 
 
1165
 
                next_index = dict_table_get_next_index(index);
1166
 
 
1167
 
                if (index->to_be_dropped) {
1168
 
 
1169
 
                        row_merge_drop_index(index, prebuilt->table, trx);
1170
 
                }
1171
 
 
1172
 
                index = next_index;
1173
 
        }
1174
 
 
1175
 
        /* Check that all flagged indexes were dropped. */
1176
 
        for (index = dict_table_get_first_index(prebuilt->table);
1177
 
             index; index = dict_table_get_next_index(index)) {
1178
 
                ut_a(!index->to_be_dropped);
1179
 
        }
1180
 
 
1181
 
#ifdef UNIV_DEBUG
1182
 
        dict_table_check_for_dup_indexes(prebuilt->table);
1183
 
#endif
1184
 
 
1185
 
func_exit:
1186
 
        trx_commit_for_mysql(trx);
1187
 
        trx_commit_for_mysql(prebuilt->trx);
1188
 
        row_mysql_unlock_data_dictionary(trx);
1189
 
 
1190
 
        /* Flush the log to reduce probability that the .frm files and
1191
 
        the InnoDB data dictionary get out-of-sync if the user runs
1192
 
        with innodb_flush_log_at_trx_commit = 0 */
1193
 
 
1194
 
        log_buffer_flush_to_disk();
1195
 
 
1196
 
        trx_free_for_mysql(trx);
1197
 
 
1198
 
        /* Tell the InnoDB server that there might be work for
1199
 
        utility threads: */
1200
 
 
1201
 
        srv_active_wake_master_thread();
1202
 
 
1203
 
        DBUG_RETURN(err);
1204
 
}