page0page.ic 28.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*****************************************************************************

Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved.

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
Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA

*****************************************************************************/

19 20
/**************************************************//**
@file include/page0page.ic
osku's avatar
osku committed
21 22 23 24 25 26
Index page routines

Created 2/2/1994 Heikki Tuuri
*******************************************************/

#include "mach0data.h"
27 28 29
#ifdef UNIV_DEBUG
# include "log0recv.h"
#endif /* !UNIV_DEBUG */
30 31 32
#ifndef UNIV_HOTBACKUP
# include "rem0cmp.h"
#endif /* !UNIV_HOTBACKUP */
osku's avatar
osku committed
33
#include "mtr0log.h"
34
#include "page0zip.h"
osku's avatar
osku committed
35 36 37 38 39 40

#ifdef UNIV_MATERIALIZE
#undef UNIV_INLINE
#define UNIV_INLINE
#endif

41
/************************************************************//**
42 43
Gets the start of a page.
@return	start of the page */
44 45 46 47
UNIV_INLINE
page_t*
page_align(
/*=======*/
48
	const void*	ptr)	/*!< in: pointer to page frame */
49 50 51
{
	return((page_t*) ut_align_down(ptr, UNIV_PAGE_SIZE));
}
52
/************************************************************//**
53 54
Gets the offset within a page.
@return	offset from the start of the page */
55 56 57 58
UNIV_INLINE
ulint
page_offset(
/*========*/
59
	const void*	ptr)	/*!< in: pointer to page frame */
60 61 62
{
	return(ut_align_offset(ptr, UNIV_PAGE_SIZE));
}
63
/*************************************************************//**
osku's avatar
osku committed
64 65
Returns the max trx id field value. */
UNIV_INLINE
66
trx_id_t
osku's avatar
osku committed
67 68
page_get_max_trx_id(
/*================*/
69
	const page_t*	page)	/*!< in: page */
osku's avatar
osku committed
70 71 72 73 74 75
{
	ut_ad(page);

	return(mach_read_from_8(page + PAGE_HEADER + PAGE_MAX_TRX_ID));
}

76
/*************************************************************//**
osku's avatar
osku committed
77 78 79 80 81 82
Sets the max trx id field value if trx_id is bigger than the previous
value. */
UNIV_INLINE
void
page_update_max_trx_id(
/*===================*/
83 84
	buf_block_t*	block,	/*!< in/out: page */
	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
marko's avatar
marko committed
85
				uncompressed part will be updated, or NULL */
86 87
	trx_id_t	trx_id,	/*!< in: transaction id */
	mtr_t*		mtr)	/*!< in/out: mini-transaction */
osku's avatar
osku committed
88
{
marko's avatar
marko committed
89
	ut_ad(block);
90 91 92 93 94 95 96 97 98
	ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
	/* During crash recovery, this function may be called on
	something else than a leaf page of a secondary index or the
	insert buffer index tree (dict_index_is_sec_or_ibuf() returns
	TRUE for the dummy indexes constructed during redo log
	application).  In that case, PAGE_MAX_TRX_ID is unused,
	and trx_id is usually zero. */
	ut_ad(!ut_dulint_is_zero(trx_id) || recv_recovery_is_on());
	ut_ad(page_is_leaf(buf_block_get_frame(block)));
osku's avatar
osku committed
99

marko's avatar
marko committed
100 101
	if (ut_dulint_cmp(page_get_max_trx_id(buf_block_get_frame(block)),
			  trx_id) < 0) {
102

103
		page_set_max_trx_id(block, page_zip, trx_id, mtr);
osku's avatar
osku committed
104 105 106
	}
}

107
/*************************************************************//**
osku's avatar
osku committed
108 109 110 111 112
Reads the given header field. */
UNIV_INLINE
ulint
page_header_get_field(
/*==================*/
113 114
	const page_t*	page,	/*!< in: page */
	ulint		field)	/*!< in: PAGE_LEVEL, ... */
osku's avatar
osku committed
115 116 117 118 119 120 121
{
	ut_ad(page);
	ut_ad(field <= PAGE_INDEX_ID);

	return(mach_read_from_2(page + PAGE_HEADER + field));
}

122
/*************************************************************//**
osku's avatar
osku committed
123 124 125 126 127
Sets the given header field. */
UNIV_INLINE
void
page_header_set_field(
/*==================*/
128 129
	page_t*		page,	/*!< in/out: page */
	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
marko's avatar
marko committed
130
				uncompressed part will be updated, or NULL */
131 132
	ulint		field,	/*!< in: PAGE_N_DIR_SLOTS, ... */
	ulint		val)	/*!< in: value */
osku's avatar
osku committed
133 134 135 136 137 138 139
{
	ut_ad(page);
	ut_ad(field <= PAGE_N_RECS);
	ut_ad(field == PAGE_N_HEAP || val < UNIV_PAGE_SIZE);
	ut_ad(field != PAGE_N_HEAP || (val & 0x7fff) < UNIV_PAGE_SIZE);

	mach_write_to_2(page + PAGE_HEADER + field, val);
marko's avatar
marko committed
140
	if (UNIV_LIKELY_NULL(page_zip)) {
141
		page_zip_write_header(page_zip,
142
				      page + PAGE_HEADER + field, 2, NULL);
marko's avatar
marko committed
143
	}
osku's avatar
osku committed
144 145
}

