~drizzle-trunk/drizzle/development

« back to all changes in this revision

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

  • Committer: Monty Taylor
  • Date: 2008-08-04 19:37:18 UTC
  • mto: (261.2.2 codestyle)
  • mto: This revision was merged to the branch mainline in revision 262.
  • Revision ID: monty@inaugust.com-20080804193718-f0rz13uli4429ozb
Changed gettext_noop() to N_()

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