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.c219
1 files changed, 175 insertions, 44 deletions
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index 78b32a5642b..d6c8d7ab412 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -28,6 +28,9 @@ Created 9/17/2000 Heikki Tuuri
#include "rem0cmp.h"
#include "log0log.h"
+/* A dummy variable used to fool the compiler */
+ibool row_mysql_identically_false = FALSE;
+
/* List of tables we should drop in background. ALTER TABLE in MySQL requires
that the table handler can drop the table in background when there are no
queries to it any more. Protected by the kernel mutex. */
@@ -63,15 +66,48 @@ 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 */
+ the space for the length may vary from 1
+ to 4 bytes */
+ byte* data, /* in: BLOB data; if the value to store
+ is SQL NULL this should be NULL pointer */
+ ulint len) /* in: BLOB length; if the value to store
+ is SQL NULL this should be 0; remember
+ also to set the NULL bit in the MySQL record
+ header! */
{
+ ulint sum = 0;
+ ulint i;
+
+ /* MySQL might assume the field is set to zero except the length and
+ the pointer fields */
+
+ memset(dest, '\0', col_len);
+
/* 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. */
+ ut_a(col_len - 8 > 1 || len < 256);
+ ut_a(col_len - 8 > 2 || len < 256 * 256);
+ ut_a(col_len - 8 > 3 || len < 256 * 256 * 256);
+
+ /* We try to track an elusive bug which probably was fixed
+ May 9, 2002, but better be sure: we probe the data buffer
+ to make sure it is in valid allocated memory */
+
+ for (i = 0; i < len; i++) {
+
+ sum += (ulint)(data + i);
+ }
+
+ /* The variable below is identically false, we just fool the
+ compiler to not optimize away our loop */
+ if (row_mysql_identically_false) {
+
+ printf("Sum %lu\n", sum);
+ }
+
mach_write_to_n_little_endian(dest, col_len - 8, len);
ut_memcpy(dest + col_len - 8, (byte*)&data, sizeof(byte*));
@@ -499,29 +535,24 @@ UNIV_INLINE
void
row_update_statistics_if_needed(
/*============================*/
- row_prebuilt_t* prebuilt) /* in: prebuilt struct */
+ dict_table_t* table) /* in: table */
{
ulint counter;
- counter = prebuilt->table->stat_modified_counter;
-
- /* Since the physical size of an InnoDB row is bigger than the
- MySQL row len, we put a safety factor 2 below */
+ counter = table->stat_modified_counter;
- counter += 2 * prebuilt->mysql_row_len;
-
- prebuilt->table->stat_modified_counter = counter;
+ table->stat_modified_counter = counter + 1;
/* Calculate new statistics if 1 / 16 of table has been modified
since the last time a statistics batch was run, or if
- stat_modified_counter > 2 000 000 000 (to avoid wrap-around) */
+ stat_modified_counter > 2 000 000 000 (to avoid wrap-around).
+ We calculate statistics at most every 16th round, since we may have
+ a counter table which is very small and updated very often. */
if (counter > 2000000000
- || ((ib_longlong)counter >
- (UNIV_PAGE_SIZE * prebuilt->table->stat_clustered_index_size)
- / 16)) {
+ || ((ib_longlong)counter > 16 + table->stat_n_rows / 16)) {
- dict_update_statistics(prebuilt->table);
+ dict_update_statistics(table);
}
}
@@ -712,7 +743,7 @@ run_again:
prebuilt->table->stat_n_rows--;
}
- row_update_statistics_if_needed(prebuilt);
+ row_update_statistics_if_needed(prebuilt->table);
trx->op_info = (char *) "";
return((int) err);
@@ -746,6 +777,43 @@ row_prebuild_sel_graph(
}
/*************************************************************************
+Creates an query graph node of 'update' type to be used in the MySQL
+interface. */
+
+upd_node_t*
+row_create_update_node_for_mysql(
+/*=============================*/
+ /* out, own: update node */
+ dict_table_t* table, /* in: table to update */
+ mem_heap_t* heap) /* in: mem heap from which allocated */
+{
+ upd_node_t* node;
+
+ node = upd_node_create(heap);
+
+ 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), heap);
+
+ node->update_n_fields = dict_table_get_n_cols(table);
+
+ 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;
+
+ return(node);
+}
+
+/*************************************************************************
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. */
@@ -767,26 +835,9 @@ row_get_prebuilt_update_vector(
/* 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 = row_create_update_node_for_mysql(table, prebuilt->heap);
- 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_node = node;
prebuilt->upd_graph =
que_node_get_parent(
@@ -914,7 +965,7 @@ run_again:
que_thr_stop_for_mysql_no_error(thr, trx);
- if (prebuilt->upd_node->is_delete) {
+ if (node->is_delete) {
if (prebuilt->table->stat_n_rows > 0) {
prebuilt->table->stat_n_rows--;
}
@@ -924,13 +975,66 @@ run_again:
srv_n_rows_updated++;
}
- row_update_statistics_if_needed(prebuilt);
+ row_update_statistics_if_needed(prebuilt->table);
trx->op_info = (char *) "";
return((int) err);
}
+/**************************************************************************
+Does a cascaded delete or set null in a foreign key operation. */
+
+ulint
+row_update_cascade_for_mysql(
+/*=========================*/
+ /* out: error code or DB_SUCCESS */
+ que_thr_t* thr, /* in: query thread */
+ upd_node_t* node, /* in: update node used in the cascade
+ or set null operation */
+ dict_table_t* table) /* in: table where we do the operation */
+{
+ ulint err;
+ trx_t* trx;
+
+ trx = thr_get_trx(thr);
+
+run_again:
+ thr->run_node = node;
+ thr->prev_node = node;
+
+ row_upd_step(thr);
+
+ err = trx->error_state;
+
+ if (err == DB_LOCK_WAIT) {
+ que_thr_stop_for_mysql(thr);
+
+ row_mysql_handle_errors(&err, trx, thr, NULL);
+
+ goto run_again;
+ }
+
+ if (err != DB_SUCCESS) {
+
+ return(err);
+ }
+
+ if (node->is_delete) {
+ if (table->stat_n_rows > 0) {
+ table->stat_n_rows--;
+ }
+
+ srv_n_rows_deleted++;
+ } else {
+ srv_n_rows_updated++;
+ }
+
+ row_update_statistics_if_needed(table);
+
+ return(err);
+}
+
/*************************************************************************
Checks if a table is such that we automatically created a clustered
index on it (on row id). */
@@ -1171,6 +1275,7 @@ row_create_table_for_mysql(
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
+ rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex));
heap = mem_heap_create(512);
@@ -1223,6 +1328,8 @@ row_create_table_for_mysql(
}
mutex_exit(&(dict_sys->mutex));
+ rw_lock_x_unlock(&(dict_foreign_key_check_lock));
+
que_graph_free((que_t*) que_node_get_parent(thr));
trx->op_info = (char *) "";
@@ -1270,6 +1377,7 @@ row_create_index_for_mysql(
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
+ rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex));
heap = mem_heap_create(512);
@@ -1300,6 +1408,7 @@ row_create_index_for_mysql(
}
mutex_exit(&(dict_sys->mutex));
+ rw_lock_x_unlock(&(dict_foreign_key_check_lock));
que_graph_free((que_t*) que_node_get_parent(thr));
@@ -1355,6 +1464,7 @@ row_table_add_foreign_constraints(
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
+ rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex));
trx->dict_operation = TRUE;
@@ -1379,6 +1489,7 @@ row_table_add_foreign_constraints(
}
mutex_exit(&(dict_sys->mutex));
+ rw_lock_x_unlock(&(dict_foreign_key_check_lock));
return((int) err);
}
@@ -1473,7 +1584,8 @@ loop:
goto already_dropped;
}
- if (table->n_mysql_handles_opened > 0) {
+ if (table->n_mysql_handles_opened > 0
+ || table->n_foreign_key_checks_running > 0) {
return(n_tables + n_tables_dropped);
}
@@ -1722,6 +1834,9 @@ row_drop_table_for_mysql(
no deadlocks can occur then in these operations */
if (!has_dict_mutex) {
+ /* Prevent foreign key checks while we are dropping the table */
+ rw_lock_x_lock(&(dict_foreign_key_check_lock));
+
mutex_enter(&(dict_sys->mutex));
}
@@ -1734,9 +1849,6 @@ row_drop_table_for_mysql(
graph->fork_type = QUE_FORK_MYSQL_INTERFACE;
- /* Prevent foreign key checks while we are dropping the table */
- rw_lock_x_lock(&(dict_foreign_key_check_lock));
-
/* Prevent purge from running while we are dropping the table */
rw_lock_s_lock(&(purge_sys->purge_is_running));
@@ -1771,6 +1883,22 @@ row_drop_table_for_mysql(
goto funct_exit;
}
+ if (table->n_foreign_key_checks_running > 0) {
+
+ ut_print_timestamp(stderr);
+ fprintf(stderr,
+ " InnoDB: You are trying to drop table %s\n"
+ "InnoDB: though there are foreign key check running on it.\n"
+ "InnoDB: Adding the table to the background drop queue.\n",
+ table->name);
+
+ row_add_table_to_background_drop_list(table);
+
+ err = DB_SUCCESS;
+
+ goto funct_exit;
+ }
+
/* Remove any locks there are on the table or its records */
lock_reset_all_on_table(table);
@@ -1798,10 +1926,9 @@ row_drop_table_for_mysql(
funct_exit:
rw_lock_s_unlock(&(purge_sys->purge_is_running));
- rw_lock_x_unlock(&(dict_foreign_key_check_lock));
-
if (!has_dict_mutex) {
mutex_exit(&(dict_sys->mutex));
+ rw_lock_x_unlock(&(dict_foreign_key_check_lock));
}
que_graph_free(graph);
@@ -1837,6 +1964,7 @@ row_drop_database_for_mysql(
trx_start_if_not_started(trx);
loop:
+ rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex));
while ((table_name = dict_get_first_table_name_in_db(name))) {
@@ -1878,6 +2006,7 @@ loop:
}
mutex_exit(&(dict_sys->mutex));
+ rw_lock_x_unlock(&(dict_foreign_key_check_lock));
trx_commit_for_mysql(trx);
@@ -2014,6 +2143,7 @@ row_rename_table_for_mysql(
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
+ rw_lock_x_lock(&(dict_foreign_key_check_lock));
mutex_enter(&(dict_sys->mutex));
table = dict_table_get_low(old_name);
@@ -2095,6 +2225,7 @@ row_rename_table_for_mysql(
}
funct_exit:
mutex_exit(&(dict_sys->mutex));
+ rw_lock_x_unlock(&(dict_foreign_key_check_lock));
que_graph_free(graph);