146
/*************************************************************//**
147 148
Returns the offset stored in the given header field.
@return	offset from the start of the page, or 0 */
osku's avatar
osku committed
149
UNIV_INLINE
150 151 152
ulint
page_header_get_offs(
/*=================*/
153 154
	const page_t*	page,	/*!< in: page */
	ulint		field)	/*!< in: PAGE_FREE, ... */
osku's avatar
osku committed
155 156 157 158 159
{
	ulint	offs;

	ut_ad(page);
	ut_ad((field == PAGE_FREE)
160 161
	      || (field == PAGE_LAST_INSERT)
	      || (field == PAGE_HEAP_TOP));
osku's avatar
osku committed
162 163 164 165 166

	offs = page_header_get_field(page, field);

	ut_ad((field != PAGE_HEAP_TOP) || offs);

167
	return(offs);
osku's avatar
osku committed
168 169
}

170
/*************************************************************//**
osku's avatar
osku committed
171 172 173 174 175
Sets the pointer stored in the given header field. */
UNIV_INLINE
void
page_header_set_ptr(
/*================*/
176 177
	page_t*		page,	/*!< in: page */
	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
marko's avatar
marko committed
178
				uncompressed part will be updated, or NULL */
179 180
	ulint		field,	/*!< in: PAGE_FREE, ... */
	const byte*	ptr)	/*!< in: pointer or NULL*/
osku's avatar
osku committed
181 182 183 184 185
{
	ulint	offs;

	ut_ad(page);
	ut_ad((field == PAGE_FREE)
186 187
	      || (field == PAGE_LAST_INSERT)
	      || (field == PAGE_HEAP_TOP));
osku's avatar
osku committed
188 189 190 191 192 193 194 195 196

	if (ptr == NULL) {
		offs = 0;
	} else {
		offs = ptr - page;
	}

	ut_ad((field != PAGE_HEAP_TOP) || offs);

marko's avatar
marko committed
197
	page_header_set_field(page, page_zip, field, offs);
osku's avatar
osku committed
198 199
}

200
#ifndef UNIV_HOTBACKUP
201
/*************************************************************//**
osku's avatar
osku committed
202 203 204 205 206 207
Resets the last insert info field in the page header. Writes to mlog
about this operation. */
UNIV_INLINE
void
page_header_reset_last_insert(
/*==========================*/
208 209
	page_t*		page,	/*!< in/out: page */
	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
210
				uncompressed part will be updated, or NULL */
211
	mtr_t*		mtr)	/*!< in: mtr */
osku's avatar
osku committed
212 213 214
{
	ut_ad(page && mtr);

215
	if (UNIV_LIKELY_NULL(page_zip)) {
216
		mach_write_to_2(page + (PAGE_HEADER + PAGE_LAST_INSERT), 0);
217
		page_zip_write_header(page_zip,
218 219
				      page + (PAGE_HEADER + PAGE_LAST_INSERT),
				      2, mtr);
220 221
	} else {
		mlog_write_ulint(page + (PAGE_HEADER + PAGE_LAST_INSERT), 0,
222
				 MLOG_2BYTES, mtr);
223
	}
osku's avatar
osku committed
224
}
225
#endif /* !UNIV_HOTBACKUP */
osku's avatar
osku committed
226

227
/************************************************************//**
228
Determine whether the page is in new-style compact format.
229 230
@return nonzero if the page is in compact format, zero if it is in
old-style format */
osku's avatar
osku committed
231 232 233 234
UNIV_INLINE
ulint
page_is_comp(
/*=========*/
235
	const page_t*	page)	/*!< in: index page */
osku's avatar
osku committed
236 237
{
	return(UNIV_EXPECT(page_header_get_field(page, PAGE_N_HEAP) & 0x8000,
238
			   0x8000));
osku's avatar
osku committed
239 240
}

241
/************************************************************//**
242 243
TRUE if the record is on a page in compact format.
@return	nonzero if in compact format */
osku's avatar
osku committed
244 245 246 247
UNIV_INLINE
ulint
page_rec_is_comp(
/*=============*/
248
	const rec_t*	rec)	/*!< in: record */
osku's avatar
osku committed
249
{
250
	return(page_is_comp(page_align(rec)));
osku's avatar
osku committed
251 252
}

253
/***************************************************************//**
254 255
Returns the heap number of a record.
@return	heap number */
256 257 258 259
UNIV_INLINE
ulint
page_rec_get_heap_no(
/*=================*/
260
	const rec_t*	rec)	/*!< in: the physical record */
261 262 263 264 265 266 267 268
{
	if (page_rec_is_comp(rec)) {
		return(rec_get_heap_no_new(rec));
	} else {
		return(rec_get_heap_no_old(rec));
	}
}

269
/************************************************************//**
270 271
Determine whether the page is a B-tree leaf.
@return	TRUE if the page is a B-tree leaf */
272 273 274 275
UNIV_INLINE
ibool
page_is_leaf(
/*=========*/
276
	const page_t*	page)	/*!< in: page */
277 278 279 280
{
	return(!*(const uint16*) (page + (PAGE_HEADER + PAGE_LEVEL)));
}

281
/************************************************************//**
282 283
Gets the offset of the first record on the page.
@return	offset of the first record in record list, relative from page */
osku's avatar
osku committed
284
UNIV_INLINE
285 286 287
ulint
page_get_infimum_offset(
/*====================*/
288
	const page_t*	page)	/*!< in: page which must have record(s) */
osku's avatar
osku committed
289 290
{
	ut_ad(page);
291
	ut_ad(!page_offset(page));
osku's avatar
osku committed
292 293

	if (page_is_comp(page)) {
294
		return(PAGE_NEW_INFIMUM);
osku's avatar
osku committed
295
	} else {
296
		return(PAGE_OLD_INFIMUM);
osku's avatar
osku committed
297 298 299
	}
}

300
/************************************************************//**
301 302
Gets the offset of the last record on the page.
@return	offset of the last record in record list, relative from page */
osku's avatar
osku committed
303
UNIV_INLINE
304 305 306
ulint
page_get_supremum_offset(
/*=====================*/
307
	const page_t*	page)	/*!< in: page which must have record(s) */
osku's avatar
osku committed
308 309
{
	ut_ad(page);
310
	ut_ad(!page_offset(page));
osku's avatar
osku committed
311 312

	if (page_is_comp(page)) {
313
		return(PAGE_NEW_SUPREMUM);
osku's avatar
osku committed
314
	} else {
315
		return(PAGE_OLD_SUPREMUM);
osku's avatar
osku committed
316 317 318
	}
}

319
/************************************************************//**
320 321
TRUE if the record is a user record on the page.
@return	TRUE if a user record */
osku's avatar
osku committed
322 323 324 325
UNIV_INLINE
ibool
page_rec_is_user_rec_low(
/*=====================*/
326
	ulint	offset)	/*!< in: record offset on page */
osku's avatar
osku committed
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
{
	ut_ad(offset >= PAGE_NEW_INFIMUM);
#if PAGE_OLD_INFIMUM < PAGE_NEW_INFIMUM
# error "PAGE_OLD_INFIMUM < PAGE_NEW_INFIMUM"
#endif
#if PAGE_OLD_SUPREMUM < PAGE_NEW_SUPREMUM
# error "PAGE_OLD_SUPREMUM < PAGE_NEW_SUPREMUM"
#endif
#if PAGE_NEW_INFIMUM > PAGE_OLD_SUPREMUM
# error "PAGE_NEW_INFIMUM > PAGE_OLD_SUPREMUM"
#endif
#if PAGE_OLD_INFIMUM > PAGE_NEW_SUPREMUM
# error "PAGE_OLD_INFIMUM > PAGE_NEW_SUPREMUM"
#endif
#if PAGE_NEW_SUPREMUM > PAGE_OLD_SUPREMUM_END
# error "PAGE_NEW_SUPREMUM > PAGE_OLD_SUPREMUM_END"
#endif
#if PAGE_OLD_SUPREMUM > PAGE_NEW_SUPREMUM_END
# error "PAGE_OLD_SUPREMUM > PAGE_NEW_SUPREMUM_END"
#endif
	ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START);

	return(UNIV_LIKELY(offset != PAGE_NEW_SUPREMUM)
350 351 352
	       && UNIV_LIKELY(offset != PAGE_NEW_INFIMUM)
	       && UNIV_LIKELY(offset != PAGE_OLD_INFIMUM)
	       && UNIV_LIKELY(offset != PAGE_OLD_SUPREMUM));
osku's avatar
osku committed
353 354
}

355
/************************************************************//**
356 357
TRUE if the record is the supremum record on a page.
@return	TRUE if the supremum record */
osku's avatar
osku committed
358 359 360 361
UNIV_INLINE
ibool
page_rec_is_supremum_low(
/*=====================*/
362
	ulint	offset)	/*!< in: record offset on page */
osku's avatar
osku committed
363 364 365 366 367
{
	ut_ad(offset >= PAGE_NEW_INFIMUM);
	ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START);

	return(UNIV_UNLIKELY(offset == PAGE_NEW_SUPREMUM)
368
	       || UNIV_UNLIKELY(offset == PAGE_OLD_SUPREMUM));
osku's avatar
osku committed
369 370
}

371
/************************************************************//**
372 373
TRUE if the record is the infimum record on a page.
@return	TRUE if the infimum record */
osku's avatar
osku committed
374 375 376
UNIV_INLINE
ibool
page_rec_is_infimum_low(
377
/*====================*/
378
	ulint	offset)	/*!< in: record offset on page */
osku's avatar
osku committed
379 380 381 382 383
{
	ut_ad(offset >= PAGE_NEW_INFIMUM);
	ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START);

	return(UNIV_UNLIKELY(offset == PAGE_NEW_INFIMUM)
384
	       || UNIV_UNLIKELY(offset == PAGE_OLD_INFIMUM));
osku's avatar
osku committed
385 386
}

387
/************************************************************//**
388 389
TRUE if the record is a user record on the page.
@return	TRUE if a user record */
osku's avatar
osku committed
390 391 392 393
UNIV_INLINE
ibool
page_rec_is_user_rec(
/*=================*/
394
	const rec_t*	rec)	/*!< in: record */
osku's avatar
osku committed
395
{
396
	return(page_rec_is_user_rec_low(page_offset(rec)));
osku's avatar
osku committed
397 398
}

399
/************************************************************//**
400 401
TRUE if the record is the supremum record on a page.
@return	TRUE if the supremum record */
osku's avatar
osku committed
402 403 404 405
UNIV_INLINE
ibool
page_rec_is_supremum(
/*=================*/
406
	const rec_t*	rec)	/*!< in: record */
osku's avatar
osku committed
407
{
408
	return(page_rec_is_supremum_low(page_offset(rec)));
osku's avatar
osku committed
409 410
}

411
/************************************************************//**
412 413
TRUE if the record is the infimum record on a page.
@return	TRUE if the infimum record */
osku's avatar
osku committed
414 415 416 417
UNIV_INLINE
ibool
page_rec_is_infimum(
/*================*/
418
	const rec_t*	rec)	/*!< in: record */
