mirror of
https://github.com/MariaDB/server.git
synced 2026-05-11 17:40:11 +02:00
MDEV-12396 IMPORT TABLESPACE cleanup
Reduce unnecessary inter-module calls for IMPORT TABLESPACE. Move some IMPORT-related code from fil0fil.cc to row0import.cc. PageCallback: Remove. Make AbstractCallback the base class. PageConverter: Define some member functions inline.
This commit is contained in:
parent
eee73ddfbb
commit
6247c64c2a
6 changed files with 1008 additions and 1183 deletions
|
|
@ -25,8 +25,6 @@ Created 10/25/1995 Heikki Tuuri
|
|||
*******************************************************/
|
||||
|
||||
#include "fil0fil.h"
|
||||
#include "fil0pagecompress.h"
|
||||
#include "fsp0pagecompress.h"
|
||||
#include "fil0crypt.h"
|
||||
|
||||
#include <debug_sync.h>
|
||||
|
|
@ -49,12 +47,10 @@ Created 10/25/1995 Heikki Tuuri
|
|||
#include "page0zip.h"
|
||||
#include "trx0sys.h"
|
||||
#include "row0mysql.h"
|
||||
#include "os0file.h"
|
||||
#ifndef UNIV_HOTBACKUP
|
||||
# include "buf0lru.h"
|
||||
# include "ibuf0ibuf.h"
|
||||
# include "sync0sync.h"
|
||||
# include "os0sync.h"
|
||||
#else /* !UNIV_HOTBACKUP */
|
||||
# include "srv0srv.h"
|
||||
static ulint srv_data_read, srv_data_written;
|
||||
|
|
@ -696,7 +692,7 @@ add_size:
|
|||
space->size += node->size;
|
||||
}
|
||||
|
||||
ulint atomic_writes = fsp_flags_get_atomic_writes(space->flags);
|
||||
ulint atomic_writes = FSP_FLAGS_GET_ATOMIC_WRITES(space->flags);
|
||||
|
||||
/* printf("Opening file %s\n", node->name); */
|
||||
|
||||
|
|
@ -3921,7 +3917,6 @@ fil_open_single_table_tablespace(
|
|||
fsp_open_info remote;
|
||||
ulint tablespaces_found = 0;
|
||||
ulint valid_tablespaces_found = 0;
|
||||
ulint atomic_writes = 0;
|
||||
fil_space_crypt_t* crypt_data = NULL;
|
||||
|
||||
#ifdef UNIV_SYNC_DEBUG
|
||||
|
|
@ -3936,7 +3931,7 @@ fil_open_single_table_tablespace(
|
|||
}
|
||||
|
||||
ut_ad(fsp_flags_is_valid(flags & ~FSP_FLAGS_MEM_MASK, id));
|
||||
atomic_writes = fsp_flags_get_atomic_writes(flags);
|
||||
const ulint atomic_writes = FSP_FLAGS_GET_ATOMIC_WRITES(flags);
|
||||
|
||||
memset(&def, 0, sizeof(def));
|
||||
memset(&dict, 0, sizeof(dict));
|
||||
|
|
@ -5841,7 +5836,8 @@ fil_io(
|
|||
} else if (type == OS_FILE_WRITE) {
|
||||
ut_ad(!srv_read_only_mode);
|
||||
srv_stats.data_written.add(len);
|
||||
if (fil_page_is_index_page((byte *)buf)) {
|
||||
if (mach_read_from_2(static_cast<const byte*>(buf)
|
||||
+ FIL_PAGE_TYPE) == FIL_PAGE_INDEX) {
|
||||
srv_stats.index_pages_written.inc();
|
||||
} else {
|
||||
srv_stats.non_index_pages_written.inc();
|
||||
|
|
@ -6338,479 +6334,6 @@ fil_close(void)
|
|||
fil_system = NULL;
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Initializes a buffer control block when the buf_pool is created. */
|
||||
static
|
||||
void
|
||||
fil_buf_block_init(
|
||||
/*===============*/
|
||||
buf_block_t* block, /*!< in: pointer to control block */
|
||||
byte* frame) /*!< in: pointer to buffer frame */
|
||||
{
|
||||
UNIV_MEM_DESC(frame, UNIV_PAGE_SIZE);
|
||||
|
||||
block->frame = frame;
|
||||
|
||||
block->page.io_fix = BUF_IO_NONE;
|
||||
/* There are assertions that check for this. */
|
||||
block->page.buf_fix_count = 1;
|
||||
block->page.state = BUF_BLOCK_READY_FOR_USE;
|
||||
|
||||
page_zip_des_init(&block->page.zip);
|
||||
}
|
||||
|
||||
struct fil_iterator_t {
|
||||
pfs_os_file_t file; /*!< File handle */
|
||||
const char* filepath; /*!< File path name */
|
||||
os_offset_t start; /*!< From where to start */
|
||||
os_offset_t end; /*!< Where to stop */
|
||||
os_offset_t file_size; /*!< File size in bytes */
|
||||
ulint page_size; /*!< Page size */
|
||||
ulint n_io_buffers; /*!< Number of pages to use
|
||||
for IO */
|
||||
byte* io_buffer; /*!< Buffer to use for IO */
|
||||
fil_space_crypt_t *crypt_data; /*!< Crypt data (if encrypted) */
|
||||
byte* crypt_io_buffer; /*!< IO buffer when encrypted */
|
||||
};
|
||||
|
||||
/********************************************************************//**
|
||||
TODO: This can be made parallel trivially by chunking up the file and creating
|
||||
a callback per thread. . Main benefit will be to use multiple CPUs for
|
||||
checksums and compressed tables. We have to do compressed tables block by
|
||||
block right now. Secondly we need to decompress/compress and copy too much
|
||||
of data. These are CPU intensive.
|
||||
|
||||
Iterate over all the pages in the tablespace.
|
||||
@param iter - Tablespace iterator
|
||||
@param block - block to use for IO
|
||||
@param callback - Callback to inspect and update page contents
|
||||
@retval DB_SUCCESS or error code */
|
||||
static
|
||||
dberr_t
|
||||
fil_iterate(
|
||||
/*========*/
|
||||
const fil_iterator_t& iter,
|
||||
buf_block_t* block,
|
||||
PageCallback& callback)
|
||||
{
|
||||
os_offset_t offset;
|
||||
ulint page_no = 0;
|
||||
ulint space_id = callback.get_space_id();
|
||||
ulint n_bytes = iter.n_io_buffers * iter.page_size;
|
||||
|
||||
ut_ad(!srv_read_only_mode);
|
||||
|
||||
/* TODO: For compressed tables we do a lot of useless
|
||||
copying for non-index pages. Unfortunately, it is
|
||||
required by buf_zip_decompress() */
|
||||
const bool row_compressed = callback.get_zip_size() > 0;
|
||||
|
||||
for (offset = iter.start; offset < iter.end; offset += n_bytes) {
|
||||
|
||||
byte* io_buffer = iter.io_buffer;
|
||||
|
||||
block->frame = io_buffer;
|
||||
|
||||
if (row_compressed) {
|
||||
page_zip_des_init(&block->page.zip);
|
||||
page_zip_set_size(&block->page.zip, iter.page_size);
|
||||
block->page.zip.data = block->frame + UNIV_PAGE_SIZE;
|
||||
ut_d(block->page.zip.m_external = true);
|
||||
ut_ad(iter.page_size == callback.get_zip_size());
|
||||
|
||||
/* Zip IO is done in the compressed page buffer. */
|
||||
io_buffer = block->page.zip.data;
|
||||
}
|
||||
|
||||
/* We have to read the exact number of bytes. Otherwise the
|
||||
InnoDB IO functions croak on failed reads. */
|
||||
|
||||
n_bytes = static_cast<ulint>(
|
||||
ut_min(static_cast<os_offset_t>(n_bytes),
|
||||
iter.end - offset));
|
||||
|
||||
ut_ad(n_bytes > 0);
|
||||
ut_ad(!(n_bytes % iter.page_size));
|
||||
|
||||
const bool encrypted = iter.crypt_data != NULL
|
||||
&& iter.crypt_data->should_encrypt();
|
||||
/* Use additional crypt io buffer if tablespace is encrypted */
|
||||
byte* const readptr = encrypted
|
||||
? iter.crypt_io_buffer : io_buffer;
|
||||
byte* const writeptr = readptr;
|
||||
|
||||
if (!os_file_read(iter.file, readptr, offset, (ulint) n_bytes)) {
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_ERROR, "os_file_read() failed");
|
||||
|
||||
return(DB_IO_ERROR);
|
||||
}
|
||||
|
||||
bool updated = false;
|
||||
os_offset_t page_off = offset;
|
||||
ulint n_pages_read = (ulint) n_bytes / iter.page_size;
|
||||
bool decrypted = false;
|
||||
|
||||
for (ulint i = 0; i < n_pages_read; ++i) {
|
||||
ulint size = iter.page_size;
|
||||
dberr_t err = DB_SUCCESS;
|
||||
byte* src = readptr + (i * size);
|
||||
byte* dst = io_buffer + (i * size);
|
||||
bool frame_changed = false;
|
||||
|
||||
ulint page_type = mach_read_from_2(src+FIL_PAGE_TYPE);
|
||||
|
||||
const bool page_compressed
|
||||
= page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED
|
||||
|| page_type == FIL_PAGE_PAGE_COMPRESSED;
|
||||
|
||||
/* If tablespace is encrypted, we need to decrypt
|
||||
the page. Note that tablespaces are not in
|
||||
fil_system during import. */
|
||||
if (encrypted) {
|
||||
decrypted = fil_space_decrypt(
|
||||
iter.crypt_data,
|
||||
dst, //dst
|
||||
iter.page_size,
|
||||
src, // src
|
||||
&err); // src
|
||||
|
||||
if (err != DB_SUCCESS) {
|
||||
return(err);
|
||||
}
|
||||
|
||||
if (decrypted) {
|
||||
updated = true;
|
||||
} else {
|
||||
if (!page_compressed && !row_compressed) {
|
||||
block->frame = src;
|
||||
frame_changed = true;
|
||||
} else {
|
||||
memcpy(dst, src, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If the original page is page_compressed, we need
|
||||
to decompress page before we can update it. */
|
||||
if (page_compressed) {
|
||||
fil_decompress_page(NULL, dst, ulong(size),
|
||||
NULL);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
buf_block_set_file_page(block, space_id, page_no++);
|
||||
|
||||
if ((err = callback(page_off, block)) != DB_SUCCESS) {
|
||||
|
||||
return(err);
|
||||
|
||||
} else if (!updated) {
|
||||
updated = buf_block_get_state(block)
|
||||
== BUF_BLOCK_FILE_PAGE;
|
||||
}
|
||||
|
||||
buf_block_set_state(block, BUF_BLOCK_NOT_USED);
|
||||
buf_block_set_state(block, BUF_BLOCK_READY_FOR_USE);
|
||||
|
||||
/* If tablespace is encrypted we use additional
|
||||
temporary scratch area where pages are read
|
||||
for decrypting readptr == crypt_io_buffer != io_buffer.
|
||||
|
||||
Destination for decryption is a buffer pool block
|
||||
block->frame == dst == io_buffer that is updated.
|
||||
Pages that did not require decryption even when
|
||||
tablespace is marked as encrypted are not copied
|
||||
instead block->frame is set to src == readptr.
|
||||
|
||||
For encryption we again use temporary scratch area
|
||||
writeptr != io_buffer == dst
|
||||
that is then written to the tablespace
|
||||
|
||||
(1) For normal tables io_buffer == dst == writeptr
|
||||
(2) For only page compressed tables
|
||||
io_buffer == dst == writeptr
|
||||
(3) For encrypted (and page compressed)
|
||||
readptr != io_buffer == dst != writeptr
|
||||
*/
|
||||
|
||||
ut_ad(!encrypted && !page_compressed ?
|
||||
src == dst && dst == writeptr + (i * size):1);
|
||||
ut_ad(page_compressed && !encrypted ?
|
||||
src == dst && dst == writeptr + (i * size):1);
|
||||
ut_ad(encrypted ?
|
||||
src != dst && dst != writeptr + (i * size):1);
|
||||
|
||||
if (encrypted) {
|
||||
memcpy(writeptr + (i * size),
|
||||
row_compressed ? block->page.zip.data :
|
||||
block->frame, size);
|
||||
}
|
||||
|
||||
if (frame_changed) {
|
||||
block->frame = dst;
|
||||
}
|
||||
|
||||
src = io_buffer + (i * size);
|
||||
|
||||
if (page_compressed) {
|
||||
ulint len = 0;
|
||||
|
||||
fil_compress_page(
|
||||
NULL,
|
||||
src,
|
||||
NULL,
|
||||
size,
|
||||
0,/* FIXME: compression level */
|
||||
512,/* FIXME: use proper block size */
|
||||
encrypted,
|
||||
&len);
|
||||
|
||||
updated = true;
|
||||
}
|
||||
|
||||
/* If tablespace is encrypted, encrypt page before we
|
||||
write it back. Note that we should not encrypt the
|
||||
buffer that is in buffer pool. */
|
||||
/* NOTE: At this stage of IMPORT the
|
||||
buffer pool is not being used at all! */
|
||||
if (decrypted && encrypted) {
|
||||
byte *dest = writeptr + (i * size);
|
||||
ulint space = mach_read_from_4(
|
||||
src + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
|
||||
ulint offset = mach_read_from_4(src + FIL_PAGE_OFFSET);
|
||||
ib_uint64_t lsn = mach_read_from_8(src + FIL_PAGE_LSN);
|
||||
|
||||
byte* tmp = fil_encrypt_buf(
|
||||
iter.crypt_data,
|
||||
space,
|
||||
offset,
|
||||
lsn,
|
||||
src,
|
||||
iter.page_size == UNIV_PAGE_SIZE ? 0 : iter.page_size,
|
||||
dest);
|
||||
|
||||
if (tmp == src) {
|
||||
/* TODO: remove unnecessary memcpy's */
|
||||
memcpy(dest, src, size);
|
||||
}
|
||||
|
||||
updated = true;
|
||||
}
|
||||
|
||||
page_off += iter.page_size;
|
||||
block->frame += iter.page_size;
|
||||
}
|
||||
|
||||
/* A page was updated in the set, write back to disk. */
|
||||
if (updated
|
||||
&& !os_file_write(
|
||||
iter.filepath, iter.file, writeptr,
|
||||
offset, (ulint) n_bytes)) {
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_ERROR, "os_file_write() failed");
|
||||
|
||||
return(DB_IO_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
return(DB_SUCCESS);
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Iterate over all the pages in the tablespace.
|
||||
@param table - the table definiton in the server
|
||||
@param n_io_buffers - number of blocks to read and write together
|
||||
@param callback - functor that will do the page updates
|
||||
@return DB_SUCCESS or error code */
|
||||
UNIV_INTERN
|
||||
dberr_t
|
||||
fil_tablespace_iterate(
|
||||
/*===================*/
|
||||
dict_table_t* table,
|
||||
ulint n_io_buffers,
|
||||
PageCallback& callback)
|
||||
{
|
||||
dberr_t err;
|
||||
pfs_os_file_t file;
|
||||
char* filepath;
|
||||
|
||||
ut_a(n_io_buffers > 0);
|
||||
ut_ad(!srv_read_only_mode);
|
||||
|
||||
DBUG_EXECUTE_IF("ib_import_trigger_corruption_1",
|
||||
return(DB_CORRUPTION););
|
||||
|
||||
if (DICT_TF_HAS_DATA_DIR(table->flags)) {
|
||||
dict_get_and_save_data_dir_path(table, false);
|
||||
ut_a(table->data_dir_path);
|
||||
|
||||
filepath = os_file_make_remote_pathname(
|
||||
table->data_dir_path, table->name, "ibd");
|
||||
} else {
|
||||
filepath = fil_make_ibd_name(table->name, false);
|
||||
}
|
||||
|
||||
{
|
||||
ibool success;
|
||||
|
||||
file = os_file_create_simple_no_error_handling(
|
||||
innodb_file_data_key, filepath,
|
||||
OS_FILE_OPEN, OS_FILE_READ_WRITE, &success, FALSE);
|
||||
|
||||
DBUG_EXECUTE_IF("fil_tablespace_iterate_failure",
|
||||
{
|
||||
static bool once;
|
||||
|
||||
if (!once || ut_rnd_interval(0, 10) == 5) {
|
||||
once = true;
|
||||
success = FALSE;
|
||||
os_file_close(file);
|
||||
}
|
||||
});
|
||||
|
||||
if (!success) {
|
||||
/* The following call prints an error message */
|
||||
os_file_get_last_error(true);
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_ERROR,
|
||||
"Trying to import a tablespace, but could not "
|
||||
"open the tablespace file %s", filepath);
|
||||
|
||||
mem_free(filepath);
|
||||
|
||||
return(DB_TABLESPACE_NOT_FOUND);
|
||||
|
||||
} else {
|
||||
err = DB_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
callback.set_file(filepath, file);
|
||||
|
||||
os_offset_t file_size = os_file_get_size(file);
|
||||
ut_a(file_size != (os_offset_t) -1);
|
||||
|
||||
/* The block we will use for every physical page */
|
||||
buf_block_t block;
|
||||
|
||||
memset(&block, 0x0, sizeof(block));
|
||||
|
||||
/* Allocate a page to read in the tablespace header, so that we
|
||||
can determine the page size and zip_size (if it is compressed).
|
||||
We allocate an extra page in case it is a compressed table. One
|
||||
page is to ensure alignement. */
|
||||
|
||||
void* page_ptr = mem_alloc(3 * UNIV_PAGE_SIZE);
|
||||
byte* page = static_cast<byte*>(ut_align(page_ptr, UNIV_PAGE_SIZE));
|
||||
|
||||
fil_buf_block_init(&block, page);
|
||||
|
||||
/* Read the first page and determine the page and zip size. */
|
||||
|
||||
if (!os_file_read(file, page, 0, UNIV_PAGE_SIZE)) {
|
||||
|
||||
err = DB_IO_ERROR;
|
||||
|
||||
} else if ((err = callback.init(file_size, &block)) == DB_SUCCESS) {
|
||||
fil_iterator_t iter;
|
||||
|
||||
iter.file = file;
|
||||
iter.start = 0;
|
||||
iter.end = file_size;
|
||||
iter.filepath = filepath;
|
||||
iter.file_size = file_size;
|
||||
iter.n_io_buffers = n_io_buffers;
|
||||
iter.page_size = callback.get_page_size();
|
||||
|
||||
/* In MariaDB/MySQL 5.6 tablespace does not exist
|
||||
during import, therefore we can't use space directly
|
||||
here. */
|
||||
ulint crypt_data_offset = fsp_header_get_crypt_offset(
|
||||
callback.get_zip_size());
|
||||
|
||||
/* read (optional) crypt data */
|
||||
iter.crypt_data = fil_space_read_crypt_data(
|
||||
0, page, crypt_data_offset);
|
||||
|
||||
/* Compressed pages can't be optimised for block IO for now.
|
||||
We do the IMPORT page by page. */
|
||||
|
||||
if (callback.get_zip_size() > 0) {
|
||||
iter.n_io_buffers = 1;
|
||||
ut_a(iter.page_size == callback.get_zip_size());
|
||||
}
|
||||
|
||||
/** If tablespace is encrypted, it needs extra buffers */
|
||||
if (iter.crypt_data != NULL) {
|
||||
/* decrease io buffers so that memory
|
||||
* consumption doesnt double
|
||||
* note: the +1 is to avoid n_io_buffers getting down to 0 */
|
||||
iter.n_io_buffers = (iter.n_io_buffers + 1) / 2;
|
||||
}
|
||||
|
||||
/** Add an extra page for compressed page scratch area. */
|
||||
|
||||
void* io_buffer = mem_alloc(
|
||||
(2 + iter.n_io_buffers) * UNIV_PAGE_SIZE);
|
||||
|
||||
iter.io_buffer = static_cast<byte*>(
|
||||
ut_align(io_buffer, UNIV_PAGE_SIZE));
|
||||
|
||||
void* crypt_io_buffer = NULL;
|
||||
if (iter.crypt_data != NULL) {
|
||||
crypt_io_buffer = mem_alloc(
|
||||
(2 + iter.n_io_buffers) * UNIV_PAGE_SIZE);
|
||||
iter.crypt_io_buffer = static_cast<byte*>(
|
||||
ut_align(crypt_io_buffer, UNIV_PAGE_SIZE));
|
||||
}
|
||||
|
||||
err = fil_iterate(iter, &block, callback);
|
||||
|
||||
mem_free(io_buffer);
|
||||
|
||||
if (crypt_io_buffer != NULL) {
|
||||
mem_free(crypt_io_buffer);
|
||||
iter.crypt_io_buffer = NULL;
|
||||
fil_space_destroy_crypt_data(&iter.crypt_data);
|
||||
}
|
||||
}
|
||||
|
||||
if (err == DB_SUCCESS) {
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_INFO, "Sync to disk");
|
||||
|
||||
if (!os_file_flush(file)) {
|
||||
ib_logf(IB_LOG_LEVEL_INFO, "os_file_flush() failed!");
|
||||
err = DB_IO_ERROR;
|
||||
} else {
|
||||
ib_logf(IB_LOG_LEVEL_INFO, "Sync to disk - done!");
|
||||
}
|
||||
}
|
||||
|
||||
os_file_close(file);
|
||||
|
||||
mem_free(page_ptr);
|
||||
mem_free(filepath);
|
||||
|
||||
return(err);
|
||||
}
|
||||
|
||||
/**
|
||||
Set the tablespace compressed table size.
|
||||
@return DB_SUCCESS if it is valie or DB_CORRUPTION if not */
|
||||
dberr_t
|
||||
PageCallback::set_zip_size(const buf_frame_t* page) UNIV_NOTHROW
|
||||
{
|
||||
m_zip_size = fsp_header_get_zip_size(page);
|
||||
|
||||
if (!ut_is_2pow(m_zip_size) || m_zip_size > UNIV_ZIP_SIZE_MAX) {
|
||||
return(DB_CORRUPTION);
|
||||
}
|
||||
|
||||
return(DB_SUCCESS);
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Delete the tablespace file and any related files like .cfg.
|
||||
This should not be called for temporary tables. */
|
||||
|
|
|
|||
|
|
@ -1292,107 +1292,6 @@ fil_delete_file(
|
|||
/*============*/
|
||||
const char* path); /*!< in: filepath of the ibd tablespace */
|
||||
|
||||
/** Callback functor. */
|
||||
struct PageCallback {
|
||||
|
||||
/**
|
||||
Default constructor */
|
||||
PageCallback()
|
||||
:
|
||||
m_zip_size(),
|
||||
m_page_size(),
|
||||
m_filepath() UNIV_NOTHROW {}
|
||||
|
||||
virtual ~PageCallback() UNIV_NOTHROW {}
|
||||
|
||||
/**
|
||||
Called for page 0 in the tablespace file at the start.
|
||||
@param file_size - size of the file in bytes
|
||||
@param block - contents of the first page in the tablespace file
|
||||
@retval DB_SUCCESS or error code.*/
|
||||
virtual dberr_t init(
|
||||
os_offset_t file_size,
|
||||
const buf_block_t* block) UNIV_NOTHROW = 0;
|
||||
|
||||
/**
|
||||
Called for every page in the tablespace. If the page was not
|
||||
updated then its state must be set to BUF_PAGE_NOT_USED. For
|
||||
compressed tables the page descriptor memory will be at offset:
|
||||
block->frame + UNIV_PAGE_SIZE;
|
||||
@param offset - physical offset within the file
|
||||
@param block - block read from file, note it is not from the buffer pool
|
||||
@retval DB_SUCCESS or error code. */
|
||||
virtual dberr_t operator()(
|
||||
os_offset_t offset,
|
||||
buf_block_t* block) UNIV_NOTHROW = 0;
|
||||
|
||||
/**
|
||||
Set the name of the physical file and the file handle that is used
|
||||
to open it for the file that is being iterated over.
|
||||
@param filename - then physical name of the tablespace file.
|
||||
@param file - OS file handle */
|
||||
void set_file(const char* filename, pfs_os_file_t file) UNIV_NOTHROW
|
||||
{
|
||||
m_file = file;
|
||||
m_filepath = filename;
|
||||
}
|
||||
|
||||
/**
|
||||
@return the space id of the tablespace */
|
||||
virtual ulint get_space_id() const UNIV_NOTHROW = 0;
|
||||
|
||||
/** The compressed page size
|
||||
@return the compressed page size */
|
||||
ulint get_zip_size() const
|
||||
{
|
||||
return(m_zip_size);
|
||||
}
|
||||
|
||||
/**
|
||||
Set the tablespace compressed table size.
|
||||
@return DB_SUCCESS if it is valie or DB_CORRUPTION if not */
|
||||
dberr_t set_zip_size(const buf_frame_t* page) UNIV_NOTHROW;
|
||||
|
||||
/** The compressed page size
|
||||
@return the compressed page size */
|
||||
ulint get_page_size() const
|
||||
{
|
||||
return(m_page_size);
|
||||
}
|
||||
|
||||
/** Compressed table page size */
|
||||
ulint m_zip_size;
|
||||
|
||||
/** The tablespace page size. */
|
||||
ulint m_page_size;
|
||||
|
||||
/** File handle to the tablespace */
|
||||
pfs_os_file_t m_file;
|
||||
|
||||
/** Physical file path. */
|
||||
const char* m_filepath;
|
||||
|
||||
protected:
|
||||
// Disable copying
|
||||
PageCallback(const PageCallback&);
|
||||
PageCallback& operator=(const PageCallback&);
|
||||
};
|
||||
|
||||
/********************************************************************//**
|
||||
Iterate over all the pages in the tablespace.
|
||||
@param table - the table definiton in the server
|
||||
@param n_io_buffers - number of blocks to read and write together
|
||||
@param callback - functor that will do the page updates
|
||||
@return DB_SUCCESS or error code */
|
||||
UNIV_INTERN
|
||||
dberr_t
|
||||
fil_tablespace_iterate(
|
||||
/*===================*/
|
||||
dict_table_t* table,
|
||||
ulint n_io_buffers,
|
||||
PageCallback& callback)
|
||||
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
||||
|
||||
/*******************************************************************//**
|
||||
Checks if a single-table tablespace for a given table name exists in the
|
||||
tablespace memory cache.
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ Created 2012-02-08 by Sunny Bains.
|
|||
#include "row0mysql.h"
|
||||
#include "srv0start.h"
|
||||
#include "row0quiesce.h"
|
||||
#include "fil0pagecompress.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -361,7 +362,8 @@ private:
|
|||
|
||||
/** Functor that is called for each physical page that is read from the
|
||||
tablespace file. */
|
||||
class AbstractCallback : public PageCallback {
|
||||
class AbstractCallback
|
||||
{
|
||||
public:
|
||||
/** Constructor
|
||||
@param trx - covering transaction */
|
||||
|
|
@ -394,6 +396,47 @@ public:
|
|||
return(get_zip_size() > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
Set the name of the physical file and the file handle that is used
|
||||
to open it for the file that is being iterated over.
|
||||
@param filename - then physical name of the tablespace file.
|
||||
@param file - OS file handle */
|
||||
void set_file(const char* filename, pfs_os_file_t file) UNIV_NOTHROW
|
||||
{
|
||||
m_file = file;
|
||||
m_filepath = filename;
|
||||
}
|
||||
|
||||
/** The compressed page size
|
||||
@return the compressed page size */
|
||||
ulint get_zip_size() const
|
||||
{
|
||||
return(m_zip_size);
|
||||
}
|
||||
|
||||
/** The compressed page size
|
||||
@return the compressed page size */
|
||||
ulint get_page_size() const
|
||||
{
|
||||
return(m_page_size);
|
||||
}
|
||||
|
||||
/**
|
||||
Called for every page in the tablespace. If the page was not
|
||||
updated then its state must be set to BUF_PAGE_NOT_USED. For
|
||||
compressed tables the page descriptor memory will be at offset:
|
||||
block->frame + UNIV_PAGE_SIZE;
|
||||
@param offset - physical offset within the file
|
||||
@param block - block read from file, note it is not from the buffer pool
|
||||
@retval DB_SUCCESS or error code. */
|
||||
virtual dberr_t operator()(
|
||||
os_offset_t offset,
|
||||
buf_block_t* block) UNIV_NOTHROW = 0;
|
||||
|
||||
/**
|
||||
@return the space id of the tablespace */
|
||||
virtual ulint get_space_id() const UNIV_NOTHROW = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
Get the data page depending on the table type, compressed or not.
|
||||
|
|
@ -509,6 +552,18 @@ protected:
|
|||
}
|
||||
|
||||
protected:
|
||||
/** Compressed table page size */
|
||||
ulint m_zip_size;
|
||||
|
||||
/** The tablespace page size. */
|
||||
ulint m_page_size;
|
||||
|
||||
/** File handle to the tablespace */
|
||||
pfs_os_file_t m_file;
|
||||
|
||||
/** Physical file path. */
|
||||
const char* m_filepath;
|
||||
|
||||
/** Covering transaction. */
|
||||
trx_t* m_trx;
|
||||
|
||||
|
|
@ -565,9 +620,9 @@ AbstractCallback::init(
|
|||
/* Since we don't know whether it is a compressed table
|
||||
or not, the data is always read into the block->frame. */
|
||||
|
||||
dberr_t err = set_zip_size(block->frame);
|
||||
m_zip_size = fsp_header_get_zip_size(page);
|
||||
|
||||
if (err != DB_SUCCESS) {
|
||||
if (!ut_is_2pow(m_zip_size) || m_zip_size > UNIV_ZIP_SIZE_MAX) {
|
||||
return(DB_CORRUPTION);
|
||||
}
|
||||
|
||||
|
|
@ -604,11 +659,7 @@ AbstractCallback::init(
|
|||
m_free_limit = mach_read_from_4(page + FSP_FREE_LIMIT);
|
||||
m_space = mach_read_from_4(page + FSP_HEADER_OFFSET + FSP_SPACE_ID);
|
||||
|
||||
if ((err = set_current_xdes(0, page)) != DB_SUCCESS) {
|
||||
return(err);
|
||||
}
|
||||
|
||||
return(DB_SUCCESS);
|
||||
return set_current_xdes(0, page);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1596,6 +1647,7 @@ IndexPurge::purge() UNIV_NOTHROW
|
|||
Constructor
|
||||
* @param cfg - config of table being imported.
|
||||
* @param trx - transaction covering the import */
|
||||
inline
|
||||
PageConverter::PageConverter(
|
||||
row_import* cfg,
|
||||
trx_t* trx)
|
||||
|
|
@ -1620,6 +1672,7 @@ Adjust the BLOB reference for a single column that is externally stored
|
|||
@param offsets - column offsets for the record
|
||||
@param i - column ordinal value
|
||||
@return DB_SUCCESS or error code */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::adjust_cluster_index_blob_column(
|
||||
rec_t* rec,
|
||||
|
|
@ -1672,6 +1725,7 @@ stored columns.
|
|||
@param rec - record to update
|
||||
@param offsets - column offsets for the record
|
||||
@return DB_SUCCESS or error code */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::adjust_cluster_index_blob_columns(
|
||||
rec_t* rec,
|
||||
|
|
@ -1705,6 +1759,7 @@ BLOB reference, write the new space id.
|
|||
@param rec - record to update
|
||||
@param offsets - column offsets for the record
|
||||
@return DB_SUCCESS or error code */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::adjust_cluster_index_blob_ref(
|
||||
rec_t* rec,
|
||||
|
|
@ -1728,6 +1783,7 @@ Purge delete-marked records, only if it is possible to do so without
|
|||
re-organising the B+tree.
|
||||
@param offsets - current row offsets.
|
||||
@return true if purge succeeded */
|
||||
inline
|
||||
bool
|
||||
PageConverter::purge(const ulint* offsets) UNIV_NOTHROW
|
||||
{
|
||||
|
|
@ -1752,6 +1808,7 @@ Adjust the BLOB references and sys fields for the current record.
|
|||
@param offsets - column offsets for the record
|
||||
@param deleted - true if row is delete marked
|
||||
@return DB_SUCCESS or error code. */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::adjust_cluster_record(
|
||||
const dict_index_t* index,
|
||||
|
|
@ -1780,6 +1837,7 @@ Update the BLOB refrences and write UNDO log entries for
|
|||
rows that can't be purged optimistically.
|
||||
@param block - block to update
|
||||
@retval DB_SUCCESS or error code */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::update_records(
|
||||
buf_block_t* block) UNIV_NOTHROW
|
||||
|
|
@ -1845,6 +1903,7 @@ PageConverter::update_records(
|
|||
/**
|
||||
Update the space, index id, trx id.
|
||||
@return DB_SUCCESS or error code */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::update_index_page(
|
||||
buf_block_t* block) UNIV_NOTHROW
|
||||
|
|
@ -1914,6 +1973,7 @@ PageConverter::update_index_page(
|
|||
Validate the space flags and update tablespace header page.
|
||||
@param block - block read from file, not from the buffer pool.
|
||||
@retval DB_SUCCESS or error code */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::update_header(
|
||||
buf_block_t* block) UNIV_NOTHROW
|
||||
|
|
@ -1953,6 +2013,7 @@ PageConverter::update_header(
|
|||
Update the page, set the space id, max trx id and index id.
|
||||
@param block - block read from file
|
||||
@retval DB_SUCCESS or error code */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::update_page(
|
||||
buf_block_t* block,
|
||||
|
|
@ -3423,6 +3484,436 @@ row_import_update_discarded_flag(
|
|||
return(err);
|
||||
}
|
||||
|
||||
struct fil_iterator_t {
|
||||
pfs_os_file_t file; /*!< File handle */
|
||||
const char* filepath; /*!< File path name */
|
||||
os_offset_t start; /*!< From where to start */
|
||||
os_offset_t end; /*!< Where to stop */
|
||||
os_offset_t file_size; /*!< File size in bytes */
|
||||
ulint page_size; /*!< Page size */
|
||||
ulint n_io_buffers; /*!< Number of pages to use
|
||||
for IO */
|
||||
byte* io_buffer; /*!< Buffer to use for IO */
|
||||
fil_space_crypt_t *crypt_data; /*!< Crypt data (if encrypted) */
|
||||
byte* crypt_io_buffer; /*!< IO buffer when encrypted */
|
||||
};
|
||||
|
||||
/********************************************************************//**
|
||||
TODO: This can be made parallel trivially by chunking up the file and creating
|
||||
a callback per thread. . Main benefit will be to use multiple CPUs for
|
||||
checksums and compressed tables. We have to do compressed tables block by
|
||||
block right now. Secondly we need to decompress/compress and copy too much
|
||||
of data. These are CPU intensive.
|
||||
|
||||
Iterate over all the pages in the tablespace.
|
||||
@param iter - Tablespace iterator
|
||||
@param block - block to use for IO
|
||||
@param callback - Callback to inspect and update page contents
|
||||
@retval DB_SUCCESS or error code */
|
||||
static
|
||||
dberr_t
|
||||
fil_iterate(
|
||||
/*========*/
|
||||
const fil_iterator_t& iter,
|
||||
buf_block_t* block,
|
||||
AbstractCallback& callback)
|
||||
{
|
||||
os_offset_t offset;
|
||||
ulint page_no = 0;
|
||||
ulint space_id = callback.get_space_id();
|
||||
ulint n_bytes = iter.n_io_buffers * iter.page_size;
|
||||
|
||||
ut_ad(!srv_read_only_mode);
|
||||
|
||||
/* TODO: For compressed tables we do a lot of useless
|
||||
copying for non-index pages. Unfortunately, it is
|
||||
required by buf_zip_decompress() */
|
||||
const bool row_compressed = callback.get_zip_size() > 0;
|
||||
|
||||
for (offset = iter.start; offset < iter.end; offset += n_bytes) {
|
||||
|
||||
byte* io_buffer = iter.io_buffer;
|
||||
|
||||
block->frame = io_buffer;
|
||||
|
||||
if (row_compressed) {
|
||||
page_zip_des_init(&block->page.zip);
|
||||
page_zip_set_size(&block->page.zip, iter.page_size);
|
||||
block->page.zip.data = block->frame + UNIV_PAGE_SIZE;
|
||||
ut_d(block->page.zip.m_external = true);
|
||||
ut_ad(iter.page_size == callback.get_zip_size());
|
||||
|
||||
/* Zip IO is done in the compressed page buffer. */
|
||||
io_buffer = block->page.zip.data;
|
||||
}
|
||||
|
||||
/* We have to read the exact number of bytes. Otherwise the
|
||||
InnoDB IO functions croak on failed reads. */
|
||||
|
||||
n_bytes = ulint(ut_min(os_offset_t(n_bytes),
|
||||
iter.end - offset));
|
||||
|
||||
ut_ad(n_bytes > 0);
|
||||
ut_ad(!(n_bytes % iter.page_size));
|
||||
|
||||
const bool encrypted = iter.crypt_data != NULL
|
||||
&& iter.crypt_data->should_encrypt();
|
||||
/* Use additional crypt io buffer if tablespace is encrypted */
|
||||
byte* const readptr = encrypted
|
||||
? iter.crypt_io_buffer : io_buffer;
|
||||
byte* const writeptr = readptr;
|
||||
|
||||
if (!os_file_read(iter.file, readptr, offset, n_bytes)) {
|
||||
ib_logf(IB_LOG_LEVEL_ERROR, "os_file_read() failed");
|
||||
return DB_IO_ERROR;
|
||||
}
|
||||
|
||||
bool updated = false;
|
||||
os_offset_t page_off = offset;
|
||||
ulint n_pages_read = (ulint) n_bytes / iter.page_size;
|
||||
bool decrypted = false;
|
||||
|
||||
for (ulint i = 0; i < n_pages_read; ++i) {
|
||||
ulint size = iter.page_size;
|
||||
dberr_t err = DB_SUCCESS;
|
||||
byte* src = readptr + (i * size);
|
||||
byte* dst = io_buffer + (i * size);
|
||||
bool frame_changed = false;
|
||||
|
||||
ulint page_type = mach_read_from_2(src+FIL_PAGE_TYPE);
|
||||
|
||||
const bool page_compressed
|
||||
= page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED
|
||||
|| page_type == FIL_PAGE_PAGE_COMPRESSED;
|
||||
|
||||
/* If tablespace is encrypted, we need to decrypt
|
||||
the page. Note that tablespaces are not in
|
||||
fil_system during import. */
|
||||
if (encrypted) {
|
||||
decrypted = fil_space_decrypt(
|
||||
iter.crypt_data,
|
||||
dst, //dst
|
||||
iter.page_size,
|
||||
src, // src
|
||||
&err);
|
||||
|
||||
if (err != DB_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (decrypted) {
|
||||
updated = true;
|
||||
} else {
|
||||
if (!page_compressed && !row_compressed) {
|
||||
block->frame = src;
|
||||
frame_changed = true;
|
||||
} else {
|
||||
memcpy(dst, src, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If the original page is page_compressed, we need
|
||||
to decompress it before adjusting further. */
|
||||
if (page_compressed) {
|
||||
fil_decompress_page(NULL, dst, ulong(size),
|
||||
NULL);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
buf_block_set_file_page(block, space_id, page_no++);
|
||||
|
||||
if ((err = callback(page_off, block)) != DB_SUCCESS) {
|
||||
return err;
|
||||
} else if (!updated) {
|
||||
updated = buf_block_get_state(block)
|
||||
== BUF_BLOCK_FILE_PAGE;
|
||||
}
|
||||
|
||||
/* If tablespace is encrypted we use additional
|
||||
temporary scratch area where pages are read
|
||||
for decrypting readptr == crypt_io_buffer != io_buffer.
|
||||
|
||||
Destination for decryption is a buffer pool block
|
||||
block->frame == dst == io_buffer that is updated.
|
||||
Pages that did not require decryption even when
|
||||
tablespace is marked as encrypted are not copied
|
||||
instead block->frame is set to src == readptr.
|
||||
|
||||
For encryption we again use temporary scratch area
|
||||
writeptr != io_buffer == dst
|
||||
that is then written to the tablespace
|
||||
|
||||
(1) For normal tables io_buffer == dst == writeptr
|
||||
(2) For only page compressed tables
|
||||
io_buffer == dst == writeptr
|
||||
(3) For encrypted (and page compressed)
|
||||
readptr != io_buffer == dst != writeptr
|
||||
*/
|
||||
|
||||
ut_ad(!encrypted && !page_compressed ?
|
||||
src == dst && dst == writeptr + (i * size):1);
|
||||
ut_ad(page_compressed && !encrypted ?
|
||||
src == dst && dst == writeptr + (i * size):1);
|
||||
ut_ad(encrypted ?
|
||||
src != dst && dst != writeptr + (i * size):1);
|
||||
|
||||
if (encrypted) {
|
||||
memcpy(writeptr + (i * size),
|
||||
row_compressed ? block->page.zip.data :
|
||||
block->frame, size);
|
||||
}
|
||||
|
||||
if (frame_changed) {
|
||||
block->frame = dst;
|
||||
}
|
||||
|
||||
src = io_buffer + (i * size);
|
||||
|
||||
if (page_compressed) {
|
||||
ulint len = 0;
|
||||
|
||||
fil_compress_page(
|
||||
NULL,
|
||||
src,
|
||||
NULL,
|
||||
size,
|
||||
0,/* FIXME: compression level */
|
||||
512,/* FIXME: use proper block size */
|
||||
encrypted,
|
||||
&len);
|
||||
|
||||
updated = true;
|
||||
}
|
||||
|
||||
/* If tablespace is encrypted, encrypt page before we
|
||||
write it back. Note that we should not encrypt the
|
||||
buffer that is in buffer pool. */
|
||||
/* NOTE: At this stage of IMPORT the
|
||||
buffer pool is not being used at all! */
|
||||
if (decrypted && encrypted) {
|
||||
byte *dest = writeptr + (i * size);
|
||||
ulint space = mach_read_from_4(
|
||||
src + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
|
||||
ulint offset = mach_read_from_4(src + FIL_PAGE_OFFSET);
|
||||
ib_uint64_t lsn = mach_read_from_8(src + FIL_PAGE_LSN);
|
||||
|
||||
byte* tmp = fil_encrypt_buf(
|
||||
iter.crypt_data,
|
||||
space,
|
||||
offset,
|
||||
lsn,
|
||||
src,
|
||||
iter.page_size == UNIV_PAGE_SIZE ? 0 : iter.page_size,
|
||||
dest);
|
||||
|
||||
if (tmp == src) {
|
||||
/* TODO: remove unnecessary memcpy's */
|
||||
memcpy(dest, src, size);
|
||||
}
|
||||
|
||||
updated = true;
|
||||
}
|
||||
|
||||
page_off += iter.page_size;
|
||||
block->frame += iter.page_size;
|
||||
}
|
||||
|
||||
/* A page was updated in the set, write back to disk. */
|
||||
if (updated
|
||||
&& !os_file_write(
|
||||
iter.filepath, iter.file, writeptr,
|
||||
offset, (ulint) n_bytes)) {
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_ERROR, "os_file_write() failed");
|
||||
return DB_IO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return DB_SUCCESS;
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Iterate over all the pages in the tablespace.
|
||||
@param table - the table definiton in the server
|
||||
@param n_io_buffers - number of blocks to read and write together
|
||||
@param callback - functor that will do the page updates
|
||||
@return DB_SUCCESS or error code */
|
||||
static
|
||||
dberr_t
|
||||
fil_tablespace_iterate(
|
||||
/*===================*/
|
||||
dict_table_t* table,
|
||||
ulint n_io_buffers,
|
||||
AbstractCallback& callback)
|
||||
{
|
||||
dberr_t err;
|
||||
pfs_os_file_t file;
|
||||
char* filepath;
|
||||
|
||||
ut_a(n_io_buffers > 0);
|
||||
ut_ad(!srv_read_only_mode);
|
||||
|
||||
DBUG_EXECUTE_IF("ib_import_trigger_corruption_1",
|
||||
return(DB_CORRUPTION););
|
||||
|
||||
if (DICT_TF_HAS_DATA_DIR(table->flags)) {
|
||||
dict_get_and_save_data_dir_path(table, false);
|
||||
ut_a(table->data_dir_path);
|
||||
|
||||
filepath = os_file_make_remote_pathname(
|
||||
table->data_dir_path, table->name, "ibd");
|
||||
} else {
|
||||
filepath = fil_make_ibd_name(table->name, false);
|
||||
}
|
||||
|
||||
{
|
||||
ibool success;
|
||||
|
||||
file = os_file_create_simple_no_error_handling(
|
||||
innodb_file_data_key, filepath,
|
||||
OS_FILE_OPEN, OS_FILE_READ_WRITE, &success, FALSE);
|
||||
|
||||
DBUG_EXECUTE_IF("fil_tablespace_iterate_failure",
|
||||
{
|
||||
static bool once;
|
||||
|
||||
if (!once || ut_rnd_interval(0, 10) == 5) {
|
||||
once = true;
|
||||
success = FALSE;
|
||||
os_file_close(file);
|
||||
}
|
||||
});
|
||||
|
||||
if (!success) {
|
||||
/* The following call prints an error message */
|
||||
os_file_get_last_error(true);
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_ERROR,
|
||||
"Trying to import a tablespace, but could not "
|
||||
"open the tablespace file %s", filepath);
|
||||
|
||||
mem_free(filepath);
|
||||
|
||||
return(DB_TABLESPACE_NOT_FOUND);
|
||||
|
||||
} else {
|
||||
err = DB_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
callback.set_file(filepath, file);
|
||||
|
||||
os_offset_t file_size = os_file_get_size(file);
|
||||
ut_a(file_size != (os_offset_t) -1);
|
||||
|
||||
/* Allocate a page to read in the tablespace header, so that we
|
||||
can determine the page size and zip_size (if it is compressed).
|
||||
We allocate an extra page in case it is a compressed table. One
|
||||
page is to ensure alignement. */
|
||||
|
||||
void* page_ptr = mem_alloc(3 * UNIV_PAGE_SIZE);
|
||||
byte* page = static_cast<byte*>(ut_align(page_ptr, UNIV_PAGE_SIZE));
|
||||
|
||||
/* The block we will use for every physical page */
|
||||
buf_block_t block;
|
||||
|
||||
memset(&block, 0, sizeof block);
|
||||
block.frame = page;
|
||||
block.page.io_fix = BUF_IO_NONE;
|
||||
block.page.buf_fix_count = 1;
|
||||
block.page.state = BUF_BLOCK_FILE_PAGE;
|
||||
|
||||
/* Read the first page and determine the page and zip size. */
|
||||
|
||||
if (!os_file_read(file, page, 0, UNIV_PAGE_SIZE)) {
|
||||
|
||||
err = DB_IO_ERROR;
|
||||
|
||||
} else if ((err = callback.init(file_size, &block)) == DB_SUCCESS) {
|
||||
fil_iterator_t iter;
|
||||
|
||||
iter.file = file;
|
||||
iter.start = 0;
|
||||
iter.end = file_size;
|
||||
iter.filepath = filepath;
|
||||
iter.file_size = file_size;
|
||||
iter.n_io_buffers = n_io_buffers;
|
||||
iter.page_size = callback.get_page_size();
|
||||
|
||||
/* In MariaDB/MySQL 5.6 tablespace does not exist
|
||||
during import, therefore we can't use space directly
|
||||
here. */
|
||||
ulint crypt_data_offset = fsp_header_get_crypt_offset(
|
||||
callback.get_zip_size());
|
||||
|
||||
/* read (optional) crypt data */
|
||||
iter.crypt_data = fil_space_read_crypt_data(
|
||||
0, page, crypt_data_offset);
|
||||
|
||||
/* Compressed pages can't be optimised for block IO for now.
|
||||
We do the IMPORT page by page. */
|
||||
|
||||
if (callback.get_zip_size() > 0) {
|
||||
iter.n_io_buffers = 1;
|
||||
ut_a(iter.page_size == callback.get_zip_size());
|
||||
}
|
||||
|
||||
/** If tablespace is encrypted, it needs extra buffers */
|
||||
if (iter.crypt_data != NULL) {
|
||||
/* decrease io buffers so that memory
|
||||
* consumption doesnt double
|
||||
* note: the +1 is to avoid n_io_buffers getting down to 0 */
|
||||
iter.n_io_buffers = (iter.n_io_buffers + 1) / 2;
|
||||
}
|
||||
|
||||
/** Add an extra page for compressed page scratch area. */
|
||||
|
||||
void* io_buffer = mem_alloc(
|
||||
(2 + iter.n_io_buffers) * UNIV_PAGE_SIZE);
|
||||
|
||||
iter.io_buffer = static_cast<byte*>(
|
||||
ut_align(io_buffer, UNIV_PAGE_SIZE));
|
||||
|
||||
void* crypt_io_buffer = NULL;
|
||||
if (iter.crypt_data != NULL) {
|
||||
crypt_io_buffer = mem_alloc(
|
||||
(2 + iter.n_io_buffers) * UNIV_PAGE_SIZE);
|
||||
iter.crypt_io_buffer = static_cast<byte*>(
|
||||
ut_align(crypt_io_buffer, UNIV_PAGE_SIZE));
|
||||
}
|
||||
|
||||
err = fil_iterate(iter, &block, callback);
|
||||
|
||||
mem_free(io_buffer);
|
||||
|
||||
if (crypt_io_buffer != NULL) {
|
||||
mem_free(crypt_io_buffer);
|
||||
iter.crypt_io_buffer = NULL;
|
||||
fil_space_destroy_crypt_data(&iter.crypt_data);
|
||||
}
|
||||
}
|
||||
|
||||
if (err == DB_SUCCESS) {
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_INFO, "Sync to disk");
|
||||
|
||||
if (!os_file_flush(file)) {
|
||||
ib_logf(IB_LOG_LEVEL_INFO, "os_file_flush() failed!");
|
||||
err = DB_IO_ERROR;
|
||||
} else {
|
||||
ib_logf(IB_LOG_LEVEL_INFO, "Sync to disk - done!");
|
||||
}
|
||||
}
|
||||
|
||||
os_file_close(file);
|
||||
|
||||
mem_free(page_ptr);
|
||||
mem_free(filepath);
|
||||
|
||||
return(err);
|
||||
}
|
||||
|
||||
/*****************************************************************//**
|
||||
Imports a tablespace. The space id in the .ibd file must match the space id
|
||||
of the table in the data dictionary.
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@ Created 10/25/1995 Heikki Tuuri
|
|||
*******************************************************/
|
||||
|
||||
#include "fil0fil.h"
|
||||
#include "fil0pagecompress.h"
|
||||
#include "fsp0pagecompress.h"
|
||||
#include "fil0crypt.h"
|
||||
|
||||
#include <debug_sync.h>
|
||||
|
|
@ -49,12 +47,10 @@ Created 10/25/1995 Heikki Tuuri
|
|||
#include "page0zip.h"
|
||||
#include "trx0sys.h"
|
||||
#include "row0mysql.h"
|
||||
#include "os0file.h"
|
||||
#ifndef UNIV_HOTBACKUP
|
||||
# include "buf0lru.h"
|
||||
# include "ibuf0ibuf.h"
|
||||
# include "sync0sync.h"
|
||||
# include "os0sync.h"
|
||||
#else /* !UNIV_HOTBACKUP */
|
||||
# include "srv0srv.h"
|
||||
static ulint srv_data_read, srv_data_written;
|
||||
|
|
@ -704,7 +700,7 @@ add_size:
|
|||
space->size += node->size;
|
||||
}
|
||||
|
||||
ulint atomic_writes = fsp_flags_get_atomic_writes(space->flags);
|
||||
ulint atomic_writes = FSP_FLAGS_GET_ATOMIC_WRITES(space->flags);
|
||||
|
||||
/* printf("Opening file %s\n", node->name); */
|
||||
|
||||
|
|
@ -4110,7 +4106,6 @@ fil_open_single_table_tablespace(
|
|||
fsp_open_info remote;
|
||||
ulint tablespaces_found = 0;
|
||||
ulint valid_tablespaces_found = 0;
|
||||
ulint atomic_writes = 0;
|
||||
fil_space_crypt_t* crypt_data = NULL;
|
||||
|
||||
#ifdef UNIV_SYNC_DEBUG
|
||||
|
|
@ -4125,7 +4120,7 @@ fil_open_single_table_tablespace(
|
|||
}
|
||||
|
||||
ut_ad(fsp_flags_is_valid(flags & ~FSP_FLAGS_MEM_MASK, id));
|
||||
atomic_writes = fsp_flags_get_atomic_writes(flags);
|
||||
const ulint atomic_writes = FSP_FLAGS_GET_ATOMIC_WRITES(flags);
|
||||
|
||||
memset(&def, 0, sizeof(def));
|
||||
memset(&dict, 0, sizeof(dict));
|
||||
|
|
@ -6151,7 +6146,8 @@ fil_io(
|
|||
} else if (type == OS_FILE_WRITE) {
|
||||
ut_ad(!srv_read_only_mode);
|
||||
srv_stats.data_written.add(len);
|
||||
if (fil_page_is_index_page((byte *)buf)) {
|
||||
if (mach_read_from_2(static_cast<const byte*>(buf)
|
||||
+ FIL_PAGE_TYPE) == FIL_PAGE_INDEX) {
|
||||
srv_stats.index_pages_written.inc();
|
||||
} else {
|
||||
srv_stats.non_index_pages_written.inc();
|
||||
|
|
@ -6683,479 +6679,6 @@ fil_close(void)
|
|||
fil_system = NULL;
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Initializes a buffer control block when the buf_pool is created. */
|
||||
static
|
||||
void
|
||||
fil_buf_block_init(
|
||||
/*===============*/
|
||||
buf_block_t* block, /*!< in: pointer to control block */
|
||||
byte* frame) /*!< in: pointer to buffer frame */
|
||||
{
|
||||
UNIV_MEM_DESC(frame, UNIV_PAGE_SIZE);
|
||||
|
||||
block->frame = frame;
|
||||
|
||||
block->page.io_fix = BUF_IO_NONE;
|
||||
/* There are assertions that check for this. */
|
||||
block->page.buf_fix_count = 1;
|
||||
block->page.state = BUF_BLOCK_READY_FOR_USE;
|
||||
|
||||
page_zip_des_init(&block->page.zip);
|
||||
}
|
||||
|
||||
struct fil_iterator_t {
|
||||
pfs_os_file_t file; /*!< File handle */
|
||||
const char* filepath; /*!< File path name */
|
||||
os_offset_t start; /*!< From where to start */
|
||||
os_offset_t end; /*!< Where to stop */
|
||||
os_offset_t file_size; /*!< File size in bytes */
|
||||
ulint page_size; /*!< Page size */
|
||||
ulint n_io_buffers; /*!< Number of pages to use
|
||||
for IO */
|
||||
byte* io_buffer; /*!< Buffer to use for IO */
|
||||
fil_space_crypt_t *crypt_data; /*!< Crypt data (if encrypted) */
|
||||
byte* crypt_io_buffer; /*!< IO buffer when encrypted */
|
||||
};
|
||||
|
||||
/********************************************************************//**
|
||||
TODO: This can be made parallel trivially by chunking up the file and creating
|
||||
a callback per thread. . Main benefit will be to use multiple CPUs for
|
||||
checksums and compressed tables. We have to do compressed tables block by
|
||||
block right now. Secondly we need to decompress/compress and copy too much
|
||||
of data. These are CPU intensive.
|
||||
|
||||
Iterate over all the pages in the tablespace.
|
||||
@param iter - Tablespace iterator
|
||||
@param block - block to use for IO
|
||||
@param callback - Callback to inspect and update page contents
|
||||
@retval DB_SUCCESS or error code */
|
||||
static
|
||||
dberr_t
|
||||
fil_iterate(
|
||||
/*========*/
|
||||
const fil_iterator_t& iter,
|
||||
buf_block_t* block,
|
||||
PageCallback& callback)
|
||||
{
|
||||
os_offset_t offset;
|
||||
ulint page_no = 0;
|
||||
ulint space_id = callback.get_space_id();
|
||||
ulint n_bytes = iter.n_io_buffers * iter.page_size;
|
||||
|
||||
ut_ad(!srv_read_only_mode);
|
||||
|
||||
/* TODO: For compressed tables we do a lot of useless
|
||||
copying for non-index pages. Unfortunately, it is
|
||||
required by buf_zip_decompress() */
|
||||
const bool row_compressed = callback.get_zip_size() > 0;
|
||||
|
||||
for (offset = iter.start; offset < iter.end; offset += n_bytes) {
|
||||
|
||||
byte* io_buffer = iter.io_buffer;
|
||||
|
||||
block->frame = io_buffer;
|
||||
|
||||
if (row_compressed) {
|
||||
page_zip_des_init(&block->page.zip);
|
||||
page_zip_set_size(&block->page.zip, iter.page_size);
|
||||
block->page.zip.data = block->frame + UNIV_PAGE_SIZE;
|
||||
ut_d(block->page.zip.m_external = true);
|
||||
ut_ad(iter.page_size == callback.get_zip_size());
|
||||
|
||||
/* Zip IO is done in the compressed page buffer. */
|
||||
io_buffer = block->page.zip.data;
|
||||
}
|
||||
|
||||
/* We have to read the exact number of bytes. Otherwise the
|
||||
InnoDB IO functions croak on failed reads. */
|
||||
|
||||
n_bytes = static_cast<ulint>(
|
||||
ut_min(static_cast<os_offset_t>(n_bytes),
|
||||
iter.end - offset));
|
||||
|
||||
ut_ad(n_bytes > 0);
|
||||
ut_ad(!(n_bytes % iter.page_size));
|
||||
|
||||
const bool encrypted = iter.crypt_data != NULL
|
||||
&& iter.crypt_data->should_encrypt();
|
||||
/* Use additional crypt io buffer if tablespace is encrypted */
|
||||
byte* const readptr = encrypted
|
||||
? iter.crypt_io_buffer : io_buffer;
|
||||
byte* const writeptr = readptr;
|
||||
|
||||
if (!os_file_read(iter.file, readptr, offset, (ulint) n_bytes)) {
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_ERROR, "os_file_read() failed");
|
||||
|
||||
return(DB_IO_ERROR);
|
||||
}
|
||||
|
||||
bool updated = false;
|
||||
os_offset_t page_off = offset;
|
||||
ulint n_pages_read = (ulint) n_bytes / iter.page_size;
|
||||
bool decrypted = false;
|
||||
|
||||
for (ulint i = 0; i < n_pages_read; ++i) {
|
||||
ulint size = iter.page_size;
|
||||
dberr_t err = DB_SUCCESS;
|
||||
byte* src = readptr + (i * size);
|
||||
byte* dst = io_buffer + (i * size);
|
||||
bool frame_changed = false;
|
||||
|
||||
ulint page_type = mach_read_from_2(src+FIL_PAGE_TYPE);
|
||||
|
||||
const bool page_compressed
|
||||
= page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED
|
||||
|| page_type == FIL_PAGE_PAGE_COMPRESSED;
|
||||
|
||||
/* If tablespace is encrypted, we need to decrypt
|
||||
the page. Note that tablespaces are not in
|
||||
fil_system during import. */
|
||||
if (encrypted) {
|
||||
decrypted = fil_space_decrypt(
|
||||
iter.crypt_data,
|
||||
dst, //dst
|
||||
iter.page_size,
|
||||
src, // src
|
||||
&err); // src
|
||||
|
||||
if (err != DB_SUCCESS) {
|
||||
return(err);
|
||||
}
|
||||
|
||||
if (decrypted) {
|
||||
updated = true;
|
||||
} else {
|
||||
if (!page_compressed && !row_compressed) {
|
||||
block->frame = src;
|
||||
frame_changed = true;
|
||||
} else {
|
||||
memcpy(dst, src, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If the original page is page_compressed, we need
|
||||
to decompress page before we can update it. */
|
||||
if (page_compressed) {
|
||||
fil_decompress_page(NULL, dst, ulong(size),
|
||||
NULL);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
buf_block_set_file_page(block, space_id, page_no++);
|
||||
|
||||
if ((err = callback(page_off, block)) != DB_SUCCESS) {
|
||||
|
||||
return(err);
|
||||
|
||||
} else if (!updated) {
|
||||
updated = buf_block_get_state(block)
|
||||
== BUF_BLOCK_FILE_PAGE;
|
||||
}
|
||||
|
||||
buf_block_set_state(block, BUF_BLOCK_NOT_USED);
|
||||
buf_block_set_state(block, BUF_BLOCK_READY_FOR_USE);
|
||||
|
||||
/* If tablespace is encrypted we use additional
|
||||
temporary scratch area where pages are read
|
||||
for decrypting readptr == crypt_io_buffer != io_buffer.
|
||||
|
||||
Destination for decryption is a buffer pool block
|
||||
block->frame == dst == io_buffer that is updated.
|
||||
Pages that did not require decryption even when
|
||||
tablespace is marked as encrypted are not copied
|
||||
instead block->frame is set to src == readptr.
|
||||
|
||||
For encryption we again use temporary scratch area
|
||||
writeptr != io_buffer == dst
|
||||
that is then written to the tablespace
|
||||
|
||||
(1) For normal tables io_buffer == dst == writeptr
|
||||
(2) For only page compressed tables
|
||||
io_buffer == dst == writeptr
|
||||
(3) For encrypted (and page compressed)
|
||||
readptr != io_buffer == dst != writeptr
|
||||
*/
|
||||
|
||||
ut_ad(!encrypted && !page_compressed ?
|
||||
src == dst && dst == writeptr + (i * size):1);
|
||||
ut_ad(page_compressed && !encrypted ?
|
||||
src == dst && dst == writeptr + (i * size):1);
|
||||
ut_ad(encrypted ?
|
||||
src != dst && dst != writeptr + (i * size):1);
|
||||
|
||||
if (encrypted) {
|
||||
memcpy(writeptr + (i * size),
|
||||
row_compressed ? block->page.zip.data :
|
||||
block->frame, size);
|
||||
}
|
||||
|
||||
if (frame_changed) {
|
||||
block->frame = dst;
|
||||
}
|
||||
|
||||
src = io_buffer + (i * size);
|
||||
|
||||
if (page_compressed) {
|
||||
ulint len = 0;
|
||||
|
||||
fil_compress_page(
|
||||
NULL,
|
||||
src,
|
||||
NULL,
|
||||
size,
|
||||
0,/* FIXME: compression level */
|
||||
512,/* FIXME: use proper block size */
|
||||
encrypted,
|
||||
&len);
|
||||
|
||||
updated = true;
|
||||
}
|
||||
|
||||
/* If tablespace is encrypted, encrypt page before we
|
||||
write it back. Note that we should not encrypt the
|
||||
buffer that is in buffer pool. */
|
||||
/* NOTE: At this stage of IMPORT the
|
||||
buffer pool is not being used at all! */
|
||||
if (decrypted && encrypted) {
|
||||
byte *dest = writeptr + (i * size);
|
||||
ulint space = mach_read_from_4(
|
||||
src + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
|
||||
ulint offset = mach_read_from_4(src + FIL_PAGE_OFFSET);
|
||||
ib_uint64_t lsn = mach_read_from_8(src + FIL_PAGE_LSN);
|
||||
|
||||
byte* tmp = fil_encrypt_buf(
|
||||
iter.crypt_data,
|
||||
space,
|
||||
offset,
|
||||
lsn,
|
||||
src,
|
||||
iter.page_size == UNIV_PAGE_SIZE ? 0 : iter.page_size,
|
||||
dest);
|
||||
|
||||
if (tmp == src) {
|
||||
/* TODO: remove unnecessary memcpy's */
|
||||
memcpy(dest, src, size);
|
||||
}
|
||||
|
||||
updated = true;
|
||||
}
|
||||
|
||||
page_off += iter.page_size;
|
||||
block->frame += iter.page_size;
|
||||
}
|
||||
|
||||
/* A page was updated in the set, write back to disk. */
|
||||
if (updated
|
||||
&& !os_file_write(
|
||||
iter.filepath, iter.file, writeptr,
|
||||
offset, (ulint) n_bytes)) {
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_ERROR, "os_file_write() failed");
|
||||
|
||||
return(DB_IO_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
return(DB_SUCCESS);
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Iterate over all the pages in the tablespace.
|
||||
@param table - the table definiton in the server
|
||||
@param n_io_buffers - number of blocks to read and write together
|
||||
@param callback - functor that will do the page updates
|
||||
@return DB_SUCCESS or error code */
|
||||
UNIV_INTERN
|
||||
dberr_t
|
||||
fil_tablespace_iterate(
|
||||
/*===================*/
|
||||
dict_table_t* table,
|
||||
ulint n_io_buffers,
|
||||
PageCallback& callback)
|
||||
{
|
||||
dberr_t err;
|
||||
pfs_os_file_t file;
|
||||
char* filepath;
|
||||
|
||||
ut_a(n_io_buffers > 0);
|
||||
ut_ad(!srv_read_only_mode);
|
||||
|
||||
DBUG_EXECUTE_IF("ib_import_trigger_corruption_1",
|
||||
return(DB_CORRUPTION););
|
||||
|
||||
if (DICT_TF_HAS_DATA_DIR(table->flags)) {
|
||||
dict_get_and_save_data_dir_path(table, false);
|
||||
ut_a(table->data_dir_path);
|
||||
|
||||
filepath = os_file_make_remote_pathname(
|
||||
table->data_dir_path, table->name, "ibd");
|
||||
} else {
|
||||
filepath = fil_make_ibd_name(table->name, false);
|
||||
}
|
||||
|
||||
{
|
||||
ibool success;
|
||||
|
||||
file = os_file_create_simple_no_error_handling(
|
||||
innodb_file_data_key, filepath,
|
||||
OS_FILE_OPEN, OS_FILE_READ_WRITE, &success, FALSE);
|
||||
|
||||
DBUG_EXECUTE_IF("fil_tablespace_iterate_failure",
|
||||
{
|
||||
static bool once;
|
||||
|
||||
if (!once || ut_rnd_interval(0, 10) == 5) {
|
||||
once = true;
|
||||
success = FALSE;
|
||||
os_file_close(file);
|
||||
}
|
||||
});
|
||||
|
||||
if (!success) {
|
||||
/* The following call prints an error message */
|
||||
os_file_get_last_error(true);
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_ERROR,
|
||||
"Trying to import a tablespace, but could not "
|
||||
"open the tablespace file %s", filepath);
|
||||
|
||||
mem_free(filepath);
|
||||
|
||||
return(DB_TABLESPACE_NOT_FOUND);
|
||||
|
||||
} else {
|
||||
err = DB_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
callback.set_file(filepath, file);
|
||||
|
||||
os_offset_t file_size = os_file_get_size(file);
|
||||
ut_a(file_size != (os_offset_t) -1);
|
||||
|
||||
/* The block we will use for every physical page */
|
||||
buf_block_t block;
|
||||
|
||||
memset(&block, 0x0, sizeof(block));
|
||||
|
||||
/* Allocate a page to read in the tablespace header, so that we
|
||||
can determine the page size and zip_size (if it is compressed).
|
||||
We allocate an extra page in case it is a compressed table. One
|
||||
page is to ensure alignement. */
|
||||
|
||||
void* page_ptr = mem_alloc(3 * UNIV_PAGE_SIZE);
|
||||
byte* page = static_cast<byte*>(ut_align(page_ptr, UNIV_PAGE_SIZE));
|
||||
|
||||
fil_buf_block_init(&block, page);
|
||||
|
||||
/* Read the first page and determine the page and zip size. */
|
||||
|
||||
if (!os_file_read(file, page, 0, UNIV_PAGE_SIZE)) {
|
||||
|
||||
err = DB_IO_ERROR;
|
||||
|
||||
} else if ((err = callback.init(file_size, &block)) == DB_SUCCESS) {
|
||||
fil_iterator_t iter;
|
||||
|
||||
iter.file = file;
|
||||
iter.start = 0;
|
||||
iter.end = file_size;
|
||||
iter.filepath = filepath;
|
||||
iter.file_size = file_size;
|
||||
iter.n_io_buffers = n_io_buffers;
|
||||
iter.page_size = callback.get_page_size();
|
||||
|
||||
/* In MariaDB/MySQL 5.6 tablespace does not exist
|
||||
during import, therefore we can't use space directly
|
||||
here. */
|
||||
ulint crypt_data_offset = fsp_header_get_crypt_offset(
|
||||
callback.get_zip_size());
|
||||
|
||||
/* read (optional) crypt data */
|
||||
iter.crypt_data = fil_space_read_crypt_data(
|
||||
0, page, crypt_data_offset);
|
||||
|
||||
/* Compressed pages can't be optimised for block IO for now.
|
||||
We do the IMPORT page by page. */
|
||||
|
||||
if (callback.get_zip_size() > 0) {
|
||||
iter.n_io_buffers = 1;
|
||||
ut_a(iter.page_size == callback.get_zip_size());
|
||||
}
|
||||
|
||||
/** If tablespace is encrypted, it needs extra buffers */
|
||||
if (iter.crypt_data != NULL) {
|
||||
/* decrease io buffers so that memory
|
||||
* consumption doesnt double
|
||||
* note: the +1 is to avoid n_io_buffers getting down to 0 */
|
||||
iter.n_io_buffers = (iter.n_io_buffers + 1) / 2;
|
||||
}
|
||||
|
||||
/** Add an extra page for compressed page scratch area. */
|
||||
|
||||
void* io_buffer = mem_alloc(
|
||||
(2 + iter.n_io_buffers) * UNIV_PAGE_SIZE);
|
||||
|
||||
iter.io_buffer = static_cast<byte*>(
|
||||
ut_align(io_buffer, UNIV_PAGE_SIZE));
|
||||
|
||||
void* crypt_io_buffer = NULL;
|
||||
if (iter.crypt_data != NULL) {
|
||||
crypt_io_buffer = mem_alloc(
|
||||
(2 + iter.n_io_buffers) * UNIV_PAGE_SIZE);
|
||||
iter.crypt_io_buffer = static_cast<byte*>(
|
||||
ut_align(crypt_io_buffer, UNIV_PAGE_SIZE));
|
||||
}
|
||||
|
||||
err = fil_iterate(iter, &block, callback);
|
||||
|
||||
mem_free(io_buffer);
|
||||
|
||||
if (crypt_io_buffer != NULL) {
|
||||
mem_free(crypt_io_buffer);
|
||||
iter.crypt_io_buffer = NULL;
|
||||
fil_space_destroy_crypt_data(&iter.crypt_data);
|
||||
}
|
||||
}
|
||||
|
||||
if (err == DB_SUCCESS) {
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_INFO, "Sync to disk");
|
||||
|
||||
if (!os_file_flush(file)) {
|
||||
ib_logf(IB_LOG_LEVEL_INFO, "os_file_flush() failed!");
|
||||
err = DB_IO_ERROR;
|
||||
} else {
|
||||
ib_logf(IB_LOG_LEVEL_INFO, "Sync to disk - done!");
|
||||
}
|
||||
}
|
||||
|
||||
os_file_close(file);
|
||||
|
||||
mem_free(page_ptr);
|
||||
mem_free(filepath);
|
||||
|
||||
return(err);
|
||||
}
|
||||
|
||||
/**
|
||||
Set the tablespace compressed table size.
|
||||
@return DB_SUCCESS if it is valie or DB_CORRUPTION if not */
|
||||
dberr_t
|
||||
PageCallback::set_zip_size(const buf_frame_t* page) UNIV_NOTHROW
|
||||
{
|
||||
m_zip_size = fsp_header_get_zip_size(page);
|
||||
|
||||
if (!ut_is_2pow(m_zip_size) || m_zip_size > UNIV_ZIP_SIZE_MAX) {
|
||||
return(DB_CORRUPTION);
|
||||
}
|
||||
|
||||
return(DB_SUCCESS);
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Delete the tablespace file and any related files like .cfg.
|
||||
This should not be called for temporary tables. */
|
||||
|
|
|
|||
|
|
@ -1309,107 +1309,6 @@ fil_delete_file(
|
|||
/*============*/
|
||||
const char* path); /*!< in: filepath of the ibd tablespace */
|
||||
|
||||
/** Callback functor. */
|
||||
struct PageCallback {
|
||||
|
||||
/**
|
||||
Default constructor */
|
||||
PageCallback()
|
||||
:
|
||||
m_zip_size(),
|
||||
m_page_size(),
|
||||
m_filepath() UNIV_NOTHROW {}
|
||||
|
||||
virtual ~PageCallback() UNIV_NOTHROW {}
|
||||
|
||||
/**
|
||||
Called for page 0 in the tablespace file at the start.
|
||||
@param file_size - size of the file in bytes
|
||||
@param block - contents of the first page in the tablespace file
|
||||
@retval DB_SUCCESS or error code.*/
|
||||
virtual dberr_t init(
|
||||
os_offset_t file_size,
|
||||
const buf_block_t* block) UNIV_NOTHROW = 0;
|
||||
|
||||
/**
|
||||
Called for every page in the tablespace. If the page was not
|
||||
updated then its state must be set to BUF_PAGE_NOT_USED. For
|
||||
compressed tables the page descriptor memory will be at offset:
|
||||
block->frame + UNIV_PAGE_SIZE;
|
||||
@param offset - physical offset within the file
|
||||
@param block - block read from file, note it is not from the buffer pool
|
||||
@retval DB_SUCCESS or error code. */
|
||||
virtual dberr_t operator()(
|
||||
os_offset_t offset,
|
||||
buf_block_t* block) UNIV_NOTHROW = 0;
|
||||
|
||||
/**
|
||||
Set the name of the physical file and the file handle that is used
|
||||
to open it for the file that is being iterated over.
|
||||
@param filename - then physical name of the tablespace file.
|
||||
@param file - OS file handle */
|
||||
void set_file(const char* filename, pfs_os_file_t file) UNIV_NOTHROW
|
||||
{
|
||||
m_file = file;
|
||||
m_filepath = filename;
|
||||
}
|
||||
|
||||
/**
|
||||
@return the space id of the tablespace */
|
||||
virtual ulint get_space_id() const UNIV_NOTHROW = 0;
|
||||
|
||||
/** The compressed page size
|
||||
@return the compressed page size */
|
||||
ulint get_zip_size() const
|
||||
{
|
||||
return(m_zip_size);
|
||||
}
|
||||
|
||||
/**
|
||||
Set the tablespace compressed table size.
|
||||
@return DB_SUCCESS if it is valie or DB_CORRUPTION if not */
|
||||
dberr_t set_zip_size(const buf_frame_t* page) UNIV_NOTHROW;
|
||||
|
||||
/** The compressed page size
|
||||
@return the compressed page size */
|
||||
ulint get_page_size() const
|
||||
{
|
||||
return(m_page_size);
|
||||
}
|
||||
|
||||
/** Compressed table page size */
|
||||
ulint m_zip_size;
|
||||
|
||||
/** The tablespace page size. */
|
||||
ulint m_page_size;
|
||||
|
||||
/** File handle to the tablespace */
|
||||
pfs_os_file_t m_file;
|
||||
|
||||
/** Physical file path. */
|
||||
const char* m_filepath;
|
||||
|
||||
protected:
|
||||
// Disable copying
|
||||
PageCallback(const PageCallback&);
|
||||
PageCallback& operator=(const PageCallback&);
|
||||
};
|
||||
|
||||
/********************************************************************//**
|
||||
Iterate over all the pages in the tablespace.
|
||||
@param table - the table definiton in the server
|
||||
@param n_io_buffers - number of blocks to read and write together
|
||||
@param callback - functor that will do the page updates
|
||||
@return DB_SUCCESS or error code */
|
||||
UNIV_INTERN
|
||||
dberr_t
|
||||
fil_tablespace_iterate(
|
||||
/*===================*/
|
||||
dict_table_t* table,
|
||||
ulint n_io_buffers,
|
||||
PageCallback& callback)
|
||||
MY_ATTRIBUTE((nonnull, warn_unused_result));
|
||||
|
||||
/*******************************************************************//**
|
||||
Checks if a single-table tablespace for a given table name exists in the
|
||||
tablespace memory cache.
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ Created 2012-02-08 by Sunny Bains.
|
|||
#include "row0mysql.h"
|
||||
#include "srv0start.h"
|
||||
#include "row0quiesce.h"
|
||||
#include "buf0buf.h"
|
||||
#include "fil0pagecompress.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -362,7 +362,8 @@ private:
|
|||
|
||||
/** Functor that is called for each physical page that is read from the
|
||||
tablespace file. */
|
||||
class AbstractCallback : public PageCallback {
|
||||
class AbstractCallback
|
||||
{
|
||||
public:
|
||||
/** Constructor
|
||||
@param trx - covering transaction */
|
||||
|
|
@ -395,6 +396,47 @@ public:
|
|||
return(get_zip_size() > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
Set the name of the physical file and the file handle that is used
|
||||
to open it for the file that is being iterated over.
|
||||
@param filename - then physical name of the tablespace file.
|
||||
@param file - OS file handle */
|
||||
void set_file(const char* filename, pfs_os_file_t file) UNIV_NOTHROW
|
||||
{
|
||||
m_file = file;
|
||||
m_filepath = filename;
|
||||
}
|
||||
|
||||
/** The compressed page size
|
||||
@return the compressed page size */
|
||||
ulint get_zip_size() const
|
||||
{
|
||||
return(m_zip_size);
|
||||
}
|
||||
|
||||
/** The compressed page size
|
||||
@return the compressed page size */
|
||||
ulint get_page_size() const
|
||||
{
|
||||
return(m_page_size);
|
||||
}
|
||||
|
||||
/**
|
||||
Called for every page in the tablespace. If the page was not
|
||||
updated then its state must be set to BUF_PAGE_NOT_USED. For
|
||||
compressed tables the page descriptor memory will be at offset:
|
||||
block->frame + UNIV_PAGE_SIZE;
|
||||
@param offset - physical offset within the file
|
||||
@param block - block read from file, note it is not from the buffer pool
|
||||
@retval DB_SUCCESS or error code. */
|
||||
virtual dberr_t operator()(
|
||||
os_offset_t offset,
|
||||
buf_block_t* block) UNIV_NOTHROW = 0;
|
||||
|
||||
/**
|
||||
@return the space id of the tablespace */
|
||||
virtual ulint get_space_id() const UNIV_NOTHROW = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
Get the data page depending on the table type, compressed or not.
|
||||
|
|
@ -510,6 +552,18 @@ protected:
|
|||
}
|
||||
|
||||
protected:
|
||||
/** Compressed table page size */
|
||||
ulint m_zip_size;
|
||||
|
||||
/** The tablespace page size. */
|
||||
ulint m_page_size;
|
||||
|
||||
/** File handle to the tablespace */
|
||||
pfs_os_file_t m_file;
|
||||
|
||||
/** Physical file path. */
|
||||
const char* m_filepath;
|
||||
|
||||
/** Covering transaction. */
|
||||
trx_t* m_trx;
|
||||
|
||||
|
|
@ -566,9 +620,9 @@ AbstractCallback::init(
|
|||
/* Since we don't know whether it is a compressed table
|
||||
or not, the data is always read into the block->frame. */
|
||||
|
||||
dberr_t err = set_zip_size(block->frame);
|
||||
m_zip_size = fsp_header_get_zip_size(page);
|
||||
|
||||
if (err != DB_SUCCESS) {
|
||||
if (!ut_is_2pow(m_zip_size) || m_zip_size > UNIV_ZIP_SIZE_MAX) {
|
||||
return(DB_CORRUPTION);
|
||||
}
|
||||
|
||||
|
|
@ -605,11 +659,7 @@ AbstractCallback::init(
|
|||
m_free_limit = mach_read_from_4(page + FSP_FREE_LIMIT);
|
||||
m_space = mach_read_from_4(page + FSP_HEADER_OFFSET + FSP_SPACE_ID);
|
||||
|
||||
if ((err = set_current_xdes(0, page)) != DB_SUCCESS) {
|
||||
return(err);
|
||||
}
|
||||
|
||||
return(DB_SUCCESS);
|
||||
return set_current_xdes(0, page);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1316,8 +1366,8 @@ row_import::match_schema(
|
|||
return(DB_ERROR);
|
||||
} else if (m_table->n_cols != m_n_cols) {
|
||||
ib_errf(thd, IB_LOG_LEVEL_ERROR, ER_TABLE_SCHEMA_MISMATCH,
|
||||
"Number of columns don't match, table has %u "
|
||||
"columns but the tablespace meta-data file has "
|
||||
"Number of columns don't match, table has %u"
|
||||
" columns but the tablespace meta-data file has "
|
||||
ULINTPF " columns",
|
||||
m_table->n_cols, m_n_cols);
|
||||
|
||||
|
|
@ -1597,6 +1647,7 @@ IndexPurge::purge() UNIV_NOTHROW
|
|||
Constructor
|
||||
* @param cfg - config of table being imported.
|
||||
* @param trx - transaction covering the import */
|
||||
inline
|
||||
PageConverter::PageConverter(
|
||||
row_import* cfg,
|
||||
trx_t* trx)
|
||||
|
|
@ -1621,6 +1672,7 @@ Adjust the BLOB reference for a single column that is externally stored
|
|||
@param offsets - column offsets for the record
|
||||
@param i - column ordinal value
|
||||
@return DB_SUCCESS or error code */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::adjust_cluster_index_blob_column(
|
||||
rec_t* rec,
|
||||
|
|
@ -1673,6 +1725,7 @@ stored columns.
|
|||
@param rec - record to update
|
||||
@param offsets - column offsets for the record
|
||||
@return DB_SUCCESS or error code */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::adjust_cluster_index_blob_columns(
|
||||
rec_t* rec,
|
||||
|
|
@ -1706,6 +1759,7 @@ BLOB reference, write the new space id.
|
|||
@param rec - record to update
|
||||
@param offsets - column offsets for the record
|
||||
@return DB_SUCCESS or error code */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::adjust_cluster_index_blob_ref(
|
||||
rec_t* rec,
|
||||
|
|
@ -1729,6 +1783,7 @@ Purge delete-marked records, only if it is possible to do so without
|
|||
re-organising the B+tree.
|
||||
@param offsets - current row offsets.
|
||||
@return true if purge succeeded */
|
||||
inline
|
||||
bool
|
||||
PageConverter::purge(const ulint* offsets) UNIV_NOTHROW
|
||||
{
|
||||
|
|
@ -1753,6 +1808,7 @@ Adjust the BLOB references and sys fields for the current record.
|
|||
@param offsets - column offsets for the record
|
||||
@param deleted - true if row is delete marked
|
||||
@return DB_SUCCESS or error code. */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::adjust_cluster_record(
|
||||
const dict_index_t* index,
|
||||
|
|
@ -1781,6 +1837,7 @@ Update the BLOB refrences and write UNDO log entries for
|
|||
rows that can't be purged optimistically.
|
||||
@param block - block to update
|
||||
@retval DB_SUCCESS or error code */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::update_records(
|
||||
buf_block_t* block) UNIV_NOTHROW
|
||||
|
|
@ -1846,6 +1903,7 @@ PageConverter::update_records(
|
|||
/**
|
||||
Update the space, index id, trx id.
|
||||
@return DB_SUCCESS or error code */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::update_index_page(
|
||||
buf_block_t* block) UNIV_NOTHROW
|
||||
|
|
@ -1915,6 +1973,7 @@ PageConverter::update_index_page(
|
|||
Validate the space flags and update tablespace header page.
|
||||
@param block - block read from file, not from the buffer pool.
|
||||
@retval DB_SUCCESS or error code */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::update_header(
|
||||
buf_block_t* block) UNIV_NOTHROW
|
||||
|
|
@ -1954,6 +2013,7 @@ PageConverter::update_header(
|
|||
Update the page, set the space id, max trx id and index id.
|
||||
@param block - block read from file
|
||||
@retval DB_SUCCESS or error code */
|
||||
inline
|
||||
dberr_t
|
||||
PageConverter::update_page(
|
||||
buf_block_t* block,
|
||||
|
|
@ -3424,6 +3484,436 @@ row_import_update_discarded_flag(
|
|||
return(err);
|
||||
}
|
||||
|
||||
struct fil_iterator_t {
|
||||
pfs_os_file_t file; /*!< File handle */
|
||||
const char* filepath; /*!< File path name */
|
||||
os_offset_t start; /*!< From where to start */
|
||||
os_offset_t end; /*!< Where to stop */
|
||||
os_offset_t file_size; /*!< File size in bytes */
|
||||
ulint page_size; /*!< Page size */
|
||||
ulint n_io_buffers; /*!< Number of pages to use
|
||||
for IO */
|
||||
byte* io_buffer; /*!< Buffer to use for IO */
|
||||
fil_space_crypt_t *crypt_data; /*!< Crypt data (if encrypted) */
|
||||
byte* crypt_io_buffer; /*!< IO buffer when encrypted */
|
||||
};
|
||||
|
||||
/********************************************************************//**
|
||||
TODO: This can be made parallel trivially by chunking up the file and creating
|
||||
a callback per thread. . Main benefit will be to use multiple CPUs for
|
||||
checksums and compressed tables. We have to do compressed tables block by
|
||||
block right now. Secondly we need to decompress/compress and copy too much
|
||||
of data. These are CPU intensive.
|
||||
|
||||
Iterate over all the pages in the tablespace.
|
||||
@param iter - Tablespace iterator
|
||||
@param block - block to use for IO
|
||||
@param callback - Callback to inspect and update page contents
|
||||
@retval DB_SUCCESS or error code */
|
||||
static
|
||||
dberr_t
|
||||
fil_iterate(
|
||||
/*========*/
|
||||
const fil_iterator_t& iter,
|
||||
buf_block_t* block,
|
||||
AbstractCallback& callback)
|
||||
{
|
||||
os_offset_t offset;
|
||||
ulint page_no = 0;
|
||||
ulint space_id = callback.get_space_id();
|
||||
ulint n_bytes = iter.n_io_buffers * iter.page_size;
|
||||
|
||||
ut_ad(!srv_read_only_mode);
|
||||
|
||||
/* TODO: For compressed tables we do a lot of useless
|
||||
copying for non-index pages. Unfortunately, it is
|
||||
required by buf_zip_decompress() */
|
||||
const bool row_compressed = callback.get_zip_size() > 0;
|
||||
|
||||
for (offset = iter.start; offset < iter.end; offset += n_bytes) {
|
||||
|
||||
byte* io_buffer = iter.io_buffer;
|
||||
|
||||
block->frame = io_buffer;
|
||||
|
||||
if (row_compressed) {
|
||||
page_zip_des_init(&block->page.zip);
|
||||
page_zip_set_size(&block->page.zip, iter.page_size);
|
||||
block->page.zip.data = block->frame + UNIV_PAGE_SIZE;
|
||||
ut_d(block->page.zip.m_external = true);
|
||||
ut_ad(iter.page_size == callback.get_zip_size());
|
||||
|
||||
/* Zip IO is done in the compressed page buffer. */
|
||||
io_buffer = block->page.zip.data;
|
||||
}
|
||||
|
||||
/* We have to read the exact number of bytes. Otherwise the
|
||||
InnoDB IO functions croak on failed reads. */
|
||||
|
||||
n_bytes = ulint(ut_min(os_offset_t(n_bytes),
|
||||
iter.end - offset));
|
||||
|
||||
ut_ad(n_bytes > 0);
|
||||
ut_ad(!(n_bytes % iter.page_size));
|
||||
|
||||
const bool encrypted = iter.crypt_data != NULL
|
||||
&& iter.crypt_data->should_encrypt();
|
||||
/* Use additional crypt io buffer if tablespace is encrypted */
|
||||
byte* const readptr = encrypted
|
||||
? iter.crypt_io_buffer : io_buffer;
|
||||
byte* const writeptr = readptr;
|
||||
|
||||
if (!os_file_read(iter.file, readptr, offset, n_bytes)) {
|
||||
ib_logf(IB_LOG_LEVEL_ERROR, "os_file_read() failed");
|
||||
return DB_IO_ERROR;
|
||||
}
|
||||
|
||||
bool updated = false;
|
||||
os_offset_t page_off = offset;
|
||||
ulint n_pages_read = (ulint) n_bytes / iter.page_size;
|
||||
bool decrypted = false;
|
||||
|
||||
for (ulint i = 0; i < n_pages_read; ++i) {
|
||||
ulint size = iter.page_size;
|
||||
dberr_t err = DB_SUCCESS;
|
||||
byte* src = readptr + (i * size);
|
||||
byte* dst = io_buffer + (i * size);
|
||||
bool frame_changed = false;
|
||||
|
||||
ulint page_type = mach_read_from_2(src+FIL_PAGE_TYPE);
|
||||
|
||||
const bool page_compressed
|
||||
= page_type == FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED
|
||||
|| page_type == FIL_PAGE_PAGE_COMPRESSED;
|
||||
|
||||
/* If tablespace is encrypted, we need to decrypt
|
||||
the page. Note that tablespaces are not in
|
||||
fil_system during import. */
|
||||
if (encrypted) {
|
||||
decrypted = fil_space_decrypt(
|
||||
iter.crypt_data,
|
||||
dst, //dst
|
||||
iter.page_size,
|
||||
src, // src
|
||||
&err);
|
||||
|
||||
if (err != DB_SUCCESS) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (decrypted) {
|
||||
updated = true;
|
||||
} else {
|
||||
if (!page_compressed && !row_compressed) {
|
||||
block->frame = src;
|
||||
frame_changed = true;
|
||||
} else {
|
||||
memcpy(dst, src, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If the original page is page_compressed, we need
|
||||
to decompress it before adjusting further. */
|
||||
if (page_compressed) {
|
||||
fil_decompress_page(NULL, dst, ulong(size),
|
||||
NULL);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
buf_block_set_file_page(block, space_id, page_no++);
|
||||
|
||||
if ((err = callback(page_off, block)) != DB_SUCCESS) {
|
||||
return err;
|
||||
} else if (!updated) {
|
||||
updated = buf_block_get_state(block)
|
||||
== BUF_BLOCK_FILE_PAGE;
|
||||
}
|
||||
|
||||
/* If tablespace is encrypted we use additional
|
||||
temporary scratch area where pages are read
|
||||
for decrypting readptr == crypt_io_buffer != io_buffer.
|
||||
|
||||
Destination for decryption is a buffer pool block
|
||||
block->frame == dst == io_buffer that is updated.
|
||||
Pages that did not require decryption even when
|
||||
tablespace is marked as encrypted are not copied
|
||||
instead block->frame is set to src == readptr.
|
||||
|
||||
For encryption we again use temporary scratch area
|
||||
writeptr != io_buffer == dst
|
||||
that is then written to the tablespace
|
||||
|
||||
(1) For normal tables io_buffer == dst == writeptr
|
||||
(2) For only page compressed tables
|
||||
io_buffer == dst == writeptr
|
||||
(3) For encrypted (and page compressed)
|
||||
readptr != io_buffer == dst != writeptr
|
||||
*/
|
||||
|
||||
ut_ad(!encrypted && !page_compressed ?
|
||||
src == dst && dst == writeptr + (i * size):1);
|
||||
ut_ad(page_compressed && !encrypted ?
|
||||
src == dst && dst == writeptr + (i * size):1);
|
||||
ut_ad(encrypted ?
|
||||
src != dst && dst != writeptr + (i * size):1);
|
||||
|
||||
if (encrypted) {
|
||||
memcpy(writeptr + (i * size),
|
||||
row_compressed ? block->page.zip.data :
|
||||
block->frame, size);
|
||||
}
|
||||
|
||||
if (frame_changed) {
|
||||
block->frame = dst;
|
||||
}
|
||||
|
||||
src = io_buffer + (i * size);
|
||||
|
||||
if (page_compressed) {
|
||||
ulint len = 0;
|
||||
|
||||
fil_compress_page(
|
||||
NULL,
|
||||
src,
|
||||
NULL,
|
||||
size,
|
||||
0,/* FIXME: compression level */
|
||||
512,/* FIXME: use proper block size */
|
||||
encrypted,
|
||||
&len);
|
||||
|
||||
updated = true;
|
||||
}
|
||||
|
||||
/* If tablespace is encrypted, encrypt page before we
|
||||
write it back. Note that we should not encrypt the
|
||||
buffer that is in buffer pool. */
|
||||
/* NOTE: At this stage of IMPORT the
|
||||
buffer pool is not being used at all! */
|
||||
if (decrypted && encrypted) {
|
||||
byte *dest = writeptr + (i * size);
|
||||
ulint space = mach_read_from_4(
|
||||
src + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
|
||||
ulint offset = mach_read_from_4(src + FIL_PAGE_OFFSET);
|
||||
ib_uint64_t lsn = mach_read_from_8(src + FIL_PAGE_LSN);
|
||||
|
||||
byte* tmp = fil_encrypt_buf(
|
||||
iter.crypt_data,
|
||||
space,
|
||||
offset,
|
||||
lsn,
|
||||
src,
|
||||
iter.page_size == UNIV_PAGE_SIZE ? 0 : iter.page_size,
|
||||
dest);
|
||||
|
||||
if (tmp == src) {
|
||||
/* TODO: remove unnecessary memcpy's */
|
||||
memcpy(dest, src, size);
|
||||
}
|
||||
|
||||
updated = true;
|
||||
}
|
||||
|
||||
page_off += iter.page_size;
|
||||
block->frame += iter.page_size;
|
||||
}
|
||||
|
||||
/* A page was updated in the set, write back to disk. */
|
||||
if (updated
|
||||
&& !os_file_write(
|
||||
iter.filepath, iter.file, writeptr,
|
||||
offset, (ulint) n_bytes)) {
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_ERROR, "os_file_write() failed");
|
||||
return DB_IO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return DB_SUCCESS;
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Iterate over all the pages in the tablespace.
|
||||
@param table - the table definiton in the server
|
||||
@param n_io_buffers - number of blocks to read and write together
|
||||
@param callback - functor that will do the page updates
|
||||
@return DB_SUCCESS or error code */
|
||||
static
|
||||
dberr_t
|
||||
fil_tablespace_iterate(
|
||||
/*===================*/
|
||||
dict_table_t* table,
|
||||
ulint n_io_buffers,
|
||||
AbstractCallback& callback)
|
||||
{
|
||||
dberr_t err;
|
||||
pfs_os_file_t file;
|
||||
char* filepath;
|
||||
|
||||
ut_a(n_io_buffers > 0);
|
||||
ut_ad(!srv_read_only_mode);
|
||||
|
||||
DBUG_EXECUTE_IF("ib_import_trigger_corruption_1",
|
||||
return(DB_CORRUPTION););
|
||||
|
||||
if (DICT_TF_HAS_DATA_DIR(table->flags)) {
|
||||
dict_get_and_save_data_dir_path(table, false);
|
||||
ut_a(table->data_dir_path);
|
||||
|
||||
filepath = os_file_make_remote_pathname(
|
||||
table->data_dir_path, table->name, "ibd");
|
||||
} else {
|
||||
filepath = fil_make_ibd_name(table->name, false);
|
||||
}
|
||||
|
||||
{
|
||||
ibool success;
|
||||
|
||||
file = os_file_create_simple_no_error_handling(
|
||||
innodb_file_data_key, filepath,
|
||||
OS_FILE_OPEN, OS_FILE_READ_WRITE, &success, FALSE);
|
||||
|
||||
DBUG_EXECUTE_IF("fil_tablespace_iterate_failure",
|
||||
{
|
||||
static bool once;
|
||||
|
||||
if (!once || ut_rnd_interval(0, 10) == 5) {
|
||||
once = true;
|
||||
success = FALSE;
|
||||
os_file_close(file);
|
||||
}
|
||||
});
|
||||
|
||||
if (!success) {
|
||||
/* The following call prints an error message */
|
||||
os_file_get_last_error(true);
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_ERROR,
|
||||
"Trying to import a tablespace, but could not "
|
||||
"open the tablespace file %s", filepath);
|
||||
|
||||
mem_free(filepath);
|
||||
|
||||
return(DB_TABLESPACE_NOT_FOUND);
|
||||
|
||||
} else {
|
||||
err = DB_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
callback.set_file(filepath, file);
|
||||
|
||||
os_offset_t file_size = os_file_get_size(file);
|
||||
ut_a(file_size != (os_offset_t) -1);
|
||||
|
||||
/* Allocate a page to read in the tablespace header, so that we
|
||||
can determine the page size and zip_size (if it is compressed).
|
||||
We allocate an extra page in case it is a compressed table. One
|
||||
page is to ensure alignement. */
|
||||
|
||||
void* page_ptr = mem_alloc(3 * UNIV_PAGE_SIZE);
|
||||
byte* page = static_cast<byte*>(ut_align(page_ptr, UNIV_PAGE_SIZE));
|
||||
|
||||
/* The block we will use for every physical page */
|
||||
buf_block_t block;
|
||||
|
||||
memset(&block, 0, sizeof block);
|
||||
block.frame = page;
|
||||
block.page.io_fix = BUF_IO_NONE;
|
||||
block.page.buf_fix_count = 1;
|
||||
block.page.state = BUF_BLOCK_FILE_PAGE;
|
||||
|
||||
/* Read the first page and determine the page and zip size. */
|
||||
|
||||
if (!os_file_read(file, page, 0, UNIV_PAGE_SIZE)) {
|
||||
|
||||
err = DB_IO_ERROR;
|
||||
|
||||
} else if ((err = callback.init(file_size, &block)) == DB_SUCCESS) {
|
||||
fil_iterator_t iter;
|
||||
|
||||
iter.file = file;
|
||||
iter.start = 0;
|
||||
iter.end = file_size;
|
||||
iter.filepath = filepath;
|
||||
iter.file_size = file_size;
|
||||
iter.n_io_buffers = n_io_buffers;
|
||||
iter.page_size = callback.get_page_size();
|
||||
|
||||
/* In MariaDB/MySQL 5.6 tablespace does not exist
|
||||
during import, therefore we can't use space directly
|
||||
here. */
|
||||
ulint crypt_data_offset = fsp_header_get_crypt_offset(
|
||||
callback.get_zip_size());
|
||||
|
||||
/* read (optional) crypt data */
|
||||
iter.crypt_data = fil_space_read_crypt_data(
|
||||
0, page, crypt_data_offset);
|
||||
|
||||
/* Compressed pages can't be optimised for block IO for now.
|
||||
We do the IMPORT page by page. */
|
||||
|
||||
if (callback.get_zip_size() > 0) {
|
||||
iter.n_io_buffers = 1;
|
||||
ut_a(iter.page_size == callback.get_zip_size());
|
||||
}
|
||||
|
||||
/** If tablespace is encrypted, it needs extra buffers */
|
||||
if (iter.crypt_data != NULL) {
|
||||
/* decrease io buffers so that memory
|
||||
* consumption doesnt double
|
||||
* note: the +1 is to avoid n_io_buffers getting down to 0 */
|
||||
iter.n_io_buffers = (iter.n_io_buffers + 1) / 2;
|
||||
}
|
||||
|
||||
/** Add an extra page for compressed page scratch area. */
|
||||
|
||||
void* io_buffer = mem_alloc(
|
||||
(2 + iter.n_io_buffers) * UNIV_PAGE_SIZE);
|
||||
|
||||
iter.io_buffer = static_cast<byte*>(
|
||||
ut_align(io_buffer, UNIV_PAGE_SIZE));
|
||||
|
||||
void* crypt_io_buffer = NULL;
|
||||
if (iter.crypt_data != NULL) {
|
||||
crypt_io_buffer = mem_alloc(
|
||||
(2 + iter.n_io_buffers) * UNIV_PAGE_SIZE);
|
||||
iter.crypt_io_buffer = static_cast<byte*>(
|
||||
ut_align(crypt_io_buffer, UNIV_PAGE_SIZE));
|
||||
}
|
||||
|
||||
err = fil_iterate(iter, &block, callback);
|
||||
|
||||
mem_free(io_buffer);
|
||||
|
||||
if (crypt_io_buffer != NULL) {
|
||||
mem_free(crypt_io_buffer);
|
||||
iter.crypt_io_buffer = NULL;
|
||||
fil_space_destroy_crypt_data(&iter.crypt_data);
|
||||
}
|
||||
}
|
||||
|
||||
if (err == DB_SUCCESS) {
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_INFO, "Sync to disk");
|
||||
|
||||
if (!os_file_flush(file)) {
|
||||
ib_logf(IB_LOG_LEVEL_INFO, "os_file_flush() failed!");
|
||||
err = DB_IO_ERROR;
|
||||
} else {
|
||||
ib_logf(IB_LOG_LEVEL_INFO, "Sync to disk - done!");
|
||||
}
|
||||
}
|
||||
|
||||
os_file_close(file);
|
||||
|
||||
mem_free(page_ptr);
|
||||
mem_free(filepath);
|
||||
|
||||
return(err);
|
||||
}
|
||||
|
||||
/*****************************************************************//**
|
||||
Imports a tablespace. The space id in the .ibd file must match the space id
|
||||
of the table in the data dictionary.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue