~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/innobase/include/page0zip.ic

  • Committer: Brian Aker
  • Date: 2008-11-04 15:39:09 UTC
  • mfrom: (575.1.2 devel)
  • Revision ID: brian@tangent.org-20081104153909-c72hn65udxs1ccal
Merge of Monty's work

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/******************************************************
 
2
Compressed page interface
 
3
 
 
4
(c) 2005 Innobase Oy
 
5
 
 
6
Created June 2005 by Marko Makela
 
7
*******************************************************/
 
8
 
 
9
#ifdef UNIV_MATERIALIZE
 
10
# undef UNIV_INLINE
 
11
# define UNIV_INLINE
 
12
#endif
 
13
 
 
14
#include "page0zip.h"
 
15
#include "page0page.h"
 
16
 
 
17
/* The format of compressed pages is as follows.
 
18
 
 
19
The header and trailer of the uncompressed pages, excluding the page
 
20
directory in the trailer, are copied as is to the header and trailer
 
21
of the compressed page.
 
22
 
 
23
At the end of the compressed page, there is a dense page directory
 
24
pointing to every user record contained on the page, including deleted
 
25
records on the free list.  The dense directory is indexed in the
 
26
collation order, i.e., in the order in which the record list is
 
27
linked on the uncompressed page.  The infimum and supremum records are
 
28
excluded.  The two most significant bits of the entries are allocated
 
29
for the delete-mark and an n_owned flag indicating the last record in
 
30
a chain of records pointed to from the sparse page directory on the
 
31
uncompressed page.
 
32
 
 
33
The data between PAGE_ZIP_START and the last page directory entry will
 
34
be written in compressed format, starting at offset PAGE_DATA.
 
35
Infimum and supremum records are not stored.  We exclude the
 
36
REC_N_NEW_EXTRA_BYTES in every record header.  These can be recovered
 
37
from the dense page directory stored at the end of the compressed
 
38
page.
 
39
 
 
40
The fields node_ptr (in non-leaf B-tree nodes; level>0), trx_id and
 
41
roll_ptr (in leaf B-tree nodes; level=0), and BLOB pointers of
 
42
externally stored columns are stored separately, in ascending order of
 
43
heap_no and column index, starting backwards from the dense page
 
44
directory.
 
45
 
 
46
The compressed data stream may be followed by a modification log
 
47
covering the compressed portion of the page, as follows.
 
48
 
 
49
MODIFICATION LOG ENTRY FORMAT
 
50
- write record:
 
51
  - (heap_no - 1) << 1 (1..2 bytes)
 
52
  - extra bytes backwards
 
53
  - data bytes
 
54
- clear record:
 
55
  - (heap_no - 1) << 1 | 1 (1..2 bytes)
 
56
 
 
57
The integer values are stored in a variable-length format:
 
58
- 0xxxxxxx: 0..127
 
59
- 1xxxxxxx xxxxxxxx: 0..32767
 
60
 
 
61
The end of the modification log is marked by a 0 byte.
 
62
 
 
63
In summary, the compressed page looks like this:
 
64
 
 
65
(1) Uncompressed page header (PAGE_DATA bytes)
 
66
(2) Compressed index information
 
67
(3) Compressed page data
 
68
(4) Page modification log (page_zip->m_start..page_zip->m_end)
 
69
(5) Empty zero-filled space
 
70
(6) BLOB pointers (on leaf pages)
 
71
  - BTR_EXTERN_FIELD_REF_SIZE for each externally stored column
 
72
  - in descending collation order
 
73
(7) Uncompressed columns of user records, n_dense * uncompressed_size bytes,
 
74
  - indexed by heap_no
 
75
  - DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN for leaf pages of clustered indexes
 
76
  - REC_NODE_PTR_SIZE for non-leaf pages
 
77
  - 0 otherwise
 
78
(8) dense page directory, stored backwards
 
79
  - n_dense = n_heap - 2
 
80
  - existing records in ascending collation order
 
81
  - deleted records (free list) in link order
 
82
*/
 
83
 
 
84
/* Start offset of the area that will be compressed */
 
85
#define PAGE_ZIP_START          PAGE_NEW_SUPREMUM_END
 
86
/* Size of an compressed page directory entry */
 
87
#define PAGE_ZIP_DIR_SLOT_SIZE  2
 
88
/* Mask of record offsets */
 
