summaryrefslogtreecommitdiff
path: root/innobase/row
diff options
context:
space:
mode:
authorunknown <heikki@donna.mysql.fi>2001-10-10 22:47:08 +0300
committerunknown <heikki@donna.mysql.fi>2001-10-10 22:47:08 +0300
commit1904897be71cba7e6f2cf1192ba0cc2e8d907e00 (patch)
treefc361924d14a3d1727a8b88f61352ed039054720 /innobase/row
parent151ffe886b4b21499471658fdf01ea8347287092 (diff)
downloadmariadb-git-1904897be71cba7e6f2cf1192ba0cc2e8d907e00.tar.gz
ut0mem.c Merge changes in InnoDB-3.23.43b
ut0ut.c Merge changes in InnoDB-3.23.43b trx0purge.c Merge changes in InnoDB-3.23.43b trx0rec.c Merge changes in InnoDB-3.23.43b trx0trx.c Merge changes in InnoDB-3.23.43b trx0undo.c Merge changes in InnoDB-3.23.43b thr0loc.c Merge changes in InnoDB-3.23.43b sync0arr.c Merge changes in InnoDB-3.23.43b sync0rw.c Merge changes in InnoDB-3.23.43b sync0sync.c Merge changes in InnoDB-3.23.43b srv0srv.c Merge changes in InnoDB-3.23.43b srv0start.c Merge changes in InnoDB-3.23.43b row0ins.c Merge changes in InnoDB-3.23.43b row0mysql.c Merge changes in InnoDB-3.23.43b row0purge.c Merge changes in InnoDB-3.23.43b row0sel.c Merge changes in InnoDB-3.23.43b row0umod.c Merge changes in InnoDB-3.23.43b row0upd.c Merge changes in InnoDB-3.23.43b row0vers.c Merge changes in InnoDB-3.23.43b rem0cmp.c Merge changes in InnoDB-3.23.43b que0que.c Merge changes in InnoDB-3.23.43b pars0opt.c Merge changes in InnoDB-3.23.43b pars0pars.c Merge changes in InnoDB-3.23.43b lexyy.c Merge changes in InnoDB-3.23.43b pars0grm.c Merge changes in InnoDB-3.23.43b page0page.c Merge changes in InnoDB-3.23.43b os0file.c Merge changes in InnoDB-3.23.43b mtr0log.c Merge changes in InnoDB-3.23.43b mem0pool.c Merge changes in InnoDB-3.23.43b log0log.c Merge changes in InnoDB-3.23.43b log0recv.c Merge changes in InnoDB-3.23.43b lock0lock.c Merge changes in InnoDB-3.23.43b ibuf0ibuf.c Merge changes in InnoDB-3.23.43b fil0fil.c Merge changes in InnoDB-3.23.43b dict0crea.c Merge changes in InnoDB-3.23.43b dict0dict.c Merge changes in InnoDB-3.23.43b dict0load.c Merge changes in InnoDB-3.23.43b dict0mem.c Merge changes in InnoDB-3.23.43b data0data.c Merge changes in InnoDB-3.23.43b data0type.c Merge changes in InnoDB-3.23.43b buf0buf.c Merge changes in InnoDB-3.23.43b buf0lru.c Merge changes in InnoDB-3.23.43b btr0btr.c Merge changes in InnoDB-3.23.43b btr0cur.c Merge changes in InnoDB-3.23.43b btr0pcur.c Merge changes in InnoDB-3.23.43b btr0sea.c Merge changes in InnoDB-3.23.43b data0type.ic Merge changes in InnoDB-3.23.43b dict0dict.ic Merge changes in InnoDB-3.23.43b mtr0mtr.ic Merge changes in InnoDB-3.23.43b row0upd.ic Merge changes in InnoDB-3.23.43b sync0ipm.ic Merge changes in InnoDB-3.23.43b sync0rw.ic Merge changes in InnoDB-3.23.43b sync0sync.ic Merge changes in InnoDB-3.23.43b trx0rseg.ic Merge changes in InnoDB-3.23.43b btr0pcur.ic Merge changes in InnoDB-3.23.43b buf0buf.ic Merge changes in InnoDB-3.23.43b data0data.ic Merge changes in InnoDB-3.23.43b row0upd.h Merge changes in InnoDB-3.23.43b srv0srv.h Merge changes in InnoDB-3.23.43b sync0arr.h Merge changes in InnoDB-3.23.43b sync0rw.h Merge changes in InnoDB-3.23.43b sync0sync.h Merge changes in InnoDB-3.23.43b trx0trx.h Merge changes in InnoDB-3.23.43b ut0mem.h Merge changes in InnoDB-3.23.43b data0data.h Merge changes in InnoDB-3.23.43b data0type.h Merge changes in InnoDB-3.23.43b db0err.h Merge changes in InnoDB-3.23.43b dict0crea.h Merge changes in InnoDB-3.23.43b dict0dict.h Merge changes in InnoDB-3.23.43b dict0load.h Merge changes in InnoDB-3.23.43b dict0mem.h Merge changes in InnoDB-3.23.43b dict0types.h Merge changes in InnoDB-3.23.43b fil0fil.h Merge changes in InnoDB-3.23.43b ibuf0ibuf.h Merge changes in InnoDB-3.23.43b lock0lock.h Merge changes in InnoDB-3.23.43b log0log.h Merge changes in InnoDB-3.23.43b mtr0mtr.h Merge changes in InnoDB-3.23.43b rem0cmp.h Merge changes in InnoDB-3.23.43b row0ins.h Merge changes in InnoDB-3.23.43b row0mysql.h Merge changes in InnoDB-3.23.43b btr0cur.h Merge changes in InnoDB-3.23.43b btr0pcur.h Merge changes in InnoDB-3.23.43b btr0sea.h Merge changes in InnoDB-3.23.43b buf0buf.h Merge changes in InnoDB-3.23.43b sql_table.cc Merge changes in InnoDB-3.23.43b sql_db.cc Merge changes in InnoDB-3.23.43b ha_innobase.cc Merge changes in InnoDB-3.23.43b handler.cc Merge changes in InnoDB-3.23.43b ha_innobase.h Merge changes in InnoDB-3.23.43b handler.h Merge changes in InnoDB-3.23.43b sql/ha_innobase.h: Merge changes in InnoDB-3.23.43b sql/handler.h: Merge changes in InnoDB-3.23.43b sql/ha_innobase.cc: Merge changes in InnoDB-3.23.43b sql/handler.cc: Merge changes in InnoDB-3.23.43b sql/sql_db.cc: Merge changes in InnoDB-3.23.43b sql/sql_table.cc: Merge changes in InnoDB-3.23.43b innobase/include/btr0cur.h: Merge changes in InnoDB-3.23.43b innobase/include/btr0pcur.h: Merge changes in InnoDB-3.23.43b innobase/include/btr0sea.h: Merge changes in InnoDB-3.23.43b innobase/include/buf0buf.h: Merge changes in InnoDB-3.23.43b innobase/include/data0data.h: Merge changes in InnoDB-3.23.43b innobase/include/data0type.h: Merge changes in InnoDB-3.23.43b innobase/include/db0err.h: Merge changes in InnoDB-3.23.43b innobase/include/dict0crea.h: Merge changes in InnoDB-3.23.43b innobase/include/dict0dict.h: Merge changes in InnoDB-3.23.43b innobase/include/dict0load.h: Merge changes in InnoDB-3.23.43b innobase/include/dict0mem.h: Merge changes in InnoDB-3.23.43b innobase/include/dict0types.h: Merge changes in InnoDB-3.23.43b innobase/include/fil0fil.h: Merge changes in InnoDB-3.23.43b innobase/include/ibuf0ibuf.h: Merge changes in InnoDB-3.23.43b innobase/include/lock0lock.h: Merge changes in InnoDB-3.23.43b innobase/include/log0log.h: Merge changes in InnoDB-3.23.43b innobase/include/mtr0mtr.h: Merge changes in InnoDB-3.23.43b innobase/include/rem0cmp.h: Merge changes in InnoDB-3.23.43b innobase/include/row0ins.h: Merge changes in InnoDB-3.23.43b innobase/include/row0mysql.h: Merge changes in InnoDB-3.23.43b innobase/include/row0upd.h: Merge changes in InnoDB-3.23.43b innobase/include/srv0srv.h: Merge changes in InnoDB-3.23.43b innobase/include/sync0arr.h: Merge changes in InnoDB-3.23.43b innobase/include/sync0rw.h: Merge changes in InnoDB-3.23.43b innobase/include/sync0sync.h: Merge changes in InnoDB-3.23.43b innobase/include/trx0trx.h: Merge changes in InnoDB-3.23.43b innobase/include/ut0mem.h: Merge changes in InnoDB-3.23.43b innobase/include/btr0pcur.ic: Merge changes in InnoDB-3.23.43b innobase/include/buf0buf.ic: Merge changes in InnoDB-3.23.43b innobase/include/data0data.ic: Merge changes in InnoDB-3.23.43b innobase/include/data0type.ic: Merge changes in InnoDB-3.23.43b innobase/include/dict0dict.ic: Merge changes in InnoDB-3.23.43b innobase/include/mtr0mtr.ic: Merge changes in InnoDB-3.23.43b innobase/include/row0upd.ic: Merge changes in InnoDB-3.23.43b innobase/include/sync0ipm.ic: Merge changes in InnoDB-3.23.43b innobase/include/sync0rw.ic: Merge changes in InnoDB-3.23.43b innobase/include/sync0sync.ic: Merge changes in InnoDB-3.23.43b innobase/include/trx0rseg.ic: Merge changes in InnoDB-3.23.43b innobase/btr/btr0btr.c: Merge changes in InnoDB-3.23.43b innobase/btr/btr0cur.c: Merge changes in InnoDB-3.23.43b innobase/btr/btr0pcur.c: Merge changes in InnoDB-3.23.43b innobase/btr/btr0sea.c: Merge changes in InnoDB-3.23.43b innobase/buf/buf0buf.c: Merge changes in InnoDB-3.23.43b innobase/buf/buf0lru.c: Merge changes in InnoDB-3.23.43b innobase/data/data0data.c: Merge changes in InnoDB-3.23.43b innobase/data/data0type.c: Merge changes in InnoDB-3.23.43b innobase/dict/dict0crea.c: Merge changes in InnoDB-3.23.43b innobase/dict/dict0dict.c: Merge changes in InnoDB-3.23.43b innobase/dict/dict0load.c: Merge changes in InnoDB-3.23.43b innobase/dict/dict0mem.c: Merge changes in InnoDB-3.23.43b innobase/fil/fil0fil.c: Merge changes in InnoDB-3.23.43b innobase/ibuf/ibuf0ibuf.c: Merge changes in InnoDB-3.23.43b innobase/lock/lock0lock.c: Merge changes in InnoDB-3.23.43b innobase/log/log0log.c: Merge changes in InnoDB-3.23.43b innobase/log/log0recv.c: Merge changes in InnoDB-3.23.43b innobase/mem/mem0pool.c: Merge changes in InnoDB-3.23.43b innobase/mtr/mtr0log.c: Merge changes in InnoDB-3.23.43b innobase/os/os0file.c: Merge changes in InnoDB-3.23.43b innobase/page/page0page.c: Merge changes in InnoDB-3.23.43b innobase/pars/lexyy.c: Merge changes in InnoDB-3.23.43b innobase/pars/pars0grm.c: Merge changes in InnoDB-3.23.43b innobase/pars/pars0opt.c: Merge changes in InnoDB-3.23.43b innobase/pars/pars0pars.c: Merge changes in InnoDB-3.23.43b innobase/que/que0que.c: Merge changes in InnoDB-3.23.43b innobase/rem/rem0cmp.c: Merge changes in InnoDB-3.23.43b innobase/row/row0ins.c: Merge changes in InnoDB-3.23.43b innobase/row/row0mysql.c: Merge changes in InnoDB-3.23.43b innobase/row/row0purge.c: Merge changes in InnoDB-3.23.43b innobase/row/row0sel.c: Merge changes in InnoDB-3.23.43b innobase/row/row0umod.c: Merge changes in InnoDB-3.23.43b innobase/row/row0upd.c: Merge changes in InnoDB-3.23.43b innobase/row/row0vers.c: Merge changes in InnoDB-3.23.43b innobase/srv/srv0srv.c: Merge changes in InnoDB-3.23.43b innobase/srv/srv0start.c: Merge changes in InnoDB-3.23.43b innobase/sync/sync0arr.c: Merge changes in InnoDB-3.23.43b innobase/sync/sync0rw.c: Merge changes in InnoDB-3.23.43b innobase/sync/sync0sync.c: Merge changes in InnoDB-3.23.43b innobase/thr/thr0loc.c: Merge changes in InnoDB-3.23.43b innobase/trx/trx0purge.c: Merge changes in InnoDB-3.23.43b innobase/trx/trx0rec.c: Merge changes in InnoDB-3.23.43b innobase/trx/trx0trx.c: Merge changes in InnoDB-3.23.43b innobase/trx/trx0undo.c: Merge changes in InnoDB-3.23.43b innobase/ut/ut0mem.c: Merge changes in InnoDB-3.23.43b innobase/ut/ut0ut.c: Merge changes in InnoDB-3.23.43b BitKeeper/etc/logging_ok: Logging to logging@openlogging.org accepted
Diffstat (limited to 'innobase/row')
-rw-r--r--innobase/row/row0ins.c244
-rw-r--r--innobase/row/row0mysql.c419
-rw-r--r--innobase/row/row0purge.c4
-rw-r--r--innobase/row/row0sel.c94
-rw-r--r--innobase/row/row0umod.c4
-rw-r--r--innobase/row/row0upd.c291
-rw-r--r--innobase/row/row0vers.c16
7 files changed, 960 insertions, 112 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(
/*=====================*/
diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c
index 373ee4ac4bd..13c0332dcef 100644
--- a/innobase/row/row0mysql.c
+++ b/innobase/row/row0mysql.c
@@ -21,6 +21,7 @@ Created 9/17/2000 Heikki Tuuri
#include "pars0pars.h"
#include "dict0dict.h"
#include "dict0crea.h"
+#include "dict0load.h"
#include "trx0roll.h"
#include "trx0purge.h"
#include "lock0lock.h"
@@ -151,7 +152,7 @@ row_mysql_handle_errors(
during the function entry */
trx_t* trx, /* in: transaction */
que_thr_t* thr, /* in: query thread */
- trx_savept_t* savept) /* in: savepoint */
+ trx_savept_t* savept) /* in: savepoint or NULL */
{
ibool timeout_expired;
ulint err;
@@ -172,12 +173,16 @@ handle_new_error:
}
} else if (err == DB_TOO_BIG_RECORD) {
/* MySQL will roll back the latest SQL statement */
+ } else if (err == DB_ROW_IS_REFERENCED
+ || err == DB_NO_REFERENCED_ROW
+ || err == DB_CANNOT_ADD_CONSTRAINT) {
+ /* MySQL will roll back the latest SQL statement */
} else if (err == DB_LOCK_WAIT) {
timeout_expired = srv_suspend_mysql_thread(thr);
if (timeout_expired) {
- trx->error_state = DB_DEADLOCK;
+ trx->error_state = DB_LOCK_WAIT_TIMEOUT;
que_thr_stop_for_mysql(thr);
@@ -188,9 +193,12 @@ handle_new_error:
return(TRUE);
- } else if (err == DB_DEADLOCK) {
- /* MySQL will roll back the latest SQL statement */
+ } else if (err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT) {
+ /* Roll back the whole transaction; this resolution was added
+ to version 3.23.43 */
+ trx_general_rollback_for_mysql(trx, FALSE, NULL);
+
} else if (err == DB_OUT_OF_FILE_SPACE) {
/* MySQL will roll back the latest SQL statement */
@@ -203,6 +211,7 @@ handle_new_error:
exit(1);
} else {
+ fprintf(stderr, "InnoDB: unknown error code %lu\n", err);
ut_a(0);
}
@@ -440,7 +449,94 @@ row_update_statistics_if_needed(
dict_update_statistics(prebuilt->table);
}
}
+
+/*************************************************************************
+Unlocks an AUTO_INC type lock possibly reserved by trx. */
+
+void
+row_unlock_table_autoinc_for_mysql(
+/*===============================*/
+ trx_t* trx) /* in: transaction */
+{
+ if (!trx->auto_inc_lock) {
+
+ return;
+ }
+
+ lock_table_unlock_auto_inc(trx);
+}
+
+/*************************************************************************
+Sets an AUTO_INC type lock on the table mentioned in prebuilt. The
+AUTO_INC lock gives exclusive access to the auto-inc counter of the
+table. The lock is reserved only for the duration of an SQL statement.
+It is not compatible with another AUTO_INC or exclusive lock on the
+table. */
+
+int
+row_lock_table_autoinc_for_mysql(
+/*=============================*/
+ /* out: error code or DB_SUCCESS */
+ row_prebuilt_t* prebuilt) /* in: prebuilt struct in the MySQL
+ table handle */
+{
+ trx_t* trx = prebuilt->trx;
+ ins_node_t* node = prebuilt->ins_node;
+ que_thr_t* thr;
+ ulint err;
+ ibool was_lock_wait;
+
+ ut_ad(trx);
+ ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
+
+ trx->op_info = "setting auto-inc lock";
+
+ if (node == NULL) {
+ row_get_prebuilt_insert_row(prebuilt);
+ node = prebuilt->ins_node;
+ }
+
+ /* We use the insert query graph as the dummy graph needed
+ in the lock module call */
+
+ thr = que_fork_get_first_thr(prebuilt->ins_graph);
+
+ que_thr_move_to_run_state_for_mysql(thr, trx);
+
+run_again:
+ thr->run_node = node;
+ thr->prev_node = node;
+
+ /* It may be that the current session has not yet started
+ its transaction, or it has been committed: */
+
+ trx_start_if_not_started(trx);
+
+ err = lock_table(0, prebuilt->table, LOCK_AUTO_INC, thr);
+
+ trx->error_state = err;
+
+ if (err != DB_SUCCESS) {
+ que_thr_stop_for_mysql(thr);
+
+ was_lock_wait = row_mysql_handle_errors(&err, trx, thr, NULL);
+
+ if (was_lock_wait) {
+ goto run_again;
+ }
+
+ trx->op_info = "";
+
+ return(err);
+ }
+
+ que_thr_stop_for_mysql_no_error(thr, trx);
+
+ trx->op_info = "";
+ return((int) err);
+}
+
/*************************************************************************
Does an insert for MySQL. */
@@ -462,6 +558,17 @@ row_insert_for_mysql(
ut_ad(trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
+ if (srv_created_new_raw || srv_force_recovery) {
+ fprintf(stderr,
+ "InnoDB: A new raw disk partition was initialized or\n"
+ "InnoDB: innodb_force_recovery is on: we do not allow\n"
+ "InnoDB: database modifications by the user. Shut down\n"
+ "InnoDB: mysqld and edit my.cnf so that newraw is replaced\n"
+ "InnoDB: with raw, and innodb_force_... is removed.\n");
+
+ return(DB_ERROR);
+ }
+
trx->op_info = "inserting";
if (node == NULL) {
@@ -634,6 +741,17 @@ row_update_for_mysql(
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
UT_NOT_USED(mysql_rec);
+ if (srv_created_new_raw || srv_force_recovery) {
+ fprintf(stderr,
+ "InnoDB: A new raw disk partition was initialized or\n"
+ "InnoDB: innodb_force_recovery is on: we do not allow\n"
+ "InnoDB: database modifications by the user. Shut down\n"
+ "InnoDB: mysqld and edit my.cnf so that newraw is replaced\n"
+ "InnoDB: with raw, and innodb_force_... is removed.\n");
+
+ return(DB_ERROR);
+ }
+
trx->op_info = "updating or deleting";
node = prebuilt->upd_node;
@@ -816,8 +934,69 @@ row_create_table_for_mysql(
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
+ if (srv_created_new_raw || srv_force_recovery) {
+ fprintf(stderr,
+ "InnoDB: A new raw disk partition was initialized or\n"
+ "InnoDB: innodb_force_recovery is on: we do not allow\n"
+ "InnoDB: database modifications by the user. Shut down\n"
+ "InnoDB: mysqld and edit my.cnf so that newraw is replaced\n"
+ "InnoDB: with raw, and innodb_force_... is removed.\n");
+
+ return(DB_ERROR);
+ }
+
trx->op_info = "creating table";
+ namelen = ut_strlen(table->name);
+
+ keywordlen = ut_strlen("innodb_monitor");
+
+ if (namelen >= keywordlen
+ && 0 == ut_memcmp(table->name + namelen - keywordlen,
+ "innodb_monitor", keywordlen)) {
+
+ /* Table name ends to characters innodb_monitor:
+ start monitor prints */
+
+ srv_print_innodb_monitor = TRUE;
+
+ /* The lock timeout monitor thread also takes care
+ of InnoDB monitor prints */
+
+ os_event_set(srv_lock_timeout_thread_event);
+ }
+
+ keywordlen = ut_strlen("innodb_lock_monitor");
+
+ if (namelen >= keywordlen
+ && 0 == ut_memcmp(table->name + namelen - keywordlen,
+ "innodb_lock_monitor", keywordlen)) {
+
+ srv_print_innodb_monitor = TRUE;
+ srv_print_innodb_lock_monitor = TRUE;
+ os_event_set(srv_lock_timeout_thread_event);
+ }
+
+ keywordlen = ut_strlen("innodb_tablespace_monitor");
+
+ if (namelen >= keywordlen
+ && 0 == ut_memcmp(table->name + namelen - keywordlen,
+ "innodb_tablespace_monitor", keywordlen)) {
+
+ srv_print_innodb_tablespace_monitor = TRUE;
+ os_event_set(srv_lock_timeout_thread_event);
+ }
+
+ keywordlen = ut_strlen("innodb_table_monitor");
+
+ if (namelen >= keywordlen
+ && 0 == ut_memcmp(table->name + namelen - keywordlen,
+ "innodb_table_monitor", keywordlen)) {
+
+ srv_print_innodb_table_monitor = TRUE;
+ os_event_set(srv_lock_timeout_thread_event);
+ }
+
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
@@ -845,9 +1024,12 @@ row_create_table_for_mysql(
trx_general_rollback_for_mysql(trx, FALSE, NULL);
if (err == DB_OUT_OF_FILE_SPACE) {
+ fprintf(stderr,
+ "InnoDB: Warning: cannot create table %s because tablespace full\n",
+ table->name);
row_drop_table_for_mysql(table->name, trx, TRUE);
} else {
- assert(err == DB_DUPLICATE_KEY);
+ ut_a(err == DB_DUPLICATE_KEY);
fprintf(stderr,
"InnoDB: Error: table %s already exists in InnoDB internal\n"
"InnoDB: data dictionary. Have you deleted the .frm file\n"
@@ -864,39 +1046,6 @@ row_create_table_for_mysql(
}
trx->error_state = DB_SUCCESS;
- } else {
- namelen = ut_strlen(table->name);
-
- keywordlen = ut_strlen("innodb_monitor");
-
- if (namelen >= keywordlen
- && 0 == ut_memcmp(table->name + namelen - keywordlen,
- "innodb_monitor", keywordlen)) {
-
- /* Table name ends to characters innodb_monitor:
- start monitor prints */
-
- srv_print_innodb_monitor = TRUE;
- }
-
- keywordlen = ut_strlen("innodb_lock_monitor");
-
- if (namelen >= keywordlen
- && 0 == ut_memcmp(table->name + namelen - keywordlen,
- "innodb_lock_monitor", keywordlen)) {
-
- srv_print_innodb_monitor = TRUE;
- srv_print_innodb_lock_monitor = TRUE;
- }
-
- keywordlen = ut_strlen("innodb_tablespace_monitor");
-
- if (namelen >= keywordlen
- && 0 == ut_memcmp(table->name + namelen - keywordlen,
- "innodb_tablespace_monitor", keywordlen)) {
-
- srv_print_innodb_tablespace_monitor = TRUE;
- }
}
mutex_exit(&(dict_sys->mutex));
@@ -970,6 +1119,65 @@ row_create_index_for_mysql(
}
/*************************************************************************
+Scans a table create SQL string and adds to the data dictionary
+the foreign key constraints declared in the string. This function
+should be called after the indexes for a table have been created.
+Each foreign key constraint must be accompanied with indexes in
+bot participating tables. The indexes are allowed to contain more
+fields than mentioned in the constraint. Check also that foreign key
+constraints which reference this table are ok. */
+
+int
+row_table_add_foreign_constraints(
+/*==============================*/
+ /* out: error code or DB_SUCCESS */
+ trx_t* trx, /* in: transaction */
+ char* sql_string, /* in: table create statement where
+ foreign keys are declared like:
+ FOREIGN KEY (a, b) REFERENCES table2(c, d),
+ table2 can be written also with the database
+ name before it: test.table2 */
+ char* name) /* in: table full name in the normalized form
+ database_name/table_name */
+{
+ ulint err;
+
+ ut_a(sql_string);
+
+ trx->op_info = "adding foreign keys";
+
+ /* Serialize data dictionary operations with dictionary mutex:
+ no deadlocks can occur then in these operations */
+
+ mutex_enter(&(dict_sys->mutex));
+
+ trx->dict_operation = TRUE;
+
+ err = dict_create_foreign_constraints(trx, sql_string, name);
+
+ if (err == DB_SUCCESS) {
+ /* Check that also referencing constraints are ok */
+ err = dict_load_foreigns(name);
+ }
+
+ if (err != DB_SUCCESS) {
+ /* We have special error handling here */
+
+ trx->error_state = DB_SUCCESS;
+
+ trx_general_rollback_for_mysql(trx, FALSE, NULL);
+
+ row_drop_table_for_mysql(name, trx, TRUE);
+
+ trx->error_state = DB_SUCCESS;
+ }
+
+ mutex_exit(&(dict_sys->mutex));
+
+ return((int) err);
+}
+
+/*************************************************************************
Drops a table for MySQL. If the name of the dropped table ends to
characters INNODB_MONITOR, then this also stops printing of monitor
output by the master thread. */
@@ -997,6 +1205,17 @@ row_drop_table_for_mysql(
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
ut_a(name != NULL);
+ if (srv_created_new_raw || srv_force_recovery) {
+ fprintf(stderr,
+ "InnoDB: A new raw disk partition was initialized or\n"
+ "InnoDB: innodb_force_recovery is on: we do not allow\n"
+ "InnoDB: database modifications by the user. Shut down\n"
+ "InnoDB: mysqld and edit my.cnf so that newraw is replaced\n"
+ "InnoDB: with raw, and innodb_force_... is removed.\n");
+
+ return(DB_ERROR);
+ }
+
trx->op_info = "dropping table";
namelen = ut_strlen(name);
@@ -1032,6 +1251,15 @@ row_drop_table_for_mysql(
srv_print_innodb_tablespace_monitor = FALSE;
}
+ keywordlen = ut_strlen("innodb_table_monitor");
+
+ if (namelen >= keywordlen
+ && 0 == ut_memcmp(name + namelen - keywordlen,
+ "innodb_table_monitor", keywordlen)) {
+
+ srv_print_innodb_table_monitor = FALSE;
+ }
+
/* 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
@@ -1039,21 +1267,49 @@ row_drop_table_for_mysql(
str1 =
"PROCEDURE DROP_TABLE_PROC () IS\n"
+ "table_name CHAR;\n"
+ "sys_foreign_id CHAR;\n"
"table_id CHAR;\n"
"index_id CHAR;\n"
+ "foreign_id CHAR;\n"
"found INT;\n"
"BEGIN\n"
- "SELECT ID INTO table_id\n"
- "FROM SYS_TABLES\n"
- "WHERE NAME ='";
-
+ "table_name := '";
+
str2 =
"';\n"
+ "SELECT ID INTO table_id\n"
+ "FROM SYS_TABLES\n"
+ "WHERE NAME = table_name;\n"
"IF (SQL % NOTFOUND) THEN\n"
" COMMIT WORK;\n"
" RETURN;\n"
"END IF;\n"
"found := 1;\n"
+ "SELECT ID INTO sys_foreign_id\n"
+ "FROM SYS_TABLES\n"
+ "WHERE NAME = 'SYS_FOREIGN';\n"
+ "IF (SQL % NOTFOUND) THEN\n"
+ " found := 0;\n"
+ "END IF;\n"
+ "IF (table_name = 'SYS_FOREIGN') THEN\n"
+ " found := 0;\n"
+ "END IF;\n"
+ "IF (table_name = 'SYS_FOREIGN_COLS') THEN\n"
+ " found := 0;\n"
+ "END IF;\n"
+ "WHILE found = 1 LOOP\n"
+ " SELECT ID INTO foreign_id\n"
+ " FROM SYS_FOREIGN\n"
+ " WHERE FOR_NAME = table_name;\n"
+ " IF (SQL % NOTFOUND) THEN\n"
+ " found := 0;\n"
+ " ELSE"
+ " DELETE FROM SYS_FOREIGN_COLS WHERE ID = foreign_id;\n"
+ " DELETE FROM SYS_FOREIGN WHERE ID = foreign_id;\n"
+ " END IF;\n"
+ "END LOOP;\n"
+ "found := 1;\n"
"WHILE found = 1 LOOP\n"
" SELECT ID INTO index_id\n"
" FROM SYS_INDEXES\n"
@@ -1095,6 +1351,9 @@ 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));
@@ -1103,6 +1362,12 @@ row_drop_table_for_mysql(
if (!table) {
err = DB_TABLE_NOT_FOUND;
+ fprintf(stderr,
+ "InnoDB: Error: table %s does not exist in the InnoDB internal\n"
+ "InnoDB: data dictionary though MySQL is trying to drop it.\n"
+ "InnoDB: Have you copied the .frm file of the table to the\n"
+ "InnoDB: MySQL database directory from another database?\n",
+ name);
goto funct_exit;
}
@@ -1138,6 +1403,8 @@ 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));
}
@@ -1150,6 +1417,49 @@ funct_exit:
}
/*************************************************************************
+Drops a database for MySQL. */
+
+int
+row_drop_database_for_mysql(
+/*========================*/
+ /* out: error code or DB_SUCCESS */
+ char* name, /* in: database name which ends to '/' */
+ trx_t* trx) /* in: transaction handle */
+{
+ char* table_name;
+ int err = DB_SUCCESS;
+
+ ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
+ ut_a(name != NULL);
+ ut_a(name[strlen(name) - 1] == '/');
+
+ trx->op_info = "dropping database";
+
+ mutex_enter(&(dict_sys->mutex));
+
+ while (table_name = dict_get_first_table_name_in_db(name)) {
+ ut_a(memcmp(table_name, name, strlen(name)) == 0);
+
+ err = row_drop_table_for_mysql(table_name, trx, TRUE);
+
+ mem_free(table_name);
+
+ if (err != DB_SUCCESS) {
+ fprintf(stderr,
+ "InnoDB: DROP DATABASE %s failed with error %lu for table %s\n",
+ name, (ulint)err, table_name);
+ break;
+ }
+ }
+
+ mutex_exit(&(dict_sys->mutex));
+
+ trx->op_info = "";
+
+ return(err);
+}
+
+/*************************************************************************
Renames a table for MySQL. */
int
@@ -1174,18 +1484,37 @@ row_rename_table_for_mysql(
ut_a(old_name != NULL);
ut_a(new_name != NULL);
+ if (srv_created_new_raw || srv_force_recovery) {
+ fprintf(stderr,
+ "InnoDB: A new raw disk partition was initialized or\n"
+ "InnoDB: innodb_force_recovery is on: we do not allow\n"
+ "InnoDB: database modifications by the user. Shut down\n"
+ "InnoDB: mysqld and edit my.cnf so that newraw is replaced\n"
+ "InnoDB: with raw, and innodb_force_... is removed.\n");
+
+ return(DB_ERROR);
+ }
+
trx->op_info = "renaming table";
str1 =
"PROCEDURE RENAME_TABLE_PROC () IS\n"
+ "new_table_name CHAR;\n"
+ "old_table_name CHAR;\n"
"BEGIN\n"
- "UPDATE SYS_TABLES SET NAME ='";
+ "new_table_name :='";
str2 =
- "' WHERE NAME = '";
+ "';\nold_table_name := '";
str3 =
"';\n"
+ "UPDATE SYS_TABLES SET NAME = new_table_name\n"
+ "WHERE NAME = old_table_name;\n"
+ "UPDATE SYS_FOREIGN SET FOR_NAME = new_table_name\n"
+ "WHERE FOR_NAME = old_table_name;\n"
+ "UPDATE SYS_FOREIGN SET REF_NAME = new_table_name\n"
+ "WHERE REF_NAME = old_table_name;\n"
"COMMIT WORK;\n"
"END;\n";
@@ -1356,7 +1685,7 @@ row_check_table_for_mysql(
dict_table_t* table = prebuilt->table;
dict_index_t* index;
ulint n_rows;
- ulint n_rows_in_table;
+ ulint n_rows_in_table = ULINT_UNDEFINED;
ulint ret = DB_SUCCESS;
prebuilt->trx->op_info = "checking table";
diff --git a/innobase/row/row0purge.c b/innobase/row/row0purge.c
index 43bc166347a..0dffa273938 100644
--- a/innobase/row/row0purge.c
+++ b/innobase/row/row0purge.c
@@ -220,7 +220,7 @@ row_purge_remove_sec_if_poss_low(
if (!found) {
/* Not found */
- /* FIXME: printf("PURGE:........sec entry not found\n"); */
+ /* printf("PURGE:........sec entry not found\n"); */
/* dtuple_print(entry); */
btr_pcur_close(&pcur);
@@ -382,7 +382,7 @@ row_purge_upd_exist_or_extern(
while (node->index != NULL) {
index = node->index;
- if (row_upd_changes_ord_field(NULL, node->index,
+ if (row_upd_changes_ord_field_binary(NULL, node->index,
node->update)) {
/* Build the older version of the index entry */
entry = row_build_index_entry(node->row, index, heap);
diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c
index d041e34a558..e42486f1e17 100644
--- a/innobase/row/row0sel.c
+++ b/innobase/row/row0sel.c
@@ -50,15 +50,21 @@ to que_run_threads: this is to allow canceling runaway queries */
/************************************************************************
Returns TRUE if the user-defined column values in a secondary index record
-are the same as the corresponding columns in the clustered index record. */
+are the same as the corresponding columns in the clustered index record.
+NOTE: the comparison is NOT done as a binary comparison, but character
+fields are compared with collation! */
static
ibool
row_sel_sec_rec_is_for_clust_rec(
/*=============================*/
- rec_t* sec_rec,
- dict_index_t* sec_index,
- rec_t* clust_rec,
- dict_index_t* clust_index)
+ /* out: TRUE if the secondary
+ record is equal to the corresponding
+ fields in the clustered record,
+ when compared with collation */
+ rec_t* sec_rec, /* in: secondary index record */
+ dict_index_t* sec_index, /* in: secondary index */
+ rec_t* clust_rec, /* in: clustered index record */
+ dict_index_t* clust_index) /* in: clustered index */
{
dict_col_t* col;
byte* sec_field;
@@ -84,9 +90,9 @@ row_sel_sec_rec_is_for_clust_rec(
return(FALSE);
}
- if (sec_len != UNIV_SQL_NULL
- && ut_memcmp(sec_field, clust_field, sec_len) != 0) {
-
+ if (0 != cmp_data_data(dict_col_get_type(col),
+ clust_field, clust_len,
+ sec_field, sec_len)) {
return(FALSE);
}
}
@@ -763,7 +769,7 @@ row_sel_open_pcur(
/*************************************************************************
Restores a stored pcur position to a table index. */
-UNIV_INLINE
+static
ibool
row_sel_restore_pcur_pos(
/*=====================*/
@@ -813,7 +819,8 @@ row_sel_restore_pcur_pos(
return(TRUE);
}
- ut_ad(relative_position == BTR_PCUR_AFTER);
+ ut_ad(relative_position == BTR_PCUR_AFTER
+ || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE);
return(FALSE);
}
@@ -835,7 +842,8 @@ row_sel_restore_pcur_pos(
plan->stored_cursor_rec_processed is TRUE, we must move to the previous
record, else there is no need to move the cursor. */
- if (relative_position == BTR_PCUR_BEFORE) {
+ if (relative_position == BTR_PCUR_BEFORE
+ || relative_position == BTR_PCUR_BEFORE_FIRST_IN_TREE) {
return(FALSE);
}
@@ -850,7 +858,8 @@ row_sel_restore_pcur_pos(
return(FALSE);
}
- ut_ad(relative_position == BTR_PCUR_AFTER);
+ ut_ad(relative_position == BTR_PCUR_AFTER
+ || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE);
return(TRUE);
}
@@ -1762,7 +1771,7 @@ fetch_step(
if (sel_node->state == SEL_NODE_CLOSED) {
/* SQL error detected */
- printf("SQL error %lu\n", DB_ERROR);
+ printf("SQL error %lu\n", (ulint)DB_ERROR);
que_thr_handle_error(thr, DB_ERROR, NULL, 0);
@@ -2251,7 +2260,7 @@ row_sel_get_clust_rec_for_mysql(
/************************************************************************
Restores cursor position after it has been stored. We have to take into
-account that the record cursor was positioned on can have been deleted.
+account that the record cursor was positioned on may have been deleted.
Then we may have to move the cursor one step up or down. */
static
ibool
@@ -2284,14 +2293,14 @@ sel_restore_position_for_mysql(
if (moves_up) {
btr_pcur_move_to_next(pcur, mtr);
-
- return(TRUE);
}
return(TRUE);
}
- if (relative_position == BTR_PCUR_AFTER) {
+ if (relative_position == BTR_PCUR_AFTER
+ || relative_position == BTR_PCUR_AFTER_LAST_IN_TREE) {
+
if (moves_up) {
return(TRUE);
}
@@ -2303,7 +2312,8 @@ sel_restore_position_for_mysql(
return(TRUE);
}
- ut_ad(relative_position == BTR_PCUR_BEFORE);
+ ut_ad(relative_position == BTR_PCUR_BEFORE
+ || relative_position == BTR_PCUR_BEFORE_FIRST_IN_TREE);
if (moves_up && btr_pcur_is_on_user_rec(pcur, mtr)) {
btr_pcur_move_to_next(pcur, mtr);
@@ -2586,21 +2596,30 @@ row_search_for_mysql(
let us try a search shortcut through the hash
index */
+ if (btr_search_latch.writer != RW_LOCK_NOT_LOCKED) {
+ /* There is an x-latch request: release
+ a possible s-latch to reduce starvation
+ and wait for BTR_SEA_TIMEOUT rounds before
+ trying to keep it again over calls from
+ MySQL */
+
+ if (trx->has_search_latch) {
+ rw_lock_s_unlock(&btr_search_latch);
+ trx->has_search_latch = FALSE;
+ }
+
+ trx->search_latch_timeout = BTR_SEA_TIMEOUT;
+
+ goto no_shortcut;
+ }
+
if (!trx->has_search_latch) {
rw_lock_s_lock(&btr_search_latch);
trx->has_search_latch = TRUE;
-
- } else if (btr_search_latch.writer_is_wait_ex) {
- /* There is an x-latch request waiting:
- release the s-latch for a moment to reduce
- starvation */
-
- rw_lock_s_unlock(&btr_search_latch);
- rw_lock_s_lock(&btr_search_latch);
}
shortcut = row_sel_try_search_shortcut_for_mysql(&rec,
- prebuilt, &mtr);
+ prebuilt, &mtr);
if (shortcut == SEL_FOUND) {
row_sel_store_mysql_rec(buf, prebuilt, rec);
@@ -2609,7 +2628,16 @@ row_search_for_mysql(
/* printf("%s shortcut\n", index->name); */
srv_n_rows_read++;
+
+ if (trx->search_latch_timeout > 0
+ && trx->has_search_latch) {
+ trx->search_latch_timeout--;
+
+ rw_lock_s_unlock(&btr_search_latch);
+ trx->has_search_latch = FALSE;
+ }
+
trx->op_info = "";
return(DB_SUCCESS);
@@ -2619,6 +2647,16 @@ row_search_for_mysql(
/* printf("%s record not found 2\n",
index->name); */
+
+ if (trx->search_latch_timeout > 0
+ && trx->has_search_latch) {
+
+ trx->search_latch_timeout--;
+
+ rw_lock_s_unlock(&btr_search_latch);
+ trx->has_search_latch = FALSE;
+ }
+
trx->op_info = "";
return(DB_RECORD_NOT_FOUND);
}
@@ -2627,7 +2665,7 @@ row_search_for_mysql(
mtr_start(&mtr);
}
}
-
+no_shortcut:
if (trx->has_search_latch) {
rw_lock_s_unlock(&btr_search_latch);
trx->has_search_latch = FALSE;
diff --git a/innobase/row/row0umod.c b/innobase/row/row0umod.c
index a7c8957d61a..37f5b1f0bc1 100644
--- a/innobase/row/row0umod.c
+++ b/innobase/row/row0umod.c
@@ -443,6 +443,8 @@ row_undo_mod_del_unmark_sec(
"InnoDB: Make a detailed bug report and send it\n");
fprintf(stderr, "InnoDB: to mysql@lists.mysql.com\n");
+ trx_print(thr_get_trx(thr));
+
mem_free(err_buf);
} else {
btr_cur = btr_pcur_get_btr_cur(&pcur);
@@ -552,7 +554,7 @@ row_undo_mod_upd_exist_sec(
while (node->index != NULL) {
index = node->index;
- if (row_upd_changes_ord_field(node->row, node->index,
+ if (row_upd_changes_ord_field_binary(node->row, node->index,
node->update)) {
/* Build the newest version of the index entry */
diff --git a/innobase/row/row0upd.c b/innobase/row/row0upd.c
index 3fa98db3a02..fa859729141 100644
--- a/innobase/row/row0upd.c
+++ b/innobase/row/row0upd.c
@@ -72,6 +72,134 @@ searched delete is obviously to keep the x-latch for several
steps of query graph execution. */
/*************************************************************************
+Checks if index currently is mentioned as a referenced index in a foreign
+key constraint. This function also loads into the dictionary cache the
+possible referencing table. */
+static
+ibool
+row_upd_index_is_referenced(
+/*========================*/
+ /* out: TRUE if referenced; NOTE that since
+ we do not hold dict_foreign_key_check_lock
+ when leaving the function, it may be that
+ the referencing table has been dropped when
+ we leave this function: this function is only
+ for heuristic use! */
+ dict_index_t* index) /* in: index */
+{
+ dict_table_t* table = index->table;
+ dict_foreign_t* foreign;
+ ulint phase = 1;
+
+try_again:
+ if (!UT_LIST_GET_FIRST(table->referenced_list)) {
+
+ return(FALSE);
+ }
+
+ if (phase == 2) {
+ mutex_enter(&(dict_sys->mutex));
+ }
+
+ rw_lock_s_lock(&dict_foreign_key_check_lock);
+
+ foreign = UT_LIST_GET_FIRST(table->referenced_list);
+
+ while (foreign) {
+ if (foreign->referenced_index == index) {
+ if (foreign->foreign_table == NULL) {
+ if (phase == 2) {
+ dict_table_get_low(foreign->
+ foreign_table_name);
+ } else {
+ phase = 2;
+ rw_lock_s_unlock(
+ &dict_foreign_key_check_lock);
+ goto try_again;
+ }
+ }
+
+ rw_lock_s_unlock(&dict_foreign_key_check_lock);
+
+ if (phase == 2) {
+ mutex_exit(&(dict_sys->mutex));
+ }
+
+ return(TRUE);
+ }
+
+ foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
+ }
+
+ rw_lock_s_unlock(&dict_foreign_key_check_lock);
+
+ if (phase == 2) {
+ mutex_exit(&(dict_sys->mutex));
+ }
+
+ return(FALSE);
+}
+
+/*************************************************************************
+Checks if possible foreign key constraints hold after a delete of the record
+under pcur. NOTE that this function will temporarily commit mtr and lose
+pcur position! */
+static
+ulint
+row_upd_check_references_constraints(
+/*=================================*/
+ /* out: DB_SUCCESS, DB_LOCK_WAIT, or an error
+ code */
+ btr_pcur_t* pcur, /* in: cursor positioned on a record; NOTE: the
+ cursor position is lost in this function! */
+ dict_table_t* table, /* in: table in question */
+ dict_index_t* index, /* in: index of the cursor */
+ que_thr_t* thr, /* in: query thread */
+ mtr_t* mtr) /* in: mtr */
+{
+ dict_foreign_t* foreign;
+ mem_heap_t* heap;
+ dtuple_t* entry;
+ rec_t* rec;
+ ulint err;
+
+ rec = btr_pcur_get_rec(pcur);
+
+ heap = mem_heap_create(500);
+
+ entry = row_rec_to_index_entry(ROW_COPY_DATA, index, rec, heap);
+
+ mtr_commit(mtr);
+
+ mtr_start(mtr);
+
+ rw_lock_s_lock(&dict_foreign_key_check_lock);
+
+ foreign = UT_LIST_GET_FIRST(table->referenced_list);
+
+ while (foreign) {
+ if (foreign->referenced_index == index) {
+
+ err = row_ins_check_foreign_constraint(FALSE, foreign,
+ table, index, entry, thr);
+ if (err != DB_SUCCESS) {
+ rw_lock_s_unlock(&dict_foreign_key_check_lock);
+ mem_heap_free(heap);
+
+ return(err);
+ }
+ }
+
+ foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
+ }
+
+ rw_lock_s_unlock(&dict_foreign_key_check_lock);
+ mem_heap_free(heap);
+
+ return(DB_SUCCESS);
+}
+
+/*************************************************************************
Creates an update node for a query graph. */
upd_node_t*
@@ -484,13 +612,73 @@ upd_ext_vec_contains(
}
/*******************************************************************
+Builds an update vector from those fields which in a secondary index entry
+differ from a record that has the equal ordering fields. NOTE: we compare
+the fields as binary strings! */
+
+upd_t*
+row_upd_build_sec_rec_difference_binary(
+/*====================================*/
+ /* out, own: update vector of differing
+ fields */
+ dict_index_t* index, /* in: index */
+ dtuple_t* entry, /* in: entry to insert */
+ rec_t* rec, /* in: secondary index record */
+ mem_heap_t* heap) /* in: memory heap from which allocated */
+{
+ upd_field_t* upd_field;
+ dfield_t* dfield;
+ byte* data;
+ ulint len;
+ upd_t* update;
+ ulint n_diff;
+ ulint i;
+
+ /* This function is used only for a secondary index */
+ ut_ad(0 == (index->type & DICT_CLUSTERED));
+
+ update = upd_create(dtuple_get_n_fields(entry), heap);
+
+ n_diff = 0;
+
+ for (i = 0; i < dtuple_get_n_fields(entry); i++) {
+
+ data = rec_get_nth_field(rec, i, &len);
+
+ dfield = dtuple_get_nth_field(entry, i);
+
+ ut_a(len == dfield_get_len(dfield));
+
+ /* NOTE: we compare the fields as binary strings!
+ (No collation) */
+
+ if (!dfield_data_is_binary_equal(dfield, len, data)) {
+
+ upd_field = upd_get_nth_field(update, n_diff);
+
+ dfield_copy(&(upd_field->new_val), dfield);
+
+ upd_field_set_field_no(upd_field, i, index);
+
+ upd_field->extern_storage = FALSE;
+
+ n_diff++;
+ }
+ }
+
+ update->n_fields = n_diff;
+
+ return(update);
+}
+
+/*******************************************************************
Builds an update vector from those fields, excluding the roll ptr and
trx id fields, which in an index entry differ from a record that has
-the equal ordering fields. */
+the equal ordering fields. NOTE: we compare the fields as binary strings! */
upd_t*
-row_upd_build_difference(
-/*=====================*/
+row_upd_build_difference_binary(
+/*============================*/
/* out, own: update vector of differing
fields, excluding roll ptr and trx id */
dict_index_t* index, /* in: clustered index */
@@ -527,10 +715,13 @@ row_upd_build_difference(
dfield = dtuple_get_nth_field(entry, i);
+ /* NOTE: we compare the fields as binary strings!
+ (No collation) */
+
if ((rec_get_nth_field_extern_bit(rec, i)
!= upd_ext_vec_contains(ext_vec, n_ext_vec, i))
|| ((i != trx_id_pos) && (i != roll_ptr_pos)
- && !dfield_data_is_equal(dfield, len, data))) {
+ && !dfield_data_is_binary_equal(dfield, len, data))) {
upd_field = upd_get_nth_field(update, n_diff);
@@ -630,13 +821,16 @@ row_upd_clust_index_replace_new_col_vals(
/***************************************************************
Checks if an update vector changes an ordering field of an index record.
This function is fast if the update vector is short or the number of ordering
-fields in the index is small. Otherwise, this can be quadratic. */
+fields in the index is small. Otherwise, this can be quadratic.
+NOTE: we compare the fields as binary strings! */
ibool
-row_upd_changes_ord_field(
-/*======================*/
+row_upd_changes_ord_field_binary(
+/*=============================*/
/* out: TRUE if update vector changes
- an ordering field in the index record */
+ an ordering field in the index record;
+ NOTE: the fields are compared as binary
+ strings */
dtuple_t* row, /* in: old value of row, or NULL if the
row and the data values in update are not
known when this function is called, e.g., at
@@ -671,7 +865,7 @@ row_upd_changes_ord_field(
if (col_pos == upd_field->field_no
&& (row == NULL
- || !dfield_datas_are_equal(
+ || !dfield_datas_are_binary_equal(
dtuple_get_nth_field(row, col_no),
&(upd_field->new_val)))) {
return(TRUE);
@@ -683,11 +877,12 @@ row_upd_changes_ord_field(
}
/***************************************************************
-Checks if an update vector changes an ordering field of an index record. */
+Checks if an update vector changes an ordering field of an index record.
+NOTE: we compare the fields as binary strings! */
ibool
-row_upd_changes_some_index_ord_field(
-/*=================================*/
+row_upd_changes_some_index_ord_field_binary(
+/*========================================*/
/* out: TRUE if update vector may change
an ordering field in an index record */
dict_table_t* table, /* in: table */
@@ -812,6 +1007,7 @@ row_upd_sec_index_entry(
upd_node_t* node, /* in: row update node */
que_thr_t* thr) /* in: query thread */
{
+ ibool check_ref;
ibool found;
dict_index_t* index;
dtuple_t* entry;
@@ -825,6 +1021,8 @@ row_upd_sec_index_entry(
index = node->index;
+ check_ref = row_upd_index_is_referenced(index);
+
heap = mem_heap_create(1024);
/* Build old index entry */
@@ -855,6 +1053,8 @@ row_upd_sec_index_entry(
"InnoDB: Make a detailed bug report and send it\n");
fprintf(stderr, "InnoDB: to mysql@lists.mysql.com\n");
+ trx_print(thr_get_trx(thr));
+
mem_free(err_buf);
} else {
/* Delete mark the old index record; it can already be
@@ -864,9 +1064,21 @@ row_upd_sec_index_entry(
if (!rec_get_deleted_flag(rec)) {
err = btr_cur_del_mark_set_sec_rec(0, btr_cur, TRUE,
thr, &mtr);
+ if (err == DB_SUCCESS && check_ref) {
+ /* NOTE that the following call loses
+ the position of pcur ! */
+ err = row_upd_check_references_constraints(
+ &pcur, index->table,
+ index, thr, &mtr);
+ if (err != DB_SUCCESS) {
+
+ goto close_cur;
+ }
+ }
+
}
}
-
+close_cur:
btr_pcur_close(&pcur);
mtr_commit(&mtr);
@@ -907,8 +1119,8 @@ row_upd_sec_step(
ut_ad(!(node->index->type & DICT_CLUSTERED));
if (node->state == UPD_NODE_UPDATE_ALL_SEC
- || row_upd_changes_ord_field(node->row, node->index,
- node->update)) {
+ || row_upd_changes_ord_field_binary(node->row, node->index,
+ node->update)) {
err = row_upd_sec_index_entry(node, thr);
return(err);
@@ -931,6 +1143,8 @@ row_upd_clust_rec_by_insert(
upd_node_t* node, /* in: row update node */
dict_index_t* index, /* in: clustered index of the record */
que_thr_t* thr, /* in: query thread */
+ ibool check_ref,/* in: TRUE if index may be referenced in
+ a foreign key constraint */
mtr_t* mtr) /* in: mtr; gets committed here */
{
mem_heap_t* heap;
@@ -958,6 +1172,7 @@ row_upd_clust_rec_by_insert(
return(err);
}
+
/* Mark as not-owned the externally stored fields which the new
row inherits from the delete marked record: purge should not
free those externally stored fields even if the delete marked
@@ -965,6 +1180,19 @@ row_upd_clust_rec_by_insert(
btr_cur_mark_extern_inherited_fields(btr_cur_get_rec(btr_cur),
node->update, mtr);
+ if (check_ref) {
+ /* NOTE that the following call loses
+ the position of pcur ! */
+ err = row_upd_check_references_constraints(
+ pcur, table,
+ index, thr, mtr);
+ if (err != DB_SUCCESS) {
+ mtr_commit(mtr);
+
+ return(err);
+ }
+ }
+
}
mtr_commit(mtr);
@@ -1095,6 +1323,8 @@ row_upd_del_mark_clust_rec(
upd_node_t* node, /* in: row update node */
dict_index_t* index, /* in: clustered index */
que_thr_t* thr, /* in: query thread */
+ ibool check_ref,/* in: TRUE if index may be referenced in
+ a foreign key constraint */
mtr_t* mtr) /* in: mtr; gets committed here */
{
btr_pcur_t* pcur;
@@ -1120,6 +1350,18 @@ row_upd_del_mark_clust_rec(
err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG, btr_cur,
TRUE, thr, mtr);
+ if (err == DB_SUCCESS && check_ref) {
+ /* NOTE that the following call loses
+ the position of pcur ! */
+ err = row_upd_check_references_constraints(pcur, index->table,
+ index, thr, mtr);
+ if (err != DB_SUCCESS) {
+ mtr_commit(mtr);
+
+ return(err);
+ }
+ }
+
mtr_commit(mtr);
return(err);
@@ -1140,12 +1382,15 @@ row_upd_clust_step(
dict_index_t* index;
btr_pcur_t* pcur;
ibool success;
+ ibool check_ref;
ulint err;
- mtr_t mtr_buf;
mtr_t* mtr;
+ mtr_t mtr_buf;
index = dict_table_get_first_index(node->table);
+ check_ref = row_upd_index_is_referenced(index);
+
pcur = node->pcur;
/* We have to restore the cursor to its position */
@@ -1210,8 +1455,8 @@ row_upd_clust_step(
/* NOTE: the following function calls will also commit mtr */
if (node->is_delete) {
- err = row_upd_del_mark_clust_rec(node, index, thr, mtr);
-
+ err = row_upd_del_mark_clust_rec(node, index, thr, check_ref,
+ mtr);
if (err != DB_SUCCESS) {
return(err);
@@ -1244,7 +1489,7 @@ row_upd_clust_step(
row_upd_store_row(node);
- if (row_upd_changes_ord_field(node->row, index, node->update)) {
+ if (row_upd_changes_ord_field_binary(node->row, index, node->update)) {
/* Update causes an ordering field (ordering fields within
the B-tree) of the clustered index record to change: perform
@@ -1257,8 +1502,8 @@ row_upd_clust_step(
choosing records to update. MySQL solves now the problem
externally! */
- err = row_upd_clust_rec_by_insert(node, index, thr, mtr);
-
+ err = row_upd_clust_rec_by_insert(node, index, thr, check_ref,
+ mtr);
if (err != DB_SUCCESS) {
return(err);
@@ -1304,8 +1549,8 @@ row_upd(
interpreter: we must calculate it on the fly: */
if (node->is_delete ||
- row_upd_changes_some_index_ord_field(node->table,
- node->update)) {
+ row_upd_changes_some_index_ord_field_binary(
+ node->table, node->update)) {
node->cmpl_info = 0;
} else {
node->cmpl_info = UPD_NODE_NO_ORD_CHANGE;
diff --git a/innobase/row/row0vers.c b/innobase/row/row0vers.c
index 4dc65669247..5b62cd2b7e3 100644
--- a/innobase/row/row0vers.c
+++ b/innobase/row/row0vers.c
@@ -269,7 +269,13 @@ row_vers_old_has_index_entry(
row = row_build(ROW_COPY_POINTERS, clust_index, rec, heap);
entry = row_build_index_entry(row, index, heap);
- if (dtuple_datas_are_equal(ientry, entry)) {
+ /* NOTE that we cannot do the comparison as binary
+ fields because the row is maybe being modified so that
+ the clustered index record has already been updated
+ to a different binary value in a char field, but the
+ collation identifies the old and new value anyway! */
+
+ if (dtuple_datas_are_ordering_equal(ientry, entry)) {
mem_heap_free(heap);
@@ -307,7 +313,13 @@ row_vers_old_has_index_entry(
prev_version, heap);
entry = row_build_index_entry(row, index, heap);
- if (dtuple_datas_are_equal(ientry, entry)) {
+ /* NOTE that we cannot do the comparison as binary
+ fields because maybe the secondary index record has
+ already been updated to a different binary value in
+ a char field, but the collation identifies the old
+ and new value anyway! */
+
+ if (dtuple_datas_are_ordering_equal(ientry, entry)) {
mem_heap_free(heap);