Commit 6d277eb8 authored by konstantin@mysql.com's avatar konstantin@mysql.com

Merge mysql.com:/opt/local/work/tmp_merge

into  mysql.com:/opt/local/work/mysql-5.1-merge
parents eee166d8 c49e6f88
...@@ -108,6 +108,9 @@ set @fvar= 123.4567; ...@@ -108,6 +108,9 @@ set @fvar= 123.4567;
prepare stmt1 from @fvar; prepare stmt1 from @fvar;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '123.4567' at line 1 ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '123.4567' at line 1
drop table t1,t2; drop table t1,t2;
deallocate prepare stmt3;
deallocate prepare stmt4;
deallocate prepare stmt5;
PREPARE stmt1 FROM "select _utf8 'A' collate utf8_bin = ?"; PREPARE stmt1 FROM "select _utf8 'A' collate utf8_bin = ?";
set @var='A'; set @var='A';
EXECUTE stmt1 USING @var; EXECUTE stmt1 USING @var;
...@@ -253,6 +256,7 @@ set names latin1; ...@@ -253,6 +256,7 @@ set names latin1;
execute ``; execute ``;
1234 1234
1234 1234
deallocate prepare ``;
set names default; set names default;
create table t1 (a varchar(10)) charset=utf8; create table t1 (a varchar(10)) charset=utf8;
insert into t1 (a) values ('yahoo'); insert into t1 (a) values ('yahoo');
...@@ -781,6 +785,7 @@ EXECUTE b12651; ...@@ -781,6 +785,7 @@ EXECUTE b12651;
1 1
DROP VIEW b12651_V1; DROP VIEW b12651_V1;
DROP TABLE b12651_T1, b12651_T2; DROP TABLE b12651_T1, b12651_T2;
DEALLOCATE PREPARE b12651;
prepare stmt from "select @@time_zone"; prepare stmt from "select @@time_zone";
execute stmt; execute stmt;
@@time_zone @@time_zone
...@@ -873,6 +878,130 @@ length(a) ...@@ -873,6 +878,130 @@ length(a)
10 10
drop table t1; drop table t1;
deallocate prepare stmt; deallocate prepare stmt;
create table t1 (col1 integer, col2 integer);
insert into t1 values(100,100),(101,101),(102,102),(103,103);
prepare stmt from 'select col1, col2 from t1 where (col1, col2) in ((?,?))';
set @a=100, @b=100;
execute stmt using @a,@b;
col1 col2
100 100
set @a=101, @b=101;
execute stmt using @a,@b;
col1 col2
101 101
set @a=102, @b=102;
execute stmt using @a,@b;
col1 col2
102 102
set @a=102, @b=103;
execute stmt using @a,@b;
col1 col2
deallocate prepare stmt;
drop table t1;
set @old_max_prepared_stmt_count= @@max_prepared_stmt_count;
show variables like 'max_prepared_stmt_count';
Variable_name Value
max_prepared_stmt_count 16382
show variables like 'prepared_stmt_count';
Variable_name Value
prepared_stmt_count 0
select @@max_prepared_stmt_count, @@prepared_stmt_count;
@@max_prepared_stmt_count @@prepared_stmt_count
16382 0
set global max_prepared_stmt_count=-1;
select @@max_prepared_stmt_count;
@@max_prepared_stmt_count
0
set global max_prepared_stmt_count=10000000000000000;
select @@max_prepared_stmt_count;
@@max_prepared_stmt_count
1048576
set global max_prepared_stmt_count=default;
select @@max_prepared_stmt_count;
@@max_prepared_stmt_count
16382
set @@max_prepared_stmt_count=1;
ERROR HY000: Variable 'max_prepared_stmt_count' is a GLOBAL variable and should be set with SET GLOBAL
set max_prepared_stmt_count=1;
ERROR HY000: Variable 'max_prepared_stmt_count' is a GLOBAL variable and should be set with SET GLOBAL
set local max_prepared_stmt_count=1;
ERROR HY000: Variable 'max_prepared_stmt_count' is a GLOBAL variable and should be set with SET GLOBAL
set local prepared_stmt_count=0;
ERROR HY000: Variable 'prepared_stmt_count' is a read only variable
set @@prepared_stmt_count=0;
ERROR HY000: Variable 'prepared_stmt_count' is a read only variable
set global prepared_stmt_count=1;
ERROR HY000: Variable 'prepared_stmt_count' is a read only variable
set global max_prepared_stmt_count=1;
select @@max_prepared_stmt_count;
@@max_prepared_stmt_count
1
set global max_prepared_stmt_count=0;
select @@max_prepared_stmt_count, @@prepared_stmt_count;
@@max_prepared_stmt_count @@prepared_stmt_count
0 0
prepare stmt from "select 1";
ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 0)
select @@prepared_stmt_count;
@@prepared_stmt_count
0
set global max_prepared_stmt_count=1;
prepare stmt from "select 1";
select @@prepared_stmt_count;
@@prepared_stmt_count
1
prepare stmt1 from "select 1";
ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 1)
select @@prepared_stmt_count;
@@prepared_stmt_count
1
deallocate prepare stmt;
select @@prepared_stmt_count;
@@prepared_stmt_count
0
prepare stmt from "select 1";
select @@prepared_stmt_count;
@@prepared_stmt_count
1
prepare stmt from "select 2";
select @@prepared_stmt_count;
@@prepared_stmt_count
1
select @@prepared_stmt_count, @@max_prepared_stmt_count;
@@prepared_stmt_count @@max_prepared_stmt_count
1 1
set global max_prepared_stmt_count=0;
prepare stmt from "select 1";
ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 0)
execute stmt;
ERROR HY000: Unknown prepared statement handler (stmt) given to EXECUTE
select @@prepared_stmt_count;
@@prepared_stmt_count
0
prepare stmt from "select 1";
ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 0)
select @@prepared_stmt_count;
@@prepared_stmt_count
0
set global max_prepared_stmt_count=3;
select @@max_prepared_stmt_count, @@prepared_stmt_count;
@@max_prepared_stmt_count @@prepared_stmt_count
3 0
prepare stmt from "select 1";
prepare stmt from "select 2";
prepare stmt1 from "select 3";
prepare stmt2 from "select 4";
ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 3)
prepare stmt2 from "select 4";
ERROR 42000: Can't create more than max_prepared_stmt_count statements (current value: 3)
select @@max_prepared_stmt_count, @@prepared_stmt_count;
@@max_prepared_stmt_count @@prepared_stmt_count
3 3
deallocate prepare stmt;
select @@max_prepared_stmt_count, @@prepared_stmt_count;
@@max_prepared_stmt_count @@prepared_stmt_count
3 0
set global max_prepared_stmt_count= @old_max_prepared_stmt_count;
create table t1 (id int); create table t1 (id int);
prepare ins_call from "insert into t1 (id) values (1)"; prepare ins_call from "insert into t1 (id) values (1)";
execute ins_call; execute ins_call;
...@@ -883,6 +1012,7 @@ drop table t1; ...@@ -883,6 +1012,7 @@ drop table t1;
create table t1 (a int, b int); create table t1 (a int, b int);
insert into t1 (a,b) values (2,8),(1,9),(3,7); insert into t1 (a,b) values (2,8),(1,9),(3,7);
prepare stmt from "select * from t1 order by ?"; prepare stmt from "select * from t1 order by ?";
set @a=NULL;
execute stmt using @a; execute stmt using @a;
a b a b
2 8 2 8
......
...@@ -114,6 +114,9 @@ set @fvar= 123.4567; ...@@ -114,6 +114,9 @@ set @fvar= 123.4567;
prepare stmt1 from @fvar; prepare stmt1 from @fvar;
drop table t1,t2; drop table t1,t2;
deallocate prepare stmt3;
deallocate prepare stmt4;
deallocate prepare stmt5;
# #
# Bug #4105: Server crash on attempt to prepare a statement with character # Bug #4105: Server crash on attempt to prepare a statement with character
...@@ -257,6 +260,7 @@ prepare `ü` from 'select 1234'; ...@@ -257,6 +260,7 @@ prepare `ü` from 'select 1234';
execute `ü` ; execute `ü` ;
set names latin1; set names latin1;
execute ``; execute ``;
deallocate prepare ``;
set names default; set names default;
...@@ -823,6 +827,7 @@ EXECUTE b12651; ...@@ -823,6 +827,7 @@ EXECUTE b12651;
DROP VIEW b12651_V1; DROP VIEW b12651_V1;
DROP TABLE b12651_T1, b12651_T2; DROP TABLE b12651_T1, b12651_T2;
DEALLOCATE PREPARE b12651;
# #
# Bug#9359 "Prepared statements take snapshot of system vars at PREPARE # Bug#9359 "Prepared statements take snapshot of system vars at PREPARE
...@@ -921,6 +926,143 @@ select length(a) from t1; ...@@ -921,6 +926,143 @@ select length(a) from t1;
drop table t1; drop table t1;
deallocate prepare stmt; deallocate prepare stmt;
#
# Bug#16248 "WHERE (col1,col2) IN ((?,?)) gives wrong results":
# check that ROW implementation is reexecution-friendly.
#
create table t1 (col1 integer, col2 integer);
insert into t1 values(100,100),(101,101),(102,102),(103,103);
prepare stmt from 'select col1, col2 from t1 where (col1, col2) in ((?,?))';
set @a=100, @b=100;
execute stmt using @a,@b;
set @a=101, @b=101;
execute stmt using @a,@b;
set @a=102, @b=102;
execute stmt using @a,@b;
set @a=102, @b=103;
execute stmt using @a,@b;
deallocate prepare stmt;
drop table t1;
#
# Bug#16365 Prepared Statements: DoS with too many open statements
# Check that the limit @@max_prpeared_stmt_count works.
#
# Save the old value
set @old_max_prepared_stmt_count= @@max_prepared_stmt_count;
#
# Disable prepared statement protocol: in this test we set
# @@max_prepared_stmt_count to 0 or 1 and would like to test the limit
# manually.
#
--disable_ps_protocol
#
# A. Check that the new variables are present in SHOW VARIABLES list.
#
show variables like 'max_prepared_stmt_count';
show variables like 'prepared_stmt_count';
#
# B. Check that the new variables are selectable.
#
select @@max_prepared_stmt_count, @@prepared_stmt_count;
#
# C. Check that max_prepared_stmt_count is settable (global only),
# whereas prepared_stmt_count is readonly.
#
set global max_prepared_stmt_count=-1;
select @@max_prepared_stmt_count;
set global max_prepared_stmt_count=10000000000000000;
select @@max_prepared_stmt_count;
set global max_prepared_stmt_count=default;
select @@max_prepared_stmt_count;
--error ER_GLOBAL_VARIABLE
set @@max_prepared_stmt_count=1;
--error ER_GLOBAL_VARIABLE
set max_prepared_stmt_count=1;
--error ER_GLOBAL_VARIABLE
set local max_prepared_stmt_count=1;
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
set local prepared_stmt_count=0;
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
set @@prepared_stmt_count=0;
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
set global prepared_stmt_count=1;
# set to a reasonable limit works
set global max_prepared_stmt_count=1;
select @@max_prepared_stmt_count;
#
# D. Check that the variables actually work.
#
set global max_prepared_stmt_count=0;
select @@max_prepared_stmt_count, @@prepared_stmt_count;
--error ER_MAX_PREPARED_STMT_COUNT_REACHED
prepare stmt from "select 1";
select @@prepared_stmt_count;
set global max_prepared_stmt_count=1;
prepare stmt from "select 1";
select @@prepared_stmt_count;
--error ER_MAX_PREPARED_STMT_COUNT_REACHED
prepare stmt1 from "select 1";
select @@prepared_stmt_count;
deallocate prepare stmt;
select @@prepared_stmt_count;
#
# E. Check that we can prepare a statement with the same name
# successfully, without hitting the limit.
#
prepare stmt from "select 1";
select @@prepared_stmt_count;
prepare stmt from "select 2";
select @@prepared_stmt_count;
#
# F. We can set the max below the current count. In this case no new
# statements should be allowed to prepare.
#
select @@prepared_stmt_count, @@max_prepared_stmt_count;
set global max_prepared_stmt_count=0;
--error ER_MAX_PREPARED_STMT_COUNT_REACHED
prepare stmt from "select 1";
# Result: the old statement is deallocated, the new is not created.
--error 1243 # ER_UNKNOWN_STMT_HANDLER
execute stmt;
select @@prepared_stmt_count;
--error ER_MAX_PREPARED_STMT_COUNT_REACHED
prepare stmt from "select 1";
select @@prepared_stmt_count;
#
# G. Show that the variables are up to date even after a connection with all
# statements in it was terminated.
#
set global max_prepared_stmt_count=3;
select @@max_prepared_stmt_count, @@prepared_stmt_count;
prepare stmt from "select 1";
connect (con1,localhost,root,,);
connection con1;
prepare stmt from "select 2";
prepare stmt1 from "select 3";
--error ER_MAX_PREPARED_STMT_COUNT_REACHED
prepare stmt2 from "select 4";
connection default;
--error ER_MAX_PREPARED_STMT_COUNT_REACHED
prepare stmt2 from "select 4";
select @@max_prepared_stmt_count, @@prepared_stmt_count;
disconnect con1;
connection default;
# Wait for the connection to die: deal with a possible race
deallocate prepare stmt;
let $count= `select @@prepared_stmt_count`;
if ($count)
{
--sleep 2
let $count= `select @@prepared_stmt_count`;
}
select @@max_prepared_stmt_count, @@prepared_stmt_count;
#
# Restore the old value.
#
set global max_prepared_stmt_count= @old_max_prepared_stmt_count;
--enable_ps_protocol
# End of 4.1 tests # End of 4.1 tests
# #
...@@ -946,6 +1088,7 @@ insert into t1 (a,b) values (2,8),(1,9),(3,7); ...@@ -946,6 +1088,7 @@ insert into t1 (a,b) values (2,8),(1,9),(3,7);
# Will order by index # Will order by index
prepare stmt from "select * from t1 order by ?"; prepare stmt from "select * from t1 order by ?";
set @a=NULL;
execute stmt using @a; execute stmt using @a;
set @a=1; set @a=1;
execute stmt using @a; execute stmt using @a;
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
*/ */
Item_row::Item_row(List<Item> &arg): Item_row::Item_row(List<Item> &arg):
Item(), used_tables_cache(0), array_holder(1), const_item_cache(1), with_null(0) Item(), used_tables_cache(0), const_item_cache(1), with_null(0)
{ {
//TODO: think placing 2-3 component items in item (as it done for function) //TODO: think placing 2-3 component items in item (as it done for function)
...@@ -85,6 +85,20 @@ bool Item_row::fix_fields(THD *thd, Item **ref) ...@@ -85,6 +85,20 @@ bool Item_row::fix_fields(THD *thd, Item **ref)
} }
void Item_row::cleanup()
{
DBUG_ENTER("Item_row::cleanup");
Item::cleanup();
/* Reset to the original values */
used_tables_cache= 0;
const_item_cache= 1;
with_null= 0;
DBUG_VOID_RETURN;
}
void Item_row::split_sum_func(THD *thd, Item **ref_pointer_array, void Item_row::split_sum_func(THD *thd, Item **ref_pointer_array,
List<Item> &fields) List<Item> &fields)
{ {
......
...@@ -19,7 +19,6 @@ class Item_row: public Item ...@@ -19,7 +19,6 @@ class Item_row: public Item
Item **items; Item **items;
table_map used_tables_cache; table_map used_tables_cache;
uint arg_count; uint arg_count;
bool array_holder;
bool const_item_cache; bool const_item_cache;
bool with_null; bool with_null;
public: public:
...@@ -29,7 +28,6 @@ public: ...@@ -29,7 +28,6 @@ public:
items(item->items), items(item->items),
used_tables_cache(item->used_tables_cache), used_tables_cache(item->used_tables_cache),
arg_count(item->arg_count), arg_count(item->arg_count),
array_holder(0),
const_item_cache(item->const_item_cache), const_item_cache(item->const_item_cache),
with_null(0) with_null(0)
{} {}
...@@ -62,6 +60,7 @@ public: ...@@ -62,6 +60,7 @@ public:
return 0; return 0;
}; };
bool fix_fields(THD *thd, Item **ref); bool fix_fields(THD *thd, Item **ref);
void cleanup();
void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields); void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields);
table_map used_tables() const { return used_tables_cache; }; table_map used_tables() const { return used_tables_cache; };
bool const_item() const { return const_item_cache; }; bool const_item() const { return const_item_cache; };
......
...@@ -1296,6 +1296,7 @@ extern ulong slave_net_timeout, slave_trans_retries; ...@@ -1296,6 +1296,7 @@ extern ulong slave_net_timeout, slave_trans_retries;
extern uint max_user_connections; extern uint max_user_connections;
extern ulong what_to_log,flush_time; extern ulong what_to_log,flush_time;
extern ulong query_buff_size, thread_stack; extern ulong query_buff_size, thread_stack;
extern ulong max_prepared_stmt_count, prepared_stmt_count;
extern ulong binlog_cache_size, max_binlog_cache_size, open_files_limit; extern ulong binlog_cache_size, max_binlog_cache_size, open_files_limit;
extern ulong max_binlog_size, max_relay_log_size; extern ulong max_binlog_size, max_relay_log_size;
#ifdef HAVE_ROW_BASED_REPLICATION #ifdef HAVE_ROW_BASED_REPLICATION
...@@ -1350,6 +1351,7 @@ extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open, LOCK_lock_db, ...@@ -1350,6 +1351,7 @@ extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open, LOCK_lock_db,
LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone, LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_global_read_lock, LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_global_read_lock,
LOCK_global_system_variables, LOCK_user_conn, LOCK_global_system_variables, LOCK_user_conn,
LOCK_prepared_stmt_count,
LOCK_bytes_sent, LOCK_bytes_received; LOCK_bytes_sent, LOCK_bytes_received;
#ifdef HAVE_OPENSSL #ifdef HAVE_OPENSSL
extern pthread_mutex_t LOCK_des_key_file; extern pthread_mutex_t LOCK_des_key_file;
......
...@@ -500,6 +500,22 @@ ulong specialflag=0; ...@@ -500,6 +500,22 @@ ulong specialflag=0;
ulong binlog_cache_use= 0, binlog_cache_disk_use= 0; ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
ulong max_connections, max_connect_errors; ulong max_connections, max_connect_errors;
uint max_user_connections= 0; uint max_user_connections= 0;
/*
Limit of the total number of prepared statements in the server.
Is necessary to protect the server against out-of-memory attacks.
*/
ulong max_prepared_stmt_count;
/*
Current total number of prepared statements in the server. This number
is exact, and therefore may not be equal to the difference between
`com_stmt_prepare' and `com_stmt_close' (global status variables), as
the latter ones account for all registered attempts to prepare
a statement (including unsuccessful ones). Prepared statements are
currently connection-local: if the same SQL query text is prepared in
two different connections, this counts as two distinct prepared
statements.
*/
ulong prepared_stmt_count=0;
ulong thread_id=1L,current_pid; ulong thread_id=1L,current_pid;
ulong slow_launch_threads = 0, sync_binlog_period; ulong slow_launch_threads = 0, sync_binlog_period;
ulong expire_logs_days = 0; ulong expire_logs_days = 0;
...@@ -577,6 +593,14 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count, ...@@ -577,6 +593,14 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count,
LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received, LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received,
LOCK_global_system_variables, LOCK_global_system_variables,
LOCK_user_conn, LOCK_slave_list, LOCK_active_mi; LOCK_user_conn, LOCK_slave_list, LOCK_active_mi;
/*
The below lock protects access to two global server variables:
max_prepared_stmt_count and prepared_stmt_count. These variables
set the limit and hold the current total number of prepared statements
in the server, respectively. As PREPARE/DEALLOCATE rate in a loaded
server may be fairly high, we need a dedicated lock.
*/
pthread_mutex_t LOCK_prepared_stmt_count;
#ifdef HAVE_OPENSSL #ifdef HAVE_OPENSSL
pthread_mutex_t LOCK_des_key_file; pthread_mutex_t LOCK_des_key_file;
#endif #endif
...@@ -1288,6 +1312,7 @@ static void clean_up_mutexes() ...@@ -1288,6 +1312,7 @@ static void clean_up_mutexes()
(void) pthread_mutex_destroy(&LOCK_global_system_variables); (void) pthread_mutex_destroy(&LOCK_global_system_variables);
(void) pthread_mutex_destroy(&LOCK_global_read_lock); (void) pthread_mutex_destroy(&LOCK_global_read_lock);
(void) pthread_mutex_destroy(&LOCK_uuid_generator); (void) pthread_mutex_destroy(&LOCK_uuid_generator);
(void) pthread_mutex_destroy(&LOCK_prepared_stmt_count);
(void) pthread_cond_destroy(&COND_thread_count); (void) pthread_cond_destroy(&COND_thread_count);
(void) pthread_cond_destroy(&COND_refresh); (void) pthread_cond_destroy(&COND_refresh);
(void) pthread_cond_destroy(&COND_thread_cache); (void) pthread_cond_destroy(&COND_thread_cache);
...@@ -2810,6 +2835,7 @@ static int init_thread_environment() ...@@ -2810,6 +2835,7 @@ static int init_thread_environment()
(void) pthread_mutex_init(&LOCK_active_mi, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_active_mi, MY_MUTEX_INIT_FAST);
(void) pthread_mutex_init(&LOCK_global_system_variables, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_global_system_variables, MY_MUTEX_INIT_FAST);
(void) pthread_mutex_init(&LOCK_global_read_lock, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_global_read_lock, MY_MUTEX_INIT_FAST);
(void) pthread_mutex_init(&LOCK_prepared_stmt_count, MY_MUTEX_INIT_FAST);
(void) pthread_mutex_init(&LOCK_uuid_generator, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_uuid_generator, MY_MUTEX_INIT_FAST);
#ifdef HAVE_OPENSSL #ifdef HAVE_OPENSSL
(void) pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST);
...@@ -4634,7 +4660,8 @@ enum options_mysqld ...@@ -4634,7 +4660,8 @@ enum options_mysqld
OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE, OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE,
OPT_MAX_CONNECTIONS, OPT_MAX_CONNECT_ERRORS, OPT_MAX_CONNECTIONS, OPT_MAX_CONNECT_ERRORS,
OPT_MAX_DELAYED_THREADS, OPT_MAX_HEP_TABLE_SIZE, OPT_MAX_DELAYED_THREADS, OPT_MAX_HEP_TABLE_SIZE,
OPT_MAX_JOIN_SIZE, OPT_MAX_RELAY_LOG_SIZE, OPT_MAX_SORT_LENGTH, OPT_MAX_JOIN_SIZE, OPT_MAX_PREPARED_STMT_COUNT,
OPT_MAX_RELAY_LOG_SIZE, OPT_MAX_SORT_LENGTH,
OPT_MAX_SEEKS_FOR_KEY, OPT_MAX_TMP_TABLES, OPT_MAX_USER_CONNECTIONS, OPT_MAX_SEEKS_FOR_KEY, OPT_MAX_TMP_TABLES, OPT_MAX_USER_CONNECTIONS,
OPT_MAX_LENGTH_FOR_SORT_DATA, OPT_MAX_LENGTH_FOR_SORT_DATA,
OPT_MAX_WRITE_LOCK_COUNT, OPT_BULK_INSERT_BUFFER_SIZE, OPT_MAX_WRITE_LOCK_COUNT, OPT_BULK_INSERT_BUFFER_SIZE,
...@@ -5890,6 +5917,10 @@ The minimum value for this variable is 4096.", ...@@ -5890,6 +5917,10 @@ The minimum value for this variable is 4096.",
(gptr*) &global_system_variables.max_length_for_sort_data, (gptr*) &global_system_variables.max_length_for_sort_data,
(gptr*) &max_system_variables.max_length_for_sort_data, 0, GET_ULONG, (gptr*) &max_system_variables.max_length_for_sort_data, 0, GET_ULONG,
REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0}, REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0},
{"max_prepared_stmt_count", OPT_MAX_PREPARED_STMT_COUNT,
"Maximum numbrer of prepared statements in the server.",
(gptr*) &max_prepared_stmt_count, (gptr*) &max_prepared_stmt_count,
0, GET_ULONG, REQUIRED_ARG, 16382, 0, 1*1024*1024, 0, 1, 0},
{"max_relay_log_size", OPT_MAX_RELAY_LOG_SIZE, {"max_relay_log_size", OPT_MAX_RELAY_LOG_SIZE,
"If non-zero: relay log will be rotated automatically when the size exceeds this value; if zero (the default): when the size exceeds max_binlog_size. 0 excepted, the minimum value for this variable is 4096.", "If non-zero: relay log will be rotated automatically when the size exceeds this value; if zero (the default): when the size exceeds max_binlog_size. 0 excepted, the minimum value for this variable is 4096.",
(gptr*) &max_relay_log_size, (gptr*) &max_relay_log_size, 0, GET_ULONG, (gptr*) &max_relay_log_size, (gptr*) &max_relay_log_size, 0, GET_ULONG,
......
...@@ -161,6 +161,7 @@ static KEY_CACHE *create_key_cache(const char *name, uint length); ...@@ -161,6 +161,7 @@ static KEY_CACHE *create_key_cache(const char *name, uint length);
void fix_sql_mode_var(THD *thd, enum_var_type type); void fix_sql_mode_var(THD *thd, enum_var_type type);
static byte *get_error_count(THD *thd); static byte *get_error_count(THD *thd);
static byte *get_warning_count(THD *thd); static byte *get_warning_count(THD *thd);
static byte *get_prepared_stmt_count(THD *thd);
/* /*
Variable definition list Variable definition list
...@@ -311,6 +312,10 @@ sys_var_thd_ha_rows sys_sql_max_join_size("sql_max_join_size", ...@@ -311,6 +312,10 @@ sys_var_thd_ha_rows sys_sql_max_join_size("sql_max_join_size",
&SV::max_join_size, &SV::max_join_size,
fix_max_join_size); fix_max_join_size);
#endif #endif
static sys_var_long_ptr_global
sys_max_prepared_stmt_count("max_prepared_stmt_count",
&max_prepared_stmt_count,
&LOCK_prepared_stmt_count);
sys_var_long_ptr sys_max_relay_log_size("max_relay_log_size", sys_var_long_ptr sys_max_relay_log_size("max_relay_log_size",
&max_relay_log_size, &max_relay_log_size,
fix_max_relay_log_size); fix_max_relay_log_size);
...@@ -604,6 +609,9 @@ static sys_var_readonly sys_warning_count("warning_count", ...@@ -604,6 +609,9 @@ static sys_var_readonly sys_warning_count("warning_count",
OPT_SESSION, OPT_SESSION,
SHOW_LONG, SHOW_LONG,
get_warning_count); get_warning_count);
static sys_var_readonly sys_prepared_stmt_count("prepared_stmt_count",
OPT_GLOBAL, SHOW_LONG,
get_prepared_stmt_count);
/* alias for last_insert_id() to be compatible with Sybase */ /* alias for last_insert_id() to be compatible with Sybase */
#ifdef HAVE_REPLICATION #ifdef HAVE_REPLICATION
...@@ -847,6 +855,8 @@ SHOW_VAR init_vars[]= { ...@@ -847,6 +855,8 @@ SHOW_VAR init_vars[]= {
{sys_max_join_size.name, (char*) &sys_max_join_size, SHOW_SYS}, {sys_max_join_size.name, (char*) &sys_max_join_size, SHOW_SYS},
{sys_max_length_for_sort_data.name, (char*) &sys_max_length_for_sort_data, {sys_max_length_for_sort_data.name, (char*) &sys_max_length_for_sort_data,
SHOW_SYS}, SHOW_SYS},
{sys_max_prepared_stmt_count.name, (char*) &sys_max_prepared_stmt_count,
SHOW_SYS},
{sys_max_relay_log_size.name, (char*) &sys_max_relay_log_size, SHOW_SYS}, {sys_max_relay_log_size.name, (char*) &sys_max_relay_log_size, SHOW_SYS},
{sys_max_seeks_for_key.name, (char*) &sys_max_seeks_for_key, SHOW_SYS}, {sys_max_seeks_for_key.name, (char*) &sys_max_seeks_for_key, SHOW_SYS},
{sys_max_sort_length.name, (char*) &sys_max_sort_length, SHOW_SYS}, {sys_max_sort_length.name, (char*) &sys_max_sort_length, SHOW_SYS},
...@@ -900,6 +910,7 @@ SHOW_VAR init_vars[]= { ...@@ -900,6 +910,7 @@ SHOW_VAR init_vars[]= {
SHOW_SYS}, SHOW_SYS},
{"pid_file", (char*) pidfile_name, SHOW_CHAR}, {"pid_file", (char*) pidfile_name, SHOW_CHAR},
{"plugin_dir", (char*) opt_plugin_dir, SHOW_CHAR}, {"plugin_dir", (char*) opt_plugin_dir, SHOW_CHAR},
{sys_prepared_stmt_count.name, (char*) &sys_prepared_stmt_count, SHOW_SYS},
{"port", (char*) &mysqld_port, SHOW_INT}, {"port", (char*) &mysqld_port, SHOW_INT},
{sys_preload_buff_size.name, (char*) &sys_preload_buff_size, SHOW_SYS}, {sys_preload_buff_size.name, (char*) &sys_preload_buff_size, SHOW_SYS},
{"protocol_version", (char*) &protocol_version, SHOW_INT}, {"protocol_version", (char*) &protocol_version, SHOW_INT},
...@@ -1367,29 +1378,40 @@ static void fix_server_id(THD *thd, enum_var_type type) ...@@ -1367,29 +1378,40 @@ static void fix_server_id(THD *thd, enum_var_type type)
server_id_supplied = 1; server_id_supplied = 1;
} }
bool sys_var_long_ptr::check(THD *thd, set_var *var)
sys_var_long_ptr::
sys_var_long_ptr(const char *name_arg, ulong *value_ptr,
sys_after_update_func after_update_arg)
:sys_var_long_ptr_global(name_arg, value_ptr,
&LOCK_global_system_variables, after_update_arg)
{}
bool sys_var_long_ptr_global::check(THD *thd, set_var *var)
{ {
longlong v= var->value->val_int(); longlong v= var->value->val_int();
var->save_result.ulonglong_value= v < 0 ? 0 : v; var->save_result.ulonglong_value= v < 0 ? 0 : v;
return 0; return 0;
} }
bool sys_var_long_ptr::update(THD *thd, set_var *var) bool sys_var_long_ptr_global::update(THD *thd, set_var *var)
{ {
ulonglong tmp= var->save_result.ulonglong_value; ulonglong tmp= var->save_result.ulonglong_value;
pthread_mutex_lock(&LOCK_global_system_variables); pthread_mutex_lock(guard);
if (option_limits) if (option_limits)
*value= (ulong) getopt_ull_limit_value(tmp, option_limits); *value= (ulong) getopt_ull_limit_value(tmp, option_limits);
else else
*value= (ulong) tmp; *value= (ulong) tmp;
pthread_mutex_unlock(&LOCK_global_system_variables); pthread_mutex_unlock(guard);
return 0; return 0;
} }
void sys_var_long_ptr::set_default(THD *thd, enum_var_type type) void sys_var_long_ptr_global::set_default(THD *thd, enum_var_type type)
{ {
pthread_mutex_lock(guard);
*value= (ulong) option_limits->def_value; *value= (ulong) option_limits->def_value;
pthread_mutex_unlock(guard);
} }
...@@ -2824,6 +2846,13 @@ static byte *get_error_count(THD *thd) ...@@ -2824,6 +2846,13 @@ static byte *get_error_count(THD *thd)
return (byte*) &thd->sys_var_tmp.long_value; return (byte*) &thd->sys_var_tmp.long_value;
} }
static byte *get_prepared_stmt_count(THD *thd)
{
pthread_mutex_lock(&LOCK_prepared_stmt_count);
thd->sys_var_tmp.ulong_value= prepared_stmt_count;
pthread_mutex_unlock(&LOCK_prepared_stmt_count);
return (byte*) &thd->sys_var_tmp.ulong_value;
}
/**************************************************************************** /****************************************************************************
Main handling of variables: Main handling of variables:
......
...@@ -48,11 +48,7 @@ public: ...@@ -48,11 +48,7 @@ public:
sys_after_update_func after_update; sys_after_update_func after_update;
bool no_support_one_shot; bool no_support_one_shot;
sys_var(const char *name_arg) sys_var(const char *name_arg,sys_after_update_func func= NULL)
:name(name_arg), after_update(0)
, no_support_one_shot(1)
{ add_sys_var(); }
sys_var(const char *name_arg,sys_after_update_func func)
:name(name_arg), after_update(func) :name(name_arg), after_update(func)
, no_support_one_shot(1) , no_support_one_shot(1)
{ add_sys_var(); } { add_sys_var(); }
...@@ -83,15 +79,35 @@ public: ...@@ -83,15 +79,35 @@ public:
}; };
class sys_var_long_ptr :public sys_var /*
A base class for all variables that require its access to
be guarded with a mutex.
*/
class sys_var_global: public sys_var
{
protected:
pthread_mutex_t *guard;
public:
sys_var_global(const char *name_arg, sys_after_update_func after_update_arg,
pthread_mutex_t *guard_arg)
:sys_var(name_arg, after_update_arg), guard(guard_arg) {}
};
/*
A global-only ulong variable that requires its access to be
protected with a mutex.
*/
class sys_var_long_ptr_global: public sys_var_global
{ {
public: public:
ulong *value; ulong *value;
sys_var_long_ptr(const char *name_arg, ulong *value_ptr) sys_var_long_ptr_global(const char *name_arg, ulong *value_ptr,
:sys_var(name_arg),value(value_ptr) {} pthread_mutex_t *guard_arg,
sys_var_long_ptr(const char *name_arg, ulong *value_ptr, sys_after_update_func after_update_arg= NULL)
sys_after_update_func func) :sys_var_global(name_arg, after_update_arg, guard_arg), value(value_ptr) {}
:sys_var(name_arg,func), value(value_ptr) {}
bool check(THD *thd, set_var *var); bool check(THD *thd, set_var *var);
bool update(THD *thd, set_var *var); bool update(THD *thd, set_var *var);
void set_default(THD *thd, enum_var_type type); void set_default(THD *thd, enum_var_type type);
...@@ -101,6 +117,18 @@ public: ...@@ -101,6 +117,18 @@ public:
}; };
/*
A global ulong variable that is protected by LOCK_global_system_variables
*/
class sys_var_long_ptr :public sys_var_long_ptr_global
{
public:
sys_var_long_ptr(const char *name_arg, ulong *value_ptr,
sys_after_update_func after_update_arg= NULL);
};
class sys_var_ulonglong_ptr :public sys_var class sys_var_ulonglong_ptr :public sys_var
{ {
public: public:
...@@ -179,7 +207,7 @@ class sys_var_const_str :public sys_var ...@@ -179,7 +207,7 @@ class sys_var_const_str :public sys_var
public: public:
char *value; // Pointer to const value char *value; // Pointer to const value
sys_var_const_str(const char *name_arg, const char *value_arg) sys_var_const_str(const char *name_arg, const char *value_arg)
:sys_var(name_arg), value((char*) value_arg) :sys_var(name_arg),value((char*) value_arg)
{} {}
bool check(THD *thd, set_var *var) bool check(THD *thd, set_var *var)
{ {
...@@ -226,10 +254,7 @@ public: ...@@ -226,10 +254,7 @@ public:
class sys_var_thd :public sys_var class sys_var_thd :public sys_var
{ {
public: public:
sys_var_thd(const char *name_arg) sys_var_thd(const char *name_arg, sys_after_update_func func= NULL)
:sys_var(name_arg)
{}
sys_var_thd(const char *name_arg, sys_after_update_func func)
:sys_var(name_arg,func) :sys_var(name_arg,func)
{} {}
bool check_type(enum_var_type type) { return 0; } bool check_type(enum_var_type type) { return 0; }
......
...@@ -5832,3 +5832,5 @@ ER_NULL_IN_VALUES_LESS_THAN ...@@ -5832,3 +5832,5 @@ ER_NULL_IN_VALUES_LESS_THAN
ER_WRONG_PARTITION_NAME ER_WRONG_PARTITION_NAME
eng "Incorrect partition name" eng "Incorrect partition name"
swe "Felaktigt partitionsnamn" swe "Felaktigt partitionsnamn"
ER_MAX_PREPARED_STMT_COUNT_REACHED 42000
eng "Can't create more than max_prepared_stmt_count statements (current value: %lu)"
...@@ -447,7 +447,7 @@ THD::~THD() ...@@ -447,7 +447,7 @@ THD::~THD()
net_end(&net); net_end(&net);
} }
#endif #endif
stmt_map.destroy(); /* close all prepared statements */ stmt_map.reset(); /* close all prepared statements */
DBUG_ASSERT(lock_info.n_cursors == 0); DBUG_ASSERT(lock_info.n_cursors == 0);
if (!cleanup_done) if (!cleanup_done)
cleanup(); cleanup();
...@@ -1769,21 +1769,72 @@ Statement_map::Statement_map() : ...@@ -1769,21 +1769,72 @@ Statement_map::Statement_map() :
} }
int Statement_map::insert(Statement *statement) /*
Insert a new statement to the thread-local statement map.
DESCRIPTION
If there was an old statement with the same name, replace it with the
new one. Otherwise, check if max_prepared_stmt_count is not reached yet,
increase prepared_stmt_count, and insert the new statement. It's okay
to delete an old statement and fail to insert the new one.
POSTCONDITIONS
All named prepared statements are also present in names_hash.
Statement names in names_hash are unique.
The statement is added only if prepared_stmt_count < max_prepard_stmt_count
last_found_statement always points to a valid statement or is 0
RETURN VALUE
0 success
1 error: out of resources or max_prepared_stmt_count limit has been
reached. An error is sent to the client, the statement is deleted.
*/
int Statement_map::insert(THD *thd, Statement *statement)
{ {
int res= my_hash_insert(&st_hash, (byte *) statement); if (my_hash_insert(&st_hash, (byte*) statement))
if (res)
return res;
if (statement->name.str)
{ {
if ((res= my_hash_insert(&names_hash, (byte*)statement))) /*
Delete is needed only in case of an insert failure. In all other
cases hash_delete will also delete the statement.
*/
delete statement;
my_error(ER_OUT_OF_RESOURCES, MYF(0));
goto err_st_hash;
}
if (statement->name.str && my_hash_insert(&names_hash, (byte*) statement))
{ {
hash_delete(&st_hash, (byte*)statement); my_error(ER_OUT_OF_RESOURCES, MYF(0));
return res; goto err_names_hash;
} }
pthread_mutex_lock(&LOCK_prepared_stmt_count);
/*
We don't check that prepared_stmt_count is <= max_prepared_stmt_count
because we would like to allow to lower the total limit
of prepared statements below the current count. In that case
no new statements can be added until prepared_stmt_count drops below
the limit.
*/
if (prepared_stmt_count >= max_prepared_stmt_count)
{
pthread_mutex_unlock(&LOCK_prepared_stmt_count);
my_error(ER_MAX_PREPARED_STMT_COUNT_REACHED, MYF(0),
max_prepared_stmt_count);
goto err_max;
} }
prepared_stmt_count++;
pthread_mutex_unlock(&LOCK_prepared_stmt_count);
last_found_statement= statement; last_found_statement= statement;
return res; return 0;
err_max:
if (statement->name.str)
hash_delete(&names_hash, (byte*) statement);
err_names_hash:
hash_delete(&st_hash, (byte*) statement);
err_st_hash:
return 1;
} }
...@@ -1797,6 +1848,47 @@ void Statement_map::close_transient_cursors() ...@@ -1797,6 +1848,47 @@ void Statement_map::close_transient_cursors()
} }
void Statement_map::erase(Statement *statement)
{
if (statement == last_found_statement)
last_found_statement= 0;
if (statement->name.str)
hash_delete(&names_hash, (byte *) statement);
hash_delete(&st_hash, (byte *) statement);
pthread_mutex_lock(&LOCK_prepared_stmt_count);
DBUG_ASSERT(prepared_stmt_count > 0);
prepared_stmt_count--;
pthread_mutex_unlock(&LOCK_prepared_stmt_count);
}
void Statement_map::reset()
{
/* Must be first, hash_free will reset st_hash.records */
pthread_mutex_lock(&LOCK_prepared_stmt_count);
DBUG_ASSERT(prepared_stmt_count >= st_hash.records);
prepared_stmt_count-= st_hash.records;
pthread_mutex_unlock(&LOCK_prepared_stmt_count);
my_hash_reset(&names_hash);
my_hash_reset(&st_hash);
last_found_statement= 0;
}
Statement_map::~Statement_map()
{
/* Must go first, hash_free will reset st_hash.records */
pthread_mutex_lock(&LOCK_prepared_stmt_count);
DBUG_ASSERT(prepared_stmt_count >= st_hash.records);
prepared_stmt_count-= st_hash.records;
pthread_mutex_unlock(&LOCK_prepared_stmt_count);
hash_free(&names_hash);
hash_free(&st_hash);
}
bool select_dumpvar::send_data(List<Item> &items) bool select_dumpvar::send_data(List<Item> &items)
{ {
List_iterator_fast<Item_func_set_user_var> li(vars); List_iterator_fast<Item_func_set_user_var> li(vars);
......
...@@ -545,7 +545,7 @@ class Statement_map ...@@ -545,7 +545,7 @@ class Statement_map
public: public:
Statement_map(); Statement_map();
int insert(Statement *statement); int insert(THD *thd, Statement *statement);
Statement *find_by_name(LEX_STRING *name) Statement *find_by_name(LEX_STRING *name)
{ {
...@@ -567,36 +567,16 @@ public: ...@@ -567,36 +567,16 @@ public:
} }
return last_found_statement; return last_found_statement;
} }
void erase(Statement *statement)
{
if (statement == last_found_statement)
last_found_statement= 0;
if (statement->name.str)
{
hash_delete(&names_hash, (byte *) statement);
}
hash_delete(&st_hash, (byte *) statement);
}
/* /*
Close all cursors of this connection that use tables of a storage Close all cursors of this connection that use tables of a storage
engine that has transaction-specific state and therefore can not engine that has transaction-specific state and therefore can not
survive COMMIT or ROLLBACK. Currently all but MyISAM cursors are closed. survive COMMIT or ROLLBACK. Currently all but MyISAM cursors are closed.
*/ */
void close_transient_cursors(); void close_transient_cursors();
void erase(Statement *statement);
/* Erase all statements (calls Statement destructor) */ /* Erase all statements (calls Statement destructor) */
void reset() void reset();
{ ~Statement_map();
my_hash_reset(&names_hash);
my_hash_reset(&st_hash);
transient_cursor_list.empty();
last_found_statement= 0;
}
void destroy()
{
hash_free(&names_hash);
hash_free(&st_hash);
}
private: private:
HASH st_hash; HASH st_hash;
HASH names_hash; HASH names_hash;
...@@ -1179,6 +1159,7 @@ public: ...@@ -1179,6 +1159,7 @@ public:
{ {
my_bool my_bool_value; my_bool my_bool_value;
long long_value; long long_value;
ulong ulong_value;
} sys_var_tmp; } sys_var_tmp;
struct { struct {
......
...@@ -1848,10 +1848,13 @@ void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length) ...@@ -1848,10 +1848,13 @@ void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length)
if (! (stmt= new Prepared_statement(thd, &thd->protocol_prep))) if (! (stmt= new Prepared_statement(thd, &thd->protocol_prep)))
DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */ DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */
if (thd->stmt_map.insert(stmt)) if (thd->stmt_map.insert(thd, stmt))
{ {
delete stmt; /*
DBUG_VOID_RETURN; /* out of memory */ The error is set in the insert. The statement itself
will be also deleted there (this is how the hash works).
*/
DBUG_VOID_RETURN;
} }
/* Reset warnings from previous command */ /* Reset warnings from previous command */
...@@ -2028,11 +2031,17 @@ void mysql_sql_stmt_prepare(THD *thd) ...@@ -2028,11 +2031,17 @@ void mysql_sql_stmt_prepare(THD *thd)
DBUG_VOID_RETURN; /* out of memory */ DBUG_VOID_RETURN; /* out of memory */
} }
if (stmt->set_name(name) || thd->stmt_map.insert(stmt)) /* Set the name first, insert should know that this statement has a name */
if (stmt->set_name(name))
{ {
delete stmt; delete stmt;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
if (thd->stmt_map.insert(thd, stmt))
{
/* The statement is deleted and an error is set if insert fails */
DBUG_VOID_RETURN;
}
if (stmt->prepare(query, query_len+1)) if (stmt->prepare(query, query_len+1))
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment