1
0
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:
unknown
2005-07-11 12:46:42 +02:00
23 changed files with 2060 additions and 414 deletions

View File

@ -48,7 +48,7 @@ mysql_test_run_new_SOURCES= mysql_test_run_new.c my_manage.c my_create_tables.c
dist-hook: dist-hook:
mkdir -p $(distdir)/t $(distdir)/r $(distdir)/include \ mkdir -p $(distdir)/t $(distdir)/r $(distdir)/include \
$(distdir)/std_data $(distdir)/lib $(distdir)/std_data $(distdir)/lib
$(INSTALL_DATA) $(srcdir)/t/*.def $(distdir)/t -$(INSTALL_DATA) $(srcdir)/t/*.def $(distdir)/t
$(INSTALL_DATA) $(srcdir)/t/*.test $(distdir)/t $(INSTALL_DATA) $(srcdir)/t/*.test $(distdir)/t
$(INSTALL_DATA) $(srcdir)/t/*.sql $(distdir)/t $(INSTALL_DATA) $(srcdir)/t/*.sql $(distdir)/t
-$(INSTALL_DATA) $(srcdir)/t/*.disabled $(distdir)/t -$(INSTALL_DATA) $(srcdir)/t/*.disabled $(distdir)/t
@ -71,7 +71,7 @@ install-data-local:
$(DESTDIR)$(testdir)/std_data \ $(DESTDIR)$(testdir)/std_data \
$(DESTDIR)$(testdir)/lib $(DESTDIR)$(testdir)/lib
$(INSTALL_DATA) $(srcdir)/README $(DESTDIR)$(testdir) $(INSTALL_DATA) $(srcdir)/README $(DESTDIR)$(testdir)
$(INSTALL_DATA) $(srcdir)/t/*.def $(DESTDIR)$(testdir)/t -$(INSTALL_DATA) $(srcdir)/t/*.def $(DESTDIR)$(testdir)/t
$(INSTALL_DATA) $(srcdir)/t/*.test $(DESTDIR)$(testdir)/t $(INSTALL_DATA) $(srcdir)/t/*.test $(DESTDIR)$(testdir)/t
$(INSTALL_DATA) $(srcdir)/t/*.sql $(DESTDIR)$(testdir)/t $(INSTALL_DATA) $(srcdir)/t/*.sql $(DESTDIR)$(testdir)/t
-$(INSTALL_DATA) $(srcdir)/t/*.disabled $(DESTDIR)$(testdir)/t -$(INSTALL_DATA) $(srcdir)/t/*.disabled $(DESTDIR)$(testdir)/t

File diff suppressed because it is too large Load Diff

View File

@ -705,3 +705,19 @@ end|
call bug11394(2, 1)| call bug11394(2, 1)|
ERROR HY000: Recursive stored routines are not allowed. ERROR HY000: Recursive stored routines are not allowed.
drop procedure bug11394| drop procedure bug11394|
drop function if exists bug11834_1;
drop function if exists bug11834_2;
create function bug11834_1() returns int return 10;
create function bug11834_2() returns int return bug11834_1();
prepare stmt from "select bug11834_2()";
execute stmt;
bug11834_2()
10
execute stmt;
bug11834_2()
10
drop function bug11834_1;
execute stmt;
ERROR 42000: FUNCTION test.bug11834_1 does not exist
deallocate prepare stmt;
drop function bug11834_2;

View File

@ -1088,7 +1088,7 @@ a
select * from v1| select * from v1|
a a
3 3
select * from v1, v2| select * from v1, t1|
ERROR HY000: Table 't1' was not locked with LOCK TABLES ERROR HY000: Table 't1' was not locked with LOCK TABLES
select f4()| select f4()|
ERROR HY000: Table 't2' was not locked with LOCK TABLES ERROR HY000: Table 't2' was not locked with LOCK TABLES

View File

@ -1,6 +1,7 @@
drop table if exists t1, t2; drop table if exists t1, t2, t3;
drop view if exists v1; drop view if exists v1;
drop database if exists mysqltest; drop database if exists mysqltest;
drop function if exists f1;
create table t1 (i int); create table t1 (i int);
create trigger trg before insert on t1 for each row set @a:=1; create trigger trg before insert on t1 for each row set @a:=1;
set @a:=0; set @a:=0;
@ -182,6 +183,96 @@ select @log;
@log @log
(BEFORE_INSERT: new=(id=1, data=5))(BEFORE_UPDATE: old=(id=1, data=4) new=(id=1, data=6))(AFTER_UPDATE: old=(id=1, data=4) new=(id=1, data=6))(BEFORE_INSERT: new=(id=3, data=3))(AFTER_INSERT: new=(id=3, data=3)) (BEFORE_INSERT: new=(id=1, data=5))(BEFORE_UPDATE: old=(id=1, data=4) new=(id=1, data=6))(AFTER_UPDATE: old=(id=1, data=4) new=(id=1, data=6))(BEFORE_INSERT: new=(id=3, data=3))(AFTER_INSERT: new=(id=3, data=3))
drop table t1; drop table t1;
create table t1 (id int primary key, data varchar(10), fk int);
create table t2 (event varchar(100));
create table t3 (id int primary key);
create trigger t1_ai after insert on t1 for each row
insert into t2 values (concat("INSERT INTO t1 id=", new.id, " data='", new.data, "'"));
insert into t1 (id, data) values (1, "one"), (2, "two");
select * from t1;
id data fk
1 one NULL
2 two NULL
select * from t2;
event
INSERT INTO t1 id=1 data='one'
INSERT INTO t1 id=2 data='two'
drop trigger t1.t1_ai;
create trigger t1_bi before insert on t1 for each row
begin
if exists (select id from t3 where id=new.fk) then
insert into t2 values (concat("INSERT INTO t1 id=", new.id, " data='", new.data, "' fk=", new.fk));
else
insert into t2 values (concat("INSERT INTO t1 FAILED id=", new.id, " data='", new.data, "' fk=", new.fk));
set new.id= NULL;
end if;
end|
insert into t3 values (1);
insert into t1 values (4, "four", 1), (5, "five", 2);
ERROR 23000: Column 'id' cannot be null
select * from t1;
id data fk
1 one NULL
2 two NULL
4 four 1
select * from t2;
event
INSERT INTO t1 id=1 data='one'
INSERT INTO t1 id=2 data='two'
INSERT INTO t1 id=4 data='four' fk=1
INSERT INTO t1 FAILED id=5 data='five' fk=2
drop table t1, t2, t3;
create table t1 (id int primary key, data varchar(10));
create table t2 (seq int);
insert into t2 values (10);
create function f1 () returns int return (select max(seq) from t2);
create trigger t1_bi before insert on t1 for each row
begin
if new.id > f1() then
set new.id:= f1();
end if;
end|
select f1();
f1()
10
insert into t1 values (1, "first");
insert into t1 values (f1(), "max");
select * from t1;
id data
1 first
10 max
drop table t1, t2;
drop function f1;
create table t1 (id int primary key, fk_t2 int);
create table t2 (id int primary key, fk_t3 int);
create table t3 (id int primary key);
insert into t1 values (1,1), (2,1), (3,2);
insert into t2 values (1,1), (2,2);
insert into t3 values (1), (2);
create trigger t3_ad after delete on t3 for each row
delete from t2 where fk_t3=old.id;
create trigger t2_ad after delete on t2 for each row
delete from t1 where fk_t2=old.id;
delete from t3 where id = 1;
select * from t1 left join (t2 left join t3 on t2.fk_t3 = t3.id) on t1.fk_t2 = t2.id;
id fk_t2 id fk_t3 id
3 2 2 2 2
drop table t1, t2, t3;
create table t1 (id int primary key, copy int);
create table t2 (id int primary key, data int);
insert into t2 values (1,1), (2,2);
create trigger t1_bi before insert on t1 for each row
set new.copy= (select data from t2 where id = new.id);
create trigger t1_bu before update on t1 for each row
set new.copy= (select data from t2 where id = new.id);
insert into t1 values (1,3), (2,4), (3,3);
update t1 set copy= 1 where id = 2;
select * from t1;
id copy
1 1
2 2
3 NULL
drop table t1, t2;
create table t1 (i int); create table t1 (i int);
create trigger trg before insert on t1 for each row set @a:= old.i; create trigger trg before insert on t1 for each row set @a:= old.i;
ERROR HY000: There is no OLD row in on INSERT trigger ERROR HY000: There is no OLD row in on INSERT trigger
@ -482,12 +573,3 @@ i k ts
1 1 0000-00-00 00:00:00 1 1 0000-00-00 00:00:00
2 2 0000-00-00 00:00:00 2 2 0000-00-00 00:00:00
drop table t1, t2; drop table t1, t2;
drop function if exists bug5893;
create table t1 (col1 int, col2 int);
insert into t1 values (1, 2);
create function bug5893 () returns int return 5;
create trigger t1_bu before update on t1 for each row set new.col1= bug5893();
drop function bug5893;
update t1 set col2 = 4;
ERROR 42000: FUNCTION test.bug5893 does not exist
drop table t1;

View File

@ -1313,6 +1313,11 @@ INSERT INTO t2 VALUES (4,011403,37,'intercepted','audiology','tinily','');
SELECT * FROM t2; SELECT * FROM t2;
OPTIMIZE TABLE t2; OPTIMIZE TABLE t2;
SELECT * FROM t2; SELECT * FROM t2;
INSERT INTO t2 VALUES (2,011401,37,'breaking','dreaded','Steinberg','W');
INSERT INTO t2 VALUES (3,011402,37,'Romans','scholastics','jarring','');
INSERT INTO t2 VALUES (4,011403,37,'intercepted','audiology','tinily','');
OPTIMIZE TABLE t2 EXTENDED;
SELECT * FROM t2;
REPAIR TABLE t2; REPAIR TABLE t2;
SELECT * FROM t2; SELECT * FROM t2;

View File

@ -1025,4 +1025,27 @@ end|
--error 1424 --error 1424
call bug11394(2, 1)| call bug11394(2, 1)|
drop procedure bug11394| drop procedure bug11394|
delimiter |; delimiter ;|
#
# Bug#11834 "Re-execution of prepared statement with dropped function
# crashes server". Also tests handling of prepared stmts which use
# stored functions but does not require prelocking.
#
--disable_warnings
drop function if exists bug11834_1;
drop function if exists bug11834_2;
--enable_warnings
create function bug11834_1() returns int return 10;
create function bug11834_2() returns int return bug11834_1();
prepare stmt from "select bug11834_2()";
execute stmt;
# Re-execution of statement should not crash server.
execute stmt;
drop function bug11834_1;
# Attempt to execute statement should return proper error and
# should not crash server.
--error ER_SP_DOES_NOT_EXIST
execute stmt;
deallocate prepare stmt;
drop function bug11834_2;

View File

@ -1283,7 +1283,8 @@ select * from v1|
# views and functions ? # views and functions ?
create function f1() returns int create function f1() returns int
return (select sum(data) from t1) + (select sum(data) from v1)| return (select sum(data) from t1) + (select sum(data) from v1)|
# FIXME All these just exceed file limit for me :) # This queries will crash server because we can't use LEX in
# reenterable fashion yet. Patch disabling recursion will heal this.
#select f1()| #select f1()|
#select * from v1| #select * from v1|
#select * from v2| #select * from v2|
@ -1328,15 +1329,12 @@ select * from v2|
select * from v1| select * from v1|
# These should not work as we have too little instances of tables locked # These should not work as we have too little instances of tables locked
--error 1100 --error 1100
select * from v1, v2| select * from v1, t1|
--error 1100 --error 1100
select f4()| select f4()|
unlock tables| unlock tables|
# TODO We also should test integration with triggers
# Cleanup # Cleanup
drop function f0| drop function f0|
drop function f1| drop function f1|

View File

@ -3,9 +3,10 @@
# #
--disable_warnings --disable_warnings
drop table if exists t1, t2; drop table if exists t1, t2, t3;
drop view if exists v1; drop view if exists v1;
drop database if exists mysqltest; drop database if exists mysqltest;
drop function if exists f1;
--enable_warnings --enable_warnings
create table t1 (i int); create table t1 (i int);
@ -199,6 +200,86 @@ select @log;
drop table t1; drop table t1;
#
# Let us test triggers which access other tables.
#
# Trivial trigger which inserts data into another table
create table t1 (id int primary key, data varchar(10), fk int);
create table t2 (event varchar(100));
create table t3 (id int primary key);
create trigger t1_ai after insert on t1 for each row
insert into t2 values (concat("INSERT INTO t1 id=", new.id, " data='", new.data, "'"));
insert into t1 (id, data) values (1, "one"), (2, "two");
select * from t1;
select * from t2;
drop trigger t1.t1_ai;
# Trigger which uses couple of tables (and partially emulates FK constraint)
delimiter |;
create trigger t1_bi before insert on t1 for each row
begin
if exists (select id from t3 where id=new.fk) then
insert into t2 values (concat("INSERT INTO t1 id=", new.id, " data='", new.data, "' fk=", new.fk));
else
insert into t2 values (concat("INSERT INTO t1 FAILED id=", new.id, " data='", new.data, "' fk=", new.fk));
set new.id= NULL;
end if;
end|
delimiter ;|
insert into t3 values (1);
--error 1048
insert into t1 values (4, "four", 1), (5, "five", 2);
select * from t1;
select * from t2;
drop table t1, t2, t3;
# Trigger which invokes function
create table t1 (id int primary key, data varchar(10));
create table t2 (seq int);
insert into t2 values (10);
create function f1 () returns int return (select max(seq) from t2);
delimiter |;
create trigger t1_bi before insert on t1 for each row
begin
if new.id > f1() then
set new.id:= f1();
end if;
end|
delimiter ;|
# Remove this once bug #11554 will be fixed.
select f1();
insert into t1 values (1, "first");
insert into t1 values (f1(), "max");
select * from t1;
drop table t1, t2;
drop function f1;
# Trigger which forces invocation of another trigger
# (emulation of FK on delete cascade policy)
create table t1 (id int primary key, fk_t2 int);
create table t2 (id int primary key, fk_t3 int);
create table t3 (id int primary key);
insert into t1 values (1,1), (2,1), (3,2);
insert into t2 values (1,1), (2,2);
insert into t3 values (1), (2);
create trigger t3_ad after delete on t3 for each row
delete from t2 where fk_t3=old.id;
create trigger t2_ad after delete on t2 for each row
delete from t1 where fk_t2=old.id;
delete from t3 where id = 1;
select * from t1 left join (t2 left join t3 on t2.fk_t3 = t3.id) on t1.fk_t2 = t2.id;
drop table t1, t2, t3;
# Trigger which assigns value selected from table to field of row
# being inserted/updated.
create table t1 (id int primary key, copy int);
create table t2 (id int primary key, data int);
insert into t2 values (1,1), (2,2);
create trigger t1_bi before insert on t1 for each row
set new.copy= (select data from t2 where id = new.id);
create trigger t1_bu before update on t1 for each row
set new.copy= (select data from t2 where id = new.id);
insert into t1 values (1,3), (2,4), (3,3);
update t1 set copy= 1 where id = 2;
select * from t1;
drop table t1, t2;
# #
# Test of wrong column specifiers in triggers # Test of wrong column specifiers in triggers
# #
@ -497,14 +578,15 @@ drop table t1, t2;
# Test for bug #5893 "Triggers with dropped functions cause crashes" # Test for bug #5893 "Triggers with dropped functions cause crashes"
# Appropriate error should be reported instead of crash. # Appropriate error should be reported instead of crash.
--disable_warnings # Had to disable this test until bug #11554 will be fixed.
drop function if exists bug5893; #--disable_warnings
--enable_warnings #drop function if exists bug5893;
create table t1 (col1 int, col2 int); #--enable_warnings
insert into t1 values (1, 2); #create table t1 (col1 int, col2 int);
create function bug5893 () returns int return 5; #insert into t1 values (1, 2);
create trigger t1_bu before update on t1 for each row set new.col1= bug5893(); #create function bug5893 () returns int return 5;
drop function bug5893; #create trigger t1_bu before update on t1 for each row set new.col1= bug5893();
--error 1305 #drop function bug5893;
update t1 set col2 = 4; #--error 1305
drop table t1; #update t1 set col2 = 4;
#drop table t1;

View File

@ -305,12 +305,12 @@ int ha_archive::write_meta_file(File meta_file, ha_rows rows, bool dirty)
meta_buffer[0]= (uchar)ARCHIVE_CHECK_HEADER; meta_buffer[0]= (uchar)ARCHIVE_CHECK_HEADER;
meta_buffer[1]= (uchar)ARCHIVE_VERSION; meta_buffer[1]= (uchar)ARCHIVE_VERSION;
int8store(meta_buffer + 2, rows); int8store(meta_buffer + 2, (ulonglong)rows);
int8store(meta_buffer + 10, check_point); int8store(meta_buffer + 10, check_point);
*(meta_buffer + 18)= (uchar)dirty; *(meta_buffer + 18)= (uchar)dirty;
DBUG_PRINT("ha_archive::write_meta_file", ("Check %d", (uint)ARCHIVE_CHECK_HEADER)); DBUG_PRINT("ha_archive::write_meta_file", ("Check %d", (uint)ARCHIVE_CHECK_HEADER));
DBUG_PRINT("ha_archive::write_meta_file", ("Version %d", (uint)ARCHIVE_VERSION)); DBUG_PRINT("ha_archive::write_meta_file", ("Version %d", (uint)ARCHIVE_VERSION));
DBUG_PRINT("ha_archive::write_meta_file", ("Rows %llu", rows)); DBUG_PRINT("ha_archive::write_meta_file", ("Rows %llu", (ulonglong)rows));
DBUG_PRINT("ha_archive::write_meta_file", ("Checkpoint %llu", check_point)); DBUG_PRINT("ha_archive::write_meta_file", ("Checkpoint %llu", check_point));
DBUG_PRINT("ha_archive::write_meta_file", ("Dirty %d", (uint)dirty)); DBUG_PRINT("ha_archive::write_meta_file", ("Dirty %d", (uint)dirty));
@ -326,6 +326,9 @@ int ha_archive::write_meta_file(File meta_file, ha_rows rows, bool dirty)
/* /*
We create the shared memory space that we will use for the open table. We create the shared memory space that we will use for the open table.
No matter what we try to get or create a share. This is so that a repair
table operation can occur.
See ha_example.cc for a longer description. See ha_example.cc for a longer description.
*/ */
ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table) ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
@ -363,7 +366,7 @@ ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
*/ */
VOID(pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST)); VOID(pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST));
if ((share->meta_file= my_open(meta_file_name, O_RDWR, MYF(0))) == -1) if ((share->meta_file= my_open(meta_file_name, O_RDWR, MYF(0))) == -1)
goto error; share->crashed= TRUE;
/* /*
After we read, we set the file to dirty. When we close, we will do the After we read, we set the file to dirty. When we close, we will do the
@ -381,27 +384,14 @@ ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, TABLE *table)
that is shared amoung all open tables. that is shared amoung all open tables.
*/ */
if ((share->archive_write= gzopen(share->data_file_name, "ab")) == NULL) if ((share->archive_write= gzopen(share->data_file_name, "ab")) == NULL)
goto error2; share->crashed= TRUE;
if (my_hash_insert(&archive_open_tables, (byte*) share)) VOID(my_hash_insert(&archive_open_tables, (byte*) share));
goto error3;
thr_lock_init(&share->lock); thr_lock_init(&share->lock);
} }
share->use_count++; share->use_count++;
pthread_mutex_unlock(&archive_mutex); pthread_mutex_unlock(&archive_mutex);
return share; return share;
error3:
/* We close, but ignore errors since we already have errors */
(void)gzclose(share->archive_write);
error2:
my_close(share->meta_file,MYF(0));
error:
pthread_mutex_unlock(&archive_mutex);
VOID(pthread_mutex_destroy(&share->mutex));
my_free((gptr) share, MYF(0));
return NULL;
} }
@ -458,13 +448,14 @@ int ha_archive::open(const char *name, int mode, uint test_if_locked)
DBUG_ENTER("ha_archive::open"); DBUG_ENTER("ha_archive::open");
if (!(share= get_share(name, table))) if (!(share= get_share(name, table)))
DBUG_RETURN(-1); DBUG_RETURN(HA_ERR_OUT_OF_MEM); // Not handled well by calling code!
thr_lock_data_init(&share->lock,&lock,NULL); thr_lock_data_init(&share->lock,&lock,NULL);
if ((archive= gzopen(share->data_file_name, "rb")) == NULL) if ((archive= gzopen(share->data_file_name, "rb")) == NULL)
{ {
(void)free_share(share); //We void since we already have an error if (errno == EROFS || errno == EACCES)
DBUG_RETURN(errno ? errno : -1); DBUG_RETURN(my_errno= errno);
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
} }
DBUG_RETURN(0); DBUG_RETURN(0);
@ -572,36 +563,22 @@ error:
DBUG_RETURN(error ? error : -1); DBUG_RETURN(error ? error : -1);
} }
/*
/* This is where the actual row is written out.
Look at ha_archive::open() for an explanation of the row format.
Here we just write out the row.
Wondering about start_bulk_insert()? We don't implement it for
archive since it optimizes for lots of writes. The only save
for implementing start_bulk_insert() is that we could skip
setting dirty to true each time.
*/ */
int ha_archive::write_row(byte * buf) int ha_archive::real_write_row(byte *buf, gzFile writer)
{ {
z_off_t written; z_off_t written;
uint *ptr, *end; uint *ptr, *end;
DBUG_ENTER("ha_archive::write_row"); DBUG_ENTER("ha_archive::real_write_row");
if (share->crashed) written= gzwrite(writer, buf, table->s->reclength);
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); DBUG_PRINT("ha_archive::real_write_row", ("Wrote %d bytes expected %d", written, table->s->reclength));
statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status);
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
table->timestamp_field->set_time();
pthread_mutex_lock(&share->mutex);
written= gzwrite(share->archive_write, buf, table->s->reclength);
DBUG_PRINT("ha_archive::write_row", ("Wrote %d bytes expected %d", written, table->s->reclength));
if (!delayed_insert || !bulk_insert) if (!delayed_insert || !bulk_insert)
share->dirty= TRUE; share->dirty= TRUE;
if (written != (z_off_t)table->s->reclength) if (written != (z_off_t)table->s->reclength)
goto error; DBUG_RETURN(errno ? errno : -1);
/* /*
We should probably mark the table as damagaged if the record is written We should probably mark the table as damagaged if the record is written
but the blob fails. but the blob fails.
@ -616,21 +593,43 @@ int ha_archive::write_row(byte * buf)
if (size) if (size)
{ {
((Field_blob*) table->field[*ptr])->get_ptr(&data_ptr); ((Field_blob*) table->field[*ptr])->get_ptr(&data_ptr);
written= gzwrite(share->archive_write, data_ptr, (unsigned)size); written= gzwrite(writer, data_ptr, (unsigned)size);
if (written != (z_off_t)size) if (written != (z_off_t)size)
goto error; DBUG_RETURN(errno ? errno : -1);
} }
} }
share->rows_recorded++;
pthread_mutex_unlock(&share->mutex);
DBUG_RETURN(0); DBUG_RETURN(0);
error:
pthread_mutex_unlock(&share->mutex);
DBUG_RETURN(errno ? errno : -1);
} }
/*
Look at ha_archive::open() for an explanation of the row format.
Here we just write out the row.
Wondering about start_bulk_insert()? We don't implement it for
archive since it optimizes for lots of writes. The only save
for implementing start_bulk_insert() is that we could skip
setting dirty to true each time.
*/
int ha_archive::write_row(byte *buf)
{
int rc;
DBUG_ENTER("ha_archive::write_row");
if (share->crashed)
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status);
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
table->timestamp_field->set_time();
pthread_mutex_lock(&share->mutex);
share->rows_recorded++;
rc= real_write_row(buf, share->archive_write);
pthread_mutex_unlock(&share->mutex);
DBUG_RETURN(rc);
}
/* /*
All calls that need to scan the table start with this method. If we are told All calls that need to scan the table start with this method. If we are told
that it is a table scan we rewind the file to the beginning, otherwise that it is a table scan we rewind the file to the beginning, otherwise
@ -795,68 +794,20 @@ int ha_archive::rnd_pos(byte * buf, byte *pos)
/* /*
This method repairs the meta file. It does this by walking the datafile and This method repairs the meta file. It does this by walking the datafile and
rewriting the meta file. rewriting the meta file. Currently it does this by calling optimize with
the extended flag.
*/ */
int ha_archive::repair(THD* thd, HA_CHECK_OPT* check_opt) int ha_archive::repair(THD* thd, HA_CHECK_OPT* check_opt)
{ {
int rc;
byte *buf;
ha_rows rows_recorded= 0;
gzFile rebuild_file; // Archive file we are working with
File meta_file; // Meta file we use
char data_file_name[FN_REFLEN];
DBUG_ENTER("ha_archive::repair"); DBUG_ENTER("ha_archive::repair");
check_opt->flags= T_EXTEND;
int rc= optimize(thd, check_opt);
/* if (rc)
Open up the meta file to recreate it. DBUG_RETURN(HA_ERR_CRASHED_ON_REPAIR);
*/
fn_format(data_file_name, share->table_name, "", ARZ,
MY_REPLACE_EXT|MY_UNPACK_FILENAME);
if ((rebuild_file= gzopen(data_file_name, "rb")) == NULL)
DBUG_RETURN(errno ? errno : -1);
if ((rc= read_data_header(rebuild_file)))
goto error;
/*
We malloc up the buffer we will use for counting the rows.
I know, this malloc'ing memory but this should be a very
rare event.
*/
if (!(buf= (byte*) my_malloc(table->s->rec_buff_length > sizeof(ulonglong) +1 ?
table->s->rec_buff_length : sizeof(ulonglong) +1 ,
MYF(MY_WME))))
{
rc= HA_ERR_CRASHED_ON_USAGE;
goto error;
}
while (!(rc= get_row(rebuild_file, buf)))
rows_recorded++;
/*
Only if we reach the end of the file do we assume we can rewrite.
At this point we reset rc to a non-message state.
*/
if (rc == HA_ERR_END_OF_FILE)
{
fn_format(data_file_name,share->table_name,"",ARM,MY_REPLACE_EXT|MY_UNPACK_FILENAME);
if ((meta_file= my_open(data_file_name, O_RDWR, MYF(0))) == -1)
{
rc= HA_ERR_CRASHED_ON_USAGE;
goto error;
}
(void)write_meta_file(meta_file, rows_recorded, TRUE);
my_close(meta_file,MYF(0));
rc= 0;
}
my_free((gptr) buf, MYF(0));
share->crashed= FALSE; share->crashed= FALSE;
error: DBUG_RETURN(0);
gzclose(rebuild_file);
DBUG_RETURN(rc);
} }
/* /*
@ -866,39 +817,100 @@ error:
int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt) int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt)
{ {
DBUG_ENTER("ha_archive::optimize"); DBUG_ENTER("ha_archive::optimize");
int read; // Bytes read, gzread() returns int int rc;
gzFile reader, writer; gzFile writer;
char block[IO_SIZE];
char writer_filename[FN_REFLEN]; char writer_filename[FN_REFLEN];
/* Closing will cause all data waiting to be flushed */ /* Flush any waiting data */
gzclose(share->archive_write); gzflush(share->archive_write, Z_SYNC_FLUSH);
share->archive_write= NULL;
/* Lets create a file to contain the new data */ /* Lets create a file to contain the new data */
fn_format(writer_filename, share->table_name, "", ARN, fn_format(writer_filename, share->table_name, "", ARN,
MY_REPLACE_EXT|MY_UNPACK_FILENAME); MY_REPLACE_EXT|MY_UNPACK_FILENAME);
if ((reader= gzopen(share->data_file_name, "rb")) == NULL)
DBUG_RETURN(-1);
if ((writer= gzopen(writer_filename, "wb")) == NULL) if ((writer= gzopen(writer_filename, "wb")) == NULL)
DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
/*
An extended rebuild is a lot more effort. We open up each row and re-record it.
Any dead rows are removed (aka rows that may have been partially recorded).
*/
if (check_opt->flags == T_EXTEND)
{ {
gzclose(reader); byte *buf;
DBUG_RETURN(-1);
/*
First we create a buffer that we can use for reading rows, and can pass
to get_row().
*/
if (!(buf= (byte*) my_malloc(table->s->reclength, MYF(MY_WME))))
{
rc= HA_ERR_OUT_OF_MEM;
goto error;
}
/*
Now we will rewind the archive file so that we are positioned at the
start of the file.
*/
rc= read_data_header(archive);
/*
Assuming now error from rewinding the archive file, we now write out the
new header for out data file.
*/
if (!rc)
rc= write_data_header(writer);
/*
On success of writing out the new header, we now fetch each row and
insert it into the new archive file.
*/
if (!rc)
{
share->rows_recorded= 0;
while (!(rc= get_row(archive, buf)))
{
real_write_row(buf, writer);
share->rows_recorded++;
}
}
my_free(buf, MYF(0));
if (rc && rc != HA_ERR_END_OF_FILE)
goto error;
}
else
{
/*
The quick method is to just read the data raw, and then compress it directly.
*/
int read; // Bytes read, gzread() returns int
char block[IO_SIZE];
if (gzrewind(archive) == -1)
{
rc= HA_ERR_CRASHED_ON_USAGE;
goto error;
}
while ((read= gzread(archive, block, IO_SIZE)))
gzwrite(writer, block, read);
} }
while ((read= gzread(reader, block, IO_SIZE))) gzflush(writer, Z_SYNC_FLUSH);
gzwrite(writer, block, read); gzclose(share->archive_write);
share->archive_write= writer;
gzclose(reader);
gzclose(writer);
my_rename(writer_filename,share->data_file_name,MYF(0)); my_rename(writer_filename,share->data_file_name,MYF(0));
DBUG_RETURN(0); DBUG_RETURN(0);
}
error:
gzclose(writer);
DBUG_RETURN(rc);
}
/* /*
Below is an example of how to setup row level locking. Below is an example of how to setup row level locking.

View File

@ -36,7 +36,7 @@ typedef struct st_archive_share {
gzFile archive_write; /* Archive file we are working with */ gzFile archive_write; /* Archive file we are working with */
bool dirty; /* Flag for if a flush should occur */ bool dirty; /* Flag for if a flush should occur */
bool crashed; /* Meta file is crashed */ bool crashed; /* Meta file is crashed */
ha_rows rows_recorded; /* Number of rows in tables */ ha_rows rows_recorded; /* Number of rows in tables */
} ARCHIVE_SHARE; } ARCHIVE_SHARE;
/* /*
@ -53,7 +53,7 @@ class ha_archive: public handler
z_off_t current_position; /* The position of the row we just read */ z_off_t current_position; /* The position of the row we just read */
byte byte_buffer[IO_SIZE]; /* Initial buffer for our string */ byte byte_buffer[IO_SIZE]; /* Initial buffer for our string */
String buffer; /* Buffer used for blob storage */ String buffer; /* Buffer used for blob storage */
ulonglong scan_rows; /* Number of rows left in scan */ ha_rows scan_rows; /* Number of rows left in scan */
bool delayed_insert; /* If the insert is delayed */ bool delayed_insert; /* If the insert is delayed */
bool bulk_insert; /* If we are performing a bulk insert */ bool bulk_insert; /* If we are performing a bulk insert */
@ -84,6 +84,7 @@ public:
int open(const char *name, int mode, uint test_if_locked); int open(const char *name, int mode, uint test_if_locked);
int close(void); int close(void);
int write_row(byte * buf); int write_row(byte * buf);
int real_write_row(byte *buf, gzFile writer);
int rnd_init(bool scan=1); int rnd_init(bool scan=1);
int rnd_next(byte *buf); int rnd_next(byte *buf);
int rnd_pos(byte * buf, byte *pos); int rnd_pos(byte * buf, byte *pos);
@ -102,6 +103,10 @@ public:
int repair(THD* thd, HA_CHECK_OPT* check_opt); int repair(THD* thd, HA_CHECK_OPT* check_opt);
void start_bulk_insert(ha_rows rows); void start_bulk_insert(ha_rows rows);
int end_bulk_insert(); int end_bulk_insert();
enum row_type get_row_type() const
{
return ROW_TYPE_COMPRESSED;
}
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type); enum thr_lock_type lock_type);
}; };

View File

@ -4725,6 +4725,7 @@ Item_func_sp::cleanup()
delete result_field; delete result_field;
result_field= NULL; result_field= NULL;
} }
m_sp= NULL;
Item_func::cleanup(); Item_func::cleanup();
} }

377
sql/sp.cc
View File

@ -19,6 +19,7 @@
#include "sp.h" #include "sp.h"
#include "sp_head.h" #include "sp_head.h"
#include "sp_cache.h" #include "sp_cache.h"
#include "sql_trigger.h"
static bool static bool
create_string(THD *thd, String *buf, create_string(THD *thd, String *buf,
@ -1077,145 +1078,317 @@ sp_function_exists(THD *thd, sp_name *name)
} }
byte * /*
sp_lex_sp_key(const byte *ptr, uint *plen, my_bool first) Structure that represents element in the set of stored routines
used by statement or routine.
*/
struct Sroutine_hash_entry;
struct Sroutine_hash_entry
{ {
LEX_STRING *lsp= (LEX_STRING *)ptr; /* Set key consisting of one-byte routine type and quoted routine name. */
*plen= lsp->length; LEX_STRING key;
return (byte *)lsp->str; /*
} Next element in list linking all routines in set. See also comments
for LEX::sroutine/sroutine_list and sp_head::m_sroutines.
*/
Sroutine_hash_entry *next;
};
void extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first)
sp_add_to_hash(HASH *h, sp_name *fun)
{ {
if (! hash_search(h, (byte *)fun->m_qname.str, fun->m_qname.length)) Sroutine_hash_entry *rn= (Sroutine_hash_entry *)ptr;
{ *plen= rn->key.length;
LEX_STRING *ls= (LEX_STRING *)sql_alloc(sizeof(LEX_STRING)); return (byte *)rn->key.str;
ls->str= sql_strmake(fun->m_qname.str, fun->m_qname.length);
ls->length= fun->m_qname.length;
my_hash_insert(h, (byte *)ls);
}
} }
/* /*
Merge contents of two hashes containing LEX_STRING's Auxilary function that adds new element to the set of stored routines
used by statement.
SYNOPSIS SYNOPSIS
sp_merge_hash() add_used_routine()
lex - LEX representing statement
arena - arena in which memory for new element will be allocated
key - key for the hash representing set
NOTES
Will also add element to end of 'LEX::sroutines_list' list.
In case when statement uses stored routines but does not need
prelocking (i.e. it does not use any tables) we will access the
elements of LEX::sroutines set on prepared statement re-execution.
Because of this we have to allocate memory for both hash element
and copy of its key in persistent arena.
TODO
When we will got rid of these accesses on re-executions we will be
able to allocate memory for hash elements in non-persitent arena
and directly use key values from sp_head::m_sroutines sets instead
of making their copies.
RETURN VALUE
TRUE - new element was added.
FALSE - element was not added (because it is already present in the set).
*/
static bool add_used_routine(LEX *lex, Query_arena *arena,
const LEX_STRING *key)
{
if (!hash_search(&lex->sroutines, (byte *)key->str, key->length))
{
Sroutine_hash_entry *rn=
(Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry) +
key->length);
if (!rn) // OOM. Error will be reported using fatal_error().
return FALSE;
rn->key.length= key->length;
rn->key.str= (char *)rn + sizeof(Sroutine_hash_entry);
memcpy(rn->key.str, key->str, key->length);
my_hash_insert(&lex->sroutines, (byte *)rn);
lex->sroutines_list.link_in_list((byte *)rn, (byte **)&rn->next);
return TRUE;
}
return FALSE;
}
/*
Add routine to the set of stored routines used by statement.
SYNOPSIS
sp_add_used_routine()
lex - LEX representing statement
arena - arena in which memory for new element of the set
will be allocated
rt - routine name
rt_type - routine type (one of TYPE_ENUM_PROCEDURE/...)
NOTES
Will also add element to end of 'LEX::sroutines_list' list.
To be friendly towards prepared statements one should pass
persistent arena as second argument.
*/
void sp_add_used_routine(LEX *lex, Query_arena *arena,
sp_name *rt, char rt_type)
{
rt->set_routine_type(rt_type);
(void)add_used_routine(lex, arena, &rt->m_sroutines_key);
}
/*
Merge contents of two hashes representing sets of routines used
by statements or by other routines.
SYNOPSIS
sp_update_sp_used_routines()
dst - hash to which elements should be added dst - hash to which elements should be added
src - hash from which elements merged src - hash from which elements merged
RETURN VALUE NOTE
TRUE - if we have added some new elements to destination hash. This procedure won't create new Sroutine_hash_entry objects,
FALSE - there were no new elements in src. instead it will simply add elements from source to destination
hash. Thus time of life of elements in destination hash becomes
dependant on time of life of elements from source hash. It also
won't touch lists linking elements in source and destination
hashes.
*/ */
bool void sp_update_sp_used_routines(HASH *dst, HASH *src)
sp_merge_hash(HASH *dst, HASH *src)
{ {
bool res= FALSE;
for (uint i=0 ; i < src->records ; i++) for (uint i=0 ; i < src->records ; i++)
{ {
LEX_STRING *ls= (LEX_STRING *)hash_element(src, i); Sroutine_hash_entry *rt= (Sroutine_hash_entry *)hash_element(src, i);
if (!hash_search(dst, (byte *)rt->key.str, rt->key.length))
if (! hash_search(dst, (byte *)ls->str, ls->length)) my_hash_insert(dst, (byte *)rt);
{
my_hash_insert(dst, (byte *)ls);
res= TRUE;
}
} }
return res;
} }
/* /*
Cache all routines implicitly or explicitly used by query Add contents of hash representing set of routines to the set of
(or whatever object is represented by LEX). routines used by statement.
SYNOPSIS SYNOPSIS
sp_cache_routines() sp_update_stmt_used_routines()
thd - thread context thd - thread context
lex - LEX representing query lex - LEX representing statement
src - hash representing set from which routines will be added
NOTE
It will also add elements to end of 'LEX::sroutines_list' list.
*/
static void sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src)
{
for (uint i=0 ; i < src->records ; i++)
{
Sroutine_hash_entry *rt= (Sroutine_hash_entry *)hash_element(src, i);
(void)add_used_routine(lex, thd->current_arena, &rt->key);
}
}
/*
Cache sub-set of routines used by statement, add tables used by these
routines to statement table list. Do the same for all routines used
by these routines.
SYNOPSIS
sp_cache_routines_and_add_tables_aux()
thd - thread context
lex - LEX representing statement
start - first routine from the list of routines to be cached
(this list defines mentioned sub-set).
NOTE NOTE
If some function is missing this won't be reported here. If some function is missing this won't be reported here.
Instead this fact will be discovered during query execution. Instead this fact will be discovered during query execution.
TODO RETURN VALUE
Currently if after passing through routine hashes we discover TRUE - some tables were added
that we have added something to them, we do one more pass to FALSE - no tables were added.
process all routines which were missed on previous pass because */
of these additions. We can avoid this if along with hashes
we use lists holding routine names and iterate other these static bool
lists instead of hashes (since addition to the end of list sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
does not reorder elements in it). Sroutine_hash_entry *start)
{
bool result= FALSE;
DBUG_ENTER("sp_cache_routines_and_add_tables_aux");
for (Sroutine_hash_entry *rt= start; rt; rt= rt->next)
{
sp_name name(rt->key.str, rt->key.length);
int type= rt->key.str[0];
sp_head *sp;
if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ?
&thd->sp_func_cache : &thd->sp_proc_cache),
&name)))
{
LEX *oldlex= thd->lex;
LEX *newlex= new st_lex;
thd->lex= newlex;
/* Pass hint pointer to mysql.proc table */
newlex->proc_table= oldlex->proc_table;
newlex->current_select= NULL;
name.m_name.str= strchr(name.m_qname.str, '.');
name.m_db.length= name.m_name.str - name.m_qname.str;
name.m_db.str= strmake_root(thd->mem_root, name.m_qname.str,
name.m_db.length);
name.m_name.str+= 1;
name.m_name.length= name.m_qname.length - name.m_db.length - 1;
if (db_find_routine(thd, type, &name, &sp) == SP_OK)
{
if (type == TYPE_ENUM_FUNCTION)
sp_cache_insert(&thd->sp_func_cache, sp);
else
sp_cache_insert(&thd->sp_proc_cache, sp);
}
delete newlex;
thd->lex= oldlex;
}
if (sp)
{
sp_update_stmt_used_routines(thd, lex, &sp->m_sroutines);
result|= sp->add_used_tables_to_table_list(thd, &lex->query_tables_last);
}
}
DBUG_RETURN(result);
}
/*
Cache all routines from the set of used by statement, add tables used
by those routines to statement table list. Do the same for all routines
used by those routines.
SYNOPSIS
sp_cache_routines_and_add_tables()
thd - thread context
lex - LEX representing statement
RETURN VALUE
TRUE - some tables were added
FALSE - no tables were added.
*/
bool
sp_cache_routines_and_add_tables(THD *thd, LEX *lex)
{
return sp_cache_routines_and_add_tables_aux(thd, lex,
(Sroutine_hash_entry *)lex->sroutines_list.first);
}
/*
Add all routines used by view to the set of routines used by statement.
Add tables used by those routines to statement table list. Do the same
for all routines used by these routines.
SYNOPSIS
sp_cache_routines_and_add_tables_for_view()
thd - thread context
lex - LEX representing statement
aux_lex - LEX representing view
*/ */
void void
sp_cache_routines(THD *thd, LEX *lex) sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, LEX *aux_lex)
{ {
bool routines_added= TRUE; Sroutine_hash_entry **last_cached_routine_ptr=
(Sroutine_hash_entry **)lex->sroutines_list.next;
DBUG_ENTER("sp_cache_routines"); sp_update_stmt_used_routines(thd, lex, &aux_lex->sroutines);
(void)sp_cache_routines_and_add_tables_aux(thd, lex,
while (routines_added) *last_cached_routine_ptr);
{
routines_added= FALSE;
for (int type= TYPE_ENUM_FUNCTION; type < TYPE_ENUM_TRIGGER; type++)
{
HASH *h= (type == TYPE_ENUM_FUNCTION ? &lex->spfuns : &lex->spprocs);
for (uint i=0 ; i < h->records ; i++)
{
LEX_STRING *ls= (LEX_STRING *)hash_element(h, i);
sp_name name(*ls);
sp_head *sp;
name.m_qname= *ls;
if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ?
&thd->sp_func_cache : &thd->sp_proc_cache),
&name)))
{
LEX *oldlex= thd->lex;
LEX *newlex= new st_lex;
thd->lex= newlex;
/* Pass hint pointer to mysql.proc table */
newlex->proc_table= oldlex->proc_table;
newlex->current_select= NULL;
name.m_name.str= strchr(name.m_qname.str, '.');
name.m_db.length= name.m_name.str - name.m_qname.str;
name.m_db.str= strmake_root(thd->mem_root, name.m_qname.str,
name.m_db.length);
name.m_name.str+= 1;
name.m_name.length= name.m_qname.length - name.m_db.length - 1;
if (db_find_routine(thd, type, &name, &sp) == SP_OK)
{
if (type == TYPE_ENUM_FUNCTION)
sp_cache_insert(&thd->sp_func_cache, sp);
else
sp_cache_insert(&thd->sp_proc_cache, sp);
}
delete newlex;
thd->lex= oldlex;
}
if (sp)
{
routines_added|= sp_merge_hash(&lex->spfuns, &sp->m_spfuns);
routines_added|= sp_merge_hash(&lex->spprocs, &sp->m_spprocs);
}
}
}
}
DBUG_VOID_RETURN;
} }
/*
Add triggers for table to the set of routines used by statement.
Add tables used by them to statement table list. Do the same for
all implicitly used routines.
SYNOPSIS
sp_cache_routines_and_add_tables_for_triggers()
thd - thread context
lex - LEX respresenting statement
triggers - triggers of the table
*/
void
sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
Table_triggers_list *triggers)
{
if (add_used_routine(lex, thd->current_arena, &triggers->sroutines_key))
{
Sroutine_hash_entry **last_cached_routine_ptr=
(Sroutine_hash_entry **)lex->sroutines_list.next;
for (int i= 0; i < 3; i++)
for (int j= 0; j < 2; j++)
if (triggers->bodies[i][j])
{
(void)triggers->bodies[i][j]->add_used_tables_to_table_list(thd,
&lex->query_tables_last);
sp_update_stmt_used_routines(thd, lex,
&triggers->bodies[i][j]->m_sroutines);
}
(void)sp_cache_routines_and_add_tables_aux(thd, lex,
*last_cached_routine_ptr);
}
}
/* /*
* Generates the CREATE... string from the table information. * Generates the CREATE... string from the table information.
* Returns TRUE on success, FALSE on (alloc) failure. * Returns TRUE on success, FALSE on (alloc) failure.

View File

@ -79,15 +79,19 @@ sp_function_exists(THD *thd, sp_name *name);
/* /*
* For precaching of functions and procedures Procedures for pre-caching of stored routines and building table list
*/ for prelocking.
void */
sp_add_to_hash(HASH *h, sp_name *fun); void sp_add_used_routine(LEX *lex, Query_arena *arena,
bool sp_name *rt, char rt_type);
sp_merge_hash(HASH *dst, HASH *src); void sp_update_sp_used_routines(HASH *dst, HASH *src);
void bool sp_cache_routines_and_add_tables(THD *thd, LEX *lex);
sp_cache_routines(THD *thd, LEX *lex); void sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex,
LEX *aux_lex);
void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
Table_triggers_list *triggers);
extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first);
// //
// Utilities... // Utilities...

View File

@ -242,8 +242,11 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type,
void void
sp_name::init_qname(THD *thd) sp_name::init_qname(THD *thd)
{ {
m_qname.length= m_db.length+m_name.length+1; m_sroutines_key.length= m_db.length + m_name.length + 2;
m_qname.str= thd->alloc(m_qname.length+1); if (!(m_sroutines_key.str= thd->alloc(m_sroutines_key.length + 1)))
return;
m_qname.length= m_sroutines_key.length - 1;
m_qname.str= m_sroutines_key.str + 1;
sprintf(m_qname.str, "%*s.%*s", sprintf(m_qname.str, "%*s.%*s",
m_db.length, (m_db.length ? m_db.str : ""), m_db.length, (m_db.length ? m_db.str : ""),
m_name.length, m_name.str); m_name.length, m_name.str);
@ -317,15 +320,12 @@ sp_head::sp_head()
{ {
extern byte * extern byte *
sp_table_key(const byte *ptr, uint *plen, my_bool first); sp_table_key(const byte *ptr, uint *plen, my_bool first);
extern byte
*sp_lex_sp_key(const byte *ptr, uint *plen, my_bool first);
DBUG_ENTER("sp_head::sp_head"); DBUG_ENTER("sp_head::sp_head");
m_backpatch.empty(); m_backpatch.empty();
m_lex.empty(); m_lex.empty();
hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0); hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0);
hash_init(&m_spfuns, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0); hash_init(&m_sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0);
hash_init(&m_spprocs, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
@ -528,8 +528,7 @@ sp_head::destroy()
} }
hash_free(&m_sptabs); hash_free(&m_sptabs);
hash_free(&m_spfuns); hash_free(&m_sroutines);
hash_free(&m_spprocs);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
@ -1064,11 +1063,10 @@ sp_head::restore_lex(THD *thd)
oldlex->trg_table_fields.push_back(&sublex->trg_table_fields); oldlex->trg_table_fields.push_back(&sublex->trg_table_fields);
/* /*
Add routines which are used by statement to respective sets for Add routines which are used by statement to respective set for
this routine this routine.
*/ */
sp_merge_hash(&m_spfuns, &sublex->spfuns); sp_update_sp_used_routines(&m_sroutines, &sublex->sroutines);
sp_merge_hash(&m_spprocs, &sublex->spprocs);
/* /*
Merge tables used by this statement (but not by its functions or Merge tables used by this statement (but not by its functions or
procedures) to multiset of tables used by this routine. procedures) to multiset of tables used by this routine.
@ -1605,16 +1603,22 @@ sp_instr_set::print(String *str)
int int
sp_instr_set_trigger_field::execute(THD *thd, uint *nextp) sp_instr_set_trigger_field::execute(THD *thd, uint *nextp)
{ {
int res= 0;
DBUG_ENTER("sp_instr_set_trigger_field::execute"); DBUG_ENTER("sp_instr_set_trigger_field::execute");
/* QQ: Still unsure what should we return in case of error 1 or -1 ? */ DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
if (!value->fixed && value->fix_fields(thd, &value) || }
trigger_field->fix_fields(thd, 0) ||
(value->save_in_field(trigger_field->field, 0) < 0))
int
sp_instr_set_trigger_field::exec_core(THD *thd, uint *nextp)
{
int res= 0;
Item *it= sp_prepare_func_item(thd, &value);
if (!it ||
!trigger_field->fixed && trigger_field->fix_fields(thd, 0) ||
(it->save_in_field(trigger_field->field, 0) < 0))
res= -1; res= -1;
*nextp= m_ip + 1; *nextp = m_ip+1;
DBUG_RETURN(res); return res;
} }
void void
@ -2438,72 +2442,3 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
return table; return table;
} }
/*
Auxilary function for adding tables used by routines used in query
to table lists.
SYNOPSIS
sp_add_sp_tables_to_table_list_aux()
thd - thread context
lex - LEX to which table list tables will be added
func_hash - routines for which tables should be added
func_cache- SP cache in which this routines should be looked up
NOTE
See sp_add_sp_tables_to_table_list() for more info.
RETURN VALUE
TRUE - some tables were added
FALSE - no tables were added.
*/
static bool
sp_add_sp_tables_to_table_list_aux(THD *thd, LEX *lex, HASH *func_hash,
sp_cache **func_cache)
{
uint i;
bool result= FALSE;
for (i= 0 ; i < func_hash->records ; i++)
{
sp_head *sp;
LEX_STRING *ls= (LEX_STRING *)hash_element(func_hash, i);
sp_name name(*ls);
name.m_qname= *ls;
if ((sp= sp_cache_lookup(func_cache, &name)))
result|= sp->add_used_tables_to_table_list(thd, &lex->query_tables_last);
}
return result;
}
/*
Add tables used by routines used in query to table list.
SYNOPSIS
sp_add_sp_tables_to_table_list()
thd - thread context
lex - LEX to which table list tables will be added
func_lex - LEX for which functions we get tables
(useful for adding tables used by view routines)
NOTE
Elements of list will be allocated in PS memroot, so this
list will be persistent between PS execetutions.
RETURN VALUE
TRUE - some tables were added
FALSE - no tables were added.
*/
bool
sp_add_sp_tables_to_table_list(THD *thd, LEX *lex, LEX *func_lex)
{
return (sp_add_sp_tables_to_table_list_aux(thd, lex, &func_lex->spfuns,
&thd->sp_func_cache) |
sp_add_sp_tables_to_table_list_aux(thd, lex, &func_lex->spprocs,
&thd->sp_proc_cache));
}

