Implementation of MWL#172: Add support for prepared statements to HANDLER READ

It includes speed optimizations for HANDLER READ by caching as much as possible in HANDLER OPEN
Other things:
- Added mysqld option --disable-thr-alarm to be able to benchmark things without thr_alarm
- Changed 'Locked' state to 'System lock' and 'Table lock' (these where used in the code but never shown to end user)
- Better error message if mysql_install_db.sh fails
- Moved handler function prototypes to sql_handler.h
- Remove not anymore used 'thd->locked' member


include/thr_alarm.h:
  Added my_disable_thr_alarm
include/thr_lock.h:
  Add new member to THR_LOCK_DATA to remember original lock type state. This is needed as thr_unlock() resets type to TL_UNLOCK.
mysql-test/include/check_no_concurrent_insert.inc:
  Locked -> Table lock
mysql-test/include/handler.inc:
  Locked -> Table lock
mysql-test/r/handler_innodb.result:
  Updated results for new tests
mysql-test/r/handler_myisam.result:
  Updated results for new tests
mysql-test/r/sp-threads.result:
  Locked -> Table lock
mysql-test/suite/binlog/t/binlog_stm_row.test:
  Locked -> Table lock
mysql-test/suite/funcs_1/datadict/processlist_val.inc:
  Locked -> Table lock
mysql-test/suite/pbxt/t/lock_multi.test:
  Locked -> Table lock
mysql-test/suite/sys_vars/r/concurrent_insert_func.result:
  Locked -> Table lock
mysql-test/suite/sys_vars/t/concurrent_insert_func.test:
  Locked -> Table lock
mysql-test/suite/sys_vars/t/delayed_insert_limit_func.test:
  Locked -> Table lock
mysql-test/suite/sys_vars/t/query_cache_wlock_invalidate_func.test:
  Locked -> Table lock
mysql-test/suite/sys_vars/t/sql_low_priority_updates_func.test:
  Locked -> Table lock
mysql-test/t/insert_notembedded.test:
  Locked -> Table lock
mysql-test/t/lock_multi.test:
  Locked -> Table lock
mysql-test/t/merge-big.test:
  Locked -> Table lock
mysql-test/t/multi_update.test:
  Locked -> Table lock
mysql-test/t/query_cache_28249.test:
  Locked -> Table lock
mysql-test/t/sp_notembedded.test:
  Locked -> Table lock
mysql-test/t/sp_sync.test:
  Locked -> Table lock
mysql-test/t/status.test:
  Locked -> Table lock
mysql-test/t/trigger_notembedded.test:
  Locked -> Table lock
mysys/thr_alarm.c:
  Added option to disable thr_alarm
mysys/thr_lock.c:
  Detect loops
scripts/mysql_install_db.sh:
  Give better error message if something goes wrong
sql/Makefile.am:
  Added sql_handler.h
sql/lock.cc:
  Split functions to allow one to cache value if store_lock() (for HANDLER functions).
  - Split mysql_lock_tables() into two functions, where first one allocates MYSQL_LOCK and other other one uses it.
  - Made get_lock_data() an external function.
  - Added argument to mysql_unlock_tables() to not free sql_lock.
  - Added argument to reset_lock_data() to reset lock structure to initial state (as after get_lock_data())
sql/mysql_priv.h:
  Moved handler function prototypes to sql_handler.h
  Added new lock functions.
sql/mysqld.cc:
  Added --thread-alarm startup option
sql/net_serv.cc:
  Don't call vio_blocking() if not needed
sql/sql_base.cc:
  include sql_handler.h
sql/sql_class.cc:
  include sql_handler.h
  Remove not anymore used 'thd->locked' member
sql/sql_class.h:
  Remove not anymore used 'thd->locked' member
sql/sql_db.cc:
  include sql_handler.h
sql/sql_delete.cc:
  include sql_handler.h
sql/sql_handler.cc:
  Rewrote all code to use SQL_HANDLER instead of TABLE_LIST (original interface)
  Rewrote mysql_ha_open() to cache all things from TABLE_LIST and items for field list, where etc.
  In mysql_ha_open() also cache MYSQL_LOCK structure from get_lock_data().
  Split functions into smaller sub functions (needed to be able to implement mysql_ha_read_prepare())
  Added mysql_ha_read_prepare() to allow one to prepare HANDLER READ.
sql/sql_handler.h:
  Interface to sql_handler.cc
sql/sql_parse.cc:
  include sql_handler.h
sql/sql_prepare.cc:
  Added mysql_test_handler_read(), prepare for HANDLER READ
sql/sql_rename.cc:
  include sql_handler.h
sql/sql_show.cc:
  Removed usage of thd->locked
sql/sql_table.cc:
  include sql_handler.h
sql/sql_trigger.cc:
  include sql_handler.h
This commit is contained in:
Michael Widenius 2011-01-04 00:55:41 +02:00
commit e63b5546c5
45 changed files with 1110 additions and 466 deletions

View file

