summaryrefslogtreecommitdiff
path: root/innobase/row/row0ins.c
diff options
context:
space:
mode:
Diffstat (limited to 'innobase/row/row0ins.c')
-rw-r--r--innobase/row/row0ins.c189
1 files changed, 150 insertions, 39 deletions
diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c
index 0da749212d2..c45818ddd26 100644
--- a/innobase/row/row0ins.c
+++ b/innobase/row/row0ins.c
@@ -50,6 +50,15 @@ innobase_invalidate_query_cache(
ulint full_name_len); /* in: full name length where also the null
chars count */
+/**********************************************************************
+This function returns true if SQL-query in the current thread
+is either REPLACE or LOAD DATA INFILE REPLACE.
+NOTE that /mysql/innobase/row/row0ins.c must contain the
+prototype for this function ! */
+
+ibool
+innobase_query_is_replace(void);
+/*===========================*/
/*************************************************************************
Creates an insert node struct. */
@@ -252,7 +261,7 @@ row_ins_sec_index_entry_by_modify(
heap = mem_heap_create(1024);
update = row_upd_build_sec_rec_difference_binary(cursor->index,
- entry, rec, heap);
+ entry, rec, thr_get_trx(thr), heap);
if (mode == BTR_MODIFY_LEAF) {
/* Try an optimistic updating of the record, keeping changes
within the page */
@@ -316,7 +325,7 @@ row_ins_clust_index_entry_by_modify(
roll_ptr */
update = row_upd_build_difference_binary(cursor->index, entry, ext_vec,
- n_ext_vec, rec, heap);
+ n_ext_vec, rec, thr_get_trx(thr), heap);
if (mode == BTR_MODIFY_LEAF) {
/* Try optimistic updating of the record, keeping changes
within the page */
@@ -554,29 +563,30 @@ row_ins_foreign_report_err(
table */
{
FILE* ef = dict_foreign_err_file;
+ trx_t* trx = thr_get_trx(thr);
mutex_enter(&dict_foreign_err_mutex);
rewind(ef);
ut_print_timestamp(ef);
fputs(" Transaction:\n", ef);
- trx_print(ef, thr_get_trx(thr));
+ trx_print(ef, trx);
fputs("Foreign key constraint fails for table ", ef);
- ut_print_name(ef, foreign->foreign_table_name);
+ ut_print_name(ef, trx, foreign->foreign_table_name);
fputs(":\n", ef);
- dict_print_info_on_foreign_key_in_create_format(ef, foreign);
+ dict_print_info_on_foreign_key_in_create_format(ef, trx, foreign);
putc('\n', ef);
fputs(errstr, ef);
fputs(" in parent table, in index ", ef);
- ut_print_name(ef, foreign->referenced_index->name);
+ ut_print_name(ef, trx, foreign->referenced_index->name);
if (entry) {
fputs(" tuple:\n", ef);
dtuple_print(ef, entry);
}
fputs("\nBut in child table ", ef);
- ut_print_name(ef, foreign->foreign_table_name);
+ ut_print_name(ef, trx, foreign->foreign_table_name);
fputs(", in index ", ef);
- ut_print_name(ef, foreign->foreign_index->name);
+ ut_print_name(ef, trx, foreign->foreign_index->name);
if (rec) {
fputs(", there is a record:\n", ef);
rec_print(ef, rec);
@@ -612,19 +622,19 @@ row_ins_foreign_report_add_err(
fputs(" Transaction:\n", ef);
trx_print(ef, trx);
fputs("Foreign key constraint fails for table ", ef);
- ut_print_name(ef, foreign->foreign_table_name);
+ ut_print_name(ef, trx, foreign->foreign_table_name);
fputs(":\n", ef);
- dict_print_info_on_foreign_key_in_create_format(ef, foreign);
+ dict_print_info_on_foreign_key_in_create_format(ef, trx, foreign);
fputs("\nTrying to add in child table, in index ", ef);
- ut_print_name(ef, foreign->foreign_index->name);
+ ut_print_name(ef, trx, foreign->foreign_index->name);
if (entry) {
fputs(" tuple:\n", ef);
dtuple_print(ef, entry);
}
fputs("\nBut in parent table ", ef);
- ut_print_name(ef, foreign->referenced_table_name);
+ ut_print_name(ef, trx, foreign->referenced_table_name);
fputs(", in index ", ef);
- ut_print_name(ef, foreign->referenced_index->name);
+ ut_print_name(ef, trx, foreign->referenced_index->name);
fputs(",\nthe closest match we can find is record:\n", ef);
if (rec && page_rec_is_supremum(rec)) {
/* If the cursor ended on a supremum record, it is better
@@ -704,11 +714,13 @@ row_ins_foreign_check_on_constraint(
ulint n_to_update;
ulint err;
ulint i;
-
+ trx_t* trx;
ut_a(thr && foreign && pcur && mtr);
+ trx = thr_get_trx(thr);
+
/* Since we are going to delete or update a row, we have to invalidate
the MySQL query cache for table */
@@ -847,7 +859,7 @@ row_ins_foreign_check_on_constraint(
fputs(
"InnoDB: error in cascade of a foreign key op\n"
"InnoDB: ", stderr);
- dict_index_name_print(stderr, index);
+ dict_index_name_print(stderr, trx, index);
fputs("\n"
"InnoDB: record ", stderr);
@@ -968,6 +980,23 @@ row_ins_foreign_check_on_constraint(
err = row_update_cascade_for_mysql(thr, cascade,
foreign->foreign_table);
+
+ if (foreign->foreign_table->n_foreign_key_checks_running == 0) {
+ fprintf(stderr,
+"InnoDB: error: table %s has the counter 0 though there is\n"
+"InnoDB: a FOREIGN KEY check running on it.\n",
+ foreign->foreign_table->name);
+ }
+
+ /* Release the data dictionary latch for a while, so that we do not
+ starve other threads from doing CREATE TABLE etc. if we have a huge
+ cascaded operation running. The counter n_foreign_key_checks_running
+ will prevent other users from dropping or ALTERing the table when we
+ release the latch. */
+
+ row_mysql_unfreeze_data_dictionary(thr_get_trx(thr));
+ row_mysql_freeze_data_dictionary(thr_get_trx(thr));
+
mtr_start(mtr);
/* Restore pcur position */
@@ -1022,6 +1051,33 @@ row_ins_set_shared_rec_lock(
return(err);
}
+
+/*************************************************************************
+Sets a exclusive lock on a record. Used in locking possible duplicate key
+records */
+static
+ulint
+row_ins_set_exclusive_rec_lock(
+/*============================*/
+ /* out: DB_SUCCESS or error code */
+ ulint type, /* in: LOCK_ORDINARY, LOCK_GAP, or
+ LOCK_REC_NOT_GAP type lock */
+ rec_t* rec, /* in: record */
+ dict_index_t* index, /* in: index */
+ que_thr_t* thr) /* in: query thread */
+{
+ ulint err;
+
+ if (index->type & DICT_CLUSTERED) {
+ err = lock_clust_rec_read_check_and_lock(0, rec, index, LOCK_X,
+ type, thr);
+ } else {
+ err = lock_sec_rec_read_check_and_lock(0, rec, index, LOCK_X,
+ type, thr);
+ }
+
+ return(err);
+}
/*******************************************************************
Checks if foreign key constraint fails for an index entry. Sets shared locks
@@ -1057,6 +1113,7 @@ row_ins_check_foreign_constraint(
ulint err;
ulint i;
mtr_t mtr;
+ trx_t* trx = thr_get_trx(thr);
run_again:
#ifdef UNIV_SYNC_DEBUG
@@ -1065,7 +1122,7 @@ run_again:
err = DB_SUCCESS;
- if (thr_get_trx(thr)->check_foreigns == FALSE) {
+ if (trx->check_foreigns == FALSE) {
/* The user has suppressed foreign key checks currently for
this session */
@@ -1123,18 +1180,18 @@ run_again:
rewind(ef);
ut_print_timestamp(ef);
fputs(" Transaction:\n", ef);
- trx_print(ef, thr_get_trx(thr));
+ trx_print(ef, trx);
fputs("Foreign key constraint fails for table ", ef);
- ut_print_name(ef, foreign->foreign_table_name);
+ ut_print_name(ef, trx, foreign->foreign_table_name);
fputs(":\n", ef);
dict_print_info_on_foreign_key_in_create_format(ef,
- foreign);
+ trx, foreign);
fputs("\nTrying to add to index ", ef);
- ut_print_name(ef, foreign->foreign_index->name);
+ ut_print_name(ef, trx, foreign->foreign_index->name);
fputs(" tuple:\n", ef);
dtuple_print(ef, entry);
fputs("\nBut the parent table ", ef);
- ut_print_name(ef, foreign->referenced_table_name);
+ ut_print_name(ef, trx, foreign->referenced_table_name);
fputs(" does not currently exist!\n", ef);
mutex_exit(&dict_foreign_err_mutex);
@@ -1267,7 +1324,7 @@ run_again:
if (check_ref) {
err = DB_NO_REFERENCED_ROW;
row_ins_foreign_report_add_err(
- thr_get_trx(thr), foreign, rec, entry);
+ trx, foreign, rec, entry);
} else {
err = DB_SUCCESS;
}
@@ -1283,7 +1340,7 @@ next_rec:
if (check_ref) {
rec = btr_pcur_get_rec(&pcur);
row_ins_foreign_report_add_err(
- thr_get_trx(thr), foreign, rec, entry);
+ trx, foreign, rec, entry);
err = DB_NO_REFERENCED_ROW;
} else {
err = DB_SUCCESS;
@@ -1302,18 +1359,18 @@ next_rec:
do_possible_lock_wait:
if (err == DB_LOCK_WAIT) {
- thr_get_trx(thr)->error_state = err;
+ trx->error_state = err;
que_thr_stop_for_mysql(thr);
srv_suspend_mysql_thread(thr);
- if (thr_get_trx(thr)->error_state == DB_SUCCESS) {
+ if (trx->error_state == DB_SUCCESS) {
goto run_again;
}
- err = thr_get_trx(thr)->error_state;
+ err = trx->error_state;
}
return(err);
@@ -1451,7 +1508,9 @@ row_ins_scan_sec_index_for_duplicate(
ulint err = DB_SUCCESS;
ibool moved;
mtr_t mtr;
-
+ trx_t* trx;
+ const char* ptr;
+
n_unique = dict_index_get_n_unique(index);
/* If the secondary index is unique, but one of the fields in the
@@ -1488,8 +1547,23 @@ row_ins_scan_sec_index_for_duplicate(
/* Try to place a lock on the index record */
- err = row_ins_set_shared_rec_lock(LOCK_ORDINARY, rec, index,
- thr);
+ trx = thr_get_trx(thr);
+ ut_ad(trx);
+
+ if (innobase_query_is_replace()) {
+
+ /* The manual defines the REPLACE semantics that it
+ is either an INSERT or DELETE(s) for duplicate key
+ + INSERT. Therefore, we should take X-lock for
+ duplicates */
+
+ err = row_ins_set_exclusive_rec_lock(
+ LOCK_ORDINARY,rec,index,thr);
+ } else {
+
+ err = row_ins_set_shared_rec_lock(
+ LOCK_ORDINARY, rec, index,thr);
+ }
if (err != DB_SUCCESS) {
@@ -1556,6 +1630,7 @@ row_ins_duplicate_error_in_clust(
page_t* page;
ulint n_unique;
trx_t* trx = thr_get_trx(thr);
+ const char* ptr;
UT_NOT_USED(mtr);
@@ -1588,9 +1663,24 @@ row_ins_duplicate_error_in_clust(
is needed in logical logging of MySQL to make
sure that in roll-forward we get the same duplicate
errors as in original execution */
-
- err = row_ins_set_shared_rec_lock(LOCK_REC_NOT_GAP,
- rec, cursor->index, thr);
+
+ if (innobase_query_is_replace()) {
+
+ /* The manual defines the REPLACE semantics
+ that it is either an INSERT or DELETE(s)
+ for duplicate key + INSERT. Therefore, we
+ should take X-lock for duplicates */
+
+ err = row_ins_set_exclusive_rec_lock(
+ LOCK_REC_NOT_GAP,rec,cursor->index,
+ thr);
+ } else {
+
+ err = row_ins_set_shared_rec_lock(
+ LOCK_REC_NOT_GAP,rec, cursor->index,
+ thr);
+ }
+
if (err != DB_SUCCESS) {
return(err);
@@ -1611,8 +1701,24 @@ row_ins_duplicate_error_in_clust(
if (rec != page_get_supremum_rec(page)) {
- err = row_ins_set_shared_rec_lock(LOCK_REC_NOT_GAP,
- rec, cursor->index, thr);
+
+ /* The manual defines the REPLACE semantics that it
+ is either an INSERT or DELETE(s) for duplicate key
+ + INSERT. Therefore, we should take X-lock for
+ duplicates. */
+
+ if (innobase_query_is_replace()) {
+
+ err = row_ins_set_exclusive_rec_lock(
+ LOCK_REC_NOT_GAP,
+ rec,cursor->index,thr);
+ } else {
+
+ err = row_ins_set_shared_rec_lock(
+ LOCK_REC_NOT_GAP,rec,
+ cursor->index, thr);
+ }
+
if (err != DB_SUCCESS) {
return(err);
@@ -1913,6 +2019,7 @@ row_ins_index_entry_set_vals(
dfield_t* row_field;
ulint n_fields;
ulint i;
+ dtype_t* cur_type;
ut_ad(entry && row);
@@ -1926,10 +2033,14 @@ row_ins_index_entry_set_vals(
/* Check column prefix indexes */
if (ind_field->prefix_len > 0
- && dfield_get_len(row_field) != UNIV_SQL_NULL
- && dfield_get_len(row_field) > ind_field->prefix_len) {
-
- field->len = ind_field->prefix_len;
+ && dfield_get_len(row_field) != UNIV_SQL_NULL) {
+
+ cur_type = dict_col_get_type(
+ dict_field_get_col(ind_field));
+
+ field->len = dtype_get_at_most_n_mbchars(cur_type,
+ ind_field->prefix_len,
+ dfield_get_len(row_field), row_field->data);
} else {
field->len = row_field->len;
}
@@ -2214,4 +2325,4 @@ error_handling:
}
return(thr);
-}
+}