Commit dee8224b authored by MySQL Build Team's avatar MySQL Build Team

Backport into build-200911241145-5.1.40sp1

> ------------------------------------------------------------
> revno: 3190 [merge]
> revision-id: kostja@sun.com-20091103174552-bfpak6r7ngf5cbjb
> parent: magnus.blaudd@sun.com-20091103170719-6b64sjnivsiyz6xy
> parent: kostja@sun.com-20091103165854-7di545xruez8w207
> committer: Konstantin Osipov <kostja@sun.com>
> branch nick: 5.1-41756
> timestamp: Tue 2009-11-03 20:45:52 +0300
> message:
>   A fix and a test case for 
>   Bug#41756 "Strange error messages about locks from InnoDB".
>         
>   In JT_EQ_REF (join_read_key()) access method, 
>   don't try to unlock rows in the handler, unless certain that 
>   a) they were locked
>   b) they are not used.
>   
>   Unlocking of rows is done by the logic of the nested join loop,
>   and is unaware of the possible caching that the access method may
>   have. This could lead to double unlocking, when a row
>   was unlocked first after reading into the cache, and then 
>   when taken from cache, as well as to unlocking of rows which
>   were actually used (but taken from cache).
>         
>   Delegate part of the unlocking logic to the access method,
>   and in JT_EQ_REF count how many times a record was actually 
>   used in the join. Unlock it only if it's usage count is 0.
>   
>   Implemented review comments.
> ------------------------------------------------------------
> Use --include-merges or -n0 to see merged revisions.
parent dda43d98
...@@ -1954,6 +1954,7 @@ int subselect_single_select_engine::exec() ...@@ -1954,6 +1954,7 @@ int subselect_single_select_engine::exec()
tab->read_record.record= tab->table->record[0]; tab->read_record.record= tab->table->record[0];
tab->read_record.thd= join->thd; tab->read_record.thd= join->thd;
tab->read_record.ref_length= tab->table->file->ref_length; tab->read_record.ref_length= tab->table->file->ref_length;
tab->read_record.unlock_row= rr_unlock_row;
*(last_changed_tab++)= tab; *(last_changed_tab++)= tab;
break; break;
} }
......
...@@ -61,6 +61,7 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, ...@@ -61,6 +61,7 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
info->file= table->file; info->file= table->file;
info->record= table->record[0]; info->record= table->record[0];
info->print_error= print_error; info->print_error= print_error;
info->unlock_row= rr_unlock_row;
table->status=0; /* And it's always found */ table->status=0; /* And it's always found */
if (!table->file->inited) if (!table->file->inited)
...@@ -186,6 +187,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, ...@@ -186,6 +187,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
} }
info->select=select; info->select=select;
info->print_error=print_error; info->print_error=print_error;
info->unlock_row= rr_unlock_row;
info->ignore_not_found_rows= 0; info->ignore_not_found_rows= 0;
table->status=0; /* And it's always found */ table->status=0; /* And it's always found */
......
...@@ -149,6 +149,7 @@ static int join_read_const_table(JOIN_TAB *tab, POSITION *pos); ...@@ -149,6 +149,7 @@ static int join_read_const_table(JOIN_TAB *tab, POSITION *pos);
static int join_read_system(JOIN_TAB *tab); static int join_read_system(JOIN_TAB *tab);
static int join_read_const(JOIN_TAB *tab); static int join_read_const(JOIN_TAB *tab);
static int join_read_key(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_always_key(JOIN_TAB *tab);
static int join_read_last_key(JOIN_TAB *tab); static int join_read_last_key(JOIN_TAB *tab);
static int join_no_more_records(READ_RECORD *info); static int join_no_more_records(READ_RECORD *info);
...@@ -5607,7 +5608,9 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, ...@@ -5607,7 +5608,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_buff2=j->ref.key_buff+ALIGN_SIZE(length);
j->ref.key_err=1; j->ref.key_err=1;
j->ref.has_record= FALSE;
j->ref.null_rejecting= 0; j->ref.null_rejecting= 0;
j->ref.use_count= 0;
keyuse=org_keyuse; keyuse=org_keyuse;
store_key **ref_key= j->ref.key_copy; store_key **ref_key= j->ref.key_copy;
...@@ -6440,6 +6443,20 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -6440,6 +6443,20 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
DBUG_RETURN(0); 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 static void
make_join_readinfo(JOIN *join, ulonglong options) make_join_readinfo(JOIN *join, ulonglong options)
{ {
...@@ -6455,6 +6472,7 @@ make_join_readinfo(JOIN *join, ulonglong options) ...@@ -6455,6 +6472,7 @@ make_join_readinfo(JOIN *join, ulonglong options)
TABLE *table=tab->table; TABLE *table=tab->table;
tab->read_record.table= table; tab->read_record.table= table;
tab->read_record.file=table->file; tab->read_record.file=table->file;
tab->read_record.unlock_row= rr_unlock_row;
tab->next_select=sub_select; /* normal select */ tab->next_select=sub_select; /* normal select */
/* /*
...@@ -6500,6 +6518,7 @@ make_join_readinfo(JOIN *join, ulonglong options) ...@@ -6500,6 +6518,7 @@ make_join_readinfo(JOIN *join, ulonglong options)
delete tab->quick; delete tab->quick;
tab->quick=0; tab->quick=0;
tab->read_first_record= join_read_key; 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; tab->read_record.read_record= join_no_more_records;
if (table->covering_keys.is_set(tab->ref.key) && if (table->covering_keys.is_set(tab->ref.key) &&
!table->no_keyread) !table->no_keyread)
...@@ -11327,7 +11346,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, ...@@ -11327,7 +11346,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
return NESTED_LOOP_NO_MORE_ROWS; return NESTED_LOOP_NO_MORE_ROWS;
} }
else else
join_tab->read_record.file->unlock_row(); join_tab->read_record.unlock_row(join_tab);
} }
else else
{ {
...@@ -11337,7 +11356,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, ...@@ -11337,7 +11356,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
*/ */
join->examined_rows++; join->examined_rows++;
join->thd->row_count++; join->thd->row_count++;
join_tab->read_record.file->unlock_row(); join_tab->read_record.unlock_row(join_tab);
} }
return NESTED_LOOP_OK; return NESTED_LOOP_OK;
} }
...@@ -11697,18 +11716,55 @@ join_read_key(JOIN_TAB *tab) ...@@ -11697,18 +11716,55 @@ join_read_key(JOIN_TAB *tab)
table->status=STATUS_NOT_FOUND; table->status=STATUS_NOT_FOUND;
return -1; 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], error=table->file->index_read_map(table->record[0],
tab->ref.key_buff, tab->ref.key_buff,
make_prev_keypart_map(tab->ref.key_parts), make_prev_keypart_map(tab->ref.key_parts),
HA_READ_KEY_EXACT); HA_READ_KEY_EXACT);
if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
return report_error(table, error); 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; table->null_row=0;
return table->status ? -1 : 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 ref access method implementation: "read_first" function
......
...@@ -58,6 +58,8 @@ class store_key; ...@@ -58,6 +58,8 @@ class store_key;
typedef struct st_table_ref typedef struct st_table_ref
{ {
bool key_err; bool key_err;
/** True if something was read into buffer in join_read_key. */
bool has_record;
uint key_parts; ///< num of ... uint key_parts; ///< num of ...
uint key_length; ///< length of key_buff uint key_length; ///< length of key_buff
int key; ///< key no int key; ///< key no
...@@ -85,6 +87,11 @@ typedef struct st_table_ref ...@@ -85,6 +87,11 @@ typedef struct st_table_ref
table_map depend_map; ///< Table depends on these tables. table_map depend_map; ///< Table depends on these tables.
/* null byte position in the key_buf. Used for REF_OR_NULL optimization */ /* null byte position in the key_buf. Used for REF_OR_NULL optimization */
uchar *null_ref_key; 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; } TABLE_REF;
......
...@@ -115,16 +115,22 @@ typedef struct st_reginfo { /* Extra info about reg */ ...@@ -115,16 +115,22 @@ typedef struct st_reginfo { /* Extra info about reg */
} REGINFO; } REGINFO;
struct st_read_record; /* For referense later */
class SQL_SELECT; class SQL_SELECT;
class THD; class THD;
class handler; class handler;
struct st_join_table;
void rr_unlock_row(st_join_table *tab);
typedef struct st_read_record { /* Parameter to read_record */ struct READ_RECORD { /* Parameter to read_record */
typedef int (*Read_func)(READ_RECORD*);
typedef void (*Unlock_row_func)(st_join_table *);
struct st_table *table; /* Head-form */ struct st_table *table; /* Head-form */
handler *file; handler *file;
struct st_table **forms; /* head and ref forms */ struct st_table **forms; /* head and ref forms */
int (*read_record)(struct st_read_record *);
Read_func read_record;
Unlock_row_func unlock_row;
THD *thd; THD *thd;
SQL_SELECT *select; SQL_SELECT *select;
uint cache_records; uint cache_records;
...@@ -136,7 +142,7 @@ typedef struct st_read_record { /* Parameter to read_record */ ...@@ -136,7 +142,7 @@ typedef struct st_read_record { /* Parameter to read_record */
uchar *cache,*cache_pos,*cache_end,*read_positions; uchar *cache,*cache_pos,*cache_end,*read_positions;
IO_CACHE *io_cache; IO_CACHE *io_cache;
bool print_error, ignore_not_found_rows; bool print_error, ignore_not_found_rows;
} READ_RECORD; };
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment