diff options
Diffstat (limited to 'innobase/row/row0mysql.c')
-rw-r--r-- | innobase/row/row0mysql.c | 1116 |
1 files changed, 1116 insertions, 0 deletions
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c new file mode 100644 index 00000000000..13d84ffd358 --- /dev/null +++ b/innobase/row/row0mysql.c @@ -0,0 +1,1116 @@ +/****************************************************** +Interface between Innobase row operations and MySQL. +Contains also create table and other data dictionary operations. + +(c) 2000 Innobase Oy + +Created 9/17/2000 Heikki Tuuri +*******************************************************/ + +#include "row0mysql.h" + +#ifdef UNIV_NONINL +#include "row0mysql.ic" +#endif + +#include "row0ins.h" +#include "row0sel.h" +#include "row0upd.h" +#include "row0row.h" +#include "que0que.h" +#include "pars0pars.h" +#include "dict0dict.h" +#include "dict0crea.h" +#include "trx0roll.h" +#include "trx0purge.h" +#include "lock0lock.h" + +/*********************************************************************** +Reads a MySQL format variable-length field (like VARCHAR) length and +returns pointer to the field data. */ + +byte* +row_mysql_read_var_ref_noninline( +/*=============================*/ + /* out: field + 2 */ + ulint* len, /* out: variable-length field length */ + byte* field) /* in: field */ +{ + return(row_mysql_read_var_ref(len, field)); +} + +/*********************************************************************** +Stores a reference to a BLOB in the MySQL format. */ + +void +row_mysql_store_blob_ref( +/*=====================*/ + byte* dest, /* in: where to store */ + ulint col_len, /* in: dest buffer size: determines into + how many bytes the BLOB length is stored, + this may vary from 1 to 4 bytes */ + byte* data, /* in: BLOB data */ + ulint len) /* in: BLOB length */ +{ + /* In dest there are 1 - 4 bytes reserved for the BLOB length, + and after that 8 bytes reserved for the pointer to the data. + In 32-bit architectures we only use the first 4 bytes of the pointer + slot. */ + + mach_write_to_n_little_endian(dest, col_len - 8, len); + + ut_memcpy(dest + col_len - 8, (byte*)&data, sizeof(byte*)); +} + +/*********************************************************************** +Reads a reference to a BLOB in the MySQL format. */ + +byte* +row_mysql_read_blob_ref( +/*====================*/ + /* out: pointer to BLOB data */ + ulint* len, /* out: BLOB length */ + byte* ref, /* in: BLOB reference in the MySQL format */ + ulint col_len) /* in: BLOB reference length (not BLOB + length) */ +{ + byte* data; + + *len = mach_read_from_n_little_endian(ref, col_len - 8); + + ut_memcpy((byte*)&data, ref + col_len - 8, sizeof(byte*)); + + return(data); +} + +/****************************************************************** +Convert a row in the MySQL format to a row in the Innobase format. */ +static +void +row_mysql_convert_row_to_innobase( +/*==============================*/ + dtuple_t* row, /* in/out: Innobase row where the + field type information is already + copied there, or will be copied + later */ + byte* buf, /* in/out: buffer to use in converting + data in columns; this must be at least + the size of mysql_rec! */ + row_prebuilt_t* prebuilt, /* in: prebuilt struct where template + must be of type ROW_MYSQL_WHOLE_ROW */ + byte* mysql_rec) /* in: row in the MySQL format; + NOTE: do not discard as long as + row is used, as row may contain + pointers to this record! */ +{ + mysql_row_templ_t* templ; + dfield_t* dfield; + ulint i; + + ut_ad(prebuilt->template_type == ROW_MYSQL_WHOLE_ROW); + ut_ad(prebuilt->mysql_template); + + for (i = 0; i < prebuilt->n_template; i++) { + + templ = prebuilt->mysql_template + i; + dfield = dtuple_get_nth_field(row, i); + + if (templ->mysql_null_bit_mask != 0) { + /* Column may be SQL NULL */ + + if (mysql_rec[templ->mysql_null_byte_offset] & + (byte) (templ->mysql_null_bit_mask)) { + + /* It is SQL NULL */ + + dfield_set_data(dfield, NULL, UNIV_SQL_NULL); + + goto next_column; + } + } + + row_mysql_store_col_in_innobase_format(dfield, + prebuilt->ins_upd_rec_buff + + templ->mysql_col_offset, + mysql_rec + templ->mysql_col_offset, + templ->mysql_col_len, + templ->type, templ->is_unsigned); +next_column: + ; + } +} + +/******************************************************************** +Handles user errors and lock waits detected by the database engine. */ + +ibool +row_mysql_handle_errors( +/*====================*/ + /* out: TRUE if it was a lock wait and + we should continue running the query thread */ + ulint* new_err,/* out: possible new error encountered in + rollback, or the old error which was + during the function entry */ + trx_t* trx, /* in: transaction */ + que_thr_t* thr, /* in: query thread */ + trx_savept_t* savept) /* in: savepoint */ +{ + ibool timeout_expired; + ulint err; + +handle_new_error: + err = trx->error_state; + + ut_a(err != DB_SUCCESS); + + trx->error_state = DB_SUCCESS; + + if (err == DB_DUPLICATE_KEY) { + if (savept) { + /* Roll back the latest, possibly incomplete + insertion or update */ + + trx_general_rollback_for_mysql(trx, TRUE, savept); + } + } else if (err == DB_TOO_BIG_RECORD) { + if (savept) { + /* Roll back the latest, possibly incomplete + insertion or update */ + + trx_general_rollback_for_mysql(trx, TRUE, savept); + } + } else if (err == DB_LOCK_WAIT) { + + timeout_expired = srv_suspend_mysql_thread(thr); + + if (timeout_expired) { + trx->error_state = DB_DEADLOCK; + + que_thr_stop_for_mysql(thr); + + goto handle_new_error; + } + + *new_err = err; + + return(TRUE); + + } else if (err == DB_DEADLOCK) { + + /* Roll back the whole transaction */ + + trx_general_rollback_for_mysql(trx, FALSE, NULL); + + } else if (err == DB_OUT_OF_FILE_SPACE) { + + /* Roll back the whole transaction */ + + trx_general_rollback_for_mysql(trx, FALSE, NULL); + } else if (err == DB_MUST_GET_MORE_FILE_SPACE) { + + ut_a(0); /* TODO: print something to MySQL error log */ + } else { + ut_a(0); + } + + if (trx->error_state != DB_SUCCESS) { + *new_err = trx->error_state; + } else { + *new_err = err; + } + + trx->error_state = DB_SUCCESS; + + return(FALSE); +} + +/************************************************************************ +Create a prebuilt struct for a MySQL table handle. */ + +row_prebuilt_t* +row_create_prebuilt( +/*================*/ + /* out, own: a prebuilt struct */ + dict_table_t* table) /* in: Innobase table handle */ +{ + row_prebuilt_t* prebuilt; + mem_heap_t* heap; + dict_index_t* clust_index; + dtuple_t* ref; + ulint ref_len; + ulint i; + + heap = mem_heap_create(128); + + prebuilt = mem_heap_alloc(heap, sizeof(row_prebuilt_t)); + + prebuilt->table = table; + + prebuilt->trx = NULL; + + prebuilt->sql_stat_start = TRUE; + + prebuilt->index = NULL; + prebuilt->n_template = 0; + prebuilt->mysql_template = NULL; + + prebuilt->heap = heap; + prebuilt->ins_node = NULL; + + prebuilt->ins_upd_rec_buff = NULL; + + prebuilt->upd_node = NULL; + prebuilt->ins_graph = NULL; + prebuilt->upd_graph = NULL; + + prebuilt->pcur = btr_pcur_create_for_mysql(); + prebuilt->clust_pcur = btr_pcur_create_for_mysql(); + + prebuilt->select_lock_type = LOCK_NONE; + + prebuilt->sel_graph = NULL; + + prebuilt->search_tuple = dtuple_create(heap, + dict_table_get_n_cols(table)); + + clust_index = dict_table_get_first_index(table); + + ref_len = dict_index_get_n_unique(clust_index); + + ref = dtuple_create(heap, ref_len); + + dict_index_copy_types(ref, clust_index, ref_len); + + prebuilt->clust_ref = ref; + + for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) { + prebuilt->fetch_cache[i] = NULL; + } + + prebuilt->n_fetch_cached = 0; + + prebuilt->blob_heap = NULL; + + prebuilt->old_vers_heap = NULL; + + return(prebuilt); +} + +/************************************************************************ +Free a prebuilt struct for a MySQL table handle. */ + +void +row_prebuilt_free( +/*==============*/ + row_prebuilt_t* prebuilt) /* in, own: prebuilt struct */ +{ + ulint i; + + btr_pcur_free_for_mysql(prebuilt->pcur); + btr_pcur_free_for_mysql(prebuilt->clust_pcur); + + if (prebuilt->mysql_template) { + mem_free(prebuilt->mysql_template); + } + + if (prebuilt->ins_graph) { + que_graph_free_recursive(prebuilt->ins_graph); + } + + if (prebuilt->sel_graph) { + que_graph_free_recursive(prebuilt->sel_graph); + } + + if (prebuilt->upd_graph) { + que_graph_free_recursive(prebuilt->upd_graph); + } + + if (prebuilt->blob_heap) { + mem_heap_free(prebuilt->blob_heap); + } + + if (prebuilt->old_vers_heap) { + mem_heap_free(prebuilt->old_vers_heap); + } + + for (i = 0; i < MYSQL_FETCH_CACHE_SIZE; i++) { + if (prebuilt->fetch_cache[i] != NULL) { + mem_free(prebuilt->fetch_cache[i]); + } + } + + mem_heap_free(prebuilt->heap); +} + +/************************************************************************* +Updates the transaction pointers in query graphs stored in the prebuilt +struct. */ + +void +row_update_prebuilt_trx( +/*====================*/ + /* out: prebuilt dtuple */ + row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL + handle */ + trx_t* trx) /* in: transaction handle */ +{ + prebuilt->trx = trx; + + if (prebuilt->ins_graph) { + prebuilt->ins_graph->trx = trx; + } + + if (prebuilt->upd_graph) { + prebuilt->upd_graph->trx = trx; + } + + if (prebuilt->sel_graph) { + prebuilt->sel_graph->trx = trx; + } +} + +/************************************************************************* +Gets pointer to a prebuilt dtuple used in insertions. If the insert graph +has not yet been built in the prebuilt struct, then this function first +builds it. */ +static +dtuple_t* +row_get_prebuilt_insert_row( +/*========================*/ + /* out: prebuilt dtuple */ + row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL + handle */ +{ + ins_node_t* node; + dtuple_t* row; + dict_table_t* table = prebuilt->table; + + ut_ad(prebuilt && table && prebuilt->trx); + + if (prebuilt->ins_node == NULL) { + + /* Not called before for this handle: create an insert node + and query graph to the prebuilt struct */ + + node = ins_node_create(INS_DIRECT, table, prebuilt->heap); + + prebuilt->ins_node = node; + + if (prebuilt->ins_upd_rec_buff == NULL) { + prebuilt->ins_upd_rec_buff = mem_heap_alloc( + prebuilt->heap, + prebuilt->mysql_row_len); + } + + row = dtuple_create(prebuilt->heap, + dict_table_get_n_cols(table)); + + dict_table_copy_types(row, table); + + ins_node_set_new_row(node, row); + + prebuilt->ins_graph = + que_node_get_parent( + pars_complete_graph_for_exec(node, + prebuilt->trx, + prebuilt->heap)); + prebuilt->ins_graph->state = QUE_FORK_ACTIVE; + } + + return(prebuilt->ins_node->row); +} + +/************************************************************************* +Updates the table modification counter and calculates new estimates +for table and index statistics if necessary. */ +UNIV_INLINE +void +row_update_statistics_if_needed( +/*============================*/ + row_prebuilt_t* prebuilt) /* in: prebuilt struct */ +{ + ulint counter; + ulint old_counter; + + counter = prebuilt->table->stat_modif_counter; + + counter += prebuilt->mysql_row_len; + prebuilt->table->stat_modif_counter = counter; + + old_counter = prebuilt->table->stat_last_estimate_counter; + + if (counter - old_counter >= DICT_STAT_CALCULATE_INTERVAL + || counter - old_counter >= + (UNIV_PAGE_SIZE + * prebuilt->table->stat_clustered_index_size / 2)) { + + dict_update_statistics(prebuilt->table); + } +} + +/************************************************************************* +Does an insert for MySQL. */ + +int +row_insert_for_mysql( +/*=================*/ + /* out: error code or DB_SUCCESS */ + byte* mysql_rec, /* in: row in the MySQL format */ + row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL + handle */ +{ + trx_savept_t savept; + que_thr_t* thr; + ulint err; + ibool was_lock_wait; + trx_t* trx = prebuilt->trx; + ins_node_t* node = prebuilt->ins_node; + + ut_ad(trx); + ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); + + if (node == NULL) { + row_get_prebuilt_insert_row(prebuilt); + node = prebuilt->ins_node; + } + + row_mysql_convert_row_to_innobase(node->row, + prebuilt->ins_upd_rec_buff, + prebuilt, mysql_rec); + savept = trx_savept_take(trx); + + thr = que_fork_get_first_thr(prebuilt->ins_graph); + + if (prebuilt->sql_stat_start) { + node->state = INS_NODE_SET_IX_LOCK; + prebuilt->sql_stat_start = FALSE; + } else { + node->state = INS_NODE_ALLOC_ROW_ID; + } + + que_thr_move_to_run_state_for_mysql(thr, trx); + +run_again: + thr->run_node = node; + thr->prev_node = node; + + row_ins_step(thr); + + err = trx->error_state; + + if (err != DB_SUCCESS) { + que_thr_stop_for_mysql(thr); + + was_lock_wait = row_mysql_handle_errors(&err, trx, thr, + &savept); + if (was_lock_wait) { + goto run_again; + } + + return(err); + } + + que_thr_stop_for_mysql_no_error(thr, trx); + + prebuilt->table->stat_n_rows++; + + if (prebuilt->table->stat_n_rows == 0) { + /* Avoid wrap-over */ + prebuilt->table->stat_n_rows--; + } + + row_update_statistics_if_needed(prebuilt); + + return((int) err); +} + +/************************************************************************* +Builds a dummy query graph used in selects. */ + +void +row_prebuild_sel_graph( +/*===================*/ + row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL + handle */ +{ + sel_node_t* node; + + ut_ad(prebuilt && prebuilt->trx); + + if (prebuilt->sel_graph == NULL) { + + node = sel_node_create(prebuilt->heap); + + prebuilt->sel_graph = + que_node_get_parent( + pars_complete_graph_for_exec(node, + prebuilt->trx, + prebuilt->heap)); + + prebuilt->sel_graph->state = QUE_FORK_ACTIVE; + } +} + +/************************************************************************* +Gets pointer to a prebuilt update vector used in updates. If the update +graph has not yet been built in the prebuilt struct, then this function +first builds it. */ + +upd_t* +row_get_prebuilt_update_vector( +/*===========================*/ + /* out: prebuilt update vector */ + row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL + handle */ +{ + dict_table_t* table = prebuilt->table; + upd_node_t* node; + + ut_ad(prebuilt && table && prebuilt->trx); + + if (prebuilt->upd_node == NULL) { + + /* Not called before for this handle: create an update node + and query graph to the prebuilt struct */ + + node = upd_node_create(prebuilt->heap); + + prebuilt->upd_node = node; + + node->in_mysql_interface = TRUE; + node->is_delete = FALSE; + node->searched_update = FALSE; + node->select_will_do_update = FALSE; + node->select = NULL; + node->pcur = btr_pcur_create_for_mysql(); + node->table = table; + + node->update = upd_create(dict_table_get_n_cols(table), + prebuilt->heap); + UT_LIST_INIT(node->columns); + node->has_clust_rec_x_lock = TRUE; + node->cmpl_info = 0; + + node->table_sym = NULL; + node->col_assign_list = NULL; + + prebuilt->upd_graph = + que_node_get_parent( + pars_complete_graph_for_exec(node, + prebuilt->trx, + prebuilt->heap)); + prebuilt->upd_graph->state = QUE_FORK_ACTIVE; + } + + return(prebuilt->upd_node->update); +} + +/************************************************************************* +Does an update or delete of a row for MySQL. */ + +int +row_update_for_mysql( +/*=================*/ + /* out: error code or DB_SUCCESS */ + byte* mysql_rec, /* in: the row to be updated, in + the MySQL format */ + row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL + handle */ +{ + trx_savept_t savept; + ulint err; + que_thr_t* thr; + ibool was_lock_wait; + dict_index_t* clust_index; + ulint ref_len; + upd_node_t* node; + dict_table_t* table = prebuilt->table; + trx_t* trx = prebuilt->trx; + mem_heap_t* heap; + dtuple_t* search_tuple; + dtuple_t* row_tuple; + mtr_t mtr; + + ut_ad(prebuilt && trx); + ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); + + node = prebuilt->upd_node; + + clust_index = dict_table_get_first_index(table); + + if (prebuilt->in_update_remember_pos) { + if (prebuilt->index == clust_index) { + btr_pcur_copy_stored_position(node->pcur, + prebuilt->pcur); + } else { + btr_pcur_copy_stored_position(node->pcur, + prebuilt->clust_pcur); + } + + ut_ad(node->pcur->rel_pos == BTR_PCUR_ON); + + goto skip_cursor_search; + } + + /* We have to search for the correct cursor position */ + + ref_len = dict_index_get_n_unique(clust_index); + + heap = mem_heap_create(450); + + row_tuple = dtuple_create(heap, dict_table_get_n_cols(table)); + dict_table_copy_types(row_tuple, table); + + if (prebuilt->ins_upd_rec_buff == NULL) { + prebuilt->ins_upd_rec_buff = mem_heap_alloc(prebuilt->heap, + prebuilt->mysql_row_len); + } + + row_mysql_convert_row_to_innobase(row_tuple, + prebuilt->ins_upd_rec_buff, + prebuilt, mysql_rec); + + search_tuple = dtuple_create(heap, ref_len); + + row_build_row_ref_from_row(search_tuple, table, row_tuple); + + mtr_start(&mtr); + + btr_pcur_open_with_no_init(clust_index, search_tuple, PAGE_CUR_LE, + BTR_SEARCH_LEAF, node->pcur, 0, &mtr); + + btr_pcur_store_position(node->pcur, &mtr); + + mtr_commit(&mtr); + + mem_heap_free(heap); + +skip_cursor_search: + savept = trx_savept_take(trx); + + thr = que_fork_get_first_thr(prebuilt->upd_graph); + + node->state = UPD_NODE_UPDATE_CLUSTERED; + + ut_ad(!prebuilt->sql_stat_start); + + que_thr_move_to_run_state_for_mysql(thr, trx); +run_again: + thr->run_node = node; + thr->prev_node = node; + + row_upd_step(thr); + + err = trx->error_state; + + if (err != DB_SUCCESS) { + que_thr_stop_for_mysql(thr); + + if (err == DB_RECORD_NOT_FOUND) { + trx->error_state = DB_SUCCESS; + + return((int) err); + } + + was_lock_wait = row_mysql_handle_errors(&err, trx, thr, + &savept); + if (was_lock_wait) { + goto run_again; + } + + return(err); + } + + que_thr_stop_for_mysql_no_error(thr, trx); + + if (prebuilt->upd_node->is_delete) { + if (prebuilt->table->stat_n_rows > 0) { + prebuilt->table->stat_n_rows--; + } + } + + row_update_statistics_if_needed(prebuilt); + + return((int) err); +} + +/************************************************************************* +Checks if a table is such that we automatically created a clustered +index on it (on row id). */ + +ibool +row_table_got_default_clust_index( +/*==============================*/ + dict_table_t* table) +{ + dict_index_t* clust_index; + + clust_index = dict_table_get_first_index(table); + + if (dtype_get_mtype(dict_index_get_nth_type(clust_index, 0)) + == DATA_SYS) { + return(TRUE); + } + + return(FALSE); +} + +/************************************************************************* +Does a table creation operation for MySQL. */ + +int +row_create_table_for_mysql( +/*=======================*/ + /* out: error code or DB_SUCCESS */ + dict_table_t* table, /* in: table definition */ + trx_t* trx) /* in: transaction handle */ +{ + tab_node_t* node; + mem_heap_t* heap; + que_thr_t* thr; + ulint err; + + ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); + + /* Serialize data dictionary operations with dictionary mutex: + no deadlocks can occur then in these operations */ + + mutex_enter(&(dict_sys->mutex)); + + heap = mem_heap_create(512); + + trx->dict_operation = TRUE; + + node = tab_create_graph_create(table, heap); + + thr = pars_complete_graph_for_exec(node, trx, heap); + + ut_a(thr == que_fork_start_command(que_node_get_parent(thr), + SESS_COMM_EXECUTE, 0)); + que_run_threads(thr); + + err = trx->error_state; + + if (err != DB_SUCCESS) { + /* We have special error handling here */ + ut_a(err == DB_OUT_OF_FILE_SPACE); + trx->error_state = DB_SUCCESS; + + trx_general_rollback_for_mysql(trx, FALSE, NULL); + + row_drop_table_for_mysql(table->name, trx, TRUE); + + trx->error_state = DB_SUCCESS; + } + + mutex_exit(&(dict_sys->mutex)); + que_graph_free((que_t*) que_node_get_parent(thr)); + + return((int) err); +} + +/************************************************************************* +Does an index creation operation for MySQL. TODO: currently failure +to create an index results in dropping the whole table! This is no problem +currently as all indexes must be created at the same time as the table. */ + +int +row_create_index_for_mysql( +/*=======================*/ + /* out: error number or DB_SUCCESS */ + dict_index_t* index, /* in: index defintion */ + trx_t* trx) /* in: transaction handle */ +{ + ind_node_t* node; + mem_heap_t* heap; + que_thr_t* thr; + ulint err; + + ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); + + /* Serialize data dictionary operations with dictionary mutex: + no deadlocks can occur then in these operations */ + + mutex_enter(&(dict_sys->mutex)); + + heap = mem_heap_create(512); + + trx->dict_operation = TRUE; + + node = ind_create_graph_create(index, heap); + + thr = pars_complete_graph_for_exec(node, trx, heap); + + ut_a(thr == que_fork_start_command(que_node_get_parent(thr), + SESS_COMM_EXECUTE, 0)); + que_run_threads(thr); + + err = trx->error_state; + + if (err != DB_SUCCESS) { + /* We have special error handling here */ + ut_a(err == DB_OUT_OF_FILE_SPACE); + + trx->error_state = DB_SUCCESS; + + trx_general_rollback_for_mysql(trx, FALSE, NULL); + + row_drop_table_for_mysql(index->table_name, trx, TRUE); + + trx->error_state = DB_SUCCESS; + } + + mutex_exit(&(dict_sys->mutex)); + + que_graph_free((que_t*) que_node_get_parent(thr)); + + return((int) err); +} + +/************************************************************************* +Drops a table for MySQL. */ + +int +row_drop_table_for_mysql( +/*=====================*/ + /* out: error code or DB_SUCCESS */ + char* name, /* in: table name */ + trx_t* trx, /* in: transaction handle */ + ibool has_dict_mutex) /* in: TRUE if the caller already owns the + dictionary system mutex */ +{ + dict_table_t* table; + que_thr_t* thr; + que_t* graph; + ulint err; + char* str1; + char* str2; + ulint len; + char buf[10000]; +retry: + ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); + ut_a(name != NULL); + + /* We use the private SQL parser of Innobase to generate the + query graphs needed in deleting the dictionary data from system + tables in Innobase. Deleting a row from SYS_INDEXES table also + frees the file segments of the B-tree associated with the index. */ + + str1 = + "PROCEDURE DROP_TABLE_PROC () IS\n" + "table_id CHAR;\n" + "index_id CHAR;\n" + "found INT;\n" + "BEGIN\n" + "SELECT ID INTO table_id\n" + "FROM SYS_TABLES\n" + "WHERE NAME ='"; + + str2 = + "';\n" + "IF (SQL % NOTFOUND) THEN\n" + " COMMIT WORK;\n" + " RETURN;\n" + "END IF;\n" + "found := 1;\n" + "WHILE found = 1 LOOP\n" + " SELECT ID INTO index_id\n" + " FROM SYS_INDEXES\n" + " WHERE TABLE_ID = table_id;\n" + " IF (SQL % NOTFOUND) THEN\n" + " found := 0;\n" + " ELSE" + " DELETE FROM SYS_FIELDS WHERE INDEX_ID = index_id;\n" + " DELETE FROM SYS_INDEXES WHERE ID = index_id;\n" + " END IF;\n" + "END LOOP;\n" + "DELETE FROM SYS_COLUMNS WHERE TABLE_ID = table_id;\n" + "DELETE FROM SYS_TABLES WHERE ID = table_id;\n" + "COMMIT WORK;\n" + "END;\n"; + + len = ut_strlen(str1); + + ut_memcpy(buf, str1, len); + ut_memcpy(buf + len, name, ut_strlen(name)); + + len += ut_strlen(name); + + ut_memcpy(buf + len, str2, ut_strlen(str2) + 1); + + /* Serialize data dictionary operations with dictionary mutex: + no deadlocks can occur then in these operations */ + + if (!has_dict_mutex) { + mutex_enter(&(dict_sys->mutex)); + } + + graph = pars_sql(buf); + + ut_a(graph); + + graph->trx = trx; + trx->graph = NULL; + + graph->fork_type = QUE_FORK_MYSQL_INTERFACE; + + /* Prevent purge from running while we are dropping the table */ + rw_lock_s_lock(&(purge_sys->purge_is_running)); + + table = dict_table_get_low(name); + + if (!table) { + err = DB_TABLE_NOT_FOUND; + + goto funct_exit; + } + + /* Check if there are any locks on the table: if yes, it cannot + be dropped: we have to wait for the locks to be released */ + + if (lock_is_on_table(table)) { + + err = DB_TABLE_IS_BEING_USED; + + goto funct_exit; + } + + /* TODO: check that MySQL prevents users from accessing the table + after this function row_drop_table_for_mysql has been called: + otherwise anyone with an open handle to the table could, for example, + come to read the table! */ + + trx->dict_operation = TRUE; + trx->table_id = table->id; + + ut_a(thr = que_fork_start_command(graph, SESS_COMM_EXECUTE, 0)); + + que_run_threads(thr); + + err = trx->error_state; + + if (err != DB_SUCCESS) { + ut_a(err == DB_OUT_OF_FILE_SPACE); + + err = DB_MUST_GET_MORE_FILE_SPACE; + + row_mysql_handle_errors(&err, trx, thr, NULL); + + ut_a(0); + } else { + dict_table_remove_from_cache(table); + } +funct_exit: + rw_lock_s_unlock(&(purge_sys->purge_is_running)); + + if (!has_dict_mutex) { + mutex_exit(&(dict_sys->mutex)); + } + + que_graph_free(graph); + + if (err == DB_TABLE_IS_BEING_USED) { + os_thread_sleep(200000); + + goto retry; + } + + return((int) err); +} + +/************************************************************************* +Renames a table for MySQL. */ + +int +row_rename_table_for_mysql( +/*=======================*/ + /* out: error code or DB_SUCCESS */ + char* old_name, /* in: old table name */ + char* new_name, /* in: new table name */ + trx_t* trx) /* in: transaction handle */ +{ + dict_table_t* table; + que_thr_t* thr; + que_t* graph; + ulint err; + char* str1; + char* str2; + char* str3; + ulint len; + char buf[10000]; + + ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); + ut_a(old_name != NULL); + ut_a(new_name != NULL); + + str1 = + "PROCEDURE RENAME_TABLE_PROC () IS\n" + "BEGIN\n" + "UPDATE SYS_TABLES SET NAME ='"; + + str2 = + "' WHERE NAME = '"; + + str3 = + "';\n" + "COMMIT WORK;\n" + "END;\n"; + + len = ut_strlen(str1); + + ut_memcpy(buf, str1, len); + + ut_memcpy(buf + len, new_name, ut_strlen(new_name)); + + len += ut_strlen(new_name); + + ut_memcpy(buf + len, str2, ut_strlen(str2)); + + len += ut_strlen(str2); + + ut_memcpy(buf + len, old_name, ut_strlen(old_name)); + + len += ut_strlen(old_name); + + ut_memcpy(buf + len, str3, ut_strlen(str3) + 1); + + /* Serialize data dictionary operations with dictionary mutex: + no deadlocks can occur then in these operations */ + + mutex_enter(&(dict_sys->mutex)); + + table = dict_table_get_low(old_name); + + graph = pars_sql(buf); + + ut_a(graph); + + graph->trx = trx; + trx->graph = NULL; + + graph->fork_type = QUE_FORK_MYSQL_INTERFACE; + + if (!table) { + err = DB_TABLE_NOT_FOUND; + + goto funct_exit; + } + + ut_a(thr = que_fork_start_command(graph, SESS_COMM_EXECUTE, 0)); + + que_run_threads(thr); + + err = trx->error_state; + + if (err != DB_SUCCESS) { + row_mysql_handle_errors(&err, trx, thr, NULL); + } else { + ut_a(dict_table_rename_in_cache(table, new_name)); + } +funct_exit: + mutex_exit(&(dict_sys->mutex)); + + que_graph_free(graph); + + return((int) err); +} |