@ -1,6 +1,6 @@
/*****************************************************************************
Copyright ( C ) 2013 , 2017 , MariaDB Corporation .
Copyright ( C ) 2013 , 2018 , MariaDB Corporation .
This program is free software ; you can redistribute it and / or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -74,73 +74,26 @@ Updated 14/02/2015
# include "snappy-c.h"
# endif
/* Used for debugging */
//#define UNIV_PAGECOMPRESS_DEBUG 1
/****************************************************************/ /**
For page compressed pages compress the page before actual write
operation .
@ return compressed page to be written */
UNIV_INTERN
byte *
fil_compress_page (
/*==============*/
fil_space_t * space , /*!< in,out: tablespace (NULL during IMPORT) */
byte * buf , /*!< in: buffer from which to write; in aio
this must be appropriately aligned */
byte * out_buf , /*!< out: compressed buffer */
ulint len , /*!< in: length of input buffer.*/
ulint level , /* in: compression level */
ulint block_size , /*!< in: block size */
bool encrypted , /*!< in: is page also encrypted */
ulint * out_len ) /*!< out: actual length of compressed
page */
/** Compress a page_compressed page before writing to a data file.
@ param [ in ] buf page to be compressed
@ param [ out ] out_buf compressed page
@ param [ in ] level compression level
@ param [ in ] block_size file system block size
@ param [ in ] encrypted whether the page will be subsequently encrypted
@ return actual length of compressed page
@ retval 0 if the page was not compressed */
ulint fil_page_compress ( const byte * buf , byte * out_buf , ulint level ,
ulint block_size , bool encrypted )
{
int err = Z_OK ;
int comp_level = int ( level ) ;
ulint header_len = FIL_PAGE_DATA + FIL_PAGE_COMPRESSED_SIZE ;
ulint write_size = 0 ;
# if HAVE_LZO
lzo_uint write_size_lzo = write_size ;
# endif
/* Cache to avoid change during function execution */
ulint comp_method = innodb_compression_algorithm ;
bool allocated = false ;
/* page_compression does not apply to tables or tablespaces
that use ROW_FORMAT = COMPRESSED */
ut_ad ( ! space | | ! FSP_FLAGS_GET_ZIP_SSIZE ( space - > flags ) ) ;
if ( encrypted ) {
header_len + = FIL_PAGE_COMPRESSION_METHOD_SIZE ;
}
if ( ! out_buf ) {
allocated = true ;
ulint size = UNIV_PAGE_SIZE ;
/* Both snappy and lzo compression methods require that
output buffer used for compression is bigger than input
buffer . Increase the allocated buffer size accordingly . */
# if HAVE_SNAPPY
if ( comp_method = = PAGE_SNAPPY_ALGORITHM ) {
size = snappy_max_compressed_length ( size ) ;
}
# endif
# if HAVE_LZO
if ( comp_method = = PAGE_LZO_ALGORITHM ) {
size + = LZO1X_1_15_MEM_COMPRESS ;
}
# endif
out_buf = static_cast < byte * > ( ut_malloc_nokey ( size ) ) ;
}
ut_ad ( buf ) ;
ut_ad ( out_buf ) ;
ut_ad ( len ) ;
ut_ad ( out_len ) ;
/* Let's not compress file space header or
extent descriptor */
switch ( fil_page_get_type ( buf ) ) {
@ -148,8 +101,7 @@ fil_compress_page(
case FIL_PAGE_TYPE_FSP_HDR :
case FIL_PAGE_TYPE_XDES :
case FIL_PAGE_PAGE_COMPRESSED :
* out_len = len ;
goto err_exit ;
return 0 ;
}
/* If no compression level was provided to this table, use system
@ -158,125 +110,113 @@ fil_compress_page(
comp_level = page_zip_level ;
}
DBUG_LOG ( " compress " , " Preparing for space "
< < ( space ? space - > id : 0 ) < < " ' "
< < ( space ? space - > name : " (import) " ) < < " ' len " < < len ) ;
write_size = UNIV_PAGE_SIZE - header_len ;
ulint write_size = srv_page_size - header_len ;
switch ( comp_method ) {
switch ( comp_method ) {
default :
ut_ad ( ! " unknown compression method " ) ;
/* fall through */
case PAGE_UNCOMPRESSED :
return 0 ;
case PAGE_ZLIB_ALGORITHM :
{
ulong len = uLong ( write_size ) ;
if ( Z_OK = = compress2 (
out_buf + header_len , & len ,
buf , uLong ( srv_page_size ) , comp_level ) ) {
write_size = len ;
goto success ;
}
}
break ;
# ifdef HAVE_LZ4
case PAGE_LZ4_ALGORITHM :
# ifdef HAVE_LZ4_COMPRESS_DEFAULT
err = LZ4_compress_default ( ( const char * ) buf ,
( char * ) out_buf + header_len , len , write_size ) ;
# else
err = LZ4_compress_limitedOutput ( ( const char * ) buf ,
( char * ) out_buf + header_len , len , write_size ) ;
# endif /* HAVE_LZ4_COMPRESS_DEFAULT */
write_size = err ;
if ( err = = 0 ) {
goto err_exit ;
# ifdef HAVE_LZ4_COMPRESS_DEFAULT
write_size = LZ4_compress_default (
reinterpret_cast < const char * > ( buf ) ,
reinterpret_cast < char * > ( out_buf ) + header_len ,
int ( srv_page_size ) , int ( write_size ) ) ;
# else
write_size = LZ4_compress_limitedOutput (
reinterpret_cast < const char * > ( buf ) ,
reinterpret_cast < char * > ( out_buf ) + header_len ,
int ( srv_page_size ) , int ( write_size ) ) ;
# endif
if ( write_size ) {
goto success ;
}
break ;
# endif /* HAVE_LZ4 */
# ifdef HAVE_LZO
case PAGE_LZO_ALGORITHM :
err = lzo1x_1_15_compress (
buf , len , out_buf + header_len , & write_size_lzo , out_buf + UNIV_PAGE_SIZE ) ;
write_size = write_size_lzo ;
if ( err ! = LZO_E_OK | | write_size > UNIV_PAGE_SIZE - header_len ) {
goto err_exit ;
case PAGE_LZO_ALGORITHM : {
lzo_uint len = write_size ;
if ( LZO_E_OK = = lzo1x_1_15_compress (
buf , srv_page_size ,
out_buf + header_len , & len ,
out_buf + srv_page_size )
& & len < = write_size ) {
write_size = len ;
goto success ;
}
break ;
}
# endif /* HAVE_LZO */
# ifdef HAVE_LZMA
case PAGE_LZMA_ALGORITHM : {
size_t out_pos = 0 ;
err = lzma_easy_buffer_encode (
comp_level ,
LZMA_CHECK_NONE ,
NULL , /* No custom allocator, use malloc/free */
reinterpret_cast < uint8_t * > ( buf ) ,
len ,
reinterpret_cast < uint8_t * > ( out_buf + header_len ) ,
& out_pos ,
( size_t ) write_size ) ;
if ( err ! = LZMA_OK | | out_pos > UNIV_PAGE_SIZE - header_len ) {
size_t out_pos = 0 ;
if ( LZMA_OK = = lzma_easy_buffer_encode (
comp_level , LZMA_CHECK_NONE , NULL ,
buf , srv_page_size , out_buf + header_len ,
& out_pos , write_size )
& & out_pos < = write_size ) {
write_size = out_pos ;
goto err_exit ;
goto success ;
}
write_size = out_pos ;
break ;
}
# endif /* HAVE_LZMA */
# ifdef HAVE_BZIP2
case PAGE_BZIP2_ALGORITHM : {
err = BZ2_bzBuffToBuffCompress (
( char * ) ( out_buf + header_len ) ,
( unsigned int * ) & write_size ,
( char * ) buf ,
len ,
1 ,
0 ,
0 ) ;
if ( err ! = BZ_OK | | write_size > UNIV_PAGE_SIZE - header_len ) {
goto err_exit ;
unsigned len = unsigned ( write_size ) ;
if ( BZ_OK = = BZ2_bzBuffToBuffCompress (
reinterpret_cast < char * > ( out_buf + header_len ) ,
& len ,
const_cast < char * > (
reinterpret_cast < const char * > ( buf ) ) ,
unsigned ( srv_page_size ) , 1 , 0 , 0 )
& & len < = write_size ) {
write_size = len ;
goto success ;
}
break ;
}
# endif /* HAVE_BZIP2 */
# ifdef HAVE_SNAPPY
case PAGE_SNAPPY_ALGORITHM :
{
snappy_status cstatus ;
write_size = snappy_max_compressed_length ( UNIV_PAGE_SIZE ) ;
cstatus = snappy_compress (
( const char * ) buf ,
( size_t ) len ,
( char * ) ( out_buf + header_len ) ,
( size_t * ) & write_size ) ;
if ( cstatus ! = SNAPPY_OK | | write_size > UNIV_PAGE_SIZE - header_len ) {
err = ( int ) cstatus ;
goto err_exit ;
case PAGE_SNAPPY_ALGORITHM : {
size_t len = snappy_max_compressed_length ( srv_page_size ) ;
if ( SNAPPY_OK = = snappy_compress (
reinterpret_cast < const char * > ( buf ) ,
srv_page_size ,
reinterpret_cast < char * > ( out_buf ) + header_len ,
& len )
& & len < = write_size ) {
write_size = len ;
goto success ;
}
break ;
}
# endif /* HAVE_SNAPPY */
case PAGE_ZLIB_ALGORITHM :
err = compress2 ( out_buf + header_len , ( ulong * ) & write_size , buf ,
uLong ( len ) , comp_level ) ;
if ( err ! = Z_OK ) {
goto err_exit ;
}
break ;
case PAGE_UNCOMPRESSED :
* out_len = len ;
return ( buf ) ;
break ;
default :
ut_error ;
break ;
}
srv_stats . pages_page_compression_error . inc ( ) ;
return 0 ;
success :
/* Set up the page header */
memcpy ( out_buf , buf , FIL_PAGE_DATA ) ;
/* Set up the checksum */
@ -307,23 +247,12 @@ fil_compress_page(
/* Verify that page can be decompressed */
{
byte * comp_page ;
byte * uncomp_page ;
comp_page = static_cast < byte * > ( ut_malloc_nokey ( UNIV_PAGE_SIZE ) ) ;
uncomp_page = static_cast < byte * > ( ut_malloc_nokey ( UNIV_PAGE_SIZE ) ) ;
memcpy ( comp_page , out_buf , UNIV_PAGE_SIZE ) ;
fil_decompress_page ( uncomp_page , comp_page , ulong ( len ) , NULL ) ;
if ( buf_page_is_corrupted ( false , uncomp_page , univ_page_size ,
space ) ) {
buf_page_print ( uncomp_page , univ_page_size ) ;
ut_ad ( 0 ) ;
}
ut_free ( comp_page ) ;
ut_free ( uncomp_page ) ;
page_t tmp_buf [ UNIV_PAGE_SIZE_MAX ] ;
page_t page [ UNIV_PAGE_SIZE_MAX ] ;
memcpy ( page , out_buf , srv_page_size ) ;
ut_ad ( fil_page_decompress ( tmp_buf , page ) ) ;
ut_ad ( ! buf_page_is_corrupted ( false , page , univ_page_size ,
NULL ) ) ;
}
# endif /* UNIV_DEBUG */
@ -347,317 +276,143 @@ fil_compress_page(
# endif
}
DBUG_LOG ( " compress " , " Succeeded for space "
< < ( space ? space - > id : 0 ) < < " ' "
< < ( space ? space - > name : " (import) " )
< < " ' len " < < len < < " out_len " < < write_size ) ;
srv_stats . page_compression_saved . add ( ( len - write_size ) ) ;
srv_stats . page_compression_saved . add ( srv_page_size - write_size ) ;
srv_stats . pages_page_compressed . inc ( ) ;
/* If we do not persistently trim rest of page, we need to write it
all */
if ( ! srv_use_trim ) {
memset ( out_buf + write_size , 0 , len - write_size ) ;
write_size = len ;
}
* out_len = write_size ;
if ( allocated ) {
/* TODO: reduce number of memcpy's */
memcpy ( buf , out_buf , len ) ;
goto exit_free ;
} else {
return ( out_buf ) ;
memset ( out_buf + write_size , 0 , srv_page_size - write_size ) ;
}
err_exit :
/* If error we leave the actual page as it was */
# ifndef UNIV_PAGECOMPRESS_DEBUG
if ( space & & ! space - > printed_compression_failure ) {
space - > printed_compression_failure = true ;
# endif
ib : : warn ( ) < < " Compression failed for space: "
< < space - > id < < " name: "
< < space - > name < < " len: "
< < len < < " err: " < < err < < " write_size: "
< < write_size
< < " compression method: "
< < fil_get_compression_alg_name ( comp_method )
< < " . " ;
# ifndef UNIV_PAGECOMPRESS_DEBUG
}
# endif
srv_stats . pages_page_compression_error . inc ( ) ;
* out_len = len ;
exit_free :
if ( allocated ) {
ut_free ( out_buf ) ;
}
return ( buf ) ;
return write_size ;
}
/****************************************************************/ /**
For page compressed pages decompress the page after actual read
operation . */
UNIV_INTERN
void
fil_decompress_page (
/*================*/
byte * page_buf , /*!< in: preallocated buffer or NULL */
byte * buf , /*!< out: buffer from which to read; in aio
this must be appropriately aligned */
ulong len , /*!< in: length of output buffer.*/
ulint * write_size , /*!< in/out: Actual payload size of
the compressed data . */
bool return_error ) /*!< in: true if only an error should
be produced when decompression fails .
By default this parameter is false . */
/** Decompress a page that may be subject to page_compressed compression.
@ param [ in , out ] tmp_buf temporary buffer ( of innodb_page_size )
@ param [ in , out ] buf possibly compressed page buffer
@ return size of the compressed data
@ retval 0 if decompression failed
@ retval srv_page_size if the page was not compressed */
ulint fil_page_decompress ( byte * tmp_buf , byte * buf )
{
int err = 0 ;
ulint actual_size = 0 ;
ib_uint64_t compression_alg = 0 ;
byte * in_buf ;
ulint ptype ;
const unsigned ptype = mach_read_from_2 ( buf + FIL_PAGE_TYPE ) ;
ulint header_len ;
ut_ad ( buf ) ;
ut_ad ( len ) ;
ptype = mach_read_from_2 ( buf + FIL_PAGE_TYPE ) ;
uint64_t compression_alg ;
switch ( ptype ) {
case FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED :
header_len = FIL_PAGE_DATA + FIL_PAGE_COMPRESSED_SIZE
+ FIL_PAGE_COMPRESSION_METHOD_SIZE ;
compression_alg = mach_read_from_2 (
FIL_PAGE_DATA + FIL_PAGE_COMPRESSED_SIZE + buf ) ;
break ;
case FIL_PAGE_PAGE_COMPRESSED :
header_len = FIL_PAGE_DATA + FIL_PAGE_COMPRESSED_SIZE ;
compression_alg = mach_read_from_8 (
FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + buf ) ;
break ;
default :
/* The page is not in our format. */
return ;
return srv_page_size ;
}
// If no buffer was given, we need to allocate temporal buffer
if ( page_buf = = NULL ) {
in_buf = static_cast < byte * > ( ut_malloc_nokey ( UNIV_PAGE_SIZE ) ) ;
memset ( in_buf , 0 , UNIV_PAGE_SIZE ) ;
} else {
in_buf = page_buf ;
if ( mach_read_from_4 ( buf + FIL_PAGE_SPACE_OR_CHKSUM )
! = BUF_NO_CHECKSUM_MAGIC ) {
return 0 ;
}
/* Before actual decompress, make sure that page type is correct */
ulint actual_size = mach_read_from_2 ( buf + FIL_PAGE_DATA ) ;
if ( mach_read_from_4 ( buf + FIL_PAGE_SPACE_OR_CHKSUM )
! = BUF_NO_CHECKSUM_MAGIC
| | ( ptype ! = FIL_PAGE_PAGE_COMPRESSED
& & ptype ! = FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED ) ) {
ib : : error ( ) < < " Corruption: We try to uncompress corrupted "
" page CRC "
< < mach_read_from_4 ( buf + FIL_PAGE_SPACE_OR_CHKSUM )
< < " type " < < ptype < < " len " < < len < < " . " ;
if ( return_error ) {
goto error_return ;
}
ut_error ;
}
/* Get compression algorithm */
if ( ptype = = FIL_PAGE_PAGE_COMPRESSED_ENCRYPTED ) {
compression_alg = static_cast < ib_uint64_t > ( mach_read_from_2 ( buf + FIL_PAGE_DATA + FIL_PAGE_COMPRESSED_SIZE ) ) ;
} else {
compression_alg = mach_read_from_8 ( buf + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION ) ;
}
/* Get the actual size of compressed page */
actual_size = mach_read_from_2 ( buf + FIL_PAGE_DATA ) ;
/* Check if payload size is corrupted */
if ( actual_size = = 0 | | actual_size > UNIV_PAGE_SIZE ) {
ib : : error ( ) < < " Corruption: We try to uncompress corrupted page "
< < " actual size: " < < actual_size
< < " compression method: "
< < fil_get_compression_alg_name ( compression_alg )
< < " . " ;
if ( return_error ) {
goto error_return ;
}
ut_error ;
}
/* Store actual payload size of the compressed data. This pointer
points to buffer pool . */
if ( write_size ) {
* write_size = actual_size ;
if ( actual_size = = 0 | | actual_size > srv_page_size - header_len ) {
return 0 ;
}
DBUG_LOG ( " compress " , " Preparing for decompress for len "
< < actual_size < < " . " ) ;
switch ( compression_alg ) {
switch ( compression_alg ) {
default :
ib : : error ( ) < < " Unknown compression algorithm "
< < compression_alg ;
return 0 ;
case PAGE_ZLIB_ALGORITHM :
err = uncompress ( in_buf , & len , buf + header_len , ( unsigned long ) actual_size ) ;
/* If uncompress fails it means that page is corrupted */
if ( err ! = Z_OK ) {
goto err_exit ;
if ( return_error ) {
goto error_return ;
{
uLong len = srv_page_size ;
if ( Z_OK ! = uncompress ( tmp_buf , & len ,
buf + header_len ,
uLong ( actual_size ) )
& & len ! = srv_page_size ) {
return 0 ;
}
}
break ;
# ifdef HAVE_LZ4
case PAGE_LZ4_ALGORITHM :
err = LZ4_decompress_fast ( ( const char * ) buf + header_len , ( char * ) in_buf , len ) ;
if ( err ! = ( int ) actual_size ) {
goto err_exit ;
if ( return_error ) {
goto error_return ;
}
if ( LZ4_decompress_safe ( reinterpret_cast < const char * > ( buf )
+ header_len ,
reinterpret_cast < char * > ( tmp_buf ) ,
actual_size , srv_page_size )
= = int ( srv_page_size ) ) {
break ;
}
break ;
return 0 ;
# endif /* HAVE_LZ4 */
# ifdef HAVE_LZO
case PAGE_LZO_ALGORITHM : {
ulint olen = 0 ;
lzo_uint olen_lzo = olen ;
err = lzo1x_decompress ( ( const unsigned char * ) buf + header_len ,
actual_size , ( unsigned char * ) in_buf , & olen_lzo , NULL ) ;
olen = olen_lzo ;
if ( err ! = LZO_E_OK | | ( olen = = 0 | | olen > UNIV_PAGE_SIZE ) ) {
len = olen ;
goto err_exit ;
if ( return_error ) {
goto error_return ;
}
lzo_uint len_lzo = srv_page_size ;
if ( LZO_E_OK = = lzo1x_decompress_safe (
buf + header_len ,
actual_size , tmp_buf , & len_lzo , NULL )
& & len_lzo = = srv_page_size ) {
break ;
}
break ;
return 0 ;
}
# endif /* HAVE_LZO */
# ifdef HAVE_LZMA
case PAGE_LZMA_ALGORITHM : {
lzma_ret ret ;
size_t src_pos = 0 ;
size_t dst_pos = 0 ;
uint64_t memlimit = UINT64_MAX ;
ret = lzma_stream_buffer_decode (
& memlimit ,
0 ,
NULL ,
buf + header_len ,
& src_pos ,
actual_size ,
in_buf ,
& dst_pos ,
len ) ;
if ( ret ! = LZMA_OK | | ( dst_pos = = 0 | | dst_pos > UNIV_PAGE_SIZE ) ) {
len = dst_pos ;
goto err_exit ;
if ( return_error ) {
goto error_return ;
}
if ( LZMA_OK = = lzma_stream_buffer_decode (
& memlimit , 0 , NULL , buf + header_len ,
& src_pos , actual_size , tmp_buf , & dst_pos ,
srv_page_size )
& & dst_pos = = srv_page_size ) {
break ;
}
break ;
return 0 ;
}
# endif /* HAVE_LZMA */
# ifdef HAVE_BZIP2
case PAGE_BZIP2_ALGORITHM : {
unsigned int dst_pos = UNIV_PAGE_SIZE ;
err = BZ2_bzBuffToBuffDecompress (
( char * ) in_buf ,
& dst_pos ,
( char * ) ( buf + header_len ) ,
actual_size ,
1 ,
0 ) ;
if ( err ! = BZ_OK | | ( dst_pos = = 0 | | dst_pos > UNIV_PAGE_SIZE ) ) {
len = dst_pos ;
goto err_exit ;
if ( return_error ) {
goto error_return ;
}
unsigned int dst_pos = srv_page_size ;
if ( BZ_OK = = BZ2_bzBuffToBuffDecompress (
reinterpret_cast < char * > ( tmp_buf ) ,
& dst_pos ,
reinterpret_cast < char * > ( buf ) + header_len ,
actual_size , 1 , 0 )
& & dst_pos = = srv_page_size ) {
break ;
}
break ;
return 0 ;
}
# endif /* HAVE_BZIP2 */
# ifdef HAVE_SNAPPY
case PAGE_SNAPPY_ALGORITHM :
{
snappy_status cstatus ;
ulint olen = UNIV_PAGE_SIZE ;
cstatus = snappy_uncompress (
( const char * ) ( buf + header_len ) ,
( size_t ) actual_size ,
( char * ) in_buf ,
( size_t * ) & olen ) ;
if ( cstatus ! = SNAPPY_OK | | ( olen = = 0 | | olen > UNIV_PAGE_SIZE ) ) {
err = ( int ) cstatus ;
len = olen ;
goto err_exit ;
if ( return_error ) {
goto error_return ;
}
case PAGE_SNAPPY_ALGORITHM : {
size_t olen = srv_page_size ;
if ( SNAPPY_OK = = snappy_uncompress (
reinterpret_cast < const char * > ( buf ) + header_len ,
actual_size ,
reinterpret_cast < char * > ( tmp_buf ) , & olen )
& & olen = = srv_page_size ) {
break ;
}
break ;
return 0 ;
}
# endif /* HAVE_SNAPPY */
default :
goto err_exit ;
if ( return_error ) {
goto error_return ;
}
break ;
}
srv_stats . pages_page_decompressed . inc ( ) ;
/* Copy the uncompressed page to the buffer pool, not
really any other options . */
memcpy ( buf , in_buf , len ) ;
error_return :
if ( page_buf ! = in_buf ) {
ut_free ( in_buf ) ;
}
return ;
err_exit :
/* Note that as we have found the page is corrupted, so
all this could be incorrect . */
ulint space_id = mach_read_from_4 ( buf + FIL_PAGE_SPACE_ID ) ;
fil_space_t * space = fil_space_acquire_for_io ( space_id ) ;
ib : : error ( ) < < " Corruption: Page is marked as compressed "
< < " space: " < < space_id < < " name: "
< < ( space ? space - > name : " NULL " )
< < " but uncompress failed with error: " < < err
< < " size: " < < actual_size
< < " len: " < < len
< < " compression method: "
< < fil_get_compression_alg_name ( compression_alg ) < < " . " ;
buf_page_print ( buf , univ_page_size ) ;
fil_space_release_for_io ( space ) ;
ut_ad ( 0 ) ;
memcpy ( buf , tmp_buf , srv_page_size ) ;
return actual_size ;
}