diff options
author | unknown <Sinisa@sinisa.nasamreza.org> | 2001-12-26 17:42:06 +0200 |
---|---|---|
committer | unknown <Sinisa@sinisa.nasamreza.org> | 2001-12-26 17:42:06 +0200 |
commit | ececdf09088c0bcf142ae4737c724bf718952fab (patch) | |
tree | 89ec1a3a0a7c33283b19588a9a564d110058eed0 | |
parent | 20aa6caa56670950d9430ca3a62c069ffe0bdd9d (diff) | |
parent | 71ce58a3b1f0b2d4ec776bc7864076603b3170f4 (diff) | |
download | mariadb-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-- | .bzrignore | 2 | ||||
-rw-r--r-- | libmysqld/lib_sql.cc | 4 | ||||
-rw-r--r-- | mysql-test/r/multi_update.result | 19 | ||||
-rw-r--r-- | mysql-test/t/multi_update.test | 25 | ||||
-rw-r--r-- | scripts/mysql_install_db.sh | 15 | ||||
-rw-r--r-- | scripts/mysql_new_fix_privilege_tables.sh | 24 | ||||
-rw-r--r-- | sql/item_timefunc.h | 41 | ||||
-rw-r--r-- | sql/lex.h | 4 | ||||
-rw-r--r-- | sql/mysql_priv.h | 2 | ||||
-rw-r--r-- | sql/mysqld.cc | 3 | ||||
-rw-r--r-- | sql/sql_acl.cc | 27 | ||||
-rw-r--r-- | sql/sql_acl.h | 2 | ||||
-rw-r--r-- | sql/sql_class.h | 30 | ||||
-rw-r--r-- | sql/sql_lex.h | 4 | ||||
-rw-r--r-- | sql/sql_parse.cc | 197 | ||||
-rw-r--r-- | sql/sql_select.cc | 15 | ||||
-rw-r--r-- | sql/sql_update.cc | 461 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 57 |
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(¤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; @@ -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 |