Below is the list of changes that have just been committed into a local
5.0 repository of heikki. When heikki does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet
1.1994 05/07/01 20:44:35 heikki@stripped +8 -0
Many files:
Fix Bug #3300 : if innodb_locks_unsafe_for_binlog is set, release locks on rows that
we do not UPDATE or DELETE
innobase/trx/trx0trx.c
1.57 05/07/01 20:43:58 heikki@stripped +2 -0
Fix Bug #3300 : if innodb_locks_unsafe_for_binlog is set, release locks on rows that
we do not UPDATE or DELETE
innobase/lock/lock0lock.c
1.63 05/07/01 20:42:11 heikki@stripped +32 -28
Fix Bug #3300 : if innodb_locks_unsafe_for_binlog is set, release locks on rows that
we do not UPDATE or DELETE
innobase/include/trx0trx.h
1.48 05/07/01 20:41:45 heikki@stripped +41 -2
Fix Bug #3300 : if innodb_locks_unsafe_for_binlog is set, release locks on rows that
we do not UPDATE or DELETE
innobase/include/row0mysql.h
1.43 05/07/01 20:41:44 heikki@stripped +14 -4
Fix Bug #3300 : if innodb_locks_unsafe_for_binlog is set, release locks on rows that
we do not UPDATE or DELETE
innobase/include/trx0trx.ic
1.3 05/07/01 20:41:40 heikki@stripped +48 -0
Fix Bug #3300 : if innodb_locks_unsafe_for_binlog is set, release locks on rows that
we do not UPDATE or DELETE
innobase/row/row0sel.c
1.93 05/07/01 20:41:27 heikki@stripped +71 -10
Fix Bug #3300 : if innodb_locks_unsafe_for_binlog is set, release locks on rows that
we do not UPDATE or DELETE
innobase/row/row0mysql.c
1.110 05/07/01 20:41:27 heikki@stripped +81 -26
Fix Bug #3300 : if innodb_locks_unsafe_for_binlog is set, release locks on rows that
we do not UPDATE or DELETE
sql/ha_innodb.cc
1.223 05/07/01 20:41:13 heikki@stripped +7 -3
Fix Bug #3300 : if innodb_locks_unsafe_for_binlog is set, release locks on rows that
we do not UPDATE or DELETE
# This is a BitKeeper patch. What follows are the unified diffs for the
# set of deltas contained in the patch. The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User: heikki
# Host: hundin.mysql.fi
# Root: /home/heikki/mysql-5.0
--- 1.42/innobase/include/row0mysql.h Wed Jun 8 15:45:47 2005
+++ 1.43/innobase/include/row0mysql.h Fri Jul 1 20:41:44 2005
@@ -243,17 +243,27 @@
the MySQL format */
row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL
handle */
-
/*************************************************************************
-Does an unlock of a row for MySQL. */
+This can only be used when srv_locks_unsafe_for_binlog is TRUE. Before
+calling this function we must use trx_reset_new_rec_lock_info() and
+trx_register_new_rec_lock() to store the information which new record locks
+really were set. This function removes a newly set lock under prebuilt->pcur,
+and also under prebuilt->clust_pcur. Currently, this is only used and tested
+in the case of an UPDATE or a DELETE statement, where the row lock is of the
+LOCK_X type.
+Thus, this implements a 'mini-rollback' that releases the latest record
+locks we set. */
int
row_unlock_for_mysql(
/*=================*/
/* out: error code or DB_SUCCESS */
- row_prebuilt_t* prebuilt); /* in: prebuilt struct in MySQL
+ row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL
handle */
-
+ ibool has_latches_on_recs);/* TRUE if called so that we have
+ the latches on the records under pcur
+ and clust_pcur, and we do not need to
+ reposition the cursors. */
/*************************************************************************
Creates an query graph node of 'update' type to be used in the MySQL
interface. */
--- 1.47/innobase/include/trx0trx.h Wed Jun 8 15:45:47 2005
+++ 1.48/innobase/include/trx0trx.h Fri Jul 1 20:41:45 2005
@@ -16,10 +16,39 @@
#include "que0types.h"
#include "mem0mem.h"
#include "read0types.h"
+#include "dict0types.h"
#include "trx0xa.h"
extern ulint trx_n_mysql_transactions;
+/*****************************************************************
+Resets the new record lock info in a transaction struct. */
+UNIV_INLINE
+void
+trx_reset_new_rec_lock_info(
+/*========================*/
+ trx_t* trx); /* in: transaction struct */
+/*****************************************************************
+Registers that we have set a new record lock on an index. This can only be
+called twice after calling trx_reset_new_rec_lock_info(), since we only have
+space to store 2 indexes! */
+UNIV_INLINE
+void
+trx_register_new_rec_lock(
+/*======================*/
+ trx_t* trx, /* in: transaction struct */
+ dict_index_t* index); /* in: trx sets a new record lock on this
+ index*/
+/*****************************************************************
+Checks if trx has set a new record lock on an index. */
+UNIV_INLINE
+ibool
+trx_new_rec_locks_contain(
+/*======================*/
+ /* out: TRUE if trx has set a new record lock
+ on index */
+ trx_t* trx, /* in: transaction struct */
+ dict_index_t* index); /* in: index */
/************************************************************************
Releases the search latch if trx has reserved it. */
@@ -495,8 +524,18 @@
lock_t* auto_inc_lock; /* possible auto-inc lock reserved by
the transaction; note that it is also
in the lock list trx_locks */
- ibool trx_create_lock;/* this is TRUE if we have created a
- new lock for a record accessed */
+ dict_index_t* new_rec_locks[2];/* these are normally NULL; if
+ srv_locks_unsafe_for_binlog is TRUE,
+ in a cursor search, if we set a new
+ record lock on an index, this is set
+ to point to the index; this is
+ used in releasing the locks under the
+ cursors if we are performing an UPDATE
+ and we determine after retrieving
+ the row that it does not need to be
+ locked; thus, these can be used to
+ implement a 'mini-rollback' that
+ releases the latest record locks */
UT_LIST_NODE_T(trx_t)
trx_list; /* list of transactions */
UT_LIST_NODE_T(trx_t)
--- 1.2/innobase/include/trx0trx.ic Fri Sep 20 05:18:37 2002
+++ 1.3/innobase/include/trx0trx.ic Fri Jul 1 20:41:40 2005
@@ -39,4 +39,52 @@
}
}
+/*****************************************************************
+Resets the new record lock info in a transaction struct. */
+UNIV_INLINE
+void
+trx_reset_new_rec_lock_info(
+/*========================*/
+ trx_t* trx) /* in: transaction struct */
+{
+ trx->new_rec_locks[0] = NULL;
+ trx->new_rec_locks[1] = NULL;
+}
+/*****************************************************************
+Registers that we have set a new record lock on an index. This can only be
+called twice after calling trx_reset_new_rec_lock_info(), since we only have
+space to store 2 indexes! */
+UNIV_INLINE
+void
+trx_register_new_rec_lock(
+/*======================*/
+ trx_t* trx, /* in: transaction struct */
+ dict_index_t* index) /* in: trx sets a new record lock on this
+ index*/
+{
+ if (trx->new_rec_locks[0] == NULL) {
+ trx->new_rec_locks[0] = index;
+
+ return;
+ }
+
+ ut_a(trx->new_rec_locks[1] == NULL);
+
+ trx->new_rec_locks[1] = index;
+}
+
+/*****************************************************************
+Checks if trx has set a new record lock on an index. */
+UNIV_INLINE
+ibool
+trx_new_rec_locks_contain(
+/*======================*/
+ /* out: TRUE if trx has set a new record lock
+ on index */
+ trx_t* trx, /* in: transaction struct */
+ dict_index_t* index) /* in: index */
+{
+ return(trx->new_rec_locks[0] == index
+ || trx->new_rec_locks[1] == index);
+}
--- 1.62/innobase/lock/lock0lock.c Tue Jun 21 07:36:01 2005
+++ 1.63/innobase/lock/lock0lock.c Fri Jul 1 20:42:11 2005
@@ -956,7 +956,7 @@
cause waits */
if ((lock_is_on_supremum || (type_mode & LOCK_GAP))
- && !(type_mode & LOCK_INSERT_INTENTION)) {
+ && !(type_mode & LOCK_INSERT_INTENTION)) {
/* Gap type locks without LOCK_INSERT_INTENTION flag
do not need to wait for anything. This is because
@@ -1765,10 +1765,7 @@
lock_rec_set_nth_bit(lock, heap_no);
HASH_INSERT(lock_t, hash, lock_sys->rec_hash,
- lock_rec_fold(space, page_no), lock);
- /* Note that we have create a new lock */
- trx->trx_create_lock = TRUE;
-
+ lock_rec_fold(space, page_no), lock);
if (type_mode & LOCK_WAIT) {
lock_set_lock_and_trx_wait(lock, trx);
@@ -1945,15 +1942,6 @@
if (similar_lock && !somebody_waits && !(type_mode & LOCK_WAIT)) {
- /* If the nth bit of a record lock is already set then we
- do not set a new lock bit, otherwice we set */
-
- if (lock_rec_get_nth_bit(similar_lock, heap_no)) {
- trx->trx_create_lock = FALSE;
- } else {
- trx->trx_create_lock = TRUE;
- }
-
lock_rec_set_nth_bit(similar_lock, heap_no);
return(similar_lock);
@@ -2005,11 +1993,14 @@
lock = lock_rec_get_first_on_page(rec);
trx = thr_get_trx(thr);
- trx->trx_create_lock = FALSE;
if (lock == NULL) {
if (!impl) {
lock_rec_create(mode, rec, index, trx);
+
+ if (srv_locks_unsafe_for_binlog) {
+ trx_register_new_rec_lock(trx, index);
+ }
}
return(TRUE);
@@ -2021,23 +2012,22 @@
}
if (lock->trx != trx
- || lock->type_mode != (mode | LOCK_REC)
- || lock_rec_get_n_bits(lock) <= heap_no) {
+ || lock->type_mode != (mode | LOCK_REC)
+ || lock_rec_get_n_bits(lock) <= heap_no) {
+
return(FALSE);
}
if (!impl) {
+ /* If the nth bit of the record lock is already set then we
+ do not set a new lock bit, otherwise we do set */
- /* If the nth bit of a record lock is already set then we
- do not set a new lock bit, otherwice we set */
-
- if (lock_rec_get_nth_bit(lock, heap_no)) {
- trx->trx_create_lock = FALSE;
- } else {
- trx->trx_create_lock = TRUE;
+ if (!lock_rec_get_nth_bit(lock, heap_no)) {
+ lock_rec_set_nth_bit(lock, heap_no);
+ if (srv_locks_unsafe_for_binlog) {
+ trx_register_new_rec_lock(trx, index);
+ }
}
-
- lock_rec_set_nth_bit(lock, heap_no);
}
return(TRUE);
@@ -2093,12 +2083,19 @@
enough already granted on the record, we have to wait. */
err = lock_rec_enqueue_waiting(mode, rec, index, thr);
+
+ if (srv_locks_unsafe_for_binlog) {
+ trx_register_new_rec_lock(trx, index);
+ }
} else {
if (!impl) {
/* Set the requested lock on the record */
lock_rec_add_to_queue(LOCK_REC | mode, rec, index,
trx);
+ if (srv_locks_unsafe_for_binlog) {
+ trx_register_new_rec_lock(trx, index);
+ }
}
err = DB_SUCCESS;
@@ -2436,8 +2433,15 @@
lock = lock_rec_get_first(rec);
+ /* If srv_locks_unsafe_for_binlog is TRUE, we do not want locks set
+ by an UPDATE or a DELETE to be inherited as gap type locks. But we
+ DO want S-locks set by a consistency constraint to be inherited also
+ then. */
+
while (lock != NULL) {
- if (!lock_rec_get_insert_intention(lock)) {
+ if (!lock_rec_get_insert_intention(lock)
+ && !(srv_locks_unsafe_for_binlog
+ && lock_get_mode(lock) == LOCK_X)) {
lock_rec_add_to_queue(LOCK_REC | lock_get_mode(lock)
| LOCK_GAP,
@@ -3069,7 +3073,7 @@
lock_rec_inherit_to_gap_if_gap_lock(rec, page_rec_get_next(rec));
lock_mutex_exit_kernel();
-}
+}
/*****************************************************************
Updates the lock table when a record is removed. */
--- 1.109/innobase/row/row0mysql.c Tue Jun 21 07:36:02 2005
+++ 1.110/innobase/row/row0mysql.c Fri Jul 1 20:41:27 2005
@@ -1429,51 +1429,106 @@
}
/*************************************************************************
-Does an unlock of a row for MySQL. */
+This can only be used when srv_locks_unsafe_for_binlog is TRUE. Before
+calling this function we must use trx_reset_new_rec_lock_info() and
+trx_register_new_rec_lock() to store the information which new record locks
+really were set. This function removes a newly set lock under prebuilt->pcur,
+and also under prebuilt->clust_pcur. Currently, this is only used and tested
+in the case of an UPDATE or a DELETE statement, where the row lock is of the
+LOCK_X type.
+Thus, this implements a 'mini-rollback' that releases the latest record
+locks we set. */
int
row_unlock_for_mysql(
/*=================*/
/* out: error code or DB_SUCCESS */
- row_prebuilt_t* prebuilt) /* in: prebuilt struct in MySQL
+ row_prebuilt_t* prebuilt, /* in: prebuilt struct in MySQL
handle */
+ ibool has_latches_on_recs)/* TRUE if called so that we have
+ the latches on the records under pcur
+ and clust_pcur, and we do not need to
+ reposition the cursors. */
{
- rec_t* rec;
- btr_pcur_t* cur = prebuilt->pcur;
+ dict_index_t* index;
+ btr_pcur_t* pcur = prebuilt->pcur;
+ btr_pcur_t* clust_pcur = prebuilt->clust_pcur;
trx_t* trx = prebuilt->trx;
+ rec_t* rec;
mtr_t mtr;
ut_ad(prebuilt && trx);
ut_ad(trx->mysql_thread_id == os_thread_get_curr_id());
-
+
+ if (!srv_locks_unsafe_for_binlog) {
+
+ fprintf(stderr,
+"InnoDB: Error: calling row_unlock_for_mysql though\n"
+"InnoDB: srv_locks_unsafe_for_binlog is FALSE.\n");
+
+ return(DB_SUCCESS);
+ }
+
trx->op_info = "unlock_row";
-
- if (srv_locks_unsafe_for_binlog) {
- if (trx->trx_create_lock == TRUE) {
- mtr_start(&mtr);
+ index = btr_pcur_get_btr_cur(pcur)->index;
+
+ if (index != NULL && trx_new_rec_locks_contain(trx, index)) {
+
+ mtr_start(&mtr);
- /* Restore a cursor position and find a record */
- btr_pcur_restore_position(BTR_SEARCH_LEAF, cur, &mtr);
- rec = btr_pcur_get_rec(cur);
-
- if (rec) {
-
- lock_rec_reset_and_release_wait(rec);
- } else {
- fputs("InnoDB: Error: "
- "Record for the lock not found\n",
- stderr);
- mem_analyze_corruption((byte*) trx);
- ut_error;
- }
+ /* Restore the cursor position and find the record */
+
+ if (!has_latches_on_recs) {
+ btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, &mtr);
+ }
+
+ rec = btr_pcur_get_rec(pcur);
+
+ mutex_enter(&kernel_mutex);
+
+ lock_rec_reset_and_release_wait(rec);
+
+ mutex_exit(&kernel_mutex);
- trx->trx_create_lock = FALSE;
- mtr_commit(&mtr);
+ mtr_commit(&mtr);
+
+ /* If the search was done through the clustered index, then
+ we have not used clust_pcur at all, and we must NOT try to
+ reset locks on clust_pcur. The values in clust_pcur may be
+ garbage! */
+
+ if (index->type & DICT_CLUSTERED) {
+
+ goto func_exit;
}
-
+ }
+
+ index = btr_pcur_get_btr_cur(clust_pcur)->index;
+
+ if (index != NULL && trx_new_rec_locks_contain(trx, index)) {
+
+ mtr_start(&mtr);
+
+ /* Restore the cursor position and find the record */
+
+ if (!has_latches_on_recs) {
+ btr_pcur_restore_position(BTR_SEARCH_LEAF, clust_pcur,
+ &mtr);
+ }
+
+ rec = btr_pcur_get_rec(pcur);
+
+ mutex_enter(&kernel_mutex);
+
+ lock_rec_reset_and_release_wait(rec);
+
+ mutex_exit(&kernel_mutex);
+
+ mtr_commit(&mtr);
}
+func_exit:
trx->op_info = "";
return(DB_SUCCESS);
--- 1.92/innobase/row/row0sel.c Mon Jun 20 11:06:12 2005
+++ 1.93/innobase/row/row0sel.c Fri Jul 1 20:41:27 2005
@@ -2784,6 +2784,10 @@
process the record the cursor is
now positioned on (i.e. we should
not go to the next record yet) */
+ ibool* same_user_rec, /* out: TRUE if we were able to restore
+ the cursor on a user record with the
+ same ordering prefix in in the
+ B-tree index */
ulint latch_mode, /* in: latch mode wished in
restoration */
btr_pcur_t* pcur, /* in: cursor whose position
@@ -2800,6 +2804,8 @@
success = btr_pcur_restore_position(latch_mode, pcur, mtr);
+ *same_user_rec = success;
+
if (relative_position == BTR_PCUR_ON) {
if (success) {
return(FALSE);
@@ -3064,10 +3070,12 @@
ulint cnt = 0;
#endif /* UNIV_SEARCH_DEBUG */
ulint next_offs;
+ ibool same_user_rec;
mtr_t mtr;
mem_heap_t* heap = NULL;
ulint offsets_[REC_OFFS_NORMAL_SIZE];
ulint* offsets = offsets_;
+
*offsets_ = (sizeof offsets_) / sizeof *offsets_;
ut_ad(index && pcur && search_tuple);
@@ -3138,6 +3146,14 @@
trx->search_latch_timeout = BTR_SEA_TIMEOUT;
}
+ /* Reset the new record lock info if we srv_locks_unsafe_for_binlog
+ is set. Then we are able to remove the record locks set here on an
+ individual row. */
+
+ if (srv_locks_unsafe_for_binlog) {
+ trx_reset_new_rec_lock_info(trx);
+ }
+
/*-------------------------------------------------------------*/
/* PHASE 1: Try to pop the row from the prefetch cache */
@@ -3396,8 +3412,9 @@
clust_index = dict_table_get_first_index(index->table);
if (UNIV_LIKELY(direction != 0)) {
- if (!sel_restore_position_for_mysql(BTR_SEARCH_LEAF, pcur,
- moves_up, &mtr)) {
+ if (!sel_restore_position_for_mysql(&same_user_rec,
+ BTR_SEARCH_LEAF,
+ pcur, moves_up, &mtr)) {
goto next_rec;
}
@@ -3659,7 +3676,7 @@
goto normal_return;
}
}
-
+
/* We are ready to look at a possible new index entry in the result
set: the cursor is now placed on a user record */
@@ -3679,6 +3696,7 @@
|| srv_locks_unsafe_for_binlog
|| (unique_search && !UNIV_UNLIKELY(rec_get_deleted_flag(
rec, page_rec_is_comp(rec))))) {
+
goto no_gap_lock;
} else {
lock_type = LOCK_ORDINARY;
@@ -3701,7 +3719,7 @@
&& dtuple_get_n_fields_cmp(search_tuple)
== dict_index_get_n_unique(index)
&& 0 == cmp_dtuple_rec(search_tuple, rec, offsets)) {
- no_gap_lock:
+no_gap_lock:
lock_type = LOCK_REC_NOT_GAP;
}
@@ -3764,6 +3782,7 @@
/* Get the clustered index record if needed */
index_rec = rec;
ut_ad(index != clust_index);
+
goto requires_clust_rec;
}
}
@@ -3773,6 +3792,15 @@
/* The record is delete-marked: we can skip it if this is
not a consistent read which might see an earlier version
of a non-clustered index record */
+
+ if (srv_locks_unsafe_for_binlog) {
+ /* No need to keep a lock on a delete-marked record
+ if we do not want to use next-key locking. */
+
+ row_unlock_for_mysql(prebuilt, TRUE);
+
+ trx_reset_new_rec_lock_info(trx);
+ }
goto next_rec;
}
@@ -3783,7 +3811,8 @@
index_rec = rec;
if (index != clust_index && prebuilt->need_to_access_clustered) {
- requires_clust_rec:
+
+requires_clust_rec:
/* Before and after this "if" block, "offsets" will be
related to "rec", which may be in a secondary index "index" or
the clustered index ("clust_index"). However, after this
@@ -3816,6 +3845,16 @@
/* The record is delete marked: we can skip it */
+ if (srv_locks_unsafe_for_binlog) {
+ /* No need to keep a lock on a delete-marked
+ record if we do not want to use next-key
+ locking. */
+
+ row_unlock_for_mysql(prebuilt, TRUE);
+
+ trx_reset_new_rec_lock_info(trx);
+ }
+
goto next_rec;
}
@@ -3908,7 +3947,7 @@
next_rec:
/*-------------------------------------------------------------*/
/* PHASE 5: Move the cursor to the next index record */
-
+
if (UNIV_UNLIKELY(mtr_has_extra_clust_latch)) {
/* We must commit mtr if we are moving to the next
non-clustered index record, because we could break the
@@ -3921,8 +3960,9 @@
mtr_has_extra_clust_latch = FALSE;
mtr_start(&mtr);
- if (sel_restore_position_for_mysql(BTR_SEARCH_LEAF, pcur,
- moves_up, &mtr)) {
+ if (sel_restore_position_for_mysql(&same_user_rec,
+ BTR_SEARCH_LEAF,
+ pcur, moves_up, &mtr)) {
#ifdef UNIV_SEARCH_DEBUG
cnt++;
#endif /* UNIV_SEARCH_DEBUG */
@@ -3976,8 +4016,29 @@
thr->lock_state = QUE_THR_LOCK_NOLOCK;
mtr_start(&mtr);
- sel_restore_position_for_mysql(BTR_SEARCH_LEAF, pcur,
- moves_up, &mtr);
+ sel_restore_position_for_mysql(&same_user_rec,
+ BTR_SEARCH_LEAF, pcur,
+ moves_up, &mtr);
+ if (srv_locks_unsafe_for_binlog && !same_user_rec) {
+ /* Since we were not able to restore the cursor
+ on the same user record, we cannot use
+ row_unlock_for_mysql() to unlock any records, and
+ we must thus reset the new rec lock info. Since
+ in lock0lock.c we have blocked the inheriting of gap
+ X-locks, we actually do not have any new record locks
+ set in this case.
+
+ Note that if we were able to restore on the 'same'
+ user record, it is still possible that we were actually
+ waiting on a delete-marked record, and meanwhile
+ it was removed by purge and inserted again by some
+ other user. But that is no problem, because in
+ rec_loop we will again try to set a lock, and
+ new_rec_lock_info in trx will be right at the end. */
+
+ trx_reset_new_rec_lock_info(trx);
+ }
+
mode = pcur->search_mode;
goto rec_loop;
--- 1.56/innobase/trx/trx0trx.c Tue Jun 21 07:36:02 2005
+++ 1.57/innobase/trx/trx0trx.c Fri Jul 1 20:43:58 2005
@@ -166,6 +166,8 @@
memset(&trx->xid, 0, sizeof(trx->xid));
trx->xid.formatID = -1;
+ trx_reset_new_rec_lock_info(trx);
+
return(trx);
}
--- 1.222/sql/ha_innodb.cc Wed Jun 29 20:52:04 2005
+++ 1.223/sql/ha_innodb.cc Fri Jul 1 20:41:13 2005
@@ -3538,7 +3538,9 @@
}
/**************************************************************************
-Deletes a lock set to a row */
+Removes a new lock set on a row. This can be called after a row has been read
+in the processing of an UPDATE or a DELETE query, if the option
+innodb_locks_unsafe_for_binlog is set. */
void
ha_innobase::unlock_row(void)
@@ -3556,8 +3558,10 @@
mem_analyze_corruption((byte *) prebuilt->trx);
ut_error;
}
-
- row_unlock_for_mysql(prebuilt);
+
+ if (srv_locks_unsafe_for_binlog) {
+ row_unlock_for_mysql(prebuilt, FALSE);
+ }
}
/**********************************************************************
| Thread |
|---|
| • bk commit into 5.0 tree (heikki:1.1994) BUG#3300 | Heikki Tuuri | 1 Jul |