2005-10-27 11:48:10 +00:00
|
|
|
/******************************************************
|
|
|
|
Compressed page interface
|
|
|
|
|
|
|
|
(c) 2005 Innobase Oy
|
|
|
|
|
|
|
|
Created June 2005 by Marko Makela
|
|
|
|
*******************************************************/
|
|
|
|
|
|
|
|
#ifdef UNIV_MATERIALIZE
|
|
|
|
# undef UNIV_INLINE
|
|
|
|
# define UNIV_INLINE
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "page0zip.h"
|
|
|
|
#include "page0page.h"
|
|
|
|
|
|
|
|
/* The format of compressed pages is as follows.
|
|
|
|
|
2005-11-24 14:13:10 +00:00
|
|
|
The header and trailer of the uncompressed pages, excluding the page
|
2005-10-27 11:48:10 +00:00
|
|
|
directory in the trailer, are copied as is to the header and trailer
|
2005-11-24 14:13:10 +00:00
|
|
|
of the compressed page.
|
|
|
|
|
|
|
|
At the end of the compressed page, there is a dense page directory
|
|
|
|
pointing to every user record contained on the page, including deleted
|
2006-02-10 15:06:17 +00:00
|
|
|
records on the free list. The dense directory is indexed in the
|
|
|
|
collation order, i.e., in the order in which the record list is
|
|
|
|
linked on the uncompressed page. The infimum and supremum records are
|
|
|
|
excluded. The two most significant bits of the entries are allocated
|
|
|
|
for the delete-mark and an n_owned flag indicating the last record in
|
|
|
|
a chain of records pointed to from the sparse page directory on the
|
2005-11-24 14:13:10 +00:00
|
|
|
uncompressed page.
|
|
|
|
|
|
|
|
The data between PAGE_ZIP_START and the last page directory entry will
|
|
|
|
be written in compressed format, starting at offset PAGE_DATA.
|
|
|
|
Infimum and supremum records are not stored. We exclude the
|
|
|
|
REC_N_NEW_EXTRA_BYTES in every record header. These can be recovered
|
|
|
|
from the dense page directory stored at the end of the compressed
|
|
|
|
page.
|
2005-10-27 11:48:10 +00:00
|
|
|
|
2006-02-10 15:06:17 +00:00
|
|
|
The fields node_ptr (in non-leaf B-tree nodes; level>0), trx_id and
|
|
|
|
roll_ptr (in leaf B-tree nodes; level=0), and BLOB pointers of
|
|
|
|
externally stored columns are stored separately, in ascending order of
|
|
|
|
heap_no and column index, starting backwards from the dense page
|
|
|
|
directory.
|
|
|
|
|
2005-10-27 11:48:10 +00:00
|
|
|
The compressed data stream may be followed by a modification log
|
|
|
|
covering the compressed portion of the page, as follows.
|
|
|
|
|
|
|
|
MODIFICATION LOG ENTRY FORMAT
|
2006-02-10 15:06:17 +00:00
|
|
|
- write record:
|
2006-03-14 14:38:45 +00:00
|
|
|
- (heap_no - 1) << 1 (1..2 bytes)
|
2006-02-10 15:06:17 +00:00
|
|
|
- extra bytes backwards
|
|
|
|
- data bytes
|
2006-03-14 14:38:45 +00:00
|
|
|
- clear record:
|
|
|
|
- (heap_no - 1) << 1 | 1 (1..2 bytes)
|
2006-02-10 15:06:17 +00:00
|
|
|
|
|
|
|
The integer values are stored in a variable-length format:
|
|
|
|
- 0xxxxxxx: 0..127
|
|
|
|
- 1xxxxxxx xxxxxxxx: 0..32767
|
|
|
|
|
|
|
|
The end of the modification log is marked by a 0 byte.
|
|
|
|
|
|
|
|
In summary, the compressed page looks like this:
|
|
|
|
|
|
|
|
(1) Uncompressed page header (PAGE_DATA bytes)
|
|
|
|
(2) Compressed index information
|
|
|
|
(3) Compressed page data
|
|
|
|
(4) Page modification log (page_zip->m_start..page_zip->m_end)
|
|
|
|
(5) Empty zero-filled space
|
2006-03-09 17:26:02 +00:00
|
|
|
(6) BLOB pointers (on leaf pages)
|
2006-02-10 15:06:17 +00:00
|
|
|
- BTR_EXTERN_FIELD_REF_SIZE for each externally stored column
|
|
|
|
- in descending collation order
|
|
|
|
(7) Uncompressed columns of user records, n_dense * uncompressed_size bytes,
|
|
|
|
- indexed by heap_no
|
2006-03-09 17:26:02 +00:00
|
|
|
- DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN for leaf pages of clustered indexes
|
|
|
|
- REC_NODE_PTR_SIZE for non-leaf pages
|
|
|
|
- 0 otherwise
|
2006-02-27 10:26:59 +00:00
|
|
|
(8) dense page directory, stored backwards
|
2006-02-10 15:06:17 +00:00
|
|
|
- n_dense = n_heap - 2
|
|
|
|
- existing records in ascending collation order
|
|
|
|
- deleted records (free list) in link order
|
|
|
|
*/
|
2005-10-27 11:48:10 +00:00
|
|
|
|
2005-11-24 14:13:10 +00:00
|
|
|
/* Start offset of the area that will be compressed */
|
|
|
|
#define PAGE_ZIP_START PAGE_NEW_SUPREMUM_END
|
|
|
|
/* Size of an compressed page directory entry */
|
|
|
|
#define PAGE_ZIP_DIR_SLOT_SIZE 2
|
|
|
|
/* Mask of record offsets */
|
|
|
|
#define PAGE_ZIP_DIR_SLOT_MASK 0x3fff
|
|
|
|
/* 'owned' flag */
|
|
|
|
#define PAGE_ZIP_DIR_SLOT_OWNED 0x4000
|
|
|
|
/* 'deleted' flag */
|
|
|
|
#define PAGE_ZIP_DIR_SLOT_DEL 0x8000
|
|
|
|
|
2006-11-27 13:44:32 +00:00
|
|
|
/**************************************************************************
|
|
|
|
Determine the size of a compressed page in bytes. */
|
|
|
|
UNIV_INLINE
|
|
|
|
ulint
|
|
|
|
page_zip_get_size(
|
|
|
|
/*==============*/
|
|
|
|
/* out: size in bytes */
|
|
|
|
const page_zip_des_t* page_zip) /* in: compressed page */
|
|
|
|
{
|
|
|
|
ulint size;
|
|
|
|
|
|
|
|
if (UNIV_UNLIKELY(!page_zip->ssize)) {
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
size = 512 << page_zip->ssize;
|
|
|
|
|
|
|
|
ut_ad(size >= PAGE_ZIP_MIN_SIZE);
|
|
|
|
ut_ad(size <= UNIV_PAGE_SIZE);
|
|
|
|
|
|
|
|
return(size);
|
|
|
|
}
|
|
|
|
/**************************************************************************
|
|
|
|
Set the size of a compressed page in bytes. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
page_zip_set_size(
|
|
|
|
/*==============*/
|
|
|
|
page_zip_des_t* page_zip, /* in/out: compressed page */
|
|
|
|
ulint size) /* in: size in bytes */
|
|
|
|
{
|
|
|
|
if (size) {
|
|
|
|
int ssize;
|
|
|
|
|
|
|
|
ut_ad(ut_is_2pow(size));
|
|
|
|
|
|
|
|
for (ssize = 1; size > (ulint) (512 << ssize); ssize++);
|
|
|
|
|
|
|
|
page_zip->ssize = ssize;
|
|
|
|
} else {
|
|
|
|
page_zip->ssize = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ut_ad(page_zip_get_size(page_zip) == size);
|
|
|
|
}
|
|
|
|
|
2006-08-17 08:14:38 +00:00
|
|
|
/**************************************************************************
|
|
|
|
Determine if a record is so big that it needs to be stored externally. */
|
|
|
|
UNIV_INLINE
|
|
|
|
ibool
|
|
|
|
page_zip_rec_needs_ext(
|
|
|
|
/*===================*/
|
|
|
|
/* out: FALSE if the entire record
|
|
|
|
can be stored locally on the page */
|
|
|
|
ulint rec_size, /* in: length of the record in bytes */
|
|
|
|
ulint comp, /* in: nonzero=compact format */
|
|
|
|
ulint zip_size) /* in: compressed page size in bytes, or 0 */
|
|
|
|
{
|
|
|
|
ut_ad(rec_size > comp ? REC_N_NEW_EXTRA_BYTES : REC_N_OLD_EXTRA_BYTES);
|
|
|
|
ut_ad(ut_is_2pow(zip_size));
|
|
|
|
|
|
|
|
#if UNIV_PAGE_SIZE > REC_MAX_DATA_SIZE
|
|
|
|
if (UNIV_UNLIKELY(rec_size >= REC_MAX_DATA_SIZE)) {
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (UNIV_UNLIKELY(!comp)) {
|
|
|
|
ut_ad(!zip_size);
|
|
|
|
return(rec_size >= page_get_free_space_of_empty(FALSE) / 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If zip_size != 0, the record should fit on the compressed page.
|
|
|
|
If not, the right-hand-side of the comparison will overwrap
|
|
|
|
and the condition will not hold. Thus, we do not need to test
|
|
|
|
for zip_size != 0. We subtract the size of the page header and
|
|
|
|
assume that compressing the index information takes 50 bytes. */
|
|
|
|
if (rec_size >= zip_size - (PAGE_DATA + 50)) {
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(rec_size >= page_get_free_space_of_empty(TRUE) / 2);
|
|
|
|
}
|
|
|
|
|
2005-10-27 11:48:10 +00:00
|
|
|
#ifdef UNIV_DEBUG
|
|
|
|
/**************************************************************************
|
|
|
|
Validate a compressed page descriptor. */
|
|
|
|
UNIV_INLINE
|
|
|
|
ibool
|
|
|
|
page_zip_simple_validate(
|
|
|
|
/*=====================*/
|
|
|
|
/* out: TRUE if ok */
|
|
|
|
const page_zip_des_t* page_zip)/* in: compressed page descriptor */
|
|
|
|
{
|
|
|
|
ut_ad(page_zip);
|
|
|
|
ut_ad(page_zip->data);
|
2006-11-27 13:44:32 +00:00
|
|
|
ut_ad(page_zip_get_size(page_zip)
|
|
|
|
> PAGE_DATA + PAGE_ZIP_DIR_SLOT_SIZE);
|
2005-10-27 11:48:10 +00:00
|
|
|
ut_ad(page_zip->m_start <= page_zip->m_end);
|
2006-11-27 13:44:32 +00:00
|
|
|
ut_ad(page_zip->m_end < page_zip_get_size(page_zip));
|
|
|
|
ut_ad(page_zip->n_blobs
|
|
|
|
< page_zip_get_size(page_zip) / BTR_EXTERN_FIELD_REF_SIZE);
|
2005-10-27 11:48:10 +00:00
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
#endif /* UNIV_DEBUG */
|
|
|
|
|
2006-03-14 14:38:45 +00:00
|
|
|
/**************************************************************************
|
|
|
|
Determine if the length of the page trailer. */
|
|
|
|
UNIV_INLINE
|
|
|
|
ibool
|
|
|
|
page_zip_get_trailer_len(
|
|
|
|
/*=====================*/
|
|
|
|
/* out: length of the page trailer,
|
|
|
|
in bytes, not including the terminating
|
|
|
|
zero byte of the modification log */
|
|
|
|
const page_zip_des_t* page_zip,/* in: compressed page */
|
2007-02-19 14:51:44 +00:00
|
|
|
ibool is_clust,/* in: TRUE if clustered index */
|
2006-03-14 14:38:45 +00:00
|
|
|
ulint* entry_size)/* out: size of the uncompressed
|
|
|
|
portion of a user record */
|
|
|
|
{
|
|
|
|
ulint uncompressed_size;
|
|
|
|
|
|
|
|
ut_ad(page_zip_simple_validate(page_zip));
|
|
|
|
|
2006-10-19 07:52:28 +00:00
|
|
|
if (UNIV_UNLIKELY(!page_is_leaf(page_zip->data))) {
|
2006-03-14 14:38:45 +00:00
|
|
|
uncompressed_size = PAGE_ZIP_DIR_SLOT_SIZE
|
2006-08-29 09:30:31 +00:00
|
|
|
+ REC_NODE_PTR_SIZE;
|
2006-08-21 10:27:15 +00:00
|
|
|
ut_ad(!page_zip->n_blobs);
|
2007-02-19 14:51:44 +00:00
|
|
|
} else if (UNIV_UNLIKELY(is_clust)) {
|
2006-03-14 14:38:45 +00:00
|
|
|
uncompressed_size = PAGE_ZIP_DIR_SLOT_SIZE
|
2006-08-29 09:30:31 +00:00
|
|
|
+ DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN;
|
2006-03-14 14:38:45 +00:00
|
|
|
} else {
|
|
|
|
uncompressed_size = PAGE_ZIP_DIR_SLOT_SIZE;
|
2006-08-21 10:27:15 +00:00
|
|
|
ut_ad(!page_zip->n_blobs);
|
2006-03-14 14:38:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (entry_size) {
|
|
|
|
*entry_size = uncompressed_size;
|
|
|
|
}
|
|
|
|
|
2006-10-19 07:52:28 +00:00
|
|
|
return((page_dir_get_n_heap(page_zip->data) - 2)
|
2006-08-29 09:30:31 +00:00
|
|
|
* uncompressed_size
|
|
|
|
+ page_zip->n_blobs * BTR_EXTERN_FIELD_REF_SIZE);
|
2005-10-27 11:48:10 +00:00
|
|
|
}
|
|
|
|
|
branches/zip: Enable the insert buffer on compressed tablespaces.
page_zip_max_ins_size(): New function.
btr_cur_optimistic_insert(), btr_cur_optimistic_delete(),
btr_page_split_and_insert(), btr_compress(): Do not update the
ibuf free bits for non-leaf pages or pages belonging to a clustered index.
The insert buffer only covers operations on leaf pages of secondary indexes.
For pages covered by the insert buffer, limit the max_ins_size to
page_zip_max_ins_size().
buf_page_get_gen(): Merge the insert buffer after decompressing the page.
buf_page_io_complete(): Relax the assertion about ibuf_count. For
compressed-only pages, the insert buffer merge takes place
in buf_page_get_gen().
ibuf_index_page_calc_free_bits(), ibuf_index_page_calc_free_from_bits(),
ibuf_index_page_calc_free(), ibuf_update_free_bits_if_full(),
ibuf_update_free_bits_low(), ibuf_update_free_bits_for_two_pages_low(),
ibuf_set_free_bits_low(): Add the parameter zip_size. Limit the maximum
insert size to page_zip_max_ins_size().
2007-02-19 20:32:06 +00:00
|
|
|
/**************************************************************************
|
|
|
|
Determine how big record can be inserted without recompressing the page. */
|
|
|
|
UNIV_INLINE
|
|
|
|
lint
|
|
|
|
page_zip_max_ins_size(
|
|
|
|
/*==================*/
|
|
|
|
/* out: TRUE if page_zip_write_rec()
|
|
|
|
will succeed */
|
|
|
|
const page_zip_des_t* page_zip,/* in: compressed page */
|
|
|
|
ibool is_clust)/* in: TRUE if clustered index */
|
|
|
|
{
|
|
|
|
ulint uncompressed_size;
|
|
|
|
ulint trailer_len;
|
|
|
|
|
|
|
|
trailer_len = page_zip_get_trailer_len(page_zip, is_clust,
|
|
|
|
&uncompressed_size);
|
|
|
|
|
|
|
|
/* When a record is created, a pointer may be added to
|
|
|
|
the dense directory.
|
|
|
|
Likewise, space for the columns that will not be
|
|
|
|
compressed will be allocated from the page trailer.
|
|
|
|
Also the BLOB pointers will be allocated from there, but
|
|
|
|
we may as well count them in the length of the record. */
|
|
|
|
|
|
|
|
trailer_len += uncompressed_size;
|
|
|
|
|
|
|
|
return((lint) page_zip_get_size(page_zip)
|
|
|
|
- trailer_len - page_zip->m_end
|
|
|
|
- (REC_N_NEW_EXTRA_BYTES - 2));
|
|
|
|
}
|
|
|
|
|
2005-10-27 11:48:10 +00:00
|
|
|
/**************************************************************************
|
|
|
|
Determine if enough space is available in the modification log. */
|
|
|
|
UNIV_INLINE
|
|
|
|
ibool
|
|
|
|
page_zip_available(
|
|
|
|
/*===============*/
|
|
|
|
/* out: TRUE if enough space
|
|
|
|
is available */
|
|
|
|
const page_zip_des_t* page_zip,/* in: compressed page */
|
2007-02-19 14:51:44 +00:00
|
|
|
ibool is_clust,/* in: TRUE if clustered index */
|
2006-02-10 15:06:17 +00:00
|
|
|
ulint length, /* in: combined size of the record */
|
|
|
|
ulint create) /* in: nonzero=add the record to
|
|
|
|
the heap */
|
2005-10-27 11:48:10 +00:00
|
|
|
{
|
2006-02-10 15:06:17 +00:00
|
|
|
ulint uncompressed_size;
|
|
|
|
ulint trailer_len;
|
2005-10-27 11:48:10 +00:00
|
|
|
|
2006-02-10 15:06:17 +00:00
|
|
|
ut_ad(length > REC_N_NEW_EXTRA_BYTES);
|
|
|
|
|
2007-02-19 14:51:44 +00:00
|
|
|
trailer_len = page_zip_get_trailer_len(page_zip, is_clust,
|
2006-08-29 09:30:31 +00:00
|
|
|
&uncompressed_size);
|
2006-02-10 15:06:17 +00:00
|
|
|
|
|
|
|
/* Subtract the fixed extra bytes and add the maximum
|
|
|
|
space needed for identifying the record (encoded heap_no). */
|
|
|
|
length -= REC_N_NEW_EXTRA_BYTES - 2;
|
|
|
|
|
|
|
|
if (UNIV_UNLIKELY(create)) {
|
|
|
|
/* When a record is created, a pointer may be added to
|
2006-02-27 10:26:59 +00:00
|
|
|
the dense directory.
|
2006-02-10 15:06:17 +00:00
|
|
|
Likewise, space for the columns that will not be
|
|
|
|
compressed will be allocated from the page trailer.
|
|
|
|
Also the BLOB pointers will be allocated from there, but
|
|
|
|
we may as well count them in the length of the record. */
|
|
|
|
|
2006-04-03 20:33:31 +00:00
|
|
|
trailer_len += uncompressed_size;
|
2006-02-10 15:06:17 +00:00
|
|
|
}
|
2005-10-27 11:48:10 +00:00
|
|
|
|
2006-08-29 09:30:31 +00:00
|
|
|
return(UNIV_LIKELY(length
|
|
|
|
+ trailer_len
|
|
|
|
+ page_zip->m_end
|
2006-11-27 13:44:32 +00:00
|
|
|
< page_zip_get_size(page_zip)));
|
2005-10-27 11:48:10 +00:00
|
|
|
}
|
|
|
|
|
2006-07-03 19:12:21 +00:00
|
|
|
/**************************************************************************
|
|
|
|
Initialize a compressed page descriptor. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
page_zip_des_init(
|
|
|
|
/*==============*/
|
|
|
|
page_zip_des_t* page_zip) /* in/out: compressed page
|
|
|
|
descriptor */
|
|
|
|
{
|
|
|
|
memset(page_zip, 0, sizeof *page_zip);
|
|
|
|
}
|
|
|
|
|
2006-02-22 13:02:40 +00:00
|
|
|
/**************************************************************************
|
|
|
|
Write a log record of writing to the uncompressed header portion of a page. */
|
|
|
|
|
|
|
|
void
|
|
|
|
page_zip_write_header_log(
|
|
|
|
/*======================*/
|
2006-04-26 09:35:18 +00:00
|
|
|
const byte* data,/* in: data on the uncompressed page */
|
|
|
|
ulint length, /* in: length of the data */
|
|
|
|
mtr_t* mtr); /* in: mini-transaction */
|
2006-02-22 13:02:40 +00:00
|
|
|
|
2005-10-27 11:48:10 +00:00
|
|
|
/**************************************************************************
|
|
|
|
Write data to the uncompressed header portion of a page. The data must
|
2006-02-10 15:06:17 +00:00
|
|
|
already have been written to the uncompressed page.
|
|
|
|
However, the data portion of the uncompressed page may differ from
|
|
|
|
the compressed page when a record is being inserted in
|
2007-02-01 09:53:26 +00:00
|
|
|
page_cur_insert_rec_zip(). */
|
2005-10-27 11:48:10 +00:00
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
page_zip_write_header(
|
|
|
|
/*==================*/
|
|
|
|
page_zip_des_t* page_zip,/* in/out: compressed page */
|
|
|
|
const byte* str, /* in: address on the uncompressed page */
|
2006-02-22 13:02:40 +00:00
|
|
|
ulint length, /* in: length of the data */
|
|
|
|
mtr_t* mtr) /* in: mini-transaction, or NULL */
|
2005-10-27 11:48:10 +00:00
|
|
|
{
|
|
|
|
ulint pos;
|
|
|
|
|
2006-10-09 15:37:19 +00:00
|
|
|
ut_ad(buf_frame_get_page_zip((byte*)str) == page_zip);
|
2005-10-27 11:48:10 +00:00
|
|
|
ut_ad(page_zip_simple_validate(page_zip));
|
|
|
|
|
2006-09-19 10:14:07 +00:00
|
|
|
pos = page_offset(str);
|
2005-10-27 11:48:10 +00:00
|
|
|
|
|
|
|
ut_ad(pos < PAGE_DATA);
|
|
|
|
|
2006-03-02 14:05:32 +00:00
|
|
|
memcpy(page_zip->data + pos, str, length);
|
2005-10-27 11:48:10 +00:00
|
|
|
|
2007-02-01 09:53:26 +00:00
|
|
|
/* The following would fail in page_cur_insert_rec_zip(). */
|
2006-02-10 15:06:17 +00:00
|
|
|
/* ut_ad(page_zip_validate(page_zip, str - pos)); */
|
2006-02-22 13:02:40 +00:00
|
|
|
|
|
|
|
if (UNIV_LIKELY_NULL(mtr)) {
|
2006-04-26 09:35:18 +00:00
|
|
|
page_zip_write_header_log(str, length, mtr);
|
2006-02-22 13:02:40 +00:00
|
|
|
}
|
2005-10-27 11:48:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNIV_MATERIALIZE
|
|
|
|
# undef UNIV_INLINE
|
|
|
|
# define UNIV_INLINE UNIV_INLINE_ORIGINAL
|
|
|
|
#endif
|