89
#define PAGE_ZIP_DIR_SLOT_MASK  0x3fff
 
90
/* 'owned' flag */
 
91
#define PAGE_ZIP_DIR_SLOT_OWNED 0x4000
 
92
/* 'deleted' flag */
 
93
#define PAGE_ZIP_DIR_SLOT_DEL   0x8000
 
94
 
 
95
/**************************************************************************
 
96
Determine the size of a compressed page in bytes. */
 
97
UNIV_INLINE
 
98
ulint
 
99
page_zip_get_size(
 
100
/*==============*/
 
101
                                                /* out: size in bytes */
 
102
        const page_zip_des_t*   page_zip)       /* in: compressed page */
 
103
{
 
104
        ulint   size;
 
105
 
 
106
        if (UNIV_UNLIKELY(!page_zip->ssize)) {
 
107
                return(0);
 
108
        }
 
109
 
 
110
        size = (PAGE_ZIP_MIN_SIZE >> 1) << page_zip->ssize;
 
111
 
 
112
        ut_ad(size >= PAGE_ZIP_MIN_SIZE);
 
113
        ut_ad(size <= UNIV_PAGE_SIZE);
 
114
 
 
115
        return(size);
 
116
}
 
117
/**************************************************************************
 
118
Set the size of a compressed page in bytes. */
 
119
UNIV_INLINE
 
120
void
 
121
page_zip_set_size(
 
122
/*==============*/
 
123
        page_zip_des_t* page_zip,       /* in/out: compressed page */
 
124
        ulint           size)           /* in: size in bytes */
 
125
{
 
126
        if (size) {
 
127
                int     ssize;
 
128
 
 
129
                ut_ad(ut_is_2pow(size));
 
130
 
 
131
                for (ssize = 1; size > (ulint) (512 << ssize); ssize++) {};
 
132
 
 
133
                page_zip->ssize = ssize;
 
134
        } else {
 
135
                page_zip->ssize = 0;
 
136
        }
 
137
 
 
138
        ut_ad(page_zip_get_size(page_zip) == size);
 
139
}
 
140
 
 
141
/**************************************************************************
 
142
Determine if a record is so big that it needs to be stored externally. */
 
143
UNIV_INLINE
 
144
ibool
 
145
page_zip_rec_needs_ext(
 
146
/*===================*/
 
147
                                /* out: FALSE if the entire record
 
148
                                can be stored locally on the page */
 
149
        ulint   rec_size,       /* in: length of the record in bytes */
 
150
        ulint   comp,           /* in: nonzero=compact format */
 
151
        ulint   zip_size)       /* in: compressed page size in bytes, or 0 */
 
152
{
 
153
        ut_ad(rec_size > comp ? REC_N_NEW_EXTRA_BYTES : REC_N_OLD_EXTRA_BYTES);
 
154
        ut_ad(ut_is_2pow(zip_size));
 
155
 
 
156
#if UNIV_PAGE_SIZE > REC_MAX_DATA_SIZE
 
157
        if (UNIV_UNLIKELY(rec_size >= REC_MAX_DATA_SIZE)) {
 
158
                return(TRUE);
 
159
        }
 
160
#endif
 
161
 
 
162
        if (UNIV_UNLIKELY(!comp)) {
 
163
                ut_ad(!zip_size);
 
164
                return(rec_size >= page_get_free_space_of_empty((ulint)FALSE) / 2);
 
165
        }
 
166
 
 
167
        /* If zip_size != 0, the record should fit on the compressed page.
 
168
        If not, the right-hand-side of the comparison will overwrap
 
169
        and the condition will not hold.  Thus, we do not need to test
 
170
        for zip_size != 0.  We subtract the size of the page header and
 
171
        assume that compressing the index information takes 50 bytes. */
 
172
        if (rec_size >= zip_size - (PAGE_DATA + 50)) {
 
173
                return(TRUE);
 
174
        }
 
175
 
 
176
        return(rec_size >= page_get_free_space_of_empty(TRUE) / 2);
 
177
}
 
178
 
 
179
#ifdef UNIV_DEBUG
 
180
/**************************************************************************
 
181
Validate a compressed page descriptor. */
 
182
UNIV_INLINE
 
183
ibool
 
184
page_zip_simple_validate(
 
185
/*=====================*/
 
186
                                        /* out: TRUE if ok */
 
187
        const page_zip_des_t*   page_zip)/* in: compressed page descriptor */
 
