mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
Merge bk-internal.mysql.com:/home/bk/mysql-5.0
into mysql.com:/home/dlenev/src/mysql-5.0-bg8406 mysql-test/r/sp.result: Auto merged mysql-test/r/trigger.result: Auto merged mysql-test/t/sp.test: Auto merged mysql-test/t/trigger.test: Auto merged sql/item_func.cc: Auto merged sql/sp.cc: Auto merged sql/sp_head.h: Auto merged sql/sql_base.cc: Auto merged sql/sql_lex.cc: Auto merged sql/sql_lex.h: Auto merged sql/sql_parse.cc: Auto merged sql/sql_trigger.cc: Auto merged mysql-test/r/sp-error.result: Manual merge. mysql-test/t/sp-error.test: Manual merge. sql/sp_head.cc: Manual merge. sql/sql_yacc.yy: Manual merge.
This commit is contained in:
@ -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
|
||||||
|
@ -1026,3 +1026,26 @@ end|
|
|||||||
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
|
||||||
#
|
#
|
||||||
|
@ -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, Item_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, Item_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, Item_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
|
||||||
|
|
||||||
|
@ -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
|
||||||
@ -7723,12 +7723,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
|
||||||
@ -7743,7 +7737,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