osku's avatar
osku committed
419
{
420
	return(page_rec_is_infimum_low(page_offset(rec)));
osku's avatar
osku committed
421 422
}

423
#ifndef UNIV_HOTBACKUP
424
/*************************************************************//**
osku's avatar
osku committed
425 426 427 428
Compares a data tuple to a physical record. Differs from the function
cmp_dtuple_rec_with_match in the way that the record must reside on an
index page, and also page infimum and supremum records can be given in
the parameter rec. These are considered as the negative infinity and
429
the positive infinity in the alphabetical order.
430 431
@return 1, 0, -1, if dtuple is greater, equal, less than rec,
respectively, when only the common first fields are compared */
osku's avatar
osku committed
432 433 434
UNIV_INLINE
int
page_cmp_dtuple_rec_with_match(
435
/*===========================*/
436 437
	const dtuple_t*	dtuple,	/*!< in: data tuple */
	const rec_t*	rec,	/*!< in: physical record on a page; may also
438 439
				be page infimum or supremum, in which case
				matched-parameter values below are not
osku's avatar
osku committed
440
				affected */
441 442
	const ulint*	offsets,/*!< in: array returned by rec_get_offsets() */
	ulint*		matched_fields, /*!< in/out: number of already completely
osku's avatar
osku committed
443 444
				matched fields; when function returns
				contains the value for current comparison */
445
	ulint*		matched_bytes) /*!< in/out: number of already matched
osku's avatar
osku committed
446 447 448 449 450 451 452 453 454 455
				bytes within the first field not completely
				matched; when function returns contains the
				value for current comparison */
{
	ulint	rec_offset;

	ut_ad(dtuple_check_typed(dtuple));
	ut_ad(rec_offs_validate(rec, NULL, offsets));
	ut_ad(!rec_offs_comp(offsets) == !page_rec_is_comp(rec));

456
	rec_offset = page_offset(rec);
osku's avatar
osku committed
457 458

	if (UNIV_UNLIKELY(rec_offset == PAGE_NEW_INFIMUM)
459
	    || UNIV_UNLIKELY(rec_offset == PAGE_OLD_INFIMUM)) {
osku's avatar
osku committed
460 461 462
		return(1);
	}
	if (UNIV_UNLIKELY(rec_offset == PAGE_NEW_SUPREMUM)
463
	    || UNIV_UNLIKELY(rec_offset == PAGE_OLD_SUPREMUM)) {
osku's avatar
osku committed
464 465 466 467
		return(-1);
	}

	return(cmp_dtuple_rec_with_match(dtuple, rec, offsets,
468 469
					 matched_fields,
					 matched_bytes));
osku's avatar
osku committed
470
}
471
#endif /* !UNIV_HOTBACKUP */
osku's avatar
osku committed
472

473
/*************************************************************//**
474 475
Gets the page number.
@return	page number */
476 477 478 479
UNIV_INLINE
ulint
page_get_page_no(
/*=============*/
480
	const page_t*	page)	/*!< in: page */
481 482 483 484 485
{
	ut_ad(page == page_align((page_t*) page));
	return(mach_read_from_4(page + FIL_PAGE_OFFSET));
}

486
/*************************************************************//**
487 488
Gets the tablespace identifier.
@return	space id */
489 490 491 492
UNIV_INLINE
ulint
page_get_space_id(
/*==============*/
493
	const page_t*	page)	/*!< in: page */
494 495 496 497 498
{
	ut_ad(page == page_align((page_t*) page));
	return(mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID));
}

499
/*************************************************************//**
osku's avatar
osku committed
500
Gets the number of user records on page (infimum and supremum records
501 502
are not user records).
@return	number of user records */
osku's avatar
osku committed
503 504 505 506
UNIV_INLINE
ulint
page_get_n_recs(
/*============*/
507
	const page_t*	page)	/*!< in: index page */
osku's avatar
osku committed
508 509 510 511
{
	return(page_header_get_field(page, PAGE_N_RECS));
}

512
/*************************************************************//**
513 514
Gets the number of dir slots in directory.
@return	number of slots */
osku's avatar
osku committed
515 516 517 518
UNIV_INLINE
ulint
page_dir_get_n_slots(
/*=================*/
519
	const page_t*	page)	/*!< in: index page */
osku's avatar
osku committed
520 521 522
{
	return(page_header_get_field(page, PAGE_N_DIR_SLOTS));
}
523
/*************************************************************//**
osku's avatar
osku committed
524 525 526 527 528
Sets the number of dir slots in directory. */
UNIV_INLINE
void
page_dir_set_n_slots(
/*=================*/
529 530
	page_t*		page,	/*!< in/out: page */
	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
marko's avatar
marko committed
531
				uncompressed part will be updated, or NULL */
532
	ulint		n_slots)/*!< in: number of slots */
osku's avatar
osku committed
533
{
marko's avatar
marko committed
534
	page_header_set_field(page, page_zip, PAGE_N_DIR_SLOTS, n_slots);
osku's avatar
osku committed
535 536
}

537
/*************************************************************//**
538 539
Gets the number of records in the heap.
@return	number of user records */
osku's avatar
osku committed
540 541 542 543
UNIV_INLINE
ulint
page_dir_get_n_heap(
/*================*/
544
	const page_t*	page)	/*!< in: index page */
