1/***************************************************************************** 2 3Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved. 4Copyright (c) 2017, 2020, MariaDB Corporation. 5 6This program is free software; you can redistribute it and/or modify it under 7the terms of the GNU General Public License as published by the Free Software 8Foundation; version 2 of the License. 9 10This program is distributed in the hope that it will be useful, but WITHOUT 11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 14You should have received a copy of the GNU General Public License along with 15this program; if not, write to the Free Software Foundation, Inc., 1651 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA 17 18*****************************************************************************/ 19 20/********************************************************************//** 21@file include/rem0rec.ic 22Record manager 23 24Created 5/30/1994 Heikki Tuuri 25*************************************************************************/ 26 27#include "mach0data.h" 28#include "ut0byte.h" 29#include "dict0boot.h" 30#include "btr0types.h" 31 32/* Offsets of the bit-fields in an old-style record. NOTE! In the table the 33most significant bytes and bits are written below less significant. 34 35 (1) byte offset (2) bit usage within byte 36 downward from 37 origin -> 1 8 bits pointer to next record 38 2 8 bits pointer to next record 39 3 1 bit short flag 40 7 bits number of fields 41 4 3 bits number of fields 42 5 bits heap number 43 5 8 bits heap number 44 6 4 bits n_owned 45 4 bits info bits 46*/ 47 48/* Offsets of the bit-fields in a new-style record. NOTE! In the table the 49most significant bytes and bits are written below less significant. 50 51 (1) byte offset (2) bit usage within byte 52 downward from 53 origin -> 1 8 bits relative offset of next record 54 2 8 bits relative offset of next record 55 the relative offset is an unsigned 16-bit 56 integer: 57 (offset_of_next_record 58 - offset_of_this_record) mod 64Ki, 59 where mod is the modulo as a non-negative 60 number; 61 we can calculate the offset of the next 62 record with the formula: 63 relative_offset + offset_of_this_record 64 mod srv_page_size 65 3 3 bits status: 66 000=REC_STATUS_ORDINARY 67 001=REC_STATUS_NODE_PTR 68 010=REC_STATUS_INFIMUM 69 011=REC_STATUS_SUPREMUM 70 100=REC_STATUS_INSTANT 71 1xx=reserved 72 5 bits heap number 73 4 8 bits heap number 74 5 4 bits n_owned 75 4 bits info bits 76*/ 77 78/* We list the byte offsets from the origin of the record, the mask, 79and the shift needed to obtain each bit-field of the record. */ 80 81#define REC_NEXT 2 82#define REC_NEXT_MASK 0xFFFFUL 83#define REC_NEXT_SHIFT 0 84 85#define REC_OLD_SHORT 3 /* This is single byte bit-field */ 86#define REC_OLD_SHORT_MASK 0x1UL 87#define REC_OLD_SHORT_SHIFT 0 88 89#define REC_OLD_N_FIELDS 4 90#define REC_OLD_N_FIELDS_MASK 0x7FEUL 91#define REC_OLD_N_FIELDS_SHIFT 1 92 93#define REC_OLD_HEAP_NO 5 94#define REC_HEAP_NO_MASK 0xFFF8UL 95#if 0 /* defined in rem0rec.h for use of page0zip.cc */ 96#define REC_NEW_HEAP_NO 4 97#define REC_HEAP_NO_SHIFT 3 98#endif 99 100#define REC_OLD_N_OWNED 6 /* This is single byte bit-field */ 101#define REC_NEW_N_OWNED 5 /* This is single byte bit-field */ 102#define REC_N_OWNED_MASK 0xFUL 103#define REC_N_OWNED_SHIFT 0 104 105#define REC_OLD_INFO_BITS 6 /* This is single byte bit-field */ 106#define REC_NEW_INFO_BITS 5 /* This is single byte bit-field */ 107#define REC_INFO_BITS_MASK 0xF0UL 108#define REC_INFO_BITS_SHIFT 0 109 110#if REC_OLD_SHORT_MASK << (8 * (REC_OLD_SHORT - 3)) \ 111 ^ REC_OLD_N_FIELDS_MASK << (8 * (REC_OLD_N_FIELDS - 4)) \ 112 ^ REC_HEAP_NO_MASK << (8 * (REC_OLD_HEAP_NO - 4)) \ 113 ^ REC_N_OWNED_MASK << (8 * (REC_OLD_N_OWNED - 3)) \ 114 ^ REC_INFO_BITS_MASK << (8 * (REC_OLD_INFO_BITS - 3)) \ 115 ^ 0xFFFFFFFFUL 116# error "sum of old-style masks != 0xFFFFFFFFUL" 117#endif 118#if REC_NEW_STATUS_MASK << (8 * (REC_NEW_STATUS - 3)) \ 119 ^ REC_HEAP_NO_MASK << (8 * (REC_NEW_HEAP_NO - 4)) \ 120 ^ REC_N_OWNED_MASK << (8 * (REC_NEW_N_OWNED - 3)) \ 121 ^ REC_INFO_BITS_MASK << (8 * (REC_NEW_INFO_BITS - 3)) \ 122 ^ 0xFFFFFFUL 123# error "sum of new-style masks != 0xFFFFFFUL" 124#endif 125 126/******************************************************//** 127Gets a bit field from within 1 byte. */ 128UNIV_INLINE 129byte 130rec_get_bit_field_1( 131/*================*/ 132 const rec_t* rec, /*!< in: pointer to record origin */ 133 ulint offs, /*!< in: offset from the origin down */ 134 ulint mask, /*!< in: mask used to filter bits */ 135 ulint shift) /*!< in: shift right applied after masking */ 136{ 137 return static_cast<byte>((*(rec - offs) & mask) >> shift); 138} 139 140/******************************************************//** 141Sets a bit field within 1 byte. */ 142UNIV_INLINE 143void 144rec_set_bit_field_1( 145/*================*/ 146 rec_t* rec, /*!< in: pointer to record origin */ 147 ulint val, /*!< in: value to set */ 148 ulint offs, /*!< in: offset from the origin down */ 149 ulint mask, /*!< in: mask used to filter bits */ 150 ulint shift) /*!< in: shift right applied after masking */ 151{ 152 ut_ad(rec); 153 ut_ad(offs <= REC_N_OLD_EXTRA_BYTES); 154 ut_ad(mask); 155 ut_ad(mask <= 0xFFUL); 156 ut_ad(((mask >> shift) << shift) == mask); 157 ut_ad(((val << shift) & mask) == (val << shift)); 158 159 mach_write_to_1(rec - offs, 160 (mach_read_from_1(rec - offs) & ~mask) 161 | (val << shift)); 162} 163 164/******************************************************//** 165Gets a bit field from within 2 bytes. */ 166UNIV_INLINE 167ulint 168rec_get_bit_field_2( 169/*================*/ 170 const rec_t* rec, /*!< in: pointer to record origin */ 171 ulint offs, /*!< in: offset from the origin down */ 172 ulint mask, /*!< in: mask used to filter bits */ 173 ulint shift) /*!< in: shift right applied after masking */ 174{ 175 ut_ad(rec); 176 177 return((mach_read_from_2(rec - offs) & mask) >> shift); 178} 179 180/******************************************************//** 181Sets a bit field within 2 bytes. */ 182UNIV_INLINE 183void 184rec_set_bit_field_2( 185/*================*/ 186 rec_t* rec, /*!< in: pointer to record origin */ 187 ulint val, /*!< in: value to set */ 188 ulint offs, /*!< in: offset from the origin down */ 189 ulint mask, /*!< in: mask used to filter bits */ 190 ulint shift) /*!< in: shift right applied after masking */ 191{ 192 ut_ad(rec); 193 ut_ad(offs <= REC_N_OLD_EXTRA_BYTES); 194 ut_ad(mask > 0xFFUL); 195 ut_ad(mask <= 0xFFFFUL); 196 ut_ad((mask >> shift) & 1); 197 ut_ad(0 == ((mask >> shift) & ((mask >> shift) + 1))); 198 ut_ad(((mask >> shift) << shift) == mask); 199 ut_ad(((val << shift) & mask) == (val << shift)); 200 201 mach_write_to_2(rec - offs, 202 (mach_read_from_2(rec - offs) & ~mask) 203 | (val << shift)); 204} 205 206/******************************************************//** 207The following function is used to get the pointer of the next chained record 208on the same page. 209@return pointer to the next chained record, or NULL if none */ 210UNIV_INLINE 211const rec_t* 212rec_get_next_ptr_const( 213/*===================*/ 214 const rec_t* rec, /*!< in: physical record */ 215 ulint comp) /*!< in: nonzero=compact page format */ 216{ 217 ulint field_value; 218 219 compile_time_assert(REC_NEXT_MASK == 0xFFFFUL); 220 compile_time_assert(REC_NEXT_SHIFT == 0); 221 222 field_value = mach_read_from_2(rec - REC_NEXT); 223 224 if (field_value == 0) { 225 226 return(NULL); 227 } 228 229 if (comp) { 230#if UNIV_PAGE_SIZE_MAX <= 32768 231 /* Note that for 64 KiB pages, field_value can 'wrap around' 232 and the debug assertion is not valid */ 233 234 /* In the following assertion, field_value is interpreted 235 as signed 16-bit integer in 2's complement arithmetics. 236 If all platforms defined int16_t in the standard headers, 237 the expression could be written simpler as 238 (int16_t) field_value + ut_align_offset(...) < srv_page_size 239 */ 240 ut_ad((field_value >= 32768 241 ? field_value - 65536 242 : field_value) 243 + ut_align_offset(rec, srv_page_size) 244 < srv_page_size); 245#endif 246 /* There must be at least REC_N_NEW_EXTRA_BYTES + 1 247 between each record. */ 248 ut_ad((field_value > REC_N_NEW_EXTRA_BYTES 249 && field_value < 32768) 250 || field_value < (uint16) -REC_N_NEW_EXTRA_BYTES); 251 252 return((byte*) ut_align_down(rec, srv_page_size) 253 + ut_align_offset(rec + field_value, srv_page_size)); 254 } else { 255 ut_ad(field_value < srv_page_size); 256 257 return((byte*) ut_align_down(rec, srv_page_size) 258 + field_value); 259 } 260} 261 262/******************************************************//** 263The following function is used to get the pointer of the next chained record 264on the same page. 265@return pointer to the next chained record, or NULL if none */ 266UNIV_INLINE 267rec_t* 268rec_get_next_ptr( 269/*=============*/ 270 rec_t* rec, /*!< in: physical record */ 271 ulint comp) /*!< in: nonzero=compact page format */ 272{ 273 return(const_cast<rec_t*>(rec_get_next_ptr_const(rec, comp))); 274} 275 276/******************************************************//** 277The following function is used to get the offset of the next chained record 278on the same page. 279@return the page offset of the next chained record, or 0 if none */ 280UNIV_INLINE 281ulint 282rec_get_next_offs( 283/*==============*/ 284 const rec_t* rec, /*!< in: physical record */ 285 ulint comp) /*!< in: nonzero=compact page format */ 286{ 287 ulint field_value; 288 compile_time_assert(REC_NEXT_MASK == 0xFFFFUL); 289 compile_time_assert(REC_NEXT_SHIFT == 0); 290 291 field_value = mach_read_from_2(rec - REC_NEXT); 292 293 if (comp) { 294#if UNIV_PAGE_SIZE_MAX <= 32768 295 /* Note that for 64 KiB pages, field_value can 'wrap around' 296 and the debug assertion is not valid */ 297 298 /* In the following assertion, field_value is interpreted 299 as signed 16-bit integer in 2's complement arithmetics. 300 If all platforms defined int16_t in the standard headers, 301 the expression could be written simpler as 302 (int16_t) field_value + ut_align_offset(...) < srv_page_size 303 */ 304 ut_ad((field_value >= 32768 305 ? field_value - 65536 306 : field_value) 307 + ut_align_offset(rec, srv_page_size) 308 < srv_page_size); 309#endif 310 if (field_value == 0) { 311 312 return(0); 313 } 314 315 /* There must be at least REC_N_NEW_EXTRA_BYTES + 1 316 between each record. */ 317 ut_ad((field_value > REC_N_NEW_EXTRA_BYTES 318 && field_value < 32768) 319 || field_value < (uint16) -REC_N_NEW_EXTRA_BYTES); 320 321 return(ut_align_offset(rec + field_value, srv_page_size)); 322 } else { 323 ut_ad(field_value < srv_page_size); 324 325 return(field_value); 326 } 327} 328 329/******************************************************//** 330The following function is used to set the next record offset field 331of an old-style record. */ 332UNIV_INLINE 333void 334rec_set_next_offs_old( 335/*==================*/ 336 rec_t* rec, /*!< in: old-style physical record */ 337 ulint next) /*!< in: offset of the next record */ 338{ 339 ut_ad(srv_page_size > next); 340 compile_time_assert(REC_NEXT_MASK == 0xFFFFUL); 341 compile_time_assert(REC_NEXT_SHIFT == 0); 342 mach_write_to_2(rec - REC_NEXT, next); 343} 344 345/******************************************************//** 346The following function is used to set the next record offset field 347of a new-style record. */ 348UNIV_INLINE 349void 350rec_set_next_offs_new( 351/*==================*/ 352 rec_t* rec, /*!< in/out: new-style physical record */ 353 ulint next) /*!< in: offset of the next record */ 354{ 355 ulint field_value; 356 357 ut_ad(srv_page_size > next); 358 359 if (!next) { 360 field_value = 0; 361 } else { 362 /* The following two statements calculate 363 next - offset_of_rec mod 64Ki, where mod is the modulo 364 as a non-negative number */ 365 366 field_value = (ulint) 367 ((lint) next 368 - (lint) ut_align_offset(rec, srv_page_size)); 369 field_value &= REC_NEXT_MASK; 370 } 371 372 mach_write_to_2(rec - REC_NEXT, field_value); 373} 374 375/******************************************************//** 376The following function is used to get the number of fields 377in an old-style record. 378@return number of data fields */ 379UNIV_INLINE 380ulint 381rec_get_n_fields_old( 382/*=================*/ 383 const rec_t* rec) /*!< in: physical record */ 384{ 385 ulint ret; 386 387 ut_ad(rec); 388 389 ret = rec_get_bit_field_2(rec, REC_OLD_N_FIELDS, 390 REC_OLD_N_FIELDS_MASK, 391 REC_OLD_N_FIELDS_SHIFT); 392 ut_ad(ret <= REC_MAX_N_FIELDS); 393 ut_ad(ret > 0); 394 395 return(ret); 396} 397 398/******************************************************//** 399The following function is used to set the number of fields 400in an old-style record. */ 401UNIV_INLINE 402void 403rec_set_n_fields_old( 404/*=================*/ 405 rec_t* rec, /*!< in: physical record */ 406 ulint n_fields) /*!< in: the number of fields */ 407{ 408 ut_ad(rec); 409 ut_ad(n_fields <= REC_MAX_N_FIELDS); 410 ut_ad(n_fields > 0); 411 412 rec_set_bit_field_2(rec, n_fields, REC_OLD_N_FIELDS, 413 REC_OLD_N_FIELDS_MASK, REC_OLD_N_FIELDS_SHIFT); 414} 415 416/******************************************************//** 417The following function is used to get the number of fields 418in a record. 419@return number of data fields */ 420UNIV_INLINE 421ulint 422rec_get_n_fields( 423/*=============*/ 424 const rec_t* rec, /*!< in: physical record */ 425 const dict_index_t* index) /*!< in: record descriptor */ 426{ 427 ut_ad(rec); 428 ut_ad(index); 429 430 if (!dict_table_is_comp(index->table)) { 431 return(rec_get_n_fields_old(rec)); 432 } 433 434 switch (rec_get_status(rec)) { 435 case REC_STATUS_INSTANT: 436 case REC_STATUS_ORDINARY: 437 return(dict_index_get_n_fields(index)); 438 case REC_STATUS_NODE_PTR: 439 return(dict_index_get_n_unique_in_tree(index) + 1); 440 case REC_STATUS_INFIMUM: 441 case REC_STATUS_SUPREMUM: 442 return(1); 443 } 444 445 ut_error; 446 return(ULINT_UNDEFINED); 447} 448 449/** Confirms the n_fields of the entry is sane with comparing the other 450record in the same page specified 451@param[in] index index 452@param[in] rec record of the same page 453@param[in] entry index entry 454@return true if n_fields is sane */ 455UNIV_INLINE 456bool 457rec_n_fields_is_sane( 458 dict_index_t* index, 459 const rec_t* rec, 460 const dtuple_t* entry) 461{ 462 const ulint n_fields = rec_get_n_fields(rec, index); 463 464 return(n_fields == dtuple_get_n_fields(entry) 465 || (index->is_instant() 466 && n_fields >= index->n_core_fields) 467 /* a record for older SYS_INDEXES table 468 (missing merge_threshold column) is acceptable. */ 469 || (index->table->id == DICT_INDEXES_ID 470 && n_fields == dtuple_get_n_fields(entry) - 1)); 471} 472 473/******************************************************//** 474The following function is used to get the number of records owned by the 475previous directory record. 476@return number of owned records */ 477UNIV_INLINE 478ulint 479rec_get_n_owned_old( 480/*================*/ 481 const rec_t* rec) /*!< in: old-style physical record */ 482{ 483 return(rec_get_bit_field_1(rec, REC_OLD_N_OWNED, 484 REC_N_OWNED_MASK, REC_N_OWNED_SHIFT)); 485} 486 487/******************************************************//** 488The following function is used to get the number of records owned by the 489previous directory record. 490@return number of owned records */ 491UNIV_INLINE 492ulint 493rec_get_n_owned_new( 494/*================*/ 495 const rec_t* rec) /*!< in: new-style physical record */ 496{ 497 return(rec_get_bit_field_1(rec, REC_NEW_N_OWNED, 498 REC_N_OWNED_MASK, REC_N_OWNED_SHIFT)); 499} 500 501/******************************************************//** 502The following function is used to retrieve the info bits of a record. 503@return info bits */ 504UNIV_INLINE 505byte 506rec_get_info_bits( 507/*==============*/ 508 const rec_t* rec, /*!< in: physical record */ 509 ulint comp) /*!< in: nonzero=compact page format */ 510{ 511 return rec_get_bit_field_1( 512 rec, comp ? REC_NEW_INFO_BITS : REC_OLD_INFO_BITS, 513 REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT); 514} 515 516/******************************************************//** 517The following function is used to retrieve the info and status 518bits of a record. (Only compact records have status bits.) 519@return info and status bits */ 520UNIV_INLINE 521byte 522rec_get_info_and_status_bits( 523/*=========================*/ 524 const rec_t* rec, /*!< in: physical record */ 525 ulint comp) /*!< in: nonzero=compact page format */ 526{ 527 compile_time_assert(!((REC_NEW_STATUS_MASK >> REC_NEW_STATUS_SHIFT) 528 & (REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT))); 529 if (comp) 530 return static_cast<byte>(rec_get_info_bits(rec, TRUE) | 531 rec_get_status(rec)); 532 else 533 return rec_get_info_bits(rec, FALSE); 534} 535/******************************************************//** 536The following function is used to set the info and status 537bits of a record. (Only compact records have status bits.) */ 538UNIV_INLINE 539void 540rec_set_info_and_status_bits( 541/*=========================*/ 542 rec_t* rec, /*!< in/out: physical record */ 543 ulint bits) /*!< in: info bits */ 544{ 545 compile_time_assert(!((REC_NEW_STATUS_MASK >> REC_NEW_STATUS_SHIFT) 546 & (REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT))); 547 rec_set_status(rec, bits & REC_NEW_STATUS_MASK); 548 rec_set_bit_field_1(rec, bits & ~REC_NEW_STATUS_MASK, 549 REC_NEW_INFO_BITS, 550 REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT); 551} 552 553/******************************************************//** 554The following function tells if record is delete marked. 555@return nonzero if delete marked */ 556UNIV_INLINE 557ulint 558rec_get_deleted_flag( 559/*=================*/ 560 const rec_t* rec, /*!< in: physical record */ 561 ulint comp) /*!< in: nonzero=compact page format */ 562{ 563 if (comp) { 564 return(rec_get_bit_field_1(rec, REC_NEW_INFO_BITS, 565 REC_INFO_DELETED_FLAG, 566 REC_INFO_BITS_SHIFT)); 567 } else { 568 return(rec_get_bit_field_1(rec, REC_OLD_INFO_BITS, 569 REC_INFO_DELETED_FLAG, 570 REC_INFO_BITS_SHIFT)); 571 } 572} 573 574/******************************************************//** 575The following function tells if a new-style record is a node pointer. 576@return TRUE if node pointer */ 577UNIV_INLINE 578bool 579rec_get_node_ptr_flag( 580/*==================*/ 581 const rec_t* rec) /*!< in: physical record */ 582{ 583 return(REC_STATUS_NODE_PTR == rec_get_status(rec)); 584} 585 586/******************************************************//** 587The following function is used to get the order number 588of an old-style record in the heap of the index page. 589@return heap order number */ 590UNIV_INLINE 591ulint 592rec_get_heap_no_old( 593/*================*/ 594 const rec_t* rec) /*!< in: physical record */ 595{ 596 return(rec_get_bit_field_2(rec, REC_OLD_HEAP_NO, 597 REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT)); 598} 599 600/******************************************************//** 601The following function is used to get the order number 602of a new-style record in the heap of the index page. 603@return heap order number */ 604UNIV_INLINE 605ulint 606rec_get_heap_no_new( 607/*================*/ 608 const rec_t* rec) /*!< in: physical record */ 609{ 610 return(rec_get_bit_field_2(rec, REC_NEW_HEAP_NO, 611 REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT)); 612} 613 614/******************************************************//** 615The following function is used to test whether the data offsets in the record 616are stored in one-byte or two-byte format. 617@return TRUE if 1-byte form */ 618UNIV_INLINE 619ibool 620rec_get_1byte_offs_flag( 621/*====================*/ 622 const rec_t* rec) /*!< in: physical record */ 623{ 624 return(rec_get_bit_field_1(rec, REC_OLD_SHORT, REC_OLD_SHORT_MASK, 625 REC_OLD_SHORT_SHIFT)); 626} 627 628/******************************************************//** 629The following function is used to set the 1-byte offsets flag. */ 630UNIV_INLINE 631void 632rec_set_1byte_offs_flag( 633/*====================*/ 634 rec_t* rec, /*!< in: physical record */ 635 ibool flag) /*!< in: TRUE if 1byte form */ 636{ 637 ut_ad(flag <= 1); 638 639 rec_set_bit_field_1(rec, flag, REC_OLD_SHORT, REC_OLD_SHORT_MASK, 640 REC_OLD_SHORT_SHIFT); 641} 642 643/******************************************************//** 644Returns the offset of nth field end if the record is stored in the 1-byte 645offsets form. If the field is SQL null, the flag is ORed in the returned 646value. 647@return offset of the start of the field, SQL null flag ORed */ 648UNIV_INLINE 649uint8_t 650rec_1_get_field_end_info( 651/*=====================*/ 652 const rec_t* rec, /*!< in: record */ 653 ulint n) /*!< in: field index */ 654{ 655 ut_ad(rec_get_1byte_offs_flag(rec)); 656 ut_ad(n < rec_get_n_fields_old(rec)); 657 658 return(mach_read_from_1(rec - (REC_N_OLD_EXTRA_BYTES + n + 1))); 659} 660 661/******************************************************//** 662Returns the offset of nth field end if the record is stored in the 2-byte 663offsets form. If the field is SQL null, the flag is ORed in the returned 664value. 665@return offset of the start of the field, SQL null flag and extern 666storage flag ORed */ 667UNIV_INLINE 668uint16_t 669rec_2_get_field_end_info( 670/*=====================*/ 671 const rec_t* rec, /*!< in: record */ 672 ulint n) /*!< in: field index */ 673{ 674 ut_ad(!rec_get_1byte_offs_flag(rec)); 675 ut_ad(n < rec_get_n_fields_old(rec)); 676 677 return(mach_read_from_2(rec - (REC_N_OLD_EXTRA_BYTES + 2 * n + 2))); 678} 679 680/******************************************************//** 681Returns nonzero if the field is stored off-page. 682@retval 0 if the field is stored in-page 683@retval REC_2BYTE_EXTERN_MASK if the field is stored externally */ 684UNIV_INLINE 685ulint 686rec_2_is_field_extern( 687/*==================*/ 688 const rec_t* rec, /*!< in: record */ 689 ulint n) /*!< in: field index */ 690{ 691 return(rec_2_get_field_end_info(rec, n) & REC_2BYTE_EXTERN_MASK); 692} 693 694/**********************************************************//** 695The following function sets the number of allocated elements 696for an array of offsets. */ 697UNIV_INLINE 698void 699rec_offs_set_n_alloc( 700/*=================*/ 701 rec_offs*offsets, /*!< out: array for rec_get_offsets(), 702 must be allocated */ 703 ulint n_alloc) /*!< in: number of elements */ 704{ 705 ut_ad(n_alloc > REC_OFFS_HEADER_SIZE); 706 MEM_UNDEFINED(offsets, n_alloc * sizeof *offsets); 707 offsets[0] = static_cast<rec_offs>(n_alloc); 708} 709 710/************************************************************//** 711The following function is used to get an offset to the nth 712data field in a record. 713@return offset from the origin of rec */ 714UNIV_INLINE 715rec_offs 716rec_get_nth_field_offs( 717/*===================*/ 718 const rec_offs* offsets,/*!< in: array returned by rec_get_offsets() */ 719 ulint n, /*!< in: index of the field */ 720 ulint* len) /*!< out: length of the field; UNIV_SQL_NULL 721 if SQL null; UNIV_SQL_DEFAULT is default value */ 722{ 723 ut_ad(n < rec_offs_n_fields(offsets)); 724 725 rec_offs offs = n == 0 ? 0 : get_value(rec_offs_base(offsets)[n]); 726 rec_offs next_offs = rec_offs_base(offsets)[1 + n]; 727 728 if (get_type(next_offs) == SQL_NULL) { 729 *len = UNIV_SQL_NULL; 730 } else if (get_type(next_offs) == DEFAULT) { 731 *len = UNIV_SQL_DEFAULT; 732 } else { 733 *len = get_value(next_offs) - offs; 734 } 735 736 return(offs); 737} 738 739/******************************************************//** 740Determine if the offsets are for a record containing null BLOB pointers. 741@return first field containing a null BLOB pointer, or NULL if none found */ 742UNIV_INLINE 743const byte* 744rec_offs_any_null_extern( 745/*=====================*/ 746 const rec_t* rec, /*!< in: record */ 747 const rec_offs* offsets) /*!< in: rec_get_offsets(rec) */ 748{ 749 ulint i; 750 ut_ad(rec_offs_validate(rec, NULL, offsets)); 751 752 if (!rec_offs_any_extern(offsets)) { 753 return(NULL); 754 } 755 756 for (i = 0; i < rec_offs_n_fields(offsets); i++) { 757 if (rec_offs_nth_extern(offsets, i)) { 758 ulint len; 759 const byte* field 760 = rec_get_nth_field(rec, offsets, i, &len); 761 762 ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); 763 if (!memcmp(field + len 764 - BTR_EXTERN_FIELD_REF_SIZE, 765 field_ref_zero, 766 BTR_EXTERN_FIELD_REF_SIZE)) { 767 return(field); 768 } 769 } 770 } 771 772 return(NULL); 773} 774 775/******************************************************//** 776Gets the physical size of a field. 777@return length of field */ 778UNIV_INLINE 779ulint 780rec_offs_nth_size( 781/*==============*/ 782 const rec_offs* offsets,/*!< in: array returned by rec_get_offsets() */ 783 ulint n) /*!< in: nth field */ 784{ 785 ut_ad(rec_offs_validate(NULL, NULL, offsets)); 786 ut_ad(n < rec_offs_n_fields(offsets)); 787 if (!n) { 788 return get_value(rec_offs_base(offsets)[1 + n]); 789 } 790 return get_value((rec_offs_base(offsets)[1 + n])) 791 - get_value(rec_offs_base(offsets)[n]); 792} 793 794/******************************************************//** 795Returns the number of extern bits set in a record. 796@return number of externally stored fields */ 797UNIV_INLINE 798ulint 799rec_offs_n_extern( 800/*==============*/ 801 const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */ 802{ 803 ulint n = 0; 804 805 if (rec_offs_any_extern(offsets)) { 806 ulint i; 807 808 for (i = rec_offs_n_fields(offsets); i--; ) { 809 if (rec_offs_nth_extern(offsets, i)) { 810 n++; 811 } 812 } 813 } 814 815 return(n); 816} 817 818/******************************************************//** 819Returns the offset of n - 1th field end if the record is stored in the 1-byte 820offsets form. If the field is SQL null, the flag is ORed in the returned 821value. This function and the 2-byte counterpart are defined here because the 822C-compiler was not able to sum negative and positive constant offsets, and 823warned of constant arithmetic overflow within the compiler. 824@return offset of the start of the PREVIOUS field, SQL null flag ORed */ 825UNIV_INLINE 826ulint 827rec_1_get_prev_field_end_info( 828/*==========================*/ 829 const rec_t* rec, /*!< in: record */ 830 ulint n) /*!< in: field index */ 831{ 832 ut_ad(rec_get_1byte_offs_flag(rec)); 833 ut_ad(n <= rec_get_n_fields_old(rec)); 834 835 return(mach_read_from_1(rec - (REC_N_OLD_EXTRA_BYTES + n))); 836} 837 838/******************************************************//** 839Returns the offset of n - 1th field end if the record is stored in the 2-byte 840offsets form. If the field is SQL null, the flag is ORed in the returned 841value. 842@return offset of the start of the PREVIOUS field, SQL null flag ORed */ 843UNIV_INLINE 844ulint 845rec_2_get_prev_field_end_info( 846/*==========================*/ 847 const rec_t* rec, /*!< in: record */ 848 ulint n) /*!< in: field index */ 849{ 850 ut_ad(!rec_get_1byte_offs_flag(rec)); 851 ut_ad(n <= rec_get_n_fields_old(rec)); 852 853 return(mach_read_from_2(rec - (REC_N_OLD_EXTRA_BYTES + 2 * n))); 854} 855 856/******************************************************//** 857Sets the field end info for the nth field if the record is stored in the 8581-byte format. */ 859UNIV_INLINE 860void 861rec_1_set_field_end_info( 862/*=====================*/ 863 rec_t* rec, /*!< in: record */ 864 ulint n, /*!< in: field index */ 865 ulint info) /*!< in: value to set */ 866{ 867 ut_ad(rec_get_1byte_offs_flag(rec)); 868 ut_ad(n < rec_get_n_fields_old(rec)); 869 870 mach_write_to_1(rec - (REC_N_OLD_EXTRA_BYTES + n + 1), info); 871} 872 873/******************************************************//** 874Sets the field end info for the nth field if the record is stored in the 8752-byte format. */ 876UNIV_INLINE 877void 878rec_2_set_field_end_info( 879/*=====================*/ 880 rec_t* rec, /*!< in: record */ 881 ulint n, /*!< in: field index */ 882 ulint info) /*!< in: value to set */ 883{ 884 ut_ad(!rec_get_1byte_offs_flag(rec)); 885 ut_ad(n < rec_get_n_fields_old(rec)); 886 887 mach_write_to_2(rec - (REC_N_OLD_EXTRA_BYTES + 2 * n + 2), info); 888} 889 890/******************************************************//** 891Returns the offset of nth field start if the record is stored in the 1-byte 892offsets form. 893@return offset of the start of the field */ 894UNIV_INLINE 895ulint 896rec_1_get_field_start_offs( 897/*=======================*/ 898 const rec_t* rec, /*!< in: record */ 899 ulint n) /*!< in: field index */ 900{ 901 ut_ad(rec_get_1byte_offs_flag(rec)); 902 ut_ad(n <= rec_get_n_fields_old(rec)); 903 904 if (n == 0) { 905 906 return(0); 907 } 908 909 return(rec_1_get_prev_field_end_info(rec, n) 910 & ~REC_1BYTE_SQL_NULL_MASK); 911} 912 913/******************************************************//** 914Returns the offset of nth field start if the record is stored in the 2-byte 915offsets form. 916@return offset of the start of the field */ 917UNIV_INLINE 918ulint 919rec_2_get_field_start_offs( 920/*=======================*/ 921 const rec_t* rec, /*!< in: record */ 922 ulint n) /*!< in: field index */ 923{ 924 ut_ad(!rec_get_1byte_offs_flag(rec)); 925 ut_ad(n <= rec_get_n_fields_old(rec)); 926 927 if (n == 0) { 928 929 return(0); 930 } 931 932 return(rec_2_get_prev_field_end_info(rec, n) 933 & ~(REC_2BYTE_SQL_NULL_MASK | REC_2BYTE_EXTERN_MASK)); 934} 935 936/******************************************************//** 937The following function is used to read the offset of the start of a data field 938in the record. The start of an SQL null field is the end offset of the 939previous non-null field, or 0, if none exists. If n is the number of the last 940field + 1, then the end offset of the last field is returned. 941@return offset of the start of the field */ 942UNIV_INLINE 943ulint 944rec_get_field_start_offs( 945/*=====================*/ 946 const rec_t* rec, /*!< in: record */ 947 ulint n) /*!< in: field index */ 948{ 949 ut_ad(rec); 950 ut_ad(n <= rec_get_n_fields_old(rec)); 951 952 if (n == 0) { 953 954 return(0); 955 } 956 957 if (rec_get_1byte_offs_flag(rec)) { 958 959 return(rec_1_get_field_start_offs(rec, n)); 960 } 961 962 return(rec_2_get_field_start_offs(rec, n)); 963} 964 965/************************************************************//** 966Gets the physical size of an old-style field. 967Also an SQL null may have a field of size > 0, 968if the data type is of a fixed size. 969@return field size in bytes */ 970UNIV_INLINE 971ulint 972rec_get_nth_field_size( 973/*===================*/ 974 const rec_t* rec, /*!< in: record */ 975 ulint n) /*!< in: index of the field */ 976{ 977 ulint os; 978 ulint next_os; 979 980 os = rec_get_field_start_offs(rec, n); 981 next_os = rec_get_field_start_offs(rec, n + 1); 982 983 ut_ad(next_os - os < srv_page_size); 984 985 return(next_os - os); 986} 987 988/**********************************************************//** 989The following function returns the data size of an old-style physical 990record, that is the sum of field lengths. SQL null fields 991are counted as length 0 fields. The value returned by the function 992is the distance from record origin to record end in bytes. 993@return size */ 994UNIV_INLINE 995ulint 996rec_get_data_size_old( 997/*==================*/ 998 const rec_t* rec) /*!< in: physical record */ 999{ 1000 ut_ad(rec); 1001 1002 return(rec_get_field_start_offs(rec, rec_get_n_fields_old(rec))); 1003} 1004 1005/**********************************************************//** 1006The following function sets the number of fields in offsets. */ 1007UNIV_INLINE 1008void 1009rec_offs_set_n_fields( 1010/*==================*/ 1011 rec_offs* offsets, /*!< in/out: array returned by 1012 rec_get_offsets() */ 1013 ulint n_fields) /*!< in: number of fields */ 1014{ 1015 ut_ad(offsets); 1016 ut_ad(n_fields > 0); 1017 ut_ad(n_fields <= REC_MAX_N_FIELDS); 1018 ut_ad(n_fields + REC_OFFS_HEADER_SIZE 1019 <= rec_offs_get_n_alloc(offsets)); 1020 offsets[1] = static_cast<rec_offs>(n_fields); 1021} 1022 1023/**********************************************************//** 1024The following function returns the data size of a physical 1025record, that is the sum of field lengths. SQL null fields 1026are counted as length 0 fields. The value returned by the function 1027is the distance from record origin to record end in bytes. 1028@return size */ 1029UNIV_INLINE 1030ulint 1031rec_offs_data_size( 1032/*===============*/ 1033 const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */ 1034{ 1035 ulint size; 1036 1037 ut_ad(rec_offs_validate(NULL, NULL, offsets)); 1038 size = get_value(rec_offs_base(offsets)[rec_offs_n_fields(offsets)]); 1039 ut_ad(size < srv_page_size); 1040 return(size); 1041} 1042 1043/**********************************************************//** 1044Returns the total size of record minus data size of record. The value 1045returned by the function is the distance from record start to record origin 1046in bytes. 1047@return size */ 1048UNIV_INLINE 1049ulint 1050rec_offs_extra_size( 1051/*================*/ 1052 const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */ 1053{ 1054 ulint size; 1055 ut_ad(rec_offs_validate(NULL, NULL, offsets)); 1056 size = *rec_offs_base(offsets) & REC_OFFS_MASK; 1057 ut_ad(size < srv_page_size); 1058 return(size); 1059} 1060 1061/**********************************************************//** 1062Returns the total size of a physical record. 1063@return size */ 1064UNIV_INLINE 1065ulint 1066rec_offs_size( 1067/*==========*/ 1068 const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */ 1069{ 1070 return(rec_offs_data_size(offsets) + rec_offs_extra_size(offsets)); 1071} 1072 1073#ifdef UNIV_DEBUG 1074/**********************************************************//** 1075Returns a pointer to the end of the record. 1076@return pointer to end */ 1077UNIV_INLINE 1078byte* 1079rec_get_end( 1080/*========*/ 1081 const rec_t* rec, /*!< in: pointer to record */ 1082 const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */ 1083{ 1084 ut_ad(rec_offs_validate(rec, NULL, offsets)); 1085 return(const_cast<rec_t*>(rec + rec_offs_data_size(offsets))); 1086} 1087 1088/**********************************************************//** 1089Returns a pointer to the start of the record. 1090@return pointer to start */ 1091UNIV_INLINE 1092byte* 1093rec_get_start( 1094/*==========*/ 1095 const rec_t* rec, /*!< in: pointer to record */ 1096 const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */ 1097{ 1098 ut_ad(rec_offs_validate(rec, NULL, offsets)); 1099 return(const_cast<rec_t*>(rec - rec_offs_extra_size(offsets))); 1100} 1101#endif /* UNIV_DEBUG */ 1102 1103/** Copy a physical record to a buffer. 1104@param[in] buf buffer 1105@param[in] rec physical record 1106@param[in] offsets array returned by rec_get_offsets() 1107@return pointer to the origin of the copy */ 1108UNIV_INLINE 1109rec_t* 1110rec_copy( 1111 void* buf, 1112 const rec_t* rec, 1113 const rec_offs* offsets) 1114{ 1115 ulint extra_len; 1116 ulint data_len; 1117 1118 ut_ad(rec != NULL); 1119 ut_ad(buf != NULL); 1120 ut_ad(rec_offs_validate(rec, NULL, offsets)); 1121 ut_ad(rec_validate(rec, offsets)); 1122 1123 extra_len = rec_offs_extra_size(offsets); 1124 data_len = rec_offs_data_size(offsets); 1125 1126 memcpy(buf, rec - extra_len, extra_len + data_len); 1127 1128 return((byte*) buf + extra_len); 1129} 1130 1131/**********************************************************//** 1132Returns the extra size of an old-style physical record if we know its 1133data size and number of fields. 1134@return extra size */ 1135UNIV_INLINE 1136ulint 1137rec_get_converted_extra_size( 1138/*=========================*/ 1139 ulint data_size, /*!< in: data size */ 1140 ulint n_fields, /*!< in: number of fields */ 1141 ulint n_ext) /*!< in: number of externally stored columns */ 1142{ 1143 if (!n_ext && data_size <= REC_1BYTE_OFFS_LIMIT) { 1144 1145 return(REC_N_OLD_EXTRA_BYTES + n_fields); 1146 } 1147 1148 return(REC_N_OLD_EXTRA_BYTES + 2 * n_fields); 1149} 1150 1151/**********************************************************//** 1152The following function returns the size of a data tuple when converted to 1153a physical record. 1154@return size */ 1155UNIV_INLINE 1156ulint 1157rec_get_converted_size( 1158/*===================*/ 1159 dict_index_t* index, /*!< in: record descriptor */ 1160 const dtuple_t* dtuple, /*!< in: data tuple */ 1161 ulint n_ext) /*!< in: number of externally stored columns */ 1162{ 1163 ulint data_size; 1164 ulint extra_size; 1165 1166 ut_ad(dtuple_check_typed(dtuple)); 1167#ifdef UNIV_DEBUG 1168 if (dict_index_is_ibuf(index)) { 1169 ut_ad(dtuple->n_fields > 1); 1170 } else if ((dtuple_get_info_bits(dtuple) & REC_NEW_STATUS_MASK) 1171 == REC_STATUS_NODE_PTR) { 1172 ut_ad(dtuple->n_fields - 1 1173 == dict_index_get_n_unique_in_tree_nonleaf(index)); 1174 } else if (index->table->id == DICT_INDEXES_ID) { 1175 /* The column SYS_INDEXES.MERGE_THRESHOLD was 1176 instantly added in MariaDB 10.2.2 (MySQL 5.7). */ 1177 ut_ad(!index->table->is_temporary()); 1178 ut_ad(index->n_fields == DICT_NUM_FIELDS__SYS_INDEXES); 1179 ut_ad(dtuple->n_fields == DICT_NUM_FIELDS__SYS_INDEXES 1180 || dtuple->n_fields 1181 == DICT_FLD__SYS_INDEXES__MERGE_THRESHOLD); 1182 } else { 1183 ut_ad(dtuple->n_fields >= index->n_core_fields); 1184 ut_ad(dtuple->n_fields <= index->n_fields 1185 || dtuple->is_alter_metadata()); 1186 } 1187#endif 1188 1189 if (dict_table_is_comp(index->table)) { 1190 return rec_get_converted_size_comp(index, dtuple, NULL); 1191 } 1192 1193 data_size = dtuple_get_data_size(dtuple, 0); 1194 1195 /* If primary key is being updated then the new record inherits 1196 externally stored fields from the delete-marked old record. 1197 In that case, n_ext may be less value than 1198 dtuple_get_n_ext(tuple). */ 1199 ut_ad(n_ext <= dtuple_get_n_ext(dtuple)); 1200 extra_size = rec_get_converted_extra_size( 1201 data_size, dtuple_get_n_fields(dtuple), n_ext); 1202 1203 return(data_size + extra_size); 1204} 1205