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.c195
1 files changed, 149 insertions, 46 deletions
diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c
index 60dbf059673..f8a98f74c09 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 */
@@ -543,7 +552,7 @@ static
void
row_ins_foreign_report_err(
/*=======================*/
- char* errstr, /* in: error string from the viewpoint
+ const char* errstr, /* in: error string from the viewpoint
of the parent table */
que_thr_t* thr, /* in: query thread whose run_node
is an update node */
@@ -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,9 +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 */
@@ -718,7 +732,7 @@ row_ins_foreign_check_on_constraint(
(DICT_FOREIGN_ON_DELETE_CASCADE
| DICT_FOREIGN_ON_DELETE_SET_NULL))) {
- row_ins_foreign_report_err((char*)"Trying to delete",
+ row_ins_foreign_report_err("Trying to delete",
thr, foreign,
btr_pcur_get_rec(pcur), entry);
@@ -731,7 +745,7 @@ row_ins_foreign_check_on_constraint(
/* This is an UPDATE */
- row_ins_foreign_report_err((char*)"Trying to update",
+ row_ins_foreign_report_err("Trying to update",
thr, foreign,
btr_pcur_get_rec(pcur), entry);
@@ -792,7 +806,7 @@ row_ins_foreign_check_on_constraint(
err = DB_ROW_IS_REFERENCED;
row_ins_foreign_report_err(
-(char*)"Trying an update, possibly causing a cyclic cascaded update\n"
+"Trying an update, possibly causing a cyclic cascaded update\n"
"in the child table,", thr, foreign, btr_pcur_get_rec(pcur), entry);
goto nonstandard_exit_func;
@@ -845,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);
@@ -927,7 +941,7 @@ row_ins_foreign_check_on_constraint(
err = DB_ROW_IS_REFERENCED;
row_ins_foreign_report_err(
-(char*)"Trying a cascaded update where the updated value in the child\n"
+"Trying a cascaded update where the updated value in the child\n"
"table would not fit in the length of the column, or the value would\n"
"be NULL and the column is declared as not NULL in the child table,",
thr, foreign, btr_pcur_get_rec(pcur), entry);
@@ -1037,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
@@ -1072,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
@@ -1080,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 */
@@ -1131,26 +1173,26 @@ run_again:
check_index = foreign->foreign_index;
}
- if (check_table == NULL) {
+ if (check_table == NULL || check_table->ibd_file_missing) {
if (check_ref) {
FILE* ef = dict_foreign_err_file;
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);
+ 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);
- fputs(" does not currently exist!\n", ef);
+ ut_print_name(ef, trx, foreign->referenced_table_name);
+ fputs("\nor its .ibd file does not currently exist!\n", ef);
mutex_exit(&dict_foreign_err_mutex);
return(DB_NO_REFERENCED_ROW);
@@ -1262,7 +1304,7 @@ run_again:
}
} else {
row_ins_foreign_report_err(
- (char*)"Trying to delete or update",
+ "Trying to delete or update",
thr, foreign, rec, entry);
err = DB_ROW_IS_REFERENCED;
@@ -1282,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;
}
@@ -1298,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;
@@ -1317,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);
@@ -1492,7 +1534,8 @@ row_ins_scan_sec_index_for_duplicate(
ulint err = DB_SUCCESS;
ibool moved;
mtr_t mtr;
-
+ trx_t* trx;
+
n_unique = dict_index_get_n_unique(index);
/* If the secondary index is unique, but one of the fields in the
@@ -1529,8 +1572,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) {
@@ -1629,9 +1687,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);
@@ -1652,8 +1725,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);
@@ -1747,6 +1836,7 @@ row_ins_index_entry_low(
ulint modify = 0; /* remove warning */
rec_t* insert_rec;
rec_t* rec;
+ rec_t* first_rec;
ulint err;
ulint n_unique;
big_rec_t* big_rec = NULL;
@@ -1779,6 +1869,14 @@ row_ins_index_entry_low(
goto function_exit;
}
+ first_rec = page_rec_get_next(page_get_infimum_rec(
+ buf_frame_align(btr_cur_get_rec(&cursor))));
+
+ if (!page_rec_is_supremum(first_rec)) {
+ ut_a((rec_get_n_fields(first_rec))
+ == dtuple_get_n_fields(entry));
+ }
+
n_unique = dict_index_get_n_unique(index);
if (index->type & DICT_UNIQUE && (cursor.up_match >= n_unique
@@ -1945,6 +2043,7 @@ row_ins_index_entry_set_vals(
dfield_t* row_field;
ulint n_fields;
ulint i;
+ dtype_t* cur_type;
ut_ad(entry && row);
@@ -1958,10 +2057,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;
}
@@ -2246,4 +2349,4 @@ error_handling:
}
return(thr);
-}
+}