osku's avatar
osku committed
545 546 547 548
{
	return(page_header_get_field(page, PAGE_N_HEAP) & 0x7fff);
}

549
/*************************************************************//**
osku's avatar
osku committed
550 551 552 553 554
Sets the number of records in the heap. */
UNIV_INLINE
void
page_dir_set_n_heap(
/*================*/
555 556
	page_t*		page,	/*!< in/out: index page */
	page_zip_des_t*	page_zip,/*!< in/out: compressed page whose
557 558 559 560
				uncompressed part will be updated, or NULL.
				Note that the size of the dense page directory
				in the compressed page trailer is
				n_heap * PAGE_ZIP_DIR_SLOT_SIZE. */
561
	ulint		n_heap)	/*!< in: number of records */
osku's avatar
osku committed
562 563
{
	ut_ad(n_heap < 0x8000);
564
	ut_ad(!page_zip || n_heap
565
	      == (page_header_get_field(page, PAGE_N_HEAP) & 0x7fff) + 1);
osku's avatar
osku committed
566

567
	page_header_set_field(page, page_zip, PAGE_N_HEAP, n_heap
568 569
			      | (0x8000
				 & page_header_get_field(page, PAGE_N_HEAP)));
osku's avatar
osku committed
570 571
}

572
#ifdef UNIV_DEBUG
573
/*************************************************************//**
574 575
Gets pointer to nth directory slot.
@return	pointer to dir slot */
osku's avatar
osku committed
576 577 578 579
UNIV_INLINE
page_dir_slot_t*
page_dir_get_nth_slot(
/*==================*/
580 581
	const page_t*	page,	/*!< in: index page */
	ulint		n)	/*!< in: position */
osku's avatar
osku committed
582 583 584
{
	ut_ad(page_dir_get_n_slots(page) > n);

585 586
	return((page_dir_slot_t*)
	       page + UNIV_PAGE_SIZE - PAGE_DIR
587
	       - (n + 1) * PAGE_DIR_SLOT_SIZE);
588
}
589
#endif /* UNIV_DEBUG */
osku's avatar
osku committed
590

591
/**************************************************************//**
592 593
Used to check the consistency of a record on a page.
@return	TRUE if succeed */
osku's avatar
osku committed
594 595 596 597
UNIV_INLINE
ibool
page_rec_check(
/*===========*/
598
	const rec_t*	rec)	/*!< in: record */
osku's avatar
osku committed
599
{
600
	const page_t*	page = page_align(rec);
osku's avatar
osku committed
601

602
	ut_a(rec);
osku's avatar
osku committed
603

marko's avatar
marko committed
604 605
	ut_a(page_offset(rec) <= page_header_get_field(page, PAGE_HEAP_TOP));
	ut_a(page_offset(rec) >= PAGE_DATA);
osku's avatar
osku committed
606 607 608 609

	return(TRUE);
}

610
/***************************************************************//**
611 612
Gets the record pointed to by a directory slot.
@return	pointer to record */
osku's avatar
osku committed
613
UNIV_INLINE
614
const rec_t*
osku's avatar
osku committed
615 616
page_dir_slot_get_rec(
/*==================*/
617
	const page_dir_slot_t*	slot)	/*!< in: directory slot */
osku's avatar
osku committed
618
{
619
	return(page_align(slot) + mach_read_from_2(slot));
osku's avatar
osku committed
620 621
}

622
/***************************************************************//**
osku's avatar
osku committed
623 624 625 626 627
This is used to set the record offset in a directory slot. */
UNIV_INLINE
void
page_dir_slot_set_rec(
/*==================*/
628 629
	page_dir_slot_t* slot,	/*!< in: directory slot */
	rec_t*		 rec)	/*!< in: record on the page */
osku's avatar
osku committed
630 631 632
{
	ut_ad(page_rec_check(rec));

633
	mach_write_to_2(slot, page_offset(rec));
osku's avatar
osku committed
634 635
}

636
/***************************************************************//**
637 638
Gets the number of records owned by a directory slot.
@return	number of records */
osku's avatar
osku committed
639 640 641 642
UNIV_INLINE
ulint
page_dir_slot_get_n_owned(
/*======================*/
643
	const page_dir_slot_t*	slot)	/*!< in: page directory slot */
osku's avatar
osku committed
644
{
645
	const rec_t*	rec	= page_dir_slot_get_rec(slot);
marko's avatar
marko committed
646 647 648 649 650
	if (page_rec_is_comp(slot)) {
		return(rec_get_n_owned_new(rec));
	} else {
		return(rec_get_n_owned_old(rec));
	}
osku's avatar
osku committed
651 652
}

653
/***************************************************************//**
osku's avatar
osku committed
654 655 656 657 658
This is used to set the owned records field of a directory slot. */
UNIV_INLINE
void
page_dir_slot_set_n_owned(
/*======================*/
659 660 661
	page_dir_slot_t*slot,	/*!< in/out: directory slot */
	page_zip_des_t*	page_zip,/*!< in/out: compressed page, or NULL */
	ulint		n)	/*!< in: number of records owned by the slot */
osku's avatar
osku committed
662
{
663
	rec_t*	rec	= (rec_t*) page_dir_slot_get_rec(slot);
marko's avatar
marko committed
664 665 666 667 668 669
	if (page_rec_is_comp(slot)) {
		rec_set_n_owned_new(rec, page_zip, n);
	} else {
		ut_ad(!page_zip);
		rec_set_n_owned_old(rec, n);
	}
osku's avatar
osku committed
670 671
}

