diff options
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r-- | sql/sql_parse.cc | 1679 |
1 files changed, 1081 insertions, 598 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index fa4a4fd4f3b..e649f109a0d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,16 +14,38 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef EMBEDDED_LIBRARY +#define net_read_timeout net_read_timeout1 +#define net_write_timeout net_write_timeout1 +#endif #include "mysql_priv.h" #include "sql_acl.h" #include "sql_repl.h" +#include "repl_failsafe.h" #include <m_ctype.h> #include <thr_alarm.h> #include <myisam.h> #include <my_dir.h> #include <assert.h> +#ifdef HAVE_OPENSSL +/* + Without SSL the handshake consists of one packet. This packet + has both client capabilites and scrambled password. + With SSL the handshake might consist of two packets. If the first + packet (client capabilities) has CLIENT_SSL flag set, we have to + switch to SSL and read the second packet. The scrambled password + is in the second packet and client_capabilites field will be ignored. + Maybe it is better to accept flags other than CLIENT_SSL from the + second packet? +*/ +#define SSL_HANDSHAKE_SIZE 2 +#define NORMAL_HANDSHAKE_SIZE 6 +#define MIN_HANDSHAKE_SIZE 2 +#else +#define MIN_HANDSHAKE_SIZE 6 +#endif /* HAVE_OPENSSL */ #define SCRAMBLE_LENGTH 8 @@ -33,16 +55,18 @@ 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, int u_length, - const char *host); -static void decrease_user_connections(const char *user, const char *host); +static int check_for_max_user_connections(UC *uc); +static bool check_mqh(THD *thd); +static void decrease_user_connections(UC *uc); static bool check_db_used(THD *thd,TABLE_LIST *tables); static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *tables); -static bool check_dup(THD *thd,const char *db,const char *name, - TABLE_LIST *tables); +static bool check_dup(const char *db, const char *name, TABLE_LIST *tables); static void mysql_init_query(THD *thd); static void remove_escape(char *name); static void refresh_status(void); +static bool append_file_to_dir(THD *thd, char **filename_ptr, + char *table_name); +static bool create_total_list(THD *thd, LEX *lex, TABLE_LIST **result); const char *any_db="*any*"; // Special symbol for check_access @@ -50,13 +74,13 @@ const char *command_name[]={ "Sleep", "Quit", "Init DB", "Query", "Field List", "Create DB", "Drop DB", "Refresh", "Shutdown", "Statistics", "Processlist", "Connect","Kill","Debug","Ping","Time","Delayed_insert","Change user", - "Binlog Dump","Table Dump", "Connect Out" + "Binlog Dump","Table Dump", "Connect Out", "Register Slave" }; bool volatile abort_slave = 0; #ifdef HAVE_OPENSSL -extern VioSSLAcceptorFd* ssl_acceptor_fd; +extern struct st_VioSSLAcceptorFd * ssl_acceptor_fd; #endif /* HAVE_OPENSSL */ #ifdef __WIN__ @@ -66,8 +90,8 @@ static void test_signal(int sig_ptr) MessageBox(NULL,"Test signal","DBUG",MB_OK); #endif #if defined(OS2) - fprintf( stderr, "Test signal %d\n", sig_ptr); - fflush( stderr); + fprintf(stderr, "Test signal %d\n", sig_ptr); + fflush(stderr); #endif } static void init_signals(void) @@ -93,43 +117,98 @@ inline bool end_active_trans(THD *thd) } +static HASH hash_user_connections; +extern pthread_mutex_t LOCK_user_conn; + +static int get_or_create_user_conn(THD *thd, const char *user, + const char *host, + uint max_questions) +{ + int return_val=0; + uint temp_len; + char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; + struct user_conn *uc; + + DBUG_ASSERT(user != 0); + DBUG_ASSERT(host != 0); + + temp_len= (uint) (strxnmov(temp_user, sizeof(temp_user)-1, user, "@", host, + NullS) - temp_user); + (void) pthread_mutex_lock(&LOCK_user_conn); + if (!(uc = (struct user_conn *) hash_search(&hash_user_connections, + (byte*) temp_user, temp_len))) + { + /* First connection for user; Create a user connection object */ + if (!(uc= ((struct user_conn*) + my_malloc(sizeof(struct user_conn) + temp_len+1, + MYF(MY_WME))))) + { + send_error(¤t_thd->net, 0, NullS); // Out of memory + return_val=1; + goto end; + } + 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_questions=max_questions; + uc->intime=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 + return_val=1; + goto end; + } + } + thd->user_connect=uc; +end: + (void) pthread_mutex_unlock(&LOCK_user_conn); + return return_val; + +} + + /* -** Check if user is ok -** Updates: -** thd->user, thd->master_access, thd->priv_user, thd->db, thd->db_access + Check if user is ok + Updates: + thd->user, thd->master_access, thd->priv_user, thd->db, thd->db_access */ 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_questions=0; thd->db=0; + thd->db_length=0; if (!(thd->user = my_strdup(user, MYF(0)))) { send_error(net,ER_OUT_OF_RESOURCES); return 1; } - thd->master_access=acl_getroot(thd->host, thd->ip, thd->user, + thd->master_access=acl_getroot(thd, thd->host, thd->ip, thd->user, passwd, thd->scramble, &thd->priv_user, protocol_version == 9 || !(thd->client_capabilities & - CLIENT_LONG_PASSWORD)); - DBUG_PRINT("general", + CLIENT_LONG_PASSWORD),&max_questions); + 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, - thd->host ? thd->host : thd->ip, thd->priv_user, + thd->host_or_ip, thd->priv_user, passwd[0] ? "yes": "no", thd->master_access, thd->db ? thd->db : "*none*")); if (thd->master_access & NO_ACCESS) { net_printf(net, ER_ACCESS_DENIED_ERROR, thd->user, - thd->host ? thd->host : thd->ip, + thd->host_or_ip, passwd[0] ? ER(ER_YES) : ER(ER_NO)); mysql_log.write(thd,COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR), thd->user, - thd->host ? thd->host : thd->ip ? thd->ip : "unknown ip", + thd->host_or_ip, passwd[0] ? ER(ER_YES) : ER(ER_NO)); return(1); // Error already given } @@ -150,17 +229,20 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, (char*) "%s@%s on %s" : (char*) "%s@%s as anonymous on %s"), user, - thd->host ? thd->host : thd->ip ? thd->ip : "unknown ip", + thd->host_or_ip, db ? db : (char*) ""); thd->db_access=0; - if (max_user_connections && - check_for_max_user_connections(user, strlen(user), thd->host)) + /* Don't allow user to connect if he has done too many queries */ + if ((max_questions || max_user_connections) && get_or_create_user_conn(thd,user,thd->host_or_ip,max_questions)) + return -1; + if (max_user_connections && thd->user_connect && + check_for_max_user_connections(thd->user_connect)) return -1; if (db && db[0]) { bool error=test(mysql_change_db(thd,db)); - if (error) - decrease_user_connections(thd->user,thd->host); + if (error && thd->user_connect) + decrease_user_connections(thd->user_connect); return error; } else @@ -169,19 +251,10 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, } /* -** check for maximum allowable user connections -** if mysql server is started with corresponding -** variable that is greater then 0 + Check for maximum allowable user connections, if the mysqld server is + started with corresponding 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))) { @@ -189,8 +262,6 @@ static byte* get_key_conn(user_conn *buff, uint *length, return (byte*) buff->user; } -#define DEF_USER_COUNT 50 - static void free_user(struct user_conn *uc) { my_free((char*) uc,MYF(0)); @@ -198,100 +269,44 @@ static void free_user(struct user_conn *uc) void init_max_user_conn(void) { - (void) hash_init(&hash_user_connections,DEF_USER_COUNT,0,0, + (void) hash_init(&hash_user_connections,max_connections,0,0, (hash_get_key) get_key_conn, (void (*)(void*)) free_user, 0); } -static int check_for_max_user_connections(const char *user, int u_length, - const char *host) +static int check_for_max_user_connections(UC *uc) { - int error=1; - uint temp_len; - char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; - struct user_conn *uc; - if (!user) - user=""; - if (!host) - host=""; + int error=0; DBUG_ENTER("check_for_max_user_connections"); - DBUG_PRINT("enter",("user: '%s' host: '%s'", user, host)); - - temp_len= (uint) (strxnmov(temp_user, sizeof(temp_user), user, "@", host, - NullS) - temp_user); - (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 connections */ - { - if (max_user_connections == (uint) uc->connections) - { - net_printf(&(current_thd->net),ER_TOO_MANY_USER_CONNECTIONS, temp_user); - goto end; - } - uc->connections++; - } - else + + if (max_user_connections <= (uint) uc->connections) { - /* the user is not found in the cache; Insert it */ - 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 - goto end; - } - uc->user=(char*) (uc+1); - memcpy(uc->user,temp_user,temp_len+1); - uc->len = temp_len; - uc->connections = 1; - if (hash_insert(&hash_user_connections, (byte*) uc)) - { - my_free((char*) uc,0); - send_error(¤t_thd->net, 0, NullS); // Out of memory - goto end; - } + net_printf(&(current_thd->net),ER_TOO_MANY_USER_CONNECTIONS, uc->user); + error=1; + goto end; } - error=0; + uc->connections++; end: - (void) pthread_mutex_unlock(&LOCK_user_conn); DBUG_RETURN(error); } -static void decrease_user_connections(const char *user, const char *host) +static void decrease_user_connections(UC *uc) { - char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; - int temp_len; - struct user_conn *uc; if (!max_user_connections) return; - if (!user) - user=""; - if (!host) - host=""; - DBUG_ENTER("decrease_user_connections"); - DBUG_PRINT("enter",("user: '%s' host: '%s'", user, host)); - temp_len= (uint) (strxnmov(temp_user, sizeof(temp_user), user, "@", host, - NullS) - temp_user); - (void) pthread_mutex_lock(&LOCK_user_conn); + DBUG_ENTER("decrease_user_connections"); - uc = (struct user_conn *) hash_search(&hash_user_connections, - (byte*) temp_user, temp_len); - 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) pthread_mutex_lock(&LOCK_user_conn); (void) hash_delete(&hash_user_connections,(byte*) uc); + (void) pthread_mutex_unlock(&LOCK_user_conn); } -end: - (void) pthread_mutex_unlock(&LOCK_user_conn); DBUG_VOID_RETURN; } @@ -303,22 +318,93 @@ void free_max_user_conn(void) /* -** check connnetion and get priviliges -** returns 0 on ok, -1 < if error is given > 0 on error. + Check if maximum queries per hour limit has been reached + returns 0 if OK. + + In theory we would need a mutex in the UC structure for this to be 100 % + safe, but as the worst scenario is that we would miss counting a couple of + queries, this isn't critical. */ +static bool check_mqh(THD *thd) +{ + bool error=0; + DBUG_ENTER("check_mqh"); + UC *uc=thd->user_connect; + DBUG_ASSERT(uc != 0); + + bool my_start = thd->start_time != 0; + time_t check_time = (my_start) ? thd->start_time : time(NULL); + if (check_time - uc->intime >= 3600) + { + (void) pthread_mutex_lock(&LOCK_user_conn); + uc->questions=1; + uc->intime=check_time; + (void) pthread_mutex_unlock(&LOCK_user_conn); + } + else if (uc->max_questions && ++(uc->questions) > uc->max_questions) + { + net_printf(&thd->net, ER_USER_LIMIT_REACHED, uc->user, "max_questions", + (long) uc->max_questions); + error=1; + goto end; + } + +end: + DBUG_RETURN(error); +} + + +static void reset_mqh(THD *thd, LEX_USER *lu, uint mq) +{ + + (void) pthread_mutex_lock(&LOCK_user_conn); + if (lu) // for GRANT + { + UC *uc; + uint temp_len=lu->user.length+lu->host.length+2; + char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; + + memcpy(temp_user,lu->user.str,lu->user.length); + memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length); + temp_user[lu->user.length]=temp_user[temp_len-1]=0; + if ((uc = (struct user_conn *) hash_search(&hash_user_connections, + (byte*) temp_user, temp_len))) + { + uc->questions=0; + uc->max_questions=mq; + } + } + else // for FLUSH PRIVILEGES + { + for (uint idx=0;idx < hash_user_connections.records; idx++) + { + char user[USERNAME_LENGTH+1]; + char *where; + UC *uc=(struct user_conn *) hash_element(&hash_user_connections, idx); + where=strchr(uc->user,'@'); + strmake(user,uc->user,where - uc->user); + uc->max_questions=get_mqh(user,where+1); + } + } + (void) pthread_mutex_unlock(&LOCK_user_conn); +} + + +/* + Check connnetion and get priviliges + Returns 0 on ok, -1 < if error is given > 0 on error. +*/ static int check_connections(THD *thd) { uint connect_errors=0; NET *net= &thd->net; - /* - ** store the connection details - */ + /* Store the connection details */ DBUG_PRINT("info", (("check_connections called by thread %d"), thd->thread_id)); - DBUG_PRINT("general",("New connection received on %s", + DBUG_PRINT("info",("New connection received on %s", vio_description(net->vio))); if (!thd->host) // If TCP/IP connection { @@ -328,6 +414,7 @@ check_connections(THD *thd) return (ER_BAD_HOST_ERROR); if (!(thd->ip = my_strdup(ip,MYF(0)))) return (ER_OUT_OF_RESOURCES); + thd->host_or_ip=thd->ip; #if !defined(HAVE_SYS_UN_H) || defined(HAVE_mit_thread) /* Fast local hostname resolve for Win32 */ if (!strcmp(thd->ip,"127.0.0.1")) @@ -341,65 +428,50 @@ check_connections(THD *thd) if (connect_errors > max_connect_errors) return(ER_HOST_IS_BLOCKED); } - DBUG_PRINT("general",("Host: %s ip: %s", - thd->host ? thd->host : "unknown host", - thd->ip ? thd->ip : "unknown ip")); + DBUG_PRINT("info",("Host: %s ip: %s", + thd->host ? thd->host : "unknown host", + thd->ip ? thd->ip : "unknown ip")); if (acl_check_host(thd->host,thd->ip)) return(ER_HOST_NOT_PRIVILEGED); } else /* Hostname given means that the connection was on a socket */ { - DBUG_PRINT("general",("Host: %s",thd->host)); + DBUG_PRINT("info",("Host: %s",thd->host)); + thd->host_or_ip=thd->host; thd->ip=0; bzero((char*) &thd->remote,sizeof(struct sockaddr)); } vio_keepalive(net->vio, TRUE); - /* nasty, but any other way? */ - uint pkt_len = 0; + ulong pkt_len=0; { /* buff[] needs to big enough to hold the server_version variable */ char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH+32],*end; int client_flags = CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB; + if (opt_using_transactions) client_flags|=CLIENT_TRANSACTIONS; #ifdef HAVE_COMPRESS client_flags |= CLIENT_COMPRESS; #endif /* HAVE_COMPRESS */ +#ifdef HAVE_OPENSSL + if (ssl_acceptor_fd) + client_flags |= CLIENT_SSL; /* Wow, SSL is avalaible! */ +#endif /* HAVE_OPENSSL */ - end=strmov(buff,server_version)+1; + end=strnmov(buff,server_version,SERVER_VERSION_LENGTH)+1; int4store((uchar*) end,thd->thread_id); end+=4; memcpy(end,thd->scramble,SCRAMBLE_LENGTH+1); end+=SCRAMBLE_LENGTH +1; -#ifdef HAVE_OPENSSL - if (ssl_acceptor_fd) - client_flags |= CLIENT_SSL; /* Wow, SSL is avalaible! */ - /* - * Without SSL the handshake consists of one packet. This packet - * has both client capabilites and scrambled password. - * With SSL the handshake might consist of two packets. If the first - * packet (client capabilities) has CLIENT_SSL flag set, we have to - * switch to SSL and read the second packet. The scrambled password - * is in the second packet and client_capabilites field will be ignored. - * Maybe it is better to accept flags other than CLIENT_SSL from the - * second packet? - */ -#define SSL_HANDSHAKE_SIZE 2 -#define NORMAL_HANDSHAKE_SIZE 6 -#define MIN_HANDSHAKE_SIZE 2 - -#else -#define MIN_HANDSHAKE_SIZE 6 -#endif /* HAVE_OPENSSL */ int2store(end,client_flags); - end[2]=MY_CHARSET_CURRENT; + end[2]=(char) MY_CHARSET_CURRENT; int2store(end+3,thd->server_status); bzero(end+5,13); end+=18; - if (net_write_command(net,protocol_version, buff, + if (net_write_command(net,(uchar) protocol_version, buff, (uint) (end-buff)) || - (pkt_len=my_net_read(net)) == packet_error || + (pkt_len= my_net_read(net)) == packet_error || pkt_len < MIN_HANDSHAKE_SIZE) { inc_host_errors(&thd->remote.sin_addr); @@ -418,23 +490,18 @@ check_connections(THD *thd) if (thd->client_capabilities & CLIENT_IGNORE_SPACE) thd->sql_mode|= MODE_IGNORE_SPACE; #ifdef HAVE_OPENSSL - DBUG_PRINT("info", - ("pkt_len:%d, client capabilities: %d", - pkt_len, thd->client_capabilities) ); + DBUG_PRINT("info", ("client capabilities: %d", thd->client_capabilities)); if (thd->client_capabilities & CLIENT_SSL) { - DBUG_PRINT("info", ("Agreed to change IO layer to SSL") ); /* Do the SSL layering. */ DBUG_PRINT("info", ("IO layer change in progress...")); - VioSocket* vio_socket = my_reinterpret_cast(VioSocket*)(net->vio); - VioSSL* vio_ssl = ssl_acceptor_fd->accept(vio_socket); - net->vio = my_reinterpret_cast(NetVio*) (vio_ssl); + sslaccept(ssl_acceptor_fd, net->vio, thd->inactive_timeout); DBUG_PRINT("info", ("Reading user information over SSL layer")); if ((pkt_len=my_net_read(net)) == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE) { - DBUG_PRINT("info", ("pkt_len:%d", pkt_len)); - DBUG_PRINT("error", ("Failed to read user information")); + DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)", + pkt_len)); inc_host_errors(&thd->remote.sin_addr); return(ER_HANDSHAKE_ERROR); } @@ -463,7 +530,7 @@ check_connections(THD *thd) if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && opt_using_transactions) thd->net.return_status= &thd->server_status; - net->timeout=net_read_timeout; + net->timeout=(uint) net_read_timeout; if (check_user(thd,COM_CONNECT, user, passwd, db, 1)) return (-1); thd->password=test(passwd[0]); @@ -481,9 +548,9 @@ pthread_handler_decl(handle_one_connection,arg) pthread_detach_this_thread(); -#if !defined( __WIN__) && !defined(OS2) /* Win32 calls this in pthread_create */ - if (my_thread_init()) // needed to be called first before we call - // DBUG_ macros +#if !defined( __WIN__) && !defined(OS2) // Win32 calls this in pthread_create + // The following calls needs to be done before we call DBUG_ macros + if (my_thread_init()) { close_connection(&thd->net,ER_OUT_OF_RESOURCES); statistic_increment(aborted_connects,&LOCK_thread_count); @@ -492,13 +559,13 @@ pthread_handler_decl(handle_one_connection,arg) } #endif - // handle_one_connection() is the only way a thread would start - // and would always be on top of the stack - // therefore, the thread stack always starts at the address of the first - // local variable of handle_one_connection, which is thd - // we need to know the start of the stack so that we could check for - // stack overruns - + /* + handle_one_connection() is the only way a thread would start + and would always be on top of the stack, therefore, the thread + stack always starts at the address of the first local variable + of handle_one_connection, which is thd. We need to know the + start of the stack so that we could check for stack overruns. + */ DBUG_PRINT("info", ("handle_one_connection called by thread %d\n", thd->thread_id)); // now that we've called my_thread_init(), it is safe to call DBUG_* @@ -530,7 +597,7 @@ pthread_handler_decl(handle_one_connection,arg) if ((error=check_connections(thd))) { // Wrong permissions if (error > 0) - net_printf(net,error,thd->host ? thd->host : thd->ip); + net_printf(net,error,thd->host_or_ip); #ifdef __NT__ if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE) sleep(1); /* must wait after eof() */ @@ -554,21 +621,22 @@ pthread_handler_decl(handle_one_connection,arg) if (do_command(thd)) break; } + if (thd->user_connect) + decrease_user_connections(thd->user_connect); free_root(&thd->mem_root,MYF(0)); if (net->error && net->vio != 0) { if (!thd->killed && opt_warnings) - sql_print_error(ER(ER_NEW_ABORTING_CONNECTION), - thd->thread_id,(thd->db ? thd->db : "unconnected"), - thd->user ? thd->user : "unauthenticated", - (thd->host ? thd->host : thd->ip ? thd->ip : "unknown"), - (net->last_errno ? ER(net->last_errno) : - ER(ER_UNKNOWN_ERROR))); + sql_print_error(ER(ER_NEW_ABORTING_CONNECTION), + thd->thread_id,(thd->db ? thd->db : "unconnected"), + thd->user ? thd->user : "unauthenticated", + thd->host_or_ip, + (net->last_errno ? ER(net->last_errno) : + ER(ER_UNKNOWN_ERROR))); send_error(net,net->last_errno,NullS); thread_safe_increment(aborted_threads,&LOCK_thread_count); } - - decrease_user_connections(thd->user,thd->host); + end_thread: close_connection(net); end_thread(thd,1); @@ -628,8 +696,17 @@ pthread_handler_decl(handle_bootstrap,arg) length--; buff[length]=0; thd->current_tablenr=0; - thd->query= thd->memdup(buff,length+1); + thd->query_length=length; + thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1); + thd->query[length] = '\0'; thd->query_id=query_id++; + if (thd->user_connect && check_mqh(thd)) + { + thd->net.error = 0; + close_thread_tables(thd); // Free tables + free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC)); + break; + } mysql_parse(thd,thd->query,length); close_thread_tables(thd); // Free tables if (thd->fatal_error) @@ -664,7 +741,7 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) int error = 0; DBUG_ENTER("mysql_table_dump"); db = (db && db[0]) ? db : thd->db; - if (!(table_list = (TABLE_LIST*) sql_calloc(sizeof(TABLE_LIST)))) + if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST)))) DBUG_RETURN(1); // out of memory table_list->db = db; table_list->real_name = table_list->name = tbl_name; @@ -686,21 +763,19 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) goto err; thd->free_list = 0; + thd->query_length=(uint) strlen(tbl_name); thd->query = tbl_name; - if((error = mysqld_dump_create_info(thd, table, -1))) - { - my_error(ER_GET_ERRNO, MYF(0)); - goto err; - } + if ((error = mysqld_dump_create_info(thd, table, -1))) + { + my_error(ER_GET_ERRNO, MYF(0)); + goto err; + } net_flush(&thd->net); - error = table->file->dump(thd,fd); - if(error) - my_error(ER_GET_ERRNO, MYF(0)); + if ((error = table->file->dump(thd,fd))) + my_error(ER_GET_ERRNO, MYF(0)); err: - close_thread_tables(thd); - DBUG_RETURN(error); } @@ -710,13 +785,10 @@ err: bool do_command(THD *thd) { char *packet; - uint old_timeout,packet_length; - bool error=0; + uint old_timeout; + ulong packet_length; NET *net; enum enum_server_command command; - // commands which will always take a long time should be marked with - // this so that they will not get logged to the slow query log - bool slow_command=FALSE; DBUG_ENTER("do_command"); net= &thd->net; @@ -724,26 +796,42 @@ bool do_command(THD *thd) packet=0; old_timeout=net->timeout; - net->timeout=thd->inactive_timeout; /* Wait max for 8 hours */ + net->timeout=(uint) thd->inactive_timeout; // Wait max for 8 hours net->last_error[0]=0; // Clear error message net->last_errno=0; net_new_transaction(net); if ((packet_length=my_net_read(net)) == packet_error) { - DBUG_PRINT("general",("Got error reading command from socket %s", - vio_description(net->vio) )); + DBUG_PRINT("info",("Got error reading command from socket %s", + vio_description(net->vio) )); return TRUE; } else { packet=(char*) net->read_pos; command = (enum enum_server_command) (uchar) packet[0]; - DBUG_PRINT("general",("Command on %s = %d (%s)", - vio_description(net->vio), command, - command_name[command])); + DBUG_PRINT("info",("Command on %s = %d (%s)", + vio_description(net->vio), command, + command_name[command])); } - net->timeout=old_timeout; /* Timeout */ + net->timeout=old_timeout; // Timeout for writing + DBUG_RETURN(dispatch_command(command,thd, packet+1, (uint) packet_length)); +} + + +bool dispatch_command(enum enum_server_command command, THD *thd, + char* packet, uint packet_length) +{ + NET *net= &thd->net; + bool error=0; + /* + Commands which will always take a long time should be marked with + this so that they will not get logged to the slow query log + */ + bool slow_command=FALSE; + DBUG_ENTER("dispatch_command"); + thd->command=command; VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id=query_id; @@ -752,27 +840,34 @@ bool do_command(THD *thd) thread_running++; VOID(pthread_mutex_unlock(&LOCK_thread_count)); thd->set_time(); - thd->lex.options=0; // We store status here - switch(command) { + thd->lex.select_lex.options=0; // We store status here + switch (command) { case COM_INIT_DB: thread_safe_increment(com_stat[SQLCOM_CHANGE_DB],&LOCK_thread_count); - if (!mysql_change_db(thd,packet+1)) + if (!mysql_change_db(thd,packet)) mysql_log.write(thd,command,"%s",thd->db); break; + case COM_REGISTER_SLAVE: + { + if (register_slave(thd, (uchar*)packet, packet_length)) + send_error(&thd->net); + else + send_ok(&thd->net); + break; + } case COM_TABLE_DUMP: { thread_safe_increment(com_other,&LOCK_thread_count); slow_command = TRUE; - char* data = packet + 1; - uint db_len = *data; - uint tbl_len = *(data + db_len + 1); - char* db = sql_alloc(db_len + tbl_len + 2); - memcpy(db, data + 1, db_len); + uint db_len = *(uchar*)packet; + uint tbl_len = *(uchar*)(packet + db_len + 1); + char* db = thd->alloc(db_len + tbl_len + 2); + memcpy(db, packet + 1, db_len); char* tbl_name = db + db_len; *tbl_name++ = 0; - memcpy(tbl_name, data + db_len + 2, tbl_len); + memcpy(tbl_name, packet + db_len + 2, tbl_len); tbl_name[tbl_len] = 0; - if(mysql_table_dump(thd, db, tbl_name, -1)) + if (mysql_table_dump(thd, db, tbl_name, -1)) send_error(&thd->net); // dump to NET break; @@ -780,16 +875,18 @@ bool do_command(THD *thd) case COM_CHANGE_USER: { thread_safe_increment(com_other,&LOCK_thread_count); - char *user= (char*) packet+1; + char *user= (char*) packet; char *passwd= strend(user)+1; char *db= strend(passwd)+1; /* Save user and privileges */ uint save_master_access=thd->master_access; uint save_db_access= thd->db_access; + uint save_db_length= thd->db_length; char *save_user= thd->user; char *save_priv_user= thd->priv_user; char *save_db= thd->db; + UC *save_uc= thd->user_connect; if ((uint) ((uchar*) db - net->read_pos) > packet_length) { // Check if protocol is ok @@ -803,11 +900,13 @@ bool do_command(THD *thd) thd->master_access=save_master_access; thd->db_access=save_db_access; thd->db=save_db; + thd->db_length=save_db_length; thd->user=save_user; thd->priv_user=save_priv_user; break; } - decrease_user_connections (save_user, thd->host); + if (max_connections && save_uc) + decrease_user_connections(save_uc); x_free((gptr) save_db); x_free((gptr) save_user); thd->password=test(passwd[0]); @@ -816,28 +915,44 @@ bool do_command(THD *thd) case COM_QUERY: { + packet_length--; // Remove end null + /* Remove garage at start and end of query */ + while (isspace(packet[0]) && packet_length > 0) + { + packet++; + packet_length--; + } char *pos=packet+packet_length; // Point at end null - /* Remove garage at end of query */ - while (packet_length > 0 && pos[-1] == ';') + while (packet_length > 0 && (pos[-1] == ';' || isspace(pos[-1]))) { pos--; packet_length--; } - *pos=0; - if (!(thd->query= (char*) thd->memdup((gptr) (packet+1),packet_length))) + /* We must allocate some extra memory for query cache */ + if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet), + packet_length, + thd->db_length+2))) break; + thd->query[packet_length]=0; thd->packet.shrink(net_buffer_length); // Reclaim some memory 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)); - mysql_parse(thd,thd->query,packet_length-1); + if (thd->user_connect && check_mqh(thd)) + { + error = TRUE; // Abort client + net->error = 0; // Don't give abort message + break; + } + /* thd->query_length is set by mysql_parse() */ + mysql_parse(thd,thd->query,packet_length); if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),WAIT_PRIOR); DBUG_PRINT("info",("query ready")); break; } - case COM_FIELD_LIST: // This isn't actually neaded + case COM_FIELD_LIST: // This isn't actually needed #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ break; @@ -853,8 +968,11 @@ bool do_command(THD *thd) break; } thd->free_list=0; - table_list.name=table_list.real_name=thd->strdup(packet+1); - thd->query=fields=thd->strdup(strend(packet+1)+1); + table_list.name=table_list.real_name=thd->strdup(packet); + packet=strend(packet)+1; + // command not cachable => no gap for data base name + if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1))) + break; mysql_log.write(thd,command,"%s %s",table_list.real_name,fields); remove_escape(table_list.real_name); // This can't have wildcards @@ -875,10 +993,10 @@ bool do_command(THD *thd) error=TRUE; // End server break; - case COM_CREATE_DB: + case COM_CREATE_DB: // QQ: To be removed { - char *db=thd->strdup(packet+1); thread_safe_increment(com_stat[SQLCOM_CREATE_DB],&LOCK_thread_count); + char *db=thd->strdup(packet); // null test to handle EOM if (!db || !stripp_sp(db) || check_db_name(db)) { @@ -887,44 +1005,49 @@ bool do_command(THD *thd) } if (check_access(thd,CREATE_ACL,db,0,1)) break; - mysql_log.write(thd,command,packet+1); - mysql_create_db(thd,db,0); + mysql_log.write(thd,command,packet); + mysql_create_db(thd,db,0,0); break; } - case COM_DROP_DB: + case COM_DROP_DB: // QQ: To be removed { - char *db=thd->strdup(packet+1); thread_safe_increment(com_stat[SQLCOM_DROP_DB],&LOCK_thread_count); + char *db=thd->strdup(packet); // null test to handle EOM if (!db || !stripp_sp(db) || check_db_name(db)) { net_printf(&thd->net,ER_WRONG_DB_NAME, db ? db : "NULL"); break; } - if (check_access(thd,DROP_ACL,db,0,1) || end_active_trans(thd)) + if (thd->locked_tables || thd->active_transaction()) + { + send_error(&thd->net,ER_LOCK_OR_ACTIVE_TRANSACTION); break; + } mysql_log.write(thd,command,db); - mysql_rm_db(thd,db,0); + mysql_rm_db(thd,db,0,0); break; } case COM_BINLOG_DUMP: { thread_safe_increment(com_other,&LOCK_thread_count); slow_command = TRUE; - if(check_access(thd, FILE_ACL, any_db)) + if (check_access(thd, FILE_ACL, any_db)) break; mysql_log.write(thd,command, 0); ulong pos; ushort flags; uint32 slave_server_id; - pos = uint4korr(packet + 1); - flags = uint2korr(packet + 5); + pos = uint4korr(packet); + flags = uint2korr(packet + 4); pthread_mutex_lock(&LOCK_server_id); - kill_zombie_dump_threads(slave_server_id = uint4korr(packet+7)); + thd->server_id=0; /* avoid suicide */ + kill_zombie_dump_threads(slave_server_id = uint4korr(packet+6)); thd->server_id = slave_server_id; pthread_mutex_unlock(&LOCK_server_id); - mysql_binlog_send(thd, thd->strdup(packet + 11), pos, flags); + mysql_binlog_send(thd, thd->strdup(packet + 10), pos, flags); + unregister_slave(thd,1,1); // fake COM_QUIT -- if we get here, the thread needs to terminate error = TRUE; net->error = 0; @@ -932,8 +1055,8 @@ bool do_command(THD *thd) } case COM_REFRESH: { - uint options=(uchar) packet[1]; thread_safe_increment(com_stat[SQLCOM_FLUSH],&LOCK_thread_count); + ulong options= (ulong) (uchar) packet[0]; if (check_access(thd,RELOAD_ACL,any_db)) break; mysql_log.write(thd,command,NullS); @@ -941,6 +1064,8 @@ bool do_command(THD *thd) send_error(net,0); else send_eof(net); + if (mqh_used) + reset_mqh(thd,(LEX_USER *) NULL, 0); break; } case COM_SHUTDOWN: @@ -970,7 +1095,7 @@ bool do_command(THD *thd) char buff[200]; ulong uptime = (ulong) (thd->start_time - start_time); sprintf((char*) buff, - "Uptime: %ld Threads: %d Questions: %lu Slow queries: %ld Opens: %ld Flush tables: %ld Open tables: %d Queries per second avg: %.3f", + "Uptime: %ld Threads: %d Questions: %lu Slow queries: %ld Opens: %ld Flush tables: %ld Open tables: %u Queries per second avg: %.3f", uptime, (int) thread_count,thd->query_id,long_query_count, opened_tables,refresh_version, cached_tables(), @@ -999,7 +1124,7 @@ bool do_command(THD *thd) case COM_PROCESS_KILL: { thread_safe_increment(com_stat[SQLCOM_KILL],&LOCK_thread_count); - ulong id=(ulong) uint4korr(packet+1); + ulong id=(ulong) uint4korr(packet); kill_one_thread(thd,id); break; } @@ -1037,7 +1162,7 @@ bool do_command(THD *thd) thd->proc_info="logging slow query"; if ((ulong) (thd->start_time - thd->time_after_lock) > long_query_time || - ((thd->lex.options & + ((thd->lex.select_lex.options & (QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED)) && (specialflag & SPECIAL_LONG_LOG_FORMAT))) { @@ -1068,33 +1193,47 @@ mysql_execute_command(void) int res=0; THD *thd=current_thd; LEX *lex= &thd->lex; - TABLE_LIST *tables=(TABLE_LIST*) lex->table_list.first; + TABLE_LIST *tables=(TABLE_LIST*) lex->select_lex.table_list.first; + SELECT_LEX *select_lex = lex->select; DBUG_ENTER("mysql_execute_command"); if (thd->slave_thread) { - // skip if we are in the slave thread, some table - // rules have been given and the table list says the query should not be - // replicated - if(table_rules_on && tables && !tables_ok(thd,tables)) + /* + Skip if we are in the slave thread, some table rules have been + given and the table list says the query should not be replicated + */ + if (table_rules_on && tables && !tables_ok(thd,tables)) DBUG_VOID_RETURN; - // this is a workaround to deal with the shortcoming - // in 3.23.44-3.23.46 masters - // in RELEASE_LOCK() logging. We re-write SELECT RELEASE_LOCK() as - // DO RELEASE_LOCK() +#ifndef TO_BE_DELETED + /* + This is a workaround to deal with the shortcoming in 3.23.44-3.23.46 + masters in RELEASE_LOCK() logging. We re-write SELECT RELEASE_LOCK() + as DO RELEASE_LOCK() + */ if (lex->sql_command == SQLCOM_SELECT) { lex->sql_command = SQLCOM_DO; - lex->insert_list = &lex->item_list; + lex->insert_list = &select_lex->item_list; } +#endif } + /* + Skip if we are in the slave thread, some table rules have been given + and the table list says the query should not be replicated + */ + if ((lex->select_lex.next && create_total_list(thd,lex,&tables)) || + (table_rules_on && tables && thd->slave_thread && + !tables_ok(thd,tables))) + DBUG_VOID_RETURN; + thread_safe_increment(com_stat[lex->sql_command],&LOCK_thread_count); switch (lex->sql_command) { case SQLCOM_SELECT: { select_result *result; - if (lex->options & SELECT_DESCRIBE) + if (select_lex->options & SELECT_DESCRIBE) lex->exchange=0; if (tables) { @@ -1112,10 +1251,12 @@ mysql_execute_command(void) break; // Error message is given } - thd->offset_limit=lex->offset_limit; - thd->select_limit=lex->select_limit+lex->offset_limit; - if (thd->select_limit < lex->select_limit) + thd->offset_limit=select_lex->offset_limit; + thd->select_limit=select_lex->select_limit+select_lex->offset_limit; + if (thd->select_limit < select_lex->select_limit) thd->select_limit= HA_POS_ERROR; // no limit + if (thd->select_limit == HA_POS_ERROR) + select_lex->options&= ~OPTION_FOUND_ROWS; if (lex->exchange) { @@ -1140,8 +1281,8 @@ mysql_execute_command(void) { res= -1; #ifdef DELETE_ITEMS - delete lex->having; - delete lex->where; + delete select_lex->having; + delete select_lex->where; #endif break; } @@ -1159,22 +1300,11 @@ mysql_execute_command(void) if (!(res=open_and_lock_tables(thd,tables))) { - res=mysql_select(thd,tables,lex->item_list, - lex->where, - (ORDER*) lex->order_list.first, - (ORDER*) lex->group_list.first, - lex->having, - (ORDER*) lex->proc_list.first, - lex->options | thd->options, - result); - if (res) - result->abort(); + query_cache.store_query(thd, tables); + res=handle_select(thd, lex, result); } - delete result; -#ifdef DELETE_ITEMS - delete lex->having; - delete lex->where; -#endif + else + delete result; break; } case SQLCOM_DO: @@ -1186,54 +1316,86 @@ mysql_execute_command(void) break; case SQLCOM_PURGE: - { - if (check_process_priv(thd)) - goto error; - res = purge_master_logs(thd, lex->to_log); - break; - } + { + if (check_process_priv(thd)) + goto error; + res = purge_master_logs(thd, lex->to_log); + break; + } + case SQLCOM_SHOW_NEW_MASTER: + { + if (check_access(thd, FILE_ACL, any_db)) + goto error; + res = show_new_master(thd); + break; + } + case SQLCOM_SHOW_SLAVE_HOSTS: + { + if (check_access(thd, FILE_ACL, any_db)) + goto error; + res = show_slave_hosts(thd); + break; + } + case SQLCOM_SHOW_BINLOG_EVENTS: + { + if (check_access(thd, FILE_ACL, any_db)) + goto error; + res = show_binlog_events(thd); + break; + } case SQLCOM_BACKUP_TABLE: - { - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL, tables) || - check_access(thd, FILE_ACL, any_db)) - goto error; /* purecov: inspected */ - res = mysql_backup_table(thd, tables); + { + if (check_db_used(thd,tables) || + check_table_access(thd,SELECT_ACL, tables) || + check_access(thd, FILE_ACL, any_db)) + goto error; /* purecov: inspected */ + res = mysql_backup_table(thd, tables); - break; - } + break; + } case SQLCOM_RESTORE_TABLE: - { - if (check_db_used(thd,tables) || - check_table_access(thd,INSERT_ACL, tables) || - check_access(thd, FILE_ACL, any_db)) - goto error; /* purecov: inspected */ - res = mysql_restore_table(thd, tables); - break; - } + { + if (check_db_used(thd,tables) || + check_table_access(thd,INSERT_ACL, tables) || + check_access(thd, FILE_ACL, any_db)) + goto error; /* purecov: inspected */ + res = mysql_restore_table(thd, tables); + break; + } case SQLCOM_CHANGE_MASTER: - { - if(check_access(thd, PROCESS_ACL, any_db)) - goto error; - res = change_master(thd); - break; - } + { + if (check_access(thd, PROCESS_ACL, any_db)) + goto error; + LOCK_ACTIVE_MI; + res = change_master(thd,active_mi); + UNLOCK_ACTIVE_MI; + break; + } case SQLCOM_SHOW_SLAVE_STAT: - { - if (check_process_priv(thd)) - goto error; - res = show_master_info(thd); - break; - } + { + if (check_process_priv(thd)) + goto error; + LOCK_ACTIVE_MI; + res = show_master_info(thd,active_mi); + UNLOCK_ACTIVE_MI; + break; + } case SQLCOM_SHOW_MASTER_STAT: - { - if (check_process_priv(thd)) - goto error; - res = show_binlog_info(thd); - break; - } + { + if (check_process_priv(thd)) + goto error; + res = show_binlog_info(thd); + break; + } + + case SQLCOM_LOAD_MASTER_DATA: // sync with master + if (check_process_priv(thd)) + goto error; + res = load_master_data(thd); + break; + case SQLCOM_LOAD_MASTER_TABLE: - + { if (!tables->db) tables->db=thd->db; if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege)) @@ -1246,21 +1408,23 @@ mysql_execute_command(void) bool error=check_grant(thd,CREATE_ACL,tables); tables->next=tmp_table_list; if (error) - goto error; + goto error; } if (strlen(tables->name) > NAME_LEN) { net_printf(&thd->net,ER_WRONG_TABLE_NAME,tables->name); break; } - - thd->last_nx_table = tables->real_name; - thd->last_nx_db = tables->db; - if (fetch_nx_table(thd, &glob_mi)) - break; // fetch_nx_table did send the error to the client - send_ok(&thd->net); + LOCK_ACTIVE_MI; + // fetch_master_table will send the error to the client on failure + if (!fetch_master_table(thd, tables->db, tables->real_name, + active_mi, 0)) + { + send_ok(&thd->net); + } + UNLOCK_ACTIVE_MI; break; - + } case SQLCOM_CREATE_TABLE: if (!tables->db) tables->db=thd->db; @@ -1285,12 +1449,25 @@ mysql_execute_command(void) res=0; break; } - if (lex->item_list.elements) // With select +#ifndef HAVE_READLINK + lex->create_info.data_file_name=lex->create_info.index_file_name=0; +#else + /* Fix names if symlinked tables */ + if (append_file_to_dir(thd, &lex->create_info.data_file_name, + tables->name) || + append_file_to_dir(thd,&lex->create_info.index_file_name, + tables->name)) + { + res=-1; + break; + } +#endif + if (select_lex->item_list.elements) // With select { select_result *result; if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && - check_dup(thd,tables->db,tables->real_name,tables->next)) + check_dup(tables->db, tables->real_name, tables->next)) { net_printf(&thd->net,ER_INSERT_TABLE_USED,tables->real_name); DBUG_VOID_RETURN; @@ -1303,31 +1480,22 @@ mysql_execute_command(void) for (table = tables->next ; table ; table=table->next) table->lock_type= lex->lock_option; } - thd->offset_limit=lex->offset_limit; - thd->select_limit=lex->select_limit+lex->offset_limit; - if (thd->select_limit < lex->select_limit) + thd->offset_limit=select_lex->offset_limit; + thd->select_limit=select_lex->select_limit+select_lex->offset_limit; + if (thd->select_limit < select_lex->select_limit) thd->select_limit= HA_POS_ERROR; // No limit + /* Skip first table, which is the table we are creating */ + lex->select_lex.table_list.first= + (byte*) (((TABLE_LIST *) lex->select_lex.table_list.first)->next); if (!(res=open_and_lock_tables(thd,tables->next))) { - if ((result=new select_create(tables->db ? tables->db : thd->db, - tables->real_name, &lex->create_info, - lex->create_list, - lex->key_list, - lex->item_list,lex->duplicates))) - { - res=mysql_select(thd,tables->next,lex->item_list, - lex->where, - (ORDER*) lex->order_list.first, - (ORDER*) lex->group_list.first, - lex->having, - (ORDER*) lex->proc_list.first, - lex->options | thd->options, - result); - if (res) - result->abort(); - delete result; - } + if ((result=new select_create(tables->db ? tables->db : thd->db, + tables->real_name, &lex->create_info, + lex->create_list, + lex->key_list, + select_lex->item_list,lex->duplicates))) + res=handle_select(thd, lex, result); else res= -1; } @@ -1356,12 +1524,19 @@ mysql_execute_command(void) break; case SQLCOM_SLAVE_START: - start_slave(thd); + { + LOCK_ACTIVE_MI; + start_slave(thd,active_mi,1 /* net report*/); + UNLOCK_ACTIVE_MI; break; + } case SQLCOM_SLAVE_STOP: - stop_slave(thd); + { + LOCK_ACTIVE_MI; + stop_slave(thd,active_mi,1/* net report*/); + UNLOCK_ACTIVE_MI; break; - + } case SQLCOM_ALTER_TABLE: #if defined(DONT_ALLOW_SHOW_COMMANDS) send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ @@ -1377,10 +1552,10 @@ mysql_execute_command(void) } if (!tables->db) tables->db=thd->db; - if (!lex->db) - lex->db=tables->db; + if (!select_lex->db) + select_lex->db=tables->db; if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege) || - check_access(thd,INSERT_ACL | CREATE_ACL,lex->db,&priv) || + check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv) || check_merge_table_access(thd, tables->db, (TABLE_LIST *) lex->create_info.merge_list.first)) @@ -1396,22 +1571,27 @@ mysql_execute_command(void) TABLE_LIST tmp_table; bzero((char*) &tmp_table,sizeof(tmp_table)); tmp_table.real_name=lex->name; - tmp_table.db=lex->db; + tmp_table.db=select_lex->db; tmp_table.grant.privilege=priv; if (check_grant(thd,INSERT_ACL | CREATE_ACL,tables)) goto error; } } + /* Don't yet allow changing of symlinks with ALTER TABLE */ + lex->create_info.data_file_name=lex->create_info.index_file_name=0; /* ALTER TABLE ends previous transaction */ if (end_active_trans(thd)) res= -1; else - res= mysql_alter_table(thd, lex->db, lex->name, + { + res= mysql_alter_table(thd, select_lex->db, lex->name, &lex->create_info, tables, lex->create_list, lex->key_list, lex->drop_list, lex->alter_list, - (ORDER *) lex->order_list.first, - lex->drop_primary, lex->duplicates); + (ORDER *) select_lex->order_list.first, + lex->drop_primary, lex->duplicates, + lex->alter_keys_onoff, lex->simple_alter); + } break; } #endif @@ -1435,11 +1615,12 @@ mysql_execute_command(void) old_list.next=new_list.next=0; if (check_grant(thd,ALTER_ACL,&old_list) || (!test_all_bits(table->next->grant.privilege, - INSERT_ACL | CREATE_ACL) && + INSERT_ACL | CREATE_ACL) && check_grant(thd,INSERT_ACL | CREATE_ACL, &new_list))) goto error; } } + query_cache.invalidate(tables); if (end_active_trans(thd)) res= -1; else if (mysql_rename_tables(thd,tables)) @@ -1473,21 +1654,23 @@ mysql_execute_command(void) } #endif case SQLCOM_REPAIR: - { - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL | INSERT_ACL, tables)) - goto error; /* purecov: inspected */ - res = mysql_repair_table(thd, tables, &lex->check_opt); - break; - } + { + if (check_db_used(thd,tables) || + check_table_access(thd,SELECT_ACL | INSERT_ACL, tables)) + goto error; /* purecov: inspected */ + res = mysql_repair_table(thd, tables, &lex->check_opt); + query_cache.invalidate(tables); + break; + } case SQLCOM_CHECK: - { - if (check_db_used(thd,tables) || - check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables)) - goto error; /* purecov: inspected */ - res = mysql_check_table(thd, tables, &lex->check_opt); - break; - } + { + if (check_db_used(thd,tables) || + check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables)) + goto error; /* purecov: inspected */ + res = mysql_check_table(thd, tables, &lex->check_opt); + query_cache.invalidate(tables); + break; + } case SQLCOM_ANALYZE: { if (check_db_used(thd,tables) || @@ -1529,23 +1712,73 @@ mysql_execute_command(void) goto error; if (grant_option && check_grant(thd,UPDATE_ACL,tables)) goto error; - if (lex->item_list.elements != lex->value_list.elements) + if (select_lex->item_list.elements != lex->value_list.elements) { send_error(&thd->net,ER_WRONG_VALUE_COUNT); DBUG_VOID_RETURN; } - res = mysql_update(thd,tables, - lex->item_list, - lex->value_list, - lex->where, - lex->select_limit, - lex->duplicates, - lex->lock_option); + if (select_lex->table_list.elements == 1) + { + res = mysql_update(thd,tables, + select_lex->item_list, + lex->value_list, + select_lex->where, + (ORDER *) select_lex->order_list.first, + select_lex->select_limit, + lex->duplicates, + lex->lock_option); #ifdef DELETE_ITEMS - delete lex->where; + delete select_lex->where; #endif - break; + } + else + { + multi_update *result; + uint table_count; + TABLE_LIST *auxi; + lex->sql_command=SQLCOM_MULTI_UPDATE; + 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, + (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_INSERT: if (check_access(thd,INSERT_ACL,tables->db,&tables->grant.privilege)) goto error; /* purecov: inspected */ @@ -1570,6 +1803,7 @@ mysql_execute_command(void) case SQLCOM_REPLACE_SELECT: case SQLCOM_INSERT_SELECT: { + // Check that we have modify privileges for the first table and // select privileges for the rest { @@ -1587,51 +1821,50 @@ mysql_execute_command(void) } select_result *result; - thd->offset_limit=lex->offset_limit; - thd->select_limit=lex->select_limit+lex->offset_limit; - if (thd->select_limit < lex->select_limit) + thd->offset_limit=select_lex->offset_limit; + thd->select_limit=select_lex->select_limit+select_lex->offset_limit; + if (thd->select_limit < select_lex->select_limit) thd->select_limit= HA_POS_ERROR; // No limit - if (check_dup(thd,tables->db,tables->real_name,tables->next)) + if (check_dup(tables->db, tables->real_name, tables->next)) { net_printf(&thd->net,ER_INSERT_TABLE_USED,tables->real_name); DBUG_VOID_RETURN; } - tables->lock_type=TL_WRITE; // update first table + tables->lock_type=TL_WRITE; // update first table { TABLE_LIST *table; for (table = tables->next ; table ; table=table->next) table->lock_type= lex->lock_option; } - if (!(res=open_and_lock_tables(thd,tables))) + + /* Skip first table, which is the table we are inserting in */ + lex->select_lex.table_list.first= + (byte*) (((TABLE_LIST *) lex->select_lex.table_list.first)->next); + if (!(res=open_and_lock_tables(thd, tables))) { if ((result=new select_insert(tables->table,&lex->field_list, - lex->sql_command == SQLCOM_REPLACE_SELECT ? - DUP_REPLACE : DUP_IGNORE))) - { - res=mysql_select(thd,tables->next,lex->item_list, - lex->where, - (ORDER*) lex->order_list.first, - (ORDER*) lex->group_list.first, - lex->having, - (ORDER*) lex->proc_list.first, - lex->options | thd->options, - result); - delete result; - } - else - res= -1; + lex->duplicates))) + res=handle_select(thd,lex,result); } -#ifdef DELETE_ITEMS - delete lex->having; - delete lex->where; -#endif + else + res= -1; break; } case SQLCOM_TRUNCATE: - lex->where=0; - lex->select_limit=HA_POS_ERROR; - /* Fall through */ + if (check_access(thd,DELETE_ACL,tables->db,&tables->grant.privilege)) + goto error; /* purecov: inspected */ + /* + Don't allow this within a transaction because we want to use + re-generate table + */ + if (thd->locked_tables || thd->active_transaction()) + { + send_error(&thd->net,ER_LOCK_OR_ACTIVE_TRANSACTION,NullS); + goto error; + } + res=mysql_truncate(thd,tables); + break; case SQLCOM_DELETE: { if (check_access(thd,DELETE_ACL,tables->db,&tables->grant.privilege)) @@ -1644,20 +1877,87 @@ mysql_execute_command(void) if (lex->sql_command == SQLCOM_TRUNCATE && end_active_trans(thd)) res= -1; else - res = mysql_delete(thd,tables,lex->where,lex->select_limit, - lex->lock_option, lex->options); + res = mysql_delete(thd,tables, select_lex->where, + (ORDER*) select_lex->order_list.first, + select_lex->select_limit, lex->lock_option, + select_lex->options); break; } - case SQLCOM_DROP_TABLE: + case SQLCOM_DELETE_MULTI: + { + TABLE_LIST *aux_tables=(TABLE_LIST *)thd->lex.auxilliary_table_list.first; + TABLE_LIST *auxi; + uint table_count=0; + multi_delete *result; + + /* sql_yacc guarantees that tables and aux_tables are not zero */ + if (check_db_used(thd, tables) || check_db_used(thd,aux_tables) || + check_table_access(thd,SELECT_ACL, tables) || + check_table_access(thd,DELETE_ACL, aux_tables)) + goto error; + if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where) + { + send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE); + goto error; + } + for (auxi=(TABLE_LIST*) aux_tables ; auxi ; auxi=auxi->next) { - if (check_table_access(thd,DROP_ACL,tables)) - goto error; /* purecov: inspected */ - if (end_active_trans(thd)) - res= -1; - else - res = mysql_rm_table(thd,tables,lex->drop_if_exists); + table_count++; + /* All tables in aux_tables must be found in FROM PART */ + TABLE_LIST *walk; + for (walk=(TABLE_LIST*) tables ; walk ; walk=walk->next) + { + if (!strcmp(auxi->real_name,walk->real_name) && + !strcmp(walk->db,auxi->db)) + break; + } + if (!walk) + { + net_printf(&thd->net,ER_NONUNIQ_TABLE,auxi->real_name); + goto error; + } + auxi->lock_type=walk->lock_type=TL_WRITE; + auxi->table= (TABLE *) walk; // Remember corresponding table } + tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege); + if (add_item_to_list(new Item_null())) + { + res= -1; + break; + } + thd->proc_info="init"; + if ((res=open_and_lock_tables(thd,tables))) + break; + /* Fix tables-to-be-deleted-from list to point at opened tables */ + for (auxi=(TABLE_LIST*) aux_tables ; auxi ; auxi=auxi->next) + auxi->table= ((TABLE_LIST*) auxi->table)->table; + if (!thd->fatal_error && (result=new multi_delete(thd,aux_tables, + lex->lock_option,table_count))) + { + res=mysql_select(thd,tables,select_lex->item_list, + select_lex->where, + (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)) + goto error; /* purecov: inspected */ + if (end_active_trans(thd)) + res= -1; + else + res = mysql_rm_table(thd,tables,lex->drop_if_exists); + } + break; case SQLCOM_DROP_INDEX: if (!tables->db) tables->db=thd->db; @@ -1672,7 +1972,7 @@ mysql_execute_command(void) break; case SQLCOM_SHOW_DATABASES: #if defined(DONT_ALLOW_SHOW_COMMANDS) - send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ DBUG_VOID_RETURN; #else if ((specialflag & SPECIAL_SKIP_SHOW_DB) && @@ -1708,13 +2008,12 @@ mysql_execute_command(void) #endif case SQLCOM_SHOW_TABLES: /* FALL THROUGH */ - case SQLCOM_SHOW_OPEN_TABLES: #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ DBUG_VOID_RETURN; #else { - char *db=lex->db ? lex->db : thd->db; + char *db=select_lex->db ? select_lex->db : thd->db; if (!db) { send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ @@ -1729,34 +2028,32 @@ mysql_execute_command(void) if (check_access(thd,SELECT_ACL,db,&thd->col_access)) goto error; /* purecov: inspected */ /* grant is checked in mysqld_show_tables */ - if (lex->sql_command == SQLCOM_SHOW_OPEN_TABLES) - res= mysqld_show_open_tables(thd,db, - (lex->wild ? lex->wild->ptr() : NullS)); - else if (lex->options & SELECT_DESCRIBE) + if (select_lex->options & SELECT_DESCRIBE) res= mysqld_extend_show_tables(thd,db, - (lex->wild ? lex->wild->ptr() : NullS)); + (lex->wild ? lex->wild->ptr() : NullS)); else res= mysqld_show_tables(thd,db, (lex->wild ? lex->wild->ptr() : NullS)); break; } #endif + case SQLCOM_SHOW_OPEN_TABLES: + res= mysqld_show_open_tables(thd,(lex->wild ? lex->wild->ptr() : NullS)); + break; case SQLCOM_SHOW_FIELDS: #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ DBUG_VOID_RETURN; #else { - char *db=tables->db ? tables->db : thd->db; - if (!db) + char *db=tables->db; + if (!*db) { send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ goto error; /* purecov: inspected */ } remove_escape(db); // Fix escaped '_' remove_escape(tables->name); - if (!tables->db) - tables->db=thd->db; if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,&thd->col_access)) goto error; /* purecov: inspected */ tables->grant.privilege=thd->col_access; @@ -1774,7 +2071,7 @@ mysql_execute_command(void) DBUG_VOID_RETURN; #else { - char *db=tables->db ? tables->db : thd->db; + char *db=tables->db; if (!db) { send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ @@ -1794,7 +2091,7 @@ mysql_execute_command(void) } #endif case SQLCOM_CHANGE_DB: - mysql_change_db(thd,lex->db); + mysql_change_db(thd,select_lex->db); break; case SQLCOM_LOAD: { @@ -1815,7 +2112,7 @@ mysql_execute_command(void) goto error; } if (check_access(thd,privilege,tables->db,&tables->grant.privilege) || - grant_option && check_grant(thd,privilege,tables)) + grant_option && check_grant(thd,privilege,tables)) goto error; } res=mysql_load(thd, lex->exchange, tables, lex->field_list, @@ -1824,22 +2121,17 @@ mysql_execute_command(void) } case SQLCOM_SET_OPTION: { - uint org_options=thd->options; - thd->options=lex->options; + ulong org_options=thd->options; + thd->options=select_lex->options; thd->update_lock_default= ((thd->options & OPTION_LOW_PRIORITY_UPDATES) ? TL_WRITE_LOW_PRIORITY : TL_WRITE); - thd->default_select_limit=lex->select_limit; + thd->default_select_limit=select_lex->select_limit; thd->tx_isolation=lex->tx_isolation; - if (thd->gemini_spin_retries != lex->gemini_spin_retries) - { - thd->gemini_spin_retries= lex->gemini_spin_retries; - ha_set_spin_retries(thd->gemini_spin_retries); - } DBUG_PRINT("info",("options: %ld limit: %ld", thd->options,(long) thd->default_select_limit)); /* Check if auto_commit mode changed */ - if ((org_options ^ lex->options) & OPTION_NOT_AUTO_COMMIT) + if ((org_options ^ select_lex->options) & OPTION_NOT_AUTO_COMMIT) { if ((org_options & OPTION_NOT_AUTO_COMMIT)) { @@ -1873,13 +2165,7 @@ mysql_execute_command(void) thd->options&= ~(ulong) (OPTION_TABLE_LOCK); } if (thd->global_read_lock) - { - thd->global_read_lock=0; - pthread_mutex_lock(&LOCK_open); - global_read_lock--; - pthread_cond_broadcast(&COND_refresh); - pthread_mutex_unlock(&LOCK_open); - } + unlock_global_read_lock(thd); send_ok(&thd->net); break; case SQLCOM_LOCK_TABLES: @@ -1891,6 +2177,8 @@ mysql_execute_command(void) } if (check_db_used(thd,tables) || end_active_trans(thd)) goto error; + if (grant_option && check_grant(thd,SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL,tables)) + goto error; thd->in_lock_tables=1; thd->options|= OPTION_TABLE_LOCK; if (!(res=open_and_lock_tables(thd,tables))) @@ -1904,30 +2192,34 @@ mysql_execute_command(void) thd->in_lock_tables=0; break; case SQLCOM_CREATE_DB: + { + if (!stripp_sp(lex->name) || check_db_name(lex->name)) { - if (!stripp_sp(lex->name) || check_db_name(lex->name)) - { - net_printf(&thd->net,ER_WRONG_DB_NAME, lex->name); - break; - } - if (check_access(thd,CREATE_ACL,lex->name,0,1)) - break; - mysql_create_db(thd,lex->name,lex->create_info.options); + net_printf(&thd->net,ER_WRONG_DB_NAME, lex->name); break; } + if (check_access(thd,CREATE_ACL,lex->name,0,1)) + break; + res=mysql_create_db(thd,lex->name,lex->create_info.options,0); + break; + } case SQLCOM_DROP_DB: + { + if (!stripp_sp(lex->name) || check_db_name(lex->name)) { - if (!stripp_sp(lex->name) || check_db_name(lex->name)) - { - net_printf(&thd->net,ER_WRONG_DB_NAME, lex->name); - break; - } - if (check_access(thd,DROP_ACL,lex->name,0,1) || - end_active_trans(thd)) - break; - mysql_rm_db(thd,lex->name,lex->drop_if_exists); + net_printf(&thd->net,ER_WRONG_DB_NAME, lex->name); + break; + } + if (check_access(thd,DROP_ACL,lex->name,0,1)) break; + if (thd->locked_tables || thd->active_transaction()) + { + send_error(&thd->net,ER_LOCK_OR_ACTIVE_TRANSACTION); + goto error; } + res=mysql_rm_db(thd,lex->name,lex->drop_if_exists,0); + break; + } case SQLCOM_CREATE_FUNCTION: if (check_access(thd,INSERT_ACL,"mysql",0,1)) break; @@ -1948,78 +2240,83 @@ mysql_execute_command(void) res= -1; #endif break; - case SQLCOM_REVOKE: - case SQLCOM_GRANT: - { - if (tables && !tables->db) - tables->db=thd->db; - if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL, - tables && tables->db ? tables->db : lex->db, - tables ? &tables->grant.privilege : 0, - tables ? 0 : 1)) - goto error; - - /* Check that the user isn't trying to change a password for another - user if he doesn't have UPDATE privilege to the MySQL database */ - - if (thd->user) // If not replication - { - LEX_USER *user; - List_iterator <LEX_USER> user_list(lex->users_list); - while ((user=user_list++)) - { - if (user->password.str && - (strcmp(thd->user,user->user.str) || - user->host.str && - my_strcasecmp(user->host.str, thd->host ? thd->host : thd->ip))) - { - if (check_access(thd, UPDATE_ACL, "mysql",0,1)) - goto error; - break; // We are allowed to do changes - } - } - } - if (tables) - { - if (grant_option && check_grant(thd, - (lex->grant | lex->grant_tot_col | - GRANT_ACL), - tables)) - goto error; - res = mysql_table_grant(thd,tables,lex->users_list, lex->columns, - lex->grant, lex->sql_command == SQLCOM_REVOKE); - if(!res) - { - mysql_update_log.write(thd, thd->query,thd->query_length); - if (mysql_bin_log.is_open()) - { - Query_log_event qinfo(thd, thd->query); - mysql_bin_log.write(&qinfo); - } - } - } - else - { - if (lex->columns.elements) - { - net_printf(&thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); - res=1; - } - else - res = mysql_grant(thd, lex->db, lex->users_list, lex->grant, - lex->sql_command == SQLCOM_REVOKE); - if (!res) - { - mysql_update_log.write(thd, thd->query,thd->query_length); - if (mysql_bin_log.is_open()) - { - Query_log_event qinfo(thd, thd->query); - mysql_bin_log.write(&qinfo); - } - } - } - break; - } + case SQLCOM_REVOKE: + case SQLCOM_GRANT: + { + if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL, + tables && tables->db ? tables->db : select_lex->db, + tables ? &tables->grant.privilege : 0, + tables ? 0 : 1)) + goto error; + + /* Check that the user isn't trying to change a password for another + user if he doesn't have UPDATE privilege to the MySQL database */ + + if (thd->user) // If not replication + { + LEX_USER *user; + List_iterator <LEX_USER> user_list(lex->users_list); + while ((user=user_list++)) + { + if (user->password.str && + (strcmp(thd->user,user->user.str) || + user->host.str && + my_strcasecmp(user->host.str, thd->host_or_ip))) + { + if (check_access(thd, UPDATE_ACL, "mysql",0,1)) + goto error; + break; // We are allowed to do changes + } + } + } + if (tables) + { + if (grant_option && check_grant(thd, + (lex->grant | lex->grant_tot_col | + GRANT_ACL), + tables)) + goto error; + if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns, + lex->grant, + lex->sql_command == SQLCOM_REVOKE))) + { + mysql_update_log.write(thd, thd->query, thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query); + mysql_bin_log.write(&qinfo); + } + } + } + else + { + if (lex->columns.elements) + { + send_error(&thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); + res=1; + } + else + res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant, + lex->sql_command == SQLCOM_REVOKE); + if (!res) + { + mysql_update_log.write(thd, thd->query, thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query); + mysql_bin_log.write(&qinfo); + } + if (mqh_used && lex->mqh) + { + List_iterator <LEX_USER> str_list(lex->users_list); + LEX_USER *user; + while ((user=str_list++)) + reset_mqh(thd,user,lex->mqh); + } + } + } + break; + } case SQLCOM_FLUSH: case SQLCOM_RESET: if (check_access(thd,RELOAD_ACL,any_db) || check_db_used(thd, tables)) @@ -2034,13 +2331,40 @@ mysql_execute_command(void) break; case SQLCOM_SHOW_GRANTS: res=0; - if ((thd->priv_user && !strcmp(thd->priv_user,lex->grant_user->user.str)) || + if ((thd->priv_user && + !strcmp(thd->priv_user,lex->grant_user->user.str)) || !check_access(thd, SELECT_ACL, "mysql",0,1)) { res = mysql_show_grants(thd,lex->grant_user); } break; + case SQLCOM_HA_OPEN: + if (check_db_used(thd,tables) || + check_table_access(thd,SELECT_ACL, tables)) + goto error; + res = mysql_ha_open(thd, tables); + break; + case SQLCOM_HA_CLOSE: + if (check_db_used(thd,tables)) + goto error; + res = mysql_ha_close(thd, tables); + break; + case SQLCOM_HA_READ: + if (check_db_used(thd,tables) || + check_table_access(thd,SELECT_ACL, tables)) + goto error; + res = mysql_ha_read(thd, tables, lex->ha_read_mode, lex->backup_dir, + lex->insert_list, lex->ha_rkey_mode, select_lex->where, + select_lex->select_limit, select_lex->offset_limit); + break; + case SQLCOM_BEGIN: + if (thd->locked_tables) + { + thd->lock=thd->locked_tables; + thd->locked_tables=0; // Will be automaticly closed + close_thread_tables(thd); // Free tables + } if (end_active_trans(thd)) { res= -1; @@ -2059,13 +2383,17 @@ mysql_execute_command(void) even if there is a problem with the OPTION_AUTO_COMMIT flag (Which of course should never happen...) */ + { thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (!ha_commit(thd)) + { send_ok(&thd->net); + } else res= -1; break; + } case SQLCOM_ROLLBACK: thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (!ha_rollback(thd)) @@ -2104,7 +2432,7 @@ error: bool check_access(THD *thd,uint want_access,const char *db, uint *save_priv, - bool dont_check_global_grants) + bool dont_check_global_grants, bool no_errors) { uint db_access,dummy; if (save_priv) @@ -2112,9 +2440,10 @@ check_access(THD *thd,uint want_access,const char *db, uint *save_priv, else save_priv= &dummy; - if (!db && !thd->db && !dont_check_global_grants) + if ((!db || !db[0]) && !thd->db && !dont_check_global_grants) { - send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: tested */ + if (!no_errors) + send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: tested */ return TRUE; /* purecov: tested */ } @@ -2126,16 +2455,17 @@ check_access(THD *thd,uint want_access,const char *db, uint *save_priv, if ((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL) || ! db && dont_check_global_grants) { // We can never grant this - net_printf(&thd->net,ER_ACCESS_DENIED_ERROR, - thd->priv_user, - thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"), - thd->password ? ER(ER_YES) : ER(ER_NO));/* purecov: tested */ + if (!no_errors) + net_printf(&thd->net,ER_ACCESS_DENIED_ERROR, + thd->priv_user, + thd->host_or_ip, + thd->password ? ER(ER_YES) : ER(ER_NO));/* purecov: tested */ return TRUE; /* purecov: tested */ } if (db == any_db) return FALSE; // Allow select on anything - + if (db && (!thd->db || strcmp(db,thd->db))) db_access=acl_get(thd->host, thd->ip, (char*) &thd->remote.sin_addr, thd->priv_user, db); /* purecov: inspected */ @@ -2149,10 +2479,11 @@ check_access(THD *thd,uint want_access,const char *db, uint *save_priv, ((grant_option && !dont_check_global_grants) && !(want_access & ~TABLE_ACLS))) return FALSE; /* Ok */ - net_printf(&thd->net,ER_DBACCESS_DENIED_ERROR, - thd->priv_user, - thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"), - db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */ + if (!no_errors) + net_printf(&thd->net,ER_DBACCESS_DENIED_ERROR, + thd->priv_user, + thd->host_or_ip, + db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */ return TRUE; /* purecov: tested */ } @@ -2169,7 +2500,8 @@ bool check_process_priv(THD *thd) */ bool -check_table_access(THD *thd,uint want_access,TABLE_LIST *tables) +check_table_access(THD *thd,uint want_access,TABLE_LIST *tables, + bool no_errors) { uint found=0,found_access=0; TABLE_LIST *org_tables=tables; @@ -2184,20 +2516,20 @@ check_table_access(THD *thd,uint want_access,TABLE_LIST *tables) tables->grant.privilege=found_access; else { - if (check_access(thd,want_access,tables->db,&tables->grant.privilege)) + if (check_access(thd,want_access,tables->db,&tables->grant.privilege, + 0, no_errors)) return TRUE; // Access denied found_access=tables->grant.privilege; found=1; } } - else if (check_access(thd,want_access,tables->db,&tables->grant.privilege)) + else if (check_access(thd,want_access,tables->db,&tables->grant.privilege, + 0, no_errors)) return TRUE; // Access denied } if (grant_option) - { - want_access &= ~EXTRA_ACL; // Remove SHOW attribute - return check_grant(thd,want_access,org_tables); - } + return check_grant(thd,want_access & ~EXTRA_ACL,org_tables, + test(want_access & EXTRA_ACL), no_errors); return FALSE; } @@ -2301,42 +2633,75 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, int *yystacksize) /**************************************************************************** - Initialize global thd variables neaded for query + Initialize global thd variables needed for query ****************************************************************************/ static void mysql_init_query(THD *thd) { DBUG_ENTER("mysql_init_query"); - thd->lex.item_list.empty(); + thd->lex.select_lex.item_list.empty(); thd->lex.value_list.empty(); - thd->lex.table_list.elements=0; - thd->free_list=0; - - thd->lex.table_list.first=0; - thd->lex.table_list.next= (byte**) &thd->lex.table_list.first; + thd->lex.select_lex.table_list.elements=0; + thd->free_list=0; thd->lex.union_option=0; + thd->lex.select = &thd->lex.select_lex; + thd->lex.select_lex.table_list.first=0; + thd->lex.select_lex.table_list.next= (byte**) &thd->lex.select_lex.table_list.first; + thd->lex.select_lex.next=0; thd->fatal_error=0; // Safety thd->last_insert_id_used=thd->query_start_used=thd->insert_id_used=0; thd->sent_row_count=thd->examined_row_count=0; + thd->safe_to_cache_query=1; DBUG_VOID_RETURN; } void mysql_init_select(LEX *lex) { - lex->where=lex->having=0; - lex->select_limit=current_thd->default_select_limit; - lex->offset_limit=0L; - lex->options=0; + SELECT_LEX *select_lex = lex->select; + select_lex->where=select_lex->having=0; + select_lex->select_limit=lex->thd->default_select_limit; + select_lex->offset_limit=0; + select_lex->options=0; + select_lex->linkage=UNSPECIFIED_TYPE; lex->exchange = 0; lex->proc_list.first=0; - lex->order_list.elements=lex->group_list.elements=0; - lex->order_list.first=0; - lex->order_list.next= (byte**) &lex->order_list.first; - lex->group_list.first=0; - lex->group_list.next= (byte**) &lex->group_list.first; + select_lex->order_list.elements=select_lex->group_list.elements=0; + select_lex->order_list.first=0; + select_lex->order_list.next= (byte**) &select_lex->order_list.first; + select_lex->group_list.first=0; + select_lex->group_list.next= (byte**) &select_lex->group_list.first; + select_lex->next = (SELECT_LEX *)NULL; +} + +bool +mysql_new_select(LEX *lex) +{ + SELECT_LEX *select_lex = (SELECT_LEX *) lex->thd->calloc(sizeof(SELECT_LEX)); + if (!select_lex) + return 1; + lex->select->next=select_lex; + lex->select=select_lex; + select_lex->table_list.next= (byte**) &select_lex->table_list.first; + select_lex->item_list.empty(); + select_lex->when_list.empty(); + select_lex->expr_list.empty(); + select_lex->interval_list.empty(); + select_lex->use_index.empty(); + select_lex->ftfunc_list.empty(); + return 0; } +void mysql_init_multi_delete(LEX *lex) +{ + lex->sql_command = SQLCOM_DELETE_MULTI; + mysql_init_select(lex); + lex->select->select_limit=HA_POS_ERROR; + 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); +} void mysql_parse(THD *thd,char *inBuf,uint length) @@ -2345,12 +2710,20 @@ mysql_parse(THD *thd,char *inBuf,uint length) mysql_init_query(thd); thd->query_length = length; - LEX *lex=lex_start(thd, (uchar*) inBuf, length); - if (!yyparse() && ! thd->fatal_error) - mysql_execute_command(); - thd->proc_info="freeing items"; - free_items(thd); /* Free strings used by items */ - lex_end(lex); + if (query_cache.send_result_to_client(thd, inBuf, length) <= 0) + { + LEX *lex=lex_start(thd, (uchar*) inBuf, length); + if (!yyparse() && ! thd->fatal_error) + { + mysql_execute_command(); + query_cache_end_of_result(&thd->net); + } + else + query_cache_abort(&thd->net); + thd->proc_info="freeing items"; + free_items(thd); /* Free strings used by items */ + lex_end(lex); + } DBUG_VOID_RETURN; } @@ -2656,6 +3029,8 @@ add_proc_to_list(Item *item) static void remove_escape(char *name) { + if (!*name) // For empty DB names + return; char *to; #ifdef USE_MB char *strend=name+(uint) strlen(name); @@ -2675,7 +3050,7 @@ static void remove_escape(char *name) } #endif if (*name == '\\' && name[1]) - name++; // Skipp '\\' + name++; // Skip '\\' *to++= *name; } *to=0; @@ -2714,7 +3089,6 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, register TABLE_LIST *ptr; THD *thd=current_thd; char *alias_str; - const char *current_db; DBUG_ENTER("add_table_to_list"); if (!table) @@ -2729,15 +3103,19 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, } if (!alias) /* Alias is case sensitive */ - if (!(alias_str=sql_strmake(alias_str,table->table.length))) + if (!(alias_str=thd->memdup(alias_str,table->table.length+1))) DBUG_RETURN(0); - if (lower_case_table_names) - casedn_str(table->table.str); + if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST)))) DBUG_RETURN(0); /* purecov: inspected */ - ptr->db= table->db.str; - ptr->real_name=table->table.str; + ptr->db= table->db.str ? table->db.str : (thd->db ? thd->db : (char*) ""); ptr->name=alias_str; + if (lower_case_table_names) + { + casedn_str(ptr->db); + casedn_str(table->table.str); + } + ptr->real_name=table->table.str; ptr->lock_type=flags; ptr->updating=updating; if (use_index) @@ -2748,26 +3126,86 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, sizeof(*ignore_index)); /* check that used name is unique */ - current_db=thd->db ? thd->db : ""; - if (flags != TL_IGNORE) { - for (TABLE_LIST *tables=(TABLE_LIST*) thd->lex.table_list.first ; tables ; + for (TABLE_LIST *tables=(TABLE_LIST*) thd->lex.select->table_list.first ; + tables ; tables=tables->next) { - if (!strcmp(alias_str,tables->name) && - !strcmp(ptr->db ? ptr->db : current_db, - tables->db ? tables->db : current_db)) + if (!strcmp(alias_str,tables->name) && !strcmp(ptr->db, tables->db)) { net_printf(&thd->net,ER_NONUNIQ_TABLE,alias_str); /* purecov: tested */ DBUG_RETURN(0); /* purecov: tested */ } } } - link_in_list(&thd->lex.table_list,(byte*) ptr,(byte**) &ptr->next); + link_in_list(&thd->lex.select->table_list,(byte*) ptr,(byte**) &ptr->next); DBUG_RETURN(ptr); } + +/* +** This is used for UNION to create a new table list of all used tables +** The table_list->table entry in all used tables are set to point +** to the entries in this list. +*/ + +static bool create_total_list(THD *thd, LEX *lex, TABLE_LIST **result) +{ + /* Handle the case when we are not using union */ + if (!lex->select_lex.next) + { + *result= (TABLE_LIST*) lex->select_lex.table_list.first; + return 0; + } + + SELECT_LEX *sl; + TABLE_LIST **new_table_list= result, *aux; + + *new_table_list=0; // end result list + for (sl= &lex->select_lex; sl; sl=sl->next) + { + if (sl->order_list.first && sl->next && !sl->braces) + { + net_printf(&thd->net,ER_WRONG_USAGE,"UNION","ORDER BY"); + return 1; + } + if ((aux= (TABLE_LIST*) sl->table_list.first)) + { + TABLE_LIST *next; + for (; aux; aux=next) + { + TABLE_LIST *cursor; + next= aux->next; + for (cursor= *result; cursor; cursor=cursor->next) + if (!strcmp(cursor->db,aux->db) && + !strcmp(cursor->real_name,aux->real_name) && + !strcmp(cursor->name, aux->name)) + break; + if (!cursor) + { + /* Add not used table to the total table list */ + aux->lock_type= lex->lock_option; + if (!(cursor = (TABLE_LIST *) thd->memdup((char*) aux, + sizeof(*aux)))) + { + send_error(&thd->net,0); + return 1; + } + *new_table_list= cursor; + new_table_list= &cursor->next; + *new_table_list=0; // end result list + } + else + aux->shared=1; // Mark that it's used twice + aux->table=(TABLE *) cursor; + } + } + } + return 0; +} + + void add_join_on(TABLE_LIST *b,Item *expr) { if (!b->on_expr) @@ -2787,22 +3225,20 @@ void add_join_natural(TABLE_LIST *a,TABLE_LIST *b) /* Check if name is used in table list */ -static bool check_dup(THD *thd,const char *db,const char *name, - TABLE_LIST *tables) +static bool check_dup(const char *db, const char *name, TABLE_LIST *tables) { - const char *thd_db=thd->db ? thd->db : any_db; for (; tables ; tables=tables->next) - if (!strcmp(name,tables->real_name) && - !strcmp(db ? db : thd_db, tables->db ? tables->db : thd_db)) + if (!strcmp(name,tables->real_name) && !strcmp(db,tables->db)) return 1; return 0; } -bool reload_acl_and_cache(THD *thd, uint options, TABLE_LIST *tables) +bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables) { bool result=0; select_errors=0; /* Write if more errors */ + // TODO: figure out what's up with the commented out line below // mysql_log.flush(); // Flush log if (options & REFRESH_GRANT) { @@ -2818,12 +3254,21 @@ bool reload_acl_and_cache(THD *thd, uint options, TABLE_LIST *tables) if (ha_flush_logs()) result=1; } + if (options & REFRESH_QUERY_CACHE_FREE) + { + query_cache.pack(); // FLUSH QUERY CACHE + options &= ~REFRESH_QUERY_CACHE; //don't flush all cache, just free memory + } + if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE)) + { + query_cache.flush(); // RESET QUERY CACHE + } if (options & (REFRESH_TABLES | REFRESH_READ_LOCK)) { - if ((options & REFRESH_READ_LOCK) && thd && ! thd->global_read_lock) + if ((options & REFRESH_READ_LOCK) && thd) { - thd->global_read_lock=1; - thread_safe_increment(global_read_lock,&LOCK_open); + if (lock_global_read_lock(thd)) + return 1; } result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables); } @@ -2834,10 +3279,22 @@ bool reload_acl_and_cache(THD *thd, uint options, TABLE_LIST *tables) if (options & REFRESH_THREADS) flush_thread_cache(); if (options & REFRESH_MASTER) - reset_master(); - if (options & REFRESH_SLAVE) - reset_slave(); - + if (reset_master(thd)) + result=1; +#ifdef OPENSSL + if (options & REFRESH_DES_KEY_FILE) + { + if (des_key_file) + result=load_des_key_file(des_key_file); + } +#endif + if (options & REFRESH_SLAVE) + { + LOCK_ACTIVE_MI; + if (reset_slave(active_mi)) + result=1; + UNLOCK_ACTIVE_MI; + } return result; } @@ -2855,7 +3312,7 @@ void kill_one_thread(THD *thd, ulong id) if ((thd->master_access & PROCESS_ACL) || !strcmp(thd->user,tmp->user)) { - tmp->prepare_to_die(); + tmp->awake(1 /*prepare to die*/); error=0; } else @@ -2884,3 +3341,29 @@ static void refresh_status(void) pthread_mutex_unlock(&LOCK_status); pthread_mutex_unlock(&THR_LOCK_keycache); } + + + /* If pointer is not a null pointer, append filename to it */ + +static bool append_file_to_dir(THD *thd, char **filename_ptr, char *table_name) +{ + char buff[FN_REFLEN],*ptr, *end; + if (!*filename_ptr) + return 0; // nothing to do + + /* Check that the filename is not too long and it's a hard path */ + if (strlen(*filename_ptr)+strlen(table_name) >= FN_REFLEN-1 || + !test_if_hard_path(*filename_ptr)) + { + my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr); + return 1; + } + /* Fix is using unix filename format on dos */ + strmov(buff,*filename_ptr); + end=convert_dirname(buff, *filename_ptr, NullS); + if (!(ptr=thd->alloc((uint) (end-buff)+(uint) strlen(table_name)+1))) + return 1; // End of memory + *filename_ptr=ptr; + strxmov(ptr,buff,table_name,NullS); + return 0; +} |