/********************************************************//** Parses a log record written by mlog_open_and_write_index. @return parsed record end, NULL if not a complete record */ UNIV_INTERN byte* mlog_parse_index( /*=============*/ byte* ptr, /*!< in: buffer */ const byte* end_ptr,/*!< in: buffer end */ ibool comp, /*!< in: TRUE=compact record format */ dict_index_t** index) /*!< out, own: dummy index */ { ulint i, n, n_uniq; dict_table_t* table; dict_index_t* ind; ut_ad(comp == FALSE || comp == TRUE); if (comp) { if (end_ptr < ptr + 4) { return(NULL); } n = mach_read_from_2(ptr); ptr += 2; n_uniq = mach_read_from_2(ptr); ptr += 2; ut_ad(n_uniq <= n); if (end_ptr < ptr + n * 2) { return(NULL); } } else { n = n_uniq = 1; } table = dict_mem_table_create("LOG_DUMMY", DICT_HDR_SPACE, n, comp ? DICT_TF_COMPACT : 0); ind = dict_mem_index_create("LOG_DUMMY", "LOG_DUMMY", DICT_HDR_SPACE, 0, n); ind->table = table; ind->n_uniq = (unsigned int) n_uniq; if (n_uniq != n) { ut_a(n_uniq + DATA_ROLL_PTR <= n); ind->type = DICT_CLUSTERED; } if (comp) { for (i = 0; i < n; i++) { ulint len = mach_read_from_2(ptr); ptr += 2; /* The high-order bit of len is the NOT NULL flag; the rest is 0 or 0x7fff for variable-length fields, and 1..0x7ffe for fixed-length fields. */ dict_mem_table_add_col( table, NULL, NULL, ((len + 1) & 0x7fff) <= 1 ? DATA_BINARY : DATA_FIXBINARY, len & 0x8000 ? DATA_NOT_NULL : 0, len & 0x7fff); dict_index_add_col(ind, table, dict_table_get_nth_col(table, i), 0); } dict_table_add_system_columns(table, table->heap); if (n_uniq != n) { /* Identify DB_TRX_ID and DB_ROLL_PTR in the index. */ ut_a(DATA_TRX_ID_LEN == dict_index_get_nth_col(ind, DATA_TRX_ID - 1 + n_uniq)->len); ut_a(DATA_ROLL_PTR_LEN == dict_index_get_nth_col(ind, DATA_ROLL_PTR - 1 + n_uniq)->len); ind->fields[DATA_TRX_ID - 1 + n_uniq].col = &table->cols[n + DATA_TRX_ID]; ind->fields[DATA_ROLL_PTR - 1 + n_uniq].col = &table->cols[n + DATA_ROLL_PTR]; } } /* avoid ut_ad(index->cached) in dict_index_get_n_unique_in_tree */ ind->cached = TRUE; *index = ind; return(ptr); }
/**********************************************************************//** Reports in the undo log of an update or delete marking of a clustered index record. @return byte offset of the inserted undo log entry on the page if succeed, 0 if fail */ static ulint trx_undo_page_report_modify( /*========================*/ page_t* undo_page, /*!< in: undo log page */ trx_t* trx, /*!< in: transaction */ dict_index_t* index, /*!< in: clustered index where update or delete marking is done */ const rec_t* rec, /*!< in: clustered index record which has NOT yet been modified */ const ulint* offsets, /*!< in: rec_get_offsets(rec, index) */ const upd_t* update, /*!< in: update vector which tells the columns to be updated; in the case of a delete, this should be set to NULL */ ulint cmpl_info, /*!< in: compiler info on secondary index updates */ mtr_t* mtr) /*!< in: mtr */ { dict_table_t* table; ulint first_free; byte* ptr; const byte* field; ulint flen; ulint col_no; ulint type_cmpl; byte* type_cmpl_ptr; ulint i; trx_id_t trx_id; ibool ignore_prefix = FALSE; byte ext_buf[REC_MAX_INDEX_COL_LEN + BTR_EXTERN_FIELD_REF_SIZE]; ut_a(dict_index_is_clust(index)); ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_UPDATE); table = index->table; first_free = mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE); ptr = undo_page + first_free; ut_ad(first_free <= UNIV_PAGE_SIZE); if (trx_undo_left(undo_page, ptr) < 50) { /* NOTE: the value 50 must be big enough so that the general fields written below fit on the undo log page */ return(0); } /* Reserve 2 bytes for the pointer to the next undo log record */ ptr += 2; /* Store first some general parameters to the undo log */ if (!update) { type_cmpl = TRX_UNDO_DEL_MARK_REC; } else if (rec_get_deleted_flag(rec, dict_table_is_comp(table))) { type_cmpl = TRX_UNDO_UPD_DEL_REC; /* We are about to update a delete marked record. We don't typically need the prefix in this case unless the delete marking is done by the same transaction (which we check below). */ ignore_prefix = TRUE; } else { type_cmpl = TRX_UNDO_UPD_EXIST_REC; } type_cmpl |= cmpl_info * TRX_UNDO_CMPL_INFO_MULT; type_cmpl_ptr = ptr; *ptr++ = (byte) type_cmpl; ptr += mach_dulint_write_much_compressed(ptr, trx->undo_no); ptr += mach_dulint_write_much_compressed(ptr, table->id); /*----------------------------------------*/ /* Store the state of the info bits */ *ptr++ = (byte) rec_get_info_bits(rec, dict_table_is_comp(table)); /* Store the values of the system columns */ field = rec_get_nth_field(rec, offsets, dict_index_get_sys_col_pos( index, DATA_TRX_ID), &flen); ut_ad(flen == DATA_TRX_ID_LEN); trx_id = trx_read_trx_id(field); /* If it is an update of a delete marked record, then we are allowed to ignore blob prefixes if the delete marking was done by some other trx as it must have committed by now for us to allow an over-write. */ if (ignore_prefix) { ignore_prefix = ut_dulint_cmp(trx_id, trx->id) != 0; } ptr += mach_dulint_write_compressed(ptr, trx_id); field = rec_get_nth_field(rec, offsets, dict_index_get_sys_col_pos( index, DATA_ROLL_PTR), &flen); ut_ad(flen == DATA_ROLL_PTR_LEN); ptr += mach_dulint_write_compressed(ptr, trx_read_roll_ptr(field)); /*----------------------------------------*/ /* Store then the fields required to uniquely determine the record which will be modified in the clustered index */ for (i = 0; i < dict_index_get_n_unique(index); i++) { field = rec_get_nth_field(rec, offsets, i, &flen); /* The ordering columns must not be stored externally. */ ut_ad(!rec_offs_nth_extern(offsets, i)); ut_ad(dict_index_get_nth_col(index, i)->ord_part); if (trx_undo_left(undo_page, ptr) < 5) { return(0); } ptr += mach_write_compressed(ptr, flen); if (flen != UNIV_SQL_NULL) { if (trx_undo_left(undo_page, ptr) < flen) { return(0); } ut_memcpy(ptr, field, flen); ptr += flen; } } /*----------------------------------------*/ /* Save to the undo log the old values of the columns to be updated. */ if (update) { if (trx_undo_left(undo_page, ptr) < 5) { return(0); } ptr += mach_write_compressed(ptr, upd_get_n_fields(update)); for (i = 0; i < upd_get_n_fields(update); i++) { ulint pos = upd_get_nth_field(update, i)->field_no; /* Write field number to undo log */ if (trx_undo_left(undo_page, ptr) < 5) { return(0); } ptr += mach_write_compressed(ptr, pos); /* Save the old value of field */ field = rec_get_nth_field(rec, offsets, pos, &flen); if (trx_undo_left(undo_page, ptr) < 15) { return(0); } if (rec_offs_nth_extern(offsets, pos)) { ptr = trx_undo_page_report_modify_ext( ptr, dict_index_get_nth_col(index, pos) ->ord_part && !ignore_prefix && flen < REC_MAX_INDEX_COL_LEN ? ext_buf : NULL, dict_table_zip_size(table), &field, &flen); /* Notify purge that it eventually has to free the old externally stored field */ trx->update_undo->del_marks = TRUE; *type_cmpl_ptr |= TRX_UNDO_UPD_EXTERN; } else { ptr += mach_write_compressed(ptr, flen); } if (flen != UNIV_SQL_NULL) { if (trx_undo_left(undo_page, ptr) < flen) { return(0); } ut_memcpy(ptr, field, flen); ptr += flen; } } } /*----------------------------------------*/ /* In the case of a delete marking, and also in the case of an update where any ordering field of any index changes, store the values of all columns which occur as ordering fields in any index. This info is used in the purge of old versions where we use it to build and search the delete marked index records, to look if we can remove them from the index tree. Note that starting from 4.0.14 also externally stored fields can be ordering in some index. Starting from 5.2, we no longer store REC_MAX_INDEX_COL_LEN first bytes to the undo log record, but we can construct the column prefix fields in the index by fetching the first page of the BLOB that is pointed to by the clustered index. This works also in crash recovery, because all pages (including BLOBs) are recovered before anything is rolled back. */ if (!update || !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { byte* old_ptr = ptr; trx->update_undo->del_marks = TRUE; if (trx_undo_left(undo_page, ptr) < 5) { return(0); } /* Reserve 2 bytes to write the number of bytes the stored fields take in this undo record */ ptr += 2; for (col_no = 0; col_no < dict_table_get_n_cols(table); col_no++) { const dict_col_t* col = dict_table_get_nth_col(table, col_no); if (col->ord_part) { ulint pos; /* Write field number to undo log */ if (trx_undo_left(undo_page, ptr) < 5 + 15) { return(0); } pos = dict_index_get_nth_col_pos(index, col_no); ptr += mach_write_compressed(ptr, pos); /* Save the old value of field */ field = rec_get_nth_field(rec, offsets, pos, &flen); if (rec_offs_nth_extern(offsets, pos)) { ptr = trx_undo_page_report_modify_ext( ptr, flen < REC_MAX_INDEX_COL_LEN && !ignore_prefix ? ext_buf : NULL, dict_table_zip_size(table), &field, &flen); } else { ptr += mach_write_compressed( ptr, flen); } if (flen != UNIV_SQL_NULL) { if (trx_undo_left(undo_page, ptr) < flen) { return(0); } ut_memcpy(ptr, field, flen); ptr += flen; } } } mach_write_to_2(old_ptr, ptr - old_ptr); } /*----------------------------------------*/ /* Write pointers to the previous and the next undo log records */ if (trx_undo_left(undo_page, ptr) < 2) { return(0); } mach_write_to_2(ptr, first_free); ptr += 2; mach_write_to_2(undo_page + first_free, ptr - undo_page); mach_write_to_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE, ptr - undo_page); /* Write to the REDO log about this change in the UNDO log */ trx_undof_page_add_undo_rec_log(undo_page, first_free, ptr - undo_page, mtr); return(first_free); }
/*******************************************************************//** Builds a partial row from an update undo log record. It contains the columns which occur as ordering in any index of the table. @return pointer to remaining part of undo record */ UNIV_INTERN byte* trx_undo_rec_get_partial_row( /*=========================*/ byte* ptr, /*!< in: remaining part in update undo log record of a suitable type, at the start of the stored index columns; NOTE that this copy of the undo log record must be preserved as long as the partial row is used, as we do NOT copy the data in the record! */ dict_index_t* index, /*!< in: clustered index */ dtuple_t** row, /*!< out, own: partial row */ ibool ignore_prefix, /*!< in: flag to indicate if we expect blob prefixes in undo. Used only in the assertion. */ mem_heap_t* heap) /*!< in: memory heap from which the memory needed is allocated */ { const byte* end_ptr; ulint row_len; ut_ad(index); ut_ad(ptr); ut_ad(row); ut_ad(heap); ut_ad(dict_index_is_clust(index)); row_len = dict_table_get_n_cols(index->table); *row = dtuple_create(heap, row_len); dict_table_copy_types(*row, index->table); end_ptr = ptr + mach_read_from_2(ptr); ptr += 2; while (ptr != end_ptr) { dfield_t* dfield; byte* field; ulint field_no; const dict_col_t* col; ulint col_no; ulint len; ulint orig_len; ptr = trx_undo_update_rec_get_field_no(ptr, &field_no); col = dict_index_get_nth_col(index, field_no); col_no = dict_col_get_no(col); ptr = trx_undo_rec_get_col_val(ptr, &field, &len, &orig_len); dfield = dtuple_get_nth_field(*row, col_no); dfield_set_data(dfield, field, len); if (len != UNIV_SQL_NULL && len >= UNIV_EXTERN_STORAGE_FIELD) { dfield_set_len(dfield, len - UNIV_EXTERN_STORAGE_FIELD); dfield_set_ext(dfield); /* If the prefix of this column is indexed, ensure that enough prefix is stored in the undo log record. */ if (!ignore_prefix && col->ord_part) { ut_a(dfield_get_len(dfield) >= 2 * BTR_EXTERN_FIELD_REF_SIZE); ut_a(dict_table_get_format(index->table) >= DICT_TF_FORMAT_ZIP || dfield_get_len(dfield) >= REC_MAX_INDEX_COL_LEN + BTR_EXTERN_FIELD_REF_SIZE); } } } return(ptr); }
/*************************************************************//** This function is used to compare two physical records. Only the common first fields are compared, and if an externally stored field is encountered, then 0 is returned. @return 1, 0, -1 if rec1 is greater, equal, less, respectively */ UNIV_INTERN int cmp_rec_rec_with_match( /*===================*/ const rec_t* rec1, /*!< in: physical record */ const rec_t* rec2, /*!< in: physical record */ const ulint* offsets1,/*!< in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/*!< in: rec_get_offsets(rec2, index) */ dict_index_t* index, /*!< in: data dictionary index */ ulint* matched_fields, /*!< in/out: number of already completely matched fields; when the function returns, contains the value the for current comparison */ ulint* matched_bytes) /*!< in/out: number of already matched bytes within the first field not completely matched; when the function returns, contains the value for the current comparison */ { ulint rec1_n_fields; /* the number of fields in rec */ ulint rec1_f_len; /* length of current field in rec */ const byte* rec1_b_ptr; /* pointer to the current byte in rec field */ ulint rec1_byte; /* value of current byte to be compared in rec */ ulint rec2_n_fields; /* the number of fields in rec */ ulint rec2_f_len; /* length of current field in rec */ const byte* rec2_b_ptr; /* pointer to the current byte in rec field */ ulint rec2_byte; /* value of current byte to be compared in rec */ ulint cur_field; /* current field number */ ulint cur_bytes; /* number of already matched bytes in current field */ int ret = 0; /* return value */ ulint comp; ut_ad(rec1 && rec2 && index); ut_ad(rec_offs_validate(rec1, index, offsets1)); ut_ad(rec_offs_validate(rec2, index, offsets2)); ut_ad(rec_offs_comp(offsets1) == rec_offs_comp(offsets2)); comp = rec_offs_comp(offsets1); rec1_n_fields = rec_offs_n_fields(offsets1); rec2_n_fields = rec_offs_n_fields(offsets2); cur_field = *matched_fields; cur_bytes = *matched_bytes; /* Match fields in a loop */ while ((cur_field < rec1_n_fields) && (cur_field < rec2_n_fields)) { ulint mtype; ulint prtype; if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { /* This is for the insert buffer B-tree. */ mtype = DATA_BINARY; prtype = 0; } else { const dict_col_t* col = dict_index_get_nth_col(index, cur_field); mtype = col->mtype; prtype = col->prtype; } rec1_b_ptr = rec_get_nth_field(rec1, offsets1, cur_field, &rec1_f_len); rec2_b_ptr = rec_get_nth_field(rec2, offsets2, cur_field, &rec2_f_len); if (cur_bytes == 0) { if (cur_field == 0) { /* Test if rec is the predefined minimum record */ if (UNIV_UNLIKELY(rec_get_info_bits(rec1, comp) & REC_INFO_MIN_REC_FLAG)) { if (!(rec_get_info_bits(rec2, comp) & REC_INFO_MIN_REC_FLAG)) { ret = -1; } goto order_resolved; } else if (UNIV_UNLIKELY (rec_get_info_bits(rec2, comp) & REC_INFO_MIN_REC_FLAG)) { ret = 1; goto order_resolved; } } if (rec_offs_nth_extern(offsets1, cur_field) || rec_offs_nth_extern(offsets2, cur_field)) { /* We do not compare to an externally stored field */ goto order_resolved; } if (rec1_f_len == UNIV_SQL_NULL || rec2_f_len == UNIV_SQL_NULL) { if (rec1_f_len == rec2_f_len) { goto next_field; } else if (rec2_f_len == UNIV_SQL_NULL) { /* We define the SQL null to be the smallest possible value of a field in the alphabetical order */ ret = 1; } else { ret = -1; } goto order_resolved; } } if (mtype >= DATA_FLOAT || (mtype == DATA_BLOB && 0 == (prtype & DATA_BINARY_TYPE) && dtype_get_charset_coll(prtype) != DATA_MYSQL_LATIN1_SWEDISH_CHARSET_COLL)) { ret = cmp_whole_field(mtype, prtype, rec1_b_ptr, (unsigned) rec1_f_len, rec2_b_ptr, (unsigned) rec2_f_len); if (ret != 0) { cur_bytes = 0; goto order_resolved; } else { goto next_field; } } /* Set the pointers at the current byte */ rec1_b_ptr = rec1_b_ptr + cur_bytes; rec2_b_ptr = rec2_b_ptr + cur_bytes; /* Compare then the fields */ for (;;) { if (rec2_f_len <= cur_bytes) { if (rec1_f_len <= cur_bytes) { goto next_field; } rec2_byte = dtype_get_pad_char(mtype, prtype); if (rec2_byte == ULINT_UNDEFINED) { ret = 1; goto order_resolved; } } else { rec2_byte = *rec2_b_ptr; } if (rec1_f_len <= cur_bytes) { rec1_byte = dtype_get_pad_char(mtype, prtype); if (rec1_byte == ULINT_UNDEFINED) { ret = -1; goto order_resolved; } } else { rec1_byte = *rec1_b_ptr; } if (rec1_byte == rec2_byte) { /* If the bytes are equal, they will remain such even after the collation transformation below */ goto next_byte; } if (mtype <= DATA_CHAR || (mtype == DATA_BLOB && !(prtype & DATA_BINARY_TYPE))) { rec1_byte = cmp_collate(rec1_byte); rec2_byte = cmp_collate(rec2_byte); } if (rec1_byte < rec2_byte) { ret = -1; goto order_resolved; } else if (rec1_byte > rec2_byte) { ret = 1; goto order_resolved; } next_byte: /* Next byte */ cur_bytes++; rec1_b_ptr++; rec2_b_ptr++; } next_field: cur_field++; cur_bytes = 0; } ut_ad(cur_bytes == 0); /* If we ran out of fields, rec1 was equal to rec2 up to the common fields */ ut_ad(ret == 0); order_resolved: ut_ad((ret >= - 1) && (ret <= 1)); *matched_fields = cur_field; *matched_bytes = cur_bytes; return(ret); }
/*************************************************************//** Compare two physical records that contain the same number of columns, none of which are stored externally. @return 1, 0, -1 if rec1 is greater, equal, less, respectively, than rec2 */ UNIV_INTERN int cmp_rec_rec_simple( /*===============*/ const rec_t* rec1, /*!< in: physical record */ const rec_t* rec2, /*!< in: physical record */ const ulint* offsets1,/*!< in: rec_get_offsets(rec1, ...) */ const ulint* offsets2,/*!< in: rec_get_offsets(rec2, ...) */ const dict_index_t* index) /*!< in: data dictionary index */ { ulint rec1_f_len; /*!< length of current field in rec1 */ const byte* rec1_b_ptr; /*!< pointer to the current byte in rec1 field */ ulint rec1_byte; /*!< value of current byte to be compared in rec1 */ ulint rec2_f_len; /*!< length of current field in rec2 */ const byte* rec2_b_ptr; /*!< pointer to the current byte in rec2 field */ ulint rec2_byte; /*!< value of current byte to be compared in rec2 */ ulint cur_field; /*!< current field number */ ulint n_uniq; n_uniq = dict_index_get_n_unique(index); ut_ad(rec_offs_n_fields(offsets1) >= n_uniq); ut_ad(rec_offs_n_fields(offsets2) >= n_uniq); ut_ad(rec_offs_comp(offsets1) == rec_offs_comp(offsets2)); for (cur_field = 0; cur_field < n_uniq; cur_field++) { ulint cur_bytes; ulint mtype; ulint prtype; { const dict_col_t* col = dict_index_get_nth_col(index, cur_field); mtype = col->mtype; prtype = col->prtype; } ut_ad(!rec_offs_nth_extern(offsets1, cur_field)); ut_ad(!rec_offs_nth_extern(offsets2, cur_field)); rec1_b_ptr = rec_get_nth_field(rec1, offsets1, cur_field, &rec1_f_len); rec2_b_ptr = rec_get_nth_field(rec2, offsets2, cur_field, &rec2_f_len); if (rec1_f_len == UNIV_SQL_NULL || rec2_f_len == UNIV_SQL_NULL) { if (rec1_f_len == rec2_f_len) { goto next_field; } else if (rec2_f_len == UNIV_SQL_NULL) { /* We define the SQL null to be the smallest possible value of a field in the alphabetical order */ return(1); } else { return(-1); } } if (mtype >= DATA_FLOAT || (mtype == DATA_BLOB && 0 == (prtype & DATA_BINARY_TYPE) && dtype_get_charset_coll(prtype) != DATA_MYSQL_LATIN1_SWEDISH_CHARSET_COLL)) { int ret = cmp_whole_field(mtype, prtype, rec1_b_ptr, (unsigned) rec1_f_len, rec2_b_ptr, (unsigned) rec2_f_len); if (ret) { return(ret); } goto next_field; } /* Compare the fields */ for (cur_bytes = 0;; cur_bytes++, rec1_b_ptr++, rec2_b_ptr++) { if (rec2_f_len <= cur_bytes) { if (rec1_f_len <= cur_bytes) { goto next_field; } rec2_byte = dtype_get_pad_char(mtype, prtype); if (rec2_byte == ULINT_UNDEFINED) { return(1); } } else { rec2_byte = *rec2_b_ptr; } if (rec1_f_len <= cur_bytes) { rec1_byte = dtype_get_pad_char(mtype, prtype); if (rec1_byte == ULINT_UNDEFINED) { return(-1); } } else { rec1_byte = *rec1_b_ptr; } if (rec1_byte == rec2_byte) { /* If the bytes are equal, they will remain such even after the collation transformation below */ continue; } if (mtype <= DATA_CHAR || (mtype == DATA_BLOB && !(prtype & DATA_BINARY_TYPE))) { rec1_byte = cmp_collate(rec1_byte); rec2_byte = cmp_collate(rec2_byte); } if (rec1_byte < rec2_byte) { return(-1); } else if (rec1_byte > rec2_byte) { return(1); } } next_field: continue; } /* If we ran out of fields, rec1 was equal to rec2. */ return(0); }
/********************************************************//** Parses a log record written by mlog_open_and_write_index. @return parsed record end, NULL if not a complete record */ UNIV_INTERN byte* mlog_parse_index( /*=============*/ byte* ptr, /*!< in: buffer */ const byte* end_ptr,/*!< in: buffer end */ ibool comp, /*!< in: TRUE=compact record format */ dict_index_t** index) /*!< out, own: dummy index */ { ulint i, n, n_uniq; dict_table_t* table; dict_index_t* ind; ibool is_gcs = FALSE; ulint n_fields_before_alter = 0; /* 快速alter table前聚集索引的字段数 */ ut_ad(comp == FALSE || comp == TRUE); if (comp) { if (end_ptr < ptr + 4) { return(NULL); } n = mach_read_from_2(ptr); if (n & 0x8000) /* 最高位为1表示GCS表 */ { is_gcs = TRUE; n &= 0x7FFF; } ptr += 2; if (is_gcs) { n_fields_before_alter = mach_read_from_2(ptr); ut_ad(n_fields_before_alter < n && n_fields_before_alter > 0); ptr += 2; /* 确保地址有效!*/ if (end_ptr < ptr + 2) { return(NULL); } } n_uniq = mach_read_from_2(ptr); ptr += 2; ut_ad(n_uniq <= n); if (end_ptr < ptr + n * 2) { return(NULL); } } else { n = n_uniq = 1; } table = dict_mem_table_create("LOG_DUMMY", DICT_HDR_SPACE, n, comp ? DICT_TF_COMPACT : 0, is_gcs, n_fields_before_alter); ind = dict_mem_index_create("LOG_DUMMY", "LOG_DUMMY", DICT_HDR_SPACE, 0, n); ind->table = table; ind->n_uniq = (unsigned int) n_uniq; if (n_uniq != n) { ut_a(n_uniq + DATA_ROLL_PTR <= n); ind->type = DICT_CLUSTERED; } if (comp) { for (i = 0; i < n; i++) { ulint len = mach_read_from_2(ptr); ptr += 2; /* The high-order bit of len is the NOT NULL flag; the rest is 0 or 0x7fff for variable-length fields, and 1..0x7ffe for fixed-length fields. */ /* redo阶段压缩属性不需指定 */ dict_mem_table_add_col( table, NULL, NULL, ((len + 1) & 0x7fff) <= 1 ? DATA_BINARY : DATA_FIXBINARY, /* 若len 为0或0x7fff,可认为是变长字段;否则是定长字段 */ len & 0x8000 ? DATA_NOT_NULL : 0, len & 0x7fff); if (is_gcs && n_fields_before_alter > 0 && n_fields_before_alter <= i) { dict_col_t* col = NULL; col = dict_table_get_nth_col(table, i); /* 添加默认值信息,但只是占位符,并不需真正的默认值信息 */ if (!dict_col_is_nullable(col)) dict_mem_table_set_col_default(table, col, table->heap); } dict_index_add_col(ind, table, dict_table_get_nth_col(table, i), 0); } dict_table_add_system_columns(table, table->heap); if (n_uniq != n) { /* Identify DB_TRX_ID and DB_ROLL_PTR in the index. */ ut_a(DATA_TRX_ID_LEN == dict_index_get_nth_col(ind, DATA_TRX_ID - 1 + n_uniq)->len); ut_a(DATA_ROLL_PTR_LEN == dict_index_get_nth_col(ind, DATA_ROLL_PTR - 1 + n_uniq)->len); ind->fields[DATA_TRX_ID - 1 + n_uniq].col = &table->cols[n + DATA_TRX_ID]; ind->fields[DATA_ROLL_PTR - 1 + n_uniq].col = &table->cols[n + DATA_ROLL_PTR]; /* set the col_ind col->ind */ ind->fields[DATA_TRX_ID - 1 + n_uniq].col_ind = ind->fields[DATA_TRX_ID - 1 + n_uniq].col->ind; ind->fields[DATA_ROLL_PTR - 1 + n_uniq].col_ind = ind->fields[DATA_ROLL_PTR - 1 + n_uniq].col->ind; } if (dict_index_is_gcs_clust_after_alter_table(ind)) { ut_ad(table->n_cols == table->n_def); ut_a(table->n_cols_before_alter_table > 0 && table->n_cols_before_alter_table <= table->n_cols); ind->n_fields_before_alter = n_fields_before_alter; ind->n_nullable_before_alter = dict_index_get_first_n_field_n_nullable(ind, ind->n_fields_before_alter); } else { ind->n_fields_before_alter = 0; ind->n_nullable_before_alter = 0; } } /* avoid ut_ad(index->cached) in dict_index_get_n_unique_in_tree */ ind->cached = TRUE; *index = ind; return(ptr); }