mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
Merge bk-internal.mysql.com:/home/bk/mysql-5.0
into bodhi.local:/opt/local/work/mysql-5.0-14897
This commit is contained in:
commit
bddfef1e36
64 changed files with 2601 additions and 822 deletions
|
@ -1 +1,4 @@
|
|||
44d03f27qNdqJmARzBoP3Is_cN5e0w
|
||||
44ec850ac2k4y2Omgr92GiWPBAVKGQ
|
||||
44edb86b1iE5knJ97MbliK_3lCiAXA
|
||||
44f33f3aj5KW5qweQeekY1LU0E9ZCg
|
||||
|
|
|
@ -211,7 +211,13 @@ typedef struct st_net {
|
|||
char last_error[MYSQL_ERRMSG_SIZE], sqlstate[SQLSTATE_LENGTH+1];
|
||||
unsigned int last_errno;
|
||||
unsigned char error;
|
||||
|
||||
/*
|
||||
'query_cache_query' should be accessed only via query cache
|
||||
functions and methods to maintain proper locking.
|
||||
*/
|
||||
gptr query_cache_query;
|
||||
|
||||
my_bool report_error; /* We should report error (we have unreported error) */
|
||||
my_bool return_errno;
|
||||
} NET;
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
# that ensure that starting conditions (environment) for the IM-test are as
|
||||
# expected.
|
||||
|
||||
# Wait for mysqld1 (guarded instance) to start.
|
||||
|
||||
--exec $MYSQL_TEST_DIR/t/wait_for_process.sh $IM_MYSQLD1_PATH_PID 30 started
|
||||
|
||||
# Check the running instances.
|
||||
|
||||
--connect (mysql1_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK)
|
||||
|
@ -14,6 +10,8 @@
|
|||
|
||||
SHOW VARIABLES LIKE 'server_id';
|
||||
|
||||
--source include/not_windows.inc
|
||||
|
||||
--connection default
|
||||
|
||||
# Let IM detect that mysqld1 is online. This delay should be longer than
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
--connect (dflt_server_con,localhost,root,,mysql,$IM_MYSQLD1_PORT,$IM_MYSQLD1_SOCK)
|
||||
--connection dflt_server_con
|
||||
|
||||
--source include/not_windows.inc
|
||||
|
||||
--connection default
|
||||
--disconnect dflt_server_con
|
|
@ -19,13 +19,39 @@ sub mtr_tonewfile($@);
|
|||
##############################################################################
|
||||
|
||||
sub mtr_get_pid_from_file ($) {
|
||||
my $file= shift;
|
||||
my $pid_file_path= shift;
|
||||
my $TOTAL_ATTEMPTS= 30;
|
||||
my $timeout= 1;
|
||||
|
||||
open(FILE,"<",$file) or mtr_error("can't open file \"$file\": $!");
|
||||
my $pid= <FILE>;
|
||||
chomp($pid);
|
||||
close FILE;
|
||||
return $pid;
|
||||
# We should read from the file until we get correct pid. As it is
|
||||
# stated in BUG#21884, pid file can be empty at some moment. So, we should
|
||||
# read it until we get valid data.
|
||||
|
||||
for (my $cur_attempt= 1; $cur_attempt <= $TOTAL_ATTEMPTS; ++$cur_attempt)
|
||||
{
|
||||
mtr_debug("Reading pid file '$pid_file_path' " .
|
||||
"($cur_attempt of $TOTAL_ATTEMPTS)...");
|
||||
|
||||
open(FILE, '<', $pid_file_path)
|
||||
or mtr_error("can't open file \"$pid_file_path\": $!");
|
||||
|
||||
my $pid= <FILE>;
|
||||
|
||||
chomp($pid) if defined $pid;
|
||||
|
||||
close FILE;
|
||||
|
||||
return $pid if defined $pid && $pid ne '';
|
||||
|
||||
mtr_debug("Pid file '$pid_file_path' is empty. " .
|
||||
"Sleeping $timeout second(s)...");
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
mtr_error("Pid file '$pid_file_path' is corrupted. " .
|
||||
"Can not retrieve PID in " .
|
||||
($timeout * $TOTAL_ATTEMPTS) . " seconds.");
|
||||
}
|
||||
|
||||
sub mtr_get_opts_from_file ($) {
|
||||
|
|
|
@ -20,7 +20,29 @@ sub mtr_record_dead_children ();
|
|||
sub mtr_exit ($);
|
||||
sub sleep_until_file_created ($$$);
|
||||
sub mtr_kill_processes ($);
|
||||
sub mtr_kill_process ($$$$);
|
||||
sub mtr_ping_mysqld_server ($);
|
||||
|
||||
# Private IM-related operations.
|
||||
|
||||
sub mtr_im_kill_process ($$$$);
|
||||
sub mtr_im_load_pids ($);
|
||||
sub mtr_im_terminate ($);
|
||||
sub mtr_im_check_alive ($);
|
||||
sub mtr_im_check_main_alive ($);
|
||||
sub mtr_im_check_angel_alive ($);
|
||||
sub mtr_im_check_mysqlds_alive ($);
|
||||
sub mtr_im_check_mysqld_alive ($$);
|
||||
sub mtr_im_cleanup ($);
|
||||
sub mtr_im_rm_file ($);
|
||||
sub mtr_im_errlog ($);
|
||||
sub mtr_im_kill ($);
|
||||
sub mtr_im_wait_for_connection ($$$);
|
||||
sub mtr_im_wait_for_mysqld($$$);
|
||||
|
||||
# Public IM-related operations.
|
||||
|
||||
sub mtr_im_start ($$);
|
||||
sub mtr_im_stop ($);
|
||||
|
||||
# static in C
|
||||
sub spawn_impl ($$$$$$$$);
|
||||
|
@ -359,40 +381,51 @@ sub mtr_process_exit_status {
|
|||
|
||||
sub mtr_kill_leftovers () {
|
||||
|
||||
# First, kill all masters and slaves that would conflict with
|
||||
# this run. Make sure to remove the PID file, if any.
|
||||
# FIXME kill IM manager first, else it will restart the servers, how?!
|
||||
mtr_debug("mtr_kill_leftovers(): started.");
|
||||
|
||||
mtr_im_stop($::instance_manager);
|
||||
|
||||
# Kill mysqld servers (masters and slaves) that would conflict with this
|
||||
# run. Make sure to remove the PID file, if any.
|
||||
# Don't touch IM-managed mysqld instances -- they should be stopped by
|
||||
# mtr_im_stop().
|
||||
|
||||
mtr_debug("Collecting mysqld-instances to shutdown...");
|
||||
|
||||
my @args;
|
||||
|
||||
for ( my $idx; $idx < 2; $idx++ )
|
||||
for ( my $idx= 0; $idx < 2; $idx++ )
|
||||
{
|
||||
my $pidfile= $::master->[$idx]->{'path_mypid'};
|
||||
my $sockfile= $::master->[$idx]->{'path_mysock'};
|
||||
my $port= $::master->[$idx]->{'path_myport'};
|
||||
|
||||
push(@args,{
|
||||
pid => 0, # We don't know the PID
|
||||
pidfile => $::instance_manager->{'instances'}->[$idx]->{'path_pid'},
|
||||
sockfile => $::instance_manager->{'instances'}->[$idx]->{'path_sock'},
|
||||
port => $::instance_manager->{'instances'}->[$idx]->{'port'},
|
||||
pidfile => $pidfile,
|
||||
sockfile => $sockfile,
|
||||
port => $port,
|
||||
});
|
||||
|
||||
mtr_debug(" - Master mysqld " .
|
||||
"(idx: $idx; pid: '$pidfile'; socket: '$sockfile'; port: $port)");
|
||||
}
|
||||
|
||||
for ( my $idx; $idx < 2; $idx++ )
|
||||
for ( my $idx= 0; $idx < 3; $idx++ )
|
||||
{
|
||||
push(@args,{
|
||||
pid => 0, # We don't know the PID
|
||||
pidfile => $::master->[$idx]->{'path_mypid'},
|
||||
sockfile => $::master->[$idx]->{'path_mysock'},
|
||||
port => $::master->[$idx]->{'path_myport'},
|
||||
});
|
||||
}
|
||||
my $pidfile= $::slave->[$idx]->{'path_mypid'};
|
||||
my $sockfile= $::slave->[$idx]->{'path_mysock'};
|
||||
my $port= $::slave->[$idx]->{'path_myport'};
|
||||
|
||||
for ( my $idx; $idx < 3; $idx++ )
|
||||
{
|
||||
push(@args,{
|
||||
pid => 0, # We don't know the PID
|
||||
pidfile => $::slave->[$idx]->{'path_mypid'},
|
||||
sockfile => $::slave->[$idx]->{'path_mysock'},
|
||||
port => $::slave->[$idx]->{'path_myport'},
|
||||
pidfile => $pidfile,
|
||||
sockfile => $sockfile,
|
||||
port => $port,
|
||||
});
|
||||
|
||||
mtr_debug(" - Slave mysqld " .
|
||||
"(idx: $idx; pid: '$pidfile'; socket: '$sockfile'; port: $port)");
|
||||
}
|
||||
|
||||
mtr_mysqladmin_shutdown(\@args, 20);
|
||||
|
@ -413,6 +446,8 @@ sub mtr_kill_leftovers () {
|
|||
# FIXME $path_run_dir or something
|
||||
my $rundir= "$::opt_vardir/run";
|
||||
|
||||
mtr_debug("Processing PID files in directory '$rundir'...");
|
||||
|
||||
if ( -d $rundir )
|
||||
{
|
||||
opendir(RUNDIR, $rundir)
|
||||
|
@ -426,8 +461,12 @@ sub mtr_kill_leftovers () {
|
|||
|
||||
if ( -f $pidfile )
|
||||
{
|
||||
mtr_debug("Processing PID file: '$pidfile'...");
|
||||
|
||||
my $pid= mtr_get_pid_from_file($pidfile);
|
||||
|
||||
mtr_debug("Got pid: $pid from file '$pidfile'");
|
||||
|
||||
# Race, could have been removed between I tested with -f
|
||||
# and the unlink() below, so I better check again with -f
|
||||
|
||||
|
@ -438,14 +477,24 @@ sub mtr_kill_leftovers () {
|
|||
|
||||
if ( $::glob_cygwin_perl or kill(0, $pid) )
|
||||
{
|
||||
mtr_debug("There is process with pid $pid -- scheduling for kill.");
|
||||
push(@pids, $pid); # We know (cygwin guess) it exists
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("There is no process with pid $pid -- skipping.");
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(RUNDIR);
|
||||
|
||||
if ( @pids )
|
||||
{
|
||||
mtr_debug("Killing the following processes with PID files: " .
|
||||
join(' ', @pids) . "...");
|
||||
|
||||
start_reap_all();
|
||||
|
||||
if ( $::glob_cygwin_perl )
|
||||
{
|
||||
# We have no (easy) way of knowing the Cygwin controlling
|
||||
|
@ -459,6 +508,7 @@ sub mtr_kill_leftovers () {
|
|||
my $retries= 10; # 10 seconds
|
||||
do
|
||||
{
|
||||
mtr_debug("Sending SIGKILL to pids: " . join(' ', @pids));
|
||||
kill(9, @pids);
|
||||
mtr_debug("Sleep 1 second waiting for processes to die");
|
||||
sleep(1) # Wait one second
|
||||
|
@ -469,19 +519,29 @@ sub mtr_kill_leftovers () {
|
|||
mtr_warning("can't kill process(es) " . join(" ", @pids));
|
||||
}
|
||||
}
|
||||
|
||||
stop_reap_all();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("Directory for PID files ($rundir) does not exist.");
|
||||
}
|
||||
|
||||
# We may have failed everything, bug we now check again if we have
|
||||
# the listen ports free to use, and if they are free, just go for it.
|
||||
|
||||
mtr_debug("Checking known mysqld servers...");
|
||||
|
||||
foreach my $srv ( @args )
|
||||
{
|
||||
if ( mtr_ping_mysqld_server($srv->{'port'}, $srv->{'sockfile'}) )
|
||||
if ( mtr_ping_mysqld_server($srv->{'port'}) )
|
||||
{
|
||||
mtr_warning("can't kill old mysqld holding port $srv->{'port'}");
|
||||
}
|
||||
}
|
||||
|
||||
mtr_debug("mtr_kill_leftovers(): finished.");
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
|
@ -653,10 +713,15 @@ sub mtr_mysqladmin_shutdown {
|
|||
my %mysql_admin_pids;
|
||||
my @to_kill_specs;
|
||||
|
||||
mtr_debug("mtr_mysqladmin_shutdown(): starting...");
|
||||
mtr_debug("Collecting mysqld-instances to shutdown...");
|
||||
|
||||
foreach my $srv ( @$spec )
|
||||
{
|
||||
if ( mtr_ping_mysqld_server($srv->{'port'}, $srv->{'sockfile'}) )
|
||||
if ( mtr_ping_mysqld_server($srv->{'port'}) )
|
||||
{
|
||||
mtr_debug("Mysqld (port: $srv->{port}) needs to be stopped.");
|
||||
|
||||
push(@to_kill_specs, $srv);
|
||||
}
|
||||
}
|
||||
|
@ -688,6 +753,9 @@ sub mtr_mysqladmin_shutdown {
|
|||
mtr_add_arg($args, "--shutdown_timeout=$adm_shutdown_tmo");
|
||||
mtr_add_arg($args, "shutdown");
|
||||
|
||||
mtr_debug("Shutting down mysqld " .
|
||||
"(port: $srv->{port}; socket: '$srv->{sockfile}')...");
|
||||
|
||||
my $path_mysqladmin_log= "$::opt_vardir/log/mysqladmin.log";
|
||||
my $pid= mtr_spawn($::exe_mysqladmin, $args,
|
||||
"", $path_mysqladmin_log, $path_mysqladmin_log, "",
|
||||
|
@ -719,14 +787,18 @@ sub mtr_mysqladmin_shutdown {
|
|||
my $res= 1; # If we just fall through, we are done
|
||||
# in the sense that the servers don't
|
||||
# listen to their ports any longer
|
||||
|
||||
mtr_debug("Waiting for mysqld servers to stop...");
|
||||
|
||||
TIME:
|
||||
while ( $timeout-- )
|
||||
{
|
||||
foreach my $srv ( @to_kill_specs )
|
||||
{
|
||||
$res= 1; # We are optimistic
|
||||
if ( mtr_ping_mysqld_server($srv->{'port'}, $srv->{'sockfile'}) )
|
||||
if ( mtr_ping_mysqld_server($srv->{'port'}) )
|
||||
{
|
||||
mtr_debug("Mysqld (port: $srv->{port}) is still alive.");
|
||||
mtr_debug("Sleep 1 second waiting for processes to stop using port");
|
||||
sleep(1); # One second
|
||||
$res= 0;
|
||||
|
@ -736,7 +808,14 @@ sub mtr_mysqladmin_shutdown {
|
|||
last; # If we got here, we are done
|
||||
}
|
||||
|
||||
$timeout or mtr_debug("At least one server is still listening to its port");
|
||||
if ($res)
|
||||
{
|
||||
mtr_debug("mtr_mysqladmin_shutdown(): All mysqld instances are down.");
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("mtr_mysqladmin_shutdown(): At least one server is alive.");
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
@ -795,7 +874,7 @@ sub stop_reap_all {
|
|||
$SIG{CHLD}= 'DEFAULT';
|
||||
}
|
||||
|
||||
sub mtr_ping_mysqld_server () {
|
||||
sub mtr_ping_mysqld_server ($) {
|
||||
my $port= shift;
|
||||
|
||||
my $remote= "localhost";
|
||||
|
@ -810,13 +889,18 @@ sub mtr_ping_mysqld_server () {
|
|||
{
|
||||
mtr_error("can't create socket: $!");
|
||||
}
|
||||
|
||||
mtr_debug("Pinging server (port: $port)...");
|
||||
|
||||
if ( connect(SOCK, $paddr) )
|
||||
{
|
||||
mtr_debug("Server (port: $port) is alive.");
|
||||
close(SOCK); # FIXME check error?
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("Server (port: $port) is dead.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -886,34 +970,6 @@ sub mtr_kill_processes ($) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
sub mtr_kill_process ($$$$) {
|
||||
my $pid= shift;
|
||||
my $signal= shift;
|
||||
my $total_retries= shift;
|
||||
my $timeout= shift;
|
||||
|
||||
for (my $cur_attempt= 1; $cur_attempt <= $total_retries; ++$cur_attempt)
|
||||
{
|
||||
mtr_debug("Sending $signal to $pid...");
|
||||
|
||||
kill($signal, $pid);
|
||||
|
||||
unless (kill (0, $pid))
|
||||
{
|
||||
mtr_debug("Process $pid died.");
|
||||
return;
|
||||
}
|
||||
|
||||
mtr_debug("Sleeping $timeout second(s) waiting for processes to die...");
|
||||
|
||||
sleep($timeout);
|
||||
}
|
||||
|
||||
mtr_debug("Process $pid is still alive after $total_retries " .
|
||||
"of sending signal $signal.");
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# When we exit, we kill off all children
|
||||
|
@ -943,4 +999,676 @@ sub mtr_exit ($) {
|
|||
exit($code);
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Instance Manager management routines.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
sub mtr_im_kill_process ($$$$) {
|
||||
my $pid_lst= shift;
|
||||
my $signal= shift;
|
||||
my $total_retries= shift;
|
||||
my $timeout= shift;
|
||||
|
||||
my %pids;
|
||||
|
||||
foreach my $pid (@{$pid_lst})
|
||||
{
|
||||
$pids{$pid}= 1;
|
||||
}
|
||||
|
||||
for (my $cur_attempt= 1; $cur_attempt <= $total_retries; ++$cur_attempt)
|
||||
{
|
||||
foreach my $pid (keys %pids)
|
||||
{
|
||||
mtr_debug("Sending $signal to $pid...");
|
||||
|
||||
kill($signal, $pid);
|
||||
|
||||
unless (kill (0, $pid))
|
||||
{
|
||||
mtr_debug("Process $pid died.");
|
||||
delete $pids{$pid};
|
||||
}
|
||||
}
|
||||
|
||||
return if scalar keys %pids == 0;
|
||||
|
||||
mtr_debug("Sleeping $timeout second(s) waiting for processes to die...");
|
||||
|
||||
sleep($timeout);
|
||||
}
|
||||
|
||||
mtr_debug("Process(es) " .
|
||||
join(' ', keys %pids) .
|
||||
" is still alive after $total_retries " .
|
||||
"of sending signal $signal.");
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
|
||||
sub mtr_im_load_pids($) {
|
||||
my $instance_manager= shift;
|
||||
|
||||
mtr_debug("Loading PID files...");
|
||||
|
||||
# Obtain mysqld-process pids.
|
||||
|
||||
my $instances = $instance_manager->{'instances'};
|
||||
|
||||
for (my $idx= 0; $idx < 2; ++$idx)
|
||||
{
|
||||
mtr_debug("IM-guarded mysqld[$idx] PID file: '" .
|
||||
$instances->[$idx]->{'path_pid'} . "'.");
|
||||
|
||||
my $mysqld_pid;
|
||||
|
||||
if (-r $instances->[$idx]->{'path_pid'})
|
||||
{
|
||||
$mysqld_pid= mtr_get_pid_from_file($instances->[$idx]->{'path_pid'});
|
||||
mtr_debug("IM-guarded mysqld[$idx] PID: $mysqld_pid.");
|
||||
}
|
||||
else
|
||||
{
|
||||
$mysqld_pid= undef;
|
||||
mtr_debug("IM-guarded mysqld[$idx]: no PID file.");
|
||||
}
|
||||
|
||||
$instances->[$idx]->{'pid'}= $mysqld_pid;
|
||||
}
|
||||
|
||||
# Re-read Instance Manager PIDs from the file, since during tests Instance
|
||||
# Manager could have been restarted, so its PIDs could have been changed.
|
||||
|
||||
# - IM-main
|
||||
|
||||
mtr_debug("IM-main PID file: '$instance_manager->{path_pid}'.");
|
||||
|
||||
if (-f $instance_manager->{'path_pid'})
|
||||
{
|
||||
$instance_manager->{'pid'} =
|
||||
mtr_get_pid_from_file($instance_manager->{'path_pid'});
|
||||
|
||||
mtr_debug("IM-main PID: $instance_manager->{pid}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("IM-main: no PID file.");
|
||||
$instance_manager->{'pid'}= undef;
|
||||
}
|
||||
|
||||
# - IM-angel
|
||||
|
||||
mtr_debug("IM-angel PID file: '$instance_manager->{path_angel_pid}'.");
|
||||
|
||||
if (-f $instance_manager->{'path_angel_pid'})
|
||||
{
|
||||
$instance_manager->{'angel_pid'} =
|
||||
mtr_get_pid_from_file($instance_manager->{'path_angel_pid'});
|
||||
|
||||
mtr_debug("IM-angel PID: $instance_manager->{'angel_pid'}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("IM-angel: no PID file.");
|
||||
$instance_manager->{'angel_pid'} = undef;
|
||||
}
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
|
||||
sub mtr_im_terminate($) {
|
||||
my $instance_manager= shift;
|
||||
|
||||
# Load pids from pid-files. We should do it first of all, because IM deletes
|
||||
# them on shutdown.
|
||||
|
||||
mtr_im_load_pids($instance_manager);
|
||||
|
||||
mtr_debug("Shutting Instance Manager down...");
|
||||
|
||||
# Ignoring SIGCHLD so that all children could rest in peace.
|
||||
|
||||
start_reap_all();
|
||||
|
||||
# Send SIGTERM to IM-main.
|
||||
|
||||
if (defined $instance_manager->{'pid'})
|
||||
{
|
||||
mtr_debug("IM-main pid: $instance_manager->{pid}.");
|
||||
mtr_debug("Stopping IM-main...");
|
||||
|
||||
mtr_im_kill_process([ $instance_manager->{'pid'} ], 'TERM', 10, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("IM-main pid: n/a.");
|
||||
}
|
||||
|
||||
# If IM-angel was alive, wait for it to die.
|
||||
|
||||
if (defined $instance_manager->{'angel_pid'})
|
||||
{
|
||||
mtr_debug("IM-angel pid: $instance_manager->{'angel_pid'}.");
|
||||
mtr_debug("Waiting for IM-angel to die...");
|
||||
|
||||
my $total_attempts= 10;
|
||||
|
||||
for (my $cur_attempt=1; $cur_attempt <= $total_attempts; ++$cur_attempt)
|
||||
{
|
||||
unless (kill (0, $instance_manager->{'angel_pid'}))
|
||||
{
|
||||
mtr_debug("IM-angel died.");
|
||||
last;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("IM-angel pid: n/a.");
|
||||
}
|
||||
|
||||
stop_reap_all();
|
||||
|
||||
# Re-load PIDs.
|
||||
|
||||
mtr_im_load_pids($instance_manager);
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
|
||||
sub mtr_im_check_alive($) {
|
||||
my $instance_manager= shift;
|
||||
|
||||
mtr_debug("Checking whether IM-components are alive...");
|
||||
|
||||
return 1 if mtr_im_check_main_alive($instance_manager);
|
||||
|
||||
return 1 if mtr_im_check_angel_alive($instance_manager);
|
||||
|
||||
return 1 if mtr_im_check_mysqlds_alive($instance_manager);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
|
||||
sub mtr_im_check_main_alive($) {
|
||||
my $instance_manager= shift;
|
||||
|
||||
# Check that the process, that we know to be IM's, is dead.
|
||||
|
||||
if (defined $instance_manager->{'pid'})
|
||||
{
|
||||
if (kill (0, $instance_manager->{'pid'}))
|
||||
{
|
||||
mtr_debug("IM-main (PID: $instance_manager->{pid}) is alive.");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("IM-main (PID: $instance_manager->{pid}) is dead.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("No PID file for IM-main.");
|
||||
}
|
||||
|
||||
# Check that IM does not accept client connections.
|
||||
|
||||
if (mtr_ping_mysqld_server($instance_manager->{'port'}))
|
||||
{
|
||||
mtr_debug("IM-main (port: $instance_manager->{port}) " .
|
||||
"is accepting connections.");
|
||||
|
||||
mtr_im_errlog("IM-main is accepting connections on port " .
|
||||
"$instance_manager->{port}, but there is no " .
|
||||
"process information.");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("IM-main (port: $instance_manager->{port}) " .
|
||||
"does not accept connections.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
|
||||
sub mtr_im_check_angel_alive($) {
|
||||
my $instance_manager= shift;
|
||||
|
||||
# Check that the process, that we know to be the Angel, is dead.
|
||||
|
||||
if (defined $instance_manager->{'angel_pid'})
|
||||
{
|
||||
if (kill (0, $instance_manager->{'angel_pid'}))
|
||||
{
|
||||
mtr_debug("IM-angel (PID: $instance_manager->{angel_pid}) is alive.");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("IM-angel (PID: $instance_manager->{angel_pid}) is dead.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("No PID file for IM-angel.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
|
||||
sub mtr_im_check_mysqlds_alive($) {
|
||||
my $instance_manager= shift;
|
||||
|
||||
mtr_debug("Checking for IM-guarded mysqld instances...");
|
||||
|
||||
my $instances = $instance_manager->{'instances'};
|
||||
|
||||
for (my $idx= 0; $idx < 2; ++$idx)
|
||||
{
|
||||
mtr_debug("Checking mysqld[$idx]...");
|
||||
|
||||
return 1
|
||||
if mtr_im_check_mysqld_alive($instance_manager, $instances->[$idx]);
|
||||
}
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
|
||||
sub mtr_im_check_mysqld_alive($$) {
|
||||
my $instance_manager= shift;
|
||||
my $mysqld_instance= shift;
|
||||
|
||||
# Check that the process is dead.
|
||||
|
||||
if (defined $instance_manager->{'pid'})
|
||||
{
|
||||
if (kill (0, $instance_manager->{'pid'}))
|
||||
{
|
||||
mtr_debug("Mysqld instance (PID: $mysqld_instance->{pid}) is alive.");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("Mysqld instance (PID: $mysqld_instance->{pid}) is dead.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("No PID file for mysqld instance.");
|
||||
}
|
||||
|
||||
# Check that mysqld does not accept client connections.
|
||||
|
||||
if (mtr_ping_mysqld_server($mysqld_instance->{'port'}))
|
||||
{
|
||||
mtr_debug("Mysqld instance (port: $mysqld_instance->{port}) " .
|
||||
"is accepting connections.");
|
||||
|
||||
mtr_im_errlog("Mysqld is accepting connections on port " .
|
||||
"$mysqld_instance->{port}, but there is no " .
|
||||
"process information.");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("Mysqld instance (port: $mysqld_instance->{port}) " .
|
||||
"does not accept connections.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
|
||||
sub mtr_im_cleanup($) {
|
||||
my $instance_manager= shift;
|
||||
|
||||
mtr_im_rm_file($instance_manager->{'path_pid'});
|
||||
mtr_im_rm_file($instance_manager->{'path_sock'});
|
||||
|
||||
mtr_im_rm_file($instance_manager->{'path_angel_pid'});
|
||||
|
||||
for (my $idx= 0; $idx < 2; ++$idx)
|
||||
{
|
||||
mtr_im_rm_file($instance_manager->{'instances'}->[$idx]->{'path_pid'});
|
||||
mtr_im_rm_file($instance_manager->{'instances'}->[$idx]->{'path_sock'});
|
||||
}
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
|
||||
sub mtr_im_rm_file($)
|
||||
{
|
||||
my $file_path= shift;
|
||||
|
||||
if (-f $file_path)
|
||||
{
|
||||
mtr_debug("Removing '$file_path'...");
|
||||
|
||||
mtr_warning("Can not remove '$file_path'.")
|
||||
unless unlink($file_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("File '$file_path' does not exist already.");
|
||||
}
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
|
||||
sub mtr_im_errlog($) {
|
||||
my $msg= shift;
|
||||
|
||||
# Complain in error log so that a warning will be shown.
|
||||
#
|
||||
# TODO: unless BUG#20761 is fixed, we will print the warning to stdout, so
|
||||
# that it can be seen on console and does not produce pushbuild error.
|
||||
|
||||
# my $errlog= "$opt_vardir/log/mysql-test-run.pl.err";
|
||||
#
|
||||
# open (ERRLOG, ">>$errlog") ||
|
||||
# mtr_error("Can not open error log ($errlog)");
|
||||
#
|
||||
# my $ts= localtime();
|
||||
# print ERRLOG
|
||||
# "Warning: [$ts] $msg\n";
|
||||
#
|
||||
# close ERRLOG;
|
||||
|
||||
my $ts= localtime();
|
||||
print "Warning: [$ts] $msg\n";
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
|
||||
sub mtr_im_kill($) {
|
||||
my $instance_manager= shift;
|
||||
|
||||
# Re-load PIDs. That can be useful because some processes could have been
|
||||
# restarted.
|
||||
|
||||
mtr_im_load_pids($instance_manager);
|
||||
|
||||
# Ignoring SIGCHLD so that all children could rest in peace.
|
||||
|
||||
start_reap_all();
|
||||
|
||||
# Kill IM-angel first of all.
|
||||
|
||||
if (defined $instance_manager->{'angel_pid'})
|
||||
{
|
||||
mtr_debug("Killing IM-angel (PID: $instance_manager->{angel_pid})...");
|
||||
mtr_im_kill_process([ $instance_manager->{'angel_pid'} ], 'KILL', 10, 1)
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("IM-angel is dead.");
|
||||
}
|
||||
|
||||
# Re-load PIDs again.
|
||||
|
||||
mtr_im_load_pids($instance_manager);
|
||||
|
||||
# Kill IM-main.
|
||||
|
||||
if (defined $instance_manager->{'pid'})
|
||||
{
|
||||
mtr_debug("Killing IM-main (PID: $instance_manager->pid})...");
|
||||
mtr_im_kill_process([ $instance_manager->{'pid'} ], 'KILL', 10, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("IM-main is dead.");
|
||||
}
|
||||
|
||||
# Re-load PIDs again.
|
||||
|
||||
mtr_im_load_pids($instance_manager);
|
||||
|
||||
# Kill guarded mysqld instances.
|
||||
|
||||
my @mysqld_pids;
|
||||
|
||||
mtr_debug("Collecting PIDs of mysqld instances to kill...");
|
||||
|
||||
for (my $idx= 0; $idx < 2; ++$idx)
|
||||
{
|
||||
my $pid= $instance_manager->{'instances'}->[$idx]->{'pid'};
|
||||
|
||||
next unless defined $pid;
|
||||
|
||||
mtr_debug(" - IM-guarded mysqld[$idx] PID: $pid.");
|
||||
|
||||
push (@mysqld_pids, $pid);
|
||||
}
|
||||
|
||||
if (scalar @mysqld_pids > 0)
|
||||
{
|
||||
mtr_debug("Killing IM-guarded mysqld instances...");
|
||||
mtr_im_kill_process(\@mysqld_pids, 'KILL', 10, 1);
|
||||
}
|
||||
|
||||
# That's all.
|
||||
|
||||
stop_reap_all();
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
|
||||
sub mtr_im_wait_for_connection($$$) {
|
||||
my $instance_manager= shift;
|
||||
my $total_attempts= shift;
|
||||
my $connect_timeout= shift;
|
||||
|
||||
mtr_debug("Waiting for IM on port $instance_manager->{port} " .
|
||||
"to start accepting connections...");
|
||||
|
||||
for (my $cur_attempt= 1; $cur_attempt <= $total_attempts; ++$cur_attempt)
|
||||
{
|
||||
mtr_debug("Trying to connect to IM ($cur_attempt of $total_attempts)...");
|
||||
|
||||
if (mtr_ping_mysqld_server($instance_manager->{'port'}))
|
||||
{
|
||||
mtr_debug("IM is accepting connections " .
|
||||
"on port $instance_manager->{port}.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
mtr_debug("Sleeping $connect_timeout...");
|
||||
sleep($connect_timeout);
|
||||
}
|
||||
|
||||
mtr_debug("IM does not accept connections " .
|
||||
"on port $instance_manager->{port} after " .
|
||||
($total_attempts * $connect_timeout) . " seconds.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
|
||||
sub mtr_im_wait_for_mysqld($$$) {
|
||||
my $mysqld= shift;
|
||||
my $total_attempts= shift;
|
||||
my $connect_timeout= shift;
|
||||
|
||||
mtr_debug("Waiting for IM-guarded mysqld on port $mysqld->{port} " .
|
||||
"to start accepting connections...");
|
||||
|
||||
for (my $cur_attempt= 1; $cur_attempt <= $total_attempts; ++$cur_attempt)
|
||||
{
|
||||
mtr_debug("Trying to connect to mysqld " .
|
||||
"($cur_attempt of $total_attempts)...");
|
||||
|
||||
if (mtr_ping_mysqld_server($mysqld->{'port'}))
|
||||
{
|
||||
mtr_debug("Mysqld is accepting connections " .
|
||||
"on port $mysqld->{port}.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
mtr_debug("Sleeping $connect_timeout...");
|
||||
sleep($connect_timeout);
|
||||
}
|
||||
|
||||
mtr_debug("Mysqld does not accept connections " .
|
||||
"on port $mysqld->{port} after " .
|
||||
($total_attempts * $connect_timeout) . " seconds.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
|
||||
sub mtr_im_start($$) {
|
||||
my $instance_manager = shift;
|
||||
my $opts = shift;
|
||||
|
||||
mtr_debug("Starting Instance Manager...");
|
||||
|
||||
my $args;
|
||||
mtr_init_args(\$args);
|
||||
mtr_add_arg($args, "--defaults-file=%s",
|
||||
$instance_manager->{'defaults_file'});
|
||||
|
||||
foreach my $opt (@{$opts})
|
||||
{
|
||||
mtr_add_arg($args, $opt);
|
||||
}
|
||||
|
||||
$instance_manager->{'pid'} =
|
||||
mtr_spawn(
|
||||
$::exe_im, # path to the executable
|
||||
$args, # cmd-line args
|
||||
'', # stdin
|
||||
$instance_manager->{'path_log'}, # stdout
|
||||
$instance_manager->{'path_err'}, # stderr
|
||||
'', # pid file path (not used)
|
||||
{ append_log_file => 1 } # append log files
|
||||
);
|
||||
|
||||
if ( ! $instance_manager->{'pid'} )
|
||||
{
|
||||
mtr_report('Could not start Instance Manager');
|
||||
return;
|
||||
}
|
||||
|
||||
# Instance Manager can be run in daemon mode. In this case, it creates
|
||||
# several processes and the parent process, created by mtr_spawn(), exits just
|
||||
# after start. So, we have to obtain Instance Manager PID from the PID file.
|
||||
|
||||
if ( ! sleep_until_file_created(
|
||||
$instance_manager->{'path_pid'},
|
||||
$instance_manager->{'start_timeout'},
|
||||
-1)) # real PID is still unknown
|
||||
{
|
||||
mtr_report("Instance Manager PID file is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
$instance_manager->{'pid'} =
|
||||
mtr_get_pid_from_file($instance_manager->{'path_pid'});
|
||||
|
||||
mtr_debug("Instance Manager started. PID: $instance_manager->{pid}.");
|
||||
|
||||
# Wait until we can connect to IM.
|
||||
|
||||
my $IM_CONNECT_TIMEOUT= 30;
|
||||
|
||||
unless (mtr_im_wait_for_connection($instance_manager,
|
||||
$IM_CONNECT_TIMEOUT, 1))
|
||||
{
|
||||
mtr_debug("Can not connect to Instance Manager " .
|
||||
"in $IM_CONNECT_TIMEOUT seconds after start.");
|
||||
mtr_debug("Aborting test suite...");
|
||||
|
||||
mtr_kill_leftovers();
|
||||
|
||||
mtr_error("Can not connect to Instance Manager " .
|
||||
"in $IM_CONNECT_TIMEOUT seconds after start.");
|
||||
}
|
||||
|
||||
# Wait until we can connect to guarded mysqld-instances
|
||||
# (in other words -- wait for IM to start guarded instances).
|
||||
|
||||
for (my $idx= 0; $idx < 2; ++$idx)
|
||||
{
|
||||
my $mysqld= $instance_manager->{'instances'}->[$idx];
|
||||
|
||||
next if exists $mysqld->{'nonguarded'};
|
||||
|
||||
mtr_debug("Waiting for mysqld[$idx] to start...");
|
||||
|
||||
unless (mtr_im_wait_for_mysqld($mysqld, 30, 1))
|
||||
{
|
||||
mtr_debug("Can not connect to mysqld[$idx] " .
|
||||
"in $IM_CONNECT_TIMEOUT seconds after start.");
|
||||
mtr_debug("Aborting test suite...");
|
||||
|
||||
mtr_kill_leftovers();
|
||||
|
||||
mtr_error("Can not connect to mysqld[$idx] " .
|
||||
"in $IM_CONNECT_TIMEOUT seconds after start.");
|
||||
}
|
||||
|
||||
mtr_debug("mysqld[$idx] started.");
|
||||
}
|
||||
|
||||
mtr_debug("Instance Manager started.");
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
|
||||
sub mtr_im_stop($) {
|
||||
my $instance_manager= shift;
|
||||
|
||||
mtr_debug("Stopping Instance Manager...");
|
||||
|
||||
# Try graceful shutdown.
|
||||
|
||||
mtr_im_terminate($instance_manager);
|
||||
|
||||
# Check that all processes died.
|
||||
|
||||
unless (mtr_im_check_alive($instance_manager))
|
||||
{
|
||||
mtr_debug("Instance Manager has been stopped successfully.");
|
||||
mtr_im_cleanup($instance_manager);
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Instance Manager don't want to die. We should kill it.
|
||||
|
||||
mtr_im_errlog("Instance Manager did not shutdown gracefully.");
|
||||
|
||||
mtr_im_kill($instance_manager);
|
||||
|
||||
# Check again that all IM-related processes have been killed.
|
||||
|
||||
my $im_is_alive= mtr_im_check_alive($instance_manager);
|
||||
|
||||
mtr_im_cleanup($instance_manager);
|
||||
|
||||
if ($im_is_alive)
|
||||
{
|
||||
mtr_error("Can not kill Instance Manager or its children.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mtr_debug("Instance Manager has been killed successfully.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
|
||||
1;
|
||||
|
|
|
@ -336,7 +336,7 @@ sub snapshot_setup ();
|
|||
sub executable_setup ();
|
||||
sub environment_setup ();
|
||||
sub kill_running_server ();
|
||||
sub kill_and_cleanup ();
|
||||
sub cleanup_stale_files ();
|
||||
sub check_ssl_support ();
|
||||
sub check_running_as_root();
|
||||
sub check_ndbcluster_support ();
|
||||
|
@ -356,8 +356,6 @@ sub mysqld_arguments ($$$$$$);
|
|||
sub stop_masters_slaves ();
|
||||
sub stop_masters ();
|
||||
sub stop_slaves ();
|
||||
sub im_start ($$);
|
||||
sub im_stop ($);
|
||||
sub run_mysqltest ($);
|
||||
sub usage ($);
|
||||
|
||||
|
@ -499,7 +497,7 @@ sub command_line_setup () {
|
|||
my $opt_master_myport= 9306;
|
||||
my $opt_slave_myport= 9308;
|
||||
$opt_ndbcluster_port= 9350;
|
||||
my $im_port= 9310;
|
||||
my $im_port= 9311;
|
||||
my $im_mysqld1_port= 9312;
|
||||
my $im_mysqld2_port= 9314;
|
||||
|
||||
|
@ -1303,6 +1301,7 @@ sub kill_running_server () {
|
|||
|
||||
mtr_report("Killing Possible Leftover Processes");
|
||||
mkpath("$opt_vardir/log"); # Needed for mysqladmin log
|
||||
|
||||
mtr_kill_leftovers();
|
||||
|
||||
$using_ndbcluster_master= $opt_with_ndbcluster;
|
||||
|
@ -1311,9 +1310,7 @@ sub kill_running_server () {
|
|||
}
|
||||
}
|
||||
|
||||
sub kill_and_cleanup () {
|
||||
|
||||
kill_running_server ();
|
||||
sub cleanup_stale_files () {
|
||||
|
||||
mtr_report("Removing Stale Files");
|
||||
|
||||
|
@ -1692,13 +1689,11 @@ sub run_suite () {
|
|||
sub initialize_servers () {
|
||||
if ( ! $glob_use_running_server )
|
||||
{
|
||||
if ( $opt_start_dirty )
|
||||
kill_running_server();
|
||||
|
||||
unless ( $opt_start_dirty )
|
||||
{
|
||||
kill_running_server();
|
||||
}
|
||||
else
|
||||
{
|
||||
kill_and_cleanup();
|
||||
cleanup_stale_files();
|
||||
mysql_install_db();
|
||||
if ( $opt_force )
|
||||
{
|
||||
|
@ -2100,7 +2095,7 @@ sub run_testcase ($) {
|
|||
|
||||
im_create_defaults_file($instance_manager);
|
||||
|
||||
im_start($instance_manager, $tinfo->{im_opts});
|
||||
mtr_im_start($instance_manager, $tinfo->{im_opts});
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
@ -2195,10 +2190,9 @@ sub run_testcase ($) {
|
|||
# Stop Instance Manager if we are processing an IM-test case.
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
if ( ! $glob_use_running_server and $tinfo->{'component_id'} eq 'im' and
|
||||
$instance_manager->{'pid'} )
|
||||
if ( ! $glob_use_running_server and $tinfo->{'component_id'} eq 'im' )
|
||||
{
|
||||
im_stop($instance_manager);
|
||||
mtr_im_stop($instance_manager);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2726,11 +2720,8 @@ sub stop_masters_slaves () {
|
|||
|
||||
print "Ending Tests\n";
|
||||
|
||||
if ( $instance_manager->{'pid'} )
|
||||
{
|
||||
print "Shutting-down Instance Manager\n";
|
||||
im_stop($instance_manager);
|
||||
}
|
||||
print "Shutting-down Instance Manager\n";
|
||||
mtr_im_stop($instance_manager);
|
||||
|
||||
print "Shutting-down MySQL daemon\n\n";
|
||||
stop_masters();
|
||||
|
@ -2792,230 +2783,6 @@ sub stop_slaves () {
|
|||
mtr_stop_mysqld_servers(\@args);
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Instance Manager management routines.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
sub im_start($$) {
|
||||
my $instance_manager = shift;
|
||||
my $opts = shift;
|
||||
|
||||
my $args;
|
||||
mtr_init_args(\$args);
|
||||
mtr_add_arg($args, "--defaults-file=%s",
|
||||
$instance_manager->{'defaults_file'});
|
||||
|
||||
foreach my $opt (@{$opts})
|
||||
{
|
||||
mtr_add_arg($args, $opt);
|
||||
}
|
||||
|
||||
$instance_manager->{'pid'} =
|
||||
mtr_spawn(
|
||||
$exe_im, # path to the executable
|
||||
$args, # cmd-line args
|
||||
'', # stdin
|
||||
$instance_manager->{'path_log'}, # stdout
|
||||
$instance_manager->{'path_err'}, # stderr
|
||||
'', # pid file path (not used)
|
||||
{ append_log_file => 1 } # append log files
|
||||
);
|
||||
|
||||
if ( ! $instance_manager->{'pid'} )
|
||||
{
|
||||
mtr_report('Could not start Instance Manager');
|
||||
return;
|
||||
}
|
||||
|
||||
# Instance Manager can be run in daemon mode. In this case, it creates
|
||||
# several processes and the parent process, created by mtr_spawn(), exits just
|
||||
# after start. So, we have to obtain Instance Manager PID from the PID file.
|
||||
|
||||
if ( ! sleep_until_file_created(
|
||||
$instance_manager->{'path_pid'},
|
||||
$instance_manager->{'start_timeout'},
|
||||
-1)) # real PID is still unknown
|
||||
{
|
||||
mtr_report("Instance Manager PID file is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
$instance_manager->{'pid'} =
|
||||
mtr_get_pid_from_file($instance_manager->{'path_pid'});
|
||||
}
|
||||
|
||||
|
||||
sub im_stop($) {
|
||||
my $instance_manager = shift;
|
||||
|
||||
# Obtain mysqld-process pids before we start stopping IM (it can delete pid
|
||||
# files).
|
||||
|
||||
my @mysqld_pids = ();
|
||||
my $instances = $instance_manager->{'instances'};
|
||||
|
||||
push(@mysqld_pids, mtr_get_pid_from_file($instances->[0]->{'path_pid'}))
|
||||
if -r $instances->[0]->{'path_pid'};
|
||||
|
||||
push(@mysqld_pids, mtr_get_pid_from_file($instances->[1]->{'path_pid'}))
|
||||
if -r $instances->[1]->{'path_pid'};
|
||||
|
||||
# Re-read pid from the file, since during tests Instance Manager could have
|
||||
# been restarted, so its pid could have been changed.
|
||||
|
||||
$instance_manager->{'pid'} =
|
||||
mtr_get_pid_from_file($instance_manager->{'path_pid'})
|
||||
if -f $instance_manager->{'path_pid'};
|
||||
|
||||
if (-f $instance_manager->{'path_angel_pid'})
|
||||
{
|
||||
$instance_manager->{'angel_pid'} =
|
||||
mtr_get_pid_from_file($instance_manager->{'path_angel_pid'})
|
||||
}
|
||||
else
|
||||
{
|
||||
$instance_manager->{'angel_pid'} = undef;
|
||||
}
|
||||
|
||||
# Inspired from mtr_stop_mysqld_servers().
|
||||
|
||||
start_reap_all();
|
||||
|
||||
# Try graceful shutdown.
|
||||
|
||||
mtr_debug("IM-main pid: $instance_manager->{'pid'}");
|
||||
mtr_debug("Stopping IM-main...");
|
||||
|
||||
mtr_kill_process($instance_manager->{'pid'}, 'TERM', 10, 1);
|
||||
|
||||
# If necessary, wait for angel process to die.
|
||||
|
||||
if (defined $instance_manager->{'angel_pid'})
|
||||
{
|
||||
mtr_debug("IM-angel pid: $instance_manager->{'angel_pid'}");
|
||||
mtr_debug("Waiting for IM-angel to die...");
|
||||
|
||||
my $total_attempts= 10;
|
||||
|
||||
for (my $cur_attempt=1; $cur_attempt <= $total_attempts; ++$cur_attempt)
|
||||
{
|
||||
unless (kill (0, $instance_manager->{'angel_pid'}))
|
||||
{
|
||||
mtr_debug("IM-angel died.");
|
||||
last;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
# Check that all processes died.
|
||||
|
||||
my $clean_shutdown= 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
# Check that IM-main died.
|
||||
|
||||
if (kill (0, $instance_manager->{'pid'}))
|
||||
{
|
||||
mtr_debug("IM-main is still alive.");
|
||||
last;
|
||||
}
|
||||
|
||||
# Check that IM-angel died.
|
||||
|
||||
if (defined $instance_manager->{'angel_pid'} &&
|
||||
kill (0, $instance_manager->{'angel_pid'}))
|
||||
{
|
||||
mtr_debug("IM-angel is still alive.");
|
||||
last;
|
||||
}
|
||||
|
||||
# Check that all guarded mysqld-instances died.
|
||||
|
||||
my $guarded_mysqlds_dead= 1;
|
||||
|
||||
foreach my $pid (@mysqld_pids)
|
||||
{
|
||||
if (kill (0, $pid))
|
||||
{
|
||||
mtr_debug("Guarded mysqld ($pid) is still alive.");
|
||||
$guarded_mysqlds_dead= 0;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
last unless $guarded_mysqlds_dead;
|
||||
|
||||
# Ok, all necessary processes are dead.
|
||||
|
||||
$clean_shutdown= 1;
|
||||
last;
|
||||
}
|
||||
|
||||
# Kill leftovers (the order is important).
|
||||
|
||||
if ($clean_shutdown)
|
||||
{
|
||||
mtr_debug("IM-shutdown was clean -- all processed died.");
|
||||
}
|
||||
else
|
||||
{
|
||||
mtr_debug("IM failed to shutdown gracefully. We have to clean the mess...");
|
||||
}
|
||||
|
||||
unless ($clean_shutdown)
|
||||
{
|
||||
|
||||
if (defined $instance_manager->{'angel_pid'})
|
||||
{
|
||||
mtr_debug("Killing IM-angel...");
|
||||
mtr_kill_process($instance_manager->{'angel_pid'}, 'KILL', 10, 1)
|
||||
}
|
||||
|
||||
mtr_debug("Killing IM-main...");
|
||||
mtr_kill_process($instance_manager->{'pid'}, 'KILL', 10, 1);
|
||||
|
||||
# Shutdown managed mysqld-processes. Some of them may be nonguarded, so IM
|
||||
# will not stop them on shutdown. So, we should firstly try to end them
|
||||
# legally.
|
||||
|
||||
mtr_debug("Killing guarded mysqld(s)...");
|
||||
mtr_kill_processes(\@mysqld_pids);
|
||||
|
||||
# Complain in error log so that a warning will be shown.
|
||||
#
|
||||
# TODO: unless BUG#20761 is fixed, we will print the warning
|
||||
# to stdout, so that it can be seen on console and does not
|
||||
# produce pushbuild error.
|
||||
|
||||
# my $errlog= "$opt_vardir/log/mysql-test-run.pl.err";
|
||||
#
|
||||
# open (ERRLOG, ">>$errlog") ||
|
||||
# mtr_error("Can not open error log ($errlog)");
|
||||
#
|
||||
# my $ts= localtime();
|
||||
# print ERRLOG
|
||||
# "Warning: [$ts] Instance Manager did not shutdown gracefully.\n";
|
||||
#
|
||||
# close ERRLOG;
|
||||
|
||||
my $ts= localtime();
|
||||
print "Warning: [$ts] Instance Manager did not shutdown gracefully.\n";
|
||||
}
|
||||
|
||||
# That's all.
|
||||
|
||||
stop_reap_all();
|
||||
|
||||
$instance_manager->{'pid'} = undef;
|
||||
$instance_manager->{'angel_pid'} = undef;
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Run include/check-testcase.test
|
||||
# Before a testcase, run in record mode, save result file to var
|
||||
|
|
|
@ -72,3 +72,16 @@ show tables;
|
|||
Tables_in_test
|
||||
t1
|
||||
drop table t1;
|
||||
drop database if exists mysqltest;
|
||||
drop table if exists t1;
|
||||
create table t1 (i int);
|
||||
lock tables t1 read;
|
||||
create database mysqltest;
|
||||
drop table t1;
|
||||
show open tables;
|
||||
drop database mysqltest;
|
||||
select 1;
|
||||
1
|
||||
1
|
||||
unlock tables;
|
||||
End of 5.0 tests
|
||||
|
|
|
@ -943,6 +943,31 @@ DROP TABLE mysqltest3.t_nn;
|
|||
DROP DATABASE mysqltest3;
|
||||
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'mysqltest_1'@'localhost';
|
||||
DROP USER 'mysqltest_1'@'localhost';
|
||||
use test;
|
||||
create user mysqltest1_thisisreallytoolong;
|
||||
ERROR HY000: Operation CREATE USER failed for 'mysqltest1_thisisreallytoolong'@'%'
|
||||
ERROR HY000: String 'mysqltest1_thisisreallytoolong' is too long for user name (should be no longer than 16)
|
||||
GRANT CREATE ON mysqltest.* TO 1234567890abcdefGHIKL@localhost;
|
||||
ERROR HY000: String '1234567890abcdefGHIKL' is too long for user name (should be no longer than 16)
|
||||
GRANT CREATE ON mysqltest.* TO some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
|
||||
ERROR HY000: String '1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY' is too long for host name (should be no longer than 60)
|
||||
REVOKE CREATE ON mysqltest.* FROM 1234567890abcdefGHIKL@localhost;
|
||||
ERROR HY000: String '1234567890abcdefGHIKL' is too long for user name (should be no longer than 16)
|
||||
REVOKE CREATE ON mysqltest.* FROM some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
|
||||
ERROR HY000: String '1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY' is too long for host name (should be no longer than 60)
|
||||
GRANT CREATE ON t1 TO 1234567890abcdefGHIKL@localhost;
|
||||
ERROR HY000: String '1234567890abcdefGHIKL' is too long for user name (should be no longer than 16)
|
||||
GRANT CREATE ON t1 TO some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
|
||||
ERROR HY000: String '1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY' is too long for host name (should be no longer than 60)
|
||||
REVOKE CREATE ON t1 FROM 1234567890abcdefGHIKL@localhost;
|
||||
ERROR HY000: String '1234567890abcdefGHIKL' is too long for user name (should be no longer than 16)
|
||||
REVOKE CREATE ON t1 FROM some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
|
||||
ERROR HY000: String '1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY' is too long for host name (should be no longer than 60)
|
||||
GRANT EXECUTE ON PROCEDURE p1 TO 1234567890abcdefGHIKL@localhost;
|
||||
ERROR HY000: String '1234567890abcdefGHIKL' is too long for user name (should be no longer than 16)
|
||||
GRANT EXECUTE ON PROCEDURE p1 TO some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
|
||||
ERROR HY000: String '1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY' is too long for host name (should be no longer than 60)
|
||||
REVOKE EXECUTE ON PROCEDURE p1 FROM 1234567890abcdefGHIKL@localhost;
|
||||
ERROR HY000: String '1234567890abcdefGHIKL' is too long for user name (should be no longer than 16)
|
||||
REVOKE EXECUTE ON PROCEDURE t1 FROM some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
|
||||
ERROR HY000: String '1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY' is too long for host name (should be no longer than 60)
|
||||
End of 5.0 tests
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
Success: the process has been started.
|
||||
SHOW VARIABLES LIKE 'server_id';
|
||||
Variable_name Value
|
||||
server_id 1
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
Success: the process has been started.
|
||||
SHOW VARIABLES LIKE 'server_id';
|
||||
Variable_name Value
|
||||
server_id 1
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
Success: the process has been started.
|
||||
SHOW VARIABLES LIKE 'server_id';
|
||||
Variable_name Value
|
||||
server_id 1
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
Success: the process has been started.
|
||||
SHOW VARIABLES LIKE 'server_id';
|
||||
Variable_name Value
|
||||
server_id 1
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
Success: the process has been started.
|
||||
SHOW VARIABLES LIKE 'server_id';
|
||||
Variable_name Value
|
||||
server_id 1
|
||||
|
|
|
@ -1297,3 +1297,18 @@ ERROR 3D000: No database selected
|
|||
create temporary table t1 (i int);
|
||||
ERROR 3D000: No database selected
|
||||
use test;
|
||||
DROP TABLE IF EXISTS t1, t2, t3;
|
||||
CREATE TABLE t1 (i BIGINT, j BIGINT);
|
||||
CREATE TABLE t2 (i BIGINT);
|
||||
CREATE TABLE t3 (i BIGINT, j BIGINT);
|
||||
PREPARE stmt FROM "SELECT * FROM t1 JOIN t2 ON (t2.i = t1.i)
|
||||
LEFT JOIN t3 ON ((t3.i, t3.j) = (t1.i, t1.j))
|
||||
WHERE t1.i = ?";
|
||||
SET @a= 1;
|
||||
EXECUTE stmt USING @a;
|
||||
i j i i j
|
||||
EXECUTE stmt USING @a;
|
||||
i j i i j
|
||||
DEALLOCATE PREPARE stmt;
|
||||
DROP TABLE IF EXISTS t1, t2, t3;
|
||||
End of 5.0 tests.
|
||||
|
|
|
@ -634,6 +634,45 @@ flush tables;
|
|||
return 5;
|
||||
end|
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create function bug8409() returns int begin reset query cache;
|
||||
return 1; end|
|
||||
ERROR 0A000: RESET is not allowed in stored function or trigger
|
||||
create function bug8409() returns int begin reset master;
|
||||
return 1; end|
|
||||
ERROR 0A000: RESET is not allowed in stored function or trigger
|
||||
create function bug8409() returns int begin reset slave;
|
||||
return 1; end|
|
||||
ERROR 0A000: RESET is not allowed in stored function or trigger
|
||||
create function bug8409() returns int begin flush hosts;
|
||||
return 1; end|
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create function bug8409() returns int begin flush privileges;
|
||||
return 1; end|
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create function bug8409() returns int begin flush tables with read lock;
|
||||
return 1; end|
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create function bug8409() returns int begin flush tables;
|
||||
return 1; end|
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create function bug8409() returns int begin flush logs;
|
||||
return 1; end|
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create function bug8409() returns int begin flush status;
|
||||
return 1; end|
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create function bug8409() returns int begin flush slave;
|
||||
return 1; end|
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create function bug8409() returns int begin flush master;
|
||||
return 1; end|
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create function bug8409() returns int begin flush des_key_file;
|
||||
return 1; end|
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create function bug8409() returns int begin flush user_resources;
|
||||
return 1; end|
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create procedure bug9529_901234567890123456789012345678901234567890123456789012345()
|
||||
begin
|
||||
end|
|
||||
|
|
|
@ -4872,8 +4872,6 @@ declare continue handler for sqlexception begin end;
|
|||
select no_such_function();
|
||||
end|
|
||||
call bug18787()|
|
||||
no_such_function()
|
||||
NULL
|
||||
drop procedure bug18787|
|
||||
create database bug18344_012345678901|
|
||||
use bug18344_012345678901|
|
||||
|
@ -5222,6 +5220,126 @@ CHARSET(p3) COLLATION(p3)
|
|||
greek greek_general_ci
|
||||
use test|
|
||||
DROP DATABASE mysqltest1|
|
||||
drop table if exists t3|
|
||||
drop table if exists t4|
|
||||
drop procedure if exists bug8153_subselect|
|
||||
drop procedure if exists bug8153_subselect_a|
|
||||
drop procedure if exists bug8153_subselect_b|
|
||||
drop procedure if exists bug8153_proc_a|
|
||||
drop procedure if exists bug8153_proc_b|
|
||||
create table t3 (a int)|
|
||||
create table t4 (a int)|
|
||||
insert into t3 values (1), (1), (2), (3)|
|
||||
insert into t4 values (1), (1)|
|
||||
create procedure bug8153_subselect()
|
||||
begin
|
||||
declare continue handler for sqlexception
|
||||
begin
|
||||
select 'statement failed';
|
||||
end;
|
||||
update t3 set a=a+1 where (select a from t4 where a=1) is null;
|
||||
select 'statement after update';
|
||||
end|
|
||||
call bug8153_subselect()|
|
||||
statement failed
|
||||
statement failed
|
||||
statement after update
|
||||
statement after update
|
||||
select * from t3|
|
||||
a
|
||||
1
|
||||
1
|
||||
2
|
||||
3
|
||||
call bug8153_subselect()|
|
||||
statement failed
|
||||
statement failed
|
||||
statement after update
|
||||
statement after update
|
||||
select * from t3|
|
||||
a
|
||||
1
|
||||
1
|
||||
2
|
||||
3
|
||||
drop procedure bug8153_subselect|
|
||||
create procedure bug8153_subselect_a()
|
||||
begin
|
||||
declare continue handler for sqlexception
|
||||
begin
|
||||
select 'in continue handler';
|
||||
end;
|
||||
select 'reachable code a1';
|
||||
call bug8153_subselect_b();
|
||||
select 'reachable code a2';
|
||||
end|
|
||||
create procedure bug8153_subselect_b()
|
||||
begin
|
||||
select 'reachable code b1';
|
||||
update t3 set a=a+1 where (select a from t4 where a=1) is null;
|
||||
select 'unreachable code b2';
|
||||
end|
|
||||
call bug8153_subselect_a()|
|
||||
reachable code a1
|
||||
reachable code a1
|
||||
reachable code b1
|
||||
reachable code b1
|
||||
in continue handler
|
||||
in continue handler
|
||||
reachable code a2
|
||||
reachable code a2
|
||||
select * from t3|
|
||||
a
|
||||
1
|
||||
1
|
||||
2
|
||||
3
|
||||
call bug8153_subselect_a()|
|
||||
reachable code a1
|
||||
reachable code a1
|
||||
reachable code b1
|
||||
reachable code b1
|
||||
in continue handler
|
||||
in continue handler
|
||||
reachable code a2
|
||||
reachable code a2
|
||||
select * from t3|
|
||||
a
|
||||
1
|
||||
1
|
||||
2
|
||||
3
|
||||
drop procedure bug8153_subselect_a|
|
||||
drop procedure bug8153_subselect_b|
|
||||
create procedure bug8153_proc_a()
|
||||
begin
|
||||
declare continue handler for sqlexception
|
||||
begin
|
||||
select 'in continue handler';
|
||||
end;
|
||||
select 'reachable code a1';
|
||||
call bug8153_proc_b();
|
||||
select 'reachable code a2';
|
||||
end|
|
||||
create procedure bug8153_proc_b()
|
||||
begin
|
||||
select 'reachable code b1';
|
||||
select no_such_function();
|
||||
select 'unreachable code b2';
|
||||
end|
|
||||
call bug8153_proc_a()|
|
||||
reachable code a1
|
||||
reachable code a1
|
||||
reachable code b1
|
||||
reachable code b1
|
||||
in continue handler
|
||||
in continue handler
|
||||
reachable code a2
|
||||
reachable code a2
|
||||
drop procedure bug8153_proc_a|
|
||||
drop procedure bug8153_proc_b|
|
||||
drop table t3|
|
||||
drop table t4|
|
||||
drop procedure if exists bug19862|
|
||||
CREATE TABLE t11 (a INT)|
|
||||
CREATE TABLE t12 (a INT)|
|
||||
|
@ -5256,4 +5374,24 @@ a
|
|||
1
|
||||
use test|
|
||||
drop table t3|
|
||||
DROP PROCEDURE IF EXISTS bug16899_p1|
|
||||
DROP FUNCTION IF EXISTS bug16899_f1|
|
||||
CREATE DEFINER=1234567890abcdefGHIKL@localhost PROCEDURE bug16899_p1()
|
||||
BEGIN
|
||||
SET @a = 1;
|
||||
END|
|
||||
ERROR HY000: String '1234567890abcdefGHIKL' is too long for user name (should be no longer than 16)
|
||||
CREATE DEFINER=some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY
|
||||
FUNCTION bug16899_f1() RETURNS INT
|
||||
BEGIN
|
||||
RETURN 1;
|
||||
END|
|
||||
ERROR HY000: String '1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY' is too long for host name (should be no longer than 60)
|
||||
drop procedure if exists bug21416|
|
||||
create procedure bug21416() show create procedure bug21416|
|
||||
call bug21416()|
|
||||
Procedure sql_mode Create Procedure
|
||||
bug21416 CREATE DEFINER=`root`@`localhost` PROCEDURE `bug21416`()
|
||||
show create procedure bug21416
|
||||
drop procedure bug21416|
|
||||
drop table t1,t2;
|
||||
|
|
|
@ -626,12 +626,51 @@ Trigger Event Table Statement Timing Created sql_mode Definer
|
|||
t1_bi INSERT t1 set new.a = '2004-01-00' BEFORE # root@localhost
|
||||
drop table t1;
|
||||
create table t1 (id int);
|
||||
create trigger t1_ai after insert on t1 for each row reset query cache;
|
||||
ERROR 0A000: RESET is not allowed in stored function or trigger
|
||||
create trigger t1_ai after insert on t1 for each row reset master;
|
||||
ERROR 0A000: RESET is not allowed in stored function or trigger
|
||||
create trigger t1_ai after insert on t1 for each row reset slave;
|
||||
ERROR 0A000: RESET is not allowed in stored function or trigger
|
||||
create trigger t1_ai after insert on t1 for each row flush hosts;
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create trigger t1_ai after insert on t1 for each row flush tables with read lock;
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create trigger t1_ai after insert on t1 for each row flush logs;
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create trigger t1_ai after insert on t1 for each row flush status;
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create trigger t1_ai after insert on t1 for each row flush slave;
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create trigger t1_ai after insert on t1 for each row flush master;
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create trigger t1_ai after insert on t1 for each row flush des_key_file;
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create trigger t1_ai after insert on t1 for each row flush user_resources;
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create trigger t1_ai after insert on t1 for each row flush tables;
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create trigger t1_ai after insert on t1 for each row flush privileges;
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
create procedure p1() flush tables;
|
||||
drop procedure if exists p1;
|
||||
create trigger t1_ai after insert on t1 for each row call p1();
|
||||
create procedure p1() flush tables;
|
||||
insert into t1 values (0);
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
drop procedure p1;
|
||||
create procedure p1() reset query cache;
|
||||
insert into t1 values (0);
|
||||
ERROR 0A000: RESET is not allowed in stored function or trigger
|
||||
drop procedure p1;
|
||||
create procedure p1() reset master;
|
||||
insert into t1 values (0);
|
||||
ERROR 0A000: RESET is not allowed in stored function or trigger
|
||||
drop procedure p1;
|
||||
create procedure p1() reset slave;
|
||||
insert into t1 values (0);
|
||||
ERROR 0A000: RESET is not allowed in stored function or trigger
|
||||
drop procedure p1;
|
||||
create procedure p1() flush hosts;
|
||||
insert into t1 values (0);
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
drop procedure p1;
|
||||
|
@ -639,6 +678,38 @@ create procedure p1() flush privileges;
|
|||
insert into t1 values (0);
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
drop procedure p1;
|
||||
create procedure p1() flush tables with read lock;
|
||||
insert into t1 values (0);
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
drop procedure p1;
|
||||
create procedure p1() flush tables;
|
||||
insert into t1 values (0);
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
drop procedure p1;
|
||||
create procedure p1() flush logs;
|
||||
insert into t1 values (0);
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
drop procedure p1;
|
||||
create procedure p1() flush status;
|
||||
insert into t1 values (0);
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
drop procedure p1;
|
||||
create procedure p1() flush slave;
|
||||
insert into t1 values (0);
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
drop procedure p1;
|
||||
create procedure p1() flush master;
|
||||
insert into t1 values (0);
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
drop procedure p1;
|
||||
create procedure p1() flush des_key_file;
|
||||
insert into t1 values (0);
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
drop procedure p1;
|
||||
create procedure p1() flush user_resources;
|
||||
insert into t1 values (0);
|
||||
ERROR 0A000: FLUSH is not allowed in stored function or trigger
|
||||
drop procedure p1;
|
||||
drop table t1;
|
||||
create table t1 (id int, data int, username varchar(16));
|
||||
insert into t1 (id, data) values (1, 0);
|
||||
|
@ -1089,4 +1160,17 @@ begin
|
|||
set @a:= 1;
|
||||
end|
|
||||
ERROR HY000: Triggers can not be created on system tables
|
||||
use test|
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t1(c INT);
|
||||
CREATE TABLE t2(c INT);
|
||||
CREATE DEFINER=1234567890abcdefGHIKL@localhost
|
||||
TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW SET @a = 1;
|
||||
ERROR HY000: String '1234567890abcdefGHIKL' is too long for user name (should be no longer than 16)
|
||||
CREATE DEFINER=some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY
|
||||
TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW SET @a = 2;
|
||||
ERROR HY000: String '1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY' is too long for host name (should be no longer than 60)
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t2;
|
||||
End of 5.0 tests
|
||||
|
|
|
@ -422,3 +422,34 @@ DROP TABLE IF EXISTS t1;
|
|||
CREATE TABLE t1(f1 CHAR(100) DEFAULT 'test');
|
||||
INSERT INTO t1 VALUES(SUBSTR(f1, 1, 3));
|
||||
DROP TABLE IF EXISTS t1;
|
||||
drop table if exists t1, t2, t3;
|
||||
create table t3 (
|
||||
id int(11),
|
||||
en varchar(255) character set utf8,
|
||||
cz varchar(255) character set utf8
|
||||
);
|
||||
truncate table t3;
|
||||
insert into t3 (id, en, cz) values
|
||||
(1,'en string 1','cz string 1'),
|
||||
(2,'en string 2','cz string 2'),
|
||||
(3,'en string 3','cz string 3');
|
||||
create table t1 (
|
||||
id int(11),
|
||||
name_id int(11)
|
||||
);
|
||||
insert into t1 (id, name_id) values (1,1), (2,3), (3,3);
|
||||
create table t2 (id int(11));
|
||||
insert into t2 (id) values (1), (2), (3);
|
||||
select t1.*, t2.id, t3.en, t3.cz from t1 left join t2 on t1.id=t2.id
|
||||
left join t3 on t1.id=t3.id order by t3.id;
|
||||
Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr
|
||||
def test t1 t1 id id 3 11 1 Y 32768 0 63
|
||||
def test t1 t1 name_id name_id 3 11 1 Y 32768 0 63
|
||||
def test t2 t2 id id 3 11 1 Y 32768 0 63
|
||||
def test t3 t3 en en 253 255 11 Y 0 0 8
|
||||
def test t3 t3 cz cz 253 255 11 Y 0 0 8
|
||||
id name_id id en cz
|
||||
1 1 1 en string 1 cz string 1
|
||||
2 3 2 en string 2 cz string 2
|
||||
3 3 3 en string 3 cz string 3
|
||||
drop table t1, t2, t3;
|
||||
|
|
|
@ -2879,3 +2879,41 @@ View Create View
|
|||
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`f1` AS `f1` from `t1` where (`t1`.`f1` between now() and (now() + interval 1 minute))
|
||||
drop view v1;
|
||||
drop table t1;
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP VIEW IF EXISTS v1;
|
||||
DROP VIEW IF EXISTS v2;
|
||||
CREATE TABLE t1(a INT, b INT);
|
||||
CREATE DEFINER=1234567890abcdefGHIKL@localhost
|
||||
VIEW v1 AS SELECT a FROM t1;
|
||||
ERROR HY000: String '1234567890abcdefGHIKL' is too long for user name (should be no longer than 16)
|
||||
CREATE DEFINER=some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY
|
||||
VIEW v2 AS SELECT b FROM t1;
|
||||
ERROR HY000: String '1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY' is too long for host name (should be no longer than 60)
|
||||
DROP TABLE t1;
|
||||
DROP FUNCTION IF EXISTS f1;
|
||||
DROP FUNCTION IF EXISTS f2;
|
||||
DROP VIEW IF EXISTS v1, v2;
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1 (i INT);
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
CREATE FUNCTION f1() RETURNS INT
|
||||
BEGIN
|
||||
INSERT INTO v1 VALUES (0);
|
||||
RETURN 0;
|
||||
END |
|
||||
SELECT f1();
|
||||
f1()
|
||||
0
|
||||
CREATE ALGORITHM=TEMPTABLE VIEW v2 AS SELECT * FROM t1;
|
||||
CREATE FUNCTION f2() RETURNS INT
|
||||
BEGIN
|
||||
INSERT INTO v2 VALUES (0);
|
||||
RETURN 0;
|
||||
END |
|
||||
SELECT f2();
|
||||
ERROR HY000: The target table v2 of the INSERT is not updatable
|
||||
DROP FUNCTION f1;
|
||||
DROP FUNCTION f2;
|
||||
DROP VIEW v1, v2;
|
||||
DROP TABLE t1;
|
||||
End of 5.0 tests.
|
||||
|
|
BIN
mysql-test/std_data/14897.frm
Normal file
BIN
mysql-test/std_data/14897.frm
Normal file
Binary file not shown.
|
@ -81,3 +81,44 @@ show tables;
|
|||
drop table t1;
|
||||
|
||||
# End of 4.1 tests
|
||||
|
||||
|
||||
#
|
||||
# Test for bug#21216 "Simultaneous DROP TABLE and SHOW OPEN TABLES causes
|
||||
# server to crash". Crash (caused by failed assertion in 5.0 or by null
|
||||
# pointer dereference in 5.1) happened when one ran SHOW OPEN TABLES
|
||||
# while concurrently doing DROP TABLE (or RENAME TABLE, CREATE TABLE LIKE
|
||||
# or any other command that takes name-lock) in other connection.
|
||||
#
|
||||
# Also includes test for similar bug#12212 "Crash that happens during
|
||||
# removing of database name from cache" reappeared in 5.1 as bug#19403
|
||||
# In its case crash happened when one concurrently executed DROP DATABASE
|
||||
# and one of name-locking command.
|
||||
#
|
||||
--disable_warnings
|
||||
drop database if exists mysqltest;
|
||||
drop table if exists t1;
|
||||
--enable_warnings
|
||||
create table t1 (i int);
|
||||
lock tables t1 read;
|
||||
create database mysqltest;
|
||||
connect (addconroot1, localhost, root,,);
|
||||
--send drop table t1
|
||||
connect (addconroot2, localhost, root,,);
|
||||
# Server should not crash in any of the following statements
|
||||
--disable_result_log
|
||||
show open tables;
|
||||
--enable_result_log
|
||||
--send drop database mysqltest
|
||||
connection default;
|
||||
select 1;
|
||||
unlock tables;
|
||||
connection addconroot1;
|
||||
--reap
|
||||
connection addconroot2;
|
||||
--reap
|
||||
disconnect addconroot1;
|
||||
disconnect addconroot2;
|
||||
connection default;
|
||||
|
||||
--echo End of 5.0 tests
|
||||
|
|
|
@ -682,8 +682,6 @@ drop table t2;
|
|||
|
||||
drop table t1;
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Bug#20214: Incorrect error when user calls SHOW CREATE VIEW on non
|
||||
# privileged view
|
||||
|
@ -807,12 +805,62 @@ DROP DATABASE mysqltest3;
|
|||
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'mysqltest_1'@'localhost';
|
||||
DROP USER 'mysqltest_1'@'localhost';
|
||||
|
||||
|
||||
# restore the original database
|
||||
use test;
|
||||
|
||||
#
|
||||
# Bug #10668: CREATE USER does not enforce username length limit
|
||||
#
|
||||
--error ER_CANNOT_USER
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
create user mysqltest1_thisisreallytoolong;
|
||||
|
||||
#
|
||||
# Test for BUG#16899: Possible buffer overflow in handling of DEFINER-clause.
|
||||
#
|
||||
# These checks are intended to ensure that appropriate errors are risen when
|
||||
# illegal user name or hostname is specified in user-clause of GRANT/REVOKE
|
||||
# statements.
|
||||
#
|
||||
|
||||
# Working with database-level privileges.
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
GRANT CREATE ON mysqltest.* TO 1234567890abcdefGHIKL@localhost;
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
GRANT CREATE ON mysqltest.* TO some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
REVOKE CREATE ON mysqltest.* FROM 1234567890abcdefGHIKL@localhost;
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
REVOKE CREATE ON mysqltest.* FROM some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
|
||||
|
||||
# Working with table-level privileges.
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
GRANT CREATE ON t1 TO 1234567890abcdefGHIKL@localhost;
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
GRANT CREATE ON t1 TO some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
REVOKE CREATE ON t1 FROM 1234567890abcdefGHIKL@localhost;
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
REVOKE CREATE ON t1 FROM some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
|
||||
|
||||
# Working with routine-level privileges.
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
GRANT EXECUTE ON PROCEDURE p1 TO 1234567890abcdefGHIKL@localhost;
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
GRANT EXECUTE ON PROCEDURE p1 TO some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
REVOKE EXECUTE ON PROCEDURE p1 FROM 1234567890abcdefGHIKL@localhost;
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
REVOKE EXECUTE ON PROCEDURE t1 FROM some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY;
|
||||
--echo End of 5.0 tests
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#
|
||||
###########################################################################
|
||||
|
||||
--source include/im_check_os.inc
|
||||
--source include/im_check_env.inc
|
||||
|
||||
###########################################################################
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#
|
||||
###########################################################################
|
||||
|
||||
--source include/im_check_os.inc
|
||||
--source include/im_check_env.inc
|
||||
|
||||
###########################################################################
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
|
||||
###########################################################################
|
||||
|
||||
--source include/im_check_os.inc
|
||||
--source include/im_check_env.inc
|
||||
|
||||
###########################################################################
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
|
||||
###########################################################################
|
||||
|
||||
--source include/im_check_os.inc
|
||||
--source include/im_check_env.inc
|
||||
|
||||
###########################################################################
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#
|
||||
###########################################################################
|
||||
|
||||
--source include/im_check_os.inc
|
||||
--source include/im_check_env.inc
|
||||
|
||||
###########################################################################
|
||||
|
|
|
@ -1329,4 +1329,33 @@ create temporary table t1 (i int);
|
|||
# Restore the old environemnt
|
||||
#
|
||||
use test;
|
||||
# End of 5.0 tests
|
||||
|
||||
|
||||
#
|
||||
# BUG#21166: Prepared statement causes signal 11 on second execution
|
||||
#
|
||||
# Changes in an item tree done by optimizer weren't properly
|
||||
# registered and went unnoticed, which resulted in preliminary freeing
|
||||
# of used memory.
|
||||
#
|
||||
--disable_warnings
|
||||
DROP TABLE IF EXISTS t1, t2, t3;
|
||||
--enable_warnings
|
||||
|
||||
CREATE TABLE t1 (i BIGINT, j BIGINT);
|
||||
CREATE TABLE t2 (i BIGINT);
|
||||
CREATE TABLE t3 (i BIGINT, j BIGINT);
|
||||
|
||||
PREPARE stmt FROM "SELECT * FROM t1 JOIN t2 ON (t2.i = t1.i)
|
||||
LEFT JOIN t3 ON ((t3.i, t3.j) = (t1.i, t1.j))
|
||||
WHERE t1.i = ?";
|
||||
|
||||
SET @a= 1;
|
||||
EXECUTE stmt USING @a;
|
||||
EXECUTE stmt USING @a;
|
||||
|
||||
DEALLOCATE PREPARE stmt;
|
||||
DROP TABLE IF EXISTS t1, t2, t3;
|
||||
|
||||
|
||||
--echo End of 5.0 tests.
|
||||
|
|
|
@ -899,6 +899,45 @@ begin
|
|||
flush tables;
|
||||
return 5;
|
||||
end|
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create function bug8409() returns int begin reset query cache;
|
||||
return 1; end|
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create function bug8409() returns int begin reset master;
|
||||
return 1; end|
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create function bug8409() returns int begin reset slave;
|
||||
return 1; end|
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create function bug8409() returns int begin flush hosts;
|
||||
return 1; end|
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create function bug8409() returns int begin flush privileges;
|
||||
return 1; end|
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create function bug8409() returns int begin flush tables with read lock;
|
||||
return 1; end|
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create function bug8409() returns int begin flush tables;
|
||||
return 1; end|
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create function bug8409() returns int begin flush logs;
|
||||
return 1; end|
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create function bug8409() returns int begin flush status;
|
||||
return 1; end|
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create function bug8409() returns int begin flush slave;
|
||||
return 1; end|
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create function bug8409() returns int begin flush master;
|
||||
return 1; end|
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create function bug8409() returns int begin flush des_key_file;
|
||||
return 1; end|
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create function bug8409() returns int begin flush user_resources;
|
||||
return 1; end|
|
||||
|
||||
|
||||
#
|
||||
|
|
|
@ -6146,6 +6146,104 @@ CALL bug16676_p2('a', @v2, @v3)|
|
|||
use test|
|
||||
|
||||
DROP DATABASE mysqltest1|
|
||||
#
|
||||
# BUG#8153: Stored procedure with subquery and continue handler, wrong result
|
||||
#
|
||||
|
||||
--disable_warnings
|
||||
drop table if exists t3|
|
||||
drop table if exists t4|
|
||||
drop procedure if exists bug8153_subselect|
|
||||
drop procedure if exists bug8153_subselect_a|
|
||||
drop procedure if exists bug8153_subselect_b|
|
||||
drop procedure if exists bug8153_proc_a|
|
||||
drop procedure if exists bug8153_proc_b|
|
||||
--enable_warnings
|
||||
|
||||
create table t3 (a int)|
|
||||
create table t4 (a int)|
|
||||
insert into t3 values (1), (1), (2), (3)|
|
||||
insert into t4 values (1), (1)|
|
||||
|
||||
## Testing the use case reported in Bug#8153
|
||||
|
||||
create procedure bug8153_subselect()
|
||||
begin
|
||||
declare continue handler for sqlexception
|
||||
begin
|
||||
select 'statement failed';
|
||||
end;
|
||||
update t3 set a=a+1 where (select a from t4 where a=1) is null;
|
||||
select 'statement after update';
|
||||
end|
|
||||
|
||||
call bug8153_subselect()|
|
||||
select * from t3|
|
||||
|
||||
call bug8153_subselect()|
|
||||
select * from t3|
|
||||
|
||||
drop procedure bug8153_subselect|
|
||||
|
||||
## Testing a subselect with a non local handler
|
||||
|
||||
create procedure bug8153_subselect_a()
|
||||
begin
|
||||
declare continue handler for sqlexception
|
||||
begin
|
||||
select 'in continue handler';
|
||||
end;
|
||||
|
||||
select 'reachable code a1';
|
||||
call bug8153_subselect_b();
|
||||
select 'reachable code a2';
|
||||
end|
|
||||
|
||||
create procedure bug8153_subselect_b()
|
||||
begin
|
||||
select 'reachable code b1';
|
||||
update t3 set a=a+1 where (select a from t4 where a=1) is null;
|
||||
select 'unreachable code b2';
|
||||
end|
|
||||
|
||||
call bug8153_subselect_a()|
|
||||
select * from t3|
|
||||
|
||||
call bug8153_subselect_a()|
|
||||
select * from t3|
|
||||
|
||||
drop procedure bug8153_subselect_a|
|
||||
drop procedure bug8153_subselect_b|
|
||||
|
||||
## Testing extra use cases, found while investigating
|
||||
## This is related to BUG#18787, with a non local handler
|
||||
|
||||
create procedure bug8153_proc_a()
|
||||
begin
|
||||
declare continue handler for sqlexception
|
||||
begin
|
||||
select 'in continue handler';
|
||||
end;
|
||||
|
||||
select 'reachable code a1';
|
||||
call bug8153_proc_b();
|
||||
select 'reachable code a2';
|
||||
end|
|
||||
|
||||
create procedure bug8153_proc_b()
|
||||
begin
|
||||
select 'reachable code b1';
|
||||
select no_such_function();
|
||||
select 'unreachable code b2';
|
||||
end|
|
||||
|
||||
call bug8153_proc_a()|
|
||||
|
||||
drop procedure bug8153_proc_a|
|
||||
drop procedure bug8153_proc_b|
|
||||
drop table t3|
|
||||
drop table t4|
|
||||
|
||||
#
|
||||
# BUG#19862: Sort with filesort by function evaluates function twice
|
||||
#
|
||||
|
@ -6188,6 +6286,42 @@ select * from (select 1 as a) as t1 natural join (select * from test.t3) as t2|
|
|||
use test|
|
||||
drop table t3|
|
||||
|
||||
|
||||
#
|
||||
# Test for BUG#16899: Possible buffer overflow in handling of DEFINER-clause.
|
||||
#
|
||||
|
||||
# Prepare.
|
||||
|
||||
--disable_warnings
|
||||
DROP PROCEDURE IF EXISTS bug16899_p1|
|
||||
DROP FUNCTION IF EXISTS bug16899_f1|
|
||||
--enable_warnings
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
CREATE DEFINER=1234567890abcdefGHIKL@localhost PROCEDURE bug16899_p1()
|
||||
BEGIN
|
||||
SET @a = 1;
|
||||
END|
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
CREATE DEFINER=some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY
|
||||
FUNCTION bug16899_f1() RETURNS INT
|
||||
BEGIN
|
||||
RETURN 1;
|
||||
END|
|
||||
|
||||
|
||||
#
|
||||
# BUG#21416: SP: Recursion level higher than zero needed for non-recursive call
|
||||
#
|
||||
--disable_warnings
|
||||
drop procedure if exists bug21416|
|
||||
--enable_warnings
|
||||
create procedure bug21416() show create procedure bug21416|
|
||||
call bug21416()|
|
||||
drop procedure bug21416|
|
||||
|
||||
#
|
||||
# BUG#NNNN: New bug synopsis
|
||||
#
|
||||
|
|
|
@ -651,17 +651,105 @@ drop table t1;
|
|||
# of functions and triggers.
|
||||
create table t1 (id int);
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create trigger t1_ai after insert on t1 for each row reset query cache;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create trigger t1_ai after insert on t1 for each row reset master;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create trigger t1_ai after insert on t1 for each row reset slave;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create trigger t1_ai after insert on t1 for each row flush hosts;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create trigger t1_ai after insert on t1 for each row flush tables with read lock;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create trigger t1_ai after insert on t1 for each row flush logs;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create trigger t1_ai after insert on t1 for each row flush status;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create trigger t1_ai after insert on t1 for each row flush slave;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create trigger t1_ai after insert on t1 for each row flush master;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create trigger t1_ai after insert on t1 for each row flush des_key_file;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create trigger t1_ai after insert on t1 for each row flush user_resources;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create trigger t1_ai after insert on t1 for each row flush tables;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create trigger t1_ai after insert on t1 for each row flush privileges;
|
||||
create procedure p1() flush tables;
|
||||
--disable_warnings
|
||||
drop procedure if exists p1;
|
||||
--enable_warnings
|
||||
|
||||
create trigger t1_ai after insert on t1 for each row call p1();
|
||||
create procedure p1() flush tables;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
insert into t1 values (0);
|
||||
|
||||
drop procedure p1;
|
||||
create procedure p1() reset query cache;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
insert into t1 values (0);
|
||||
|
||||
drop procedure p1;
|
||||
create procedure p1() reset master;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
insert into t1 values (0);
|
||||
|
||||
drop procedure p1;
|
||||
create procedure p1() reset slave;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
insert into t1 values (0);
|
||||
|
||||
drop procedure p1;
|
||||
create procedure p1() flush hosts;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
insert into t1 values (0);
|
||||
|
||||
drop procedure p1;
|
||||
create procedure p1() flush privileges;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
insert into t1 values (0);
|
||||
|
||||
drop procedure p1;
|
||||
create procedure p1() flush tables with read lock;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
insert into t1 values (0);
|
||||
|
||||
drop procedure p1;
|
||||
create procedure p1() flush tables;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
insert into t1 values (0);
|
||||
|
||||
drop procedure p1;
|
||||
create procedure p1() flush logs;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
insert into t1 values (0);
|
||||
|
||||
drop procedure p1;
|
||||
create procedure p1() flush status;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
insert into t1 values (0);
|
||||
|
||||
drop procedure p1;
|
||||
create procedure p1() flush slave;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
insert into t1 values (0);
|
||||
|
||||
drop procedure p1;
|
||||
create procedure p1() flush master;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
insert into t1 values (0);
|
||||
|
||||
drop procedure p1;
|
||||
create procedure p1() flush des_key_file;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
insert into t1 values (0);
|
||||
|
||||
drop procedure p1;
|
||||
create procedure p1() flush user_resources;
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
insert into t1 values (0);
|
||||
|
||||
drop procedure p1;
|
||||
drop table t1;
|
||||
|
||||
|
@ -1301,6 +1389,36 @@ create trigger wont_work after update on event for each row
|
|||
begin
|
||||
set @a:= 1;
|
||||
end|
|
||||
use test|
|
||||
delimiter ;|
|
||||
|
||||
|
||||
#
|
||||
# Test for BUG#16899: Possible buffer overflow in handling of DEFINER-clause.
|
||||
#
|
||||
|
||||
# Prepare.
|
||||
|
||||
--disable_warnings
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
--enable_warnings
|
||||
|
||||
CREATE TABLE t1(c INT);
|
||||
CREATE TABLE t2(c INT);
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
CREATE DEFINER=1234567890abcdefGHIKL@localhost
|
||||
TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW SET @a = 1;
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
CREATE DEFINER=some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY
|
||||
TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW SET @a = 2;
|
||||
|
||||
# Cleanup.
|
||||
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t2;
|
||||
|
||||
|
||||
--echo End of 5.0 tests
|
||||
|
|
|
@ -146,3 +146,44 @@ DROP TABLE IF EXISTS t1;
|
|||
CREATE TABLE t1(f1 CHAR(100) DEFAULT 'test');
|
||||
INSERT INTO t1 VALUES(SUBSTR(f1, 1, 3));
|
||||
DROP TABLE IF EXISTS t1;
|
||||
|
||||
#
|
||||
# Bug#14897 "ResultSet.getString("table.column") sometimes doesn't find the
|
||||
# column"
|
||||
# Test that after upgrading an old 4.1 VARCHAR column to 5.0 VARCHAR we preserve
|
||||
# the original column metadata.
|
||||
#
|
||||
--disable_warnings
|
||||
drop table if exists t1, t2, t3;
|
||||
--enable_warnings
|
||||
|
||||
create table t3 (
|
||||
id int(11),
|
||||
en varchar(255) character set utf8,
|
||||
cz varchar(255) character set utf8
|
||||
);
|
||||
system cp $MYSQL_TEST_DIR/std_data/14897.frm $MYSQLTEST_VARDIR/master-data/test/t3.frm;
|
||||
truncate table t3;
|
||||
insert into t3 (id, en, cz) values
|
||||
(1,'en string 1','cz string 1'),
|
||||
(2,'en string 2','cz string 2'),
|
||||
(3,'en string 3','cz string 3');
|
||||
|
||||
create table t1 (
|
||||
id int(11),
|
||||
name_id int(11)
|
||||
);
|
||||
insert into t1 (id, name_id) values (1,1), (2,3), (3,3);
|
||||
|
||||
create table t2 (id int(11));
|
||||
insert into t2 (id) values (1), (2), (3);
|
||||
|
||||
# max_length is different for varchar fields in ps-protocol and we can't
|
||||
# replace a single metadata column, disable PS protocol
|
||||
--disable_ps_protocol
|
||||
--enable_metadata
|
||||
select t1.*, t2.id, t3.en, t3.cz from t1 left join t2 on t1.id=t2.id
|
||||
left join t3 on t1.id=t3.id order by t3.id;
|
||||
--disable_metadata
|
||||
--enable_ps_protocol
|
||||
drop table t1, t2, t3;
|
||||
|
|
|
@ -2760,3 +2760,78 @@ create view v1 as select * from t1 where f1 between now() and now() + interval 1
|
|||
show create view v1;
|
||||
drop view v1;
|
||||
drop table t1;
|
||||
#
|
||||
# Test for BUG#16899: Possible buffer overflow in handling of DEFINER-clause.
|
||||
#
|
||||
|
||||
# Prepare.
|
||||
|
||||
--disable_warnings
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP VIEW IF EXISTS v1;
|
||||
DROP VIEW IF EXISTS v2;
|
||||
--enable_warnings
|
||||
|
||||
CREATE TABLE t1(a INT, b INT);
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
CREATE DEFINER=1234567890abcdefGHIKL@localhost
|
||||
VIEW v1 AS SELECT a FROM t1;
|
||||
|
||||
--error ER_WRONG_STRING_LENGTH
|
||||
CREATE DEFINER=some_user_name@1234567890abcdefghij1234567890abcdefghij1234567890abcdefghijQWERTY
|
||||
VIEW v2 AS SELECT b FROM t1;
|
||||
|
||||
# Cleanup.
|
||||
|
||||
DROP TABLE t1;
|
||||
|
||||
|
||||
#
|
||||
# BUG#17591: Updatable view not possible with trigger or stored
|
||||
# function
|
||||
#
|
||||
# During prelocking phase we didn't update lock type of view tables,
|
||||
# hence READ lock was always requested.
|
||||
#
|
||||
--disable_warnings
|
||||
DROP FUNCTION IF EXISTS f1;
|
||||
DROP FUNCTION IF EXISTS f2;
|
||||
DROP VIEW IF EXISTS v1, v2;
|
||||
DROP TABLE IF EXISTS t1;
|
||||
--enable_warnings
|
||||
|
||||
CREATE TABLE t1 (i INT);
|
||||
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
|
||||
delimiter |;
|
||||
CREATE FUNCTION f1() RETURNS INT
|
||||
BEGIN
|
||||
INSERT INTO v1 VALUES (0);
|
||||
RETURN 0;
|
||||
END |
|
||||
delimiter ;|
|
||||
|
||||
SELECT f1();
|
||||
|
||||
CREATE ALGORITHM=TEMPTABLE VIEW v2 AS SELECT * FROM t1;
|
||||
|
||||
delimiter |;
|
||||
CREATE FUNCTION f2() RETURNS INT
|
||||
BEGIN
|
||||
INSERT INTO v2 VALUES (0);
|
||||
RETURN 0;
|
||||
END |
|
||||
delimiter ;|
|
||||
|
||||
--error ER_NON_UPDATABLE_TABLE
|
||||
SELECT f2();
|
||||
|
||||
DROP FUNCTION f1;
|
||||
DROP FUNCTION f2;
|
||||
DROP VIEW v1, v2;
|
||||
DROP TABLE t1;
|
||||
|
||||
|
||||
--echo End of 5.0 tests.
|
||||
|
|
|
@ -117,8 +117,9 @@ DEFS = -DMYSQL_SERVER \
|
|||
|
||||
BUILT_SOURCES = sql_yacc.cc sql_yacc.h lex_hash.h
|
||||
EXTRA_DIST = $(BUILT_SOURCES)
|
||||
DISTCLEANFILES = lex_hash.h
|
||||
AM_YFLAGS = -d
|
||||
DISTCLEANFILES = lex_hash.h sql_yacc.output
|
||||
|
||||
AM_YFLAGS = -d --debug --verbose
|
||||
|
||||
mysql_tzinfo_to_sql.cc:
|
||||
rm -f mysql_tzinfo_to_sql.cc
|
||||
|
|
27
sql/field.cc
27
sql/field.cc
|
@ -6154,15 +6154,26 @@ Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table,
|
|||
Field *new_field;
|
||||
|
||||
if (type() != MYSQL_TYPE_VAR_STRING || keep_type)
|
||||
return Field::new_field(root, new_table, keep_type);
|
||||
new_field= Field::new_field(root, new_table, keep_type);
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
Old VARCHAR field which should be modified to a VARCHAR on copy
|
||||
This is done to ensure that ALTER TABLE will convert old VARCHAR fields
|
||||
to now VARCHAR fields.
|
||||
*/
|
||||
return new Field_varstring(field_length, maybe_null(),
|
||||
field_name, new_table, charset());
|
||||
/*
|
||||
Old VARCHAR field which should be modified to a VARCHAR on copy
|
||||
This is done to ensure that ALTER TABLE will convert old VARCHAR fields
|
||||
to now VARCHAR fields.
|
||||
*/
|
||||
new_field= new Field_varstring(field_length, maybe_null(),
|
||||
field_name, new_table, charset());
|
||||
/*
|
||||
Normally orig_table is different from table only if field was created
|
||||
via ::new_field. Here we alter the type of field, so ::new_field is
|
||||
not applicable. But we still need to preserve the original field
|
||||
metadata for the client-server protocol.
|
||||
*/
|
||||
new_field->orig_table= orig_table;
|
||||
}
|
||||
return new_field;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
|
72
sql/item.cc
72
sql/item.cc
|
@ -422,6 +422,49 @@ void Item::rename(char *new_name)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
Traverse item tree possibly transforming it (replacing items).
|
||||
|
||||
SYNOPSIS
|
||||
Item::transform()
|
||||
transformer functor that performs transformation of a subtree
|
||||
arg opaque argument passed to the functor
|
||||
|
||||
DESCRIPTION
|
||||
This function is designed to ease transformation of Item trees.
|
||||
|
||||
Re-execution note: every such transformation is registered for
|
||||
rollback by THD::change_item_tree() and is rolled back at the end
|
||||
of execution by THD::rollback_item_tree_changes().
|
||||
|
||||
Therefore:
|
||||
|
||||
- this function can not be used at prepared statement prepare
|
||||
(in particular, in fix_fields!), as only permanent
|
||||
transformation of Item trees are allowed at prepare.
|
||||
|
||||
- the transformer function shall allocate new Items in execution
|
||||
memory root (thd->mem_root) and not anywhere else: allocated
|
||||
items will be gone in the end of execution.
|
||||
|
||||
If you don't need to transform an item tree, but only traverse
|
||||
it, please use Item::walk() instead.
|
||||
|
||||
|
||||
RETURN VALUE
|
||||
Returns pointer to the new subtree root. THD::change_item_tree()
|
||||
should be called for it if transformation took place, i.e. if a
|
||||
pointer to newly allocated item is returned.
|
||||
*/
|
||||
|
||||
Item* Item::transform(Item_transformer transformer, byte *arg)
|
||||
{
|
||||
DBUG_ASSERT(!current_thd->is_stmt_prepare());
|
||||
|
||||
return (this->*transformer)(arg);
|
||||
}
|
||||
|
||||
|
||||
Item_ident::Item_ident(Name_resolution_context *context_arg,
|
||||
const char *db_name_arg,const char *table_name_arg,
|
||||
const char *field_name_arg)
|
||||
|
@ -3802,11 +3845,11 @@ Item *Item_field::equal_fields_propagator(byte *arg)
|
|||
See comments in Arg_comparator::set_compare_func() for details
|
||||
*/
|
||||
|
||||
Item *Item_field::set_no_const_sub(byte *arg)
|
||||
bool Item_field::set_no_const_sub(byte *arg)
|
||||
{
|
||||
if (field->charset() != &my_charset_bin)
|
||||
no_const_subst=1;
|
||||
return this;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
@ -5308,6 +5351,31 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
This method like the walk method traverses the item tree, but at the
|
||||
same time it can replace some nodes in the tree
|
||||
*/
|
||||
|
||||
Item *Item_default_value::transform(Item_transformer transformer, byte *args)
|
||||
{
|
||||
DBUG_ASSERT(!current_thd->is_stmt_prepare());
|
||||
|
||||
Item *new_item= arg->transform(transformer, args);
|
||||
if (!new_item)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
THD::change_item_tree() should be called only if the tree was
|
||||
really transformed, i.e. when a new item has been created.
|
||||
Otherwise we'll be allocating a lot of unnecessary memory for
|
||||
change records at each execution.
|
||||
*/
|
||||
if (arg != new_item)
|
||||
current_thd->change_item_tree(&arg, new_item);
|
||||
return (this->*transformer)(args);
|
||||
}
|
||||
|
||||
|
||||
bool Item_insert_value::eq(const Item *item, bool binary_cmp) const
|
||||
{
|
||||
return item->type() == INSERT_VALUE_ITEM &&
|
||||
|
|
22
sql/item.h
22
sql/item.h
|
@ -734,10 +734,7 @@ public:
|
|||
return (this->*processor)(arg);
|
||||
}
|
||||
|
||||
virtual Item* transform(Item_transformer transformer, byte *arg)
|
||||
{
|
||||
return (this->*transformer)(arg);
|
||||
}
|
||||
virtual Item* transform(Item_transformer transformer, byte *arg);
|
||||
|
||||
virtual void traverse_cond(Cond_traverser traverser,
|
||||
void *arg, traverse_order order)
|
||||
|
@ -755,7 +752,7 @@ public:
|
|||
virtual bool is_expensive_processor(byte *arg) { return 0; }
|
||||
|
||||
virtual Item *equal_fields_propagator(byte * arg) { return this; }
|
||||
virtual Item *set_no_const_sub(byte *arg) { return this; }
|
||||
virtual bool set_no_const_sub(byte *arg) { return FALSE; }
|
||||
virtual Item *replace_equal_field(byte * arg) { return this; }
|
||||
|
||||
/*
|
||||
|
@ -1255,7 +1252,7 @@ public:
|
|||
}
|
||||
Item_equal *find_item_equal(COND_EQUAL *cond_equal);
|
||||
Item *equal_fields_propagator(byte *arg);
|
||||
Item *set_no_const_sub(byte *arg);
|
||||
bool set_no_const_sub(byte *arg);
|
||||
Item *replace_equal_field(byte *arg);
|
||||
inline uint32 max_disp_length() { return field->max_length(); }
|
||||
Item_field *filed_for_view_update() { return this; }
|
||||
|
@ -2116,18 +2113,7 @@ public:
|
|||
(this->*processor)(args);
|
||||
}
|
||||
|
||||
/*
|
||||
This method like the walk method traverses the item tree, but
|
||||
at the same time it can replace some nodes in the tree
|
||||
*/
|
||||
Item *transform(Item_transformer transformer, byte *args)
|
||||
{
|
||||
Item *new_item= arg->transform(transformer, args);
|
||||
if (!new_item)
|
||||
return 0;
|
||||
arg= new_item;
|
||||
return (this->*transformer)(args);
|
||||
}
|
||||
Item *transform(Item_transformer transformer, byte *args);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -511,8 +511,8 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type)
|
|||
which would be transformed to:
|
||||
WHERE col= 'j'
|
||||
*/
|
||||
(*a)->transform(&Item::set_no_const_sub, (byte*) 0);
|
||||
(*b)->transform(&Item::set_no_const_sub, (byte*) 0);
|
||||
(*a)->walk(&Item::set_no_const_sub, (byte*) 0);
|
||||
(*b)->walk(&Item::set_no_const_sub, (byte*) 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2765,6 +2765,8 @@ bool Item_cond::walk(Item_processor processor, byte *arg)
|
|||
|
||||
Item *Item_cond::transform(Item_transformer transformer, byte *arg)
|
||||
{
|
||||
DBUG_ASSERT(!current_thd->is_stmt_prepare());
|
||||
|
||||
List_iterator<Item> li(list);
|
||||
Item *item;
|
||||
while ((item= li++))
|
||||
|
@ -2772,8 +2774,15 @@ Item *Item_cond::transform(Item_transformer transformer, byte *arg)
|
|||
Item *new_item= item->transform(transformer, arg);
|
||||
if (!new_item)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
THD::change_item_tree() should be called only if the tree was
|
||||
really transformed, i.e. when a new item has been created.
|
||||
Otherwise we'll be allocating a lot of unnecessary memory for
|
||||
change records at each execution.
|
||||
*/
|
||||
if (new_item != item)
|
||||
li.replace(new_item);
|
||||
current_thd->change_item_tree(li.ref(), new_item);
|
||||
}
|
||||
return Item_func::transform(transformer, arg);
|
||||
}
|
||||
|
@ -4018,6 +4027,8 @@ bool Item_equal::walk(Item_processor processor, byte *arg)
|
|||
|
||||
Item *Item_equal::transform(Item_transformer transformer, byte *arg)
|
||||
{
|
||||
DBUG_ASSERT(!current_thd->is_stmt_prepare());
|
||||
|
||||
List_iterator<Item_field> it(fields);
|
||||
Item *item;
|
||||
while ((item= it++))
|
||||
|
@ -4025,8 +4036,15 @@ Item *Item_equal::transform(Item_transformer transformer, byte *arg)
|
|||
Item *new_item= item->transform(transformer, arg);
|
||||
if (!new_item)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
THD::change_item_tree() should be called only if the tree was
|
||||
really transformed, i.e. when a new item has been created.
|
||||
Otherwise we'll be allocating a lot of unnecessary memory for
|
||||
change records at each execution.
|
||||
*/
|
||||
if (new_item != item)
|
||||
it.replace((Item_field *) new_item);
|
||||
current_thd->change_item_tree((Item **) it.ref(), new_item);
|
||||
}
|
||||
return Item_func::transform(transformer, arg);
|
||||
}
|
||||
|
|
|
@ -258,6 +258,8 @@ void Item_func::traverse_cond(Cond_traverser traverser,
|
|||
|
||||
Item *Item_func::transform(Item_transformer transformer, byte *argument)
|
||||
{
|
||||
DBUG_ASSERT(!current_thd->is_stmt_prepare());
|
||||
|
||||
if (arg_count)
|
||||
{
|
||||
Item **arg,**arg_end;
|
||||
|
@ -266,6 +268,13 @@ Item *Item_func::transform(Item_transformer transformer, byte *argument)
|
|||
Item *new_item= (*arg)->transform(transformer, argument);
|
||||
if (!new_item)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
THD::change_item_tree() should be called only if the tree was
|
||||
really transformed, i.e. when a new item has been created.
|
||||
Otherwise we'll be allocating a lot of unnecessary memory for
|
||||
change records at each execution.
|
||||
*/
|
||||
if (*arg != new_item)
|
||||
current_thd->change_item_tree(arg, new_item);
|
||||
}
|
||||
|
|
|
@ -154,12 +154,22 @@ bool Item_row::walk(Item_processor processor, byte *arg)
|
|||
|
||||
Item *Item_row::transform(Item_transformer transformer, byte *arg)
|
||||
{
|
||||
DBUG_ASSERT(!current_thd->is_stmt_prepare());
|
||||
|
||||
for (uint i= 0; i < arg_count; i++)
|
||||
{
|
||||
Item *new_item= items[i]->transform(transformer, arg);
|
||||
if (!new_item)
|
||||
return 0;
|
||||
items[i]= new_item;
|
||||
|
||||
/*
|
||||
THD::change_item_tree() should be called only if the tree was
|
||||
really transformed, i.e. when a new item has been created.
|
||||
Otherwise we'll be allocating a lot of unnecessary memory for
|
||||
change records at each execution.
|
||||
*/
|
||||
if (items[i] != new_item)
|
||||
current_thd->change_item_tree(&items[i], new_item);
|
||||
}
|
||||
return (this->*transformer)(arg);
|
||||
}
|
||||
|
|
|
@ -2051,6 +2051,26 @@ String *Item_func_make_set::val_str(String *str)
|
|||
}
|
||||
|
||||
|
||||
Item *Item_func_make_set::transform(Item_transformer transformer, byte *arg)
|
||||
{
|
||||
DBUG_ASSERT(!current_thd->is_stmt_prepare());
|
||||
|
||||
Item *new_item= item->transform(transformer, arg);
|
||||
if (!new_item)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
THD::change_item_tree() should be called only if the tree was
|
||||
really transformed, i.e. when a new item has been created.
|
||||
Otherwise we'll be allocating a lot of unnecessary memory for
|
||||
change records at each execution.
|
||||
*/
|
||||
if (item != new_item)
|
||||
current_thd->change_item_tree(&item, new_item);
|
||||
return Item_str_func::transform(transformer, arg);
|
||||
}
|
||||
|
||||
|
||||
void Item_func_make_set::print(String *str)
|
||||
{
|
||||
str->append(STRING_WITH_LEN("make_set("));
|
||||
|
|
|
@ -475,14 +475,7 @@ public:
|
|||
return item->walk(processor, arg) ||
|
||||
Item_str_func::walk(processor, arg);
|
||||
}
|
||||
Item *transform(Item_transformer transformer, byte *arg)
|
||||
{
|
||||
Item *new_item= item->transform(transformer, arg);
|
||||
if (!new_item)
|
||||
return 0;
|
||||
item= new_item;
|
||||
return Item_str_func::transform(transformer, arg);
|
||||
}
|
||||
Item *transform(Item_transformer transformer, byte *arg);
|
||||
void print(String *str);
|
||||
};
|
||||
|
||||
|
|
|
@ -854,6 +854,7 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list)
|
|||
TABLE *table;
|
||||
char key[MAX_DBKEY_LENGTH];
|
||||
char *db= table_list->db;
|
||||
int table_in_key_offset;
|
||||
uint key_length;
|
||||
HASH_SEARCH_STATE state;
|
||||
DBUG_ENTER("lock_table_name");
|
||||
|
@ -861,8 +862,9 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list)
|
|||
|
||||
safe_mutex_assert_owner(&LOCK_open);
|
||||
|
||||
key_length=(uint) (strmov(strmov(key,db)+1,table_list->table_name)
|
||||
-key)+ 1;
|
||||
table_in_key_offset= strmov(key, db) - key + 1;
|
||||
key_length= (uint)(strmov(key + table_in_key_offset, table_list->table_name)
|
||||
- key) + 1;
|
||||
|
||||
|
||||
/* Only insert the table if we haven't insert it already */
|
||||
|
@ -883,6 +885,7 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list)
|
|||
table->s= &table->share_not_to_be_used;
|
||||
memcpy((table->s->table_cache_key= (char*) (table+1)), key, key_length);
|
||||
table->s->db= table->s->table_cache_key;
|
||||
table->s->table_name= table->s->table_cache_key + table_in_key_offset;
|
||||
table->s->key_length=key_length;
|
||||
table->in_use=thd;
|
||||
table->locked_by_name=1;
|
||||
|
|
|
@ -566,6 +566,7 @@ void get_default_definer(THD *thd, LEX_USER *definer);
|
|||
LEX_USER *create_default_definer(THD *thd);
|
||||
LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name);
|
||||
LEX_USER *get_current_user(THD *thd, LEX_USER *user);
|
||||
bool check_string_length(LEX_STRING *str, const char *err_msg, uint max_length);
|
||||
|
||||
enum enum_mysql_completiontype {
|
||||
ROLLBACK_RELEASE=-2, ROLLBACK=1, ROLLBACK_AND_CHAIN=7,
|
||||
|
@ -1511,6 +1512,9 @@ void free_list(I_List <i_string> *list);
|
|||
|
||||
/* sql_yacc.cc */
|
||||
extern int MYSQLparse(void *thd);
|
||||
#ifndef DBUG_OFF
|
||||
extern void turn_parser_debug_on();
|
||||
#endif
|
||||
|
||||
/* frm_crypt.cc */
|
||||
#ifdef HAVE_CRYPTED_FRM
|
||||
|
|
|
@ -2392,10 +2392,8 @@ static int my_message_sql(uint error, const char *str, myf MyFlags)
|
|||
if ((thd= current_thd))
|
||||
{
|
||||
if (thd->spcont &&
|
||||
thd->spcont->find_handler(error, MYSQL_ERROR::WARN_LEVEL_ERROR))
|
||||
thd->spcont->handle_error(error, MYSQL_ERROR::WARN_LEVEL_ERROR, thd))
|
||||
{
|
||||
if (! thd->spcont->found_handler_here())
|
||||
thd->net.report_error= 1; /* Make "select" abort correctly */
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -96,8 +96,11 @@ extern uint test_flags;
|
|||
extern ulong bytes_sent, bytes_received, net_big_packet_count;
|
||||
extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received;
|
||||
#ifndef MYSQL_INSTANCE_MANAGER
|
||||
extern void query_cache_insert(NET *net, const char *packet, ulong length);
|
||||
#ifdef HAVE_QUERY_CACHE
|
||||
#define USE_QUERY_CACHE
|
||||
extern void query_cache_init_query(NET *net);
|
||||
extern void query_cache_insert(NET *net, const char *packet, ulong length);
|
||||
#endif // HAVE_QUERY_CACHE
|
||||
#define update_statistics(A) A
|
||||
#endif /* MYSQL_INSTANCE_MANGER */
|
||||
#endif /* defined(MYSQL_SERVER) && !defined(MYSQL_INSTANCE_MANAGER) */
|
||||
|
@ -133,7 +136,11 @@ my_bool my_net_init(NET *net, Vio* vio)
|
|||
net->compress=0; net->reading_or_writing=0;
|
||||
net->where_b = net->remain_in_buf=0;
|
||||
net->last_errno=0;
|
||||
net->query_cache_query=0;
|
||||
#ifdef USE_QUERY_CACHE
|
||||
query_cache_init_query(net);
|
||||
#else
|
||||
net->query_cache_query= 0;
|
||||
#endif
|
||||
net->report_error= 0;
|
||||
|
||||
if (vio != 0) /* If real connection */
|
||||
|
@ -552,10 +559,8 @@ net_real_write(NET *net,const char *packet,ulong len)
|
|||
my_bool net_blocking = vio_is_blocking(net->vio);
|
||||
DBUG_ENTER("net_real_write");
|
||||
|
||||
#if defined(MYSQL_SERVER) && defined(HAVE_QUERY_CACHE) \
|
||||
&& !defined(MYSQL_INSTANCE_MANAGER)
|
||||
if (net->query_cache_query != 0)
|
||||
query_cache_insert(net, packet, len);
|
||||
#if defined(MYSQL_SERVER) && defined(USE_QUERY_CACHE)
|
||||
query_cache_insert(net, packet, len);
|
||||
#endif
|
||||
|
||||
if (net->error == 2)
|
||||
|
|
|
@ -53,8 +53,18 @@ bool Protocol_prep::net_store_data(const char *from, uint length)
|
|||
}
|
||||
|
||||
|
||||
/* Send a error string to client */
|
||||
/*
|
||||
Send a error string to client
|
||||
|
||||
Design note:
|
||||
|
||||
net_printf_error and net_send_error are low-level functions
|
||||
that shall be used only when a new connection is being
|
||||
established or at server startup.
|
||||
For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's
|
||||
critical that every error that can be intercepted is issued in one
|
||||
place only, my_message_sql.
|
||||
*/
|
||||
void net_send_error(THD *thd, uint sql_errno, const char *err)
|
||||
{
|
||||
NET *net= &thd->net;
|
||||
|
@ -64,19 +74,15 @@ void net_send_error(THD *thd, uint sql_errno, const char *err)
|
|||
err ? err : net->last_error[0] ?
|
||||
net->last_error : "NULL"));
|
||||
|
||||
DBUG_ASSERT(!thd->spcont);
|
||||
|
||||
if (net && net->no_send_error)
|
||||
{
|
||||
thd->clear_error();
|
||||
DBUG_PRINT("info", ("sending error messages prohibited"));
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
if (thd->spcont && thd->spcont->find_handler(sql_errno,
|
||||
MYSQL_ERROR::WARN_LEVEL_ERROR))
|
||||
{
|
||||
if (! thd->spcont->found_handler_here())
|
||||
thd->net.report_error= 1; /* Make "select" abort correctly */
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
thd->query_error= 1; // needed to catch query errors during replication
|
||||
if (!err)
|
||||
{
|
||||
|
@ -117,6 +123,15 @@ void net_send_error(THD *thd, uint sql_errno, const char *err)
|
|||
Write error package and flush to client
|
||||
It's a little too low level, but I don't want to use another buffer for
|
||||
this
|
||||
|
||||
Design note:
|
||||
|
||||
net_printf_error and net_send_error are low-level functions
|
||||
that shall be used only when a new connection is being
|
||||
established or at server startup.
|
||||
For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's
|
||||
critical that every error that can be intercepted is issued in one
|
||||
place only, my_message_sql.
|
||||
*/
|
||||
|
||||
void
|
||||
|
@ -136,6 +151,8 @@ net_printf_error(THD *thd, uint errcode, ...)
|
|||
DBUG_ENTER("net_printf_error");
|
||||
DBUG_PRINT("enter",("message: %u",errcode));
|
||||
|
||||
DBUG_ASSERT(!thd->spcont);
|
||||
|
||||
if (net && net->no_send_error)
|
||||
{
|
||||
thd->clear_error();
|
||||
|
@ -143,13 +160,6 @@ net_printf_error(THD *thd, uint errcode, ...)
|
|||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
if (thd->spcont && thd->spcont->find_handler(errcode,
|
||||
MYSQL_ERROR::WARN_LEVEL_ERROR))
|
||||
{
|
||||
if (! thd->spcont->found_handler_here())
|
||||
thd->net.report_error= 1; /* Make "select" abort correctly */
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
thd->query_error= 1; // needed to catch query errors during replication
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
query_cache_abort(net); // Safety
|
||||
|
|
|
@ -5623,3 +5623,9 @@ ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA
|
|||
eng "Triggers can not be created on system tables"
|
||||
ER_REMOVED_SPACES
|
||||
eng "Leading spaces are removed from name '%s'"
|
||||
ER_USERNAME
|
||||
eng "user name"
|
||||
ER_HOSTNAME
|
||||
eng "host name"
|
||||
ER_WRONG_STRING_LENGTH
|
||||
eng "String '%-.70s' is too long for %s (should be no longer than %d)"
|
||||
|
|
21
sql/sp.cc
21
sql/sp.cc
|
@ -1006,6 +1006,12 @@ sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp,
|
|||
}
|
||||
DBUG_RETURN(sp->m_first_free_instance);
|
||||
}
|
||||
/*
|
||||
Actually depth could be +1 than the actual value in case a SP calls
|
||||
SHOW CREATE PROCEDURE. Hence, the linked list could hold up to one more
|
||||
instance.
|
||||
*/
|
||||
|
||||
level= sp->m_last_cached_sp->m_recursion_level + 1;
|
||||
if (level > depth)
|
||||
{
|
||||
|
@ -1175,19 +1181,22 @@ sp_update_procedure(THD *thd, sp_name *name, st_sp_chistics *chistics)
|
|||
int
|
||||
sp_show_create_procedure(THD *thd, sp_name *name)
|
||||
{
|
||||
int ret= SP_KEY_NOT_FOUND;
|
||||
sp_head *sp;
|
||||
DBUG_ENTER("sp_show_create_procedure");
|
||||
DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str));
|
||||
|
||||
/*
|
||||
Increase the recursion limit for this statement. SHOW CREATE PROCEDURE
|
||||
does not do actual recursion.
|
||||
*/
|
||||
thd->variables.max_sp_recursion_depth++;
|
||||
if ((sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name,
|
||||
&thd->sp_proc_cache, FALSE)))
|
||||
{
|
||||
int ret= sp->show_create_procedure(thd);
|
||||
ret= sp->show_create_procedure(thd);
|
||||
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
DBUG_RETURN(SP_KEY_NOT_FOUND);
|
||||
thd->variables.max_sp_recursion_depth--;
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -230,6 +230,12 @@ sp_get_flags_for_command(LEX *lex)
|
|||
else
|
||||
flags= sp_head::HAS_COMMIT_OR_ROLLBACK;
|
||||
break;
|
||||
case SQLCOM_FLUSH:
|
||||
flags= sp_head::HAS_SQLCOM_FLUSH;
|
||||
break;
|
||||
case SQLCOM_RESET:
|
||||
flags= sp_head::HAS_SQLCOM_RESET;
|
||||
break;
|
||||
case SQLCOM_CREATE_INDEX:
|
||||
case SQLCOM_CREATE_DB:
|
||||
case SQLCOM_CREATE_VIEW:
|
||||
|
|
|
@ -115,7 +115,9 @@ public:
|
|||
IS_INVOKED= 32, // Is set if this sp_head is being used
|
||||
HAS_SET_AUTOCOMMIT_STMT= 64,// Is set if a procedure with 'set autocommit'
|
||||
/* Is set if a procedure with COMMIT (implicit or explicit) | ROLLBACK */
|
||||
HAS_COMMIT_OR_ROLLBACK= 128
|
||||
HAS_COMMIT_OR_ROLLBACK= 128,
|
||||
HAS_SQLCOM_RESET= 2048,
|
||||
HAS_SQLCOM_FLUSH= 4096
|
||||
};
|
||||
|
||||
/* TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */
|
||||
|
@ -335,14 +337,16 @@ public:
|
|||
my_error(ER_SP_NO_RETSET, MYF(0), where);
|
||||
else if (m_flags & HAS_SET_AUTOCOMMIT_STMT)
|
||||
my_error(ER_SP_CANT_SET_AUTOCOMMIT, MYF(0));
|
||||
else if (m_type != TYPE_ENUM_PROCEDURE &&
|
||||
(m_flags & sp_head::HAS_COMMIT_OR_ROLLBACK))
|
||||
{
|
||||
else if (m_flags & HAS_COMMIT_OR_ROLLBACK)
|
||||
my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
|
||||
return TRUE;
|
||||
}
|
||||
else if (m_flags & HAS_SQLCOM_RESET)
|
||||
my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "RESET");
|
||||
else if (m_flags & HAS_SQLCOM_FLUSH)
|
||||
my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "FLUSH");
|
||||
|
||||
return test(m_flags &
|
||||
(CONTAINS_DYNAMIC_SQL|MULTI_RESULTS|HAS_SET_AUTOCOMMIT_STMT));
|
||||
(CONTAINS_DYNAMIC_SQL|MULTI_RESULTS|HAS_SET_AUTOCOMMIT_STMT|
|
||||
HAS_COMMIT_OR_ROLLBACK|HAS_SQLCOM_RESET|HAS_SQLCOM_FLUSH));
|
||||
}
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
|
|
|
@ -260,6 +260,65 @@ sp_rcontext::find_handler(uint sql_errno,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
Handle the error for a given errno.
|
||||
The severity of the error is adjusted depending of the current sql_mode.
|
||||
If an handler is present for the error (see find_handler()),
|
||||
this function will return true.
|
||||
If a handler is found and if the severity of the error indicate
|
||||
that the current instruction executed should abort,
|
||||
the flag thd->net.report_error is also set.
|
||||
This will cause the execution of the current instruction in a
|
||||
sp_instr* to fail, and give control to the handler code itself
|
||||
in the sp_head::execute() loop.
|
||||
|
||||
SYNOPSIS
|
||||
sql_errno The error code
|
||||
level Warning level
|
||||
thd The current thread
|
||||
- thd->net.report_error is an optional output.
|
||||
|
||||
RETURN
|
||||
TRUE if a handler was found.
|
||||
FALSE if no handler was found.
|
||||
*/
|
||||
bool
|
||||
sp_rcontext::handle_error(uint sql_errno,
|
||||
MYSQL_ERROR::enum_warning_level level,
|
||||
THD *thd)
|
||||
{
|
||||
bool handled= FALSE;
|
||||
MYSQL_ERROR::enum_warning_level elevated_level= level;
|
||||
|
||||
|
||||
/* Depending on the sql_mode of execution,
|
||||
warnings may be considered errors */
|
||||
if ((level == MYSQL_ERROR::WARN_LEVEL_WARN) &&
|
||||
thd->really_abort_on_warning())
|
||||
{
|
||||
elevated_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
|
||||
}
|
||||
|
||||
if (find_handler(sql_errno, elevated_level))
|
||||
{
|
||||
if (elevated_level == MYSQL_ERROR::WARN_LEVEL_ERROR)
|
||||
{
|
||||
/*
|
||||
Forces to abort the current instruction execution.
|
||||
NOTE: This code is altering the original meaning of
|
||||
the net.report_error flag (send an error to the client).
|
||||
In the context of stored procedures with error handlers,
|
||||
the flag is reused to cause error propagation,
|
||||
until the error handler is reached.
|
||||
No messages will be sent to the client in that context.
|
||||
*/
|
||||
thd->net.report_error= 1;
|
||||
}
|
||||
handled= TRUE;
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
void
|
||||
sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
|
||||
|
|
|
@ -128,6 +128,12 @@ class sp_rcontext : public Sql_alloc
|
|||
bool
|
||||
find_handler(uint sql_errno,MYSQL_ERROR::enum_warning_level level);
|
||||
|
||||
// If there is an error handler for this error, handle it and return TRUE.
|
||||
bool
|
||||
handle_error(uint sql_errno,
|
||||
MYSQL_ERROR::enum_warning_level level,
|
||||
THD *thd);
|
||||
|
||||
// Returns handler type and sets *ip to location if one was found
|
||||
inline int
|
||||
found_handler(uint *ip, uint *fp)
|
||||
|
|
|
@ -2903,14 +2903,6 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
|
|||
result= TRUE;
|
||||
continue;
|
||||
}
|
||||
if (Str->host.length > HOSTNAME_LENGTH ||
|
||||
Str->user.length > USERNAME_LENGTH)
|
||||
{
|
||||
my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
|
||||
MYF(0));
|
||||
result= TRUE;
|
||||
continue;
|
||||
}
|
||||
/* Create user if needed */
|
||||
error=replace_user_table(thd, tables[0].table, *Str,
|
||||
0, revoke_grant, create_new_users,
|
||||
|
@ -3115,15 +3107,6 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
|
|||
result= TRUE;
|
||||
continue;
|
||||
}
|
||||
if (Str->host.length > HOSTNAME_LENGTH ||
|
||||
Str->user.length > USERNAME_LENGTH)
|
||||
{
|
||||
if (!no_error)
|
||||
my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
|
||||
MYF(0));
|
||||
result= TRUE;
|
||||
continue;
|
||||
}
|
||||
/* Create user if needed */
|
||||
error=replace_user_table(thd, tables[0].table, *Str,
|
||||
0, revoke_grant, create_new_users,
|
||||
|
@ -3249,14 +3232,6 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
|
|||
result= TRUE;
|
||||
continue;
|
||||
}
|
||||
if (Str->host.length > HOSTNAME_LENGTH ||
|
||||
Str->user.length > USERNAME_LENGTH)
|
||||
{
|
||||
my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
|
||||
MYF(0));
|
||||
result= -1;
|
||||
continue;
|
||||
}
|
||||
if (replace_user_table(thd, tables[0].table, *Str,
|
||||
(!db ? rights : 0), revoke_grant, create_new_users,
|
||||
test(thd->variables.sql_mode &
|
||||
|
@ -4162,14 +4137,6 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
|
|||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
if (lex_user->host.length > HOSTNAME_LENGTH ||
|
||||
lex_user->user.length > USERNAME_LENGTH)
|
||||
{
|
||||
my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
|
||||
MYF(0));
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
rw_rdlock(&LOCK_grant);
|
||||
VOID(pthread_mutex_lock(&acl_cache->lock));
|
||||
|
||||
|
@ -5265,14 +5232,6 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (user_name->host.length > HOSTNAME_LENGTH ||
|
||||
user_name->user.length > USERNAME_LENGTH)
|
||||
{
|
||||
append_user(&wrong_users, user_name);
|
||||
result= TRUE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
Search all in-memory structures and grant tables
|
||||
for a mention of the new user name.
|
||||
|
|
588
sql/sql_cache.cc
588
sql/sql_cache.cc
|
@ -564,22 +564,63 @@ byte *query_cache_query_get_key(const byte *record, uint *length,
|
|||
Functions to store things into the query cache
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
Note on double-check locking (DCL) usage.
|
||||
|
||||
Below, in query_cache_insert(), query_cache_abort() and
|
||||
query_cache_end_of_result() we use what is called double-check
|
||||
locking (DCL) for NET::query_cache_query. I.e. we test it first
|
||||
without a lock, and, if positive, test again under the lock.
|
||||
|
||||
This means that if we see 'NET::query_cache_query == 0' without a
|
||||
lock we will skip the operation. But this is safe here: when we
|
||||
started to cache a query, we called Query_cache::store_query(), and
|
||||
NET::query_cache_query was set to non-zero in this thread (and the
|
||||
thread always sees results of its memory operations, mutex or not).
|
||||
If later we see 'NET::query_cache_query == 0' without locking a
|
||||
mutex, that may only mean that some other thread have reset it by
|
||||
invalidating the query. Skipping the operation in this case is the
|
||||
right thing to do, as NET::query_cache_query won't get non-zero for
|
||||
this query again.
|
||||
|
||||
See also comments in Query_cache::store_query() and
|
||||
Query_cache::send_result_to_client().
|
||||
|
||||
NOTE, however, that double-check locking is not applicable in
|
||||
'invalidate' functions, as we may erroneously skip invalidation,
|
||||
because the thread doing invalidation may never see non-zero
|
||||
NET::query_cache_query.
|
||||
*/
|
||||
|
||||
|
||||
void query_cache_init_query(NET *net)
|
||||
{
|
||||
/*
|
||||
It is safe to initialize 'NET::query_cache_query' without a lock
|
||||
here, because before it will be accessed from different threads it
|
||||
will be set in this thread under a lock, and access from the same
|
||||
thread is always safe.
|
||||
*/
|
||||
net->query_cache_query= 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Insert the packet into the query cache.
|
||||
This should only be called if net->query_cache_query != 0
|
||||
*/
|
||||
|
||||
void query_cache_insert(NET *net, const char *packet, ulong length)
|
||||
{
|
||||
DBUG_ENTER("query_cache_insert");
|
||||
|
||||
/* See the comment on double-check locking usage above. */
|
||||
if (net->query_cache_query == 0)
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
STRUCT_LOCK(&query_cache.structure_guard_mutex);
|
||||
/*
|
||||
It is very unlikely that following condition is TRUE (it is possible
|
||||
only if other thread is resizing cache), so we check it only after guard
|
||||
mutex lock
|
||||
*/
|
||||
if (unlikely(query_cache.query_cache_size == 0))
|
||||
|
||||
if (unlikely(query_cache.query_cache_size == 0 ||
|
||||
query_cache.flush_in_progress))
|
||||
{
|
||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||
DBUG_VOID_RETURN;
|
||||
|
@ -616,10 +657,10 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
|
|||
header->result(result);
|
||||
header->last_pkt_nr= net->pkt_nr;
|
||||
BLOCK_UNLOCK_WR(query_block);
|
||||
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
|
||||
}
|
||||
else
|
||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
@ -628,33 +669,33 @@ void query_cache_abort(NET *net)
|
|||
{
|
||||
DBUG_ENTER("query_cache_abort");
|
||||
|
||||
if (net->query_cache_query != 0) // Quick check on unlocked structure
|
||||
{
|
||||
STRUCT_LOCK(&query_cache.structure_guard_mutex);
|
||||
/*
|
||||
It is very unlikely that following condition is TRUE (it is possible
|
||||
only if other thread is resizing cache), so we check it only after guard
|
||||
mutex lock
|
||||
*/
|
||||
if (unlikely(query_cache.query_cache_size == 0))
|
||||
{
|
||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
/* See the comment on double-check locking usage above. */
|
||||
if (net->query_cache_query == 0)
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
Query_cache_block *query_block = ((Query_cache_block*)
|
||||
net->query_cache_query);
|
||||
if (query_block) // Test if changed by other thread
|
||||
{
|
||||
DUMP(&query_cache);
|
||||
BLOCK_LOCK_WR(query_block);
|
||||
// The following call will remove the lock on query_block
|
||||
query_cache.free_query(query_block);
|
||||
}
|
||||
net->query_cache_query=0;
|
||||
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
|
||||
STRUCT_LOCK(&query_cache.structure_guard_mutex);
|
||||
|
||||
if (unlikely(query_cache.query_cache_size == 0 ||
|
||||
query_cache.flush_in_progress))
|
||||
{
|
||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
Query_cache_block *query_block= ((Query_cache_block*)
|
||||
net->query_cache_query);
|
||||
if (query_block) // Test if changed by other thread
|
||||
{
|
||||
DUMP(&query_cache);
|
||||
BLOCK_LOCK_WR(query_block);
|
||||
// The following call will remove the lock on query_block
|
||||
query_cache.free_query(query_block);
|
||||
net->query_cache_query= 0;
|
||||
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
|
||||
}
|
||||
|
||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
@ -663,60 +704,65 @@ void query_cache_end_of_result(THD *thd)
|
|||
{
|
||||
DBUG_ENTER("query_cache_end_of_result");
|
||||
|
||||
if (thd->net.query_cache_query != 0) // Quick check on unlocked structure
|
||||
{
|
||||
#ifdef EMBEDDED_LIBRARY
|
||||
query_cache_insert(&thd->net, (char*)thd,
|
||||
emb_count_querycache_size(thd));
|
||||
#endif
|
||||
STRUCT_LOCK(&query_cache.structure_guard_mutex);
|
||||
/*
|
||||
It is very unlikely that following condition is TRUE (it is possible
|
||||
only if other thread is resizing cache), so we check it only after guard
|
||||
mutex lock
|
||||
*/
|
||||
if (unlikely(query_cache.query_cache_size == 0))
|
||||
{
|
||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
/* See the comment on double-check locking usage above. */
|
||||
if (thd->net.query_cache_query == 0)
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
Query_cache_block *query_block = ((Query_cache_block*)
|
||||
thd->net.query_cache_query);
|
||||
if (query_block)
|
||||
{
|
||||
DUMP(&query_cache);
|
||||
BLOCK_LOCK_WR(query_block);
|
||||
Query_cache_query *header = query_block->query();
|
||||
Query_cache_block *last_result_block = header->result()->prev;
|
||||
ulong allign_size = ALIGN_SIZE(last_result_block->used);
|
||||
ulong len = max(query_cache.min_allocation_unit, allign_size);
|
||||
if (last_result_block->length >= query_cache.min_allocation_unit + len)
|
||||
query_cache.split_block(last_result_block,len);
|
||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||
#ifdef EMBEDDED_LIBRARY
|
||||
query_cache_insert(&thd->net, (char*)thd,
|
||||
emb_count_querycache_size(thd));
|
||||
#endif
|
||||
|
||||
STRUCT_LOCK(&query_cache.structure_guard_mutex);
|
||||
|
||||
if (unlikely(query_cache.query_cache_size == 0 ||
|
||||
query_cache.flush_in_progress))
|
||||
{
|
||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
Query_cache_block *query_block= ((Query_cache_block*)
|
||||
thd->net.query_cache_query);
|
||||
if (query_block)
|
||||
{
|
||||
DUMP(&query_cache);
|
||||
BLOCK_LOCK_WR(query_block);
|
||||
Query_cache_query *header= query_block->query();
|
||||
Query_cache_block *last_result_block= header->result()->prev;
|
||||
ulong allign_size= ALIGN_SIZE(last_result_block->used);
|
||||
ulong len= max(query_cache.min_allocation_unit, allign_size);
|
||||
if (last_result_block->length >= query_cache.min_allocation_unit + len)
|
||||
query_cache.split_block(last_result_block,len);
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
if (header->result() == 0)
|
||||
{
|
||||
DBUG_PRINT("error", ("end of data whith no result. query '%s'",
|
||||
header->query()));
|
||||
query_cache.wreck(__LINE__, "");
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
#endif
|
||||
header->found_rows(current_thd->limit_found_rows);
|
||||
header->result()->type = Query_cache_block::RESULT;
|
||||
header->writer(0);
|
||||
BLOCK_UNLOCK_WR(query_block);
|
||||
}
|
||||
else
|
||||
if (header->result() == 0)
|
||||
{
|
||||
// Cache was flushed or resized and query was deleted => do nothing
|
||||
DBUG_PRINT("error", ("end of data whith no result. query '%s'",
|
||||
header->query()));
|
||||
query_cache.wreck(__LINE__, "");
|
||||
|
||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
thd->net.query_cache_query=0;
|
||||
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
|
||||
#endif
|
||||
header->found_rows(current_thd->limit_found_rows);
|
||||
header->result()->type= Query_cache_block::RESULT;
|
||||
header->writer(0);
|
||||
thd->net.query_cache_query= 0;
|
||||
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
|
||||
|
||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||
|
||||
BLOCK_UNLOCK_WR(query_block);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cache was flushed or resized and query was deleted => do nothing
|
||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||
}
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
@ -762,8 +808,7 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
|
|||
query_cache_size_arg));
|
||||
DBUG_ASSERT(initialized);
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size > 0)
|
||||
free_cache();
|
||||
free_cache();
|
||||
query_cache_size= query_cache_size_arg;
|
||||
::query_cache_size= init_cache();
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
|
@ -784,7 +829,15 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
|
|||
TABLE_COUNTER_TYPE local_tables;
|
||||
ulong tot_length;
|
||||
DBUG_ENTER("Query_cache::store_query");
|
||||
if (query_cache_size == 0 || thd->locked_tables)
|
||||
/*
|
||||
Testing 'query_cache_size' without a lock here is safe: the thing
|
||||
we may loose is that the query won't be cached, but we save on
|
||||
mutex locking in the case when query cache is disabled or the
|
||||
query is uncachable.
|
||||
|
||||
See also a note on double-check locking usage above.
|
||||
*/
|
||||
if (thd->locked_tables || query_cache_size == 0)
|
||||
DBUG_VOID_RETURN;
|
||||
uint8 tables_type= 0;
|
||||
|
||||
|
@ -836,9 +889,9 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu",
|
|||
acquiring the query cache mutex.
|
||||
*/
|
||||
ha_release_temporary_latches(thd);
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
|
||||
if (query_cache_size == 0)
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size == 0 || flush_in_progress)
|
||||
{
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
DBUG_VOID_RETURN;
|
||||
|
@ -912,11 +965,12 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu",
|
|||
double_linked_list_simple_include(query_block, &queries_blocks);
|
||||
inserts++;
|
||||
queries_in_cache++;
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
|
||||
net->query_cache_query= (gptr) query_block;
|
||||
header->writer(net);
|
||||
header->tables_type(tables_type);
|
||||
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
|
||||
// init_n_lock make query block locked
|
||||
BLOCK_UNLOCK_WR(query_block);
|
||||
}
|
||||
|
@ -970,12 +1024,16 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
|
|||
Query_cache_query_flags flags;
|
||||
DBUG_ENTER("Query_cache::send_result_to_client");
|
||||
|
||||
if (query_cache_size == 0 || thd->locked_tables ||
|
||||
thd->variables.query_cache_type == 0)
|
||||
goto err;
|
||||
/*
|
||||
Testing 'query_cache_size' without a lock here is safe: the thing
|
||||
we may loose is that the query won't be served from cache, but we
|
||||
save on mutex locking in the case when query cache is disabled.
|
||||
|
||||
/* Check that we haven't forgot to reset the query cache variables */
|
||||
DBUG_ASSERT(thd->net.query_cache_query == 0);
|
||||
See also a note on double-check locking usage above.
|
||||
*/
|
||||
if (thd->locked_tables || thd->variables.query_cache_type == 0 ||
|
||||
query_cache_size == 0)
|
||||
goto err;
|
||||
|
||||
if (!thd->lex->safe_to_cache_query)
|
||||
{
|
||||
|
@ -1011,11 +1069,15 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
|
|||
}
|
||||
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size == 0)
|
||||
if (query_cache_size == 0 || flush_in_progress)
|
||||
{
|
||||
DBUG_PRINT("qcache", ("query cache disabled"));
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
/* Check that we haven't forgot to reset the query cache variables */
|
||||
DBUG_ASSERT(thd->net.query_cache_query == 0);
|
||||
|
||||
Query_cache_block *query_block;
|
||||
|
||||
tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE;
|
||||
|
@ -1241,45 +1303,43 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
|
|||
my_bool using_transactions)
|
||||
{
|
||||
DBUG_ENTER("Query_cache::invalidate (table list)");
|
||||
if (query_cache_size > 0)
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size > 0 && !flush_in_progress)
|
||||
{
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size > 0)
|
||||
{
|
||||
DUMP(this);
|
||||
DUMP(this);
|
||||
|
||||
using_transactions = using_transactions &&
|
||||
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
|
||||
for (; tables_used; tables_used= tables_used->next_local)
|
||||
{
|
||||
DBUG_ASSERT(!using_transactions || tables_used->table!=0);
|
||||
if (tables_used->derived)
|
||||
continue;
|
||||
if (using_transactions &&
|
||||
(tables_used->table->file->table_cache_type() ==
|
||||
HA_CACHE_TBL_TRANSACT))
|
||||
/*
|
||||
Tables_used->table can't be 0 in transaction.
|
||||
Only 'drop' invalidate not opened table, but 'drop'
|
||||
force transaction finish.
|
||||
*/
|
||||
thd->add_changed_table(tables_used->table);
|
||||
else
|
||||
invalidate_table(tables_used);
|
||||
}
|
||||
using_transactions= using_transactions &&
|
||||
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
|
||||
for (; tables_used; tables_used= tables_used->next_local)
|
||||
{
|
||||
DBUG_ASSERT(!using_transactions || tables_used->table!=0);
|
||||
if (tables_used->derived)
|
||||
continue;
|
||||
if (using_transactions &&
|
||||
(tables_used->table->file->table_cache_type() ==
|
||||
HA_CACHE_TBL_TRANSACT))
|
||||
/*
|
||||
Tables_used->table can't be 0 in transaction.
|
||||
Only 'drop' invalidate not opened table, but 'drop'
|
||||
force transaction finish.
|
||||
*/
|
||||
thd->add_changed_table(tables_used->table);
|
||||
else
|
||||
invalidate_table(tables_used);
|
||||
}
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
}
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
|
||||
{
|
||||
DBUG_ENTER("Query_cache::invalidate (changed table list)");
|
||||
if (query_cache_size > 0 && tables_used)
|
||||
if (tables_used)
|
||||
{
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size > 0)
|
||||
if (query_cache_size > 0 && !flush_in_progress)
|
||||
{
|
||||
DUMP(this);
|
||||
for (; tables_used; tables_used= tables_used->next)
|
||||
|
@ -1309,10 +1369,10 @@ void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
|
|||
void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used)
|
||||
{
|
||||
DBUG_ENTER("Query_cache::invalidate_locked_for_write");
|
||||
if (query_cache_size > 0 && tables_used)
|
||||
if (tables_used)
|
||||
{
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size > 0)
|
||||
if (query_cache_size > 0 && !flush_in_progress)
|
||||
{
|
||||
DUMP(this);
|
||||
for (; tables_used; tables_used= tables_used->next_local)
|
||||
|
@ -1336,21 +1396,19 @@ void Query_cache::invalidate(THD *thd, TABLE *table,
|
|||
{
|
||||
DBUG_ENTER("Query_cache::invalidate (table)");
|
||||
|
||||
if (query_cache_size > 0)
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size > 0 && !flush_in_progress)
|
||||
{
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size > 0)
|
||||
{
|
||||
using_transactions = using_transactions &&
|
||||
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
|
||||
if (using_transactions &&
|
||||
(table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT))
|
||||
thd->add_changed_table(table);
|
||||
else
|
||||
invalidate_table(table);
|
||||
}
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
using_transactions= using_transactions &&
|
||||
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
|
||||
if (using_transactions &&
|
||||
(table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT))
|
||||
thd->add_changed_table(table);
|
||||
else
|
||||
invalidate_table(table);
|
||||
}
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
@ -1359,20 +1417,18 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
|
|||
{
|
||||
DBUG_ENTER("Query_cache::invalidate (key)");
|
||||
|
||||
if (query_cache_size > 0)
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size > 0 && !flush_in_progress)
|
||||
{
|
||||
using_transactions = using_transactions &&
|
||||
using_transactions= using_transactions &&
|
||||
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
|
||||
if (using_transactions) // used for innodb => has_transactions() is TRUE
|
||||
thd->add_changed_table(key, key_length);
|
||||
else
|
||||
{
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size > 0)
|
||||
invalidate_table((byte*)key, key_length);
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
}
|
||||
invalidate_table((byte*)key, key_length);
|
||||
}
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
@ -1383,38 +1439,36 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
|
|||
void Query_cache::invalidate(char *db)
|
||||
{
|
||||
DBUG_ENTER("Query_cache::invalidate (db)");
|
||||
if (query_cache_size > 0)
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size > 0 && !flush_in_progress)
|
||||
{
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size > 0)
|
||||
{
|
||||
DUMP(this);
|
||||
DUMP(this);
|
||||
restart_search:
|
||||
if (tables_blocks)
|
||||
if (tables_blocks)
|
||||
{
|
||||
Query_cache_block *curr= tables_blocks;
|
||||
Query_cache_block *next;
|
||||
do
|
||||
{
|
||||
Query_cache_block *curr= tables_blocks;
|
||||
Query_cache_block *next;
|
||||
do
|
||||
{
|
||||
next= curr->next;
|
||||
if (strcmp(db, (char*)(curr->table()->db())) == 0)
|
||||
invalidate_table(curr);
|
||||
/*
|
||||
invalidate_table can freed block on which point 'next' (if
|
||||
table of this block used only in queries which was deleted
|
||||
by invalidate_table). As far as we do not allocate new blocks
|
||||
and mark all headers of freed blocks as 'FREE' (even if they are
|
||||
merged with other blocks) we can just test type of block
|
||||
to be sure that block is not deleted
|
||||
*/
|
||||
if (next->type == Query_cache_block::FREE)
|
||||
goto restart_search;
|
||||
curr= next;
|
||||
} while (curr != tables_blocks);
|
||||
}
|
||||
next= curr->next;
|
||||
if (strcmp(db, (char*)(curr->table()->db())) == 0)
|
||||
invalidate_table(curr);
|
||||
/*
|
||||
invalidate_table can freed block on which point 'next' (if
|
||||
table of this block used only in queries which was deleted
|
||||
by invalidate_table). As far as we do not allocate new blocks
|
||||
and mark all headers of freed blocks as 'FREE' (even if they are
|
||||
merged with other blocks) we can just test type of block
|
||||
to be sure that block is not deleted
|
||||
*/
|
||||
if (next->type == Query_cache_block::FREE)
|
||||
goto restart_search;
|
||||
curr= next;
|
||||
} while (curr != tables_blocks);
|
||||
}
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
}
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
@ -1422,23 +1476,22 @@ void Query_cache::invalidate(char *db)
|
|||
void Query_cache::invalidate_by_MyISAM_filename(const char *filename)
|
||||
{
|
||||
DBUG_ENTER("Query_cache::invalidate_by_MyISAM_filename");
|
||||
if (query_cache_size > 0)
|
||||
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size > 0 && !flush_in_progress)
|
||||
{
|
||||
/* Calculate the key outside the lock to make the lock shorter */
|
||||
char key[MAX_DBKEY_LENGTH];
|
||||
uint32 db_length;
|
||||
uint key_length= filename_2_table_key(key, filename, &db_length);
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (query_cache_size > 0) // Safety if cache removed
|
||||
{
|
||||
Query_cache_block *table_block;
|
||||
if ((table_block = (Query_cache_block*) hash_search(&tables,
|
||||
(byte*) key,
|
||||
key_length)))
|
||||
invalidate_table(table_block);
|
||||
}
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
Query_cache_block *table_block;
|
||||
if ((table_block = (Query_cache_block*) hash_search(&tables,
|
||||
(byte*) key,
|
||||
key_length)))
|
||||
invalidate_table(table_block);
|
||||
}
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
@ -1483,7 +1536,12 @@ void Query_cache::destroy()
|
|||
}
|
||||
else
|
||||
{
|
||||
/* Underlying code expects the lock. */
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
free_cache();
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
|
||||
pthread_cond_destroy(&COND_flush_finished);
|
||||
pthread_mutex_destroy(&structure_guard_mutex);
|
||||
initialized = 0;
|
||||
}
|
||||
|
@ -1499,6 +1557,8 @@ void Query_cache::init()
|
|||
{
|
||||
DBUG_ENTER("Query_cache::init");
|
||||
pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST);
|
||||
pthread_cond_init(&COND_flush_finished, NULL);
|
||||
flush_in_progress= FALSE;
|
||||
initialized = 1;
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
@ -1694,6 +1754,17 @@ void Query_cache::make_disabled()
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
free_cache() - free all resources allocated by the cache.
|
||||
|
||||
SYNOPSIS
|
||||
free_cache()
|
||||
|
||||
DESCRIPTION
|
||||
This function frees all resources allocated by the cache. You
|
||||
have to call init_cache() before using the cache again.
|
||||
*/
|
||||
|
||||
void Query_cache::free_cache()
|
||||
{
|
||||
DBUG_ENTER("Query_cache::free_cache");
|
||||
|
@ -1728,17 +1799,51 @@ void Query_cache::free_cache()
|
|||
Free block data
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
The following assumes we have a lock on the cache
|
||||
flush_cache() - flush the cache.
|
||||
|
||||
SYNOPSIS
|
||||
flush_cache()
|
||||
|
||||
DESCRIPTION
|
||||
This function will flush cache contents. It assumes we have
|
||||
'structure_guard_mutex' locked. The function sets the
|
||||
flush_in_progress flag and releases the lock, so other threads may
|
||||
proceed skipping the cache as if it is disabled. Concurrent
|
||||
flushes are performed in turn.
|
||||
*/
|
||||
|
||||
void Query_cache::flush_cache()
|
||||
{
|
||||
/*
|
||||
If there is flush in progress, wait for it to finish, and then do
|
||||
our flush. This is necessary because something could be added to
|
||||
the cache before we acquire the lock again, and some code (like
|
||||
Query_cache::free_cache()) depends on the fact that after the
|
||||
flush the cache is empty.
|
||||
*/
|
||||
while (flush_in_progress)
|
||||
pthread_cond_wait(&COND_flush_finished, &structure_guard_mutex);
|
||||
|
||||
/*
|
||||
Setting 'flush_in_progress' will prevent other threads from using
|
||||
the cache while we are in the middle of the flush, and we release
|
||||
the lock so that other threads won't block.
|
||||
*/
|
||||
flush_in_progress= TRUE;
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
|
||||
my_hash_reset(&queries);
|
||||
while (queries_blocks != 0)
|
||||
{
|
||||
BLOCK_LOCK_WR(queries_blocks);
|
||||
free_query(queries_blocks);
|
||||
free_query_internal(queries_blocks);
|
||||
}
|
||||
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
flush_in_progress= FALSE;
|
||||
pthread_cond_signal(&COND_flush_finished);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1784,36 +1889,48 @@ my_bool Query_cache::free_old_query()
|
|||
DBUG_RETURN(1); // Nothing to remove
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Free query from query cache.
|
||||
query_block must be locked for writing.
|
||||
This function will remove (and destroy) the lock for the query.
|
||||
free_query_internal() - free query from query cache.
|
||||
|
||||
SYNOPSIS
|
||||
free_query_internal()
|
||||
query_block Query_cache_block representing the query
|
||||
|
||||
DESCRIPTION
|
||||
This function will remove the query from a cache, and place its
|
||||
memory blocks to the list of free blocks. 'query_block' must be
|
||||
locked for writing, this function will release (and destroy) this
|
||||
lock.
|
||||
|
||||
NOTE
|
||||
'query_block' should be removed from 'queries' hash _before_
|
||||
calling this method, as the lock will be destroyed here.
|
||||
*/
|
||||
|
||||
void Query_cache::free_query(Query_cache_block *query_block)
|
||||
void Query_cache::free_query_internal(Query_cache_block *query_block)
|
||||
{
|
||||
DBUG_ENTER("Query_cache::free_query");
|
||||
DBUG_ENTER("Query_cache::free_query_internal");
|
||||
DBUG_PRINT("qcache", ("free query 0x%lx %lu bytes result",
|
||||
(ulong) query_block,
|
||||
query_block->query()->length() ));
|
||||
|
||||
queries_in_cache--;
|
||||
hash_delete(&queries,(byte *) query_block);
|
||||
|
||||
Query_cache_query *query = query_block->query();
|
||||
Query_cache_query *query= query_block->query();
|
||||
|
||||
if (query->writer() != 0)
|
||||
{
|
||||
/* Tell MySQL that this query should not be cached anymore */
|
||||
query->writer()->query_cache_query = 0;
|
||||
query->writer()->query_cache_query= 0;
|
||||
query->writer(0);
|
||||
}
|
||||
double_linked_list_exclude(query_block, &queries_blocks);
|
||||
Query_cache_block_table *table=query_block->table(0);
|
||||
Query_cache_block_table *table= query_block->table(0);
|
||||
|
||||
for (TABLE_COUNTER_TYPE i=0; i < query_block->n_tables; i++)
|
||||
for (TABLE_COUNTER_TYPE i= 0; i < query_block->n_tables; i++)
|
||||
unlink_table(table++);
|
||||
Query_cache_block *result_block = query->result();
|
||||
Query_cache_block *result_block= query->result();
|
||||
|
||||
/*
|
||||
The following is true when query destruction was called and no results
|
||||
|
@ -1827,11 +1944,11 @@ void Query_cache::free_query(Query_cache_block *query_block)
|
|||
refused++;
|
||||
inserts--;
|
||||
}
|
||||
Query_cache_block *block = result_block;
|
||||
Query_cache_block *block= result_block;
|
||||
do
|
||||
{
|
||||
Query_cache_block *current = block;
|
||||
block = block->next;
|
||||
Query_cache_block *current= block;
|
||||
block= block->next;
|
||||
free_memory_block(current);
|
||||
} while (block != result_block);
|
||||
}
|
||||
|
@ -1848,6 +1965,32 @@ void Query_cache::free_query(Query_cache_block *query_block)
|
|||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
free_query() - free query from query cache.
|
||||
|
||||
SYNOPSIS
|
||||
free_query()
|
||||
query_block Query_cache_block representing the query
|
||||
|
||||
DESCRIPTION
|
||||
This function will remove 'query_block' from 'queries' hash, and
|
||||
then call free_query_internal(), which see.
|
||||
*/
|
||||
|
||||
void Query_cache::free_query(Query_cache_block *query_block)
|
||||
{
|
||||
DBUG_ENTER("Query_cache::free_query");
|
||||
DBUG_PRINT("qcache", ("free query 0x%lx %lu bytes result",
|
||||
(ulong) query_block,
|
||||
query_block->query()->length() ));
|
||||
|
||||
hash_delete(&queries,(byte *) query_block);
|
||||
free_query_internal(query_block);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
Query data creation
|
||||
*****************************************************************************/
|
||||
|
@ -2431,12 +2574,8 @@ Query_cache::allocate_block(ulong len, my_bool not_less, ulong min,
|
|||
if (!under_guard)
|
||||
{
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
/*
|
||||
It is very unlikely that following condition is TRUE (it is possible
|
||||
only if other thread is resizing cache), so we check it only after
|
||||
guard mutex lock
|
||||
*/
|
||||
if (unlikely(query_cache.query_cache_size == 0))
|
||||
|
||||
if (unlikely(query_cache.query_cache_size == 0 || flush_in_progress))
|
||||
{
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
DBUG_RETURN(0);
|
||||
|
@ -2892,11 +3031,9 @@ static TABLE_COUNTER_TYPE process_and_count_tables(TABLE_LIST *tables_used,
|
|||
(query without tables are not cached)
|
||||
*/
|
||||
|
||||
TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len,
|
||||
char *query,
|
||||
LEX *lex,
|
||||
TABLE_LIST *tables_used,
|
||||
uint8 *tables_type)
|
||||
TABLE_COUNTER_TYPE
|
||||
Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex,
|
||||
TABLE_LIST *tables_used, uint8 *tables_type)
|
||||
{
|
||||
TABLE_COUNTER_TYPE table_count;
|
||||
DBUG_ENTER("Query_cache::is_cacheable");
|
||||
|
@ -2979,13 +3116,10 @@ my_bool Query_cache::ask_handler_allowance(THD *thd,
|
|||
void Query_cache::pack_cache()
|
||||
{
|
||||
DBUG_ENTER("Query_cache::pack_cache");
|
||||
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
/*
|
||||
It is very unlikely that following condition is TRUE (it is possible
|
||||
only if other thread is resizing cache), so we check it only after
|
||||
guard mutex lock
|
||||
*/
|
||||
if (unlikely(query_cache_size == 0))
|
||||
|
||||
if (unlikely(query_cache_size == 0 || flush_in_progress))
|
||||
{
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
DBUG_VOID_RETURN;
|
||||
|
@ -3300,7 +3434,7 @@ my_bool Query_cache::join_results(ulong join_limit)
|
|||
DBUG_ENTER("Query_cache::join_results");
|
||||
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
if (queries_blocks != 0)
|
||||
if (queries_blocks != 0 && !flush_in_progress)
|
||||
{
|
||||
DBUG_ASSERT(query_cache_size > 0);
|
||||
Query_cache_block *block = queries_blocks;
|
||||
|
@ -3587,31 +3721,23 @@ void Query_cache::tables_dump()
|
|||
}
|
||||
|
||||
|
||||
my_bool Query_cache::check_integrity(bool not_locked)
|
||||
my_bool Query_cache::check_integrity(bool locked)
|
||||
{
|
||||
my_bool result = 0;
|
||||
uint i;
|
||||
DBUG_ENTER("check_integrity");
|
||||
|
||||
if (query_cache_size == 0)
|
||||
if (!locked)
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
|
||||
if (unlikely(query_cache_size == 0 || flush_in_progress))
|
||||
{
|
||||
if (!locked)
|
||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||
|
||||
DBUG_PRINT("qcache", ("Query Cache not initialized"));
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
if (!not_locked)
|
||||
{
|
||||
STRUCT_LOCK(&structure_guard_mutex);
|
||||
/*
|
||||
It is very unlikely that following condition is TRUE (it is possible
|
||||
only if other thread is resizing cache), so we check it only after
|
||||
guard mutex lock
|
||||
*/
|
||||
if (unlikely(query_cache_size == 0))
|
||||
{
|
||||
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (hash_check(&queries))
|
||||
{
|
||||
|
@ -3856,7 +3982,7 @@ my_bool Query_cache::check_integrity(bool not_locked)
|
|||
}
|
||||
}
|
||||
DBUG_ASSERT(result == 0);
|
||||
if (!not_locked)
|
||||
if (!locked)
|
||||
STRUCT_UNLOCK(&structure_guard_mutex);
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
|
|
|
@ -195,7 +195,6 @@ extern "C"
|
|||
byte *query_cache_table_get_key(const byte *record, uint *length,
|
||||
my_bool not_used);
|
||||
}
|
||||
void query_cache_insert(NET *thd, const char *packet, ulong length);
|
||||
extern "C" void query_cache_invalidate_by_MyISAM_filename(const char* filename);
|
||||
|
||||
|
||||
|
@ -241,6 +240,12 @@ public:
|
|||
ulong free_memory, queries_in_cache, hits, inserts, refused,
|
||||
free_memory_blocks, total_blocks, lowmem_prunes;
|
||||
|
||||
private:
|
||||
pthread_cond_t COND_flush_finished;
|
||||
bool flush_in_progress;
|
||||
|
||||
void free_query_internal(Query_cache_block *point);
|
||||
|
||||
protected:
|
||||
/*
|
||||
The following mutex is locked when searching or changing global
|
||||
|
@ -249,6 +254,12 @@ protected:
|
|||
LOCK SEQUENCE (to prevent deadlocks):
|
||||
1. structure_guard_mutex
|
||||
2. query block (for operation inside query (query block/results))
|
||||
|
||||
Thread doing cache flush releases the mutex once it sets
|
||||
flush_in_progress flag, so other threads may bypass the cache as
|
||||
if it is disabled, not waiting for reset to finish. The exception
|
||||
is other threads that were going to do cache flush---they'll wait
|
||||
till the end of a flush operation.
|
||||
*/
|
||||
pthread_mutex_t structure_guard_mutex;
|
||||
byte *cache; // cache memory
|
||||
|
@ -358,6 +369,7 @@ protected:
|
|||
If query is cacheable return number tables in query
|
||||
(query without tables not cached)
|
||||
*/
|
||||
static
|
||||
TABLE_COUNTER_TYPE is_cacheable(THD *thd, uint32 query_len, char *query,
|
||||
LEX *lex, TABLE_LIST *tables_used,
|
||||
uint8 *tables_type);
|
||||
|
@ -410,6 +422,7 @@ protected:
|
|||
|
||||
void destroy();
|
||||
|
||||
friend void query_cache_init_query(NET *net);
|
||||
friend void query_cache_insert(NET *net, const char *packet, ulong length);
|
||||
friend void query_cache_end_of_result(THD *thd);
|
||||
friend void query_cache_abort(NET *net);
|
||||
|
@ -435,6 +448,8 @@ protected:
|
|||
|
||||
extern Query_cache query_cache;
|
||||
extern TYPELIB query_cache_type_typelib;
|
||||
void query_cache_init_query(NET *net);
|
||||
void query_cache_insert(NET *net, const char *packet, ulong length);
|
||||
void query_cache_end_of_result(THD *thd);
|
||||
void query_cache_abort(NET *net);
|
||||
|
||||
|
|
|
@ -224,7 +224,7 @@ THD::THD()
|
|||
#endif
|
||||
client_capabilities= 0; // minimalistic client
|
||||
net.last_error[0]=0; // If error on boot
|
||||
net.query_cache_query=0; // If error on boot
|
||||
query_cache_init_query(&net); // If error on boot
|
||||
ull=0;
|
||||
system_thread= cleanup_done= abort_on_warning= no_warnings_for_error= 0;
|
||||
peer_port= 0; // For SHOW PROCESSLIST
|
||||
|
|
|
@ -139,14 +139,8 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
|
|||
}
|
||||
|
||||
if (thd->spcont &&
|
||||
thd->spcont->find_handler(code,
|
||||
((int) level >=
|
||||
(int) MYSQL_ERROR::WARN_LEVEL_WARN &&
|
||||
thd->really_abort_on_warning()) ?
|
||||
MYSQL_ERROR::WARN_LEVEL_ERROR : level))
|
||||
thd->spcont->handle_error(code, level, thd))
|
||||
{
|
||||
if (! thd->spcont->found_handler_here())
|
||||
thd->net.report_error= 1; /* Make "select" abort correctly */
|
||||
DBUG_RETURN(NULL);
|
||||
}
|
||||
query_cache_abort(&thd->net);
|
||||
|
|
|
@ -5787,7 +5787,6 @@ void mysql_init_multi_delete(LEX *lex)
|
|||
lex->query_tables_last= &lex->query_tables;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
When you modify mysql_parse(), you may need to mofify
|
||||
mysql_test_parse_for_slave() in this same file.
|
||||
|
@ -5796,6 +5795,9 @@ void mysql_init_multi_delete(LEX *lex)
|
|||
void mysql_parse(THD *thd, char *inBuf, uint length)
|
||||
{
|
||||
DBUG_ENTER("mysql_parse");
|
||||
|
||||
DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););
|
||||
|
||||
mysql_init_query(thd, (uchar*) inBuf, length);
|
||||
if (query_cache_send_result_to_client(thd, inBuf, length) <= 0)
|
||||
{
|
||||
|
@ -6690,11 +6692,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
|
|||
select_errors=0; /* Write if more errors */
|
||||
bool tmp_write_to_binlog= 1;
|
||||
|
||||
if (thd && thd->in_sub_stmt)
|
||||
{
|
||||
my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "FLUSH");
|
||||
return 1;
|
||||
}
|
||||
DBUG_ASSERT(!thd || !thd->in_sub_stmt);
|
||||
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
if (options & REFRESH_GRANT)
|
||||
|
@ -7542,16 +7540,34 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
|
|||
|
||||
LEX_USER *get_current_user(THD *thd, LEX_USER *user)
|
||||
{
|
||||
LEX_USER *curr_user;
|
||||
if (!user->user.str) // current_user
|
||||
{
|
||||
if (!(curr_user= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
|
||||
{
|
||||
my_error(ER_OUTOFMEMORY, MYF(0), sizeof(LEX_USER));
|
||||
return 0;
|
||||
}
|
||||
get_default_definer(thd, curr_user);
|
||||
return curr_user;
|
||||
}
|
||||
return create_default_definer(thd);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Check that length of a string does not exceed some limit.
|
||||
|
||||
SYNOPSIS
|
||||
check_string_length()
|
||||
str string to be checked
|
||||
err_msg error message to be displayed if the string is too long
|
||||
max_length max length
|
||||
|
||||
RETURN
|
||||
FALSE the passed string is not longer than max_length
|
||||
TRUE the passed string is longer than max_length
|
||||
*/
|
||||
|
||||
bool check_string_length(LEX_STRING *str, const char *err_msg,
|
||||
uint max_length)
|
||||
{
|
||||
if (str->length <= max_length)
|
||||
return FALSE;
|
||||
|
||||
my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_length);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
@ -158,11 +158,13 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||
{
|
||||
TABLE *table;
|
||||
bool result= TRUE;
|
||||
LEX_STRING definer_user;
|
||||
LEX_STRING definer_host;
|
||||
String stmt_query;
|
||||
|
||||
DBUG_ENTER("mysql_create_or_drop_trigger");
|
||||
|
||||
/* Charset of the buffer for statement must be system one. */
|
||||
stmt_query.set_charset(system_charset_info);
|
||||
|
||||
/*
|
||||
QQ: This function could be merged in mysql_alter_table() function
|
||||
But do we want this ?
|
||||
|
@ -264,8 +266,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||
}
|
||||
|
||||
result= (create ?
|
||||
table->triggers->create_trigger(thd, tables, &definer_user, &definer_host):
|
||||
table->triggers->drop_trigger(thd, tables));
|
||||
table->triggers->create_trigger(thd, tables, &stmt_query):
|
||||
table->triggers->drop_trigger(thd, tables, &stmt_query));
|
||||
|
||||
end:
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
|
@ -277,32 +279,9 @@ end:
|
|||
{
|
||||
thd->clear_error();
|
||||
|
||||
String log_query(thd->query, thd->query_length, system_charset_info);
|
||||
|
||||
if (create)
|
||||
{
|
||||
log_query.set((char *) 0, 0, system_charset_info); /* reset log_query */
|
||||
|
||||
log_query.append(STRING_WITH_LEN("CREATE "));
|
||||
|
||||
if (definer_user.str && definer_host.str)
|
||||
{
|
||||
/*
|
||||
Append definer-clause if the trigger is SUID (a usual trigger in
|
||||
new MySQL versions).
|
||||
*/
|
||||
|
||||
append_definer(thd, &log_query, &definer_user, &definer_host);
|
||||
}
|
||||
|
||||
log_query.append(thd->lex->stmt_definition_begin,
|
||||
(char *)thd->lex->sphead->m_body_begin -
|
||||
thd->lex->stmt_definition_begin +
|
||||
thd->lex->sphead->m_body.length);
|
||||
}
|
||||
|
||||
/* Such a statement can always go directly to binlog, no trans cache. */
|
||||
Query_log_event qinfo(thd, log_query.ptr(), log_query.length(), 0, FALSE);
|
||||
Query_log_event qinfo(thd, stmt_query.ptr(), stmt_query.length(), 0,
|
||||
FALSE);
|
||||
mysql_bin_log.write(&qinfo);
|
||||
}
|
||||
|
||||
|
@ -322,22 +301,8 @@ end:
|
|||
LEX)
|
||||
tables - table list containing one open table for which the
|
||||
trigger is created.
|
||||
definer_user - [out] after a call it points to 0-terminated string or
|
||||
contains the NULL-string:
|
||||
- 0-terminated is returned if the trigger is SUID. The
|
||||
string contains user name part of the actual trigger
|
||||
definer.
|
||||
- NULL-string is returned if the trigger is non-SUID.
|
||||
Anyway, the caller is responsible to provide memory for
|
||||
storing LEX_STRING object.
|
||||
definer_host - [out] after a call it points to 0-terminated string or
|
||||
contains the NULL-string:
|
||||
- 0-terminated string is returned if the trigger is
|
||||
SUID. The string contains host name part of the
|
||||
actual trigger definer.
|
||||
- NULL-string is returned if the trigger is non-SUID.
|
||||
Anyway, the caller is responsible to provide memory for
|
||||
storing LEX_STRING object.
|
||||
stmt_query - [OUT] after successful return, this string contains
|
||||
well-formed statement for creation this trigger.
|
||||
|
||||
NOTE
|
||||
- Assumes that trigger name is fully qualified.
|
||||
|
@ -352,8 +317,7 @@ end:
|
|||
True - error
|
||||
*/
|
||||
bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
||||
LEX_STRING *definer_user,
|
||||
LEX_STRING *definer_host)
|
||||
String *stmt_query)
|
||||
{
|
||||
LEX *lex= thd->lex;
|
||||
TABLE *table= tables->table;
|
||||
|
@ -361,6 +325,8 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
|||
trigname_path[FN_REFLEN];
|
||||
LEX_STRING dir, file, trigname_file;
|
||||
LEX_STRING *trg_def, *name;
|
||||
LEX_STRING definer_user;
|
||||
LEX_STRING definer_host;
|
||||
ulonglong *trg_sql_mode;
|
||||
char trg_definer_holder[USER_HOST_BUFF_SIZE];
|
||||
LEX_STRING *trg_definer;
|
||||
|
@ -508,8 +474,6 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
|||
definers_list.push_back(trg_definer, &table->mem_root))
|
||||
goto err_with_cleanup;
|
||||
|
||||
trg_def->str= thd->query;
|
||||
trg_def->length= thd->query_length;
|
||||
*trg_sql_mode= thd->variables.sql_mode;
|
||||
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
|
@ -529,27 +493,54 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
|||
{
|
||||
/* SUID trigger. */
|
||||
|
||||
*definer_user= lex->definer->user;
|
||||
*definer_host= lex->definer->host;
|
||||
definer_user= lex->definer->user;
|
||||
definer_host= lex->definer->host;
|
||||
|
||||
trg_definer->str= trg_definer_holder;
|
||||
trg_definer->length= strxmov(trg_definer->str, definer_user->str, "@",
|
||||
definer_host->str, NullS) - trg_definer->str;
|
||||
trg_definer->length= strxmov(trg_definer->str, definer_user.str, "@",
|
||||
definer_host.str, NullS) - trg_definer->str;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* non-SUID trigger. */
|
||||
|
||||
definer_user->str= 0;
|
||||
definer_user->length= 0;
|
||||
definer_user.str= 0;
|
||||
definer_user.length= 0;
|
||||
|
||||
definer_host->str= 0;
|
||||
definer_host->length= 0;
|
||||
definer_host.str= 0;
|
||||
definer_host.length= 0;
|
||||
|
||||
trg_definer->str= (char*) "";
|
||||
trg_definer->length= 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Create well-formed trigger definition query. Original query is not
|
||||
appropriated, because definer-clause can be not truncated.
|
||||
*/
|
||||
|
||||
stmt_query->append(STRING_WITH_LEN("CREATE "));
|
||||
|
||||
if (trg_definer)
|
||||
{
|
||||
/*
|
||||
Append definer-clause if the trigger is SUID (a usual trigger in
|
||||
new MySQL versions).
|
||||
*/
|
||||
|
||||
append_definer(thd, stmt_query, &definer_user, &definer_host);
|
||||
}
|
||||
|
||||
stmt_query->append(thd->lex->stmt_definition_begin,
|
||||
(char *) thd->lex->sphead->m_body_begin -
|
||||
thd->lex->stmt_definition_begin +
|
||||
thd->lex->sphead->m_body.length);
|
||||
|
||||
trg_def->str= stmt_query->c_ptr();
|
||||
trg_def->length= stmt_query->length();
|
||||
|
||||
/* Create trigger definition file. */
|
||||
|
||||
if (!sql_create_definition_file(&dir, &file, &triggers_file_type,
|
||||
(gptr)this, triggers_file_parameters, 0))
|
||||
return 0;
|
||||
|
@ -647,15 +638,19 @@ static bool save_trigger_file(Table_triggers_list *triggers, const char *db,
|
|||
|
||||
SYNOPSIS
|
||||
drop_trigger()
|
||||
thd - current thread context (including trigger definition in LEX)
|
||||
tables - table list containing one open table for which trigger is
|
||||
dropped.
|
||||
thd - current thread context
|
||||
(including trigger definition in LEX)
|
||||
tables - table list containing one open table for which trigger
|
||||
is dropped.
|
||||
stmt_query - [OUT] after successful return, this string contains
|
||||
well-formed statement for creation this trigger.
|
||||
|
||||
RETURN VALUE
|
||||
False - success
|
||||
True - error
|
||||
*/
|
||||
bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables)
|
||||
bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables,
|
||||
String *stmt_query)
|
||||
{
|
||||
LEX *lex= thd->lex;
|
||||
LEX_STRING *name;
|
||||
|
@ -665,6 +660,8 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables)
|
|||
List_iterator<LEX_STRING> it_definer(definers_list);
|
||||
char path[FN_REFLEN];
|
||||
|
||||
stmt_query->append(thd->query, thd->query_length);
|
||||
|
||||
while ((name= it_name++))
|
||||
{
|
||||
it_def++;
|
||||
|
|
|
@ -92,10 +92,8 @@ public:
|
|||
}
|
||||
~Table_triggers_list();
|
||||
|
||||
bool create_trigger(THD *thd, TABLE_LIST *table,
|
||||
LEX_STRING *definer_user,
|
||||
LEX_STRING *definer_host);
|
||||
bool drop_trigger(THD *thd, TABLE_LIST *table);
|
||||
bool create_trigger(THD *thd, TABLE_LIST *table, String *stmt_query);
|
||||
bool drop_trigger(THD *thd, TABLE_LIST *table, String *stmt_query);
|
||||
bool process_triggers(THD *thd, trg_event_type event,
|
||||
trg_action_time_type time_type,
|
||||
bool old_row_is_record1);
|
||||
|
|
|
@ -1060,6 +1060,31 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
|
|||
table->next_global= view_tables;
|
||||
}
|
||||
|
||||
bool view_is_mergeable= (table->algorithm != VIEW_ALGORITHM_TMPTABLE &&
|
||||
lex->can_be_merged());
|
||||
TABLE_LIST *view_main_select_tables;
|
||||
if (view_is_mergeable)
|
||||
{
|
||||
/*
|
||||
Currently 'view_main_select_tables' differs from 'view_tables'
|
||||
only then view has CONVERT_TZ() function in its select list.
|
||||
This may change in future, for example if we enable merging of
|
||||
views with subqueries in select list.
|
||||
*/
|
||||
view_main_select_tables=
|
||||
(TABLE_LIST*)lex->select_lex.table_list.first;
|
||||
|
||||
/*
|
||||
Let us set proper lock type for tables of the view's main
|
||||
select since we may want to perform update or insert on
|
||||
view. This won't work for view containing union. But this is
|
||||
ok since we don't allow insert and update on such views
|
||||
anyway.
|
||||
*/
|
||||
for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local)
|
||||
tbl->lock_type= table->lock_type;
|
||||
}
|
||||
|
||||
/*
|
||||
If we are opening this view as part of implicit LOCK TABLES, then
|
||||
this view serves as simple placeholder and we should not continue
|
||||
|
@ -1114,43 +1139,26 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
|
|||
- VIEW SELECT allow merging
|
||||
- VIEW used in subquery or command support MERGE algorithm
|
||||
*/
|
||||
if (table->algorithm != VIEW_ALGORITHM_TMPTABLE &&
|
||||
lex->can_be_merged() &&
|
||||
if (view_is_mergeable &&
|
||||
(table->select_lex->master_unit() != &old_lex->unit ||
|
||||
old_lex->can_use_merged()) &&
|
||||
!old_lex->can_not_use_merged())
|
||||
{
|
||||
List_iterator_fast<TABLE_LIST> ti(view_select->top_join_list);
|
||||
/*
|
||||
Currently 'view_main_select_tables' differs from 'view_tables'
|
||||
only then view has CONVERT_TZ() function in its select list.
|
||||
This may change in future, for example if we enable merging
|
||||
of views with subqueries in select list.
|
||||
*/
|
||||
TABLE_LIST *view_main_select_tables=
|
||||
(TABLE_LIST*)lex->select_lex.table_list.first;
|
||||
/* lex should contain at least one table */
|
||||
DBUG_ASSERT(view_main_select_tables != 0);
|
||||
|
||||
List_iterator_fast<TABLE_LIST> ti(view_select->top_join_list);
|
||||
|
||||
table->effective_algorithm= VIEW_ALGORITHM_MERGE;
|
||||
DBUG_PRINT("info", ("algorithm: MERGE"));
|
||||
table->updatable= (table->updatable_view != 0);
|
||||
table->effective_with_check=
|
||||
old_lex->get_effective_with_check(table);
|
||||
table->merge_underlying_list= view_main_select_tables;
|
||||
/*
|
||||
Let us set proper lock type for tables of the view's main select
|
||||
since we may want to perform update or insert on view. This won't
|
||||
work for view containing union. But this is ok since we don't
|
||||
allow insert and update on such views anyway.
|
||||
|
||||
Also we fill correct wanted privileges.
|
||||
*/
|
||||
for (tbl= table->merge_underlying_list; tbl; tbl= tbl->next_local)
|
||||
{
|
||||
tbl->lock_type= table->lock_type;
|
||||
/* Fill correct wanted privileges. */
|
||||
for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local)
|
||||
tbl->grant.want_privilege= top_view->grant.orig_want_privilege;
|
||||
}
|
||||
|
||||
/* prepare view context */
|
||||
lex->select_lex.context.resolve_in_table_list_only(view_main_select_tables);
|
||||
|
|
|
@ -68,6 +68,34 @@ inline Item *is_truth_value(Item *A, bool v1, bool v2)
|
|||
new Item_int((char *) (v1 ? "FALSE" : "TRUE"),!v1, 1));
|
||||
}
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
#define YYDEBUG 1
|
||||
#else
|
||||
#define YYDEBUG 0
|
||||
#endif
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
void turn_parser_debug_on()
|
||||
{
|
||||
/*
|
||||
MYSQLdebug is in sql/sql_yacc.cc, in bison generated code.
|
||||
Turning this option on is **VERY** verbose, and should be
|
||||
used when investigating a syntax error problem only.
|
||||
|
||||
The syntax to run with bison traces is as follows :
|
||||
- Starting a server manually :
|
||||
mysqld --debug="d,parser_debug" ...
|
||||
- Running a test :
|
||||
mysql-test-run.pl --mysqld="--debug=d,parser_debug" ...
|
||||
|
||||
The result will be in the process stderr (var/log/master.err)
|
||||
*/
|
||||
|
||||
extern int yydebug;
|
||||
yydebug= 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
%}
|
||||
%union {
|
||||
int num;
|
||||
|
@ -6759,17 +6787,8 @@ flush:
|
|||
FLUSH_SYM opt_no_write_to_binlog
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
if (lex->sphead && lex->sphead->m_type != TYPE_ENUM_PROCEDURE)
|
||||
{
|
||||
/*
|
||||
Note that both FLUSH TABLES and FLUSH PRIVILEGES will break
|
||||
execution in prelocked mode. So it is better to disable
|
||||
FLUSH in stored functions and triggers completely.
|
||||
*/
|
||||
my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "FLUSH");
|
||||
YYABORT;
|
||||
}
|
||||
lex->sql_command= SQLCOM_FLUSH; lex->type=0;
|
||||
lex->sql_command= SQLCOM_FLUSH;
|
||||
lex->type= 0;
|
||||
lex->no_write_to_binlog= $2;
|
||||
}
|
||||
flush_options
|
||||
|
@ -7487,6 +7506,9 @@ user:
|
|||
$$->user = $1;
|
||||
$$->host.str= (char *) "%";
|
||||
$$->host.length= 1;
|
||||
|
||||
if (check_string_length(&$$->user, ER(ER_USERNAME), USERNAME_LENGTH))
|
||||
YYABORT;
|
||||
}
|
||||
| ident_or_text '@' ident_or_text
|
||||
{
|
||||
|
@ -7494,6 +7516,11 @@ user:
|
|||
if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
|
||||
YYABORT;
|
||||
$$->user = $1; $$->host=$3;
|
||||
|
||||
if (check_string_length(&$$->user, ER(ER_USERNAME), USERNAME_LENGTH) ||
|
||||
check_string_length(&$$->host, ER(ER_HOSTNAME),
|
||||
HOSTNAME_LENGTH))
|
||||
YYABORT;
|
||||
}
|
||||
| CURRENT_USER optional_braces
|
||||
{
|
||||
|
@ -8971,15 +8998,9 @@ definer:
|
|||
*/
|
||||
YYTHD->lex->definer= 0;
|
||||
}
|
||||
| DEFINER_SYM EQ CURRENT_USER optional_braces
|
||||
| DEFINER_SYM EQ user
|
||||
{
|
||||
if (! (YYTHD->lex->definer= create_default_definer(YYTHD)))
|
||||
YYABORT;
|
||||
}
|
||||
| DEFINER_SYM EQ ident_or_text '@' ident_or_text
|
||||
{
|
||||
if (!(YYTHD->lex->definer= create_definer(YYTHD, &$3, &$5)))
|
||||
YYABORT;
|
||||
YYTHD->lex->definer= get_current_user(YYTHD, $3);
|
||||
}
|
||||
;
|
||||
|
||||
|
|
Loading…
Reference in a new issue