Commit 71ce58a3 authored by unknown's avatar unknown

This is a large push. Included are :

* multi-table updates
* new paid feature for limiting number of queries per hour for users
* optional syntax for multi-table deletes
* optimization for SQL_CALC_FOUND_ROWS
* a small addition for CREATE .. SELECT that will be of future use

I know that all this will require many additions to documentation, 
which I have not done, but I am at Arjen's disposal to help him document
all this.



libmysqld/lib_sql.cc:
  This is a small change required due to new paid feature of limiting 
  number of queries per hour for each user.
mysql-test/r/multi_update.result:
  Change of the result to accomodate multi-table updates
mysql-test/t/multi_update.test:
  Change in multi-table tests to accomodate multi-table updates
BitKeeper/etc/ignore:
  Added 50 sql/new.cc to the ignore list
scripts/mysql_install_db.sh:
  This is a small change required due to new paid feature of limiting 
  number of queries per hour for each user.
sql/item_timefunc.h:
  This is a small feature that will be of use later in the proper 
  creation of the tables out of CREATE ... SELECT...
  
  Further changes will come after this resolve/push/test passes ..
sql/lex.h:
  This is a small change required due to new paid feature of limiting 
  number of queries per hour for each user.
sql/mysql_priv.h:
  This is a small change required due to new paid feature of limiting 
  number of queries per hour for each user.
sql/mysqld.cc:
  This is a small change required due to new paid feature of limiting 
  number of queries per hour for each user.
sql/sql_acl.cc:
  This is a small change required due to new paid feature of limiting 
  number of queries per hour for each user.
sql/sql_acl.h:
  This is a small change required due to new paid feature of limiting 
  number of queries per hour for each user.
sql/sql_class.h:
  These are changes required for multi-table updates.
sql/sql_lex.h:
  Changes required for both multi-table updates and limiting number of 
  queries per hour (paid feature).
sql/sql_parse.cc:
  Ha ! So many changes. 
  
  * multi-table updates
  * limiting number of queries per hour for users
sql/sql_select.cc:
  Optimisation for SQL_CALC_FOUND_ROWS, when a query involves a single
  table, without WHERE or GROUP clause
sql/sql_update.cc:
  multi-table updates
sql/sql_yacc.yy:
  Many things :
  * multi-table updates
  * limiting number of queries per hour for users
  * new optional syntax for multi-table deletes. This one is different 
    from the one demanded by user. USING clause MUST include ALL tables, 
    not just the ones that are used and not deleted from.
