diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item_subselect.cc | 1 | ||||
-rw-r--r-- | sql/records.cc | 2 | ||||
-rw-r--r-- | sql/records.h | 4 | ||||
-rw-r--r-- | sql/sql_select.cc | 60 | ||||
-rw-r--r-- | sql/sql_select.h | 7 |
5 files changed, 72 insertions, 2 deletions
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 335b9f79e78..0ce9c555fea 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1956,6 +1956,7 @@ int subselect_single_select_engine::exec() tab->read_record.record= tab->table->record[0]; tab->read_record.thd= join->thd; tab->read_record.ref_length= tab->table->file->ref_length; + tab->read_record.unlock_row= rr_unlock_row; *(last_changed_tab++)= tab; break; } diff --git a/sql/records.cc b/sql/records.cc index 93b19aefbaf..8fd63d104a4 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -67,6 +67,7 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, info->file= table->file; info->record= table->record[0]; info->print_error= print_error; + info->unlock_row= rr_unlock_row; table->status=0; /* And it's always found */ if (!table->file->inited) @@ -192,6 +193,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, } 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 */ diff --git a/sql/records.h b/sql/records.h index 9207a05f826..ae81a31ee1a 100644 --- a/sql/records.h +++ b/sql/records.h @@ -43,11 +43,13 @@ class SQL_SELECT; struct READ_RECORD { typedef int (*Read_func)(READ_RECORD*); + typedef void (*Unlock_row_func)(st_join_table *); typedef int (*Setup_func)(struct st_join_table*); TABLE *table; /* Head-form */ handler *file; TABLE **forms; /* head and ref forms */ + Unlock_row_func unlock_row; Read_func read_record; THD *thd; SQL_SELECT *select; @@ -72,4 +74,6 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, bool print_error, uint idx); void end_read_record(READ_RECORD *info); +void rr_unlock_row(st_join_table *tab); + #endif /* SQL_RECORDS_H */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 310f26cd192..6b67a216341 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -150,6 +150,7 @@ static int join_read_const_table(JOIN_TAB *tab, POSITION *pos); static int join_read_system(JOIN_TAB *tab); static int join_read_const(JOIN_TAB *tab); static int join_read_key(JOIN_TAB *tab); +static void join_read_key_unlock_row(st_join_table *tab); static int join_read_always_key(JOIN_TAB *tab); static int join_read_last_key(JOIN_TAB *tab); static int join_no_more_records(READ_RECORD *info); @@ -5736,7 +5737,9 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, } j->ref.key_buff2=j->ref.key_buff+ALIGN_SIZE(length); j->ref.key_err=1; + j->ref.has_record= FALSE; j->ref.null_rejecting= 0; + j->ref.use_count= 0; keyuse=org_keyuse; store_key **ref_key= j->ref.key_copy; @@ -6569,6 +6572,20 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) DBUG_RETURN(0); } + +/** + The default implementation of unlock-row method of READ_RECORD, + used in all access methods. +*/ + +void rr_unlock_row(st_join_table *tab) +{ + READ_RECORD *info= &tab->read_record; + info->file->unlock_row(); +} + + + static void make_join_readinfo(JOIN *join, ulonglong options) { @@ -6584,6 +6601,7 @@ make_join_readinfo(JOIN *join, ulonglong options) TABLE *table=tab->table; tab->read_record.table= table; tab->read_record.file=table->file; + tab->read_record.unlock_row= rr_unlock_row; tab->next_select=sub_select; /* normal select */ /* @@ -6629,6 +6647,7 @@ make_join_readinfo(JOIN *join, ulonglong options) delete tab->quick; tab->quick=0; tab->read_first_record= join_read_key; + tab->read_record.unlock_row= join_read_key_unlock_row; tab->read_record.read_record= join_no_more_records; if (table->covering_keys.is_set(tab->ref.key) && !table->no_keyread) @@ -11472,7 +11491,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, else { join->thd->warning_info->inc_current_row_for_warning(); - join_tab->read_record.file->unlock_row(); + join_tab->read_record.unlock_row(join_tab); } } else @@ -11483,7 +11502,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, */ join->examined_rows++; join->thd->warning_info->inc_current_row_for_warning(); - join_tab->read_record.file->unlock_row(); + join_tab->read_record.unlock_row(join_tab); } return NESTED_LOOP_OK; } @@ -11843,18 +11862,55 @@ join_read_key(JOIN_TAB *tab) table->status=STATUS_NOT_FOUND; return -1; } + /* + Moving away from the current record. Unlock the row + in the handler if it did not match the partial WHERE. + */ + if (tab->ref.has_record && tab->ref.use_count == 0) + { + tab->read_record.file->unlock_row(); + tab->ref.has_record= FALSE; + } error=table->file->index_read_map(table->record[0], tab->ref.key_buff, make_prev_keypart_map(tab->ref.key_parts), HA_READ_KEY_EXACT); if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) return report_error(table, error); + + if (! error) + { + tab->ref.has_record= TRUE; + tab->ref.use_count= 1; + } + } + else if (table->status == 0) + { + DBUG_ASSERT(tab->ref.has_record); + tab->ref.use_count++; } table->null_row=0; return table->status ? -1 : 0; } +/** + Since join_read_key may buffer a record, do not unlock + it if it was not used in this invocation of join_read_key(). + Only count locks, thus remembering if the record was left unused, + and unlock already when pruning the current value of + TABLE_REF buffer. + @sa join_read_key() +*/ + +static void +join_read_key_unlock_row(st_join_table *tab) +{ + DBUG_ASSERT(tab->ref.use_count); + if (tab->ref.use_count) + tab->ref.use_count--; +} + /* ref access method implementation: "read_first" function diff --git a/sql/sql_select.h b/sql/sql_select.h index 76b3d1717f4..e049e4ed765 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -61,6 +61,8 @@ class store_key; typedef struct st_table_ref { bool key_err; + /** True if something was read into buffer in join_read_key. */ + bool has_record; uint key_parts; ///< num of ... uint key_length; ///< length of key_buff int key; ///< key no @@ -88,6 +90,11 @@ typedef struct st_table_ref table_map depend_map; ///< Table depends on these tables. /* null byte position in the key_buf. Used for REF_OR_NULL optimization */ uchar *null_ref_key; + /* + The number of times the record associated with this key was used + in the join. + */ + ha_rows use_count; } TABLE_REF; |