summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <Sinisa@sinisa.nasamreza.org>2001-12-26 17:42:06 +0200
committerunknown <Sinisa@sinisa.nasamreza.org>2001-12-26 17:42:06 +0200
commitececdf09088c0bcf142ae4737c724bf718952fab (patch)
tree89ec1a3a0a7c33283b19588a9a564d110058eed0
parent20aa6caa56670950d9430ca3a62c069ffe0bdd9d (diff)
parent71ce58a3b1f0b2d4ec776bc7864076603b3170f4 (diff)
downloadmariadb-git-ececdf09088c0bcf142ae4737c724bf718952fab.tar.gz
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...
-rw-r--r--.bzrignore2
-rw-r--r--libmysqld/lib_sql.cc4
-rw-r--r--mysql-test/r/multi_update.result19
-rw-r--r--mysql-test/t/multi_update.test25
-rw-r--r--scripts/mysql_install_db.sh15
-rw-r--r--scripts/mysql_new_fix_privilege_tables.sh24
-rw-r--r--sql/item_timefunc.h41
-rw-r--r--sql/lex.h4
-rw-r--r--sql/mysql_priv.h2
-rw-r--r--sql/mysqld.cc3
-rw-r--r--sql/sql_acl.cc27
-rw-r--r--sql/sql_acl.h2
-rw-r--r--sql/sql_class.h30
-rw-r--r--sql/sql_lex.h4
-rw-r--r--sql/sql_parse.cc197
-rw-r--r--sql/sql_select.cc15
-rw-r--r--sql/sql_update.cc461
-rw-r--r--sql/sql_yacc.yy57
18 files changed, 846 insertions, 86 deletions
diff --git a/.bzrignore b/.bzrignore
index 7c1105386e2..0b9549a818c 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -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
diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc
index f9d5ccfec72..ed666659af1 100644
--- a/libmysqld/lib_sql.cc
+++ b/libmysqld/lib_sql.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,
diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result
index f8bc19dd065..b1423667c44 100644
--- a/mysql-test/r/multi_update.result
+++ b/mysql-test/r/multi_update.result
@@ -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
diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test
index 0fc4d923469..11697f794a9 100644
--- a/mysql-test/t/multi_update.test
+++ b/mysql-test/t/multi_update.test
@@ -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;
diff --git a/scripts/mysql_install_db.sh b/scripts/mysql_install_db.sh
index bf577a6cc5c..473b182de25 100644
--- a/scripts/mysql_install_db.sh
+++ b/scripts/mysql_install_db.sh
@@ -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
diff --git a/scripts/mysql_new_fix_privilege_tables.sh b/scripts/mysql_new_fix_privilege_tables.sh
new file mode 100644
index 00000000000..71c6de4ffd1
--- /dev/null
+++ b/scripts/mysql_new_fix_privilege_tables.sh
@@ -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
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index bb33e4541aa..fbf3b7f05e0 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -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);}
diff --git a/sql/lex.h b/sql/lex.h
index 59c71f20cd7..887fcb78754 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -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},
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 739991f55b1..ba39199a1ad 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -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;
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index e4e24c5ce53..1aa030dba9e 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -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
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 5da31df81ab..091db4e4b39 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -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();
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index 4453194e0b8..3b8616a660a 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -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);
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 1eac7843e96..b608892f7af 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -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();
+ };
+
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 4fc98963874..4c6a24ae420 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -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;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 3662aa301e2..5bebc66e5b6 100644
--- a/sql/sql_parse.cc
+++ b/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(&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:
@@ -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;
@@ -269,7 +339,10 @@ static int check_for_max_user_connections(const char *user, const char *host)
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);
@@ -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:
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index c168ce775fc..60a40730afc 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -4733,9 +4733,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
}
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 4cf5366a8ba..5e36d22744b 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -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;
+}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 5282fb157e6..06f4ba88248 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -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