/*******************************************************************//** 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); }
/*******************************************************************//** 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); }
/*******************************************************************//** An inverse function to row_build_index_entry. Builds a row from a record in a clustered index. @return own: row built; see the NOTE below! */ UNIV_INTERN dtuple_t* row_build( /*======*/ ulint type, /*!< in: ROW_COPY_POINTERS or ROW_COPY_DATA; the latter copies also the data fields to heap while the first only places pointers to data fields on the index page, and thus is more efficient */ const dict_index_t* index, /*!< in: clustered index */ const rec_t* rec, /*!< in: record in the clustered 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 dtuple is used! */ const ulint* offsets,/*!< in: rec_get_offsets(rec,index) or NULL, in which case this function will invoke rec_get_offsets() */ const dict_table_t* col_table, /*!< in: table, to check which externally stored columns occur in the ordering columns of an index, or NULL if index->table should be consulted instead */ row_ext_t** ext, /*!< out, own: cache of externally stored column prefixes, or NULL */ mem_heap_t* heap) /*!< in: memory heap from which the memory needed is allocated */ { dtuple_t* row; const dict_table_t* table; ulint n_fields; ulint n_ext_cols; ulint* ext_cols = NULL; /* remove warning */ ulint len; ulint row_len; byte* buf; ulint i; ulint j; mem_heap_t* tmp_heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs_init(offsets_); ut_ad(index && rec && heap); ut_ad(dict_index_is_clust(index)); ut_ad(!mutex_own(&kernel_mutex)); if (!offsets) { offsets = rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &tmp_heap); } else { ut_ad(rec_offs_validate(rec, index, offsets)); } #if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG if (rec_offs_any_null_extern(rec, offsets)) { /* This condition can occur during crash recovery before trx_rollback_active() has completed execution, or when a concurrently executing row_ins_index_entry_low() has committed the B-tree mini-transaction but has not yet managed to restore the cursor position for writing the big_rec. */ ut_a(trx_undo_roll_ptr_is_insert( row_get_rec_roll_ptr(rec, index, offsets))); } #endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */ if (type != ROW_COPY_POINTERS) { /* 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, (ulint*) offsets); } table = index->table; row_len = dict_table_get_n_cols(table); row = dtuple_create(heap, row_len); dict_table_copy_types(row, table); dtuple_set_info_bits(row, rec_get_info_bits( rec, dict_table_is_comp(table))); n_fields = rec_offs_n_fields(offsets); n_ext_cols = rec_offs_n_extern(offsets); if (n_ext_cols) { ext_cols = mem_heap_alloc(heap, n_ext_cols * sizeof *ext_cols); } for (i = j = 0; i < n_fields; i++) { dict_field_t* ind_field = dict_index_get_nth_field(index, i); const dict_col_t* col = dict_field_get_col(ind_field); ulint col_no = dict_col_get_no(col); dfield_t* dfield = dtuple_get_nth_field(row, col_no); if (ind_field->prefix_len == 0) { const byte* field = rec_get_nth_field( rec, offsets, i, &len); dfield_set_data(dfield, field, len); } if (rec_offs_nth_extern(offsets, i)) { dfield_set_ext(dfield); if (UNIV_LIKELY_NULL(col_table)) { ut_a(col_no < dict_table_get_n_cols(col_table)); col = dict_table_get_nth_col( col_table, col_no); } if (col->ord_part) { /* We will have to fetch prefixes of externally stored columns that are referenced by column prefixes. */ ext_cols[j++] = col_no; } } } ut_ad(dtuple_check_typed(row)); if (!ext) { /* REDUNDANT and COMPACT formats store a local 768-byte prefix of each externally stored column. No cache is needed. */ ut_ad(dict_table_get_format(index->table) < DICT_TF_FORMAT_ZIP); } else if (j) { *ext = row_ext_create(j, ext_cols, row, dict_table_zip_size(index->table), heap); } else { *ext = NULL; } if (tmp_heap) { mem_heap_free(tmp_heap); } return(row); }
/***********************************************************//** Looks for the clustered index record when node has the row reference. The pcur in node is used in the search. If found, stores the row to node, and stores the position of pcur, and detaches it. The pcur must be closed by the caller in any case. @return TRUE if found; NOTE the node->pcur must be closed by the caller, regardless of the return value */ UNIV_INTERN ibool row_undo_search_clust_to_pcur( /*==========================*/ undo_node_t* node) /*!< in: row undo node */ { dict_index_t* clust_index; ibool found; mtr_t mtr; ibool ret; rec_t* rec; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; ulint* offsets = offsets_; rec_offs_init(offsets_); mtr_start(&mtr); clust_index = dict_table_get_first_index(node->table); found = row_search_on_row_ref(&(node->pcur), BTR_MODIFY_LEAF, node->table, node->ref, &mtr); rec = btr_pcur_get_rec(&(node->pcur)); offsets = rec_get_offsets(rec, clust_index, offsets, ULINT_UNDEFINED, &heap); if (!found || 0 != ut_dulint_cmp(node->roll_ptr, row_get_rec_roll_ptr(rec, clust_index, offsets))) { /* We must remove the reservation on the undo log record BEFORE releasing the latch on the clustered index page: this is to make sure that some thread will eventually undo the modification corresponding to node->roll_ptr. */ /* fputs("--------------------undoing a previous version\n", stderr); */ ret = FALSE; } else { node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets, NULL, &node->ext, node->heap); if (node->update) { node->undo_row = dtuple_copy(node->row, node->heap); row_upd_replace(node->undo_row, &node->undo_ext, clust_index, node->update, node->heap); } else { node->undo_row = NULL; node->undo_ext = NULL; } btr_pcur_store_position(&(node->pcur), &mtr); ret = TRUE; } btr_pcur_commit_specify_mtr(&(node->pcur), &mtr); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } return(ret); }
/***********************************************************//** Removes a delete marked clustered index record if possible. @return TRUE if success, or if not found, or if modified after the delete marking */ static ibool row_purge_remove_clust_if_poss_low( /*===============================*/ purge_node_t* node, /*!< in: row purge node */ ulint mode) /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */ { dict_index_t* index; btr_pcur_t* pcur; btr_cur_t* btr_cur; ibool success; ulint err; mtr_t mtr; rec_t* rec; mem_heap_t* heap = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs_init(offsets_); index = dict_table_get_first_index(node->table); pcur = &(node->pcur); btr_cur = btr_pcur_get_btr_cur(pcur); log_free_check(); mtr_start(&mtr); success = row_purge_reposition_pcur(mode, node, &mtr); if (!success) { /* The record is already removed */ btr_pcur_commit_specify_mtr(pcur, &mtr); return(TRUE); } rec = btr_pcur_get_rec(pcur); if (node->roll_ptr != row_get_rec_roll_ptr( rec, index, rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap))) { if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } /* Someone else has modified the record later: do not remove */ btr_pcur_commit_specify_mtr(pcur, &mtr); return(TRUE); } if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } if (mode == BTR_MODIFY_LEAF) { success = btr_cur_optimistic_delete(btr_cur, &mtr); } else { ut_ad(mode == BTR_MODIFY_TREE); btr_cur_pessimistic_delete(&err, FALSE, btr_cur, RB_NONE, &mtr); if (err == DB_SUCCESS) { success = TRUE; } else if (err == DB_OUT_OF_FILE_SPACE) { success = FALSE; } else { ut_error; } } btr_pcur_commit_specify_mtr(pcur, &mtr); return(success); }