📄 rem0rec.c
字号:
/************************************************************************Record manager(c) 1994-2001 Innobase OyCreated 5/30/1994 Heikki Tuuri*************************************************************************/#include "rem0rec.h"#ifdef UNIV_NONINL#include "rem0rec.ic"#endif#include "mtr0mtr.h"#include "mtr0log.h"/* PHYSICAL RECORD (OLD STYLE) ===========================The physical record, which is the data type of all the recordsfound in index pages of the database, has the following format(lower addresses and more significant bits inside a byte are belowrepresented on a higher text line):| offset of the end of the last field of data, the most significant bit is set to 1 if and only if the field is SQL-null, if the offset is 2-byte, then the second most significant bit is set to 1 if the field is stored on another page: mostly this will occur in the case of big BLOB fields |... | offset of the end of the first field of data + the SQL-null bit || 4 bits used to delete mark a record, and mark a predefined minimum record in alphabetical order || 4 bits giving the number of records owned by this record (this term is explained in page0page.h) || 13 bits giving the order number of this record in the heap of the index page || 10 bits giving the number of fields in this record || 1 bit which is set to 1 if the offsets above are given in one byte format, 0 if in two byte format || two bytes giving an absolute pointer to the next record in the page |ORIGIN of the record| first field of data | ... | last field of data |The origin of the record is the start address of the first field of data. The offsets are given relative to the origin. The offsets of the data fields are stored in an invertedorder because then the offset of the first fields are near the origin, giving maybe a better processor cache hit rate in searches.The offsets of the data fields are given as one-byte (if there are less than 127 bytes of data in the record) or two-byte unsigned integers. The most significant bitis not part of the offset, instead it indicates the SQL-nullif the bit is set to 1. *//* PHYSICAL RECORD (NEW STYLE) ===========================The physical record, which is the data type of all the recordsfound in index pages of the database, has the following format(lower addresses and more significant bits inside a byte are belowrepresented on a higher text line):| length of the last non-null variable-length field of data: if the maximum length is 255, one byte; otherwise, 0xxxxxxx (one byte, length=0..127), or 1exxxxxxxxxxxxxx (two bytes, length=128..16383, extern storage flag) |...| length of first variable-length field of data || SQL-null flags (1 bit per nullable field), padded to full bytes || 4 bits used to delete mark a record, and mark a predefined minimum record in alphabetical order || 4 bits giving the number of records owned by this record (this term is explained in page0page.h) || 13 bits giving the order number of this record in the heap of the index page || 3 bits record type: 000=conventional, 001=node pointer (inside B-tree), 010=infimum, 011=supremum, 1xx=reserved || two bytes giving a relative pointer to the next record in the page |ORIGIN of the record| first field of data |...| last field of data |The origin of the record is the start address of the first fieldof data. The offsets are given relative to the origin.The offsets of the data fields are stored in an invertedorder because then the offset of the first fields are near theorigin, giving maybe a better processor cache hit rate in searches.The offsets of the data fields are given as one-byte(if there are less than 127 bytes of data in the record)or two-byte unsigned integers. The most significant bitis not part of the offset, instead it indicates the SQL-nullif the bit is set to 1. *//* CANONICAL COORDINATES. A record can be seen as a singlestring of 'characters' in the following way: catenate the bytesin each field, in the order of fields. An SQL-null fieldis taken to be an empty sequence of bytes. Then afterthe position of each field insert in the string the 'character' <FIELD-END>, except that after an SQL-null fieldinsert <NULL-FIELD-END>. Now the ordinal position of eachbyte in this canonical string is its canonical coordinate.So, for the record ("AA", SQL-NULL, "BB", ""), the canonicalstring is "AA<FIELD_END><NULL-FIELD-END>BB<FIELD-END><FIELD-END>".We identify prefixes (= initial segments) of a recordwith prefixes of the canonical string. The canonicallength of the prefix is the length of the correspondingprefix of the canonical string. The canonical length ofa record is the length of its canonical string.For example, the maximal common prefix of records("AA", SQL-NULL, "BB", "C") and ("AA", SQL-NULL, "B", "C")is "AA<FIELD-END><NULL-FIELD-END>B", and its canonicallength is 5.A complete-field prefix of a record is a prefix which ends at theend of some field (containing also <FIELD-END>).A record is a complete-field prefix of another record, ifthe corresponding canonical strings have the same property. */ulint rec_dummy; /* this is used to fool compiler in rec_validate *//*******************************************************************Validates the consistency of an old-style physical record. */staticiboolrec_validate_old(/*=============*/ /* out: TRUE if ok */ rec_t* rec); /* in: physical record *//**********************************************************The following function determines the offsets to each field in therecord. The offsets are written to a previously allocated array ofulint, where rec_offs_n_fields(offsets) has been initialized to thenumber of fields in the record. The rest of the array will beinitialized by this function. rec_offs_base(offsets)[0] will be setto the extra size (if REC_OFFS_COMPACT is set, the record is in thenew format), and rec_offs_base(offsets)[1..n_fields] will be set tooffsets past the end of fields 0..n_fields, or to the beginning offields 1..n_fields+1. When the high-order bit of the offset at [i+1]is set (REC_OFFS_SQL_NULL), the field i is NULL. When the secondhigh-order bit of the offset at [i+1] is set (REC_OFFS_EXTERNAL), thefield i is being stored externally. */staticvoidrec_init_offsets(/*=============*/ /* out: the offsets */ rec_t* rec, /* in: physical record */ dict_index_t* index, /* in: record descriptor */ ulint* offsets)/* in/out: array of offsets; in: n=rec_offs_n_fields(offsets) */{ ulint i = 0; ulint offs; rec_offs_make_valid(rec, index, offsets); if (UNIV_LIKELY(index->table->comp)) { const byte* nulls; const byte* lens; dict_field_t* field; ulint null_mask; ulint status = rec_get_status(rec); ulint n_node_ptr_field = ULINT_UNDEFINED; switch (UNIV_EXPECT(status, REC_STATUS_ORDINARY)) { case REC_STATUS_INFIMUM: case REC_STATUS_SUPREMUM: /* the field is 8 bytes long */ rec_offs_base(offsets)[0] = REC_N_NEW_EXTRA_BYTES | REC_OFFS_COMPACT; rec_offs_base(offsets)[1] = 8; return; case REC_STATUS_NODE_PTR: n_node_ptr_field = dict_index_get_n_unique_in_tree(index); break; case REC_STATUS_ORDINARY: break; } nulls = rec - (REC_N_NEW_EXTRA_BYTES + 1); lens = nulls - (index->n_nullable + 7) / 8; offs = 0; null_mask = 1; /* read the lengths of fields 0..n */ do { ulint len; if (UNIV_UNLIKELY(i == n_node_ptr_field)) { len = offs += 4; goto resolved; } field = dict_index_get_nth_field(index, i); if (!(dtype_get_prtype(dict_col_get_type( dict_field_get_col(field))) & DATA_NOT_NULL)) { /* nullable field => read the null flag */ if (UNIV_UNLIKELY(!(byte) null_mask)) { nulls--; null_mask = 1; } if (*nulls & null_mask) { null_mask <<= 1; /* No length is stored for NULL fields. We do not advance offs, and we set the length to zero and enable the SQL NULL flag in offsets[]. */ len = offs | REC_OFFS_SQL_NULL; goto resolved; } null_mask <<= 1; } if (UNIV_UNLIKELY(!field->fixed_len)) { /* Variable-length field: read the length */ dtype_t* type = dict_col_get_type( dict_field_get_col(field)); len = *lens--; if (UNIV_UNLIKELY(dtype_get_len(type) > 255) || UNIV_UNLIKELY(dtype_get_mtype(type) == DATA_BLOB)) { if (len & 0x80) { /* 1exxxxxxx xxxxxxxx */ len <<= 8; len |= *lens--; offs += len & 0x3fff; if (UNIV_UNLIKELY(len & 0x4000)) { len = offs | REC_OFFS_EXTERNAL; } else { len = offs; } goto resolved; } } len = offs += len; } else { len = offs += field->fixed_len; } resolved: rec_offs_base(offsets)[i + 1] = len; } while (++i < rec_offs_n_fields(offsets)); *rec_offs_base(offsets) = (rec - (lens + 1)) | REC_OFFS_COMPACT; } else { /* Old-style record: determine extra size and end offsets */ offs = REC_N_OLD_EXTRA_BYTES; if (rec_get_1byte_offs_flag(rec)) { offs += rec_offs_n_fields(offsets); *rec_offs_base(offsets) = offs; /* Determine offsets to fields */ do { offs = rec_1_get_field_end_info(rec, i); if (offs & REC_1BYTE_SQL_NULL_MASK) { offs &= ~REC_1BYTE_SQL_NULL_MASK; offs |= REC_OFFS_SQL_NULL; } rec_offs_base(offsets)[1 + i] = offs; } while (++i < rec_offs_n_fields(offsets)); } else { offs += 2 * rec_offs_n_fields(offsets); *rec_offs_base(offsets) = offs; /* Determine offsets to fields */ do { offs = rec_2_get_field_end_info(rec, i); if (offs & REC_2BYTE_SQL_NULL_MASK) { offs &= ~REC_2BYTE_SQL_NULL_MASK; offs |= REC_OFFS_SQL_NULL; } if (offs & REC_2BYTE_EXTERN_MASK) { offs &= ~REC_2BYTE_EXTERN_MASK; offs |= REC_OFFS_EXTERNAL; } rec_offs_base(offsets)[1 + i] = offs; } while (++i < rec_offs_n_fields(offsets)); } }}/**********************************************************The following function determines the offsets to each fieldin the record. It can reuse a previously returned array. */ulint*rec_get_offsets_func(/*=================*/ /* out: the new offsets */ rec_t* rec, /* in: physical record */ dict_index_t* index, /* in: record descriptor */ ulint* offsets,/* in: array consisting of offsets[0] allocated elements, or an array from rec_get_offsets(), or NULL */ ulint n_fields,/* in: maximum number of initialized fields (ULINT_UNDEFINED if all fields) */ mem_heap_t** heap, /* in/out: memory heap */ const char* file, /* in: file name where called */ ulint line) /* in: line number where called */{ ulint n; ulint size; ut_ad(rec); ut_ad(index); ut_ad(heap); if (UNIV_LIKELY(index->table->comp)) { switch (UNIV_EXPECT(rec_get_status(rec), REC_STATUS_ORDINARY)) { case REC_STATUS_ORDINARY: n = dict_index_get_n_fields(index); break; case REC_STATUS_NODE_PTR: n = dict_index_get_n_unique_in_tree(index) + 1; break; case REC_STATUS_INFIMUM: case REC_STATUS_SUPREMUM: /* infimum or supremum record */ n = 1; break; default: ut_error; return(NULL); } } else { n = rec_get_n_fields_old(rec); } if (UNIV_UNLIKELY(n_fields < n)) { n = n_fields; } size = n + (1 + REC_OFFS_HEADER_SIZE); if (UNIV_UNLIKELY(!offsets) || UNIV_UNLIKELY(rec_offs_get_n_alloc(offsets) < size)) { if (!*heap) { *heap = mem_heap_create_func(size * sizeof(ulint), NULL, MEM_HEAP_DYNAMIC, file, line); } offsets = mem_heap_alloc(*heap, size * sizeof(ulint)); rec_offs_set_n_alloc(offsets, size); } rec_offs_set_n_fields(offsets, n); rec_init_offsets(rec, index, offsets); return(offsets);}/****************************************************************The following function is used to get a pointer to the nthdata field in an old-style record. */byte*rec_get_nth_field_old(/*==================*/ /* out: pointer to the field */ 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 */{ ulint os; ulint next_os; ut_ad(rec && len); ut_ad(n < rec_get_n_fields_old(rec)); if (n > REC_MAX_N_FIELDS) { fprintf(stderr, "Error: trying to access field %lu in rec\n", (ulong) n); ut_error; } if (rec == NULL) { fputs("Error: rec is NULL pointer\n", stderr); ut_error; } if (rec_get_1byte_offs_flag(rec)) { os = rec_1_get_field_start_offs(rec, n); next_os = rec_1_get_field_end_info(rec, n); if (next_os & REC_1BYTE_SQL_NULL_MASK) { *len = UNIV_SQL_NULL; return(rec + os); } next_os = next_os & ~REC_1BYTE_SQL_NULL_MASK; } else { os = rec_2_get_field_start_offs(rec, n); next_os = rec_2_get_field_end_info(rec, n); if (next_os & REC_2BYTE_SQL_NULL_MASK) { *len = UNIV_SQL_NULL; return(rec + os); } next_os = next_os & ~(REC_2BYTE_SQL_NULL_MASK | REC_2BYTE_EXTERN_MASK); } *len = next_os - os; ut_ad(*len < UNIV_PAGE_SIZE); return(rec + os);}/**************************************************************The following function returns the size of a data tuple when converted toa new-style physical record. */ulintrec_get_converted_size_new(/*=======================*/ /* out: size */ dict_index_t* index, /* in: record descriptor */ dtuple_t* dtuple) /* in: data tuple */{ ulint size = REC_N_NEW_EXTRA_BYTES + (index->n_nullable + 7) / 8; dict_field_t* field; dtype_t* type; ulint i; ulint n_fields; ut_ad(index && dtuple); ut_ad(index->table->comp); switch (dtuple_get_info_bits(dtuple) & REC_NEW_STATUS_MASK) { case REC_STATUS_ORDINARY: n_fields = dict_index_get_n_fields(index); ut_ad(n_fields == dtuple_get_n_fields(dtuple)); break; case REC_STATUS_NODE_PTR: n_fields = dict_index_get_n_unique_in_tree(index); ut_ad(n_fields + 1 == dtuple_get_n_fields(dtuple)); ut_ad(dtuple_get_nth_field(dtuple, n_fields)->len == 4); size += 4; /* child page number */ break; case REC_STATUS_INFIMUM: case REC_STATUS_SUPREMUM: /* infimum or supremum record, 8 bytes */ return(size + 8); /* no extra data needed */ default: ut_a(0); return(ULINT_UNDEFINED); } /* read the lengths of fields 0..n */ for (i = 0; i < n_fields; i++) { ulint len = dtuple_get_nth_field(dtuple, i)->len; field = dict_index_get_nth_field(index, i); type = dict_col_get_type(dict_field_get_col(field)); ut_ad(len != UNIV_SQL_NULL || !(dtype_get_prtype(type) & DATA_NOT_NULL)); if (len == UNIV_SQL_NULL) { /* No length is stored for NULL fields. */ continue; } ut_ad(len <= dtype_get_len(type) || dtype_get_mtype(type) == DATA_BLOB); ut_ad(!field->fixed_len || len == field->fixed_len); if (field->fixed_len) { } else if (len < 128 || (dtype_get_len(type) < 256 && dtype_get_mtype(type) != DATA_BLOB)) { size++; } else { size += 2; } size += len;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -