mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
Implement MySQL framework to support consistent read views in
cursors for InnoDB. The idea of the patch is that if MySQL requests a consistent read view, we open one when open a cursor, set is as the active view to a transaction when fetch from the cursor, and close together with cursor close. This patch is associated to bugs #11813, #11832, and #11833. Contains after review fixes.
This commit is contained in:
@ -69,6 +69,35 @@ read_view_print(
|
||||
/*============*/
|
||||
read_view_t* view); /* in: read view */
|
||||
|
||||
/*************************************************************************
|
||||
Create a consistent cursor view for mysql to be used in cursors. In this
|
||||
consistent read view modifications done by the creating transaction or future
|
||||
transactions are not visible. */
|
||||
|
||||
cursor_view_t*
|
||||
read_cursor_view_create_for_mysql(
|
||||
/*==============================*/
|
||||
trx_t* cr_trx);/* in: trx where cursor view is created */
|
||||
|
||||
/*************************************************************************
|
||||
Close a given consistent cursor view for and restore global read view
|
||||
back to a transaction. */
|
||||
|
||||
void
|
||||
read_cursor_view_close_for_mysql(
|
||||
/*=============================*/
|
||||
trx_t* trx, /* in: trx */
|
||||
cursor_view_t* curview); /* in: cursor view to be closed */
|
||||
/*************************************************************************
|
||||
This function sets a given consistent cursor view to a transaction
|
||||
read view if given consistent cursor view is not null. Otherwice, function
|
||||
restores a global read view to a transaction read view. */
|
||||
|
||||
void
|
||||
read_cursor_set_for_mysql(
|
||||
/*======================*/
|
||||
trx_t* trx, /* in: transaction where cursor is set */
|
||||
cursor_view_t* curview);/* in: consistent cursor view to be set */
|
||||
|
||||
/* Read view lists the trx ids of those transactions for which a consistent
|
||||
read should not see the modifications to the database. */
|
||||
@ -100,6 +129,17 @@ struct read_view_struct{
|
||||
/* List of read views in trx_sys */
|
||||
};
|
||||
|
||||
/* Implement InnoDB framework to support consistent read views in
|
||||
cursors. This struct holds both heap where consistent read view
|
||||
is allocated and pointer to a read view. */
|
||||
|
||||
struct cursor_view_struct{
|
||||
mem_heap_t* heap;
|
||||
/* Memory heap for the cursor view */
|
||||
read_view_t* read_view;
|
||||
/* Consistent read view of the cursor*/
|
||||
};
|
||||
|
||||
#ifndef UNIV_NONINL
|
||||
#include "read0read.ic"
|
||||
#endif
|
||||
|
@ -10,5 +10,6 @@ Created 2/16/1997 Heikki Tuuri
|
||||
#define read0types_h
|
||||
|
||||
typedef struct read_view_struct read_view_t;
|
||||
typedef struct cursor_view_struct cursor_view_t;
|
||||
|
||||
#endif
|
||||
|
@ -602,8 +602,19 @@ struct trx_struct{
|
||||
UT_LIST_BASE_NODE_T(lock_t)
|
||||
trx_locks; /* locks reserved by the transaction */
|
||||
/*------------------------------*/
|
||||
mem_heap_t* read_view_heap; /* memory heap for the read view */
|
||||
read_view_t* read_view; /* consistent read view or NULL */
|
||||
mem_heap_t* global_read_view_heap;
|
||||
/* memory heap for the global read
|
||||
view */
|
||||
read_view_t* global_read_view;
|
||||
/* consistent read view used in the
|
||||
transaction is stored here if
|
||||
transaction is using a consistent
|
||||
read view associated to a cursor */
|
||||
read_view_t* read_view; /* consistent read view used in the
|
||||
transaction or NULL, this read view
|
||||
can be normal read view associated
|
||||
to a transaction or read view
|
||||
associated to a cursor */
|
||||
/*------------------------------*/
|
||||
UT_LIST_BASE_NODE_T(trx_named_savept_t)
|
||||
trx_savepoints; /* savepoints set with SAVEPOINT ...,
|
||||
|
@ -212,15 +212,16 @@ read_view_close_for_mysql(
|
||||
/*======================*/
|
||||
trx_t* trx) /* in: trx which has a read view */
|
||||
{
|
||||
ut_a(trx->read_view);
|
||||
ut_a(trx->global_read_view);
|
||||
|
||||
mutex_enter(&kernel_mutex);
|
||||
|
||||
read_view_close(trx->read_view);
|
||||
read_view_close(trx->global_read_view);
|
||||
|
||||
mem_heap_empty(trx->read_view_heap);
|
||||
mem_heap_empty(trx->global_read_view_heap);
|
||||
|
||||
trx->read_view = NULL;
|
||||
trx->global_read_view = NULL;
|
||||
|
||||
mutex_exit(&kernel_mutex);
|
||||
}
|
||||
@ -258,3 +259,137 @@ read_view_print(
|
||||
(ulong) ut_dulint_get_low(read_view_get_nth_trx_id(view, i)));
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
Create a consistent cursor view for mysql to be used in cursors. In this
|
||||
consistent read view modifications done by the creating transaction or future
|
||||
transactions are not visible. */
|
||||
|
||||
cursor_view_t*
|
||||
read_cursor_view_create_for_mysql(
|
||||
/*==============================*/
|
||||
trx_t* cr_trx) /* in: trx where cursor view is created */
|
||||
{
|
||||
cursor_view_t* curview;
|
||||
read_view_t* view;
|
||||
mem_heap_t* heap;
|
||||
trx_t* trx;
|
||||
ulint n;
|
||||
|
||||
ut_a(cr_trx);
|
||||
|
||||
/* Use larger heap than in trx_create when creating a read_view
|
||||
because cursors are quite long. */
|
||||
|
||||
heap = mem_heap_create(512);
|
||||
|
||||
curview = (cursor_view_t*) mem_heap_alloc(heap, sizeof(cursor_view_t));
|
||||
curview->heap = heap;
|
||||
|
||||
mutex_enter(&kernel_mutex);
|
||||
|
||||
curview->read_view = read_view_create_low(
|
||||
UT_LIST_GET_LEN(trx_sys->trx_list),
|
||||
curview->heap);
|
||||
|
||||
view = curview->read_view;
|
||||
view->creator = cr_trx;
|
||||
|
||||
/* No future transactions should be visible in the view */
|
||||
|
||||
view->low_limit_no = trx_sys->max_trx_id;
|
||||
view->low_limit_id = view->low_limit_no;
|
||||
|
||||
view->can_be_too_old = FALSE;
|
||||
|
||||
n = 0;
|
||||
trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
|
||||
|
||||
/* No active transaction should be visible, not even cr_trx !*/
|
||||
|
||||
while (trx) {
|
||||
if (trx->conc_state == TRX_ACTIVE ||
|
||||
trx->conc_state == TRX_PREPARED) {
|
||||
|
||||
read_view_set_nth_trx_id(view, n, trx->id);
|
||||
|
||||
n++;
|
||||
|
||||
/* NOTE that a transaction whose trx number is <
|
||||
trx_sys->max_trx_id can still be active, if it is
|
||||
in the middle of its commit! Note that when a
|
||||
transaction starts, we initialize trx->no to
|
||||
ut_dulint_max. */
|
||||
|
||||
if (ut_dulint_cmp(view->low_limit_no, trx->no) > 0) {
|
||||
|
||||
view->low_limit_no = trx->no;
|
||||
}
|
||||
}
|
||||
|
||||
trx = UT_LIST_GET_NEXT(trx_list, trx);
|
||||
}
|
||||
|
||||
view->n_trx_ids = n;
|
||||
|
||||
if (n > 0) {
|
||||
/* The last active transaction has the smallest id: */
|
||||
view->up_limit_id = read_view_get_nth_trx_id(view, n - 1);
|
||||
} else {
|
||||
view->up_limit_id = view->low_limit_id;
|
||||
}
|
||||
|
||||
UT_LIST_ADD_FIRST(view_list, trx_sys->view_list, view);
|
||||
|
||||
mutex_exit(&kernel_mutex);
|
||||
|
||||
return(curview);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
Close a given consistent cursor view for and restore global read view
|
||||
back to a transaction. */
|
||||
|
||||
void
|
||||
read_cursor_view_close_for_mysql(
|
||||
/*=============================*/
|
||||
trx_t* trx, /* in: trx */
|
||||
cursor_view_t* curview)/* in: cursor view to be closed */
|
||||
{
|
||||
ut_a(curview);
|
||||
ut_a(curview->read_view);
|
||||
ut_a(curview->heap);
|
||||
|
||||
mutex_enter(&kernel_mutex);
|
||||
|
||||
read_view_close(curview->read_view);
|
||||
trx->read_view = trx->global_read_view;
|
||||
|
||||
mutex_exit(&kernel_mutex);
|
||||
|
||||
mem_heap_free(curview->heap);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
This function sets a given consistent cursor view to a transaction
|
||||
read view if given consistent cursor view is not null. Otherwice, function
|
||||
restores a global read view to a transaction read view. */
|
||||
|
||||
void
|
||||
read_cursor_set_for_mysql(
|
||||
/*======================*/
|
||||
trx_t* trx, /* in: transaction where cursor is set */
|
||||
cursor_view_t* curview)/* in: consistent cursor view to be set */
|
||||
{
|
||||
ut_a(trx);
|
||||
|
||||
mutex_enter(&kernel_mutex);
|
||||
|
||||
if (UNIV_LIKELY(curview != NULL)) {
|
||||
trx->read_view = curview->read_view;
|
||||
} else {
|
||||
trx->read_view = trx->global_read_view;
|
||||
}
|
||||
|
||||
mutex_exit(&kernel_mutex);
|
||||
}
|
||||
|
@ -4083,6 +4083,11 @@ normal_return:
|
||||
}
|
||||
|
||||
func_exit:
|
||||
/* Restore a global read view back to transaction. This forces
|
||||
MySQL always to set cursor view before fetch if it is used. */
|
||||
|
||||
trx->read_view = trx->global_read_view;
|
||||
|
||||
trx->op_info = "";
|
||||
if (UNIV_LIKELY_NULL(heap)) {
|
||||
mem_heap_free(heap);
|
||||
@ -4136,7 +4141,8 @@ row_search_check_if_query_cache_permitted(
|
||||
&& !trx->read_view) {
|
||||
|
||||
trx->read_view = read_view_open_now(trx,
|
||||
trx->read_view_heap);
|
||||
trx->global_read_view_heap);
|
||||
trx->global_read_view = trx->read_view;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,8 @@ trx_create(
|
||||
|
||||
trx->auto_inc_lock = NULL;
|
||||
|
||||
trx->read_view_heap = mem_heap_create(256);
|
||||
trx->global_read_view_heap = mem_heap_create(256);
|
||||
trx->global_read_view = NULL;
|
||||
trx->read_view = NULL;
|
||||
|
||||
/* Set X/Open XA transaction identification to NULL */
|
||||
@ -318,10 +319,12 @@ trx_free(
|
||||
|
||||
ut_a(UT_LIST_GET_LEN(trx->trx_locks) == 0);
|
||||
|
||||
if (trx->read_view_heap) {
|
||||
mem_heap_free(trx->read_view_heap);
|
||||
if (trx->global_read_view_heap) {
|
||||
mem_heap_free(trx->global_read_view_heap);
|
||||
}
|
||||
|
||||
trx->global_read_view = NULL;
|
||||
|
||||
ut_a(trx->read_view == NULL);
|
||||
|
||||
mem_free(trx);
|
||||
@ -831,10 +834,23 @@ trx_commit_off_kernel(
|
||||
lock_release_off_kernel(trx);
|
||||
|
||||
if (trx->read_view) {
|
||||
/* If transaction has a global read view this case
|
||||
means that transaction has been using a consistent
|
||||
read view associated to a cursor. Only the global
|
||||
read view associated to a transaction is closed
|
||||
and read view is then removed from the transaction.
|
||||
If read view associated to a cursor is still used
|
||||
it must be re-registered to another transaction. */
|
||||
|
||||
if (UNIV_LIKELY_NULL(trx->global_read_view)) {
|
||||
trx->read_view = trx->global_read_view;
|
||||
}
|
||||
|
||||
read_view_close(trx->read_view);
|
||||
|
||||
mem_heap_empty(trx->read_view_heap);
|
||||
mem_heap_empty(trx->global_read_view_heap);
|
||||
trx->read_view = NULL;
|
||||
trx->global_read_view = NULL;
|
||||
}
|
||||
|
||||
if (must_flush_log) {
|
||||
@ -964,7 +980,9 @@ trx_assign_read_view(
|
||||
mutex_enter(&kernel_mutex);
|
||||
|
||||
if (!trx->read_view) {
|
||||
trx->read_view = read_view_open_now(trx, trx->read_view_heap);
|
||||
trx->read_view = read_view_open_now(trx,
|
||||
trx->global_read_view_heap);
|
||||
trx->global_read_view = trx->read_view;
|
||||
}
|
||||
|
||||
mutex_exit(&kernel_mutex);
|
||||
|
@ -5879,7 +5879,7 @@ ha_innobase::start_stmt(
|
||||
innobase_release_stat_resources(trx);
|
||||
|
||||
if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
|
||||
&& trx->read_view) {
|
||||
&& trx->global_read_view) {
|
||||
/* At low transaction isolation levels we let
|
||||
each consistent read set its own snapshot */
|
||||
|
||||
@ -6100,7 +6100,7 @@ ha_innobase::external_lock(
|
||||
}
|
||||
} else {
|
||||
if (trx->isolation_level <= TRX_ISO_READ_COMMITTED
|
||||
&& trx->read_view) {
|
||||
&& trx->global_read_view) {
|
||||
|
||||
/* At low transaction isolation levels we let
|
||||
each consistent read set its own snapshot */
|
||||
@ -7130,4 +7130,45 @@ innobase_rollback_by_xid(
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
This function creates a consistent view for a cursor and start a transaction
|
||||
if it has not been started. This consistent view is then used inside of MySQL
|
||||
when accesing records using a cursor. */
|
||||
|
||||
void*
|
||||
innobase_create_cursor_view(void)
|
||||
/*=============================*/
|
||||
/* out: Pointer to cursor view or NULL */
|
||||
{
|
||||
return(read_cursor_view_create_for_mysql(
|
||||
check_trx_exists(current_thd)));
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
This function closes the given consistent cursor view. Note that
|
||||
global read view is restored to a transaction and a transaction is
|
||||
started if it has not been started. */
|
||||
|
||||
void
|
||||
innobase_close_cursor_view(
|
||||
/*=======================*/
|
||||
void* curview)/* in: Consistent read view to be closed */
|
||||
{
|
||||
read_cursor_view_close_for_mysql(check_trx_exists(current_thd),
|
||||
(cursor_view_t*) curview);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
This function sets the given consistent cursor view to a transaction.
|
||||
If a transaction does not exist, transaction is started. */
|
||||
|
||||
void
|
||||
innobase_set_cursor_view(
|
||||
/*=====================*/
|
||||
void* curview)/* in: Consistent cursor view to be closed */
|
||||
{
|
||||
read_cursor_set_for_mysql(check_trx_exists(current_thd),
|
||||
(cursor_view_t*) curview);
|
||||
}
|
||||
|
||||
#endif /* HAVE_INNOBASE_DB */
|
||||
|
@ -310,3 +310,32 @@ int innobase_xa_end(THD *thd);
|
||||
|
||||
int innobase_repl_report_sent_binlog(THD *thd, char *log_file_name,
|
||||
my_off_t end_offset);
|
||||
|
||||
/***********************************************************************
|
||||
This function creates a consistent view for a cursor and start a transaction
|
||||
if it has not been started. This consistent view is then used inside of MySQL
|
||||
when accesing records using a cursor. */
|
||||
|
||||
void*
|
||||
innobase_create_cursor_view(void);
|
||||
/*=============================*/
|
||||
/* out: Pointer to cursor view or NULL */
|
||||
|
||||
/***********************************************************************
|
||||
This function closes the given consistent cursor view. Note that
|
||||
global read view is restored to a transaction and a transaction is
|
||||
started if it has not been started. */
|
||||
|
||||
void
|
||||
innobase_close_cursor_view(
|
||||
/*=======================*/
|
||||
void* curview); /* in: Consistent read view to be closed */
|
||||
|
||||
/***********************************************************************
|
||||
This function sets the given consistent cursor view to a transaction.
|
||||
If a transaction does not exist, transaction is started. */
|
||||
|
||||
void
|
||||
innobase_set_cursor_view(
|
||||
/*=====================*/
|
||||
void* curview); /* in: Consistent read view to be closed */
|
||||
|
Reference in New Issue
Block a user