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)|
|
||||
ERROR HY000: Recursive stored routines are not allowed.
|
||||
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|
|
||||
a
|
||||
3
|
||||
select * from v1, v2|
|
||||
select * from v1, t1|
|
||||
ERROR HY000: Table 't1' was not locked with LOCK TABLES
|
||||
select f4()|
|
||||
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 database if exists mysqltest;
|
||||
drop function if exists f1;
|
||||
create table t1 (i int);
|
||||
create trigger trg before insert on t1 for each row set @a:=1;
|
||||
set @a:=0;
|
||||
@ -182,6 +183,96 @@ select @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))
|
||||
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 trigger trg before insert on t1 for each row set @a:= old.i;
|
||||
ERROR HY000: There is no OLD row in on INSERT trigger
|
||||
|
@ -1026,3 +1026,26 @@ end|
|
||||
call bug11394(2, 1)|
|
||||
drop procedure bug11394|
|
||||
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 ?
|
||||
create function f1() returns int
|
||||
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 * from v1|
|
||||
#select * from v2|
|
||||
@ -1328,15 +1329,12 @@ select * from v2|
|
||||
select * from v1|
|
||||
# These should not work as we have too little instances of tables locked
|
||||
--error 1100
|
||||
select * from v1, v2|
|
||||
select * from v1, t1|
|
||||
--error 1100
|
||||
select f4()|
|
||||
unlock tables|
|
||||
|
||||
|
||||
# TODO We also should test integration with triggers
|
||||
|
||||
|
||||
# Cleanup
|
||||
drop function f0|
|
||||
drop function f1|
|
||||
|
@ -3,9 +3,10 @@
|
||||
#
|
||||
|
||||
--disable_warnings
|
||||
drop table if exists t1, t2;
|
||||
drop table if exists t1, t2, t3;
|
||||
drop view if exists v1;
|
||||
drop database if exists mysqltest;
|
||||
drop function if exists f1;
|
||||
--enable_warnings
|
||||
|
||||
create table t1 (i int);
|
||||
@ -199,6 +200,86 @@ select @log;
|
||||
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
|
||||
#
|
||||
|
@ -4725,6 +4725,7 @@ Item_func_sp::cleanup()
|
||||
delete result_field;
|
||||
result_field= NULL;
|
||||
}
|
||||
m_sp= NULL;
|
||||
Item_func::cleanup();
|
||||
}
|
||||
|
||||
|
377
sql/sp.cc
377
sql/sp.cc
@ -19,6 +19,7 @@
|
||||
#include "sp.h"
|
||||
#include "sp_head.h"
|
||||
#include "sp_cache.h"
|
||||
#include "sql_trigger.h"
|
||||
|
||||
static bool
|
||||
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;
|
||||
*plen= lsp->length;
|
||||
return (byte *)lsp->str;
|
||||
}
|
||||
/* Set key consisting of one-byte routine type and quoted routine name. */
|
||||
LEX_STRING key;
|
||||
/*
|
||||
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
|
||||
sp_add_to_hash(HASH *h, sp_name *fun)
|
||||
extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first)
|
||||
{
|
||||
if (! hash_search(h, (byte *)fun->m_qname.str, fun->m_qname.length))
|
||||
{
|
||||
LEX_STRING *ls= (LEX_STRING *)sql_alloc(sizeof(LEX_STRING));
|
||||
ls->str= sql_strmake(fun->m_qname.str, fun->m_qname.length);
|
||||
ls->length= fun->m_qname.length;
|
||||
|
||||
my_hash_insert(h, (byte *)ls);
|
||||
}
|
||||
Sroutine_hash_entry *rn= (Sroutine_hash_entry *)ptr;
|
||||
*plen= rn->key.length;
|
||||
return (byte *)rn->key.str;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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
|
||||
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
|
||||
src - hash from which elements merged
|
||||
|
||||
RETURN VALUE
|
||||
TRUE - if we have added some new elements to destination hash.
|
||||
FALSE - there were no new elements in src.
|
||||
NOTE
|
||||
This procedure won't create new Sroutine_hash_entry objects,
|
||||
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
|
||||
sp_merge_hash(HASH *dst, HASH *src)
|
||||
void sp_update_sp_used_routines(HASH *dst, HASH *src)
|
||||
{
|
||||
bool res= FALSE;
|
||||
for (uint i=0 ; i < src->records ; i++)
|
||||
{
|
||||
LEX_STRING *ls= (LEX_STRING *)hash_element(src, i);
|
||||
|
||||
if (! hash_search(dst, (byte *)ls->str, ls->length))
|
||||
{
|
||||
my_hash_insert(dst, (byte *)ls);
|
||||
res= TRUE;
|
||||
}
|
||||
Sroutine_hash_entry *rt= (Sroutine_hash_entry *)hash_element(src, i);
|
||||
if (!hash_search(dst, (byte *)rt->key.str, rt->key.length))
|
||||
my_hash_insert(dst, (byte *)rt);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Cache all routines implicitly or explicitly used by query
|
||||
(or whatever object is represented by LEX).
|
||||
Add contents of hash representing set of routines to the set of
|
||||
routines used by statement.
|
||||
|
||||
SYNOPSIS
|
||||
sp_cache_routines()
|
||||
sp_update_stmt_used_routines()
|
||||
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
|
||||
If some function is missing this won't be reported here.
|
||||
Instead this fact will be discovered during query execution.
|
||||
|
||||
TODO
|
||||
Currently if after passing through routine hashes we discover
|
||||
that we have added something to them, we do one more pass to
|
||||
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
|
||||
lists instead of hashes (since addition to the end of list
|
||||
does not reorder elements in it).
|
||||
RETURN VALUE
|
||||
TRUE - some tables were added
|
||||
FALSE - no tables were added.
|
||||
*/
|
||||
|
||||
static bool
|
||||
sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
|
||||
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
|
||||
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;
|
||||
|
||||
DBUG_ENTER("sp_cache_routines");
|
||||
|
||||
while (routines_added)
|
||||
{
|
||||
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;
|
||||
Sroutine_hash_entry **last_cached_routine_ptr=
|
||||
(Sroutine_hash_entry **)lex->sroutines_list.next;
|
||||
sp_update_stmt_used_routines(thd, lex, &aux_lex->sroutines);
|
||||
(void)sp_cache_routines_and_add_tables_aux(thd, lex,
|
||||
*last_cached_routine_ptr);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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.
|
||||
* 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
|
||||
*/
|
||||
void
|
||||
sp_add_to_hash(HASH *h, sp_name *fun);
|
||||
bool
|
||||
sp_merge_hash(HASH *dst, HASH *src);
|
||||
void
|
||||
sp_cache_routines(THD *thd, LEX *lex);
|
||||
Procedures for pre-caching of stored routines and building table list
|
||||
for prelocking.
|
||||
*/
|
||||
void sp_add_used_routine(LEX *lex, Item_arena *arena,
|
||||
sp_name *rt, char rt_type);
|
||||
void sp_update_sp_used_routines(HASH *dst, HASH *src);
|
||||
bool sp_cache_routines_and_add_tables(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...
|
||||
|
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
|
||||
sp_name::init_qname(THD *thd)
|
||||
{
|
||||
m_qname.length= m_db.length+m_name.length+1;
|
||||
m_qname.str= thd->alloc(m_qname.length+1);
|
||||
m_sroutines_key.length= m_db.length + m_name.length + 2;
|
||||
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",
|
||||
m_db.length, (m_db.length ? m_db.str : ""),
|
||||
m_name.length, m_name.str);
|
||||
@ -317,15 +320,12 @@ sp_head::sp_head()
|
||||
{
|
||||
extern byte *
|
||||
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");
|
||||
|
||||
m_backpatch.empty();
|
||||
m_lex.empty();
|
||||
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_spprocs, 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);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
@ -528,8 +528,7 @@ sp_head::destroy()
|
||||
}
|
||||
|
||||
hash_free(&m_sptabs);
|
||||
hash_free(&m_spfuns);
|
||||
hash_free(&m_spprocs);
|
||||
hash_free(&m_sroutines);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
@ -1064,11 +1063,10 @@ sp_head::restore_lex(THD *thd)
|
||||
oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
|
||||
|
||||
/*
|
||||
Add routines which are used by statement to respective sets for
|
||||
this routine
|
||||
Add routines which are used by statement to respective set for
|
||||
this routine.
|
||||
*/
|
||||
sp_merge_hash(&m_spfuns, &sublex->spfuns);
|
||||
sp_merge_hash(&m_spprocs, &sublex->spprocs);
|
||||
sp_update_sp_used_routines(&m_sroutines, &sublex->sroutines);
|
||||
/*
|
||||
Merge tables used by this statement (but not by its functions or
|
||||
procedures) to multiset of tables used by this routine.
|
||||
@ -1605,16 +1603,22 @@ sp_instr_set::print(String *str)
|
||||
int
|
||||
sp_instr_set_trigger_field::execute(THD *thd, uint *nextp)
|
||||
{
|
||||
int res= 0;
|
||||
|
||||
DBUG_ENTER("sp_instr_set_trigger_field::execute");
|
||||
/* QQ: Still unsure what should we return in case of error 1 or -1 ? */
|
||||
if (!value->fixed && value->fix_fields(thd, &value) ||
|
||||
trigger_field->fix_fields(thd, 0) ||
|
||||
(value->save_in_field(trigger_field->field, 0) < 0))
|
||||
DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
*nextp= m_ip + 1;
|
||||
DBUG_RETURN(res);
|
||||
*nextp = m_ip+1;
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
@ -2438,72 +2442,3 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
|
||||
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_name;
|
||||
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)
|
||||
: m_name(name)
|
||||
{
|
||||
m_db.str= m_qname.str= 0;
|
||||
m_db.length= m_qname.length= 0;
|
||||
m_db.str= m_qname.str= m_sroutines_key.str= 0;
|
||||
m_db.length= m_qname.length= m_sroutines_key.length= 0;
|
||||
}
|
||||
|
||||
sp_name(LEX_STRING db, LEX_STRING name)
|
||||
: m_db(db), m_name(name)
|
||||
{
|
||||
m_qname.str= 0;
|
||||
m_qname.length= 0;
|
||||
m_qname.str= m_sroutines_key.str= 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.
|
||||
void init_qname(THD *thd); // thd for memroot allocation
|
||||
|
||||
void set_routine_type(char type)
|
||||
{
|
||||
m_sroutines_key.str[0]= type;
|
||||
}
|
||||
|
||||
~sp_name()
|
||||
{}
|
||||
};
|
||||
@ -107,13 +133,13 @@ public:
|
||||
longlong m_created;
|
||||
longlong m_modified;
|
||||
/*
|
||||
Sets containing names of SP and SF used by this routine.
|
||||
|
||||
TODO Probably we should combine these two hashes in one. It will
|
||||
decrease memory overhead ans simplify algorithms using them. The
|
||||
same applies to similar hashes in LEX.
|
||||
Set containing names of stored routines used by this routine.
|
||||
Note that unlike elements of similar set for statement elements of this
|
||||
set are not linked in one list. Because of this we are able save memory
|
||||
by using for this set same objects that are used in 'sroutines' sets
|
||||
for statements of which this stored routine consists.
|
||||
*/
|
||||
HASH m_spfuns, m_spprocs;
|
||||
HASH m_sroutines;
|
||||
// Pointers set during parsing
|
||||
uchar *m_param_begin, *m_param_end, *m_body_begin;
|
||||
|
||||
@ -474,10 +500,11 @@ class sp_instr_set_trigger_field : public sp_instr
|
||||
public:
|
||||
|
||||
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),
|
||||
trigger_field(trg_fld),
|
||||
value(val)
|
||||
value(val), m_lex_keeper(lex, TRUE)
|
||||
{}
|
||||
|
||||
virtual ~sp_instr_set_trigger_field()
|
||||
@ -485,11 +512,14 @@ public:
|
||||
|
||||
virtual int execute(THD *thd, uint *nextp);
|
||||
|
||||
virtual int exec_core(THD *thd, uint *nextp);
|
||||
|
||||
virtual void print(String *str);
|
||||
|
||||
private:
|
||||
Item_trigger_field *trigger_field;
|
||||
Item *value;
|
||||
sp_lex_keeper m_lex_keeper;
|
||||
}; // class sp_instr_trigger_field : public sp_instr
|
||||
|
||||
|
||||
@ -954,7 +984,5 @@ TABLE_LIST *
|
||||
sp_add_to_query_tables(THD *thd, LEX *lex,
|
||||
const char *db, const char *name,
|
||||
thr_lock_type locktype);
|
||||
bool
|
||||
sp_add_sp_tables_to_table_list(THD *thd, LEX *lex, LEX *func_lex);
|
||||
|
||||
#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...
|
||||
*/
|
||||
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;
|
||||
|
||||
sp_cache_routines(thd, thd->lex);
|
||||
save_query_tables_last= thd->lex->query_tables_last;
|
||||
TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
|
||||
|
||||
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)
|
||||
{
|
||||
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.
|
||||
*/
|
||||
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 */
|
||||
if (!query_tables_last_own)
|
||||
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 */
|
||||
hash_free(&tables->view->spfuns);
|
||||
hash_free(&tables->view->spprocs);
|
||||
hash_free(&tables->view->sroutines);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1914,9 +1908,6 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
|
||||
prelocking list.
|
||||
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.
|
||||
|
||||
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() &&
|
||||
tables->table->triggers &&
|
||||
@ -1924,6 +1915,8 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
|
||||
{
|
||||
if (!query_tables_last_own)
|
||||
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));
|
||||
}
|
||||
|
@ -172,10 +172,9 @@ void lex_start(THD *thd, uchar *buf,uint length)
|
||||
lex->proc_list.first= 0;
|
||||
lex->query_tables_own_last= 0;
|
||||
|
||||
if (lex->spfuns.records)
|
||||
my_hash_reset(&lex->spfuns);
|
||||
if (lex->spprocs.records)
|
||||
my_hash_reset(&lex->spprocs);
|
||||
if (lex->sroutines.records)
|
||||
my_hash_reset(&lex->sroutines);
|
||||
lex->sroutines_list.empty();
|
||||
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
|
||||
|
||||
|
@ -807,8 +807,14 @@ typedef struct st_lex
|
||||
bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
|
||||
bool all_privileges;
|
||||
sp_pcontext *spcont;
|
||||
HASH spfuns; /* Called functions */
|
||||
HASH spprocs; /* Called procedures */
|
||||
/* Set of stored routines called by statement. */
|
||||
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;
|
||||
bool only_view; /* used for SHOW CREATE TABLE/VIEW */
|
||||
/*
|
||||
@ -841,17 +847,11 @@ typedef struct st_lex
|
||||
*/
|
||||
uchar *fname_start, *fname_end;
|
||||
|
||||
st_lex() :result(0), sql_command(SQLCOM_END), query_tables_own_last(0)
|
||||
{
|
||||
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);
|
||||
}
|
||||
st_lex();
|
||||
|
||||
virtual ~st_lex()
|
||||
{
|
||||
hash_free(&spfuns);
|
||||
hash_free(&spprocs);
|
||||
hash_free(&sroutines);
|
||||
}
|
||||
|
||||
inline void uncacheable(uint8 cause)
|
||||
|
@ -2302,8 +2302,7 @@ mysql_execute_command(THD *thd)
|
||||
Don't reset warnings when executing a stored routine.
|
||||
*/
|
||||
if ((all_tables || &lex->select_lex != lex->all_selects_list ||
|
||||
lex->spfuns.records || lex->spprocs.records) &&
|
||||
!thd->spcont)
|
||||
lex->sroutines.records) && !thd->spcont)
|
||||
mysql_reset_errors(thd, 0);
|
||||
|
||||
#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 "sp_head.h"
|
||||
#include "sql_trigger.h"
|
||||
@ -417,6 +434,18 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
@ -28,6 +45,14 @@ class Table_triggers_list: public Sql_alloc
|
||||
used in CREATE/DROP TRIGGER for looking up trigger by name.
|
||||
*/
|
||||
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:
|
||||
/*
|
||||
@ -112,6 +137,8 @@ public:
|
||||
}
|
||||
|
||||
friend class Item_trigger_field;
|
||||
friend void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
|
||||
Table_triggers_list *triggers);
|
||||
|
||||
private:
|
||||
bool prepare_record1_accessors(TABLE *table);
|
||||
|
@ -1531,7 +1531,7 @@ call:
|
||||
lex->sql_command= SQLCOM_CALL;
|
||||
lex->spname= $2;
|
||||
lex->value_list.empty();
|
||||
sp_add_to_hash(&lex->spprocs, $2);
|
||||
sp_add_used_routine(lex, YYTHD, $2, TYPE_ENUM_PROCEDURE);
|
||||
}
|
||||
'(' sp_cparam_list ')' {}
|
||||
;
|
||||
@ -4695,7 +4695,7 @@ simple_expr:
|
||||
sp_name *name= new sp_name($1, $3);
|
||||
|
||||
name->init_qname(YYTHD);
|
||||
sp_add_to_hash(&lex->spfuns, name);
|
||||
sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION);
|
||||
if ($5)
|
||||
$$= new Item_func_sp(&lex->current_select->context, name, *$5);
|
||||
else
|
||||
@ -4785,7 +4785,7 @@ simple_expr:
|
||||
LEX *lex= Lex;
|
||||
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)
|
||||
$$= new Item_func_sp(&lex->current_select->context, name, *$3);
|
||||
else
|
||||
@ -7723,12 +7723,6 @@ sys_option_value:
|
||||
yyerror(ER(ER_SYNTAX_ERROR));
|
||||
YYABORT;
|
||||
}
|
||||
if (lex->query_tables)
|
||||
{
|
||||
my_message(ER_SP_SUBSELECT_NYI, ER(ER_SP_SUBSELECT_NYI),
|
||||
MYF(0));
|
||||
YYABORT;
|
||||
}
|
||||
if ($4)
|
||||
it= $4;
|
||||
else
|
||||
@ -7743,7 +7737,8 @@ sys_option_value:
|
||||
$2.base_name.str)) ||
|
||||
!(i= new sp_instr_set_trigger_field(lex->sphead->
|
||||
instructions(),
|
||||
lex->spcont, trg_fld, it)))
|
||||
lex->spcont, trg_fld,
|
||||
it, lex)))
|
||||
YYABORT;
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user