@ -88,6 +88,7 @@ typedef struct st_alarm {
extern uint thr_client_alarm;
extern pthread_t alarm_thread;
extern my_bool my_disable_thr_alarm;
#define thr_alarm_init(A) (*(A))=0
#define thr_alarm_in_use(A) (*(A)!= 0)

View file

@ -123,8 +123,10 @@ typedef struct st_thr_lock_data {
struct st_thr_lock *lock;
pthread_cond_t *cond;
void *status_param; /* Param to status functions */
void *debug_print_param;
enum thr_lock_type type;
enum thr_lock_type org_type; /* Cache for MariaDB */
void *debug_print_param; /* For error messages */
uint priority;
} THR_LOCK_DATA;

View file

@ -43,7 +43,7 @@ connection default;
# of our statement.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "insert into $table (i) values (0)";
where state = "Table lock" and info = "insert into $table (i) values (0)";
--source include/wait_condition.inc
--disable_result_log

View file

@ -77,8 +77,9 @@ handler t2 read a prev limit 10;
handler t2 read a>=(16) limit 4;
handler t2 read a>=(16) limit 2,2;
select * from t1 where a>=16 limit 2,2;
handler t2 read a last limit 3;
handler t2 read a=(16) limit 1,3;
handler t2 read a=(19);
handler t2 read a=(19) where b="yyy";
@ -105,6 +106,79 @@ eval alter table t1 engine = $engine_type;
--error 1109
handler t2 read first;
handler t1 open;
handler t1 read a=(16) limit 1,3;
flush tables;
handler t1 read a=(16) limit 1,3;
handler t1 close;
#
# Test with prepared statements
#
handler t1 open;
prepare stmt from 'handler t1 read a=(?) limit ?,?';
set @a=16,@b=1,@c=100;
execute stmt using @a,@b,@c;
set @a=16,@b=2,@c=1;
execute stmt using @a,@b,@c;
set @a=16,@b=0,@c=2;
execute stmt using @a,@b,@c;
deallocate prepare stmt;
prepare stmt from 'handler t1 read a next limit ?';
handler t1 read a>=(11);
set @a=3;
execute stmt using @a;
execute stmt using @a;
execute stmt using @a;
deallocate prepare stmt;
prepare stmt from 'handler t1 read b prev limit ?';
execute stmt using @a;
execute stmt using @a;
execute stmt using @a;
execute stmt using @a;
deallocate prepare stmt;
prepare stmt from 'handler t1 read b=(?,?)';
set @a=14, @b='aaa';
execute stmt using @a,@b;
set @a=14, @b='not found';
execute stmt using @a,@b;
deallocate prepare stmt;
prepare stmt from 'handler t1 read b=(1+?) limit 10';
set @a=15;
execute stmt using @a;
execute stmt using @a;
deallocate prepare stmt;
prepare stmt from 'handler t1 read a>=(?) where a < ? limit 5';
set @a=15, @b=20;
execute stmt using @a,@b;
execute stmt using @a,@b;
deallocate prepare stmt;
prepare stmt from 'handler t1 read a=(?)';
set @a=16;
execute stmt using @a;
alter table t1 add c int;
--error 1109
execute stmt using @a;
deallocate prepare stmt;
--error 1109
handler t1 close;
handler t1 open;
prepare stmt from 'handler t1 read a=(?)';
flush tables;
set @a=16;
--error ER_NEED_REPREPARE
execute stmt using @a;
deallocate prepare stmt;
handler t1 close;
#
# DROP TABLE / ALTER TABLE
#

View file

@ -115,11 +115,18 @@ handler t2 read a>=(16) limit 2,2;
a b
17 ddd
18 eee
select * from t1 where a>=16 limit 2,2;
a b
17 ddd
18 eee
handler t2 read a last limit 3;
a b
22 iii
21 hhh
20 ggg
handler t2 read a=(16) limit 1,3;
a b
16 xxx
handler t2 read a=(19);
a b
19 fff
@ -161,6 +168,128 @@ a b
alter table t1 engine = InnoDB;
handler t2 read first;
ERROR 42S02: Unknown table 't2' in HANDLER
handler t1 open;
handler t1 read a=(16) limit 1,3;
a b
16 xxx
flush tables;
handler t1 read a=(16) limit 1,3;
a b
16 xxx
handler t1 close;
handler t1 open;
prepare stmt from 'handler t1 read a=(?) limit ?,?';
set @a=16,@b=1,@c=100;
execute stmt using @a,@b,@c;
a b
16 xxx
set @a=16,@b=2,@c=1;
execute stmt using @a,@b,@c;
a b
set @a=16,@b=0,@c=2;
execute stmt using @a,@b,@c;
a b
16 ccc
16 xxx
deallocate prepare stmt;
prepare stmt from 'handler t1 read a next limit ?';
handler t1 read a>=(11);
a b
14 aaa
set @a=3;
execute stmt using @a;
a b
15 bbb
16 ccc
16 xxx
execute stmt using @a;
a b
17 ddd
18 eee
19 fff
execute stmt using @a;
a b
19 yyy
20 ggg
21 hhh
deallocate prepare stmt;
prepare stmt from 'handler t1 read b prev limit ?';
execute stmt using @a;
a b
22 iii
21 hhh
20 ggg
execute stmt using @a;
a b
19 yyy
19 fff
18 eee
execute stmt using @a;
a b
17 ddd
16 xxx
16 ccc
execute stmt using @a;
a b
15 bbb
14 aaa
deallocate prepare stmt;
prepare stmt from 'handler t1 read b=(?,?)';
set @a=14, @b='aaa';
execute stmt using @a,@b;
a b
14 aaa
set @a=14, @b='not found';
execute stmt using @a,@b;
a b
deallocate prepare stmt;
prepare stmt from 'handler t1 read b=(1+?) limit 10';
set @a=15;
execute stmt using @a;
a b
16 ccc
16 xxx
execute stmt using @a;
a b
16 ccc
16 xxx
deallocate prepare stmt;
prepare stmt from 'handler t1 read a>=(?) where a < ? limit 5';
set @a=15, @b=20;
execute stmt using @a,@b;
a b
15 bbb
16 ccc
16 xxx
17 ddd
18 eee
execute stmt using @a,@b;
a b
15 bbb
16 ccc
16 xxx
17 ddd
18 eee
deallocate prepare stmt;
prepare stmt from 'handler t1 read a=(?)';
set @a=16;
execute stmt using @a;
a b
16 ccc
alter table t1 add c int;
execute stmt using @a;
ERROR 42S02: Unknown table 't1' in HANDLER
deallocate prepare stmt;
handler t1 close;
ERROR 42S02: Unknown table 't1' in HANDLER
handler t1 open;
prepare stmt from 'handler t1 read a=(?)';
flush tables;
set @a=16;
execute stmt using @a;
ERROR HY000: Prepared statement needs to be re-prepared
deallocate prepare stmt;
handler t1 close;
handler t1 open as t2;
drop table t1;
create table t1 (a int);

View file

@ -115,11 +115,18 @@ handler t2 read a>=(16) limit 2,2;
a b
17 ddd
18 eee
select * from t1 where a>=16 limit 2,2;
a b
17 ddd
18 eee
handler t2 read a last limit 3;
a b
22 iii
21 hhh
20 ggg
handler t2 read a=(16) limit 1,3;
a b
16 xxx
handler t2 read a=(19);
a b
19 fff
@ -161,6 +168,128 @@ a b
alter table t1 engine = MyISAM;
handler t2 read first;
ERROR 42S02: Unknown table 't2' in HANDLER
handler t1 open;
handler t1 read a=(16) limit 1,3;
a b
16 xxx
flush tables;
handler t1 read a=(16) limit 1,3;
a b
16 xxx
handler t1 close;
handler t1 open;
prepare stmt from 'handler t1 read a=(?) limit ?,?';
set @a=16,@b=1,@c=100;
execute stmt using @a,@b,@c;
a b
16 xxx
set @a=16,@b=2,@c=1;
execute stmt using @a,@b,@c;
a b
set @a=16,@b=0,@c=2;
execute stmt using @a,@b,@c;
a b
16 ccc
16 xxx
deallocate prepare stmt;
prepare stmt from 'handler t1 read a next limit ?';
handler t1 read a>=(11);
a b
14 aaa
set @a=3;
execute stmt using @a;
a b
15 bbb
16 ccc
16 xxx
execute stmt using @a;
a b
17 ddd
18 eee
19 fff
execute stmt using @a;
a b
19 yyy
20 ggg
21 hhh
deallocate prepare stmt;
prepare stmt from 'handler t1 read b prev limit ?';
execute stmt using @a;
a b
22 iii
21 hhh
20 ggg
execute stmt using @a;
a b
19 yyy
19 fff
18 eee
execute stmt using @a;
a b
17 ddd
16 xxx
16 ccc
execute stmt using @a;
a b
15 bbb
14 aaa
deallocate prepare stmt;
prepare stmt from 'handler t1 read b=(?,?)';
set @a=14, @b='aaa';
execute stmt using @a,@b;
a b
14 aaa
set @a=14, @b='not found';
execute stmt using @a,@b;
a b
deallocate prepare stmt;
prepare stmt from 'handler t1 read b=(1+?) limit 10';
set @a=15;
execute stmt using @a;
a b
16 ccc
16 xxx
execute stmt using @a;
a b
16 ccc
16 xxx
deallocate prepare stmt;
prepare stmt from 'handler t1 read a>=(?) where a < ? limit 5';
set @a=15, @b=20;
execute stmt using @a,@b;
a b
15 bbb
16 ccc
16 xxx
17 ddd
18 eee
execute stmt using @a,@b;
a b
15 bbb
16 ccc
16 xxx
17 ddd
18 eee
deallocate prepare stmt;
prepare stmt from 'handler t1 read a=(?)';
set @a=16;
execute stmt using @a;
a b
16 ccc
alter table t1 add c int;
execute stmt using @a;
ERROR 42S02: Unknown table 't1' in HANDLER
deallocate prepare stmt;
handler t1 close;
ERROR 42S02: Unknown table 't1' in HANDLER
handler t1 open;
prepare stmt from 'handler t1 read a=(?)';
flush tables;
set @a=16;
execute stmt using @a;
ERROR HY000: Prepared statement needs to be re-prepared
deallocate prepare stmt;
handler t1 close;
handler t1 open as t2;
drop table t1;
create table t1 (a int);

View file

@ -35,7 +35,7 @@ call bug9486();
show processlist;
Id User Host db Command Time State Info
# root localhost test Sleep # NULL
# root localhost test Query # Locked update t1, t2 set val= 1 where id1=id2
# root localhost test Query # Table lock update t1, t2 set val= 1 where id1=id2
# root localhost test Query # NULL show processlist
# root localhost test Sleep # NULL
unlock tables;

View file

@ -60,7 +60,7 @@ let $wait_condition=
--echo # con1
let $wait_condition=
SELECT COUNT(*) = 1 FROM information_schema.processlist WHERE
state = "Locked" and info = "INSERT INTO t2 VALUES (3)";
state = "Table Lock" and info = "INSERT INTO t2 VALUES (3)";
--source include/wait_condition.inc
SELECT RELEASE_LOCK('Bug#34306');
--connection con2

View file

@ -368,13 +368,13 @@ echo
;
connection default;
echo
# Poll till INFO is no more NULL and State = 'Locked'.
# Poll till INFO is no more NULL and State = "Table Lock".
;
let $wait_condition= SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE INFO IS NOT NULL AND STATE = 'Locked';
WHERE INFO IS NOT NULL AND STATE = "Table Lock";
--source include/wait_condition.inc
#
# Expect to see the state 'Locked' for the third connection because the SELECT
# Expect to see the state "Table Lock" for the third connection because the SELECT
# collides with the WRITE TABLE LOCK.
--replace_column 1 <ID> 3 <HOST_NAME> 6 <TIME> 9 <TIME_MS>
SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST;
@ -423,10 +423,10 @@ echo
;
connection default;
echo
# Poll till INFO is no more NULL and State = 'Locked'.
# Poll till INFO is no more NULL and State = "Table Lock".
;
let $wait_condition= SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE INFO IS NOT NULL AND STATE = 'Locked';
WHERE INFO IS NOT NULL AND STATE = "Table Lock";
--source include/wait_condition.inc
echo
# Expect result:

View file

@ -48,7 +48,7 @@ insert t1 select * from t2;
connection locker;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "insert t1 select * from t2";
where state = "Table Lock" and info = "insert t1 select * from t2";
--source include/wait_condition.inc
drop table t2;
connection reader;
@ -72,7 +72,7 @@ connection locker;
# Sleep a bit till the insert of connection reader is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "insert t1 select * from t2";
where state = "Table Lock" and info = "insert t1 select * from t2";
--source include/wait_condition.inc
drop table t2;
connection reader;
@ -251,7 +251,7 @@ connection reader;
# Wait till connection writer is blocked
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "alter table t1 auto_increment=0";
where state = "Table Lock" and info = "alter table t1 auto_increment=0";
--source include/wait_condition.inc
send
alter table t1 auto_increment=0;
@ -259,7 +259,7 @@ connection locker;
# Wait till connection reader is blocked
let $wait_condition=
select count(*) = 2 from information_schema.processlist
where state = "Locked" and info = "alter table t1 auto_increment=0";
where state = "Table Lock" and info = "alter table t1 auto_increment=0";
--source include/wait_condition.inc
unlock tables;
connection writer;
@ -414,16 +414,16 @@ update t1 set i= 10;
connection reader;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "update t1 set i= 10";
where state = "Table Lock" and info = "update t1 set i= 10";
--source include/wait_condition.inc
send
select * from t1;
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "select * from t1";
where state = "Table Lock" and info = "select * from t1";
--source include/wait_condition.inc
let $ID= `select id from information_schema.processlist where state = "Locked" and info = "update t1 set i= 10"`;
let $ID= `select id from information_schema.processlist where state = "Table Lock" and info = "update t1 set i= 10"`;
--replace_result $ID ID
eval kill query $ID;
connection reader;
@ -557,7 +557,7 @@ connection waiter;
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "insert into t1 values(1)";
where state = "Table Lock" and info = "insert into t1 values(1)";
--source include/wait_condition.inc
let $tlwb= `show status like 'Table_locks_waited'`;
unlock tables;

View file

@ -37,9 +37,9 @@ INSERT INTO t1(name) VALUES('Record_7');
connection default;
## show processlist info and state ##
SELECT state,info FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE state= "Locked" AND info LIKE "INSERT INTO t1%";
WHERE state= "Table Lock" AND info LIKE "INSERT INTO t1%";
state info
Locked INSERT INTO t1(name) VALUES('Record_7')
Table lock INSERT INTO t1(name) VALUES('Record_7')
## table contents befor UNLOCK ##
SELECT * FROM t1;
name

View file

@ -98,12 +98,12 @@ INSERT INTO t1(name) VALUES('Record_7');
connection default;
# wait until INSERT will be locked (low performance)
let $wait_condition= SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE state= "Locked" AND info LIKE "INSERT INTO t1%";
WHERE state= "Table Lock" AND info LIKE "INSERT INTO t1%";
--source include/wait_condition.inc
--echo ## show processlist info and state ##
SELECT state,info FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE state= "Locked" AND info LIKE "INSERT INTO t1%";
WHERE state= "Table Lock" AND info LIKE "INSERT INTO t1%";
--echo ## table contents befor UNLOCK ##
SELECT * FROM t1;
UNLOCK TABLES;

View file

@ -122,7 +122,7 @@ connection default;
--echo ** Wait till con0 is blocked **
let $wait_condition=
SELECT COUNT(*) = 1 FROM information_schema.processlist
WHERE state = 'Locked' AND info = '$my_select';
WHERE state = "Table Lock" AND info = '$my_select';
--source include/wait_condition.inc
UNLOCK TABLES;
@ -207,7 +207,7 @@ connection default;
--echo ** Wait till con0 is blocked **
let $wait_condition=
SELECT COUNT(*) = 1 FROM information_schema.processlist
WHERE state = 'Locked' AND info = '$my_select';
WHERE state = "Table Lock" AND info = '$my_select';
--source include/wait_condition.inc
UNLOCK TABLES;

View file

@ -139,7 +139,7 @@ send SELECT * FROM t1;
connection con0;
--echo wait until table is locked
let $wait_condition= SELECT count(*) > 0 FROM information_schema.processlist WHERE state= 'Locked';
let $wait_condition= SELECT count(*) > 0 FROM information_schema.processlist WHERE state= "Table Lock";
--source include/wait_condition.inc
UNLOCK TABLES;

View file

@ -85,7 +85,7 @@ delimiter ;|
--echo ** Connection con0 **
connection con0;
let $wait_condition = SELECT COUNT(*) > 0 FROM information_schema.processlist WHERE state='Locked' AND info LIKE 'UPDATE t1 SET a = CONCAT(a,"-updated")';
let $wait_condition = SELECT COUNT(*) > 0 FROM information_schema.processlist WHERE state="Table Lock" AND info LIKE 'UPDATE t1 SET a = CONCAT(a,"-updated")';
--source include/wait_condition.inc
--echo ** Asynchronous Execution **
@ -101,7 +101,7 @@ delimiter ;|
--echo ** Connection default **
connection default;
let $wait_condition= SELECT count(*) = 2 FROM information_schema.processlist WHERE state LIKE 'Locked';
let $wait_condition= SELECT count(*) = 2 FROM information_schema.processlist WHERE state LIKE "Table Lock";
--source include/wait_condition.inc
UNLOCK TABLES;
@ -156,7 +156,7 @@ delimiter ;|
--echo ** Connection con0 **
connection con0;
let $wait_condition = SELECT COUNT(*) > 0 FROM information_schema.processlist WHERE state='Locked' AND info LIKE 'UPDATE t1 SET a = CONCAT(a,"-updated")';
let $wait_condition = SELECT COUNT(*) > 0 FROM information_schema.processlist WHERE state="Table Lock" AND info LIKE 'UPDATE t1 SET a = CONCAT(a,"-updated")';
--source include/wait_condition.inc
--echo ** Asynchronous Execution **
@ -172,7 +172,7 @@ delimiter ;|
--echo ** Connection default **
connection default;
let $wait_condition= SELECT count(*) = 2 FROM information_schema.processlist WHERE state LIKE 'Locked';
let $wait_condition= SELECT count(*) = 2 FROM information_schema.processlist WHERE state LIKE "Table Lock";
--source include/wait_condition.inc
UNLOCK TABLES;

View file

@ -174,7 +174,7 @@ connection default;
# we must wait till the insert opens and locks the table
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and id = $ID;
where state = "Table lock" and id = $ID;
--source include/wait_condition.inc
connect (select,localhost,root,,);
--echo connection: select

View file

@ -24,7 +24,7 @@ connection reader;
# Sleep a bit till the update of connection writer is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "update low_priority t1 set n = 4";
where state = "Table lock" and info = "update low_priority t1 set n = 4";
--source include/wait_condition.inc
send
select n from t1;
@ -32,7 +32,7 @@ connection locker;
# Sleep a bit till the select of connection reader is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "select n from t1";
where state = "Table lock" and info = "select n from t1";
--source include/wait_condition.inc
unlock tables;
connection writer;
@ -52,7 +52,7 @@ connection reader;
# Sleep a bit till the update of connection writer is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "update low_priority t1 set n = 4";
where state = "Table Lock" and info = "update low_priority t1 set n = 4";
--source include/wait_condition.inc
select n from t1;
connection locker;
@ -96,7 +96,7 @@ insert t1 select * from t2;
connection locker;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "insert t1 select * from t2";
where state = "Table Lock" and info = "insert t1 select * from t2";
--source include/wait_condition.inc
drop table t2;
connection reader;
@ -120,7 +120,7 @@ connection locker;
# Sleep a bit till the insert of connection reader is in work and hangs
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "insert t1 select * from t2";
where state = "Table Lock" and info = "insert t1 select * from t2";
--source include/wait_condition.inc
drop table t2;
connection reader;
@ -299,7 +299,7 @@ connection reader;
# Wait till connection writer is blocked
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "alter table t1 auto_increment=0";
where state = "Table Lock" and info = "alter table t1 auto_increment=0";
--source include/wait_condition.inc
send
alter table t1 auto_increment=0;
@ -307,7 +307,7 @@ connection locker;
# Wait till connection reader is blocked
let $wait_condition=
select count(*) = 2 from information_schema.processlist
where state = "Locked" and info = "alter table t1 auto_increment=0";
where state = "Table Lock" and info = "alter table t1 auto_increment=0";
--source include/wait_condition.inc
unlock tables;
connection writer;
@ -462,16 +462,16 @@ update t1 set i= 10;
connection reader;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "update t1 set i= 10";
where state = "Table Lock" and info = "update t1 set i= 10";
--source include/wait_condition.inc
send
select * from t1;
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "select * from t1";
where state = "Table Lock" and info = "select * from t1";
--source include/wait_condition.inc
let $ID= `select id from information_schema.processlist where state = "Locked" and info = "update t1 set i= 10"`;
let $ID= `select id from information_schema.processlist where state = "Table Lock" and info = "update t1 set i= 10"`;
--replace_result $ID ID
eval kill query $ID;
connection reader;
@ -622,7 +622,7 @@ connection waiter;
connection default;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "insert into t1 values(1)";
where state = "Table Lock" and info = "insert into t1 values(1)";
--source include/wait_condition.inc
let $tlwb= `show status like 'Table_locks_waited'`;
unlock tables;

View file

@ -52,7 +52,7 @@ connection default;
#--sleep 8
#SELECT ID,STATE,INFO FROM INFORMATION_SCHEMA.PROCESSLIST;
let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE ID = $con1_id AND STATE = 'Locked';
WHERE ID = $con1_id AND STATE = "Table Lock";
--source include/wait_condition.inc
#SELECT NOW();
--echo # Kick INSERT out of thr_multi_lock().

View file

@ -498,9 +498,9 @@ send alter table t1 add column c int default 100 after a;
connect (updater,localhost,root,,test);
connection updater;
# Wait till "alter table t1 ..." of session changer is in work.
# = There is one session is in state "Locked".
# = There is one session is in state "Table Lock".
let $wait_condition= select count(*)= 1 from information_schema.processlist
where state= 'Locked';
where state= "Table Lock";
--source include/wait_condition.inc
send update t1, v1 set t1.b=t1.a+t1.b+v1.b where t1.a=v1.a;
@ -509,9 +509,9 @@ connection locker;
# - "alter table t1 ..." of session changer and
# - "update t1, v1 ..." of session updater
# are in work.
# = There are two session is in state "Locked".
# = There are two session is in state "Table Lock".
let $wait_condition= select count(*)= 2 from information_schema.processlist
where state= 'Locked';
where state= "Table Lock";
--source include/wait_condition.inc
unlock tables;

View file

@ -64,12 +64,12 @@ connection user3;
# The values marked with 'X' must be reached.
--echo # Poll till the select of connection user1 is blocked by the write lock on t1.
let $wait_condition= SELECT COUNT(*) = 1 FROM information_schema.processlist
WHERE state = 'Locked'
WHERE state = "Table Lock"
AND info = '$select_for_qc';
--source include/wait_condition.inc
eval
SELECT user,command,state,info FROM information_schema.processlist
WHERE state = 'Locked'
WHERE state = "Table Lock"
AND info = '$select_for_qc';
INSERT INTO t1 VALUES (4);

View file

@ -276,7 +276,7 @@ set session low_priority_updates=on;
connection rl_wait;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Locked" and
where state = "Table lock" and
info = "update t1 set value='updated' where value='old'";
--source include/wait_condition.inc

View file

@ -34,7 +34,7 @@ SET DEBUG_SYNC = 'multi_update_reopen_tables SIGNAL parked WAIT_FOR go';
connection con1;
let $wait_condition= SELECT 1 FROM information_schema.processlist WHERE ID = $ID AND
state = "Locked";
state = "Table lock";
--source include/wait_condition.inc
DROP TABLE t1, t2;
SET DEBUG_SYNC = 'now WAIT_FOR parked';

View file

@ -58,7 +58,7 @@ let $ID= `select connection_id()`;
connection con2;
--echo # Switched to connection: con2
# wait for the other query to start executing
let $wait_condition= select 1 from INFORMATION_SCHEMA.PROCESSLIST where ID = $ID and STATE = "Locked";
let $wait_condition= select 1 from INFORMATION_SCHEMA.PROCESSLIST where ID = $ID and STATE = "Table Lock";
--source include/wait_condition.inc
unlock tables;

View file

@ -916,7 +916,7 @@ INSERT INTO t1 VALUES (5);
CONNECTION rl_contender;
# Wait until wl_acquirer is waiting for the read lock on t2 to be released.
let $wait_condition=
SELECT STATE = 'Locked' FROM INFORMATION_SCHEMA.PROCESSLIST
SELECT STATE = "Table Lock" FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE ID = $wl_acquirer_thread_id;
--source include/wait_condition.inc
# must not "see" the row inserted by the INSERT (as it must run before the

View file

@ -36,7 +36,7 @@
uint thr_client_alarm;
static int alarm_aborted=1; /* No alarm thread */
my_bool thr_alarm_inited= 0;
my_bool thr_alarm_inited= 0, my_disable_thr_alarm= 0;
volatile my_bool alarm_thread_running= 0;
time_t next_alarm_expire_time= ~ (time_t) 0;
static sig_handler process_alarm_part2(int sig);
@ -173,6 +173,21 @@ my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm_data)
DBUG_ENTER("thr_alarm");
DBUG_PRINT("enter",("thread: %s sec: %d",my_thread_name(),sec));
if (my_disable_thr_alarm)
{
(*alrm)= &alarm_data->alarmed;
alarm_data->alarmed= 1; /* Abort if interrupted */
DBUG_RETURN(0);
}
if (unlikely(alarm_aborted))
{ /* No signal thread */
DBUG_PRINT("info", ("alarm aborted"));
if (alarm_aborted > 0)
goto abort_no_unlock;
sec= 1; /* Abort mode */
}
now= my_time(0);
if (!alarm_data)
{
@ -190,13 +205,6 @@ my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm_data)
one_signal_hand_sigmask(SIG_BLOCK,&full_signal_set,&old_mask);
pthread_mutex_lock(&LOCK_alarm); /* Lock from threads & alarms */
if (unlikely(alarm_aborted))
{ /* No signal thread */
DBUG_PRINT("info", ("alarm aborted"));
if (alarm_aborted > 0)
goto abort;
sec= 1; /* Abort mode */
}
if (alarm_queue.elements >= max_used_alarms)
{
if (alarm_queue.elements == alarm_queue.max_elements)
@ -251,6 +259,8 @@ void thr_end_alarm(thr_alarm_t *alarmed)
#endif
DBUG_ENTER("thr_end_alarm");
if (my_disable_thr_alarm)
DBUG_VOID_RETURN;
one_signal_hand_sigmask(SIG_BLOCK,&full_signal_set,&old_mask);
alarm_data= (ALARM*) ((uchar*) *alarmed - offsetof(ALARM,alarmed));
pthread_mutex_lock(&LOCK_alarm);

View file

@ -230,14 +230,16 @@ static void check_locks(THR_LOCK *lock, const char *where,
if (found_errors < MAX_FOUND_ERRORS)
{
uint count=0;
uint count=0, count2= 0;
THR_LOCK_DATA *data;
for (data=lock->read.data ; data ; data=data->next)
{
count2++;
if (data->type == TL_READ_NO_INSERT)
count++;
/* Protect against infinite loop. */
DBUG_ASSERT(count <= lock->read_no_write_count);
DBUG_ASSERT(count <= lock->read_no_write_count &&
count2 <= MAX_LOCKS);
}
if (count != lock->read_no_write_count)
{
@ -288,7 +290,10 @@ static void check_locks(THR_LOCK *lock, const char *where,
if (lock->write.data->type == TL_WRITE_CONCURRENT_INSERT)
{
THR_LOCK_DATA *data;
for (data=lock->write.data->next ; data ; data=data->next)
uint count= 0;
for (data=lock->write.data->next;
data && count < MAX_LOCKS;
data=data->next)
{
if (data->type != TL_WRITE_CONCURRENT_INSERT)
{

View file

@ -398,9 +398,14 @@ else
echo "Installation of system tables failed! Examine the logs in"
echo "$ldata for more information."
echo
echo "You can try to start the mysqld daemon with:"
echo "The problem could be conflicting information in an external"
echo "my.cnf files. You can ignore these by doing:"
echo
echo " shell> $mysqld --skip-grant &"
echo " shell> /scripts/mysql_install_db --defaults-file=~/.my.cnf"
echo
echo "You can also try to start the mysqld daemon with:"
echo
echo " shell> $mysqld --skip-grant --general-log &"
echo
echo "and use the command line tool $bindir/mysql"
echo "to connect to the mysql database and look at the grant tables:"
@ -408,9 +413,6 @@ else
echo " shell> $bindir/mysql -u root mysql"
echo " mysql> show tables"
echo
echo "Try 'mysqld --help' if you have problems with paths. Using"
echo "--general-log gives you a log in $ldata that may be helpful."
echo
echo "The latest information about mysql_install_db is available at"
echo "http://kb.askmonty.org/v/installing-system-tables-mysql_install_db."
echo "MariaDB is hosted on launchpad; You can find the latest source and"

View file

@ -81,7 +81,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
event_data_objects.h event_scheduler.h \
sql_partition.h partition_info.h partition_element.h \
contributors.h sql_servers.h \
multi_range_read.h \
multi_range_read.h sql_handler.h \
sql_join_cache.h \
create_options.h \
sql_expression_cache.h

View file

@ -84,41 +84,11 @@
extern HASH open_cache;
/* flags for get_lock_data */
#define GET_LOCK_UNLOCK 1
#define GET_LOCK_STORE_LOCKS 2
static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count,
uint flags, TABLE **write_locked);
static void reset_lock_data(MYSQL_LOCK *sql_lock);
static void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock);
static int lock_external(THD *thd, TABLE **table,uint count);
static int unlock_external(THD *thd, TABLE **table,uint count);
static void print_lock_error(int error, const char *);
/*
Lock tables.
SYNOPSIS
mysql_lock_tables()
thd The current thread.
tables An array of pointers to the tables to lock.
count The number of tables to lock.
flags Options:
MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock
MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY Ignore SET GLOBAL READ_ONLY
MYSQL_LOCK_IGNORE_FLUSH Ignore a flush tables.
MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN Instead of reopening altered
or dropped tables by itself,
mysql_lock_tables() should
notify upper level and rely
on caller doing this.
need_reopen Out parameter, TRUE if some tables were altered
or deleted and should be reopened by caller.
RETURN
A lock structure pointer on success.
NULL on error or if some tables should be reopen.
*/
/* Map the return value of thr_lock to an error from errmsg.txt */
static int thr_lock_errno_to_mysql[]=
@ -132,6 +102,7 @@ static int thr_lock_errno_to_mysql[]=
@param flags Lock flags
@return 0 if all the check passed, non zero if a check failed.
*/
int mysql_lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
{
bool log_table_write_query;
@ -194,81 +165,118 @@ int mysql_lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
DBUG_RETURN(0);
}
/*
Lock tables.
SYNOPSIS
mysql_lock_tables()
thd The current thread.
tables An array of pointers to the tables to lock.
count The number of tables to lock.
flags Options:
MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock
MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY Ignore SET GLOBAL READ_ONLY
MYSQL_LOCK_IGNORE_FLUSH Ignore a flush tables.
MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN Instead of reopening altered
or dropped tables by itself,
mysql_lock_tables() should
notify upper level and rely
on caller doing this.
need_reopen Out parameter, TRUE if some tables were altered
or deleted and should be reopened by caller.
RETURN
A lock structure pointer on success.
NULL on error or if some tables should be reopen.
*/
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
uint flags, bool *need_reopen)
{
MYSQL_LOCK *sql_lock;
TABLE *write_lock_used;
int rc;
DBUG_ENTER("mysql_lock_tables");
MYSQL_LOCK *sql_lock;
DBUG_ENTER("mysql_lock_tables(tables)");
*need_reopen= FALSE;
if (mysql_lock_tables_check(thd, tables, count, flags))
DBUG_RETURN (NULL);
DBUG_RETURN(NULL);
if (!(sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS,
&write_lock_used)) ||
! sql_lock->table_count)
DBUG_RETURN(sql_lock);
if (mysql_lock_tables(thd, sql_lock, write_lock_used != 0, flags,
need_reopen))
{
/* Clear the lock type of all lock data to avoid reusage. */
reset_lock_data(sql_lock, 1);
my_free(sql_lock, MYF(0));
sql_lock= 0;
}
DBUG_RETURN(sql_lock);
}
/**
Lock a table based on a MYSQL_LOCK structure.
mysql_lock_tables()
@param thd The current thread.
@param sql_lock Tables that should be locked
@param write_lock_used 1 if any of the tables are write locked
@param flags See mysql_lock_tables()
@param need_reopen Out parameter, TRUE if some tables were altered
or deleted and should be reopened by caller.
@return 0 ok
@return 1 error
*/
bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock,
bool write_lock_used,
uint flags, bool *need_reopen)
{
int rc;
bool error= 1;
DBUG_ENTER("mysql_lock_tables(sql_lock)");
*need_reopen= FALSE;
for (;;)
{
if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS,
&write_lock_used)) ||
! sql_lock->table_count)
break;
if (global_read_lock && write_lock_used &&
! (flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK))
if (write_lock_used && !(flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK))
{
/*
Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
Wait until the lock is gone
*/
if (wait_if_global_read_lock(thd, 1, 1))
if (global_read_lock)
{
/* Clear the lock type of all lock data to avoid reusage. */
reset_lock_data(sql_lock);
my_free((uchar*) sql_lock,MYF(0));
sql_lock=0;
break;
/*
Someone has issued LOCK ALL TABLES FOR READ and we want a write lock
Wait until the lock is gone
*/
if (wait_if_global_read_lock(thd, 1, 1))
break;
if (thd->version != refresh_version)
goto retry;
}
if (thd->version != refresh_version)
{
/* Clear the lock type of all lock data to avoid reusage. */
reset_lock_data(sql_lock);
my_free((uchar*) sql_lock,MYF(0));
goto retry;
}
}
if (!(flags & MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY) &&
write_lock_used &&
opt_readonly &&
!(thd->security_ctx->master_access & SUPER_ACL) &&
!thd->slave_thread)
{
/*
Someone has issued SET GLOBAL READ_ONLY=1 and we want a write lock.
We do not wait for READ_ONLY=0, and fail.
*/
reset_lock_data(sql_lock);
my_free((uchar*) sql_lock, MYF(0));
sql_lock=0;
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
break;
if (opt_readonly &&
!(thd->security_ctx->master_access & SUPER_ACL) &&
!thd->slave_thread)
{
/*
Someone has issued SET GLOBAL READ_ONLY=1 and we want a write lock.
We do not wait for READ_ONLY=0, and fail.
*/
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
break;
}
}
thd_proc_info(thd, "System lock");
DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
if (lock_external(thd, sql_lock->table, sql_lock->table_count))
{
/* Clear the lock type of all lock data to avoid reusage. */
reset_lock_data(sql_lock);
my_free((uchar*) sql_lock,MYF(0));
sql_lock=0;
break;
}
thd_proc_info(thd, "Table lock");
DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
thd->locked=1;
/* Copy the lock data array. thr_multi_lock() reorders its contens. */
memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
sql_lock->lock_count * sizeof(*sql_lock->locks));
@ -277,70 +285,66 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
sql_lock->lock_count,
sql_lock->lock_count,
thd->lock_id)];
if (rc > 1) /* a timeout or a deadlock */
if (rc) /* Locking failed */
{
VOID(unlock_external(thd, sql_lock->table, sql_lock->table_count));
my_error(rc, MYF(0));
my_free((uchar*) sql_lock,MYF(0));
sql_lock= 0;
break;
if (rc > 1)
{
/* a timeout or a deadlock */
my_error(rc, MYF(0));
break;
}
/* We where aborted and should try again from upper level*/
thd->some_tables_deleted= 1;
}
else if (rc == 1) /* aborted */
else
{
/*
reset_lock_data is required here. If thr_multi_lock fails it
resets lock type for tables, which were locked before (and
including) one that caused error. Lock type for other tables
preserved.
Lock worked. Now check that nothing happend while we where waiting
to get the lock that would require us to free it.
*/
reset_lock_data(sql_lock);
thd->some_tables_deleted=1; // Try again
sql_lock->lock_count= 0; // Locks are already freed
}
else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
{
/*
Thread was killed or lock aborted. Let upper level close all
used tables and retry or give error.
*/
thd->locked=0;
break;
}
else if (!thd->open_tables)
{
// Only using temporary tables, no need to unlock
thd->some_tables_deleted=0;
thd->locked=0;
break;
}
thd_proc_info(thd, 0);
error= 0;
if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
{
/*
Table was not signaled for deletion or we don't care if it was.
Return with table as locked.
*/
break;
}
else if (!thd->open_tables && !(flags & MYSQL_LOCK_NOT_TEMPORARY))
{
/*
Only using temporary tables, no need to unlock.
We need the flag as open_tables is not enough to distingush if
we are only using temporary tables for tables used trough
the HANDLER interface.
We reset some_tables_deleted as it doesn't make sense to have this
one when we are only using temporary tables.
*/
thd->some_tables_deleted=0;
break;
}
/* some table was altered or deleted. reopen tables marked deleted */
error= 1;
mysql_unlock_tables(thd, sql_lock, 0);
}
/* some table was altered or deleted. reopen tables marked deleted */
mysql_unlock_tables(thd,sql_lock);
thd->locked=0;
retry:
sql_lock=0;
if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN)
{
*need_reopen= TRUE;
break;
}
if (wait_for_tables(thd))
break; // Couldn't open tables
}
thd_proc_info(thd, 0);
if (thd->killed)
{
thd->send_kill_message();
if (sql_lock)
{
mysql_unlock_tables(thd,sql_lock);
sql_lock=0;
}
break; // Couldn't open tables
reset_lock_data(sql_lock, 0); // Set org locks and retry
}
thd_proc_info(thd, 0);
thd->set_time_after_lock();
DBUG_RETURN (sql_lock);
DBUG_RETURN(error);
}
@ -380,15 +384,15 @@ static int lock_external(THD *thd, TABLE **tables, uint count)
DBUG_RETURN(0);
}
void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock)
void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock)
{
DBUG_ENTER("mysql_unlock_tables");
if (sql_lock->table_count)
VOID(unlock_external(thd,sql_lock->table,sql_lock->table_count));
if (sql_lock->lock_count)
thr_multi_unlock(sql_lock->locks,sql_lock->lock_count, 0);
my_free((uchar*) sql_lock,MYF(0));
if (free_lock)
my_free((uchar*) sql_lock,MYF(0));
DBUG_VOID_RETURN;
}
@ -847,12 +851,12 @@ static int unlock_external(THD *thd, TABLE **table,uint count)
@param write_lock_used Store pointer to last table with WRITE_ALLOW_WRITE
*/
static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
uint flags, TABLE **write_lock_used)
MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
uint flags, TABLE **write_lock_used)
{
uint i,tables,lock_count;
MYSQL_LOCK *sql_lock;
THR_LOCK_DATA **locks, **locks_buf, **locks_start;
THR_LOCK_DATA **locks, **locks_buf;
TABLE **to, **table_buf;
DBUG_ENTER("get_lock_data");
@ -891,7 +895,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
{
TABLE *table;
enum thr_lock_type lock_type;
THR_LOCK_DATA **locks_start;
if ((table=table_ptr[i])->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE)
continue;
lock_type= table->reginfo.lock_type;
@ -904,12 +908,11 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
my_error(ER_OPEN_AS_READONLY,MYF(0),table->alias.c_ptr());
/* Clear the lock type of the lock data that are stored already. */
sql_lock->lock_count= (uint) (locks - sql_lock->locks);
reset_lock_data(sql_lock);
reset_lock_data(sql_lock, 1);
my_free((uchar*) sql_lock,MYF(0));
DBUG_RETURN(0);
}
}
THR_LOCK_DATA **org_locks = locks;
locks_start= locks;
locks= table->file->store_lock(thd, locks,
(flags & GET_LOCK_UNLOCK) ? TL_IGNORE :
@ -922,8 +925,13 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
}
*to++= table;
if (locks)
for ( ; org_locks != locks ; org_locks++)
(*org_locks)->debug_print_param= (void *) table;
{
for ( ; locks_start != locks ; locks_start++)
{
(*locks_start)->debug_print_param= (void *) table;
(*locks_start)->org_type= (*locks_start)->type;
}
}
}
/*
We do not use 'tables', because there are cases where store_lock()
@ -964,10 +972,13 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
Clear the lock type of all lock data. This ensures that the next
lock request will set its lock type properly.
@param sql_lock The MySQL lock.
@param sql_lock The MySQL lock.
@param unlock If set, then set lock type to TL_UNLOCK,
otherwise set to original lock type from
get_store_lock().
*/
static void reset_lock_data(MYSQL_LOCK *sql_lock)
static void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock)
{
THR_LOCK_DATA **ldata;
THR_LOCK_DATA **ldata_end;
@ -975,10 +986,7 @@ static void reset_lock_data(MYSQL_LOCK *sql_lock)
for (ldata= sql_lock->locks, ldata_end= ldata + sql_lock->lock_count;
ldata < ldata_end;
ldata++)
{
/* Reset lock type. */
(*ldata)->type= TL_UNLOCK;
}
(*ldata)->type= unlock ? TL_UNLOCK : (*ldata)->org_type;
}

