summaryrefslogtreecommitdiff
path: root/innobase/row/row0mysql.c
diff options
context:
space:
mode:
Diffstat (limited to 'innobase/row/row0mysql.c')
-rw-r--r--innobase/row/row0mysql.c1116
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);
+}