~drizzle-trunk/drizzle/development

641.2.2 by Monty Taylor
InnoDB Plugin 1.0.3
1
/*****************************************************************************
2
3
Copyright (c) 2005, 2009, Innobase Oy. All Rights Reserved.
4
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.
8
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.
12
13
You should have received a copy of the GNU General Public License along with
1802.10.2 by Monty Taylor
Update all of the copyright headers to include the correct address.
14
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15
St, Fifth Floor, Boston, MA 02110-1301 USA
641.2.2 by Monty Taylor
InnoDB Plugin 1.0.3
16
17
*****************************************************************************/
18
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
19
/**************************************************//**
20
@file include/page0zip.ic
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
21
Compressed page interface
22
23
Created June 2005 by Marko Makela
24
*******************************************************/
25
26
#ifdef UNIV_MATERIALIZE
27
# undef UNIV_INLINE
28
# define UNIV_INLINE
29
#endif
30
31
#include "page0zip.h"
32
#include "page0page.h"
33
34
/* The format of compressed pages is as follows.
35
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.
39
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
48
uncompressed page.
49
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
55
page.
56
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
61
directory.
62
63
The compressed data stream may be followed by a modification log
64
covering the compressed portion of the page, as follows.
65
66
MODIFICATION LOG ENTRY FORMAT
67
- write record:
68
  - (heap_no - 1) << 1 (1..2 bytes)
69
  - extra bytes backwards
70
  - data bytes
71
- clear record:
72
  - (heap_no - 1) << 1 | 1 (1..2 bytes)
73
74
The integer values are stored in a variable-length format:
75
- 0xxxxxxx: 0..127
76
- 1xxxxxxx xxxxxxxx: 0..32767
77
78
The end of the modification log is marked by a 0 byte.
79
80
In summary, the compressed page looks like this:
81
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,
91
  - indexed by heap_no
92
  - DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN for leaf pages of clustered indexes
93
  - REC_NODE_PTR_SIZE for non-leaf pages
94
  - 0 otherwise
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
99
*/
100
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
101
/** Start offset of the area that will be compressed */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
102
#define PAGE_ZIP_START		PAGE_NEW_SUPREMUM_END
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
103
/** Size of an compressed page directory entry */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
104
#define PAGE_ZIP_DIR_SLOT_SIZE	2
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
105
/** Mask of record offsets */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
106
#define PAGE_ZIP_DIR_SLOT_MASK	0x3fff
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
107
/** 'owned' flag */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
108
#define PAGE_ZIP_DIR_SLOT_OWNED	0x4000
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
109
/** 'deleted' flag */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
110
#define PAGE_ZIP_DIR_SLOT_DEL	0x8000
111
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
112
/**********************************************************************//**
113
Determine the size of a compressed page in bytes.
114
@return	size in bytes */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
115
UNIV_INLINE
116
ulint
117
page_zip_get_size(
118
/*==============*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
119
	const page_zip_des_t*	page_zip)	/*!< in: compressed page */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
120
{
121
	ulint	size;
122
123
	if (UNIV_UNLIKELY(!page_zip->ssize)) {
124
		return(0);
125
	}
126
127
	size = (PAGE_ZIP_MIN_SIZE >> 1) << page_zip->ssize;
128
129
	ut_ad(size >= PAGE_ZIP_MIN_SIZE);
130
	ut_ad(size <= UNIV_PAGE_SIZE);
131
132
	return(size);
133
}
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
134
/**********************************************************************//**
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
135
Set the size of a compressed page in bytes. */
136
UNIV_INLINE
137
void
138
page_zip_set_size(
139
/*==============*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
140
	page_zip_des_t*	page_zip,	/*!< in/out: compressed page */
141
	ulint		size)		/*!< in: size in bytes */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
142
{
143
	if (size) {
144
		int	ssize;
145
146
		ut_ad(ut_is_2pow(size));
147
641.2.2 by Monty Taylor
InnoDB Plugin 1.0.3
148
		for (ssize = 1; size > (ulint) (512 << ssize); ssize++) {
149
		}
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
150
151
		page_zip->ssize = ssize;
152
	} else {
153
		page_zip->ssize = 0;
154
	}
155
156
	ut_ad(page_zip_get_size(page_zip) == size);
157
}
158
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
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 */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
163
UNIV_INLINE
164
ibool
165
page_zip_rec_needs_ext(
166
/*===================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
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;
641.2.1 by Monty Taylor
InnoDB Plugin 1.0.2
170
				ignored if zip_size == 0 */
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
171
	ulint	zip_size)	/*!< in: compressed page size in bytes, or 0 */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
172
{
173
	ut_ad(rec_size > comp ? REC_N_NEW_EXTRA_BYTES : REC_N_OLD_EXTRA_BYTES);
174
	ut_ad(ut_is_2pow(zip_size));
641.2.1 by Monty Taylor
InnoDB Plugin 1.0.2
175
	ut_ad(comp || !zip_size);
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
176
177
#if UNIV_PAGE_SIZE > REC_MAX_DATA_SIZE
178
	if (UNIV_UNLIKELY(rec_size >= REC_MAX_DATA_SIZE)) {
179
		return(TRUE);
180
	}
181
#endif
182
641.2.1 by Monty Taylor
InnoDB Plugin 1.0.2
183
	if (UNIV_UNLIKELY(zip_size)) {
184
		ut_ad(comp);
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);
194
	}
195
196
	return(rec_size >= page_get_free_space_of_empty(comp) / 2);
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
197
}
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
198
#endif /* !UNIV_HOTBACKUP */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
199
200
#ifdef UNIV_DEBUG
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
201
/**********************************************************************//**
202
Validate a compressed page descriptor.
203
@return	TRUE if ok */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
204
UNIV_INLINE
205
ibool
206
page_zip_simple_validate(
207
/*=====================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
208
	const page_zip_des_t*	page_zip)/*!< in: compressed page descriptor */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
209
{
210
	ut_ad(page_zip);
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);
219
	return(TRUE);
220
}
221
#endif /* UNIV_DEBUG */
222
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
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 */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
227
UNIV_INLINE
228
ibool
229
page_zip_get_trailer_len(
230
/*=====================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
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
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
234
					portion of a user record */
235
{
236
	ulint	uncompressed_size;
237
238
	ut_ad(page_zip_simple_validate(page_zip));
239
	UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip));
