/************************************************************************ Record manager (c) 1994-1996 Innobase Oy Created 5/30/1994 Heikki Tuuri *************************************************************************/ #include "mach0data.h" #include "ut0byte.h" /* Offsets of the bit-fields in the record. NOTE! In the table the most significant bytes and bits are written below less significant. (1) byte offset (2) bit usage within byte downward from origin -> 1 8 bits pointer to next record 2 8 bits pointer to next record 3 1 bit short flag 7 bits number of fields 4 3 bits number of fields 5 bits heap number 5 8 bits heap number 6 4 bits n_owned 4 bits info bits */ /* We list the byte offsets from the origin of the record, the mask, and the shift needed to obtain each bit-field of the record. */ #define REC_NEXT 2 #define REC_NEXT_MASK 0xFFFFUL #define REC_NEXT_SHIFT 0 #define REC_SHORT 3 /* This is single byte bit-field */ #define REC_SHORT_MASK 0x1UL #define REC_SHORT_SHIFT 0 #define REC_N_FIELDS 4 #define REC_N_FIELDS_MASK 0x7FEUL #define REC_N_FIELDS_SHIFT 1 #define REC_HEAP_NO 5 #define REC_HEAP_NO_MASK 0xFFF8UL #define REC_HEAP_NO_SHIFT 3 #define REC_N_OWNED 6 /* This is single byte bit-field */ #define REC_N_OWNED_MASK 0xFUL #define REC_N_OWNED_SHIFT 0 #define REC_INFO_BITS_MASK 0xF0UL #define REC_INFO_BITS_SHIFT 0 /* The deleted flag in info bits */ #define REC_INFO_DELETED_FLAG 0x20UL /* when bit is set to 1, it means the record has been delete marked */ /* The following masks are used to filter the SQL null bit from one-byte and two-byte offsets */ #define REC_1BYTE_SQL_NULL_MASK 0x80UL #define REC_2BYTE_SQL_NULL_MASK 0x8000UL /* In a 2-byte offset the second most significant bit denotes a field stored to another page: */ #define REC_2BYTE_EXTERN_MASK 0x4000UL /**************************************************************** Return field length or UNIV_SQL_NULL. */ UNIV_INLINE ulint rec_get_nth_field_len( /*==================*/ /* out: length of the field; UNIV_SQL_NULL if SQL null */ rec_t* rec, /* in: record */ ulint n) /* in: index of the field */ { ulint len; rec_get_nth_field(rec, n, &len); return(len); } /*************************************************************** Sets the value of the ith field SQL null bit. */ void rec_set_nth_field_null_bit( /*=======================*/ rec_t* rec, /* in: record */ ulint i, /* in: ith field */ ibool val); /* in: value to set */ /*************************************************************** Sets a record field to SQL null. The physical size of the field is not changed. */ void rec_set_nth_field_sql_null( /*=======================*/ rec_t* rec, /* in: record */ ulint n); /* in: index of the field */ /********************************************************** Gets a bit field from within 1 byte. */ UNIV_INLINE ulint rec_get_bit_field_1( /*================*/ rec_t* rec, /* in: pointer to record origin */ ulint offs, /* in: offset from the origin down */ ulint mask, /* in: mask used to filter bits */ ulint shift) /* in: shift right applied after masking */ { ut_ad(rec); return((mach_read_from_1(rec - offs) & mask) >> shift); } /********************************************************** Sets a bit field within 1 byte. */ UNIV_INLINE void rec_set_bit_field_1( /*================*/ rec_t* rec, /* in: pointer to record origin */ ulint val, /* in: value to set */ ulint offs, /* in: offset from the origin down */ ulint mask, /* in: mask used to filter bits */ ulint shift) /* in: shift right applied after masking */ { ut_ad(rec); ut_ad(offs <= REC_N_EXTRA_BYTES); ut_ad(mask); ut_ad(mask <= 0xFFUL); ut_ad(((mask >> shift) << shift) == mask); ut_ad(((val << shift) & mask) == (val << shift)); mach_write_to_1(rec - offs, (mach_read_from_1(rec - offs) & ~mask) | (val << shift)); } /********************************************************** Gets a bit field from within 2 bytes. */ UNIV_INLINE ulint rec_get_bit_field_2( /*================*/ rec_t* rec, /* in: pointer to record origin */ ulint offs, /* in: offset from the origin down */ ulint mask, /* in: mask used to filter bits */ ulint shift) /* in: shift right applied after masking */ { ut_ad(rec); return((mach_read_from_2(rec - offs) & mask) >> shift); } /********************************************************** Sets a bit field within 2 bytes. */ UNIV_INLINE void rec_set_bit_field_2( /*================*/ rec_t* rec, /* in: pointer to record origin */ ulint val, /* in: value to set */ ulint offs, /* in: offset from the origin down */ ulint mask, /* in: mask used to filter bits */ ulint shift) /* in: shift right applied after masking */ { ut_ad(rec); ut_ad(offs <= REC_N_EXTRA_BYTES); ut_ad(mask > 0xFFUL); ut_ad(mask <= 0xFFFFUL); ut_ad((mask >> shift) & 1); ut_ad(0 == ((mask >> shift) & ((mask >> shift) + 1))); ut_ad(((mask >> shift) << shift) == mask); ut_ad(((val << shift) & mask) == (val << shift)); #ifdef UNIV_DEBUG { ulint m; /* The following assertion checks that the masks of currently defined bit-fields in bytes 3-6 do not overlap. */ m = (ulint)((REC_SHORT_MASK << (8 * (REC_SHORT - 3))) + (REC_N_FIELDS_MASK << (8 * (REC_N_FIELDS - 4))) + (REC_HEAP_NO_MASK << (8 * (REC_HEAP_NO - 4))) + (REC_N_OWNED_MASK << (8 * (REC_N_OWNED - 3))) + (REC_INFO_BITS_MASK << (8 * (REC_INFO_BITS - 3)))); if (m != ut_dbg_zero + 0xFFFFFFFFUL) { fprintf(stderr, "Sum of masks %lx\n", m); ut_error; } } #endif mach_write_to_2(rec - offs, (mach_read_from_2(rec - offs) & ~mask) | (val << shift)); } /********************************************************** The following function is used to get the offset of the next chained record on the same page. */ UNIV_INLINE ulint rec_get_next_offs( /*==============*/ /* out: the page offset of the next chained record */ rec_t* rec) /* in: physical record */ { ulint ret; ut_ad(rec); ret = rec_get_bit_field_2(rec, REC_NEXT, REC_NEXT_MASK, REC_NEXT_SHIFT); ut_ad(ret < UNIV_PAGE_SIZE); return(ret); } /********************************************************** The following function is used to set the next record offset field of the record. */ UNIV_INLINE void rec_set_next_offs( /*==============*/ rec_t* rec, /* in: physical record */ ulint next) /* in: offset of the next record */ { ut_ad(rec); ut_ad(UNIV_PAGE_SIZE > next); rec_set_bit_field_2(rec, next, REC_NEXT, REC_NEXT_MASK, REC_NEXT_SHIFT); } /********************************************************** The following function is used to get the number of fields in the record. */ UNIV_INLINE ulint rec_get_n_fields( /*=============*/ /* out: number of data fields */ rec_t* rec) /* in: physical record */ { ulint ret; ut_ad(rec); ret = rec_get_bit_field_2(rec, REC_N_FIELDS, REC_N_FIELDS_MASK, REC_N_FIELDS_SHIFT); ut_ad(ret <= REC_MAX_N_FIELDS); ut_ad(ret > 0); return(ret); } /********************************************************** The following function is used to set the number of fields field in the record. */ UNIV_INLINE void rec_set_n_fields( /*=============*/ rec_t* rec, /* in: physical record */ ulint n_fields) /* in: the number of fields */ { ut_ad(rec); ut_ad(n_fields <= REC_MAX_N_FIELDS); ut_ad(n_fields > 0); rec_set_bit_field_2(rec, n_fields, REC_N_FIELDS, REC_N_FIELDS_MASK, REC_N_FIELDS_SHIFT); } /********************************************************** The following function is used to get the number of records owned by the previous directory record. */ UNIV_INLINE ulint rec_get_n_owned( /*============*/ /* out: number of owned records */ rec_t* rec) /* in: physical record */ { ulint ret; ut_ad(rec); ret = rec_get_bit_field_1(rec, REC_N_OWNED, REC_N_OWNED_MASK, REC_N_OWNED_SHIFT); ut_ad(ret <= REC_MAX_N_OWNED); return(ret); } /********************************************************** The following function is used to set the number of owned records. */ UNIV_INLINE void rec_set_n_owned( /*============*/ rec_t* rec, /* in: physical record */ ulint n_owned) /* in: the number of owned */ { ut_ad(rec); ut_ad(n_owned <= REC_MAX_N_OWNED); rec_set_bit_field_1(rec, n_owned, REC_N_OWNED, REC_N_OWNED_MASK, REC_N_OWNED_SHIFT); } /********************************************************** The following function is used to retrieve the info bits of a record. */ UNIV_INLINE ulint rec_get_info_bits( /*==============*/ /* out: info bits */ rec_t* rec) /* in: physical record */ { ulint ret; ut_ad(rec); ret = rec_get_bit_field_1(rec, REC_INFO_BITS, REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT); ut_ad((ret & ~REC_INFO_BITS_MASK) == 0); return(ret); } /********************************************************** The following function is used to set the info bits of a record. */ UNIV_INLINE void rec_set_info_bits( /*==============*/ rec_t* rec, /* in: physical record */ ulint bits) /* in: info bits */ { ut_ad(rec); ut_ad((bits & ~REC_INFO_BITS_MASK) == 0); rec_set_bit_field_1(rec, bits, REC_INFO_BITS, REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT); } /********************************************************** Gets the value of the deleted flag in info bits. */ UNIV_INLINE ibool rec_info_bits_get_deleted_flag( /*===========================*/ /* out: TRUE if deleted flag set */ ulint info_bits) /* in: info bits from a record */ { if (info_bits & REC_INFO_DELETED_FLAG) { return(TRUE); } return(FALSE); } /********************************************************** The following function tells if record is delete marked. */ UNIV_INLINE ibool rec_get_deleted_flag( /*=================*/ /* out: TRUE if delete marked */ rec_t* rec) /* in: physical record */ { if (REC_INFO_DELETED_FLAG & rec_get_info_bits(rec)) { return(TRUE); } return(FALSE); } /********************************************************** The following function is used to set the deleted bit. */ UNIV_INLINE void rec_set_deleted_flag( /*=================*/ rec_t* rec, /* in: physical record */ ibool flag) /* in: TRUE if delete marked */ { ulint old_val; ulint new_val; ut_ad(TRUE == 1); ut_ad(flag <= TRUE); old_val = rec_get_info_bits(rec); if (flag) { new_val = REC_INFO_DELETED_FLAG | old_val; } else { new_val = ~REC_INFO_DELETED_FLAG & old_val; } rec_set_info_bits(rec, new_val); } /********************************************************** The following function is used to get the order number of the record in the heap of the index page. */ UNIV_INLINE ulint rec_get_heap_no( /*=============*/ /* out: heap order number */ rec_t* rec) /* in: physical record */ { ulint ret; ut_ad(rec); ret = rec_get_bit_field_2(rec, REC_HEAP_NO, REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT); ut_ad(ret <= REC_MAX_HEAP_NO); return(ret); } /********************************************************** The following function is used to set the heap number field in the record. */ UNIV_INLINE void rec_set_heap_no( /*=============*/ rec_t* rec, /* in: physical record */ ulint heap_no)/* in: the heap number */ { ut_ad(heap_no <= REC_MAX_HEAP_NO); rec_set_bit_field_2(rec, heap_no, REC_HEAP_NO, REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT); } /********************************************************** The following function is used to test whether the data offsets in the record are stored in one-byte or two-byte format. */ UNIV_INLINE ibool rec_get_1byte_offs_flag( /*====================*/ /* out: TRUE if 1-byte form */ rec_t* rec) /* in: physical record */ { ut_ad(TRUE == 1); return(rec_get_bit_field_1(rec, REC_SHORT, REC_SHORT_MASK, REC_SHORT_SHIFT)); } /********************************************************** The following function is used to set the 1-byte offsets flag. */ UNIV_INLINE void rec_set_1byte_offs_flag( /*====================*/ rec_t* rec, /* in: physical record */ ibool flag) /* in: TRUE if 1byte form */ { ut_ad(TRUE == 1); ut_ad(flag <= TRUE); rec_set_bit_field_1(rec, flag, REC_SHORT, REC_SHORT_MASK, REC_SHORT_SHIFT); } /********************************************************** Returns the offset of nth field end if the record is stored in the 1-byte offsets form. If the field is SQL null, the flag is ORed in the returned value. */ UNIV_INLINE ulint rec_1_get_field_end_info( /*=====================*/ /* out: offset of the start of the field, SQL null flag ORed */ rec_t* rec, /* in: record */ ulint n) /* in: field index */ { ut_ad(rec_get_1byte_offs_flag(rec)); ut_ad(n < rec_get_n_fields(rec)); return(mach_read_from_1(rec - (REC_N_EXTRA_BYTES + n + 1))); } /********************************************************** Returns the offset of nth field end if the record is stored in the 2-byte offsets form. If the field is SQL null, the flag is ORed in the returned value. */ UNIV_INLINE ulint rec_2_get_field_end_info( /*=====================*/ /* out: offset of the start of the field, SQL null flag and extern storage flag ORed */ rec_t* rec, /* in: record */ ulint n) /* in: field index */ { ut_ad(!rec_get_1byte_offs_flag(rec)); ut_ad(n < rec_get_n_fields(rec)); return(mach_read_from_2(rec - (REC_N_EXTRA_BYTES + 2 * n + 2))); } /*************************************************************** Gets the value of the ith field extern storage bit. If it is TRUE it means that the field is stored on another page. */ UNIV_INLINE ibool rec_get_nth_field_extern_bit( /*=========================*/ /* in: TRUE or FALSE */ rec_t* rec, /* in: record */ ulint i) /* in: ith field */ { ulint info; if (rec_get_1byte_offs_flag(rec)) { return(FALSE); } info = rec_2_get_field_end_info(rec, i); if (info & REC_2BYTE_EXTERN_MASK) { return(TRUE); } return(FALSE); } /********************************************************** Returns TRUE if the extern bit is set in any of the fields of rec. */ UNIV_INLINE ibool rec_contains_externally_stored_field( /*=================================*/ /* out: TRUE if a field is stored externally */ rec_t* rec) /* in: record */ { ulint n; ulint i; if (rec_get_1byte_offs_flag(rec)) { return(FALSE); } n = rec_get_n_fields(rec); for (i = 0; i < n; i++) { if (rec_get_nth_field_extern_bit(rec, i)) { return(TRUE); } } return(FALSE); } /********************************************************** Returns the offset of n - 1th field end if the record is stored in the 1-byte offsets form. If the field is SQL null, the flag is ORed in the returned value. This function and the 2-byte counterpart are defined here because the C-compilerwas not able to sum negative and positive constant offsets, and warned of constant arithmetic overflow within the compiler. */ UNIV_INLINE ulint rec_1_get_prev_field_end_info( /*==========================*/ /* out: offset of the start of the PREVIOUS field, SQL null flag ORed */ rec_t* rec, /* in: record */ ulint n) /* in: field index */ { ut_ad(rec_get_1byte_offs_flag(rec)); ut_ad(n <= rec_get_n_fields(rec)); return(mach_read_from_1(rec - (REC_N_EXTRA_BYTES + n))); } /********************************************************** Returns the offset of n - 1th field end if the record is stored in the 2-byte offsets form. If the field is SQL null, the flag is ORed in the returned value. */ UNIV_INLINE ulint rec_2_get_prev_field_end_info( /*==========================*/ /* out: offset of the start of the PREVIOUS field, SQL null flag ORed */ rec_t* rec, /* in: record */ ulint n) /* in: field index */ { ut_ad(!rec_get_1byte_offs_flag(rec)); ut_ad(n <= rec_get_n_fields(rec)); return(mach_read_from_2(rec - (REC_N_EXTRA_BYTES + 2 * n))); } /********************************************************** Sets the field end info for the nth field if the record is stored in the 1-byte format. */ UNIV_INLINE void rec_1_set_field_end_info( /*=====================*/ rec_t* rec, /* in: record */ ulint n, /* in: field index */ ulint info) /* in: value to set */ { ut_ad(rec_get_1byte_offs_flag(rec)); ut_ad(n < rec_get_n_fields(rec)); mach_write_to_1(rec - (REC_N_EXTRA_BYTES + n + 1), info); } /********************************************************** Sets the field end info for the nth field if the record is stored in the 2-byte format. */ UNIV_INLINE void rec_2_set_field_end_info( /*=====================*/ rec_t* rec, /* in: record */ ulint n, /* in: field index */ ulint info) /* in: value to set */ { ut_ad(!rec_get_1byte_offs_flag(rec)); ut_ad(n < rec_get_n_fields(rec)); mach_write_to_2(rec - (REC_N_EXTRA_BYTES + 2 * n + 2), info); } /********************************************************** Returns the offset of nth field start if the record is stored in the 1-byte offsets form. */ UNIV_INLINE ulint rec_1_get_field_start_offs( /*=======================*/ /* out: offset of the start of the field */ rec_t* rec, /* in: record */ ulint n) /* in: field index */ { ut_ad(rec_get_1byte_offs_flag(rec)); ut_ad(n <= rec_get_n_fields(rec)); if (n == 0) { return(0); } return(rec_1_get_prev_field_end_info(rec, n) & ~REC_1BYTE_SQL_NULL_MASK); } /********************************************************** Returns the offset of nth field start if the record is stored in the 2-byte offsets form. */ UNIV_INLINE ulint rec_2_get_field_start_offs( /*=======================*/ /* out: offset of the start of the field */ rec_t* rec, /* in: record */ ulint n) /* in: field index */ { ut_ad(!rec_get_1byte_offs_flag(rec)); ut_ad(n <= rec_get_n_fields(rec)); if (n == 0) { return(0); } return(rec_2_get_prev_field_end_info(rec, n) & ~(REC_2BYTE_SQL_NULL_MASK | REC_2BYTE_EXTERN_MASK)); } /********************************************************** The following function is used to read the offset of the start of a data field in the record. The start of an SQL null field is the end offset of the previous non-null field, or 0, if none exists. If n is the number of the last field + 1, then the end offset of the last field is returned. */ UNIV_INLINE ulint rec_get_field_start_offs( /*=====================*/ /* out: offset of the start of the field */ rec_t* rec, /* in: record */ ulint n) /* in: field index */ { ut_ad(rec); ut_ad(n <= rec_get_n_fields(rec)); if (n == 0) { return(0); } if (rec_get_1byte_offs_flag(rec)) { return(rec_1_get_field_start_offs(rec, n)); } return(rec_2_get_field_start_offs(rec, n)); } /**************************************************************** Gets the physical size of a field. Also an SQL null may have a field of size > 0, if the data type is of a fixed size. */ UNIV_INLINE ulint rec_get_nth_field_size( /*===================*/ /* out: field size in bytes */ rec_t* rec, /* in: record */ ulint n) /* in: index of the field */ { ulint os; ulint next_os; os = rec_get_field_start_offs(rec, n); next_os = rec_get_field_start_offs(rec, n + 1); ut_ad(next_os - os < UNIV_PAGE_SIZE); return(next_os - os); } /**************************************************************** The following function is used to get a copy of the nth data field in a record to a buffer. */ UNIV_INLINE void rec_copy_nth_field( /*===============*/ void* buf, /* in: pointer to the buffer */ rec_t* rec, /* in: record */ ulint n, /* in: index of the field */ ulint* len) /* out: length of the field; UNIV_SQL_NULL if SQL null */ { byte* ptr; ut_ad(buf && rec && len); ptr = rec_get_nth_field(rec, n, len); if (*len == UNIV_SQL_NULL) { return; } ut_memcpy(buf, ptr, *len); } /*************************************************************** This is used to modify the value of an already existing field in a record. The previous value must have exactly the same size as the new value. If len is UNIV_SQL_NULL then the field is treated as an SQL null. */ UNIV_INLINE void rec_set_nth_field( /*==============*/ rec_t* rec, /* in: record */ ulint n, /* in: index of the field */ void* data, /* in: pointer to the data if not SQL null */ ulint len) /* in: length of the data or UNIV_SQL_NULL */ { byte* data2; ulint len2; ut_ad((len == UNIV_SQL_NULL) || (rec_get_nth_field_size(rec, n) == len)); if (len == UNIV_SQL_NULL) { rec_set_nth_field_sql_null(rec, n); return; } data2 = rec_get_nth_field(rec, n, &len2); ut_memcpy(data2, data, len); if (len2 == UNIV_SQL_NULL) { rec_set_nth_field_null_bit(rec, n, FALSE); } } /************************************************************** The following function returns the data size of a physical record, that is the sum of field lengths. SQL null fields are counted as length 0 fields. The value returned by the function is the distance from record origin to record end in bytes. */ UNIV_INLINE ulint rec_get_data_size( /*==============*/ /* out: size */ rec_t* rec) /* in: physical record */ { ut_ad(rec); return(rec_get_field_start_offs(rec, rec_get_n_fields(rec))); } /************************************************************** Returns the total size of record minus data size of record. The value returned by the function is the distance from record start to record origin in bytes. */ UNIV_INLINE ulint rec_get_extra_size( /*===============*/ /* out: size */ rec_t* rec) /* in: physical record */ { ulint n_fields; ut_ad(rec); n_fields = rec_get_n_fields(rec); if (rec_get_1byte_offs_flag(rec)) { return(REC_N_EXTRA_BYTES + n_fields); } return(REC_N_EXTRA_BYTES + 2 * n_fields); } /************************************************************** Returns the total size of a physical record. */ UNIV_INLINE ulint rec_get_size( /*=========*/ /* out: size */ rec_t* rec) /* in: physical record */ { ulint n_fields; ut_ad(rec); n_fields = rec_get_n_fields(rec); if (rec_get_1byte_offs_flag(rec)) { return(REC_N_EXTRA_BYTES + n_fields + rec_1_get_field_start_offs(rec, n_fields)); } return(REC_N_EXTRA_BYTES + 2 * n_fields + rec_2_get_field_start_offs(rec, n_fields)); } /************************************************************** Returns a pointer to the end of the record. */ UNIV_INLINE byte* rec_get_end( /*========*/ /* out: pointer to end */ rec_t* rec) /* in: pointer to record */ { return(rec + rec_get_data_size(rec)); } /************************************************************** Returns a pointer to the start of the record. */ UNIV_INLINE byte* rec_get_start( /*==========*/ /* out: pointer to start */ rec_t* rec) /* in: pointer to record */ { return(rec - rec_get_extra_size(rec)); } /******************************************************************* Copies a physical record to a buffer. */ UNIV_INLINE rec_t* rec_copy( /*=====*/ /* out: pointer to the origin of the copied record */ void* buf, /* in: buffer */ rec_t* rec) /* in: physical record */ { ulint extra_len; ulint data_len; ut_ad(rec && buf); ut_ad(rec_validate(rec)); extra_len = rec_get_extra_size(rec); data_len = rec_get_data_size(rec); ut_memcpy(buf, rec - extra_len, extra_len + data_len); return((byte*)buf + extra_len); } /************************************************************** Returns the extra size of a physical record if we know its data size and the number of fields. */ UNIV_INLINE ulint rec_get_converted_extra_size( /*=========================*/ /* out: extra size */ ulint data_size, /* in: data size */ ulint n_fields) /* in: number of fields */ { if (data_size <= REC_1BYTE_OFFS_LIMIT) { return(REC_N_EXTRA_BYTES + n_fields); } return(REC_N_EXTRA_BYTES + 2 * n_fields); } /************************************************************** The following function returns the size of a data tuple when converted to a physical record. */ UNIV_INLINE ulint rec_get_converted_size( /*===================*/ /* out: size */ dtuple_t* dtuple) /* in: data tuple */ { ulint data_size; ulint extra_size; ut_ad(dtuple); ut_ad(dtuple_check_typed(dtuple)); data_size = dtuple_get_data_size(dtuple); extra_size = rec_get_converted_extra_size( data_size, dtuple_get_n_fields(dtuple)); return(data_size + extra_size); } /**************************************************************** Folds a prefix of a physical record to a ulint. Folds only existing fields, that is, checks that we do not run out of the record. */ UNIV_INLINE ulint rec_fold( /*=====*/ /* out: the folded value */ rec_t* rec, /* in: the physical record */ ulint n_fields, /* in: number of complete fields to fold */ ulint n_bytes, /* in: number of bytes to fold in an incomplete last field */ dulint tree_id) /* in: index tree id */ { ulint i; byte* data; ulint len; ulint fold; ulint n_fields_rec; ut_ad(rec_validate(rec)); ut_ad(n_fields <= rec_get_n_fields(rec)); ut_ad((n_fields < rec_get_n_fields(rec)) || (n_bytes == 0)); ut_ad(n_fields + n_bytes > 0); n_fields_rec = rec_get_n_fields(rec); if (n_fields > n_fields_rec) { n_fields = n_fields_rec; } if (n_fields == n_fields_rec) { n_bytes = 0; } fold = ut_fold_dulint(tree_id); for (i = 0; i < n_fields; i++) { data = rec_get_nth_field(rec, i, &len); if (len != UNIV_SQL_NULL) { fold = ut_fold_ulint_pair(fold, ut_fold_binary(data, len)); } } if (n_bytes > 0) { data = rec_get_nth_field(rec, i, &len); if (len != UNIV_SQL_NULL) { if (len > n_bytes) { len = n_bytes; } fold = ut_fold_ulint_pair(fold, ut_fold_binary(data, len)); } } return(fold); } /************************************************************* Builds a physical record out of a data tuple and stores it beginning from the address destination. */ UNIV_INLINE rec_t* rec_convert_dtuple_to_rec( /*======================*/ /* out: pointer to the origin of physical record */ byte* destination, /* in: start address of the physical record */ dtuple_t* dtuple) /* in: data tuple */ { return(rec_convert_dtuple_to_rec_low(destination, dtuple, dtuple_get_data_size(dtuple))); }