polardbxengine/storage/innobase/include/btr0bulk.h

426 lines
13 KiB
C++

/*****************************************************************************
Copyright (c) 2014, 2019, Oracle and/or its affiliates. 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, version 2.0, as published by the
Free Software Foundation.
This program is also distributed with certain software (including but not
limited to OpenSSL) that is licensed under separate terms, as designated in a
particular file or component or in included license documentation. The authors
of MySQL hereby grant you an additional permission to link the program and
your derivative works with the separately licensed software that they have
included with MySQL.
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, version 2.0,
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.,
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*****************************************************************************/
/** @file include/btr0bulk.h
The B-tree bulk load
Created 03/11/2014 Shaohua Wang
*************************************************************************/
#ifndef btr0bulk_h
#define btr0bulk_h
#include <stddef.h>
#include <vector>
#include "dict0dict.h"
#include "page0cur.h"
#include "ut0new.h"
/** Innodb B-tree index fill factor for bulk load. */
extern long innobase_fill_factor;
/*
The proper function call sequence of PageBulk is as below:
-- PageBulk::init
-- PageBulk::insert
-- PageBulk::finish
-- PageBulk::compress(COMPRESSED table only)
-- PageBulk::pageSplit(COMPRESSED table only)
-- PageBulk::commit
*/
class PageBulk {
public:
/** Page split point descriptor. */
struct SplitPoint {
/** Record being the point of split.
* All records before this record should stay on current on page.
* This record and all following records should be moved to new page. */
rec_t *m_rec;
/** Number of records before this record. */
ulint m_n_rec_before;
};
/** Constructor
@param[in] index B-tree index
@param[in] page_no page number
@param[in] level page level
@param[in] trx_id transaction id
@param[in] observer flush observer */
PageBulk(dict_index_t *index, trx_id_t trx_id, page_no_t page_no, ulint level,
FlushObserver *observer)
: m_heap(nullptr),
m_index(index),
m_mtr(nullptr),
m_trx_id(trx_id),
m_block(nullptr),
m_page(nullptr),
m_page_zip(nullptr),
m_cur_rec(nullptr),
m_page_no(page_no),
m_level(level),
m_is_comp(dict_table_is_comp(index->table)),
m_heap_top(nullptr),
m_rec_no(0),
m_free_space(0),
m_reserved_space(0),
m_padding_space(0),
#ifdef UNIV_DEBUG
m_total_data(0),
#endif /* UNIV_DEBUG */
m_modify_clock(0),
m_flush_observer(observer),
m_last_slotted_rec(nullptr),
m_slotted_rec_no(0),
m_modified(false) {
ut_ad(!dict_index_is_spatial(m_index));
}
/** Destructor */
~PageBulk() {
if (m_heap) {
mem_heap_free(m_heap);
}
}
/** Initialize members and allocate page if needed and start mtr.
@note Must be called and only once right after constructor.
@return error code */
dberr_t init() MY_ATTRIBUTE((warn_unused_result));
/** Insert a tuple in the page.
@param[in] tuple tuple to insert
@param[in] big_rec external record
@param[in] rec_size record size
@param[in] n_ext number of externally stored columns
@return error code */
dberr_t insert(const dtuple_t *tuple, const big_rec_t *big_rec,
ulint rec_size, ulint n_ext)
MY_ATTRIBUTE((warn_unused_result));
/** Mark end of insertion to the page. Scan records to set page dirs,
and set page header members. The scan is incremental (slots and records
which assignment could be "finalized" are not checked again. Check the
m_slotted_rec_no usage, note it could be reset in some cases like
during split.
Note: we refer to page_copy_rec_list_end_to_created_page. */
void finish();
/** Commit mtr for a page
@param[in] success Flag whether all inserts succeed. */
void commit(bool success);
/** Compress if it is compressed table
@return true compress successfully or no need to compress
@return false compress failed. */
bool compress() MY_ATTRIBUTE((warn_unused_result));
/** Check whether the record needs to be stored externally.
@return false if the entire record can be stored locally on the page */
bool needExt(const dtuple_t *tuple, ulint rec_size) const
MY_ATTRIBUTE((warn_unused_result));
/** Get node pointer
@return node pointer */
dtuple_t *getNodePtr();
/** Split the page records between this and given bulk.
* @param new_page_bulk The new bulk to store split records. */
void split(PageBulk &new_page_bulk);
/** Copy all records from page.
@param[in] src_page Page with records to copy. */
void copyAll(const page_t *src_page);
/** Set next page
@param[in] next_page_no next page no */
void setNext(page_no_t next_page_no);
/** Set previous page
@param[in] prev_page_no previous page no */
void setPrev(page_no_t prev_page_no);
/** Release block by committing mtr */
inline void release();
/** Start mtr and latch block */
inline void latch();
/** Check if required space is available in the page for the rec
to be inserted. We check fill factor & padding here.
@param[in] rec_size required space
@return true if space is available */
inline bool isSpaceAvailable(ulint rec_size) const;
/** Get page no */
page_no_t getPageNo() const { return (m_page_no); }
/** Get page level */
ulint getLevel() const { return (m_level); }
/** Get record no */
ulint getRecNo() const { return (m_rec_no); }
/** Get page */
const page_t *getPage() const { return (m_page); }
/** Check if table is compressed.
@return true if table is compressed, false otherwise. */
bool isTableCompressed() const { return (m_page_zip != nullptr); }
#ifdef UNIV_DEBUG
/** Check if index is X locked */
bool isIndexXLocked();
#endif // UNIV_DEBUG
private:
/** Get page split point. We split a page in half when compression
fails, and the split record and all following records should be copied
to the new page.
@return split record descriptor */
SplitPoint getSplitRec();
/** Copy given and all following records.
@param[in] first_rec first record to copy */
void copyRecords(const rec_t *first_rec);
/** Remove all records after split rec including itself.
@param[in] split_point split point descriptor */
void splitTrim(const SplitPoint &split_point);
/** Insert a record in the page.
@param[in] rec record
@param[in] offsets record offsets */
void insert(const rec_t *rec, ulint *offsets);
/** Store external record
Since the record is not logged yet, so we don't log update to the record.
the blob data is logged first, then the record is logged in bulk mode.
@param[in] big_rec external record
@param[in] offsets record offsets
@return error code */
dberr_t storeExt(const big_rec_t *big_rec, ulint *offsets)
MY_ATTRIBUTE((warn_unused_result));
/** Memory heap for internal allocation */
mem_heap_t *m_heap;
/** The index B-tree */
dict_index_t *m_index;
/** The min-transaction */
mtr_t *m_mtr;
/** The transaction id */
trx_id_t m_trx_id;
/** The buffer block */
buf_block_t *m_block;
/** The page */
page_t *m_page;
/** The page zip descriptor */
page_zip_des_t *m_page_zip;
/** The current rec, just before the next insert rec */
rec_t *m_cur_rec;
/** The page no */
page_no_t m_page_no;
/** The page level in B-tree */
ulint m_level;
/** Flag: is page in compact format */
const bool m_is_comp;
/** The heap top in page for next insert */
byte *m_heap_top;
/** User record no */
ulint m_rec_no;
/** The free space left in the page */
ulint m_free_space;
/** The reserved space for fill factor */
ulint m_reserved_space;
/** The padding space for compressed page */
ulint m_padding_space;
#ifdef UNIV_DEBUG
/** Total data in the page */
ulint m_total_data;
#endif /* UNIV_DEBUG */
/** The modify clock value of the buffer block
when the block is re-pinned */
ib_uint64_t m_modify_clock;
/** Flush observer */
FlushObserver *m_flush_observer;
/** Last record assigned to a slot. */
rec_t *m_last_slotted_rec;
/** Number of records assigned to slots. */
ulint m_slotted_rec_no;
/** Page modified flag. */
bool m_modified;
};
class BtrBulk {
public:
using page_bulk_vector = std::vector<PageBulk *, ut_allocator<PageBulk *>>;
/** Constructor
@param[in] index B-tree index
@param[in] trx_id transaction id
@param[in] observer flush observer */
BtrBulk(dict_index_t *index, trx_id_t trx_id, FlushObserver *observer);
/** Destructor */
~BtrBulk();
/** Initialization
@note Must be called right after constructor. */
dberr_t init() MY_ATTRIBUTE((warn_unused_result));
/** Insert a tuple
@param[in] tuple tuple to insert.
@return error code */
dberr_t insert(dtuple_t *tuple) MY_ATTRIBUTE((warn_unused_result)) {
return (insert(tuple, 0));
}
/** Btree bulk load finish. We commit the last page in each level
and copy the last page in top level to the root page of the index
if no error occurs.
@param[in] err whether bulk load was successful until now
@return error code */
dberr_t finish(dberr_t err) MY_ATTRIBUTE((warn_unused_result));
/** Release all latches */
void release();
/** Re-latch all latches */
void latch();
private:
/** Insert a tuple to a page in a level
@param[in] tuple tuple to insert
@param[in] level B-tree level
@return error code */
dberr_t insert(dtuple_t *tuple, ulint level)
MY_ATTRIBUTE((warn_unused_result));
/** Split a page
@param[in] page_bulk page to split
@param[in] next_page_bulk next page
@return error code */
dberr_t pageSplit(PageBulk *page_bulk, PageBulk *next_page_bulk)
MY_ATTRIBUTE((warn_unused_result));
/** Commit(finish) a page. We set next/prev page no, compress a page of
compressed table and split the page if compression fails, insert a node
pointer to father page if needed, and commit mini-transaction.
@param[in] page_bulk page to commit
@param[in] next_page_bulk next page
@param[in] insert_father flag whether need to insert node ptr
@return error code */
dberr_t pageCommit(PageBulk *page_bulk, PageBulk *next_page_bulk,
bool insert_father) MY_ATTRIBUTE((warn_unused_result));
/** Abort a page when an error occurs
@param[in] page_bulk page bulk object
@note We should call pageAbort for a PageBulk object, which is not in
m_page_bulks after pageCommit, and we will commit or abort PageBulk
objects in function "finish". */
void pageAbort(PageBulk *page_bulk) { page_bulk->commit(false); }
/** Prepare space to insert a tuple.
@param[in,out] page_bulk page bulk that will be used to store the record.
It may be replaced if there is not enough space
to hold the record.
@param[in] level B-tree level
@param[in] rec_size record size
@return error code */
dberr_t prepareSpace(PageBulk *&page_bulk, ulint level, ulint rec_size)
MY_ATTRIBUTE((warn_unused_result));
/** Insert a tuple to a page.
@param[in] page_bulk page bulk object
@param[in] tuple tuple to insert
@param[in] big_rec big record vector, maybe NULL if there is no
data to be stored externally.
@param[in] rec_size record size
@param[in] n_ext number of externally stored columns
@return error code */
dberr_t insert(PageBulk *page_bulk, dtuple_t *tuple, big_rec_t *big_rec,
ulint rec_size, ulint n_ext)
MY_ATTRIBUTE((warn_unused_result));
/** Log free check */
void logFreeCheck();
/** Btree page bulk load finish. Commits the last page in each level
if no error occurs. Also releases all page bulks.
@param[in] err whether bulk load was successful until now
@param[out] last_page_no last page number
@return error code */
dberr_t finishAllPageBulks(dberr_t err, page_no_t &last_page_no)
MY_ATTRIBUTE((warn_unused_result));
private:
/** B-tree index */
dict_index_t *m_index;
/** Transaction id */
trx_id_t m_trx_id;
/** Root page level */
ulint m_root_level;
/** Flush observer */
FlushObserver *m_flush_observer;
/** Page cursor vector for all level */
page_bulk_vector *m_page_bulks;
#ifdef UNIV_DEBUG
/** State of the index. Used for asserting at the end of a
bulk load operation to ensure that the online status of the
index does not change */
unsigned m_index_online;
#endif // UNIV_DEBUG
};
#endif