parent 1baa5527
......@@ -425,3 +425,5 @@ vio/test-sslclient
vio/test-sslserver
vio/viotest-ssl
libmysqld/mf_iocache.cc
50
sql/new.cc
......@@ -228,11 +228,11 @@ check_connections2(THD * thd)
return 0;
}
static bool check_user(THD *thd,enum_server_command command, const char *user,
const char *passwd, const char *db, bool check_count)
{
NET *net= &thd->net;
uint max=0;
thd->db=0;
if (!(thd->user = my_strdup(user, MYF(0))))
......@@ -244,7 +244,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user,
passwd, thd->scramble, &thd->priv_user,
protocol_version == 9 ||
!(thd->client_capabilities &
CLIENT_LONG_PASSWORD));
CLIENT_LONG_PASSWORD),&max);
DBUG_PRINT("general",
("Capabilities: %d packet_length: %d Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'",
thd->client_capabilities, thd->max_packet_length,
......
......@@ -2,32 +2,33 @@ drop table if exists t1,t2,t3;
create table t1(id1 int not null auto_increment primary key, t char(12));
create table t2(id2 int not null, t char(12));
create table t3(id3 int not null, t char(12), index(id3));
delete t1.*, t2.*, t3.* from t1,t2,t3 where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 9500;
update t1,t2,t3 set t1.t="aaa", t2.t="bbb", t3.t="cc" where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 90;
delete t1.*, t2.*, t3.* from t1,t2,t3 where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 95;
check table t1, t2, t3;
Table Op Msg_type Msg_text
test.t1 check status OK
test.t2 check status OK
test.t3 check status OK
select count(*) from t1 where id1 > 9500;
select count(*) from t1 where id1 > 95;
count(*)
0
select count(*) from t2 where id2 > 9500;
select count(*) from t2 where id2 > 95;
count(*)
0
select count(*) from t3 where id3 > 9500;
select count(*) from t3 where id3 > 95;
count(*)
0
delete t1, t2, t3 from t1,t2,t3 where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 500;
select count(*) from t1 where id1 > 500;
delete t1, t2, t3 from t1,t2,t3 where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 5;
select count(*) from t1 where id1 > 5;
count(*)
0
select count(*) from t2 where id2 > 500;
select count(*) from t2 where id2 > 5;
count(*)
0
select count(*) from t3 where id3 > 500;
select count(*) from t3 where id3 > 5;
count(*)
0
delete t1, t2, t3 from t1,t2,t3 where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 0;
delete from t1, t2, t3 using t1,t2,t3 where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 0;
select count(*) from t1 where id1;
count(*)
0
......
......@@ -2,15 +2,15 @@
# Only run the test if we are using --big-test, because this test takes a
# long time
#
-- require r/big_test.require
eval select $BIG_TEST as using_big_test;
#-- require r/big_test.require
#eval select $BIG_TEST as using_big_test;
drop table if exists t1,t2,t3;
create table t1(id1 int not null auto_increment primary key, t char(12));
create table t2(id2 int not null, t char(12));
create table t3(id3 int not null, t char(12), index(id3));
disable_query_log;
let $1 = 10000;
let $1 = 100;
while ($1)
{
let $2 = 5;
......@@ -29,20 +29,21 @@ while ($1)
dec $1;
}
enable_query_log;
delete t1.*, t2.*, t3.* from t1,t2,t3 where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 9500;
update t1,t2,t3 set t1.t="aaa", t2.t="bbb", t3.t="cc" where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 90;
delete t1.*, t2.*, t3.* from t1,t2,t3 where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 95;
check table t1, t2, t3;
select count(*) from t1 where id1 > 9500;
select count(*) from t2 where id2 > 9500;
select count(*) from t3 where id3 > 9500;
select count(*) from t1 where id1 > 95;
select count(*) from t2 where id2 > 95;
select count(*) from t3 where id3 > 95;
delete t1, t2, t3 from t1,t2,t3 where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 500;
select count(*) from t1 where id1 > 500;
select count(*) from t2 where id2 > 500;
select count(*) from t3 where id3 > 500;
delete t1, t2, t3 from t1,t2,t3 where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 5;
select count(*) from t1 where id1 > 5;
select count(*) from t2 where id2 > 5;
select count(*) from t3 where id3 > 5;
delete t1, t2, t3 from t1,t2,t3 where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 0;
delete from t1, t2, t3 using t1,t2,t3 where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 0;
# These queries will force a scan of the table
select count(*) from t1 where id1;
......
......@@ -228,18 +228,21 @@ then
c_u="$c_u ssl_cipher BLOB NOT NULL,"
c_u="$c_u x509_issuer BLOB NOT NULL,"
c_u="$c_u x509_subject BLOB NOT NULL,"
c_u="$c_u max_questions int(11) unsigned DEFAULT 0 NOT NULL,"
c_u="$c_u max_updates int(11) unsigned DEFAULT 0 NOT NULL,"
c_u="$c_u max_connections int(11) unsigned DEFAULT 0 NOT NULL,"
c_u="$c_u PRIMARY KEY Host (Host,User)"
c_u="$c_u )"
c_u="$c_u comment='Users and global privileges';"
i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','NONE','','','');
INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','NONE','','','');
i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','NONE','','','',0,0,0);
INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','NONE','','','',0,0,0);
REPLACE INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','NONE','','','');
REPLACE INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','NONE','','','');
REPLACE INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','NONE','','','',0,0,0);
REPLACE INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','NONE','','','',0,0,0);
INSERT INTO user VALUES ('localhost','','','N','N','N','N','N','N','N','N','N','N','N','N','N','N','NONE','','','');
INSERT INTO user VALUES ('$hostname','','','N','N','N','N','N','N','N','N','N','N','N','N','N','N','NONE','','','');"
INSERT INTO user VALUES ('localhost','','','N','N','N','N','N','N','N','N','N','N','N','N','N','N','NONE','','','',0,0,0);
INSERT INTO user VALUES ('$hostname','','','N','N','N','N','N','N','N','N','N','N','N','N','N','N','NONE','','','',0,0,0);"
fi
if test ! -f $mdata/func.frm
......
#!/bin/sh
echo "This scripts updates the mysql.user, mysql.db, mysql.host and the"
echo "mysql.func table to MySQL 3.22.14 and above."
echo ""
echo "This is needed if you want to use the new GRANT functions,"
echo "CREATE AGGREAGATE FUNCTION or want to use the more secure passwords in 3.23"
echo ""
echo "If you get Access denied errors, you should run this script again"
echo "and give the MySQL root user password as a argument!"
root_password="$1"
host="localhost"
# Fix old password format, add File_priv and func table
echo ""
echo "If your tables are already up to date or partially up to date you will"
echo "get some warnings about 'Duplicated column name'. You can safely ignore these!"
@bindir@/mysql -f --user=root --password="$root_password" --host="$host" mysql <<END_OF_DATA
alter table user add max_questions int(11) unsigned DEFAULT 0 NOT NULL;
alter table user add max_updates int(11) unsigned DEFAULT 0 NOT NULL;
alter table user add max_connections int(11) unsigned DEFAULT 0 NOT NULL;
END_OF_DATA
......@@ -229,6 +229,23 @@ class Item_date :public Item_func
const char *func_name() const { return "date"; }
void fix_length_and_dec() { decimals=0; max_length=10; }
bool save_in_field(Field *to);
void make_field(Send_field *tmp_field)
{
init_make_field(tmp_field,FIELD_TYPE_DATE);
}
};
class Item_date_func :public Item_str_func
{
public:
Item_date_func() :Item_str_func() {}
Item_date_func(Item *a) :Item_str_func(a) {}
Item_date_func(Item *a,Item *b) :Item_str_func(a,b) {}
void make_field(Send_field *tmp_field)
{
init_make_field(tmp_field,FIELD_TYPE_DATETIME);
}
};
......@@ -247,6 +264,10 @@ class Item_func_curtime :public Item_func
{ str_value.set(buff,buff_length); return &str_value; }
const char *func_name() const { return "curtime"; }
void fix_length_and_dec();
void make_field(Send_field *tmp_field)
{
init_make_field(tmp_field,FIELD_TYPE_TIME);
}
};
......@@ -263,15 +284,15 @@ class Item_func_curdate :public Item_date
};
class Item_func_now :public Item_func
class Item_func_now :public Item_date_func
{
longlong value;
char buff[20];
uint buff_length;
TIME ltime;
public:
Item_func_now() :Item_func() {}
Item_func_now(Item *a) :Item_func(a) {}
Item_func_now() :Item_date_func() {}
Item_func_now(Item *a) :Item_date_func(a) {}
enum Item_result result_type () const { return STRING_RESULT; }
double val() { return (double) value; }
longlong val_int() { return value; }
......@@ -307,16 +328,16 @@ class Item_func_date_format :public Item_str_func
};
class Item_func_from_unixtime :public Item_func
class Item_func_from_unixtime :public Item_date_func
{
public:
Item_func_from_unixtime(Item *a) :Item_func(a) {}
Item_func_from_unixtime(Item *a) :Item_date_func(a) {}
double val() { return (double) Item_func_from_unixtime::val_int(); }
longlong val_int();
String *val_str(String *str);
const char *func_name() const { return "from_unixtime"; }
void fix_length_and_dec() { decimals=0; max_length=19; }
enum Item_result result_type () const { return STRING_RESULT; }
// enum Item_result result_type () const { return STRING_RESULT; }
bool get_date(TIME *res,bool fuzzy_date);
};
......@@ -330,6 +351,10 @@ class Item_func_sec_to_time :public Item_str_func
String *val_str(String *);
void fix_length_and_dec() { maybe_null=1; max_length=13; }
const char *func_name() const { return "sec_to_time"; }
void make_field(Send_field *tmp_field)
{
init_make_field(tmp_field,FIELD_TYPE_TIME);
}
};
enum interval_type { INTERVAL_YEAR, INTERVAL_MONTH, INTERVAL_DAY,
......@@ -339,7 +364,7 @@ enum interval_type { INTERVAL_YEAR, INTERVAL_MONTH, INTERVAL_DAY,
INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND,
INTERVAL_MINUTE_SECOND};
class Item_date_add_interval :public Item_str_func
class Item_date_add_interval :public Item_date_func
{
const interval_type int_type;
String value;
......@@ -347,7 +372,7 @@ class Item_date_add_interval :public Item_str_func
public:
Item_date_add_interval(Item *a,Item *b,interval_type type_arg,bool neg_arg)
:Item_str_func(a,b),int_type(type_arg), date_sub_interval(neg_arg) {}
:Item_date_func(a,b),int_type(type_arg), date_sub_interval(neg_arg) {}
String *val_str(String *);
const char *func_name() const { return "date_add_interval"; }
void fix_length_and_dec() { maybe_null=1; max_length=19; value.alloc(32);}
......
......@@ -224,6 +224,7 @@ static SYMBOL symbols[] = {
{ "MASTER_SERVER_ID", SYM(MASTER_SERVER_ID_SYM),0,0},
{ "MASTER_USER", SYM(MASTER_USER_SYM),0,0},
{ "MAX_ROWS", SYM(MAX_ROWS),0,0},
{ "MAXIMUM", SYM(MAXIMUM),0,0},
{ "MATCH", SYM(MATCH),0,0},
{ "MEDIUMBLOB", SYM(MEDIUMBLOB),0,0},
{ "MEDIUMTEXT", SYM(MEDIUMTEXT),0,0},
......@@ -237,6 +238,7 @@ static SYMBOL symbols[] = {
{ "MODE", SYM(MODE_SYM),0,0},
{ "MODIFY", SYM(MODIFY_SYM),0,0},
{ "MONTH", SYM(MONTH_SYM),0,0},
{ "MQH", SYM(MQH_SYM),0,0},
{ "MRG_MYISAM", SYM(MERGE_SYM),0,0},
{ "MYISAM", SYM(MYISAM_SYM),0,0},
{ "NATURAL", SYM(NATURAL),0,0},
......@@ -260,6 +262,7 @@ static SYMBOL symbols[] = {
{ "PACK_KEYS", SYM(PACK_KEYS_SYM),0,0},
{ "PARTIAL", SYM(PARTIAL),0,0},
{ "PASSWORD", SYM(PASSWORD),0,0},
{ "PER", SYM(PER_SYM),0,0},
{ "PURGE", SYM(PURGE),0,0},
{ "PRECISION", SYM(PRECISION),0,0},
{ "PREV", SYM(PREV_SYM),0,0},
......@@ -268,6 +271,7 @@ static SYMBOL symbols[] = {
{ "PROCESS" , SYM(PROCESS),0,0},
{ "PROCESSLIST", SYM(PROCESSLIST_SYM),0,0},
{ "PRIVILEGES", SYM(PRIVILEGES),0,0},
{ "QUERIES", SYM(QUERIES),0,0},
{ "QUICK", SYM(QUICK),0,0},
{ "RAID0", SYM(RAID_0_SYM),0,0},
{ "READ", SYM(READ_SYM),0,0},
......
......@@ -492,7 +492,7 @@ int write_record(TABLE *table,COPY_INFO *info);
/* bits set in manager_status */
#define MANAGER_BERKELEY_LOG_CLEANUP (1L << 0)
extern ulong volatile manager_status;
extern bool volatile manager_thread_in_use;
extern bool volatile manager_thread_in_use, mqh_used;
extern pthread_t manager_thread;
extern pthread_mutex_t LOCK_manager;
extern pthread_cond_t COND_manager;
......
......@@ -219,6 +219,7 @@ static bool opt_log,opt_update_log,opt_bin_log,opt_slow_log,opt_noacl,
bool opt_sql_bin_update = 0, opt_log_slave_updates = 0, opt_safe_show_db=0,
opt_show_slave_auth_info = 0, opt_old_rpl_compat = 0,
opt_safe_user_create = 0, opt_no_mix_types = 0;
volatile bool mqh_used = 0;
FILE *bootstrap_file=0;
int segfaulted = 0; // ensure we do not enter SIGSEGV handler twice
extern MASTER_INFO glob_mi;
......@@ -1900,7 +1901,7 @@ The server will not act as a slave.");
}
if (!opt_noacl)
(void) grant_init();
if (max_user_connections)
if (max_user_connections || mqh_used)
init_max_user_conn();
#ifdef HAVE_DLOPEN
......
......@@ -58,7 +58,7 @@ class ACL_USER :public ACL_ACCESS
{
public:
acl_host_and_ip host;
uint hostname_length;
uint hostname_length, questions, updates;
char *user,*password;
ulong salt[2];
#ifdef HAVE_OPENSSL
......@@ -240,6 +240,15 @@ int acl_init(bool dont_read_acl_tables)
user.access=get_access(table,3);
user.sort=get_sort(2,user.host.hostname,user.user);
user.hostname_length=user.host.hostname ? (uint) strlen(user.host.hostname) : 0;
if (table->fields >=23)
{
char *ptr = get_field(&mem, table, 21);
user.questions=atoi(ptr);
ptr = get_field(&mem, table, 22);
user.updates=atoi(ptr);
if (user.questions)
mqh_used=true;
}
#ifndef TO_BE_REMOVED
if (table->fields <= 13)
{ // Without grant
......@@ -423,7 +432,7 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
/* Get master privilges for user (priviliges for all tables). Required to connect */
uint acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
const char *password,const char *message,char **priv_user,
bool old_ver)
bool old_ver, uint *max)
{
uint user_access=NO_ACCESS;
*priv_user=(char*) user;
......@@ -536,6 +545,7 @@ uint acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
#else /* HAVE_OPENSSL */
user_access=acl_user->access;
#endif /* HAVE_OPENSSL */
*max=acl_user->questions;
if (!acl_user->user)
*priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */
break;
......@@ -569,6 +579,7 @@ static void acl_update_user(const char *user, const char *host,
const char *ssl_cipher,
const char *x509_issuer,
const char *x509_subject,
unsigned int mqh,
uint privileges)
{
for (uint i=0 ; i < acl_users.elements ; i++)
......@@ -582,6 +593,7 @@ static void acl_update_user(const char *user, const char *host,
acl_user->host.hostname && !strcmp(host,acl_user->host.hostname))
{
acl_user->access=privileges;
acl_user->questions=mqh;
#ifdef HAVE_OPENSSL
acl_user->ssl_type=ssl_type;
acl_user->ssl_cipher=ssl_cipher;
......@@ -611,6 +623,7 @@ static void acl_insert_user(const char *user, const char *host,
const char *ssl_cipher,
const char *x509_issuer,
const char *x509_subject,
unsigned int mqh,
uint privileges)
{
ACL_USER acl_user;
......@@ -618,6 +631,7 @@ static void acl_insert_user(const char *user, const char *host,
update_hostname(&acl_user.host,strdup_root(&mem,host));
acl_user.password=0;
acl_user.access=privileges;
acl_user.questions=mqh;
acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
acl_user.hostname_length=(uint) strlen(acl_user.host.hostname);
#ifdef HAVE_OPENSSL
......@@ -1192,6 +1206,13 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
}
}
#endif /* HAVE_OPENSSL */
if (table->fields>=23 && thd->lex.mqh)
{
char buff[33];
int len =int2str((long)thd->lex.mqh,buff,10) - buff;
table->field[21]->store(buff,len);
mqh_used=true;
}
if (old_row_exists)
{
/*
......@@ -1230,6 +1251,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
thd->lex.ssl_cipher,
thd->lex.x509_issuer,
thd->lex.x509_subject,
thd->lex.mqh,
rights);
else
acl_insert_user(combo.user.str,combo.host.str,password,
......@@ -1237,6 +1259,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
thd->lex.ssl_cipher,
thd->lex.x509_issuer,
thd->lex.x509_subject,
thd->lex.mqh,
rights);
}
table->file->index_end();
......
......@@ -61,7 +61,7 @@ uint acl_get(const char *host, const char *ip, const char *bin_ip,
const char *user, const char *db);
uint acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
const char *password,const char *scramble,char **priv_user,
bool old_ver);
bool old_ver, uint *max);
bool acl_check_host(const char *host, const char *ip);
bool change_password(THD *thd, const char *host, const char *user,
char *password);
......
......@@ -652,4 +652,33 @@ class Unique :public Sql_alloc
bool send_eof();
};
class multi_update : public select_result {
TABLE_LIST *update_tables, *table_being_updated;
// Unique **tempfiles;
COPY_INFO *infos;
TABLE **tmp_tables;
THD *thd;
ha_rows updated, found;
List<Item> fields;
List <Item> **fields_by_tables;
thr_lock_type lock_option;
enum enum_duplicates dupl;
uint num_of_tables, num_fields, num_updated, *save_time_stamps, *field_sequence;
int error;
bool do_update;
public:
multi_update(THD *thd_arg, TABLE_LIST *ut, List<Item> &fs,
enum enum_duplicates handle_duplicates,
thr_lock_type lock_option_arg, uint num);
~multi_update();
int prepare(List<Item> &list);
bool send_fields(List<Item> &list,
uint flag) { return 0; }
bool send_data(List<Item> &items);
void initialize_tables (JOIN *join);
void send_error(uint errcode,const char *err);
int do_updates (bool from_send_error);
bool send_eof();
};
......@@ -55,7 +55,7 @@ enum enum_sql_command {
SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_SHOW_BINLOGS,
SQLCOM_SHOW_OPEN_TABLES, SQLCOM_LOAD_MASTER_DATA,
SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ,
SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_MULTI_DELETE,
SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_MULTI_DELETE, SQLCOM_MULTI_UPDATE,
SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER
};
......@@ -181,7 +181,7 @@ typedef struct st_lex {
enum enum_ha_read_modes ha_read_mode;
enum ha_rkey_function ha_rkey_mode;
enum enum_enable_or_disable alter_keys_onoff;
uint grant,grant_tot_col,which_columns, union_option;
uint grant,grant_tot_col,which_columns, union_option, mqh;
thr_lock_type lock_option;
bool drop_primary,drop_if_exists,local_file;
bool in_comment,ignore_space,verbose,simple_alter, option_type;
......
......@@ -37,7 +37,7 @@ extern "C" int gethostname(char *name, int namelen);
#endif
static int check_for_max_user_connections(const char *user, int u_length,
const char *host);
const char *host, uint max);
static void decrease_user_connections(const char *user, const char *host);
static bool check_db_used(THD *thd,TABLE_LIST *tables);
static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *tables);
......@@ -98,6 +98,95 @@ inline bool end_active_trans(THD *thd)
}
static HASH hash_user_connections;
extern pthread_mutex_t LOCK_user_conn;
struct user_conn {
char *user;
uint len, connections, questions, max;
time_t intime;
};
static byte* get_key_conn(user_conn *buff, uint *length,
my_bool not_used __attribute__((unused)))
{
*length=buff->len;
return (byte*) buff->user;
}
#define DEF_USER_COUNT 50
static void free_user(struct user_conn *uc)
{
my_free((char*) uc,MYF(0));
}
/*
** Check if maximum queries per hour limit has been reached
** returns 0 if OK.
*/
static bool check_mqh(THD *thd, const char *user, const char *host,uint max)
{
uint temp_len;
char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2];
struct user_conn *uc;
if (!user)
user="";
if (!host)
host="";
temp_len= (uint) (strxnmov(temp_user, sizeof(temp_user), user, "@", host,
NullS) - temp_user);
//This would be MUCH faster if there was already temp_user made in THD !!! May I ??
(void) pthread_mutex_lock(&LOCK_user_conn);
uc = (struct user_conn *) hash_search(&hash_user_connections,
(byte*) temp_user, temp_len);
if (uc) /* user found ; check for no. of queries */
{
bool my_start = thd->start_time != 0;
time_t check_time = (my_start) ? thd->start_time : time(NULL);
if (check_time - uc->intime >= 3600)
{
uc->questions=(uint)my_start;
uc->intime=check_time;
}
else if (uc->max && ++(uc->questions) > uc->max)
{
(void) pthread_mutex_unlock(&LOCK_user_conn);
send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); // change this to appropriate message
return 1;
}
}
else
{
struct user_conn *uc= ((struct user_conn*)
my_malloc(sizeof(struct user_conn) + temp_len+1,
MYF(MY_WME)));
if (!uc)
{
send_error(&current_thd->net, 0, NullS); // Out of memory
(void) pthread_mutex_unlock(&LOCK_user_conn);
return 1;
}
uc->user=(char*) (uc+1);
memcpy(uc->user,temp_user,temp_len+1);
uc->len = temp_len;
uc->connections = 1;
uc->questions=0;
uc->max=max;
uc->intime=current_thd->thr_create_time;
if (hash_insert(&hash_user_connections, (byte*) uc))
{
my_free((char*) uc,0);
send_error(&current_thd->net, 0, NullS); // Out of memory
(void) pthread_mutex_unlock(&LOCK_user_conn);
return 1;
}
}
(void) pthread_mutex_unlock(&LOCK_user_conn);
return 0;
}
/*
** Check if user is ok
** Updates:
......@@ -108,6 +197,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user,
const char *passwd, const char *db, bool check_count)
{
NET *net= &thd->net;
uint max=0;
thd->db=0;
if (!(thd->user = my_strdup(user, MYF(0))))
......@@ -119,7 +209,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user,
passwd, thd->scramble, &thd->priv_user,
protocol_version == 9 ||
!(thd->client_capabilities &
CLIENT_LONG_PASSWORD));
CLIENT_LONG_PASSWORD),&max);
DBUG_PRINT("info",
("Capabilities: %d packet_length: %d Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'",
thd->client_capabilities, thd->max_packet_length,
......@@ -150,6 +240,8 @@ static bool check_user(THD *thd,enum_server_command command, const char *user,
return(1);
}
}
if (mqh_used && max && check_mqh(thd,user,thd->host,max))
return -1;
mysql_log.write(thd,command,
(thd->priv_user == thd->user ?
(char*) "%s@%s on %s" :
......@@ -159,7 +251,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user,
db ? db : (char*) "");
thd->db_access=0;
if (max_user_connections &&
check_for_max_user_connections(user, strlen(user), thd->host))
check_for_max_user_connections(user, strlen(user), thd->host, max))
return -1;
if (db && db[0])
{
......@@ -179,28 +271,6 @@ static bool check_user(THD *thd,enum_server_command command, const char *user,
** variable that is greater then 0
*/
static HASH hash_user_connections;
extern pthread_mutex_t LOCK_user_conn;
struct user_conn {
char *user;
uint len, connections;
};
static byte* get_key_conn(user_conn *buff, uint *length,
my_bool not_used __attribute__((unused)))
{
*length=buff->len;
return (byte*) buff->user;
}
#define DEF_USER_COUNT 50
static void free_user(struct user_conn *uc)
{
my_free((char*) uc,MYF(0));
}
void init_max_user_conn(void)
{
(void) hash_init(&hash_user_connections,DEF_USER_COUNT,0,0,
......@@ -210,7 +280,7 @@ void init_max_user_conn(void)
static int check_for_max_user_connections(const char *user, int u_length,
const char *host)
const char *host, uint max)
{
int error=1;
uint temp_len;
......@@ -251,7 +321,10 @@ static int check_for_max_user_connections(const char *user, int u_length,
uc->user=(char*) (uc+1);
memcpy(uc->user,temp_user,temp_len+1);
uc->len = temp_len;
uc->connections = 1;
uc->connections = 1;
uc->questions=0;
uc->max=max;
uc->intime=current_thd->thr_create_time;
if (hash_insert(&hash_user_connections, (byte*) uc))
{
my_free((char*) uc,0);
......@@ -290,7 +363,7 @@ static void decrease_user_connections(const char *user, const char *host)
dbug_assert(uc != 0); // We should always find the user
if (!uc)
goto end; // Safety; Something went wrong
if (! --uc->connections)
if (! --uc->connections && !mqh_used)
{
/* Last connection for user; Delete it */
(void) hash_delete(&hash_user_connections,(byte*) uc);
......@@ -306,7 +379,6 @@ void free_max_user_conn(void)
hash_free(&hash_user_connections);
}
/*
** check connnetion and get priviliges
** returns 0 on ok, -1 < if error is given > 0 on error.
......@@ -849,6 +921,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
mysql_log.write(thd,command,"%s",thd->query);
DBUG_PRINT("query",("%s",thd->query));
if (mqh_used && check_mqh(thd,thd->user,thd->host,0))
{
error = TRUE;
net->error = 0;
break;
}
mysql_parse(thd,thd->query,packet_length-1);
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),WAIT_PRIOR);
......@@ -959,6 +1037,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
send_error(net,0);
else
send_eof(net);
if (mqh_used && hash_user_connections.array.buffer == 0)
init_max_user_conn();
break;
}
case COM_SHUTDOWN:
......@@ -1092,6 +1172,8 @@ mysql_execute_command(void)
(table_rules_on && tables && thd->slave_thread &&
!tables_ok(thd,tables)))
DBUG_VOID_RETURN;
if (lex->sql_command==SQLCOM_UPDATE && select_lex->table_list.elements > 1)
lex->sql_command=SQLCOM_MULTI_UPDATE;
switch (lex->sql_command) {
case SQLCOM_SELECT:
......@@ -1727,6 +1809,59 @@ mysql_execute_command(void)
close_thread_tables(thd);
break;
}
case SQLCOM_MULTI_UPDATE:
multi_update *result;
uint table_count;
TABLE_LIST *auxi;
if (check_access(thd,UPDATE_ACL,tables->db,&tables->grant.privilege))
goto error;
if (grant_option && check_grant(thd,UPDATE_ACL,tables))
goto error;
if (select_lex->item_list.elements != lex->value_list.elements)
{
send_error(&thd->net,ER_WRONG_VALUE_COUNT);
DBUG_VOID_RETURN;
}
for (auxi=(TABLE_LIST*) tables, table_count=0 ; auxi ; auxi=auxi->next)
{
table_count++;
auxi->lock_type=TL_WRITE;
}
if (select_lex->order_list.elements || (select_lex->select_limit && select_lex->select_limit < INT_MAX))
{
send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /// will have to come up with something better eventually
DBUG_VOID_RETURN;
}
tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
if ((res=open_and_lock_tables(thd,tables)))
break;
if (!setup_fields(thd,tables,select_lex->item_list,1,0,0) &&
!setup_fields(thd,tables,lex->value_list,0,0,0) && ! thd->fatal_error &&
(result=new multi_update(thd,tables,select_lex->item_list,lex->duplicates,
lex->lock_option, table_count)))
{
List <Item> total_list;
List_iterator <Item> field_list(select_lex->item_list);
List_iterator <Item> value_list(lex->value_list);
Item *item;
while ((item=field_list++))
total_list.push_back(item);
while ((item=value_list++))
total_list.push_back(item);
res=mysql_select(thd,tables,total_list,
select_lex->where,select_lex->ftfunc_list,
(ORDER *)NULL,(ORDER *)NULL,(Item *)NULL,
(ORDER *)NULL,
select_lex->options | thd->options |
SELECT_NO_JOIN_CACHE,
result);
delete result;
}
else
res= -1; // Error is not sent
close_thread_tables(thd);
break;
case SQLCOM_DROP_TABLE:
{
if (check_table_access(thd,DROP_ACL,tables))
......@@ -2080,6 +2215,8 @@ mysql_execute_command(void)
}
}
}
if (mqh_used && hash_user_connections.array.buffer == 0)
init_max_user_conn();
break;
}
case SQLCOM_FLUSH:
......
......@@ -4731,9 +4731,18 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
{
if (join->select_options & OPTION_FOUND_ROWS)
{
join->do_send_rows=0;
join->thd->select_limit = HA_POS_ERROR;
DBUG_RETURN(0);
JOIN_TAB *jt=join->join_tab;
if ((join->tables == 1) && !join->tmp_table && !join->sort_and_group && !join->send_group_parts && !join->having && !jt->select_cond )
{
join->select_options ^= OPTION_FOUND_ROWS;
join->send_records = jt->records;
}
else
{
join->do_send_rows=0;
join->thd->select_limit = HA_POS_ERROR;
DBUG_RETURN(0);
}
}
DBUG_RETURN(-3); // Abort nicely
}
......
This diff is collapsed.
......@@ -80,6 +80,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token NEXT_SYM
%token PREV_SYM
%token SQL_CALC_FOUND_ROWS
%token QUERIES
%token MQH_SYM
%token PER_SYM
%token MAXIMUM
%token EQ
%token EQUAL_SYM
......@@ -556,7 +561,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
opt_outer table_list table_name opt_option opt_place opt_low_priority
opt_attribute opt_attribute_list attribute column_list column_list_id
opt_column_list grant_privileges opt_table user_list grant_option
grant_privilege grant_privilege_list
grant_privilege grant_privilege_list mqh_option
flush_options flush_option insert_lock_option replace_lock_option
equal optional_braces opt_key_definition key_usage_list2
opt_mi_check_type opt_to mi_check_types normal_join
......@@ -2025,7 +2030,12 @@ opt_order_clause:
| order_clause
order_clause:
ORDER_SYM BY { Select->sort_default=1; } order_list
ORDER_SYM BY
{
if (Lex->sql_command==SQLCOM_MULTI_UPDATE)
YYABORT;
Select->sort_default=1;
} order_list
order_list:
order_list ',' order_ident order_dir
......@@ -2061,6 +2071,8 @@ limit_clause:
delete_limit_clause:
/* empty */
{
if (Lex->sql_command==SQLCOM_MULTI_UPDATE)
YYABORT;
Select->select_limit= HA_POS_ERROR;
}
| LIMIT ulonglong_num
......@@ -2290,11 +2302,7 @@ values:
/* Update rows in a table */
update:
UPDATE_SYM opt_low_priority opt_ignore table_name
SET update_list
where_clause
opt_order_clause
delete_limit_clause
UPDATE_SYM
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_UPDATE;
......@@ -2302,6 +2310,7 @@ update:
lex->select->order_list.first=0;
lex->select->order_list.next= (byte**) &lex->select->order_list.first;
}
opt_low_priority opt_ignore join_table_list SET update_list where_clause opt_order_clause delete_limit_clause
update_list:
update_list ',' simple_ident equal expr
......@@ -2353,6 +2362,24 @@ single_multi:
lex->select->table_list.first=0;
lex->select->table_list.next= (byte**) &(lex->select->table_list.first);
} join_table_list where_clause
| FROM table_wild_list
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_MULTI_DELETE;
mysql_init_select(lex);
lex->select->select_limit=HA_POS_ERROR;
lex->auxilliary_table_list.elements=0;
lex->auxilliary_table_list.first=0;
lex->auxilliary_table_list.next= (byte**) &(lex->auxilliary_table_list.first);
}
USING
{
LEX *lex=Lex;
lex->auxilliary_table_list=lex->select_lex.table_list;
lex->select->table_list.elements=0;
lex->select->table_list.first=0;
lex->select->table_list.next= (byte**) &(lex->select->table_list.first);
} join_table_list where_clause
table_wild_list:
......@@ -3267,9 +3294,10 @@ grant:
lex->select->db=0;
lex->ssl_type=SSL_TYPE_NONE;
lex->ssl_cipher=lex->x509_subject=lex->x509_issuer=0;
lex->mqh=0;
}
grant_privileges ON opt_table TO_SYM user_list
require_clause grant_option
require_clause grant_option mqh_option
grant_privileges:
grant_privilege_list {}
......@@ -3460,6 +3488,19 @@ grant_option:
/* empty */ {}
| WITH GRANT OPTION { Lex->grant |= GRANT_ACL;}
mqh_option:
/* empty */ {}
| AND WITH short_or_long_one EQ NUM
{
Lex->mqh=atoi($5.str);
if (Lex->mqh > 65535)
YYABORT;
}
short_or_long_one:
MQH_SYM
| MAXIMUM QUERIES PER_SYM HOUR_SYM
begin:
BEGIN_SYM { Lex->sql_command = SQLCOM_BEGIN;} opt_work
......
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