View File

@ -48,24 +48,50 @@ public:
LEX_STRING m_db; LEX_STRING m_db;
LEX_STRING m_name; LEX_STRING m_name;
LEX_STRING m_qname; LEX_STRING m_qname;
/*
Key representing routine in the set of stored routines used by statement.
Consists of 1-byte routine type and m_qname (which usually refences to
same buffer). Note that one must complete initialization of the key by
calling set_routine_type().
*/
LEX_STRING m_sroutines_key;
sp_name(LEX_STRING name) sp_name(LEX_STRING name)
: m_name(name) : m_name(name)
{ {
m_db.str= m_qname.str= 0; m_db.str= m_qname.str= m_sroutines_key.str= 0;
m_db.length= m_qname.length= 0; m_db.length= m_qname.length= m_sroutines_key.length= 0;
} }
sp_name(LEX_STRING db, LEX_STRING name) sp_name(LEX_STRING db, LEX_STRING name)
: m_db(db), m_name(name) : m_db(db), m_name(name)
{ {
m_qname.str= 0; m_qname.str= m_sroutines_key.str= 0;
m_qname.length= 0; m_qname.length= m_sroutines_key.length= 0;
}
/*
Creates temporary sp_name object from key, used mainly
for SP-cache lookups.
*/
sp_name(char *key, uint key_len)
{
m_sroutines_key.str= key;
m_sroutines_key.length= key_len;
m_name.str= m_qname.str= key + 1;
m_name.length= m_qname.length= key_len - 1;
m_db.str= 0;
m_db.length= 0;
} }
// Init. the qualified name from the db and name. // Init. the qualified name from the db and name.
void init_qname(THD *thd); // thd for memroot allocation void init_qname(THD *thd); // thd for memroot allocation
void set_routine_type(char type)
{
m_sroutines_key.str[0]= type;
}
~sp_name() ~sp_name()
{} {}
}; };
@ -107,13 +133,13 @@ public:
longlong m_created; longlong m_created;
longlong m_modified; longlong m_modified;
/* /*
Sets containing names of SP and SF used by this routine. Set containing names of stored routines used by this routine.
Note that unlike elements of similar set for statement elements of this
TODO Probably we should combine these two hashes in one. It will set are not linked in one list. Because of this we are able save memory
decrease memory overhead ans simplify algorithms using them. The by using for this set same objects that are used in 'sroutines' sets
same applies to similar hashes in LEX. for statements of which this stored routine consists.
*/ */
HASH m_spfuns, m_spprocs; HASH m_sroutines;
// Pointers set during parsing // Pointers set during parsing
uchar *m_param_begin, *m_param_end, *m_body_begin; uchar *m_param_begin, *m_param_end, *m_body_begin;
@ -474,10 +500,11 @@ class sp_instr_set_trigger_field : public sp_instr
public: public:
sp_instr_set_trigger_field(uint ip, sp_pcontext *ctx, sp_instr_set_trigger_field(uint ip, sp_pcontext *ctx,
Item_trigger_field *trg_fld, Item *val) Item_trigger_field *trg_fld,
Item *val, LEX *lex)
: sp_instr(ip, ctx), : sp_instr(ip, ctx),
trigger_field(trg_fld), trigger_field(trg_fld),
value(val) value(val), m_lex_keeper(lex, TRUE)
{} {}
virtual ~sp_instr_set_trigger_field() virtual ~sp_instr_set_trigger_field()
@ -485,11 +512,14 @@ public:
virtual int execute(THD *thd, uint *nextp); virtual int execute(THD *thd, uint *nextp);
virtual int exec_core(THD *thd, uint *nextp);
virtual void print(String *str); virtual void print(String *str);
private: private:
Item_trigger_field *trigger_field; Item_trigger_field *trigger_field;
Item *value; Item *value;
sp_lex_keeper m_lex_keeper;
}; // class sp_instr_trigger_field : public sp_instr }; // class sp_instr_trigger_field : public sp_instr
@ -954,7 +984,5 @@ TABLE_LIST *
sp_add_to_query_tables(THD *thd, LEX *lex, sp_add_to_query_tables(THD *thd, LEX *lex,
const char *db, const char *name, const char *db, const char *name,
thr_lock_type locktype); thr_lock_type locktype);
bool
sp_add_sp_tables_to_table_list(THD *thd, LEX *lex, LEX *func_lex);
#endif /* _SP_HEAD_H_ */ #endif /* _SP_HEAD_H_ */