188
{
 
189
        ut_ad(page_zip);
 
190
        ut_ad(page_zip->data);
 
191
        ut_ad(page_zip->ssize < PAGE_ZIP_NUM_SSIZE);
 
192
        ut_ad(page_zip_get_size(page_zip)
 
193
              > PAGE_DATA + PAGE_ZIP_DIR_SLOT_SIZE);
 
194
        ut_ad(page_zip->m_start <= page_zip->m_end);
 
195
        ut_ad(page_zip->m_end < page_zip_get_size(page_zip));
 
196
        ut_ad(page_zip->n_blobs
 
197
              < page_zip_get_size(page_zip) / BTR_EXTERN_FIELD_REF_SIZE);
 
198
        return(TRUE);
 
199
}
 
200
#endif /* UNIV_DEBUG */
 
201
 
 
202
/**************************************************************************
 
203
Determine if the length of the page trailer. */
 
204
UNIV_INLINE
 
205
ibool
 
206
page_zip_get_trailer_len(
 
207
/*=====================*/
 
208
                                        /* out: length of the page trailer,
 
209
                                        in bytes, not including the terminating
 
210
                                        zero byte of the modification log */
 
211
        const page_zip_des_t*   page_zip,/* in: compressed page */
 
212
        ibool                   is_clust,/* in: TRUE if clustered index */
 
213
        ulint*                  entry_size)/* out: size of the uncompressed
 
214
                                        portion of a user record */
 
215
{
 
216
        ulint   uncompressed_size;
 
217
 
 
218
        ut_ad(page_zip_simple_validate(page_zip));
 
219
        UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip));
 
220
 
 
221
        if (UNIV_UNLIKELY(!page_is_leaf(page_zip->data))) {
 
222
                uncompressed_size = PAGE_ZIP_DIR_SLOT_SIZE
 
223
                        + REC_NODE_PTR_SIZE;
 
224
                ut_ad(!page_zip->n_blobs);
 
225
        } else if (UNIV_UNLIKELY(is_clust)) {
 
226
                uncompressed_size = PAGE_ZIP_DIR_SLOT_SIZE
 
227
                        + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN;
 
228
        } else {
 
229
                uncompressed_size = PAGE_ZIP_DIR_SLOT_SIZE;
 
230
                ut_ad(!page_zip->n_blobs);
 
231
        }
 
232
 
 
233
        if (entry_size) {
 
234
                *entry_size = uncompressed_size;
 
235
        }
 
236
 
 
237
        return((page_dir_get_n_heap(page_zip->data) - 2)
 
238
               * uncompressed_size
 
239
               + page_zip->n_blobs * BTR_EXTERN_FIELD_REF_SIZE);
 
240
}
 
241
 
 
242
/**************************************************************************
 
243
Determine how big record can be inserted without recompressing the page. */
 
244
UNIV_INLINE
 
245
lint
 
246
page_zip_max_ins_size(
 
247
/*==================*/
 
248
                                        /* out: a positive number
 
249
                                        indicating the maximum size of
 
250
                                        a record whose insertion is
 
251
                                        guaranteed to succeed, or
 
252
                                        zero or negative */
 
253
        const page_zip_des_t*   page_zip,/* in: compressed page */
 
254
        ibool                   is_clust)/* in: TRUE if clustered index */
 
255
{
 
256
        ulint   uncompressed_size;
 
257
        ulint   trailer_len;
 
258
 
 
259
        trailer_len = page_zip_get_trailer_len(page_zip, is_clust,
 
260
                                               &uncompressed_size);
 
261
 
 
262
        /* When a record is created, a pointer may be added to
 
263
        the dense directory.
 
264
        Likewise, space for the columns that will not be
 
265
        compressed will be allocated from the page trailer.
 
266
        Also the BLOB pointers will be allocated from there, but
 
267
        we may as well count them in the length of the record. */
 
268
 
 
269
        trailer_len += uncompressed_size;
 
270
 
 
271
        return((lint) page_zip_get_size(page_zip)
 
272
               - trailer_len - page_zip->m_end
 
273
               - (REC_N_NEW_EXTRA_BYTES - 2));
 
274
}
 
275
 
 
276
/**************************************************************************
 
277
Determine if enough space is available in the modification log. */
 
