1 /************************************************************************
4 (c) 1994-1996 Innobase Oy
6 Created 5/30/1994 Heikki Tuuri
7 *************************************************************************/
11 #include "dict0dict.h"
12 #include "btr0types.h"
14 /* Compact flag ORed to the extra size returned by rec_get_offsets() */
15 #define REC_OFFS_COMPACT ((ulint) 1 << 31)
16 /* SQL NULL flag in offsets returned by rec_get_offsets() */
17 #define REC_OFFS_SQL_NULL ((ulint) 1 << 31)
18 /* External flag in offsets returned by rec_get_offsets() */
19 #define REC_OFFS_EXTERNAL ((ulint) 1 << 30)
20 /* Mask for offsets returned by rec_get_offsets() */
21 #define REC_OFFS_MASK (REC_OFFS_EXTERNAL - 1)
23 /* Offsets of the bit-fields in an old-style record. NOTE! In the table the
24 most significant bytes and bits are written below less significant.
26 (1) byte offset (2) bit usage within byte
28 origin -> 1 8 bits pointer to next record
29 2 8 bits pointer to next record
31 7 bits number of fields
32 4 3 bits number of fields
39 /* Offsets of the bit-fields in a new-style record. NOTE! In the table the
40 most significant bytes and bits are written below less significant.
42 (1) byte offset (2) bit usage within byte
44 origin -> 1 8 bits relative offset of next record
45 2 8 bits relative offset of next record
46 the relative offset is an unsigned 16-bit
48 (offset_of_next_record
49 - offset_of_this_record) mod 64Ki,
50 where mod is the modulo as a non-negative
52 we can calculate the the offset of the next
53 record with the formula:
54 relative_offset + offset_of_this_record
57 000=conventional record
58 001=node pointer record (inside B-tree)
68 /* We list the byte offsets from the origin of the record, the mask,
69 and the shift needed to obtain each bit-field of the record. */
72 #define REC_NEXT_MASK 0xFFFFUL
73 #define REC_NEXT_SHIFT 0
75 #define REC_OLD_SHORT 3 /* This is single byte bit-field */
76 #define REC_OLD_SHORT_MASK 0x1UL
77 #define REC_OLD_SHORT_SHIFT 0
79 #define REC_OLD_N_FIELDS 4
80 #define REC_OLD_N_FIELDS_MASK 0x7FEUL
81 #define REC_OLD_N_FIELDS_SHIFT 1
83 #define REC_NEW_STATUS 3 /* This is single byte bit-field */
84 #define REC_NEW_STATUS_MASK 0x7UL
85 #define REC_NEW_STATUS_SHIFT 0
87 #define REC_OLD_HEAP_NO 5
88 #define REC_NEW_HEAP_NO 4
89 #define REC_HEAP_NO_MASK 0xFFF8UL
90 #define REC_HEAP_NO_SHIFT 3
92 #define REC_OLD_N_OWNED 6 /* This is single byte bit-field */
93 #define REC_NEW_N_OWNED 5 /* This is single byte bit-field */
94 #define REC_N_OWNED_MASK 0xFUL
95 #define REC_N_OWNED_SHIFT 0
97 #define REC_OLD_INFO_BITS 6 /* This is single byte bit-field */
98 #define REC_NEW_INFO_BITS 5 /* This is single byte bit-field */
99 #define REC_INFO_BITS_MASK 0xF0UL
100 #define REC_INFO_BITS_SHIFT 0
102 /* The following masks are used to filter the SQL null bit from
103 one-byte and two-byte offsets */
105 #define REC_1BYTE_SQL_NULL_MASK 0x80UL
106 #define REC_2BYTE_SQL_NULL_MASK 0x8000UL
108 /* In a 2-byte offset the second most significant bit denotes
109 a field stored to another page: */
111 #define REC_2BYTE_EXTERN_MASK 0x4000UL
113 #if REC_OLD_SHORT_MASK << (8 * (REC_OLD_SHORT - 3)) \
114 ^ REC_OLD_N_FIELDS_MASK << (8 * (REC_OLD_N_FIELDS - 4)) \
115 ^ REC_HEAP_NO_MASK << (8 * (REC_OLD_HEAP_NO - 4)) \
116 ^ REC_N_OWNED_MASK << (8 * (REC_OLD_N_OWNED - 3)) \
117 ^ REC_INFO_BITS_MASK << (8 * (REC_OLD_INFO_BITS - 3)) \
119 # error "sum of old-style masks != 0xFFFFFFFFUL"
121 #if REC_NEW_STATUS_MASK << (8 * (REC_NEW_STATUS - 3)) \
122 ^ REC_HEAP_NO_MASK << (8 * (REC_NEW_HEAP_NO - 4)) \
123 ^ REC_N_OWNED_MASK << (8 * (REC_NEW_N_OWNED - 3)) \
124 ^ REC_INFO_BITS_MASK << (8 * (REC_NEW_INFO_BITS - 3)) \
126 # error "sum of new-style masks != 0xFFFFFFUL"
129 /***************************************************************
130 Sets the value of the ith field SQL null bit of an old-style record. */
133 rec_set_nth_field_null_bit(
134 /*=======================*/
135 rec_t* rec, /* in: record */
136 ulint i, /* in: ith field */
137 ibool val); /* in: value to set */
138 /***************************************************************
139 Sets an old-style record field to SQL null.
140 The physical size of the field is not changed. */
143 rec_set_nth_field_sql_null(
144 /*=======================*/
145 rec_t* rec, /* in: record */
146 ulint n); /* in: index of the field */
148 /***************************************************************
149 Sets the value of the ith field extern storage bit of an old-style record. */
152 rec_set_nth_field_extern_bit_old(
153 /*=============================*/
154 rec_t* rec, /* in: old-style record */
155 ulint i, /* in: ith field */
156 ibool val, /* in: value to set */
157 mtr_t* mtr); /* in: mtr holding an X-latch to the page where
158 rec is, or NULL; in the NULL case we do not
159 write to log about the change */
160 /***************************************************************
161 Sets the value of the ith field extern storage bit of a new-style record. */
164 rec_set_nth_field_extern_bit_new(
165 /*=============================*/
166 rec_t* rec, /* in: record */
167 dict_index_t* index, /* in: record descriptor */
168 ulint ith, /* in: ith field */
169 ibool val, /* in: value to set */
170 mtr_t* mtr); /* in: mtr holding an X-latch to the page
171 where rec is, or NULL; in the NULL case
172 we do not write to log about the change */
174 /**********************************************************
175 Gets a bit field from within 1 byte. */
180 rec_t* rec, /* in: pointer to record origin */
181 ulint offs, /* in: offset from the origin down */
182 ulint mask, /* in: mask used to filter bits */
183 ulint shift) /* in: shift right applied after masking */
187 return((mach_read_from_1(rec - offs) & mask) >> shift);
190 /**********************************************************
191 Sets a bit field within 1 byte. */
196 rec_t* rec, /* in: pointer to record origin */
197 ulint val, /* in: value to set */
198 ulint offs, /* in: offset from the origin down */
199 ulint mask, /* in: mask used to filter bits */
200 ulint shift) /* in: shift right applied after masking */
203 ut_ad(offs <= REC_N_OLD_EXTRA_BYTES);
205 ut_ad(mask <= 0xFFUL);
206 ut_ad(((mask >> shift) << shift) == mask);
207 ut_ad(((val << shift) & mask) == (val << shift));
209 mach_write_to_1(rec - offs,
210 (mach_read_from_1(rec - offs) & ~mask)
214 /**********************************************************
215 Gets a bit field from within 2 bytes. */
220 rec_t* rec, /* in: pointer to record origin */
221 ulint offs, /* in: offset from the origin down */
222 ulint mask, /* in: mask used to filter bits */
223 ulint shift) /* in: shift right applied after masking */
227 return((mach_read_from_2(rec - offs) & mask) >> shift);
230 /**********************************************************
231 Sets a bit field within 2 bytes. */
236 rec_t* rec, /* in: pointer to record origin */
237 ulint val, /* in: value to set */
238 ulint offs, /* in: offset from the origin down */
239 ulint mask, /* in: mask used to filter bits */
240 ulint shift) /* in: shift right applied after masking */
243 ut_ad(offs <= REC_N_OLD_EXTRA_BYTES);
244 ut_ad(mask > 0xFFUL);
245 ut_ad(mask <= 0xFFFFUL);
246 ut_ad((mask >> shift) & 1);
247 ut_ad(0 == ((mask >> shift) & ((mask >> shift) + 1)));
248 ut_ad(((mask >> shift) << shift) == mask);
249 ut_ad(((val << shift) & mask) == (val << shift));
251 mach_write_to_2(rec - offs,
252 (mach_read_from_2(rec - offs) & ~mask)
256 /**********************************************************
257 The following function is used to get the offset of the next chained record
263 /* out: the page offset of the next chained record, or
265 rec_t* rec, /* in: physical record */
266 ulint comp) /* in: nonzero=compact page format */
269 #if REC_NEXT_MASK != 0xFFFFUL
270 # error "REC_NEXT_MASK != 0xFFFFUL"
273 # error "REC_NEXT_SHIFT != 0"
276 field_value = mach_read_from_2(rec - REC_NEXT);
279 #if UNIV_PAGE_SIZE <= 32768
280 /* Note that for 64 KiB pages, field_value can 'wrap around'
281 and the debug assertion is not valid */
283 /* In the following assertion, field_value is interpreted
284 as signed 16-bit integer in 2's complement arithmetics.
285 If all platforms defined int16_t in the standard headers,
286 the expression could be written simpler as
287 (int16_t) field_value + ut_align_offset(...) < UNIV_PAGE_SIZE
289 ut_ad((field_value >= 32768
290 ? field_value - 65536
292 + ut_align_offset(rec, UNIV_PAGE_SIZE)
295 if (field_value == 0) {
300 return(ut_align_offset(rec + field_value, UNIV_PAGE_SIZE));
302 ut_ad(field_value < UNIV_PAGE_SIZE);
308 /**********************************************************
309 The following function is used to set the next record offset field of the
315 rec_t* rec, /* in: physical record */
316 ulint comp, /* in: nonzero=compact page format */
317 ulint next) /* in: offset of the next record, or 0 if none */
320 ut_ad(UNIV_PAGE_SIZE > next);
321 #if REC_NEXT_MASK != 0xFFFFUL
322 # error "REC_NEXT_MASK != 0xFFFFUL"
325 # error "REC_NEXT_SHIFT != 0"
332 /* The following two statements calculate
333 next - offset_of_rec mod 64Ki, where mod is the modulo
334 as a non-negative number */
336 field_value = (ulint)((lint)next
337 - (lint)ut_align_offset(
338 rec, UNIV_PAGE_SIZE));
339 field_value &= REC_NEXT_MASK;
344 mach_write_to_2(rec - REC_NEXT, field_value);
346 mach_write_to_2(rec - REC_NEXT, next);
350 /**********************************************************
351 The following function is used to get the number of fields
352 in an old-style record. */
355 rec_get_n_fields_old(
356 /*=================*/
357 /* out: number of data fields */
358 rec_t* rec) /* in: physical record */
364 ret = rec_get_bit_field_2(rec, REC_OLD_N_FIELDS,
365 REC_OLD_N_FIELDS_MASK,
366 REC_OLD_N_FIELDS_SHIFT);
367 ut_ad(ret <= REC_MAX_N_FIELDS);
373 /**********************************************************
374 The following function is used to set the number of fields
375 in an old-style record. */
378 rec_set_n_fields_old(
379 /*=================*/
380 rec_t* rec, /* in: physical record */
381 ulint n_fields) /* in: the number of fields */
384 ut_ad(n_fields <= REC_MAX_N_FIELDS);
387 rec_set_bit_field_2(rec, n_fields, REC_OLD_N_FIELDS,
388 REC_OLD_N_FIELDS_MASK, REC_OLD_N_FIELDS_SHIFT);
391 /**********************************************************
392 The following function retrieves the status bits of a new-style record. */
397 /* out: status bits */
398 rec_t* rec) /* in: physical record */
404 ret = rec_get_bit_field_1(rec, REC_NEW_STATUS,
405 REC_NEW_STATUS_MASK, REC_NEW_STATUS_SHIFT);
406 ut_ad((ret & ~REC_NEW_STATUS_MASK) == 0);
411 /**********************************************************
412 The following function is used to get the number of fields
418 /* out: number of data fields */
419 rec_t* rec, /* in: physical record */
420 dict_index_t* index) /* in: record descriptor */
425 if (!dict_table_is_comp(index->table)) {
426 return(rec_get_n_fields_old(rec));
429 switch (rec_get_status(rec)) {
430 case REC_STATUS_ORDINARY:
431 return(dict_index_get_n_fields(index));
432 case REC_STATUS_NODE_PTR:
433 return(dict_index_get_n_unique_in_tree(index) + 1);
434 case REC_STATUS_INFIMUM:
435 case REC_STATUS_SUPREMUM:
439 return(ULINT_UNDEFINED);
443 /**********************************************************
444 The following function is used to get the number of records owned by the
445 previous directory record. */
450 /* out: number of owned records */
451 rec_t* rec, /* in: physical record */
452 ulint comp) /* in: nonzero=compact page format */
458 ret = rec_get_bit_field_1(rec,
459 comp ? REC_NEW_N_OWNED : REC_OLD_N_OWNED,
460 REC_N_OWNED_MASK, REC_N_OWNED_SHIFT);
461 ut_ad(ret <= REC_MAX_N_OWNED);
466 /**********************************************************
467 The following function is used to set the number of owned records. */
472 rec_t* rec, /* in: physical record */
473 ulint comp, /* in: nonzero=compact page format */
474 ulint n_owned) /* in: the number of owned */
477 ut_ad(n_owned <= REC_MAX_N_OWNED);
479 rec_set_bit_field_1(rec, n_owned,
480 comp ? REC_NEW_N_OWNED : REC_OLD_N_OWNED,
481 REC_N_OWNED_MASK, REC_N_OWNED_SHIFT);
484 /**********************************************************
485 The following function is used to retrieve the info bits of a record. */
491 rec_t* rec, /* in: physical record */
492 ulint comp) /* in: nonzero=compact page format */
498 ret = rec_get_bit_field_1(rec,
499 comp ? REC_NEW_INFO_BITS : REC_OLD_INFO_BITS,
500 REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT);
501 ut_ad((ret & ~REC_INFO_BITS_MASK) == 0);
506 /**********************************************************
507 The following function is used to set the info bits of a record. */
512 rec_t* rec, /* in: physical record */
513 ulint comp, /* in: nonzero=compact page format */
514 ulint bits) /* in: info bits */
517 ut_ad((bits & ~REC_INFO_BITS_MASK) == 0);
519 rec_set_bit_field_1(rec, bits,
520 comp ? REC_NEW_INFO_BITS : REC_OLD_INFO_BITS,
521 REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT);
524 /**********************************************************
525 The following function is used to set the status bits of a new-style record. */
530 rec_t* rec, /* in: physical record */
531 ulint bits) /* in: info bits */
534 ut_ad((bits & ~REC_NEW_STATUS_MASK) == 0);
536 rec_set_bit_field_1(rec, bits, REC_NEW_STATUS,
537 REC_NEW_STATUS_MASK, REC_NEW_STATUS_SHIFT);
540 /**********************************************************
541 The following function is used to retrieve the info and status
542 bits of a record. (Only compact records have status bits.) */
545 rec_get_info_and_status_bits(
546 /*=========================*/
548 rec_t* rec, /* in: physical record */
549 ulint comp) /* in: nonzero=compact page format */
552 #if (REC_NEW_STATUS_MASK >> REC_NEW_STATUS_SHIFT) \
553 & (REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT)
554 # error "REC_NEW_STATUS_MASK and REC_INFO_BITS_MASK overlap"
556 if (UNIV_EXPECT(comp, REC_OFFS_COMPACT)) {
557 bits = rec_get_info_bits(rec, TRUE) | rec_get_status(rec);
559 bits = rec_get_info_bits(rec, FALSE);
560 ut_ad(!(bits & ~(REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT)));
564 /**********************************************************
565 The following function is used to set the info and status
566 bits of a record. (Only compact records have status bits.) */
569 rec_set_info_and_status_bits(
570 /*=========================*/
571 rec_t* rec, /* in: physical record */
572 ulint comp, /* in: nonzero=compact page format */
573 ulint bits) /* in: info bits */
575 #if (REC_NEW_STATUS_MASK >> REC_NEW_STATUS_SHIFT) \
576 & (REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT)
577 # error "REC_NEW_STATUS_MASK and REC_INFO_BITS_MASK overlap"
580 rec_set_status(rec, bits & REC_NEW_STATUS_MASK);
582 ut_ad(!(bits & ~(REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT)));
584 rec_set_info_bits(rec, comp, bits & ~REC_NEW_STATUS_MASK);
587 /**********************************************************
588 The following function tells if record is delete marked. */
591 rec_get_deleted_flag(
592 /*=================*/
593 /* out: nonzero if delete marked */
594 rec_t* rec, /* in: physical record */
595 ulint comp) /* in: nonzero=compact page format */
597 if (UNIV_EXPECT(comp, REC_OFFS_COMPACT)) {
598 return(UNIV_UNLIKELY(
599 rec_get_bit_field_1(rec, REC_NEW_INFO_BITS,
600 REC_INFO_DELETED_FLAG,
601 REC_INFO_BITS_SHIFT)));
603 return(UNIV_UNLIKELY(
604 rec_get_bit_field_1(rec, REC_OLD_INFO_BITS,
605 REC_INFO_DELETED_FLAG,
606 REC_INFO_BITS_SHIFT)));
610 /**********************************************************
611 The following function is used to set the deleted bit. */
614 rec_set_deleted_flag(
615 /*=================*/
616 rec_t* rec, /* in: physical record */
617 ulint comp, /* in: nonzero=compact page format */
618 ulint flag) /* in: nonzero if delete marked */
622 val = rec_get_info_bits(rec, comp);
625 val |= REC_INFO_DELETED_FLAG;
627 val &= ~REC_INFO_DELETED_FLAG;
630 rec_set_info_bits(rec, comp, val);
633 /**********************************************************
634 The following function tells if a new-style record is a node pointer. */
637 rec_get_node_ptr_flag(
638 /*==================*/
639 /* out: TRUE if node pointer */
640 rec_t* rec) /* in: physical record */
642 return(REC_STATUS_NODE_PTR == rec_get_status(rec));
645 /**********************************************************
646 The following function is used to get the order number of the record in the
647 heap of the index page. */
652 /* out: heap order number */
653 rec_t* rec, /* in: physical record */
654 ulint comp) /* in: nonzero=compact page format */
660 ret = rec_get_bit_field_2(rec,
661 comp ? REC_NEW_HEAP_NO : REC_OLD_HEAP_NO,
662 REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT);
663 ut_ad(ret <= REC_MAX_HEAP_NO);
668 /**********************************************************
669 The following function is used to set the heap number field in the record. */
674 rec_t* rec, /* in: physical record */
675 ulint comp, /* in: nonzero=compact page format */
676 ulint heap_no)/* in: the heap number */
678 ut_ad(heap_no <= REC_MAX_HEAP_NO);
680 rec_set_bit_field_2(rec, heap_no,
681 comp ? REC_NEW_HEAP_NO : REC_OLD_HEAP_NO,
682 REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT);
685 /**********************************************************
686 The following function is used to test whether the data offsets in the record
687 are stored in one-byte or two-byte format. */
690 rec_get_1byte_offs_flag(
691 /*====================*/
692 /* out: TRUE if 1-byte form */
693 rec_t* rec) /* in: physical record */
699 return(rec_get_bit_field_1(rec, REC_OLD_SHORT, REC_OLD_SHORT_MASK,
700 REC_OLD_SHORT_SHIFT));
703 /**********************************************************
704 The following function is used to set the 1-byte offsets flag. */
707 rec_set_1byte_offs_flag(
708 /*====================*/
709 rec_t* rec, /* in: physical record */
710 ibool flag) /* in: TRUE if 1byte form */
717 rec_set_bit_field_1(rec, flag, REC_OLD_SHORT, REC_OLD_SHORT_MASK,
718 REC_OLD_SHORT_SHIFT);
721 /**********************************************************
722 Returns the offset of nth field end if the record is stored in the 1-byte
723 offsets form. If the field is SQL null, the flag is ORed in the returned
727 rec_1_get_field_end_info(
728 /*=====================*/
729 /* out: offset of the start of the field, SQL null
731 rec_t* rec, /* in: record */
732 ulint n) /* in: field index */
734 ut_ad(rec_get_1byte_offs_flag(rec));
735 ut_ad(n < rec_get_n_fields_old(rec));
737 return(mach_read_from_1(rec - (REC_N_OLD_EXTRA_BYTES + n + 1)));
740 /**********************************************************
741 Returns the offset of nth field end if the record is stored in the 2-byte
742 offsets form. If the field is SQL null, the flag is ORed in the returned
746 rec_2_get_field_end_info(
747 /*=====================*/
748 /* out: offset of the start of the field, SQL null
749 flag and extern storage flag ORed */
750 rec_t* rec, /* in: record */
751 ulint n) /* in: field index */
753 ut_ad(!rec_get_1byte_offs_flag(rec));
754 ut_ad(n < rec_get_n_fields_old(rec));
756 return(mach_read_from_2(rec - (REC_N_OLD_EXTRA_BYTES + 2 * n + 2)));
760 /* Length of the rec_get_offsets() header */
761 # define REC_OFFS_HEADER_SIZE 4
762 #else /* UNIV_DEBUG */
763 /* Length of the rec_get_offsets() header */
764 # define REC_OFFS_HEADER_SIZE 2
765 #endif /* UNIV_DEBUG */
767 /* Get the base address of offsets. The extra_size is stored at
768 this position, and following positions hold the end offsets of
770 #define rec_offs_base(offsets) (offsets + REC_OFFS_HEADER_SIZE)
772 /**************************************************************
773 The following function returns the number of allocated elements
774 for an array of offsets. */
777 rec_offs_get_n_alloc(
778 /*=================*/
779 /* out: number of elements */
780 const ulint* offsets)/* in: array for rec_get_offsets() */
784 n_alloc = offsets[0];
789 /**************************************************************
790 The following function sets the number of allocated elements
791 for an array of offsets. */
794 rec_offs_set_n_alloc(
795 /*=================*/
796 ulint* offsets, /* out: array for rec_get_offsets(),
798 ulint n_alloc) /* in: number of elements */
802 UNIV_MEM_ASSERT_AND_ALLOC(offsets, n_alloc * sizeof *offsets);
803 offsets[0] = n_alloc;
806 /**************************************************************
807 The following function returns the number of fields in a record. */
812 /* out: number of fields */
813 const ulint* offsets)/* in: array returned by rec_get_offsets() */
817 n_fields = offsets[1];
819 ut_ad(n_fields <= REC_MAX_N_FIELDS);
820 ut_ad(n_fields + REC_OFFS_HEADER_SIZE
821 <= rec_offs_get_n_alloc(offsets));
825 /****************************************************************
826 Validates offsets returned by rec_get_offsets(). */
831 /* out: TRUE if valid */
832 rec_t* rec, /* in: record or NULL */
833 dict_index_t* index, /* in: record descriptor or NULL */
834 const ulint* offsets)/* in: array returned by rec_get_offsets() */
836 ulint i = rec_offs_n_fields(offsets);
837 ulint last = ULINT_MAX;
838 ulint comp = *rec_offs_base(offsets) & REC_OFFS_COMPACT;
841 ut_ad((ulint) rec == offsets[2]);
843 ut_a(rec_get_n_fields_old(rec) >= i);
848 ut_ad((ulint) index == offsets[3]);
849 max_n_fields = ut_max(
850 dict_index_get_n_fields(index),
851 dict_index_get_n_unique_in_tree(index) + 1);
853 switch (rec_get_status(rec)) {
854 case REC_STATUS_ORDINARY:
856 case REC_STATUS_NODE_PTR:
857 max_n_fields = dict_index_get_n_unique_in_tree(
860 case REC_STATUS_INFIMUM:
861 case REC_STATUS_SUPREMUM:
868 /* index->n_def == 0 for dummy indexes if !comp */
869 ut_a(!comp || index->n_def);
870 ut_a(!index->n_def || i <= max_n_fields);
873 ulint curr = rec_offs_base(offsets)[1 + i] & REC_OFFS_MASK;
879 /****************************************************************
880 Updates debug data in offsets, in order to avoid bogus
881 rec_offs_validate() failures. */
886 rec_t* rec __attribute__((unused)),
888 dict_index_t* index __attribute__((unused)),
889 /* in: record descriptor */
890 ulint* offsets __attribute__((unused)))
891 /* in: array returned by rec_get_offsets() */
894 ut_ad(rec_get_n_fields(rec, index) >= rec_offs_n_fields(offsets));
895 offsets[2] = (ulint) rec;
896 offsets[3] = (ulint) index;
897 #endif /* UNIV_DEBUG */
900 /****************************************************************
901 The following function is used to get a pointer to the nth
902 data field in a record. */
907 /* out: pointer to the field */
908 rec_t* rec, /* in: record */
909 const ulint* offsets,/* in: array returned by rec_get_offsets() */
910 ulint n, /* in: index of the field */
911 ulint* len) /* out: length of the field; UNIV_SQL_NULL
917 ut_ad(rec_offs_validate(rec, NULL, offsets));
918 ut_ad(n < rec_offs_n_fields(offsets));
921 if (UNIV_UNLIKELY(n == 0)) {
924 field = rec + (rec_offs_base(offsets)[n] & REC_OFFS_MASK);
927 length = rec_offs_base(offsets)[1 + n];
929 if (length & REC_OFFS_SQL_NULL) {
930 length = UNIV_SQL_NULL;
932 length &= REC_OFFS_MASK;
933 length -= field - rec;
940 /**********************************************************
941 Determine if the offsets are for a record in the new
947 /* out: nonzero if compact format */
948 const ulint* offsets)/* in: array returned by rec_get_offsets() */
950 ut_ad(rec_offs_validate(NULL, NULL, offsets));
951 return(*rec_offs_base(offsets) & REC_OFFS_COMPACT);
954 /**********************************************************
955 Returns nonzero if the extern bit is set in nth field of rec. */
960 /* out: nonzero if externally stored */
961 const ulint* offsets,/* in: array returned by rec_get_offsets() */
962 ulint n) /* in: nth field */
964 ut_ad(rec_offs_validate(NULL, NULL, offsets));
965 ut_ad(n < rec_offs_n_fields(offsets));
966 return(UNIV_UNLIKELY(rec_offs_base(offsets)[1 + n]
967 & REC_OFFS_EXTERNAL));
970 /**********************************************************
971 Returns nonzero if the SQL NULL bit is set in nth field of rec. */
974 rec_offs_nth_sql_null(
975 /*==================*/
976 /* out: nonzero if SQL NULL */
977 const ulint* offsets,/* in: array returned by rec_get_offsets() */
978 ulint n) /* in: nth field */
980 ut_ad(rec_offs_validate(NULL, NULL, offsets));
981 ut_ad(n < rec_offs_n_fields(offsets));
982 return(UNIV_UNLIKELY(rec_offs_base(offsets)[1 + n]
983 & REC_OFFS_SQL_NULL));
986 /**********************************************************
987 Gets the physical size of a field. */
992 /* out: length of field */
993 const ulint* offsets,/* in: array returned by rec_get_offsets() */
994 ulint n) /* in: nth field */
996 ut_ad(rec_offs_validate(NULL, NULL, offsets));
997 ut_ad(n < rec_offs_n_fields(offsets));
999 return(rec_offs_base(offsets)[1 + n] & REC_OFFS_MASK);
1001 return((rec_offs_base(offsets)[1 + n] - rec_offs_base(offsets)[n])
1005 /**********************************************************
1006 Returns TRUE if the extern bit is set in any of the fields
1007 of an old-style record. */
1010 rec_offs_any_extern(
1011 /*================*/
1012 /* out: TRUE if a field is stored externally */
1013 const ulint* offsets)/* in: array returned by rec_get_offsets() */
1016 for (i = rec_offs_n_fields(offsets); i--; ) {
1017 if (rec_offs_nth_extern(offsets, i)) {
1024 #if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
1025 /********************************************************
1026 Determine if the offsets are for a record containing null BLOB pointers. */
1029 rec_offs_any_null_extern(
1030 /*=====================*/
1031 /* out: first field containing
1032 a null BLOB pointer,
1033 or NULL if none found */
1034 rec_t* rec, /*!< in: record */
1035 const ulint* offsets) /*!< in: rec_get_offsets(rec) */
1038 ut_ad(rec_offs_validate(rec, NULL, offsets));
1040 for (i = 0; i < rec_offs_n_fields(offsets); i++) {
1041 if (rec_offs_nth_extern(offsets, i)) {
1044 = rec_get_nth_field(rec, offsets, i, &len);
1046 ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
1047 if (!memcmp(field + len
1048 - BTR_EXTERN_FIELD_REF_SIZE,
1050 BTR_EXTERN_FIELD_REF_SIZE)) {
1058 #endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
1060 /***************************************************************
1061 Sets the value of the ith field extern storage bit. */
1064 rec_set_nth_field_extern_bit(
1065 /*=========================*/
1066 rec_t* rec, /* in: record */
1067 dict_index_t* index, /* in: record descriptor */
1068 ulint i, /* in: ith field */
1069 ibool val, /* in: value to set */
1070 mtr_t* mtr) /* in: mtr holding an X-latch to the page
1071 where rec is, or NULL; in the NULL case
1072 we do not write to log about the change */
1074 if (dict_table_is_comp(index->table)) {
1075 rec_set_nth_field_extern_bit_new(rec, index, i, val, mtr);
1077 rec_set_nth_field_extern_bit_old(rec, i, val, mtr);
1081 /**********************************************************
1082 Returns the offset of n - 1th field end if the record is stored in the 1-byte
1083 offsets form. If the field is SQL null, the flag is ORed in the returned
1084 value. This function and the 2-byte counterpart are defined here because the
1085 C-compiler was not able to sum negative and positive constant offsets, and
1086 warned of constant arithmetic overflow within the compiler. */
1089 rec_1_get_prev_field_end_info(
1090 /*==========================*/
1091 /* out: offset of the start of the PREVIOUS field, SQL
1093 rec_t* rec, /* in: record */
1094 ulint n) /* in: field index */
1096 ut_ad(rec_get_1byte_offs_flag(rec));
1097 ut_ad(n <= rec_get_n_fields_old(rec));
1099 return(mach_read_from_1(rec - (REC_N_OLD_EXTRA_BYTES + n)));
1102 /**********************************************************
1103 Returns the offset of n - 1th field end if the record is stored in the 2-byte
1104 offsets form. If the field is SQL null, the flag is ORed in the returned
1108 rec_2_get_prev_field_end_info(
1109 /*==========================*/
1110 /* out: offset of the start of the PREVIOUS field, SQL
1112 rec_t* rec, /* in: record */
1113 ulint n) /* in: field index */
1115 ut_ad(!rec_get_1byte_offs_flag(rec));
1116 ut_ad(n <= rec_get_n_fields_old(rec));
1118 return(mach_read_from_2(rec - (REC_N_OLD_EXTRA_BYTES + 2 * n)));
1121 /**********************************************************
1122 Sets the field end info for the nth field if the record is stored in the
1126 rec_1_set_field_end_info(
1127 /*=====================*/
1128 rec_t* rec, /* in: record */
1129 ulint n, /* in: field index */
1130 ulint info) /* in: value to set */
1132 ut_ad(rec_get_1byte_offs_flag(rec));
1133 ut_ad(n < rec_get_n_fields_old(rec));
1135 mach_write_to_1(rec - (REC_N_OLD_EXTRA_BYTES + n + 1), info);
1138 /**********************************************************
1139 Sets the field end info for the nth field if the record is stored in the
1143 rec_2_set_field_end_info(
1144 /*=====================*/
1145 rec_t* rec, /* in: record */
1146 ulint n, /* in: field index */
1147 ulint info) /* in: value to set */
1149 ut_ad(!rec_get_1byte_offs_flag(rec));
1150 ut_ad(n < rec_get_n_fields_old(rec));
1152 mach_write_to_2(rec - (REC_N_OLD_EXTRA_BYTES + 2 * n + 2), info);
1155 /**********************************************************
1156 Returns the offset of nth field start if the record is stored in the 1-byte
1160 rec_1_get_field_start_offs(
1161 /*=======================*/
1162 /* out: offset of the start of the field */
1163 rec_t* rec, /* in: record */
1164 ulint n) /* in: field index */
1166 ut_ad(rec_get_1byte_offs_flag(rec));
1167 ut_ad(n <= rec_get_n_fields_old(rec));
1174 return(rec_1_get_prev_field_end_info(rec, n)
1175 & ~REC_1BYTE_SQL_NULL_MASK);
1178 /**********************************************************
1179 Returns the offset of nth field start if the record is stored in the 2-byte
1183 rec_2_get_field_start_offs(
1184 /*=======================*/
1185 /* out: offset of the start of the field */
1186 rec_t* rec, /* in: record */
1187 ulint n) /* in: field index */
1189 ut_ad(!rec_get_1byte_offs_flag(rec));
1190 ut_ad(n <= rec_get_n_fields_old(rec));
1197 return(rec_2_get_prev_field_end_info(rec, n)
1198 & ~(REC_2BYTE_SQL_NULL_MASK | REC_2BYTE_EXTERN_MASK));
1201 /**********************************************************
1202 The following function is used to read the offset of the start of a data field
1203 in the record. The start of an SQL null field is the end offset of the
1204 previous non-null field, or 0, if none exists. If n is the number of the last
1205 field + 1, then the end offset of the last field is returned. */
1208 rec_get_field_start_offs(
1209 /*=====================*/
1210 /* out: offset of the start of the field */
1211 rec_t* rec, /* in: record */
1212 ulint n) /* in: field index */
1215 ut_ad(n <= rec_get_n_fields_old(rec));
1222 if (rec_get_1byte_offs_flag(rec)) {
1224 return(rec_1_get_field_start_offs(rec, n));
1227 return(rec_2_get_field_start_offs(rec, n));
1230 /****************************************************************
1231 Gets the physical size of an old-style field.
1232 Also an SQL null may have a field of size > 0,
1233 if the data type is of a fixed size. */
1236 rec_get_nth_field_size(
1237 /*===================*/
1238 /* out: field size in bytes */
1239 rec_t* rec, /* in: record */
1240 ulint n) /* in: index of the field */
1245 os = rec_get_field_start_offs(rec, n);
1246 next_os = rec_get_field_start_offs(rec, n + 1);
1248 ut_ad(next_os - os < UNIV_PAGE_SIZE);
1250 return(next_os - os);
1253 /***************************************************************
1254 This is used to modify the value of an already existing field in a record.
1255 The previous value must have exactly the same size as the new value. If len
1256 is UNIV_SQL_NULL then the field is treated as an SQL null.
1257 For records in ROW_FORMAT=COMPACT (new-style records), len must not be
1258 UNIV_SQL_NULL unless the field already is SQL null. */
1263 rec_t* rec, /* in: record */
1264 const ulint* offsets,/* in: array returned by rec_get_offsets() */
1265 ulint n, /* in: index number of the field */
1266 const void* data, /* in: pointer to the data
1268 ulint len) /* in: length of the data or UNIV_SQL_NULL */
1274 ut_ad(rec_offs_validate(rec, NULL, offsets));
1276 if (UNIV_UNLIKELY(len == UNIV_SQL_NULL)) {
1277 if (!rec_offs_nth_sql_null(offsets, n)) {
1278 ut_a(!rec_offs_comp(offsets));
1279 rec_set_nth_field_sql_null(rec, n);
1285 data2 = rec_get_nth_field(rec, offsets, n, &len2);
1286 if (len2 == UNIV_SQL_NULL) {
1287 ut_ad(!rec_offs_comp(offsets));
1288 rec_set_nth_field_null_bit(rec, n, FALSE);
1289 ut_ad(len == rec_get_nth_field_size(rec, n));
1294 ut_memcpy(data2, data, len);
1297 /**************************************************************
1298 The following function returns the data size of an old-style physical
1299 record, that is the sum of field lengths. SQL null fields
1300 are counted as length 0 fields. The value returned by the function
1301 is the distance from record origin to record end in bytes. */
1304 rec_get_data_size_old(
1305 /*==================*/
1307 rec_t* rec) /* in: physical record */
1311 return(rec_get_field_start_offs(rec, rec_get_n_fields_old(rec)));
1314 /**************************************************************
1315 The following function sets the number of fields in offsets. */
1318 rec_offs_set_n_fields(
1319 /*==================*/
1320 ulint* offsets, /* in/out: array returned by
1321 rec_get_offsets() */
1322 ulint n_fields) /* in: number of fields */
1325 ut_ad(n_fields > 0);
1326 ut_ad(n_fields <= REC_MAX_N_FIELDS);
1327 ut_ad(n_fields + REC_OFFS_HEADER_SIZE
1328 <= rec_offs_get_n_alloc(offsets));
1329 offsets[1] = n_fields;
1332 /**************************************************************
1333 The following function returns the data size of a physical
1334 record, that is the sum of field lengths. SQL null fields
1335 are counted as length 0 fields. The value returned by the function
1336 is the distance from record origin to record end in bytes. */
1342 const ulint* offsets)/* in: array returned by rec_get_offsets() */
1346 ut_ad(rec_offs_validate(NULL, NULL, offsets));
1347 size = rec_offs_base(offsets)[rec_offs_n_fields(offsets)]
1349 ut_ad(size < UNIV_PAGE_SIZE);
1353 /**************************************************************
1354 Returns the total size of record minus data size of record. The value
1355 returned by the function is the distance from record start to record origin
1359 rec_offs_extra_size(
1360 /*================*/
1362 const ulint* offsets)/* in: array returned by rec_get_offsets() */
1365 ut_ad(rec_offs_validate(NULL, NULL, offsets));
1366 size = *rec_offs_base(offsets) & ~REC_OFFS_COMPACT;
1367 ut_ad(size < UNIV_PAGE_SIZE);
1371 /**************************************************************
1372 Returns the total size of a physical record. */
1378 const ulint* offsets)/* in: array returned by rec_get_offsets() */
1380 return(rec_offs_data_size(offsets) + rec_offs_extra_size(offsets));
1383 /**************************************************************
1384 Returns a pointer to the end of the record. */
1389 /* out: pointer to end */
1390 rec_t* rec, /* in: pointer to record */
1391 const ulint* offsets)/* in: array returned by rec_get_offsets() */
1393 return(rec + rec_offs_data_size(offsets));
1396 /**************************************************************
1397 Returns a pointer to the start of the record. */
1402 /* out: pointer to start */
1403 rec_t* rec, /* in: pointer to record */
1404 const ulint* offsets)/* in: array returned by rec_get_offsets() */
1406 return(rec - rec_offs_extra_size(offsets));
1409 /*******************************************************************
1410 Copies a physical record to a buffer. */
1415 /* out: pointer to the origin of the copy */
1416 void* buf, /* in: buffer */
1417 const rec_t* rec, /* in: physical record */
1418 const ulint* offsets)/* in: array returned by rec_get_offsets() */
1424 ut_ad(rec_offs_validate((rec_t*) rec, NULL, offsets));
1425 ut_ad(rec_validate((rec_t*) rec, offsets));
1427 extra_len = rec_offs_extra_size(offsets);
1428 data_len = rec_offs_data_size(offsets);
1430 ut_memcpy(buf, rec - extra_len, extra_len + data_len);
1432 return((byte*)buf + extra_len);
1435 /**************************************************************
1436 Returns the extra size of an old-style physical record if we know its
1437 data size and number of fields. */
1440 rec_get_converted_extra_size(
1441 /*=========================*/
1442 /* out: extra size */
1443 ulint data_size, /* in: data size */
1444 ulint n_fields) /* in: number of fields */
1446 if (data_size <= REC_1BYTE_OFFS_LIMIT) {
1448 return(REC_N_OLD_EXTRA_BYTES + n_fields);
1451 return(REC_N_OLD_EXTRA_BYTES + 2 * n_fields);
1454 /**************************************************************
1455 The following function returns the size of a data tuple when converted to
1456 a new-style physical record. */
1459 rec_get_converted_size_new(
1460 /*=======================*/
1462 dict_index_t* index, /* in: record descriptor */
1463 dtuple_t* dtuple);/* in: data tuple */
1464 /**************************************************************
1465 The following function returns the size of a data tuple when converted to
1466 a physical record. */
1469 rec_get_converted_size(
1470 /*===================*/
1472 dict_index_t* index, /* in: record descriptor */
1473 dtuple_t* dtuple) /* in: data tuple */
1480 ut_ad(dtuple_check_typed(dtuple));
1482 ut_ad(index->type & DICT_UNIVERSAL
1483 || dtuple_get_n_fields(dtuple)
1484 == (((dtuple_get_info_bits(dtuple) & REC_NEW_STATUS_MASK)
1485 == REC_STATUS_NODE_PTR)
1486 ? dict_index_get_n_unique_in_tree(index) + 1
1487 : dict_index_get_n_fields(index)));
1489 if (dict_table_is_comp(index->table)) {
1490 return(rec_get_converted_size_new(index, dtuple));
1493 data_size = dtuple_get_data_size(dtuple);
1495 extra_size = rec_get_converted_extra_size(
1496 data_size, dtuple_get_n_fields(dtuple));
1498 return(data_size + extra_size);
1501 /****************************************************************
1502 Folds a prefix of a physical record to a ulint. Folds only existing fields,
1503 that is, checks that we do not run out of the record. */
1508 /* out: the folded value */
1509 rec_t* rec, /* in: the physical record */
1510 const ulint* offsets, /* in: array returned by
1511 rec_get_offsets() */
1512 ulint n_fields, /* in: number of complete
1514 ulint n_bytes, /* in: number of bytes to fold
1515 in an incomplete last field */
1516 dulint tree_id) /* in: index tree id */
1524 ut_ad(rec_offs_validate(rec, NULL, offsets));
1525 ut_ad(rec_validate((rec_t*) rec, offsets));
1526 ut_ad(n_fields + n_bytes > 0);
1528 n_fields_rec = rec_offs_n_fields(offsets);
1529 ut_ad(n_fields <= n_fields_rec);
1530 ut_ad(n_fields < n_fields_rec || n_bytes == 0);
1532 if (n_fields > n_fields_rec) {
1533 n_fields = n_fields_rec;
1536 if (n_fields == n_fields_rec) {
1540 fold = ut_fold_dulint(tree_id);
1542 for (i = 0; i < n_fields; i++) {
1543 data = rec_get_nth_field(rec, offsets, i, &len);
1545 if (len != UNIV_SQL_NULL) {
1546 fold = ut_fold_ulint_pair(fold,
1547 ut_fold_binary(data, len));
1552 data = rec_get_nth_field(rec, offsets, i, &len);
1554 if (len != UNIV_SQL_NULL) {
1555 if (len > n_bytes) {
1559 fold = ut_fold_ulint_pair(fold,
1560 ut_fold_binary(data, len));