Commit 4709a172 authored by heikki@donna.mysql.fi's avatar heikki@donna.mysql.fi

srv0srv.h Merging work for 3.23.37

os0thread.c	Set created thread priority to QUERY_PRIOR if specified
row0mysql.c	Drop table removes locks on the table, error handling changed
row0ins.c	Insert now always sets a shared lock on a duplicate or suspected duplicate record
lock0lock.h	Can remove locks from a table to be dropped
lock0lock.c	Can remove locks from a table to be dropped
srv0srv.c	Try to do log flush every second
srv0start.c	No need for a slash at the end of dir
trx0trx.h	Added an IGNORE option field to trx handle
trx0trx.c	Added an IGNORE option field to trx handle
ha_innobase.h	Added last_query_id field to handle
ha_innobase.cc	Fix drop table hang, roll-forward etc.
parent 54b9d367
sasha@mysql.sashanet.com heikki@donna.mysql.fi
monty@donna.mysql.fi
...@@ -396,6 +396,14 @@ lock_release_off_kernel( ...@@ -396,6 +396,14 @@ lock_release_off_kernel(
/*====================*/ /*====================*/
trx_t* trx); /* in: transaction */ trx_t* trx); /* in: transaction */
/************************************************************************* /*************************************************************************
Resets all locks, both table and record locks, on a table to be dropped.
No lock is allowed to be a wait lock. */
void
lock_reset_all_on_table(
/*====================*/
dict_table_t* table); /* in: table to be dropped */
/*************************************************************************
Calculates the fold value of a page file address: used in inserting or Calculates the fold value of a page file address: used in inserting or
searching for a lock in the hash table. */ searching for a lock in the hash table. */
UNIV_INLINE UNIV_INLINE
......
...@@ -48,6 +48,9 @@ extern dulint srv_archive_recovery_limit_lsn; ...@@ -48,6 +48,9 @@ extern dulint srv_archive_recovery_limit_lsn;
extern ulint srv_lock_wait_timeout; extern ulint srv_lock_wait_timeout;
extern ibool srv_set_thread_priorities;
extern int srv_query_thread_priority;
/*-------------------------------------------*/ /*-------------------------------------------*/
extern ulint srv_n_spin_wait_rounds; extern ulint srv_n_spin_wait_rounds;
extern ulint srv_spin_wait_delay; extern ulint srv_spin_wait_delay;
......
...@@ -282,7 +282,11 @@ struct trx_struct{ ...@@ -282,7 +282,11 @@ struct trx_struct{
ulint n_mysql_tables_in_use; /* number of Innobase tables ulint n_mysql_tables_in_use; /* number of Innobase tables
used in the processing of the current used in the processing of the current
SQL statement in MySQL */ SQL statement in MySQL */
UT_LIST_NODE_T(trx_t) ibool ignore_duplicates_in_insert;
/* in an insert roll back only insert
of the latest row in case
of a duplicate key error */
UT_LIST_NODE_T(trx_t)
trx_list; /* list of transactions */ trx_list; /* list of transactions */
/*------------------------------*/ /*------------------------------*/
mutex_t undo_mutex; /* mutex protecting the fields in this mutex_t undo_mutex; /* mutex protecting the fields in this
......
...@@ -700,24 +700,24 @@ lock_mode_stronger_or_eq( ...@@ -700,24 +700,24 @@ lock_mode_stronger_or_eq(
ulint mode1, /* in: lock mode */ ulint mode1, /* in: lock mode */
ulint mode2) /* in: lock mode */ ulint mode2) /* in: lock mode */
{ {
ut_ad((mode1 == LOCK_X) || (mode1 == LOCK_S) || (mode1 == LOCK_IX) ut_ad(mode1 == LOCK_X || mode1 == LOCK_S || mode1 == LOCK_IX
|| (mode1 == LOCK_IS)); || mode1 == LOCK_IS);
ut_ad((mode2 == LOCK_X) || (mode2 == LOCK_S) || (mode2 == LOCK_IX) ut_ad(mode2 == LOCK_X || mode2 == LOCK_S || mode2 == LOCK_IX
|| (mode2 == LOCK_IS)); || mode2 == LOCK_IS);
if (mode1 == LOCK_X) { if (mode1 == LOCK_X) {
return(TRUE); return(TRUE);
} else if ((mode1 == LOCK_S) } else if (mode1 == LOCK_S
&& ((mode2 == LOCK_S) || (mode2 == LOCK_IS))) { && (mode2 == LOCK_S || mode2 == LOCK_IS)) {
return(TRUE); return(TRUE);
} else if ((mode1 == LOCK_IS) && (mode2 == LOCK_IS)) { } else if (mode1 == LOCK_IS && mode2 == LOCK_IS) {
return(TRUE); return(TRUE);
} else if ((mode1 == LOCK_IX) && ((mode2 == LOCK_IX) } else if (mode1 == LOCK_IX && (mode2 == LOCK_IX
|| (mode2 == LOCK_IS))) { || mode2 == LOCK_IS)) {
return(TRUE); return(TRUE);
} }
...@@ -734,12 +734,12 @@ lock_mode_compatible( ...@@ -734,12 +734,12 @@ lock_mode_compatible(
ulint mode1, /* in: lock mode */ ulint mode1, /* in: lock mode */
ulint mode2) /* in: lock mode */ ulint mode2) /* in: lock mode */
{ {
ut_ad((mode1 == LOCK_X) || (mode1 == LOCK_S) || (mode1 == LOCK_IX) ut_ad(mode1 == LOCK_X || mode1 == LOCK_S || mode1 == LOCK_IX
|| (mode1 == LOCK_IS)); || mode1 == LOCK_IS);
ut_ad((mode2 == LOCK_X) || (mode2 == LOCK_S) || (mode2 == LOCK_IX) ut_ad(mode2 == LOCK_X || mode2 == LOCK_S || mode2 == LOCK_IX
|| (mode2 == LOCK_IS)); || mode2 == LOCK_IS);
if ((mode1 == LOCK_S) && ((mode2 == LOCK_IS) || (mode2 == LOCK_S))) { if (mode1 == LOCK_S && (mode2 == LOCK_IS || mode2 == LOCK_S)) {
return(TRUE); return(TRUE);
...@@ -747,13 +747,13 @@ lock_mode_compatible( ...@@ -747,13 +747,13 @@ lock_mode_compatible(
return(FALSE); return(FALSE);
} else if ((mode1 == LOCK_IS) && ((mode2 == LOCK_IS) } else if (mode1 == LOCK_IS && (mode2 == LOCK_IS
|| (mode2 == LOCK_IX) || mode2 == LOCK_IX
|| (mode2 == LOCK_S))) { || mode2 == LOCK_S)) {
return(TRUE); return(TRUE);
} else if ((mode1 == LOCK_IX) && ((mode2 == LOCK_IS) } else if (mode1 == LOCK_IX && (mode2 == LOCK_IS
|| (mode2 == LOCK_IX))) { || mode2 == LOCK_IX)) {
return(TRUE); return(TRUE);
} }
...@@ -769,7 +769,7 @@ lock_get_confl_mode( ...@@ -769,7 +769,7 @@ lock_get_confl_mode(
/* out: conflicting basic lock mode */ /* out: conflicting basic lock mode */
ulint mode) /* in: LOCK_S or LOCK_X */ ulint mode) /* in: LOCK_S or LOCK_X */
{ {
ut_ad((mode == LOCK_X) || (mode == LOCK_S)); ut_ad(mode == LOCK_X || mode == LOCK_S);
if (mode == LOCK_S) { if (mode == LOCK_S) {
...@@ -792,7 +792,7 @@ lock_has_to_wait( ...@@ -792,7 +792,7 @@ lock_has_to_wait(
lock_t* lock2) /* in: another lock; NOTE that it is assumed that this lock_t* lock2) /* in: another lock; NOTE that it is assumed that this
has a lock bit set on the same record as in lock1 */ has a lock bit set on the same record as in lock1 */
{ {
if ((lock1->trx != lock2->trx) if (lock1->trx != lock2->trx
&& !lock_mode_compatible(lock_get_mode(lock1), && !lock_mode_compatible(lock_get_mode(lock1),
lock_get_mode(lock2))) { lock_get_mode(lock2))) {
return(TRUE); return(TRUE);
...@@ -1228,8 +1228,8 @@ lock_table_has( ...@@ -1228,8 +1228,8 @@ lock_table_has(
while (lock != NULL) { while (lock != NULL) {
if ((lock->trx == trx) if (lock->trx == trx
&& (lock_mode_stronger_or_eq(lock_get_mode(lock), mode))) { && lock_mode_stronger_or_eq(lock_get_mode(lock), mode)) {
/* The same trx already has locked the table in /* The same trx already has locked the table in
a mode stronger or equal to the mode given */ a mode stronger or equal to the mode given */
...@@ -1267,7 +1267,7 @@ lock_rec_has_expl( ...@@ -1267,7 +1267,7 @@ lock_rec_has_expl(
lock = lock_rec_get_first(rec); lock = lock_rec_get_first(rec);
while (lock) { while (lock) {
if ((lock->trx == trx) if (lock->trx == trx
&& lock_mode_stronger_or_eq(lock_get_mode(lock), mode) && lock_mode_stronger_or_eq(lock_get_mode(lock), mode)
&& !lock_get_wait(lock) && !lock_get_wait(lock)
&& !(lock_rec_get_gap(lock) && !(lock_rec_get_gap(lock)
...@@ -1306,7 +1306,7 @@ lock_rec_other_has_expl_req( ...@@ -1306,7 +1306,7 @@ lock_rec_other_has_expl_req(
lock = lock_rec_get_first(rec); lock = lock_rec_get_first(rec);
while (lock) { while (lock) {
if ((lock->trx != trx) if (lock->trx != trx
&& (gap || && (gap ||
!(lock_rec_get_gap(lock) || page_rec_is_supremum(rec))) !(lock_rec_get_gap(lock) || page_rec_is_supremum(rec)))
&& (wait || !lock_get_wait(lock)) && (wait || !lock_get_wait(lock))
...@@ -1344,9 +1344,9 @@ lock_rec_find_similar_on_page( ...@@ -1344,9 +1344,9 @@ lock_rec_find_similar_on_page(
lock = lock_rec_get_first_on_page(rec); lock = lock_rec_get_first_on_page(rec);
while (lock != NULL) { while (lock != NULL) {
if ((lock->trx == trx) if (lock->trx == trx
&& (lock->type_mode == type_mode) && lock->type_mode == type_mode
&& (lock_rec_get_n_bits(lock) > heap_no)) { && lock_rec_get_n_bits(lock) > heap_no) {
return(lock); return(lock);
} }
...@@ -1653,9 +1653,9 @@ lock_rec_lock_fast( ...@@ -1653,9 +1653,9 @@ lock_rec_lock_fast(
return(FALSE); return(FALSE);
} }
if ((lock->trx != thr_get_trx(thr)) if (lock->trx != thr_get_trx(thr)
|| (lock->type_mode != (mode | LOCK_REC)) || lock->type_mode != (mode | LOCK_REC)
|| (lock_rec_get_n_bits(lock) <= heap_no)) { || lock_rec_get_n_bits(lock) <= heap_no) {
return(FALSE); return(FALSE);
} }
...@@ -1836,7 +1836,7 @@ lock_rec_cancel( ...@@ -1836,7 +1836,7 @@ lock_rec_cancel(
{ {
ut_ad(mutex_own(&kernel_mutex)); ut_ad(mutex_own(&kernel_mutex));
/* Reset the bit in lock bitmap */ /* Reset the bit (there can be only one set bit) in the lock bitmap */
lock_rec_reset_nth_bit(lock, lock_rec_find_set_bit(lock)); lock_rec_reset_nth_bit(lock, lock_rec_find_set_bit(lock));
/* Reset the wait flag and the back pointer to lock in trx */ /* Reset the wait flag and the back pointer to lock in trx */
...@@ -1884,8 +1884,7 @@ lock_rec_dequeue_from_page( ...@@ -1884,8 +1884,7 @@ lock_rec_dequeue_from_page(
lock = lock_rec_get_first_on_page_addr(space, page_no); lock = lock_rec_get_first_on_page_addr(space, page_no);
while (lock != NULL) { while (lock != NULL) {
if (lock_get_wait(lock) if (lock_get_wait(lock)
&& !lock_rec_has_to_wait_in_queue(lock)) { && !lock_rec_has_to_wait_in_queue(lock)) {
...@@ -1897,6 +1896,33 @@ lock_rec_dequeue_from_page( ...@@ -1897,6 +1896,33 @@ lock_rec_dequeue_from_page(
} }
} }
/*****************************************************************
Removes a record lock request, waiting or granted, from the queue. */
static
void
lock_rec_discard(
/*=============*/
lock_t* in_lock)/* in: record lock object: all record locks which
are contained in this lock object are removed */
{
ulint space;
ulint page_no;
trx_t* trx;
ut_ad(mutex_own(&kernel_mutex));
ut_ad(lock_get_type(in_lock) == LOCK_REC);
trx = in_lock->trx;
space = in_lock->un_member.rec_lock.space;
page_no = in_lock->un_member.rec_lock.page_no;
HASH_DELETE(lock_t, hash, lock_sys->rec_hash,
lock_rec_fold(space, page_no), in_lock);
UT_LIST_REMOVE(trx_locks, trx->trx_locks, in_lock);
}
/***************************************************************** /*****************************************************************
Removes record lock objects set on an index page which is discarded. This Removes record lock objects set on an index page which is discarded. This
function does not move locks, or check for waiting locks, therefore the function does not move locks, or check for waiting locks, therefore the
...@@ -1911,7 +1937,6 @@ lock_rec_free_all_from_discard_page( ...@@ -1911,7 +1937,6 @@ lock_rec_free_all_from_discard_page(
ulint page_no; ulint page_no;
lock_t* lock; lock_t* lock;
lock_t* next_lock; lock_t* next_lock;
trx_t* trx;
ut_ad(mutex_own(&kernel_mutex)); ut_ad(mutex_own(&kernel_mutex));
...@@ -1926,12 +1951,8 @@ lock_rec_free_all_from_discard_page( ...@@ -1926,12 +1951,8 @@ lock_rec_free_all_from_discard_page(
next_lock = lock_rec_get_next_on_page(lock); next_lock = lock_rec_get_next_on_page(lock);
HASH_DELETE(lock_t, hash, lock_sys->rec_hash, lock_rec_discard(lock);
lock_rec_fold(space, page_no), lock);
trx = lock->trx;
UT_LIST_REMOVE(trx_locks, trx->trx_locks, lock);
lock = next_lock; lock = next_lock;
} }
} }
...@@ -1957,7 +1978,6 @@ lock_rec_reset_and_release_wait( ...@@ -1957,7 +1978,6 @@ lock_rec_reset_and_release_wait(
lock = lock_rec_get_first(rec); lock = lock_rec_get_first(rec);
while (lock != NULL) { while (lock != NULL) {
if (lock_get_wait(lock)) { if (lock_get_wait(lock)) {
lock_rec_cancel(lock); lock_rec_cancel(lock);
} else { } else {
...@@ -3088,6 +3108,69 @@ lock_release_off_kernel( ...@@ -3088,6 +3108,69 @@ lock_release_off_kernel(
mem_heap_empty(trx->lock_heap); mem_heap_empty(trx->lock_heap);
} }
/*************************************************************************
Resets all record and table locks of a transaction on a table to be dropped.
No lock is allowed to be a wait lock. */
static
void
lock_reset_all_on_table_for_trx(
/*============================*/
dict_table_t* table, /* in: table to be dropped */
trx_t* trx) /* in: a transaction */
{
lock_t* lock;
lock_t* prev_lock;
ut_ad(mutex_own(&kernel_mutex));
lock = UT_LIST_GET_LAST(trx->trx_locks);
while (lock != NULL) {
prev_lock = UT_LIST_GET_PREV(trx_locks, lock);
if (lock_get_type(lock) == LOCK_REC
&& lock->index->table == table) {
ut_a(!lock_get_wait(lock));
lock_rec_discard(lock);
} else if (lock_get_type(lock) == LOCK_TABLE
&& lock->un_member.tab_lock.table == table) {
ut_a(!lock_get_wait(lock));
lock_table_remove_low(lock);
}
lock = prev_lock;
}
}
/*************************************************************************
Resets all locks, both table and record locks, on a table to be dropped.
No lock is allowed to be a wait lock. */
void
lock_reset_all_on_table(
/*====================*/
dict_table_t* table) /* in: table to be dropped */
{
lock_t* lock;
mutex_enter(&kernel_mutex);
lock = UT_LIST_GET_FIRST(table->locks);
while (lock) {
ut_a(!lock_get_wait(lock));
lock_reset_all_on_table_for_trx(table, lock->trx);
lock = UT_LIST_GET_FIRST(table->locks);
}
mutex_exit(&kernel_mutex);
}
/*===================== VALIDATION AND DEBUGGING ====================*/ /*===================== VALIDATION AND DEBUGGING ====================*/
/************************************************************************* /*************************************************************************
...@@ -3394,7 +3477,7 @@ lock_rec_queue_validate( ...@@ -3394,7 +3477,7 @@ lock_rec_queue_validate(
while (lock) { while (lock) {
ut_a(lock->trx->conc_state == TRX_ACTIVE ut_a(lock->trx->conc_state == TRX_ACTIVE
|| lock->trx->conc_state || lock->trx->conc_state
== TRX_COMMITTED_IN_MEMORY); == TRX_COMMITTED_IN_MEMORY);
ut_a(trx_in_trx_list(lock->trx)); ut_a(trx_in_trx_list(lock->trx));
...@@ -3414,7 +3497,7 @@ lock_rec_queue_validate( ...@@ -3414,7 +3497,7 @@ lock_rec_queue_validate(
return(TRUE); return(TRUE);
} }
if (index && index->type & DICT_CLUSTERED) { if (index && (index->type & DICT_CLUSTERED)) {
impl_trx = lock_clust_rec_some_has_impl(rec, index); impl_trx = lock_clust_rec_some_has_impl(rec, index);
...@@ -3525,12 +3608,12 @@ loop: ...@@ -3525,12 +3608,12 @@ loop:
} }
ut_a(trx_in_trx_list(lock->trx)); ut_a(trx_in_trx_list(lock->trx));
ut_a(((lock->trx)->conc_state == TRX_ACTIVE) ut_a(lock->trx->conc_state == TRX_ACTIVE
|| ((lock->trx)->conc_state == TRX_COMMITTED_IN_MEMORY)); || lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY);
for (i = nth_bit; i < lock_rec_get_n_bits(lock); i++) { for (i = nth_bit; i < lock_rec_get_n_bits(lock); i++) {
if ((i == 1) || lock_rec_get_nth_bit(lock, i)) { if (i == 1 || lock_rec_get_nth_bit(lock, i)) {
index = lock->index; index = lock->index;
rec = page_find_rec_with_heap_no(page, i); rec = page_find_rec_with_heap_no(page, i);
...@@ -3894,9 +3977,9 @@ lock_sec_rec_read_check_and_lock( ...@@ -3894,9 +3977,9 @@ lock_sec_rec_read_check_and_lock(
lock_mutex_enter_kernel(); lock_mutex_enter_kernel();
ut_ad((mode != LOCK_X) ut_ad(mode != LOCK_X
|| lock_table_has(thr_get_trx(thr), index->table, LOCK_IX)); || lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
ut_ad((mode != LOCK_S) ut_ad(mode != LOCK_S
|| lock_table_has(thr_get_trx(thr), index->table, LOCK_IS)); || lock_table_has(thr_get_trx(thr), index->table, LOCK_IS));
/* Some transaction may have an implicit x-lock on the record only /* Some transaction may have an implicit x-lock on the record only
...@@ -3956,9 +4039,9 @@ lock_clust_rec_read_check_and_lock( ...@@ -3956,9 +4039,9 @@ lock_clust_rec_read_check_and_lock(
lock_mutex_enter_kernel(); lock_mutex_enter_kernel();
ut_ad((mode != LOCK_X) ut_ad(mode != LOCK_X
|| lock_table_has(thr_get_trx(thr), index->table, LOCK_IX)); || lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));
ut_ad((mode != LOCK_S) ut_ad(mode != LOCK_S
|| lock_table_has(thr_get_trx(thr), index->table, LOCK_IS)); || lock_table_has(thr_get_trx(thr), index->table, LOCK_IS));
if (!page_rec_is_supremum(rec)) { if (!page_rec_is_supremum(rec)) {
......
...@@ -16,6 +16,8 @@ Created 9/8/1995 Heikki Tuuri ...@@ -16,6 +16,8 @@ Created 9/8/1995 Heikki Tuuri
#include <windows.h> #include <windows.h>
#endif #endif
#include "srv0srv.h"
/********************************************************************* /*********************************************************************
Returns the thread identifier of current thread. */ Returns the thread identifier of current thread. */
...@@ -70,6 +72,15 @@ os_thread_create( ...@@ -70,6 +72,15 @@ os_thread_create(
thread_id); thread_id);
ut_a(thread); ut_a(thread);
if (srv_set_thread_priorities) {
/* Set created thread priority the same as a normal query
in MYSQL: we try to prevent starvation of threads by
assigning same priority QUERY_PRIOR to all */
ut_a(SetThreadPriority(thread, srv_query_thread_priority));
}
return(thread); return(thread);
#else #else
int ret; int ret;
...@@ -82,6 +93,11 @@ os_thread_create( ...@@ -82,6 +93,11 @@ os_thread_create(
pthread_attr_destroy(&attr); pthread_attr_destroy(&attr);
if (srv_set_thread_priorities) {
my_pthread_setprio(pthread, srv_query_thread_priority);
}
return(pthread); return(pthread);
#endif #endif
} }
......
...@@ -284,15 +284,15 @@ ibool ...@@ -284,15 +284,15 @@ ibool
row_ins_dupl_error_with_rec( row_ins_dupl_error_with_rec(
/*========================*/ /*========================*/
/* out: TRUE if error */ /* out: TRUE if error */
rec_t* rec, /* in: user record */ rec_t* rec, /* in: user record; NOTE that we assume
that the caller already has a record lock on
the record! */
dtuple_t* entry, /* in: entry to insert */ dtuple_t* entry, /* in: entry to insert */
dict_index_t* index, /* in: index */ dict_index_t* index) /* in: index */
trx_t* trx) /* in: inserting transaction */
{ {
ulint matched_fields; ulint matched_fields;
ulint matched_bytes; ulint matched_bytes;
ulint n_unique; ulint n_unique;
trx_t* impl_trx;
n_unique = dict_index_get_n_unique(index); n_unique = dict_index_get_n_unique(index);
...@@ -311,46 +311,55 @@ row_ins_dupl_error_with_rec( ...@@ -311,46 +311,55 @@ row_ins_dupl_error_with_rec(
return(TRUE); return(TRUE);
} }
/* If we get here, the record has its delete mark set. It is still return(FALSE);
a unique key violation if the transaction which set the delete mark }
is currently active and is not trx itself. We check if some
transaction has an implicit x-lock on the record. */
mutex_enter(&kernel_mutex); /*************************************************************************
Sets a shared lock on a record. Used in locking possible duplicate key
records. */
static
ulint
row_ins_set_shared_rec_lock(
/*========================*/
/* out: DB_SUCCESS or error code */
rec_t* rec, /* in: record */
dict_index_t* index, /* in: index */
que_thr_t* thr) /* in: query thread */
{
ulint err;
if (index->type & DICT_CLUSTERED) { if (index->type & DICT_CLUSTERED) {
impl_trx = lock_clust_rec_some_has_impl(rec, index); err = lock_clust_rec_read_check_and_lock(0, rec, index, LOCK_S,
thr);
} else { } else {
impl_trx = lock_sec_rec_some_has_impl_off_kernel(rec, index); err = lock_sec_rec_read_check_and_lock(0, rec, index, LOCK_S,
thr);
} }
mutex_exit(&kernel_mutex); return(err);
}
if (impl_trx && impl_trx != trx) {
return(TRUE);
}
return(FALSE);
}
/******************************************************************* /*******************************************************************
Scans a unique non-clustered index at a given index entry to determine 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. */ whether a uniqueness violation has occurred for the key value of the entry.
Set shared locks on possible duplicate records. */
static static
ulint ulint
row_ins_scan_sec_index_for_duplicate( row_ins_scan_sec_index_for_duplicate(
/*=================================*/ /*=================================*/
/* out: DB_SUCCESS or DB_DUPLICATE_KEY */ /* out: DB_SUCCESS, DB_DUPLICATE_KEY, or
DB_LOCK_WAIT */
dict_index_t* index, /* in: non-clustered unique index */ dict_index_t* index, /* in: non-clustered unique index */
dtuple_t* entry, /* in: index entry */ dtuple_t* entry, /* in: index entry */
trx_t* trx) /* in: inserting transaction */ que_thr_t* thr) /* in: query thread */
{ {
ulint dupl_count = 0;
int cmp; int cmp;
ulint n_fields_cmp; ulint n_fields_cmp;
rec_t* rec; rec_t* rec;
btr_pcur_t pcur; btr_pcur_t pcur;
trx_t* trx = thr_get_trx(thr);
ulint err = DB_SUCCESS;
ibool moved;
mtr_t mtr; mtr_t mtr;
mtr_start(&mtr); mtr_start(&mtr);
...@@ -361,32 +370,45 @@ row_ins_scan_sec_index_for_duplicate( ...@@ -361,32 +370,45 @@ row_ins_scan_sec_index_for_duplicate(
dtuple_set_n_fields_cmp(entry, dict_index_get_n_unique(index)); dtuple_set_n_fields_cmp(entry, dict_index_get_n_unique(index));
btr_pcur_open_on_user_rec(index, entry, PAGE_CUR_GE, btr_pcur_open(index, entry, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr);
BTR_SEARCH_LEAF, &pcur, &mtr);
/* Scan index records and check that there are no duplicates */ /* Scan index records and check if there is a duplicate */
for (;;) { for (;;) {
if (btr_pcur_is_after_last_in_tree(&pcur, &mtr)) { 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, index, thr);
if (err != DB_SUCCESS) {
break; break;
} }
rec = btr_pcur_get_rec(&pcur); if (rec == page_get_supremum_rec(buf_frame_align(rec))) {
goto next_rec;
}
cmp = cmp_dtuple_rec(entry, rec); cmp = cmp_dtuple_rec(entry, rec);
if (cmp == 0) { if (cmp == 0) {
if (row_ins_dupl_error_with_rec(rec, entry, index, if (row_ins_dupl_error_with_rec(rec, entry, index)) {
trx)) { /* printf("Duplicate key in index %s\n",
dupl_count++;
if (dupl_count > 1) {
/* printf(
"Duplicate key in index %s\n",
index->name); index->name);
dtuple_print(entry); */ dtuple_print(entry); */
}
err = DB_DUPLICATE_KEY;
trx->error_info = index;
break;
} }
} }
...@@ -395,8 +417,12 @@ row_ins_scan_sec_index_for_duplicate( ...@@ -395,8 +417,12 @@ row_ins_scan_sec_index_for_duplicate(
} }
ut_a(cmp == 0); ut_a(cmp == 0);
next_rec:
moved = btr_pcur_move_to_next(&pcur, &mtr);
btr_pcur_move_to_next_user_rec(&pcur, &mtr); if (!moved) {
break;
}
} }
mtr_commit(&mtr); mtr_commit(&mtr);
...@@ -404,44 +430,35 @@ row_ins_scan_sec_index_for_duplicate( ...@@ -404,44 +430,35 @@ row_ins_scan_sec_index_for_duplicate(
/* Restore old value */ /* Restore old value */
dtuple_set_n_fields_cmp(entry, n_fields_cmp); dtuple_set_n_fields_cmp(entry, n_fields_cmp);
ut_a(dupl_count >= 1); return(err);
if (dupl_count > 1) {
trx->error_info = index;
return(DB_DUPLICATE_KEY);
}
return(DB_SUCCESS);
} }
/******************************************************************* /*******************************************************************
Tries to check if a unique key violation error would occur at an index entry Checks if a unique key violation error would occur at an index entry
insert. */ insert. Sets shared locks on possible duplicate records. Works only
for a clustered index! */
static static
ulint ulint
row_ins_duplicate_error( row_ins_duplicate_error_in_clust(
/*====================*/ /*=============================*/
/* out: DB_SUCCESS if no error /* out: DB_SUCCESS if no error,
DB_DUPLICATE_KEY if error, DB_DUPLICATE_KEY if error, DB_LOCK_WAIT if we
DB_STRONG_FAIL if this is a non-clustered have to wait for a lock on a possible
index record and we cannot determine yet duplicate record */
if there will be an error: in this last
case we must call
row_ins_scan_sec_index_for_duplicate
AFTER the insertion of the record! */
btr_cur_t* cursor, /* in: B-tree cursor */ btr_cur_t* cursor, /* in: B-tree cursor */
dtuple_t* entry, /* in: entry to insert */ dtuple_t* entry, /* in: entry to insert */
trx_t* trx, /* in: inserting transaction */ que_thr_t* thr, /* in: query thread */
mtr_t* mtr, /* in: mtr */ mtr_t* mtr) /* in: mtr */
rec_t** dupl_rec)/* out: record with which duplicate error */
{ {
ulint err;
rec_t* rec; rec_t* rec;
page_t* page; page_t* page;
ulint n_unique; ulint n_unique;
trx_t* trx = thr_get_trx(thr);
UT_NOT_USED(mtr); UT_NOT_USED(mtr);
ut_a(cursor->index->type & DICT_CLUSTERED);
ut_ad(cursor->index->type & DICT_UNIQUE); ut_ad(cursor->index->type & DICT_UNIQUE);
/* NOTE: For unique non-clustered indexes there may be any number /* NOTE: For unique non-clustered indexes there may be any number
...@@ -466,9 +483,20 @@ row_ins_duplicate_error( ...@@ -466,9 +483,20 @@ row_ins_duplicate_error(
if (rec != page_get_infimum_rec(page)) { if (rec != page_get_infimum_rec(page)) {
/* We set a lock on the possible duplicate: this
is needed in logical logging of MySQL to make
sure that in roll-forward we get the same duplicate
errors as in original execution */
err = row_ins_set_shared_rec_lock(rec, cursor->index,
thr);
if (err != DB_SUCCESS) {
return(err);
}
if (row_ins_dupl_error_with_rec(rec, entry, if (row_ins_dupl_error_with_rec(rec, entry,
cursor->index, trx)) { cursor->index)) {
*dupl_rec = rec;
trx->error_info = cursor->index; trx->error_info = cursor->index;
return(DB_DUPLICATE_KEY); return(DB_DUPLICATE_KEY);
...@@ -483,9 +511,15 @@ row_ins_duplicate_error( ...@@ -483,9 +511,15 @@ row_ins_duplicate_error(
if (rec != page_get_supremum_rec(page)) { if (rec != page_get_supremum_rec(page)) {
err = row_ins_set_shared_rec_lock(rec, cursor->index,
thr);
if (err != DB_SUCCESS) {
return(err);
}
if (row_ins_dupl_error_with_rec(rec, entry, if (row_ins_dupl_error_with_rec(rec, entry,
cursor->index, trx)) { cursor->index)) {
*dupl_rec = rec;
trx->error_info = cursor->index; trx->error_info = cursor->index;
return(DB_DUPLICATE_KEY); return(DB_DUPLICATE_KEY);
...@@ -496,15 +530,7 @@ row_ins_duplicate_error( ...@@ -496,15 +530,7 @@ row_ins_duplicate_error(
/* This should never happen */ /* This should never happen */
} }
if (cursor->index->type & DICT_CLUSTERED) { return(DB_SUCCESS);
return(DB_SUCCESS);
}
/* It was a non-clustered index: we must scan the index after the
insertion to be sure if there will be duplicate key error */
return(DB_STRONG_FAIL);
} }
/******************************************************************* /*******************************************************************
...@@ -574,18 +600,15 @@ row_ins_index_entry_low( ...@@ -574,18 +600,15 @@ row_ins_index_entry_low(
que_thr_t* thr) /* in: query thread */ que_thr_t* thr) /* in: query thread */
{ {
btr_cur_t cursor; btr_cur_t cursor;
ulint dupl = DB_SUCCESS;
ulint modify; ulint modify;
rec_t* dummy_rec; rec_t* dummy_rec;
rec_t* rec; rec_t* rec;
rec_t* dupl_rec; /* Note that this may be undefined
for a non-clustered index even if
there is a duplicate key */
ulint err; ulint err;
ulint n_unique; ulint n_unique;
mtr_t mtr; mtr_t mtr;
log_free_check(); log_free_check();
mtr_start(&mtr); mtr_start(&mtr);
cursor.thr = thr; cursor.thr = thr;
...@@ -611,19 +634,37 @@ row_ins_index_entry_low( ...@@ -611,19 +634,37 @@ row_ins_index_entry_low(
if (index->type & DICT_UNIQUE && (cursor.up_match >= n_unique if (index->type & DICT_UNIQUE && (cursor.up_match >= n_unique
|| cursor.low_match >= n_unique)) { || cursor.low_match >= n_unique)) {
dupl = row_ins_duplicate_error(&cursor, entry, if (index->type & DICT_CLUSTERED) {
thr_get_trx(thr), &mtr, &dupl_rec); /* Note that the following may return also
if (dupl == DB_DUPLICATE_KEY) { DB_LOCK_WAIT */
/* printf("Duplicate key in index %s lm %lu\n", err = row_ins_duplicate_error_in_clust(&cursor,
cursor->index->name, cursor->low_match); entry, thr, &mtr);
rec_print(rec); if (err != DB_SUCCESS) {
dtuple_print(entry); */
err = dupl; goto function_exit;
}
} else {
mtr_commit(&mtr);
err = row_ins_scan_sec_index_for_duplicate(index,
entry, thr);
mtr_start(&mtr);
goto function_exit; if (err != DB_SUCCESS) {
}
goto function_exit;
}
/* We did not find a duplicate and we have now
locked with s-locks the necessary records to
prevent any insertion of a duplicate by another
transaction. Let us now reposition the cursor and
continue the insertion. */
btr_cur_search_to_nth_level(index, 0, entry,
PAGE_CUR_LE, mode | BTR_INSERT,
&cursor, 0, &mtr);
}
} }
modify = row_ins_must_modify(&cursor); modify = row_ins_must_modify(&cursor);
...@@ -659,19 +700,6 @@ row_ins_index_entry_low( ...@@ -659,19 +700,6 @@ row_ins_index_entry_low(
function_exit: function_exit:
mtr_commit(&mtr); mtr_commit(&mtr);
if (err == DB_SUCCESS && dupl == DB_STRONG_FAIL) {
/* We were not able to determine before the insertion
whether there will be a duplicate key error: do the check
now */
err = row_ins_scan_sec_index_for_duplicate(index, entry,
thr_get_trx(thr));
}
ut_ad(err != DB_DUPLICATE_KEY || index->type & DICT_CLUSTERED
|| DB_DUPLICATE_KEY ==
row_ins_scan_sec_index_for_duplicate(index, entry,
thr_get_trx(thr)));
return(err); return(err);
} }
......
...@@ -163,19 +163,14 @@ handle_new_error: ...@@ -163,19 +163,14 @@ handle_new_error:
trx->error_state = DB_SUCCESS; trx->error_state = DB_SUCCESS;
if (err == DB_DUPLICATE_KEY) { if (err == DB_DUPLICATE_KEY) {
if (savept) { if (savept) {
/* Roll back the latest, possibly incomplete /* Roll back the latest, possibly incomplete
insertion or update */ insertion or update */
trx_general_rollback_for_mysql(trx, TRUE, savept); trx_general_rollback_for_mysql(trx, TRUE, savept);
} }
} else if (err == DB_TOO_BIG_RECORD) { } else if (err == DB_TOO_BIG_RECORD) {
if (savept) { /* MySQL will roll back the latest SQL statement */
/* Roll back the latest, possibly incomplete
insertion or update */
trx_general_rollback_for_mysql(trx, TRUE, savept);
}
} else if (err == DB_LOCK_WAIT) { } else if (err == DB_LOCK_WAIT) {
timeout_expired = srv_suspend_mysql_thread(thr); timeout_expired = srv_suspend_mysql_thread(thr);
...@@ -193,19 +188,19 @@ handle_new_error: ...@@ -193,19 +188,19 @@ handle_new_error:
return(TRUE); return(TRUE);
} else if (err == DB_DEADLOCK) { } else if (err == DB_DEADLOCK) {
/* MySQL will roll back the latest SQL statement */
/* Roll back the whole transaction */
trx_general_rollback_for_mysql(trx, FALSE, NULL);
} else if (err == DB_OUT_OF_FILE_SPACE) { } else if (err == DB_OUT_OF_FILE_SPACE) {
/* MySQL will roll back the latest SQL statement */
/* Roll back the whole transaction */
trx_general_rollback_for_mysql(trx, FALSE, NULL);
} else if (err == DB_MUST_GET_MORE_FILE_SPACE) { } else if (err == DB_MUST_GET_MORE_FILE_SPACE) {
ut_a(0); /* TODO: print something to MySQL error log */ fprintf(stderr,
"InnoDB: The database cannot continue operation because of\n"
"InnoDB: lack of space. You must add a new data file to\n"
"InnoDB: my.cnf and restart the database.\n");
exit(1);
} else { } else {
ut_a(0); ut_a(0);
} }
...@@ -919,7 +914,7 @@ row_drop_table_for_mysql( ...@@ -919,7 +914,7 @@ row_drop_table_for_mysql(
char* str2; char* str2;
ulint len; ulint len;
char buf[10000]; char buf[10000];
retry:
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
ut_a(name != NULL); ut_a(name != NULL);
...@@ -997,20 +992,14 @@ retry: ...@@ -997,20 +992,14 @@ retry:
goto funct_exit; goto funct_exit;
} }
/* Check if there are any locks on the table: if yes, it cannot /* Remove any locks there are on the table or its records */
be dropped: we have to wait for the locks to be released */
lock_reset_all_on_table(table);
if (lock_is_on_table(table)) {
err = DB_TABLE_IS_BEING_USED;
goto funct_exit;
}
/* TODO: check that MySQL prevents users from accessing the table /* TODO: check that MySQL prevents users from accessing the table
after this function row_drop_table_for_mysql has been called: after this function row_drop_table_for_mysql has been called:
otherwise anyone with an open handle to the table could, for example, otherwise anyone with an open handle to the table could, for example,
come to read the table! */ come to read the table! Monty said that it prevents. */
trx->dict_operation = TRUE; trx->dict_operation = TRUE;
trx->table_id = table->id; trx->table_id = table->id;
...@@ -1041,12 +1030,6 @@ funct_exit: ...@@ -1041,12 +1030,6 @@ funct_exit:
que_graph_free(graph); que_graph_free(graph);
if (err == DB_TABLE_IS_BEING_USED) {
os_thread_sleep(200000);
goto retry;
}
return((int) err); return((int) err);
} }
......
...@@ -87,6 +87,9 @@ ibool srv_archive_recovery = 0; ...@@ -87,6 +87,9 @@ ibool srv_archive_recovery = 0;
dulint srv_archive_recovery_limit_lsn; dulint srv_archive_recovery_limit_lsn;
ulint srv_lock_wait_timeout = 1024 * 1024 * 1024; ulint srv_lock_wait_timeout = 1024 * 1024 * 1024;
ibool srv_set_thread_priorities = TRUE;
int srv_query_thread_priority = 0;
/*-------------------------------------------*/ /*-------------------------------------------*/
ulint srv_n_spin_wait_rounds = 20; ulint srv_n_spin_wait_rounds = 20;
ulint srv_spin_wait_delay = 5; ulint srv_spin_wait_delay = 5;
...@@ -1837,6 +1840,8 @@ srv_master_thread( ...@@ -1837,6 +1840,8 @@ srv_master_thread(
ulint n_pages_flushed; ulint n_pages_flushed;
ulint n_bytes_archived; ulint n_bytes_archived;
ulint i; ulint i;
time_t last_flush_time;
time_t current_time;
UT_NOT_USED(arg); UT_NOT_USED(arg);
...@@ -1861,6 +1866,12 @@ loop: ...@@ -1861,6 +1866,12 @@ loop:
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
os_thread_sleep(1000000); os_thread_sleep(1000000);
/* We flush the log once in a second even if no commit
is issued or the we have specified in my.cnf no flush
at transaction commit */
log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP);
if (srv_activity_count == old_activity_count) { if (srv_activity_count == old_activity_count) {
if (srv_print_thread_releases) { if (srv_print_thread_releases) {
...@@ -1877,10 +1888,19 @@ loop: ...@@ -1877,10 +1888,19 @@ loop:
n_pages_purged = 1; n_pages_purged = 1;
last_flush_time = time(NULL);
while (n_pages_purged) { while (n_pages_purged) {
n_pages_purged = trx_purge();
/* TODO: replace this by a check if we are running /* TODO: replace this by a check if we are running
out of file space! */ out of file space! */
n_pages_purged = trx_purge();
current_time = time(NULL);
if (difftime(current_time, last_flush_time) > 1) {
log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP);
last_flush_time = current_time;
}
} }
background_loop: background_loop:
......
...@@ -113,6 +113,65 @@ io_handler_thread( ...@@ -113,6 +113,65 @@ io_handler_thread(
#endif #endif
} }
#ifdef __WIN__
#define SRV_PATH_SEPARATOR "\\"
#else
#define SRV_PATH_SEPARATOR "/"
#endif
/*************************************************************************
Normalizes a directory path for Windows: converts slashes to backslashes. */
static
void
srv_normalize_path_for_win(
/*=======================*/
char* str) /* in/out: null-terminated character string */
{
#ifdef __WIN__
ulint i;
for (i = 0; i < ut_strlen(str); i++) {
if (str[i] == '/') {
str[i] = '\\';
}
}
#endif
}
/*************************************************************************
Adds a slash or a backslash to the end of a string if it is missing. */
static
char*
srv_add_path_separator_if_needed(
/*=============================*/
/* out, own: string which has the separator */
char* str) /* in: null-terminated character string */
{
char* out_str;
if (ut_strlen(str) == 0) {
out_str = ut_malloc(2);
sprintf(out_str, "%s", SRV_PATH_SEPARATOR);
return(out_str);
}
if (str[ut_strlen(str) - 1] == SRV_PATH_SEPARATOR[0]) {
out_str = ut_malloc(ut_strlen(str) + 1);
sprintf(out_str, "%s", str);
return(out_str);
}
out_str = ut_malloc(ut_strlen(str) + 2);
sprintf(out_str, "%s%s", str, SRV_PATH_SEPARATOR);
return(out_str);
}
/************************************************************************* /*************************************************************************
Creates or opens the log files. */ Creates or opens the log files. */
static static
...@@ -136,7 +195,11 @@ open_or_create_log_file( ...@@ -136,7 +195,11 @@ open_or_create_log_file(
UT_NOT_USED(create_new_db); UT_NOT_USED(create_new_db);
*log_file_created = FALSE; *log_file_created = FALSE;
srv_normalize_path_for_win(srv_log_group_home_dirs[k]);
srv_log_group_home_dirs[k] = srv_add_path_separator_if_needed(
srv_log_group_home_dirs[k]);
sprintf(name, "%s%s%lu", srv_log_group_home_dirs[k], "ib_logfile", i); sprintf(name, "%s%s%lu", srv_log_group_home_dirs[k], "ib_logfile", i);
files[i] = os_file_create(name, OS_FILE_CREATE, OS_FILE_NORMAL, &ret); files[i] = os_file_create(name, OS_FILE_CREATE, OS_FILE_NORMAL, &ret);
...@@ -258,7 +321,11 @@ open_or_create_data_files( ...@@ -258,7 +321,11 @@ open_or_create_data_files(
*create_new_db = FALSE; *create_new_db = FALSE;
srv_normalize_path_for_win(srv_data_home);
srv_data_home = srv_add_path_separator_if_needed(srv_data_home);
for (i = 0; i < srv_n_data_files; i++) { for (i = 0; i < srv_n_data_files; i++) {
srv_normalize_path_for_win(srv_data_file_names[i]);
sprintf(name, "%s%s", srv_data_home, srv_data_file_names[i]); sprintf(name, "%s%s", srv_data_home, srv_data_file_names[i]);
...@@ -525,6 +592,14 @@ innobase_start_or_create_for_mysql(void) ...@@ -525,6 +592,14 @@ innobase_start_or_create_for_mysql(void)
os_thread_create(io_handler_thread, n + i, thread_ids + i); os_thread_create(io_handler_thread, n + i, thread_ids + i);
} }
if (0 != ut_strcmp(srv_log_group_home_dirs[0], srv_arch_dir)) {
fprintf(stderr,
"InnoDB: Error: you must set the log group home dir in my.cnf the\n"
"InnoDB: same as log arch dir.\n");
return(DB_ERROR);
}
err = open_or_create_data_files(&create_new_db, err = open_or_create_data_files(&create_new_db,
&min_flushed_lsn, &min_arch_log_no, &min_flushed_lsn, &min_arch_log_no,
&max_flushed_lsn, &max_arch_log_no, &max_flushed_lsn, &max_arch_log_no,
...@@ -536,6 +611,9 @@ innobase_start_or_create_for_mysql(void) ...@@ -536,6 +611,9 @@ innobase_start_or_create_for_mysql(void)
return((int) err); return((int) err);
} }
srv_normalize_path_for_win(srv_arch_dir);
srv_arch_dir = srv_add_path_separator_if_needed(srv_arch_dir);
for (k = 0; k < srv_n_log_groups; k++) { for (k = 0; k < srv_n_log_groups; k++) {
for (i = 0; i < srv_n_log_files; i++) { for (i = 0; i < srv_n_log_files; i++) {
......
...@@ -64,6 +64,8 @@ trx_create( ...@@ -64,6 +64,8 @@ trx_create(
trx->n_mysql_tables_in_use = 0; trx->n_mysql_tables_in_use = 0;
trx->ignore_duplicates_in_insert = FALSE;
mutex_create(&(trx->undo_mutex)); mutex_create(&(trx->undo_mutex));
mutex_set_level(&(trx->undo_mutex), SYNC_TRX_UNDO); mutex_set_level(&(trx->undo_mutex), SYNC_TRX_UNDO);
......
...@@ -35,20 +35,21 @@ Innobase */ ...@@ -35,20 +35,21 @@ Innobase */
#define MAX_ULONG_BIT ((ulong) 1 << (sizeof(ulong)*8-1)) #define MAX_ULONG_BIT ((ulong) 1 << (sizeof(ulong)*8-1))
/* The following must be declared here so that we can handle SAFE_MUTEX */
pthread_mutex_t innobase_mutex;
#include "ha_innobase.h" #include "ha_innobase.h"
/* We must declare this here because we undef SAFE_MUTEX below */
pthread_mutex_t innobase_mutex;
/* Store MySQL definition of 'byte': in Linux it is char while Innobase /* Store MySQL definition of 'byte': in Linux it is char while Innobase
uses unsigned char */ uses unsigned char */
typedef byte mysql_byte; typedef byte mysql_byte;
#define INSIDE_HA_INNOBASE_CC
#ifdef SAFE_MUTEX #ifdef SAFE_MUTEX
#undef pthread_mutex_t #undef pthread_mutex_t
#endif #endif
#define INSIDE_HA_INNOBASE_CC
/* Include necessary Innobase headers */ /* Include necessary Innobase headers */
extern "C" { extern "C" {
#include "../innobase/include/univ.i" #include "../innobase/include/univ.i"
...@@ -97,6 +98,8 @@ ulong innobase_active_counter = 0; ...@@ -97,6 +98,8 @@ ulong innobase_active_counter = 0;
char* innobase_home = NULL; char* innobase_home = NULL;
char innodb_dummy_stmt_trx_handle = 'D';
static HASH innobase_open_tables; static HASH innobase_open_tables;
static mysql_byte* innobase_get_key(INNOBASE_SHARE *share,uint *length, static mysql_byte* innobase_get_key(INNOBASE_SHARE *share,uint *length,
...@@ -198,12 +201,13 @@ check_trx_exists( ...@@ -198,12 +201,13 @@ check_trx_exists(
thd->transaction.all.innobase_tid = trx; thd->transaction.all.innobase_tid = trx;
/* The execution of a single SQL statement is denoted by /* The execution of a single SQL statement is denoted by
a 'transaction' handle which is a NULL pointer: Innobase a 'transaction' handle which is a dummy pointer: Innobase
remembers internally where the latest SQL statement remembers internally where the latest SQL statement
started, and if error handling requires rolling back the started, and if error handling requires rolling back the
latest statement, Innobase does a rollback to a savepoint. */ latest statement, Innobase does a rollback to a savepoint. */
thd->transaction.stmt.innobase_tid = NULL; thd->transaction.stmt.innobase_tid =
(void*)&innodb_dummy_stmt_trx_handle;
} }
return(trx); return(trx);
...@@ -272,10 +276,14 @@ innobase_parse_data_file_paths_and_sizes(void) ...@@ -272,10 +276,14 @@ innobase_parse_data_file_paths_and_sizes(void)
size = strtoul(str, &endp, 10); size = strtoul(str, &endp, 10);
str = endp; str = endp;
if (*str != 'M') {
if ((*str != 'M') && (*str != 'G')) {
size = size / (1024 * 1024); size = size / (1024 * 1024);
} else { } else if (*str == 'G') {
size = size * 1024;
str++; str++;
} else {
str++;
} }
if (size == 0) { if (size == 0) {
...@@ -318,10 +326,14 @@ innobase_parse_data_file_paths_and_sizes(void) ...@@ -318,10 +326,14 @@ innobase_parse_data_file_paths_and_sizes(void)
size = strtoul(str, &endp, 10); size = strtoul(str, &endp, 10);
str = endp; str = endp;
if (*str != 'M') {
if ((*str != 'M') && (*str != 'G')) {
size = size / (1024 * 1024); size = size / (1024 * 1024);
} else { } else if (*str == 'G') {
size = size * 1024;
str++; str++;
} else {
str++;
} }
srv_data_file_names[i] = path; srv_data_file_names[i] = path;
...@@ -419,6 +431,13 @@ innobase_init(void) ...@@ -419,6 +431,13 @@ innobase_init(void)
DBUG_ENTER("innobase_init"); DBUG_ENTER("innobase_init");
if (specialflag & SPECIAL_NO_PRIOR) {
srv_set_thread_priorities = FALSE;
} else {
srv_set_thread_priorities = TRUE;
srv_query_thread_priority = QUERY_PRIOR;
}
/* Use current_dir if no paths are set */ /* Use current_dir if no paths are set */
current_dir[0]=FN_CURLIB; current_dir[0]=FN_CURLIB;
current_dir[1]=FN_LIBCHAR; current_dir[1]=FN_LIBCHAR;
...@@ -557,8 +576,9 @@ innobase_commit( ...@@ -557,8 +576,9 @@ innobase_commit(
trx = check_trx_exists(thd); trx = check_trx_exists(thd);
if (trx_handle) { if (trx_handle != (void*)&innodb_dummy_stmt_trx_handle) {
trx_commit_for_mysql(trx); trx_commit_for_mysql(trx);
trx_mark_sql_stat_end(trx);
} else { } else {
trx_mark_sql_stat_end(trx); trx_mark_sql_stat_end(trx);
} }
...@@ -585,9 +605,7 @@ innobase_rollback( ...@@ -585,9 +605,7 @@ innobase_rollback(
/* out: 0 or error number */ /* out: 0 or error number */
THD* thd, /* in: handle to the MySQL thread of the user THD* thd, /* in: handle to the MySQL thread of the user
whose transaction should be rolled back */ whose transaction should be rolled back */
void* trx_handle)/* in: Innobase trx handle or NULL: NULL means void* trx_handle)/* in: Innobase trx handle or a dummy stmt handle */
that the current SQL statement should be rolled
back */
{ {
int error = 0; int error = 0;
trx_t* trx; trx_t* trx;
...@@ -597,10 +615,11 @@ innobase_rollback( ...@@ -597,10 +615,11 @@ innobase_rollback(
trx = check_trx_exists(thd); trx = check_trx_exists(thd);
if (trx_handle) { if (trx_handle != (void*)&innodb_dummy_stmt_trx_handle) {
error = trx_rollback_for_mysql(trx); error = trx_rollback_for_mysql(trx);
} else { } else {
error = trx_rollback_last_sql_stat_for_mysql(trx); error = trx_rollback_last_sql_stat_for_mysql(trx);
trx_mark_sql_stat_end(trx);
} }
DBUG_RETURN(convert_error_code_to_mysql(error)); DBUG_RETURN(convert_error_code_to_mysql(error));
...@@ -618,7 +637,8 @@ innobase_close_connection( ...@@ -618,7 +637,8 @@ innobase_close_connection(
whose transaction should be rolled back */ whose transaction should be rolled back */
{ {
if (NULL != thd->transaction.all.innobase_tid) { if (NULL != thd->transaction.all.innobase_tid) {
trx_rollback_for_mysql((trx_t*)
(thd->transaction.all.innobase_tid));
trx_free_for_mysql((trx_t*) trx_free_for_mysql((trx_t*)
(thd->transaction.all.innobase_tid)); (thd->transaction.all.innobase_tid));
} }
...@@ -726,6 +746,8 @@ ha_innobase::open( ...@@ -726,6 +746,8 @@ ha_innobase::open(
user_thd = NULL; user_thd = NULL;
last_query_id = (ulong)-1;
if (!(share=get_share(name))) if (!(share=get_share(name)))
DBUG_RETURN(1); DBUG_RETURN(1);
...@@ -1229,6 +1251,11 @@ ha_innobase::write_row( ...@@ -1229,6 +1251,11 @@ ha_innobase::write_row(
update_timestamp(record + table->time_stamp - 1); update_timestamp(record + table->time_stamp - 1);
} }
if (last_query_id != user_thd->query_id) {
prebuilt->sql_stat_start = TRUE;
last_query_id = user_thd->query_id;
}
if (table->next_number_field && record == table->record[0]) { if (table->next_number_field && record == table->record[0]) {
/* Set the 'in_update_remember_pos' flag to FALSE to /* Set the 'in_update_remember_pos' flag to FALSE to
make sure all columns are fetched in the select done by make sure all columns are fetched in the select done by
...@@ -1255,8 +1282,17 @@ ha_innobase::write_row( ...@@ -1255,8 +1282,17 @@ ha_innobase::write_row(
build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW); build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW);
} }
if (user_thd->lex.sql_command == SQLCOM_INSERT
&& user_thd->lex.duplicates == DUP_IGNORE) {
prebuilt->trx->ignore_duplicates_in_insert = TRUE;
} else {
prebuilt->trx->ignore_duplicates_in_insert = FALSE;
}
error = row_insert_for_mysql((byte*) record, prebuilt); error = row_insert_for_mysql((byte*) record, prebuilt);
prebuilt->trx->ignore_duplicates_in_insert = FALSE;
error = convert_error_code_to_mysql(error); error = convert_error_code_to_mysql(error);
/* Tell Innobase server that there might be work for /* Tell Innobase server that there might be work for
...@@ -1441,6 +1477,11 @@ ha_innobase::update_row( ...@@ -1441,6 +1477,11 @@ ha_innobase::update_row(
DBUG_ENTER("ha_innobase::update_row"); DBUG_ENTER("ha_innobase::update_row");
if (last_query_id != user_thd->query_id) {
prebuilt->sql_stat_start = TRUE;
last_query_id = user_thd->query_id;
}
if (prebuilt->upd_node) { if (prebuilt->upd_node) {
uvect = prebuilt->upd_node->update; uvect = prebuilt->upd_node->update;
} else { } else {
...@@ -1485,6 +1526,11 @@ ha_innobase::delete_row( ...@@ -1485,6 +1526,11 @@ ha_innobase::delete_row(
DBUG_ENTER("ha_innobase::delete_row"); DBUG_ENTER("ha_innobase::delete_row");
if (last_query_id != user_thd->query_id) {
prebuilt->sql_stat_start = TRUE;
last_query_id = user_thd->query_id;
}
if (!prebuilt->upd_node) { if (!prebuilt->upd_node) {
row_get_prebuilt_update_vector(prebuilt); row_get_prebuilt_update_vector(prebuilt);
} }
...@@ -1590,6 +1636,11 @@ ha_innobase::index_read( ...@@ -1590,6 +1636,11 @@ ha_innobase::index_read(
DBUG_ENTER("index_read"); DBUG_ENTER("index_read");
statistic_increment(ha_read_key_count, &LOCK_status); statistic_increment(ha_read_key_count, &LOCK_status);
if (last_query_id != user_thd->query_id) {
prebuilt->sql_stat_start = TRUE;
last_query_id = user_thd->query_id;
}
index = prebuilt->index; index = prebuilt->index;
/* Note that if the select is used for an update, we always /* Note that if the select is used for an update, we always
...@@ -2622,7 +2673,6 @@ ha_innobase::update_table_comment( ...@@ -2622,7 +2673,6 @@ ha_innobase::update_table_comment(
return(str); return(str);
} }
/**************************************************************************** /****************************************************************************
Handling the shared INNOBASE_SHARE structure that is needed to provide table Handling the shared INNOBASE_SHARE structure that is needed to provide table
locking. locking.
...@@ -2697,12 +2747,18 @@ ha_innobase::store_lock( ...@@ -2697,12 +2747,18 @@ ha_innobase::store_lock(
{ {
row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
if (lock_type == TL_READ_WITH_SHARED_LOCKS) { if (lock_type == TL_READ_WITH_SHARED_LOCKS ||
/* This is a SELECT ... IN SHARE MODE */ lock_type == TL_READ_NO_INSERT) {
/* This is a SELECT ... IN SHARE MODE, or
we are doing a complex SQL statement like
INSERT INTO ... SELECT ... and the logical logging
requires the use of a locking read */
prebuilt->select_lock_type = LOCK_S; prebuilt->select_lock_type = LOCK_S;
} else { } else {
/* We set possible LOCK_X value in external_lock, not yet /* We set possible LOCK_X value in external_lock, not yet
here even if this would be SELECT ... FOR UPDATE */ here even if this would be SELECT ... FOR UPDATE */
prebuilt->select_lock_type = LOCK_NONE; prebuilt->select_lock_type = LOCK_NONE;
} }
......
...@@ -41,6 +41,8 @@ class ha_innobase: public handler ...@@ -41,6 +41,8 @@ class ha_innobase: public handler
THD* user_thd; /* the thread handle of the user THD* user_thd; /* the thread handle of the user
currently using the handle; this is currently using the handle; this is
set in external_lock function */ set in external_lock function */
ulong last_query_id; /* the latest query id where the
handle was used */
THR_LOCK_DATA lock; THR_LOCK_DATA lock;
INNOBASE_SHARE *share; INNOBASE_SHARE *share;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment