polardbxengine/storage/innobase/os/file.cc

264 lines
8.3 KiB
C++

/***********************************************************************
Copyright (c) 1995, 2019, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2009, Percona Inc.
Portions of this file contain modifications contributed and copyrighted
by Percona Inc.. Those modifications are
gratefully acknowledged and are described briefly in the InnoDB
documentation. The contributions by Percona Inc. are incorporated with
their permission, and subject to the conditions contained in the file
COPYING.Percona.
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 os/file.cc
The interface to the operating system file i/o primitives
Created 10/21/1995 Heikki Tuuri
*******************************************************/
/** NOTE: The functions in this file should only use functions from
other files in library. The code in this file is used to make a library for
external tools. */
#include "db0err.h"
#include "fil0fil.h"
#include "mach0data.h"
#include "os0file.h"
#include "univ.i"
#include <lz4.h>
#include <zlib.h>
/**
@param[in] type The compression type
@return the string representation */
const char *Compression::to_string(Type type) {
switch (type) {
case NONE:
return ("None");
case ZLIB:
return ("Zlib");
case LZ4:
return ("LZ4");
}
ut_ad(0);
return ("<UNKNOWN>");
}
/**
@param[in] meta Page Meta data
@return the string representation */
std::string Compression::to_string(const Compression::meta_t &meta) {
std::ostringstream stream;
stream << "version: " << int(meta.m_version) << " "
<< "algorithm: " << meta.m_algorithm << " "
<< "(" << to_string(meta.m_algorithm) << ") "
<< "orginal_type: " << meta.m_original_type << " "
<< "original_size: " << meta.m_original_size << " "
<< "compressed_size: " << meta.m_compressed_size;
return (stream.str());
}
/** @return true if it is a compressed page */
bool Compression::is_compressed_page(const byte *page) {
return (mach_read_from_2(page + FIL_PAGE_TYPE) == FIL_PAGE_COMPRESSED);
}
/** Deserizlise the page header compression meta-data
@param[in] page Pointer to the page header
@param[out] control Deserialised data */
void Compression::deserialize_header(const byte *page,
Compression::meta_t *control) {
ut_ad(is_compressed_page(page));
control->m_version =
static_cast<uint8_t>(mach_read_from_1(page + FIL_PAGE_VERSION));
control->m_original_type =
static_cast<uint16_t>(mach_read_from_2(page + FIL_PAGE_ORIGINAL_TYPE_V1));
control->m_compressed_size =
static_cast<uint16_t>(mach_read_from_2(page + FIL_PAGE_COMPRESS_SIZE_V1));
control->m_original_size =
static_cast<uint16_t>(mach_read_from_2(page + FIL_PAGE_ORIGINAL_SIZE_V1));
control->m_algorithm =
static_cast<Type>(mach_read_from_1(page + FIL_PAGE_ALGORITHM_V1));
}
/** Decompress the page data contents. Page type must be FIL_PAGE_COMPRESSED, if
not then the source contents are left unchanged and DB_SUCCESS is returned.
@param[in] dblwr_recover true of double write recovery in progress
@param[in,out] src Data read from disk, decompressed data will be
copied to this page
@param[in,out] dst Scratch area to use for decompression
@param[in] dst_len Size of the scratch area in bytes
@return DB_SUCCESS or error code */
dberr_t Compression::deserialize(bool dblwr_recover, byte *src, byte *dst,
ulint dst_len) {
if (!is_compressed_page(src)) {
/* There is nothing we can do. */
return (DB_SUCCESS);
}
meta_t header;
deserialize_header(src, &header);
byte *ptr = src + FIL_PAGE_DATA;
if (header.m_version != 1 ||
header.m_original_size < UNIV_PAGE_SIZE_MIN - (FIL_PAGE_DATA + 8) ||
header.m_original_size > UNIV_PAGE_SIZE_MAX - FIL_PAGE_DATA ||
dst_len < header.m_original_size + FIL_PAGE_DATA) {
/* The last check could potentially return DB_OVERFLOW,
the caller should be able to retry with a larger buffer. */
return (DB_CORRUPTION);
}
// FIXME: We should use TLS for this and reduce the malloc/free
bool allocated;
/* The caller doesn't know what to expect */
if (dst == NULL) {
/* Add a safety margin of an additional 50% */
ulint n_bytes = header.m_original_size + (header.m_original_size / 2);
dst = reinterpret_cast<byte *>(ut_malloc_nokey(n_bytes));
if (dst == NULL) {
return (DB_OUT_OF_MEMORY);
}
allocated = true;
} else {
allocated = false;
}
int ret;
Compression compression;
ulint len = header.m_original_size;
compression.m_type = static_cast<Compression::Type>(header.m_algorithm);
switch (compression.m_type) {
case Compression::ZLIB: {
uLongf zlen = header.m_original_size;
if (uncompress(dst, &zlen, ptr, header.m_compressed_size) != Z_OK) {
if (allocated) {
ut_free(dst);
}
return (DB_IO_DECOMPRESS_FAIL);
}
len = static_cast<ulint>(zlen);
break;
}
case Compression::LZ4:
if (dblwr_recover) {
ret = LZ4_decompress_safe(
reinterpret_cast<char *>(ptr), reinterpret_cast<char *>(dst),
header.m_compressed_size, header.m_original_size);
} else {
/* This can potentially read beyond the input
buffer if the data is malformed. According to
the LZ4 documentation it is a little faster
than the above function. When recovering from
the double write buffer we can afford to us the
slower function above. */
ret = LZ4_decompress_fast(reinterpret_cast<char *>(ptr),
reinterpret_cast<char *>(dst),
header.m_original_size);
}
if (ret < 0) {
if (allocated) {
ut_free(dst);
}
return (DB_IO_DECOMPRESS_FAIL);
}
break;
default:
#ifdef UNIV_NO_ERR_MSGS
ib::error()
#else
ib::error(ER_IB_MSG_741)
#endif /* UNIV_NO_ERR_MSGS */
<< "Compression algorithm support missing: "
<< Compression::to_string(compression.m_type);
if (allocated) {
ut_free(dst);
}
return (DB_UNSUPPORTED);
}
/* Leave the header alone */
memmove(src + FIL_PAGE_DATA, dst, len);
mach_write_to_2(src + FIL_PAGE_TYPE, header.m_original_type);
ut_ad(dblwr_recover || memcmp(src + FIL_PAGE_LSN + 4,
src + (header.m_original_size + FIL_PAGE_DATA) -
FIL_PAGE_END_LSN_OLD_CHKSUM + 4,
4) == 0);
if (allocated) {
ut_free(dst);
}
return (DB_SUCCESS);
}
/** Decompress the page data contents. Page type must be FIL_PAGE_COMPRESSED, if
not then the source contents are left unchanged and DB_SUCCESS is returned.
@param[in] dblwr_recover true of double write recovery in progress
@param[in,out] src Data read from disk, decompressed data will be
copied to this page
@param[in,out] dst Scratch area to use for decompression
@param[in] dst_len Size of the scratch area in bytes
@return DB_SUCCESS or error code */
dberr_t os_file_decompress_page(bool dblwr_recover, byte *src, byte *dst,
ulint dst_len) {
return (Compression::deserialize(dblwr_recover, src, dst, dst_len));
}