View File

@ -1802,16 +1802,13 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
may be still zero for prelocked statement... may be still zero for prelocked statement...
*/ */
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() && if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
(thd->lex->spfuns.records || thd->lex->spprocs.records)) thd->lex->sroutines.records)
{ {
TABLE_LIST **save_query_tables_last; TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
sp_cache_routines(thd, thd->lex);
save_query_tables_last= thd->lex->query_tables_last;
DBUG_ASSERT(thd->lex->query_tables == *start); DBUG_ASSERT(thd->lex->query_tables == *start);
if (sp_add_sp_tables_to_table_list(thd, thd->lex, thd->lex) || if (sp_cache_routines_and_add_tables(thd, thd->lex) ||
*start) *start)
{ {
query_tables_last_own= save_query_tables_last; query_tables_last_own= save_query_tables_last;
@ -1847,19 +1844,16 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
and add tables used by them to table list. and add tables used by them to table list.
*/ */
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() && if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
(tables->view->spfuns.records || tables->view->spprocs.records)) tables->view->sroutines.records)
{ {
// FIXME We should catch recursion for both views and funcs here
sp_cache_routines(thd, tables->view);
/* We have at least one table in TL here */ /* We have at least one table in TL here */
if (!query_tables_last_own) if (!query_tables_last_own)
query_tables_last_own= thd->lex->query_tables_last; query_tables_last_own= thd->lex->query_tables_last;
sp_add_sp_tables_to_table_list(thd, thd->lex, tables->view); sp_cache_routines_and_add_tables_for_view(thd, thd->lex,
tables->view);
} }
/* Cleanup hashes because destructo for this LEX is never called */ /* Cleanup hashes because destructo for this LEX is never called */
hash_free(&tables->view->spfuns); hash_free(&tables->view->sroutines);
hash_free(&tables->view->spprocs);
continue; continue;
} }
@ -1914,9 +1908,6 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
prelocking list. prelocking list.
If we lock table for reading we won't update it so there is no need to If we lock table for reading we won't update it so there is no need to
process its triggers since they never will be activated. process its triggers since they never will be activated.
FIXME Now we are simply turning on prelocking. Proper integration
and testing is to be done later.
*/ */
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() && if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
tables->table->triggers && tables->table->triggers &&
@ -1924,6 +1915,8 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
{ {
if (!query_tables_last_own) if (!query_tables_last_own)
query_tables_last_own= thd->lex->query_tables_last; query_tables_last_own= thd->lex->query_tables_last;
sp_cache_routines_and_add_tables_for_triggers(thd, thd->lex,
tables->table->triggers);
} }
free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC)); free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
} }

