mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
Fix for bug in LOAD DATA INFILE and replication
Fix for SHOW VARIABLES in embedded server Docs/internals.texi: Added documentation for join_buffer_size configure.in: Changed version number sql/log_event.cc: Fix for bug in LOAD DATA INFILE sql/log_event.h: Fix for bug in LOAD DATA INFILE sql/slave.cc: Fix for bug in LOAD DATA INFILE sql/sql_show.cc: Fix for SHOW VARIABLES in embedded server
This commit is contained in:
parent
05bbf3efab
commit
1543bad3e7
6 changed files with 129 additions and 15 deletions
|
@ -96,13 +96,84 @@ cached for each user/database combination.
|
|||
Many use of @code{GROUP BY} or @code{DISTINCT} caches all found rows in
|
||||
a @code{HEAP} table. (This is a very quick in-memory table with hash index.)
|
||||
|
||||
@item Join Row Cache
|
||||
@item Join buffer Cache
|
||||
For every full join in a @code{SELECT} statement (a full join here means
|
||||
there were no keys that one could use to find the next table in a list),
|
||||
the found rows are cached in a join cache. One @code{SELECT} query can
|
||||
use many join caches in the worst case.
|
||||
@end table
|
||||
|
||||
@node join_buffer_size, flush tables, caching, Top
|
||||
@subchapter How MySQL uses the join_buffer cache
|
||||
|
||||
Basic information about @code{join_buffer_size}:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
It's only used in the case when join type is of type @code{ALL} or
|
||||
@code{index}; In other words: no possible keys can be used.
|
||||
@item
|
||||
A join buffer is never allocated for the first not-const table,
|
||||
even it it would be of type @code{ALL}/@code{index}.
|
||||
@item
|
||||
The buffer is allocated when we need to do a each full join between two
|
||||
tables and freed after the query is done.
|
||||
@item
|
||||
Accepted row combinations of tables before the @code{ALL}/@code{index}
|
||||
able is stored in the cache and is used to compare against each read
|
||||
row in the @code{ALL} table.
|
||||
@item
|
||||
We only store the used fields in the join_buffer cache, not the
|
||||
whole rows.
|
||||
@end itemize
|
||||
|
||||
Assume you have the following join:
|
||||
|
||||
@example
|
||||
Table name Type
|
||||
t1 range
|
||||
t2 ref
|
||||
t3 @code{ALL}
|
||||
@end example
|
||||
|
||||
The join is then done as follows:
|
||||
|
||||
@example
|
||||
- While rows in t1 matching range
|
||||
- Read through all rows in t2 according to reference key
|
||||
- Store used fields form t1,t2 in cache
|
||||
- If cache is full
|
||||
- Read through all rows in t3
|
||||
- Compare t3 row against all t1,t2 combination in cache
|
||||
- If rows satisfying join condition, send it to client
|
||||
- Empty cache
|
||||
|
||||
- Read through all rows in t3
|
||||
- Compare t3 row against all stored t1,t2 combinations in cache
|
||||
- If rows satisfying join condition, send it to client
|
||||
@end example
|
||||
|
||||
The above means that table t3 is scanned
|
||||
|
||||
@example
|
||||
(size-of-stored-row(t1,t2) * accepted-row-cominations(t1,t2))/
|
||||
join_buffer_size+1
|
||||
@end example
|
||||
times.
|
||||
|
||||
Some conclusions:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
The larger the join_buff_size, the fewer scans of t3.
|
||||
If @code{join_buff_size} is already large enough to hold all previous row
|
||||
combinations then there is no speed to gain by making it bigger.
|
||||
@item
|
||||
If there is several tables of @code{ALL}/@code{index} then the we
|
||||
allocate one @code{join_buffer_size buffer} for each of them and use the
|
||||
same algorithm described above to handle it. (In other words, we store
|
||||
the same row combination several times into different buffers)
|
||||
@end itemize
|
||||
|
||||
@node flush tables, filesort, caching, Top
|
||||
@chapter How MySQL Handles @code{FLUSH TABLES}
|
||||
|
|
|
@ -4,7 +4,7 @@ dnl Process this file with autoconf to produce a configure script.
|
|||
AC_INIT(sql/mysqld.cc)
|
||||
AC_CANONICAL_SYSTEM
|
||||
# The Docs Makefile.am parses this line!
|
||||
AM_INIT_AUTOMAKE(mysql, 4.0.8-gamma)
|
||||
AM_INIT_AUTOMAKE(mysql, 4.0.9-gamma)
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
|
||||
PROTOCOL_VERSION=10
|
||||
|
|
|
@ -206,7 +206,19 @@ Log_event::Log_event(const char* buf, bool old_format)
|
|||
|
||||
int Log_event::exec_event(struct st_relay_log_info* rli)
|
||||
{
|
||||
if (rli) // QQ When is this not true ?
|
||||
/*
|
||||
rli is null when (as far as I (Guilhem) know)
|
||||
the caller is
|
||||
Load_log_event::exec_event *and* that one is called from
|
||||
Execute_load_log_event::exec_event.
|
||||
In this case, we don't do anything here ;
|
||||
Execute_load_log_event::exec_event will call Log_event::exec_event
|
||||
again later with the proper rli.
|
||||
Strictly speaking, if we were sure that rli is null
|
||||
only in the case discussed above, 'if (rli)' is useless here.
|
||||
But as we are not 100% sure, keep it for now.
|
||||
*/
|
||||
if (rli)
|
||||
{
|
||||
if (rli->inside_transaction)
|
||||
rli->inc_pending(get_event_len());
|
||||
|
@ -1773,8 +1785,34 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli)
|
|||
return Log_event::exec_event(rli);
|
||||
}
|
||||
|
||||
/*
|
||||
Does the data loading job when executing a LOAD DATA on the slave
|
||||
|
||||
int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli)
|
||||
SYNOPSIS
|
||||
Load_log_event::exec_event
|
||||
net
|
||||
rli
|
||||
use_rli_only_for_errors - if set to 1, rli is provided to
|
||||
Load_log_event::exec_event only for this
|
||||
function to have RPL_LOG_NAME and
|
||||
rli->last_slave_error, both being used by
|
||||
error reports. rli's position advancing
|
||||
is skipped (done by the caller which is
|
||||
Execute_load_log_event::exec_event).
|
||||
- if set to 0, rli is provided for full use,
|
||||
i.e. for error reports and position
|
||||
advancing.
|
||||
|
||||
DESCRIPTION
|
||||
Does the data loading job when executing a LOAD DATA on the slave
|
||||
|
||||
RETURN VALUE
|
||||
0 Success
|
||||
1 Failure
|
||||
*/
|
||||
|
||||
int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
|
||||
bool use_rli_only_for_errors)
|
||||
{
|
||||
init_sql_alloc(&thd->mem_root, 8192,0);
|
||||
thd->db = rewrite_db((char*)db);
|
||||
|
@ -1836,8 +1874,12 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli)
|
|||
TL_WRITE))
|
||||
thd->query_error = 1;
|
||||
if (thd->cuted_fields)
|
||||
/*
|
||||
log_pos is the position of the LOAD
|
||||
event in the master log
|
||||
*/
|
||||
sql_print_error("Slave: load data infile at position %s in log \
|
||||
'%s' produced %d warning(s)", llstr(rli->master_log_pos,llbuff), RPL_LOG_NAME,
|
||||
'%s' produced %d warning(s)", llstr(log_pos,llbuff), RPL_LOG_NAME,
|
||||
thd->cuted_fields );
|
||||
if (net)
|
||||
net->pkt_nr= thd->net.pkt_nr;
|
||||
|
@ -1877,7 +1919,7 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli)
|
|||
return 1;
|
||||
}
|
||||
|
||||
return Log_event::exec_event(rli);
|
||||
return ( use_rli_only_for_errors ? 0 : Log_event::exec_event(rli) );
|
||||
}
|
||||
|
||||
|
||||
|
@ -2132,7 +2174,11 @@ int Execute_load_log_event::exec_event(struct st_relay_log_info* rli)
|
|||
save_options = thd->options;
|
||||
thd->options &= ~ (ulong) (OPTION_BIN_LOG);
|
||||
lev->thd = thd;
|
||||
if (lev->exec_event(0,0))
|
||||
/*
|
||||
lev->exec_event should use rli only for errors
|
||||
i.e. should not advance rli's position
|
||||
*/
|
||||
if (lev->exec_event(0,rli,1))
|
||||
{
|
||||
slave_print_error(rli,my_errno, "Failed executing load from '%s'", fname);
|
||||
thd->options = save_options;
|
||||
|
|
|
@ -417,9 +417,10 @@ public:
|
|||
const char* get_db() { return db; }
|
||||
int exec_event(struct st_relay_log_info* rli)
|
||||
{
|
||||
return exec_event(thd->slave_net,rli);
|
||||
return exec_event(thd->slave_net,rli,0);
|
||||
}
|
||||
int exec_event(NET* net, struct st_relay_log_info* rli);
|
||||
int exec_event(NET* net, struct st_relay_log_info* rli,
|
||||
bool use_rli_only_for_errors);
|
||||
#else
|
||||
void print(FILE* file, bool short_form = 0, char* last_db = 0);
|
||||
#endif
|
||||
|
|
|
@ -2343,12 +2343,6 @@ static int process_io_rotate(MASTER_INFO *mi, Rotate_log_event *rev)
|
|||
|
||||
memcpy(mi->master_log_name, rev->new_log_ident, rev->ident_len+1);
|
||||
mi->master_log_pos= rev->pos;
|
||||
|
||||
pthread_mutex_lock(&mi->rli.data_lock);
|
||||
memcpy(mi->rli.master_log_name, rev->new_log_ident, rev->ident_len+1);
|
||||
mi->rli.master_log_pos= rev->pos;
|
||||
pthread_mutex_unlock(&mi->rli.data_lock);
|
||||
|
||||
DBUG_PRINT("info", ("master_log_pos: '%s' %d",
|
||||
mi->master_log_name, (ulong) mi->master_log_pos));
|
||||
#ifndef DBUG_OFF
|
||||
|
|
|
@ -1217,6 +1217,7 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables,
|
|||
case SHOW_RPL_STATUS:
|
||||
net_store_data(&packet2, rpl_status_type[(int)rpl_status]);
|
||||
break;
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
case SHOW_SLAVE_RUNNING:
|
||||
{
|
||||
LOCK_ACTIVE_MI;
|
||||
|
@ -1226,6 +1227,7 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables,
|
|||
UNLOCK_ACTIVE_MI;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case SHOW_OPENTABLES:
|
||||
net_store_data(&packet2,(uint32) cached_tables());
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue