Drizzled Public API Documentation

row0mysql.cc

00001 /*****************************************************************************
00002 
00003 Copyright (C) 2000, 2010, Innobase Oy. All Rights Reserved.
00004 
00005 This program is free software; you can redistribute it and/or modify it under
00006 the terms of the GNU General Public License as published by the Free Software
00007 Foundation; version 2 of the License.
00008 
00009 This program is distributed in the hope that it will be useful, but WITHOUT
00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00011 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00012 
00013 You should have received a copy of the GNU General Public License along with
00014 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
00015 St, Fifth Floor, Boston, MA 02110-1301 USA
00016 
00017 *****************************************************************************/
00018 
00019 /**************************************************/
00027 #include "row0mysql.h"
00028 
00029 #ifdef UNIV_NONINL
00030 #include "row0mysql.ic"
00031 #endif
00032 
00033 #include "row0ins.h"
00034 #include "row0merge.h"
00035 #include "row0sel.h"
00036 #include "row0upd.h"
00037 #include "row0row.h"
00038 #include "que0que.h"
00039 #include "pars0pars.h"
00040 #include "dict0dict.h"
00041 #include "dict0crea.h"
00042 #include "dict0load.h"
00043 #include "dict0boot.h"
00044 #include "trx0roll.h"
00045 #include "trx0purge.h"
00046 #include "trx0rec.h"
00047 #include "trx0undo.h"
00048 #include "lock0lock.h"
00049 #include "rem0cmp.h"
00050 #include "log0log.h"
00051 #include "btr0sea.h"
00052 #include "fil0fil.h"
00053 #include "ibuf0ibuf.h"
00054 
00055 #include <errno.h>
00056 
00058 UNIV_INTERN ibool row_rollback_on_timeout = FALSE;
00059 
00061 typedef struct row_mysql_drop_struct  row_mysql_drop_t;
00062 
00064 struct row_mysql_drop_struct{
00065   char*       table_name; 
00066   UT_LIST_NODE_T(row_mysql_drop_t)row_mysql_drop_list;
00068 };
00069 
00075 static UT_LIST_BASE_NODE_T(row_mysql_drop_t)  row_mysql_drop_list;
00077 static ibool  row_mysql_drop_list_inited  = FALSE;
00078 
00080 /* @{ */
00081 static const char S_innodb_monitor[] = "innodb_monitor";
00082 static const char S_innodb_lock_monitor[] = "innodb_lock_monitor";
00083 static const char S_innodb_tablespace_monitor[] = "innodb_tablespace_monitor";
00084 static const char S_innodb_table_monitor[] = "innodb_table_monitor";
00085 static const char S_innodb_mem_validate[] = "innodb_mem_validate";
00086 /* @} */
00087 
00094 #define STR_EQ(str1, str1_len, str2_onstack) \
00095   ((str1_len) == sizeof(str2_onstack) \
00096    && memcmp(str1, str2_onstack, sizeof(str2_onstack)) == 0)
00097 
00098 /*********************************************************************/
00105 static
00106 ibool
00107 row_add_table_to_background_drop_list(
00108 /*==================================*/
00109   const char* name);  
00111 /*******************************************************************/
00113 static
00114 void
00115 row_mysql_delay_if_needed(void)
00116 /*===========================*/
00117 {
00118   if (srv_dml_needed_delay) {
00119     os_thread_sleep(srv_dml_needed_delay);
00120   }
00121 }
00122 
00123 /*******************************************************************/
00125 UNIV_INTERN
00126 void
00127 row_mysql_prebuilt_free_blob_heap(
00128 /*==============================*/
00129   row_prebuilt_t* prebuilt) 
00131 {
00132   mem_heap_free(prebuilt->blob_heap);
00133   prebuilt->blob_heap = NULL;
00134 }
00135 
00136 /*******************************************************************/
00141 UNIV_INTERN
00142 byte*
00143 row_mysql_store_true_var_len(
00144 /*=========================*/
00145   byte* dest, 
00146   ulint len,  
00147   ulint lenlen) 
00148 {
00149   if (lenlen == 2) {
00150     ut_a(len < 256 * 256);
00151 
00152     mach_write_to_2_little_endian(dest, len);
00153 
00154     return(dest + 2);
00155   }
00156 
00157   ut_a(lenlen == 1);
00158   ut_a(len < 256);
00159 
00160   mach_write_to_1(dest, len);
00161 
00162   return(dest + 1);
00163 }
00164 
00165 /*******************************************************************/
00170 UNIV_INTERN
00171 const byte*
00172 row_mysql_read_true_varchar(
00173 /*========================*/
00174   ulint*    len,  
00175   const byte* field,  
00176   ulint   lenlen) 
00178 {
00179   if (lenlen == 2) {
00180     *len = mach_read_from_2_little_endian(field);
00181 
00182     return(field + 2);
00183   }
00184 
00185   ut_a(lenlen == 1);
00186 
00187   *len = mach_read_from_1(field);
00188 
00189   return(field + 1);
00190 }
00191 
00192 /*******************************************************************/
00194 UNIV_INTERN
00195 void
00196 row_mysql_store_blob_ref(
00197 /*=====================*/
00198   byte*   dest, 
00199   ulint   col_len,
00203   const void* data, 
00205   ulint   len)  
00209 {
00210   /* MySQL might assume the field is set to zero except the length and
00211   the pointer fields */
00212 
00213   memset(dest, '\0', col_len);
00214 
00215   /* In dest there are 1 - 4 bytes reserved for the BLOB length,
00216   and after that 8 bytes reserved for the pointer to the data.
00217   In 32-bit architectures we only use the first 4 bytes of the pointer
00218   slot. */
00219 
00220   ut_a(col_len - 8 > 1 || len < 256);
00221   ut_a(col_len - 8 > 2 || len < 256 * 256);
00222   ut_a(col_len - 8 > 3 || len < 256 * 256 * 256);
00223 
00224   mach_write_to_n_little_endian(dest, col_len - 8, len);
00225 
00226   memcpy(dest + col_len - 8, &data, sizeof data);
00227 }
00228 
00229 /*******************************************************************/
00232 UNIV_INTERN
00233 const byte*
00234 row_mysql_read_blob_ref(
00235 /*====================*/
00236   ulint*    len,    
00237   const byte* ref,    
00239   ulint   col_len)  
00241 {
00242   byte* data;
00243 
00244   *len = mach_read_from_n_little_endian(ref, col_len - 8);
00245 
00246   memcpy(&data, ref + col_len - 8, sizeof data);
00247 
00248   return(data);
00249 }
00250 
00251 /**************************************************************/
00253 UNIV_INTERN
00254 void
00255 row_mysql_pad_col(
00256 /*==============*/
00257   ulint mbminlen, 
00259   byte* pad,    
00260   ulint len)    
00261 {
00262   const byte* pad_end;
00263 
00264   switch (UNIV_EXPECT(mbminlen, 1)) {
00265   default:
00266     ut_error;
00267   case 1:
00268     /* space=0x20 */
00269     memset(pad, 0x20, len);
00270     break;
00271   case 2:
00272     /* space=0x0020 */
00273     pad_end = pad + len;
00274     ut_a(!(len % 2));
00275     do {
00276       *pad++ = 0x00;
00277       *pad++ = 0x20;
00278     } while (pad < pad_end);
00279     break;
00280   case 4:
00281     /* space=0x00000020 */
00282     pad_end = pad + len;
00283     ut_a(!(len % 4));
00284     do {
00285       *pad++ = 0x00;
00286       *pad++ = 0x00;
00287       *pad++ = 0x00;
00288       *pad++ = 0x20;
00289     } while (pad < pad_end);
00290     break;
00291   }
00292 }
00293 
00294 /**************************************************************/
00299 UNIV_INTERN
00300 byte*
00301 row_mysql_store_col_in_innobase_format(
00302 /*===================================*/
00303   dfield_t* dfield,   
00306   byte*   buf,    
00309   ibool   row_format_col, 
00316   const byte* mysql_data, 
00321   ulint   col_len,  
00327   ulint   comp)   
00328 {
00329   const byte* ptr = mysql_data;
00330   const dtype_t*  dtype;
00331   ulint   type;
00332   ulint   lenlen;
00333 
00334   dtype = dfield_get_type(dfield);
00335 
00336   type = dtype->mtype;
00337 
00338   if (type == DATA_INT) {
00339     /* Store integer data in Innobase in a big-endian format,
00340     sign bit negated if the data is a signed integer. In MySQL,
00341     integers are stored in a little-endian format. */
00342 
00343     byte* p = buf + col_len;
00344 
00345     for (;;) {
00346       p--;
00347       *p = *mysql_data;
00348       if (p == buf) {
00349         break;
00350       }
00351       mysql_data++;
00352     }
00353 
00354     if (!(dtype->prtype & DATA_UNSIGNED)) {
00355 
00356       *buf ^= 128;
00357     }
00358 
00359     ptr = buf;
00360     buf += col_len;
00361   } else if ((type == DATA_VARCHAR
00362         || type == DATA_VARMYSQL
00363         || type == DATA_BINARY)) {
00364 
00365     if (dtype_get_mysql_type(dtype) == DATA_MYSQL_TRUE_VARCHAR) {
00366       /* The length of the actual data is stored to 1 or 2
00367       bytes at the start of the field */
00368 
00369       if (row_format_col) {
00370         if (dtype->prtype & DATA_LONG_TRUE_VARCHAR) {
00371           lenlen = 2;
00372         } else {
00373           lenlen = 1;
00374         }
00375       } else {
00376         /* In a MySQL key value, lenlen is always 2 */
00377         lenlen = 2;
00378       }
00379 
00380       ptr = row_mysql_read_true_varchar(&col_len, mysql_data,
00381                 lenlen);
00382     } else {
00383       /* Remove trailing spaces from old style VARCHAR
00384       columns. */
00385 
00386       /* Handle Unicode strings differently. */
00387       ulint mbminlen  = dtype_get_mbminlen(dtype);
00388 
00389       ptr = mysql_data;
00390 
00391       switch (mbminlen) {
00392       default:
00393         ut_error;
00394       case 4:
00395         /* space=0x00000020 */
00396         /* Trim "half-chars", just in case. */
00397         col_len &= ~3;
00398 
00399         while (col_len >= 4
00400                && ptr[col_len - 4] == 0x00
00401                && ptr[col_len - 3] == 0x00
00402                && ptr[col_len - 2] == 0x00
00403                && ptr[col_len - 1] == 0x20) {
00404           col_len -= 4;
00405         }
00406         break;
00407       case 2:
00408         /* space=0x0020 */
00409         /* Trim "half-chars", just in case. */
00410         col_len &= ~1;
00411 
00412         while (col_len >= 2 && ptr[col_len - 2] == 0x00
00413                && ptr[col_len - 1] == 0x20) {
00414           col_len -= 2;
00415         }
00416         break;
00417       case 1:
00418         /* space=0x20 */
00419         while (col_len > 0
00420                && ptr[col_len - 1] == 0x20) {
00421           col_len--;
00422         }
00423       }
00424     }
00425   } else if (comp && type == DATA_MYSQL
00426        && dtype_get_mbminlen(dtype) == 1
00427        && dtype_get_mbmaxlen(dtype) > 1) {
00428     /* In some cases we strip trailing spaces from UTF-8 and other
00429     multibyte charsets, from FIXED-length CHAR columns, to save
00430     space. UTF-8 would otherwise normally use 3 * the string length
00431     bytes to store an ASCII string! */
00432 
00433     /* We assume that this CHAR field is encoded in a
00434     variable-length character set where spaces have
00435     1:1 correspondence to 0x20 bytes, such as UTF-8.
00436 
00437     Consider a CHAR(n) field, a field of n characters.
00438     It will contain between n * mbminlen and n * mbmaxlen bytes.
00439     We will try to truncate it to n bytes by stripping
00440     space padding.  If the field contains single-byte
00441     characters only, it will be truncated to n characters.
00442     Consider a CHAR(5) field containing the string ".a   "
00443     where "." denotes a 3-byte character represented by
00444     the bytes "$%&".  After our stripping, the string will
00445     be stored as "$%&a " (5 bytes).  The string ".abc "
00446     will be stored as "$%&abc" (6 bytes).
00447 
00448     The space padding will be restored in row0sel.c, function
00449     row_sel_field_store_in_mysql_format(). */
00450 
00451     ulint   n_chars;
00452 
00453     ut_a(!(dtype_get_len(dtype) % dtype_get_mbmaxlen(dtype)));
00454 
00455     n_chars = dtype_get_len(dtype) / dtype_get_mbmaxlen(dtype);
00456 
00457     /* Strip space padding. */
00458     while (col_len > n_chars && ptr[col_len - 1] == 0x20) {
00459       col_len--;
00460     }
00461   } else if (type == DATA_BLOB && row_format_col) {
00462 
00463     ptr = row_mysql_read_blob_ref(&col_len, mysql_data, col_len);
00464   }
00465 
00466   dfield_set_data(dfield, ptr, col_len);
00467 
00468   return(buf);
00469 }
00470 
00471 /**************************************************************/
00475 static
00476 void
00477 row_mysql_convert_row_to_innobase(
00478 /*==============================*/
00479   dtuple_t* row,    
00482   row_prebuilt_t* prebuilt, 
00484   byte*   mysql_rec)  
00488 {
00489   const mysql_row_templ_t*templ;
00490   dfield_t*   dfield;
00491   ulint     i;
00492 
00493   ut_ad(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW);
00494   ut_ad(prebuilt->mysql_template);
00495 
00496   for (i = 0; i < prebuilt->n_template; i++) {
00497 
00498     templ = prebuilt->mysql_template + i;
00499     dfield = dtuple_get_nth_field(row, i);
00500 
00501     if (templ->mysql_null_bit_mask != 0) {
00502       /* Column may be SQL NULL */
00503 
00504       if (mysql_rec[templ->mysql_null_byte_offset]
00505           & (byte) (templ->mysql_null_bit_mask)) {
00506 
00507         /* It is SQL NULL */
00508 
00509         dfield_set_null(dfield);
00510 
00511         goto next_column;
00512       }
00513     }
00514 
00515     row_mysql_store_col_in_innobase_format(
00516       dfield,
00517       prebuilt->ins_upd_rec_buff + templ->mysql_col_offset,
00518       TRUE, /* MySQL row format data */
00519       mysql_rec + templ->mysql_col_offset,
00520       templ->mysql_col_len,
00521       dict_table_is_comp(prebuilt->table));
00522 next_column:
00523     ;
00524   }
00525 }
00526 
00527 /****************************************************************/
00531 UNIV_INTERN
00532 ibool
00533 row_mysql_handle_errors(
00534 /*====================*/
00535   ulint*    new_err,
00539   trx_t*    trx,  
00540   que_thr_t*  thr,  
00541   trx_savept_t* savept) 
00542 {
00543   ulint err;
00544 
00545 handle_new_error:
00546   err = trx->error_state;
00547 
00548   ut_a(err != DB_SUCCESS);
00549 
00550   trx->error_state = DB_SUCCESS;
00551 
00552   switch (err) {
00553   case DB_LOCK_WAIT_TIMEOUT:
00554     if (row_rollback_on_timeout) {
00555       trx_general_rollback_for_mysql(trx, NULL);
00556       break;
00557     }
00558     /* fall through */
00559   case DB_DUPLICATE_KEY:
00560   case DB_FOREIGN_DUPLICATE_KEY:
00561   case DB_TOO_BIG_RECORD:
00562   case DB_ROW_IS_REFERENCED:
00563   case DB_NO_REFERENCED_ROW:
00564   case DB_CANNOT_ADD_CONSTRAINT:
00565   case DB_TOO_MANY_CONCURRENT_TRXS:
00566   case DB_OUT_OF_FILE_SPACE:
00567   case DB_INTERRUPTED:
00568     if (savept) {
00569       /* Roll back the latest, possibly incomplete
00570       insertion or update */
00571 
00572       trx_general_rollback_for_mysql(trx, savept);
00573     }
00574     /* MySQL will roll back the latest SQL statement */
00575     break;
00576   case DB_LOCK_WAIT:
00577     srv_suspend_mysql_thread(thr);
00578 
00579     if (trx->error_state != DB_SUCCESS) {
00580       que_thr_stop_for_mysql(thr);
00581 
00582       goto handle_new_error;
00583     }
00584 
00585     *new_err = err;
00586 
00587     return(TRUE);
00588 
00589   case DB_DEADLOCK:
00590   case DB_LOCK_TABLE_FULL:
00591     /* Roll back the whole transaction; this resolution was added
00592     to version 3.23.43 */
00593 
00594     trx_general_rollback_for_mysql(trx, NULL);
00595     break;
00596 
00597   case DB_MUST_GET_MORE_FILE_SPACE:
00598     fputs("InnoDB: The database cannot continue"
00599           " operation because of\n"
00600           "InnoDB: lack of space. You must add"
00601           " a new data file to\n"
00602           "InnoDB: my.cnf and restart the database.\n", stderr);
00603 
00604     exit(1);
00605 
00606   case DB_CORRUPTION:
00607     fputs("InnoDB: We detected index corruption"
00608           " in an InnoDB type table.\n"
00609           "InnoDB: You have to dump + drop + reimport"
00610           " the table or, in\n"
00611           "InnoDB: a case of widespread corruption,"
00612           " dump all InnoDB\n"
00613           "InnoDB: tables and recreate the"
00614           " whole InnoDB tablespace.\n"
00615           "InnoDB: If the mysqld server crashes"
00616           " after the startup or when\n"
00617           "InnoDB: you dump the tables, look at\n"
00618           "InnoDB: " REFMAN "forcing-recovery.html"
00619           " for help.\n", stderr);
00620     break;
00621   case DB_FOREIGN_EXCEED_MAX_CASCADE:
00622     fprintf(stderr, "InnoDB: Cannot delete/update rows with"
00623       " cascading foreign key constraints that exceed max"
00624       " depth of %lu\n"
00625       "Please drop excessive foreign constraints"
00626       " and try again\n", (ulong) DICT_FK_MAX_RECURSIVE_LOAD);
00627     break;
00628   default:
00629     fprintf(stderr, "InnoDB: unknown error code %lu\n",
00630       (ulong) err);
00631     ut_error;
00632   }
00633 
00634   if (trx->error_state != DB_SUCCESS) {
00635     *new_err = trx->error_state;
00636   } else {
00637     *new_err = err;
00638   }
00639 
00640   trx->error_state = DB_SUCCESS;
00641 
00642   return(FALSE);
00643 }
00644 
00645 /********************************************************************/
00648 UNIV_INTERN
00649 row_prebuilt_t*
00650 row_create_prebuilt(
00651 /*================*/
00652   dict_table_t* table)  
00653 {
00654   row_prebuilt_t* prebuilt;
00655   mem_heap_t* heap;
00656   dict_index_t* clust_index;
00657   dtuple_t* ref;
00658   ulint   ref_len;
00659 
00660   heap = mem_heap_create(sizeof *prebuilt + 128);
00661 
00662   prebuilt = static_cast<row_prebuilt_t *>(mem_heap_zalloc(heap, sizeof *prebuilt));
00663 
00664   prebuilt->magic_n = ROW_PREBUILT_ALLOCATED;
00665   prebuilt->magic_n2 = ROW_PREBUILT_ALLOCATED;
00666 
00667   prebuilt->table = table;
00668 
00669   prebuilt->sql_stat_start = TRUE;
00670   prebuilt->heap = heap;
00671 
00672   prebuilt->pcur = btr_pcur_create_for_mysql();
00673   prebuilt->clust_pcur = btr_pcur_create_for_mysql();
00674 
00675   prebuilt->select_lock_type = LOCK_NONE;
00676   prebuilt->stored_select_lock_type = 99999999;
00677   UNIV_MEM_INVALID(&prebuilt->stored_select_lock_type,
00678        sizeof prebuilt->stored_select_lock_type);
00679 
00680   prebuilt->search_tuple = dtuple_create(
00681     heap, 2 * dict_table_get_n_cols(table));
00682 
00683   clust_index = dict_table_get_first_index(table);
00684 
00685   /* Make sure that search_tuple is long enough for clustered index */
00686   ut_a(2 * dict_table_get_n_cols(table) >= clust_index->n_fields);
00687 
00688   ref_len = dict_index_get_n_unique(clust_index);
00689 
00690   ref = dtuple_create(heap, ref_len);
00691 
00692   dict_index_copy_types(ref, clust_index, ref_len);
00693 
00694   prebuilt->clust_ref = ref;
00695 
00696   prebuilt->autoinc_error = 0;
00697   prebuilt->autoinc_offset = 0;
00698 
00699   /* Default to 1, we will set the actual value later in 
00700   ha_innobase::get_auto_increment(). */
00701   prebuilt->autoinc_increment = 1;
00702 
00703   prebuilt->autoinc_last_value = 0;
00704 
00705   return(prebuilt);
00706 }
00707 
00708 /********************************************************************/
00710 UNIV_INTERN
00711 void
00712 row_prebuilt_free(
00713 /*==============*/
00714   row_prebuilt_t* prebuilt, 
00715   ibool   dict_locked)  
00716 {
00717   ulint i;
00718 
00719   if (UNIV_UNLIKELY
00720       (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED
00721        || prebuilt->magic_n2 != ROW_PREBUILT_ALLOCATED)) {
00722 
00723     fprintf(stderr,
00724       "InnoDB: Error: trying to free a corrupt\n"
00725       "InnoDB: table handle. Magic n %lu,"
00726       " magic n2 %lu, table name ",
00727       (ulong) prebuilt->magic_n,
00728       (ulong) prebuilt->magic_n2);
00729     ut_print_name(stderr, NULL, TRUE, prebuilt->table->name);
00730     putc('\n', stderr);
00731 
00732     mem_analyze_corruption(prebuilt);
00733 
00734     ut_error;
00735   }
00736 
00737   prebuilt->magic_n = ROW_PREBUILT_FREED;
00738   prebuilt->magic_n2 = ROW_PREBUILT_FREED;
00739 
00740   btr_pcur_free_for_mysql(prebuilt->pcur);
00741   btr_pcur_free_for_mysql(prebuilt->clust_pcur);
00742 
00743   if (prebuilt->mysql_template) {
00744     mem_free(prebuilt->mysql_template);
00745   }
00746 
00747   if (prebuilt->ins_graph) {
00748     que_graph_free_recursive(prebuilt->ins_graph);
00749   }
00750 
00751   if (prebuilt->sel_graph) {
00752     que_graph_free_recursive(prebuilt->sel_graph);
00753   }
00754 
00755   if (prebuilt->upd_graph) {
00756     que_graph_free_recursive(prebuilt->upd_graph);
00757   }
00758 
00759   if (prebuilt->blob_heap) {
00760     mem_heap_free(prebuilt->blob_heap);
00761   }
00762 
00763   if (prebuilt->old_vers_heap) {
00764     mem_heap_free(prebuilt->old_vers_heap);
00765   }
00766 
00767   for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) {
00768     if (prebuilt->fetch_cache[i] != NULL) {
00769 
00770       if ((ROW_PREBUILT_FETCH_MAGIC_N != mach_read_from_4(
00771              (prebuilt->fetch_cache[i]) - 4))
00772           || (ROW_PREBUILT_FETCH_MAGIC_N != mach_read_from_4(
00773           (prebuilt->fetch_cache[i])
00774           + prebuilt->mysql_row_len))) {
00775         fputs("InnoDB: Error: trying to free"
00776               " a corrupt fetch buffer.\n", stderr);
00777 
00778         mem_analyze_corruption(
00779           prebuilt->fetch_cache[i]);
00780 
00781         ut_error;
00782       }
00783 
00784       mem_free((prebuilt->fetch_cache[i]) - 4);
00785     }
00786   }
00787 
00788   dict_table_decrement_handle_count(prebuilt->table, dict_locked);
00789 
00790   mem_heap_free(prebuilt->heap);
00791 }
00792 
00793 /*********************************************************************/
00796 UNIV_INTERN
00797 void
00798 row_update_prebuilt_trx(
00799 /*====================*/
00800   row_prebuilt_t* prebuilt, 
00802   trx_t*    trx)    
00803 {
00804   if (trx->magic_n != TRX_MAGIC_N) {
00805     fprintf(stderr,
00806       "InnoDB: Error: trying to use a corrupt\n"
00807       "InnoDB: trx handle. Magic n %lu\n",
00808       (ulong) trx->magic_n);
00809 
00810     mem_analyze_corruption(trx);
00811 
00812     ut_error;
00813   }
00814 
00815   if (prebuilt->magic_n != ROW_PREBUILT_ALLOCATED) {
00816     fprintf(stderr,
00817       "InnoDB: Error: trying to use a corrupt\n"
00818       "InnoDB: table handle. Magic n %lu, table name ",
00819       (ulong) prebuilt->magic_n);
00820     ut_print_name(stderr, trx, TRUE, prebuilt->table->name);
00821     putc('\n', stderr);
00822 
00823     mem_analyze_corruption(prebuilt);
00824 
00825     ut_error;
00826   }
00827 
00828   prebuilt->trx = trx;
00829 
00830   if (prebuilt->ins_graph) {
00831     prebuilt->ins_graph->trx = trx;
00832   }
00833 
00834   if (prebuilt->upd_graph) {
00835     prebuilt->upd_graph->trx = trx;
00836   }
00837 
00838   if (prebuilt->sel_graph) {
00839     prebuilt->sel_graph->trx = trx;
00840   }
00841 }
00842 
00843 dtuple_t* row_get_prebuilt_insert_row(row_prebuilt_t* prebuilt);
00844 
00845 /*********************************************************************/
00850 dtuple_t*
00851 row_get_prebuilt_insert_row(
00852 /*========================*/
00853   row_prebuilt_t* prebuilt) 
00855 {
00856   ins_node_t* node;
00857   dtuple_t* row;
00858   dict_table_t* table = prebuilt->table;
00859 
00860   ut_ad(prebuilt && table && prebuilt->trx);
00861 
00862   if (prebuilt->ins_node == NULL) {
00863 
00864     /* Not called before for this handle: create an insert node
00865     and query graph to the prebuilt struct */
00866 
00867     node = ins_node_create(INS_DIRECT, table, prebuilt->heap);
00868 
00869     prebuilt->ins_node = node;
00870 
00871     if (prebuilt->ins_upd_rec_buff == NULL) {
00872       prebuilt->ins_upd_rec_buff = static_cast<byte *>(mem_heap_alloc(
00873         prebuilt->heap, prebuilt->mysql_row_len));
00874     }
00875 
00876     row = dtuple_create(prebuilt->heap,
00877             dict_table_get_n_cols(table));
00878 
00879     dict_table_copy_types(row, table);
00880 
00881     ins_node_set_new_row(node, row);
00882 
00883     prebuilt->ins_graph = static_cast<que_fork_t *>(que_node_get_parent(
00884       pars_complete_graph_for_exec(node,
00885                  prebuilt->trx,
00886                  prebuilt->heap)));
00887     prebuilt->ins_graph->state = QUE_FORK_ACTIVE;
00888   }
00889 
00890   return(prebuilt->ins_node->row);
00891 }
00892 
00893 /*********************************************************************/
00896 UNIV_INLINE
00897 void
00898 row_update_statistics_if_needed(
00899 /*============================*/
00900   dict_table_t* table)  
00901 {
00902   ulint counter;
00903 
00904   counter = table->stat_modified_counter;
00905 
00906   table->stat_modified_counter = counter + 1;
00907 
00908   /* Calculate new statistics if 1 / 16 of table has been modified
00909   since the last time a statistics batch was run, or if
00910   stat_modified_counter > 2 000 000 000 (to avoid wrap-around).
00911   We calculate statistics at most every 16th round, since we may have
00912   a counter table which is very small and updated very often. */
00913 
00914   if (counter > 2000000000
00915       || ((ib_int64_t)counter > 16 + table->stat_n_rows / 16)) {
00916 
00917     dict_update_statistics(table, FALSE /* update even if stats
00918                 are initialized */);
00919   }
00920 }
00921 
00922 /*********************************************************************/
00926 UNIV_INTERN
00927 void
00928 row_unlock_table_autoinc_for_mysql(
00929 /*===============================*/
00930   trx_t*  trx)  
00931 {
00932   if (lock_trx_holds_autoinc_locks(trx)) {
00933     mutex_enter(&kernel_mutex);
00934 
00935     lock_release_autoinc_locks(trx);
00936 
00937     mutex_exit(&kernel_mutex);
00938   }
00939 }
00940 
00941 /*********************************************************************/
00948 UNIV_INTERN
00949 int
00950 row_lock_table_autoinc_for_mysql(
00951 /*=============================*/
00952   row_prebuilt_t* prebuilt) 
00954 {
00955   trx_t*      trx = prebuilt->trx;
00956   ins_node_t*   node  = prebuilt->ins_node;
00957   const dict_table_t* table = prebuilt->table;
00958   que_thr_t*    thr;
00959   ulint     err;
00960   ibool     was_lock_wait;
00961 
00962   ut_ad(trx);
00963   ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
00964 
00965   /* If we already hold an AUTOINC lock on the table then do nothing.
00966         Note: We peek at the value of the current owner without acquiring
00967   the kernel mutex. **/
00968   if (trx == table->autoinc_trx) {
00969 
00970     return(DB_SUCCESS);
00971   }
00972 
00973   trx->op_info = "setting auto-inc lock";
00974 
00975   if (node == NULL) {
00976     row_get_prebuilt_insert_row(prebuilt);
00977     node = prebuilt->ins_node;
00978   }
00979 
00980   /* We use the insert query graph as the dummy graph needed
00981   in the lock module call */
00982 
00983   thr = que_fork_get_first_thr(prebuilt->ins_graph);
00984 
00985   que_thr_move_to_run_state_for_mysql(thr, trx);
00986 
00987 run_again:
00988   thr->run_node = node;
00989   thr->prev_node = node;
00990 
00991   /* It may be that the current session has not yet started
00992   its transaction, or it has been committed: */
00993 
00994   trx_start_if_not_started(trx);
00995 
00996   err = lock_table(0, prebuilt->table, LOCK_AUTO_INC, thr);
00997 
00998   trx->error_state = err;
00999 
01000   if (err != DB_SUCCESS) {
01001     que_thr_stop_for_mysql(thr);
01002 
01003     was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL);
01004 
01005     if (was_lock_wait) {
01006       goto run_again;
01007     }
01008 
01009     trx->op_info = "";
01010 
01011     return((int) err);
01012   }
01013 
01014   que_thr_stop_for_mysql_no_error(thr, trx);
01015 
01016   trx->op_info = "";
01017 
01018   return((int) err);
01019 }
01020 
01021 /*********************************************************************/
01024 UNIV_INTERN
01025 int
01026 row_lock_table_for_mysql(
01027 /*=====================*/
01028   row_prebuilt_t* prebuilt, 
01030   dict_table_t* table,    
01034   ulint   mode)   
01036 {
01037   trx_t*    trx   = prebuilt->trx;
01038   que_thr_t*  thr;
01039   ulint   err;
01040   ibool   was_lock_wait;
01041 
01042   ut_ad(trx);
01043   ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
01044 
01045   trx->op_info = "setting table lock";
01046 
01047   if (prebuilt->sel_graph == NULL) {
01048     /* Build a dummy select query graph */
01049     row_prebuild_sel_graph(prebuilt);
01050   }
01051 
01052   /* We use the select query graph as the dummy graph needed
01053   in the lock module call */
01054 
01055   thr = que_fork_get_first_thr(prebuilt->sel_graph);
01056 
01057   que_thr_move_to_run_state_for_mysql(thr, trx);
01058 
01059 run_again:
01060   thr->run_node = thr;
01061   thr->prev_node = thr->common.parent;
01062 
01063   /* It may be that the current session has not yet started
01064   its transaction, or it has been committed: */
01065 
01066   trx_start_if_not_started(trx);
01067 
01068   if (table) {
01069     err = lock_table(0, table, static_cast<lock_mode>(mode), thr);
01070   } else {
01071     err = lock_table(0, prebuilt->table,
01072          static_cast<lock_mode>(prebuilt->select_lock_type), thr);
01073   }
01074 
01075   trx->error_state = err;
01076 
01077   if (err != DB_SUCCESS) {
01078     que_thr_stop_for_mysql(thr);
01079 
01080     was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL);
01081 
01082     if (was_lock_wait) {
01083       goto run_again;
01084     }
01085 
01086     trx->op_info = "";
01087 
01088     return((int) err);
01089   }
01090 
01091   que_thr_stop_for_mysql_no_error(thr, trx);
01092 
01093   trx->op_info = "";
01094 
01095   return((int) err);
01096 }
01097 
01098 /*********************************************************************/
01101 UNIV_INTERN
01102 int
01103 row_insert_for_mysql(
01104 /*=================*/
01105   byte*   mysql_rec,  
01106   row_prebuilt_t* prebuilt) 
01108 {
01109   trx_savept_t  savept;
01110   que_thr_t*  thr;
01111   ulint   err;
01112   ibool   was_lock_wait;
01113   trx_t*    trx   = prebuilt->trx;
01114   ins_node_t* node    = prebuilt->ins_node;
01115 
01116   ut_ad(trx);
01117   ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
01118 
01119   if (prebuilt->table->ibd_file_missing) {
01120     ut_print_timestamp(stderr);
01121     fprintf(stderr, "  InnoDB: Error:\n"
01122       "InnoDB: MySQL is trying to use a table handle"
01123       " but the .ibd file for\n"
01124       "InnoDB: table %s does not exist.\n"
01125       "InnoDB: Have you deleted the .ibd file"
01126       " from the database directory under\n"
01127       "InnoDB: the MySQL datadir, or have you"
01128       " used DISCARD TABLESPACE?\n"
01129       "InnoDB: Look from\n"
01130       "InnoDB: " REFMAN "innodb-troubleshooting.html\n"
01131       "InnoDB: how you can resolve the problem.\n",
01132       prebuilt->table->name);
01133     return(DB_ERROR);
01134   }
01135 
01136   if (UNIV_UNLIKELY(prebuilt->magic_n != ROW_PREBUILT_ALLOCATED)) {
01137     fprintf(stderr,
01138       "InnoDB: Error: trying to free a corrupt\n"
01139       "InnoDB: table handle. Magic n %lu, table name ",
01140       (ulong) prebuilt->magic_n);
01141     ut_print_name(stderr, trx, TRUE, prebuilt->table->name);
01142     putc('\n', stderr);
01143 
01144     mem_analyze_corruption(prebuilt);
01145 
01146     ut_error;
01147   }
01148 
01149   if (UNIV_UNLIKELY(srv_created_new_raw || srv_force_recovery)) {
01150     fputs("InnoDB: A new raw disk partition was initialized or\n"
01151           "InnoDB: innodb_force_recovery is on: we do not allow\n"
01152           "InnoDB: database modifications by the user. Shut down\n"
01153           "InnoDB: mysqld and edit my.cnf so that"
01154           " newraw is replaced\n"
01155           "InnoDB: with raw, and innodb_force_... is removed.\n",
01156           stderr);
01157 
01158     return(DB_ERROR);
01159   }
01160 
01161   trx->op_info = "inserting";
01162 
01163   row_mysql_delay_if_needed();
01164 
01165   trx_start_if_not_started(trx);
01166 
01167   if (node == NULL) {
01168     row_get_prebuilt_insert_row(prebuilt);
01169     node = prebuilt->ins_node;
01170   }
01171 
01172   row_mysql_convert_row_to_innobase(node->row, prebuilt, mysql_rec);
01173 
01174   savept = trx_savept_take(trx);
01175 
01176   thr = que_fork_get_first_thr(prebuilt->ins_graph);
01177 
01178   if (prebuilt->sql_stat_start) {
01179     node->state = INS_NODE_SET_IX_LOCK;
01180     prebuilt->sql_stat_start = FALSE;
01181   } else {
01182     node->state = INS_NODE_ALLOC_ROW_ID;
01183   }
01184 
01185   que_thr_move_to_run_state_for_mysql(thr, trx);
01186 
01187 run_again:
01188   thr->run_node = node;
01189   thr->prev_node = node;
01190 
01191   row_ins_step(thr);
01192 
01193   err = trx->error_state;
01194 
01195   if (err != DB_SUCCESS) {
01196     que_thr_stop_for_mysql(thr);
01197 
01198     /* TODO: what is this? */ thr->lock_state= QUE_THR_LOCK_ROW;
01199 
01200     was_lock_wait = row_mysql_handle_errors(&err, trx, thr,
01201               &savept);
01202     thr->lock_state= QUE_THR_LOCK_NOLOCK;
01203 
01204     if (was_lock_wait) {
01205       goto run_again;
01206     }
01207 
01208     trx->op_info = "";
01209 
01210     return((int) err);
01211   }
01212 
01213   que_thr_stop_for_mysql_no_error(thr, trx);
01214 
01215   prebuilt->table->stat_n_rows++;
01216 
01217   srv_n_rows_inserted++;
01218 
01219   if (prebuilt->table->stat_n_rows == 0) {
01220     /* Avoid wrap-over */
01221     prebuilt->table->stat_n_rows--;
01222   }
01223 
01224   row_update_statistics_if_needed(prebuilt->table);
01225   trx->op_info = "";
01226 
01227   return((int) err);
01228 }
01229 
01230 /*********************************************************************/
01232 UNIV_INTERN
01233 void
01234 row_prebuild_sel_graph(
01235 /*===================*/
01236   row_prebuilt_t* prebuilt) 
01238 {
01239   sel_node_t* node;
01240 
01241   ut_ad(prebuilt && prebuilt->trx);
01242 
01243   if (prebuilt->sel_graph == NULL) {
01244 
01245     node = sel_node_create(prebuilt->heap);
01246 
01247     prebuilt->sel_graph = static_cast<que_fork_t *>(que_node_get_parent(
01248       pars_complete_graph_for_exec(node,
01249                  prebuilt->trx,
01250                  prebuilt->heap)));
01251 
01252     prebuilt->sel_graph->state = QUE_FORK_ACTIVE;
01253   }
01254 }
01255 
01256 /*********************************************************************/
01260 UNIV_INTERN
01261 upd_node_t*
01262 row_create_update_node_for_mysql(
01263 /*=============================*/
01264   dict_table_t* table,  
01265   mem_heap_t* heap) 
01266 {
01267   upd_node_t* node;
01268 
01269   node = upd_node_create(heap);
01270 
01271   node->in_mysql_interface = TRUE;
01272   node->is_delete = FALSE;
01273   node->searched_update = FALSE;
01274   node->select = NULL;
01275   node->pcur = btr_pcur_create_for_mysql();
01276   node->table = table;
01277 
01278   node->update = upd_create(dict_table_get_n_cols(table), heap);
01279 
01280   node->update_n_fields = dict_table_get_n_cols(table);
01281 
01282   UT_LIST_INIT(node->columns);
01283   node->has_clust_rec_x_lock = TRUE;
01284   node->cmpl_info = 0;
01285 
01286   node->table_sym = NULL;
01287   node->col_assign_list = NULL;
01288 
01289   return(node);
01290 }
01291 
01292 /*********************************************************************/
01297 UNIV_INTERN
01298 upd_t*
01299 row_get_prebuilt_update_vector(
01300 /*===========================*/
01301   row_prebuilt_t* prebuilt) 
01303 {
01304   dict_table_t* table = prebuilt->table;
01305   upd_node_t* node;
01306 
01307   ut_ad(prebuilt && table && prebuilt->trx);
01308 
01309   if (prebuilt->upd_node == NULL) {
01310 
01311     /* Not called before for this handle: create an update node
01312     and query graph to the prebuilt struct */
01313 
01314     node = row_create_update_node_for_mysql(table, prebuilt->heap);
01315 
01316     prebuilt->upd_node = node;
01317 
01318     prebuilt->upd_graph = static_cast<que_fork_t *>(que_node_get_parent(
01319       pars_complete_graph_for_exec(node,
01320                  prebuilt->trx,
01321                  prebuilt->heap)));
01322     prebuilt->upd_graph->state = QUE_FORK_ACTIVE;
01323   }
01324 
01325   return(prebuilt->upd_node->update);
01326 }
01327 
01328 /*********************************************************************/
01331 UNIV_INTERN
01332 int
01333 row_update_for_mysql(
01334 /*=================*/
01335   byte*   mysql_rec,  
01337   row_prebuilt_t* prebuilt) 
01339 {
01340   trx_savept_t  savept;
01341   ulint   err;
01342   que_thr_t*  thr;
01343   ibool   was_lock_wait;
01344   dict_index_t* clust_index;
01345   /*  ulint   ref_len; */
01346   upd_node_t* node;
01347   dict_table_t* table   = prebuilt->table;
01348   trx_t*    trx   = prebuilt->trx;
01349 
01350   ut_ad(prebuilt && trx);
01351   ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
01352   UT_NOT_USED(mysql_rec);
01353 
01354   if (prebuilt->table->ibd_file_missing) {
01355     ut_print_timestamp(stderr);
01356     fprintf(stderr, "  InnoDB: Error:\n"
01357       "InnoDB: MySQL is trying to use a table handle"
01358       " but the .ibd file for\n"
01359       "InnoDB: table %s does not exist.\n"
01360       "InnoDB: Have you deleted the .ibd file"
01361       " from the database directory under\n"
01362       "InnoDB: the MySQL datadir, or have you"
01363       " used DISCARD TABLESPACE?\n"
01364       "InnoDB: Look from\n"
01365       "InnoDB: " REFMAN "innodb-troubleshooting.html\n"
01366       "InnoDB: how you can resolve the problem.\n",
01367       prebuilt->table->name);
01368     return(DB_ERROR);
01369   }
01370 
01371   if (UNIV_UNLIKELY(prebuilt->magic_n != ROW_PREBUILT_ALLOCATED)) {
01372     fprintf(stderr,
01373       "InnoDB: Error: trying to free a corrupt\n"
01374       "InnoDB: table handle. Magic n %lu, table name ",
01375       (ulong) prebuilt->magic_n);
01376     ut_print_name(stderr, trx, TRUE, prebuilt->table->name);
01377     putc('\n', stderr);
01378 
01379     mem_analyze_corruption(prebuilt);
01380 
01381     ut_error;
01382   }
01383 
01384   if (UNIV_UNLIKELY(srv_created_new_raw || srv_force_recovery)) {
01385     fputs("InnoDB: A new raw disk partition was initialized or\n"
01386           "InnoDB: innodb_force_recovery is on: we do not allow\n"
01387           "InnoDB: database modifications by the user. Shut down\n"
01388           "InnoDB: mysqld and edit my.cnf so that newraw"
01389           " is replaced\n"
01390           "InnoDB: with raw, and innodb_force_... is removed.\n",
01391           stderr);
01392 
01393     return(DB_ERROR);
01394   }
01395 
01396   trx->op_info = "updating or deleting";
01397 
01398   row_mysql_delay_if_needed();
01399 
01400   trx_start_if_not_started(trx);
01401 
01402   node = prebuilt->upd_node;
01403 
01404   clust_index = dict_table_get_first_index(table);
01405 
01406   if (prebuilt->pcur->btr_cur.index == clust_index) {
01407     btr_pcur_copy_stored_position(node->pcur, prebuilt->pcur);
01408   } else {
01409     btr_pcur_copy_stored_position(node->pcur,
01410                 prebuilt->clust_pcur);
01411   }
01412 
01413   ut_a(node->pcur->rel_pos == BTR_PCUR_ON);
01414 
01415   /* MySQL seems to call rnd_pos before updating each row it
01416   has cached: we can get the correct cursor position from
01417   prebuilt->pcur; NOTE that we cannot build the row reference
01418   from mysql_rec if the clustered index was automatically
01419   generated for the table: MySQL does not know anything about
01420   the row id used as the clustered index key */
01421 
01422   savept = trx_savept_take(trx);
01423 
01424   thr = que_fork_get_first_thr(prebuilt->upd_graph);
01425 
01426   node->state = UPD_NODE_UPDATE_CLUSTERED;
01427 
01428   ut_ad(!prebuilt->sql_stat_start);
01429 
01430   que_thr_move_to_run_state_for_mysql(thr, trx);
01431 
01432 run_again:
01433   thr->run_node = node;
01434   thr->prev_node = node;
01435   thr->fk_cascade_depth = 0;
01436 
01437   row_upd_step(thr);
01438 
01439   /* The recursive call for cascading update/delete happens
01440   in above row_upd_step(), reset the counter once we come
01441   out of the recursive call, so it does not accumulate for
01442   different row deletes */
01443   thr->fk_cascade_depth = 0;
01444 
01445   err = trx->error_state;
01446 
01447   /* Reset fk_cascade_depth back to 0 */
01448   thr->fk_cascade_depth = 0;
01449 
01450   if (err != DB_SUCCESS) {
01451     que_thr_stop_for_mysql(thr);
01452 
01453     if (err == DB_RECORD_NOT_FOUND) {
01454       trx->error_state = DB_SUCCESS;
01455       trx->op_info = "";
01456 
01457       return((int) err);
01458     }
01459 
01460     thr->lock_state= QUE_THR_LOCK_ROW;
01461     was_lock_wait = row_mysql_handle_errors(&err, trx, thr,
01462               &savept);
01463     thr->lock_state= QUE_THR_LOCK_NOLOCK;
01464 
01465     if (was_lock_wait) {
01466       goto run_again;
01467     }
01468 
01469     trx->op_info = "";
01470 
01471     return((int) err);
01472   }
01473 
01474   que_thr_stop_for_mysql_no_error(thr, trx);
01475 
01476   if (node->is_delete) {
01477     if (prebuilt->table->stat_n_rows > 0) {
01478       prebuilt->table->stat_n_rows--;
01479     }
01480 
01481     srv_n_rows_deleted++;
01482   } else {
01483     srv_n_rows_updated++;
01484   }
01485 
01486   /* We update table statistics only if it is a DELETE or UPDATE
01487   that changes indexed columns, UPDATEs that change only non-indexed
01488   columns would not affect statistics. */
01489   if (node->is_delete || !(node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
01490     row_update_statistics_if_needed(prebuilt->table);
01491   }
01492 
01493   trx->op_info = "";
01494 
01495   return((int) err);
01496 }
01497 
01498 /*********************************************************************/
01508 UNIV_INTERN
01509 int
01510 row_unlock_for_mysql(
01511 /*=================*/
01512   row_prebuilt_t* prebuilt, 
01514   ibool   has_latches_on_recs)
01519 {
01520   btr_pcur_t* pcur    = prebuilt->pcur;
01521   btr_pcur_t* clust_pcur  = prebuilt->clust_pcur;
01522   trx_t*    trx   = prebuilt->trx;
01523 
01524   ut_ad(prebuilt && trx);
01525   ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
01526 
01527   if (UNIV_UNLIKELY
01528       (!srv_locks_unsafe_for_binlog
01529        && trx->isolation_level > TRX_ISO_READ_COMMITTED)) {
01530 
01531     fprintf(stderr,
01532       "InnoDB: Error: calling row_unlock_for_mysql though\n"
01533       "InnoDB: innodb_locks_unsafe_for_binlog is FALSE and\n"
01534       "InnoDB: this session is not using"
01535       " READ COMMITTED isolation level.\n");
01536 
01537     return(DB_SUCCESS);
01538   }
01539 
01540   trx->op_info = "unlock_row";
01541 
01542   if (prebuilt->new_rec_locks >= 1) {
01543 
01544     const rec_t*  rec;
01545     dict_index_t* index;
01546     trx_id_t  rec_trx_id;
01547     mtr_t   mtr;
01548 
01549     mtr_start(&mtr);
01550 
01551     /* Restore the cursor position and find the record */
01552 
01553     if (!has_latches_on_recs) {
01554       btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, &mtr);
01555     }
01556 
01557     rec = btr_pcur_get_rec(pcur);
01558     index = btr_pcur_get_btr_cur(pcur)->index;
01559 
01560     if (prebuilt->new_rec_locks >= 2) {
01561       /* Restore the cursor position and find the record
01562       in the clustered index. */
01563 
01564       if (!has_latches_on_recs) {
01565         btr_pcur_restore_position(BTR_SEARCH_LEAF,
01566                 clust_pcur, &mtr);
01567       }
01568 
01569       rec = btr_pcur_get_rec(clust_pcur);
01570       index = btr_pcur_get_btr_cur(clust_pcur)->index;
01571     }
01572 
01573     if (UNIV_UNLIKELY(!dict_index_is_clust(index))) {
01574       /* This is not a clustered index record.  We
01575       do not know how to unlock the record. */
01576       goto no_unlock;
01577     }
01578 
01579     /* If the record has been modified by this
01580     transaction, do not unlock it. */
01581 
01582     if (index->trx_id_offset) {
01583       rec_trx_id = trx_read_trx_id(rec
01584                  + index->trx_id_offset);
01585     } else {
01586       mem_heap_t* heap      = NULL;
01587       ulint offsets_[REC_OFFS_NORMAL_SIZE];
01588       ulint*  offsets       = offsets_;
01589 
01590       rec_offs_init(offsets_);
01591       offsets = rec_get_offsets(rec, index, offsets,
01592               ULINT_UNDEFINED, &heap);
01593 
01594       rec_trx_id = row_get_rec_trx_id(rec, index, offsets);
01595 
01596       if (UNIV_LIKELY_NULL(heap)) {
01597         mem_heap_free(heap);
01598       }
01599     }
01600 
01601     if (rec_trx_id != trx->id) {
01602       /* We did not update the record: unlock it */
01603 
01604       rec = btr_pcur_get_rec(pcur);
01605       index = btr_pcur_get_btr_cur(pcur)->index;
01606 
01607       lock_rec_unlock(trx, btr_pcur_get_block(pcur),
01608           rec, static_cast<lock_mode>(prebuilt->select_lock_type));
01609 
01610       if (prebuilt->new_rec_locks >= 2) {
01611         rec = btr_pcur_get_rec(clust_pcur);
01612         index = btr_pcur_get_btr_cur(clust_pcur)->index;
01613 
01614         lock_rec_unlock(trx,
01615             btr_pcur_get_block(clust_pcur),
01616             rec,
01617             static_cast<lock_mode>(prebuilt->select_lock_type));
01618       }
01619     }
01620 no_unlock:
01621     mtr_commit(&mtr);
01622   }
01623 
01624   trx->op_info = "";
01625 
01626   return(DB_SUCCESS);
01627 }
01628 
01629 /**********************************************************************/
01632 UNIV_INTERN
01633 ulint
01634 row_update_cascade_for_mysql(
01635 /*=========================*/
01636   que_thr_t*  thr,  
01637   upd_node_t* node, 
01639   dict_table_t* table)  
01640 {
01641   ulint err;
01642   trx_t*  trx;
01643 
01644   trx = thr_get_trx(thr);
01645 
01646   /* Increment fk_cascade_depth to record the recursive call depth on
01647   a single update/delete that affects multiple tables chained
01648   together with foreign key relations. */
01649   thr->fk_cascade_depth++;
01650 
01651   if (thr->fk_cascade_depth > FK_MAX_CASCADE_DEL) {
01652     return (DB_FOREIGN_EXCEED_MAX_CASCADE);
01653   }
01654 run_again:
01655   thr->run_node = node;
01656   thr->prev_node = node;
01657 
01658   row_upd_step(thr);
01659 
01660   /* The recursive call for cascading update/delete happens
01661   in above row_upd_step(), reset the counter once we come
01662   out of the recursive call, so it does not accumulate for
01663   different row deletes */
01664   thr->fk_cascade_depth = 0;
01665 
01666   err = trx->error_state;
01667 
01668   /* Note that the cascade node is a subnode of another InnoDB
01669   query graph node. We do a normal lock wait in this node, but
01670   all errors are handled by the parent node. */
01671 
01672   if (err == DB_LOCK_WAIT) {
01673     /* Handle lock wait here */
01674 
01675     que_thr_stop_for_mysql(thr);
01676 
01677     srv_suspend_mysql_thread(thr);
01678 
01679     /* Note that a lock wait may also end in a lock wait timeout,
01680     or this transaction is picked as a victim in selective
01681     deadlock resolution */
01682 
01683     if (trx->error_state != DB_SUCCESS) {
01684 
01685       return(trx->error_state);
01686     }
01687 
01688     /* Retry operation after a normal lock wait */
01689 
01690     goto run_again;
01691   }
01692 
01693   if (err != DB_SUCCESS) {
01694 
01695     return(err);
01696   }
01697 
01698   if (node->is_delete) {
01699     if (table->stat_n_rows > 0) {
01700       table->stat_n_rows--;
01701     }
01702 
01703     srv_n_rows_deleted++;
01704   } else {
01705     srv_n_rows_updated++;
01706   }
01707 
01708   row_update_statistics_if_needed(table);
01709 
01710   return(err);
01711 }
01712 
01713 /*********************************************************************/
01717 UNIV_INTERN
01718 ibool
01719 row_table_got_default_clust_index(
01720 /*==============================*/
01721   const dict_table_t* table)  
01722 {
01723   const dict_index_t* clust_index;
01724 
01725   clust_index = dict_table_get_first_index(table);
01726 
01727   return(dict_index_get_nth_col(clust_index, 0)->mtype == DATA_SYS);
01728 }
01729 
01730 /*********************************************************************/
01733 UNIV_INTERN
01734 void
01735 row_mysql_freeze_data_dictionary_func(
01736 /*==================================*/
01737   trx_t*    trx,  
01738   const char* file, 
01739   ulint   line) 
01740 {
01741   ut_a(trx->dict_operation_lock_mode == 0);
01742 
01743   rw_lock_s_lock_func(&dict_operation_lock, 0, file, line);
01744 
01745   trx->dict_operation_lock_mode = RW_S_LATCH;
01746 }
01747 
01748 /*********************************************************************/
01750 UNIV_INTERN
01751 void
01752 row_mysql_unfreeze_data_dictionary(
01753 /*===============================*/
01754   trx_t*  trx)  
01755 {
01756   ut_a(trx->dict_operation_lock_mode == RW_S_LATCH);
01757 
01758   rw_lock_s_unlock(&dict_operation_lock);
01759 
01760   trx->dict_operation_lock_mode = 0;
01761 }
01762 
01763 /*********************************************************************/
01766 UNIV_INTERN
01767 void
01768 row_mysql_lock_data_dictionary_func(
01769 /*================================*/
01770   trx_t*    trx,  
01771   const char* file, 
01772   ulint   line) 
01773 {
01774   ut_a(trx->dict_operation_lock_mode == 0
01775        || trx->dict_operation_lock_mode == RW_X_LATCH);
01776 
01777   /* Serialize data dictionary operations with dictionary mutex:
01778   no deadlocks or lock waits can occur then in these operations */
01779 
01780   rw_lock_x_lock_func(&dict_operation_lock, 0, file, line);
01781   trx->dict_operation_lock_mode = RW_X_LATCH;
01782 
01783   mutex_enter(&(dict_sys->mutex));
01784 }
01785 
01786 /*********************************************************************/
01788 UNIV_INTERN
01789 void
01790 row_mysql_unlock_data_dictionary(
01791 /*=============================*/
01792   trx_t*  trx)  
01793 {
01794   ut_a(trx->dict_operation_lock_mode == RW_X_LATCH);
01795 
01796   /* Serialize data dictionary operations with dictionary mutex:
01797   no deadlocks can occur then in these operations */
01798 
01799   mutex_exit(&(dict_sys->mutex));
01800   rw_lock_x_unlock(&dict_operation_lock);
01801 
01802   trx->dict_operation_lock_mode = 0;
01803 }
01804 
01805 /*********************************************************************/
01812 UNIV_INTERN
01813 int
01814 row_create_table_for_mysql(
01815 /*=======================*/
01816   dict_table_t* table,  
01818   trx_t*    trx)  
01819 {
01820   tab_node_t* node;
01821   mem_heap_t* heap;
01822   que_thr_t*  thr;
01823   const char* table_name;
01824   ulint   table_name_len;
01825   ulint   err;
01826 
01827   ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
01828 #ifdef UNIV_SYNC_DEBUG
01829   ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
01830 #endif /* UNIV_SYNC_DEBUG */
01831   ut_ad(mutex_own(&(dict_sys->mutex)));
01832   ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);
01833 
01834   if (srv_created_new_raw) {
01835     fputs("InnoDB: A new raw disk partition was initialized:\n"
01836           "InnoDB: we do not allow database modifications"
01837           " by the user.\n"
01838           "InnoDB: Shut down mysqld and edit my.cnf so that newraw"
01839           " is replaced with raw.\n", stderr);
01840     dict_mem_table_free(table);
01841     trx_commit_for_mysql(trx);
01842 
01843     return(DB_ERROR);
01844   }
01845 
01846   trx->op_info = "creating table";
01847 
01848   trx_start_if_not_started(trx);
01849 
01850   /* The table name is prefixed with the database name and a '/'.
01851   Certain table names starting with 'innodb_' have their special
01852   meaning regardless of the database name.  Thus, we need to
01853   ignore the database name prefix in the comparisons. */
01854   table_name = strchr(table->name, '/');
01855   ut_a(table_name);
01856   table_name++;
01857   table_name_len = strlen(table_name) + 1;
01858 
01859   if (STR_EQ(table_name, table_name_len, S_innodb_monitor)) {
01860 
01861     /* Table equals "innodb_monitor":
01862     start monitor prints */
01863 
01864     srv_print_innodb_monitor = TRUE;
01865 
01866     /* The lock timeout monitor thread also takes care
01867     of InnoDB monitor prints */
01868 
01869     os_event_set(srv_lock_timeout_thread_event);
01870   } else if (STR_EQ(table_name, table_name_len,
01871         S_innodb_lock_monitor)) {
01872 
01873     srv_print_innodb_monitor = TRUE;
01874     srv_print_innodb_lock_monitor = TRUE;
01875     os_event_set(srv_lock_timeout_thread_event);
01876   } else if (STR_EQ(table_name, table_name_len,
01877         S_innodb_tablespace_monitor)) {
01878 
01879     srv_print_innodb_tablespace_monitor = TRUE;
01880     os_event_set(srv_lock_timeout_thread_event);
01881   } else if (STR_EQ(table_name, table_name_len,
01882         S_innodb_table_monitor)) {
01883 
01884     srv_print_innodb_table_monitor = TRUE;
01885     os_event_set(srv_lock_timeout_thread_event);
01886   } else if (STR_EQ(table_name, table_name_len,
01887         S_innodb_mem_validate)) {
01888     /* We define here a debugging feature intended for
01889     developers */
01890 
01891     fputs("Validating InnoDB memory:\n"
01892           "to use this feature you must compile InnoDB with\n"
01893           "UNIV_MEM_DEBUG defined in univ.i and"
01894           " the server must be\n"
01895           "quiet because allocation from a mem heap"
01896           " is not protected\n"
01897           "by any semaphore.\n", stderr);
01898 #ifdef UNIV_MEM_DEBUG
01899     ut_a(mem_validate());
01900     fputs("Memory validated\n", stderr);
01901 #else /* UNIV_MEM_DEBUG */
01902     fputs("Memory NOT validated (recompile with UNIV_MEM_DEBUG)\n",
01903           stderr);
01904 #endif /* UNIV_MEM_DEBUG */
01905   }
01906 
01907   heap = mem_heap_create(512);
01908 
01909   trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
01910 
01911   node = tab_create_graph_create(table, heap);
01912 
01913   thr = pars_complete_graph_for_exec(node, trx, heap);
01914 
01915   ut_a(thr == que_fork_start_command(static_cast<que_fork_t *>(que_node_get_parent(thr))));
01916   que_run_threads(thr);
01917 
01918   err = trx->error_state;
01919 
01920   switch (err) {
01921   case DB_SUCCESS:
01922     break;
01923   case DB_OUT_OF_FILE_SPACE:
01924     trx->error_state = DB_SUCCESS;
01925     trx_general_rollback_for_mysql(trx, NULL);
01926 
01927     ut_print_timestamp(stderr);
01928     fputs("  InnoDB: Warning: cannot create table ",
01929           stderr);
01930     ut_print_name(stderr, trx, TRUE, table->name);
01931     fputs(" because tablespace full\n", stderr);
01932 
01933     if (dict_table_get_low(table->name)) {
01934 
01935       row_drop_table_for_mysql(table->name, trx, FALSE);
01936       trx_commit_for_mysql(trx);
01937     }
01938     break;
01939 
01940   case DB_DUPLICATE_KEY:
01941   default:
01942     /* We may also get err == DB_ERROR if the .ibd file for the
01943     table already exists */
01944 
01945     trx->error_state = DB_SUCCESS;
01946     trx_general_rollback_for_mysql(trx, NULL);
01947     dict_mem_table_free(table);
01948     break;
01949   }
01950 
01951   que_graph_free((que_t*) que_node_get_parent(thr));
01952 
01953   trx->op_info = "";
01954 
01955   return((int) err);
01956 }
01957 
01958 /*********************************************************************/
01963 UNIV_INTERN
01964 int
01965 row_create_index_for_mysql(
01966 /*=======================*/
01967   dict_index_t* index,    
01969   trx_t*    trx,    
01970   const ulint*  field_lengths)  
01976 {
01977   ind_node_t* node;
01978   mem_heap_t* heap;
01979   que_thr_t*  thr;
01980   ulint   err;
01981   ulint   i;
01982   ulint   len;
01983   char*   table_name;
01984 
01985 #ifdef UNIV_SYNC_DEBUG
01986   ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
01987 #endif /* UNIV_SYNC_DEBUG */
01988   ut_ad(mutex_own(&(dict_sys->mutex)));
01989   ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
01990 
01991   trx->op_info = "creating index";
01992 
01993   /* Copy the table name because we may want to drop the
01994   table later, after the index object is freed (inside
01995   que_run_threads()) and thus index->table_name is not available. */
01996   table_name = mem_strdup(index->table_name);
01997 
01998   trx_start_if_not_started(trx);
01999 
02000   /* Check that the same column does not appear twice in the index.
02001   Starting from 4.0.14, InnoDB should be able to cope with that, but
02002   safer not to allow them. */
02003 
02004   for (i = 0; i < dict_index_get_n_fields(index); i++) {
02005     ulint   j;
02006 
02007     for (j = 0; j < i; j++) {
02008       if (0 == ut_strcmp(
02009             dict_index_get_nth_field(index, j)->name,
02010             dict_index_get_nth_field(index, i)->name)) {
02011         ut_print_timestamp(stderr);
02012 
02013         fputs("  InnoDB: Error: column ", stderr);
02014         ut_print_name(stderr, trx, FALSE,
02015                 dict_index_get_nth_field(
02016                   index, i)->name);
02017         fputs(" appears twice in ", stderr);
02018         dict_index_name_print(stderr, trx, index);
02019         fputs("\n"
02020               "InnoDB: This is not allowed"
02021               " in InnoDB.\n", stderr);
02022 
02023         err = DB_COL_APPEARS_TWICE_IN_INDEX;
02024 
02025         goto error_handling;
02026       }
02027     }
02028 
02029     /* Check also that prefix_len and actual length
02030     < DICT_MAX_INDEX_COL_LEN */
02031 
02032     len = dict_index_get_nth_field(index, i)->prefix_len;
02033 
02034     if (field_lengths) {
02035       len = ut_max(len, field_lengths[i]);
02036     }
02037 
02038     if (len >= DICT_MAX_INDEX_COL_LEN) {
02039       err = DB_TOO_BIG_RECORD;
02040 
02041       goto error_handling;
02042     }
02043   }
02044 
02045   heap = mem_heap_create(512);
02046 
02047   trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
02048 
02049   /* Note that the space id where we store the index is inherited from
02050   the table in dict_build_index_def_step() in dict0crea.c. */
02051 
02052   node = ind_create_graph_create(index, heap);
02053 
02054   thr = pars_complete_graph_for_exec(node, trx, heap);
02055 
02056   ut_a(thr == que_fork_start_command(static_cast<que_fork_t *>(que_node_get_parent(thr))));
02057   que_run_threads(thr);
02058 
02059   err = trx->error_state;
02060 
02061   que_graph_free((que_t*) que_node_get_parent(thr));
02062 
02063 error_handling:
02064   if (err != DB_SUCCESS) {
02065     /* We have special error handling here */
02066 
02067     trx->error_state = DB_SUCCESS;
02068 
02069     trx_general_rollback_for_mysql(trx, NULL);
02070 
02071     row_drop_table_for_mysql(table_name, trx, FALSE);
02072 
02073     trx_commit_for_mysql(trx);
02074 
02075     trx->error_state = DB_SUCCESS;
02076   }
02077 
02078   trx->op_info = "";
02079 
02080   mem_free(table_name);
02081 
02082   return((int) err);
02083 }
02084 
02085 /*********************************************************************/
02094 UNIV_INTERN
02095 int
02096 row_table_add_foreign_constraints(
02097 /*==============================*/
02098   trx_t*    trx,    
02099   const char* sql_string, 
02104   size_t    sql_length, 
02105   const char* name,   
02108   ibool   reject_fks) 
02111 {
02112   ulint err;
02113 
02114   ut_ad(mutex_own(&(dict_sys->mutex)));
02115 #ifdef UNIV_SYNC_DEBUG
02116   ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
02117 #endif /* UNIV_SYNC_DEBUG */
02118   ut_a(sql_string);
02119 
02120   trx->op_info = "adding foreign keys";
02121 
02122   trx_start_if_not_started(trx);
02123 
02124   trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
02125 
02126   err = dict_create_foreign_constraints(trx, sql_string, sql_length,
02127                 name, reject_fks);
02128   if (err == DB_SUCCESS) {
02129     /* Check that also referencing constraints are ok */
02130     err = dict_load_foreigns(name, FALSE, TRUE);
02131   }
02132 
02133   if (err != DB_SUCCESS) {
02134     /* We have special error handling here */
02135 
02136     trx->error_state = DB_SUCCESS;
02137 
02138     trx_general_rollback_for_mysql(trx, NULL);
02139 
02140     row_drop_table_for_mysql(name, trx, FALSE);
02141 
02142     trx_commit_for_mysql(trx);
02143 
02144     trx->error_state = DB_SUCCESS;
02145   }
02146 
02147   return((int) err);
02148 }
02149 
02150 /*********************************************************************/
02158 static
02159 int
02160 row_drop_table_for_mysql_in_background(
02161 /*===================================*/
02162   const char* name) 
02163 {
02164   ulint error;
02165   trx_t*  trx;
02166 
02167   trx = trx_allocate_for_background();
02168 
02169   /* If the original transaction was dropping a table referenced by
02170   foreign keys, we must set the following to be able to drop the
02171   table: */
02172 
02173   trx->check_foreigns = FALSE;
02174 
02175   /*  fputs("InnoDB: Error: Dropping table ", stderr);
02176   ut_print_name(stderr, trx, TRUE, name);
02177   fputs(" in background drop list\n", stderr); */
02178 
02179   /* Try to drop the table in InnoDB */
02180 
02181   error = row_drop_table_for_mysql(name, trx, FALSE);
02182 
02183   /* Flush the log to reduce probability that the .frm files and
02184   the InnoDB data dictionary get out-of-sync if the user runs
02185   with innodb_flush_log_at_trx_commit = 0 */
02186 
02187   log_buffer_flush_to_disk();
02188 
02189   trx_commit_for_mysql(trx);
02190 
02191   trx_free_for_background(trx);
02192 
02193   return((int) error);
02194 }
02195 
02196 /*********************************************************************/
02201 UNIV_INTERN
02202 ulint
02203 row_drop_tables_for_mysql_in_background(void)
02204 /*=========================================*/
02205 {
02206   row_mysql_drop_t* drop;
02207   dict_table_t*   table;
02208   ulint     n_tables;
02209   ulint     n_tables_dropped = 0;
02210 loop:
02211   mutex_enter(&kernel_mutex);
02212 
02213   if (!row_mysql_drop_list_inited) {
02214 
02215     UT_LIST_INIT(row_mysql_drop_list);
02216     row_mysql_drop_list_inited = TRUE;
02217   }
02218 
02219   drop = UT_LIST_GET_FIRST(row_mysql_drop_list);
02220 
02221   n_tables = UT_LIST_GET_LEN(row_mysql_drop_list);
02222 
02223   mutex_exit(&kernel_mutex);
02224 
02225   if (drop == NULL) {
02226     /* All tables dropped */
02227 
02228     return(n_tables + n_tables_dropped);
02229   }
02230 
02231   mutex_enter(&(dict_sys->mutex));
02232   table = dict_table_get_low(drop->table_name);
02233   mutex_exit(&(dict_sys->mutex));
02234 
02235   if (table == NULL) {
02236     /* If for some reason the table has already been dropped
02237     through some other mechanism, do not try to drop it */
02238 
02239     goto already_dropped;
02240   }
02241 
02242   if (DB_SUCCESS != row_drop_table_for_mysql_in_background(
02243         drop->table_name)) {
02244     /* If the DROP fails for some table, we return, and let the
02245     main thread retry later */
02246 
02247     return(n_tables + n_tables_dropped);
02248   }
02249 
02250   n_tables_dropped++;
02251 
02252 already_dropped:
02253   mutex_enter(&kernel_mutex);
02254 
02255   UT_LIST_REMOVE(row_mysql_drop_list, row_mysql_drop_list, drop);
02256 
02257   ut_print_timestamp(stderr);
02258   fputs("  InnoDB: Dropped table ", stderr);
02259   ut_print_name(stderr, NULL, TRUE, drop->table_name);
02260   fputs(" in background drop queue.\n", stderr);
02261 
02262   mem_free(drop->table_name);
02263 
02264   mem_free(drop);
02265 
02266   mutex_exit(&kernel_mutex);
02267 
02268   goto loop;
02269 }
02270 
02271 /*********************************************************************/
02275 UNIV_INTERN
02276 ulint
02277 row_get_background_drop_list_len_low(void)
02278 /*======================================*/
02279 {
02280   ut_ad(mutex_own(&kernel_mutex));
02281 
02282   if (!row_mysql_drop_list_inited) {
02283 
02284     UT_LIST_INIT(row_mysql_drop_list);
02285     row_mysql_drop_list_inited = TRUE;
02286   }
02287 
02288   return(UT_LIST_GET_LEN(row_mysql_drop_list));
02289 }
02290 
02291 /*********************************************************************/
02298 static
02299 ibool
02300 row_add_table_to_background_drop_list(
02301 /*==================================*/
02302   const char* name) 
02303 {
02304   row_mysql_drop_t* drop;
02305 
02306   mutex_enter(&kernel_mutex);
02307 
02308   if (!row_mysql_drop_list_inited) {
02309 
02310     UT_LIST_INIT(row_mysql_drop_list);
02311     row_mysql_drop_list_inited = TRUE;
02312   }
02313 
02314   /* Look if the table already is in the drop list */
02315   drop = UT_LIST_GET_FIRST(row_mysql_drop_list);
02316 
02317   while (drop != NULL) {
02318     if (strcmp(drop->table_name, name) == 0) {
02319       /* Already in the list */
02320 
02321       mutex_exit(&kernel_mutex);
02322 
02323       return(FALSE);
02324     }
02325 
02326     drop = UT_LIST_GET_NEXT(row_mysql_drop_list, drop);
02327   }
02328 
02329   drop = static_cast<row_mysql_drop_t *>(mem_alloc(sizeof(row_mysql_drop_t)));
02330 
02331   drop->table_name = mem_strdup(name);
02332 
02333   UT_LIST_ADD_LAST(row_mysql_drop_list, row_mysql_drop_list, drop);
02334 
02335   /*  fputs("InnoDB: Adding table ", stderr);
02336   ut_print_name(stderr, trx, TRUE, drop->table_name);
02337   fputs(" to background drop list\n", stderr); */
02338 
02339   mutex_exit(&kernel_mutex);
02340 
02341   return(TRUE);
02342 }
02343 
02344 /*********************************************************************/
02349 UNIV_INTERN
02350 int
02351 row_discard_tablespace_for_mysql(
02352 /*=============================*/
02353   const char* name, 
02354   trx_t*    trx)  
02355 {
02356   dict_foreign_t* foreign;
02357   table_id_t  new_id;
02358   dict_table_t* table;
02359   ibool   success;
02360   ulint   err;
02361   pars_info_t*  info = NULL;
02362 
02363   /* How do we prevent crashes caused by ongoing operations on
02364   the table? Old operations could try to access non-existent
02365   pages.
02366 
02367   1) SQL queries, INSERT, SELECT, ...: we must get an exclusive
02368   MySQL table lock on the table before we can do DISCARD
02369   TABLESPACE. Then there are no running queries on the table.
02370 
02371   2) Purge and rollback: we assign a new table id for the
02372   table. Since purge and rollback look for the table based on
02373   the table id, they see the table as 'dropped' and discard
02374   their operations.
02375 
02376   3) Insert buffer: we remove all entries for the tablespace in
02377   the insert buffer tree; as long as the tablespace mem object
02378   does not exist, ongoing insert buffer page merges are
02379   discarded in buf0rea.c. If we recreate the tablespace mem
02380   object with IMPORT TABLESPACE later, then the tablespace will
02381   have the same id, but the tablespace_version field in the mem
02382   object is different, and ongoing old insert buffer page merges
02383   get discarded.
02384 
02385   4) Linear readahead and random readahead: we use the same
02386   method as in 3) to discard ongoing operations.
02387 
02388   5) FOREIGN KEY operations: if
02389   table->n_foreign_key_checks_running > 0, we do not allow the
02390   discard. We also reserve the data dictionary latch. */
02391 
02392   ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
02393 
02394   trx->op_info = "discarding tablespace";
02395   trx_start_if_not_started(trx);
02396 
02397   /* Serialize data dictionary operations with dictionary mutex:
02398   no deadlocks can occur then in these operations */
02399 
02400   row_mysql_lock_data_dictionary(trx);
02401 
02402   table = dict_table_get_low(name);
02403 
02404   if (!table) {
02405     err = DB_TABLE_NOT_FOUND;
02406 
02407     goto funct_exit;
02408   }
02409 
02410   if (table->space == 0) {
02411     ut_print_timestamp(stderr);
02412     fputs("  InnoDB: Error: table ", stderr);
02413     ut_print_name(stderr, trx, TRUE, name);
02414     fputs("\n"
02415           "InnoDB: is in the system tablespace 0"
02416           " which cannot be discarded\n", stderr);
02417     err = DB_ERROR;
02418 
02419     goto funct_exit;
02420   }
02421 
02422   if (table->n_foreign_key_checks_running > 0) {
02423 
02424     ut_print_timestamp(stderr);
02425     fputs("  InnoDB: You are trying to DISCARD table ", stderr);
02426     ut_print_name(stderr, trx, TRUE, table->name);
02427     fputs("\n"
02428           "InnoDB: though there is a foreign key check"
02429           " running on it.\n"
02430           "InnoDB: Cannot discard the table.\n",
02431           stderr);
02432 
02433     err = DB_ERROR;
02434 
02435     goto funct_exit;
02436   }
02437 
02438   /* Check if the table is referenced by foreign key constraints from
02439   some other table (not the table itself) */
02440 
02441   foreign = UT_LIST_GET_FIRST(table->referenced_list);
02442 
02443   while (foreign && foreign->foreign_table == table) {
02444     foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
02445   }
02446 
02447   if (foreign && trx->check_foreigns) {
02448 
02449     FILE* ef  = dict_foreign_err_file;
02450 
02451     /* We only allow discarding a referenced table if
02452     FOREIGN_KEY_CHECKS is set to 0 */
02453 
02454     err = DB_CANNOT_DROP_CONSTRAINT;
02455 
02456     mutex_enter(&dict_foreign_err_mutex);
02457     rewind(ef);
02458     ut_print_timestamp(ef);
02459 
02460     fputs("  Cannot DISCARD table ", ef);
02461     ut_print_name(stderr, trx, TRUE, name);
02462     fputs("\n"
02463           "because it is referenced by ", ef);
02464     ut_print_name(stderr, trx, TRUE, foreign->foreign_table_name);
02465     putc('\n', ef);
02466     mutex_exit(&dict_foreign_err_mutex);
02467 
02468     goto funct_exit;
02469   }
02470 
02471   dict_hdr_get_new_id(&new_id, NULL, NULL);
02472 
02473   /* Remove all locks except the table-level S and X locks. */
02474   lock_remove_all_on_table(table, FALSE);
02475 
02476   info = pars_info_create();
02477 
02478   pars_info_add_str_literal(info, "table_name", name);
02479   pars_info_add_ull_literal(info, "new_id", new_id);
02480 
02481   err = que_eval_sql(info,
02482          "PROCEDURE DISCARD_TABLESPACE_PROC () IS\n"
02483          "old_id CHAR;\n"
02484          "BEGIN\n"
02485          "SELECT ID INTO old_id\n"
02486          "FROM SYS_TABLES\n"
02487          "WHERE NAME = :table_name\n"
02488          "LOCK IN SHARE MODE;\n"
02489          "IF (SQL % NOTFOUND) THEN\n"
02490          "       COMMIT WORK;\n"
02491          "       RETURN;\n"
02492          "END IF;\n"
02493          "UPDATE SYS_TABLES SET ID = :new_id\n"
02494          " WHERE ID = old_id;\n"
02495          "UPDATE SYS_COLUMNS SET TABLE_ID = :new_id\n"
02496          " WHERE TABLE_ID = old_id;\n"
02497          "UPDATE SYS_INDEXES SET TABLE_ID = :new_id\n"
02498          " WHERE TABLE_ID = old_id;\n"
02499          "COMMIT WORK;\n"
02500          "END;\n"
02501          , FALSE, trx);
02502 
02503   if (err != DB_SUCCESS) {
02504     trx->error_state = DB_SUCCESS;
02505     trx_general_rollback_for_mysql(trx, NULL);
02506     trx->error_state = DB_SUCCESS;
02507   } else {
02508     dict_table_change_id_in_cache(table, new_id);
02509 
02510     success = fil_discard_tablespace(table->space);
02511 
02512     if (!success) {
02513       trx->error_state = DB_SUCCESS;
02514       trx_general_rollback_for_mysql(trx, NULL);
02515       trx->error_state = DB_SUCCESS;
02516 
02517       err = DB_ERROR;
02518     } else {
02519       /* Set the flag which tells that now it is legal to
02520       IMPORT a tablespace for this table */
02521       table->tablespace_discarded = TRUE;
02522       table->ibd_file_missing = TRUE;
02523     }
02524   }
02525 
02526 funct_exit:
02527   trx_commit_for_mysql(trx);
02528 
02529   row_mysql_unlock_data_dictionary(trx);
02530 
02531   trx->op_info = "";
02532 
02533   return((int) err);
02534 }
02535 
02536 /*****************************************************************/
02540 UNIV_INTERN
02541 int
02542 row_import_tablespace_for_mysql(
02543 /*============================*/
02544   const char* name, 
02545   trx_t*    trx)  
02546 {
02547   dict_table_t* table;
02548   ibool   success;
02549   ib_uint64_t current_lsn;
02550   ulint   err   = DB_SUCCESS;
02551 
02552   ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
02553 
02554   trx_start_if_not_started(trx);
02555 
02556   trx->op_info = "importing tablespace";
02557 
02558   current_lsn = log_get_lsn();
02559 
02560   /* It is possible, though very improbable, that the lsn's in the
02561   tablespace to be imported have risen above the current system lsn, if
02562   a lengthy purge, ibuf merge, or rollback was performed on a backup
02563   taken with ibbackup. If that is the case, reset page lsn's in the
02564   file. We assume that mysqld was shut down after it performed these
02565   cleanup operations on the .ibd file, so that it stamped the latest lsn
02566   to the FIL_PAGE_FILE_FLUSH_LSN in the first page of the .ibd file.
02567 
02568   TODO: reset also the trx id's in clustered index records and write
02569   a new space id to each data page. That would allow us to import clean
02570   .ibd files from another MySQL installation. */
02571 
02572   success = fil_reset_too_high_lsns(name, current_lsn);
02573 
02574   if (!success) {
02575     ut_print_timestamp(stderr);
02576     fputs("  InnoDB: Error: cannot reset lsn's in table ", stderr);
02577     ut_print_name(stderr, trx, TRUE, name);
02578     fputs("\n"
02579           "InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n",
02580           stderr);
02581 
02582     err = DB_ERROR;
02583 
02584     row_mysql_lock_data_dictionary(trx);
02585 
02586     goto funct_exit;
02587   }
02588 
02589   /* Serialize data dictionary operations with dictionary mutex:
02590   no deadlocks can occur then in these operations */
02591 
02592   row_mysql_lock_data_dictionary(trx);
02593 
02594   table = dict_table_get_low(name);
02595 
02596   if (!table) {
02597     ut_print_timestamp(stderr);
02598     fputs("  InnoDB: table ", stderr);
02599     ut_print_name(stderr, trx, TRUE, name);
02600     fputs("\n"
02601           "InnoDB: does not exist in the InnoDB data dictionary\n"
02602           "InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n",
02603           stderr);
02604 
02605     err = DB_TABLE_NOT_FOUND;
02606 
02607     goto funct_exit;
02608   }
02609 
02610   if (table->space == 0) {
02611     ut_print_timestamp(stderr);
02612     fputs("  InnoDB: Error: table ", stderr);
02613     ut_print_name(stderr, trx, TRUE, name);
02614     fputs("\n"
02615           "InnoDB: is in the system tablespace 0"
02616           " which cannot be imported\n", stderr);
02617     err = DB_ERROR;
02618 
02619     goto funct_exit;
02620   }
02621 
02622   if (!table->tablespace_discarded) {
02623     ut_print_timestamp(stderr);
02624     fputs("  InnoDB: Error: you are trying to"
02625           " IMPORT a tablespace\n"
02626           "InnoDB: ", stderr);
02627     ut_print_name(stderr, trx, TRUE, name);
02628     fputs(", though you have not called DISCARD on it yet\n"
02629           "InnoDB: during the lifetime of the mysqld process!\n",
02630           stderr);
02631 
02632     err = DB_ERROR;
02633 
02634     goto funct_exit;
02635   }
02636 
02637   /* Play safe and remove all insert buffer entries, though we should
02638   have removed them already when DISCARD TABLESPACE was called */
02639 
02640   ibuf_delete_for_discarded_space(table->space);
02641 
02642   success = fil_open_single_table_tablespace(
02643     TRUE, table->space,
02644     table->flags == DICT_TF_COMPACT ? 0 : table->flags,
02645     table->name);
02646   if (success) {
02647     table->ibd_file_missing = FALSE;
02648     table->tablespace_discarded = FALSE;
02649   } else {
02650     if (table->ibd_file_missing) {
02651       ut_print_timestamp(stderr);
02652       fputs("  InnoDB: cannot find or open in the"
02653             " database directory the .ibd file of\n"
02654             "InnoDB: table ", stderr);
02655       ut_print_name(stderr, trx, TRUE, name);
02656       fputs("\n"
02657             "InnoDB: in ALTER TABLE ... IMPORT TABLESPACE\n",
02658             stderr);
02659     }
02660 
02661     err = DB_ERROR;
02662   }
02663 
02664 funct_exit:
02665   trx_commit_for_mysql(trx);
02666 
02667   row_mysql_unlock_data_dictionary(trx);
02668 
02669   trx->op_info = "";
02670 
02671   return((int) err);
02672 }
02673 
02674 /*********************************************************************/
02677 UNIV_INTERN
02678 int
02679 row_truncate_table_for_mysql(
02680 /*=========================*/
02681   dict_table_t* table,  
02682   trx_t*    trx)  
02683 {
02684   dict_foreign_t* foreign;
02685   ulint   err;
02686   mem_heap_t* heap;
02687   byte*   buf;
02688   dtuple_t* tuple;
02689   dfield_t* dfield;
02690   dict_index_t* sys_index;
02691   btr_pcur_t  pcur;
02692   mtr_t   mtr;
02693   table_id_t  new_id;
02694   ulint   recreate_space = 0;
02695   pars_info_t*  info = NULL;
02696 
02697   /* How do we prevent crashes caused by ongoing operations on
02698   the table? Old operations could try to access non-existent
02699   pages.
02700 
02701   1) SQL queries, INSERT, SELECT, ...: we must get an exclusive
02702   MySQL table lock on the table before we can do TRUNCATE
02703   TABLE. Then there are no running queries on the table. This is
02704   guaranteed, because in ha_innobase::store_lock(), we do not
02705   weaken the TL_WRITE lock requested by MySQL when executing
02706   SQLCOM_TRUNCATE.
02707 
02708   2) Purge and rollback: we assign a new table id for the
02709   table. Since purge and rollback look for the table based on
02710   the table id, they see the table as 'dropped' and discard
02711   their operations.
02712 
02713   3) Insert buffer: TRUNCATE TABLE is analogous to DROP TABLE,
02714   so we do not have to remove insert buffer records, as the
02715   insert buffer works at a low level. If a freed page is later
02716   reallocated, the allocator will remove the ibuf entries for
02717   it.
02718 
02719   When we truncate *.ibd files by recreating them (analogous to
02720   DISCARD TABLESPACE), we remove all entries for the table in the
02721   insert buffer tree.  This is not strictly necessary, because
02722   in 6) we will assign a new tablespace identifier, but we can
02723   free up some space in the system tablespace.
02724 
02725   4) Linear readahead and random readahead: we use the same
02726   method as in 3) to discard ongoing operations. (This is only
02727   relevant for TRUNCATE TABLE by DISCARD TABLESPACE.)
02728 
02729   5) FOREIGN KEY operations: if
02730   table->n_foreign_key_checks_running > 0, we do not allow the
02731   TRUNCATE. We also reserve the data dictionary latch.
02732 
02733   6) Crash recovery: To prevent the application of pre-truncation
02734   redo log records on the truncated tablespace, we will assign
02735   a new tablespace identifier to the truncated tablespace. */
02736 
02737   ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
02738   ut_ad(table);
02739 
02740   if (srv_created_new_raw) {
02741     fputs("InnoDB: A new raw disk partition was initialized:\n"
02742           "InnoDB: we do not allow database modifications"
02743           " by the user.\n"
02744           "InnoDB: Shut down mysqld and edit my.cnf so that newraw"
02745           " is replaced with raw.\n", stderr);
02746 
02747     return(DB_ERROR);
02748   }
02749 
02750   trx->op_info = "truncating table";
02751 
02752   trx_start_if_not_started(trx);
02753 
02754   /* Serialize data dictionary operations with dictionary mutex:
02755   no deadlocks can occur then in these operations */
02756 
02757   ut_a(trx->dict_operation_lock_mode == 0);
02758   /* Prevent foreign key checks etc. while we are truncating the
02759   table */
02760 
02761   row_mysql_lock_data_dictionary(trx);
02762 
02763   ut_ad(mutex_own(&(dict_sys->mutex)));
02764 #ifdef UNIV_SYNC_DEBUG
02765   ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
02766 #endif /* UNIV_SYNC_DEBUG */
02767 
02768   /* Check if the table is referenced by foreign key constraints from
02769   some other table (not the table itself) */
02770 
02771   foreign = UT_LIST_GET_FIRST(table->referenced_list);
02772 
02773   while (foreign && foreign->foreign_table == table) {
02774     foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
02775   }
02776 
02777   if (foreign && trx->check_foreigns) {
02778     FILE* ef  = dict_foreign_err_file;
02779 
02780     /* We only allow truncating a referenced table if
02781     FOREIGN_KEY_CHECKS is set to 0 */
02782 
02783     mutex_enter(&dict_foreign_err_mutex);
02784     rewind(ef);
02785     ut_print_timestamp(ef);
02786 
02787     fputs("  Cannot truncate table ", ef);
02788     ut_print_name(ef, trx, TRUE, table->name);
02789     fputs(" by DROP+CREATE\n"
02790           "InnoDB: because it is referenced by ", ef);
02791     ut_print_name(ef, trx, TRUE, foreign->foreign_table_name);
02792     putc('\n', ef);
02793     mutex_exit(&dict_foreign_err_mutex);
02794 
02795     err = DB_ERROR;
02796     goto funct_exit;
02797   }
02798 
02799   /* TODO: could we replace the counter n_foreign_key_checks_running
02800   with lock checks on the table? Acquire here an exclusive lock on the
02801   table, and rewrite lock0lock.c and the lock wait in srv0srv.c so that
02802   they can cope with the table having been truncated here? Foreign key
02803   checks take an IS or IX lock on the table. */
02804 
02805   if (table->n_foreign_key_checks_running > 0) {
02806     ut_print_timestamp(stderr);
02807     fputs("  InnoDB: Cannot truncate table ", stderr);
02808     ut_print_name(stderr, trx, TRUE, table->name);
02809     fputs(" by DROP+CREATE\n"
02810           "InnoDB: because there is a foreign key check"
02811           " running on it.\n",
02812           stderr);
02813     err = DB_ERROR;
02814 
02815     goto funct_exit;
02816   }
02817 
02818   /* Remove all locks except the table-level S and X locks. */
02819   lock_remove_all_on_table(table, FALSE);
02820 
02821   trx->table_id = table->id;
02822 
02823   if (table->space && !table->dir_path_of_temp_table) {
02824     /* Discard and create the single-table tablespace. */
02825     ulint space = table->space;
02826     ulint flags = fil_space_get_flags(space);
02827 
02828     if (flags != ULINT_UNDEFINED
02829         && fil_discard_tablespace(space)) {
02830 
02831       dict_index_t* index;
02832 
02833       dict_hdr_get_new_id(NULL, NULL, &space);
02834 
02835       /* Lock all index trees for this table. We must
02836       do so after dict_hdr_get_new_id() to preserve
02837       the latch order */
02838       dict_table_x_lock_indexes(table);
02839 
02840       if (space == ULINT_UNDEFINED
02841           || fil_create_new_single_table_tablespace(
02842             space, table->name, FALSE, flags,
02843             FIL_IBD_FILE_INITIAL_SIZE) != DB_SUCCESS) {
02844         dict_table_x_unlock_indexes(table);
02845         ut_print_timestamp(stderr);
02846         fprintf(stderr,
02847           "  InnoDB: TRUNCATE TABLE %s failed to"
02848           " create a new tablespace\n",
02849           table->name);
02850         table->ibd_file_missing = 1;
02851         err = DB_ERROR;
02852         goto funct_exit;
02853       }
02854 
02855       recreate_space = space;
02856 
02857       /* Replace the space_id in the data dictionary cache.
02858       The persisent data dictionary (SYS_TABLES.SPACE
02859       and SYS_INDEXES.SPACE) are updated later in this
02860       function. */
02861       table->space = space;
02862       index = dict_table_get_first_index(table);
02863       do {
02864         index->space = space;
02865         index = dict_table_get_next_index(index);
02866       } while (index);
02867 
02868       mtr_start(&mtr);
02869       fsp_header_init(space,
02870           FIL_IBD_FILE_INITIAL_SIZE, &mtr);
02871       mtr_commit(&mtr);
02872     }
02873   } else {
02874     /* Lock all index trees for this table, as we will
02875     truncate the table/index and possibly change their metadata.
02876     All DML/DDL are blocked by table level lock, with
02877     a few exceptions such as queries into information schema
02878     about the table, MySQL could try to access index stats
02879     for this kind of query, we need to use index locks to
02880     sync up */
02881     dict_table_x_lock_indexes(table);
02882   }
02883 
02884   /* scan SYS_INDEXES for all indexes of the table */
02885   heap = mem_heap_create(800);
02886 
02887   tuple = dtuple_create(heap, 1);
02888   dfield = dtuple_get_nth_field(tuple, 0);
02889 
02890   buf = static_cast<byte *>(mem_heap_alloc(heap, 8));
02891   mach_write_to_8(buf, table->id);
02892 
02893   dfield_set_data(dfield, buf, 8);
02894   sys_index = dict_table_get_first_index(dict_sys->sys_indexes);
02895   dict_index_copy_types(tuple, sys_index, 1);
02896 
02897   mtr_start(&mtr);
02898   btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE,
02899           BTR_MODIFY_LEAF, &pcur, &mtr);
02900   for (;;) {
02901     rec_t*    rec;
02902     const byte* field;
02903     ulint   len;
02904     ulint   root_page_no;
02905 
02906     if (!btr_pcur_is_on_user_rec(&pcur)) {
02907       /* The end of SYS_INDEXES has been reached. */
02908       break;
02909     }
02910 
02911     rec = btr_pcur_get_rec(&pcur);
02912 
02913     field = rec_get_nth_field_old(rec, 0, &len);
02914     ut_ad(len == 8);
02915 
02916     if (memcmp(buf, field, len) != 0) {
02917       /* End of indexes for the table (TABLE_ID mismatch). */
02918       break;
02919     }
02920 
02921     if (rec_get_deleted_flag(rec, FALSE)) {
02922       /* The index has been dropped. */
02923       goto next_rec;
02924     }
02925 
02926     /* This call may commit and restart mtr
02927     and reposition pcur. */
02928     root_page_no = dict_truncate_index_tree(table, recreate_space,
02929               &pcur, &mtr);
02930 
02931     rec = btr_pcur_get_rec(&pcur);
02932 
02933     if (root_page_no != FIL_NULL) {
02934       page_rec_write_index_page_no(
02935         rec, DICT_SYS_INDEXES_PAGE_NO_FIELD,
02936         root_page_no, &mtr);
02937       /* We will need to commit and restart the
02938       mini-transaction in order to avoid deadlocks.
02939       The dict_truncate_index_tree() call has allocated
02940       a page in this mini-transaction, and the rest of
02941       this loop could latch another index page. */
02942       mtr_commit(&mtr);
02943       mtr_start(&mtr);
02944       btr_pcur_restore_position(BTR_MODIFY_LEAF,
02945               &pcur, &mtr);
02946     }
02947 
02948 next_rec:
02949     btr_pcur_move_to_next_user_rec(&pcur, &mtr);
02950   }
02951 
02952   btr_pcur_close(&pcur);
02953   mtr_commit(&mtr);
02954 
02955   mem_heap_free(heap);
02956 
02957   /* Done with index truncation, release index tree locks,
02958   subsequent work relates to table level metadata change */
02959   dict_table_x_unlock_indexes(table);
02960 
02961   dict_hdr_get_new_id(&new_id, NULL, NULL);
02962 
02963   info = pars_info_create();
02964 
02965   pars_info_add_int4_literal(info, "space", (lint) table->space);
02966   pars_info_add_ull_literal(info, "old_id", table->id);
02967   pars_info_add_ull_literal(info, "new_id", new_id);
02968 
02969   err = que_eval_sql(info,
02970          "PROCEDURE RENUMBER_TABLESPACE_PROC () IS\n"
02971          "BEGIN\n"
02972          "UPDATE SYS_TABLES"
02973          " SET ID = :new_id, SPACE = :space\n"
02974          " WHERE ID = :old_id;\n"
02975          "UPDATE SYS_COLUMNS SET TABLE_ID = :new_id\n"
02976          " WHERE TABLE_ID = :old_id;\n"
02977          "UPDATE SYS_INDEXES"
02978          " SET TABLE_ID = :new_id, SPACE = :space\n"
02979          " WHERE TABLE_ID = :old_id;\n"
02980          "COMMIT WORK;\n"
02981          "END;\n"
02982          , FALSE, trx);
02983 
02984   if (err != DB_SUCCESS) {
02985     trx->error_state = DB_SUCCESS;
02986     trx_general_rollback_for_mysql(trx, NULL);
02987     trx->error_state = DB_SUCCESS;
02988     ut_print_timestamp(stderr);
02989     fputs("  InnoDB: Unable to assign a new identifier to table ",
02990           stderr);
02991     ut_print_name(stderr, trx, TRUE, table->name);
02992     fputs("\n"
02993           "InnoDB: after truncating it.  Background processes"
02994           " may corrupt the table!\n", stderr);
02995     err = DB_ERROR;
02996   } else {
02997     dict_table_change_id_in_cache(table, new_id);
02998   }
02999 
03000   /* 
03001           MySQL calls ha_innobase::reset_auto_increment() which does
03002           the same thing. 
03003         */
03004   dict_table_autoinc_lock(table);
03005   dict_table_autoinc_initialize(table, 1);
03006   dict_table_autoinc_unlock(table);
03007   dict_update_statistics(table, FALSE /* update even if stats are
03008               initialized */);
03009 
03010   trx_commit_for_mysql(trx);
03011 
03012 funct_exit:
03013 
03014   row_mysql_unlock_data_dictionary(trx);
03015 
03016   trx->op_info = "";
03017 
03018   srv_wake_master_thread();
03019 
03020   return((int) err);
03021 }
03022 
03023 /*********************************************************************/
03031 UNIV_INTERN
03032 int
03033 row_drop_table_for_mysql(
03034 /*=====================*/
03035   const char* name, 
03036   trx_t*    trx,  
03037   ibool   drop_db)
03038 {
03039   dict_foreign_t* foreign;
03040   dict_table_t* table;
03041   ulint   space_id;
03042   ulint   err;
03043   const char* table_name;
03044   ulint   namelen;
03045   ibool   locked_dictionary = FALSE;
03046   pars_info_t*    info      = NULL;
03047 
03048   ut_a(name != NULL);
03049 
03050   if (srv_created_new_raw) {
03051     fputs("InnoDB: A new raw disk partition was initialized:\n"
03052           "InnoDB: we do not allow database modifications"
03053           " by the user.\n"
03054           "InnoDB: Shut down mysqld and edit my.cnf so that newraw"
03055           " is replaced with raw.\n", stderr);
03056 
03057     return(DB_ERROR);
03058   }
03059 
03060   trx->op_info = "dropping table";
03061 
03062   trx_start_if_not_started(trx);
03063 
03064   /* The table name is prefixed with the database name and a '/'.
03065   Certain table names starting with 'innodb_' have their special
03066   meaning regardless of the database name.  Thus, we need to
03067   ignore the database name prefix in the comparisons. */
03068   table_name = strchr(name, '/');
03069   ut_a(table_name);
03070   table_name++;
03071   namelen = strlen(table_name) + 1;
03072 
03073   if (namelen == sizeof S_innodb_monitor
03074       && !memcmp(table_name, S_innodb_monitor,
03075            sizeof S_innodb_monitor)) {
03076 
03077     /* Table name equals "innodb_monitor":
03078     stop monitor prints */
03079 
03080     srv_print_innodb_monitor = FALSE;
03081     srv_print_innodb_lock_monitor = FALSE;
03082   } else if (namelen == sizeof S_innodb_lock_monitor
03083        && !memcmp(table_name, S_innodb_lock_monitor,
03084             sizeof S_innodb_lock_monitor)) {
03085     srv_print_innodb_monitor = FALSE;
03086     srv_print_innodb_lock_monitor = FALSE;
03087   } else if (namelen == sizeof S_innodb_tablespace_monitor
03088        && !memcmp(table_name, S_innodb_tablespace_monitor,
03089             sizeof S_innodb_tablespace_monitor)) {
03090 
03091     srv_print_innodb_tablespace_monitor = FALSE;
03092   } else if (namelen == sizeof S_innodb_table_monitor
03093        && !memcmp(table_name, S_innodb_table_monitor,
03094             sizeof S_innodb_table_monitor)) {
03095 
03096     srv_print_innodb_table_monitor = FALSE;
03097   }
03098 
03099   /* Serialize data dictionary operations with dictionary mutex:
03100   no deadlocks can occur then in these operations */
03101 
03102   if (trx->dict_operation_lock_mode != RW_X_LATCH) {
03103     /* Prevent foreign key checks etc. while we are dropping the
03104     table */
03105 
03106     row_mysql_lock_data_dictionary(trx);
03107 
03108     locked_dictionary = TRUE;
03109   }
03110 
03111   ut_ad(mutex_own(&(dict_sys->mutex)));
03112 #ifdef UNIV_SYNC_DEBUG
03113   ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
03114 #endif /* UNIV_SYNC_DEBUG */
03115 
03116   table = dict_table_get_low(name);
03117 
03118   if (!table) {
03119 #if defined(BUILD_DRIZZLE)
03120     err = ENOENT;
03121 #else
03122     err = DB_TABLE_NOT_FOUND;
03123     ut_print_timestamp(stderr);
03124 
03125     fputs("  InnoDB: Error: table ", stderr);
03126     ut_print_name(stderr, trx, TRUE, name);
03127     fputs(" does not exist in the InnoDB internal\n"
03128           "InnoDB: data dictionary though MySQL is"
03129           " trying to drop it.\n"
03130           "InnoDB: Have you copied the .frm file"
03131           " of the table to the\n"
03132           "InnoDB: MySQL database directory"
03133           " from another database?\n"
03134           "InnoDB: You can look for further help from\n"
03135           "InnoDB: " REFMAN "innodb-troubleshooting.html\n",
03136           stderr);
03137 #endif /* BUILD_DRIZZLE */
03138     goto funct_exit;
03139   }
03140 
03141   /* Check if the table is referenced by foreign key constraints from
03142   some other table (not the table itself) */
03143 
03144   foreign = UT_LIST_GET_FIRST(table->referenced_list);
03145 
03146   while (foreign && foreign->foreign_table == table) {
03147 check_next_foreign:
03148     foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
03149   }
03150 
03151   if (foreign && trx->check_foreigns
03152       && !(drop_db && dict_tables_have_same_db(
03153        name, foreign->foreign_table_name))) {
03154     FILE* ef  = dict_foreign_err_file;
03155 
03156     /* We only allow dropping a referenced table if
03157     FOREIGN_KEY_CHECKS is set to 0 */
03158 
03159     err = DB_CANNOT_DROP_CONSTRAINT;
03160 
03161     mutex_enter(&dict_foreign_err_mutex);
03162     rewind(ef);
03163     ut_print_timestamp(ef);
03164 
03165     fputs("  Cannot drop table ", ef);
03166     ut_print_name(ef, trx, TRUE, name);
03167     fputs("\n"
03168           "because it is referenced by ", ef);
03169     ut_print_name(ef, trx, TRUE, foreign->foreign_table_name);
03170     putc('\n', ef);
03171     mutex_exit(&dict_foreign_err_mutex);
03172 
03173     goto funct_exit;
03174   }
03175 
03176   if (foreign && trx->check_foreigns) {
03177     goto check_next_foreign;
03178   }
03179 
03180   if (table->n_mysql_handles_opened > 0) {
03181     ibool added;
03182 
03183     added = row_add_table_to_background_drop_list(table->name);
03184 
03185     if (added) {
03186       ut_print_timestamp(stderr);
03187       fputs("  InnoDB: Warning: MySQL is"
03188             " trying to drop table ", stderr);
03189       ut_print_name(stderr, trx, TRUE, table->name);
03190       fputs("\n"
03191             "InnoDB: though there are still"
03192             " open handles to it.\n"
03193             "InnoDB: Adding the table to the"
03194             " background drop queue.\n",
03195             stderr);
03196 
03197       /* We return DB_SUCCESS to MySQL though the drop will
03198       happen lazily later */
03199       err = DB_SUCCESS;
03200     } else {
03201       /* The table is already in the background drop list */
03202       err = DB_ERROR;
03203     }
03204 
03205     goto funct_exit;
03206   }
03207 
03208   /* TODO: could we replace the counter n_foreign_key_checks_running
03209   with lock checks on the table? Acquire here an exclusive lock on the
03210   table, and rewrite lock0lock.c and the lock wait in srv0srv.c so that
03211   they can cope with the table having been dropped here? Foreign key
03212   checks take an IS or IX lock on the table. */
03213 
03214   if (table->n_foreign_key_checks_running > 0) {
03215 
03216     const char* i_table_name = table->name;
03217     ibool   added;
03218 
03219     added = row_add_table_to_background_drop_list(i_table_name);
03220 
03221     if (added) {
03222       ut_print_timestamp(stderr);
03223       fputs("  InnoDB: You are trying to drop table ",
03224             stderr);
03225       ut_print_name(stderr, trx, TRUE, i_table_name);
03226       fputs("\n"
03227             "InnoDB: though there is a"
03228             " foreign key check running on it.\n"
03229             "InnoDB: Adding the table to"
03230             " the background drop queue.\n",
03231             stderr);
03232 
03233       /* We return DB_SUCCESS to MySQL though the drop will
03234       happen lazily later */
03235 
03236       err = DB_SUCCESS;
03237     } else {
03238       /* The table is already in the background drop list */
03239       err = DB_ERROR;
03240     }
03241 
03242     goto funct_exit;
03243   }
03244 
03245   /* Remove all locks there are on the table or its records */
03246   lock_remove_all_on_table(table, TRUE);
03247 
03248   trx_set_dict_operation(trx, TRX_DICT_OP_TABLE);
03249   trx->table_id = table->id;
03250 
03251   /* We use the private SQL parser of Innobase to generate the
03252   query graphs needed in deleting the dictionary data from system
03253   tables in Innobase. Deleting a row from SYS_INDEXES table also
03254   frees the file segments of the B-tree associated with the index. */
03255 
03256   info = pars_info_create();
03257 
03258   pars_info_add_str_literal(info, "table_name", name);
03259 
03260   err = que_eval_sql(info,
03261          "PROCEDURE DROP_TABLE_PROC () IS\n"
03262          "sys_foreign_id CHAR;\n"
03263          "table_id CHAR;\n"
03264          "index_id CHAR;\n"
03265          "foreign_id CHAR;\n"
03266          "found INT;\n"
03267          "BEGIN\n"
03268          "SELECT ID INTO table_id\n"
03269          "FROM SYS_TABLES\n"
03270          "WHERE NAME = :table_name\n"
03271          "LOCK IN SHARE MODE;\n"
03272          "IF (SQL % NOTFOUND) THEN\n"
03273          "       RETURN;\n"
03274          "END IF;\n"
03275          "found := 1;\n"
03276          "SELECT ID INTO sys_foreign_id\n"
03277          "FROM SYS_TABLES\n"
03278          "WHERE NAME = 'SYS_FOREIGN'\n"
03279          "LOCK IN SHARE MODE;\n"
03280          "IF (SQL % NOTFOUND) THEN\n"
03281          "       found := 0;\n"
03282          "END IF;\n"
03283          "IF (:table_name = 'SYS_FOREIGN') THEN\n"
03284          "       found := 0;\n"
03285          "END IF;\n"
03286          "IF (:table_name = 'SYS_FOREIGN_COLS') THEN\n"
03287          "       found := 0;\n"
03288          "END IF;\n"
03289          "WHILE found = 1 LOOP\n"
03290          "       SELECT ID INTO foreign_id\n"
03291          "       FROM SYS_FOREIGN\n"
03292          "       WHERE FOR_NAME = :table_name\n"
03293          "               AND TO_BINARY(FOR_NAME)\n"
03294          "                 = TO_BINARY(:table_name)\n"
03295          "               LOCK IN SHARE MODE;\n"
03296          "       IF (SQL % NOTFOUND) THEN\n"
03297          "               found := 0;\n"
03298          "       ELSE\n"
03299          "               DELETE FROM SYS_FOREIGN_COLS\n"
03300          "               WHERE ID = foreign_id;\n"
03301          "               DELETE FROM SYS_FOREIGN\n"
03302          "               WHERE ID = foreign_id;\n"
03303          "       END IF;\n"
03304          "END LOOP;\n"
03305          "found := 1;\n"
03306          "WHILE found = 1 LOOP\n"
03307          "       SELECT ID INTO index_id\n"
03308          "       FROM SYS_INDEXES\n"
03309          "       WHERE TABLE_ID = table_id\n"
03310          "       LOCK IN SHARE MODE;\n"
03311          "       IF (SQL % NOTFOUND) THEN\n"
03312          "               found := 0;\n"
03313          "       ELSE\n"
03314          "               DELETE FROM SYS_FIELDS\n"
03315          "               WHERE INDEX_ID = index_id;\n"
03316          "               DELETE FROM SYS_INDEXES\n"
03317          "               WHERE ID = index_id\n"
03318          "               AND TABLE_ID = table_id;\n"
03319          "       END IF;\n"
03320          "END LOOP;\n"
03321          "DELETE FROM SYS_COLUMNS\n"
03322          "WHERE TABLE_ID = table_id;\n"
03323          "DELETE FROM SYS_TABLES\n"
03324          "WHERE ID = table_id;\n"
03325          "END;\n"
03326          , FALSE, trx);
03327 
03328   switch (err) {
03329     ibool   is_temp;
03330     const char* name_or_path;
03331     mem_heap_t* heap;
03332 
03333   case DB_SUCCESS:
03334 
03335     heap = mem_heap_create(200);
03336 
03337     /* Clone the name, in case it has been allocated
03338     from table->heap, which will be freed by
03339     dict_table_remove_from_cache(table) below. */
03340     name = mem_heap_strdup(heap, name);
03341     space_id = table->space;
03342 
03343     if (table->dir_path_of_temp_table != NULL) {
03344       name_or_path = mem_heap_strdup(
03345         heap, table->dir_path_of_temp_table);
03346       is_temp = TRUE;
03347     } else {
03348       name_or_path = name;
03349       is_temp = (table->flags >> DICT_TF2_SHIFT)
03350         & DICT_TF2_TEMPORARY;
03351     }
03352 
03353     dict_table_remove_from_cache(table);
03354 
03355     if (dict_load_table(name, TRUE) != NULL) {
03356       ut_print_timestamp(stderr);
03357       fputs("  InnoDB: Error: not able to remove table ",
03358             stderr);
03359       ut_print_name(stderr, trx, TRUE, name);
03360       fputs(" from the dictionary cache!\n", stderr);
03361       err = DB_ERROR;
03362     }
03363 
03364     /* Do not drop possible .ibd tablespace if something went
03365     wrong: we do not want to delete valuable data of the user */
03366 
03367     if (err == DB_SUCCESS && space_id > 0) {
03368       if (!fil_space_for_table_exists_in_mem(space_id,
03369                      name_or_path,
03370                      is_temp, FALSE,
03371                      !is_temp)) {
03372         err = DB_SUCCESS;
03373 
03374         fprintf(stderr,
03375           "InnoDB: We removed now the InnoDB"
03376           " internal data dictionary entry\n"
03377           "InnoDB: of table ");
03378         ut_print_name(stderr, trx, TRUE, name);
03379         fprintf(stderr, ".\n");
03380       } else if (!fil_delete_tablespace(space_id)) {
03381         fprintf(stderr,
03382           "InnoDB: We removed now the InnoDB"
03383           " internal data dictionary entry\n"
03384           "InnoDB: of table ");
03385         ut_print_name(stderr, trx, TRUE, name);
03386         fprintf(stderr, ".\n");
03387 
03388         ut_print_timestamp(stderr);
03389         fprintf(stderr,
03390           "  InnoDB: Error: not able to"
03391           " delete tablespace %lu of table ",
03392           (ulong) space_id);
03393         ut_print_name(stderr, trx, TRUE, name);
03394         fputs("!\n", stderr);
03395         err = DB_ERROR;
03396       }
03397     }
03398 
03399     mem_heap_free(heap);
03400     break;
03401 
03402   case DB_TOO_MANY_CONCURRENT_TRXS:
03403     /* Cannot even find a free slot for the
03404     the undo log. We can directly exit here
03405     and return the DB_TOO_MANY_CONCURRENT_TRXS
03406     error. */
03407     break;
03408 
03409   case DB_OUT_OF_FILE_SPACE:
03410     err = DB_MUST_GET_MORE_FILE_SPACE;
03411 
03412     row_mysql_handle_errors(&err, trx, NULL, NULL);
03413 
03414     /* Fall through to raise error */
03415 
03416   default:
03417     /* No other possible error returns */
03418     ut_error;
03419   }
03420 
03421 funct_exit:
03422 
03423   if (locked_dictionary) {
03424     trx_commit_for_mysql(trx);
03425 
03426     row_mysql_unlock_data_dictionary(trx);
03427   }
03428 
03429   trx->op_info = "";
03430 
03431   srv_wake_master_thread();
03432 
03433   return((int) err);
03434 }
03435 
03436 /*********************************************************************/
03438 UNIV_INTERN
03439 void
03440 row_mysql_drop_temp_tables(void)
03441 /*============================*/
03442 {
03443   trx_t*    trx;
03444   btr_pcur_t  pcur;
03445   mtr_t   mtr;
03446   mem_heap_t* heap;
03447 
03448   trx = trx_allocate_for_background();
03449   trx->op_info = "dropping temporary tables";
03450   row_mysql_lock_data_dictionary(trx);
03451 
03452   heap = mem_heap_create(200);
03453 
03454   mtr_start(&mtr);
03455 
03456   btr_pcur_open_at_index_side(
03457     TRUE,
03458     dict_table_get_first_index(dict_sys->sys_tables),
03459     BTR_SEARCH_LEAF, &pcur, TRUE, &mtr);
03460 
03461   for (;;) {
03462     const rec_t*  rec;
03463     const byte* field;
03464     ulint   len;
03465     const char* table_name;
03466     dict_table_t* table;
03467 
03468     btr_pcur_move_to_next_user_rec(&pcur, &mtr);
03469 
03470     if (!btr_pcur_is_on_user_rec(&pcur)) {
03471       break;
03472     }
03473 
03474     rec = btr_pcur_get_rec(&pcur);
03475     field = rec_get_nth_field_old(rec, 4/*N_COLS*/, &len);
03476     if (len != 4 || !(mach_read_from_4(field) & 0x80000000UL)) {
03477       continue;
03478     }
03479 
03480     /* Because this is not a ROW_FORMAT=REDUNDANT table,
03481     the is_temp flag is valid.  Examine it. */
03482 
03483     field = rec_get_nth_field_old(rec, 7/*MIX_LEN*/, &len);
03484     if (len != 4
03485         || !(mach_read_from_4(field) & DICT_TF2_TEMPORARY)) {
03486       continue;
03487     }
03488 
03489     /* This is a temporary table. */
03490     field = rec_get_nth_field_old(rec, 0/*NAME*/, &len);
03491     if (len == UNIV_SQL_NULL || len == 0) {
03492       /* Corrupted SYS_TABLES.NAME */
03493       continue;
03494     }
03495 
03496     table_name = mem_heap_strdupl(heap, (const char*) field, len);
03497 
03498     btr_pcur_store_position(&pcur, &mtr);
03499     btr_pcur_commit_specify_mtr(&pcur, &mtr);
03500 
03501     table = dict_load_table(table_name, TRUE);
03502 
03503     if (table) {
03504       row_drop_table_for_mysql(table_name, trx, FALSE);
03505       trx_commit_for_mysql(trx);
03506     }
03507 
03508     mtr_start(&mtr);
03509     btr_pcur_restore_position(BTR_SEARCH_LEAF,
03510             &pcur, &mtr);
03511   }
03512 
03513   btr_pcur_close(&pcur);
03514   mtr_commit(&mtr);
03515   mem_heap_free(heap);
03516   row_mysql_unlock_data_dictionary(trx);
03517   trx_free_for_background(trx);
03518 }
03519 
03520 /*******************************************************************/
03524 static
03525 ulint
03526 drop_all_foreign_keys_in_db(
03527 /*========================*/
03528   const char* name, 
03529   trx_t*    trx)  
03530 {
03531   pars_info_t*  pinfo;
03532   ulint   err;
03533 
03534   ut_a(name[strlen(name) - 1] == '/');
03535 
03536   pinfo = pars_info_create();
03537 
03538   pars_info_add_str_literal(pinfo, "dbname", name);
03539 
03541 #define TABLE_NOT_IN_THIS_DB \
03542 "SUBSTR(for_name, 0, LENGTH(:dbname)) <> :dbname"
03543 
03544   err = que_eval_sql(pinfo,
03545          "PROCEDURE DROP_ALL_FOREIGN_KEYS_PROC () IS\n"
03546          "foreign_id CHAR;\n"
03547          "for_name CHAR;\n"
03548          "found INT;\n"
03549          "DECLARE CURSOR cur IS\n"
03550          "SELECT ID, FOR_NAME FROM SYS_FOREIGN\n"
03551          "WHERE FOR_NAME >= :dbname\n"
03552          "LOCK IN SHARE MODE\n"
03553          "ORDER BY FOR_NAME;\n"
03554          "BEGIN\n"
03555          "found := 1;\n"
03556          "OPEN cur;\n"
03557          "WHILE found = 1 LOOP\n"
03558          "        FETCH cur INTO foreign_id, for_name;\n"
03559          "        IF (SQL % NOTFOUND) THEN\n"
03560          "                found := 0;\n"
03561          "        ELSIF (" TABLE_NOT_IN_THIS_DB ") THEN\n"
03562          "                found := 0;\n"
03563          "        ELSIF (1=1) THEN\n"
03564          "                DELETE FROM SYS_FOREIGN_COLS\n"
03565          "                WHERE ID = foreign_id;\n"
03566          "                DELETE FROM SYS_FOREIGN\n"
03567          "                WHERE ID = foreign_id;\n"
03568          "        END IF;\n"
03569          "END LOOP;\n"
03570          "CLOSE cur;\n"
03571          "COMMIT WORK;\n"
03572          "END;\n",
03573          FALSE, /* do not reserve dict mutex,
03574           we are already holding it */
03575          trx);
03576 
03577   return(err);
03578 }
03579 
03580 /*********************************************************************/
03583 UNIV_INTERN
03584 int
03585 row_drop_database_for_mysql(
03586 /*========================*/
03587   const char* name, 
03588   trx_t*    trx)  
03589 {
03590   dict_table_t* table;
03591   char* table_name;
03592   int err = DB_SUCCESS;
03593   ulint namelen = strlen(name);
03594 
03595   ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
03596   ut_a(name != NULL);
03597   ut_a(name[namelen - 1] == '/');
03598 
03599   trx->op_info = "dropping database";
03600 
03601   trx_start_if_not_started(trx);
03602 loop:
03603   row_mysql_lock_data_dictionary(trx);
03604 
03605   while ((table_name = dict_get_first_table_name_in_db(name))) {
03606     ut_a(memcmp(table_name, name, namelen) == 0);
03607 
03608                 // For the time being I would like to see how often we see
03609                 // lost temporary tables. --Brian
03610                 fprintf(stderr, "Dropping lost temporary table %s\n", table_name);
03611 
03612     table = dict_table_get_low(table_name);
03613 
03614     ut_a(table);
03615 
03616     /* Wait until MySQL does not have any queries running on
03617     the table */
03618 
03619     if (table->n_mysql_handles_opened > 0) {
03620       row_mysql_unlock_data_dictionary(trx);
03621 
03622       ut_print_timestamp(stderr);
03623       fputs("  InnoDB: Warning: MySQL is trying to"
03624             " drop database ", stderr);
03625       ut_print_name(stderr, trx, TRUE, name);
03626       fputs("\n"
03627             "InnoDB: though there are still"
03628             " open handles to table ", stderr);
03629       ut_print_name(stderr, trx, TRUE, table_name);
03630       fputs(".\n", stderr);
03631 
03632       os_thread_sleep(1000000);
03633 
03634       mem_free(table_name);
03635 
03636       goto loop;
03637     }
03638 
03639     err = row_drop_table_for_mysql(table_name, trx, TRUE);
03640     trx_commit_for_mysql(trx);
03641 
03642     if (err != DB_SUCCESS) {
03643       fputs("InnoDB: DROP DATABASE ", stderr);
03644       ut_print_name(stderr, trx, TRUE, name);
03645       fprintf(stderr, " failed with error %lu for table ",
03646         (ulint) err);
03647       ut_print_name(stderr, trx, TRUE, table_name);
03648       putc('\n', stderr);
03649       mem_free(table_name);
03650       break;
03651     }
03652 
03653     mem_free(table_name);
03654   }
03655 
03656   if (err == DB_SUCCESS) {
03657     /* after dropping all tables try to drop all leftover
03658     foreign keys in case orphaned ones exist */
03659     err = (int) drop_all_foreign_keys_in_db(name, trx);
03660 
03661     if (err != DB_SUCCESS) {
03662       fputs("InnoDB: DROP DATABASE ", stderr);
03663       ut_print_name(stderr, trx, TRUE, name);
03664       fprintf(stderr, " failed with error %d while "
03665         "dropping all foreign keys", err);
03666     }
03667   }
03668 
03669   trx_commit_for_mysql(trx);
03670 
03671   row_mysql_unlock_data_dictionary(trx);
03672 
03673   trx->op_info = "";
03674 
03675   return(err);
03676 }
03677 
03678 /*********************************************************************/
03682 static
03683 ibool
03684 row_is_mysql_tmp_table_name(
03685 /*========================*/
03686   const char* name) 
03688 {
03689   return(strstr(name, "/#sql") != NULL);
03690   /* return(strstr(name, "/@0023sql") != NULL); */
03691 }
03692 
03693 /****************************************************************/
03696 static
03697 int
03698 row_delete_constraint_low(
03699 /*======================*/
03700   const char* id,   
03701   trx_t*    trx)    
03702 {
03703   pars_info_t*  info = pars_info_create();
03704 
03705   pars_info_add_str_literal(info, "id", id);
03706 
03707   return((int) que_eval_sql(info,
03708           "PROCEDURE DELETE_CONSTRAINT () IS\n"
03709           "BEGIN\n"
03710           "DELETE FROM SYS_FOREIGN_COLS WHERE ID = :id;\n"
03711           "DELETE FROM SYS_FOREIGN WHERE ID = :id;\n"
03712           "END;\n"
03713           , FALSE, trx));
03714 }
03715 
03716 /****************************************************************/
03719 static
03720 int
03721 row_delete_constraint(
03722 /*==================*/
03723   const char* id,   
03724   const char* database_name,  
03726   mem_heap_t* heap,   
03727   trx_t*    trx)    
03728 {
03729   ulint   err;
03730 
03731   /* New format constraints have ids <databasename>/<constraintname>. */
03732   err = row_delete_constraint_low(
03733     mem_heap_strcat(heap, database_name, id), trx);
03734 
03735   if ((err == DB_SUCCESS) && !strchr(id, '/')) {
03736     /* Old format < 4.0.18 constraints have constraint ids
03737     <number>_<number>. We only try deleting them if the
03738     constraint name does not contain a '/' character, otherwise
03739     deleting a new format constraint named 'foo/bar' from
03740     database 'baz' would remove constraint 'bar' from database
03741     'foo', if it existed. */
03742 
03743     err = row_delete_constraint_low(id, trx);
03744   }
03745 
03746   return((int) err);
03747 }
03748 
03749 /*********************************************************************/
03752 UNIV_INTERN
03753 ulint
03754 row_rename_table_for_mysql(
03755 /*=======================*/
03756   const char* old_name, 
03757   const char* new_name, 
03758   trx_t*    trx,    
03759   ibool   commit)   
03760 {
03761   dict_table_t* table;
03762   ulint   err     = DB_ERROR;
03763   mem_heap_t* heap      = NULL;
03764   const char**  constraints_to_drop = NULL;
03765   ulint   n_constraints_to_drop = 0;
03766   ibool   old_is_tmp, new_is_tmp;
03767   pars_info_t*  info      = NULL;
03768 
03769   ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
03770   ut_a(old_name != NULL);
03771   ut_a(new_name != NULL);
03772 
03773   if (srv_created_new_raw || srv_force_recovery) {
03774     fputs("InnoDB: A new raw disk partition was initialized or\n"
03775           "InnoDB: innodb_force_recovery is on: we do not allow\n"
03776           "InnoDB: database modifications by the user. Shut down\n"
03777           "InnoDB: mysqld and edit my.cnf so that newraw"
03778           " is replaced\n"
03779           "InnoDB: with raw, and innodb_force_... is removed.\n",
03780           stderr);
03781 
03782     goto funct_exit;
03783   }
03784 
03785   trx->op_info = "renaming table";
03786   trx_start_if_not_started(trx);
03787 
03788   old_is_tmp = row_is_mysql_tmp_table_name(old_name);
03789   new_is_tmp = row_is_mysql_tmp_table_name(new_name);
03790 
03791   table = dict_table_get_low(old_name);
03792 
03793   if (!table) {
03794 #if defined(BUILD_DRIZZLE)
03795     err = ENOENT;
03796 #else
03797     err = DB_TABLE_NOT_FOUND;
03798     ut_print_timestamp(stderr);
03799 
03800     fputs("  InnoDB: Error: table ", stderr);
03801     ut_print_name(stderr, trx, TRUE, old_name);
03802     fputs(" does not exist in the InnoDB internal\n"
03803           "InnoDB: data dictionary though MySQL is"
03804           " trying to rename the table.\n"
03805           "InnoDB: Have you copied the .frm file"
03806           " of the table to the\n"
03807           "InnoDB: MySQL database directory"
03808           " from another database?\n"
03809           "InnoDB: You can look for further help from\n"
03810           "InnoDB: " REFMAN "innodb-troubleshooting.html\n",
03811           stderr);
03812 #endif /* BUILD_DRIZZLE */
03813     goto funct_exit;
03814   } else if (table->ibd_file_missing) {
03815     err = DB_TABLE_NOT_FOUND;
03816     ut_print_timestamp(stderr);
03817 
03818     fputs("  InnoDB: Error: table ", stderr);
03819     ut_print_name(stderr, trx, TRUE, old_name);
03820     fputs(" does not have an .ibd file"
03821           " in the database directory.\n"
03822           "InnoDB: You can look for further help from\n"
03823           "InnoDB: " REFMAN "innodb-troubleshooting.html\n",
03824           stderr);
03825     goto funct_exit;
03826   } else if (new_is_tmp) {
03827     /* MySQL is doing an ALTER TABLE command and it renames the
03828     original table to a temporary table name. We want to preserve
03829     the original foreign key constraint definitions despite the
03830     name change. An exception is those constraints for which
03831     the ALTER TABLE contained DROP FOREIGN KEY <foreign key id>.*/
03832 
03833     heap = mem_heap_create(100);
03834 
03835     err = dict_foreign_parse_drop_constraints(
03836       heap, trx, table, &n_constraints_to_drop,
03837       &constraints_to_drop);
03838 
03839     if (err != DB_SUCCESS) {
03840 
03841       goto funct_exit;
03842     }
03843   }
03844 
03845   /* We use the private SQL parser of Innobase to generate the query
03846   graphs needed in updating the dictionary data from system tables. */
03847 
03848   info = pars_info_create();
03849 
03850   pars_info_add_str_literal(info, "new_table_name", new_name);
03851   pars_info_add_str_literal(info, "old_table_name", old_name);
03852 
03853   err = que_eval_sql(info,
03854          "PROCEDURE RENAME_TABLE () IS\n"
03855          "BEGIN\n"
03856          "UPDATE SYS_TABLES SET NAME = :new_table_name\n"
03857          " WHERE NAME = :old_table_name;\n"
03858          "END;\n"
03859          , FALSE, trx);
03860 
03861   if (err != DB_SUCCESS) {
03862 
03863     goto end;
03864   } else if (!new_is_tmp) {
03865     /* Rename all constraints. */
03866 
03867     info = pars_info_create();
03868 
03869     pars_info_add_str_literal(info, "new_table_name", new_name);
03870     pars_info_add_str_literal(info, "old_table_name", old_name);
03871 
03872     err = que_eval_sql(
03873       info,
03874       "PROCEDURE RENAME_CONSTRAINT_IDS () IS\n"
03875       "gen_constr_prefix CHAR;\n"
03876       "new_db_name CHAR;\n"
03877       "foreign_id CHAR;\n"
03878       "new_foreign_id CHAR;\n"
03879       "old_db_name_len INT;\n"
03880       "old_t_name_len INT;\n"
03881       "new_db_name_len INT;\n"
03882       "id_len INT;\n"
03883       "found INT;\n"
03884       "BEGIN\n"
03885       "found := 1;\n"
03886       "old_db_name_len := INSTR(:old_table_name, '/')-1;\n"
03887       "new_db_name_len := INSTR(:new_table_name, '/')-1;\n"
03888       "new_db_name := SUBSTR(:new_table_name, 0,\n"
03889       "                      new_db_name_len);\n"
03890       "old_t_name_len := LENGTH(:old_table_name);\n"
03891       "gen_constr_prefix := CONCAT(:old_table_name,\n"
03892       "                            '_ibfk_');\n"
03893       "WHILE found = 1 LOOP\n"
03894       "       SELECT ID INTO foreign_id\n"
03895       "        FROM SYS_FOREIGN\n"
03896       "        WHERE FOR_NAME = :old_table_name\n"
03897       "         AND TO_BINARY(FOR_NAME)\n"
03898       "           = TO_BINARY(:old_table_name)\n"
03899       "         LOCK IN SHARE MODE;\n"
03900       "       IF (SQL % NOTFOUND) THEN\n"
03901       "        found := 0;\n"
03902       "       ELSE\n"
03903       "        UPDATE SYS_FOREIGN\n"
03904       "        SET FOR_NAME = :new_table_name\n"
03905       "         WHERE ID = foreign_id;\n"
03906       "        id_len := LENGTH(foreign_id);\n"
03907       "        IF (INSTR(foreign_id, '/') > 0) THEN\n"
03908       "               IF (INSTR(foreign_id,\n"
03909       "                         gen_constr_prefix) > 0)\n"
03910       "               THEN\n"
03911       "                new_foreign_id :=\n"
03912       "                CONCAT(:new_table_name,\n"
03913       "                SUBSTR(foreign_id, old_t_name_len,\n"
03914       "                       id_len - old_t_name_len));\n"
03915       "               ELSE\n"
03916       "                new_foreign_id :=\n"
03917       "                CONCAT(new_db_name,\n"
03918       "                SUBSTR(foreign_id,\n"
03919       "                       old_db_name_len,\n"
03920       "                       id_len - old_db_name_len));\n"
03921       "               END IF;\n"
03922       "               UPDATE SYS_FOREIGN\n"
03923       "                SET ID = new_foreign_id\n"
03924       "                WHERE ID = foreign_id;\n"
03925       "               UPDATE SYS_FOREIGN_COLS\n"
03926       "                SET ID = new_foreign_id\n"
03927       "                WHERE ID = foreign_id;\n"
03928       "        END IF;\n"
03929       "       END IF;\n"
03930       "END LOOP;\n"
03931       "UPDATE SYS_FOREIGN SET REF_NAME = :new_table_name\n"
03932       "WHERE REF_NAME = :old_table_name\n"
03933       "  AND TO_BINARY(REF_NAME)\n"
03934       "    = TO_BINARY(:old_table_name);\n"
03935       "END;\n"
03936       , FALSE, trx);
03937 
03938   } else if (n_constraints_to_drop > 0) {
03939     /* Drop some constraints of tmp tables. */
03940 
03941     ulint db_name_len = dict_get_db_name_len(old_name) + 1;
03942     char* db_name = mem_heap_strdupl(heap, old_name,
03943                db_name_len);
03944     ulint i;
03945 
03946     for (i = 0; i < n_constraints_to_drop; i++) {
03947       err = row_delete_constraint(constraints_to_drop[i],
03948                 db_name, heap, trx);
03949 
03950       if (err != DB_SUCCESS) {
03951         break;
03952       }
03953     }
03954   }
03955 
03956 end:
03957   if (err != DB_SUCCESS) {
03958     if (err == DB_DUPLICATE_KEY) {
03959       ut_print_timestamp(stderr);
03960       fputs("  InnoDB: Error; possible reasons:\n"
03961             "InnoDB: 1) Table rename would cause"
03962             " two FOREIGN KEY constraints\n"
03963             "InnoDB: to have the same internal name"
03964             " in case-insensitive comparison.\n"
03965             "InnoDB: 2) table ", stderr);
03966       ut_print_name(stderr, trx, TRUE, new_name);
03967       fputs(" exists in the InnoDB internal data\n"
03968             "InnoDB: dictionary though MySQL is"
03969             " trying to rename table ", stderr);
03970       ut_print_name(stderr, trx, TRUE, old_name);
03971       fputs(" to it.\n"
03972             "InnoDB: Have you deleted the .frm file"
03973             " and not used DROP TABLE?\n"
03974             "InnoDB: You can look for further help from\n"
03975             "InnoDB: " REFMAN "innodb-troubleshooting.html\n"
03976             "InnoDB: If table ", stderr);
03977       ut_print_name(stderr, trx, TRUE, new_name);
03978       fputs(" is a temporary table #sql..., then"
03979             " it can be that\n"
03980             "InnoDB: there are still queries running"
03981             " on the table, and it will be\n"
03982             "InnoDB: dropped automatically when"
03983             " the queries end.\n"
03984             "InnoDB: You can drop the orphaned table"
03985             " inside InnoDB by\n"
03986             "InnoDB: creating an InnoDB table with"
03987             " the same name in another\n"
03988             "InnoDB: database and copying the .frm file"
03989             " to the current database.\n"
03990             "InnoDB: Then MySQL thinks the table exists,"
03991             " and DROP TABLE will\n"
03992             "InnoDB: succeed.\n", stderr);
03993     }
03994     trx->error_state = DB_SUCCESS;
03995     trx_general_rollback_for_mysql(trx, NULL);
03996     trx->error_state = DB_SUCCESS;
03997   } else {
03998     /* The following call will also rename the .ibd data file if
03999     the table is stored in a single-table tablespace */
04000 
04001     if (!dict_table_rename_in_cache(table, new_name,
04002             !new_is_tmp)) {
04003       trx->error_state = DB_SUCCESS;
04004       trx_general_rollback_for_mysql(trx, NULL);
04005       trx->error_state = DB_SUCCESS;
04006       goto funct_exit;
04007     }
04008 
04009     /* We only want to switch off some of the type checking in
04010     an ALTER, not in a RENAME. */
04011 
04012     err = dict_load_foreigns(
04013       new_name, FALSE, !old_is_tmp || trx->check_foreigns);
04014 
04015     if (err != DB_SUCCESS) {
04016       ut_print_timestamp(stderr);
04017 
04018       if (old_is_tmp) {
04019         fputs("  InnoDB: Error: in ALTER TABLE ",
04020               stderr);
04021         ut_print_name(stderr, trx, TRUE, new_name);
04022         fputs("\n"
04023               "InnoDB: has or is referenced"
04024               " in foreign key constraints\n"
04025               "InnoDB: which are not compatible"
04026               " with the new table definition.\n",
04027               stderr);
04028       } else {
04029         fputs("  InnoDB: Error: in RENAME TABLE"
04030               " table ",
04031               stderr);
04032         ut_print_name(stderr, trx, TRUE, new_name);
04033         fputs("\n"
04034               "InnoDB: is referenced in"
04035               " foreign key constraints\n"
04036               "InnoDB: which are not compatible"
04037               " with the new table definition.\n",
04038               stderr);
04039       }
04040 
04041       ut_a(dict_table_rename_in_cache(table,
04042               old_name, FALSE));
04043       trx->error_state = DB_SUCCESS;
04044       trx_general_rollback_for_mysql(trx, NULL);
04045       trx->error_state = DB_SUCCESS;
04046     }
04047   }
04048 
04049 funct_exit:
04050 
04051   if (commit) {
04052     trx_commit_for_mysql(trx);
04053   }
04054 
04055   if (UNIV_LIKELY_NULL(heap)) {
04056     mem_heap_free(heap);
04057   }
04058 
04059   trx->op_info = "";
04060 
04061   return(err);
04062 }
04063 
04064 /*********************************************************************/
04069 UNIV_INTERN
04070 ibool
04071 row_check_index_for_mysql(
04072 /*======================*/
04073   row_prebuilt_t*   prebuilt, 
04075   const dict_index_t* index,    
04076   ulint*      n_rows)   
04078 {
04079   dtuple_t* prev_entry  = NULL;
04080   ulint   matched_fields;
04081   ulint   matched_bytes;
04082   byte*   buf;
04083   ulint   ret;
04084   rec_t*    rec;
04085   ibool   is_ok   = TRUE;
04086   int   cmp;
04087   ibool   contains_null;
04088   ulint   i;
04089   ulint   cnt;
04090   mem_heap_t* heap    = NULL;
04091   ulint   n_ext;
04092   ulint   offsets_[REC_OFFS_NORMAL_SIZE];
04093   ulint*    offsets;
04094   rec_offs_init(offsets_);
04095 
04096   *n_rows = 0;
04097 
04098   buf = static_cast<byte *>(mem_alloc(UNIV_PAGE_SIZE));
04099   heap = mem_heap_create(100);
04100 
04101   cnt = 1000;
04102 
04103   ret = row_search_for_mysql(buf, PAGE_CUR_G, prebuilt, 0, 0);
04104 loop:
04105   /* Check thd->killed every 1,000 scanned rows */
04106   if (--cnt == 0) {
04107     if (trx_is_interrupted(prebuilt->trx)) {
04108       goto func_exit;
04109     }
04110     cnt = 1000;
04111   }
04112 
04113   switch (ret) {
04114   case DB_SUCCESS:
04115     break;
04116   default:
04117     ut_print_timestamp(stderr);
04118     fputs("  InnoDB: Warning: CHECK TABLE on ", stderr);
04119     dict_index_name_print(stderr, prebuilt->trx, index);
04120     fprintf(stderr, " returned %lu\n", ret);
04121     /* fall through (this error is ignored by CHECK TABLE) */
04122   case DB_END_OF_INDEX:
04123 func_exit:
04124     mem_free(buf);
04125     mem_heap_free(heap);
04126 
04127     return(is_ok);
04128   }
04129 
04130   *n_rows = *n_rows + 1;
04131 
04132   /* row_search... returns the index record in buf, record origin offset
04133   within buf stored in the first 4 bytes, because we have built a dummy
04134   template */
04135 
04136   rec = buf + mach_read_from_4(buf);
04137 
04138   offsets = rec_get_offsets(rec, index, offsets_,
04139           ULINT_UNDEFINED, &heap);
04140 
04141   if (prev_entry != NULL) {
04142     matched_fields = 0;
04143     matched_bytes = 0;
04144 
04145     cmp = cmp_dtuple_rec_with_match(prev_entry, rec, offsets,
04146             &matched_fields,
04147             &matched_bytes);
04148     contains_null = FALSE;
04149 
04150     /* In a unique secondary index we allow equal key values if
04151     they contain SQL NULLs */
04152 
04153     for (i = 0;
04154          i < dict_index_get_n_ordering_defined_by_user(index);
04155          i++) {
04156       if (UNIV_SQL_NULL == dfield_get_len(
04157             dtuple_get_nth_field(prev_entry, i))) {
04158 
04159         contains_null = TRUE;
04160       }
04161     }
04162 
04163     if (cmp > 0) {
04164       fputs("InnoDB: index records in a wrong order in ",
04165             stderr);
04166 not_ok:
04167       dict_index_name_print(stderr,
04168                 prebuilt->trx, index);
04169       fputs("\n"
04170             "InnoDB: prev record ", stderr);
04171       dtuple_print(stderr, prev_entry);
04172       fputs("\n"
04173             "InnoDB: record ", stderr);
04174       rec_print_new(stderr, rec, offsets);
04175       putc('\n', stderr);
04176       is_ok = FALSE;
04177     } else if (dict_index_is_unique(index)
04178          && !contains_null
04179          && matched_fields
04180          >= dict_index_get_n_ordering_defined_by_user(
04181            index)) {
04182 
04183       fputs("InnoDB: duplicate key in ", stderr);
04184       goto not_ok;
04185     }
04186   }
04187 
04188   {
04189     mem_heap_t* tmp_heap = NULL;
04190 
04191     /* Empty the heap on each round.  But preserve offsets[]
04192     for the row_rec_to_index_entry() call, by copying them
04193     into a separate memory heap when needed. */
04194     if (UNIV_UNLIKELY(offsets != offsets_)) {
04195       ulint size = rec_offs_get_n_alloc(offsets)
04196         * sizeof *offsets;
04197 
04198       tmp_heap = mem_heap_create(size);
04199       offsets = static_cast<ulint *>(mem_heap_dup(tmp_heap, offsets, size));
04200     }
04201 
04202     mem_heap_empty(heap);
04203 
04204     prev_entry = row_rec_to_index_entry(ROW_COPY_DATA, rec,
04205                 index, offsets,
04206                 &n_ext, heap);
04207 
04208     if (UNIV_LIKELY_NULL(tmp_heap)) {
04209       mem_heap_free(tmp_heap);
04210     }
04211   }
04212 
04213   ret = row_search_for_mysql(buf, PAGE_CUR_G, prebuilt, 0, ROW_SEL_NEXT);
04214 
04215   goto loop;
04216 }
04217 
04218 /*********************************************************************/
04221 UNIV_INTERN
04222 ibool
04223 row_is_magic_monitor_table(
04224 /*=======================*/
04225   const char* table_name) 
04227 {
04228   const char* name; /* table_name without database/ */
04229   ulint   len;
04230 
04231   name = strchr(table_name, '/');
04232   ut_a(name != NULL);
04233   name++;
04234   len = strlen(name) + 1;
04235 
04236   if (STR_EQ(name, len, S_innodb_monitor)
04237       || STR_EQ(name, len, S_innodb_lock_monitor)
04238       || STR_EQ(name, len, S_innodb_tablespace_monitor)
04239       || STR_EQ(name, len, S_innodb_table_monitor)
04240       || STR_EQ(name, len, S_innodb_mem_validate)) {
04241 
04242     return(TRUE);
04243   }
04244 
04245   return(FALSE);
04246 }