672
/************************************************************//**
osku's avatar
osku committed
673 674 675 676 677 678 679
Calculates the space reserved for directory slots of a given number of
records. The exact value is a fraction number n * PAGE_DIR_SLOT_SIZE /
PAGE_DIR_SLOT_MIN_N_OWNED, and it is rounded upwards to an integer. */
UNIV_INLINE
ulint
page_dir_calc_reserved_space(
/*=========================*/
680
	ulint	n_recs)		/*!< in: number of records */
osku's avatar
osku committed
681 682
{
	return((PAGE_DIR_SLOT_SIZE * n_recs + PAGE_DIR_SLOT_MIN_N_OWNED - 1)
683
	       / PAGE_DIR_SLOT_MIN_N_OWNED);
684
}
osku's avatar
osku committed
685

686
/************************************************************//**
687 688
Gets the pointer to the next record on the page.
@return	pointer to next record */
osku's avatar
osku committed
689
UNIV_INLINE
690
const rec_t*
marko's avatar
marko committed
691 692
page_rec_get_next_low(
/*==================*/
693 694
	const rec_t*	rec,	/*!< in: pointer to record */
	ulint		comp)	/*!< in: nonzero=compact page layout */
osku's avatar
osku committed
695
{
696 697
	ulint		offs;
	const page_t*	page;
osku's avatar
osku committed
698

699
	ut_ad(page_rec_check(rec));
osku's avatar
osku committed
700

701
	page = page_align(rec);
osku's avatar
osku committed
702

marko's avatar
marko committed
703
	offs = rec_get_next_offs(rec, comp);
osku's avatar
osku committed
704 705 706

	if (UNIV_UNLIKELY(offs >= UNIV_PAGE_SIZE)) {
		fprintf(stderr,
707 708
			"InnoDB: Next record offset is nonsensical %lu"
			" in record at offset %lu\n"
709 710 711
			"InnoDB: rec address %p, space id %lu, page %lu\n",
			(ulong)offs, (ulong) page_offset(rec),
			(void*) rec,
712 713
			(ulong) page_get_space_id(page),
			(ulong) page_get_page_no(page));
714
		buf_page_print(page, 0);
osku's avatar
osku committed
715 716 717 718 719

		ut_error;
	}

	if (UNIV_UNLIKELY(offs == 0)) {
720

osku's avatar
osku committed
721 722 723 724 725 726
		return(NULL);
	}

	return(page + offs);
}

727
/************************************************************//**
728 729
Gets the pointer to the next record on the page.
@return	pointer to next record */
marko's avatar
marko committed
730 731 732 733
UNIV_INLINE
rec_t*
page_rec_get_next(
/*==============*/
734
	rec_t*	rec)	/*!< in: pointer to record */
735 736 737 738
{
	return((rec_t*) page_rec_get_next_low(rec, page_rec_is_comp(rec)));
}

739
/************************************************************//**
740 741
Gets the pointer to the next record on the page.
@return	pointer to next record */
742 743 744 745
UNIV_INLINE
const rec_t*
page_rec_get_next_const(
/*====================*/
746
	const rec_t*	rec)	/*!< in: pointer to record */
marko's avatar
marko committed
747 748 749
{
	return(page_rec_get_next_low(rec, page_rec_is_comp(rec)));
}
750

751
/************************************************************//**
752
Sets the pointer to the next record on the page. */
osku's avatar
osku committed
753 754 755 756
UNIV_INLINE
void
page_rec_set_next(
/*==============*/
757
	rec_t*	rec,		/*!< in: pointer to record,
758
				must not be page supremum */
759
	rec_t*	next)		/*!< in: pointer to next record,
760
				must not be page infimum */
osku's avatar
osku committed
761 762 763
{
	ulint	offs;

764
	ut_ad(page_rec_check(rec));
osku's avatar
osku committed
765
	ut_ad(!page_rec_is_supremum(rec));
766
	ut_ad(rec != next);
osku's avatar
osku committed
767

marko's avatar
marko committed
768
	ut_ad(!next || !page_rec_is_infimum(next));
769
	ut_ad(!next || page_align(rec) == page_align(next));
marko's avatar
marko committed
770

marko's avatar
marko committed
771
	if (UNIV_LIKELY(next != NULL)) {
772
		offs = page_offset(next);
marko's avatar
marko committed
773 774 775 776
	} else {
		offs = 0;
	}

marko's avatar
marko committed
777
	if (page_rec_is_comp(rec)) {
778
		rec_set_next_offs_new(rec, offs);
osku's avatar
osku committed
779
	} else {
marko's avatar
marko committed
780
		rec_set_next_offs_old(rec, offs);
osku's avatar
osku committed
781 782 783
	}
}

784
/************************************************************//**
785 786
Gets the pointer to the previous record.
@return	pointer to previous record */
osku's avatar
osku committed
787
UNIV_INLINE
788 789 790
const rec_t*
page_rec_get_prev_const(
/*====================*/
791
	const rec_t*	rec)	/*!< in: pointer to record, must not be page
792
				infimum */
