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