278
UNIV_INLINE
 
279
ibool
 
280
page_zip_available(
 
281
/*===============*/
 
282
                                        /* out: TRUE if enough space
 
283
                                        is available */
 
284
        const page_zip_des_t*   page_zip,/* in: compressed page */
 
285
        ibool                   is_clust,/* in: TRUE if clustered index */
 
286
        ulint                   length, /* in: combined size of the record */
 
287
        ulint                   create) /* in: nonzero=add the record to
 
288
                                        the heap */
 
289
{
 
290
        ulint   uncompressed_size;
 
291
        ulint   trailer_len;
 
292
 
 
293
        ut_ad(length > REC_N_NEW_EXTRA_BYTES);
 
294
 
 
295
        trailer_len = page_zip_get_trailer_len(page_zip, is_clust,
 
296
                                               &uncompressed_size);
 
297
 
 
298
        /* Subtract the fixed extra bytes and add the maximum
 
299
        space needed for identifying the record (encoded heap_no). */
 
300
        length -= REC_N_NEW_EXTRA_BYTES - 2;
 
301
 
 
302
        if (UNIV_UNLIKELY(create)) {
 
303
                /* When a record is created, a pointer may be added to
 
304
                the dense directory.
 
305
                Likewise, space for the columns that will not be
 
306
                compressed will be allocated from the page trailer.
 
307
                Also the BLOB pointers will be allocated from there, but
 
308
                we may as well count them in the length of the record. */
 
309
 
 
310
                trailer_len += uncompressed_size;
 
311
        }
 
312
 
 
313
        return(UNIV_LIKELY(length
 
314
                           + trailer_len
 
315
                           + page_zip->m_end
 
316
                           < page_zip_get_size(page_zip)));
 
317
}
 
318
 
 
319
/**************************************************************************
 
320
Initialize a compressed page descriptor. */
 
321
UNIV_INLINE
 
322
void
 
323
page_zip_des_init(
 
324
/*==============*/
 
325
        page_zip_des_t* page_zip)       /* in/out: compressed page
 
326
                                        descriptor */
 
327
{
 
328
        memset(page_zip, 0, sizeof *page_zip);
 
329
}
 
330
 
 
331
/**************************************************************************
 
332
Write a log record of writing to the uncompressed header portion of a page. */
 
333
UNIV_INTERN
 
334
void
 
335
page_zip_write_header_log(
 
336
/*======================*/
 
337
        const byte*     data,/* in: data on the uncompressed page */
 
338
        ulint           length, /* in: length of the data */
 
339
        mtr_t*          mtr);   /* in: mini-transaction */
 
340
 
 
341
/**************************************************************************
 
342
Write data to the uncompressed header portion of a page.  The data must
 
343
already have been written to the uncompressed page.
 
344
However, the data portion of the uncompressed page may differ from
 
345
the compressed page when a record is being inserted in
 
346
page_cur_insert_rec_zip(). */
 
347
UNIV_INLINE
 
348
void
 
349
page_zip_write_header(
 
350
/*==================*/
 
351
        page_zip_des_t* page_zip,/* in/out: compressed page */
 
352
        const byte*     str,    /* in: address on the uncompressed page */
 
353
        ulint           length, /* in: length of the data */
 
354
        mtr_t*          mtr)    /* in: mini-transaction, or NULL */
 
355
{
 
356
        ulint   pos;
 
357
 
 
358
        ut_ad(buf_frame_get_page_zip(str) == page_zip);
 
359
        ut_ad(page_zip_simple_validate(page_zip));
 
360
        UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip));
 
361
 
 
362
        pos = page_offset(str);
 
363
 
 
364
        ut_ad(pos < PAGE_DATA);
 
365
 
 
366
        memcpy(page_zip->data + pos, str, length);
 
367
 
 
368
        /* The following would fail in page_cur_insert_rec_zip(). */
 
369
        /* ut_ad(page_zip_validate(page_zip, str - pos)); */
 
370
 
 
371
        if (UNIV_LIKELY_NULL(mtr)) {
 
372
                page_zip_write_header_log(str, length, mtr);
 
373
        }
 
374
}
 
375
 
 
376
#ifdef UNIV_MATERIALIZE
 
377
# undef UNIV_INLINE
 
378
# define UNIV_INLINE    UNIV_INLINE_ORIGINAL
 
379
#endif