http://www.programgo.com/article/73361220588/;jsessionid=A91A8B2A2F2E0BFCB207B6DB20B7121E
http://blog.csdn.net/wudongxu/article/details/6683846
http://www.soso.io/article/83289.html
ha_innobase::open
ib_table = dict_table_get(norm_name, TRUE, ignore_err);
prebuilt = row_create_prebuilt(ib_table, table->s->reclength);
ha_innobase::rnd_next
if(第一次查找)
index_first(buf);
else
general_fetch(buf, ROW_SEL_NEXT, 0);
void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, SQL_SELECT *select, int use_record_cache, bool print_error, bool disable_rr_cache) { IO_CACHE *tempfile; DBUG_ENTER("init_read_record"); bzero((char*) info,sizeof(*info)); info->thd=thd; info->table=table; info->file= table->file; info->forms= &info->table; /* Only one table */ if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE && !table->sort.addon_field) (void) table->file->extra(HA_EXTRA_MMAP); if (table->sort.addon_field) { info->rec_buf= table->sort.addon_buf; info->ref_length= table->sort.addon_length; } else { empty_record(table); info->record= table->record[0]; info->ref_length= table->file->ref_length; } info->select=select; info->print_error=print_error; info->unlock_row= rr_unlock_row; info->ignore_not_found_rows= 0; table->status=0; /* And it's always found */ if (select && my_b_inited(&select->file)) tempfile= &select->file; else if (select && select->quick && select->quick->clustered_pk_range()) { /* In case of QUICK_INDEX_MERGE_SELECT with clustered pk range we have to use its own access method(i.e QUICK_INDEX_MERGE_SELECT::get_next()) as sort file does not contain rowids which satisfy clustered pk range. */ tempfile= 0; } else tempfile= table->sort.io_cache; if (tempfile && my_b_inited(tempfile)) // Test if ref-records was used { DBUG_PRINT("info",("using rr_from_tempfile")); info->read_record= (table->sort.addon_field ? rr_unpack_from_tempfile : rr_from_tempfile); info->io_cache=tempfile; reinit_io_cache(info->io_cache,READ_CACHE,0L,0,0); info->ref_pos=table->file->ref; if (!table->file->inited) table->file->ha_rnd_init(0); /* table->sort.addon_field is checked because if we use addon fields, it doesn't make sense to use cache - we don't read from the table and table->sort.io_cache is read sequentially */ if (!disable_rr_cache && !table->sort.addon_field && ! (specialflag & SPECIAL_SAFE_MODE) && thd->variables.read_rnd_buff_size && !(table->file->ha_table_flags() & HA_FAST_KEY_READ) && (table->db_stat & HA_READ_ONLY || table->reginfo.lock_type <= TL_READ_NO_INSERT) && (ulonglong) table->s->reclength* (table->file->stats.records+ table->file->stats.deleted) > (ulonglong) MIN_FILE_LENGTH_TO_USE_ROW_CACHE && info->io_cache->end_of_file/info->ref_length * table->s->reclength > (my_off_t) MIN_ROWS_TO_USE_TABLE_CACHE && !table->s->blob_fields && info->ref_length <= MAX_REFLENGTH) { if (! init_rr_cache(thd, info)) { DBUG_PRINT("info",("using rr_from_cache")); info->read_record=rr_from_cache; } } } else if (select && select->quick) { DBUG_PRINT("info",("using rr_quick")); info->read_record=rr_quick; } else if (table->sort.record_pointers) { DBUG_PRINT("info",("using record_pointers")); table->file->ha_rnd_init(0); info->cache_pos=table->sort.record_pointers; info->cache_end=info->cache_pos+ table->sort.found_records*info->ref_length; info->read_record= (table->sort.addon_field ? rr_unpack_from_buffer : rr_from_pointers); } else { DBUG_PRINT("info",("using rr_sequential")); info->read_record=rr_sequential; table->file->ha_rnd_init(1); /* We can use record cache if we don't update dynamic length tables */ if (!table->no_cache && (use_record_cache > 0 || (int) table->reginfo.lock_type <= (int) TL_READ_HIGH_PRIORITY || !(table->s->db_options_in_use & HA_OPTION_PACK_RECORD) || (use_record_cache < 0 && !(table->file->ha_table_flags() & HA_NOT_DELETE_WITH_CACHE)))) (void) table->file->extra_opt(HA_EXTRA_CACHE, thd->variables.read_buff_size); } /* Condition pushdown to storage engine */ if ((thd->variables.optimizer_switch & OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN) && select && select->cond && (select->cond->used_tables() & table->map) && !table->file->pushed_cond) table->file->cond_push(select->cond); DBUG_VOID_RETURN; } /* init_read_record */
int rr_sequential(READ_RECORD *info) { int tmp; while ((tmp=info->file->rnd_next(info->record))) { /* rnd_next can return RECORD_DELETED for MyISAM when one thread is reading and another deleting without locks. */ if (info->thd->killed || (tmp != HA_ERR_RECORD_DELETED)) { tmp= rr_handle_error(info, tmp); break; } } return tmp; }
在调用rnd_next之前,先调用 index_init,其再调用change_active_index,作用是将prebuilt->index的值赋值给prebuilt->search_tuple
扫表时读取下一行,当然也可作为读取第一行数据
/*****************************************************************//** Reads the next row in a table scan (also used to read the FIRST row in a table scan). @return 0, HA_ERR_END_OF_FILE, or error number */ UNIV_INTERN int ha_innobase::rnd_next( /*==================*/ uchar* buf) /*!< in/out: returns the row in this buffer, in MySQL format */ { int error; if (start_of_scan) { error = index_first(buf); start_of_scan = 0; } else { error = general_fetch(buf, ROW_SEL_NEXT, 0); } DBUG_RETURN(error); }
index_first的实现
/********************************************************************//** Positions a cursor on the first record in an index and reads the corresponding row to buf. @return 0, HA_ERR_END_OF_FILE, or error code */ UNIV_INTERN int ha_innobase::index_first(uchar* buf) /*!< in/out: buffer for the row */ { int error; error = index_read(buf, NULL, 0, HA_READ_AFTER_KEY); DBUG_RETURN(error); }
UNIV_INTERN int ha_innobase::index_read( /*====================*/ uchar* buf, /*!< in/out: buffer for the returned row */ const uchar* key_ptr, /*!< in: key value; if this is NULL we position the cursor at the start or end of index; this can also contain an InnoDB row id, in which case key_len is the InnoDB row id length; the key value can also be a prefix of a full key value, and the last column can be a prefix of a full column */ uint key_len,/*!< in: key value length */ enum ha_rkey_function find_flag)/*!< in: search flags from my_base.h */ { ulint mode; dict_index_t* index; ulint match_mode = 0; int error; ulint ret; DBUG_ENTER("index_read"); DEBUG_SYNC_C("ha_innobase_index_read_begin"); index = prebuilt->index; if (UNIV_UNLIKELY(index == NULL) || dict_index_is_corrupted(index)) { prebuilt->index_usable = FALSE; DBUG_RETURN(HA_ERR_CRASHED); } /* Note that if the index for which the search template is built is not necessarily prebuilt->index, but can also be the clustered index */ if (prebuilt->sql_stat_start) { build_template(prebuilt, user_thd, table, ROW_MYSQL_REC_FIELDS); } if (key_ptr) { /* Convert the search key value to InnoDB format into prebuilt->search_tuple */ row_sel_convert_mysql_key_to_innobase( prebuilt->search_tuple, srch_key_val1, sizeof(srch_key_val1), index, (byte*) key_ptr, (ulint) key_len, prebuilt->trx); DBUG_ASSERT(prebuilt->search_tuple->n_fields > 0); } else { /* We position the cursor to the last or the first entry in the index */ //重要!!!! 设置fields dtuple_set_n_fields(prebuilt->search_tuple, 0); } mode = convert_search_mode_to_innobase(find_flag); match_mode = 0; if (find_flag == HA_READ_KEY_EXACT) { match_mode = ROW_SEL_EXACT; } else if (find_flag == HA_READ_PREFIX || find_flag == HA_READ_PREFIX_LAST) { match_mode = ROW_SEL_EXACT_PREFIX; } last_match_mode = (uint) match_mode; if (mode != PAGE_CUR_UNSUPP) { ret = row_search_for_mysql((byte*) buf, mode, prebuilt,match_mode, 0); } else { ret = DB_UNSUPPORTED; } switch (ret) { case DB_SUCCESS: error = 0; table->status = 0; break; case DB_RECORD_NOT_FOUND: error = HA_ERR_KEY_NOT_FOUND; table->status = STATUS_NOT_FOUND; break; case DB_END_OF_INDEX: error = HA_ERR_KEY_NOT_FOUND; table->status = STATUS_NOT_FOUND; break; default: error = convert_error_code_to_mysql((int) ret, prebuilt->table->flags, user_thd); table->status = STATUS_NOT_FOUND; break; } DBUG_RETURN(error); }
row_search_for_mysql函数