osku's avatar
osku committed
793
{
794
	const page_dir_slot_t*	slot;
osku's avatar
osku committed
795
	ulint			slot_no;
796 797 798
	const rec_t*		rec2;
	const rec_t*		prev_rec = NULL;
	const page_t*		page;
osku's avatar
osku committed
799

800
	ut_ad(page_rec_check(rec));
osku's avatar
osku committed
801

802
	page = page_align(rec);
osku's avatar
osku committed
803 804 805 806 807 808

	ut_ad(!page_rec_is_infimum(rec));

	slot_no = page_dir_find_owner_slot(rec);

	ut_a(slot_no != 0);
809

osku's avatar
osku committed
810
	slot = page_dir_get_nth_slot(page, slot_no - 1);
811

osku's avatar
osku committed
812
	rec2 = page_dir_slot_get_rec(slot);
813

marko's avatar
marko committed
814 815 816 817 818 819 820 821 822 823
	if (page_is_comp(page)) {
		while (rec != rec2) {
			prev_rec = rec2;
			rec2 = page_rec_get_next_low(rec2, TRUE);
		}
	} else {
		while (rec != rec2) {
			prev_rec = rec2;
			rec2 = page_rec_get_next_low(rec2, FALSE);
		}
osku's avatar
osku committed
824
	}
825

osku's avatar
osku committed
826 827 828 829 830
	ut_a(prev_rec);

	return(prev_rec);
}

831
/************************************************************//**
832 833
Gets the pointer to the previous record.
@return	pointer to previous record */
834 835 836 837
UNIV_INLINE
rec_t*
page_rec_get_prev(
/*==============*/
838
	rec_t*	rec)	/*!< in: pointer to record, must not be page
839 840 841 842 843
			infimum */
{
	return((rec_t*) page_rec_get_prev_const(rec));
}

844
/***************************************************************//**
845 846
Looks for the record which owns the given record.
@return	the owner record */
osku's avatar
osku committed
847 848 849 850
UNIV_INLINE
rec_t*
page_rec_find_owner_rec(
/*====================*/
851
	rec_t*	rec)	/*!< in: the physical record */
osku's avatar
osku committed
852 853 854 855
{
	ut_ad(page_rec_check(rec));

	if (page_rec_is_comp(rec)) {
marko's avatar
marko committed
856
		while (rec_get_n_owned_new(rec) == 0) {
osku's avatar
osku committed
857 858 859
			rec = page_rec_get_next(rec);
		}
	} else {
marko's avatar
marko committed
860
		while (rec_get_n_owned_old(rec) == 0) {
osku's avatar
osku committed
861 862 863 864 865 866 867
			rec = page_rec_get_next(rec);
		}
	}

	return(rec);
}

868
/**********************************************************//**
marko's avatar
marko committed
869
Returns the base extra size of a physical record.  This is the
870 871
size of the fixed header, independent of the record size.
@return	REC_N_NEW_EXTRA_BYTES or REC_N_OLD_EXTRA_BYTES */
marko's avatar
marko committed
872 873 874 875
UNIV_INLINE
ulint
page_rec_get_base_extra_size(
/*=========================*/
876
	const rec_t*	rec)	/*!< in: physical record */
marko's avatar
marko committed
877 878 879 880 881 882 883
{
#if REC_N_NEW_EXTRA_BYTES + 1 != REC_N_OLD_EXTRA_BYTES
# error "REC_N_NEW_EXTRA_BYTES + 1 != REC_N_OLD_EXTRA_BYTES"
#endif
	return(REC_N_NEW_EXTRA_BYTES + (ulint) !page_rec_is_comp(rec));
}

884
/************************************************************//**
osku's avatar
osku committed
885
Returns the sum of the sizes of the records in the record list, excluding
886 887
the infimum and supremum records.
@return	data in bytes */
osku's avatar
osku committed
888 889 890 891
UNIV_INLINE
ulint
page_get_data_size(
/*===============*/
892
	const page_t*	page)	/*!< in: index page */
osku's avatar
osku committed
893 894 895 896
{
	ulint	ret;

	ret = (ulint)(page_header_get_field(page, PAGE_HEAP_TOP)
897 898 899 900
		      - (page_is_comp(page)
			 ? PAGE_NEW_SUPREMUM_END
			 : PAGE_OLD_SUPREMUM_END)
		      - page_header_get_field(page, PAGE_GARBAGE));
osku's avatar
osku committed
901 902 903 904 905 906

	ut_ad(ret < UNIV_PAGE_SIZE);

	return(ret);
}

907

908
/************************************************************//**
909
Allocates a block of memory from the free list of an index page. */
910
UNIV_INTERN
911 912 913
void
page_mem_alloc_free(
/*================*/
914 915
	page_t*		page,	/*!< in/out: index page */
	page_zip_des_t*	page_zip,/*!< in/out: compressed page with enough
916 917
				space available for inserting the record,
				or NULL */
918
	rec_t*		next_rec,/*!< in: pointer to the new head of the
919
				free record list */
920
	ulint		need)	/*!< in: number of bytes allocated */
921 922 923 924
{
	ulint		garbage;

#ifdef UNIV_DEBUG
925
	const rec_t*	old_rec	= page_header_get_ptr(page, PAGE_FREE);
926 927
	ulint		next_offs;

928
	ut_ad(old_rec);
929 930
	next_offs = rec_get_next_offs(old_rec, page_is_comp(page));
	ut_ad(next_rec == (next_offs ? page + next_offs : NULL));
931 932 933 934 935 936 937 938 939 940
#endif

	page_header_set_ptr(page, page_zip, PAGE_FREE, next_rec);

	garbage = page_header_get_field(page, PAGE_GARBAGE);
	ut_ad(garbage >= need);

	page_header_set_field(page, page_zip, PAGE_GARBAGE, garbage - need);
}