View File

@ -172,10 +172,9 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->proc_list.first= 0; lex->proc_list.first= 0;
lex->query_tables_own_last= 0; lex->query_tables_own_last= 0;
if (lex->spfuns.records) if (lex->sroutines.records)
my_hash_reset(&lex->spfuns); my_hash_reset(&lex->sroutines);
if (lex->spprocs.records) lex->sroutines_list.empty();
my_hash_reset(&lex->spprocs);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
@ -1571,6 +1570,28 @@ void st_select_lex::print_limit(THD *thd, String *str)
} }
/*
Initialize LEX object.
SYNOPSIS
st_lex::st_lex()
NOTE
LEX object initialized with this constructor can be used as part of
THD object for which one can safely call open_tables(), lock_tables()
and close_thread_tables() functions. But it is not yet ready for
statement parsing. On should use lex_start() function to prepare LEX
for this.
*/
st_lex::st_lex()
:result(0), sql_command(SQLCOM_END), query_tables_own_last(0)
{
hash_init(&sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0);
sroutines_list.empty();
}
/* /*
Check whether the merging algorithm can be used on this VIEW Check whether the merging algorithm can be used on this VIEW
@ -1976,10 +1997,8 @@ void st_lex::cleanup_after_one_table_open()
select_lex.cut_subtree(); select_lex.cut_subtree();
} }
time_zone_tables_used= 0; time_zone_tables_used= 0;
if (spfuns.records) if (sroutines.records)
my_hash_reset(&spfuns); my_hash_reset(&sroutines);
if (spprocs.records)
my_hash_reset(&spprocs);
} }

View File

@ -807,8 +807,14 @@ typedef struct st_lex
bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */ bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
bool all_privileges; bool all_privileges;
sp_pcontext *spcont; sp_pcontext *spcont;
HASH spfuns; /* Called functions */ /* Set of stored routines called by statement. */
HASH spprocs; /* Called procedures */ HASH sroutines;
/*
List linking elements of 'sroutines' set. Allows you to add new elements
to this set as you iterate through the list of existing elements.
*/
SQL_LIST sroutines_list;
st_sp_chistics sp_chistics; st_sp_chistics sp_chistics;
bool only_view; /* used for SHOW CREATE TABLE/VIEW */ bool only_view; /* used for SHOW CREATE TABLE/VIEW */
/* /*
@ -841,17 +847,11 @@ typedef struct st_lex
*/ */
uchar *fname_start, *fname_end; uchar *fname_start, *fname_end;
st_lex() :result(0), sql_command(SQLCOM_END), query_tables_own_last(0) st_lex();
{
extern byte *sp_lex_sp_key(const byte *ptr, uint *plen, my_bool first);
hash_init(&spfuns, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
hash_init(&spprocs, system_charset_info, 0, 0, 0, sp_lex_sp_key, 0, 0);
}
virtual ~st_lex() virtual ~st_lex()
{ {
hash_free(&spfuns); hash_free(&sroutines);
hash_free(&spprocs);
} }
inline void uncacheable(uint8 cause) inline void uncacheable(uint8 cause)

View File

@ -2302,8 +2302,7 @@ mysql_execute_command(THD *thd)
Don't reset warnings when executing a stored routine. Don't reset warnings when executing a stored routine.
*/ */
if ((all_tables || &lex->select_lex != lex->all_selects_list || if ((all_tables || &lex->select_lex != lex->all_selects_list ||
lex->spfuns.records || lex->spprocs.records) && lex->sroutines.records) && !thd->spcont)
!thd->spcont)
mysql_reset_errors(thd, 0); mysql_reset_errors(thd, 0);
#ifdef HAVE_REPLICATION #ifdef HAVE_REPLICATION

View File

@ -1,3 +1,20 @@
/* Copyright (C) 2004-2005 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "mysql_priv.h" #include "mysql_priv.h"
#include "sp_head.h" #include "sp_head.h"
#include "sql_trigger.h" #include "sql_trigger.h"
@ -417,6 +434,18 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
table->triggers= triggers; table->triggers= triggers;
/*
Construct key that will represent triggers for this table in the set
of routines used by statement.
*/
triggers->sroutines_key.length= 1+strlen(db)+1+strlen(table_name)+1;
if (!(triggers->sroutines_key.str=
alloc_root(&table->mem_root, triggers->sroutines_key.length)))
DBUG_RETURN(1);
triggers->sroutines_key.str[0]= TYPE_ENUM_TRIGGER;
strmov(strmov(strmov(triggers->sroutines_key.str+1, db), "."),
table_name);
/* /*
TODO: This could be avoided if there is no triggers TODO: This could be avoided if there is no triggers
for UPDATE and DELETE. for UPDATE and DELETE.

View File

@ -1,3 +1,20 @@
/* Copyright (C) 2004-2005 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* /*
This class holds all information about triggers of table. This class holds all information about triggers of table.
@ -28,6 +45,14 @@ class Table_triggers_list: public Sql_alloc
used in CREATE/DROP TRIGGER for looking up trigger by name. used in CREATE/DROP TRIGGER for looking up trigger by name.
*/ */
List<LEX_STRING> names_list; List<LEX_STRING> names_list;
/*
Key representing triggers for this table in set of all stored
routines used by statement.
TODO: We won't need this member once triggers namespace will be
database-wide instead of table-wide because then we will be able
to use key based on sp_name as for other stored routines.
*/
LEX_STRING sroutines_key;
public: public:
/* /*
@ -112,6 +137,8 @@ public:
} }
friend class Item_trigger_field; friend class Item_trigger_field;
friend void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
Table_triggers_list *triggers);
private: private:
bool prepare_record1_accessors(TABLE *table); bool prepare_record1_accessors(TABLE *table);

View File

@ -1531,7 +1531,7 @@ call:
lex->sql_command= SQLCOM_CALL; lex->sql_command= SQLCOM_CALL;
lex->spname= $2; lex->spname= $2;
lex->value_list.empty(); lex->value_list.empty();
sp_add_to_hash(&lex->spprocs, $2); sp_add_used_routine(lex, YYTHD, $2, TYPE_ENUM_PROCEDURE);
} }
'(' sp_cparam_list ')' {} '(' sp_cparam_list ')' {}
; ;
@ -4695,7 +4695,7 @@ simple_expr:
sp_name *name= new sp_name($1, $3); sp_name *name= new sp_name($1, $3);
name->init_qname(YYTHD); name->init_qname(YYTHD);
sp_add_to_hash(&lex->spfuns, name); sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION);
if ($5) if ($5)
$$= new Item_func_sp(&lex->current_select->context, name, *$5); $$= new Item_func_sp(&lex->current_select->context, name, *$5);
else else
@ -4785,7 +4785,7 @@ simple_expr:
LEX *lex= Lex; LEX *lex= Lex;
sp_name *name= sp_name_current_db_new(YYTHD, $1); sp_name *name= sp_name_current_db_new(YYTHD, $1);
sp_add_to_hash(&lex->spfuns, name); sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION);
if ($3) if ($3)
$$= new Item_func_sp(&lex->current_select->context, name, *$3); $$= new Item_func_sp(&lex->current_select->context, name, *$3);
else else
@ -7740,12 +7740,6 @@ sys_option_value:
yyerror(ER(ER_SYNTAX_ERROR)); yyerror(ER(ER_SYNTAX_ERROR));
YYABORT; YYABORT;
} }
if (lex->query_tables)
{
my_message(ER_SP_SUBSELECT_NYI, ER(ER_SP_SUBSELECT_NYI),
MYF(0));
YYABORT;
}
if ($4) if ($4)
it= $4; it= $4;
else else
@ -7760,7 +7754,8 @@ sys_option_value:
$2.base_name.str)) || $2.base_name.str)) ||
!(i= new sp_instr_set_trigger_field(lex->sphead-> !(i= new sp_instr_set_trigger_field(lex->sphead->
instructions(), instructions(),
lex->spcont, trg_fld, it))) lex->spcont, trg_fld,
it, lex)))
YYABORT; YYABORT;
/* /*