View file

@ -1487,15 +1487,6 @@ void mysqld_stmt_reset(THD *thd, char *packet);
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
void reinit_stmt_before_use(THD *thd, LEX *lex);
/* sql_handler.cc */
bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen);
bool mysql_ha_close(THD *thd, TABLE_LIST *tables);
bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *,
List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows);
void mysql_ha_flush(THD *thd);
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked);
void mysql_ha_cleanup(THD *thd);
/* sql_base.cc */
#define TMP_TABLE_KEY_EXTRA 8
void set_item_name(Item *item,char *pos,uint length);
@ -2194,6 +2185,10 @@ extern struct st_VioSSLFd * ssl_acceptor_fd;
MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
uint flags, bool *need_reopen);
bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock,
bool write_lock_used,
uint flags, bool *need_reopen);
/* mysql_lock_tables() and open_table() flags bits */
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK 0x0001
#define MYSQL_LOCK_IGNORE_FLUSH 0x0002
@ -2201,8 +2196,12 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count,
#define MYSQL_OPEN_TEMPORARY_ONLY 0x0008
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0010
#define MYSQL_LOCK_PERF_SCHEMA 0x0020
#define MYSQL_LOCK_NOT_TEMPORARY 0x0040
/* flags for get_lock_data */
#define GET_LOCK_UNLOCK 1
#define GET_LOCK_STORE_LOCKS 2
void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock= 1);
void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);
void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count);
void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table,
@ -2223,6 +2222,8 @@ bool make_global_read_lock_block_commit(THD *thd);
bool set_protect_against_global_read_lock(void);
void unset_protect_against_global_read_lock(void);
void broadcast_refresh(void);
MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
uint flags, TABLE **write_lock_used);
/* Lock based on name */
int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list);

