Changes for the BitKeeper resolve.
One more notice: limiting number of queries per hour for different users has been done on 4.0.1 , but I can port it to 3.23.* This will require only certain changes, like number of columns in user table. I will also make a test case for it, after it is approved by Monty, as result file will depend on the error message text. BitKeeper/etc/ignore: auto-union libmysqld/lib_sql.cc: Auto merged scripts/mysql_install_db.sh: Auto merged sql/item_timefunc.h: Auto merged sql/mysqld.cc: Auto merged sql/sql_acl.cc: Auto merged sql/sql_acl.h: Auto merged sql/sql_select.cc: Auto merged sql/sql_update.cc: Auto merged sql/mysql_priv.h: Auto merged sql/lex.h: Changes for BitKeeper resolve... sql/sql_class.h: Changes for BitKeeper resolve... sql/sql_lex.h: Changes for BitKeeper resolve... sql/sql_parse.cc: Changes for BitKeeper resolve... sql/sql_yacc.yy: Changes for BitKeeper resolve...
This commit is contained in:
commit
ececdf0908
@ -21,6 +21,7 @@
|
||||
.out
|
||||
.snprj/*
|
||||
.vimrc
|
||||
50
|
||||
=6
|
||||
BitKeeper/etc/config
|
||||
BitKeeper/etc/csets
|
||||
@ -413,6 +414,7 @@ sql/mysqld
|
||||
sql/mysqld-purecov
|
||||
sql/mysqld-purify
|
||||
sql/mysqld-quantify
|
||||
sql/new.cc
|
||||
sql/share/*.sys
|
||||
sql/share/charsets/gmon.out
|
||||
sql/share/gmon.out
|
||||
|
@ -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
|
||||
|
24
scripts/mysql_new_fix_privilege_tables.sh
Normal file
24
scripts/mysql_new_fix_privilege_tables.sh
Normal file
@ -0,0 +1,24 @@
|
||||
#!/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 @@ public:
|
||||
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 @@ public:
|
||||
{ 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 @@ public:
|
||||
};
|
||||
|
||||
|
||||
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 @@ public:
|
||||
};
|
||||
|
||||
|
||||
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 @@ public:
|
||||
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);}
|
||||
|
@ -228,6 +228,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},
|
||||
@ -241,6 +242,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},
|
||||
@ -265,6 +267,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},
|
||||
@ -273,6 +276,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},
|
||||
{ "QUERY", SYM(QUERY_SYM),0,0},
|
||||
{ "QUICK", SYM(QUICK),0,0},
|
||||
{ "RAID0", SYM(RAID_0_SYM),0,0},
|
||||
|
@ -512,7 +512,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;
|
||||
|
@ -228,6 +228,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;
|
||||
@ -1960,7 +1961,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
|
||||
@ -241,6 +241,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
|
||||
@ -424,7 +433,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;
|
||||
@ -537,6 +546,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;
|
||||
@ -570,6 +580,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++)
|
||||
@ -583,6 +594,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;
|
||||
@ -612,6 +624,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;
|
||||
@ -619,6 +632,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
|
||||
@ -1207,6 +1221,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)
|
||||
{
|
||||
/*
|
||||
@ -1245,6 +1266,7 @@ end:
|
||||
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,
|
||||
@ -1252,6 +1274,7 @@ end:
|
||||
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);
|
||||
|
@ -670,3 +670,33 @@ public:
|
||||
int do_deletes (bool from_send_error);
|
||||
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_DELETE_MULTI,
|
||||
SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_MULTI_DELETE, SQLCOM_MULTI_UPDATE,
|
||||
SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER, SQLCOM_DO,
|
||||
SQLCOM_END
|
||||
};
|
||||
@ -182,7 +182,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;
|
||||
|
195
sql/sql_parse.cc
195
sql/sql_parse.cc
@ -55,7 +55,7 @@ extern "C" pthread_mutex_t THR_LOCK_keycache;
|
||||
extern "C" int gethostname(char *name, int namelen);
|
||||
#endif
|
||||
|
||||
static int check_for_max_user_connections(const char *user, const char *host);
|
||||
static int check_for_max_user_connections(const char *user, 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);
|
||||
@ -116,6 +116,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(¤t_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(¤t_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:
|
||||
@ -126,6 +215,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;
|
||||
thd->db_length=0;
|
||||
|
||||
@ -138,7 +228,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,
|
||||
@ -169,6 +259,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" :
|
||||
@ -178,7 +270,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, thd->host))
|
||||
check_for_max_user_connections(user, thd->host, max))
|
||||
return -1;
|
||||
if (db && db[0])
|
||||
{
|
||||
@ -198,28 +290,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,
|
||||
@ -228,7 +298,7 @@ void init_max_user_conn(void)
|
||||
}
|
||||
|
||||
|
||||
static int check_for_max_user_connections(const char *user, const char *host)
|
||||
static int check_for_max_user_connections(const char *user, const char *host, uint max)
|
||||
{
|
||||
int error=1;
|
||||
uint temp_len;
|
||||
@ -270,6 +340,9 @@ static int check_for_max_user_connections(const char *user, const char *host)
|
||||
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);
|
||||
@ -308,7 +381,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);
|
||||
@ -324,7 +397,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.
|
||||
@ -863,7 +935,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
||||
mysql_log.write(thd,command,"%s",thd->query);
|
||||
DBUG_PRINT("query",("'%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;
|
||||
}
|
||||
/* thd->query_length is set by mysql_parse() */
|
||||
mysql_parse(thd,thd->query,packet_length);
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
@ -983,6 +1061,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:
|
||||
@ -1145,6 +1225,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;
|
||||
|
||||
thread_safe_increment(com_stat[lex->sql_command],&LOCK_thread_count);
|
||||
switch (lex->sql_command) {
|
||||
@ -1798,6 +1880,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))
|
||||
@ -2151,6 +2286,8 @@ mysql_execute_command(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mqh_used && hash_user_connections.array.buffer == 0)
|
||||
init_max_user_conn();
|
||||
break;
|
||||
}
|
||||
case SQLCOM_FLUSH:
|
||||
|
@ -4732,11 +4732,20 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
|
||||
if (++join->send_records >= join->thd->select_limit && join->do_send_rows)
|
||||
{
|
||||
if (join->select_options & OPTION_FOUND_ROWS)
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,15 @@
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
|
||||
/* Update of records */
|
||||
/* Update of records
|
||||
|
||||
Multi-table updates were introduced by Monty and Sinisa <sinisa@mysql.com>
|
||||
|
||||
*/
|
||||
|
||||
#include "mysql_priv.h"
|
||||
#include "sql_acl.h"
|
||||
#include "sql_select.h"
|
||||
|
||||
/* Return 0 if row hasn't changed */
|
||||
|
||||
@ -340,3 +345,457 @@ int mysql_update(THD *thd,
|
||||
free_io_cache(table);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** update multiple tables from join
|
||||
***************************************************************************/
|
||||
|
||||
multi_update::multi_update(THD *thd_arg, TABLE_LIST *ut, List<Item> &fs,
|
||||
enum enum_duplicates handle_duplicates, thr_lock_type lock_option_arg, uint num)
|
||||
: update_tables (ut), thd(thd_arg), updated(0), found(0), fields(fs), lock_option(lock_option_arg),
|
||||
dupl(handle_duplicates), num_of_tables(num), num_fields(0), num_updated(0) , error(0), do_update(false)
|
||||
{
|
||||
save_time_stamps = (uint *) sql_calloc (sizeof(uint) * num_of_tables);
|
||||
tmp_tables = (TABLE **)NULL;
|
||||
int counter=0;
|
||||
ulong timestamp_query_id;
|
||||
for (TABLE_LIST *dt=ut ; dt ; dt=dt->next,counter++)
|
||||
{
|
||||
TABLE *table=ut->table;
|
||||
(void) ut->table->file->extra(HA_EXTRA_NO_READCHECK);
|
||||
(void) ut->table->file->extra(HA_EXTRA_NO_KEYREAD);
|
||||
dt->table->used_keys=0;
|
||||
if (table->timestamp_field)
|
||||
{
|
||||
// Don't set timestamp column if this is modified
|
||||
timestamp_query_id=table->timestamp_field->query_id;
|
||||
table->timestamp_field->query_id=thd->query_id-1;
|
||||
if (table->timestamp_field->query_id == thd->query_id)
|
||||
table->time_stamp=0;
|
||||
else
|
||||
table->timestamp_field->query_id=timestamp_query_id;
|
||||
}
|
||||
save_time_stamps[counter]=table->time_stamp;
|
||||
}
|
||||
error = 1; // In case we do not reach prepare we have to reset timestamps
|
||||
}
|
||||
|
||||
int
|
||||
multi_update::prepare(List<Item> &values)
|
||||
{
|
||||
DBUG_ENTER("multi_update::prepare");
|
||||
do_update = true;
|
||||
thd->count_cuted_fields=1;
|
||||
thd->cuted_fields=0L;
|
||||
thd->proc_info="updating the main table";
|
||||
TABLE_LIST *table_ref;
|
||||
|
||||
if (thd->options & OPTION_SAFE_UPDATES)
|
||||
{
|
||||
for (table_ref=update_tables; table_ref; table_ref=table_ref->next)
|
||||
{
|
||||
TABLE *table=table_ref->table;
|
||||
if ((thd->options & OPTION_SAFE_UPDATES) && !table->quick_keys)
|
||||
{
|
||||
my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,MYF(0));
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Here I have to connect fields with tables and only update tables that need to be updated ...
|
||||
|
||||
// I calculate num_updated and fill-up table_sequence
|
||||
// Set table_list->shared to true or false, depending on whether table is to be updated or not
|
||||
Item_field *item;
|
||||
List_iterator<Item> it(fields);
|
||||
num_fields=fields.elements;
|
||||
field_sequence = (uint *) sql_alloc(sizeof(uint)*num_fields);
|
||||
uint *int_ptr=field_sequence;
|
||||
while ((item= (Item_field *)it++))
|
||||
{
|
||||
unsigned int counter=0;
|
||||
for (table_ref=update_tables; table_ref; table_ref=table_ref->next, counter++)
|
||||
{
|
||||
if (table_ref->table == item->field->table && !table_ref->shared)
|
||||
{
|
||||
num_updated++;
|
||||
table_ref->shared=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!table_ref)
|
||||
{
|
||||
error = 1; // A proper error message is due here
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
else
|
||||
*int_ptr++=counter;
|
||||
}
|
||||
if (!num_updated)
|
||||
{
|
||||
error = 1; // A proper error message is due here
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
// Here, I have to allocate the array of temporary tables
|
||||
// I have to treat a case of num_updated=1 differently in send_data() method.
|
||||
if (num_updated > 1)
|
||||
{
|
||||
tmp_tables = (TABLE **) sql_calloc(sizeof(TABLE *) * (num_updated - 1));
|
||||
infos = (COPY_INFO *) sql_calloc(sizeof(COPY_INFO) * (num_updated - 1));
|
||||
fields_by_tables = (List_item **)sql_calloc(sizeof(List_item *) * num_updated);
|
||||
unsigned int counter;
|
||||
List<Item> *temp_fields;
|
||||
for (table_ref=update_tables, counter = 0; table_ref; table_ref=table_ref->next)
|
||||
{
|
||||
if (!table_ref->shared)
|
||||
continue;
|
||||
// Here we have to add row offset as an additional field ...
|
||||
if (!(temp_fields = (List_item *)sql_calloc(sizeof(List_item))))
|
||||
{
|
||||
error = 1; // A proper error message is due here
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
temp_fields->empty();
|
||||
it.rewind(); int_ptr=field_sequence;
|
||||
while ((item= (Item_field *)it++))
|
||||
{
|
||||
if (*int_ptr++ == counter)
|
||||
temp_fields->push_back(item);
|
||||
}
|
||||
if (counter)
|
||||
{
|
||||
Field_string offset(table_ref->table->file->ref_length,false,"offset",table_ref->table,true);
|
||||
temp_fields->push_front(new Item_field(((Field *)&offset)));
|
||||
// Here I make tmp tables
|
||||
int cnt=counter-1;
|
||||
TMP_TABLE_PARAM tmp_table_param;
|
||||
bzero((char*) &tmp_table_param,sizeof(tmp_table_param));
|
||||
tmp_table_param.field_count=temp_fields->elements;
|
||||
if (!(tmp_tables[cnt]=create_tmp_table(thd, &tmp_table_param, *temp_fields,
|
||||
(ORDER*) 0, 1, 0, 0, TMP_TABLE_ALL_COLUMNS)))
|
||||
{
|
||||
error = 1; // A proper error message is due here
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
tmp_tables[cnt]->file->extra(HA_EXTRA_WRITE_CACHE);
|
||||
tmp_tables[cnt]->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
|
||||
infos[cnt].handle_duplicates=DUP_IGNORE;
|
||||
temp_fields->pop(); // because we shall use those for values only ...
|
||||
}
|
||||
fields_by_tables[counter]=temp_fields;
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
error = 0; // Timestamps do not need to be restored, so far ...
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
multi_update::initialize_tables(JOIN *join)
|
||||
{
|
||||
/* We skip it as it only makes a mess ...........
|
||||
TABLE_LIST *walk;
|
||||
table_map tables_to_update_from=0;
|
||||
for (walk= update_tables ; walk ; walk=walk->next)
|
||||
tables_to_update_from|= walk->table->map;
|
||||
|
||||
walk= update_tables;
|
||||
for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables;
|
||||
tab < end;
|
||||
tab++)
|
||||
{
|
||||
if (tab->table->map & tables_to_update_from)
|
||||
{
|
||||
We are going to update from this table
|
||||
walk->table=tab->table;
|
||||
walk=walk->next;
|
||||
if (tab == join->join_tab)
|
||||
tab->table->no_keyread=1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
multi_update::~multi_update()
|
||||
{
|
||||
/* Add back EXTRA_READCHECK; In 4.0.1 we shouldn't need this anymore */
|
||||
int counter = 0;
|
||||
for (table_being_updated=update_tables ;
|
||||
table_being_updated ;
|
||||
counter++, table_being_updated=table_being_updated->next)
|
||||
{
|
||||
TABLE *table=table_being_updated->table;
|
||||
(void)table->file->extra(HA_EXTRA_READCHECK);
|
||||
if (error)
|
||||
table->time_stamp=save_time_stamps[counter];
|
||||
}
|
||||
if (tmp_tables)
|
||||
for (uint counter = 0; counter < num_updated-1; counter++)
|
||||
if (tmp_tables[counter])
|
||||
free_tmp_table(thd,tmp_tables[counter]);
|
||||
}
|
||||
|
||||
|
||||
bool multi_update::send_data(List<Item> &values)
|
||||
{
|
||||
List<Item> real_values(values);
|
||||
for (uint counter = 0; counter < fields.elements; counter++)
|
||||
real_values.pop();
|
||||
// We have skipped fields ....
|
||||
if (num_updated == 1)
|
||||
{
|
||||
for (table_being_updated=update_tables ;
|
||||
table_being_updated ;
|
||||
table_being_updated=table_being_updated->next)
|
||||
{
|
||||
if (!table_being_updated->shared)
|
||||
continue;
|
||||
TABLE *table=table_being_updated->table;
|
||||
/* Check if we are using outer join and we didn't find the row */
|
||||
if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED))
|
||||
return 0;
|
||||
table->file->position(table->record[0]);
|
||||
// Only one table being updated receives a completely different treatment
|
||||
table->status|= STATUS_UPDATED;
|
||||
store_record(table,1);
|
||||
if (fill_record(fields,real_values))
|
||||
return 1;
|
||||
found++;
|
||||
if (/* compare_record(table, query_id) && */
|
||||
!(error=table->file->update_row(table->record[1], table->record[0])))
|
||||
updated++;
|
||||
return error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int secure_counter= -1;
|
||||
for (table_being_updated=update_tables ;
|
||||
table_being_updated ;
|
||||
table_being_updated=table_being_updated->next, secure_counter++)
|
||||
{
|
||||
if (!table_being_updated->shared)
|
||||
continue;
|
||||
|
||||
TABLE *table=table_being_updated->table;
|
||||
/* Check if we are using outer join and we didn't find the row */
|
||||
if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED))
|
||||
continue;
|
||||
table->file->position(table->record[0]);
|
||||
Item *item;
|
||||
List_iterator<Item> it(real_values);
|
||||
List <Item> values_by_table;
|
||||
uint *int_ptr=field_sequence;
|
||||
while ((item= (Item *)it++))
|
||||
{
|
||||
if (*int_ptr++ == (uint) (secure_counter + 1))
|
||||
values_by_table.push_back(item);
|
||||
}
|
||||
// Here I am breaking values as per each table
|
||||
if (secure_counter < 0)
|
||||
{
|
||||
table->status|= STATUS_UPDATED;
|
||||
store_record(table,1);
|
||||
if (fill_record(*fields_by_tables[0],values_by_table))
|
||||
return 1;
|
||||
found++;
|
||||
if (/*compare_record(table, query_id) && */
|
||||
!(error=table->file->update_row(table->record[1], table->record[0])))
|
||||
updated++;
|
||||
else
|
||||
{
|
||||
table->file->print_error(error,MYF(0));
|
||||
if (!error) error=1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Here I insert into each temporary table
|
||||
values_by_table.push_front(new Item_string(table->file->ref,table->file->ref_length));
|
||||
fill_record(tmp_tables[secure_counter]->field,values_by_table);
|
||||
error= write_record(tmp_tables[secure_counter],&(infos[secure_counter]));
|
||||
if (error)
|
||||
{
|
||||
error=-1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Return true if some table is not transaction safe */
|
||||
|
||||
static bool some_table_is_not_transaction_safe (TABLE_LIST *tl)
|
||||
{
|
||||
for (; tl ; tl=tl->next)
|
||||
{
|
||||
if (!(tl->table->file->has_transactions()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void multi_update::send_error(uint errcode,const char *err)
|
||||
{
|
||||
/* First send error what ever it is ... */
|
||||
::send_error(&thd->net,errcode,err);
|
||||
|
||||
/* reset used flags */
|
||||
update_tables->table->no_keyread=0;
|
||||
|
||||
/* If nothing updated return */
|
||||
if (!updated)
|
||||
return;
|
||||
/* Below can happen when thread is killed early ... */
|
||||
if (!table_being_updated)
|
||||
table_being_updated=update_tables;
|
||||
|
||||
/*
|
||||
If rows from the first table only has been updated and it is transactional,
|
||||
just do rollback.
|
||||
The same if all tables are transactional, regardless of where we are.
|
||||
In all other cases do attempt updates ...
|
||||
*/
|
||||
if ((table_being_updated->table->file->has_transactions() &&
|
||||
table_being_updated == update_tables) ||
|
||||
!some_table_is_not_transaction_safe(update_tables->next))
|
||||
ha_rollback_stmt(thd);
|
||||
else if (do_update)
|
||||
VOID(do_updates(true));
|
||||
}
|
||||
|
||||
|
||||
int multi_update::do_updates (bool from_send_error)
|
||||
{
|
||||
int error = 0, counter = 0;
|
||||
|
||||
if (num_updated == 1) return 0;
|
||||
if (from_send_error)
|
||||
{
|
||||
/* Found out table number for 'table_being_updated' */
|
||||
for (TABLE_LIST *aux=update_tables;
|
||||
aux != table_being_updated;
|
||||
aux=aux->next)
|
||||
counter++;
|
||||
}
|
||||
else
|
||||
table_being_updated = update_tables;
|
||||
|
||||
do_update = false;
|
||||
for (table_being_updated=table_being_updated->next;
|
||||
table_being_updated ;
|
||||
table_being_updated=table_being_updated->next, counter++)
|
||||
{
|
||||
if (!table_being_updated->shared)
|
||||
continue;
|
||||
|
||||
TABLE *table = table_being_updated->table;
|
||||
TABLE *tmp_table=tmp_tables[counter];
|
||||
if (tmp_table->file->extra(HA_EXTRA_NO_CACHE))
|
||||
{
|
||||
error=1;
|
||||
break;
|
||||
}
|
||||
List<Item> list;
|
||||
Field **ptr=tmp_table->field,*field;
|
||||
// This is supposed to be something like insert_fields
|
||||
thd->used_tables|=tmp_table->map;
|
||||
while ((field = *ptr++))
|
||||
{
|
||||
list.push_back((Item *)new Item_field(field));
|
||||
if (field->query_id == thd->query_id)
|
||||
thd->dupp_field=field;
|
||||
field->query_id=thd->query_id;
|
||||
tmp_table->used_keys&=field->part_of_key;
|
||||
}
|
||||
tmp_table->used_fields=tmp_table->fields;
|
||||
error=0; list.pop(); // we get position some other way ...
|
||||
error = tmp_table->file->rnd_init(1);
|
||||
if (error)
|
||||
return error;
|
||||
bool not_trans_safe = some_table_is_not_transaction_safe(update_tables);
|
||||
while (!(error=tmp_table->file->rnd_next(tmp_table->record[0])) &&
|
||||
(!thd->killed || from_send_error || not_trans_safe))
|
||||
{
|
||||
found++;
|
||||
error= table->file->rnd_pos(table->record[0], (*(tmp_table->field))->ptr);
|
||||
if (error)
|
||||
return error;
|
||||
table->status|= STATUS_UPDATED;
|
||||
store_record(table,1);
|
||||
error= fill_record(*fields_by_tables[counter + 1],list) /*|| compare_record(table, query_id)*/ ||
|
||||
table->file->update_row(table->record[1],table->record[0]);
|
||||
if (error)
|
||||
{
|
||||
table->file->print_error(error,MYF(0));
|
||||
break;
|
||||
}
|
||||
else
|
||||
updated++;
|
||||
}
|
||||
if (error == HA_ERR_END_OF_FILE)
|
||||
error = 0;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
bool multi_update::send_eof()
|
||||
{
|
||||
thd->proc_info="updating the reference tables"; /* out: 1 if error, 0 if success */
|
||||
|
||||
/* Does updates for the last n - 1 tables, returns 0 if ok */
|
||||
int error = do_updates(false); /* do_updates returns 0 if success */
|
||||
|
||||
/* reset used flags */
|
||||
update_tables->table->no_keyread=0;
|
||||
if (error == -1) error = 0;
|
||||
thd->proc_info="end";
|
||||
if (error)
|
||||
send_error(error,"An error occured in multi-table update");
|
||||
|
||||
/* Write the SQL statement to the binlog if we updated
|
||||
rows and we succeeded, or also in an error case when there
|
||||
was a non-transaction-safe table involved, since
|
||||
modifications in it cannot be rolled back. */
|
||||
|
||||
if (updated &&
|
||||
(!error || some_table_is_not_transaction_safe(update_tables)))
|
||||
{
|
||||
mysql_update_log.write(thd,thd->query,thd->query_length);
|
||||
Query_log_event qinfo(thd, thd->query);
|
||||
|
||||
/* mysql_bin_log is not open if binlogging or replication
|
||||
is not used */
|
||||
|
||||
if (mysql_bin_log.is_open() && mysql_bin_log.write(&qinfo) &&
|
||||
!some_table_is_not_transaction_safe(update_tables))
|
||||
error=1; /* Log write failed: roll back
|
||||
the SQL statement */
|
||||
|
||||
/* Commit or rollback the current SQL statement */
|
||||
|
||||
VOID(ha_autocommit_or_rollback(thd,error > 0));
|
||||
}
|
||||
else
|
||||
error=0; // this can happen only if it is end of file error
|
||||
if (!error) // if the above log write did not fail ...
|
||||
{
|
||||
char buff[80];
|
||||
sprintf(buff,ER(ER_UPDATE_INFO), (long) found, (long) updated,
|
||||
(long) thd->cuted_fields);
|
||||
::send_ok(&thd->net,
|
||||
(thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
|
||||
thd->insert_id_used ? thd->insert_id() : 0L,buff);
|
||||
}
|
||||
thd->count_cuted_fields=0;
|
||||
return 0;
|
||||
}
|
||||
|
@ -81,6 +81,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
|
||||
@ -570,7 +575,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
|
||||
@ -2094,7 +2099,12 @@ opt_order_clause:
|
||||
| order_clause
|
||||
|
||||
order_clause:
|
||||
ORDER_SYM BY 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
|
||||
@ -2124,6 +2134,8 @@ limit_clause:
|
||||
delete_limit_clause:
|
||||
/* empty */
|
||||
{
|
||||
if (Lex->sql_command==SQLCOM_MULTI_UPDATE)
|
||||
YYABORT;
|
||||
Select->select_limit= HA_POS_ERROR;
|
||||
}
|
||||
| LIMIT ulonglong_num
|
||||
@ -2371,7 +2383,7 @@ values:
|
||||
/* Update rows in a table */
|
||||
|
||||
update:
|
||||
UPDATE_SYM opt_low_priority opt_ignore table_name
|
||||
UPDATE_SYM
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
lex->sql_command = SQLCOM_UPDATE;
|
||||
@ -2379,10 +2391,7 @@ update:
|
||||
lex->select->order_list.first=0;
|
||||
lex->select->order_list.next= (byte**) &lex->select->order_list.first;
|
||||
}
|
||||
SET update_list
|
||||
where_clause
|
||||
opt_order_clause
|
||||
delete_limit_clause
|
||||
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
|
||||
@ -2434,6 +2443,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:
|
||||
@ -3372,9 +3399,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 {}
|
||||
@ -3565,6 +3593,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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user