240
241
	if (UNIV_UNLIKELY(!page_is_leaf(page_zip->data))) {
242
		uncompressed_size = PAGE_ZIP_DIR_SLOT_SIZE
243
			+ REC_NODE_PTR_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;
248
	} else {
249
		uncompressed_size = PAGE_ZIP_DIR_SLOT_SIZE;
250
		ut_ad(!page_zip->n_blobs);
251
	}
252
253
	if (entry_size) {
254
		*entry_size = uncompressed_size;
255
	}
256
257
	return((page_dir_get_n_heap(page_zip->data) - 2)
258
	       * uncompressed_size
259
	       + page_zip->n_blobs * BTR_EXTERN_FIELD_REF_SIZE);
260
}
261
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
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 */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
266
UNIV_INLINE
267
lint
268
page_zip_max_ins_size(
269
/*==================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
270
	const page_zip_des_t*	page_zip,/*!< in: compressed page */
271
	ibool			is_clust)/*!< in: TRUE if clustered index */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
272
{
273
	ulint	uncompressed_size;
274
	ulint	trailer_len;
275
276
	trailer_len = page_zip_get_trailer_len(page_zip, is_clust,
277
					       &uncompressed_size);
278
279
	/* When a record is created, a pointer may be added to
280
	the dense directory.
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. */
285
286
	trailer_len += uncompressed_size;
287
288
	return((lint) page_zip_get_size(page_zip)
289
	       - trailer_len - page_zip->m_end
290
	       - (REC_N_NEW_EXTRA_BYTES - 2));
291
}
292
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
293
/**********************************************************************//**
294
Determine if enough space is available in the modification log.
295
@return	TRUE if enough space is available */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
296
UNIV_INLINE
297
ibool
298
page_zip_available(
299
/*===============*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
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
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
304
					the heap */
305
{
306
	ulint	uncompressed_size;
307
	ulint	trailer_len;
308
309
	ut_ad(length > REC_N_NEW_EXTRA_BYTES);
310
311
	trailer_len = page_zip_get_trailer_len(page_zip, is_clust,
312
					       &uncompressed_size);
313
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;
317
318
	if (UNIV_UNLIKELY(create)) {
319
		/* When a record is created, a pointer may be added to
320
		the dense directory.
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. */
325
326
		trailer_len += uncompressed_size;
327
	}
328
329
	return(UNIV_LIKELY(length
330
			   + trailer_len
331
			   + page_zip->m_end
332
			   < page_zip_get_size(page_zip)));
333
}
334
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
335
/**********************************************************************//**
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
336
Initialize a compressed page descriptor. */
337
UNIV_INLINE
338
void
339
page_zip_des_init(
340
/*==============*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
341
	page_zip_des_t*	page_zip)	/*!< in/out: compressed page
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
342
					descriptor */
343
{
344
	memset(page_zip, 0, sizeof *page_zip);
345
}
346
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
347
/**********************************************************************//**
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
348
Write a log record of writing to the uncompressed header portion of a page. */
349
UNIV_INTERN
350
void
351
page_zip_write_header_log(
352
/*======================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
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 */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
356
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
357
/**********************************************************************//**
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
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(). */
363
UNIV_INLINE
364
void
365
page_zip_write_header(
366
/*==================*/
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
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 */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
371
{
372
	ulint	pos;
373
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
374
	ut_ad(PAGE_ZIP_MATCH(str, page_zip));
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
375
	ut_ad(page_zip_simple_validate(page_zip));
376
	UNIV_MEM_ASSERT_RW(page_zip->data, page_zip_get_size(page_zip));
377
378
	pos = page_offset(str);
379
380
	ut_ad(pos < PAGE_DATA);
381
382
	memcpy(page_zip->data + pos, str, length);
383
384
	/* The following would fail in page_cur_insert_rec_zip(). */
385
	/* ut_ad(page_zip_validate(page_zip, str - pos)); */
386
387
	if (UNIV_LIKELY_NULL(mtr)) {
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
388
#ifndef UNIV_HOTBACKUP
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
389
		page_zip_write_header_log(str, length, mtr);
641.2.3 by Monty Taylor
InnoDB Plugin 1.0.4
390
#endif /* !UNIV_HOTBACKUP */
641.1.2 by Monty Taylor
Imported 1.0.1 with clean - with no changes.
391
	}
392
}
393
394
#ifdef UNIV_MATERIALIZE
395
# undef UNIV_INLINE
396
# define UNIV_INLINE	UNIV_INLINE_ORIGINAL
397
#endif