mirror of
https://github.com/MariaDB/server.git
synced 2025-07-02 14:22:51 +03:00
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. mysql-test/r/innodb_lock_wait_timeout_1.result: Update results (Bug41756). mysql-test/t/innodb_lock_wait_timeout_1.test: Add a test case (Bug#41756). sql/item_subselect.cc: Complete struct READ_RECORD initialization with a new member to unlock records. sql/records.cc: Extend READ_RECORD API with a method to unlock read records. sql/sql_select.cc: 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. sql/sql_select.h: Add members to TABLE_REF to count TABLE_REF buffer usage count. sql/structs.h: Update declarations.
This commit is contained in:
@ -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_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);
|
||||
@ -5628,7 +5629,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;
|
||||
@ -6461,6 +6464,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)
|
||||
{
|
||||
@ -6476,6 +6493,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 */
|
||||
|
||||
/*
|
||||
@ -6521,6 +6539,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)
|
||||
@ -11357,7 +11376,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
|
||||
return NESTED_LOOP_NO_MORE_ROWS;
|
||||
}
|
||||
else
|
||||
join_tab->read_record.file->unlock_row();
|
||||
join_tab->read_record.unlock_row(join_tab);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -11367,7 +11386,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
|
||||
*/
|
||||
join->examined_rows++;
|
||||
join->thd->row_count++;
|
||||
join_tab->read_record.file->unlock_row();
|
||||
join_tab->read_record.unlock_row(join_tab);
|
||||
}
|
||||
return NESTED_LOOP_OK;
|
||||
}
|
||||
@ -11727,18 +11746,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
|
||||
|
||||
|
Reference in New Issue
Block a user