1 /*****************************************************************************
2 
3 Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
4 Copyright (c) 2017, 2021, MariaDB Corporation.
5 
6 This program is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free Software
8 Foundation; version 2 of the License.
9 
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along with
15 this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
17 
18 *****************************************************************************/
19 
20 /********************************************************************//**
21 @file include/rem0rec.h
22 Record manager
23 
24 Created 5/30/1994 Heikki Tuuri
25 *************************************************************************/
26 
27 #ifndef rem0rec_h
28 #define rem0rec_h
29 
30 #ifndef UNIV_INNOCHECKSUM
31 #include "data0data.h"
32 #include "rem0types.h"
33 #include "mtr0types.h"
34 #include "page0types.h"
35 #include "dict0dict.h"
36 #include "trx0types.h"
37 #endif /*! UNIV_INNOCHECKSUM */
38 #include <ostream>
39 #include <sstream>
40 
41 /* Number of extra bytes in an old-style record,
42 in addition to the data and the offsets */
43 #define REC_N_OLD_EXTRA_BYTES	6
44 /* Number of extra bytes in a new-style record,
45 in addition to the data and the offsets */
46 #define REC_N_NEW_EXTRA_BYTES	5
47 
48 #define REC_NEW_STATUS		3	/* This is single byte bit-field */
49 #define REC_NEW_STATUS_MASK	0x7UL
50 #define REC_NEW_STATUS_SHIFT	0
51 
52 /* The following four constants are needed in page0zip.cc in order to
53 efficiently compress and decompress pages. */
54 
55 /* The offset of heap_no in a compact record */
56 #define REC_NEW_HEAP_NO		4
57 /* The shift of heap_no in a compact record.
58 The status is stored in the low-order bits. */
59 #define	REC_HEAP_NO_SHIFT	3
60 
61 /* Length of a B-tree node pointer, in bytes */
62 #define REC_NODE_PTR_SIZE	4
63 
64 #ifndef UNIV_INNOCHECKSUM
65 /** SQL null flag in a 1-byte offset of ROW_FORMAT=REDUNDANT records */
66 static const rec_offs REC_1BYTE_SQL_NULL_MASK= 0x80;
67 /** SQL null flag in a 2-byte offset of ROW_FORMAT=REDUNDANT records */
68 static const rec_offs REC_2BYTE_SQL_NULL_MASK= 0x8000;
69 
70 /** In a 2-byte offset of ROW_FORMAT=REDUNDANT records, the second most
71 significant bit denotes that the tail of a field is stored off-page. */
72 static const rec_offs REC_2BYTE_EXTERN_MASK= 0x4000;
73 
74 static const size_t RECORD_OFFSET= 2;
75 static const size_t INDEX_OFFSET=
76     RECORD_OFFSET + sizeof(rec_t *) / sizeof(rec_offs);
77 #endif /* UNIV_INNOCHECKSUM */
78 
79 /* Length of the rec_get_offsets() header */
80 static const size_t REC_OFFS_HEADER_SIZE=
81 #ifdef UNIV_DEBUG
82 #ifndef UNIV_INNOCHECKSUM
83     sizeof(rec_t *) / sizeof(rec_offs) +
84     sizeof(dict_index_t *) / sizeof(rec_offs) +
85 #endif /* UNIV_INNOCHECKSUM */
86 #endif /* UNIV_DEBUG */
87     2;
88 
89 /* Number of elements that should be initially allocated for the
90 offsets[] array, first passed to rec_get_offsets() */
91 static const size_t REC_OFFS_NORMAL_SIZE= 300;
92 static const size_t REC_OFFS_SMALL_SIZE= 18;
93 static const size_t REC_OFFS_SEC_INDEX_SIZE=
94     /* PK max key parts */ 16 + /* sec idx max key parts */ 16 +
95     /* child page number for non-leaf pages */ 1;
96 
97 /** Get the base address of offsets.  The extra_size is stored at
98 this position, and following positions hold the end offsets of
99 the fields. */
100 #define rec_offs_base(offsets) (offsets + REC_OFFS_HEADER_SIZE)
101 
102 #ifndef UNIV_INNOCHECKSUM
103 /* Offset consists of two parts: 2 upper bits is type and all other bits is
104 value */
105 
106 /** Only 4 different values is possible! */
107 enum field_type_t
108 {
109   /** normal field */
110   STORED_IN_RECORD= 0 << 14,
111   /** this field is stored off-page */
112   STORED_OFFPAGE= 1 << 14,
113   /** just an SQL NULL */
114   SQL_NULL= 2 << 14,
115   /** instantly added field */
116   DEFAULT= 3 << 14,
117 };
118 
119 /** without 2 upper bits */
120 static const rec_offs DATA_MASK= 0x3fff;
121 /** 2 upper bits */
122 static const rec_offs TYPE_MASK= ~DATA_MASK;
get_type(rec_offs n)123 inline field_type_t get_type(rec_offs n)
124 {
125   return static_cast<field_type_t>(n & TYPE_MASK);
126 }
set_type(rec_offs & n,field_type_t type)127 inline void set_type(rec_offs &n, field_type_t type)
128 {
129   n= (n & DATA_MASK) | static_cast<rec_offs>(type);
130 }
get_value(rec_offs n)131 inline rec_offs get_value(rec_offs n) { return n & DATA_MASK; }
combine(rec_offs value,field_type_t type)132 inline rec_offs combine(rec_offs value, field_type_t type)
133 {
134   return get_value(value) | static_cast<rec_offs>(type);
135 }
136 
137 /** Compact flag ORed to the extra size returned by rec_get_offsets() */
138 const rec_offs REC_OFFS_COMPACT= ~(rec_offs(~0) >> 1);
139 /** External flag in offsets returned by rec_get_offsets() */
140 const rec_offs REC_OFFS_EXTERNAL= REC_OFFS_COMPACT >> 1;
141 /** Default value flag in offsets returned by rec_get_offsets() */
142 const rec_offs REC_OFFS_DEFAULT= REC_OFFS_COMPACT >> 2;
143 const rec_offs REC_OFFS_MASK= REC_OFFS_DEFAULT - 1;
144 /******************************************************//**
145 The following function is used to get the pointer of the next chained record
146 on the same page.
147 @return pointer to the next chained record, or NULL if none */
148 UNIV_INLINE
149 const rec_t*
150 rec_get_next_ptr_const(
151 /*===================*/
152 	const rec_t*	rec,	/*!< in: physical record */
153 	ulint		comp)	/*!< in: nonzero=compact page format */
154 	MY_ATTRIBUTE((warn_unused_result));
155 /******************************************************//**
156 The following function is used to get the pointer of the next chained record
157 on the same page.
158 @return pointer to the next chained record, or NULL if none */
159 UNIV_INLINE
160 rec_t*
161 rec_get_next_ptr(
162 /*=============*/
163 	rec_t*	rec,	/*!< in: physical record */
164 	ulint	comp)	/*!< in: nonzero=compact page format */
165 	MY_ATTRIBUTE((warn_unused_result));
166 /******************************************************//**
167 The following function is used to get the offset of the
168 next chained record on the same page.
169 @return the page offset of the next chained record, or 0 if none */
170 UNIV_INLINE
171 ulint
172 rec_get_next_offs(
173 /*==============*/
174 	const rec_t*	rec,	/*!< in: physical record */
175 	ulint		comp)	/*!< in: nonzero=compact page format */
176 	MY_ATTRIBUTE((warn_unused_result));
177 /******************************************************//**
178 The following function is used to set the next record offset field
179 of an old-style record. */
180 UNIV_INLINE
181 void
182 rec_set_next_offs_old(
183 /*==================*/
184 	rec_t*	rec,	/*!< in: old-style physical record */
185 	ulint	next)	/*!< in: offset of the next record */
186 	MY_ATTRIBUTE((nonnull));
187 /******************************************************//**
188 The following function is used to set the next record offset field
189 of a new-style record. */
190 UNIV_INLINE
191 void
192 rec_set_next_offs_new(
193 /*==================*/
194 	rec_t*	rec,	/*!< in/out: new-style physical record */
195 	ulint	next)	/*!< in: offset of the next record */
196 	MY_ATTRIBUTE((nonnull));
197 /******************************************************//**
198 The following function is used to get the number of fields
199 in an old-style record.
200 @return number of data fields */
201 UNIV_INLINE
202 ulint
203 rec_get_n_fields_old(
204 /*=================*/
205 	const rec_t*	rec)	/*!< in: physical record */
206 	MY_ATTRIBUTE((warn_unused_result));
207 /******************************************************//**
208 The following function is used to get the number of fields
209 in a record.
210 @return number of data fields */
211 UNIV_INLINE
212 ulint
213 rec_get_n_fields(
214 /*=============*/
215 	const rec_t*		rec,	/*!< in: physical record */
216 	const dict_index_t*	index)	/*!< in: record descriptor */
217 	MY_ATTRIBUTE((warn_unused_result));
218 
219 /** Confirms the n_fields of the entry is sane with comparing the other
220 record in the same page specified
221 @param[in]	index	index
222 @param[in]	rec	record of the same page
223 @param[in]	entry	index entry
224 @return	true if n_fields is sane */
225 UNIV_INLINE
226 bool
227 rec_n_fields_is_sane(
228 	dict_index_t*	index,
229 	const rec_t*	rec,
230 	const dtuple_t*	entry)
231 	MY_ATTRIBUTE((warn_unused_result));
232 
233 /******************************************************//**
234 The following function is used to get the number of records owned by the
235 previous directory record.
236 @return number of owned records */
237 UNIV_INLINE
238 ulint
239 rec_get_n_owned_old(
240 /*================*/
241 	const rec_t*	rec)	/*!< in: old-style physical record */
242 	MY_ATTRIBUTE((warn_unused_result));
243 /******************************************************//**
244 The following function is used to set the number of owned records. */
245 UNIV_INLINE
246 void
247 rec_set_n_owned_old(
248 /*================*/
249 	rec_t*	rec,		/*!< in: old-style physical record */
250 	ulint	n_owned)	/*!< in: the number of owned */
251 	MY_ATTRIBUTE((nonnull));
252 /******************************************************//**
253 The following function is used to get the number of records owned by the
254 previous directory record.
255 @return number of owned records */
256 UNIV_INLINE
257 ulint
258 rec_get_n_owned_new(
259 /*================*/
260 	const rec_t*	rec)	/*!< in: new-style physical record */
261 	MY_ATTRIBUTE((warn_unused_result));
262 /******************************************************//**
263 The following function is used to set the number of owned records. */
264 UNIV_INLINE
265 void
266 rec_set_n_owned_new(
267 /*================*/
268 	rec_t*		rec,	/*!< in/out: new-style physical record */
269 	page_zip_des_t*	page_zip,/*!< in/out: compressed page, or NULL */
270 	ulint		n_owned)/*!< in: the number of owned */
271 	MY_ATTRIBUTE((nonnull(1)));
272 /******************************************************//**
273 The following function is used to retrieve the info bits of
274 a record.
275 @return info bits */
276 UNIV_INLINE
277 ulint
278 rec_get_info_bits(
279 /*==============*/
280 	const rec_t*	rec,	/*!< in: physical record */
281 	ulint		comp)	/*!< in: nonzero=compact page format */
282 	MY_ATTRIBUTE((warn_unused_result));
283 /******************************************************//**
284 The following function is used to set the info bits of a record. */
285 UNIV_INLINE
286 void
287 rec_set_info_bits_old(
288 /*==================*/
289 	rec_t*	rec,	/*!< in: old-style physical record */
290 	ulint	bits)	/*!< in: info bits */
291 	MY_ATTRIBUTE((nonnull));
292 /******************************************************//**
293 The following function is used to set the info bits of a record. */
294 UNIV_INLINE
295 void
296 rec_set_info_bits_new(
297 /*==================*/
298 	rec_t*	rec,	/*!< in/out: new-style physical record */
299 	ulint	bits)	/*!< in: info bits */
300 	MY_ATTRIBUTE((nonnull));
301 
302 /** Determine the status bits of a non-REDUNDANT record.
303 @param[in]	rec	ROW_FORMAT=COMPACT,DYNAMIC,COMPRESSED record
304 @return status bits */
305 inline
306 rec_comp_status_t
rec_get_status(const rec_t * rec)307 rec_get_status(const rec_t* rec)
308 {
309 	byte bits = rec[-REC_NEW_STATUS] & REC_NEW_STATUS_MASK;
310 	ut_ad(bits <= REC_STATUS_INSTANT);
311 	return static_cast<rec_comp_status_t>(bits);
312 }
313 
314 /** Set the status bits of a non-REDUNDANT record.
315 @param[in,out]	rec	ROW_FORMAT=COMPACT,DYNAMIC,COMPRESSED record
316 @param[in]	bits	status bits */
317 inline
318 void
rec_set_status(rec_t * rec,byte bits)319 rec_set_status(rec_t* rec, byte bits)
320 {
321 	ut_ad(bits <= REC_STATUS_INSTANT);
322 	rec[-REC_NEW_STATUS] = (rec[-REC_NEW_STATUS] & ~REC_NEW_STATUS_MASK)
323 		| bits;
324 }
325 
326 /** Get the length of added field count in a REC_STATUS_INSTANT record.
327 @param[in]	n_add_field	number of added fields, minus one
328 @return	storage size of the field count, in bytes */
rec_get_n_add_field_len(ulint n_add_field)329 inline unsigned rec_get_n_add_field_len(ulint n_add_field)
330 {
331 	ut_ad(n_add_field < REC_MAX_N_FIELDS);
332 	return n_add_field < 0x80 ? 1 : 2;
333 }
334 
335 /** Get the added field count in a REC_STATUS_INSTANT record.
336 @param[in,out]	header	variable header of a REC_STATUS_INSTANT record
337 @return	number of added fields */
rec_get_n_add_field(const byte * & header)338 inline unsigned rec_get_n_add_field(const byte*& header)
339 {
340 	unsigned n_fields_add = *--header;
341 	if (n_fields_add < 0x80) {
342 		ut_ad(rec_get_n_add_field_len(n_fields_add) == 1);
343 		return n_fields_add;
344 	}
345 
346 	n_fields_add &= 0x7f;
347 	n_fields_add |= unsigned(*--header) << 7;
348 	ut_ad(n_fields_add < REC_MAX_N_FIELDS);
349 	ut_ad(rec_get_n_add_field_len(n_fields_add) == 2);
350 	return n_fields_add;
351 }
352 
353 /** Set the added field count in a REC_STATUS_INSTANT record.
354 @param[in,out]	header	variable header of a REC_STATUS_INSTANT record
355 @param[in]	n_add	number of added fields, minus 1
356 @return	record header before the number of added fields */
rec_set_n_add_field(byte * & header,ulint n_add)357 inline void rec_set_n_add_field(byte*& header, ulint n_add)
358 {
359 	ut_ad(n_add < REC_MAX_N_FIELDS);
360 
361 	if (n_add < 0x80) {
362 		*header-- = byte(n_add);
363 	} else {
364 		*header-- = byte(n_add) | 0x80;
365 		*header-- = byte(n_add >> 7);
366 	}
367 }
368 
369 /******************************************************//**
370 The following function is used to retrieve the info and status
371 bits of a record.  (Only compact records have status bits.)
372 @return info bits */
373 UNIV_INLINE
374 ulint
375 rec_get_info_and_status_bits(
376 /*=========================*/
377 	const rec_t*	rec,	/*!< in: physical record */
378 	ulint		comp)	/*!< in: nonzero=compact page format */
379 	MY_ATTRIBUTE((warn_unused_result));
380 /******************************************************//**
381 The following function is used to set the info and status
382 bits of a record.  (Only compact records have status bits.) */
383 UNIV_INLINE
384 void
385 rec_set_info_and_status_bits(
386 /*=========================*/
387 	rec_t*	rec,	/*!< in/out: compact physical record */
388 	ulint	bits)	/*!< in: info bits */
389 	MY_ATTRIBUTE((nonnull));
390 
391 /******************************************************//**
392 The following function tells if record is delete marked.
393 @return nonzero if delete marked */
394 UNIV_INLINE
395 ulint
396 rec_get_deleted_flag(
397 /*=================*/
398 	const rec_t*	rec,	/*!< in: physical record */
399 	ulint		comp)	/*!< in: nonzero=compact page format */
400 	MY_ATTRIBUTE((warn_unused_result));
401 /******************************************************//**
402 The following function is used to set the deleted bit. */
403 UNIV_INLINE
404 void
405 rec_set_deleted_flag_old(
406 /*=====================*/
407 	rec_t*	rec,	/*!< in: old-style physical record */
408 	ulint	flag)	/*!< in: nonzero if delete marked */
409 	MY_ATTRIBUTE((nonnull));
410 /******************************************************//**
411 The following function is used to set the deleted bit. */
412 UNIV_INLINE
413 void
414 rec_set_deleted_flag_new(
415 /*=====================*/
416 	rec_t*		rec,	/*!< in/out: new-style physical record */
417 	page_zip_des_t*	page_zip,/*!< in/out: compressed page, or NULL */
418 	ulint		flag)	/*!< in: nonzero if delete marked */
419 	MY_ATTRIBUTE((nonnull(1)));
420 /******************************************************//**
421 The following function tells if a new-style record is a node pointer.
422 @return TRUE if node pointer */
423 UNIV_INLINE
424 bool
425 rec_get_node_ptr_flag(
426 /*==================*/
427 	const rec_t*	rec)	/*!< in: physical record */
428 	MY_ATTRIBUTE((warn_unused_result));
429 /******************************************************//**
430 The following function is used to get the order number
431 of an old-style record in the heap of the index page.
432 @return heap order number */
433 UNIV_INLINE
434 ulint
435 rec_get_heap_no_old(
436 /*================*/
437 	const rec_t*	rec)	/*!< in: physical record */
438 	MY_ATTRIBUTE((warn_unused_result));
439 /******************************************************//**
440 The following function is used to set the heap number
441 field in an old-style record. */
442 UNIV_INLINE
443 void
444 rec_set_heap_no_old(
445 /*================*/
446 	rec_t*	rec,	/*!< in: physical record */
447 	ulint	heap_no)/*!< in: the heap number */
448 	MY_ATTRIBUTE((nonnull));
449 /******************************************************//**
450 The following function is used to get the order number
451 of a new-style record in the heap of the index page.
452 @return heap order number */
453 UNIV_INLINE
454 ulint
455 rec_get_heap_no_new(
456 /*================*/
457 	const rec_t*	rec)	/*!< in: physical record */
458 	MY_ATTRIBUTE((warn_unused_result));
459 /******************************************************//**
460 The following function is used to set the heap number
461 field in a new-style record. */
462 UNIV_INLINE
463 void
464 rec_set_heap_no_new(
465 /*================*/
466 	rec_t*	rec,	/*!< in/out: physical record */
467 	ulint	heap_no)/*!< in: the heap number */
468 	MY_ATTRIBUTE((nonnull));
469 /******************************************************//**
470 The following function is used to test whether the data offsets
471 in the record are stored in one-byte or two-byte format.
472 @return TRUE if 1-byte form */
473 UNIV_INLINE
474 ibool
475 rec_get_1byte_offs_flag(
476 /*====================*/
477 	const rec_t*	rec)	/*!< in: physical record */
478 	MY_ATTRIBUTE((warn_unused_result));
479 
480 /******************************************************//**
481 The following function is used to set the 1-byte offsets flag. */
482 UNIV_INLINE
483 void
484 rec_set_1byte_offs_flag(
485 /*====================*/
486 	rec_t*	rec,	/*!< in: physical record */
487 	ibool	flag)	/*!< in: TRUE if 1byte form */
488 	MY_ATTRIBUTE((nonnull));
489 
490 /******************************************************//**
491 Returns the offset of nth field end if the record is stored in the 1-byte
492 offsets form. If the field is SQL null, the flag is ORed in the returned
493 value.
494 @return offset of the start of the field, SQL null flag ORed */
495 UNIV_INLINE
496 uint8_t
497 rec_1_get_field_end_info(
498 /*=====================*/
499 	const rec_t*	rec,	/*!< in: record */
500 	ulint		n)	/*!< in: field index */
501 	MY_ATTRIBUTE((warn_unused_result));
502 
503 /******************************************************//**
504 Returns the offset of nth field end if the record is stored in the 2-byte
505 offsets form. If the field is SQL null, the flag is ORed in the returned
506 value.
507 @return offset of the start of the field, SQL null flag and extern
508 storage flag ORed */
509 UNIV_INLINE
510 uint16_t
511 rec_2_get_field_end_info(
512 /*=====================*/
513 	const rec_t*	rec,	/*!< in: record */
514 	ulint		n)	/*!< in: field index */
515 	MY_ATTRIBUTE((warn_unused_result));
516 
517 /******************************************************//**
518 Returns nonzero if the field is stored off-page.
519 @retval 0 if the field is stored in-page
520 @retval REC_2BYTE_EXTERN_MASK if the field is stored externally */
521 UNIV_INLINE
522 ulint
523 rec_2_is_field_extern(
524 /*==================*/
525 	const rec_t*	rec,	/*!< in: record */
526 	ulint		n)	/*!< in: field index */
527 	MY_ATTRIBUTE((warn_unused_result));
528 
529 /******************************************************//**
530 Determine how many of the first n columns in a compact
531 physical record are stored externally.
532 @return number of externally stored columns */
533 ulint
534 rec_get_n_extern_new(
535 /*=================*/
536 	const rec_t*		rec,	/*!< in: compact physical record */
537 	const dict_index_t*	index,	/*!< in: record descriptor */
538 	ulint			n)	/*!< in: number of columns to scan */
539 	MY_ATTRIBUTE((nonnull, warn_unused_result));
540 
541 /** Determine the offsets to each field in an index record.
542 @param[in]	rec		physical record
543 @param[in]	index		the index that the record belongs to
544 @param[in,out]	offsets		array comprising offsets[0] allocated elements,
545 				or an array from rec_get_offsets(), or NULL
546 @param[in]	n_core		0, or index->n_core_fields for leaf page
547 @param[in]	n_fields	maximum number of offsets to compute
548 				(ULINT_UNDEFINED to compute all offsets)
549 @param[in,out]	heap		memory heap
550 @return the new offsets */
551 rec_offs*
552 rec_get_offsets_func(
553 	const rec_t*		rec,
554 	const dict_index_t*	index,
555 	rec_offs*		offsets,
556 	ulint			n_core,
557 	ulint			n_fields,
558 #ifdef UNIV_DEBUG
559 	const char*		file,	/*!< in: file name where called */
560 	unsigned		line,	/*!< in: line number where called */
561 #endif /* UNIV_DEBUG */
562 	mem_heap_t**		heap)	/*!< in/out: memory heap */
563 #ifdef UNIV_DEBUG
564 	MY_ATTRIBUTE((nonnull(1,2,6,8),warn_unused_result));
565 #else /* UNIV_DEBUG */
566 	MY_ATTRIBUTE((nonnull(1,2,6),warn_unused_result));
567 #endif /* UNIV_DEBUG */
568 
569 #ifdef UNIV_DEBUG
570 # define rec_get_offsets(rec, index, offsets, leaf, n, heap)		\
571 	rec_get_offsets_func(rec,index,offsets,leaf,n,__FILE__,__LINE__,heap)
572 #else /* UNIV_DEBUG */
573 # define rec_get_offsets(rec, index, offsets, leaf, n, heap)		\
574 	rec_get_offsets_func(rec, index, offsets, leaf, n, heap)
575 #endif /* UNIV_DEBUG */
576 
577 /******************************************************//**
578 The following function determines the offsets to each field
579 in the record.  It can reuse a previously allocated array. */
580 void
581 rec_get_offsets_reverse(
582 /*====================*/
583 	const byte*		extra,	/*!< in: the extra bytes of a
584 					compact record in reverse order,
585 					excluding the fixed-size
586 					REC_N_NEW_EXTRA_BYTES */
587 	const dict_index_t*	index,	/*!< in: record descriptor */
588 	ulint			node_ptr,/*!< in: nonzero=node pointer,
589 					0=leaf node */
590 	rec_offs*		offsets)/*!< in/out: array consisting of
591 					offsets[0] allocated elements */
592 	MY_ATTRIBUTE((nonnull));
593 #ifdef UNIV_DEBUG
594 /** Validate offsets returned by rec_get_offsets().
595 @param[in]	rec	record, or NULL
596 @param[in]	index	the index that the record belongs in, or NULL
597 @param[in,out]	offsets	the offsets of the record
598 @return true */
599 bool
600 rec_offs_validate(
601 	const rec_t*		rec,
602 	const dict_index_t*	index,
603 	const rec_offs*		offsets)
604 	MY_ATTRIBUTE((nonnull(3), warn_unused_result));
605 /** Update debug data in offsets, in order to tame rec_offs_validate().
606 @param[in]	rec	record
607 @param[in]	index	the index that the record belongs in
608 @param[in]	leaf	whether the record resides in a leaf page
609 @param[in,out]	offsets	offsets from rec_get_offsets() to adjust */
610 void
611 rec_offs_make_valid(
612 	const rec_t*		rec,
613 	const dict_index_t*	index,
614 	bool			leaf,
615 	rec_offs*		offsets)
616 	MY_ATTRIBUTE((nonnull));
617 #else
618 # define rec_offs_make_valid(rec, index, leaf, offsets)
619 #endif /* UNIV_DEBUG */
620 
621 /************************************************************//**
622 The following function is used to get the offset to the nth
623 data field in an old-style record.
624 @return offset to the field */
625 ulint
626 rec_get_nth_field_offs_old(
627 /*=======================*/
628 	const rec_t*	rec,	/*!< in: record */
629 	ulint		n,	/*!< in: index of the field */
630 	ulint*		len)	/*!< out: length of the field; UNIV_SQL_NULL
631 				if SQL null */
632 	MY_ATTRIBUTE((nonnull));
633 #define rec_get_nth_field_old(rec, n, len) \
634 ((rec) + rec_get_nth_field_offs_old(rec, n, len))
635 /************************************************************//**
636 Gets the physical size of an old-style field.
637 Also an SQL null may have a field of size > 0,
638 if the data type is of a fixed size.
639 @return field size in bytes */
640 UNIV_INLINE
641 ulint
642 rec_get_nth_field_size(
643 /*===================*/
644 	const rec_t*	rec,	/*!< in: record */
645 	ulint		n)	/*!< in: index of the field */
646 	MY_ATTRIBUTE((warn_unused_result));
647 /************************************************************//**
648 The following function is used to get an offset to the nth
649 data field in a record.
650 @return offset from the origin of rec */
651 UNIV_INLINE
652 rec_offs
653 rec_get_nth_field_offs(
654 /*===================*/
655 	const rec_offs*	offsets,/*!< in: array returned by rec_get_offsets() */
656 	ulint		n,	/*!< in: index of the field */
657 	ulint*		len)	/*!< out: length of the field; UNIV_SQL_NULL
658 				if SQL null */
659 	MY_ATTRIBUTE((nonnull));
660 #define rec_get_nth_field(rec, offsets, n, len) \
661 ((rec) + rec_get_nth_field_offs(offsets, n, len))
662 /******************************************************//**
663 Determine if the offsets are for a record containing null BLOB pointers.
664 @return first field containing a null BLOB pointer, or NULL if none found */
665 UNIV_INLINE
666 const byte*
667 rec_offs_any_null_extern(
668 /*=====================*/
669 	const rec_t*	rec,		/*!< in: record */
670 	const rec_offs*	offsets)	/*!< in: rec_get_offsets(rec) */
671 	MY_ATTRIBUTE((warn_unused_result));
672 
673 /** Mark the nth field as externally stored.
674 @param[in]	offsets		array returned by rec_get_offsets()
675 @param[in]	n		nth field */
676 void
677 rec_offs_make_nth_extern(
678         rec_offs*	offsets,
679         const ulint     n);
680 
681 MY_ATTRIBUTE((nonnull))
682 /** Determine the number of allocated elements for an array of offsets.
683 @param[in]	offsets		offsets after rec_offs_set_n_alloc()
684 @return number of elements */
rec_offs_get_n_alloc(const rec_offs * offsets)685 inline ulint rec_offs_get_n_alloc(const rec_offs *offsets)
686 {
687   ut_ad(offsets);
688   ulint n_alloc= offsets[0];
689   ut_ad(n_alloc > REC_OFFS_HEADER_SIZE);
690   MEM_CHECK_ADDRESSABLE(offsets, n_alloc * sizeof *offsets);
691   return n_alloc;
692 }
693 
694 /** Determine the number of fields for which offsets have been initialized.
695 @param[in]	offsets	rec_get_offsets()
696 @return number of fields */
697 inline
698 ulint
rec_offs_n_fields(const rec_offs * offsets)699 rec_offs_n_fields(const rec_offs* offsets)
700 {
701 	ulint	n_fields;
702 	ut_ad(offsets);
703 	n_fields = offsets[1];
704 	ut_ad(n_fields > 0);
705 	ut_ad(n_fields <= REC_MAX_N_FIELDS);
706 	ut_ad(n_fields + REC_OFFS_HEADER_SIZE
707 	      <= rec_offs_get_n_alloc(offsets));
708 	return(n_fields);
709 }
710 
711 /** Get a flag of a record field.
712 @param[in]	offsets	rec_get_offsets()
713 @param[in]	n	nth field
714 @param[in]	flag	flag to extract
715 @return	type of the record field */
rec_offs_nth_type(const rec_offs * offsets,ulint n)716 inline field_type_t rec_offs_nth_type(const rec_offs *offsets, ulint n)
717 {
718   ut_ad(rec_offs_validate(NULL, NULL, offsets));
719   ut_ad(n < rec_offs_n_fields(offsets));
720   return get_type(rec_offs_base(offsets)[1 + n]);
721 }
722 
723 /** Determine if a record field is missing
724 (should be replaced by dict_index_t::instant_field_value()).
725 @param[in]	offsets	rec_get_offsets()
726 @param[in]	n	nth field
727 @return	nonzero if default bit is set */
rec_offs_nth_default(const rec_offs * offsets,ulint n)728 inline ulint rec_offs_nth_default(const rec_offs *offsets, ulint n)
729 {
730   return rec_offs_nth_type(offsets, n) == DEFAULT;
731 }
732 
733 /** Determine if a record field is SQL NULL
734 (should be replaced by dict_index_t::instant_field_value()).
735 @param[in]	offsets	rec_get_offsets()
736 @param[in]	n	nth field
737 @return	nonzero if SQL NULL set */
rec_offs_nth_sql_null(const rec_offs * offsets,ulint n)738 inline ulint rec_offs_nth_sql_null(const rec_offs *offsets, ulint n)
739 {
740   return rec_offs_nth_type(offsets, n) == SQL_NULL;
741 }
742 
743 /** Determine if a record field is stored off-page.
744 @param[in]	offsets	rec_get_offsets()
745 @param[in]	n	nth field
746 Returns nonzero if the extern bit is set in nth field of rec.
747 @return nonzero if externally stored */
rec_offs_nth_extern(const rec_offs * offsets,ulint n)748 inline ulint rec_offs_nth_extern(const rec_offs *offsets, ulint n)
749 {
750   return rec_offs_nth_type(offsets, n) == STORED_OFFPAGE;
751 }
752 
753 /** Get a global flag of a record.
754 @param[in]	offsets	rec_get_offsets()
755 @param[in]	flag	flag to extract
756 @return	the flag of the record field */
rec_offs_any_flag(const rec_offs * offsets,ulint flag)757 inline ulint rec_offs_any_flag(const rec_offs *offsets, ulint flag)
758 {
759   ut_ad(rec_offs_validate(NULL, NULL, offsets));
760   return *rec_offs_base(offsets) & flag;
761 }
762 
763 /** Determine if the offsets are for a record containing off-page columns.
764 @param[in]	offsets	rec_get_offsets()
765 @return nonzero if any off-page columns exist */
rec_offs_any_extern(const rec_offs * offsets)766 inline bool rec_offs_any_extern(const rec_offs *offsets)
767 {
768   return rec_offs_any_flag(offsets, REC_OFFS_EXTERNAL);
769 }
770 
771 /** Determine if the offsets are for a record that is missing fields.
772 @param[in]	offsets	rec_get_offsets()
773 @return nonzero if any fields need to be replaced with
774 		dict_index_t::instant_field_value() */
rec_offs_any_default(const rec_offs * offsets)775 inline ulint rec_offs_any_default(const rec_offs *offsets)
776 {
777   return rec_offs_any_flag(offsets, REC_OFFS_DEFAULT);
778 }
779 
780 /** Determine if the offsets are for other than ROW_FORMAT=REDUNDANT.
781 @param[in]	offsets	rec_get_offsets()
782 @return	nonzero	if ROW_FORMAT is COMPACT,DYNAMIC or COMPRESSED
783 @retval	0	if ROW_FORMAT=REDUNDANT */
rec_offs_comp(const rec_offs * offsets)784 inline ulint rec_offs_comp(const rec_offs *offsets)
785 {
786   ut_ad(rec_offs_validate(NULL, NULL, offsets));
787   return (*rec_offs_base(offsets) & REC_OFFS_COMPACT);
788 }
789 
790 /** Determine if the record is the metadata pseudo-record
791 in the clustered index for instant ADD COLUMN or ALTER TABLE.
792 @param[in]	rec	leaf page record
793 @param[in]	comp	0 if ROW_FORMAT=REDUNDANT, else nonzero
794 @return	whether the record is the metadata pseudo-record */
rec_is_metadata(const rec_t * rec,ulint comp)795 inline bool rec_is_metadata(const rec_t* rec, ulint comp)
796 {
797 	bool is = !!(rec_get_info_bits(rec, comp) & REC_INFO_MIN_REC_FLAG);
798 	ut_ad(!is || !comp || rec_get_status(rec) == REC_STATUS_INSTANT);
799 	return is;
800 }
801 
802 /** Determine if the record is the metadata pseudo-record
803 in the clustered index for instant ADD COLUMN or ALTER TABLE.
804 @param[in]	rec	leaf page record
805 @param[in]	index	index of the record
806 @return	whether the record is the metadata pseudo-record */
rec_is_metadata(const rec_t * rec,const dict_index_t & index)807 inline bool rec_is_metadata(const rec_t* rec, const dict_index_t& index)
808 {
809 	bool is = rec_is_metadata(rec, dict_table_is_comp(index.table));
810 	ut_ad(!is || index.is_instant());
811 	return is;
812 }
813 
814 /** Determine if the record is the metadata pseudo-record
815 in the clustered index for instant ADD COLUMN (not other ALTER TABLE).
816 @param[in]	rec	leaf page record
817 @param[in]	comp	0 if ROW_FORMAT=REDUNDANT, else nonzero
818 @return	whether the record is the metadata pseudo-record */
rec_is_add_metadata(const rec_t * rec,ulint comp)819 inline bool rec_is_add_metadata(const rec_t* rec, ulint comp)
820 {
821 	bool is = rec_get_info_bits(rec, comp) == REC_INFO_MIN_REC_FLAG;
822 	ut_ad(!is || !comp || rec_get_status(rec) == REC_STATUS_INSTANT);
823 	return is;
824 }
825 
826 /** Determine if the record is the metadata pseudo-record
827 in the clustered index for instant ADD COLUMN (not other ALTER TABLE).
828 @param[in]	rec	leaf page record
829 @param[in]	index	index of the record
830 @return	whether the record is the metadata pseudo-record */
rec_is_add_metadata(const rec_t * rec,const dict_index_t & index)831 inline bool rec_is_add_metadata(const rec_t* rec, const dict_index_t& index)
832 {
833 	bool is = rec_is_add_metadata(rec, dict_table_is_comp(index.table));
834 	ut_ad(!is || index.is_instant());
835 	return is;
836 }
837 
838 /** Determine if the record is the metadata pseudo-record
839 in the clustered index for instant ALTER TABLE (not plain ADD COLUMN).
840 @param[in]	rec	leaf page record
841 @param[in]	comp	0 if ROW_FORMAT=REDUNDANT, else nonzero
842 @return	whether the record is the ALTER TABLE metadata pseudo-record */
rec_is_alter_metadata(const rec_t * rec,ulint comp)843 inline bool rec_is_alter_metadata(const rec_t* rec, ulint comp)
844 {
845 	bool is = !(~rec_get_info_bits(rec, comp)
846 		    & (REC_INFO_MIN_REC_FLAG | REC_INFO_DELETED_FLAG));
847 	ut_ad(!is || rec_is_metadata(rec, comp));
848 	return is;
849 }
850 
851 /** Determine if the record is the metadata pseudo-record
852 in the clustered index for instant ALTER TABLE (not plain ADD COLUMN).
853 @param[in]	rec	leaf page record
854 @param[in]	index	index of the record
855 @return	whether the record is the ALTER TABLE metadata pseudo-record */
rec_is_alter_metadata(const rec_t * rec,const dict_index_t & index)856 inline bool rec_is_alter_metadata(const rec_t* rec, const dict_index_t& index)
857 {
858 	bool is = rec_is_alter_metadata(rec, dict_table_is_comp(index.table));
859 	ut_ad(!is || index.is_dummy || index.is_instant());
860 	return is;
861 }
862 
863 /** Determine if a record is delete-marked (not a metadata pseudo-record).
864 @param[in]	rec	record
865 @param[in]	comp	nonzero if ROW_FORMAT!=REDUNDANT
866 @return	whether the record is a delete-marked user record */
rec_is_delete_marked(const rec_t * rec,ulint comp)867 inline bool rec_is_delete_marked(const rec_t* rec, ulint comp)
868 {
869 	return (rec_get_info_bits(rec, comp)
870 		& (REC_INFO_MIN_REC_FLAG | REC_INFO_DELETED_FLAG))
871 		== REC_INFO_DELETED_FLAG;
872 }
873 
874 /** Get the nth field from an index.
875 @param[in]	rec	index record
876 @param[in]	index	index
877 @param[in]	offsets	rec_get_offsets(rec, index)
878 @param[in]	n	field number
879 @param[out]	len	length of the field in bytes, or UNIV_SQL_NULL
880 @return a read-only copy of the index field */
881 inline
882 const byte*
rec_get_nth_cfield(const rec_t * rec,const dict_index_t * index,const rec_offs * offsets,ulint n,ulint * len)883 rec_get_nth_cfield(
884 	const rec_t*		rec,
885 	const dict_index_t*	index,
886 	const rec_offs*		offsets,
887 	ulint			n,
888 	ulint*			len)
889 {
890 	/* Because this function may be invoked by innobase_rec_to_mysql()
891 	for reporting a duplicate key during ALTER TABLE or
892 	CREATE UNIQUE INDEX, and in that case the rec omit the fixed-size
893 	header of 5 or 6 bytes, the check
894 	rec_offs_validate(rec, index, offsets) must be avoided here. */
895 	if (!rec_offs_nth_default(offsets, n)) {
896 		return rec_get_nth_field(rec, offsets, n, len);
897 	}
898 	return index->instant_field_value(n, len);
899 }
900 
901 /******************************************************//**
902 Gets the physical size of a field.
903 @return length of field */
904 UNIV_INLINE
905 ulint
906 rec_offs_nth_size(
907 /*==============*/
908 	const rec_offs*	offsets,/*!< in: array returned by rec_get_offsets() */
909 	ulint		n)	/*!< in: nth field */
910 	MY_ATTRIBUTE((warn_unused_result));
911 
912 /******************************************************//**
913 Returns the number of extern bits set in a record.
914 @return number of externally stored fields */
915 UNIV_INLINE
916 ulint
917 rec_offs_n_extern(
918 /*==============*/
919 	const rec_offs*	offsets)/*!< in: array returned by rec_get_offsets() */
920 	MY_ATTRIBUTE((warn_unused_result));
921 /***********************************************************//**
922 This is used to modify the value of an already existing field in a record.
923 The previous value must have exactly the same size as the new value. If len
924 is UNIV_SQL_NULL then the field is treated as an SQL null.
925 For records in ROW_FORMAT=COMPACT (new-style records), len must not be
926 UNIV_SQL_NULL unless the field already is SQL null. */
927 UNIV_INLINE
928 void
929 rec_set_nth_field(
930 /*==============*/
931 	rec_t*		rec,	/*!< in: record */
932 	const rec_offs*	offsets,/*!< in: array returned by rec_get_offsets() */
933 	ulint		n,	/*!< in: index number of the field */
934 	const void*	data,	/*!< in: pointer to the data if not SQL null */
935 	ulint		len)	/*!< in: length of the data or UNIV_SQL_NULL.
936 				If not SQL null, must have the same
937 				length as the previous value.
938 				If SQL null, previous value must be
939 				SQL null. */
940 	MY_ATTRIBUTE((nonnull(1,2)));
941 /**********************************************************//**
942 The following function returns the data size of an old-style physical
943 record, that is the sum of field lengths. SQL null fields
944 are counted as length 0 fields. The value returned by the function
945 is the distance from record origin to record end in bytes.
946 @return size */
947 UNIV_INLINE
948 ulint
949 rec_get_data_size_old(
950 /*==================*/
951 	const rec_t*	rec)	/*!< in: physical record */
952 	MY_ATTRIBUTE((warn_unused_result));
953 /**********************************************************//**
954 The following function sets the number of allocated elements
955 for an array of offsets. */
956 UNIV_INLINE
957 void
958 rec_offs_set_n_alloc(
959 /*=================*/
960 	rec_offs*offsets,	/*!< out: array for rec_get_offsets(),
961 				must be allocated */
962 	ulint	n_alloc)	/*!< in: number of elements */
963 	MY_ATTRIBUTE((nonnull));
964 #define rec_offs_init(offsets) \
965 	rec_offs_set_n_alloc(offsets, (sizeof offsets) / sizeof *offsets)
966 /**********************************************************//**
967 The following function returns the data size of a physical
968 record, that is the sum of field lengths. SQL null fields
969 are counted as length 0 fields. The value returned by the function
970 is the distance from record origin to record end in bytes.
971 @return size */
972 UNIV_INLINE
973 ulint
974 rec_offs_data_size(
975 /*===============*/
976 	const rec_offs*	offsets)/*!< in: array returned by rec_get_offsets() */
977 	MY_ATTRIBUTE((warn_unused_result));
978 /**********************************************************//**
979 Returns the total size of record minus data size of record.
980 The value returned by the function is the distance from record
981 start to record origin in bytes.
982 @return size */
983 UNIV_INLINE
984 ulint
985 rec_offs_extra_size(
986 /*================*/
987 	const rec_offs*	offsets)/*!< in: array returned by rec_get_offsets() */
988 	MY_ATTRIBUTE((warn_unused_result));
989 /**********************************************************//**
990 Returns the total size of a physical record.
991 @return size */
992 UNIV_INLINE
993 ulint
994 rec_offs_size(
995 /*==========*/
996 	const rec_offs*	offsets)/*!< in: array returned by rec_get_offsets() */
997 	MY_ATTRIBUTE((warn_unused_result));
998 #ifdef UNIV_DEBUG
999 /**********************************************************//**
1000 Returns a pointer to the start of the record.
1001 @return pointer to start */
1002 UNIV_INLINE
1003 byte*
1004 rec_get_start(
1005 /*==========*/
1006 	const rec_t*	rec,	/*!< in: pointer to record */
1007 	const rec_offs*	offsets)/*!< in: array returned by rec_get_offsets() */
1008 	MY_ATTRIBUTE((warn_unused_result));
1009 /**********************************************************//**
1010 Returns a pointer to the end of the record.
1011 @return pointer to end */
1012 UNIV_INLINE
1013 byte*
1014 rec_get_end(
1015 /*========*/
1016 	const rec_t*	rec,	/*!< in: pointer to record */
1017 	const rec_offs*	offsets)/*!< in: array returned by rec_get_offsets() */
1018 	MY_ATTRIBUTE((warn_unused_result));
1019 #else /* UNIV_DEBUG */
1020 # define rec_get_start(rec, offsets) ((rec) - rec_offs_extra_size(offsets))
1021 # define rec_get_end(rec, offsets) ((rec) + rec_offs_data_size(offsets))
1022 #endif /* UNIV_DEBUG */
1023 
1024 /** Copy a physical record to a buffer.
1025 @param[in]	buf	buffer
1026 @param[in]	rec	physical record
1027 @param[in]	offsets	array returned by rec_get_offsets()
1028 @return pointer to the origin of the copy */
1029 UNIV_INLINE
1030 rec_t*
1031 rec_copy(
1032 	void*		buf,
1033 	const rec_t*	rec,
1034 	const rec_offs*	offsets);
1035 
1036 /** Determine the size of a data tuple prefix in a temporary file.
1037 @tparam redundant_temp whether to use the ROW_FORMAT=REDUNDANT format
1038 @param[in]	index		clustered or secondary index
1039 @param[in]	fields		data fields
1040 @param[in]	n_fields	number of data fields
1041 @param[out]	extra		record header size
1042 @param[in]	status		REC_STATUS_ORDINARY or REC_STATUS_INSTANT
1043 @return	total size, in bytes */
1044 template<bool redundant_temp>
1045 ulint
1046 rec_get_converted_size_temp(
1047 	const dict_index_t*	index,
1048 	const dfield_t*		fields,
1049 	ulint			n_fields,
1050 	ulint*			extra,
1051 	rec_comp_status_t	status = REC_STATUS_ORDINARY)
1052 	MY_ATTRIBUTE((warn_unused_result, nonnull));
1053 
1054 /** Determine the offset to each field in temporary file.
1055 @param[in]	rec	temporary file record
1056 @param[in]	index	index of that the record belongs to
1057 @param[in,out]	offsets	offsets to the fields; in: rec_offs_n_fields(offsets)
1058 @param[in]	n_core	number of core fields (index->n_core_fields)
1059 @param[in]	def_val	default values for non-core fields
1060 @param[in]	status	REC_STATUS_ORDINARY or REC_STATUS_INSTANT */
1061 void
1062 rec_init_offsets_temp(
1063 	const rec_t*		rec,
1064 	const dict_index_t*	index,
1065 	rec_offs*		offsets,
1066 	ulint			n_core,
1067 	const dict_col_t::def_t*def_val,
1068 	rec_comp_status_t	status = REC_STATUS_ORDINARY)
1069 	MY_ATTRIBUTE((nonnull(1,2,3)));
1070 /** Determine the offset to each field in temporary file.
1071 @param[in]	rec	temporary file record
1072 @param[in]	index	index of that the record belongs to
1073 @param[in,out]	offsets	offsets to the fields; in: rec_offs_n_fields(offsets)
1074 */
1075 void
1076 rec_init_offsets_temp(
1077 	const rec_t*		rec,
1078 	const dict_index_t*	index,
1079 	rec_offs*		offsets)
1080 	MY_ATTRIBUTE((nonnull));
1081 
1082 /** Convert a data tuple prefix to the temporary file format.
1083 @tparam redundant_temp whether to use the ROW_FORMAT=REDUNDANT format
1084 @param[out]	rec		record in temporary file format
1085 @param[in]	index		clustered or secondary index
1086 @param[in]	fields		data fields
1087 @param[in]	n_fields	number of data fields
1088 @param[in]	status		REC_STATUS_ORDINARY or REC_STATUS_INSTANT */
1089 template<bool redundant_temp>
1090 void
1091 rec_convert_dtuple_to_temp(
1092 	rec_t*			rec,
1093 	const dict_index_t*	index,
1094 	const dfield_t*		fields,
1095 	ulint			n_fields,
1096 	rec_comp_status_t	status = REC_STATUS_ORDINARY)
1097 	MY_ATTRIBUTE((nonnull));
1098 
1099 /**************************************************************//**
1100 Copies the first n fields of a physical record to a new physical record in
1101 a buffer.
1102 @return own: copied record */
1103 rec_t*
1104 rec_copy_prefix_to_buf(
1105 /*===================*/
1106 	const rec_t*		rec,		/*!< in: physical record */
1107 	const dict_index_t*	index,		/*!< in: record descriptor */
1108 	ulint			n_fields,	/*!< in: number of fields
1109 						to copy */
1110 	byte**			buf,		/*!< in/out: memory buffer
1111 						for the copied prefix,
1112 						or NULL */
1113 	ulint*			buf_size)	/*!< in/out: buffer size */
1114 	MY_ATTRIBUTE((nonnull));
1115 /*********************************************************//**
1116 Builds a physical record out of a data tuple and
1117 stores it into the given buffer.
1118 @return pointer to the origin of physical record */
1119 rec_t*
1120 rec_convert_dtuple_to_rec(
1121 /*======================*/
1122 	byte*			buf,	/*!< in: start address of the
1123 					physical record */
1124 	const dict_index_t*	index,	/*!< in: record descriptor */
1125 	const dtuple_t*		dtuple,	/*!< in: data tuple */
1126 	ulint			n_ext)	/*!< in: number of
1127 					externally stored columns */
1128 	MY_ATTRIBUTE((warn_unused_result));
1129 /**********************************************************//**
1130 Returns the extra size of an old-style physical record if we know its
1131 data size and number of fields.
1132 @return extra size */
1133 UNIV_INLINE
1134 ulint
1135 rec_get_converted_extra_size(
1136 /*=========================*/
1137 	ulint	data_size,	/*!< in: data size */
1138 	ulint	n_fields,	/*!< in: number of fields */
1139 	ulint	n_ext)		/*!< in: number of externally stored columns */
1140 	MY_ATTRIBUTE((const));
1141 /**********************************************************//**
1142 Determines the size of a data tuple prefix in ROW_FORMAT=COMPACT.
1143 @return total size */
1144 ulint
1145 rec_get_converted_size_comp_prefix(
1146 /*===============================*/
1147 	const dict_index_t*	index,	/*!< in: record descriptor */
1148 	const dfield_t*		fields,	/*!< in: array of data fields */
1149 	ulint			n_fields,/*!< in: number of data fields */
1150 	ulint*			extra)	/*!< out: extra size */
1151 	MY_ATTRIBUTE((warn_unused_result, nonnull(1,2)));
1152 
1153 /** Determine the size of a record in ROW_FORMAT=COMPACT.
1154 @param[in]	index		record descriptor. dict_table_is_comp()
1155 				is assumed to hold, even if it doesn't
1156 @param[in]	tuple		logical record
1157 @param[out]	extra		extra size
1158 @return total size */
1159 ulint
1160 rec_get_converted_size_comp(
1161 	const dict_index_t*	index,
1162 	const dtuple_t*		tuple,
1163 	ulint*			extra)
1164 	MY_ATTRIBUTE((nonnull(1,2)));
1165 
1166 /**********************************************************//**
1167 The following function returns the size of a data tuple when converted to
1168 a physical record.
1169 @return size */
1170 UNIV_INLINE
1171 ulint
1172 rec_get_converted_size(
1173 /*===================*/
1174 	dict_index_t*	index,	/*!< in: record descriptor */
1175 	const dtuple_t*	dtuple,	/*!< in: data tuple */
1176 	ulint		n_ext)	/*!< in: number of externally stored columns */
1177 	MY_ATTRIBUTE((warn_unused_result, nonnull));
1178 /** Copy the first n fields of a (copy of a) physical record to a data tuple.
1179 The fields are copied into the memory heap.
1180 @param[out]	tuple		data tuple
1181 @param[in]	rec		index record, or a copy thereof
1182 @param[in]	index		index of rec
1183 @param[in]	n_core		index->n_core_fields at the time rec was
1184 				copied, or 0 if non-leaf page record
1185 @param[in]	n_fields	number of fields to copy
1186 @param[in,out]	heap		memory heap */
1187 void
1188 rec_copy_prefix_to_dtuple(
1189 	dtuple_t*		tuple,
1190 	const rec_t*		rec,
1191 	const dict_index_t*	index,
1192 	ulint			n_core,
1193 	ulint			n_fields,
1194 	mem_heap_t*		heap)
1195 	MY_ATTRIBUTE((nonnull));
1196 /***************************************************************//**
1197 Validates the consistency of a physical record.
1198 @return TRUE if ok */
1199 ibool
1200 rec_validate(
1201 /*=========*/
1202 	const rec_t*	rec,	/*!< in: physical record */
1203 	const rec_offs*	offsets)/*!< in: array returned by rec_get_offsets() */
1204 	MY_ATTRIBUTE((nonnull));
1205 /***************************************************************//**
1206 Prints an old-style physical record. */
1207 void
1208 rec_print_old(
1209 /*==========*/
1210 	FILE*		file,	/*!< in: file where to print */
1211 	const rec_t*	rec)	/*!< in: physical record */
1212 	MY_ATTRIBUTE((nonnull));
1213 /***************************************************************//**
1214 Prints a spatial index record. */
1215 void
1216 rec_print_mbr_rec(
1217 /*==========*/
1218 	FILE*		file,	/*!< in: file where to print */
1219 	const rec_t*	rec,	/*!< in: physical record */
1220 	const rec_offs*	offsets)/*!< in: array returned by rec_get_offsets() */
1221 	MY_ATTRIBUTE((nonnull));
1222 /***************************************************************//**
1223 Prints a physical record. */
1224 void
1225 rec_print_new(
1226 /*==========*/
1227 	FILE*		file,	/*!< in: file where to print */
1228 	const rec_t*	rec,	/*!< in: physical record */
1229 	const rec_offs*	offsets)/*!< in: array returned by rec_get_offsets() */
1230 	MY_ATTRIBUTE((nonnull));
1231 /***************************************************************//**
1232 Prints a physical record. */
1233 void
1234 rec_print(
1235 /*======*/
1236 	FILE*			file,	/*!< in: file where to print */
1237 	const rec_t*		rec,	/*!< in: physical record */
1238 	const dict_index_t*	index)	/*!< in: record descriptor */
1239 	MY_ATTRIBUTE((nonnull));
1240 
1241 /** Pretty-print a record.
1242 @param[in,out]	o	output stream
1243 @param[in]	rec	physical record
1244 @param[in]	info	rec_get_info_bits(rec)
1245 @param[in]	offsets	rec_get_offsets(rec) */
1246 void
1247 rec_print(
1248 	std::ostream&	o,
1249 	const rec_t*	rec,
1250 	ulint		info,
1251 	const rec_offs*	offsets);
1252 
1253 /** Wrapper for pretty-printing a record */
1254 struct rec_index_print
1255 {
1256 	/** Constructor */
rec_index_printrec_index_print1257 	rec_index_print(const rec_t* rec, const dict_index_t* index) :
1258 		m_rec(rec), m_index(index)
1259 	{}
1260 
1261 	/** Record */
1262 	const rec_t*		m_rec;
1263 	/** Index */
1264 	const dict_index_t*	m_index;
1265 };
1266 
1267 /** Display a record.
1268 @param[in,out]	o	output stream
1269 @param[in]	r	record to display
1270 @return	the output stream */
1271 std::ostream&
1272 operator<<(std::ostream& o, const rec_index_print& r);
1273 
1274 /** Wrapper for pretty-printing a record */
1275 struct rec_offsets_print
1276 {
1277 	/** Constructor */
rec_offsets_printrec_offsets_print1278 	rec_offsets_print(const rec_t* rec, const rec_offs* offsets) :
1279 		m_rec(rec), m_offsets(offsets)
1280 	{}
1281 
1282 	/** Record */
1283 	const rec_t*		m_rec;
1284 	/** Offsets to each field */
1285 	const rec_offs*		m_offsets;
1286 };
1287 
1288 /** Display a record.
1289 @param[in,out]	o	output stream
1290 @param[in]	r	record to display
1291 @return	the output stream */
1292 ATTRIBUTE_COLD
1293 std::ostream&
1294 operator<<(std::ostream& o, const rec_offsets_print& r);
1295 
1296 /** Pretty-printer of records and tuples */
1297 class rec_printer : public std::ostringstream {
1298 public:
1299 	/** Construct a pretty-printed record.
1300 	@param rec	record with header
1301 	@param offsets	rec_get_offsets(rec, ...) */
1302 	ATTRIBUTE_COLD
rec_printer(const rec_t * rec,const rec_offs * offsets)1303 	rec_printer(const rec_t* rec, const rec_offs* offsets)
1304 		:
1305 		std::ostringstream ()
1306 	{
1307 		rec_print(*this, rec,
1308 			  rec_get_info_bits(rec, rec_offs_comp(offsets)),
1309 			  offsets);
1310 	}
1311 
1312 	/** Construct a pretty-printed record.
1313 	@param rec record, possibly lacking header
1314 	@param info rec_get_info_bits(rec)
1315 	@param offsets rec_get_offsets(rec, ...) */
1316 	ATTRIBUTE_COLD
rec_printer(const rec_t * rec,ulint info,const rec_offs * offsets)1317 	rec_printer(const rec_t* rec, ulint info, const rec_offs* offsets)
1318 		:
1319 		std::ostringstream ()
1320 	{
1321 		rec_print(*this, rec, info, offsets);
1322 	}
1323 
1324 	/** Construct a pretty-printed tuple.
1325 	@param tuple	data tuple */
1326 	ATTRIBUTE_COLD
rec_printer(const dtuple_t * tuple)1327 	rec_printer(const dtuple_t* tuple)
1328 		:
1329 		std::ostringstream ()
1330 	{
1331 		dtuple_print(*this, tuple);
1332 	}
1333 
1334 	/** Construct a pretty-printed tuple.
1335 	@param field	array of data tuple fields
1336 	@param n	number of fields */
1337 	ATTRIBUTE_COLD
rec_printer(const dfield_t * field,ulint n)1338 	rec_printer(const dfield_t* field, ulint n)
1339 		:
1340 		std::ostringstream ()
1341 	{
1342 		dfield_print(*this, field, n);
1343 	}
1344 
1345 	/** Destructor */
~rec_printer()1346 	~rec_printer() override {}
1347 
1348 private:
1349 	/** Copy constructor */
1350 	rec_printer(const rec_printer& other);
1351 	/** Assignment operator */
1352 	rec_printer& operator=(const rec_printer& other);
1353 };
1354 
1355 
1356 # ifdef UNIV_DEBUG
1357 /** Read the DB_TRX_ID of a clustered index record.
1358 @param[in]	rec	clustered index record
1359 @param[in]	index	clustered index
1360 @return the value of DB_TRX_ID */
1361 trx_id_t
1362 rec_get_trx_id(
1363 	const rec_t*		rec,
1364 	const dict_index_t*	index)
1365 	MY_ATTRIBUTE((nonnull, warn_unused_result));
1366 # endif /* UNIV_DEBUG */
1367 
1368 /* Maximum lengths for the data in a physical record if the offsets
1369 are given in one byte (resp. two byte) format. */
1370 #define REC_1BYTE_OFFS_LIMIT	0x7FUL
1371 #define REC_2BYTE_OFFS_LIMIT	0x7FFFUL
1372 
1373 /* The data size of record must not be larger than this on
1374 REDUNDANT row format because we reserve two upmost bits in a
1375 two byte offset for special purposes */
1376 #define REDUNDANT_REC_MAX_DATA_SIZE    (16383)
1377 
1378 /* The data size of record must be smaller than this on
1379 COMPRESSED row format because we reserve two upmost bits in a
1380 two byte offset for special purposes */
1381 #define COMPRESSED_REC_MAX_DATA_SIZE   (16384)
1382 
1383 #ifdef WITH_WSREP
1384 int wsrep_rec_get_foreign_key(
1385 	byte 		*buf,     /* out: extracted key */
1386 	ulint 		*buf_len, /* in/out: length of buf */
1387 	const rec_t*	rec,	  /* in: physical record */
1388 	dict_index_t*	index_for,  /* in: index for foreign table */
1389 	dict_index_t*	index_ref,  /* in: index for referenced table */
1390 	ibool		new_protocol); /* in: protocol > 1 */
1391 #endif /* WITH_WSREP */
1392 
1393 #include "rem0rec.inl"
1394 
1395 #endif /* !UNIV_INNOCHECKSUM */
1396 #endif /* rem0rec_h */
1397