941
/*************************************************************//**
942 943
Calculates free space if a page is emptied.
@return	free space */
osku's avatar
osku committed
944 945 946 947
UNIV_INLINE
ulint
page_get_free_space_of_empty(
/*=========================*/
948
	ulint	comp)		/*!< in: nonzero=compact page layout */
osku's avatar
osku committed
949 950 951
{
	if (UNIV_LIKELY(comp)) {
		return((ulint)(UNIV_PAGE_SIZE
952 953 954
			       - PAGE_NEW_SUPREMUM_END
			       - PAGE_DIR
			       - 2 * PAGE_DIR_SLOT_SIZE));
osku's avatar
osku committed
955 956 957
	}

	return((ulint)(UNIV_PAGE_SIZE
958 959 960
		       - PAGE_OLD_SUPREMUM_END
		       - PAGE_DIR
		       - 2 * PAGE_DIR_SLOT_SIZE));
osku's avatar
osku committed
961 962
}

963
/************************************************************//**
osku's avatar
osku committed
964 965 966 967 968
Each user record on a page, and also the deleted user records in the heap
takes its size plus the fraction of the dir cell size /
PAGE_DIR_SLOT_MIN_N_OWNED bytes for it. If the sum of these exceeds the
value of page_get_free_space_of_empty, the insert is impossible, otherwise
it is allowed. This function returns the maximum combined size of records
969 970
which can be inserted on top of the record heap.
@return	maximum combined size for inserted records */
osku's avatar
osku committed
971 972 973 974
UNIV_INLINE
ulint
page_get_max_insert_size(
/*=====================*/
975 976
	const page_t*	page,	/*!< in: index page */
	ulint		n_recs)	/*!< in: number of records */
osku's avatar
osku committed
977 978 979 980 981 982
{
	ulint	occupied;
	ulint	free_space;

	if (page_is_comp(page)) {
		occupied = page_header_get_field(page, PAGE_HEAP_TOP)
983
			- PAGE_NEW_SUPREMUM_END
984 985
			+ page_dir_calc_reserved_space(
				n_recs + page_dir_get_n_heap(page) - 2);
osku's avatar
osku committed
986 987 988 989

		free_space = page_get_free_space_of_empty(TRUE);
	} else {
		occupied = page_header_get_field(page, PAGE_HEAP_TOP)
990
			- PAGE_OLD_SUPREMUM_END
991 992
			+ page_dir_calc_reserved_space(
				n_recs + page_dir_get_n_heap(page) - 2);
osku's avatar
osku committed
993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008

		free_space = page_get_free_space_of_empty(FALSE);
	}

	/* Above the 'n_recs +' part reserves directory space for the new
	inserted records; the '- 2' excludes page infimum and supremum
	records */

	if (occupied > free_space) {

		return(0);
	}

	return(free_space - occupied);
}

1009
/************************************************************//**
osku's avatar
osku committed
1010
Returns the maximum combined size of records which can be inserted on top
1011 1012
of the record heap if a page is first reorganized.
@return	maximum combined size for inserted records */
osku's avatar
osku committed
1013 1014 1015 1016
UNIV_INLINE
ulint
page_get_max_insert_size_after_reorganize(
/*======================================*/
1017 1018
	const page_t*	page,	/*!< in: index page */
	ulint		n_recs)	/*!< in: number of records */
osku's avatar
osku committed
1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
{
	ulint	occupied;
	ulint	free_space;

	occupied = page_get_data_size(page)
		+ page_dir_calc_reserved_space(n_recs + page_get_n_recs(page));

	free_space = page_get_free_space_of_empty(page_is_comp(page));

	if (occupied > free_space) {

		return(0);
	}

	return(free_space - occupied);
}

1036
/************************************************************//**
osku's avatar
osku committed
1037 1038 1039 1040 1041
Puts a record to free list. */
UNIV_INLINE
void
page_mem_free(
/*==========*/
1042 1043 1044 1045 1046
	page_t*		page,	/*!< in/out: index page */
	page_zip_des_t*	page_zip,/*!< in/out: compressed page, or NULL */
	rec_t*		rec,	/*!< in: pointer to the (origin of) record */
	dict_index_t*	index,	/*!< in: index of rec */
	const ulint*	offsets)/*!< in: array returned by rec_get_offsets() */
osku's avatar
osku committed
1047 1048 1049 1050
{
	rec_t*		free;
	ulint		garbage;

1051
	ut_ad(rec_offs_validate(rec, index, offsets));
osku's avatar
osku committed
1052 1053
	free = page_header_get_ptr(page, PAGE_FREE);

1054
	page_rec_set_next(rec, free);
marko's avatar
marko committed
1055
	page_header_set_ptr(page, page_zip, PAGE_FREE, rec);
osku's avatar
osku committed
1056 1057 1058

	garbage = page_header_get_field(page, PAGE_GARBAGE);

marko's avatar
marko committed
1059
	page_header_set_field(page, page_zip, PAGE_GARBAGE,
1060
			      garbage + rec_offs_size(offsets));
1061 1062 1063 1064 1065

	if (UNIV_LIKELY_NULL(page_zip)) {
		page_zip_dir_delete(page_zip, rec, index, offsets, free);
	} else {
		page_header_set_field(page, page_zip, PAGE_N_RECS,
1066
				      page_get_n_recs(page) - 1);
1067
	}
osku's avatar
osku committed
1068 1069 1070 1071 1072 1073
}

#ifdef UNIV_MATERIALIZE
#undef UNIV_INLINE
#define UNIV_INLINE	UNIV_INLINE_ORIGINAL
#endif