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