@ -61,7 +61,7 @@
# include "slave.h"
# ifndef EMBEDDED_LIBRARY
static TABLE * delayed_get_table ( THD * thd , TABLE_LIST * table_list ) ;
static bool delayed_get_table ( THD * thd , TABLE_LIST * table_list ) ;
static int write_delayed ( THD * thd , TABLE * table , enum_duplicates dup , bool ignore ,
char * query , uint query_length , bool log_on ) ;
static void end_delayed_insert ( THD * thd ) ;
@ -409,6 +409,7 @@ void mark_fields_used_by_triggers_for_insert_stmt(THD *thd, TABLE *table,
downgrade the lock in handler : : store_lock ( ) method .
*/
static
void upgrade_lock_type ( THD * thd , thr_lock_type * lock_type ,
enum_duplicates duplic ,
bool is_multi_insert )
@ -422,29 +423,37 @@ void upgrade_lock_type(THD *thd, thr_lock_type *lock_type,
if ( * lock_type = = TL_WRITE_DELAYED )
{
# ifdef EMBEDDED_LIBRARY
/* No auxiliary threads in the embedded server. */
* lock_type = TL_WRITE ;
return ;
# else
/*
We do not use delayed threads if :
- we ' re running in the safe mode or skip - new - the featur e
is disabled in these modes
- we ' re running this query in statement level replication ,
on a replication slave - because we must ensure serial
execution of queries on the slave
- we ' re running in the safe mode or skip - new mode - - the
feature is disabled in these modes
- we ' re executing this statement on a replication slave - -
we need to ensure serial execution of queries on the
slave
- it is INSERT . . ON DUPLICATE KEY UPDATE - in this case the
insert cannot be concurrent
- this statement is directly or indirectly invoked from
a stored function or trigger ( under pre - locking ) - to
avoid deadlocks , since INSERT DELAYED involves a lock
upgrade ( TL_WRITE_DELAYED - > TL_WRITE ) which we should not
attempt while keeping other table level locks .
- this statement itself may require pre - locking .
We should upgrade the lock even though in most cases
delayed functionality may work . Unfortunately , we can ' t
easily identify whether the subject table is not used in
the statement indirectly via a stored function or trigger :
if it is used , that will lead to a deadlock between the
client connection and the delayed thread .
*/
if ( specialflag & ( SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE ) | |
thd - > slave_thread | |
thd - > variables . max_insert_delayed_threads = = 0 )
thd - > variables . max_insert_delayed_threads = = 0 | |
thd - > prelocked_mode | |
thd - > lex - > uses_stored_routines ( ) )
{
* lock_type = TL_WRITE ;
return ;
}
# endif
bool log_on = ( thd - > options & OPTION_BIN_LOG | |
! ( thd - > security_ctx - > master_access & SUPER_ACL ) ) ;
if ( log_on & & mysql_bin_log . is_open ( ) & & is_multi_insert )
@ -470,6 +479,72 @@ void upgrade_lock_type(THD *thd, thr_lock_type *lock_type,
}
/**
Find or create a delayed insert thread for the first table in
the table list , then open and lock the remaining tables .
If a table can not be used with insert delayed , upgrade the lock
and open and lock all tables using the standard mechanism .
@ param thd thread context
@ param table_list list of " descriptors " for tables referenced
directly in statement SQL text .
The first element in the list corresponds to
the destination table for inserts , remaining
tables , if any , are usually tables referenced
by sub - queries in the right part of the
INSERT .
@ return Status of the operation . In case of success ' table '
member of every table_list element points to an instance of
class TABLE .
@ sa open_and_lock_tables for more information about MySQL table
level locking
*/
static
bool open_and_lock_for_insert_delayed ( THD * thd , TABLE_LIST * table_list )
{
DBUG_ENTER ( " open_and_lock_for_insert_delayed " ) ;
# ifndef EMBEDDED_LIBRARY
if ( delayed_get_table ( thd , table_list ) )
DBUG_RETURN ( TRUE ) ;
if ( table_list - > table )
{
/*
Open tables used for sub - selects or in stored functions , will also
cache these functions .
*/
if ( open_and_lock_tables ( thd , table_list - > next_global ) )
{
end_delayed_insert ( thd ) ;
DBUG_RETURN ( TRUE ) ;
}
/*
First table was not processed by open_and_lock_tables ( ) ,
we need to set updatability flag " by hand " .
*/
if ( ! table_list - > derived & & ! table_list - > view )
table_list - > updatable = 1 ; // usual table
DBUG_RETURN ( FALSE ) ;
}
# endif
/*
* This is embedded library and we don ' t have auxiliary
threads OR
* a lock upgrade was requested inside delayed_get_table
because
- there are too many delayed insert threads OR
- the table has triggers .
Use a normal insert .
*/
table_list - > lock_type = TL_WRITE ;
DBUG_RETURN ( open_and_lock_tables ( thd , table_list ) ) ;
}
/**
INSERT statement implementation
*/
@ -514,7 +589,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
*/
upgrade_lock_type ( thd , & table_list - > lock_type , duplic ,
values_list . elements > 1 ) ;
lock_type = table_list - > lock_type ;
/*
We can ' t write - delayed into a table locked with LOCK TABLES :
@ -522,7 +596,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
never be able to get a lock on the table . QQQ : why not
upgrade the lock here instead ?
*/
if ( lock_type = = TL_WRITE_DELAYED & & thd - > locked_tables & &
if ( table_list - > lock_type = = TL_WRITE_DELAYED & & thd - > locked_tables & &
find_locked_table ( thd , table_list - > db , table_list - > table_name ) )
{
my_error ( ER_DELAYED_INSERT_TABLE_LOCKED , MYF ( 0 ) ,
@ -530,36 +604,16 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
DBUG_RETURN ( TRUE ) ;
}
# ifndef EMBEDDED_LIBRARY
if ( lock_type = = TL_WRITE_DELAYED )
if ( table_list - > lock_type = = TL_WRITE_DELAYED )
{
res = 1 ;
if ( ( table = delayed_get_table ( thd , table_list ) ) & & ! thd - > is_fatal_error )
{
/*
Open tables used for sub - selects or in stored functions , will also
cache these functions .
*/
res = open_and_lock_tables ( thd , table_list - > next_global ) ;
/*
First is not processed by open_and_lock_tables ( ) = > we need set
updateability flags " by hands " .
*/
if ( ! table_list - > derived & & ! table_list - > view )
table_list - > updatable = 1 ; // usual table
}
else if ( thd - > net . last_errno ! = ER_WRONG_OBJECT )
{
/* Too many delayed insert threads; Use a normal insert */
table_list - > lock_type = lock_type = TL_WRITE ;
res = open_and_lock_tables ( thd , table_list ) ;
}
if ( open_and_lock_for_insert_delayed ( thd , table_list ) )
DBUG_RETURN ( TRUE ) ;
}
else
# endif /* EMBEDDED_LIBRARY */
res = open_and_lock_tables ( thd , table_list ) ;
if ( res | | thd - > is_fatal_error )
DBUG_RETURN ( TRUE ) ;
{
if ( open_and_lock_tables ( thd , table_list ) )
DBUG_RETURN ( TRUE ) ;
}
thd - > proc_info = " init " ;
thd - > used_tables = 0 ;
@ -577,6 +631,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
/* mysql_prepare_insert set table_list->table if it was not set */
table = table_list - > table ;
lock_type = table_list - > lock_type ;
context = & thd - > lex - > select_lex . context ;
/*
@ -1634,19 +1689,32 @@ Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
Attempt to find or create a delayed insert thread to handle inserts
into this table .
@ return Return a local copy of the table in the delayed thread
@ retval NULL too many delayed threads OR
this thread ran out of resources OR
a newly created delayed insert thread ran out of resources OR
the delayed insert thread failed to open the table .
In the last three cases an error is set in THD .
@ return In case of success , table_list - > table points to a local copy
of the delayed table or is set to NULL , which indicates a
request for lock upgrade . In case of failure , value of
table_list - > table is undefined .
@ retval TRUE - this thread ran out of resources OR
- a newly created delayed insert thread ran out of
resources OR
- the created thread failed to open and lock the table
( e . g . because it does not exist ) OR
- the table opened in the created thread turned out to
be a view
@ retval FALSE - table successfully opened OR
- too many delayed insert threads OR
- the table has triggers and we have to fall back to
a normal INSERT
Two latter cases indicate a request for lock upgrade .
XXX : why do we regard INSERT DELAYED into a view as an error and
do not simply a lock upgrade ?
*/
static TABLE * delayed_get_table ( THD * thd , TABLE_LIST * table_list )
static
bool delayed_get_table ( THD * thd , TABLE_LIST * table_list )
{
int error ;
Delayed_insert * tmp ;
TABLE * table ;
DBUG_ENTER ( " delayed_get_table " ) ;
/* Must be set in the parser */
@ -1672,7 +1740,8 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list)
if ( ! ( tmp = new Delayed_insert ( ) ) )
{
my_error ( ER_OUTOFMEMORY , MYF ( 0 ) , sizeof ( Delayed_insert ) ) ;
goto err1 ;
thd - > fatal_error ( ) ;
goto end_create ;
}
pthread_mutex_lock ( & LOCK_thread_count ) ;
thread_count + + ;
@ -1681,9 +1750,10 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list)
tmp - > thd . query = my_strdup ( table_list - > table_name , MYF ( MY_WME ) ) ;
if ( tmp - > thd . db = = NULL | | tmp - > thd . query = = NULL )
{
/* The error is reported */
delete tmp ;
my_message ( ER_OUT_OF_RESOURCES , ER ( ER_OUT_OF_RESOURCES ) , MYF ( 0 ) ) ;
goto err1 ;
thd - > fatal_error ( ) ;
goto end_create ;
}
tmp - > table_list = * table_list ; // Needed to open table
tmp - > table_list . alias = tmp - > table_list . table_name = tmp - > thd . query ;
@ -1699,7 +1769,8 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list)
tmp - > unlock ( ) ;
delete tmp ;
my_error ( ER_CANT_CREATE_THREAD , MYF ( 0 ) , error ) ;
goto err1 ;
thd - > fatal_error ( ) ;
goto end_create ;
}
/* Wait until table is open */
@ -1712,41 +1783,44 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list)
thd - > proc_info = " got old table " ;
if ( tmp - > thd . killed )
{
if ( tmp - > thd . is_fatal _error)
if ( tmp - > thd . net . report _error)
{
/* Copy error message and abort */
thd - > fatal_error ( ) ;
strmov ( thd - > net . last_error , tmp - > thd . net . last_error ) ;
thd - > net . last_errno = tmp - > thd . net . last_errno ;
/*
Copy the error message . Note that we don ' t treat fatal
errors in the delayed thread as fatal errors in the
main thread . Use of my_message will enable stored
procedures continue handlers .
*/
my_message ( tmp - > thd . net . last_errno , tmp - > thd . net . last_error ,
MYF ( 0 ) ) ;
}
tmp - > unlock ( ) ;
goto err ;
goto end_create ;
}
if ( thd - > killed )
{
tmp - > unlock ( ) ;
goto err ;
goto end_create ;
}
}
pthread_mutex_unlock ( & LOCK_delayed_create ) ;
}
pthread_mutex_lock ( & tmp - > mutex ) ;
table = tmp - > get_local_table ( thd ) ;
table_list - > table = tmp - > get_local_table ( thd ) ;
pthread_mutex_unlock ( & tmp - > mutex ) ;
if ( table )
if ( table_list - > table )
{
DBUG_ASSERT ( tmp - > thd . net . report_error = = 0 & & thd - > net . report_error = = 0 ) ;
thd - > di = tmp ;
else if ( tmp - > thd . is_fatal_error )
thd - > fatal_error ( ) ;
}
/* Unlock the delayed insert object after its last access. */
tmp - > unlock ( ) ;
DBUG_RETURN ( ( table_list - > table = table ) ) ;
DBUG_RETURN ( table_list - > table = = NULL ) ;
err1 :
thd - > fatal_error ( ) ;
err :
end_create :
pthread_mutex_unlock ( & LOCK_delayed_create ) ;
DBUG_RETURN ( 0 ) ; // Continue with normal insert
DBUG_RETURN ( thd - > net . report_error ) ;
}
@ -1761,6 +1835,9 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list)
@ pre This function is called from the client thread . Delayed
insert thread mutex must be acquired before invoking this
function .
@ return Not - NULL table object on success . NULL in case of an error ,
which is set in client_thd .
*/
TABLE * Delayed_insert : : get_local_table ( THD * client_thd )
@ -1786,8 +1863,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
goto error ;
if ( dead )
{
strmov ( client_thd - > net . last_error , thd . net . last_error ) ;
client_thd - > net . last_errno = thd . net . last_errno ;
my_message ( thd . net . last_errno , thd . net . last_error , MYF ( 0 ) ) ;
goto error ;
}
}
@ -1832,7 +1908,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
for ( org_field = table - > field ; * org_field ; org_field + + , field + + )
{
if ( ! ( * field = ( * org_field ) - > new_field ( client_thd - > mem_root , copy , 1 ) ) )
DBUG_RETURN ( 0 ) ;
goto error ;
( * field ) - > orig_table = copy ; // Remove connection
( * field ) - > move_field ( adjust_ptrs ) ; // Point at copy->record[0]
if ( * org_field = = found_next_number_field )
@ -1872,8 +1948,9 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
/* Put a question in queue */
static int write_delayed ( THD * thd , TABLE * table , enum_duplicates duplic , bool ignore ,
char * query , uint query_length , bool log_on )
static
int write_delayed ( THD * thd , TABLE * table , enum_duplicates duplic , bool ignore ,
char * query , uint query_length , bool log_on )
{
delayed_row * row = 0 ;
Delayed_insert * di = thd - > di ;
@ -1940,6 +2017,10 @@ static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic, bool igno
DBUG_RETURN ( 1 ) ;
}
/**
Signal the delayed insert thread that this user connection
is finished using it for this statement .
*/
static void end_delayed_insert ( THD * thd )
{
@ -2052,6 +2133,15 @@ pthread_handler_t handle_delayed_insert(void *arg)
my_error ( ER_ILLEGAL_HA , MYF ( 0 ) , di - > table_list . table_name ) ;
goto end ;
}
if ( di - > table - > triggers )
{
/*
Table has triggers . This is not an error , but we do
not support triggers with delayed insert . Terminate the delayed
thread without an error and thus request lock upgrade .
*/
goto end ;
}
di - > table - > copy_blobs = 1 ;
/* One can now use this */