View file

@ -457,7 +457,7 @@ static bool volatile ready_to_exit;
static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0;
static my_bool opt_short_log_format= 0;
static my_bool opt_ignore_wrong_options= 0, opt_expect_abort= 0;
static my_bool opt_sync= 0;
static my_bool opt_sync= 0, opt_thread_alarm;
static uint kill_cached_threads, wake_thread;
ulong thread_created;
uint thread_handling;
@ -6003,7 +6003,7 @@ enum options_mysqld
OPT_RANGE_ALLOC_BLOCK_SIZE, OPT_ALLOW_SUSPICIOUS_UDFS,
OPT_QUERY_ALLOC_BLOCK_SIZE, OPT_QUERY_PREALLOC_SIZE,
OPT_TRANS_ALLOC_BLOCK_SIZE, OPT_TRANS_PREALLOC_SIZE,
OPT_SYNC_FRM, OPT_SYNC_BINLOG, OPT_SYNC,
OPT_SYNC_FRM, OPT_SYNC_BINLOG, OPT_SYNC, OPT_THREAD_ALARM,
OPT_SYNC_REPLICATION,
OPT_SYNC_REPLICATION_SLAVE_ID,
OPT_SYNC_REPLICATION_TIMEOUT,
@ -6328,7 +6328,7 @@ struct my_option my_long_options[] =
"Disable initialization of builtin InnoDB plugin.",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"init-connect", OPT_INIT_CONNECT,
"Command(s) that are executed for each new connection.",
"Command(s) that are executed for each new connection (but not for SUPER users).",
&opt_init_connect, &opt_init_connect, 0, GET_STR_ALLOC,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#ifndef DISABLE_GRANT_OPTIONS
@ -7579,6 +7579,10 @@ thread is in the relay logs.",
"error. Used only if the connection has active cursors.",
&table_lock_wait_timeout, &table_lock_wait_timeout,
0, GET_ULONG, REQUIRED_ARG, 50, 1, 1024 * 1024 * 1024, 0, 1, 0},
{"thread-alarm", OPT_THREAD_ALARM,
"Enable/disable system thread alarm calls. Should only be turned off when running tests or debugging!!",
&opt_thread_alarm, &opt_thread_alarm, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0,
0},
{"thread_cache_size", OPT_THREAD_CACHE_SIZE,
"How many threads we should keep in a cache for reuse.",
&thread_cache_size, &thread_cache_size, 0, GET_ULONG,
@ -9358,6 +9362,7 @@ static int get_options(int *argc,char **argv)
*/
my_disable_locking= myisam_single_user= test(opt_external_locking == 0);
my_disable_sync= opt_sync == 0;
my_disable_thr_alarm= opt_thread_alarm == 0;
my_default_record_cache_size=global_system_variables.read_buff_size;
myisam_max_temp_length=
(my_off_t) global_system_variables.myisam_max_sort_file_size;

View file

@ -697,7 +697,8 @@ net_real_write(NET *net,const uchar *packet, size_t len)
{
my_bool old_mode;
thr_end_alarm(&alarmed);
vio_blocking(net->vio, net_blocking, &old_mode);
if (!net_blocking)
vio_blocking(net->vio, net_blocking, &old_mode);
}
net->reading_or_writing=0;
DBUG_RETURN(((int) (pos != end)));
@ -988,7 +989,8 @@ end:
{
my_bool old_mode;
thr_end_alarm(&alarmed);
vio_blocking(net->vio, net_blocking, &old_mode);
if (!net_blocking)
vio_blocking(net->vio, net_blocking, &old_mode);
}
net->reading_or_writing=0;
#ifdef DEBUG_DATA_PACKETS

View file

@ -22,6 +22,7 @@
#include "sp_head.h"
#include "sp.h"
#include "sql_trigger.h"
#include "sql_handler.h"
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>

View file

@ -44,6 +44,7 @@
#include "sp_cache.h"
#include "sql_select.h" /* declares create_tmp_table() */
#include "debug_sync.h"
#include "sql_handler.h"
/*
The following is used to initialise Table_ident with a internal
@ -702,7 +703,7 @@ THD::THD()
catalog= (char*)"std"; // the only catalog we have for now
main_security_ctx.init();
security_ctx= &main_security_ctx;
locked=some_tables_deleted=no_errors=password= 0;
some_tables_deleted=no_errors=password= 0;
query_start_used= 0;
count_cuted_fields= CHECK_FIELD_IGNORE;
killed= NOT_KILLED;

View file

@ -1944,7 +1944,7 @@ public:
bool slave_thread, one_shot_set;
/* tells if current statement should binlog row-based(1) or stmt-based(0) */
bool current_stmt_binlog_row_based;
bool locked, some_tables_deleted;
bool some_tables_deleted;
bool last_cuted_field;
bool no_errors, password;
bool extra_port; /* If extra connection */

View file

@ -20,6 +20,7 @@
#include <mysys_err.h>
#include "sp.h"
#include "events.h"
#include "sql_handler.h"
#include <my_dir.h>
#include <m_ctype.h>
#include "log.h"

View file

@ -23,6 +23,7 @@
#include "sql_select.h"
#include "sp_head.h"
#include "sql_trigger.h"
#include "sql_handler.h"
/**
Implement DELETE SQL word.

View file

@ -56,15 +56,41 @@
second container. When the table is flushed, the pointer is cleared.
*/
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
#endif
#include "mysql_priv.h"
#include "sql_select.h"
#include <assert.h>
#include "sql_handler.h"
#define HANDLER_TABLES_HASH_SIZE 120
static enum enum_ha_read_modes rkey_to_rnext[]=
{ RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV };
/*
Set handler to state after create, but keep base information about
which table is used
*/
void SQL_HANDLER::reset()
{
fields.empty();
arena.free_items();
free_root(&mem_root, MYF(0));
my_free(lock, MYF(MY_ALLOW_ZERO_PTR));
init();
}
/* Free all allocated data */
SQL_HANDLER::~SQL_HANDLER()
{
reset();
my_free(base_data, MYF(MY_ALLOW_ZERO_PTR));
}
/*
Get hash key and hash key length.
@ -84,11 +110,11 @@ static enum enum_ha_read_modes rkey_to_rnext[]=
Pointer to the TABLE_LIST struct.
*/
static char *mysql_ha_hash_get_key(TABLE_LIST *tables, size_t *key_len_p,
static char *mysql_ha_hash_get_key(SQL_HANDLER *table, size_t *key_len,
my_bool first __attribute__((unused)))
{
*key_len_p= strlen(tables->alias) + 1 ; /* include '\0' in comparisons */
return tables->alias;
*key_len= table->handler_name.length + 1 ; /* include '\0' in comparisons */
return table->handler_name.str;
}
@ -106,9 +132,9 @@ static char *mysql_ha_hash_get_key(TABLE_LIST *tables, size_t *key_len_p,
Nothing
*/
static void mysql_ha_hash_free(TABLE_LIST *tables)
static void mysql_ha_hash_free(SQL_HANDLER *table)
{
my_free((char*) tables, MYF(0));
delete table;
}
/**
@ -120,14 +146,21 @@ static void mysql_ha_hash_free(TABLE_LIST *tables)
@note Though this function takes a list of tables, only the first list entry
will be closed.
@mote handler_object is not deleted!
@note Broadcasts refresh if it closed a table with old version.
*/
static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
static void mysql_ha_close_table(SQL_HANDLER *handler,
bool is_locked)
{
THD *thd= handler->thd;
TABLE *table= handler->table;
TABLE **table_ptr;
/* check if table was already closed */
if (!table)
return;
/*
Though we could take the table pointer from hash_tables->table,
we must follow the thd->handler_tables chain anyway, as we need the
@ -135,13 +168,13 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
for close_thread_table().
*/
for (table_ptr= &(thd->handler_tables);
*table_ptr && (*table_ptr != tables->table);
*table_ptr && (*table_ptr != table);
table_ptr= &(*table_ptr)->next)
;
if (*table_ptr)
{
(*table_ptr)->file->ha_index_or_rnd_end();
table->file->ha_index_or_rnd_end();
if (! is_locked)
VOID(pthread_mutex_lock(&LOCK_open));
if (close_thread_table(thd, table_ptr))
@ -152,17 +185,15 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
if (! is_locked)
VOID(pthread_mutex_unlock(&LOCK_open));
}
else if (tables->table)
else
{
/* Must be a temporary table */
TABLE *table= tables->table;
table->file->ha_index_or_rnd_end();
table->query_id= thd->query_id;
table->open_by_handler= 0;
}
/* Mark table as closed, ready for re-open if necessary. */
tables->table= NULL;
my_free(handler->lock, MYF(MY_ALLOW_ZERO_PTR));
handler->init();
}
/*
@ -178,7 +209,7 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
Though this function takes a list of tables, only the first list entry
will be opened.
'reopen' is set when a handler table is to be re-opened. In this case,
'tables' is the pointer to the hashed TABLE_LIST object which has been
'tables' is the pointer to the hashed SQL_HANDLER object which has been
saved on the original open.
'reopen' is also used to suppress the sending of an 'ok' message.
@ -187,17 +218,17 @@ static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables,
TRUE Error
*/
bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
{
TABLE_LIST *hash_tables = NULL;
char *db, *name, *alias;
uint dblen, namelen, aliaslen, counter;
SQL_HANDLER *sql_handler= 0;
uint counter;
int error;
TABLE *backup_open_tables;
TABLE *table, *backup_open_tables, *write_lock_used;
Query_arena backup_arena;
DBUG_ENTER("mysql_ha_open");
DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d",
tables->db, tables->table_name, tables->alias,
(int) reopen));
reopen != 0));
if (tables->schema_table)
{
@ -210,7 +241,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
if (! hash_inited(&thd->handler_tables_hash))
{
/*
HASH entries are of type TABLE_LIST.
HASH entries are of type SQL_HANDLER
*/
if (hash_init(&thd->handler_tables_hash, &my_charset_latin1,
HANDLER_TABLES_HASH_SIZE, 0, 0,
@ -288,8 +319,10 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
if (error)
goto err;
table= tables->table;
/* There can be only one table in '*tables'. */
if (! (tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
if (! (table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
{
my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
goto err;
@ -297,36 +330,69 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
if (! reopen)
{
/* copy the TABLE_LIST struct */
dblen= strlen(tables->db) + 1;
namelen= strlen(tables->table_name) + 1;
aliaslen= strlen(tables->alias) + 1;
if (!(my_multi_malloc(MYF(MY_WME),
&hash_tables, (uint) sizeof(*hash_tables),
&db, (uint) dblen,
&name, (uint) namelen,
&alias, (uint) aliaslen,
/* copy data to sql_handler */
if (!(sql_handler= new SQL_HANDLER(thd)))
goto err;
init_alloc_root(&sql_handler->mem_root, 1024, 0);
sql_handler->table= table;
sql_handler->db.length= strlen(tables->db);
sql_handler->table_name.length= strlen(tables->table_name);
sql_handler->handler_name.length= strlen(tables->alias);
if (!(my_multi_malloc(MY_WME,
&sql_handler->db.str,
(uint) sql_handler->db.length + 1,
&sql_handler->table_name.str,
(uint) sql_handler->table_name.length + 1,
&sql_handler->handler_name.str,
(uint) sql_handler->handler_name.length + 1,
NullS)))
goto err;
/* structure copy */
*hash_tables= *tables;
hash_tables->db= db;
hash_tables->table_name= name;
hash_tables->alias= alias;
memcpy(hash_tables->db, tables->db, dblen);
memcpy(hash_tables->table_name, tables->table_name, namelen);
memcpy(hash_tables->alias, tables->alias, aliaslen);
sql_handler->base_data= sql_handler->db.str; // Free this
memcpy(sql_handler->db.str, tables->db, sql_handler->db.length +1);
memcpy(sql_handler->table_name.str, tables->table_name,
sql_handler->table_name.length+1);
memcpy(sql_handler->handler_name.str, tables->alias,
sql_handler->handler_name.length +1);
/* add to hash */
if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
if (my_hash_insert(&thd->handler_tables_hash, (uchar*) sql_handler))
goto err;
}
else
{
sql_handler= reopen;
sql_handler->reset();
}
sql_handler->table= table;
if (!(sql_handler->lock= get_lock_data(thd, &sql_handler->table, 1,
GET_LOCK_STORE_LOCKS,
&write_lock_used)))
goto err;
/* Get a list of all fields for send_fields */
thd->set_n_backup_active_arena(&sql_handler->arena, &backup_arena);
error= table->fill_item_list(&sql_handler->fields);
thd->restore_active_arena(&sql_handler->arena, &backup_arena);
if (error)
{
if (reopen)
sql_handler= 0;
goto err;
}
/* Always read all columns */
table->read_set= &table->s->all_set;
table->vcol_set= &table->s->all_set;
/*
If it's a temp table, don't reset table->query_id as the table is
being used by this handler. Otherwise, no meaning at all.
*/
tables->table->open_by_handler= 1;
table->open_by_handler= 1;
if (! reopen)
my_ok(thd);
@ -334,10 +400,13 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
DBUG_RETURN(FALSE);
err:
if (hash_tables)
my_free((char*) hash_tables, MYF(0));
delete sql_handler;
if (tables->table)
mysql_ha_close_table(thd, tables, FALSE);
{
SQL_HANDLER tmp_sql_handler(thd);
tmp_sql_handler.table= tables->table;
mysql_ha_close_table(&tmp_sql_handler, FALSE);
}
DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE);
}
@ -362,17 +431,17 @@ err:
bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
{
TABLE_LIST *hash_tables;
SQL_HANDLER *handler;
DBUG_ENTER("mysql_ha_close");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias));
if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
(uchar*) tables->alias,
strlen(tables->alias) + 1)))
if ((handler= (SQL_HANDLER*) hash_search(&thd->handler_tables_hash,
(uchar*) tables->alias,
strlen(tables->alias) + 1)))
{
mysql_ha_close_table(thd, hash_tables, FALSE);
hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
mysql_ha_close_table(handler, FALSE);
hash_delete(&thd->handler_tables_hash, (uchar*) handler);
}
else
{
@ -387,6 +456,161 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
}
/**
Finds an open HANDLER table.
@params name Name of handler to open
@return 0 failure
@return handler
*/
SQL_HANDLER *mysql_ha_find_handler(THD *thd, const char *name)
{
SQL_HANDLER *handler;
if ((handler= (SQL_HANDLER*) hash_search(&thd->handler_tables_hash,
(uchar*) name,
strlen(name) + 1)))
{
DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' table: %p",
handler->db.str,
handler->table_name.str,
handler->handler_name.str, handler->table));
if (!handler->table)
{
/* The handler table has been closed. Re-open it. */
TABLE_LIST tmp;
tmp.init_one_table(handler->db.str, handler->table_name.str,
TL_READ);
tmp.alias= handler->handler_name.str;
if (mysql_ha_open(thd, &tmp, handler))
{
DBUG_PRINT("exit",("reopen failed"));
return 0;
}
}
}
else
{
my_error(ER_UNKNOWN_TABLE, MYF(0), name, "HANDLER");
return 0;
}
return handler;
}
/**
Check that condition and key name are ok
@param handler
@param mode Read mode (RFIRST, RNEXT etc...)
@param keyname Key to use.
@param key_expr List of key column values
@param cond Where clause
@param in_prepare If we are in prepare phase (we can't evalute items yet)
@return 0 ok
@return 1 error
In ok, then values of used key and mode is stored in sql_handler
*/
static bool
mysql_ha_fix_cond_and_key(SQL_HANDLER *handler,
enum enum_ha_read_modes mode, char *keyname,
List<Item> *key_expr,
Item *cond, bool in_prepare)
{
THD *thd= handler->thd;
TABLE *table= handler->table;
if (cond)
{
/* This can only be true for temp tables */
if (table->query_id != thd->query_id)
cond->cleanup(); // File was reopened
if ((!cond->fixed &&
cond->fix_fields(thd, &cond)) || cond->check_cols(1))
return 1;
}
if (keyname)
{
/* Check if same as last keyname. If not, do a full lookup */
if (handler->keyno < 0 ||
my_strcasecmp(&my_charset_latin1,
keyname,
table->s->key_info[handler->keyno].name))
{
if ((handler->keyno= find_type(keyname, &table->s->keynames, 1+2)-1)<0)
{
my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), keyname,
handler->handler_name);
return 1;
}
}
/* Check key parts */
if (mode == RKEY)
{
TABLE *table= handler->table;
KEY *keyinfo= table->key_info + handler->keyno;
KEY_PART_INFO *key_part= keyinfo->key_part;
List_iterator<Item> it_ke(*key_expr);
Item *item;
key_part_map keypart_map;
uint key_len;
if (key_expr->elements > keyinfo->key_parts)
{
my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), keyinfo->key_parts);
return 1;
}
for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++)
{
my_bitmap_map *old_map;
/* note that 'item' can be changed by fix_fields() call */
if ((!item->fixed &&
item->fix_fields(thd, it_ke.ref())) ||
(item= *it_ke.ref())->check_cols(1))
return 1;
if (item->used_tables() & ~(RAND_TABLE_BIT | PARAM_TABLE_BIT))
{
my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ");
return 1;
}
if (!in_prepare)
{
old_map= dbug_tmp_use_all_columns(table, table->write_set);
(void) item->save_in_field(key_part->field, 1);
dbug_tmp_restore_column_map(table->write_set, old_map);
}
key_len+= key_part->store_length;
keypart_map= (keypart_map << 1) | 1;
}
handler->keypart_map= keypart_map;
handler->key_len= key_len;
}
else
{
/*
Check if the same index involved.
We need to always do this check because we may not have yet
called the handler since the last keyno change.
*/
if ((uint) handler->keyno != table->file->get_index())
{
if (mode == RNEXT)
mode= RFIRST;
else if (mode == RPREV)
mode= RLAST;
}
}
}
handler->mode= mode; // Store adjusted mode
return 0;
}
/*
Read from a HANDLER table.
@ -413,147 +637,77 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
enum ha_rkey_function ha_rkey_mode, Item *cond,
ha_rows select_limit_cnt, ha_rows offset_limit_cnt)
{
TABLE_LIST *hash_tables;
TABLE *table, *backup_open_tables;
MYSQL_LOCK *lock;
SQL_HANDLER *handler;
TABLE *table;
List<Item> list;
Protocol *protocol= thd->protocol;
char buff[MAX_FIELD_WIDTH];
String buffer(buff, sizeof(buff), system_charset_info);
int error, keyno= -1;
int error, keyno;
uint num_rows;
uchar *UNINIT_VAR(key);
uint UNINIT_VAR(key_len);
bool need_reopen;
List_iterator<Item> it;
DBUG_ENTER("mysql_ha_read");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias));
thd->lex->select_lex.context.resolve_in_table_list_only(tables);
list.push_front(new Item_field(&thd->lex->select_lex.context,
NULL, NULL, "*"));
List_iterator<Item> it(list);
it++;
retry:
if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
(uchar*) tables->alias,
strlen(tables->alias) + 1)))
if (!(handler= mysql_ha_find_handler(thd, tables->alias)))
goto err0;
table= handler->table;
tables->table= table; // This is used by fix_fields
/* save open_tables state */
if (handler->lock->lock_count > 0)
{
table= hash_tables->table;
DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' table: 0x%lx",
hash_tables->db, hash_tables->table_name,
hash_tables->alias, (long) table));
if (!table)
bool lock_error;
handler->lock->locks[0]->type= handler->lock->locks[0]->org_type;
lock_error= mysql_lock_tables(thd, handler->lock, 0,
(MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN |
(handler->table->s->tmp_table ==
NO_TMP_TABLE ?
MYSQL_LOCK_NOT_TEMPORARY : 0)),
&need_reopen);
if (need_reopen)
{
/*
The handler table has been closed. Re-open it.
*/
if (mysql_ha_open(thd, hash_tables, 1))
mysql_ha_close_table(handler, FALSE);
if (thd->stmt_arena->is_stmt_execute())
{
DBUG_PRINT("exit",("reopen failed"));
/*
As we have already sent field list and types to the client, we can't
handle any changes in the table format for prepared statements.
Better to force a reprepare.
*/
my_error(ER_NEED_REPREPARE, MYF(0));
goto err0;
}
table= hash_tables->table;
DBUG_PRINT("info",("re-opened '%s'.'%s' as '%s' tab %p",
hash_tables->db, hash_tables->table_name,
hash_tables->alias, table));
/*
The lock might have been aborted, we need to manually reset
thd->some_tables_deleted because handler's tables are closed
in a non-standard way. Otherwise we might loop indefinitely.
*/
thd->some_tables_deleted= 0;
goto retry;
}
#if MYSQL_VERSION_ID < 40100
if (*tables->db && strcmp(table->table_cache_key, tables->db))
{
DBUG_PRINT("info",("wrong db"));
table= NULL;
}
#endif
}
else
table= NULL;
if (!table)
{
#if MYSQL_VERSION_ID < 40100
char buff[MAX_DBKEY_LENGTH];
if (*tables->db)
strxnmov(buff, sizeof(buff)-1, tables->db, ".", tables->table_name,
NullS);
else
strncpy(buff, tables->alias, sizeof(buff));
my_error(ER_UNKNOWN_TABLE, MYF(0), buff, "HANDLER");
#else
my_error(ER_UNKNOWN_TABLE, MYF(0), tables->alias, "HANDLER");
#endif
goto err0;
}
tables->table=table;
/* save open_tables state */
backup_open_tables= thd->open_tables;
/*
mysql_lock_tables() needs thd->open_tables to be set correctly to
be able to handle aborts properly. When the abort happens, it's
safe to not protect thd->handler_tables because it won't close any
tables.
*/
thd->open_tables= thd->handler_tables;
lock= mysql_lock_tables(thd, &tables->table, 1,
MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, &need_reopen);
/* restore previous context */
thd->open_tables= backup_open_tables;
if (need_reopen)
{
mysql_ha_close_table(thd, hash_tables, FALSE);
/*
The lock might have been aborted, we need to manually reset
thd->some_tables_deleted because handler's tables are closed
in a non-standard way. Otherwise we might loop indefinitely.
*/
thd->some_tables_deleted= 0;
goto retry;
if (lock_error)
goto err0; // mysql_lock_tables() printed error message already
}
if (!lock)
goto err0; // mysql_lock_tables() printed error message already
// Always read all columns
tables->table->read_set= &tables->table->s->all_set;
if (cond)
{
if (table->query_id != thd->query_id)
cond->cleanup(); // File was reopened
if ((!cond->fixed &&
cond->fix_fields(thd, &cond)) || cond->check_cols(1))
goto err;
}
if (keyname)
{
if ((keyno=find_type(keyname, &table->s->keynames, 1+2)-1)<0)
{
my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), keyname, tables->alias);
goto err;
}
/* Check if the same index involved. */
if ((uint) keyno != table->file->get_index())
{
if (mode == RNEXT)
mode= RFIRST;
else if (mode == RPREV)
mode= RLAST;
}
}
if (insert_fields(thd, &thd->lex->select_lex.context,
tables->db, tables->alias, &it, 0))
if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, cond, 0))
goto err;
mode= handler->mode;
keyno= handler->keyno;
protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
it.init(handler->fields);
protocol->send_fields(&handler->fields,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
/*
In ::external_lock InnoDB resets the fields which tell it that
@ -576,9 +730,7 @@ retry:
error= table->file->ha_index_next(table->record[0]);
}
else
{
error= table->file->ha_rnd_next(table->record[0]);
}
break;
}
/* else fall through */
@ -595,7 +747,7 @@ retry:
if (!(error= table->file->ha_rnd_init(1)))
error= table->file->ha_rnd_next(table->record[0]);
}
mode=RNEXT;
mode= RNEXT;
break;
case RPREV:
DBUG_ASSERT(keyname != 0);
@ -612,7 +764,7 @@ retry:
table->file->ha_index_or_rnd_end();
table->file->ha_index_init(keyno, 1);
error= table->file->ha_index_last(table->record[0]);
mode=RPREV;
mode= RPREV;
break;
case RNEXT_SAME:
/* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...) */
@ -622,44 +774,17 @@ retry:
case RKEY:
{
DBUG_ASSERT(keyname != 0);
KEY *keyinfo=table->key_info+keyno;
KEY_PART_INFO *key_part=keyinfo->key_part;
if (key_expr->elements > keyinfo->key_parts)
{
my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), keyinfo->key_parts);
goto err;
}
List_iterator<Item> it_ke(*key_expr);
Item *item;
key_part_map keypart_map;
for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++)
{
my_bitmap_map *old_map;
// 'item' can be changed by fix_fields() call
if ((!item->fixed &&
item->fix_fields(thd, it_ke.ref())) ||
(item= *it_ke.ref())->check_cols(1))
goto err;
if (item->used_tables() & ~RAND_TABLE_BIT)
{
my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ");
goto err;
}
old_map= dbug_tmp_use_all_columns(table, table->write_set);
(void) item->save_in_field(key_part->field, 1);
dbug_tmp_restore_column_map(table->write_set, old_map);
key_len+=key_part->store_length;
keypart_map= (keypart_map << 1) | 1;
}
if (!(key= (uchar*) thd->calloc(ALIGN_SIZE(key_len))))
if (!(key= (uchar*) thd->calloc(ALIGN_SIZE(handler->key_len))))
goto err;
table->file->ha_index_or_rnd_end();
table->file->ha_index_init(keyno, 1);
key_copy(key, table->record[0], table->key_info + keyno, key_len);
key_copy(key, table->record[0], table->key_info + keyno,
handler->key_len);
error= table->file->ha_index_read_map(table->record[0],
key, keypart_map, ha_rkey_mode);
mode=rkey_to_rnext[(int)ha_rkey_mode];
key, handler->keypart_map,
ha_rkey_mode);
mode= rkey_to_rnext[(int)ha_rkey_mode];
break;
}
default:
@ -703,19 +828,41 @@ retry:
num_rows++;
}
ok:
mysql_unlock_tables(thd,lock);
mysql_unlock_tables(thd, handler->lock, 0);
my_eof(thd);
DBUG_PRINT("exit",("OK"));
DBUG_RETURN(FALSE);
err:
mysql_unlock_tables(thd,lock);
mysql_unlock_tables(thd, handler->lock, 0);
err0:
DBUG_PRINT("exit",("ERROR"));
DBUG_RETURN(TRUE);
}
/**
Prepare for handler read
For parameters, see mysql_ha_read()
*/
SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables,
enum enum_ha_read_modes mode, char *keyname,
List<Item> *key_expr, Item *cond)
{
SQL_HANDLER *handler;
DBUG_ENTER("mysql_ha_read_prepare");
if (!(handler= mysql_ha_find_handler(thd, tables->alias)))
DBUG_RETURN(0);
tables->table= handler->table; // This is used by fix_fields
if (mysql_ha_fix_cond_and_key(handler, mode, keyname, key_expr, cond, 1))
DBUG_RETURN(0);
DBUG_RETURN(handler);
}
/**
Scan the handler tables hash for matching tables.
@ -727,30 +874,32 @@ err0:
table was matched.
*/
static TABLE_LIST *mysql_ha_find(THD *thd, TABLE_LIST *tables)
static SQL_HANDLER *mysql_ha_find_match(THD *thd, TABLE_LIST *tables)
{
TABLE_LIST *hash_tables, *head= NULL, *first= tables;
DBUG_ENTER("mysql_ha_find");
SQL_HANDLER *hash_tables, *head= NULL;
TABLE_LIST *first= tables;
DBUG_ENTER("mysql_ha_find_match");
/* search for all handlers with matching table names */
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
hash_tables= (SQL_HANDLER*) hash_element(&thd->handler_tables_hash, i);
for (tables= first; tables; tables= tables->next_local)
{
if ((! *tables->db ||
! my_strcasecmp(&my_charset_latin1, hash_tables->db, tables->db)) &&
! my_strcasecmp(&my_charset_latin1, hash_tables->table_name,
! my_strcasecmp(&my_charset_latin1, hash_tables->db.str,
tables->db)) &&
! my_strcasecmp(&my_charset_latin1, hash_tables->table_name.str,
tables->table_name))
{
/* Link into hash_tables list */
hash_tables->next= head;
head= hash_tables;
break;
}
if (tables)
{
hash_tables->next_local= head;
head= hash_tables;
}
}
}
DBUG_RETURN(head);
}
@ -767,18 +916,18 @@ static TABLE_LIST *mysql_ha_find(THD *thd, TABLE_LIST *tables)
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked)
{
TABLE_LIST *hash_tables, *next;
SQL_HANDLER *hash_tables, *next;
DBUG_ENTER("mysql_ha_rm_tables");
DBUG_ASSERT(tables);
hash_tables= mysql_ha_find(thd, tables);
hash_tables= mysql_ha_find_match(thd, tables);
while (hash_tables)
{
next= hash_tables->next_local;
next= hash_tables->next;
if (hash_tables->table)
mysql_ha_close_table(thd, hash_tables, is_locked);
mysql_ha_close_table(hash_tables, is_locked);
hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
hash_tables= next;
}
@ -798,16 +947,16 @@ void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked)
void mysql_ha_flush(THD *thd)
{
TABLE_LIST *hash_tables;
SQL_HANDLER *hash_tables;
DBUG_ENTER("mysql_ha_flush");
safe_mutex_assert_owner(&LOCK_open);
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
hash_tables= (SQL_HANDLER*) hash_element(&thd->handler_tables_hash, i);
if (hash_tables->table && hash_tables->table->needs_reopen_or_name_lock())
mysql_ha_close_table(thd, hash_tables, TRUE);
mysql_ha_close_table(hash_tables, TRUE);
}
DBUG_VOID_RETURN;
@ -824,14 +973,14 @@ void mysql_ha_flush(THD *thd)
void mysql_ha_cleanup(THD *thd)
{
TABLE_LIST *hash_tables;
SQL_HANDLER *hash_tables;
DBUG_ENTER("mysql_ha_cleanup");
for (uint i= 0; i < thd->handler_tables_hash.records; i++)
{
hash_tables= (TABLE_LIST*) hash_element(&thd->handler_tables_hash, i);
hash_tables= (SQL_HANDLER*) hash_element(&thd->handler_tables_hash, i);
if (hash_tables->table)
mysql_ha_close_table(thd, hash_tables, FALSE);
mysql_ha_close_table(hash_tables, FALSE);
}
hash_free(&thd->handler_tables_hash);

61
sql/sql_handler.h Normal file
View file

@ -0,0 +1,61 @@
/* Copyright (C) 2010 Monty Program 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; version 2 of the License.
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 */
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
/* Open handlers are stored here */
class SQL_HANDLER {
public:
TABLE *table;
List<Item> fields; /* Fields, set on open */
THD *thd;
LEX_STRING handler_name;
LEX_STRING db;
LEX_STRING table_name;
MEM_ROOT mem_root;
MYSQL_LOCK *lock;
key_part_map keypart_map;
int keyno; /* Used key */
uint key_len;
enum enum_ha_read_modes mode;
/* This is only used when deleting many handler objects */
SQL_HANDLER *next;
Query_arena arena;
char *base_data;
SQL_HANDLER(THD *thd_arg) :
thd(thd_arg), arena(&mem_root, Query_arena::INITIALIZED)
{ init(); clear_alloc_root(&mem_root); base_data= 0; }
void init() { keyno= -1; table= 0; lock= 0; }
void reset();
~SQL_HANDLER();
};
bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen);
bool mysql_ha_close(THD *thd, TABLE_LIST *tables);
bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *,
List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows);
void mysql_ha_flush(THD *thd);
void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables, bool is_locked);
void mysql_ha_cleanup(THD *thd);
SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables,
enum enum_ha_read_modes mode, char *keyname,
List<Item> *key_expr, Item *cond);

