diff options
author | unknown <heikki@donna.mysql.fi> | 2001-10-10 22:47:08 +0300 |
---|---|---|
committer | unknown <heikki@donna.mysql.fi> | 2001-10-10 22:47:08 +0300 |
commit | 1904897be71cba7e6f2cf1192ba0cc2e8d907e00 (patch) | |
tree | fc361924d14a3d1727a8b88f61352ed039054720 /innobase/row | |
parent | 151ffe886b4b21499471658fdf01ea8347287092 (diff) | |
download | mariadb-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.c | 244 | ||||
-rw-r--r-- | innobase/row/row0mysql.c | 419 | ||||
-rw-r--r-- | innobase/row/row0purge.c | 4 | ||||
-rw-r--r-- | innobase/row/row0sel.c | 94 | ||||
-rw-r--r-- | innobase/row/row0umod.c | 4 | ||||
-rw-r--r-- | innobase/row/row0upd.c | 291 | ||||
-rw-r--r-- | innobase/row/row0vers.c | 16 |
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); |