1
/*****************************************************************************
3
Copyright (C) 2005, 2009, Innobase Oy. All Rights Reserved.
5
This program is free software; you can redistribute it and/or modify it under
6
the terms of the GNU General Public License as published by the Free Software
7
Foundation; version 2 of the License.
9
This program is distributed in the hope that it will be useful, but WITHOUT
10
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
You should have received a copy of the GNU General Public License along with
14
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15
St, Fifth Floor, Boston, MA 02110-1301 USA
17
*****************************************************************************/
19
/**************************************************//**
20
@file include/page0zip.ic
21
Compressed page interface
23
Created June 2005 by Marko Makela
24
*******************************************************/
26
#ifdef UNIV_MATERIALIZE
32
#include "page0page.h"
34
/* The format of compressed pages is as follows.
36
The header and trailer of the uncompressed pages, excluding the page
37
directory in the trailer, are copied as is to the header and trailer
38
of the compressed page.
40
At the end of the compressed page, there is a dense page directory
41
pointing to every user record contained on the page, including deleted
42
records on the free list. The dense directory is indexed in the
43
collation order, i.e., in the order in which the record list is
44
linked on the uncompressed page. The infimum and supremum records are
45
excluded. The two most significant bits of the entries are allocated
46
for the delete-mark and an n_owned flag indicating the last record in
47
a chain of records pointed to from the sparse page directory on the
50
The data between PAGE_ZIP_START and the last page directory entry will
51
be written in compressed format, starting at offset PAGE_DATA.
52
Infimum and supremum records are not stored. We exclude the
53
REC_N_NEW_EXTRA_BYTES in every record header. These can be recovered
54
from the dense page directory stored at the end of the compressed
57
The fields node_ptr (in non-leaf B-tree nodes; level>0), trx_id and
58
roll_ptr (in leaf B-tree nodes; level=0), and BLOB pointers of
59
externally stored columns are stored separately, in ascending order of
60
heap_no and column index, starting backwards from the dense page
63
The compressed data stream may be followed by a modification log
64
covering the compressed portion of the page, as follows.
66
MODIFICATION LOG ENTRY FORMAT
68
- (heap_no - 1) << 1 (1..2 bytes)
69
- extra bytes backwards
72
- (heap_no - 1) << 1 | 1 (1..2 bytes)
74
The integer values are stored in a variable-length format:
76
- 1xxxxxxx xxxxxxxx: 0..32767
78
The end of the modification log is marked by a 0 byte.
80
In summary, the compressed page looks like this:
82
(1) Uncompressed page header (PAGE_DATA bytes)
83
(2) Compressed index information
84
(3) Compressed page data
85
(4) Page modification log (page_zip->m_start..page_zip->m_end)
86
(5) Empty zero-filled space
87
(6) BLOB pointers (on leaf pages)
88
- BTR_EXTERN_FIELD_REF_SIZE for each externally stored column
89
- in descending collation order
90
(7) Uncompressed columns of user records, n_dense * uncompressed_size bytes,
92
- DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN for leaf pages of clustered indexes
93
- REC_NODE_PTR_SIZE for non-leaf pages
95
(8) dense page directory, stored backwards
96
- n_dense = n_heap - 2
97
- existing records in ascending collation order
98
- deleted records (free list) in link order
101
/** Start offset of the area that will be compressed */
102
#define PAGE_ZIP_START PAGE_NEW_SUPREMUM_END
103
/** Size of an compressed page directory entry */
104
#define PAGE_ZIP_DIR_SLOT_SIZE 2
105
/** Mask of record offsets */
106
#define PAGE_ZIP_DIR_SLOT_MASK 0x3fff
108
#define PAGE_ZIP_DIR_SLOT_OWNED 0x4000
109
/** 'deleted' flag */
110
#define PAGE_ZIP_DIR_SLOT_DEL 0x8000
112
/**********************************************************************//**
113
Determine the size of a compressed page in bytes.
114
@return size in bytes */
119
const page_zip_des_t* page_zip) /*!< in: compressed page */
123
if (UNIV_UNLIKELY(!page_zip->ssize)) {
127
size = (PAGE_ZIP_MIN_SIZE >> 1) << page_zip->ssize;
129
ut_ad(size >= PAGE_ZIP_MIN_SIZE);
130
ut_ad(size <= UNIV_PAGE_SIZE);
134
/**********************************************************************//**
135
Set the size of a compressed page in bytes. */
140
page_zip_des_t* page_zip, /*!< in/out: compressed page */
141
ulint size) /*!< in: size in bytes */
146
ut_ad(ut_is_2pow(size));
148
for (ssize = 1; size > (ulint) (512 << ssize); ssize++) {
151
page_zip->ssize = ssize;
156
ut_ad(page_zip_get_size(page_zip) == size);
159
#ifndef UNIV_HOTBACKUP
160
/**********************************************************************//**
161
Determine if a record is so big that it needs to be stored externally.
162
@return FALSE if the entire record can be stored locally on the page */
165
page_zip_rec_needs_ext(
166
/*===================*/
167
ulint rec_size, /*!< in: length of the record in bytes */
168
ulint comp, /*!< in: nonzero=compact format */
169
ulint n_fields, /*!< in: number of fields in the record;
170
ignored if zip_size == 0 */
171
ulint zip_size) /*!< in: compressed page size in bytes, or 0 */
173
ut_ad(rec_size > comp ? REC_N_NEW_EXTRA_BYTES : REC_N_OLD_EXTRA_BYTES);
174
ut_ad(ut_is_2pow(zip_size));
175
ut_ad(comp || !zip_size);
177
#if UNIV_PAGE_SIZE > REC_MAX_DATA_SIZE
178
if (UNIV_UNLIKELY(rec_size >= REC_MAX_DATA_SIZE)) {
183
if (UNIV_UNLIKELY(zip_size)) {
185
/* On a compressed page, there is a two-byte entry in
186
the dense page directory for every record. But there
187
is no record header. There should be enough room for
188
one record on an empty leaf page. Subtract 1 byte for
189
the encoded heap number. Check also the available space
190
on the uncompressed page. */
191
return(rec_size - (REC_N_NEW_EXTRA_BYTES - 2)
192
>= (page_zip_empty_size(n_fields, zip_size) - 1)
193
|| rec_size >= page_get_free_space_of_empty(TRUE) / 2);
196
return(rec_size >= page_get_free_space_of_empty(comp) / 2);
198
#endif /* !UNIV_HOTBACKUP */
201
/**********************************************************************//**
202
Validate a compressed page descriptor.
203
@return TRUE if ok */
206
page_zip_simple_validate(
207
/*=====================*/
208
const page_zip_des_t* page_zip)/*!< in: compressed page descriptor */
211
ut_ad(page_zip->data);
212
ut_ad(page_zip->ssize < PAGE_ZIP_NUM_SSIZE);
213
ut_ad(page_zip_get_size(page_zip)
214
> PAGE_DATA + PAGE_ZIP_DIR_SLOT_SIZE);
215
ut_ad(page_zip->m_start <= page_zip->m_end);
216
ut_ad(page_zip->m_end < page_zip_get_size(page_zip));
217
ut_ad(page_zip->n_blobs
218
< page_zip_get_size(page_zip) / BTR_EXTERN_FIELD_REF_SIZE);
221
#endif /* UNIV_DEBUG */
223
/**********************************************************************//**
224
Determine if the length of the page trailer.
225
@return length of the page trailer, in bytes, not including the
226
terminating zero byte of the modification log */
229
page_zip_get_trailer_len(
230
/*=====================*/
231
const page_zip_des_t* page_zip,/*!< in: compressed page */
232
ibool is_clust,/*!< in: TRUE if clustered index */
233
ulint* entry_size)/*!< out: size of the uncompressed
234
portion of a user record */
236
ulint uncompressed_size;
238
ut_ad(page_zip_simple_validate(page_zip));
239
UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip));
241
if (UNIV_UNLIKELY(!page_is_leaf(page_zip->data))) {
242
uncompressed_size = PAGE_ZIP_DIR_SLOT_SIZE
244
ut_ad(!page_zip->n_blobs);
245
} else if (UNIV_UNLIKELY(is_clust)) {
246
uncompressed_size = PAGE_ZIP_DIR_SLOT_SIZE
247
+ DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN;
249
uncompressed_size = PAGE_ZIP_DIR_SLOT_SIZE;
250
ut_ad(!page_zip->n_blobs);
254
*entry_size = uncompressed_size;
257
return((page_dir_get_n_heap(page_zip->data) - 2)
259
+ page_zip->n_blobs * BTR_EXTERN_FIELD_REF_SIZE);
262
/**********************************************************************//**
263
Determine how big record can be inserted without recompressing the page.
264
@return a positive number indicating the maximum size of a record
265
whose insertion is guaranteed to succeed, or zero or negative */
268
page_zip_max_ins_size(
269
/*==================*/
270
const page_zip_des_t* page_zip,/*!< in: compressed page */
271
ibool is_clust)/*!< in: TRUE if clustered index */
273
ulint uncompressed_size;
276
trailer_len = page_zip_get_trailer_len(page_zip, is_clust,
279
/* When a record is created, a pointer may be added to
281
Likewise, space for the columns that will not be
282
compressed will be allocated from the page trailer.
283
Also the BLOB pointers will be allocated from there, but
284
we may as well count them in the length of the record. */
286
trailer_len += uncompressed_size;
288
return((lint) page_zip_get_size(page_zip)
289
- trailer_len - page_zip->m_end
290
- (REC_N_NEW_EXTRA_BYTES - 2));
293
/**********************************************************************//**
294
Determine if enough space is available in the modification log.
295
@return TRUE if enough space is available */
300
const page_zip_des_t* page_zip,/*!< in: compressed page */
301
ibool is_clust,/*!< in: TRUE if clustered index */
302
ulint length, /*!< in: combined size of the record */
303
ulint create) /*!< in: nonzero=add the record to
306
ulint uncompressed_size;
309
ut_ad(length > REC_N_NEW_EXTRA_BYTES);
311
trailer_len = page_zip_get_trailer_len(page_zip, is_clust,
314
/* Subtract the fixed extra bytes and add the maximum
315
space needed for identifying the record (encoded heap_no). */
316
length -= REC_N_NEW_EXTRA_BYTES - 2;
318
if (UNIV_UNLIKELY(create)) {
319
/* When a record is created, a pointer may be added to
321
Likewise, space for the columns that will not be
322
compressed will be allocated from the page trailer.
323
Also the BLOB pointers will be allocated from there, but
324
we may as well count them in the length of the record. */
326
trailer_len += uncompressed_size;
329
return(UNIV_LIKELY(length
332
< page_zip_get_size(page_zip)));
335
/**********************************************************************//**
336
Initialize a compressed page descriptor. */
341
page_zip_des_t* page_zip) /*!< in/out: compressed page
344
memset(page_zip, 0, sizeof *page_zip);
347
/**********************************************************************//**
348
Write a log record of writing to the uncompressed header portion of a page. */
351
page_zip_write_header_log(
352
/*======================*/
353
const byte* data,/*!< in: data on the uncompressed page */
354
ulint length, /*!< in: length of the data */
355
mtr_t* mtr); /*!< in: mini-transaction */
357
/**********************************************************************//**
358
Write data to the uncompressed header portion of a page. The data must
359
already have been written to the uncompressed page.
360
However, the data portion of the uncompressed page may differ from
361
the compressed page when a record is being inserted in
362
page_cur_insert_rec_zip(). */
365
page_zip_write_header(
366
/*==================*/
367
page_zip_des_t* page_zip,/*!< in/out: compressed page */
368
const byte* str, /*!< in: address on the uncompressed page */
369
ulint length, /*!< in: length of the data */
370
mtr_t* mtr) /*!< in: mini-transaction, or NULL */
374
ut_ad(PAGE_ZIP_MATCH(str, page_zip));
375
ut_ad(page_zip_simple_validate(page_zip));
376
UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip));
378
pos = page_offset(str);
380
ut_ad(pos < PAGE_DATA);
382
memcpy(page_zip->data + pos, str, length);
384
/* The following would fail in page_cur_insert_rec_zip(). */
385
/* ut_ad(page_zip_validate(page_zip, str - pos)); */
387
if (UNIV_LIKELY_NULL(mtr)) {
388
#ifndef UNIV_HOTBACKUP
389
page_zip_write_header_log(str, length, mtr);
390
#endif /* !UNIV_HOTBACKUP */
394
#ifdef UNIV_MATERIALIZE
396
# define UNIV_INLINE UNIV_INLINE_ORIGINAL