summaryrefslogtreecommitdiff
path: root/storage/xtradb/fut/fut0lst.c
diff options
context:
space:
mode:
Diffstat (limited to 'storage/xtradb/fut/fut0lst.c')
-rw-r--r--storage/xtradb/fut/fut0lst.c529
1 files changed, 529 insertions, 0 deletions
diff --git a/storage/xtradb/fut/fut0lst.c b/storage/xtradb/fut/fut0lst.c
new file mode 100644
index 00000000000..bea27ab70d1
--- /dev/null
+++ b/storage/xtradb/fut/fut0lst.c
@@ -0,0 +1,529 @@
+/*****************************************************************************
+
+Copyright (c) 1995, 2009, Innobase Oy. All Rights Reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; version 2 of the License.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place, Suite 330, Boston, MA 02111-1307 USA
+
+*****************************************************************************/
+
+/**********************************************************************
+File-based list utilities
+
+Created 11/28/1995 Heikki Tuuri
+***********************************************************************/
+
+#include "fut0lst.h"
+
+#ifdef UNIV_NONINL
+#include "fut0lst.ic"
+#endif
+
+#include "buf0buf.h"
+#include "page0page.h"
+
+/************************************************************************
+Adds a node to an empty list. */
+static
+void
+flst_add_to_empty(
+/*==============*/
+ flst_base_node_t* base, /* in: pointer to base node of
+ empty list */
+ flst_node_t* node, /* in: node to add */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ulint space;
+ fil_addr_t node_addr;
+ ulint len;
+
+ ut_ad(mtr && base && node);
+ ut_ad(base != node);
+ ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX));
+ ut_ad(mtr_memo_contains_page(mtr, node, MTR_MEMO_PAGE_X_FIX));
+ len = flst_get_len(base, mtr);
+ ut_a(len == 0);
+
+ buf_ptr_get_fsp_addr(node, &space, &node_addr);
+
+ /* Update first and last fields of base node */
+ flst_write_addr(base + FLST_FIRST, node_addr, mtr);
+ flst_write_addr(base + FLST_LAST, node_addr, mtr);
+
+ /* Set prev and next fields of node to add */
+ flst_write_addr(node + FLST_PREV, fil_addr_null, mtr);
+ flst_write_addr(node + FLST_NEXT, fil_addr_null, mtr);
+
+ /* Update len of base node */
+ mlog_write_ulint(base + FLST_LEN, len + 1, MLOG_4BYTES, mtr);
+}
+
+/************************************************************************
+Adds a node as the last node in a list. */
+UNIV_INTERN
+void
+flst_add_last(
+/*==========*/
+ flst_base_node_t* base, /* in: pointer to base node of list */
+ flst_node_t* node, /* in: node to add */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ulint space;
+ fil_addr_t node_addr;
+ ulint len;
+ fil_addr_t last_addr;
+ flst_node_t* last_node;
+
+ ut_ad(mtr && base && node);
+ ut_ad(base != node);
+ ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX));
+ ut_ad(mtr_memo_contains_page(mtr, node, MTR_MEMO_PAGE_X_FIX));
+ len = flst_get_len(base, mtr);
+ last_addr = flst_get_last(base, mtr);
+
+ buf_ptr_get_fsp_addr(node, &space, &node_addr);
+
+ /* If the list is not empty, call flst_insert_after */
+ if (len != 0) {
+ if (last_addr.page == node_addr.page) {
+ last_node = page_align(node) + last_addr.boffset;
+ } else {
+ ulint zip_size = fil_space_get_zip_size(space);
+
+ last_node = fut_get_ptr(space, zip_size, last_addr,
+ RW_X_LATCH, mtr);
+ }
+
+ flst_insert_after(base, last_node, node, mtr);
+ } else {
+ /* else call flst_add_to_empty */
+ flst_add_to_empty(base, node, mtr);
+ }
+}
+
+/************************************************************************
+Adds a node as the first node in a list. */
+UNIV_INTERN
+void
+flst_add_first(
+/*===========*/
+ flst_base_node_t* base, /* in: pointer to base node of list */
+ flst_node_t* node, /* in: node to add */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ulint space;
+ fil_addr_t node_addr;
+ ulint len;
+ fil_addr_t first_addr;
+ flst_node_t* first_node;
+
+ ut_ad(mtr && base && node);
+ ut_ad(base != node);
+ ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX));
+ ut_ad(mtr_memo_contains_page(mtr, node, MTR_MEMO_PAGE_X_FIX));
+ len = flst_get_len(base, mtr);
+ first_addr = flst_get_first(base, mtr);
+
+ buf_ptr_get_fsp_addr(node, &space, &node_addr);
+
+ /* If the list is not empty, call flst_insert_before */
+ if (len != 0) {
+ if (first_addr.page == node_addr.page) {
+ first_node = page_align(node) + first_addr.boffset;
+ } else {
+ ulint zip_size = fil_space_get_zip_size(space);
+
+ first_node = fut_get_ptr(space, zip_size, first_addr,
+ RW_X_LATCH, mtr);
+ }
+
+ flst_insert_before(base, node, first_node, mtr);
+ } else {
+ /* else call flst_add_to_empty */
+ flst_add_to_empty(base, node, mtr);
+ }
+}
+
+/************************************************************************
+Inserts a node after another in a list. */
+UNIV_INTERN
+void
+flst_insert_after(
+/*==============*/
+ flst_base_node_t* base, /* in: pointer to base node of list */
+ flst_node_t* node1, /* in: node to insert after */
+ flst_node_t* node2, /* in: node to add */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ulint space;
+ fil_addr_t node1_addr;
+ fil_addr_t node2_addr;
+ flst_node_t* node3;
+ fil_addr_t node3_addr;
+ ulint len;
+
+ ut_ad(mtr && node1 && node2 && base);
+ ut_ad(base != node1);
+ ut_ad(base != node2);
+ ut_ad(node2 != node1);
+ ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX));
+ ut_ad(mtr_memo_contains_page(mtr, node1, MTR_MEMO_PAGE_X_FIX));
+ ut_ad(mtr_memo_contains_page(mtr, node2, MTR_MEMO_PAGE_X_FIX));
+
+ buf_ptr_get_fsp_addr(node1, &space, &node1_addr);
+ buf_ptr_get_fsp_addr(node2, &space, &node2_addr);
+
+ node3_addr = flst_get_next_addr(node1, mtr);
+
+ /* Set prev and next fields of node2 */
+ flst_write_addr(node2 + FLST_PREV, node1_addr, mtr);
+ flst_write_addr(node2 + FLST_NEXT, node3_addr, mtr);
+
+ if (!fil_addr_is_null(node3_addr)) {
+ /* Update prev field of node3 */
+ ulint zip_size = fil_space_get_zip_size(space);
+
+ node3 = fut_get_ptr(space, zip_size,
+ node3_addr, RW_X_LATCH, mtr);
+ flst_write_addr(node3 + FLST_PREV, node2_addr, mtr);
+ } else {
+ /* node1 was last in list: update last field in base */
+ flst_write_addr(base + FLST_LAST, node2_addr, mtr);
+ }
+
+ /* Set next field of node1 */
+ flst_write_addr(node1 + FLST_NEXT, node2_addr, mtr);
+
+ /* Update len of base node */
+ len = flst_get_len(base, mtr);
+ mlog_write_ulint(base + FLST_LEN, len + 1, MLOG_4BYTES, mtr);
+}
+
+/************************************************************************
+Inserts a node before another in a list. */
+UNIV_INTERN
+void
+flst_insert_before(
+/*===============*/
+ flst_base_node_t* base, /* in: pointer to base node of list */
+ flst_node_t* node2, /* in: node to insert */
+ flst_node_t* node3, /* in: node to insert before */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ulint space;
+ flst_node_t* node1;
+ fil_addr_t node1_addr;
+ fil_addr_t node2_addr;
+ fil_addr_t node3_addr;
+ ulint len;
+
+ ut_ad(mtr && node2 && node3 && base);
+ ut_ad(base != node2);
+ ut_ad(base != node3);
+ ut_ad(node2 != node3);
+ ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX));
+ ut_ad(mtr_memo_contains_page(mtr, node2, MTR_MEMO_PAGE_X_FIX));
+ ut_ad(mtr_memo_contains_page(mtr, node3, MTR_MEMO_PAGE_X_FIX));
+
+ buf_ptr_get_fsp_addr(node2, &space, &node2_addr);
+ buf_ptr_get_fsp_addr(node3, &space, &node3_addr);
+
+ node1_addr = flst_get_prev_addr(node3, mtr);
+
+ /* Set prev and next fields of node2 */
+ flst_write_addr(node2 + FLST_PREV, node1_addr, mtr);
+ flst_write_addr(node2 + FLST_NEXT, node3_addr, mtr);
+
+ if (!fil_addr_is_null(node1_addr)) {
+ ulint zip_size = fil_space_get_zip_size(space);
+ /* Update next field of node1 */
+ node1 = fut_get_ptr(space, zip_size, node1_addr,
+ RW_X_LATCH, mtr);
+ flst_write_addr(node1 + FLST_NEXT, node2_addr, mtr);
+ } else {
+ /* node3 was first in list: update first field in base */
+ flst_write_addr(base + FLST_FIRST, node2_addr, mtr);
+ }
+
+ /* Set prev field of node3 */
+ flst_write_addr(node3 + FLST_PREV, node2_addr, mtr);
+
+ /* Update len of base node */
+ len = flst_get_len(base, mtr);
+ mlog_write_ulint(base + FLST_LEN, len + 1, MLOG_4BYTES, mtr);
+}
+
+/************************************************************************
+Removes a node. */
+UNIV_INTERN
+void
+flst_remove(
+/*========*/
+ flst_base_node_t* base, /* in: pointer to base node of list */
+ flst_node_t* node2, /* in: node to remove */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ulint space;
+ ulint zip_size;
+ flst_node_t* node1;
+ fil_addr_t node1_addr;
+ fil_addr_t node2_addr;
+ flst_node_t* node3;
+ fil_addr_t node3_addr;
+ ulint len;
+
+ ut_ad(mtr && node2 && base);
+ ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX));
+ ut_ad(mtr_memo_contains_page(mtr, node2, MTR_MEMO_PAGE_X_FIX));
+
+ buf_ptr_get_fsp_addr(node2, &space, &node2_addr);
+ zip_size = fil_space_get_zip_size(space);
+
+ node1_addr = flst_get_prev_addr(node2, mtr);
+ node3_addr = flst_get_next_addr(node2, mtr);
+
+ if (!fil_addr_is_null(node1_addr)) {
+
+ /* Update next field of node1 */
+
+ if (node1_addr.page == node2_addr.page) {
+
+ node1 = page_align(node2) + node1_addr.boffset;
+ } else {
+ node1 = fut_get_ptr(space, zip_size,
+ node1_addr, RW_X_LATCH, mtr);
+ }
+
+ ut_ad(node1 != node2);
+
+ flst_write_addr(node1 + FLST_NEXT, node3_addr, mtr);
+ } else {
+ /* node2 was first in list: update first field in base */
+ flst_write_addr(base + FLST_FIRST, node3_addr, mtr);
+ }
+
+ if (!fil_addr_is_null(node3_addr)) {
+ /* Update prev field of node3 */
+
+ if (node3_addr.page == node2_addr.page) {
+
+ node3 = page_align(node2) + node3_addr.boffset;
+ } else {
+ node3 = fut_get_ptr(space, zip_size,
+ node3_addr, RW_X_LATCH, mtr);
+ }
+
+ ut_ad(node2 != node3);
+
+ flst_write_addr(node3 + FLST_PREV, node1_addr, mtr);
+ } else {
+ /* node2 was last in list: update last field in base */
+ flst_write_addr(base + FLST_LAST, node1_addr, mtr);
+ }
+
+ /* Update len of base node */
+ len = flst_get_len(base, mtr);
+ ut_ad(len > 0);
+
+ mlog_write_ulint(base + FLST_LEN, len - 1, MLOG_4BYTES, mtr);
+}
+
+/************************************************************************
+Cuts off the tail of the list, including the node given. The number of
+nodes which will be removed must be provided by the caller, as this function
+does not measure the length of the tail. */
+UNIV_INTERN
+void
+flst_cut_end(
+/*=========*/
+ flst_base_node_t* base, /* in: pointer to base node of list */
+ flst_node_t* node2, /* in: first node to remove */
+ ulint n_nodes,/* in: number of nodes to remove,
+ must be >= 1 */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ ulint space;
+ flst_node_t* node1;
+ fil_addr_t node1_addr;
+ fil_addr_t node2_addr;
+ ulint len;
+
+ ut_ad(mtr && node2 && base);
+ ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX));
+ ut_ad(mtr_memo_contains_page(mtr, node2, MTR_MEMO_PAGE_X_FIX));
+ ut_ad(n_nodes > 0);
+
+ buf_ptr_get_fsp_addr(node2, &space, &node2_addr);
+
+ node1_addr = flst_get_prev_addr(node2, mtr);
+
+ if (!fil_addr_is_null(node1_addr)) {
+
+ /* Update next field of node1 */
+
+ if (node1_addr.page == node2_addr.page) {
+
+ node1 = page_align(node2) + node1_addr.boffset;
+ } else {
+ node1 = fut_get_ptr(space,
+ fil_space_get_zip_size(space),
+ node1_addr, RW_X_LATCH, mtr);
+ }
+
+ flst_write_addr(node1 + FLST_NEXT, fil_addr_null, mtr);
+ } else {
+ /* node2 was first in list: update the field in base */
+ flst_write_addr(base + FLST_FIRST, fil_addr_null, mtr);
+ }
+
+ flst_write_addr(base + FLST_LAST, node1_addr, mtr);
+
+ /* Update len of base node */
+ len = flst_get_len(base, mtr);
+ ut_ad(len >= n_nodes);
+
+ mlog_write_ulint(base + FLST_LEN, len - n_nodes, MLOG_4BYTES, mtr);
+}
+
+/************************************************************************
+Cuts off the tail of the list, not including the given node. The number of
+nodes which will be removed must be provided by the caller, as this function
+does not measure the length of the tail. */
+UNIV_INTERN
+void
+flst_truncate_end(
+/*==============*/
+ flst_base_node_t* base, /* in: pointer to base node of list */
+ flst_node_t* node2, /* in: first node not to remove */
+ ulint n_nodes,/* in: number of nodes to remove */
+ mtr_t* mtr) /* in: mini-transaction handle */
+{
+ fil_addr_t node2_addr;
+ ulint len;
+ ulint space;
+
+ ut_ad(mtr && node2 && base);
+ ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX));
+ ut_ad(mtr_memo_contains_page(mtr, node2, MTR_MEMO_PAGE_X_FIX));
+ if (n_nodes == 0) {
+
+ ut_ad(fil_addr_is_null(flst_get_next_addr(node2, mtr)));
+
+ return;
+ }
+
+ buf_ptr_get_fsp_addr(node2, &space, &node2_addr);
+
+ /* Update next field of node2 */
+ flst_write_addr(node2 + FLST_NEXT, fil_addr_null, mtr);
+
+ flst_write_addr(base + FLST_LAST, node2_addr, mtr);
+
+ /* Update len of base node */
+ len = flst_get_len(base, mtr);
+ ut_ad(len >= n_nodes);
+
+ mlog_write_ulint(base + FLST_LEN, len - n_nodes, MLOG_4BYTES, mtr);
+}
+
+/************************************************************************
+Validates a file-based list. */
+UNIV_INTERN
+ibool
+flst_validate(
+/*==========*/
+ /* out: TRUE if ok */
+ const flst_base_node_t* base, /* in: pointer to base node of list */
+ mtr_t* mtr1) /* in: mtr */
+{
+ ulint space;
+ ulint zip_size;
+ const flst_node_t* node;
+ fil_addr_t node_addr;
+ fil_addr_t base_addr;
+ ulint len;
+ ulint i;
+ mtr_t mtr2;
+
+ ut_ad(base);
+ ut_ad(mtr_memo_contains_page(mtr1, base, MTR_MEMO_PAGE_X_FIX));
+
+ /* We use two mini-transaction handles: the first is used to
+ lock the base node, and prevent other threads from modifying the
+ list. The second is used to traverse the list. We cannot run the
+ second mtr without committing it at times, because if the list
+ is long, then the x-locked pages could fill the buffer resulting
+ in a deadlock. */
+
+ /* Find out the space id */
+ buf_ptr_get_fsp_addr(base, &space, &base_addr);
+ zip_size = fil_space_get_zip_size(space);
+
+ len = flst_get_len(base, mtr1);
+ node_addr = flst_get_first(base, mtr1);
+
+ for (i = 0; i < len; i++) {
+ mtr_start(&mtr2);
+
+ node = fut_get_ptr(space, zip_size,
+ node_addr, RW_X_LATCH, &mtr2);
+ node_addr = flst_get_next_addr(node, &mtr2);
+
+ mtr_commit(&mtr2); /* Commit mtr2 each round to prevent buffer
+ becoming full */
+ }
+
+ ut_a(fil_addr_is_null(node_addr));
+
+ node_addr = flst_get_last(base, mtr1);
+
+ for (i = 0; i < len; i++) {
+ mtr_start(&mtr2);
+
+ node = fut_get_ptr(space, zip_size,
+ node_addr, RW_X_LATCH, &mtr2);
+ node_addr = flst_get_prev_addr(node, &mtr2);
+
+ mtr_commit(&mtr2); /* Commit mtr2 each round to prevent buffer
+ becoming full */
+ }
+
+ ut_a(fil_addr_is_null(node_addr));
+
+ return(TRUE);
+}
+
+/************************************************************************
+Prints info of a file-based list. */
+UNIV_INTERN
+void
+flst_print(
+/*=======*/
+ const flst_base_node_t* base, /* in: pointer to base node of list */
+ mtr_t* mtr) /* in: mtr */
+{
+ const buf_frame_t* frame;
+ ulint len;
+
+ ut_ad(base && mtr);
+ ut_ad(mtr_memo_contains_page(mtr, base, MTR_MEMO_PAGE_X_FIX));
+ frame = page_align((byte*) base);
+
+ len = flst_get_len(base, mtr);
+
+ fprintf(stderr,
+ "FILE-BASED LIST:\n"
+ "Base node in space %lu page %lu byte offset %lu; len %lu\n",
+ (ulong) page_get_space_id(frame),
+ (ulong) page_get_page_no(frame),
+ (ulong) page_offset(base), (ulong) len);
+}