mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
Merge mysql.com:/usr/local/bk/mysql-5.0
into mysql.com:/home/pem/work/mysql-5.0 mysql-test/r/sp-error.result: Auto merged mysql-test/r/sp.result: Auto merged mysql-test/t/sp-error.test: Auto merged mysql-test/t/sp.test: Auto merged sql/sp.cc: Auto merged sql/sql_yacc.yy: Auto merged
This commit is contained in:
@ -48,7 +48,7 @@ mysql_test_run_new_SOURCES= mysql_test_run_new.c my_manage.c my_create_tables.c
|
|||||||
dist-hook:
|
dist-hook:
|
||||||
mkdir -p $(distdir)/t $(distdir)/r $(distdir)/include \
|
mkdir -p $(distdir)/t $(distdir)/r $(distdir)/include \
|
||||||
$(distdir)/std_data $(distdir)/lib
|
$(distdir)/std_data $(distdir)/lib
|
||||||
$(INSTALL_DATA) $(srcdir)/t/*.def $(distdir)/t
|
-$(INSTALL_DATA) $(srcdir)/t/*.def $(distdir)/t
|
||||||
$(INSTALL_DATA) $(srcdir)/t/*.test $(distdir)/t
|
$(INSTALL_DATA) $(srcdir)/t/*.test $(distdir)/t
|
||||||
$(INSTALL_DATA) $(srcdir)/t/*.sql $(distdir)/t
|
$(INSTALL_DATA) $(srcdir)/t/*.sql $(distdir)/t
|
||||||
-$(INSTALL_DATA) $(srcdir)/t/*.disabled $(distdir)/t
|
-$(INSTALL_DATA) $(srcdir)/t/*.disabled $(distdir)/t
|
||||||
@ -71,7 +71,7 @@ install-data-local:
|
|||||||
$(DESTDIR)$(testdir)/std_data \
|
$(DESTDIR)$(testdir)/std_data \
|
||||||
$(DESTDIR)$(testdir)/lib
|
$(DESTDIR)$(testdir)/lib
|
||||||
$(INSTALL_DATA) $(srcdir)/README $(DESTDIR)$(testdir)
|
$(INSTALL_DATA) $(srcdir)/README $(DESTDIR)$(testdir)
|
||||||
$(INSTALL_DATA) $(srcdir)/t/*.def $(DESTDIR)$(testdir)/t
|
-$(INSTALL_DATA) $(srcdir)/t/*.def $(DESTDIR)$(testdir)/t
|
||||||
$(INSTALL_DATA) $(srcdir)/t/*.test $(DESTDIR)$(testdir)/t
|
$(INSTALL_DATA) $(srcdir)/t/*.test $(DESTDIR)$(testdir)/t
|
||||||
$(INSTALL_DATA) $(srcdir)/t/*.sql $(DESTDIR)$(testdir)/t
|
$(INSTALL_DATA) $(srcdir)/t/*.sql $(DESTDIR)$(testdir)/t
|
||||||
-$(INSTALL_DATA) $(srcdir)/t/*.disabled $(DESTDIR)$(testdir)/t
|
-$(INSTALL_DATA) $(srcdir)/t/*.disabled $(DESTDIR)$(testdir)/t
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -705,3 +705,19 @@ end|
|
|||||||
call bug11394(2, 1)|
|
call bug11394(2, 1)|
|
||||||
ERROR HY000: Recursive stored routines are not allowed.
|
ERROR HY000: Recursive stored routines are not allowed.
|
||||||
drop procedure bug11394|
|
drop procedure bug11394|
|
||||||
|
drop function if exists bug11834_1;
|
||||||
|
drop function if exists bug11834_2;
|
||||||
|
create function bug11834_1() returns int return 10;
|
||||||
|
create function bug11834_2() returns int return bug11834_1();
|
||||||
|
prepare stmt from "select bug11834_2()";
|
||||||
|
execute stmt;
|
||||||
|
bug11834_2()
|
||||||
|
10
|
||||||
|
execute stmt;
|
||||||
|
bug11834_2()
|
||||||
|
10
|
||||||
|
drop function bug11834_1;
|
||||||
|
execute stmt;
|
||||||
|
ERROR 42000: FUNCTION test.bug11834_1 does not exist
|
||||||
|
deallocate prepare stmt;
|
||||||
|
drop function bug11834_2;
|
||||||
|
@ -1088,7 +1088,7 @@ a
|
|||||||
select * from v1|
|
select * from v1|
|
||||||
a
|
a
|
||||||
3
|
3
|
||||||
select * from v1, v2|
|
select * from v1, t1|
|
||||||
ERROR HY000: Table 't1' was not locked with LOCK TABLES
|
ERROR HY000: Table 't1' was not locked with LOCK TABLES
|
||||||
select f4()|
|
select f4()|
|
||||||
ERROR HY000: Table 't2' was not locked with LOCK TABLES
|
ERROR HY000: Table 't2' was not locked with LOCK TABLES
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
drop table if exists t1, t2;
|
drop table if exists t1, t2, t3;
|
||||||
drop view if exists v1;
|
drop view if exists v1;
|
||||||
drop database if exists mysqltest;
|
drop database if exists mysqltest;
|
||||||
|
drop function if exists f1;
|
||||||
create table t1 (i int);
|
create table t1 (i int);
|
||||||
create trigger trg before insert on t1 for each row set @a:=1;
|
create trigger trg before insert on t1 for each row set @a:=1;
|
||||||
set @a:=0;
|
set @a:=0;
|
||||||
@ -182,6 +183,96 @@ select @log;
|
|||||||
@log
|
@log
|
||||||
(BEFORE_INSERT: new=(id=1, data=5))(BEFORE_UPDATE: old=(id=1, data=4) new=(id=1, data=6))(AFTER_UPDATE: old=(id=1, data=4) new=(id=1, data=6))(BEFORE_INSERT: new=(id=3, data=3))(AFTER_INSERT: new=(id=3, data=3))
|
(BEFORE_INSERT: new=(id=1, data=5))(BEFORE_UPDATE: old=(id=1, data=4) new=(id=1, data=6))(AFTER_UPDATE: old=(id=1, data=4) new=(id=1, data=6))(BEFORE_INSERT: new=(id=3, data=3))(AFTER_INSERT: new=(id=3, data=3))
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
create table t1 (id int primary key, data varchar(10), fk int);
|
||||||
|
create table t2 (event varchar(100));
|
||||||
|
create table t3 (id int primary key);
|
||||||
|
create trigger t1_ai after insert on t1 for each row
|
||||||
|
insert into t2 values (concat("INSERT INTO t1 id=", new.id, " data='", new.data, "'"));
|
||||||
|
insert into t1 (id, data) values (1, "one"), (2, "two");
|
||||||
|
select * from t1;
|
||||||
|
id data fk
|
||||||
|
1 one NULL
|
||||||
|
2 two NULL
|
||||||
|
select * from t2;
|
||||||
|
event
|
||||||
|
INSERT INTO t1 id=1 data='one'
|
||||||
|
INSERT INTO t1 id=2 data='two'
|
||||||
|
drop trigger t1.t1_ai;
|
||||||
|
create trigger t1_bi before insert on t1 for each row
|
||||||
|
begin
|
||||||
|
if exists (select id from t3 where id=new.fk) then
|
||||||
|
insert into t2 values (concat("INSERT INTO t1 id=", new.id, " data='", new.data, "' fk=", new.fk));
|
||||||
|
else
|
||||||
|
insert into t2 values (concat("INSERT INTO t1 FAILED id=", new.id, " data='", new.data, "' fk=", new.fk));
|
||||||
|
set new.id= NULL;
|
||||||
|
end if;
|
||||||
|
end|
|
||||||
|
insert into t3 values (1);
|
||||||
|
insert into t1 values (4, "four", 1), (5, "five", 2);
|
||||||
|
ERROR 23000: Column 'id' cannot be null
|
||||||
|
select * from t1;
|
||||||
|
id data fk
|
||||||
|
1 one NULL
|
||||||
|
2 two NULL
|
||||||
|
4 four 1
|
||||||
|
select * from t2;
|
||||||
|
event
|
||||||
|
INSERT INTO t1 id=1 data='one'
|
||||||
|
INSERT INTO t1 id=2 data='two'
|
||||||
|
INSERT INTO t1 id=4 data='four' fk=1
|
||||||
|
INSERT INTO t1 FAILED id=5 data='five' fk=2
|
||||||
|
drop table t1, t2, t3;
|
||||||
|
create table t1 (id int primary key, data varchar(10));
|
||||||
|
create table t2 (seq int);
|
||||||
|
insert into t2 values (10);
|
||||||
|
create function f1 () returns int return (select max(seq) from t2);
|
||||||
|
create trigger t1_bi before insert on t1 for each row
|
||||||
|
begin
|
||||||
|
if new.id > f1() then
|
||||||
|
set new.id:= f1();
|
||||||
|
end if;
|
||||||
|
end|
|
||||||
|
select f1();
|
||||||
|
f1()
|
||||||
|
10
|
||||||
|
insert into t1 values (1, "first");
|
||||||
|
insert into t1 values (f1(), "max");
|
||||||
|
select * from t1;
|
||||||
|
id data
|
||||||
|
1 first
|
||||||
|
10 max
|
||||||
|
drop table t1, t2;
|
||||||
|
drop function f1;
|
||||||
|
create table t1 (id int primary key, fk_t2 int);
|
||||||
|
create table t2 (id int primary key, fk_t3 int);
|
||||||
|
create table t3 (id int primary key);
|
||||||
|
insert into t1 values (1,1), (2,1), (3,2);
|
||||||
|
insert into t2 values (1,1), (2,2);
|
||||||
|
insert into t3 values (1), (2);
|
||||||
|
create trigger t3_ad after delete on t3 for each row
|
||||||
|
delete from t2 where fk_t3=old.id;
|
||||||
|
create trigger t2_ad after delete on t2 for each row
|
||||||
|
delete from t1 where fk_t2=old.id;
|
||||||
|
delete from t3 where id = 1;
|
||||||
|
select * from t1 left join (t2 left join t3 on t2.fk_t3 = t3.id) on t1.fk_t2 = t2.id;
|
||||||
|
id fk_t2 id fk_t3 id
|
||||||
|
3 2 2 2 2
|
||||||
|
drop table t1, t2, t3;
|
||||||
|
create table t1 (id int primary key, copy int);
|
||||||
|
create table t2 (id int primary key, data int);
|
||||||
|
insert into t2 values (1,1), (2,2);
|
||||||
|
create trigger t1_bi before insert on t1 for each row
|
||||||
|
set new.copy= (select data from t2 where id = new.id);
|
||||||
|
create trigger t1_bu before update on t1 for each row
|
||||||
|
set new.copy= (select data from t2 where id = new.id);
|
||||||
|
insert into t1 values (1,3), (2,4), (3,3);
|
||||||
|
update t1 set copy= 1 where id = 2;
|
||||||
|
select * from t1;
|
||||||
|
id copy
|
||||||
|
1 1
|
||||||
|
2 2
|
||||||
|
3 NULL
|
||||||
|
drop table t1, t2;
|
||||||
create table t1 (i int);
|
create table t1 (i int);
|
||||||
create trigger trg before insert on t1 for each row set @a:= old.i;
|
create trigger trg before insert on t1 for each row set @a:= old.i;
|
||||||
ERROR HY000: There is no OLD row in on INSERT trigger
|
ERROR HY000: There is no OLD row in on INSERT trigger
|
||||||
@ -482,12 +573,3 @@ i k ts
|
|||||||
1 1 0000-00-00 00:00:00
|
1 1 0000-00-00 00:00:00
|
||||||
2 2 0000-00-00 00:00:00
|
2 2 0000-00-00 00:00:00
|
||||||
drop table t1, t2;
|
drop table t1, t2;
|
||||||
drop function if exists bug5893;
|
|
||||||
create table t1 (col1 int, col2 int);
|
|
||||||
insert into t1 values (1, 2);
|
|
||||||
create function bug5893 () returns int return 5;
|
|
||||||
create trigger t1_bu before update on t1 for each row set new.col1= bug5893();
|
|
||||||
drop function bug5893;
|
|
||||||
update t1 set col2 = 4;
|
|
||||||
ERROR 42000: FUNCTION test.bug5893 does not exist
|
|
||||||
drop table t1;
|
|
||||||
|
@ -1313,6 +1313,11 @@ INSERT INTO t2 VALUES (4,011403,37,'intercepted','audiology','tinily','');
|
|||||||
SELECT * FROM t2;
|
SELECT * FROM t2;
|
||||||
OPTIMIZE TABLE t2;
|
OPTIMIZE TABLE t2;
|
||||||
SELECT * FROM t2;
|
SELECT * FROM t2;
|
||||||
|
INSERT INTO t2 VALUES (2,011401,37,'breaking','dreaded','Steinberg','W');
|
||||||
|
INSERT INTO t2 VALUES (3,011402,37,'Romans','scholastics','jarring','');
|
||||||
|
INSERT INTO t2 VALUES (4,011403,37,'intercepted','audiology','tinily','');
|
||||||
|
OPTIMIZE TABLE t2 EXTENDED;
|
||||||
|
SELECT * FROM t2;
|
||||||
REPAIR TABLE t2;
|
REPAIR TABLE t2;
|
||||||
SELECT * FROM t2;
|
SELECT * FROM t2;
|
||||||
|
|
||||||
|
@ -1025,4 +1025,27 @@ end|
|
|||||||
--error 1424
|
--error 1424
|
||||||
call bug11394(2, 1)|
|
call bug11394(2, 1)|
|
||||||
drop procedure bug11394|
|
drop procedure bug11394|
|
||||||
delimiter |;
|
delimiter ;|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Bug#11834 "Re-execution of prepared statement with dropped function
|
||||||
|
# crashes server". Also tests handling of prepared stmts which use
|
||||||
|
# stored functions but does not require prelocking.
|
||||||
|
#
|
||||||
|
--disable_warnings
|
||||||
|
drop function if exists bug11834_1;
|
||||||
|
drop function if exists bug11834_2;
|
||||||
|
--enable_warnings
|
||||||
|
create function bug11834_1() returns int return 10;
|
||||||
|
create function bug11834_2() returns int return bug11834_1();
|
||||||
|
prepare stmt from "select bug11834_2()";
|
||||||
|
execute stmt;
|
||||||
|
# Re-execution of statement should not crash server.
|
||||||
|
execute stmt;
|
||||||
|
drop function bug11834_1;
|
||||||
|
# Attempt to execute statement should return proper error and
|
||||||
|
# should not crash server.
|
||||||
|
--error ER_SP_DOES_NOT_EXIST
|
||||||
|
execute stmt;
|
||||||
|
deallocate prepare stmt;
|
||||||
|
drop function bug11834_2;
|
||||||
|
@ -1283,7 +1283,8 @@ select * from v1|
|
|||||||
# views and functions ?
|
# views and functions ?
|
||||||
create function f1() returns int
|
create function f1() returns int
|
||||||
return (select sum(data) from t1) + (select sum(data) from v1)|
|
return (select sum(data) from t1) + (select sum(data) from v1)|
|
||||||
# FIXME All these just exceed file limit for me :)
|
# This queries will crash server because we can't use LEX in
|
||||||
|
# reenterable fashion yet. Patch disabling recursion will heal this.
|
||||||
#select f1()|
|
#select f1()|
|
||||||
#select * from v1|
|
#select * from v1|
|
||||||
#select * from v2|
|
#select * from v2|
|
||||||
@ -1328,15 +1329,12 @@ select * from v2|
|
|||||||
select * from v1|
|
select * from v1|
|
||||||
# These should not work as we have too little instances of tables locked
|
# These should not work as we have too little instances of tables locked
|
||||||
--error 1100
|
--error 1100
|
||||||
select * from v1, v2|
|
select * from v1, t1|
|
||||||
--error 1100
|
--error 1100
|
||||||
select f4()|
|
select f4()|
|
||||||
unlock tables|
|
unlock tables|
|
||||||
|
|
||||||
|
|
||||||
# TODO We also should test integration with triggers
|
|
||||||
|
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
drop function f0|
|
drop function f0|
|
||||||
drop function f1|
|
drop function f1|
|
||||||
|
@ -3,9 +3,10 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
--disable_warnings
|
--disable_warnings
|
||||||
drop table if exists t1, t2;
|
drop table if exists t1, t2, t3;
|
||||||
drop view if exists v1;
|
drop view if exists v1;
|
||||||
drop database if exists mysqltest;
|
drop database if exists mysqltest;
|
||||||
|
drop function if exists f1;
|
||||||
--enable_warnings
|
--enable_warnings
|
||||||
|
|
||||||
create table t1 (i int);
|
create table t1 (i int);
|
||||||
@ -199,6 +200,86 @@ select @log;
|
|||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Let us test triggers which access other tables.
|
||||||
|
#
|
||||||
|
# Trivial trigger which inserts data into another table
|
||||||
|
create table t1 (id int primary key, data varchar(10), fk int);
|
||||||
|
create table t2 (event varchar(100));
|
||||||
|
create table t3 (id int primary key);
|
||||||
|
create trigger t1_ai after insert on t1 for each row
|
||||||
|
insert into t2 values (concat("INSERT INTO t1 id=", new.id, " data='", new.data, "'"));
|
||||||
|
insert into t1 (id, data) values (1, "one"), (2, "two");
|
||||||
|
select * from t1;
|
||||||
|
select * from t2;
|
||||||
|
drop trigger t1.t1_ai;
|
||||||
|
# Trigger which uses couple of tables (and partially emulates FK constraint)
|
||||||
|
delimiter |;
|
||||||
|
create trigger t1_bi before insert on t1 for each row
|
||||||
|
begin
|
||||||
|
if exists (select id from t3 where id=new.fk) then
|
||||||
|
insert into t2 values (concat("INSERT INTO t1 id=", new.id, " data='", new.data, "' fk=", new.fk));
|
||||||
|
else
|
||||||
|
insert into t2 values (concat("INSERT INTO t1 FAILED id=", new.id, " data='", new.data, "' fk=", new.fk));
|
||||||
|
set new.id= NULL;
|
||||||
|
end if;
|
||||||
|
end|
|
||||||
|
delimiter ;|
|
||||||
|
insert into t3 values (1);
|
||||||
|
--error 1048
|
||||||
|
insert into t1 values (4, "four", 1), (5, "five", 2);
|
||||||
|
select * from t1;
|
||||||
|
select * from t2;
|
||||||
|
drop table t1, t2, t3;
|
||||||
|
# Trigger which invokes function
|
||||||
|
create table t1 (id int primary key, data varchar(10));
|
||||||
|
create table t2 (seq int);
|
||||||
|
insert into t2 values (10);
|
||||||
|
create function f1 () returns int return (select max(seq) from t2);
|
||||||
|
delimiter |;
|
||||||
|
create trigger t1_bi before insert on t1 for each row
|
||||||
|
begin
|
||||||
|
if new.id > f1() then
|
||||||
|
set new.id:= f1();
|
||||||
|
end if;
|
||||||
|
end|
|
||||||
|
delimiter ;|
|
||||||
|
# Remove this once bug #11554 will be fixed.
|
||||||
|
select f1();
|
||||||
|
insert into t1 values (1, "first");
|
||||||
|
insert into t1 values (f1(), "max");
|
||||||
|
select * from t1;
|
||||||
|
drop table t1, t2;
|
||||||
|
drop function f1;
|
||||||
|
# Trigger which forces invocation of another trigger
|
||||||
|
# (emulation of FK on delete cascade policy)
|
||||||
|
create table t1 (id int primary key, fk_t2 int);
|
||||||
|
create table t2 (id int primary key, fk_t3 int);
|
||||||
|
create table t3 (id int primary key);
|
||||||
|
insert into t1 values (1,1), (2,1), (3,2);
|
||||||
|
insert into t2 values (1,1), (2,2);
|
||||||
|
insert into t3 values (1), (2);
|
||||||
|
create trigger t3_ad after delete on t3 for each row
|
||||||
|
delete from t2 where fk_t3=old.id;
|
||||||
|
create trigger t2_ad after delete on t2 for each row
|
||||||
|
delete from t1 where fk_t2=old.id;
|
||||||
|
delete from t3 where id = 1;
|
||||||
|
select * from t1 left join (t2 left join t3 on t2.fk_t3 = t3.id) on t1.fk_t2 = t2.id;
|
||||||
|
drop table t1, t2, t3;
|
||||||
|
# Trigger which assigns value selected from table to field of row
|
||||||
|
# being inserted/updated.
|
||||||
|
create table t1 (id int primary key, copy int);
|
||||||
|
create table t2 (id int primary key, data int);
|
||||||
|
insert into t2 values (1,1), (2,2);
|
||||||
|
create trigger t1_bi before insert on t1 for each row
|
||||||
|
set new.copy= (select data from t2 where id = new.id);
|
||||||
|
create trigger t1_bu before update on t1 for each row
|
||||||
|
set new.copy= (select data from t2 where id = new.id);
|
||||||
|
insert into t1 values (1,3), (2,4), (3,3);
|
||||||
|
update t1 set copy= 1 where id = 2;
|
||||||
|
select * from t1;
|
||||||
|
drop table t1, t2;
|
||||||
|
|
||||||
#
|
#
|
||||||
# Test of wrong column specifiers in triggers
|
# Test of wrong column specifiers in triggers
|
||||||
#
|
#
|
||||||
@ -497,14 +578,15 @@ drop table t1, t2;
|
|||||||
|
|
||||||
# Test for bug #5893 "Triggers with dropped functions cause crashes"
|
# Test for bug #5893 "Triggers with dropped functions cause crashes"
|
||||||
# Appropriate error should be reported instead of crash.
|
# Appropriate error should be reported instead of crash.
|
||||||
--disable_warnings
|
# Had to disable this test until bug #11554 will be fixed.
|
||||||
drop function if exists bug5893;
|
#--disable_warnings
|
||||||
--enable_warnings
|
#drop function if exists bug5893;
|
||||||
create table t1 (col1 int, col2 int);
|
#--enable_warnings
|
||||||
insert into t1 values (1, 2);
|
#create table t1 (col1 int, col2 int);
|
||||||
create function bug5893 () returns int return 5;
|
#insert into t1 values (1, 2);
|
||||||
create trigger t1_bu before update on t1 for each row set new.col1= bug5893();
|
#create function bug5893 () returns int return 5;
|
||||||
drop function bug5893;
|
#create trigger t1_bu before update on t1 for each row set new.col1= bug5893();
|
||||||
--error 1305
|
#drop function bug5893;
|
||||||
update t1 set col2 = 4;
|
#--error 1305
|
||||||
drop table t1;
|
#update t1 set col2 = 4;
|
||||||
|
#drop table t1;
|
||||||
|
@ -305,12 +305,12 @@ int ha_archive::write_meta_file(File meta_file, ha_rows rows, bool dirty)
|
|||||||
|
|
||||||
meta_buffer[0]= (uchar)ARCHIVE_CHECK_HEADER;
|
meta_buffer[0]= (uchar)ARCHIVE_CHECK_HEADER;
|
||||||
meta_buffer[1]= (uchar)ARCHIVE_VERSION;
|
meta_buffer[1]= (uchar)ARCHIVE_VERSION;
|
||||||
int8store(meta_buffer + 2, rows);
|
int8store(meta_buffer + 2, (ulonglong)rows);
|
||||||
int8store(meta_buffer + 10, check_point);
|
int8store(meta_buffer + 10, check_point);
|
||||||
*(meta_buffer + 18)= (uchar)dirty;
|
*(meta_buffer + 18)= (uchar)dirty;
|
||||||
DBUG_PRINT("ha_archive::write_meta_file", ("Check %d", (uint)ARCHIVE_CHECK_HEADER));
|
DBUG_PRINT("ha_archive::write_meta_file", ("Check %d", (uint)ARCHIVE_CHECK_HEADER));
|
||||||
DBUG_PRINT("ha_archive::write_meta_file", ("Version %d", (uint)ARCHIVE_VERSION));
|
DBUG_PRINT("ha_archive::write_meta_file", ("Version %d", (uint)ARCHIVE_VERSION));
|
||||||
DBUG_PRINT("ha_archive::write_meta_file", ("Rows %llu", rows));
|
DBUG_PRINT("ha_archive::write_meta_file", ("Rows %llu", (ulonglong)rows));
|
||||||
DBUG_PRINT("ha_archive::write_meta_file", ("Checkpoint %llu", check_point));
|
DBUG_PRINT("ha_archive::write_meta_file", ("Checkpoint %llu", check_point));
|
||||||
DBUG_PRINT("ha_archive::write_meta_file", ("Dirty %d", (uint)dirty));
|
DBUG_PRINT("ha_archive::write_meta_file", ("Dirty %d", (uint)dirty));
|
||||||
|
|
||||||
@ -326,6 +326,9 @@ int ha_archive::write_meta_file(File meta_file, ha_rows rows, bool dirty)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
We create the shared memory space that we will use for the open table.
|
We create the shared memory space that we will use for the open table.
|
||||||
|
No matter what we try to get or create a share. This is so that a repair
|
||||||
|
table operation can occur.
|
||||||
|
|
||||||
See ha_example.cc for a longer description.
|
See ha_example.cc for a longer description.
|
||||||
*/
|
*/
|
||||||
ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
|
ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
|
||||||
@ -363,7 +366,7 @@ ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
|
|||||||
*/
|
*/
|
||||||
VOID(pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST));
|
VOID(pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST));
|
||||||
if ((share->meta_file= my_open(meta_file_name, O_RDWR, MYF(0))) == -1)
|
if ((share->meta_file= my_open(meta_file_name, O_RDWR, MYF(0))) == -1)
|
||||||
goto error;
|
share->crashed= TRUE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
After we read, we set the file to dirty. When we close, we will do the
|
After we read, we set the file to dirty. When we close, we will do the
|
||||||
@ -381,27 +384,14 @@ ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
|
|||||||
that is shared amoung all open tables.
|
that is shared amoung all open tables.
|
||||||
*/
|
*/
|
||||||
if ((share->archive_write= gzopen(share->data_file_name, "ab")) == NULL)
|
if ((share->archive_write= gzopen(share->data_file_name, "ab")) == NULL)
|
||||||
goto error2;
|
share->crashed= TRUE;
|
||||||
if (my_hash_insert(&archive_open_tables, (byte*) share))
|
VOID(my_hash_insert(&archive_open_tables, (byte*) share));
|
||||||
goto error3;
|
|
||||||
thr_lock_init(&share->lock);
|
thr_lock_init(&share->lock);
|
||||||
}
|
}
|
||||||
share->use_count++;
|
share->use_count++;
|
||||||
pthread_mutex_unlock(&archive_mutex);
|
pthread_mutex_unlock(&archive_mutex);
|
||||||
|
|
||||||
return share;
|
return share;
|
||||||
|
|
||||||
error3:
|
|
||||||
/* We close, but ignore errors since we already have errors */
|
|
||||||
(void)gzclose(share->archive_write);
|
|
||||||
error2:
|
|
||||||
my_close(share->meta_file,MYF(0));
|
|
||||||
error:
|
|
||||||
pthread_mutex_unlock(&archive_mutex);
|
|
||||||
VOID(pthread_mutex_destroy(&share->mutex));
|
|
||||||
my_free((gptr) share, MYF(0));
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -458,13 +448,14 @@ int ha_archive::open(const char *name, int mode, uint test_if_locked)
|
|||||||
DBUG_ENTER("ha_archive::open");
|
DBUG_ENTER("ha_archive::open");
|
||||||
|
|
||||||
if (!(share= get_share(name, table)))
|
if (!(share= get_share(name, table)))
|
||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(HA_ERR_OUT_OF_MEM); // Not handled well by calling code!
|
||||||
thr_lock_data_init(&share->lock,&lock,NULL);
|
thr_lock_data_init(&share->lock,&lock,NULL);
|
||||||
|
|
||||||
if ((archive= gzopen(share->data_file_name, "rb")) == NULL)
|
if ((archive= gzopen(share->data_file_name, "rb")) == NULL)
|
||||||
{
|
{
|
||||||
(void)free_share(share); //We void since we already have an error
|
if (errno == EROFS || errno == EACCES)
|
||||||
DBUG_RETURN(errno ? errno : -1);
|
DBUG_RETURN(my_errno= errno);
|
||||||
|
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
@ -572,36 +563,22 @@ error:
|
|||||||
DBUG_RETURN(error ? error : -1);
|
DBUG_RETURN(error ? error : -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
/*
|
This is where the actual row is written out.
|
||||||
Look at ha_archive::open() for an explanation of the row format.
|
|
||||||
Here we just write out the row.
|
|
||||||
|
|
||||||
Wondering about start_bulk_insert()? We don't implement it for
|
|
||||||
archive since it optimizes for lots of writes. The only save
|
|
||||||
for implementing start_bulk_insert() is that we could skip
|
|
||||||
setting dirty to true each time.
|
|
||||||
*/
|
*/
|
||||||
int ha_archive::write_row(byte * buf)
|
int ha_archive::real_write_row(byte *buf, gzFile writer)
|
||||||
{
|
{
|
||||||
z_off_t written;
|
z_off_t written;
|
||||||
uint *ptr, *end;
|
uint *ptr, *end;
|
||||||
DBUG_ENTER("ha_archive::write_row");
|
DBUG_ENTER("ha_archive::real_write_row");
|
||||||
|
|
||||||
if (share->crashed)
|
written= gzwrite(writer, buf, table->s->reclength);
|
||||||
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
DBUG_PRINT("ha_archive::real_write_row", ("Wrote %d bytes expected %d", written, table->s->reclength));
|
||||||
|
|
||||||
statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status);
|
|
||||||
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
|
|
||||||
table->timestamp_field->set_time();
|
|
||||||
pthread_mutex_lock(&share->mutex);
|
|
||||||
written= gzwrite(share->archive_write, buf, table->s->reclength);
|
|
||||||
DBUG_PRINT("ha_archive::write_row", ("Wrote %d bytes expected %d", written, table->s->reclength));
|
|
||||||
if (!delayed_insert || !bulk_insert)
|
if (!delayed_insert || !bulk_insert)
|
||||||
share->dirty= TRUE;
|
share->dirty= TRUE;
|
||||||
|
|
||||||
if (written != (z_off_t)table->s->reclength)
|
if (written != (z_off_t)table->s->reclength)
|
||||||
goto error;
|
DBUG_RETURN(errno ? errno : -1);
|
||||||
/*
|
/*
|
||||||
We should probably mark the table as damagaged if the record is written
|
We should probably mark the table as damagaged if the record is written
|
||||||
but the blob fails.
|
but the blob fails.
|
||||||
@ -616,21 +593,43 @@ int ha_archive::write_row(byte * buf)
|
|||||||
if (size)
|
if (size)
|
||||||
{
|
{
|
||||||
((Field_blob*) table->field[*ptr])->get_ptr(&data_ptr);
|
((Field_blob*) table->field[*ptr])->get_ptr(&data_ptr);
|
||||||
written= gzwrite(share->archive_write, data_ptr, (unsigned)size);
|
written= gzwrite(writer, data_ptr, (unsigned)size);
|
||||||
if (written != (z_off_t)size)
|
if (written != (z_off_t)size)
|
||||||
goto error;
|
DBUG_RETURN(errno ? errno : -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
share->rows_recorded++;
|
|
||||||
pthread_mutex_unlock(&share->mutex);
|
|
||||||
|
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
error:
|
|
||||||
pthread_mutex_unlock(&share->mutex);
|
|
||||||
DBUG_RETURN(errno ? errno : -1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Look at ha_archive::open() for an explanation of the row format.
|
||||||
|
Here we just write out the row.
|
||||||
|
|
||||||
|
Wondering about start_bulk_insert()? We don't implement it for
|
||||||
|
archive since it optimizes for lots of writes. The only save
|
||||||
|
for implementing start_bulk_insert() is that we could skip
|
||||||
|
setting dirty to true each time.
|
||||||
|
*/
|
||||||
|
int ha_archive::write_row(byte *buf)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
DBUG_ENTER("ha_archive::write_row");
|
||||||
|
|
||||||
|
if (share->crashed)
|
||||||
|
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
||||||
|
|
||||||
|
statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status);
|
||||||
|
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
|
||||||
|
table->timestamp_field->set_time();
|
||||||
|
pthread_mutex_lock(&share->mutex);
|
||||||
|
share->rows_recorded++;
|
||||||
|
rc= real_write_row(buf, share->archive_write);
|
||||||
|
pthread_mutex_unlock(&share->mutex);
|
||||||
|
|
||||||
|
DBUG_RETURN(rc);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
All calls that need to scan the table start with this method. If we are told
|
All calls that need to scan the table start with this method. If we are told
|
||||||
that it is a table scan we rewind the file to the beginning, otherwise
|
that it is a table scan we rewind the file to the beginning, otherwise
|
||||||
@ -795,68 +794,20 @@ int ha_archive::rnd_pos(byte * buf, byte *pos)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
This method repairs the meta file. It does this by walking the datafile and
|
This method repairs the meta file. It does this by walking the datafile and
|
||||||
rewriting the meta file.
|
rewriting the meta file. Currently it does this by calling optimize with
|
||||||
|
the extended flag.
|
||||||
*/
|
*/
|
||||||
int ha_archive::repair(THD* thd, HA_CHECK_OPT* check_opt)
|
int ha_archive::repair(THD* thd, HA_CHECK_OPT* check_opt)
|
||||||
{
|
{
|
||||||
int rc;
|
|
||||||
byte *buf;
|
|
||||||
ha_rows rows_recorded= 0;
|
|
||||||
gzFile rebuild_file; // Archive file we are working with
|
|
||||||
File meta_file; // Meta file we use
|
|
||||||
char data_file_name[FN_REFLEN];
|
|
||||||
DBUG_ENTER("ha_archive::repair");
|
DBUG_ENTER("ha_archive::repair");
|
||||||
|
check_opt->flags= T_EXTEND;
|
||||||
|
int rc= optimize(thd, check_opt);
|
||||||
|
|
||||||
/*
|
if (rc)
|
||||||
Open up the meta file to recreate it.
|
DBUG_RETURN(HA_ERR_CRASHED_ON_REPAIR);
|
||||||
*/
|
|
||||||
fn_format(data_file_name, share->table_name, "", ARZ,
|
|
||||||
MY_REPLACE_EXT|MY_UNPACK_FILENAME);
|
|
||||||
if ((rebuild_file= gzopen(data_file_name, "rb")) == NULL)
|
|
||||||
DBUG_RETURN(errno ? errno : -1);
|
|
||||||
|
|
||||||
if ((rc= read_data_header(rebuild_file)))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/*
|
|
||||||
We malloc up the buffer we will use for counting the rows.
|
|
||||||
I know, this malloc'ing memory but this should be a very
|
|
||||||
rare event.
|
|
||||||
*/
|
|
||||||
if (!(buf= (byte*) my_malloc(table->s->rec_buff_length > sizeof(ulonglong) +1 ?
|
|
||||||
table->s->rec_buff_length : sizeof(ulonglong) +1 ,
|
|
||||||
MYF(MY_WME))))
|
|
||||||
{
|
|
||||||
rc= HA_ERR_CRASHED_ON_USAGE;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!(rc= get_row(rebuild_file, buf)))
|
|
||||||
rows_recorded++;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Only if we reach the end of the file do we assume we can rewrite.
|
|
||||||
At this point we reset rc to a non-message state.
|
|
||||||
*/
|
|
||||||
if (rc == HA_ERR_END_OF_FILE)
|
|
||||||
{
|
|
||||||
fn_format(data_file_name,share->table_name,"",ARM,MY_REPLACE_EXT|MY_UNPACK_FILENAME);
|
|
||||||
if ((meta_file= my_open(data_file_name, O_RDWR, MYF(0))) == -1)
|
|
||||||
{
|
|
||||||
rc= HA_ERR_CRASHED_ON_USAGE;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
(void)write_meta_file(meta_file, rows_recorded, TRUE);
|
|
||||||
my_close(meta_file,MYF(0));
|
|
||||||
rc= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
my_free((gptr) buf, MYF(0));
|
|
||||||
share->crashed= FALSE;
|
share->crashed= FALSE;
|
||||||
error:
|
DBUG_RETURN(0);
|
||||||
gzclose(rebuild_file);
|
|
||||||
|
|
||||||
DBUG_RETURN(rc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -866,39 +817,100 @@ error:
|
|||||||
int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt)
|
int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt)
|
||||||
{
|
{
|
||||||
DBUG_ENTER("ha_archive::optimize");
|
DBUG_ENTER("ha_archive::optimize");
|
||||||
int read; // Bytes read, gzread() returns int
|
int rc;
|
||||||
gzFile reader, writer;
|
gzFile writer;
|
||||||
char block[IO_SIZE];
|
|
||||||
char writer_filename[FN_REFLEN];
|
char writer_filename[FN_REFLEN];
|
||||||
|
|
||||||
/* Closing will cause all data waiting to be flushed */
|
/* Flush any waiting data */
|
||||||
gzclose(share->archive_write);
|
gzflush(share->archive_write, Z_SYNC_FLUSH);
|
||||||
share->archive_write= NULL;
|
|
||||||
|
|
||||||
/* Lets create a file to contain the new data */
|
/* Lets create a file to contain the new data */
|
||||||
fn_format(writer_filename, share->table_name, "", ARN,
|
fn_format(writer_filename, share->table_name, "", ARN,
|
||||||
MY_REPLACE_EXT|MY_UNPACK_FILENAME);
|
MY_REPLACE_EXT|MY_UNPACK_FILENAME);
|
||||||
|
|
||||||
if ((reader= gzopen(share->data_file_name, "rb")) == NULL)
|
|
||||||
DBUG_RETURN(-1);
|
|
||||||
|
|
||||||
if ((writer= gzopen(writer_filename, "wb")) == NULL)
|
if ((writer= gzopen(writer_filename, "wb")) == NULL)
|
||||||
|
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
An extended rebuild is a lot more effort. We open up each row and re-record it.
|
||||||
|
Any dead rows are removed (aka rows that may have been partially recorded).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (check_opt->flags == T_EXTEND)
|
||||||
{
|
{
|
||||||
gzclose(reader);
|
byte *buf;
|
||||||
DBUG_RETURN(-1);
|
|
||||||
|
/*
|
||||||
|
First we create a buffer that we can use for reading rows, and can pass
|
||||||
|
to get_row().
|
||||||
|
*/
|
||||||
|
if (!(buf= (byte*) my_malloc(table->s->reclength, MYF(MY_WME))))
|
||||||
|
{
|
||||||
|
rc= HA_ERR_OUT_OF_MEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Now we will rewind the archive file so that we are positioned at the
|
||||||
|
start of the file.
|
||||||
|
*/
|
||||||
|
rc= read_data_header(archive);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Assuming now error from rewinding the archive file, we now write out the
|
||||||
|
new header for out data file.
|
||||||
|
*/
|
||||||
|
if (!rc)
|
||||||
|
rc= write_data_header(writer);
|
||||||
|
|
||||||
|
/*
|
||||||
|
On success of writing out the new header, we now fetch each row and
|
||||||
|
insert it into the new archive file.
|
||||||
|
*/
|
||||||
|
if (!rc)
|
||||||
|
{
|
||||||
|
share->rows_recorded= 0;
|
||||||
|
while (!(rc= get_row(archive, buf)))
|
||||||
|
{
|
||||||
|
real_write_row(buf, writer);
|
||||||
|
share->rows_recorded++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my_free(buf, MYF(0));
|
||||||
|
if (rc && rc != HA_ERR_END_OF_FILE)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
The quick method is to just read the data raw, and then compress it directly.
|
||||||
|
*/
|
||||||
|
int read; // Bytes read, gzread() returns int
|
||||||
|
char block[IO_SIZE];
|
||||||
|
if (gzrewind(archive) == -1)
|
||||||
|
{
|
||||||
|
rc= HA_ERR_CRASHED_ON_USAGE;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((read= gzread(archive, block, IO_SIZE)))
|
||||||
|
gzwrite(writer, block, read);
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((read= gzread(reader, block, IO_SIZE)))
|
gzflush(writer, Z_SYNC_FLUSH);
|
||||||
gzwrite(writer, block, read);
|
gzclose(share->archive_write);
|
||||||
|
share->archive_write= writer;
|
||||||
gzclose(reader);
|
|
||||||
gzclose(writer);
|
|
||||||
|
|
||||||
my_rename(writer_filename,share->data_file_name,MYF(0));
|
my_rename(writer_filename,share->data_file_name,MYF(0));
|
||||||
|
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
|
||||||
|
|
||||||
|
error:
|
||||||
|
gzclose(writer);
|
||||||
|
|
||||||
|
DBUG_RETURN(rc);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Below is an example of how to setup row level locking.
|
Below is an example of how to setup row level locking.
|
||||||
|
@ -36,7 +36,7 @@ typedef struct st_archive_share {
|
|||||||
gzFile archive_write; /* Archive file we are working with */
|
gzFile archive_write; /* Archive file we are working with */
|
||||||
bool dirty; /* Flag for if a flush should occur */
|
bool dirty; /* Flag for if a flush should occur */
|
||||||
bool crashed; /* Meta file is crashed */
|
bool crashed; /* Meta file is crashed */
|
||||||
ha_rows rows_recorded; /* Number of rows in tables */
|
ha_rows rows_recorded; /* Number of rows in tables */
|
||||||
} ARCHIVE_SHARE;
|
} ARCHIVE_SHARE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -53,7 +53,7 @@ class ha_archive: public handler
|
|||||||
z_off_t current_position; /* The position of the row we just read */
|
z_off_t current_position; /* The position of the row we just read */
|
||||||
byte byte_buffer[IO_SIZE]; /* Initial buffer for our string */
|
byte byte_buffer[IO_SIZE]; /* Initial buffer for our string */
|
||||||
String buffer; /* Buffer used for blob storage */
|
String buffer; /* Buffer used for blob storage */
|
||||||
ulonglong scan_rows; /* Number of rows left in scan */
|
ha_rows scan_rows; /* Number of rows left in scan */
|
||||||
bool delayed_insert; /* If the insert is delayed */
|
bool delayed_insert; /* If the insert is delayed */
|
||||||
bool bulk_insert; /* If we are performing a bulk insert */
|
bool bulk_insert; /* If we are performing a bulk insert */
|
||||||
|
|
||||||
@ -84,6 +84,7 @@ public:
|
|||||||
int open(const char *name, int mode, uint test_if_locked);
|
int open(const char *name, int mode, uint test_if_locked);
|
||||||
int close(void);
|
int close(void);
|
||||||
int write_row(byte * buf);
|
int write_row(byte * buf);
|
||||||
|
int real_write_row(byte *buf, gzFile writer);
|
||||||
int rnd_init(bool scan=1);
|
int rnd_init(bool scan=1);
|
||||||
int rnd_next(byte *buf);
|
int rnd_next(byte *buf);
|
||||||
int rnd_pos(byte * buf, byte *pos);
|
int rnd_pos(byte * buf, byte *pos);
|
||||||
@ -102,6 +103,10 @@ public:
|
|||||||
int repair(THD* thd, HA_CHECK_OPT* check_opt);
|
int repair(THD* thd, HA_CHECK_OPT* check_opt);
|
||||||
void start_bulk_insert(ha_rows rows);
|
void start_bulk_insert(ha_rows rows);
|
||||||
int end_bulk_insert();
|
int end_bulk_insert();
|
||||||
|
enum row_type get_row_type() const
|
||||||
|
{
|
||||||
|
return ROW_TYPE_COMPRESSED;
|
||||||
|
}
|
||||||
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
|
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
|
||||||
enum thr_lock_type lock_type);
|
enum thr_lock_type lock_type);
|
||||||
};
|
};
|
||||||
|
@ -4725,6 +4725,7 @@ Item_func_sp::cleanup()
|
|||||||
delete result_field;
|
delete result_field;
|
||||||
result_field= NULL;
|
result_field= NULL;
|
||||||
}
|
}
|
||||||
|
m_sp= NULL;
|
||||||
Item_func::cleanup();
|
Item_func::cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
377
sql/sp.cc
377
sql/sp.cc
@ -19,6 +19,7 @@
|
|||||||
#include "sp.h"
|
#include "sp.h"
|
||||||
#include "sp_head.h"
|
#include "sp_head.h"
|
||||||
#include "sp_cache.h"
|
#include "sp_cache.h"
|
||||||
|
#include "sql_trigger.h"
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
create_string(THD *thd, String *buf,
|
create_string(THD *thd, String *buf,
|
||||||
@ -1077,145 +1078,317 @@ sp_function_exists(THD *thd, sp_name *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
byte *
|
/*
|
||||||
sp_lex_sp_key(const byte *ptr, uint *plen, my_bool first)
|
Structure that represents element in the set of stored routines
|
||||||
|
used by statement or routine.
|
||||||
|
*/
|
||||||
|
struct Sroutine_hash_entry;
|
||||||
|
|
||||||
|
struct Sroutine_hash_entry
|
||||||
{
|
{
|
||||||
LEX_STRING *lsp= (LEX_STRING *)ptr;
|
/* Set key consisting of one-byte routine type and quoted routine name. */
|
||||||
*plen= lsp->length;
|
LEX_STRING key;
|
||||||
return (byte *)lsp->str;
|
/*
|
||||||
}
|
Next element in list linking all routines in set. See also comments
|
||||||
|
for LEX::sroutine/sroutine_list and sp_head::m_sroutines.
|
||||||
|
*/
|
||||||
|
Sroutine_hash_entry *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
void
|
extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first)
|
||||||
sp_add_to_hash(HASH *h, sp_name *fun)
|
|
||||||
{
|
{
|
||||||
if (! hash_search(h, (byte *)fun->m_qname.str, fun->m_qname.length))
|
Sroutine_hash_entry *rn= (Sroutine_hash_entry *)ptr;
|
||||||
{
|
*plen= rn->key.length;
|
||||||
LEX_STRING *ls= (LEX_STRING *)sql_alloc(sizeof(LEX_STRING));
|
return (byte *)rn->key.str;
|
||||||
ls->str= sql_strmake(fun->m_qname.str, fun->m_qname.length);
|
|
||||||
ls->length= fun->m_qname.length;
|
|
||||||
|
|
||||||
my_hash_insert(h, (byte *)ls);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Merge contents of two hashes containing LEX_STRING's
|
Auxilary function that adds new element to the set of stored routines
|
||||||
|
used by statement.
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
sp_merge_hash()
|
add_used_routine()
|
||||||
|
lex - LEX representing statement
|
||||||
|
arena - arena in which memory for new element will be allocated
|
||||||
|
key - key for the hash representing set
|
||||||
|
|
||||||
|
NOTES
|
||||||
|
Will also add element to end of 'LEX::sroutines_list' list.
|
||||||
|
|
||||||
|
In case when statement uses stored routines but does not need
|
||||||
|
prelocking (i.e. it does not use any tables) we will access the
|
||||||
|
elements of LEX::sroutines set on prepared statement re-execution.
|
||||||
|
Because of this we have to allocate memory for both hash element
|
||||||
|
and copy of its key in persistent arena.
|
||||||
|
|
||||||
|
TODO
|
||||||
|
When we will got rid of these accesses on re-executions we will be
|
||||||
|
able to allocate memory for hash elements in non-persitent arena
|
||||||
|
and directly use key values from sp_head::m_sroutines sets instead
|
||||||
|
of making their copies.
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
TRUE - new element was added.
|
||||||
|
FALSE - element was not added (because it is already present in the set).
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool add_used_routine(LEX *lex, Query_arena *arena,
|
||||||
|
const LEX_STRING *key)
|
||||||
|
{
|
||||||
|
if (!hash_search(&lex->sroutines, (byte *)key->str, key->length))
|
||||||
|
{
|
||||||
|
Sroutine_hash_entry *rn=
|
||||||
|
(Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry) +
|
||||||
|
key->length);
|
||||||
|
if (!rn) // OOM. Error will be reported using fatal_error().
|
||||||
|
return FALSE;
|
||||||
|
rn->key.length= key->length;
|
||||||
|
rn->key.str= (char *)rn + sizeof(Sroutine_hash_entry);
|
||||||
|
memcpy(rn->key.str, key->str, key->length);
|
||||||
|
my_hash_insert(&lex->sroutines, (byte *)rn);
|
||||||
|
lex->sroutines_list.link_in_list((byte *)rn, (byte **)&rn->next);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add routine to the set of stored routines used by statement.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
sp_add_used_routine()
|
||||||
|
lex - LEX representing statement
|
||||||
|
arena - arena in which memory for new element of the set
|
||||||
|
will be allocated
|
||||||
|
rt - routine name
|
||||||
|
rt_type - routine type (one of TYPE_ENUM_PROCEDURE/...)
|
||||||
|
|
||||||
|
NOTES
|
||||||
|
Will also add element to end of 'LEX::sroutines_list' list.
|
||||||
|
|
||||||
|
To be friendly towards prepared statements one should pass
|
||||||
|
persistent arena as second argument.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void sp_add_used_routine(LEX *lex, Query_arena *arena,
|
||||||
|
sp_name *rt, char rt_type)
|
||||||
|
{
|
||||||
|
rt->set_routine_type(rt_type);
|
||||||
|
(void)add_used_routine(lex, arena, &rt->m_sroutines_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Merge contents of two hashes representing sets of routines used
|
||||||
|
by statements or by other routines.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
sp_update_sp_used_routines()
|
||||||
dst - hash to which elements should be added
|
dst - hash to which elements should be added
|
||||||
src - hash from which elements merged
|
src - hash from which elements merged
|
||||||
|
|
||||||
RETURN VALUE
|
NOTE
|
||||||
TRUE - if we have added some new elements to destination hash.
|
This procedure won't create new Sroutine_hash_entry objects,
|
||||||
FALSE - there were no new elements in src.
|
instead it will simply add elements from source to destination
|
||||||
|
hash. Thus time of life of elements in destination hash becomes
|
||||||
|
dependant on time of life of elements from source hash. It also
|
||||||
|
won't touch lists linking elements in source and destination
|
||||||
|
hashes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool
|
void sp_update_sp_used_routines(HASH *dst, HASH *src)
|
||||||
sp_merge_hash(HASH *dst, HASH *src)
|
|
||||||
{
|
{
|
||||||
bool res= FALSE;
|
|
||||||
for (uint i=0 ; i < src->records ; i++)
|
for (uint i=0 ; i < src->records ; i++)
|
||||||
{
|
{
|
||||||
LEX_STRING *ls= (LEX_STRING *)hash_element(src, i);
|
Sroutine_hash_entry *rt= (Sroutine_hash_entry *)hash_element(src, i);
|
||||||
|
if (!hash_search(dst, (byte *)rt->key.str, rt->key.length))
|
||||||
if (! hash_search(dst, (byte *)ls->str, ls->length))
|
my_hash_insert(dst, (byte *)rt);
|
||||||
{
|
|
||||||
my_hash_insert(dst, (byte *)ls);
|
|
||||||
res= TRUE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Cache all routines implicitly or explicitly used by query
|
Add contents of hash representing set of routines to the set of
|
||||||
(or whatever object is represented by LEX).
|
routines used by statement.
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
sp_cache_routines()
|
sp_update_stmt_used_routines()
|
||||||
thd - thread context
|
thd - thread context
|
||||||
lex - LEX representing query
|
lex - LEX representing statement
|
||||||
|
src - hash representing set from which routines will be added
|
||||||
|
|
||||||
|
NOTE
|
||||||
|
It will also add elements to end of 'LEX::sroutines_list' list.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src)
|
||||||
|
{
|
||||||
|
for (uint i=0 ; i < src->records ; i++)
|
||||||
|
{
|
||||||
|
Sroutine_hash_entry *rt= (Sroutine_hash_entry *)hash_element(src, i);
|
||||||
|
(void)add_used_routine(lex, thd->current_arena, &rt->key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Cache sub-set of routines used by statement, add tables used by these
|
||||||
|
routines to statement table list. Do the same for all routines used
|
||||||
|
by these routines.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
sp_cache_routines_and_add_tables_aux()
|
||||||
|
thd - thread context
|
||||||
|
lex - LEX representing statement
|
||||||
|
start - first routine from the list of routines to be cached
|
||||||
|
(this list defines mentioned sub-set).
|
||||||
|
|
||||||
NOTE
|
NOTE
|
||||||
If some function is missing this won't be reported here.
|
If some function is missing this won't be reported here.
|
||||||
Instead this fact will be discovered during query execution.
|
Instead this fact will be discovered during query execution.
|
||||||
|
|
||||||
TODO
|
RETURN VALUE
|
||||||
Currently if after passing through routine hashes we discover
|
TRUE - some tables were added
|
||||||
that we have added something to them, we do one more pass to
|
FALSE - no tables were added.
|
||||||
process all routines which were missed on previous pass because
|
*/
|
||||||
of these additions. We can avoid this if along with hashes
|
|
||||||
we use lists holding routine names and iterate other these
|
static bool
|
||||||
lists instead of hashes (since addition to the end of list
|
sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
|
||||||
does not reorder elements in it).
|
Sroutine_hash_entry *start)
|
||||||
|
{
|
||||||
|
bool result= FALSE;
|
||||||
|
|
||||||
|
DBUG_ENTER("sp_cache_routines_and_add_tables_aux");
|
||||||
|
|
||||||
|
for (Sroutine_hash_entry *rt= start; rt; rt= rt->next)
|
||||||
|
{
|
||||||
|
sp_name name(rt->key.str, rt->key.length);
|
||||||
|
int type= rt->key.str[0];
|
||||||
|
sp_head *sp;
|
||||||
|
|
||||||
|
if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ?
|
||||||
|
&thd->sp_func_cache : &thd->sp_proc_cache),
|
||||||
|
&name)))
|
||||||
|
{
|
||||||
|
LEX *oldlex= thd->lex;
|
||||||
|
LEX *newlex= new st_lex;
|
||||||
|
thd->lex= newlex;
|
||||||
|
/* Pass hint pointer to mysql.proc table */
|
||||||
|
newlex->proc_table= oldlex->proc_table;
|
||||||
|
newlex->current_select= NULL;
|
||||||
|
name.m_name.str= strchr(name.m_qname.str, '.');
|
||||||
|
name.m_db.length= name.m_name.str - name.m_qname.str;
|
||||||
|
name.m_db.str= strmake_root(thd->mem_root, name.m_qname.str,
|
||||||
|
name.m_db.length);
|
||||||
|
name.m_name.str+= 1;
|
||||||
|
name.m_name.length= name.m_qname.length - name.m_db.length - 1;
|
||||||
|
|
||||||
|
if (db_find_routine(thd, type, &name, &sp) == SP_OK)
|
||||||
|
{
|
||||||
|
if (type == TYPE_ENUM_FUNCTION)
|
||||||
|
sp_cache_insert(&thd->sp_func_cache, sp);
|
||||||
|
else
|
||||||
|
sp_cache_insert(&thd->sp_proc_cache, sp);
|
||||||
|
}
|
||||||
|
delete newlex;
|
||||||
|
thd->lex= oldlex;
|
||||||
|
}
|
||||||
|
if (sp)
|
||||||
|
{
|
||||||
|
sp_update_stmt_used_routines(thd, lex, &sp->m_sroutines);
|
||||||
|
result|= sp->add_used_tables_to_table_list(thd, &lex->query_tables_last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBUG_RETURN(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Cache all routines from the set of used by statement, add tables used
|
||||||
|
by those routines to statement table list. Do the same for all routines
|
||||||
|
used by those routines.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
sp_cache_routines_and_add_tables()
|
||||||
|
thd - thread context
|
||||||
|
lex - LEX representing statement
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
TRUE - some tables were added
|
||||||
|
FALSE - no tables were added.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool
|
||||||
|
sp_cache_routines_and_add_tables(THD *thd, LEX *lex)
|
||||||
|
{
|
||||||
|
|
||||||
|
return sp_cache_routines_and_add_tables_aux(thd, lex,
|
||||||
|
(Sroutine_hash_entry *)lex->sroutines_list.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add all routines used by view to the set of routines used by statement.
|
||||||
|
Add tables used by those routines to statement table list. Do the same
|
||||||
|
for all routines used by these routines.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
sp_cache_routines_and_add_tables_for_view()
|
||||||
|
thd - thread context
|
||||||
|
lex - LEX representing statement
|
||||||
|
aux_lex - LEX representing view
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
void
|
||||||
sp_cache_routines(THD *thd, LEX *lex)
|
sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, LEX *aux_lex)
|
||||||
{
|
{
|
||||||
bool routines_added= TRUE;
|
Sroutine_hash_entry **last_cached_routine_ptr=
|
||||||
|
(Sroutine_hash_entry **)lex->sroutines_list.next;
|
||||||
DBUG_ENTER("sp_cache_routines");
|
sp_update_stmt_used_routines(thd, lex, &aux_lex->sroutines);
|
||||||
|
(void)sp_cache_routines_and_add_tables_aux(thd, lex,
|
||||||
while (routines_added)
|
*last_cached_routine_ptr);
|
||||||
{
|
|
||||||
routines_added= FALSE;
|
|
||||||
|
|
||||||
for (int type= TYPE_ENUM_FUNCTION; type < TYPE_ENUM_TRIGGER; type++)
|
|
||||||
{
|
|
||||||
HASH *h= (type == TYPE_ENUM_FUNCTION ? &lex->spfuns : &lex->spprocs);
|
|
||||||
|
|
||||||
for (uint i=0 ; i < h->records ; i++)
|
|
||||||
{
|
|
||||||
LEX_STRING *ls= (LEX_STRING *)hash_element(h, i);
|
|
||||||
sp_name name(*ls);
|
|
||||||
sp_head *sp;
|
|
||||||
|
|
||||||
name.m_qname= *ls;
|
|
||||||
if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ?
|
|
||||||
&thd->sp_func_cache : &thd->sp_proc_cache),
|
|
||||||
&name)))
|
|
||||||
{
|
|
||||||
LEX *oldlex= thd->lex;
|
|
||||||
LEX *newlex= new st_lex;
|
|
||||||
|
|
||||||
thd->lex= newlex;
|
|
||||||
/* Pass hint pointer to mysql.proc table */
|
|
||||||
newlex->proc_table= oldlex->proc_table;
|
|
||||||
newlex->current_select= NULL;
|
|
||||||
name.m_name.str= strchr(name.m_qname.str, '.');
|
|
||||||
name.m_db.length= name.m_name.str - name.m_qname.str;
|
|
||||||
name.m_db.str= strmake_root(thd->mem_root, name.m_qname.str,
|
|
||||||
name.m_db.length);
|
|
||||||
name.m_name.str+= 1;
|
|
||||||
name.m_name.length= name.m_qname.length - name.m_db.length - 1;
|
|
||||||
|
|
||||||
if (db_find_routine(thd, type, &name, &sp) == SP_OK)
|
|
||||||
{
|
|
||||||
if (type == TYPE_ENUM_FUNCTION)
|
|
||||||
sp_cache_insert(&thd->sp_func_cache, sp);
|
|
||||||
else
|
|
||||||
sp_cache_insert(&thd->sp_proc_cache, sp);
|
|
||||||
}
|
|
||||||
delete newlex;
|
|
||||||
thd->lex= oldlex;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sp)
|
|
||||||
{
|
|
||||||
routines_added|= sp_merge_hash(&lex->spfuns, &sp->m_spfuns);
|
|
||||||
routines_added|= sp_merge_hash(&lex->spprocs, &sp->m_spprocs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DBUG_VOID_RETURN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add triggers for table to the set of routines used by statement.
|
||||||
|
Add tables used by them to statement table list. Do the same for
|
||||||
|
all implicitly used routines.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
sp_cache_routines_and_add_tables_for_triggers()
|
||||||
|
thd - thread context
|
||||||
|
lex - LEX respresenting statement
|
||||||
|
triggers - triggers of the table
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
|
||||||
|
Table_triggers_list *triggers)
|
||||||
|
{
|
||||||
|
if (add_used_routine(lex, thd->current_arena, &triggers->sroutines_key))
|
||||||
|
{
|
||||||
|
Sroutine_hash_entry **last_cached_routine_ptr=
|
||||||
|
(Sroutine_hash_entry **)lex->sroutines_list.next;
|
||||||
|
for (int i= 0; i < 3; i++)
|
||||||
|
for (int j= 0; j < 2; j++)
|
||||||
|
if (triggers->bodies[i][j])
|
||||||
|
{
|
||||||
|
(void)triggers->bodies[i][j]->add_used_tables_to_table_list(thd,
|
||||||
|
&lex->query_tables_last);
|
||||||
|
sp_update_stmt_used_routines(thd, lex,
|
||||||
|
&triggers->bodies[i][j]->m_sroutines);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)sp_cache_routines_and_add_tables_aux(thd, lex,
|
||||||
|
*last_cached_routine_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generates the CREATE... string from the table information.
|
* Generates the CREATE... string from the table information.
|
||||||
* Returns TRUE on success, FALSE on (alloc) failure.
|
* Returns TRUE on success, FALSE on (alloc) failure.
|
||||||
|
20
sql/sp.h
20
sql/sp.h
@ -79,15 +79,19 @@ sp_function_exists(THD *thd, sp_name *name);
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For precaching of functions and procedures
|
Procedures for pre-caching of stored routines and building table list
|
||||||
*/
|
for prelocking.
|
||||||
void
|
*/
|
||||||
sp_add_to_hash(HASH *h, sp_name *fun);
|
void sp_add_used_routine(LEX *lex, Query_arena *arena,
|
||||||
bool
|
sp_name *rt, char rt_type);
|
||||||
sp_merge_hash(HASH *dst, HASH *src);
|
void sp_update_sp_used_routines(HASH *dst, HASH *src);
|
||||||
void
|
bool sp_cache_routines_and_add_tables(THD *thd, LEX *lex);
|
||||||
sp_cache_routines(THD *thd, LEX *lex);
|
void sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex,
|
||||||
|
LEX *aux_lex);
|
||||||
|
void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
|
||||||
|
Table_triggers_list *triggers);
|
||||||
|
|
||||||
|
extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Utilities...
|
// Utilities...
|
||||||
|
113
sql/sp_head.cc
113
sql/sp_head.cc
@ -242,8 +242,11 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type,
|
|||||||
void
|
void
|
||||||
sp_name::init_qname(THD *thd)
|
sp_name::init_qname(THD *thd)
|
||||||
{
|
{
|
||||||
m_qname.length= m_db.length+m_name.length+1;
|
m_sroutines_key.length= m_db.length + m_name.length + 2;
|
||||||
m_qname.str= thd->alloc(m_qname.length+1);
|
if (!(m_sroutines_key.str= thd->alloc(m_sroutines_key.length + 1)))
|
||||||
|
return;
|
||||||
|
m_qname.length= m_sroutines_key.length - 1;
|
||||||
|
m_qname.str= m_sroutines_key.str + 1;
|
||||||
sprintf(m_qname.str, "%*s.%*s",
|
sprintf(m_qname.str, "%*s.%*s",
|
||||||
m_db.length, (m_db.length ? m_db.str : ""),
|
m_db.length, (m_db.length ? m_db.str : ""),
|
||||||
m_name.length, m_name.str);
|
m_name.length, m_name.str);
|
||||||
@ -317,15 +320,12 @@ sp_head::sp_head()
|
|||||||
{
|
{
|
||||||
extern byte *
|
extern byte *
|
||||||
sp_table_key(const byte *ptr, uint *plen, my_bool first);
|
sp_table_key(const byte *ptr, uint *plen, my_bool first);
|
||||||
extern byte
|
|
||||||
*sp_lex_sp_key(const byte *ptr, uint *plen, my_bool first);
|
|
||||||
DBUG_ENTER("sp_head::sp_head");
|
DBUG_ENTER("sp_head::sp_head");
|
||||||
|
|
||||||
m_backpatch.empty();
|
m_backpatch.empty();
|
||||||
m_lex.empty();
|
m_lex.empty();
|
||||||
hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
|
hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
|
||||||
hash_init(&m_spfuns, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
|
hash_init(&m_sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0);
|
||||||
hash_init(&m_spprocs, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
|
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,8 +528,7 @@ sp_head::destroy()
|
|||||||
}
|
}
|
||||||
|
|
||||||
hash_free(&m_sptabs);
|
hash_free(&m_sptabs);
|
||||||
hash_free(&m_spfuns);
|
hash_free(&m_sroutines);
|
||||||
hash_free(&m_spprocs);
|
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1064,11 +1063,10 @@ sp_head::restore_lex(THD *thd)
|
|||||||
oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
|
oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Add routines which are used by statement to respective sets for
|
Add routines which are used by statement to respective set for
|
||||||
this routine
|
this routine.
|
||||||
*/
|
*/
|
||||||
sp_merge_hash(&m_spfuns, &sublex->spfuns);
|
sp_update_sp_used_routines(&m_sroutines, &sublex->sroutines);
|
||||||
sp_merge_hash(&m_spprocs, &sublex->spprocs);
|
|
||||||
/*
|
/*
|
||||||
Merge tables used by this statement (but not by its functions or
|
Merge tables used by this statement (but not by its functions or
|
||||||
procedures) to multiset of tables used by this routine.
|
procedures) to multiset of tables used by this routine.
|
||||||
@ -1605,16 +1603,22 @@ sp_instr_set::print(String *str)
|
|||||||
int
|
int
|
||||||
sp_instr_set_trigger_field::execute(THD *thd, uint *nextp)
|
sp_instr_set_trigger_field::execute(THD *thd, uint *nextp)
|
||||||
{
|
{
|
||||||
int res= 0;
|
|
||||||
|
|
||||||
DBUG_ENTER("sp_instr_set_trigger_field::execute");
|
DBUG_ENTER("sp_instr_set_trigger_field::execute");
|
||||||
/* QQ: Still unsure what should we return in case of error 1 or -1 ? */
|
DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
|
||||||
if (!value->fixed && value->fix_fields(thd, &value) ||
|
}
|
||||||
trigger_field->fix_fields(thd, 0) ||
|
|
||||||
(value->save_in_field(trigger_field->field, 0) < 0))
|
|
||||||
|
int
|
||||||
|
sp_instr_set_trigger_field::exec_core(THD *thd, uint *nextp)
|
||||||
|
{
|
||||||
|
int res= 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;
|
res= -1;
|
||||||
*nextp= m_ip + 1;
|
*nextp = m_ip+1;
|
||||||
DBUG_RETURN(res);
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -2438,72 +2442,3 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
|
|||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Auxilary function for adding tables used by routines used in query
|
|
||||||
to table lists.
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
sp_add_sp_tables_to_table_list_aux()
|
|
||||||
thd - thread context
|
|
||||||
lex - LEX to which table list tables will be added
|
|
||||||
func_hash - routines for which tables should be added
|
|
||||||
func_cache- SP cache in which this routines should be looked up
|
|
||||||
|
|
||||||
NOTE
|
|
||||||
See sp_add_sp_tables_to_table_list() for more info.
|
|
||||||
|
|
||||||
RETURN VALUE
|
|
||||||
TRUE - some tables were added
|
|
||||||
FALSE - no tables were added.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static bool
|
|
||||||
sp_add_sp_tables_to_table_list_aux(THD *thd, LEX *lex, HASH *func_hash,
|
|
||||||
sp_cache **func_cache)
|
|
||||||
{
|
|
||||||
uint i;
|
|
||||||
bool result= FALSE;
|
|
||||||
|
|
||||||
for (i= 0 ; i < func_hash->records ; i++)
|
|
||||||
{
|
|
||||||
sp_head *sp;
|
|
||||||
LEX_STRING *ls= (LEX_STRING *)hash_element(func_hash, i);
|
|
||||||
sp_name name(*ls);
|
|
||||||
|
|
||||||
name.m_qname= *ls;
|
|
||||||
if ((sp= sp_cache_lookup(func_cache, &name)))
|
|
||||||
result|= sp->add_used_tables_to_table_list(thd, &lex->query_tables_last);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Add tables used by routines used in query to table list.
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
sp_add_sp_tables_to_table_list()
|
|
||||||
thd - thread context
|
|
||||||
lex - LEX to which table list tables will be added
|
|
||||||
func_lex - LEX for which functions we get tables
|
|
||||||
(useful for adding tables used by view routines)
|
|
||||||
|
|
||||||
NOTE
|
|
||||||
Elements of list will be allocated in PS memroot, so this
|
|
||||||
list will be persistent between PS execetutions.
|
|
||||||
|
|
||||||
RETURN VALUE
|
|
||||||
TRUE - some tables were added
|
|
||||||
FALSE - no tables were added.
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool
|
|
||||||
sp_add_sp_tables_to_table_list(THD *thd, LEX *lex, LEX *func_lex)
|
|
||||||
{
|
|
||||||
return (sp_add_sp_tables_to_table_list_aux(thd, lex, &func_lex->spfuns,
|
|
||||||
&thd->sp_func_cache) |
|
|
||||||
sp_add_sp_tables_to_table_list_aux(thd, lex, &func_lex->spprocs,
|
|
||||||
&thd->sp_proc_cache));
|
|
||||||
}
|
|
||||||
|
@ -48,24 +48,50 @@ public:
|
|||||||
LEX_STRING m_db;
|
LEX_STRING m_db;
|
||||||
LEX_STRING m_name;
|
LEX_STRING m_name;
|
||||||
LEX_STRING m_qname;
|
LEX_STRING m_qname;
|
||||||
|
/*
|
||||||
|
Key representing routine in the set of stored routines used by statement.
|
||||||
|
Consists of 1-byte routine type and m_qname (which usually refences to
|
||||||
|
same buffer). Note that one must complete initialization of the key by
|
||||||
|
calling set_routine_type().
|
||||||
|
*/
|
||||||
|
LEX_STRING m_sroutines_key;
|
||||||
|
|
||||||
sp_name(LEX_STRING name)
|
sp_name(LEX_STRING name)
|
||||||
: m_name(name)
|
: m_name(name)
|
||||||
{
|
{
|
||||||
m_db.str= m_qname.str= 0;
|
m_db.str= m_qname.str= m_sroutines_key.str= 0;
|
||||||
m_db.length= m_qname.length= 0;
|
m_db.length= m_qname.length= m_sroutines_key.length= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sp_name(LEX_STRING db, LEX_STRING name)
|
sp_name(LEX_STRING db, LEX_STRING name)
|
||||||
: m_db(db), m_name(name)
|
: m_db(db), m_name(name)
|
||||||
{
|
{
|
||||||
m_qname.str= 0;
|
m_qname.str= m_sroutines_key.str= 0;
|
||||||
m_qname.length= 0;
|
m_qname.length= m_sroutines_key.length= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Creates temporary sp_name object from key, used mainly
|
||||||
|
for SP-cache lookups.
|
||||||
|
*/
|
||||||
|
sp_name(char *key, uint key_len)
|
||||||
|
{
|
||||||
|
m_sroutines_key.str= key;
|
||||||
|
m_sroutines_key.length= key_len;
|
||||||
|
m_name.str= m_qname.str= key + 1;
|
||||||
|
m_name.length= m_qname.length= key_len - 1;
|
||||||
|
m_db.str= 0;
|
||||||
|
m_db.length= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init. the qualified name from the db and name.
|
// Init. the qualified name from the db and name.
|
||||||
void init_qname(THD *thd); // thd for memroot allocation
|
void init_qname(THD *thd); // thd for memroot allocation
|
||||||
|
|
||||||
|
void set_routine_type(char type)
|
||||||
|
{
|
||||||
|
m_sroutines_key.str[0]= type;
|
||||||
|
}
|
||||||
|
|
||||||
~sp_name()
|
~sp_name()
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
@ -107,13 +133,13 @@ public:
|
|||||||
longlong m_created;
|
longlong m_created;
|
||||||
longlong m_modified;
|
longlong m_modified;
|
||||||
/*
|
/*
|
||||||
Sets containing names of SP and SF used by this routine.
|
Set containing names of stored routines used by this routine.
|
||||||
|
Note that unlike elements of similar set for statement elements of this
|
||||||
TODO Probably we should combine these two hashes in one. It will
|
set are not linked in one list. Because of this we are able save memory
|
||||||
decrease memory overhead ans simplify algorithms using them. The
|
by using for this set same objects that are used in 'sroutines' sets
|
||||||
same applies to similar hashes in LEX.
|
for statements of which this stored routine consists.
|
||||||
*/
|
*/
|
||||||
HASH m_spfuns, m_spprocs;
|
HASH m_sroutines;
|
||||||
// Pointers set during parsing
|
// Pointers set during parsing
|
||||||
uchar *m_param_begin, *m_param_end, *m_body_begin;
|
uchar *m_param_begin, *m_param_end, *m_body_begin;
|
||||||
|
|
||||||
@ -474,10 +500,11 @@ class sp_instr_set_trigger_field : public sp_instr
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
sp_instr_set_trigger_field(uint ip, sp_pcontext *ctx,
|
sp_instr_set_trigger_field(uint ip, sp_pcontext *ctx,
|
||||||
Item_trigger_field *trg_fld, Item *val)
|
Item_trigger_field *trg_fld,
|
||||||
|
Item *val, LEX *lex)
|
||||||
: sp_instr(ip, ctx),
|
: sp_instr(ip, ctx),
|
||||||
trigger_field(trg_fld),
|
trigger_field(trg_fld),
|
||||||
value(val)
|
value(val), m_lex_keeper(lex, TRUE)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual ~sp_instr_set_trigger_field()
|
virtual ~sp_instr_set_trigger_field()
|
||||||
@ -485,11 +512,14 @@ public:
|
|||||||
|
|
||||||
virtual int execute(THD *thd, uint *nextp);
|
virtual int execute(THD *thd, uint *nextp);
|
||||||
|
|
||||||
|
virtual int exec_core(THD *thd, uint *nextp);
|
||||||
|
|
||||||
virtual void print(String *str);
|
virtual void print(String *str);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Item_trigger_field *trigger_field;
|
Item_trigger_field *trigger_field;
|
||||||
Item *value;
|
Item *value;
|
||||||
|
sp_lex_keeper m_lex_keeper;
|
||||||
}; // class sp_instr_trigger_field : public sp_instr
|
}; // class sp_instr_trigger_field : public sp_instr
|
||||||
|
|
||||||
|
|
||||||
@ -954,7 +984,5 @@ TABLE_LIST *
|
|||||||
sp_add_to_query_tables(THD *thd, LEX *lex,
|
sp_add_to_query_tables(THD *thd, LEX *lex,
|
||||||
const char *db, const char *name,
|
const char *db, const char *name,
|
||||||
thr_lock_type locktype);
|
thr_lock_type locktype);
|
||||||
bool
|
|
||||||
sp_add_sp_tables_to_table_list(THD *thd, LEX *lex, LEX *func_lex);
|
|
||||||
|
|
||||||
#endif /* _SP_HEAD_H_ */
|
#endif /* _SP_HEAD_H_ */
|
||||||
|
@ -1802,16 +1802,13 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
|
|||||||
may be still zero for prelocked statement...
|
may be still zero for prelocked statement...
|
||||||
*/
|
*/
|
||||||
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
|
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
|
||||||
(thd->lex->spfuns.records || thd->lex->spprocs.records))
|
thd->lex->sroutines.records)
|
||||||
{
|
{
|
||||||
TABLE_LIST **save_query_tables_last;
|
TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
|
||||||
|
|
||||||
sp_cache_routines(thd, thd->lex);
|
|
||||||
save_query_tables_last= thd->lex->query_tables_last;
|
|
||||||
|
|
||||||
DBUG_ASSERT(thd->lex->query_tables == *start);
|
DBUG_ASSERT(thd->lex->query_tables == *start);
|
||||||
|
|
||||||
if (sp_add_sp_tables_to_table_list(thd, thd->lex, thd->lex) ||
|
if (sp_cache_routines_and_add_tables(thd, thd->lex) ||
|
||||||
*start)
|
*start)
|
||||||
{
|
{
|
||||||
query_tables_last_own= save_query_tables_last;
|
query_tables_last_own= save_query_tables_last;
|
||||||
@ -1847,19 +1844,16 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
|
|||||||
and add tables used by them to table list.
|
and add tables used by them to table list.
|
||||||
*/
|
*/
|
||||||
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
|
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
|
||||||
(tables->view->spfuns.records || tables->view->spprocs.records))
|
tables->view->sroutines.records)
|
||||||
{
|
{
|
||||||
// FIXME We should catch recursion for both views and funcs here
|
|
||||||
sp_cache_routines(thd, tables->view);
|
|
||||||
|
|
||||||
/* We have at least one table in TL here */
|
/* We have at least one table in TL here */
|
||||||
if (!query_tables_last_own)
|
if (!query_tables_last_own)
|
||||||
query_tables_last_own= thd->lex->query_tables_last;
|
query_tables_last_own= thd->lex->query_tables_last;
|
||||||
sp_add_sp_tables_to_table_list(thd, thd->lex, tables->view);
|
sp_cache_routines_and_add_tables_for_view(thd, thd->lex,
|
||||||
|
tables->view);
|
||||||
}
|
}
|
||||||
/* Cleanup hashes because destructo for this LEX is never called */
|
/* Cleanup hashes because destructo for this LEX is never called */
|
||||||
hash_free(&tables->view->spfuns);
|
hash_free(&tables->view->sroutines);
|
||||||
hash_free(&tables->view->spprocs);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1914,9 +1908,6 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
|
|||||||
prelocking list.
|
prelocking list.
|
||||||
If we lock table for reading we won't update it so there is no need to
|
If we lock table for reading we won't update it so there is no need to
|
||||||
process its triggers since they never will be activated.
|
process its triggers since they never will be activated.
|
||||||
|
|
||||||
FIXME Now we are simply turning on prelocking. Proper integration
|
|
||||||
and testing is to be done later.
|
|
||||||
*/
|
*/
|
||||||
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
|
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
|
||||||
tables->table->triggers &&
|
tables->table->triggers &&
|
||||||
@ -1924,6 +1915,8 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
|
|||||||
{
|
{
|
||||||
if (!query_tables_last_own)
|
if (!query_tables_last_own)
|
||||||
query_tables_last_own= thd->lex->query_tables_last;
|
query_tables_last_own= thd->lex->query_tables_last;
|
||||||
|
sp_cache_routines_and_add_tables_for_triggers(thd, thd->lex,
|
||||||
|
tables->table->triggers);
|
||||||
}
|
}
|
||||||
free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
|
free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
|
||||||
}
|
}
|
||||||
|
@ -172,10 +172,9 @@ void lex_start(THD *thd, uchar *buf,uint length)
|
|||||||
lex->proc_list.first= 0;
|
lex->proc_list.first= 0;
|
||||||
lex->query_tables_own_last= 0;
|
lex->query_tables_own_last= 0;
|
||||||
|
|
||||||
if (lex->spfuns.records)
|
if (lex->sroutines.records)
|
||||||
my_hash_reset(&lex->spfuns);
|
my_hash_reset(&lex->sroutines);
|
||||||
if (lex->spprocs.records)
|
lex->sroutines_list.empty();
|
||||||
my_hash_reset(&lex->spprocs);
|
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1571,6 +1570,28 @@ void st_select_lex::print_limit(THD *thd, String *str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Initialize LEX object.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
st_lex::st_lex()
|
||||||
|
|
||||||
|
NOTE
|
||||||
|
LEX object initialized with this constructor can be used as part of
|
||||||
|
THD object for which one can safely call open_tables(), lock_tables()
|
||||||
|
and close_thread_tables() functions. But it is not yet ready for
|
||||||
|
statement parsing. On should use lex_start() function to prepare LEX
|
||||||
|
for this.
|
||||||
|
*/
|
||||||
|
|
||||||
|
st_lex::st_lex()
|
||||||
|
:result(0), sql_command(SQLCOM_END), query_tables_own_last(0)
|
||||||
|
{
|
||||||
|
hash_init(&sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0);
|
||||||
|
sroutines_list.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Check whether the merging algorithm can be used on this VIEW
|
Check whether the merging algorithm can be used on this VIEW
|
||||||
|
|
||||||
@ -1976,10 +1997,8 @@ void st_lex::cleanup_after_one_table_open()
|
|||||||
select_lex.cut_subtree();
|
select_lex.cut_subtree();
|
||||||
}
|
}
|
||||||
time_zone_tables_used= 0;
|
time_zone_tables_used= 0;
|
||||||
if (spfuns.records)
|
if (sroutines.records)
|
||||||
my_hash_reset(&spfuns);
|
my_hash_reset(&sroutines);
|
||||||
if (spprocs.records)
|
|
||||||
my_hash_reset(&spprocs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -807,8 +807,14 @@ typedef struct st_lex
|
|||||||
bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
|
bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
|
||||||
bool all_privileges;
|
bool all_privileges;
|
||||||
sp_pcontext *spcont;
|
sp_pcontext *spcont;
|
||||||
HASH spfuns; /* Called functions */
|
/* Set of stored routines called by statement. */
|
||||||
HASH spprocs; /* Called procedures */
|
HASH sroutines;
|
||||||
|
/*
|
||||||
|
List linking elements of 'sroutines' set. Allows you to add new elements
|
||||||
|
to this set as you iterate through the list of existing elements.
|
||||||
|
*/
|
||||||
|
SQL_LIST sroutines_list;
|
||||||
|
|
||||||
st_sp_chistics sp_chistics;
|
st_sp_chistics sp_chistics;
|
||||||
bool only_view; /* used for SHOW CREATE TABLE/VIEW */
|
bool only_view; /* used for SHOW CREATE TABLE/VIEW */
|
||||||
/*
|
/*
|
||||||
@ -841,17 +847,11 @@ typedef struct st_lex
|
|||||||
*/
|
*/
|
||||||
uchar *fname_start, *fname_end;
|
uchar *fname_start, *fname_end;
|
||||||
|
|
||||||
st_lex() :result(0), sql_command(SQLCOM_END), query_tables_own_last(0)
|
st_lex();
|
||||||
{
|
|
||||||
extern byte *sp_lex_sp_key(const byte *ptr, uint *plen, my_bool first);
|
|
||||||
hash_init(&spfuns, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
|
|
||||||
hash_init(&spprocs, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~st_lex()
|
virtual ~st_lex()
|
||||||
{
|
{
|
||||||
hash_free(&spfuns);
|
hash_free(&sroutines);
|
||||||
hash_free(&spprocs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void uncacheable(uint8 cause)
|
inline void uncacheable(uint8 cause)
|
||||||
|
@ -2302,8 +2302,7 @@ mysql_execute_command(THD *thd)
|
|||||||
Don't reset warnings when executing a stored routine.
|
Don't reset warnings when executing a stored routine.
|
||||||
*/
|
*/
|
||||||
if ((all_tables || &lex->select_lex != lex->all_selects_list ||
|
if ((all_tables || &lex->select_lex != lex->all_selects_list ||
|
||||||
lex->spfuns.records || lex->spprocs.records) &&
|
lex->sroutines.records) && !thd->spcont)
|
||||||
!thd->spcont)
|
|
||||||
mysql_reset_errors(thd, 0);
|
mysql_reset_errors(thd, 0);
|
||||||
|
|
||||||
#ifdef HAVE_REPLICATION
|
#ifdef HAVE_REPLICATION
|
||||||
|
@ -1,3 +1,20 @@
|
|||||||
|
/* Copyright (C) 2004-2005 MySQL AB
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
|
|
||||||
#include "mysql_priv.h"
|
#include "mysql_priv.h"
|
||||||
#include "sp_head.h"
|
#include "sp_head.h"
|
||||||
#include "sql_trigger.h"
|
#include "sql_trigger.h"
|
||||||
@ -417,6 +434,18 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
|
|||||||
|
|
||||||
table->triggers= triggers;
|
table->triggers= triggers;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Construct key that will represent triggers for this table in the set
|
||||||
|
of routines used by statement.
|
||||||
|
*/
|
||||||
|
triggers->sroutines_key.length= 1+strlen(db)+1+strlen(table_name)+1;
|
||||||
|
if (!(triggers->sroutines_key.str=
|
||||||
|
alloc_root(&table->mem_root, triggers->sroutines_key.length)))
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
triggers->sroutines_key.str[0]= TYPE_ENUM_TRIGGER;
|
||||||
|
strmov(strmov(strmov(triggers->sroutines_key.str+1, db), "."),
|
||||||
|
table_name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: This could be avoided if there is no triggers
|
TODO: This could be avoided if there is no triggers
|
||||||
for UPDATE and DELETE.
|
for UPDATE and DELETE.
|
||||||
|
@ -1,3 +1,20 @@
|
|||||||
|
/* Copyright (C) 2004-2005 MySQL AB
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This class holds all information about triggers of table.
|
This class holds all information about triggers of table.
|
||||||
|
|
||||||
@ -28,6 +45,14 @@ class Table_triggers_list: public Sql_alloc
|
|||||||
used in CREATE/DROP TRIGGER for looking up trigger by name.
|
used in CREATE/DROP TRIGGER for looking up trigger by name.
|
||||||
*/
|
*/
|
||||||
List<LEX_STRING> names_list;
|
List<LEX_STRING> names_list;
|
||||||
|
/*
|
||||||
|
Key representing triggers for this table in set of all stored
|
||||||
|
routines used by statement.
|
||||||
|
TODO: We won't need this member once triggers namespace will be
|
||||||
|
database-wide instead of table-wide because then we will be able
|
||||||
|
to use key based on sp_name as for other stored routines.
|
||||||
|
*/
|
||||||
|
LEX_STRING sroutines_key;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*
|
/*
|
||||||
@ -112,6 +137,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
friend class Item_trigger_field;
|
friend class Item_trigger_field;
|
||||||
|
friend void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
|
||||||
|
Table_triggers_list *triggers);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool prepare_record1_accessors(TABLE *table);
|
bool prepare_record1_accessors(TABLE *table);
|
||||||
|
@ -1531,7 +1531,7 @@ call:
|
|||||||
lex->sql_command= SQLCOM_CALL;
|
lex->sql_command= SQLCOM_CALL;
|
||||||
lex->spname= $2;
|
lex->spname= $2;
|
||||||
lex->value_list.empty();
|
lex->value_list.empty();
|
||||||
sp_add_to_hash(&lex->spprocs, $2);
|
sp_add_used_routine(lex, YYTHD, $2, TYPE_ENUM_PROCEDURE);
|
||||||
}
|
}
|
||||||
'(' sp_cparam_list ')' {}
|
'(' sp_cparam_list ')' {}
|
||||||
;
|
;
|
||||||
@ -4695,7 +4695,7 @@ simple_expr:
|
|||||||
sp_name *name= new sp_name($1, $3);
|
sp_name *name= new sp_name($1, $3);
|
||||||
|
|
||||||
name->init_qname(YYTHD);
|
name->init_qname(YYTHD);
|
||||||
sp_add_to_hash(&lex->spfuns, name);
|
sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION);
|
||||||
if ($5)
|
if ($5)
|
||||||
$$= new Item_func_sp(&lex->current_select->context, name, *$5);
|
$$= new Item_func_sp(&lex->current_select->context, name, *$5);
|
||||||
else
|
else
|
||||||
@ -4785,7 +4785,7 @@ simple_expr:
|
|||||||
LEX *lex= Lex;
|
LEX *lex= Lex;
|
||||||
sp_name *name= sp_name_current_db_new(YYTHD, $1);
|
sp_name *name= sp_name_current_db_new(YYTHD, $1);
|
||||||
|
|
||||||
sp_add_to_hash(&lex->spfuns, name);
|
sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION);
|
||||||
if ($3)
|
if ($3)
|
||||||
$$= new Item_func_sp(&lex->current_select->context, name, *$3);
|
$$= new Item_func_sp(&lex->current_select->context, name, *$3);
|
||||||
else
|
else
|
||||||
@ -7740,12 +7740,6 @@ sys_option_value:
|
|||||||
yyerror(ER(ER_SYNTAX_ERROR));
|
yyerror(ER(ER_SYNTAX_ERROR));
|
||||||
YYABORT;
|
YYABORT;
|
||||||
}
|
}
|
||||||
if (lex->query_tables)
|
|
||||||
{
|
|
||||||
my_message(ER_SP_SUBSELECT_NYI, ER(ER_SP_SUBSELECT_NYI),
|
|
||||||
MYF(0));
|
|
||||||
YYABORT;
|
|
||||||
}
|
|
||||||
if ($4)
|
if ($4)
|
||||||
it= $4;
|
it= $4;
|
||||||
else
|
else
|
||||||
@ -7760,7 +7754,8 @@ sys_option_value:
|
|||||||
$2.base_name.str)) ||
|
$2.base_name.str)) ||
|
||||||
!(i= new sp_instr_set_trigger_field(lex->sphead->
|
!(i= new sp_instr_set_trigger_field(lex->sphead->
|
||||||
instructions(),
|
instructions(),
|
||||||
lex->spcont, trg_fld, it)))
|
lex->spcont, trg_fld,
|
||||||
|
it, lex)))
|
||||||
YYABORT;
|
YYABORT;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user