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.c244
1 files changed, 233 insertions, 11 deletions
diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c
index 8542dcae326..92cac5a55cf 100644
--- a/innobase/row/row0ins.c
+++ b/innobase/row/row0ins.c
@@ -207,16 +207,33 @@ row_ins_sec_index_entry_by_modify(
/*==============================*/
/* out: DB_SUCCESS or error code */
btr_cur_t* cursor, /* in: B-tree cursor */
+ dtuple_t* entry, /* in: index entry to insert */
que_thr_t* thr, /* in: query thread */
mtr_t* mtr) /* in: mtr */
{
- ulint err;
-
- ut_ad(((cursor->index)->type & DICT_CLUSTERED) == 0);
- ut_ad(rec_get_deleted_flag(btr_cur_get_rec(cursor)));
+ mem_heap_t* heap;
+ upd_t* update;
+ rec_t* rec;
+ ulint err;
+
+ rec = btr_cur_get_rec(cursor);
+
+ ut_ad((cursor->index->type & DICT_CLUSTERED) == 0);
+ ut_ad(rec_get_deleted_flag(rec));
- /* We just remove the delete mark from the secondary index record */
- err = btr_cur_del_mark_set_sec_rec(0, cursor, FALSE, thr, mtr);
+ /* We know that in the ordering entry and rec are identified.
+ But in their binary form there may be differences if there
+ are char fields in them. Therefore we have to calculate the
+ difference and do an update-in-place if necessary. */
+
+ heap = mem_heap_create(1024);
+
+ update = row_upd_build_sec_rec_difference_binary(cursor->index,
+ entry, rec, heap);
+
+ err = btr_cur_update_sec_rec_in_place(cursor, update, thr, mtr);
+
+ mem_heap_free(heap);
return(err);
}
@@ -262,7 +279,7 @@ row_ins_clust_index_entry_by_modify(
/* Build an update vector containing all the fields to be modified;
NOTE that this vector may contain also system columns! */
- update = row_upd_build_difference(cursor->index, entry, ext_vec,
+ update = row_upd_build_difference_binary(cursor->index, entry, ext_vec,
n_ext_vec, rec, heap);
if (mode == BTR_MODIFY_LEAF) {
/* Try optimistic updating of the record, keeping changes
@@ -348,6 +365,203 @@ row_ins_set_shared_rec_lock(
}
/*******************************************************************
+Checks if foreign key constraint fails for an index entry. Sets shared locks
+which lock either the success or the failure of the constraint. NOTE that
+the caller must have a shared latch on dict_foreign_key_check_lock. */
+
+ulint
+row_ins_check_foreign_constraint(
+/*=============================*/
+ /* out: DB_SUCCESS, DB_LOCK_WAIT,
+ DB_NO_REFERENCED_ROW,
+ or DB_ROW_IS_REFERENCED */
+ ibool check_ref,/* in: TRUE If we want to check that
+ the referenced table is ok, FALSE if we
+ want to to check the foreign key table */
+ dict_foreign_t* foreign,/* in: foreign constraint; NOTE that the
+ tables mentioned in it must be in the
+ dictionary cache if they exist at all */
+ dict_table_t* table, /* in: if check_ref is TRUE, then the foreign
+ table, else the referenced table */
+ dict_index_t* index, /* in: index in table */
+ dtuple_t* entry, /* in: index entry for index */
+ que_thr_t* thr) /* in: query thread */
+{
+ dict_table_t* check_table;
+ dict_index_t* check_index;
+ ulint n_fields_cmp;
+ rec_t* rec;
+ btr_pcur_t pcur;
+ ibool moved;
+ int cmp;
+ ulint err;
+ mtr_t mtr;
+
+ ut_ad(rw_lock_own(&dict_foreign_key_check_lock, RW_LOCK_SHARED));
+
+ if (check_ref) {
+ check_table = foreign->referenced_table;
+ check_index = foreign->referenced_index;
+ } else {
+ check_table = foreign->foreign_table;
+ check_index = foreign->foreign_index;
+ }
+
+ if (check_table == NULL) {
+ if (check_ref) {
+ return(DB_NO_REFERENCED_ROW);
+ }
+
+ return(DB_SUCCESS);
+ }
+
+ ut_a(check_table && check_index);
+
+ if (check_table != table) {
+ /* We already have a LOCK_IX on table, but not necessarily
+ on check_table */
+
+ err = lock_table(0, check_table, LOCK_IS, thr);
+
+ if (err != DB_SUCCESS) {
+
+ return(err);
+ }
+ }
+
+ mtr_start(&mtr);
+
+ /* Store old value on n_fields_cmp */
+
+ n_fields_cmp = dtuple_get_n_fields_cmp(entry);
+
+ dtuple_set_n_fields_cmp(entry, foreign->n_fields);
+
+ btr_pcur_open(check_index, entry, PAGE_CUR_GE,
+ BTR_SEARCH_LEAF, &pcur, &mtr);
+
+ /* Scan index records and check if there is a matching record */
+
+ for (;;) {
+ rec = btr_pcur_get_rec(&pcur);
+
+ if (rec == page_get_infimum_rec(buf_frame_align(rec))) {
+
+ goto next_rec;
+ }
+
+ /* Try to place a lock on the index record */
+
+ err = row_ins_set_shared_rec_lock(rec, check_index, thr);
+
+ if (err != DB_SUCCESS) {
+
+ break;
+ }
+
+ if (rec == page_get_supremum_rec(buf_frame_align(rec))) {
+
+ goto next_rec;
+ }
+
+ cmp = cmp_dtuple_rec(entry, rec);
+
+ if (cmp == 0) {
+ if (!rec_get_deleted_flag(rec)) {
+ /* Found a matching record */
+
+ if (check_ref) {
+ err = DB_SUCCESS;
+ } else {
+ err = DB_ROW_IS_REFERENCED;
+ }
+
+ break;
+ }
+ }
+
+ if (cmp < 0) {
+ if (check_ref) {
+ err = DB_NO_REFERENCED_ROW;
+ } else {
+ err = DB_SUCCESS;
+ }
+
+ break;
+ }
+
+ ut_a(cmp == 0);
+next_rec:
+ moved = btr_pcur_move_to_next(&pcur, &mtr);
+
+ if (!moved) {
+ if (check_ref) {
+ err = DB_NO_REFERENCED_ROW;
+ } else {
+ err = DB_SUCCESS;
+ }
+
+ break;
+ }
+ }
+
+ mtr_commit(&mtr);
+
+ /* Restore old value */
+ dtuple_set_n_fields_cmp(entry, n_fields_cmp);
+
+ return(err);
+}
+
+/*******************************************************************
+Checks if foreign key constraints fail for an index entry. If index
+is not mentioned in any constraint, this function does nothing,
+Otherwise does searches to the indexes of referenced tables and
+sets shared locks which lock either the success or the failure of
+a constraint. */
+static
+ulint
+row_ins_check_foreign_constraints(
+/*==============================*/
+ /* out: DB_SUCCESS, DB_LOCK_WAIT, or error
+ code */
+ dict_table_t* table, /* in: table */
+ dict_index_t* index, /* in: index */
+ dtuple_t* entry, /* in: index entry for index */
+ que_thr_t* thr) /* in: query thread */
+{
+ dict_foreign_t* foreign;
+ ulint err;
+
+ foreign = UT_LIST_GET_FIRST(table->foreign_list);
+
+ while (foreign) {
+ if (foreign->foreign_index == index) {
+
+ if (foreign->referenced_table == NULL) {
+ dict_table_get(foreign->referenced_table_name,
+ thr_get_trx(thr));
+ }
+
+ rw_lock_s_lock(&dict_foreign_key_check_lock);
+
+ err = row_ins_check_foreign_constraint(TRUE, foreign,
+ table, index, entry, thr);
+
+ rw_lock_s_unlock(&dict_foreign_key_check_lock);
+
+ if (err != DB_SUCCESS) {
+ return(err);
+ }
+ }
+
+ foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
+ }
+
+ return(DB_SUCCESS);
+}
+
+/*******************************************************************
Scans a unique non-clustered index at a given index entry to determine
whether a uniqueness violation has occurred for the key value of the entry.
Set shared locks on possible duplicate records. */
@@ -365,7 +579,6 @@ row_ins_scan_sec_index_for_duplicate(
ulint n_fields_cmp;
rec_t* rec;
btr_pcur_t pcur;
- trx_t* trx = thr_get_trx(thr);
ulint err = DB_SUCCESS;
ibool moved;
mtr_t mtr;
@@ -414,7 +627,7 @@ row_ins_scan_sec_index_for_duplicate(
err = DB_DUPLICATE_KEY;
- trx->error_info = index;
+ thr_get_trx(thr)->error_info = index;
break;
}
@@ -699,7 +912,7 @@ row_ins_index_entry_low(
ext_vec, n_ext_vec,
thr, &mtr);
} else {
- err = row_ins_sec_index_entry_by_modify(&cursor,
+ err = row_ins_sec_index_entry_by_modify(&cursor, entry,
thr, &mtr);
}
@@ -765,6 +978,15 @@ row_ins_index_entry(
{
ulint err;
+ if (UT_LIST_GET_FIRST(index->table->foreign_list)) {
+ err = row_ins_check_foreign_constraints(index->table, index,
+ entry, thr);
+ if (err != DB_SUCCESS) {
+
+ return(err);
+ }
+ }
+
/* Try first optimistic descent to the B-tree */
err = row_ins_index_entry_low(BTR_MODIFY_LEAF, index, entry,
@@ -812,7 +1034,7 @@ row_ins_index_entry_set_vals(
/***************************************************************
Inserts a single index entry to the table. */
-UNIV_INLINE
+static
ulint
row_ins_index_entry_step(
/*=====================*/