mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
Merge mysql.com:/usr/local/bk/mysql-5.0
into mysql.com:/home/pem/work/mysql-5.0 mysql-test/r/sp-error.result: Auto merged mysql-test/r/sp.result: Auto merged mysql-test/t/sp-error.test: Auto merged mysql-test/t/sp.test: Auto merged sql/sp.cc: Auto merged sql/sql_yacc.yy: Auto merged
This commit is contained in:
@ -48,7 +48,7 @@ mysql_test_run_new_SOURCES= mysql_test_run_new.c my_manage.c my_create_tables.c
|
||||
dist-hook:
|
||||
mkdir -p $(distdir)/t $(distdir)/r $(distdir)/include \
|
||||
$(distdir)/std_data $(distdir)/lib
|
||||
$(INSTALL_DATA) $(srcdir)/t/*.def $(distdir)/t
|
||||
-$(INSTALL_DATA) $(srcdir)/t/*.def $(distdir)/t
|
||||
$(INSTALL_DATA) $(srcdir)/t/*.test $(distdir)/t
|
||||
$(INSTALL_DATA) $(srcdir)/t/*.sql $(distdir)/t
|
||||
-$(INSTALL_DATA) $(srcdir)/t/*.disabled $(distdir)/t
|
||||
@ -71,7 +71,7 @@ install-data-local:
|
||||
$(DESTDIR)$(testdir)/std_data \
|
||||
$(DESTDIR)$(testdir)/lib
|
||||
$(INSTALL_DATA) $(srcdir)/README $(DESTDIR)$(testdir)
|
||||
$(INSTALL_DATA) $(srcdir)/t/*.def $(DESTDIR)$(testdir)/t
|
||||
-$(INSTALL_DATA) $(srcdir)/t/*.def $(DESTDIR)$(testdir)/t
|
||||
$(INSTALL_DATA) $(srcdir)/t/*.test $(DESTDIR)$(testdir)/t
|
||||
$(INSTALL_DATA) $(srcdir)/t/*.sql $(DESTDIR)$(testdir)/t
|
||||
-$(INSTALL_DATA) $(srcdir)/t/*.disabled $(DESTDIR)$(testdir)/t
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -705,3 +705,19 @@ end|
|
||||
call bug11394(2, 1)|
|
||||
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
|
||||
@ -482,12 +573,3 @@ i k ts
|
||||
1 1 0000-00-00 00:00:00
|
||||
2 2 0000-00-00 00:00:00
|
||||
drop table t1, t2;
|
||||
drop function if exists bug5893;
|
||||
create table t1 (col1 int, col2 int);
|
||||
insert into t1 values (1, 2);
|
||||
create function bug5893 () returns int return 5;
|
||||
create trigger t1_bu before update on t1 for each row set new.col1= bug5893();
|
||||
drop function bug5893;
|
||||
update t1 set col2 = 4;
|
||||
ERROR 42000: FUNCTION test.bug5893 does not exist
|
||||
drop table t1;
|
||||
|
@ -1313,6 +1313,11 @@ INSERT INTO t2 VALUES (4,011403,37,'intercepted','audiology','tinily','');
|
||||
SELECT * FROM t2;
|
||||
OPTIMIZE TABLE t2;
|
||||
SELECT * FROM t2;
|
||||
INSERT INTO t2 VALUES (2,011401,37,'breaking','dreaded','Steinberg','W');
|
||||
INSERT INTO t2 VALUES (3,011402,37,'Romans','scholastics','jarring','');
|
||||
INSERT INTO t2 VALUES (4,011403,37,'intercepted','audiology','tinily','');
|
||||
OPTIMIZE TABLE t2 EXTENDED;
|
||||
SELECT * FROM t2;
|
||||
REPAIR TABLE t2;
|
||||
SELECT * FROM t2;
|
||||
|
||||
|
@ -1025,4 +1025,27 @@ end|
|
||||
--error 1424
|
||||
call bug11394(2, 1)|
|
||||
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 ?
|
||||
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
|
||||
#
|
||||
@ -497,14 +578,15 @@ drop table t1, t2;
|
||||
|
||||
# Test for bug #5893 "Triggers with dropped functions cause crashes"
|
||||
# Appropriate error should be reported instead of crash.
|
||||
--disable_warnings
|
||||
drop function if exists bug5893;
|
||||
--enable_warnings
|
||||
create table t1 (col1 int, col2 int);
|
||||
insert into t1 values (1, 2);
|
||||
create function bug5893 () returns int return 5;
|
||||
create trigger t1_bu before update on t1 for each row set new.col1= bug5893();
|
||||
drop function bug5893;
|
||||
--error 1305
|
||||
update t1 set col2 = 4;
|
||||
drop table t1;
|
||||
# Had to disable this test until bug #11554 will be fixed.
|
||||
#--disable_warnings
|
||||
#drop function if exists bug5893;
|
||||
#--enable_warnings
|
||||
#create table t1 (col1 int, col2 int);
|
||||
#insert into t1 values (1, 2);
|
||||
#create function bug5893 () returns int return 5;
|
||||
#create trigger t1_bu before update on t1 for each row set new.col1= bug5893();
|
||||
#drop function bug5893;
|
||||
#--error 1305
|
||||
#update t1 set col2 = 4;
|
||||
#drop table t1;
|
||||
|
@ -305,12 +305,12 @@ int ha_archive::write_meta_file(File meta_file, ha_rows rows, bool dirty)
|
||||
|
||||
meta_buffer[0]= (uchar)ARCHIVE_CHECK_HEADER;
|
||||
meta_buffer[1]= (uchar)ARCHIVE_VERSION;
|
||||
int8store(meta_buffer + 2, rows);
|
||||
int8store(meta_buffer + 2, (ulonglong)rows);
|
||||
int8store(meta_buffer + 10, check_point);
|
||||
*(meta_buffer + 18)= (uchar)dirty;
|
||||
DBUG_PRINT("ha_archive::write_meta_file", ("Check %d", (uint)ARCHIVE_CHECK_HEADER));
|
||||
DBUG_PRINT("ha_archive::write_meta_file", ("Version %d", (uint)ARCHIVE_VERSION));
|
||||
DBUG_PRINT("ha_archive::write_meta_file", ("Rows %llu", rows));
|
||||
DBUG_PRINT("ha_archive::write_meta_file", ("Rows %llu", (ulonglong)rows));
|
||||
DBUG_PRINT("ha_archive::write_meta_file", ("Checkpoint %llu", check_point));
|
||||
DBUG_PRINT("ha_archive::write_meta_file", ("Dirty %d", (uint)dirty));
|
||||
|
||||
@ -326,6 +326,9 @@ int ha_archive::write_meta_file(File meta_file, ha_rows rows, bool dirty)
|
||||
|
||||
/*
|
||||
We create the shared memory space that we will use for the open table.
|
||||
No matter what we try to get or create a share. This is so that a repair
|
||||
table operation can occur.
|
||||
|
||||
See ha_example.cc for a longer description.
|
||||
*/
|
||||
ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
|
||||
@ -363,7 +366,7 @@ ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
|
||||
*/
|
||||
VOID(pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST));
|
||||
if ((share->meta_file= my_open(meta_file_name, O_RDWR, MYF(0))) == -1)
|
||||
goto error;
|
||||
share->crashed= TRUE;
|
||||
|
||||
/*
|
||||
After we read, we set the file to dirty. When we close, we will do the
|
||||
@ -381,27 +384,14 @@ ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
|
||||
that is shared amoung all open tables.
|
||||
*/
|
||||
if ((share->archive_write= gzopen(share->data_file_name, "ab")) == NULL)
|
||||
goto error2;
|
||||
if (my_hash_insert(&archive_open_tables, (byte*) share))
|
||||
goto error3;
|
||||
share->crashed= TRUE;
|
||||
VOID(my_hash_insert(&archive_open_tables, (byte*) share));
|
||||
thr_lock_init(&share->lock);
|
||||
}
|
||||
share->use_count++;
|
||||
pthread_mutex_unlock(&archive_mutex);
|
||||
|
||||
return share;
|
||||
|
||||
error3:
|
||||
/* We close, but ignore errors since we already have errors */
|
||||
(void)gzclose(share->archive_write);
|
||||
error2:
|
||||
my_close(share->meta_file,MYF(0));
|
||||
error:
|
||||
pthread_mutex_unlock(&archive_mutex);
|
||||
VOID(pthread_mutex_destroy(&share->mutex));
|
||||
my_free((gptr) share, MYF(0));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@ -458,13 +448,14 @@ int ha_archive::open(const char *name, int mode, uint test_if_locked)
|
||||
DBUG_ENTER("ha_archive::open");
|
||||
|
||||
if (!(share= get_share(name, table)))
|
||||
DBUG_RETURN(-1);
|
||||
DBUG_RETURN(HA_ERR_OUT_OF_MEM); // Not handled well by calling code!
|
||||
thr_lock_data_init(&share->lock,&lock,NULL);
|
||||
|
||||
if ((archive= gzopen(share->data_file_name, "rb")) == NULL)
|
||||
{
|
||||
(void)free_share(share); //We void since we already have an error
|
||||
DBUG_RETURN(errno ? errno : -1);
|
||||
if (errno == EROFS || errno == EACCES)
|
||||
DBUG_RETURN(my_errno= errno);
|
||||
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
||||
}
|
||||
|
||||
DBUG_RETURN(0);
|
||||
@ -572,36 +563,22 @@ error:
|
||||
DBUG_RETURN(error ? error : -1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Look at ha_archive::open() for an explanation of the row format.
|
||||
Here we just write out the row.
|
||||
|
||||
Wondering about start_bulk_insert()? We don't implement it for
|
||||
archive since it optimizes for lots of writes. The only save
|
||||
for implementing start_bulk_insert() is that we could skip
|
||||
setting dirty to true each time.
|
||||
This is where the actual row is written out.
|
||||
*/
|
||||
int ha_archive::write_row(byte * buf)
|
||||
int ha_archive::real_write_row(byte *buf, gzFile writer)
|
||||
{
|
||||
z_off_t written;
|
||||
uint *ptr, *end;
|
||||
DBUG_ENTER("ha_archive::write_row");
|
||||
DBUG_ENTER("ha_archive::real_write_row");
|
||||
|
||||
if (share->crashed)
|
||||
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
||||
|
||||
statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status);
|
||||
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
|
||||
table->timestamp_field->set_time();
|
||||
pthread_mutex_lock(&share->mutex);
|
||||
written= gzwrite(share->archive_write, buf, table->s->reclength);
|
||||
DBUG_PRINT("ha_archive::write_row", ("Wrote %d bytes expected %d", written, table->s->reclength));
|
||||
written= gzwrite(writer, buf, table->s->reclength);
|
||||
DBUG_PRINT("ha_archive::real_write_row", ("Wrote %d bytes expected %d", written, table->s->reclength));
|
||||
if (!delayed_insert || !bulk_insert)
|
||||
share->dirty= TRUE;
|
||||
|
||||
if (written != (z_off_t)table->s->reclength)
|
||||
goto error;
|
||||
DBUG_RETURN(errno ? errno : -1);
|
||||
/*
|
||||
We should probably mark the table as damagaged if the record is written
|
||||
but the blob fails.
|
||||
@ -616,21 +593,43 @@ int ha_archive::write_row(byte * buf)
|
||||
if (size)
|
||||
{
|
||||
((Field_blob*) table->field[*ptr])->get_ptr(&data_ptr);
|
||||
written= gzwrite(share->archive_write, data_ptr, (unsigned)size);
|
||||
written= gzwrite(writer, data_ptr, (unsigned)size);
|
||||
if (written != (z_off_t)size)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
share->rows_recorded++;
|
||||
pthread_mutex_unlock(&share->mutex);
|
||||
|
||||
DBUG_RETURN(0);
|
||||
error:
|
||||
pthread_mutex_unlock(&share->mutex);
|
||||
DBUG_RETURN(errno ? errno : -1);
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Look at ha_archive::open() for an explanation of the row format.
|
||||
Here we just write out the row.
|
||||
|
||||
Wondering about start_bulk_insert()? We don't implement it for
|
||||
archive since it optimizes for lots of writes. The only save
|
||||
for implementing start_bulk_insert() is that we could skip
|
||||
setting dirty to true each time.
|
||||
*/
|
||||
int ha_archive::write_row(byte *buf)
|
||||
{
|
||||
int rc;
|
||||
DBUG_ENTER("ha_archive::write_row");
|
||||
|
||||
if (share->crashed)
|
||||
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
||||
|
||||
statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status);
|
||||
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
|
||||
table->timestamp_field->set_time();
|
||||
pthread_mutex_lock(&share->mutex);
|
||||
share->rows_recorded++;
|
||||
rc= real_write_row(buf, share->archive_write);
|
||||
pthread_mutex_unlock(&share->mutex);
|
||||
|
||||
DBUG_RETURN(rc);
|
||||
}
|
||||
|
||||
/*
|
||||
All calls that need to scan the table start with this method. If we are told
|
||||
that it is a table scan we rewind the file to the beginning, otherwise
|
||||
@ -795,68 +794,20 @@ int ha_archive::rnd_pos(byte * buf, byte *pos)
|
||||
|
||||
/*
|
||||
This method repairs the meta file. It does this by walking the datafile and
|
||||
rewriting the meta file.
|
||||
rewriting the meta file. Currently it does this by calling optimize with
|
||||
the extended flag.
|
||||
*/
|
||||
int ha_archive::repair(THD* thd, HA_CHECK_OPT* check_opt)
|
||||
{
|
||||
int rc;
|
||||
byte *buf;
|
||||
ha_rows rows_recorded= 0;
|
||||
gzFile rebuild_file; // Archive file we are working with
|
||||
File meta_file; // Meta file we use
|
||||
char data_file_name[FN_REFLEN];
|
||||
DBUG_ENTER("ha_archive::repair");
|
||||
check_opt->flags= T_EXTEND;
|
||||
int rc= optimize(thd, check_opt);
|
||||
|
||||
/*
|
||||
Open up the meta file to recreate it.
|
||||
*/
|
||||
fn_format(data_file_name, share->table_name, "", ARZ,
|
||||
MY_REPLACE_EXT|MY_UNPACK_FILENAME);
|
||||
if ((rebuild_file= gzopen(data_file_name, "rb")) == NULL)
|
||||
DBUG_RETURN(errno ? errno : -1);
|
||||
if (rc)
|
||||
DBUG_RETURN(HA_ERR_CRASHED_ON_REPAIR);
|
||||
|
||||
if ((rc= read_data_header(rebuild_file)))
|
||||
goto error;
|
||||
|
||||
/*
|
||||
We malloc up the buffer we will use for counting the rows.
|
||||
I know, this malloc'ing memory but this should be a very
|
||||
rare event.
|
||||
*/
|
||||
if (!(buf= (byte*) my_malloc(table->s->rec_buff_length > sizeof(ulonglong) +1 ?
|
||||
table->s->rec_buff_length : sizeof(ulonglong) +1 ,
|
||||
MYF(MY_WME))))
|
||||
{
|
||||
rc= HA_ERR_CRASHED_ON_USAGE;
|
||||
goto error;
|
||||
}
|
||||
|
||||
while (!(rc= get_row(rebuild_file, buf)))
|
||||
rows_recorded++;
|
||||
|
||||
/*
|
||||
Only if we reach the end of the file do we assume we can rewrite.
|
||||
At this point we reset rc to a non-message state.
|
||||
*/
|
||||
if (rc == HA_ERR_END_OF_FILE)
|
||||
{
|
||||
fn_format(data_file_name,share->table_name,"",ARM,MY_REPLACE_EXT|MY_UNPACK_FILENAME);
|
||||
if ((meta_file= my_open(data_file_name, O_RDWR, MYF(0))) == -1)
|
||||
{
|
||||
rc= HA_ERR_CRASHED_ON_USAGE;
|
||||
goto error;
|
||||
}
|
||||
(void)write_meta_file(meta_file, rows_recorded, TRUE);
|
||||
my_close(meta_file,MYF(0));
|
||||
rc= 0;
|
||||
}
|
||||
|
||||
my_free((gptr) buf, MYF(0));
|
||||
share->crashed= FALSE;
|
||||
error:
|
||||
gzclose(rebuild_file);
|
||||
|
||||
DBUG_RETURN(rc);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -866,39 +817,100 @@ error:
|
||||
int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt)
|
||||
{
|
||||
DBUG_ENTER("ha_archive::optimize");
|
||||
int read; // Bytes read, gzread() returns int
|
||||
gzFile reader, writer;
|
||||
char block[IO_SIZE];
|
||||
int rc;
|
||||
gzFile writer;
|
||||
char writer_filename[FN_REFLEN];
|
||||
|
||||
/* Closing will cause all data waiting to be flushed */
|
||||
gzclose(share->archive_write);
|
||||
share->archive_write= NULL;
|
||||
/* Flush any waiting data */
|
||||
gzflush(share->archive_write, Z_SYNC_FLUSH);
|
||||
|
||||
/* Lets create a file to contain the new data */
|
||||
fn_format(writer_filename, share->table_name, "", ARN,
|
||||
MY_REPLACE_EXT|MY_UNPACK_FILENAME);
|
||||
|
||||
if ((reader= gzopen(share->data_file_name, "rb")) == NULL)
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
if ((writer= gzopen(writer_filename, "wb")) == NULL)
|
||||
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
|
||||
|
||||
/*
|
||||
An extended rebuild is a lot more effort. We open up each row and re-record it.
|
||||
Any dead rows are removed (aka rows that may have been partially recorded).
|
||||
*/
|
||||
|
||||
if (check_opt->flags == T_EXTEND)
|
||||
{
|
||||
gzclose(reader);
|
||||
DBUG_RETURN(-1);
|
||||
byte *buf;
|
||||
|
||||
/*
|
||||
First we create a buffer that we can use for reading rows, and can pass
|
||||
to get_row().
|
||||
*/
|
||||
if (!(buf= (byte*) my_malloc(table->s->reclength, MYF(MY_WME))))
|
||||
{
|
||||
rc= HA_ERR_OUT_OF_MEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
while ((read= gzread(reader, block, IO_SIZE)))
|
||||
gzwrite(writer, block, read);
|
||||
/*
|
||||
Now we will rewind the archive file so that we are positioned at the
|
||||
start of the file.
|
||||
*/
|
||||
rc= read_data_header(archive);
|
||||
|
||||
gzclose(reader);
|
||||
gzclose(writer);
|
||||
/*
|
||||
Assuming now error from rewinding the archive file, we now write out the
|
||||
new header for out data file.
|
||||
*/
|
||||
if (!rc)
|
||||
rc= write_data_header(writer);
|
||||
|
||||
/*
|
||||
On success of writing out the new header, we now fetch each row and
|
||||
insert it into the new archive file.
|
||||
*/
|
||||
if (!rc)
|
||||
{
|
||||
share->rows_recorded= 0;
|
||||
while (!(rc= get_row(archive, buf)))
|
||||
{
|
||||
real_write_row(buf, writer);
|
||||
share->rows_recorded++;
|
||||
}
|
||||
}
|
||||
|
||||
my_free(buf, MYF(0));
|
||||
if (rc && rc != HA_ERR_END_OF_FILE)
|
||||
goto error;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
The quick method is to just read the data raw, and then compress it directly.
|
||||
*/
|
||||
int read; // Bytes read, gzread() returns int
|
||||
char block[IO_SIZE];
|
||||
if (gzrewind(archive) == -1)
|
||||
{
|
||||
rc= HA_ERR_CRASHED_ON_USAGE;
|
||||
goto error;
|
||||
}
|
||||
|
||||
while ((read= gzread(archive, block, IO_SIZE)))
|
||||
gzwrite(writer, block, read);
|
||||
}
|
||||
|
||||
gzflush(writer, Z_SYNC_FLUSH);
|
||||
gzclose(share->archive_write);
|
||||
share->archive_write= writer;
|
||||
|
||||
my_rename(writer_filename,share->data_file_name,MYF(0));
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
error:
|
||||
gzclose(writer);
|
||||
|
||||
DBUG_RETURN(rc);
|
||||
}
|
||||
|
||||
/*
|
||||
Below is an example of how to setup row level locking.
|
||||
|
@ -53,7 +53,7 @@ class ha_archive: public handler
|
||||
z_off_t current_position; /* The position of the row we just read */
|
||||
byte byte_buffer[IO_SIZE]; /* Initial buffer for our string */
|
||||
String buffer; /* Buffer used for blob storage */
|
||||
ulonglong scan_rows; /* Number of rows left in scan */
|
||||
ha_rows scan_rows; /* Number of rows left in scan */
|
||||
bool delayed_insert; /* If the insert is delayed */
|
||||
bool bulk_insert; /* If we are performing a bulk insert */
|
||||
|
||||
@ -84,6 +84,7 @@ public:
|
||||
int open(const char *name, int mode, uint test_if_locked);
|
||||
int close(void);
|
||||
int write_row(byte * buf);
|
||||
int real_write_row(byte *buf, gzFile writer);
|
||||
int rnd_init(bool scan=1);
|
||||
int rnd_next(byte *buf);
|
||||
int rnd_pos(byte * buf, byte *pos);
|
||||
@ -102,6 +103,10 @@ public:
|
||||
int repair(THD* thd, HA_CHECK_OPT* check_opt);
|
||||
void start_bulk_insert(ha_rows rows);
|
||||
int end_bulk_insert();
|
||||
enum row_type get_row_type() const
|
||||
{
|
||||
return ROW_TYPE_COMPRESSED;
|
||||
}
|
||||
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
|
||||
enum thr_lock_type lock_type);
|
||||
};
|
||||
|
@ -4725,6 +4725,7 @@ Item_func_sp::cleanup()
|
||||
delete result_field;
|
||||
result_field= NULL;
|
||||
}
|
||||
m_sp= NULL;
|
||||
Item_func::cleanup();
|
||||
}
|
||||
|
||||
|
305
sql/sp.cc
305
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,112 +1078,203 @@ 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, Query_arena *arena,
|
||||
const LEX_STRING *key)
|
||||
{
|
||||
if (!hash_search(&lex->sroutines, (byte *)key->str, key->length))
|
||||
{
|
||||
Sroutine_hash_entry *rn=
|
||||
(Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry) +
|
||||
key->length);
|
||||
if (!rn) // OOM. Error will be reported using fatal_error().
|
||||
return FALSE;
|
||||
rn->key.length= key->length;
|
||||
rn->key.str= (char *)rn + sizeof(Sroutine_hash_entry);
|
||||
memcpy(rn->key.str, key->str, key->length);
|
||||
my_hash_insert(&lex->sroutines, (byte *)rn);
|
||||
lex->sroutines_list.link_in_list((byte *)rn, (byte **)&rn->next);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Add routine to the set of stored routines used by statement.
|
||||
|
||||
SYNOPSIS
|
||||
sp_add_used_routine()
|
||||
lex - LEX representing statement
|
||||
arena - arena in which memory for new element of the set
|
||||
will be allocated
|
||||
rt - routine name
|
||||
rt_type - routine type (one of TYPE_ENUM_PROCEDURE/...)
|
||||
|
||||
NOTES
|
||||
Will also add element to end of 'LEX::sroutines_list' list.
|
||||
|
||||
To be friendly towards prepared statements one should pass
|
||||
persistent arena as second argument.
|
||||
*/
|
||||
|
||||
void sp_add_used_routine(LEX *lex, Query_arena *arena,
|
||||
sp_name *rt, char rt_type)
|
||||
{
|
||||
rt->set_routine_type(rt_type);
|
||||
(void)add_used_routine(lex, arena, &rt->m_sroutines_key);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Merge contents of two hashes representing sets of routines used
|
||||
by statements or by other routines.
|
||||
|
||||
SYNOPSIS
|
||||
sp_update_sp_used_routines()
|
||||
dst - hash to which elements should be added
|
||||
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.
|
||||
*/
|
||||
|
||||
void
|
||||
sp_cache_routines(THD *thd, LEX *lex)
|
||||
static bool
|
||||
sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
|
||||
Sroutine_hash_entry *start)
|
||||
{
|
||||
bool routines_added= TRUE;
|
||||
bool result= FALSE;
|
||||
|
||||
DBUG_ENTER("sp_cache_routines");
|
||||
DBUG_ENTER("sp_cache_routines_and_add_tables_aux");
|
||||
|
||||
while (routines_added)
|
||||
for (Sroutine_hash_entry *rt= start; rt; rt= rt->next)
|
||||
{
|
||||
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_name name(rt->key.str, rt->key.length);
|
||||
int type= rt->key.str[0];
|
||||
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;
|
||||
@ -1204,17 +1296,98 @@ sp_cache_routines(THD *thd, LEX *lex)
|
||||
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);
|
||||
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_and_add_tables_for_view(THD *thd, LEX *lex, LEX *aux_lex)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generates the CREATE... string from the table information.
|
||||
|
18
sql/sp.h
18
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);
|
||||
bool
|
||||
sp_merge_hash(HASH *dst, HASH *src);
|
||||
void
|
||||
sp_cache_routines(THD *thd, LEX *lex);
|
||||
void sp_add_used_routine(LEX *lex, Query_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...
|
||||
|
111
sql/sp_head.cc
111
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);
|
||||
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
|
||||
|
||||
@ -1976,10 +1997,8 @@ void st_lex::cleanup_after_one_table_open()
|
||||
select_lex.cut_subtree();
|
||||
}
|
||||
time_zone_tables_used= 0;
|
||||
if (spfuns.records)
|
||||
my_hash_reset(&spfuns);
|
||||
if (spprocs.records)
|
||||
my_hash_reset(&spprocs);
|
||||
if (sroutines.records)
|
||||
my_hash_reset(&sroutines);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
@ -7740,12 +7740,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
|
||||
@ -7760,7 +7754,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