mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
Merge mysql.com:/opt/local/work/mysql-5.0-root
into mysql.com:/opt/local/work/mysql-5.0-runtime-merge
This commit is contained in:
@ -20,6 +20,7 @@ sub mtr_record_dead_children ();
|
|||||||
sub mtr_exit ($);
|
sub mtr_exit ($);
|
||||||
sub sleep_until_file_created ($$$);
|
sub sleep_until_file_created ($$$);
|
||||||
sub mtr_kill_processes ($);
|
sub mtr_kill_processes ($);
|
||||||
|
sub mtr_kill_process ($$$$);
|
||||||
|
|
||||||
# static in C
|
# static in C
|
||||||
sub spawn_impl ($$$$$$$$);
|
sub spawn_impl ($$$$$$$$);
|
||||||
@ -885,6 +886,25 @@ sub mtr_kill_processes ($) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub mtr_kill_process ($$$$) {
|
||||||
|
my $pid= shift;
|
||||||
|
my $signal= shift;
|
||||||
|
my $retries= shift;
|
||||||
|
my $timeout= shift;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
kill($signal, $pid);
|
||||||
|
|
||||||
|
last unless kill (0, $pid) and $retries--;
|
||||||
|
|
||||||
|
mtr_debug("Sleep $timeout second waiting for processes to die");
|
||||||
|
|
||||||
|
sleep($timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
# When we exit, we kill off all children
|
# When we exit, we kill off all children
|
||||||
|
@ -918,6 +918,7 @@ sub command_line_setup () {
|
|||||||
path_err => "$opt_vardir/log/im.err",
|
path_err => "$opt_vardir/log/im.err",
|
||||||
path_log => "$opt_vardir/log/im.log",
|
path_log => "$opt_vardir/log/im.log",
|
||||||
path_pid => "$opt_vardir/run/im.pid",
|
path_pid => "$opt_vardir/run/im.pid",
|
||||||
|
path_angel_pid => "$opt_vardir/run/im.angel.pid",
|
||||||
path_sock => "$sockdir/im.sock",
|
path_sock => "$sockdir/im.sock",
|
||||||
port => $im_port,
|
port => $im_port,
|
||||||
start_timeout => $master->[0]->{'start_timeout'},
|
start_timeout => $master->[0]->{'start_timeout'},
|
||||||
@ -1188,6 +1189,7 @@ sub environment_setup () {
|
|||||||
$ENV{'NDB_STATUS_OK'}= "YES";
|
$ENV{'NDB_STATUS_OK'}= "YES";
|
||||||
|
|
||||||
$ENV{'IM_PATH_PID'}= $instance_manager->{path_pid};
|
$ENV{'IM_PATH_PID'}= $instance_manager->{path_pid};
|
||||||
|
$ENV{'IM_PATH_ANGEL_PID'}= $instance_manager->{path_angel_pid};
|
||||||
$ENV{'IM_PORT'}= $instance_manager->{port};
|
$ENV{'IM_PORT'}= $instance_manager->{port};
|
||||||
|
|
||||||
$ENV{'IM_MYSQLD1_SOCK'}= $instance_manager->{instances}->[0]->{path_sock};
|
$ENV{'IM_MYSQLD1_SOCK'}= $instance_manager->{instances}->[0]->{path_sock};
|
||||||
@ -1813,6 +1815,7 @@ sub im_create_defaults_file($) {
|
|||||||
|
|
||||||
[manager]
|
[manager]
|
||||||
pid-file = $instance_manager->{path_pid}
|
pid-file = $instance_manager->{path_pid}
|
||||||
|
angel-pid-file = $instance_manager->{path_angel_pid}
|
||||||
socket = $instance_manager->{path_sock}
|
socket = $instance_manager->{path_sock}
|
||||||
port = $instance_manager->{port}
|
port = $instance_manager->{port}
|
||||||
password-file = $instance_manager->{password_file}
|
password-file = $instance_manager->{password_file}
|
||||||
@ -2805,6 +2808,18 @@ sub im_start($$) {
|
|||||||
sub im_stop($) {
|
sub im_stop($) {
|
||||||
my $instance_manager = shift;
|
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
|
# Re-read pid from the file, since during tests Instance Manager could have
|
||||||
# been restarted, so its pid could have been changed.
|
# been restarted, so its pid could have been changed.
|
||||||
|
|
||||||
@ -2812,34 +2827,79 @@ sub im_stop($) {
|
|||||||
mtr_get_pid_from_file($instance_manager->{'path_pid'})
|
mtr_get_pid_from_file($instance_manager->{'path_pid'})
|
||||||
if -f $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().
|
# Inspired from mtr_stop_mysqld_servers().
|
||||||
|
|
||||||
start_reap_all();
|
start_reap_all();
|
||||||
|
|
||||||
# Create list of pids. We should stop Instance Manager and all started
|
# Try graceful shutdown.
|
||||||
# mysqld-instances. Some of them may be nonguarded, so IM will not stop them
|
|
||||||
# on shutdown.
|
|
||||||
|
|
||||||
my @pids = ( $instance_manager->{'pid'} );
|
mtr_kill_process($instance_manager->{'pid'}, 'TERM', 10, 1);
|
||||||
my $instances = $instance_manager->{'instances'};
|
|
||||||
|
|
||||||
if ( -r $instances->[0]->{'path_pid'} )
|
# Check that all processes died.
|
||||||
|
|
||||||
|
my $clean_shutdown= 0;
|
||||||
|
|
||||||
|
while (1)
|
||||||
{
|
{
|
||||||
push(@pids, mtr_get_pid_from_file($instances->[0]->{'path_pid'}));
|
last if kill (0, $instance_manager->{'pid'});
|
||||||
|
|
||||||
|
last if (defined $instance_manager->{'angel_pid'}) &&
|
||||||
|
kill (0, $instance_manager->{'angel_pid'});
|
||||||
|
|
||||||
|
foreach my $pid (@mysqld_pids)
|
||||||
|
{
|
||||||
|
last if kill (0, $pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( -r $instances->[1]->{'path_pid'} )
|
$clean_shutdown= 1;
|
||||||
{
|
last;
|
||||||
push(@pids, mtr_get_pid_from_file($instances->[1]->{'path_pid'}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Kill processes.
|
# Kill leftovers (the order is important).
|
||||||
|
|
||||||
mtr_kill_processes(\@pids);
|
unless ($clean_shutdown)
|
||||||
|
{
|
||||||
|
mtr_kill_process($instance_manager->{'angel_pid'}, 'KILL', 10, 1)
|
||||||
|
if defined $instance_manager->{'angel_pid'};
|
||||||
|
|
||||||
|
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_kill_processes(\@mysqld_pids);
|
||||||
|
|
||||||
|
# Complain in error log so that a warning will be shown.
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
# That's all.
|
||||||
|
|
||||||
stop_reap_all();
|
stop_reap_all();
|
||||||
|
|
||||||
$instance_manager->{'pid'} = undef;
|
$instance_manager->{'pid'} = undef;
|
||||||
|
$instance_manager->{'angel_pid'} = undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,3 +53,7 @@ id select_type table type possible_keys key key_len ref rows Extra
|
|||||||
1 SIMPLE <09><><EFBFBD> ref <09><><EFBFBD>0,<2C><><EFBFBD>01 <09><><EFBFBD>0 5 const 1 Using where; Using index
|
1 SIMPLE <09><><EFBFBD> ref <09><><EFBFBD>0,<2C><><EFBFBD>01 <09><><EFBFBD>0 5 const 1 Using where; Using index
|
||||||
drop table <20><><EFBFBD>;
|
drop table <20><><EFBFBD>;
|
||||||
set names latin1;
|
set names latin1;
|
||||||
|
select 3 into @v1;
|
||||||
|
explain select 3 into @v1;
|
||||||
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
|
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used
|
||||||
|
@ -282,9 +282,9 @@ select @tmp_x, @tmp_y, @tmp_z|
|
|||||||
@tmp_x @tmp_y @tmp_z
|
@tmp_x @tmp_y @tmp_z
|
||||||
42 45 87
|
42 45 87
|
||||||
call p(42, 43, @tmp_z)|
|
call p(42, 43, @tmp_z)|
|
||||||
ERROR 42000: OUT or INOUT argument 2 for routine test.p is not a variable
|
ERROR 42000: OUT or INOUT argument 2 for routine test.p is not a variable or NEW pseudo-variable in BEFORE trigger
|
||||||
call p(42, @tmp_y, 43)|
|
call p(42, @tmp_y, 43)|
|
||||||
ERROR 42000: OUT or INOUT argument 3 for routine test.p is not a variable
|
ERROR 42000: OUT or INOUT argument 3 for routine test.p is not a variable or NEW pseudo-variable in BEFORE trigger
|
||||||
drop procedure p|
|
drop procedure p|
|
||||||
create procedure p() begin end|
|
create procedure p() begin end|
|
||||||
lock table t1 read|
|
lock table t1 read|
|
||||||
|
@ -4904,4 +4904,60 @@ schema_name
|
|||||||
select routine_name,routine_schema from information_schema.routines where
|
select routine_name,routine_schema from information_schema.routines where
|
||||||
routine_schema like 'bug18344%'|
|
routine_schema like 'bug18344%'|
|
||||||
routine_name routine_schema
|
routine_name routine_schema
|
||||||
|
drop function if exists bug12472|
|
||||||
|
create function bug12472() returns int return (select count(*) from t1)|
|
||||||
|
create table t3 as select bug12472() as i|
|
||||||
|
show create table t3|
|
||||||
|
Table Create Table
|
||||||
|
t3 CREATE TABLE `t3` (
|
||||||
|
`i` int(11) default NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
select * from t3|
|
||||||
|
i
|
||||||
|
0
|
||||||
|
drop table t3|
|
||||||
|
create view v1 as select bug12472() as j|
|
||||||
|
create table t3 as select * from v1|
|
||||||
|
show create table t3|
|
||||||
|
Table Create Table
|
||||||
|
t3 CREATE TABLE `t3` (
|
||||||
|
`j` bigint(11) default NULL
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=latin1
|
||||||
|
select * from t3|
|
||||||
|
j
|
||||||
|
0
|
||||||
|
drop table t3|
|
||||||
|
drop view v1|
|
||||||
|
drop function bug12472|
|
||||||
|
DROP FUNCTION IF EXISTS bug18589_f1|
|
||||||
|
DROP PROCEDURE IF EXISTS bug18589_p1|
|
||||||
|
DROP PROCEDURE IF EXISTS bug18589_p2|
|
||||||
|
CREATE FUNCTION bug18589_f1(arg TEXT) RETURNS TEXT
|
||||||
|
BEGIN
|
||||||
|
RETURN CONCAT(arg, "");
|
||||||
|
END|
|
||||||
|
CREATE PROCEDURE bug18589_p1(arg TEXT, OUT ret TEXT)
|
||||||
|
BEGIN
|
||||||
|
SET ret = CONCAT(arg, "");
|
||||||
|
END|
|
||||||
|
CREATE PROCEDURE bug18589_p2(arg TEXT)
|
||||||
|
BEGIN
|
||||||
|
DECLARE v TEXT;
|
||||||
|
CALL bug18589_p1(arg, v);
|
||||||
|
SELECT v;
|
||||||
|
END|
|
||||||
|
SELECT bug18589_f1(REPEAT("a", 767))|
|
||||||
|
bug18589_f1(REPEAT("a", 767))
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
SET @bug18589_v1 = ""|
|
||||||
|
CALL bug18589_p1(REPEAT("a", 767), @bug18589_v1)|
|
||||||
|
SELECT @bug18589_v1|
|
||||||
|
@bug18589_v1
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
CALL bug18589_p2(REPEAT("a", 767))|
|
||||||
|
v
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
DROP FUNCTION bug18589_f1|
|
||||||
|
DROP PROCEDURE bug18589_p1|
|
||||||
|
DROP PROCEDURE bug18589_p2|
|
||||||
drop table t1,t2;
|
drop table t1,t2;
|
||||||
|
@ -310,3 +310,87 @@ SELECT @mysqltest_var;
|
|||||||
Hello, world!
|
Hello, world!
|
||||||
DROP USER mysqltest_u1@localhost;
|
DROP USER mysqltest_u1@localhost;
|
||||||
DROP DATABASE mysqltest_db1;
|
DROP DATABASE mysqltest_db1;
|
||||||
|
DELETE FROM mysql.user WHERE User LIKE 'mysqltest_%';
|
||||||
|
DELETE FROM mysql.db WHERE User LIKE 'mysqltest_%';
|
||||||
|
DELETE FROM mysql.tables_priv WHERE User LIKE 'mysqltest_%';
|
||||||
|
DELETE FROM mysql.columns_priv WHERE User LIKE 'mysqltest_%';
|
||||||
|
FLUSH PRIVILEGES;
|
||||||
|
DROP DATABASE IF EXISTS mysqltest_db1;
|
||||||
|
CREATE DATABASE mysqltest_db1;
|
||||||
|
USE mysqltest_db1;
|
||||||
|
CREATE TABLE t1 (i1 INT);
|
||||||
|
CREATE TABLE t2 (i1 INT);
|
||||||
|
CREATE USER mysqltest_dfn@localhost;
|
||||||
|
CREATE USER mysqltest_inv@localhost;
|
||||||
|
GRANT EXECUTE, CREATE ROUTINE, SUPER ON *.* TO mysqltest_dfn@localhost;
|
||||||
|
GRANT INSERT ON mysqltest_db1.* TO mysqltest_inv@localhost;
|
||||||
|
CREATE PROCEDURE p1(OUT i INT) DETERMINISTIC NO SQL SET i = 3;
|
||||||
|
CREATE PROCEDURE p2(INOUT i INT) DETERMINISTIC NO SQL SET i = i * 5;
|
||||||
|
CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW
|
||||||
|
CALL p2(NEW.i1);
|
||||||
|
INSERT INTO t1 VALUES (7);
|
||||||
|
ERROR 42000: UPDATE command denied to user 'mysqltest_dfn'@'localhost' for column 'i1' in table 't1'
|
||||||
|
INSERT INTO t2 VALUES (11);
|
||||||
|
ERROR 42000: SELECT,UPDATE command denied to user 'mysqltest_dfn'@'localhost' for column 'i1' in table 't2'
|
||||||
|
DROP TRIGGER t2_bi;
|
||||||
|
DROP TRIGGER t1_bi;
|
||||||
|
GRANT SELECT ON mysqltest_db1.* TO mysqltest_dfn@localhost;
|
||||||
|
CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW
|
||||||
|
CALL p2(NEW.i1);
|
||||||
|
INSERT INTO t1 VALUES (13);
|
||||||
|
ERROR 42000: UPDATE command denied to user 'mysqltest_dfn'@'localhost' for column 'i1' in table 't1'
|
||||||
|
INSERT INTO t2 VALUES (17);
|
||||||
|
ERROR 42000: UPDATE command denied to user 'mysqltest_dfn'@'localhost' for column 'i1' in table 't2'
|
||||||
|
REVOKE SELECT ON mysqltest_db1.* FROM mysqltest_dfn@localhost;
|
||||||
|
DROP TRIGGER t2_bi;
|
||||||
|
DROP TRIGGER t1_bi;
|
||||||
|
GRANT UPDATE ON mysqltest_db1.* TO mysqltest_dfn@localhost;
|
||||||
|
CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW
|
||||||
|
CALL p2(NEW.i1);
|
||||||
|
INSERT INTO t1 VALUES (19);
|
||||||
|
INSERT INTO t2 VALUES (23);
|
||||||
|
ERROR 42000: SELECT command denied to user 'mysqltest_dfn'@'localhost' for column 'i1' in table 't2'
|
||||||
|
REVOKE UPDATE ON mysqltest_db1.* FROM mysqltest_dfn@localhost;
|
||||||
|
DROP TRIGGER t2_bi;
|
||||||
|
DROP TRIGGER t1_bi;
|
||||||
|
GRANT SELECT, UPDATE ON mysqltest_db1.* TO mysqltest_dfn@localhost;
|
||||||
|
CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW
|
||||||
|
CALL p2(NEW.i1);
|
||||||
|
INSERT INTO t1 VALUES (29);
|
||||||
|
INSERT INTO t2 VALUES (31);
|
||||||
|
REVOKE SELECT, UPDATE ON mysqltest_db1.* FROM mysqltest_dfn@localhost;
|
||||||
|
DROP TRIGGER t2_bi;
|
||||||
|
DROP TRIGGER t1_bi;
|
||||||
|
DROP PROCEDURE p2;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
GRANT UPDATE ON mysqltest_db1.* TO mysqltest_dfn@localhost;
|
||||||
|
CREATE PROCEDURE p1(OUT i INT) DETERMINISTIC NO SQL SET i = 37;
|
||||||
|
CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
INSERT INTO t1 VALUES (41);
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
CREATE PROCEDURE p1(IN i INT) DETERMINISTIC NO SQL SET @v1 = i + 43;
|
||||||
|
INSERT INTO t1 VALUES (47);
|
||||||
|
ERROR 42000: SELECT command denied to user 'mysqltest_dfn'@'localhost' for column 'i1' in table 't1'
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
CREATE PROCEDURE p1(INOUT i INT) DETERMINISTIC NO SQL SET i = i + 51;
|
||||||
|
INSERT INTO t1 VALUES (53);
|
||||||
|
ERROR 42000: SELECT command denied to user 'mysqltest_dfn'@'localhost' for column 'i1' in table 't1'
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
REVOKE UPDATE ON mysqltest_db1.* FROM mysqltest_dfn@localhost;
|
||||||
|
DROP TRIGGER t1_bi;
|
||||||
|
DROP USER mysqltest_inv@localhost;
|
||||||
|
DROP USER mysqltest_dfn@localhost;
|
||||||
|
DROP TABLE t2;
|
||||||
|
DROP TABLE t1;
|
||||||
|
DROP DATABASE mysqltest_db1;
|
||||||
|
USE test;
|
||||||
|
End of 5.0 tests.
|
||||||
|
@ -998,3 +998,95 @@ SELECT * FROM t1 WHERE conn_id != trigger_conn_id;
|
|||||||
conn_id trigger_conn_id
|
conn_id trigger_conn_id
|
||||||
DROP TRIGGER t1_bi;
|
DROP TRIGGER t1_bi;
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
DROP TABLE IF EXISTS t1;
|
||||||
|
CREATE TABLE t1 (i1 INT);
|
||||||
|
SET @save_sql_mode=@@sql_mode;
|
||||||
|
SET SQL_MODE='';
|
||||||
|
CREATE TRIGGER t1_ai AFTER INSERT ON t1 FOR EACH ROW
|
||||||
|
SET @x = 5/0;
|
||||||
|
SET SQL_MODE='traditional';
|
||||||
|
CREATE TRIGGER t1_au AFTER UPDATE ON t1 FOR EACH ROW
|
||||||
|
SET @x = 5/0;
|
||||||
|
SET @x=1;
|
||||||
|
INSERT INTO t1 VALUES (@x);
|
||||||
|
SELECT @x;
|
||||||
|
@x
|
||||||
|
NULL
|
||||||
|
SET @x=2;
|
||||||
|
UPDATE t1 SET i1 = @x;
|
||||||
|
ERROR 22012: Division by 0
|
||||||
|
SELECT @x;
|
||||||
|
@x
|
||||||
|
2
|
||||||
|
SET SQL_MODE='';
|
||||||
|
SET @x=3;
|
||||||
|
INSERT INTO t1 VALUES (@x);
|
||||||
|
SELECT @x;
|
||||||
|
@x
|
||||||
|
NULL
|
||||||
|
SET @x=4;
|
||||||
|
UPDATE t1 SET i1 = @x;
|
||||||
|
ERROR 22012: Division by 0
|
||||||
|
SELECT @x;
|
||||||
|
@x
|
||||||
|
4
|
||||||
|
SET @@sql_mode=@save_sql_mode;
|
||||||
|
DROP TRIGGER t1_ai;
|
||||||
|
DROP TRIGGER t1_au;
|
||||||
|
DROP TABLE t1;
|
||||||
|
DROP TABLE IF EXISTS t1;
|
||||||
|
DROP PROCEDURE IF EXISTS p1;
|
||||||
|
DROP PROCEDURE IF EXISTS p2;
|
||||||
|
CREATE TABLE t1 (i1 INT);
|
||||||
|
INSERT INTO t1 VALUES (3);
|
||||||
|
CREATE PROCEDURE p1(OUT i1 INT) DETERMINISTIC NO SQL SET i1 = 5;
|
||||||
|
CREATE PROCEDURE p2(INOUT i1 INT) DETERMINISTIC NO SQL SET i1 = i1 * 7;
|
||||||
|
CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
CALL p2(NEW.i1);
|
||||||
|
END//
|
||||||
|
UPDATE t1 SET i1 = 11 WHERE i1 = 3;
|
||||||
|
DROP TRIGGER t1_bu;
|
||||||
|
DROP PROCEDURE p2;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
INSERT INTO t1 VALUES (13);
|
||||||
|
CREATE PROCEDURE p1(OUT i1 INT) DETERMINISTIC NO SQL SET @a = 17;
|
||||||
|
CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 FOR EACH ROW
|
||||||
|
CALL p1(OLD.i1);
|
||||||
|
UPDATE t1 SET i1 = 19 WHERE i1 = 13;
|
||||||
|
ERROR 42000: OUT or INOUT argument 1 for routine test.p1 is not a variable or NEW pseudo-variable in BEFORE trigger
|
||||||
|
DROP TRIGGER t1_bu;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
INSERT INTO t1 VALUES (23);
|
||||||
|
CREATE PROCEDURE p1(INOUT i1 INT) DETERMINISTIC NO SQL SET @a = i1 * 29;
|
||||||
|
CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 FOR EACH ROW
|
||||||
|
CALL p1(OLD.i1);
|
||||||
|
UPDATE t1 SET i1 = 31 WHERE i1 = 23;
|
||||||
|
ERROR 42000: OUT or INOUT argument 1 for routine test.p1 is not a variable or NEW pseudo-variable in BEFORE trigger
|
||||||
|
DROP TRIGGER t1_bu;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
INSERT INTO t1 VALUES (37);
|
||||||
|
CREATE PROCEDURE p1(OUT i1 INT) DETERMINISTIC NO SQL SET @a = 41;
|
||||||
|
CREATE TRIGGER t1_au AFTER UPDATE ON t1 FOR EACH ROW
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
UPDATE t1 SET i1 = 43 WHERE i1 = 37;
|
||||||
|
ERROR 42000: OUT or INOUT argument 1 for routine test.p1 is not a variable or NEW pseudo-variable in BEFORE trigger
|
||||||
|
DROP TRIGGER t1_au;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
INSERT INTO t1 VALUES (47);
|
||||||
|
CREATE PROCEDURE p1(INOUT i1 INT) DETERMINISTIC NO SQL SET @a = i1 * 49;
|
||||||
|
CREATE TRIGGER t1_au AFTER UPDATE ON t1 FOR EACH ROW
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
UPDATE t1 SET i1 = 51 WHERE i1 = 47;
|
||||||
|
ERROR 42000: OUT or INOUT argument 1 for routine test.p1 is not a variable or NEW pseudo-variable in BEFORE trigger
|
||||||
|
DROP TRIGGER t1_au;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
SELECT * FROM t1;
|
||||||
|
i1
|
||||||
|
35
|
||||||
|
13
|
||||||
|
23
|
||||||
|
43
|
||||||
|
51
|
||||||
|
DROP TABLE t1;
|
||||||
|
@ -43,3 +43,12 @@ drop table
|
|||||||
set names latin1;
|
set names latin1;
|
||||||
|
|
||||||
# End of 4.1 tests
|
# End of 4.1 tests
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Bug#15463: EXPLAIN SELECT..INTO hangs the client (QB, command line)
|
||||||
|
#
|
||||||
|
select 3 into @v1;
|
||||||
|
explain select 3 into @v1;
|
||||||
|
|
||||||
|
# End of 5.0 tests.
|
||||||
|
@ -61,9 +61,15 @@ connection con2;
|
|||||||
sleep 1;
|
sleep 1;
|
||||||
show tables;
|
show tables;
|
||||||
UNLOCK TABLES;
|
UNLOCK TABLES;
|
||||||
sleep 1;
|
connection con1;
|
||||||
|
reap;
|
||||||
|
connection con2;
|
||||||
show tables;
|
show tables;
|
||||||
|
|
||||||
drop table t2, t4;
|
drop table t2, t4;
|
||||||
|
|
||||||
|
disconnect con2;
|
||||||
|
disconnect con1;
|
||||||
|
connection default;
|
||||||
|
|
||||||
# End of 4.1 tests
|
# End of 4.1 tests
|
||||||
|
@ -5773,6 +5773,76 @@ select routine_name,routine_schema from information_schema.routines where
|
|||||||
routine_schema like 'bug18344%'|
|
routine_schema like 'bug18344%'|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# BUG#12472/BUG#15137 'CREATE TABLE ... SELECT ... which explicitly or
|
||||||
|
# implicitly uses stored function gives "Table not locked" error'.
|
||||||
|
#
|
||||||
|
--disable_warnings
|
||||||
|
drop function if exists bug12472|
|
||||||
|
--enable_warnings
|
||||||
|
create function bug12472() returns int return (select count(*) from t1)|
|
||||||
|
# Check case when function is used directly
|
||||||
|
create table t3 as select bug12472() as i|
|
||||||
|
show create table t3|
|
||||||
|
select * from t3|
|
||||||
|
drop table t3|
|
||||||
|
# Check case when function is used indirectly through view
|
||||||
|
create view v1 as select bug12472() as j|
|
||||||
|
create table t3 as select * from v1|
|
||||||
|
show create table t3|
|
||||||
|
select * from t3|
|
||||||
|
drop table t3|
|
||||||
|
drop view v1|
|
||||||
|
drop function bug12472|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# BUG#18587: Function that accepts and returns TEXT garbles data if longer than
|
||||||
|
# 766 chars
|
||||||
|
#
|
||||||
|
|
||||||
|
# Prepare.
|
||||||
|
|
||||||
|
--disable_warnings
|
||||||
|
DROP FUNCTION IF EXISTS bug18589_f1|
|
||||||
|
DROP PROCEDURE IF EXISTS bug18589_p1|
|
||||||
|
DROP PROCEDURE IF EXISTS bug18589_p2|
|
||||||
|
--enable_warnings
|
||||||
|
|
||||||
|
CREATE FUNCTION bug18589_f1(arg TEXT) RETURNS TEXT
|
||||||
|
BEGIN
|
||||||
|
RETURN CONCAT(arg, "");
|
||||||
|
END|
|
||||||
|
|
||||||
|
CREATE PROCEDURE bug18589_p1(arg TEXT, OUT ret TEXT)
|
||||||
|
BEGIN
|
||||||
|
SET ret = CONCAT(arg, "");
|
||||||
|
END|
|
||||||
|
|
||||||
|
CREATE PROCEDURE bug18589_p2(arg TEXT)
|
||||||
|
BEGIN
|
||||||
|
DECLARE v TEXT;
|
||||||
|
CALL bug18589_p1(arg, v);
|
||||||
|
SELECT v;
|
||||||
|
END|
|
||||||
|
|
||||||
|
# Test case.
|
||||||
|
|
||||||
|
SELECT bug18589_f1(REPEAT("a", 767))|
|
||||||
|
|
||||||
|
SET @bug18589_v1 = ""|
|
||||||
|
CALL bug18589_p1(REPEAT("a", 767), @bug18589_v1)|
|
||||||
|
SELECT @bug18589_v1|
|
||||||
|
|
||||||
|
CALL bug18589_p2(REPEAT("a", 767))|
|
||||||
|
|
||||||
|
# Cleanup.
|
||||||
|
|
||||||
|
DROP FUNCTION bug18589_f1|
|
||||||
|
DROP PROCEDURE bug18589_p1|
|
||||||
|
DROP PROCEDURE bug18589_p2|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# BUG#NNNN: New bug synopsis
|
# BUG#NNNN: New bug synopsis
|
||||||
#
|
#
|
||||||
|
@ -564,3 +564,176 @@ SELECT @mysqltest_var;
|
|||||||
DROP USER mysqltest_u1@localhost;
|
DROP USER mysqltest_u1@localhost;
|
||||||
|
|
||||||
DROP DATABASE mysqltest_db1;
|
DROP DATABASE mysqltest_db1;
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test for bug #14635 Accept NEW.x as INOUT parameters to stored
|
||||||
|
# procedures from within triggers
|
||||||
|
#
|
||||||
|
# We require UPDATE privilege when NEW.x passed as OUT parameter, and
|
||||||
|
# SELECT and UPDATE when NEW.x passed as INOUT parameter.
|
||||||
|
#
|
||||||
|
DELETE FROM mysql.user WHERE User LIKE 'mysqltest_%';
|
||||||
|
DELETE FROM mysql.db WHERE User LIKE 'mysqltest_%';
|
||||||
|
DELETE FROM mysql.tables_priv WHERE User LIKE 'mysqltest_%';
|
||||||
|
DELETE FROM mysql.columns_priv WHERE User LIKE 'mysqltest_%';
|
||||||
|
FLUSH PRIVILEGES;
|
||||||
|
|
||||||
|
--disable_warnings
|
||||||
|
DROP DATABASE IF EXISTS mysqltest_db1;
|
||||||
|
--enable_warnings
|
||||||
|
|
||||||
|
CREATE DATABASE mysqltest_db1;
|
||||||
|
USE mysqltest_db1;
|
||||||
|
|
||||||
|
CREATE TABLE t1 (i1 INT);
|
||||||
|
CREATE TABLE t2 (i1 INT);
|
||||||
|
|
||||||
|
CREATE USER mysqltest_dfn@localhost;
|
||||||
|
CREATE USER mysqltest_inv@localhost;
|
||||||
|
|
||||||
|
GRANT EXECUTE, CREATE ROUTINE, SUPER ON *.* TO mysqltest_dfn@localhost;
|
||||||
|
GRANT INSERT ON mysqltest_db1.* TO mysqltest_inv@localhost;
|
||||||
|
|
||||||
|
connect (definer,localhost,mysqltest_dfn,,mysqltest_db1);
|
||||||
|
connect (invoker,localhost,mysqltest_inv,,mysqltest_db1);
|
||||||
|
|
||||||
|
connection definer;
|
||||||
|
CREATE PROCEDURE p1(OUT i INT) DETERMINISTIC NO SQL SET i = 3;
|
||||||
|
CREATE PROCEDURE p2(INOUT i INT) DETERMINISTIC NO SQL SET i = i * 5;
|
||||||
|
|
||||||
|
# Check that having no privilege won't work.
|
||||||
|
connection definer;
|
||||||
|
CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW
|
||||||
|
CALL p2(NEW.i1);
|
||||||
|
|
||||||
|
connection invoker;
|
||||||
|
--error ER_COLUMNACCESS_DENIED_ERROR
|
||||||
|
INSERT INTO t1 VALUES (7);
|
||||||
|
--error ER_COLUMNACCESS_DENIED_ERROR
|
||||||
|
INSERT INTO t2 VALUES (11);
|
||||||
|
|
||||||
|
connection definer;
|
||||||
|
DROP TRIGGER t2_bi;
|
||||||
|
DROP TRIGGER t1_bi;
|
||||||
|
|
||||||
|
# Check that having only SELECT privilege is not enough.
|
||||||
|
connection default;
|
||||||
|
GRANT SELECT ON mysqltest_db1.* TO mysqltest_dfn@localhost;
|
||||||
|
|
||||||
|
connection definer;
|
||||||
|
CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW
|
||||||
|
CALL p2(NEW.i1);
|
||||||
|
|
||||||
|
connection invoker;
|
||||||
|
--error ER_COLUMNACCESS_DENIED_ERROR
|
||||||
|
INSERT INTO t1 VALUES (13);
|
||||||
|
--error ER_COLUMNACCESS_DENIED_ERROR
|
||||||
|
INSERT INTO t2 VALUES (17);
|
||||||
|
|
||||||
|
connection default;
|
||||||
|
REVOKE SELECT ON mysqltest_db1.* FROM mysqltest_dfn@localhost;
|
||||||
|
|
||||||
|
connection definer;
|
||||||
|
DROP TRIGGER t2_bi;
|
||||||
|
DROP TRIGGER t1_bi;
|
||||||
|
|
||||||
|
# Check that having only UPDATE privilege is enough for OUT parameter,
|
||||||
|
# but not for INOUT parameter.
|
||||||
|
connection default;
|
||||||
|
GRANT UPDATE ON mysqltest_db1.* TO mysqltest_dfn@localhost;
|
||||||
|
|
||||||
|
connection definer;
|
||||||
|
CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW
|
||||||
|
CALL p2(NEW.i1);
|
||||||
|
|
||||||
|
connection invoker;
|
||||||
|
INSERT INTO t1 VALUES (19);
|
||||||
|
--error ER_COLUMNACCESS_DENIED_ERROR
|
||||||
|
INSERT INTO t2 VALUES (23);
|
||||||
|
|
||||||
|
connection default;
|
||||||
|
REVOKE UPDATE ON mysqltest_db1.* FROM mysqltest_dfn@localhost;
|
||||||
|
|
||||||
|
connection definer;
|
||||||
|
DROP TRIGGER t2_bi;
|
||||||
|
DROP TRIGGER t1_bi;
|
||||||
|
|
||||||
|
# Check that having SELECT and UPDATE privileges is enough.
|
||||||
|
connection default;
|
||||||
|
GRANT SELECT, UPDATE ON mysqltest_db1.* TO mysqltest_dfn@localhost;
|
||||||
|
|
||||||
|
connection definer;
|
||||||
|
CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
CREATE TRIGGER t2_bi BEFORE INSERT ON t2 FOR EACH ROW
|
||||||
|
CALL p2(NEW.i1);
|
||||||
|
|
||||||
|
connection invoker;
|
||||||
|
INSERT INTO t1 VALUES (29);
|
||||||
|
INSERT INTO t2 VALUES (31);
|
||||||
|
|
||||||
|
connection default;
|
||||||
|
REVOKE SELECT, UPDATE ON mysqltest_db1.* FROM mysqltest_dfn@localhost;
|
||||||
|
|
||||||
|
connection definer;
|
||||||
|
DROP TRIGGER t2_bi;
|
||||||
|
DROP TRIGGER t1_bi;
|
||||||
|
|
||||||
|
connection default;
|
||||||
|
DROP PROCEDURE p2;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
# Check that late procedure redefining won't open a security hole.
|
||||||
|
connection default;
|
||||||
|
GRANT UPDATE ON mysqltest_db1.* TO mysqltest_dfn@localhost;
|
||||||
|
|
||||||
|
connection definer;
|
||||||
|
CREATE PROCEDURE p1(OUT i INT) DETERMINISTIC NO SQL SET i = 37;
|
||||||
|
CREATE TRIGGER t1_bi BEFORE INSERT ON t1 FOR EACH ROW
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
|
||||||
|
connection invoker;
|
||||||
|
INSERT INTO t1 VALUES (41);
|
||||||
|
|
||||||
|
connection definer;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
CREATE PROCEDURE p1(IN i INT) DETERMINISTIC NO SQL SET @v1 = i + 43;
|
||||||
|
|
||||||
|
connection invoker;
|
||||||
|
--error ER_COLUMNACCESS_DENIED_ERROR
|
||||||
|
INSERT INTO t1 VALUES (47);
|
||||||
|
|
||||||
|
connection definer;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
CREATE PROCEDURE p1(INOUT i INT) DETERMINISTIC NO SQL SET i = i + 51;
|
||||||
|
|
||||||
|
connection invoker;
|
||||||
|
--error ER_COLUMNACCESS_DENIED_ERROR
|
||||||
|
INSERT INTO t1 VALUES (53);
|
||||||
|
|
||||||
|
connection default;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
REVOKE UPDATE ON mysqltest_db1.* FROM mysqltest_dfn@localhost;
|
||||||
|
|
||||||
|
connection definer;
|
||||||
|
DROP TRIGGER t1_bi;
|
||||||
|
|
||||||
|
# Cleanup.
|
||||||
|
disconnect definer;
|
||||||
|
disconnect invoker;
|
||||||
|
connection default;
|
||||||
|
DROP USER mysqltest_inv@localhost;
|
||||||
|
DROP USER mysqltest_dfn@localhost;
|
||||||
|
DROP TABLE t2;
|
||||||
|
DROP TABLE t1;
|
||||||
|
DROP DATABASE mysqltest_db1;
|
||||||
|
USE test;
|
||||||
|
|
||||||
|
--echo End of 5.0 tests.
|
||||||
|
@ -1165,4 +1165,126 @@ SELECT * FROM t1 WHERE conn_id != trigger_conn_id;
|
|||||||
DROP TRIGGER t1_bi;
|
DROP TRIGGER t1_bi;
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Bug#6951: Triggers/Traditional: SET @ result wrong
|
||||||
|
#
|
||||||
|
--disable_warnings
|
||||||
|
DROP TABLE IF EXISTS t1;
|
||||||
|
--enable_warnings
|
||||||
|
|
||||||
|
CREATE TABLE t1 (i1 INT);
|
||||||
|
|
||||||
|
SET @save_sql_mode=@@sql_mode;
|
||||||
|
|
||||||
|
SET SQL_MODE='';
|
||||||
|
|
||||||
|
CREATE TRIGGER t1_ai AFTER INSERT ON t1 FOR EACH ROW
|
||||||
|
SET @x = 5/0;
|
||||||
|
|
||||||
|
SET SQL_MODE='traditional';
|
||||||
|
|
||||||
|
CREATE TRIGGER t1_au AFTER UPDATE ON t1 FOR EACH ROW
|
||||||
|
SET @x = 5/0;
|
||||||
|
|
||||||
|
SET @x=1;
|
||||||
|
INSERT INTO t1 VALUES (@x);
|
||||||
|
SELECT @x;
|
||||||
|
|
||||||
|
SET @x=2;
|
||||||
|
--error 1365
|
||||||
|
UPDATE t1 SET i1 = @x;
|
||||||
|
SELECT @x;
|
||||||
|
|
||||||
|
SET SQL_MODE='';
|
||||||
|
|
||||||
|
SET @x=3;
|
||||||
|
INSERT INTO t1 VALUES (@x);
|
||||||
|
SELECT @x;
|
||||||
|
|
||||||
|
SET @x=4;
|
||||||
|
--error 1365
|
||||||
|
UPDATE t1 SET i1 = @x;
|
||||||
|
SELECT @x;
|
||||||
|
|
||||||
|
SET @@sql_mode=@save_sql_mode;
|
||||||
|
|
||||||
|
DROP TRIGGER t1_ai;
|
||||||
|
DROP TRIGGER t1_au;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test for bug #14635 Accept NEW.x as INOUT parameters to stored
|
||||||
|
# procedures from within triggers
|
||||||
|
#
|
||||||
|
--disable_warnings
|
||||||
|
DROP TABLE IF EXISTS t1;
|
||||||
|
DROP PROCEDURE IF EXISTS p1;
|
||||||
|
DROP PROCEDURE IF EXISTS p2;
|
||||||
|
--enable_warnings
|
||||||
|
|
||||||
|
CREATE TABLE t1 (i1 INT);
|
||||||
|
|
||||||
|
# Check that NEW.x pseudo variable is accepted as INOUT and OUT
|
||||||
|
# parameter to stored routine.
|
||||||
|
INSERT INTO t1 VALUES (3);
|
||||||
|
CREATE PROCEDURE p1(OUT i1 INT) DETERMINISTIC NO SQL SET i1 = 5;
|
||||||
|
CREATE PROCEDURE p2(INOUT i1 INT) DETERMINISTIC NO SQL SET i1 = i1 * 7;
|
||||||
|
delimiter //;
|
||||||
|
CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
CALL p2(NEW.i1);
|
||||||
|
END//
|
||||||
|
delimiter ;//
|
||||||
|
UPDATE t1 SET i1 = 11 WHERE i1 = 3;
|
||||||
|
DROP TRIGGER t1_bu;
|
||||||
|
DROP PROCEDURE p2;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
# Check that OLD.x pseudo variable is not accepted as INOUT and OUT
|
||||||
|
# parameter to stored routine.
|
||||||
|
INSERT INTO t1 VALUES (13);
|
||||||
|
CREATE PROCEDURE p1(OUT i1 INT) DETERMINISTIC NO SQL SET @a = 17;
|
||||||
|
CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 FOR EACH ROW
|
||||||
|
CALL p1(OLD.i1);
|
||||||
|
--error ER_SP_NOT_VAR_ARG
|
||||||
|
UPDATE t1 SET i1 = 19 WHERE i1 = 13;
|
||||||
|
DROP TRIGGER t1_bu;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
INSERT INTO t1 VALUES (23);
|
||||||
|
CREATE PROCEDURE p1(INOUT i1 INT) DETERMINISTIC NO SQL SET @a = i1 * 29;
|
||||||
|
CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 FOR EACH ROW
|
||||||
|
CALL p1(OLD.i1);
|
||||||
|
--error ER_SP_NOT_VAR_ARG
|
||||||
|
UPDATE t1 SET i1 = 31 WHERE i1 = 23;
|
||||||
|
DROP TRIGGER t1_bu;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
# Check that NEW.x pseudo variable is read-only in the AFTER TRIGGER.
|
||||||
|
INSERT INTO t1 VALUES (37);
|
||||||
|
CREATE PROCEDURE p1(OUT i1 INT) DETERMINISTIC NO SQL SET @a = 41;
|
||||||
|
CREATE TRIGGER t1_au AFTER UPDATE ON t1 FOR EACH ROW
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
--error ER_SP_NOT_VAR_ARG
|
||||||
|
UPDATE t1 SET i1 = 43 WHERE i1 = 37;
|
||||||
|
DROP TRIGGER t1_au;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
INSERT INTO t1 VALUES (47);
|
||||||
|
CREATE PROCEDURE p1(INOUT i1 INT) DETERMINISTIC NO SQL SET @a = i1 * 49;
|
||||||
|
CREATE TRIGGER t1_au AFTER UPDATE ON t1 FOR EACH ROW
|
||||||
|
CALL p1(NEW.i1);
|
||||||
|
--error ER_SP_NOT_VAR_ARG
|
||||||
|
UPDATE t1 SET i1 = 51 WHERE i1 = 47;
|
||||||
|
DROP TRIGGER t1_au;
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
|
||||||
|
# Post requisite.
|
||||||
|
SELECT * FROM t1;
|
||||||
|
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
# End of 5.0 tests
|
# End of 5.0 tests
|
||||||
|
@ -35,12 +35,12 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static int create_pid_file(const char *pid_file_name)
|
int create_pid_file(const char *pid_file_name, int pid)
|
||||||
{
|
{
|
||||||
if (FILE *pid_file= my_fopen(pid_file_name,
|
if (FILE *pid_file= my_fopen(pid_file_name,
|
||||||
O_WRONLY | O_CREAT | O_BINARY, MYF(0)))
|
O_WRONLY | O_CREAT | O_BINARY, MYF(0)))
|
||||||
{
|
{
|
||||||
fprintf(pid_file, "%d\n", (int) getpid());
|
fprintf(pid_file, "%d\n", (int) pid);
|
||||||
my_fclose(pid_file, MYF(0));
|
my_fclose(pid_file, MYF(0));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -138,8 +138,13 @@ void manager(const Options &options)
|
|||||||
if (user_map.load(options.password_file_name))
|
if (user_map.load(options.password_file_name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* write pid file */
|
/* write Instance Manager pid file */
|
||||||
if (create_pid_file(options.pid_file_name))
|
|
||||||
|
log_info("IM pid file: '%s'; PID: %d.",
|
||||||
|
(const char *) options.pid_file_name,
|
||||||
|
(int) manager_pid);
|
||||||
|
|
||||||
|
if (create_pid_file(options.pid_file_name, manager_pid))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sigset_t mask;
|
sigset_t mask;
|
||||||
|
@ -20,4 +20,6 @@ struct Options;
|
|||||||
|
|
||||||
void manager(const Options &options);
|
void manager(const Options &options);
|
||||||
|
|
||||||
|
int create_pid_file(const char *pid_file_name, int pid);
|
||||||
|
|
||||||
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H
|
#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_MANAGER_H
|
||||||
|
@ -338,6 +338,14 @@ spawn:
|
|||||||
/* Here we return to main, and fall into manager */
|
/* Here we return to main, and fall into manager */
|
||||||
break;
|
break;
|
||||||
default: // parent, success
|
default: // parent, success
|
||||||
|
pid= getpid(); /* Get our pid. */
|
||||||
|
|
||||||
|
log_info("Angel pid file: '%s'; PID: %d.",
|
||||||
|
(const char *) options.angel_pid_file_name,
|
||||||
|
(int) pid);
|
||||||
|
|
||||||
|
create_pid_file(Options::angel_pid_file_name, pid);
|
||||||
|
|
||||||
while (child_status == CHILD_OK && is_terminated == 0)
|
while (child_status == CHILD_OK && is_terminated == 0)
|
||||||
sigsuspend(&zeromask);
|
sigsuspend(&zeromask);
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ const char *Options::user= 0; /* No default value */
|
|||||||
const char *default_password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME);
|
const char *default_password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME);
|
||||||
const char *default_log_file_name= QUOTE(DEFAULT_LOG_FILE_NAME);
|
const char *default_log_file_name= QUOTE(DEFAULT_LOG_FILE_NAME);
|
||||||
const char *Options::config_file= QUOTE(DEFAULT_CONFIG_FILE);
|
const char *Options::config_file= QUOTE(DEFAULT_CONFIG_FILE);
|
||||||
|
const char *Options::angel_pid_file_name= NULL;
|
||||||
#endif
|
#endif
|
||||||
const char *Options::log_file_name= default_log_file_name;
|
const char *Options::log_file_name= default_log_file_name;
|
||||||
const char *Options::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME);
|
const char *Options::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME);
|
||||||
@ -58,6 +59,9 @@ char **Options::saved_argv= NULL;
|
|||||||
/* Remember if the config file was forced */
|
/* Remember if the config file was forced */
|
||||||
bool Options::is_forced_default_file= 0;
|
bool Options::is_forced_default_file= 0;
|
||||||
|
|
||||||
|
static const char * const ANGEL_PID_FILE_SUFFIX= ".angel.pid";
|
||||||
|
static const int ANGEL_PID_FILE_SUFFIX_LEN= strlen(ANGEL_PID_FILE_SUFFIX);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
List of options, accepted by the instance manager.
|
List of options, accepted by the instance manager.
|
||||||
List must be closed with empty option.
|
List must be closed with empty option.
|
||||||
@ -72,6 +76,7 @@ enum options {
|
|||||||
#ifndef __WIN__
|
#ifndef __WIN__
|
||||||
OPT_RUN_AS_SERVICE,
|
OPT_RUN_AS_SERVICE,
|
||||||
OPT_USER,
|
OPT_USER,
|
||||||
|
OPT_ANGEL_PID_FILE,
|
||||||
#else
|
#else
|
||||||
OPT_INSTALL_SERVICE,
|
OPT_INSTALL_SERVICE,
|
||||||
OPT_REMOVE_SERVICE,
|
OPT_REMOVE_SERVICE,
|
||||||
@ -96,6 +101,13 @@ static struct my_option my_long_options[] =
|
|||||||
(gptr *) &Options::pid_file_name, (gptr *) &Options::pid_file_name,
|
(gptr *) &Options::pid_file_name, (gptr *) &Options::pid_file_name,
|
||||||
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
|
|
||||||
|
#ifndef __WIN__
|
||||||
|
{ "angel-pid-file", OPT_ANGEL_PID_FILE, "Pid file for angel process.",
|
||||||
|
(gptr *) &Options::angel_pid_file_name,
|
||||||
|
(gptr *) &Options::angel_pid_file_name,
|
||||||
|
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
|
#endif
|
||||||
|
|
||||||
{ "socket", OPT_SOCKET, "Socket file to use for connection.",
|
{ "socket", OPT_SOCKET, "Socket file to use for connection.",
|
||||||
(gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name,
|
(gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name,
|
||||||
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
|
||||||
@ -290,6 +302,46 @@ int Options::load(int argc, char **argv)
|
|||||||
get_one_option)) != 0)
|
get_one_option)) != 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
#ifndef __WIN__
|
||||||
|
if (Options::run_as_service)
|
||||||
|
{
|
||||||
|
if (Options::angel_pid_file_name == NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Calculate angel pid file on the IM pid file basis: replace the
|
||||||
|
extension (everything after the last dot) of the pid file basename to
|
||||||
|
'.angel.pid'.
|
||||||
|
*/
|
||||||
|
|
||||||
|
char *angel_pid_file_name;
|
||||||
|
char *base_name_ptr;
|
||||||
|
char *ext_ptr;
|
||||||
|
|
||||||
|
angel_pid_file_name= (char *) malloc(strlen(Options::pid_file_name) +
|
||||||
|
ANGEL_PID_FILE_SUFFIX_LEN);
|
||||||
|
|
||||||
|
strcpy(angel_pid_file_name, Options::pid_file_name);
|
||||||
|
|
||||||
|
base_name_ptr= strrchr(angel_pid_file_name, '/');
|
||||||
|
|
||||||
|
if (!base_name_ptr)
|
||||||
|
base_name_ptr= angel_pid_file_name + 1;
|
||||||
|
|
||||||
|
ext_ptr= strrchr(base_name_ptr, '.');
|
||||||
|
if (ext_ptr)
|
||||||
|
*ext_ptr= 0;
|
||||||
|
|
||||||
|
strcat(angel_pid_file_name, ANGEL_PID_FILE_SUFFIX);
|
||||||
|
|
||||||
|
Options::angel_pid_file_name= angel_pid_file_name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Options::angel_pid_file_name= strdup(Options::angel_pid_file_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
@ -301,6 +353,11 @@ void Options::cleanup()
|
|||||||
/* free_defaults returns nothing */
|
/* free_defaults returns nothing */
|
||||||
if (Options::saved_argv != NULL)
|
if (Options::saved_argv != NULL)
|
||||||
free_defaults(Options::saved_argv);
|
free_defaults(Options::saved_argv);
|
||||||
|
|
||||||
|
#ifndef __WIN__
|
||||||
|
if (Options::run_as_service)
|
||||||
|
free((void *) Options::angel_pid_file_name);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __WIN__
|
#ifdef __WIN__
|
||||||
|
@ -35,6 +35,7 @@ struct Options
|
|||||||
#else
|
#else
|
||||||
static char run_as_service; /* handle_options doesn't support bool */
|
static char run_as_service; /* handle_options doesn't support bool */
|
||||||
static const char *user;
|
static const char *user;
|
||||||
|
static const char *angel_pid_file_name;
|
||||||
#endif
|
#endif
|
||||||
static bool is_forced_default_file;
|
static bool is_forced_default_file;
|
||||||
static const char *log_file_name;
|
static const char *log_file_name;
|
||||||
|
@ -642,7 +642,8 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*)
|
|||||||
|
|
||||||
void field_conv(Field *to,Field *from)
|
void field_conv(Field *to,Field *from)
|
||||||
{
|
{
|
||||||
if (to->real_type() == from->real_type())
|
if (to->real_type() == from->real_type() &&
|
||||||
|
!(to->type() == FIELD_TYPE_BLOB && to->table->copy_blobs))
|
||||||
{
|
{
|
||||||
if (to->pack_length() == from->pack_length() &&
|
if (to->pack_length() == from->pack_length() &&
|
||||||
!(to->flags & UNSIGNED_FLAG && !(from->flags & UNSIGNED_FLAG)) &&
|
!(to->flags & UNSIGNED_FLAG && !(from->flags & UNSIGNED_FLAG)) &&
|
||||||
|
29
sql/item.cc
29
sql/item.cc
@ -958,6 +958,12 @@ void Item_splocal::print(String *str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item *it)
|
||||||
|
{
|
||||||
|
return ctx->set_variable(thd, get_var_idx(), it);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
Item_case_expr methods
|
Item_case_expr methods
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
@ -5359,6 +5365,25 @@ bool Item_trigger_field::eq(const Item *item, bool binary_cmp) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Item_trigger_field::set_required_privilege(const bool rw)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Require SELECT and UPDATE privilege if this field will be read and
|
||||||
|
set, and only UPDATE privilege for setting the field.
|
||||||
|
*/
|
||||||
|
want_privilege= (rw ? SELECT_ACL | UPDATE_ACL : UPDATE_ACL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Item_trigger_field::set_value(THD *thd, sp_rcontext */*ctx*/, Item *it)
|
||||||
|
{
|
||||||
|
Item *item= sp_prepare_func_item(thd, &it);
|
||||||
|
|
||||||
|
return (!item || (!fixed && fix_fields(thd, 0)) ||
|
||||||
|
(item->save_in_field(field, 0) < 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Item_trigger_field::fix_fields(THD *thd, Item **items)
|
bool Item_trigger_field::fix_fields(THD *thd, Item **items)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -5381,8 +5406,7 @@ bool Item_trigger_field::fix_fields(THD *thd, Item **items)
|
|||||||
|
|
||||||
if (table_grants)
|
if (table_grants)
|
||||||
{
|
{
|
||||||
table_grants->want_privilege=
|
table_grants->want_privilege= want_privilege;
|
||||||
access_type == AT_READ ? SELECT_ACL : UPDATE_ACL;
|
|
||||||
|
|
||||||
if (check_grant_column(thd, table_grants, triggers->table->s->db,
|
if (check_grant_column(thd, table_grants, triggers->table->s->db,
|
||||||
triggers->table->s->table_name, field_name,
|
triggers->table->s->table_name, field_name,
|
||||||
@ -5414,6 +5438,7 @@ void Item_trigger_field::print(String *str)
|
|||||||
|
|
||||||
void Item_trigger_field::cleanup()
|
void Item_trigger_field::cleanup()
|
||||||
{
|
{
|
||||||
|
want_privilege= original_privilege;
|
||||||
/*
|
/*
|
||||||
Since special nature of Item_trigger_field we should not do most of
|
Since special nature of Item_trigger_field we should not do most of
|
||||||
things from Item_field::cleanup() or Item_ident::cleanup() here.
|
things from Item_field::cleanup() or Item_ident::cleanup() here.
|
||||||
|
101
sql/item.h
101
sql/item.h
@ -372,6 +372,42 @@ public:
|
|||||||
|
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
|
||||||
|
class sp_rcontext;
|
||||||
|
|
||||||
|
|
||||||
|
class Settable_routine_parameter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
Set required privileges for accessing the parameter.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
set_required_privilege()
|
||||||
|
rw if 'rw' is true then we are going to read and set the
|
||||||
|
parameter, so SELECT and UPDATE privileges might be
|
||||||
|
required, otherwise we only reading it and SELECT
|
||||||
|
privilege might be required.
|
||||||
|
*/
|
||||||
|
virtual void set_required_privilege(bool rw) {};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Set parameter value.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
set_value()
|
||||||
|
thd thread handle
|
||||||
|
ctx context to which parameter belongs (if it is local
|
||||||
|
variable).
|
||||||
|
it item which represents new value
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
FALSE if parameter value has been set,
|
||||||
|
TRUE if error has occured.
|
||||||
|
*/
|
||||||
|
virtual bool set_value(THD *thd, sp_rcontext *ctx, Item *it)= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
typedef bool (Item::*Item_processor)(byte *arg);
|
typedef bool (Item::*Item_processor)(byte *arg);
|
||||||
typedef Item* (Item::*Item_transformer) (byte *arg);
|
typedef Item* (Item::*Item_transformer) (byte *arg);
|
||||||
typedef void (*Cond_traverser) (const Item *item, void *arg);
|
typedef void (*Cond_traverser) (const Item *item, void *arg);
|
||||||
@ -744,6 +780,15 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual bool is_splocal() { return 0; } /* Needed for error checking */
|
virtual bool is_splocal() { return 0; } /* Needed for error checking */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Return Settable_routine_parameter interface of the Item. Return 0
|
||||||
|
if this Item is not Settable_routine_parameter.
|
||||||
|
*/
|
||||||
|
virtual Settable_routine_parameter *get_settable_routine_parameter()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -842,7 +887,8 @@ inline bool Item_sp_variable::send(Protocol *protocol, String *str)
|
|||||||
runtime.
|
runtime.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
class Item_splocal :public Item_sp_variable
|
class Item_splocal :public Item_sp_variable,
|
||||||
|
private Settable_routine_parameter
|
||||||
{
|
{
|
||||||
uint m_var_idx;
|
uint m_var_idx;
|
||||||
|
|
||||||
@ -880,6 +926,15 @@ public:
|
|||||||
|
|
||||||
inline enum Type type() const;
|
inline enum Type type() const;
|
||||||
inline Item_result result_type() const;
|
inline Item_result result_type() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool set_value(THD *thd, sp_rcontext *ctx, Item *it);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Settable_routine_parameter *get_settable_routine_parameter()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
@ -2100,14 +2155,13 @@ class Table_triggers_list;
|
|||||||
two Field instances representing either OLD or NEW version of this
|
two Field instances representing either OLD or NEW version of this
|
||||||
field.
|
field.
|
||||||
*/
|
*/
|
||||||
class Item_trigger_field : public Item_field
|
class Item_trigger_field : public Item_field,
|
||||||
|
private Settable_routine_parameter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/* Is this item represents row from NEW or OLD row ? */
|
/* Is this item represents row from NEW or OLD row ? */
|
||||||
enum row_version_type {OLD_ROW, NEW_ROW};
|
enum row_version_type {OLD_ROW, NEW_ROW};
|
||||||
row_version_type row_version;
|
row_version_type row_version;
|
||||||
/* Is this item used for reading or updating the value? */
|
|
||||||
enum access_types { AT_READ = 0x1, AT_UPDATE = 0x2 };
|
|
||||||
/* Next in list of all Item_trigger_field's in trigger */
|
/* Next in list of all Item_trigger_field's in trigger */
|
||||||
Item_trigger_field *next_trg_field;
|
Item_trigger_field *next_trg_field;
|
||||||
/* Index of the field in the TABLE::field array */
|
/* Index of the field in the TABLE::field array */
|
||||||
@ -2118,11 +2172,11 @@ public:
|
|||||||
Item_trigger_field(Name_resolution_context *context_arg,
|
Item_trigger_field(Name_resolution_context *context_arg,
|
||||||
row_version_type row_ver_arg,
|
row_version_type row_ver_arg,
|
||||||
const char *field_name_arg,
|
const char *field_name_arg,
|
||||||
access_types access_type_arg)
|
ulong priv, const bool ro)
|
||||||
:Item_field(context_arg,
|
:Item_field(context_arg,
|
||||||
(const char *)NULL, (const char *)NULL, field_name_arg),
|
(const char *)NULL, (const char *)NULL, field_name_arg),
|
||||||
row_version(row_ver_arg), field_idx((uint)-1),
|
row_version(row_ver_arg), field_idx((uint)-1), original_privilege(priv),
|
||||||
access_type(access_type_arg), table_grants(NULL)
|
want_privilege(priv), table_grants(NULL), read_only (ro)
|
||||||
{}
|
{}
|
||||||
void setup_field(THD *thd, TABLE *table, GRANT_INFO *table_grant_info);
|
void setup_field(THD *thd, TABLE *table, GRANT_INFO *table_grant_info);
|
||||||
enum Type type() const { return TRIGGER_FIELD_ITEM; }
|
enum Type type() const { return TRIGGER_FIELD_ITEM; }
|
||||||
@ -2133,8 +2187,39 @@ public:
|
|||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
access_types access_type;
|
void set_required_privilege(const bool rw);
|
||||||
|
bool set_value(THD *thd, sp_rcontext *ctx, Item *it);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Settable_routine_parameter *get_settable_routine_parameter()
|
||||||
|
{
|
||||||
|
return (read_only ? 0 : this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_value(THD *thd, Item *it)
|
||||||
|
{
|
||||||
|
return set_value(thd, NULL, it);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*
|
||||||
|
'want_privilege' holds privileges required to perform operation on
|
||||||
|
this trigger field (SELECT_ACL if we are going to read it and
|
||||||
|
UPDATE_ACL if we are going to update it). It is initialized at
|
||||||
|
parse time but can be updated later if this trigger field is used
|
||||||
|
as OUT or INOUT parameter of stored routine (in this case
|
||||||
|
set_required_privilege() is called to appropriately update
|
||||||
|
want_privilege and cleanup() is responsible for restoring of
|
||||||
|
original want_privilege once parameter's value is updated).
|
||||||
|
*/
|
||||||
|
ulong original_privilege;
|
||||||
|
ulong want_privilege;
|
||||||
GRANT_INFO *table_grants;
|
GRANT_INFO *table_grants;
|
||||||
|
/*
|
||||||
|
Trigger field is read-only unless it belongs to the NEW row in a
|
||||||
|
BEFORE INSERT of BEFORE UPDATE trigger.
|
||||||
|
*/
|
||||||
|
bool read_only;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -4120,6 +4120,18 @@ bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Item_func_get_user_var::set_value(THD *thd,
|
||||||
|
sp_rcontext */*ctx*/, Item *it)
|
||||||
|
{
|
||||||
|
Item_func_set_user_var *suv= new Item_func_set_user_var(get_name(), it);
|
||||||
|
/*
|
||||||
|
Item_func_set_user_var is not fixed after construction, call
|
||||||
|
fix_fields().
|
||||||
|
*/
|
||||||
|
return (!suv || suv->fix_fields(thd, &it) || suv->check() || suv->update());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref)
|
bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref)
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(fixed == 0);
|
DBUG_ASSERT(fixed == 0);
|
||||||
@ -4752,6 +4764,7 @@ Item_func_sp::sp_result_field(void) const
|
|||||||
dummy_table->alias = empty_name;
|
dummy_table->alias = empty_name;
|
||||||
dummy_table->maybe_null = maybe_null;
|
dummy_table->maybe_null = maybe_null;
|
||||||
dummy_table->in_use= current_thd;
|
dummy_table->in_use= current_thd;
|
||||||
|
dummy_table->copy_blobs= TRUE;
|
||||||
share->table_cache_key = empty_name;
|
share->table_cache_key = empty_name;
|
||||||
share->table_name = empty_name;
|
share->table_name = empty_name;
|
||||||
}
|
}
|
||||||
|
@ -1179,7 +1179,8 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class Item_func_get_user_var :public Item_func
|
class Item_func_get_user_var :public Item_func,
|
||||||
|
private Settable_routine_parameter
|
||||||
{
|
{
|
||||||
user_var_entry *var_entry;
|
user_var_entry *var_entry;
|
||||||
|
|
||||||
@ -1206,6 +1207,15 @@ public:
|
|||||||
table_map used_tables() const
|
table_map used_tables() const
|
||||||
{ return const_item() ? 0 : RAND_TABLE_BIT; }
|
{ return const_item() ? 0 : RAND_TABLE_BIT; }
|
||||||
bool eq(const Item *item, bool binary_cmp) const;
|
bool eq(const Item *item, bool binary_cmp) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool set_value(THD *thd, sp_rcontext *ctx, Item *it);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Settable_routine_parameter *get_settable_routine_parameter()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -718,12 +718,6 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name,
|
|||||||
List<create_field> &fields, List<Key> &keys,
|
List<create_field> &fields, List<Key> &keys,
|
||||||
bool tmp_table, uint select_field_count);
|
bool tmp_table, uint select_field_count);
|
||||||
|
|
||||||
TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
|
|
||||||
TABLE_LIST *create_table,
|
|
||||||
List<create_field> *extra_fields,
|
|
||||||
List<Key> *keys,
|
|
||||||
List<Item> *items,
|
|
||||||
MYSQL_LOCK **lock);
|
|
||||||
bool mysql_alter_table(THD *thd, char *new_db, char *new_name,
|
bool mysql_alter_table(THD *thd, char *new_db, char *new_name,
|
||||||
HA_CREATE_INFO *create_info,
|
HA_CREATE_INFO *create_info,
|
||||||
TABLE_LIST *table_list,
|
TABLE_LIST *table_list,
|
||||||
@ -1315,10 +1309,11 @@ extern struct st_VioSSLFd * ssl_acceptor_fd;
|
|||||||
|
|
||||||
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
|
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
|
||||||
uint flags, bool *need_reopen);
|
uint flags, bool *need_reopen);
|
||||||
/* mysql_lock_tables() flags bits */
|
/* mysql_lock_tables() and open_table() flags bits */
|
||||||
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK 0x0001
|
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK 0x0001
|
||||||
#define MYSQL_LOCK_IGNORE_FLUSH 0x0002
|
#define MYSQL_LOCK_IGNORE_FLUSH 0x0002
|
||||||
#define MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN 0x0004
|
#define MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN 0x0004
|
||||||
|
#define MYSQL_OPEN_IGNORE_LOCKED_TABLES 0x0008
|
||||||
|
|
||||||
void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
|
void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
|
||||||
void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
|
void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
|
||||||
|
@ -5475,7 +5475,7 @@ ER_SP_DUP_HANDLER 42000
|
|||||||
eng "Duplicate handler declared in the same block"
|
eng "Duplicate handler declared in the same block"
|
||||||
ger "Doppelter Handler im selben Block deklariert"
|
ger "Doppelter Handler im selben Block deklariert"
|
||||||
ER_SP_NOT_VAR_ARG 42000
|
ER_SP_NOT_VAR_ARG 42000
|
||||||
eng "OUT or INOUT argument %d for routine %s is not a variable"
|
eng "OUT or INOUT argument %d for routine %s is not a variable or NEW pseudo-variable in BEFORE trigger"
|
||||||
ger "OUT- oder INOUT-Argument %d f<>r Routine %s ist keine Variable"
|
ger "OUT- oder INOUT-Argument %d f<>r Routine %s ist keine Variable"
|
||||||
ER_SP_NO_RETSET 0A000
|
ER_SP_NO_RETSET 0A000
|
||||||
eng "Not allowed to return a result set from a %s"
|
eng "Not allowed to return a result set from a %s"
|
||||||
|
213
sql/sp_head.cc
213
sql/sp_head.cc
@ -936,6 +936,7 @@ sp_head::execute(THD *thd)
|
|||||||
bool err_status= FALSE;
|
bool err_status= FALSE;
|
||||||
uint ip= 0;
|
uint ip= 0;
|
||||||
ulong save_sql_mode;
|
ulong save_sql_mode;
|
||||||
|
bool save_abort_on_warning;
|
||||||
Query_arena *old_arena;
|
Query_arena *old_arena;
|
||||||
/* per-instruction arena */
|
/* per-instruction arena */
|
||||||
MEM_ROOT execute_mem_root;
|
MEM_ROOT execute_mem_root;
|
||||||
@ -996,6 +997,10 @@ sp_head::execute(THD *thd)
|
|||||||
thd->derived_tables= 0;
|
thd->derived_tables= 0;
|
||||||
save_sql_mode= thd->variables.sql_mode;
|
save_sql_mode= thd->variables.sql_mode;
|
||||||
thd->variables.sql_mode= m_sql_mode;
|
thd->variables.sql_mode= m_sql_mode;
|
||||||
|
save_abort_on_warning= thd->abort_on_warning;
|
||||||
|
thd->abort_on_warning=
|
||||||
|
(m_sql_mode & (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
It is also more efficient to save/restore current thd->lex once when
|
It is also more efficient to save/restore current thd->lex once when
|
||||||
do it in each instruction
|
do it in each instruction
|
||||||
@ -1128,6 +1133,7 @@ sp_head::execute(THD *thd)
|
|||||||
DBUG_ASSERT(!thd->derived_tables);
|
DBUG_ASSERT(!thd->derived_tables);
|
||||||
thd->derived_tables= old_derived_tables;
|
thd->derived_tables= old_derived_tables;
|
||||||
thd->variables.sql_mode= save_sql_mode;
|
thd->variables.sql_mode= save_sql_mode;
|
||||||
|
thd->abort_on_warning= save_abort_on_warning;
|
||||||
|
|
||||||
thd->stmt_arena= old_arena;
|
thd->stmt_arena= old_arena;
|
||||||
state= EXECUTED;
|
state= EXECUTED;
|
||||||
@ -1204,128 +1210,142 @@ bool
|
|||||||
sp_head::execute_function(THD *thd, Item **argp, uint argcount,
|
sp_head::execute_function(THD *thd, Item **argp, uint argcount,
|
||||||
Field *return_value_fld)
|
Field *return_value_fld)
|
||||||
{
|
{
|
||||||
Item_cache **param_values;
|
|
||||||
ulonglong binlog_save_options;
|
ulonglong binlog_save_options;
|
||||||
bool need_binlog_call;
|
bool need_binlog_call;
|
||||||
uint params;
|
uint arg_no;
|
||||||
sp_rcontext *octx = thd->spcont;
|
sp_rcontext *octx = thd->spcont;
|
||||||
sp_rcontext *nctx = NULL;
|
sp_rcontext *nctx = NULL;
|
||||||
|
char buf[STRING_BUFFER_USUAL_SIZE];
|
||||||
|
String binlog_buf(buf, sizeof(buf), &my_charset_bin);
|
||||||
bool err_status= FALSE;
|
bool err_status= FALSE;
|
||||||
|
MEM_ROOT call_mem_root;
|
||||||
|
Query_arena call_arena(&call_mem_root, Query_arena::INITIALIZED_FOR_SP);
|
||||||
|
Query_arena backup_arena;
|
||||||
|
|
||||||
DBUG_ENTER("sp_head::execute_function");
|
DBUG_ENTER("sp_head::execute_function");
|
||||||
DBUG_PRINT("info", ("function %s", m_name.str));
|
DBUG_PRINT("info", ("function %s", m_name.str));
|
||||||
|
|
||||||
params = m_pcont->context_var_count();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Check that the function is called with all specified arguments.
|
Check that the function is called with all specified arguments.
|
||||||
|
|
||||||
If it is not, use my_error() to report an error, or it will not terminate
|
If it is not, use my_error() to report an error, or it will not terminate
|
||||||
the invoking query properly.
|
the invoking query properly.
|
||||||
*/
|
*/
|
||||||
|
if (argcount != m_pcont->context_var_count())
|
||||||
if (argcount != params)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Need to use my_error here, or it will not terminate the
|
Need to use my_error here, or it will not terminate the
|
||||||
invoking query properly.
|
invoking query properly.
|
||||||
*/
|
*/
|
||||||
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0),
|
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0),
|
||||||
"FUNCTION", m_qname.str, params, argcount);
|
"FUNCTION", m_qname.str, m_pcont->context_var_count(), argcount);
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
/* Allocate param_values to be used for dumping the call into binlog. */
|
Prepare arena and memroot for objects which lifetime is whole
|
||||||
|
duration of function call (sp_rcontext, it's tables and items,
|
||||||
if (!(param_values= (Item_cache**)thd->alloc(sizeof(Item_cache*)*argcount)))
|
sp_cursor and Item_cache holders for case expressions).
|
||||||
DBUG_RETURN(TRUE);
|
We can't use caller's arena/memroot for those objects because
|
||||||
|
in this case some fixed amount of memory will be consumed for
|
||||||
// QQ Should have some error checking here? (types, etc...)
|
each function/trigger invocation and so statements which involve
|
||||||
|
lot of them will hog memory.
|
||||||
|
TODO: we should create sp_rcontext once per command and reuse
|
||||||
|
it on subsequent executions of a function/trigger.
|
||||||
|
*/
|
||||||
|
init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
|
||||||
|
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
|
||||||
|
|
||||||
if (!(nctx= new sp_rcontext(m_pcont, return_value_fld, octx)) ||
|
if (!(nctx= new sp_rcontext(m_pcont, return_value_fld, octx)) ||
|
||||||
nctx->init(thd))
|
nctx->init(thd))
|
||||||
{
|
{
|
||||||
delete nctx; /* Delete nctx if it was init() that failed. */
|
thd->restore_active_arena(&call_arena, &backup_arena);
|
||||||
DBUG_RETURN(TRUE);
|
err_status= TRUE;
|
||||||
|
goto err_with_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
We have to switch temporarily back to callers arena/memroot.
|
||||||
|
Function arguments belong to the caller and so the may reference
|
||||||
|
memory which they will allocate during calculation long after
|
||||||
|
this function call will be finished (e.g. in Item::cleanup()).
|
||||||
|
*/
|
||||||
|
thd->restore_active_arena(&call_arena, &backup_arena);
|
||||||
|
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
nctx->sp= this;
|
nctx->sp= this;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Pass arguments. */
|
/* Pass arguments. */
|
||||||
|
for (arg_no= 0; arg_no < argcount; arg_no++)
|
||||||
|
{
|
||||||
|
/* Arguments must be fixed in Item_func_sp::fix_fields */
|
||||||
|
DBUG_ASSERT(argp[arg_no]->fixed);
|
||||||
|
|
||||||
{
|
if ((err_status= nctx->set_variable(thd, arg_no, argp[arg_no])))
|
||||||
uint i;
|
goto err_with_cleanup;
|
||||||
|
|
||||||
for (i= 0 ; i < argcount ; i++)
|
|
||||||
{
|
|
||||||
if (!argp[i]->fixed && argp[i]->fix_fields(thd, &argp[i]))
|
|
||||||
{
|
|
||||||
err_status= TRUE;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
param_values[i]= Item_cache::get_cache(argp[i]->result_type());
|
need_binlog_call= mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG);
|
||||||
param_values[i]->store(argp[i]);
|
|
||||||
|
|
||||||
if (nctx->set_variable(thd, i, param_values[i]))
|
/*
|
||||||
|
Remember the original arguments for unrolled replication of functions
|
||||||
|
before they are changed by execution.
|
||||||
|
*/
|
||||||
|
if (need_binlog_call)
|
||||||
{
|
{
|
||||||
err_status= TRUE;
|
binlog_buf.length(0);
|
||||||
break;
|
binlog_buf.append(STRING_WITH_LEN("SELECT "));
|
||||||
}
|
append_identifier(thd, &binlog_buf, m_name.str, m_name.length);
|
||||||
}
|
binlog_buf.append('(');
|
||||||
}
|
for (arg_no= 0; arg_no < argcount; arg_no++)
|
||||||
|
|
||||||
if (err_status)
|
|
||||||
{
|
{
|
||||||
delete nctx;
|
String str_value_holder;
|
||||||
DBUG_RETURN(TRUE);
|
String *str_value;
|
||||||
}
|
|
||||||
|
|
||||||
|
if (arg_no)
|
||||||
|
binlog_buf.append(',');
|
||||||
|
|
||||||
|
str_value= sp_get_item_value(nctx->get_item(arg_no),
|
||||||
|
&str_value_holder);
|
||||||
|
|
||||||
|
if (str_value)
|
||||||
|
binlog_buf.append(*str_value);
|
||||||
|
else
|
||||||
|
binlog_buf.append(STRING_WITH_LEN("NULL"));
|
||||||
|
}
|
||||||
|
binlog_buf.append(')');
|
||||||
|
}
|
||||||
thd->spcont= nctx;
|
thd->spcont= nctx;
|
||||||
|
|
||||||
binlog_save_options= thd->options;
|
binlog_save_options= thd->options;
|
||||||
need_binlog_call= mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG);
|
|
||||||
if (need_binlog_call)
|
if (need_binlog_call)
|
||||||
{
|
{
|
||||||
reset_dynamic(&thd->user_var_events);
|
reset_dynamic(&thd->user_var_events);
|
||||||
mysql_bin_log.start_union_events(thd);
|
mysql_bin_log.start_union_events(thd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Switch to call arena/mem_root so objects like sp_cursor or
|
||||||
|
Item_cache holders for case expressions can be allocated on it.
|
||||||
|
|
||||||
|
TODO: In future we should associate call arena/mem_root with
|
||||||
|
sp_rcontext and allocate all these objects (and sp_rcontext
|
||||||
|
itself) on it directly rather than juggle with arenas.
|
||||||
|
*/
|
||||||
|
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
|
||||||
|
|
||||||
thd->options&= ~OPTION_BIN_LOG;
|
thd->options&= ~OPTION_BIN_LOG;
|
||||||
err_status= execute(thd);
|
err_status= execute(thd);
|
||||||
thd->options= binlog_save_options;
|
thd->options= binlog_save_options;
|
||||||
|
|
||||||
|
thd->restore_active_arena(&call_arena, &backup_arena);
|
||||||
|
|
||||||
if (need_binlog_call)
|
if (need_binlog_call)
|
||||||
mysql_bin_log.stop_union_events(thd);
|
mysql_bin_log.stop_union_events(thd);
|
||||||
|
|
||||||
if (need_binlog_call && thd->binlog_evt_union.unioned_events)
|
if (need_binlog_call && thd->binlog_evt_union.unioned_events)
|
||||||
{
|
{
|
||||||
char buf[256];
|
Query_log_event qinfo(thd, binlog_buf.ptr(), binlog_buf.length(),
|
||||||
String bufstr(buf, sizeof(buf), &my_charset_bin);
|
|
||||||
bufstr.length(0);
|
|
||||||
bufstr.append(STRING_WITH_LEN("SELECT "));
|
|
||||||
append_identifier(thd, &bufstr, m_name.str, m_name.length);
|
|
||||||
bufstr.append('(');
|
|
||||||
for (uint i=0; i < argcount; i++)
|
|
||||||
{
|
|
||||||
String str_value_holder;
|
|
||||||
String *str_value;
|
|
||||||
|
|
||||||
if (i)
|
|
||||||
bufstr.append(',');
|
|
||||||
|
|
||||||
str_value= sp_get_item_value(param_values[i], &str_value_holder);
|
|
||||||
|
|
||||||
if (str_value)
|
|
||||||
bufstr.append(*str_value);
|
|
||||||
else
|
|
||||||
bufstr.append(STRING_WITH_LEN("NULL"));
|
|
||||||
}
|
|
||||||
bufstr.append(')');
|
|
||||||
|
|
||||||
Query_log_event qinfo(thd, bufstr.ptr(), bufstr.length(),
|
|
||||||
thd->binlog_evt_union.unioned_events_trans, FALSE);
|
thd->binlog_evt_union.unioned_events_trans, FALSE);
|
||||||
if (mysql_bin_log.write(&qinfo) &&
|
if (mysql_bin_log.write(&qinfo) &&
|
||||||
thd->binlog_evt_union.unioned_events_trans)
|
thd->binlog_evt_union.unioned_events_trans)
|
||||||
@ -1348,27 +1368,19 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
nctx->pop_all_cursors(); // To avoid memory leaks after an error
|
nctx->pop_all_cursors(); // To avoid memory leaks after an error
|
||||||
|
|
||||||
|
err_with_cleanup:
|
||||||
delete nctx;
|
delete nctx;
|
||||||
|
call_arena.free_items();
|
||||||
|
free_root(&call_mem_root, MYF(0));
|
||||||
thd->spcont= octx;
|
thd->spcont= octx;
|
||||||
|
|
||||||
DBUG_RETURN(err_status);
|
DBUG_RETURN(err_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Item_func_get_user_var *item_is_user_var(Item *it)
|
|
||||||
{
|
|
||||||
if (it->type() == Item::FUNC_ITEM)
|
|
||||||
{
|
|
||||||
Item_func *fi= static_cast<Item_func*>(it);
|
|
||||||
|
|
||||||
if (fi->functype() == Item_func::GUSERVAR_FUNC)
|
|
||||||
return static_cast<Item_func_get_user_var*>(fi);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Execute a procedure.
|
Execute a procedure.
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
@ -1444,22 +1456,28 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||||||
for (uint i= 0 ; i < params ; i++)
|
for (uint i= 0 ; i < params ; i++)
|
||||||
{
|
{
|
||||||
Item *arg_item= it_args++;
|
Item *arg_item= it_args++;
|
||||||
sp_variable_t *spvar= m_pcont->find_variable(i);
|
|
||||||
|
|
||||||
if (!arg_item)
|
if (!arg_item)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
sp_variable_t *spvar= m_pcont->find_variable(i);
|
||||||
|
|
||||||
if (!spvar)
|
if (!spvar)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (spvar->mode != sp_param_in)
|
if (spvar->mode != sp_param_in)
|
||||||
{
|
{
|
||||||
if (!arg_item->is_splocal() && !item_is_user_var(arg_item))
|
Settable_routine_parameter *srp=
|
||||||
|
arg_item->get_settable_routine_parameter();
|
||||||
|
|
||||||
|
if (!srp)
|
||||||
{
|
{
|
||||||
my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, m_qname.str);
|
my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, m_qname.str);
|
||||||
err_status= TRUE;
|
err_status= TRUE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srp->set_required_privilege(spvar->mode == sp_param_inout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spvar->mode == sp_param_out)
|
if (spvar->mode == sp_param_out)
|
||||||
@ -1527,37 +1545,17 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||||||
if (spvar->mode == sp_param_in)
|
if (spvar->mode == sp_param_in)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (arg_item->is_splocal())
|
Settable_routine_parameter *srp=
|
||||||
{
|
arg_item->get_settable_routine_parameter();
|
||||||
if (octx->set_variable(thd,
|
|
||||||
((Item_splocal*) arg_item)->get_var_idx(),
|
DBUG_ASSERT(srp);
|
||||||
nctx->get_item(i)))
|
|
||||||
|
if (srp->set_value(thd, octx, nctx->get_item(i)))
|
||||||
{
|
{
|
||||||
err_status= TRUE;
|
err_status= TRUE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
Item_func_get_user_var *guv= item_is_user_var(arg_item);
|
|
||||||
|
|
||||||
if (guv)
|
|
||||||
{
|
|
||||||
Item *item= nctx->get_item(i);
|
|
||||||
Item_func_set_user_var *suv;
|
|
||||||
|
|
||||||
suv= new Item_func_set_user_var(guv->get_name(), item);
|
|
||||||
/*
|
|
||||||
Item_func_set_user_var is not fixed after construction,
|
|
||||||
call fix_fields().
|
|
||||||
*/
|
|
||||||
if ((err_status= test(!suv || suv->fix_fields(thd, &item) ||
|
|
||||||
suv->check() || suv->update())))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!save_spcont)
|
if (!save_spcont)
|
||||||
@ -2393,12 +2391,7 @@ sp_instr_set_trigger_field::execute(THD *thd, uint *nextp)
|
|||||||
int
|
int
|
||||||
sp_instr_set_trigger_field::exec_core(THD *thd, uint *nextp)
|
sp_instr_set_trigger_field::exec_core(THD *thd, uint *nextp)
|
||||||
{
|
{
|
||||||
int res= 0;
|
const int res= (trigger_field->set_value(thd, value) ? -1 : 0);
|
||||||
Item *it= sp_prepare_func_item(thd, &value);
|
|
||||||
if (!it ||
|
|
||||||
!trigger_field->fixed && trigger_field->fix_fields(thd, 0) ||
|
|
||||||
(it->save_in_field(trigger_field->field, 0) < 0))
|
|
||||||
res= -1;
|
|
||||||
*nextp = m_ip+1;
|
*nextp = m_ip+1;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -1160,6 +1160,8 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list)
|
|||||||
MYSQL_LOCK_IGNORE_FLUSH - Open table even if
|
MYSQL_LOCK_IGNORE_FLUSH - Open table even if
|
||||||
someone has done a flush or namelock on it.
|
someone has done a flush or namelock on it.
|
||||||
No version number checking is done.
|
No version number checking is done.
|
||||||
|
MYSQL_OPEN_IGNORE_LOCKED_TABLES - Open table
|
||||||
|
ignoring set of locked tables and prelocked mode.
|
||||||
|
|
||||||
IMPLEMENTATION
|
IMPLEMENTATION
|
||||||
Uses a cache of open tables to find a table not in use.
|
Uses a cache of open tables to find a table not in use.
|
||||||
@ -1219,7 +1221,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thd->locked_tables || thd->prelocked_mode)
|
if (!(flags & MYSQL_OPEN_IGNORE_LOCKED_TABLES) &&
|
||||||
|
(thd->locked_tables || thd->prelocked_mode))
|
||||||
{ // Using table locks
|
{ // Using table locks
|
||||||
TABLE *best_table= 0;
|
TABLE *best_table= 0;
|
||||||
int best_distance= INT_MIN;
|
int best_distance= INT_MIN;
|
||||||
|
@ -2437,6 +2437,153 @@ bool select_insert::send_eof()
|
|||||||
CREATE TABLE (SELECT) ...
|
CREATE TABLE (SELECT) ...
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create table from lists of fields and items (or open existing table
|
||||||
|
with same name).
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
create_table_from_items()
|
||||||
|
thd in Thread object
|
||||||
|
create_info in Create information (like MAX_ROWS, ENGINE or
|
||||||
|
temporary table flag)
|
||||||
|
create_table in Pointer to TABLE_LIST object providing database
|
||||||
|
and name for table to be created or to be open
|
||||||
|
extra_fields in/out Initial list of fields for table to be created
|
||||||
|
keys in List of keys for table to be created
|
||||||
|
items in List of items which should be used to produce rest
|
||||||
|
of fields for the table (corresponding fields will
|
||||||
|
be added to the end of 'extra_fields' list)
|
||||||
|
lock out Pointer to the MYSQL_LOCK object for table created
|
||||||
|
(open) will be returned in this parameter. Since
|
||||||
|
this table is not included in THD::lock caller is
|
||||||
|
responsible for explicitly unlocking this table.
|
||||||
|
|
||||||
|
NOTES
|
||||||
|
If 'create_info->options' bitmask has HA_LEX_CREATE_IF_NOT_EXISTS
|
||||||
|
flag and table with name provided already exists then this function will
|
||||||
|
simply open existing table.
|
||||||
|
Also note that create, open and lock sequence in this function is not
|
||||||
|
atomic and thus contains gap for deadlock and can cause other troubles.
|
||||||
|
Since this function contains some logic specific to CREATE TABLE ... SELECT
|
||||||
|
it should be changed before it can be used in other contexts.
|
||||||
|
|
||||||
|
RETURN VALUES
|
||||||
|
non-zero Pointer to TABLE object for table created or opened
|
||||||
|
0 Error
|
||||||
|
*/
|
||||||
|
|
||||||
|
static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
|
||||||
|
TABLE_LIST *create_table,
|
||||||
|
List<create_field> *extra_fields,
|
||||||
|
List<Key> *keys, List<Item> *items,
|
||||||
|
MYSQL_LOCK **lock)
|
||||||
|
{
|
||||||
|
TABLE tmp_table; // Used during 'create_field()'
|
||||||
|
TABLE *table= 0;
|
||||||
|
uint select_field_count= items->elements;
|
||||||
|
/* Add selected items to field list */
|
||||||
|
List_iterator_fast<Item> it(*items);
|
||||||
|
Item *item;
|
||||||
|
Field *tmp_field;
|
||||||
|
bool not_used;
|
||||||
|
DBUG_ENTER("create_table_from_items");
|
||||||
|
|
||||||
|
tmp_table.alias= 0;
|
||||||
|
tmp_table.timestamp_field= 0;
|
||||||
|
tmp_table.s= &tmp_table.share_not_to_be_used;
|
||||||
|
tmp_table.s->db_create_options=0;
|
||||||
|
tmp_table.s->blob_ptr_size= portable_sizeof_char_ptr;
|
||||||
|
tmp_table.s->db_low_byte_first= test(create_info->db_type == DB_TYPE_MYISAM ||
|
||||||
|
create_info->db_type == DB_TYPE_HEAP);
|
||||||
|
tmp_table.null_row=tmp_table.maybe_null=0;
|
||||||
|
|
||||||
|
while ((item=it++))
|
||||||
|
{
|
||||||
|
create_field *cr_field;
|
||||||
|
Field *field;
|
||||||
|
if (item->type() == Item::FUNC_ITEM)
|
||||||
|
field=item->tmp_table_field(&tmp_table);
|
||||||
|
else
|
||||||
|
field=create_tmp_field(thd, &tmp_table, item, item->type(),
|
||||||
|
(Item ***) 0, &tmp_field, 0, 0, 0, 0, 0);
|
||||||
|
if (!field ||
|
||||||
|
!(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ?
|
||||||
|
((Item_field *)item)->field :
|
||||||
|
(Field*) 0))))
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
if (item->maybe_null)
|
||||||
|
cr_field->flags &= ~NOT_NULL_FLAG;
|
||||||
|
extra_fields->push_back(cr_field);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
create and lock table
|
||||||
|
|
||||||
|
We don't log the statement, it will be logged later.
|
||||||
|
|
||||||
|
If this is a HEAP table, the automatic DELETE FROM which is written to the
|
||||||
|
binlog when a HEAP table is opened for the first time since startup, must
|
||||||
|
not be written: 1) it would be wrong (imagine we're in CREATE SELECT: we
|
||||||
|
don't want to delete from it) 2) it would be written before the CREATE
|
||||||
|
TABLE, which is a wrong order. So we keep binary logging disabled when we
|
||||||
|
open_table().
|
||||||
|
NOTE: By locking table which we just have created (or for which we just have
|
||||||
|
have found that it already exists) separately from other tables used by the
|
||||||
|
statement we create potential window for deadlock.
|
||||||
|
TODO: create and open should be done atomic !
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
tmp_disable_binlog(thd);
|
||||||
|
if (!mysql_create_table(thd, create_table->db, create_table->table_name,
|
||||||
|
create_info, *extra_fields, *keys, 0,
|
||||||
|
select_field_count))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
If we are here in prelocked mode we either create temporary table
|
||||||
|
or prelocked mode is caused by the SELECT part of this statement.
|
||||||
|
*/
|
||||||
|
DBUG_ASSERT(!thd->prelocked_mode ||
|
||||||
|
create_info->options & HA_LEX_CREATE_TMP_TABLE ||
|
||||||
|
thd->lex->requires_prelocking());
|
||||||
|
|
||||||
|
/*
|
||||||
|
NOTE: We don't want to ignore set of locked tables here if we are
|
||||||
|
under explicit LOCK TABLES since it will open gap for deadlock
|
||||||
|
too wide (and also is not backward compatible).
|
||||||
|
*/
|
||||||
|
if (! (table= open_table(thd, create_table, thd->mem_root, (bool*) 0,
|
||||||
|
(MYSQL_LOCK_IGNORE_FLUSH |
|
||||||
|
((thd->prelocked_mode == PRELOCKED) ?
|
||||||
|
MYSQL_OPEN_IGNORE_LOCKED_TABLES:0)))))
|
||||||
|
quick_rm_table(create_info->db_type, create_table->db,
|
||||||
|
table_case_name(create_info, create_table->table_name));
|
||||||
|
}
|
||||||
|
reenable_binlog(thd);
|
||||||
|
if (!table) // open failed
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
FIXME: What happens if trigger manages to be created while we are
|
||||||
|
obtaining this lock ? May be it is sensible just to disable
|
||||||
|
trigger execution in this case ? Or will MYSQL_LOCK_IGNORE_FLUSH
|
||||||
|
save us from that ?
|
||||||
|
*/
|
||||||
|
table->reginfo.lock_type=TL_WRITE;
|
||||||
|
if (! ((*lock)= mysql_lock_tables(thd, &table, 1,
|
||||||
|
MYSQL_LOCK_IGNORE_FLUSH, ¬_used)))
|
||||||
|
{
|
||||||
|
VOID(pthread_mutex_lock(&LOCK_open));
|
||||||
|
hash_delete(&open_cache,(byte*) table);
|
||||||
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||||
|
quick_rm_table(create_info->db_type, create_table->db,
|
||||||
|
table_case_name(create_info, create_table->table_name));
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
|
table->file->extra(HA_EXTRA_WRITE_CACHE);
|
||||||
|
DBUG_RETURN(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
|
select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
|
||||||
{
|
{
|
||||||
|
@ -5709,6 +5709,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
DBUG_ASSERT(thd->net.report_error);
|
||||||
DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
|
DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
|
||||||
thd->is_fatal_error));
|
thd->is_fatal_error));
|
||||||
query_cache_abort(&thd->net);
|
query_cache_abort(&thd->net);
|
||||||
@ -7226,7 +7227,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
|
|||||||
lex->create_info.merge_list.first))
|
lex->create_info.merge_list.first))
|
||||||
goto err;
|
goto err;
|
||||||
if (grant_option && want_priv != CREATE_TMP_ACL &&
|
if (grant_option && want_priv != CREATE_TMP_ACL &&
|
||||||
check_grant(thd, want_priv, create_table, 0, UINT_MAX, 0))
|
check_grant(thd, want_priv, create_table, 0, 1, 0))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (select_lex->item_list.elements)
|
if (select_lex->item_list.elements)
|
||||||
|
@ -1795,105 +1795,6 @@ make_unique_key_name(const char *field_name,KEY *start,KEY *end)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************
|
|
||||||
** Create table from a list of fields and items
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
|
|
||||||
TABLE_LIST *create_table,
|
|
||||||
List<create_field> *extra_fields,
|
|
||||||
List<Key> *keys,
|
|
||||||
List<Item> *items,
|
|
||||||
MYSQL_LOCK **lock)
|
|
||||||
{
|
|
||||||
TABLE tmp_table; // Used during 'create_field()'
|
|
||||||
TABLE *table= 0;
|
|
||||||
uint select_field_count= items->elements;
|
|
||||||
/* Add selected items to field list */
|
|
||||||
List_iterator_fast<Item> it(*items);
|
|
||||||
Item *item;
|
|
||||||
Field *tmp_field;
|
|
||||||
bool not_used;
|
|
||||||
DBUG_ENTER("create_table_from_items");
|
|
||||||
|
|
||||||
tmp_table.alias= 0;
|
|
||||||
tmp_table.timestamp_field= 0;
|
|
||||||
tmp_table.s= &tmp_table.share_not_to_be_used;
|
|
||||||
tmp_table.s->db_create_options=0;
|
|
||||||
tmp_table.s->blob_ptr_size= portable_sizeof_char_ptr;
|
|
||||||
tmp_table.s->db_low_byte_first= test(create_info->db_type == DB_TYPE_MYISAM ||
|
|
||||||
create_info->db_type == DB_TYPE_HEAP);
|
|
||||||
tmp_table.null_row=tmp_table.maybe_null=0;
|
|
||||||
|
|
||||||
while ((item=it++))
|
|
||||||
{
|
|
||||||
create_field *cr_field;
|
|
||||||
Field *field;
|
|
||||||
if (item->type() == Item::FUNC_ITEM)
|
|
||||||
field=item->tmp_table_field(&tmp_table);
|
|
||||||
else
|
|
||||||
field=create_tmp_field(thd, &tmp_table, item, item->type(),
|
|
||||||
(Item ***) 0, &tmp_field, 0, 0, 0, 0, 0);
|
|
||||||
if (!field ||
|
|
||||||
!(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ?
|
|
||||||
((Item_field *)item)->field :
|
|
||||||
(Field*) 0))))
|
|
||||||
DBUG_RETURN(0);
|
|
||||||
if (item->maybe_null)
|
|
||||||
cr_field->flags &= ~NOT_NULL_FLAG;
|
|
||||||
extra_fields->push_back(cr_field);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
create and lock table
|
|
||||||
|
|
||||||
We don't log the statement, it will be logged later.
|
|
||||||
|
|
||||||
If this is a HEAP table, the automatic DELETE FROM which is written to the
|
|
||||||
binlog when a HEAP table is opened for the first time since startup, must
|
|
||||||
not be written: 1) it would be wrong (imagine we're in CREATE SELECT: we
|
|
||||||
don't want to delete from it) 2) it would be written before the CREATE
|
|
||||||
TABLE, which is a wrong order. So we keep binary logging disabled when we
|
|
||||||
open_table().
|
|
||||||
TODO: create and open should be done atomic !
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
tmp_disable_binlog(thd);
|
|
||||||
if (!mysql_create_table(thd, create_table->db, create_table->table_name,
|
|
||||||
create_info, *extra_fields, *keys, 0,
|
|
||||||
select_field_count))
|
|
||||||
{
|
|
||||||
if (! (table= open_table(thd, create_table, thd->mem_root, (bool*) 0,
|
|
||||||
MYSQL_LOCK_IGNORE_FLUSH)))
|
|
||||||
quick_rm_table(create_info->db_type, create_table->db,
|
|
||||||
table_case_name(create_info, create_table->table_name));
|
|
||||||
}
|
|
||||||
reenable_binlog(thd);
|
|
||||||
if (!table) // open failed
|
|
||||||
DBUG_RETURN(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
FIXME: What happens if trigger manages to be created while we are
|
|
||||||
obtaining this lock ? May be it is sensible just to disable
|
|
||||||
trigger execution in this case ? Or will MYSQL_LOCK_IGNORE_FLUSH
|
|
||||||
save us from that ?
|
|
||||||
*/
|
|
||||||
table->reginfo.lock_type=TL_WRITE;
|
|
||||||
if (! ((*lock)= mysql_lock_tables(thd, &table, 1,
|
|
||||||
MYSQL_LOCK_IGNORE_FLUSH, ¬_used)))
|
|
||||||
{
|
|
||||||
VOID(pthread_mutex_lock(&LOCK_open));
|
|
||||||
hash_delete(&open_cache,(byte*) table);
|
|
||||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
|
||||||
quick_rm_table(create_info->db_type, create_table->db,
|
|
||||||
table_case_name(create_info, create_table->table_name));
|
|
||||||
DBUG_RETURN(0);
|
|
||||||
}
|
|
||||||
table->file->extra(HA_EXTRA_WRITE_CACHE);
|
|
||||||
DBUG_RETURN(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
** Alter a table definition
|
** Alter a table definition
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
@ -5799,7 +5799,11 @@ select_var_ident:
|
|||||||
if (lex->result)
|
if (lex->result)
|
||||||
((select_dumpvar *)lex->result)->var_list.push_back( new my_var($2,0,0,(enum_field_types)0));
|
((select_dumpvar *)lex->result)->var_list.push_back( new my_var($2,0,0,(enum_field_types)0));
|
||||||
else
|
else
|
||||||
YYABORT;
|
/*
|
||||||
|
The parser won't create select_result instance only
|
||||||
|
if it's an EXPLAIN.
|
||||||
|
*/
|
||||||
|
DBUG_ASSERT(lex->describe);
|
||||||
}
|
}
|
||||||
| ident_or_text
|
| ident_or_text
|
||||||
{
|
{
|
||||||
@ -5811,9 +5815,7 @@ select_var_ident:
|
|||||||
my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str);
|
my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str);
|
||||||
YYABORT;
|
YYABORT;
|
||||||
}
|
}
|
||||||
if (! lex->result)
|
if (lex->result)
|
||||||
YYABORT;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
my_var *var;
|
my_var *var;
|
||||||
((select_dumpvar *)lex->result)->
|
((select_dumpvar *)lex->result)->
|
||||||
@ -5823,6 +5825,14 @@ select_var_ident:
|
|||||||
var->sp= lex->sphead;
|
var->sp= lex->sphead;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
The parser won't create select_result instance only
|
||||||
|
if it's an EXPLAIN.
|
||||||
|
*/
|
||||||
|
DBUG_ASSERT(lex->describe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -7210,12 +7220,18 @@ simple_ident_q:
|
|||||||
YYABORT;
|
YYABORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DBUG_ASSERT(!new_row ||
|
||||||
|
(lex->trg_chistics.event == TRG_EVENT_INSERT ||
|
||||||
|
lex->trg_chistics.event == TRG_EVENT_UPDATE));
|
||||||
|
const bool read_only=
|
||||||
|
!(new_row && lex->trg_chistics.action_time == TRG_ACTION_BEFORE);
|
||||||
if (!(trg_fld= new Item_trigger_field(Lex->current_context(),
|
if (!(trg_fld= new Item_trigger_field(Lex->current_context(),
|
||||||
new_row ?
|
new_row ?
|
||||||
Item_trigger_field::NEW_ROW:
|
Item_trigger_field::NEW_ROW:
|
||||||
Item_trigger_field::OLD_ROW,
|
Item_trigger_field::OLD_ROW,
|
||||||
$3.str,
|
$3.str,
|
||||||
Item_trigger_field::AT_READ)))
|
SELECT_ACL,
|
||||||
|
read_only)))
|
||||||
YYABORT;
|
YYABORT;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -7851,11 +7867,13 @@ sys_option_value:
|
|||||||
it= new Item_null();
|
it= new Item_null();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DBUG_ASSERT(lex->trg_chistics.action_time == TRG_ACTION_BEFORE &&
|
||||||
|
(lex->trg_chistics.event == TRG_EVENT_INSERT ||
|
||||||
|
lex->trg_chistics.event == TRG_EVENT_UPDATE));
|
||||||
if (!(trg_fld= new Item_trigger_field(Lex->current_context(),
|
if (!(trg_fld= new Item_trigger_field(Lex->current_context(),
|
||||||
Item_trigger_field::NEW_ROW,
|
Item_trigger_field::NEW_ROW,
|
||||||
$2.base_name.str,
|
$2.base_name.str,
|
||||||
Item_trigger_field::AT_UPDATE)
|
UPDATE_ACL, FALSE)) ||
|
||||||
) ||
|
|
||||||
!(sp_fld= new sp_instr_set_trigger_field(lex->sphead->
|
!(sp_fld= new sp_instr_set_trigger_field(lex->sphead->
|
||||||
instructions(),
|
instructions(),
|
||||||
lex->spcont,
|
lex->spcont,
|
||||||
|
Reference in New Issue
Block a user