View file

@ -28,6 +28,7 @@
#include "events.h"
#include "sql_trigger.h"
#include "debug_sync.h"
#include "sql_handler.h"
#ifdef WITH_ARIA_STORAGE_ENGINE
#include "../storage/maria/ha_maria.h"

View file

@ -95,6 +95,7 @@ When one supplies long data for a placeholder:
#else
#include <mysql_com.h>
#endif
#include "sql_handler.h"
/**
A result class used to send cursor rows using the binary protocol.
@ -243,6 +244,8 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
int error;
THD *thd= stmt->thd;
DBUG_ENTER("send_prep_stmt");
DBUG_PRINT("enter",("stmt->id: %lu columns: %d param_count: %d",
stmt->id, columns, stmt->param_count));
buff[0]= 0; /* OK packet indicator */
int4store(buff+1, stmt->id);
@ -1835,6 +1838,56 @@ static bool mysql_test_insert_select(Prepared_statement *stmt,
return res;
}
/**
Validate SELECT statement.
In case of success, if this query is not EXPLAIN, send column list info
back to the client.
@param stmt prepared statement
@param tables list of tables used in the query
@retval 0 success
@retval 1 error, error message is set in THD
@retval 2 success, and statement metadata has been sent
*/
static int mysql_test_handler_read(Prepared_statement *stmt,
TABLE_LIST *tables)
{
THD *thd= stmt->thd;
LEX *lex= stmt->lex;
SQL_HANDLER *ha_table;
DBUG_ENTER("mysql_test_select");
lex->select_lex.context.resolve_in_select_list= TRUE;
/*
We don't have to test for permissions as this is already done during
HANDLER OPEN
*/
if (!(ha_table= mysql_ha_read_prepare(thd, tables, lex->ha_read_mode,
lex->ident.str,
lex->insert_list,
lex->select_lex.where)))
DBUG_RETURN(1);
if (!stmt->is_sql_prepare())
{
if (!lex->result && !(lex->result= new (stmt->mem_root) select_send))
{
my_error(ER_OUTOFMEMORY, MYF(0), sizeof(select_send));
DBUG_RETURN(1);
}
if (send_prep_stmt(stmt, ha_table->fields.elements) ||
lex->result->send_fields(ha_table->fields, Protocol::SEND_EOF) ||
thd->protocol->flush())
DBUG_RETURN(1);
DBUG_RETURN(2);
}
DBUG_RETURN(0);
}
/**
Perform semantic analysis of the parsed tree and send a response packet
@ -1949,6 +2002,14 @@ static bool check_prepared_statement(Prepared_statement *stmt)
res= mysql_test_insert_select(stmt, tables);
break;
case SQLCOM_HA_READ:
res= mysql_test_handler_read(stmt, tables);
{
/* Statement and field info has already been sent */
DBUG_RETURN(FALSE);
}
break;
/*
Note that we don't need to have cases in this list if they are
marked with CF_STATUS_COMMAND in sql_command_flags

View file

@ -19,7 +19,7 @@
#include "mysql_priv.h"
#include "sql_trigger.h"
#include "sql_handler.h"
static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list,
bool skip_error);

View file

@ -1942,8 +1942,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
pthread_mutex_lock(&mysys_var->mutex);
thd_info->proc_info= (char*) (tmp->killed == THD::KILL_CONNECTION? "Killed" : 0);
#ifndef EMBEDDED_LIBRARY
thd_info->state_info= (char*) (tmp->locked ? "Locked" :
tmp->net.reading_or_writing ?
thd_info->state_info= (char*) (tmp->net.reading_or_writing ?
(tmp->net.reading_or_writing == 2 ?
"Writing to net" :
thd_info->command == COM_SLEEP ? "" :
@ -2068,8 +2067,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
table->field[5]->store(utime / 1000000, TRUE);
/* STATE */
#ifndef EMBEDDED_LIBRARY
val= (char*) (tmp->locked ? "Locked" :
tmp->net.reading_or_writing ?
val= (char*) (tmp->net.reading_or_writing ?
(tmp->net.reading_or_writing == 2 ?
"Writing to net" :
tmp->command == COM_SLEEP ? "" :

View file

@ -24,6 +24,7 @@
#include "sql_trigger.h"
#include "sql_show.h"
#include "debug_sync.h"
#include "sql_handler.h"
#ifdef __WIN__
#include <io.h>

View file

@ -19,6 +19,7 @@
#include "sp_head.h"
#include "sql_trigger.h"
#include "parse_file.h"
#include "sql_handler.h"
/*************************************************************************/