/*******************************************************************//** Skips a row reference from an undo log record. @return pointer to remaining part of undo record */ UNIV_INTERN byte* trx_undo_rec_skip_row_ref( /*======================*/ byte* ptr, /*!< in: remaining part in update undo log record, at the start of the row reference */ dict_index_t* index) /*!< in: clustered index */ { ulint ref_len; ulint i; ut_ad(index && ptr); ut_a(dict_index_is_clust(index)); ref_len = dict_index_get_n_unique(index); for (i = 0; i < ref_len; i++) { byte* field; ulint len; ulint orig_len; ptr = trx_undo_rec_get_col_val(ptr, &field, &len, &orig_len); } return(ptr); }
byte* trx_undo_rec_skip_row_ref( /*======================*/ /* out: pointer to remaining part of undo record */ byte* ptr, /* in: remaining part in update undo log record, at the start of the row reference */ dict_index_t* index) /* in: clustered index */ { byte* field; ulint len; ulint ref_len; ulint i; ut_ad(index && ptr); ut_a(index->type & DICT_CLUSTERED); ref_len = dict_index_get_n_unique(index); for (i = 0; i < ref_len; i++) { ptr = trx_undo_rec_get_col_val(ptr, &field, &len); } return(ptr); }
ibool row_upd_changes_ord_field_binary( /*=============================*/ /* out: TRUE if update vector changes an ordering field in the index record; NOTE: the fields are compared as binary strings */ dtuple_t* row, /* in: old value of row, or NULL if the row and the data values in update are not known when this function is called, e.g., at compile time */ dict_index_t* index, /* in: index of the record */ upd_t* update) /* in: update vector for the row */ { upd_field_t* upd_field; dict_field_t* ind_field; dict_col_t* col; ulint n_unique; ulint n_upd_fields; ulint col_pos; ulint col_no; ulint i, j; ut_ad(update && index); n_unique = dict_index_get_n_unique(index); n_upd_fields = upd_get_n_fields(update); for (i = 0; i < n_unique; i++) { ind_field = dict_index_get_nth_field(index, i); col = dict_field_get_col(ind_field); col_pos = dict_col_get_clust_pos(col); col_no = dict_col_get_no(col); for (j = 0; j < n_upd_fields; j++) { upd_field = upd_get_nth_field(update, j); if (col_pos == upd_field->field_no && (row == NULL || !dfield_datas_are_binary_equal( dtuple_get_nth_field(row, col_no), &(upd_field->new_val)))) { return(TRUE); } } } return(FALSE); }
void row_build_row_ref_from_row( /*=======================*/ dtuple_t* ref, /* in/out: row reference built; see the NOTE below! ref must have the right number of fields! */ dict_table_t* table, /* in: table */ dtuple_t* row) /* in: row NOTE: the data fields in ref will point directly into data of this row */ { dict_index_t* clust_index; dict_field_t* field; dfield_t* dfield; dfield_t* dfield2; dict_col_t* col; ulint ref_len; ulint i; ut_ad(ref && table && row); clust_index = dict_table_get_first_index(table); ref_len = dict_index_get_n_unique(clust_index); ut_ad(ref_len == dtuple_get_n_fields(ref)); for (i = 0; i < ref_len; i++) { dfield = dtuple_get_nth_field(ref, i); field = dict_index_get_nth_field(clust_index, i); col = dict_field_get_col(field); dfield2 = dtuple_get_nth_field(row, dict_col_get_no(col)); dfield_copy(dfield, dfield2); if (field->prefix_len > 0 && dfield->len != UNIV_SQL_NULL && dfield->len > field->prefix_len) { dfield->len = field->prefix_len; } } ut_ad(dtuple_check_typed(ref)); }
/*******************************************************************//** Builds a row reference from an undo log record. @return pointer to remaining part of undo record */ UNIV_INTERN byte* trx_undo_rec_get_row_ref( /*=====================*/ byte* ptr, /*!< in: remaining part of a copy of an undo log record, at the start of the row reference; NOTE that this copy of the undo log record must be preserved as long as the row reference is used, as we do NOT copy the data in the record! */ dict_index_t* index, /*!< in: clustered index */ dtuple_t** ref, /*!< out, own: row reference */ mem_heap_t* heap) /*!< in: memory heap from which the memory needed is allocated */ { ulint ref_len; ulint i; ut_ad(index && ptr && ref && heap); ut_a(dict_index_is_clust(index)); ref_len = dict_index_get_n_unique(index); *ref = dtuple_create(heap, ref_len); dict_index_copy_types(*ref, index, ref_len); for (i = 0; i < ref_len; i++) { dfield_t* dfield; byte* field; ulint len; ulint orig_len; dfield = dtuple_get_nth_field(*ref, i); ptr = trx_undo_rec_get_col_val(ptr, &field, &len, &orig_len); dfield_set_data(dfield, field, len); } return(ptr); }
ibool row_search_on_row_ref( /*==================*/ /* out: TRUE if found */ btr_pcur_t* pcur, /* in/out: persistent cursor, which must be closed by the caller */ ulint mode, /* in: BTR_MODIFY_LEAF, ... */ dict_table_t* table, /* in: table */ dtuple_t* ref, /* in: row reference */ mtr_t* mtr) /* in: mtr */ { ulint low_match; rec_t* rec; dict_index_t* index; page_t* page; ut_ad(dtuple_check_typed(ref)); index = dict_table_get_first_index(table); ut_a(dtuple_get_n_fields(ref) == dict_index_get_n_unique(index)); btr_pcur_open(index, ref, PAGE_CUR_LE, mode, pcur, mtr); low_match = btr_pcur_get_low_match(pcur); rec = btr_pcur_get_rec(pcur); page = buf_frame_align(rec); if (rec == page_get_infimum_rec(page)) { return(FALSE); } if (low_match != dtuple_get_n_fields(ref)) { return(FALSE); } return(TRUE); }
/*******************************************************************//** Fills the "lock_data" member of i_s_locks_row_t object. If memory can not be allocated then FALSE is returned. @return FALSE if allocation fails */ static ibool fill_lock_data( /*===========*/ const char** lock_data,/*!< out: "lock_data" to fill */ const lock_t* lock, /*!< in: lock used to find the data */ ulint heap_no,/*!< in: rec num used to find the data */ trx_i_s_cache_t* cache) /*!< in/out: cache where to store volatile data */ { mtr_t mtr; const buf_block_t* block; const page_t* page; const rec_t* rec; ut_a(lock_get_type(lock) == LOCK_REC); mtr_start(&mtr); block = buf_page_try_get(lock_rec_get_space_id(lock), lock_rec_get_page_no(lock), &mtr); if (block == NULL) { *lock_data = NULL; mtr_commit(&mtr); return(TRUE); } page = (const page_t*) buf_block_get_frame(block); rec = page_find_rec_with_heap_no(page, heap_no); if (page_rec_is_infimum(rec)) { *lock_data = ha_storage_put_str_memlim( cache->storage, "infimum pseudo-record", MAX_ALLOWED_FOR_STORAGE(cache)); } else if (page_rec_is_supremum(rec)) { *lock_data = ha_storage_put_str_memlim( cache->storage, "supremum pseudo-record", MAX_ALLOWED_FOR_STORAGE(cache)); } else { const dict_index_t* index; ulint n_fields; mem_heap_t* heap; ulint offsets_onstack[REC_OFFS_NORMAL_SIZE]; ulint* offsets; char buf[TRX_I_S_LOCK_DATA_MAX_LEN]; ulint buf_used; ulint i; rec_offs_init(offsets_onstack); offsets = offsets_onstack; index = lock_rec_get_index(lock); n_fields = dict_index_get_n_unique(index); ut_a(n_fields > 0); heap = NULL; offsets = rec_get_offsets(rec, index, offsets, n_fields, &heap); /* format and store the data */ buf_used = 0; for (i = 0; i < n_fields; i++) { buf_used += put_nth_field( buf + buf_used, sizeof(buf) - buf_used, i, index, rec, offsets) - 1; } *lock_data = (const char*) ha_storage_put_memlim( cache->storage, buf, buf_used + 1, MAX_ALLOWED_FOR_STORAGE(cache)); if (UNIV_UNLIKELY(heap != NULL)) { /* this means that rec_get_offsets() has created a new heap and has stored offsets in it; check that this is really the case and free the heap */ ut_a(offsets != offsets_onstack); mem_heap_free(heap); } } mtr_commit(&mtr); if (*lock_data == NULL) { return(FALSE); } return(TRUE); }
/**********************************************************************//** 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); }
/**********************************************************************//** Reports in the undo log of an insert of a clustered index record. @return offset of the inserted entry on the page if succeed, 0 if fail */ static ulint trx_undo_page_report_insert( /*========================*/ page_t* undo_page, /*!< in: undo log page */ trx_t* trx, /*!< in: transaction */ dict_index_t* index, /*!< in: clustered index */ const dtuple_t* clust_entry, /*!< in: index entry which will be inserted to the clustered index */ mtr_t* mtr) /*!< in: mtr */ { ulint first_free; byte* ptr; ulint i; ut_ad(dict_index_is_clust(index)); ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_INSERT); 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) < 2 + 1 + 11 + 11) { /* Not enough space for writing the general parameters */ 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 */ *ptr++ = TRX_UNDO_INSERT_REC; ptr += mach_dulint_write_much_compressed(ptr, trx->undo_no); ptr += mach_dulint_write_much_compressed(ptr, index->table->id); /*----------------------------------------*/ /* Store then the fields required to uniquely determine the record to be inserted in the clustered index */ for (i = 0; i < dict_index_get_n_unique(index); i++) { const dfield_t* field = dtuple_get_nth_field(clust_entry, i); ulint flen = dfield_get_len(field); 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, dfield_get_data(field), flen); ptr += flen; } } return(trx_undo_page_set_next_prev_and_add(undo_page, ptr, mtr)); }
/*******************************************************************//** Builds from a secondary index record a row reference with which we can search the clustered index record. */ UNIV_INTERN void row_build_row_ref_in_tuple( /*=======================*/ dtuple_t* ref, /*!< in/out: row reference built; see the NOTE below! */ const rec_t* rec, /*!< in: record in the index; NOTE: the data fields in ref will point directly into this record, therefore, the buffer page of this record must be at least s-latched and the latch held as long as the row reference is used! */ const dict_index_t* index, /*!< in: secondary index */ ulint* offsets,/*!< in: rec_get_offsets(rec, index) or NULL */ trx_t* trx) /*!< in: transaction */ { const dict_index_t* clust_index; dfield_t* dfield; const byte* field; ulint len; ulint ref_len; ulint pos; ulint clust_col_prefix_len; ulint i; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs_init(offsets_); ut_a(ref); ut_a(index); ut_a(rec); ut_ad(!dict_index_is_clust(index)); if (UNIV_UNLIKELY(!index->table)) { fputs("InnoDB: table ", stderr); notfound: ut_print_name(stderr, trx, TRUE, index->table_name); fputs(" for index ", stderr); ut_print_name(stderr, trx, FALSE, index->name); fputs(" not found\n", stderr); ut_error; } clust_index = dict_table_get_first_index(index->table); if (UNIV_UNLIKELY(!clust_index)) { fputs("InnoDB: clust index for table ", stderr); goto notfound; } if (!offsets) { offsets = rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap); } else { ut_ad(rec_offs_validate(rec, index, offsets)); } /* Secondary indexes must not contain externally stored columns. */ ut_ad(!rec_offs_any_extern(offsets)); ref_len = dict_index_get_n_unique(clust_index); ut_ad(ref_len == dtuple_get_n_fields(ref)); dict_index_copy_types(ref, clust_index, ref_len); for (i = 0; i < ref_len; i++) { dfield = dtuple_get_nth_field(ref, i); pos = dict_index_get_nth_field_pos(index, clust_index, i); ut_a(pos != ULINT_UNDEFINED); field = rec_get_nth_field(rec, offsets, pos, &len); dfield_set_data(dfield, field, len); /* If the primary key contains a column prefix, then the secondary index may contain a longer prefix of the same column, or the full column, and we must adjust the length accordingly. */ clust_col_prefix_len = dict_index_get_nth_field( clust_index, i)->prefix_len; if (clust_col_prefix_len > 0) { if (len != UNIV_SQL_NULL) { const dtype_t* dtype = dfield_get_type(dfield); dfield_set_len(dfield, dtype_get_at_most_n_mbchars( dtype->prtype, dtype->mbminlen, dtype->mbmaxlen, clust_col_prefix_len, len, (char*) field)); } } } ut_ad(dtuple_check_typed(ref)); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } }
/*******************************************************************//** Builds from a secondary index record a row reference with which we can search the clustered index record. @return own: row reference built; see the NOTE below! */ UNIV_INTERN dtuple_t* row_build_row_ref( /*==============*/ ulint type, /*!< in: ROW_COPY_DATA, or ROW_COPY_POINTERS: the former copies also the data fields to heap, whereas the latter only places pointers to data fields on the index page */ dict_index_t* index, /*!< in: secondary index */ const rec_t* rec, /*!< in: record in the index; NOTE: in the case ROW_COPY_POINTERS the data fields in the row will point directly into this record, therefore, the buffer page of this record must be at least s-latched and the latch held as long as the row reference is used! */ mem_heap_t* heap) /*!< in: memory heap from which the memory needed is allocated */ { dict_table_t* table; dict_index_t* clust_index; dfield_t* dfield; dtuple_t* ref; const byte* field; ulint len; ulint ref_len; ulint pos; byte* buf; ulint clust_col_prefix_len; ulint i; mem_heap_t* tmp_heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); ut_ad(index && rec && heap); ut_ad(!dict_index_is_clust(index)); offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &tmp_heap); /* Secondary indexes must not contain externally stored columns. */ ut_ad(!rec_offs_any_extern(offsets)); if (type == ROW_COPY_DATA) { /* Take a copy of rec to heap */ buf = mem_heap_alloc(heap, rec_offs_size(offsets)); rec = rec_copy(buf, rec, offsets); /* Avoid a debug assertion in rec_offs_validate(). */ rec_offs_make_valid(rec, index, offsets); } table = index->table; clust_index = dict_table_get_first_index(table); ref_len = dict_index_get_n_unique(clust_index); ref = dtuple_create(heap, ref_len); dict_index_copy_types(ref, clust_index, ref_len); for (i = 0; i < ref_len; i++) { dfield = dtuple_get_nth_field(ref, i); pos = dict_index_get_nth_field_pos(index, clust_index, i); ut_a(pos != ULINT_UNDEFINED); field = rec_get_nth_field(rec, offsets, pos, &len); dfield_set_data(dfield, field, len); /* If the primary key contains a column prefix, then the secondary index may contain a longer prefix of the same column, or the full column, and we must adjust the length accordingly. */ clust_col_prefix_len = dict_index_get_nth_field( clust_index, i)->prefix_len; if (clust_col_prefix_len > 0) { if (len != UNIV_SQL_NULL) { const dtype_t* dtype = dfield_get_type(dfield); dfield_set_len(dfield, dtype_get_at_most_n_mbchars( dtype->prtype, dtype->mbminlen, dtype->mbmaxlen, clust_col_prefix_len, len, (char*) field)); } } } ut_ad(dtuple_check_typed(ref)); if (tmp_heap) { mem_heap_free(tmp_heap); } return(ref); }
/*************************************************************//** 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); }
/************************************************************************** Reports in the undo log of an update or delete marking of a clustered index record. */ static ulint trx_undo_page_report_modify( /*========================*/ /* out: byte offset of the inserted undo log entry on the page if succeed, 0 if fail */ 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 */ rec_t* rec, /* in: clustered index record which has NOT yet been modified */ const ulint* offsets, /* in: rec_get_offsets(rec, index) */ 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; upd_field_t* upd_field; ulint first_free; byte* ptr; ulint len; byte* field; ulint flen; ulint pos; dulint roll_ptr; dulint trx_id; ulint bits; ulint col_no; byte* old_ptr; ulint type_cmpl; byte* type_cmpl_ptr; ulint i; ut_a(index->type & DICT_CLUSTERED); 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) { if (rec_get_deleted_flag(rec, dict_table_is_comp(table))) { type_cmpl = TRX_UNDO_UPD_DEL_REC; } else { type_cmpl = TRX_UNDO_UPD_EXIST_REC; } } else { type_cmpl = TRX_UNDO_DEL_MARK_REC; } type_cmpl = type_cmpl | (cmpl_info * TRX_UNDO_CMPL_INFO_MULT); mach_write_to_1(ptr, type_cmpl); type_cmpl_ptr = ptr; ptr++; len = mach_dulint_write_much_compressed(ptr, trx->undo_no); ptr += len; len = mach_dulint_write_much_compressed(ptr, table->id); ptr += len; /*----------------------------------------*/ /* Store the state of the info bits */ bits = rec_get_info_bits(rec, dict_table_is_comp(table)); mach_write_to_1(ptr, bits); ptr += 1; /* Store the values of the system columns */ field = rec_get_nth_field(rec, offsets, dict_index_get_sys_col_pos( index, DATA_TRX_ID), &len); ut_ad(len == DATA_TRX_ID_LEN); trx_id = trx_read_trx_id(field); field = rec_get_nth_field(rec, offsets, dict_index_get_sys_col_pos( index, DATA_ROLL_PTR), &len); ut_ad(len == DATA_ROLL_PTR_LEN); roll_ptr = trx_read_roll_ptr(field); len = mach_dulint_write_compressed(ptr, trx_id); ptr += len; len = mach_dulint_write_compressed(ptr, roll_ptr); ptr += len; /*----------------------------------------*/ /* 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); if (trx_undo_left(undo_page, ptr) < 4) { return(0); } len = mach_write_compressed(ptr, flen); ptr += len; 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); } len = mach_write_compressed(ptr, upd_get_n_fields(update)); ptr += len; for (i = 0; i < upd_get_n_fields(update); i++) { upd_field = upd_get_nth_field(update, i); pos = upd_field->field_no; /* Write field number to undo log */ if (trx_undo_left(undo_page, ptr) < 5) { return(0); } len = mach_write_compressed(ptr, pos); ptr += len; /* Save the old value of field */ field = rec_get_nth_field(rec, offsets, pos, &flen); if (trx_undo_left(undo_page, ptr) < 5) { return(0); } if (rec_offs_nth_extern(offsets, pos)) { /* If a field has external storage, we add to flen the flag */ len = mach_write_compressed( ptr, UNIV_EXTERN_STORAGE_FIELD + flen); /* Notify purge that it eventually has to free the old externally stored field */ trx->update_undo->del_marks = TRUE; *type_cmpl_ptr = *type_cmpl_ptr | TRX_UNDO_UPD_EXTERN; } else { len = mach_write_compressed(ptr, flen); } ptr += len; 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. But we always store at least 384 first bytes locally to the clustered index record, which means we can construct the column prefix fields in the index from the stored data. */ if (!update || !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { trx->update_undo->del_marks = TRUE; if (trx_undo_left(undo_page, ptr) < 5) { return(0); } old_ptr = ptr; /* 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 > 0) { pos = dict_index_get_nth_col_pos(index, col_no); /* Write field number to undo log */ if (trx_undo_left(undo_page, ptr) < 5) { return(0); } len = mach_write_compressed(ptr, pos); ptr += len; /* Save the old value of field */ field = rec_get_nth_field(rec, offsets, pos, &flen); if (trx_undo_left(undo_page, ptr) < 5) { return(0); } len = mach_write_compressed(ptr, flen); ptr += len; 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); }
/************************************************************************** Reports in the undo log of an insert of a clustered index record. */ static ulint trx_undo_page_report_insert( /*========================*/ /* out: offset of the inserted entry on the page if succeed, 0 if fail */ page_t* undo_page, /* in: undo log page */ trx_t* trx, /* in: transaction */ dict_index_t* index, /* in: clustered index */ dtuple_t* clust_entry, /* in: index entry which will be inserted to the clustered index */ mtr_t* mtr) /* in: mtr */ { ulint first_free; byte* ptr; ulint len; dfield_t* field; ulint flen; ulint i; ut_ad(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE) == TRX_UNDO_INSERT); 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) < 30) { /* NOTE: the value 30 must be big enough such 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 */ mach_write_to_1(ptr, TRX_UNDO_INSERT_REC); ptr++; len = mach_dulint_write_much_compressed(ptr, trx->undo_no); ptr += len; len = mach_dulint_write_much_compressed(ptr, (index->table)->id); ptr += len; /*----------------------------------------*/ /* Store then the fields required to uniquely determine the record to be inserted in the clustered index */ for (i = 0; i < dict_index_get_n_unique(index); i++) { field = dtuple_get_nth_field(clust_entry, i); flen = dfield_get_len(field); if (trx_undo_left(undo_page, ptr) < 5) { return(0); } len = mach_write_compressed(ptr, flen); ptr += len; if (flen != UNIV_SQL_NULL) { if (trx_undo_left(undo_page, ptr) < flen) { return(0); } ut_memcpy(ptr, dfield_get_data(field), flen); ptr += flen; } } if (trx_undo_left(undo_page, ptr) < 2) { return(0); } /*----------------------------------------*/ /* 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 the log entry to the REDO log of this change in the UNDO log */ trx_undof_page_add_undo_rec_log(undo_page, first_free, ptr - undo_page, mtr); return(first_free); }
void row_build_row_ref_in_tuple( /*=======================*/ dtuple_t* ref, /* in/out: row reference built; see the NOTE below! */ dict_index_t* index, /* in: index */ rec_t* rec) /* in: record in the index; NOTE: the data fields in ref will point directly into this record, therefore, the buffer page of this record must be at least s-latched and the latch held as long as the row reference is used! */ { dict_table_t* table; dict_index_t* clust_index; dfield_t* dfield; byte* field; ulint len; ulint ref_len; ulint pos; ulint i; ut_a(ref && index && rec); table = index->table; if (!table) { fprintf(stderr, "InnoDB: table %s for index %s not found\n", index->table_name, index->name); ut_a(0); } clust_index = dict_table_get_first_index(table); if (!clust_index) { fprintf(stderr, "InnoDB: clust index for table %s for index %s not found\n", index->table_name, index->name); ut_a(0); } ref_len = dict_index_get_n_unique(clust_index); ut_ad(ref_len == dtuple_get_n_fields(ref)); dict_index_copy_types(ref, clust_index, ref_len); for (i = 0; i < ref_len; i++) { dfield = dtuple_get_nth_field(ref, i); pos = dict_index_get_nth_field_pos(index, clust_index, i); ut_a(pos != ULINT_UNDEFINED); field = rec_get_nth_field(rec, pos, &len); dfield_set_data(dfield, field, len); } ut_ad(dtuple_check_typed(ref)); }
dtuple_t* row_build_row_ref( /*==============*/ /* out, own: row reference built; see the NOTE below! */ ulint type, /* in: ROW_COPY_DATA, or ROW_COPY_POINTERS: the former copies also the data fields to heap, whereas the latter only places pointers to data fields on the index page */ dict_index_t* index, /* in: index */ rec_t* rec, /* in: record in the index; NOTE: in the case ROW_COPY_POINTERS the data fields in the row will point directly into this record, therefore, the buffer page of this record must be at least s-latched and the latch held as long as the row reference is used! */ mem_heap_t* heap) /* in: memory heap from which the memory needed is allocated */ { dict_table_t* table; dict_index_t* clust_index; dfield_t* dfield; dtuple_t* ref; byte* field; ulint len; ulint ref_len; ulint pos; byte* buf; ulint i; ut_ad(index && rec && heap); if (type == ROW_COPY_DATA) { /* Take a copy of rec to heap */ buf = mem_heap_alloc(heap, rec_get_size(rec)); rec = rec_copy(buf, rec); } table = index->table; clust_index = dict_table_get_first_index(table); ref_len = dict_index_get_n_unique(clust_index); ref = dtuple_create(heap, ref_len); dict_index_copy_types(ref, clust_index, ref_len); for (i = 0; i < ref_len; i++) { dfield = dtuple_get_nth_field(ref, i); pos = dict_index_get_nth_field_pos(index, clust_index, i); ut_a(pos != ULINT_UNDEFINED); field = rec_get_nth_field(rec, pos, &len); dfield_set_data(dfield, field, len); } ut_ad(dtuple_check_typed(ref)); return(ref); }