1/*****************************************************************************
2
3Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
4
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License, version 2.0,
7as published by the Free Software Foundation.
8
9This program is also distributed with certain software (including
10but not limited to OpenSSL) that is licensed under separate terms,
11as designated in a particular file or component or in included license
12documentation.  The authors of MySQL hereby grant you an additional
13permission to link the program and your derivative works with the
14separately licensed software that they have included with MySQL.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19GNU General Public License, version 2.0, for more details.
20
21You should have received a copy of the GNU General Public License along with
22this program; if not, write to the Free Software Foundation, Inc.,
2351 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
24
25*****************************************************************************/
26
27/********************************************************************//**
28@file include/rem0rec.ic
29Record manager
30
31Created 5/30/1994 Heikki Tuuri
32*************************************************************************/
33
34#include "mach0data.h"
35#include "ut0byte.h"
36#include "dict0dict.h"
37#include "btr0types.h"
38
39/* Compact flag ORed to the extra size returned by rec_get_offsets() */
40#define REC_OFFS_COMPACT	((ulint) 1 << 31)
41/* SQL NULL flag in offsets returned by rec_get_offsets() */
42#define REC_OFFS_SQL_NULL	((ulint) 1 << 31)
43/* External flag in offsets returned by rec_get_offsets() */
44#define REC_OFFS_EXTERNAL	((ulint) 1 << 30)
45/* Mask for offsets returned by rec_get_offsets() */
46#define REC_OFFS_MASK		(REC_OFFS_EXTERNAL - 1)
47
48/* Offsets of the bit-fields in an old-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 pointer to next record
54			2	8 bits pointer to next record
55			3	1 bit short flag
56				7 bits number of fields
57			4	3 bits number of fields
58				5 bits heap number
59			5	8 bits heap number
60			6	4 bits n_owned
61				4 bits info bits
62*/
63
64/* Offsets of the bit-fields in a new-style record. NOTE! In the table the
65most significant bytes and bits are written below less significant.
66
67	(1) byte offset		(2) bit usage within byte
68	downward from
69	origin ->	1	8 bits relative offset of next record
70			2	8 bits relative offset of next record
71				  the relative offset is an unsigned 16-bit
72				  integer:
73				  (offset_of_next_record
74				   - offset_of_this_record) mod 64Ki,
75				  where mod is the modulo as a non-negative
76				  number;
77				  we can calculate the offset of the next
78				  record with the formula:
79				  relative_offset + offset_of_this_record
80				  mod UNIV_PAGE_SIZE
81			3	3 bits status:
82					000=conventional record
83					001=node pointer record (inside B-tree)
84					010=infimum record
85					011=supremum record
86					1xx=reserved
87				5 bits heap number
88			4	8 bits heap number
89			5	4 bits n_owned
90				4 bits info bits
91*/
92
93/* We list the byte offsets from the origin of the record, the mask,
94and the shift needed to obtain each bit-field of the record. */
95
96#define REC_NEXT		2
97#define REC_NEXT_MASK		0xFFFFUL
98#define REC_NEXT_SHIFT		0
99
100#define REC_OLD_SHORT		3	/* This is single byte bit-field */
101#define REC_OLD_SHORT_MASK	0x1UL
102#define REC_OLD_SHORT_SHIFT	0
103
104#define REC_OLD_N_FIELDS	4
105#define REC_OLD_N_FIELDS_MASK	0x7FEUL
106#define REC_OLD_N_FIELDS_SHIFT	1
107
108#define REC_NEW_STATUS		3	/* This is single byte bit-field */
109#define REC_NEW_STATUS_MASK	0x7UL
110#define REC_NEW_STATUS_SHIFT	0
111
112#define REC_OLD_HEAP_NO		5
113#define REC_HEAP_NO_MASK	0xFFF8UL
114#if 0 /* defined in rem0rec.h for use of page0zip.cc */
115#define REC_NEW_HEAP_NO		4
116#define	REC_HEAP_NO_SHIFT	3
117#endif
118
119#define REC_OLD_N_OWNED		6	/* This is single byte bit-field */
120#define REC_NEW_N_OWNED		5	/* This is single byte bit-field */
121#define	REC_N_OWNED_MASK	0xFUL
122#define REC_N_OWNED_SHIFT	0
123
124#define REC_OLD_INFO_BITS	6	/* This is single byte bit-field */
125#define REC_NEW_INFO_BITS	5	/* This is single byte bit-field */
126#define	REC_INFO_BITS_MASK	0xF0UL
127#define REC_INFO_BITS_SHIFT	0
128
129#if REC_OLD_SHORT_MASK << (8 * (REC_OLD_SHORT - 3)) \
130		^ REC_OLD_N_FIELDS_MASK << (8 * (REC_OLD_N_FIELDS - 4)) \
131		^ REC_HEAP_NO_MASK << (8 * (REC_OLD_HEAP_NO - 4)) \
132		^ REC_N_OWNED_MASK << (8 * (REC_OLD_N_OWNED - 3)) \
133		^ REC_INFO_BITS_MASK << (8 * (REC_OLD_INFO_BITS - 3)) \
134		^ 0xFFFFFFFFUL
135# error "sum of old-style masks != 0xFFFFFFFFUL"
136#endif
137#if REC_NEW_STATUS_MASK << (8 * (REC_NEW_STATUS - 3)) \
138		^ REC_HEAP_NO_MASK << (8 * (REC_NEW_HEAP_NO - 4)) \
139		^ REC_N_OWNED_MASK << (8 * (REC_NEW_N_OWNED - 3)) \
140		^ REC_INFO_BITS_MASK << (8 * (REC_NEW_INFO_BITS - 3)) \
141		^ 0xFFFFFFUL
142# error "sum of new-style masks != 0xFFFFFFUL"
143#endif
144
145/***********************************************************//**
146Sets the value of the ith field SQL null bit of an old-style record. */
147UNIV_INTERN
148void
149rec_set_nth_field_null_bit(
150/*=======================*/
151	rec_t*	rec,	/*!< in: record */
152	ulint	i,	/*!< in: ith field */
153	ibool	val);	/*!< in: value to set */
154/***********************************************************//**
155Sets an old-style record field to SQL null.
156The physical size of the field is not changed. */
157UNIV_INTERN
158void
159rec_set_nth_field_sql_null(
160/*=======================*/
161	rec_t*	rec,	/*!< in: record */
162	ulint	n);	/*!< in: index of the field */
163
164/******************************************************//**
165Gets a bit field from within 1 byte. */
166UNIV_INLINE
167ulint
168rec_get_bit_field_1(
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_1(rec - offs) & mask) >> shift);
178}
179
180/******************************************************//**
181Sets a bit field within 1 byte. */
182UNIV_INLINE
183void
184rec_set_bit_field_1(
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);
195	ut_ad(mask <= 0xFFUL);
196	ut_ad(((mask >> shift) << shift) == mask);
197	ut_ad(((val << shift) & mask) == (val << shift));
198
199	mach_write_to_1(rec - offs,
200			(mach_read_from_1(rec - offs) & ~mask)
201			| (val << shift));
202}
203
204/******************************************************//**
205Gets a bit field from within 2 bytes. */
206UNIV_INLINE
207ulint
208rec_get_bit_field_2(
209/*================*/
210	const rec_t*	rec,	/*!< in: pointer to record origin */
211	ulint		offs,	/*!< in: offset from the origin down */
212	ulint		mask,	/*!< in: mask used to filter bits */
213	ulint		shift)	/*!< in: shift right applied after masking */
214{
215	ut_ad(rec);
216
217	return((mach_read_from_2(rec - offs) & mask) >> shift);
218}
219
220/******************************************************//**
221Sets a bit field within 2 bytes. */
222UNIV_INLINE
223void
224rec_set_bit_field_2(
225/*================*/
226	rec_t*	rec,	/*!< in: pointer to record origin */
227	ulint	val,	/*!< in: value to set */
228	ulint	offs,	/*!< in: offset from the origin down */
229	ulint	mask,	/*!< in: mask used to filter bits */
230	ulint	shift)	/*!< in: shift right applied after masking */
231{
232	ut_ad(rec);
233	ut_ad(offs <= REC_N_OLD_EXTRA_BYTES);
234	ut_ad(mask > 0xFFUL);
235	ut_ad(mask <= 0xFFFFUL);
236	ut_ad((mask >> shift) & 1);
237	ut_ad(0 == ((mask >> shift) & ((mask >> shift) + 1)));
238	ut_ad(((mask >> shift) << shift) == mask);
239	ut_ad(((val << shift) & mask) == (val << shift));
240
241	mach_write_to_2(rec - offs,
242			(mach_read_from_2(rec - offs) & ~mask)
243			| (val << shift));
244}
245
246/******************************************************//**
247The following function is used to get the pointer of the next chained record
248on the same page.
249@return	pointer to the next chained record, or NULL if none */
250UNIV_INLINE
251const rec_t*
252rec_get_next_ptr_const(
253/*===================*/
254	const rec_t*	rec,	/*!< in: physical record */
255	ulint		comp)	/*!< in: nonzero=compact page format */
256{
257	ulint	field_value;
258
259	ut_ad(REC_NEXT_MASK == 0xFFFFUL);
260	ut_ad(REC_NEXT_SHIFT == 0);
261
262	field_value = mach_read_from_2(rec - REC_NEXT);
263
264	if (field_value == 0) {
265
266		return(NULL);
267	}
268
269	if (comp) {
270#if UNIV_PAGE_SIZE_MAX <= 32768
271		/* Note that for 64 KiB pages, field_value can 'wrap around'
272		and the debug assertion is not valid */
273
274		/* In the following assertion, field_value is interpreted
275		as signed 16-bit integer in 2's complement arithmetics.
276		If all platforms defined int16_t in the standard headers,
277		the expression could be written simpler as
278		(int16_t) field_value + ut_align_offset(...) < UNIV_PAGE_SIZE
279		*/
280		ut_ad((field_value >= 32768
281		       ? field_value - 65536
282		       : field_value)
283		      + ut_align_offset(rec, UNIV_PAGE_SIZE)
284		      < UNIV_PAGE_SIZE);
285#endif
286		/* There must be at least REC_N_NEW_EXTRA_BYTES + 1
287		between each record. */
288		ut_ad((field_value > REC_N_NEW_EXTRA_BYTES
289		       && field_value < 32768)
290		      || field_value < (uint16) -REC_N_NEW_EXTRA_BYTES);
291
292		return((byte*) ut_align_down(rec, UNIV_PAGE_SIZE)
293		       + ut_align_offset(rec + field_value, UNIV_PAGE_SIZE));
294	} else {
295		ut_ad(field_value < UNIV_PAGE_SIZE);
296
297		return((byte*) ut_align_down(rec, UNIV_PAGE_SIZE)
298		       + field_value);
299	}
300}
301
302/******************************************************//**
303The following function is used to get the pointer of the next chained record
304on the same page.
305@return	pointer to the next chained record, or NULL if none */
306UNIV_INLINE
307rec_t*
308rec_get_next_ptr(
309/*=============*/
310	rec_t*	rec,	/*!< in: physical record */
311	ulint	comp)	/*!< in: nonzero=compact page format */
312{
313	return(const_cast<rec_t*>(rec_get_next_ptr_const(rec, comp)));
314}
315
316/******************************************************//**
317The following function is used to get the offset of the next chained record
318on the same page.
319@return	the page offset of the next chained record, or 0 if none */
320UNIV_INLINE
321ulint
322rec_get_next_offs(
323/*==============*/
324	const rec_t*	rec,	/*!< in: physical record */
325	ulint		comp)	/*!< in: nonzero=compact page format */
326{
327	ulint	field_value;
328#if REC_NEXT_MASK != 0xFFFFUL
329# error "REC_NEXT_MASK != 0xFFFFUL"
330#endif
331#if REC_NEXT_SHIFT
332# error "REC_NEXT_SHIFT != 0"
333#endif
334
335	field_value = mach_read_from_2(rec - REC_NEXT);
336
337	if (comp) {
338#if UNIV_PAGE_SIZE_MAX <= 32768
339		/* Note that for 64 KiB pages, field_value can 'wrap around'
340		and the debug assertion is not valid */
341
342		/* In the following assertion, field_value is interpreted
343		as signed 16-bit integer in 2's complement arithmetics.
344		If all platforms defined int16_t in the standard headers,
345		the expression could be written simpler as
346		(int16_t) field_value + ut_align_offset(...) < UNIV_PAGE_SIZE
347		*/
348		ut_ad((field_value >= 32768
349		       ? field_value - 65536
350		       : field_value)
351		      + ut_align_offset(rec, UNIV_PAGE_SIZE)
352		      < UNIV_PAGE_SIZE);
353#endif
354		if (field_value == 0) {
355
356			return(0);
357		}
358
359		/* There must be at least REC_N_NEW_EXTRA_BYTES + 1
360		between each record. */
361		ut_ad((field_value > REC_N_NEW_EXTRA_BYTES
362		       && field_value < 32768)
363		      || field_value < (uint16) -REC_N_NEW_EXTRA_BYTES);
364
365		return(ut_align_offset(rec + field_value, UNIV_PAGE_SIZE));
366	} else {
367		ut_ad(field_value < UNIV_PAGE_SIZE);
368
369		return(field_value);
370	}
371}
372
373/******************************************************//**
374The following function is used to set the next record offset field
375of an old-style record. */
376UNIV_INLINE
377void
378rec_set_next_offs_old(
379/*==================*/
380	rec_t*	rec,	/*!< in: old-style physical record */
381	ulint	next)	/*!< in: offset of the next record */
382{
383	ut_ad(rec);
384	ut_ad(UNIV_PAGE_SIZE > next);
385#if REC_NEXT_MASK != 0xFFFFUL
386# error "REC_NEXT_MASK != 0xFFFFUL"
387#endif
388#if REC_NEXT_SHIFT
389# error "REC_NEXT_SHIFT != 0"
390#endif
391
392	mach_write_to_2(rec - REC_NEXT, next);
393}
394
395/******************************************************//**
396The following function is used to set the next record offset field
397of a new-style record. */
398UNIV_INLINE
399void
400rec_set_next_offs_new(
401/*==================*/
402	rec_t*	rec,	/*!< in/out: new-style physical record */
403	ulint	next)	/*!< in: offset of the next record */
404{
405	ulint	field_value;
406
407	ut_ad(rec);
408	ut_ad(UNIV_PAGE_SIZE > next);
409
410	if (!next) {
411		field_value = 0;
412	} else {
413		/* The following two statements calculate
414		next - offset_of_rec mod 64Ki, where mod is the modulo
415		as a non-negative number */
416
417		field_value = (ulint)
418			((lint) next
419			 - (lint) ut_align_offset(rec, UNIV_PAGE_SIZE));
420		field_value &= REC_NEXT_MASK;
421	}
422
423	mach_write_to_2(rec - REC_NEXT, field_value);
424}
425
426/******************************************************//**
427The following function is used to get the number of fields
428in an old-style record.
429@return	number of data fields */
430UNIV_INLINE
431ulint
432rec_get_n_fields_old(
433/*=================*/
434	const rec_t*	rec)	/*!< in: physical record */
435{
436	ulint	ret;
437
438	ut_ad(rec);
439
440	ret = rec_get_bit_field_2(rec, REC_OLD_N_FIELDS,
441				  REC_OLD_N_FIELDS_MASK,
442				  REC_OLD_N_FIELDS_SHIFT);
443	ut_ad(ret <= REC_MAX_N_FIELDS);
444	ut_ad(ret > 0);
445
446	return(ret);
447}
448
449/******************************************************//**
450The following function is used to set the number of fields
451in an old-style record. */
452UNIV_INLINE
453void
454rec_set_n_fields_old(
455/*=================*/
456	rec_t*	rec,		/*!< in: physical record */
457	ulint	n_fields)	/*!< in: the number of fields */
458{
459	ut_ad(rec);
460	ut_ad(n_fields <= REC_MAX_N_FIELDS);
461	ut_ad(n_fields > 0);
462
463	rec_set_bit_field_2(rec, n_fields, REC_OLD_N_FIELDS,
464			    REC_OLD_N_FIELDS_MASK, REC_OLD_N_FIELDS_SHIFT);
465}
466
467/******************************************************//**
468The following function retrieves the status bits of a new-style record.
469@return	status bits */
470UNIV_INLINE
471ulint
472rec_get_status(
473/*===========*/
474	const rec_t*	rec)	/*!< in: physical record */
475{
476	ulint	ret;
477
478	ut_ad(rec);
479
480	ret = rec_get_bit_field_1(rec, REC_NEW_STATUS,
481				  REC_NEW_STATUS_MASK, REC_NEW_STATUS_SHIFT);
482	ut_ad((ret & ~REC_NEW_STATUS_MASK) == 0);
483
484	return(ret);
485}
486
487/******************************************************//**
488The following function is used to get the number of fields
489in a record.
490@return	number of data fields */
491UNIV_INLINE
492ulint
493rec_get_n_fields(
494/*=============*/
495	const rec_t*		rec,	/*!< in: physical record */
496	const dict_index_t*	index)	/*!< in: record descriptor */
497{
498	ut_ad(rec);
499	ut_ad(index);
500
501	if (!dict_table_is_comp(index->table)) {
502		return(rec_get_n_fields_old(rec));
503	}
504
505	switch (rec_get_status(rec)) {
506	case REC_STATUS_ORDINARY:
507		return(dict_index_get_n_fields(index));
508	case REC_STATUS_NODE_PTR:
509		return(dict_index_get_n_unique_in_tree(index) + 1);
510	case REC_STATUS_INFIMUM:
511	case REC_STATUS_SUPREMUM:
512		return(1);
513	default:
514		ut_error;
515		return(ULINT_UNDEFINED);
516	}
517}
518
519/******************************************************//**
520The following function is used to get the number of records owned by the
521previous directory record.
522@return	number of owned records */
523UNIV_INLINE
524ulint
525rec_get_n_owned_old(
526/*================*/
527	const rec_t*	rec)	/*!< in: old-style physical record */
528{
529	return(rec_get_bit_field_1(rec, REC_OLD_N_OWNED,
530				   REC_N_OWNED_MASK, REC_N_OWNED_SHIFT));
531}
532
533/******************************************************//**
534The following function is used to set the number of owned records. */
535UNIV_INLINE
536void
537rec_set_n_owned_old(
538/*================*/
539	rec_t*	rec,		/*!< in: old-style physical record */
540	ulint	n_owned)	/*!< in: the number of owned */
541{
542	rec_set_bit_field_1(rec, n_owned, REC_OLD_N_OWNED,
543			    REC_N_OWNED_MASK, REC_N_OWNED_SHIFT);
544}
545
546/******************************************************//**
547The following function is used to get the number of records owned by the
548previous directory record.
549@return	number of owned records */
550UNIV_INLINE
551ulint
552rec_get_n_owned_new(
553/*================*/
554	const rec_t*	rec)	/*!< in: new-style physical record */
555{
556	return(rec_get_bit_field_1(rec, REC_NEW_N_OWNED,
557				   REC_N_OWNED_MASK, REC_N_OWNED_SHIFT));
558}
559
560/******************************************************//**
561The following function is used to set the number of owned records. */
562UNIV_INLINE
563void
564rec_set_n_owned_new(
565/*================*/
566	rec_t*		rec,	/*!< in/out: new-style physical record */
567	page_zip_des_t*	page_zip,/*!< in/out: compressed page, or NULL */
568	ulint		n_owned)/*!< in: the number of owned */
569{
570	rec_set_bit_field_1(rec, n_owned, REC_NEW_N_OWNED,
571			    REC_N_OWNED_MASK, REC_N_OWNED_SHIFT);
572	if (page_zip && rec_get_status(rec) != REC_STATUS_SUPREMUM) {
573		page_zip_rec_set_owned(page_zip, rec, n_owned);
574	}
575}
576
577/******************************************************//**
578The following function is used to retrieve the info bits of a record.
579@return	info bits */
580UNIV_INLINE
581ulint
582rec_get_info_bits(
583/*==============*/
584	const rec_t*	rec,	/*!< in: physical record */
585	ulint		comp)	/*!< in: nonzero=compact page format */
586{
587	return(rec_get_bit_field_1(
588		       rec, comp ? REC_NEW_INFO_BITS : REC_OLD_INFO_BITS,
589		       REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT));
590}
591
592/******************************************************//**
593The following function is used to set the info bits of a record. */
594UNIV_INLINE
595void
596rec_set_info_bits_old(
597/*==================*/
598	rec_t*	rec,	/*!< in: old-style physical record */
599	ulint	bits)	/*!< in: info bits */
600{
601	rec_set_bit_field_1(rec, bits, REC_OLD_INFO_BITS,
602			    REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT);
603}
604/******************************************************//**
605The following function is used to set the info bits of a record. */
606UNIV_INLINE
607void
608rec_set_info_bits_new(
609/*==================*/
610	rec_t*	rec,	/*!< in/out: new-style physical record */
611	ulint	bits)	/*!< in: info bits */
612{
613	rec_set_bit_field_1(rec, bits, REC_NEW_INFO_BITS,
614			    REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT);
615}
616
617/******************************************************//**
618The following function is used to set the status bits of a new-style record. */
619UNIV_INLINE
620void
621rec_set_status(
622/*===========*/
623	rec_t*	rec,	/*!< in/out: physical record */
624	ulint	bits)	/*!< in: info bits */
625{
626	rec_set_bit_field_1(rec, bits, REC_NEW_STATUS,
627			    REC_NEW_STATUS_MASK, REC_NEW_STATUS_SHIFT);
628}
629
630/******************************************************//**
631The following function is used to retrieve the info and status
632bits of a record.  (Only compact records have status bits.)
633@return	info bits */
634UNIV_INLINE
635ulint
636rec_get_info_and_status_bits(
637/*=========================*/
638	const rec_t*	rec,	/*!< in: physical record */
639	ulint		comp)	/*!< in: nonzero=compact page format */
640{
641	ulint	bits;
642#if (REC_NEW_STATUS_MASK >> REC_NEW_STATUS_SHIFT) \
643& (REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT)
644# error "REC_NEW_STATUS_MASK and REC_INFO_BITS_MASK overlap"
645#endif
646	if (comp) {
647		bits = rec_get_info_bits(rec, TRUE) | rec_get_status(rec);
648	} else {
649		bits = rec_get_info_bits(rec, FALSE);
650		ut_ad(!(bits & ~(REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT)));
651	}
652	return(bits);
653}
654/******************************************************//**
655The following function is used to set the info and status
656bits of a record.  (Only compact records have status bits.) */
657UNIV_INLINE
658void
659rec_set_info_and_status_bits(
660/*=========================*/
661	rec_t*	rec,	/*!< in/out: physical record */
662	ulint	bits)	/*!< in: info bits */
663{
664#if (REC_NEW_STATUS_MASK >> REC_NEW_STATUS_SHIFT) \
665& (REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT)
666# error "REC_NEW_STATUS_MASK and REC_INFO_BITS_MASK overlap"
667#endif
668	rec_set_status(rec, bits & REC_NEW_STATUS_MASK);
669	rec_set_info_bits_new(rec, bits & ~REC_NEW_STATUS_MASK);
670}
671
672/******************************************************//**
673The following function tells if record is delete marked.
674@return	nonzero if delete marked */
675UNIV_INLINE
676ulint
677rec_get_deleted_flag(
678/*=================*/
679	const rec_t*	rec,	/*!< in: physical record */
680	ulint		comp)	/*!< in: nonzero=compact page format */
681{
682	if (comp) {
683		return(rec_get_bit_field_1(rec, REC_NEW_INFO_BITS,
684					   REC_INFO_DELETED_FLAG,
685					   REC_INFO_BITS_SHIFT));
686	} else {
687		return(rec_get_bit_field_1(rec, REC_OLD_INFO_BITS,
688					   REC_INFO_DELETED_FLAG,
689					   REC_INFO_BITS_SHIFT));
690	}
691}
692
693/******************************************************//**
694The following function is used to set the deleted bit. */
695UNIV_INLINE
696void
697rec_set_deleted_flag_old(
698/*=====================*/
699	rec_t*	rec,	/*!< in: old-style physical record */
700	ulint	flag)	/*!< in: nonzero if delete marked */
701{
702	ulint	val;
703
704	val = rec_get_info_bits(rec, FALSE);
705
706	if (flag) {
707		val |= REC_INFO_DELETED_FLAG;
708	} else {
709		val &= ~REC_INFO_DELETED_FLAG;
710	}
711
712	rec_set_info_bits_old(rec, val);
713}
714
715/******************************************************//**
716The following function is used to set the deleted bit. */
717UNIV_INLINE
718void
719rec_set_deleted_flag_new(
720/*=====================*/
721	rec_t*		rec,	/*!< in/out: new-style physical record */
722	page_zip_des_t*	page_zip,/*!< in/out: compressed page, or NULL */
723	ulint		flag)	/*!< in: nonzero if delete marked */
724{
725	ulint	val;
726
727	val = rec_get_info_bits(rec, TRUE);
728
729	if (flag) {
730		val |= REC_INFO_DELETED_FLAG;
731	} else {
732		val &= ~REC_INFO_DELETED_FLAG;
733	}
734
735	rec_set_info_bits_new(rec, val);
736
737	if (page_zip) {
738		page_zip_rec_set_deleted(page_zip, rec, flag);
739	}
740}
741
742/******************************************************//**
743The following function tells if a new-style record is a node pointer.
744@return	TRUE if node pointer */
745UNIV_INLINE
746ibool
747rec_get_node_ptr_flag(
748/*==================*/
749	const rec_t*	rec)	/*!< in: physical record */
750{
751	return(REC_STATUS_NODE_PTR == rec_get_status(rec));
752}
753
754/******************************************************//**
755The following function is used to get the order number
756of an old-style record in the heap of the index page.
757@return	heap order number */
758UNIV_INLINE
759ulint
760rec_get_heap_no_old(
761/*================*/
762	const rec_t*	rec)	/*!< in: physical record */
763{
764	return(rec_get_bit_field_2(rec, REC_OLD_HEAP_NO,
765				   REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT));
766}
767
768/******************************************************//**
769The following function is used to set the heap number
770field in an old-style record. */
771UNIV_INLINE
772void
773rec_set_heap_no_old(
774/*================*/
775	rec_t*	rec,	/*!< in: physical record */
776	ulint	heap_no)/*!< in: the heap number */
777{
778	rec_set_bit_field_2(rec, heap_no, REC_OLD_HEAP_NO,
779			    REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT);
780}
781
782/******************************************************//**
783The following function is used to get the order number
784of a new-style record in the heap of the index page.
785@return	heap order number */
786UNIV_INLINE
787ulint
788rec_get_heap_no_new(
789/*================*/
790	const rec_t*	rec)	/*!< in: physical record */
791{
792	return(rec_get_bit_field_2(rec, REC_NEW_HEAP_NO,
793				   REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT));
794}
795
796/******************************************************//**
797The following function is used to set the heap number
798field in a new-style record. */
799UNIV_INLINE
800void
801rec_set_heap_no_new(
802/*================*/
803	rec_t*	rec,	/*!< in/out: physical record */
804	ulint	heap_no)/*!< in: the heap number */
805{
806	rec_set_bit_field_2(rec, heap_no, REC_NEW_HEAP_NO,
807			    REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT);
808}
809
810/******************************************************//**
811The following function is used to test whether the data offsets in the record
812are stored in one-byte or two-byte format.
813@return	TRUE if 1-byte form */
814UNIV_INLINE
815ibool
816rec_get_1byte_offs_flag(
817/*====================*/
818	const rec_t*	rec)	/*!< in: physical record */
819{
820#if TRUE != 1
821#error "TRUE != 1"
822#endif
823
824	return(rec_get_bit_field_1(rec, REC_OLD_SHORT, REC_OLD_SHORT_MASK,
825				   REC_OLD_SHORT_SHIFT));
826}
827
828/******************************************************//**
829The following function is used to set the 1-byte offsets flag. */
830UNIV_INLINE
831void
832rec_set_1byte_offs_flag(
833/*====================*/
834	rec_t*	rec,	/*!< in: physical record */
835	ibool	flag)	/*!< in: TRUE if 1byte form */
836{
837#if TRUE != 1
838#error "TRUE != 1"
839#endif
840	ut_ad(flag <= TRUE);
841
842	rec_set_bit_field_1(rec, flag, REC_OLD_SHORT, REC_OLD_SHORT_MASK,
843			    REC_OLD_SHORT_SHIFT);
844}
845
846/******************************************************//**
847Returns the offset of nth field end if the record is stored in the 1-byte
848offsets form. If the field is SQL null, the flag is ORed in the returned
849value.
850@return	offset of the start of the field, SQL null flag ORed */
851UNIV_INLINE
852ulint
853rec_1_get_field_end_info(
854/*=====================*/
855	const rec_t*	rec,	/*!< in: record */
856	ulint		n)	/*!< in: field index */
857{
858	ut_ad(rec_get_1byte_offs_flag(rec));
859	ut_ad(n < rec_get_n_fields_old(rec));
860
861	return(mach_read_from_1(rec - (REC_N_OLD_EXTRA_BYTES + n + 1)));
862}
863
864/******************************************************//**
865Returns the offset of nth field end if the record is stored in the 2-byte
866offsets form. If the field is SQL null, the flag is ORed in the returned
867value.
868@return offset of the start of the field, SQL null flag and extern
869storage flag ORed */
870UNIV_INLINE
871ulint
872rec_2_get_field_end_info(
873/*=====================*/
874	const rec_t*	rec,	/*!< in: record */
875	ulint		n)	/*!< in: field index */
876{
877	ut_ad(!rec_get_1byte_offs_flag(rec));
878	ut_ad(n < rec_get_n_fields_old(rec));
879
880	return(mach_read_from_2(rec - (REC_N_OLD_EXTRA_BYTES + 2 * n + 2)));
881}
882
883/******************************************************//**
884Returns nonzero if the field is stored off-page.
885@retval 0 if the field is stored in-page
886@retval REC_2BYTE_EXTERN_MASK if the field is stored externally */
887UNIV_INLINE
888ulint
889rec_2_is_field_extern(
890/*==================*/
891	const rec_t*	rec,	/*!< in: record */
892	ulint		n)	/*!< in: field index */
893{
894	return(rec_2_get_field_end_info(rec, n) & REC_2BYTE_EXTERN_MASK);
895}
896
897/* Get the base address of offsets.  The extra_size is stored at
898this position, and following positions hold the end offsets of
899the fields. */
900#define rec_offs_base(offsets) (offsets + REC_OFFS_HEADER_SIZE)
901
902/**********************************************************//**
903The following function returns the number of allocated elements
904for an array of offsets.
905@return	number of elements */
906UNIV_INLINE
907ulint
908rec_offs_get_n_alloc(
909/*=================*/
910	const ulint*	offsets)/*!< in: array for rec_get_offsets() */
911{
912	ulint	n_alloc;
913	ut_ad(offsets);
914	n_alloc = offsets[0];
915	ut_ad(n_alloc > REC_OFFS_HEADER_SIZE);
916	UNIV_MEM_ASSERT_W(offsets, n_alloc * sizeof *offsets);
917	return(n_alloc);
918}
919
920/**********************************************************//**
921The following function sets the number of allocated elements
922for an array of offsets. */
923UNIV_INLINE
924void
925rec_offs_set_n_alloc(
926/*=================*/
927	ulint*	offsets,	/*!< out: array for rec_get_offsets(),
928				must be allocated */
929	ulint	n_alloc)	/*!< in: number of elements */
930{
931	ut_ad(offsets);
932	ut_ad(n_alloc > REC_OFFS_HEADER_SIZE);
933	UNIV_MEM_ASSERT_AND_ALLOC(offsets, n_alloc * sizeof *offsets);
934	offsets[0] = n_alloc;
935}
936
937/**********************************************************//**
938The following function returns the number of fields in a record.
939@return	number of fields */
940UNIV_INLINE
941ulint
942rec_offs_n_fields(
943/*==============*/
944	const ulint*	offsets)/*!< in: array returned by rec_get_offsets() */
945{
946	ulint	n_fields;
947	ut_ad(offsets);
948	n_fields = offsets[1];
949	ut_ad(n_fields > 0);
950	ut_ad(n_fields <= REC_MAX_N_FIELDS);
951	ut_ad(n_fields + REC_OFFS_HEADER_SIZE
952	      <= rec_offs_get_n_alloc(offsets));
953	return(n_fields);
954}
955
956/************************************************************//**
957Validates offsets returned by rec_get_offsets().
958@return	TRUE if valid */
959UNIV_INLINE
960ibool
961rec_offs_validate(
962/*==============*/
963	const rec_t*		rec,	/*!< in: record or NULL */
964	const dict_index_t*	index,	/*!< in: record descriptor or NULL */
965	const ulint*		offsets)/*!< in: array returned by
966					rec_get_offsets() */
967{
968	ulint	i	= rec_offs_n_fields(offsets);
969	ulint	last	= ULINT_MAX;
970	ulint	comp	= *rec_offs_base(offsets) & REC_OFFS_COMPACT;
971
972	if (rec) {
973		ut_ad((ulint) rec == offsets[2]);
974		if (!comp) {
975			ut_a(rec_get_n_fields_old(rec) >= i);
976		}
977	}
978	if (index) {
979		ulint max_n_fields;
980		ut_ad((ulint) index == offsets[3]);
981		max_n_fields = ut_max(
982			dict_index_get_n_fields(index),
983			dict_index_get_n_unique_in_tree(index) + 1);
984		if (comp && rec) {
985			switch (rec_get_status(rec)) {
986			case REC_STATUS_ORDINARY:
987				break;
988			case REC_STATUS_NODE_PTR:
989				max_n_fields = dict_index_get_n_unique_in_tree(
990					index) + 1;
991				break;
992			case REC_STATUS_INFIMUM:
993			case REC_STATUS_SUPREMUM:
994				max_n_fields = 1;
995				break;
996			default:
997				ut_error;
998			}
999		}
1000		/* index->n_def == 0 for dummy indexes if !comp */
1001		ut_a(!comp || index->n_def);
1002		ut_a(!index->n_def || i <= max_n_fields);
1003	}
1004	while (i--) {
1005		ulint	curr = rec_offs_base(offsets)[1 + i] & REC_OFFS_MASK;
1006		ut_a(curr <= last);
1007		last = curr;
1008	}
1009	return(TRUE);
1010}
1011#ifdef UNIV_DEBUG
1012/************************************************************//**
1013Updates debug data in offsets, in order to avoid bogus
1014rec_offs_validate() failures. */
1015UNIV_INLINE
1016void
1017rec_offs_make_valid(
1018/*================*/
1019	const rec_t*		rec,	/*!< in: record */
1020	const dict_index_t*	index,	/*!< in: record descriptor */
1021	ulint*			offsets)/*!< in: array returned by
1022					rec_get_offsets() */
1023{
1024	ut_ad(rec);
1025	ut_ad(index);
1026	ut_ad(offsets);
1027	ut_ad(rec_get_n_fields(rec, index) >= rec_offs_n_fields(offsets));
1028	offsets[2] = (ulint) rec;
1029	offsets[3] = (ulint) index;
1030}
1031#endif /* UNIV_DEBUG */
1032
1033/************************************************************//**
1034The following function is used to get an offset to the nth
1035data field in a record.
1036@return	offset from the origin of rec */
1037UNIV_INLINE
1038ulint
1039rec_get_nth_field_offs(
1040/*===================*/
1041	const ulint*	offsets,/*!< in: array returned by rec_get_offsets() */
1042	ulint		n,	/*!< in: index of the field */
1043	ulint*		len)	/*!< out: length of the field; UNIV_SQL_NULL
1044				if SQL null */
1045{
1046	ulint	offs;
1047	ulint	length;
1048	ut_ad(n < rec_offs_n_fields(offsets));
1049	ut_ad(len);
1050
1051	if (n == 0) {
1052		offs = 0;
1053	} else {
1054		offs = rec_offs_base(offsets)[n] & REC_OFFS_MASK;
1055	}
1056
1057	length = rec_offs_base(offsets)[1 + n];
1058
1059	if (length & REC_OFFS_SQL_NULL) {
1060		length = UNIV_SQL_NULL;
1061	} else {
1062		length &= REC_OFFS_MASK;
1063		length -= offs;
1064	}
1065
1066	*len = length;
1067	return(offs);
1068}
1069
1070/******************************************************//**
1071Determine if the offsets are for a record in the new
1072compact format.
1073@return	nonzero if compact format */
1074UNIV_INLINE
1075ulint
1076rec_offs_comp(
1077/*==========*/
1078	const ulint*	offsets)/*!< in: array returned by rec_get_offsets() */
1079{
1080	ut_ad(rec_offs_validate(NULL, NULL, offsets));
1081	return(*rec_offs_base(offsets) & REC_OFFS_COMPACT);
1082}
1083
1084/******************************************************//**
1085Determine if the offsets are for a record containing
1086externally stored columns.
1087@return	nonzero if externally stored */
1088UNIV_INLINE
1089ulint
1090rec_offs_any_extern(
1091/*================*/
1092	const ulint*	offsets)/*!< in: array returned by rec_get_offsets() */
1093{
1094	ut_ad(rec_offs_validate(NULL, NULL, offsets));
1095	return(*rec_offs_base(offsets) & REC_OFFS_EXTERNAL);
1096}
1097
1098/******************************************************//**
1099Determine if the offsets are for a record containing null BLOB pointers.
1100@return	first field containing a null BLOB pointer, or NULL if none found */
1101UNIV_INLINE
1102const byte*
1103rec_offs_any_null_extern(
1104/*=====================*/
1105	const rec_t*	rec,		/*!< in: record */
1106	const ulint*	offsets)	/*!< in: rec_get_offsets(rec) */
1107{
1108	ulint	i;
1109	ut_ad(rec_offs_validate(rec, NULL, offsets));
1110
1111	if (!rec_offs_any_extern(offsets)) {
1112		return(NULL);
1113	}
1114
1115	for (i = 0; i < rec_offs_n_fields(offsets); i++) {
1116		if (rec_offs_nth_extern(offsets, i)) {
1117			ulint		len;
1118			const byte*	field
1119				= rec_get_nth_field(rec, offsets, i, &len);
1120
1121			ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
1122			if (!memcmp(field + len
1123				    - BTR_EXTERN_FIELD_REF_SIZE,
1124				    field_ref_zero,
1125				    BTR_EXTERN_FIELD_REF_SIZE)) {
1126				return(field);
1127			}
1128		}
1129	}
1130
1131	return(NULL);
1132}
1133
1134/******************************************************//**
1135Returns nonzero if the extern bit is set in nth field of rec.
1136@return	nonzero if externally stored */
1137UNIV_INLINE
1138ulint
1139rec_offs_nth_extern(
1140/*================*/
1141	const ulint*	offsets,/*!< in: array returned by rec_get_offsets() */
1142	ulint		n)	/*!< in: nth field */
1143{
1144	ut_ad(rec_offs_validate(NULL, NULL, offsets));
1145	ut_ad(n < rec_offs_n_fields(offsets));
1146	return(rec_offs_base(offsets)[1 + n] & REC_OFFS_EXTERNAL);
1147}
1148
1149/******************************************************//**
1150Returns nonzero if the SQL NULL bit is set in nth field of rec.
1151@return	nonzero if SQL NULL */
1152UNIV_INLINE
1153ulint
1154rec_offs_nth_sql_null(
1155/*==================*/
1156	const ulint*	offsets,/*!< in: array returned by rec_get_offsets() */
1157	ulint		n)	/*!< in: nth field */
1158{
1159	ut_ad(rec_offs_validate(NULL, NULL, offsets));
1160	ut_ad(n < rec_offs_n_fields(offsets));
1161	return(rec_offs_base(offsets)[1 + n] & REC_OFFS_SQL_NULL);
1162}
1163
1164/******************************************************//**
1165Gets the physical size of a field.
1166@return	length of field */
1167UNIV_INLINE
1168ulint
1169rec_offs_nth_size(
1170/*==============*/
1171	const ulint*	offsets,/*!< in: array returned by rec_get_offsets() */
1172	ulint		n)	/*!< in: nth field */
1173{
1174	ut_ad(rec_offs_validate(NULL, NULL, offsets));
1175	ut_ad(n < rec_offs_n_fields(offsets));
1176	if (!n) {
1177		return(rec_offs_base(offsets)[1 + n] & REC_OFFS_MASK);
1178	}
1179	return((rec_offs_base(offsets)[1 + n] - rec_offs_base(offsets)[n])
1180	       & REC_OFFS_MASK);
1181}
1182
1183/******************************************************//**
1184Returns the number of extern bits set in a record.
1185@return	number of externally stored fields */
1186UNIV_INLINE
1187ulint
1188rec_offs_n_extern(
1189/*==============*/
1190	const ulint*	offsets)/*!< in: array returned by rec_get_offsets() */
1191{
1192	ulint	n = 0;
1193
1194	if (rec_offs_any_extern(offsets)) {
1195		ulint	i;
1196
1197		for (i = rec_offs_n_fields(offsets); i--; ) {
1198			if (rec_offs_nth_extern(offsets, i)) {
1199				n++;
1200			}
1201		}
1202	}
1203
1204	return(n);
1205}
1206
1207/******************************************************//**
1208Returns the offset of n - 1th field end if the record is stored in the 1-byte
1209offsets form. If the field is SQL null, the flag is ORed in the returned
1210value. This function and the 2-byte counterpart are defined here because the
1211C-compiler was not able to sum negative and positive constant offsets, and
1212warned of constant arithmetic overflow within the compiler.
1213@return	offset of the start of the PREVIOUS field, SQL null flag ORed */
1214UNIV_INLINE
1215ulint
1216rec_1_get_prev_field_end_info(
1217/*==========================*/
1218	const rec_t*	rec,	/*!< in: record */
1219	ulint		n)	/*!< in: field index */
1220{
1221	ut_ad(rec_get_1byte_offs_flag(rec));
1222	ut_ad(n <= rec_get_n_fields_old(rec));
1223
1224	return(mach_read_from_1(rec - (REC_N_OLD_EXTRA_BYTES + n)));
1225}
1226
1227/******************************************************//**
1228Returns the offset of n - 1th field end if the record is stored in the 2-byte
1229offsets form. If the field is SQL null, the flag is ORed in the returned
1230value.
1231@return	offset of the start of the PREVIOUS field, SQL null flag ORed */
1232UNIV_INLINE
1233ulint
1234rec_2_get_prev_field_end_info(
1235/*==========================*/
1236	const rec_t*	rec,	/*!< in: record */
1237	ulint		n)	/*!< in: field index */
1238{
1239	ut_ad(!rec_get_1byte_offs_flag(rec));
1240	ut_ad(n <= rec_get_n_fields_old(rec));
1241
1242	return(mach_read_from_2(rec - (REC_N_OLD_EXTRA_BYTES + 2 * n)));
1243}
1244
1245/******************************************************//**
1246Sets the field end info for the nth field if the record is stored in the
12471-byte format. */
1248UNIV_INLINE
1249void
1250rec_1_set_field_end_info(
1251/*=====================*/
1252	rec_t*	rec,	/*!< in: record */
1253	ulint	n,	/*!< in: field index */
1254	ulint	info)	/*!< in: value to set */
1255{
1256	ut_ad(rec_get_1byte_offs_flag(rec));
1257	ut_ad(n < rec_get_n_fields_old(rec));
1258
1259	mach_write_to_1(rec - (REC_N_OLD_EXTRA_BYTES + n + 1), info);
1260}
1261
1262/******************************************************//**
1263Sets the field end info for the nth field if the record is stored in the
12642-byte format. */
1265UNIV_INLINE
1266void
1267rec_2_set_field_end_info(
1268/*=====================*/
1269	rec_t*	rec,	/*!< in: record */
1270	ulint	n,	/*!< in: field index */
1271	ulint	info)	/*!< in: value to set */
1272{
1273	ut_ad(!rec_get_1byte_offs_flag(rec));
1274	ut_ad(n < rec_get_n_fields_old(rec));
1275
1276	mach_write_to_2(rec - (REC_N_OLD_EXTRA_BYTES + 2 * n + 2), info);
1277}
1278
1279/******************************************************//**
1280Returns the offset of nth field start if the record is stored in the 1-byte
1281offsets form.
1282@return	offset of the start of the field */
1283UNIV_INLINE
1284ulint
1285rec_1_get_field_start_offs(
1286/*=======================*/
1287	const rec_t*	rec,	/*!< in: record */
1288	ulint		n)	/*!< in: field index */
1289{
1290	ut_ad(rec_get_1byte_offs_flag(rec));
1291	ut_ad(n <= rec_get_n_fields_old(rec));
1292
1293	if (n == 0) {
1294
1295		return(0);
1296	}
1297
1298	return(rec_1_get_prev_field_end_info(rec, n)
1299	       & ~REC_1BYTE_SQL_NULL_MASK);
1300}
1301
1302/******************************************************//**
1303Returns the offset of nth field start if the record is stored in the 2-byte
1304offsets form.
1305@return	offset of the start of the field */
1306UNIV_INLINE
1307ulint
1308rec_2_get_field_start_offs(
1309/*=======================*/
1310	const rec_t*	rec,	/*!< in: record */
1311	ulint		n)	/*!< in: field index */
1312{
1313	ut_ad(!rec_get_1byte_offs_flag(rec));
1314	ut_ad(n <= rec_get_n_fields_old(rec));
1315
1316	if (n == 0) {
1317
1318		return(0);
1319	}
1320
1321	return(rec_2_get_prev_field_end_info(rec, n)
1322	       & ~(REC_2BYTE_SQL_NULL_MASK | REC_2BYTE_EXTERN_MASK));
1323}
1324
1325/******************************************************//**
1326The following function is used to read the offset of the start of a data field
1327in the record. The start of an SQL null field is the end offset of the
1328previous non-null field, or 0, if none exists. If n is the number of the last
1329field + 1, then the end offset of the last field is returned.
1330@return	offset of the start of the field */
1331UNIV_INLINE
1332ulint
1333rec_get_field_start_offs(
1334/*=====================*/
1335	const rec_t*	rec,	/*!< in: record */
1336	ulint		n)	/*!< in: field index */
1337{
1338	ut_ad(rec);
1339	ut_ad(n <= rec_get_n_fields_old(rec));
1340
1341	if (n == 0) {
1342
1343		return(0);
1344	}
1345
1346	if (rec_get_1byte_offs_flag(rec)) {
1347
1348		return(rec_1_get_field_start_offs(rec, n));
1349	}
1350
1351	return(rec_2_get_field_start_offs(rec, n));
1352}
1353
1354/************************************************************//**
1355Gets the physical size of an old-style field.
1356Also an SQL null may have a field of size > 0,
1357if the data type is of a fixed size.
1358@return	field size in bytes */
1359UNIV_INLINE
1360ulint
1361rec_get_nth_field_size(
1362/*===================*/
1363	const rec_t*	rec,	/*!< in: record */
1364	ulint		n)	/*!< in: index of the field */
1365{
1366	ulint	os;
1367	ulint	next_os;
1368
1369	os = rec_get_field_start_offs(rec, n);
1370	next_os = rec_get_field_start_offs(rec, n + 1);
1371
1372	ut_ad(next_os - os < UNIV_PAGE_SIZE);
1373
1374	return(next_os - os);
1375}
1376
1377/***********************************************************//**
1378This is used to modify the value of an already existing field in a record.
1379The previous value must have exactly the same size as the new value. If len
1380is UNIV_SQL_NULL then the field is treated as an SQL null.
1381For records in ROW_FORMAT=COMPACT (new-style records), len must not be
1382UNIV_SQL_NULL unless the field already is SQL null. */
1383UNIV_INLINE
1384void
1385rec_set_nth_field(
1386/*==============*/
1387	rec_t*		rec,	/*!< in: record */
1388	const ulint*	offsets,/*!< in: array returned by rec_get_offsets() */
1389	ulint		n,	/*!< in: index number of the field */
1390	const void*	data,	/*!< in: pointer to the data
1391				if not SQL null */
1392	ulint		len)	/*!< in: length of the data or UNIV_SQL_NULL */
1393{
1394	byte*	data2;
1395	ulint	len2;
1396
1397	ut_ad(rec);
1398	ut_ad(rec_offs_validate(rec, NULL, offsets));
1399
1400	if (len == UNIV_SQL_NULL) {
1401		if (!rec_offs_nth_sql_null(offsets, n)) {
1402			ut_a(!rec_offs_comp(offsets));
1403			rec_set_nth_field_sql_null(rec, n);
1404		}
1405
1406		return;
1407	}
1408
1409	data2 = rec_get_nth_field(rec, offsets, n, &len2);
1410	if (len2 == UNIV_SQL_NULL) {
1411		ut_ad(!rec_offs_comp(offsets));
1412		rec_set_nth_field_null_bit(rec, n, FALSE);
1413		ut_ad(len == rec_get_nth_field_size(rec, n));
1414	} else {
1415		ut_ad(len2 == len);
1416	}
1417
1418	ut_memcpy(data2, data, len);
1419}
1420
1421/**********************************************************//**
1422The following function returns the data size of an old-style physical
1423record, that is the sum of field lengths. SQL null fields
1424are counted as length 0 fields. The value returned by the function
1425is the distance from record origin to record end in bytes.
1426@return	size */
1427UNIV_INLINE
1428ulint
1429rec_get_data_size_old(
1430/*==================*/
1431	const rec_t*	rec)	/*!< in: physical record */
1432{
1433	ut_ad(rec);
1434
1435	return(rec_get_field_start_offs(rec, rec_get_n_fields_old(rec)));
1436}
1437
1438/**********************************************************//**
1439The following function sets the number of fields in offsets. */
1440UNIV_INLINE
1441void
1442rec_offs_set_n_fields(
1443/*==================*/
1444	ulint*	offsets,	/*!< in/out: array returned by
1445				rec_get_offsets() */
1446	ulint	n_fields)	/*!< in: number of fields */
1447{
1448	ut_ad(offsets);
1449	ut_ad(n_fields > 0);
1450	ut_ad(n_fields <= REC_MAX_N_FIELDS);
1451	ut_ad(n_fields + REC_OFFS_HEADER_SIZE
1452	      <= rec_offs_get_n_alloc(offsets));
1453	offsets[1] = n_fields;
1454}
1455
1456/**********************************************************//**
1457The following function returns the data size of a physical
1458record, that is the sum of field lengths. SQL null fields
1459are counted as length 0 fields. The value returned by the function
1460is the distance from record origin to record end in bytes.
1461@return	size */
1462UNIV_INLINE
1463ulint
1464rec_offs_data_size(
1465/*===============*/
1466	const ulint*	offsets)/*!< in: array returned by rec_get_offsets() */
1467{
1468	ulint	size;
1469
1470	ut_ad(rec_offs_validate(NULL, NULL, offsets));
1471	size = rec_offs_base(offsets)[rec_offs_n_fields(offsets)]
1472		& REC_OFFS_MASK;
1473	ut_ad(size < UNIV_PAGE_SIZE);
1474	return(size);
1475}
1476
1477/**********************************************************//**
1478Returns the total size of record minus data size of record. The value
1479returned by the function is the distance from record start to record origin
1480in bytes.
1481@return	size */
1482UNIV_INLINE
1483ulint
1484rec_offs_extra_size(
1485/*================*/
1486	const ulint*	offsets)/*!< in: array returned by rec_get_offsets() */
1487{
1488	ulint	size;
1489	ut_ad(rec_offs_validate(NULL, NULL, offsets));
1490	size = *rec_offs_base(offsets) & ~(REC_OFFS_COMPACT | REC_OFFS_EXTERNAL);
1491	ut_ad(size < UNIV_PAGE_SIZE);
1492	return(size);
1493}
1494
1495/**********************************************************//**
1496Returns the total size of a physical record.
1497@return	size */
1498UNIV_INLINE
1499ulint
1500rec_offs_size(
1501/*==========*/
1502	const ulint*	offsets)/*!< in: array returned by rec_get_offsets() */
1503{
1504	return(rec_offs_data_size(offsets) + rec_offs_extra_size(offsets));
1505}
1506
1507#ifdef UNIV_DEBUG
1508/**********************************************************//**
1509Returns a pointer to the end of the record.
1510@return	pointer to end */
1511UNIV_INLINE
1512byte*
1513rec_get_end(
1514/*========*/
1515	const rec_t*	rec,	/*!< in: pointer to record */
1516	const ulint*	offsets)/*!< in: array returned by rec_get_offsets() */
1517{
1518	ut_ad(rec_offs_validate(rec, NULL, offsets));
1519	return(const_cast<rec_t*>(rec + rec_offs_data_size(offsets)));
1520}
1521
1522/**********************************************************//**
1523Returns a pointer to the start of the record.
1524@return	pointer to start */
1525UNIV_INLINE
1526byte*
1527rec_get_start(
1528/*==========*/
1529	const rec_t*	rec,	/*!< in: pointer to record */
1530	const ulint*	offsets)/*!< in: array returned by rec_get_offsets() */
1531{
1532	ut_ad(rec_offs_validate(rec, NULL, offsets));
1533	return(const_cast<rec_t*>(rec - rec_offs_extra_size(offsets)));
1534}
1535#endif /* UNIV_DEBUG */
1536
1537/***************************************************************//**
1538Copies a physical record to a buffer.
1539@return	pointer to the origin of the copy */
1540UNIV_INLINE
1541rec_t*
1542rec_copy(
1543/*=====*/
1544	void*		buf,	/*!< in: buffer */
1545	const rec_t*	rec,	/*!< in: physical record */
1546	const ulint*	offsets)/*!< in: array returned by rec_get_offsets() */
1547{
1548	ulint	extra_len;
1549	ulint	data_len;
1550
1551	ut_ad(rec != NULL);
1552	ut_ad(buf != NULL);
1553	ut_ad(rec_offs_validate(rec, NULL, offsets));
1554	ut_ad(rec_validate(rec, offsets));
1555
1556	extra_len = rec_offs_extra_size(offsets);
1557	data_len = rec_offs_data_size(offsets);
1558
1559	ut_memcpy(buf, rec - extra_len, extra_len + data_len);
1560
1561	return((byte*) buf + extra_len);
1562}
1563
1564/**********************************************************//**
1565Returns the extra size of an old-style physical record if we know its
1566data size and number of fields.
1567@return	extra size */
1568UNIV_INLINE
1569ulint
1570rec_get_converted_extra_size(
1571/*=========================*/
1572	ulint	data_size,	/*!< in: data size */
1573	ulint	n_fields,	/*!< in: number of fields */
1574	ulint	n_ext)		/*!< in: number of externally stored columns */
1575{
1576	if (!n_ext && data_size <= REC_1BYTE_OFFS_LIMIT) {
1577
1578		return(REC_N_OLD_EXTRA_BYTES + n_fields);
1579	}
1580
1581	return(REC_N_OLD_EXTRA_BYTES + 2 * n_fields);
1582}
1583
1584/**********************************************************//**
1585The following function returns the size of a data tuple when converted to
1586a physical record.
1587@return	size */
1588UNIV_INLINE
1589ulint
1590rec_get_converted_size(
1591/*===================*/
1592	dict_index_t*	index,	/*!< in: record descriptor */
1593	const dtuple_t*	dtuple,	/*!< in: data tuple */
1594	ulint		n_ext)	/*!< in: number of externally stored columns */
1595{
1596	ulint	data_size;
1597	ulint	extra_size;
1598
1599	ut_ad(index);
1600	ut_ad(dtuple);
1601	ut_ad(dtuple_check_typed(dtuple));
1602
1603	ut_ad(dict_index_is_univ(index)
1604	      || dtuple_get_n_fields(dtuple)
1605	      == (((dtuple_get_info_bits(dtuple) & REC_NEW_STATUS_MASK)
1606		   == REC_STATUS_NODE_PTR)
1607		  ? dict_index_get_n_unique_in_tree(index) + 1
1608		  : dict_index_get_n_fields(index)));
1609
1610	if (dict_table_is_comp(index->table)) {
1611		return(rec_get_converted_size_comp(index,
1612						   dtuple_get_info_bits(dtuple)
1613						   & REC_NEW_STATUS_MASK,
1614						   dtuple->fields,
1615						   dtuple->n_fields, NULL));
1616	}
1617
1618	data_size = dtuple_get_data_size(dtuple, 0);
1619
1620	extra_size = rec_get_converted_extra_size(
1621		data_size, dtuple_get_n_fields(dtuple), n_ext);
1622
1623#if 0
1624	/* This code is inactive since it may be the wrong place to add
1625	in the size of node pointers used in parent pages AND it is not
1626	currently needed since ha_innobase::max_supported_key_length()
1627	ensures that the key size limit for each page size is well below
1628	the actual limit ((free space on page / 4) - record overhead).
1629	But those limits will need to be raised when InnoDB can
1630	support multiple page sizes.  At that time, we will need
1631	to consider the node pointer on these universal btrees. */
1632
1633	if (dict_index_is_univ(index)) {
1634		/* This is for the insert buffer B-tree.
1635		All fields in the leaf tuple ascend to the
1636		parent node plus the child page pointer. */
1637
1638		/* ibuf cannot contain externally stored fields */
1639		ut_ad(n_ext == 0);
1640
1641		/* Add the data pointer and recompute extra_size
1642		based on one more field. */
1643		data_size += REC_NODE_PTR_SIZE;
1644		extra_size = rec_get_converted_extra_size(
1645			data_size,
1646			dtuple_get_n_fields(dtuple) + 1,
1647			0);
1648
1649		/* Be sure dtuple->n_fields has this node ptr
1650		accounted for.  This function should correspond to
1651		what rec_convert_dtuple_to_rec() needs in storage.
1652		In optimistic insert or update-not-in-place, we will
1653		have to ensure that if the record is converted to a
1654		node pointer, it will not become too large.*/
1655	}
1656#endif
1657
1658	return(data_size + extra_size);
1659}
1660
1661#ifndef UNIV_HOTBACKUP
1662/************************************************************//**
1663Folds a prefix of a physical record to a ulint. Folds only existing fields,
1664that is, checks that we do not run out of the record.
1665@return	the folded value */
1666UNIV_INLINE
1667ulint
1668rec_fold(
1669/*=====*/
1670	const rec_t*	rec,		/*!< in: the physical record */
1671	const ulint*	offsets,	/*!< in: array returned by
1672					rec_get_offsets() */
1673	ulint		n_fields,	/*!< in: number of complete
1674					fields to fold */
1675	ulint		n_bytes,	/*!< in: number of bytes to fold
1676					in an incomplete last field */
1677	index_id_t	tree_id)	/*!< in: index tree id */
1678{
1679	ulint		i;
1680	const byte*	data;
1681	ulint		len;
1682	ulint		fold;
1683	ulint		n_fields_rec;
1684
1685	ut_ad(rec_offs_validate(rec, NULL, offsets));
1686	ut_ad(rec_validate(rec, offsets));
1687	ut_ad(n_fields + n_bytes > 0);
1688
1689	n_fields_rec = rec_offs_n_fields(offsets);
1690	ut_ad(n_fields <= n_fields_rec);
1691	ut_ad(n_fields < n_fields_rec || n_bytes == 0);
1692
1693	if (n_fields > n_fields_rec) {
1694		n_fields = n_fields_rec;
1695	}
1696
1697	if (n_fields == n_fields_rec) {
1698		n_bytes = 0;
1699	}
1700
1701	fold = ut_fold_ull(tree_id);
1702
1703	for (i = 0; i < n_fields; i++) {
1704		data = rec_get_nth_field(rec, offsets, i, &len);
1705
1706		if (len != UNIV_SQL_NULL) {
1707			fold = ut_fold_ulint_pair(fold,
1708						  ut_fold_binary(data, len));
1709		}
1710	}
1711
1712	if (n_bytes > 0) {
1713		data = rec_get_nth_field(rec, offsets, i, &len);
1714
1715		if (len != UNIV_SQL_NULL) {
1716			if (len > n_bytes) {
1717				len = n_bytes;
1718			}
1719
1720			fold = ut_fold_ulint_pair(fold,
1721						  ut_fold_binary(data, len));
1722		}
1723	}
1724
1725	return(fold);
1726}
1727#endif /* !UNIV_HOTBACKUP */
1728