diff options
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r-- | sql/sql_parse.cc | 2627 |
1 files changed, 2627 insertions, 0 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc new file mode 100644 index 00000000000..0c7b0b8187f --- /dev/null +++ b/sql/sql_parse.cc @@ -0,0 +1,2627 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#include "mysql_priv.h" +#include "sql_acl.h" +#include <m_ctype.h> +#include <thr_alarm.h> +#include <myisam.h> +#include <my_dir.h> + +#define SCRAMBLE_LENGTH 8 + +extern pthread_handler_decl(handle_slave,arg); +extern bool slave_running; +extern char* master_host; +extern pthread_t slave_real_id; +extern MASTER_INFO glob_mi; +extern my_string opt_bin_logname, master_info_file; +extern I_List<i_string> binlog_do_db, binlog_ignore_db; + +extern int yyparse(void); +extern "C" pthread_mutex_t THR_LOCK_keycache; + +static bool check_table_access(THD *thd,uint want_access,TABLE_LIST *tables); +static bool check_lock_tables(THD *thd,TABLE_LIST *tables); +static bool check_dup(THD *thd,const char *db,const char *name, + TABLE_LIST *tables); +static void mysql_init_query(THD *thd); +static void remove_escape(char *name); +static void kill_one_thread(THD *thd, ulong thread); +static void refresh_status(void); +static int start_slave(THD* thd = 0, bool net_report = 1); +static int stop_slave(THD* thd = 0, bool net_report = 1); +static int change_master(THD* thd); +static void reset_slave(); +static void reset_master(); + + +static const char *any_db="*any*"; // Special symbol for check_access + +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","Start Slave", "Abort Slave" +}; + +bool volatile abort_slave = 0; + +#ifdef HAVE_OPENSSL +extern VioSSLAcceptorFd* ssl_acceptor_fd; +#endif /* HAVE_OPENSSL */ + +#ifdef __WIN__ +static void test_signal(int sig_ptr) +{ +#ifndef DBUG_OFF + MessageBox(NULL,"Test signal","DBUG",MB_OK); +#endif +} +static void init_signals(void) +{ + int signals[7] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGBREAK,SIGABRT } ; + for(int i=0 ; i < 7 ; i++) + signal( signals[i], test_signal) ; +} +#endif + +/* +** 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; + thd->db=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, + passwd, thd->scramble, &thd->priv_user, + protocol_version == 9 || + !(thd->client_capabilities & + CLIENT_LONG_PASSWORD)); + 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, + thd->host ? thd->host : thd->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, + passwd[0] ? ER(ER_YES) : ER(ER_NO)); + mysql_log.write(COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR), + thd->user, + thd->host ? thd->host : thd->ip ? thd->ip : "unknown ip", + passwd[0] ? ER(ER_YES) : ER(ER_NO)); + return(1); // Error already given + } + if (check_count) + { + VOID(pthread_mutex_lock(&LOCK_thread_count)); + bool tmp=(thread_count - delayed_insert_threads >= max_connections && + !(thd->master_access & PROCESS_ACL)); + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + if (tmp) + { // Too many connections + send_error(net, ER_CON_COUNT_ERROR); + return(1); + } + } + mysql_log.write(command, + (thd->priv_user == thd->user ? + (char*) "%s@%s on %s" : + (char*) "%s@%s as anonymous on %s"), + user, + thd->host ? thd->host : thd->ip ? thd->ip : "unknown ip", + db ? db : (char*) ""); + thd->db_access=0; + if (db && db[0]) + return test(mysql_change_db(thd,db)); + else + send_ok(net); // Ready to handle questions + return 0; // ok +} + + +/* +** 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 + */ + DBUG_PRINT("info", (("check_connections called by thread %d"), + thd->thread_id)); + DBUG_PRINT("general",("New connection received on %s", + vio_description(net->vio))); + if (!thd->host) // If TCP/IP connection + { + char ip[17]; + + if (vio_peer_addr(net->vio,ip)) + return (ER_BAD_HOST_ERROR); + if (!(thd->ip = my_strdup(ip,MYF(0)))) + return (ER_OUT_OF_RESOURCES); +#if !defined(HAVE_SYS_UN_H) || defined(HAVE_mit_thread) + /* Fast local hostname resolve for Win32 */ + if (!strcmp(thd->ip,"127.0.0.1")) + { + if (!(thd->host=my_strdup("localhost",MYF(0)))) + return (ER_OUT_OF_RESOURCES); + } + else +#endif + if (!(specialflag & SPECIAL_NO_RESOLVE)) + { + vio_in_addr(net->vio,&thd->remote.sin_addr); + thd->host=ip_to_hostname(&thd->remote.sin_addr,&connect_errors); + 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")); + if (acl_check_host(thd->host,thd->ip)) + return(ER_HOST_NOT_PRIVILEGED); + } + else /* No hostname means that the connection was on a socket */ + { + DBUG_PRINT("general",("Host: localhost")); + thd->ip=0; + bzero((char*) &thd->remote,sizeof(struct sockaddr)); + } + vio_keepalive(net->vio, TRUE); + + /* nasty, but any other way? */ + uint pkt_len = 0; + { + char buff[60],*end; + int client_flags = CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB | + CLIENT_TRANSACTIONS; + LINT_INIT(pkt_len); + + end=strmov(buff,server_version)+1; + int4store((uchar*) end,thd->thread_id); + end+=4; + memcpy(end,thd->scramble,SCRAMBLE_LENGTH+1); + end+=SCRAMBLE_LENGTH +1; +#ifdef HAVE_COMPRESS + client_flags |= CLIENT_COMPRESS; +#endif /* HAVE_COMPRESS */ +#ifdef HAVE_OPENSSL + if (ssl_acceptor_fd!=0) + 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; + int2store(end+3,thd->server_status); + bzero(end+5,13); + end+=18; + if (net_write_command(net,protocol_version, buff, + (uint) (end-buff)) || + (pkt_len=my_net_read(net)) == packet_error || + pkt_len < MIN_HANDSHAKE_SIZE) + { + inc_host_errors(&thd->remote.sin_addr); + return(ER_HANDSHAKE_ERROR); + } + } +#ifdef _CUSTOMCONFIG_ +#include "_cust_sql_parse.h" +#endif + if (connect_errors) + reset_host_errors(&thd->remote.sin_addr); + if (thd->packet.alloc(net_buffer_length)) + return(ER_OUT_OF_RESOURCES); + + thd->client_capabilities=uint2korr(net->read_pos); +#ifdef HAVE_OPENSSL + DBUG_PRINT("info", + ("pkt_len:%d, client capabilities: %d", + pkt_len, 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); + 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")); + inc_host_errors(&thd->remote.sin_addr); + return(ER_HANDSHAKE_ERROR); + } + } + else + { + DBUG_PRINT("info", ("Leaving IO layer intact")); + if (pkt_len < NORMAL_HANDSHAKE_SIZE) + { + inc_host_errors(&thd->remote.sin_addr); + return ER_HANDSHAKE_ERROR; + } + } +#endif + + thd->max_packet_length=uint3korr(net->read_pos+2); + char *user= (char*) net->read_pos+5; + char *passwd= strend(user)+1; + char *db=0; + if (passwd[0] && strlen(passwd) != SCRAMBLE_LENGTH) + return ER_HANDSHAKE_ERROR; + if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB) + db=strend(passwd)+1; + if (thd->client_capabilities & CLIENT_INTERACTIVE) + thd->inactive_timeout=net_interactive_timeout; + if (thd->client_capabilities & CLIENT_TRANSACTIONS) + thd->net.return_status= &thd->server_status; + net->timeout=net_read_timeout; + if (check_user(thd,COM_CONNECT, user, passwd, db, 1)) + return (-1); + thd->password=test(passwd[0]); + return 0; +} + + +pthread_handler_decl(handle_one_connection,arg) +{ + THD *thd=(THD*) arg; + uint launch_time = + (thd->thr_create_time = time(NULL)) - thd->connect_time; + if (launch_time >= slow_launch_time) + statistic_increment(slow_launch_threads,&LOCK_status ); + + pthread_detach_this_thread(); + +#ifndef __WIN__ /* Win32 calls this in pthread_create */ + if (my_thread_init()) // needed to be called first before we call + // DBUG_ macros + { + close_connection(&thd->net,ER_OUT_OF_RESOURCES); + statistic_increment(aborted_connects,&LOCK_thread_count); + end_thread(thd,0); + return 0; + } +#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 + + 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_* + +#ifdef __WIN__ + init_signals(); // IRENA; testing ? +#else + sigset_t set; + VOID(sigemptyset(&set)); // Get mask in use + VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); +#endif + if (thd->store_globals()) + { + close_connection(&thd->net,ER_OUT_OF_RESOURCES); + statistic_increment(aborted_connects,&LOCK_thread_count); + end_thread(thd,0); + return 0; + } + + do + { + int error; + NET *net= &thd->net; + + thd->mysys_var=my_thread_var; + thd->dbug_thread_id=my_thread_id(); + thd->thread_stack= (char*) &thd; + + if ((error=check_connections(thd))) + { // Wrong permissions + if (error > 0) + net_printf(net,error,thd->host ? thd->host : thd->ip); +#ifdef __NT__ + if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE) + sleep(1); /* must wait after eof() */ +#endif + statistic_increment(aborted_connects,&LOCK_thread_count); + goto end_thread; + } + + thd->alloc.free=thd->alloc.used=0; + if (thd->max_join_size == HA_POS_ERROR) + thd->options |= OPTION_BIG_SELECTS; + if (thd->client_capabilities & CLIENT_COMPRESS) + net->compress=1; // Use compression + if (thd->options & OPTION_ANSI_MODE) + thd->client_capabilities|=CLIENT_IGNORE_SPACE; + + thd->proc_info=0; + thd->version=refresh_version; + thd->set_time(); + while (!net->error && net->vio != 0 && !thd->killed) + { + if (do_command(thd)) + break; + } + if (net->error && net->vio != 0) + { + sql_print_error(ER(ER_NEW_ABORTING_CONNECTION), + thd->thread_id,(thd->db ? thd->db : "unconnected"), + thd->user, + (thd->host ? thd->host : thd->ip ? thd->ip : "unknown"), + (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); + } + +end_thread: + close_connection(net); + end_thread(thd,1); + /* + If end_thread returns, we are either running with --one-thread + or this thread has been schedule to handle the next query + */ + thd= current_thd; + } while (!(test_flags & TEST_NO_THREADS)); + /* The following is only executed if we are not using --one-thread */ + return(0); /* purecov: deadcode */ +} + + +int handle_bootstrap(THD *thd,FILE *file) +{ + DBUG_ENTER("handle_bootstrap"); + thd->thread_stack= (char*) &thd; + + if (init_thr_lock() || + my_pthread_setspecific_ptr(THR_THD, thd) || + my_pthread_setspecific_ptr(THR_MALLOC, &thd->alloc) || + my_pthread_setspecific_ptr(THR_NET, &thd->net)) + { + close_connection(&thd->net,ER_OUT_OF_RESOURCES); + DBUG_RETURN(-1); + } + thd->mysys_var=my_thread_var; + thd->dbug_thread_id=my_thread_id(); +#ifndef __WIN__ + sigset_t set; + VOID(sigemptyset(&set)); // Get mask in use + VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); +#endif + + thd->alloc.free=thd->alloc.used=0; + if (thd->max_join_size == (ulong) ~0L) + thd->options |= OPTION_BIG_SELECTS; + + thd->proc_info=0; + thd->version=refresh_version; + + char *buff= (char*) thd->net.buff; + while (fgets(buff, thd->net.max_packet, file)) + { + uint length=strlen(buff); + while (length && (isspace(buff[length-1]) || buff[length-1] == ';')) + length--; + buff[length]=0; + init_sql_alloc(&thd->alloc,8192); + thd->current_tablenr=0; + thd->query= sql_memdup(buff,length+1); + thd->query_id=query_id++; + mysql_parse(thd,thd->query,length); + close_thread_tables(thd); // Free tables + if (thd->fatal_error) + { + DBUG_RETURN(-1); + } + free_root(&thd->alloc); + } + DBUG_RETURN(0); +} + + +static inline void free_items(THD *thd) +{ + /* This works because items are allocated with sql_alloc() */ + for (Item *item=thd->free_list ; item ; item=item->next) + delete item; +} + +int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) +{ + TABLE* table; + TABLE_LIST* table_list; + 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)))) + DBUG_RETURN(1); // out of memory + table_list->db = db; + table_list->real_name = table_list->name = tbl_name; + table_list->lock_type = TL_READ_NO_INSERT; + table_list->next = 0; + remove_escape(table_list->real_name); + + if(!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT))) + DBUG_RETURN(1); + + if(check_access(thd, SELECT_ACL, db, &table_list->grant.privilege)) + goto err; + if(grant_option && check_grant(thd, SELECT_ACL, table_list)) + goto err; + + thd->free_list = 0; + thd->query = tbl_name; + 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)); + +err: + + close_thread_tables(thd); + + DBUG_RETURN(error); +} + + + + /* Execute one command from socket (query or simple command) */ + +bool do_command(THD *thd) +{ + char *packet; + uint old_timeout,packet_length; + bool error=0; + NET *net; + enum enum_server_command command; + DBUG_ENTER("do_command"); + + init_sql_alloc(&thd->alloc,8192); + net= &thd->net; + thd->current_tablenr=0; + + packet=0; + old_timeout=net->timeout; + net->timeout=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) )); + return TRUE; + } + else + { + packet=(char*) net->read_pos; + command = (enum enum_server_command) (uchar) packet[0]; + DBUG_PRINT("general",("Command on socket %s = %d (%s)", + vio_description(net->vio), command, + command_name[command])); + } + net->timeout=old_timeout; /* Timeout */ + thd->command=command; + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->query_id=query_id; + if (command != COM_STATISTICS && command != COM_PING) + query_id++; + thread_running++; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + thd->set_time(); + switch(command) { + case COM_INIT_DB: + if (!mysql_change_db(thd,packet+1)) + mysql_log.write(command,"%s",thd->db); + break; + case COM_TABLE_DUMP: + { + 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); + char* tbl_name = db + db_len; + *tbl_name++ = 0; + memcpy(tbl_name, data + db_len + 2, tbl_len); + tbl_name[tbl_len] = 0; + if(mysql_table_dump(thd, db, tbl_name, -1)) + send_error(&thd->net); // dump to NET + + break; + } + case COM_CHANGE_USER: + { + char *user= (char*) packet+1; + 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; + char *save_user= thd->user; + char *save_priv_user= thd->priv_user; + char *save_db= thd->db; + + if ((uint) ((uchar*) db - net->read_pos) > packet_length) + { // Check if protocol is ok + send_error(net, ER_UNKNOWN_COM_ERROR); + break; + } + if (check_user(thd, COM_CHANGE_USER, user, passwd, db,0)) + { // Restore old user + x_free(thd->user); + x_free(thd->db); + thd->master_access=save_master_access; + thd->db_access=save_db_access; + thd->db=save_db; + thd->user=save_user; + thd->priv_user=save_priv_user; + break; + } + x_free((gptr) save_db); + x_free((gptr) save_user); + thd->password=test(passwd[0]); + break; + } + + case COM_QUERY: + { + char *pos=packet+packet_length; // Point at end null + /* Remove garage at end of query */ + while (packet_length > 0 && pos[-1] == ';') + { + pos--; + packet_length--; + } + *pos=0; + if (!(thd->query= (char*) sql_memdup((gptr) (packet+1),packet_length))) + break; + thd->packet.shrink(net_buffer_length); // Reclaim some memory + if (!(specialflag & SPECIAL_NO_PRIOR)) + my_pthread_setprio(pthread_self(),QUERY_PRIOR); + mysql_log.write(command,"%s",thd->query); + DBUG_PRINT("query",("%s",thd->query)); + mysql_parse(thd,thd->query,packet_length-1); + 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 +#ifdef DONT_ALLOW_SHOW_COMMANDS + send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + break; +#else + { + char *fields; + TABLE_LIST table_list; + bzero((char*) &table_list,sizeof(table_list)); + if (!(table_list.db=thd->db)) + { + send_error(net,ER_NO_DB_ERROR); + break; + } + thd->free_list=0; + table_list.name=table_list.real_name=sql_strdup(packet+1); + thd->query=fields=sql_strdup(strend(packet+1)+1); + mysql_log.write(command,"%s %s",table_list.real_name,fields); + remove_escape(table_list.real_name); // This can't have wildcards + + if (check_access(thd,SELECT_ACL,table_list.db,&thd->col_access)) + break; + table_list.grant.privilege=thd->col_access; + if (grant_option && check_grant(thd,SELECT_ACL,&table_list,2)) + break; + mysqld_list_fields(thd,&table_list,fields); + free_items(thd); + break; + } +#endif + case COM_QUIT: + mysql_log.write(command,NullS); + net->error=0; // Don't give 'abort' message + error=TRUE; // End server + break; + + case COM_CREATE_DB: + { + char *db=sql_strdup(packet+1); + if (check_access(thd,CREATE_ACL,db,0,1)) + break; + mysql_log.write(command,packet+1); + mysql_create_db(thd,db,0); + break; + } + case COM_DROP_DB: + { + char *db=sql_strdup(packet+1); + if (check_access(thd,DROP_ACL,db,0,1)) + break; + mysql_log.write(command,db); + mysql_rm_db(thd,db,0); + break; + } + case COM_BINLOG_DUMP: + { + if(check_access(thd, FILE_ACL, any_db)) + break; + mysql_log.write(command, 0); + + ulong pos; + ushort flags; + pos = uint4korr(packet + 1); + flags = uint2korr(packet + 5); + mysql_binlog_send(thd, sql_strdup(packet + 7), pos, flags); + break; + } + case COM_REFRESH: + { + uint options=(uchar) packet[1]; + if (check_access(thd,RELOAD_ACL,any_db)) + break; + mysql_log.write(command,NullS); + if (reload_acl_and_cache(options)) + send_error(net,0); + else + send_eof(net); + break; + } + case COM_SHUTDOWN: + if (check_access(thd,SHUTDOWN_ACL,any_db)) + break; /* purecov: inspected */ + DBUG_PRINT("quit",("Got shutdown command")); + mysql_log.write(command,NullS); + send_eof(net); +#ifdef __WIN__ + sleep(1); // must wait after eof() +#endif + send_eof(net); // This is for 'quit request' + close_connection(net); + close_thread_tables(thd); // Free before kill + free_root(&thd->alloc); + kill_mysql(); + error=TRUE; + break; + + case COM_STATISTICS: + { + mysql_log.write(command,NullS); + char buff[200]; + ulong uptime = (ulong) (time((time_t*) 0) - 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, + (int) thread_count,thd->query_id,long_query_count, + opened_tables,refresh_version, cached_tables(), + uptime ? (float)thd->query_id/(float)uptime : 0); +#ifdef SAFEMALLOC + if (lCurMemory) // Using SAFEMALLOC + sprintf(strend(buff), " Memory in use: %ldK Max memory used: %ldK", + (lCurMemory+1023L)/1024L,(lMaxMemory+1023L)/1024L); + #endif + VOID(my_net_write(net, buff,strlen(buff))); + VOID(net_flush(net)); + break; + } + case COM_PING: + send_ok(net); // Tell client we are alive + break; + case COM_PROCESS_INFO: + if (!thd->priv_user[0] && check_access(thd,PROCESS_ACL,any_db)) + break; + mysql_log.write(command,NullS); + mysqld_list_processes(thd,thd->master_access & PROCESS_ACL ? NullS : + thd->priv_user,0); + break; + case COM_PROCESS_KILL: + { + ulong id=(ulong) uint4korr(packet+1); + kill_one_thread(thd,id); + break; + } + case COM_DEBUG: + if (check_access(thd,PROCESS_ACL,any_db)) + break; /* purecov: inspected */ + mysql_print_status(thd); + mysql_log.write(command,NullS); + send_eof(net); + break; + case COM_SLEEP: + case COM_CONNECT: // Impossible here + case COM_TIME: // Impossible from client + case COM_DELAYED_INSERT: + default: + send_error(net, ER_UNKNOWN_COM_ERROR); + break; + } + if (thd->lock || thd->open_tables) + { + thd->proc_info="closing tables"; + close_thread_tables(thd); /* Free tables */ + } + thd->proc_info="cleaning up"; + + if (thd->fatal_error) + send_error(net,0); // End of memory ? + + time_t start_of_query=thd->start_time; + thd->set_time(); + if ((ulong) (thd->start_time - start_of_query) > long_query_time) + { + long_query_count++; + mysql_slow_log.write(thd->query, thd->query_length, + (ulong) (thd->start_time - start_of_query)); + } + VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list + thd->proc_info=0; + thd->command=COM_SLEEP; + thd->query=0; + thread_running--; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + thd->packet.shrink(net_buffer_length); // Reclaim some memory + free_root(&thd->alloc); + DBUG_RETURN(error); +} + +/**************************************************************************** +** mysql_execute_command +** Execute command saved in thd and current_lex->sql_command +****************************************************************************/ + +void +mysql_execute_command(void) +{ + int res=0; + THD *thd=current_thd; + LEX *lex=current_lex; + TABLE_LIST *tables=(TABLE_LIST*) lex->table_list.first; + DBUG_ENTER("mysql_execute_command"); + + switch (lex->sql_command) { + case SQLCOM_SELECT: + { + select_result *result; + if (lex->options & SELECT_DESCRIBE) + lex->exchange=0; + if (tables) + { + res=check_table_access(thd, + lex->exchange ? SELECT_ACL | FILE_ACL : + SELECT_ACL, + tables); + } + else + res=check_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, + any_db); + if (res) + { + res=0; + 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->select_limit= HA_POS_ERROR; // no limit + + if (lex->exchange) + { + if (lex->exchange->dumpfile) + { + if (!(result=new select_dump(lex->exchange))) + { + res= -1; + break; + } + } + else + { + if (!(result=new select_export(lex->exchange))) + { + res= -1; + break; + } + } + } + else if (!(result=new select_send())) + { + res= -1; +#ifdef DELETE_ITEMS + delete lex->having; + delete lex->where; +#endif + break; + } + + if (lex->options & SELECT_HIGH_PRIORITY) + { + TABLE_LIST *table; + for (table = tables ; table ; table=table->next) + table->lock_type=TL_READ_HIGH_PRIORITY; + } + + if (!(res=open_and_lock_tables(thd,tables))) + { + res=mysql_select(thd,tables,lex->item_list, + lex->where, + lex->ftfunc_list, + (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; +#ifdef DELETE_ITEMS + delete lex->having; + delete lex->where; +#endif + break; + } + case SQLCOM_CHANGE_MASTER: + { + if(check_access(thd, PROCESS_ACL, any_db)) + goto error; + res = change_master(thd); + break; + } + case SQLCOM_SHOW_SLAVE_STAT: + { + if(check_access(thd, PROCESS_ACL, any_db)) + goto error; + res = show_master_info(thd); + break; + } + case SQLCOM_SHOW_MASTER_STAT: + { + if(check_access(thd, PROCESS_ACL, any_db)) + goto error; + res = show_binlog_info(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)) + goto error; /* purecov: inspected */ + if (grant_option) + { + /* Check that the first table has CREATE privilege */ + TABLE_LIST *tmp_table_list=tables->next; + tables->next=0; + bool error=check_grant(thd,CREATE_ACL,tables); + tables->next=tmp_table_list; + if (error) + goto error; + } + if (strlen(tables->name) > NAME_LEN) + { + net_printf(&thd->net,ER_WRONG_TABLE_NAME,tables->name); + res=0; + break; + } + + thd->last_nx_table = tables->real_name; + thd->last_nx_db = tables->db; + if(fetch_nx_table(thd, &glob_mi)) + // fetch_nx_table is responsible for sending + // the error + { + res = 0; + thd->net.no_send_ok = 0; // easier to do it here + // this way we make sure that when we are done, we are clean + break; + } + + res = 0; + send_ok(&thd->net); + break; + + case SQLCOM_CREATE_TABLE: +#ifdef DEMO_VERSION + send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); +#else + if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege)) + goto error; /* purecov: inspected */ + if (grant_option) + { + /* Check that the first table has CREATE privilege */ + TABLE_LIST *tmp_table_list=tables->next; + tables->next=0; + bool error=check_grant(thd,CREATE_ACL,tables); + tables->next=tmp_table_list; + if (error) + goto error; + } + if (strlen(tables->name) > NAME_LEN) + { + net_printf(&thd->net,ER_WRONG_TABLE_NAME,tables->name); + res=0; + break; + } + if (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)) + { + net_printf(&thd->net,ER_INSERT_TABLE_USED,tables->real_name); + DBUG_VOID_RETURN; + } + if (tables->next) + { + if (check_table_access(thd, SELECT_ACL, tables->next)) + goto error; // 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->select_limit= HA_POS_ERROR; // No limit + + 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, + lex->ftfunc_list, + (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; + } + else + res= -1; + } + } + else // regular create + { + res = mysql_create_table(thd,tables->db ? tables->db : thd->db, + tables->real_name, &lex->create_info, + lex->create_list, + lex->key_list,0, 0); // do logging + if (!res) + send_ok(&thd->net); + } + break; + case SQLCOM_CREATE_INDEX: + if (!tables->db) + tables->db=thd->db; + if (check_access(thd,INDEX_ACL,tables->db,&tables->grant.privilege)) + goto error; /* purecov: inspected */ + if (grant_option && check_grant(thd,INDEX_ACL,tables)) + goto error; + res = mysql_create_index(thd, tables, lex->key_list); +#endif + break; + + case SQLCOM_SLAVE_START: + start_slave(thd); + break; + case SQLCOM_SLAVE_STOP: + stop_slave(thd); + break; + + + case SQLCOM_ALTER_TABLE: +#if defined(DONT_ALLOW_SHOW_COMMANDS) + send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + break; +#else + { + uint priv=0; + if (lex->name && strlen(lex->name) > NAME_LEN) + { + net_printf(&thd->net,ER_WRONG_TABLE_NAME,lex->name); + res=0; + break; + } + if (!lex->db) + 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)) + goto error; /* purecov: inspected */ + if (!tables->db) + tables->db=thd->db; + if (grant_option) + { + if (check_grant(thd,ALTER_ACL,tables)) + goto error; + if (lex->name && !test_all_bits(priv,INSERT_ACL | CREATE_ACL)) + { // Rename of table + TABLE_LIST tmp_table; + bzero((char*) &tmp_table,sizeof(tmp_table)); + tmp_table.real_name=lex->name; + tmp_table.db=lex->db; + tmp_table.grant.privilege=priv; + if (check_grant(thd,INSERT_ACL | CREATE_ACL,tables)) + goto error; + } + } + /* ALTER TABLE ends previous transaction */ + if (!(thd->options & OPTION_AUTO_COMMIT) && ha_commit(thd)) + res= -1; + else + res= mysql_alter_table(thd, lex->db, lex->name, + &lex->create_info, + tables, lex->create_list, + lex->key_list, lex->drop_list, lex->alter_list, + lex->drop_primary, lex->duplicates); + break; + } +#endif + case SQLCOM_SHOW_CREATE: + { + if(! tables->db) + tables->db = thd->db; + if (!tables->db) + { + send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ + goto error; /* purecov: inspected */ + } + res = mysqld_show_create(thd, tables); + break; + } + case SQLCOM_REPAIR: + { + if (!tables->db) + tables->db=thd->db; + if (check_access(thd,SELECT_ACL | INSERT_ACL,tables->db, + &tables->grant.privilege)) + goto error; /* purecov: inspected */ + if (grant_option && check_grant(thd,SELECT_ACL | INSERT_ACL,tables)) + goto error; + res = mysql_repair_table(thd, tables, &lex->check_opt); + break; + } + case SQLCOM_CHECK: + { + if (!tables->db) + tables->db=thd->db; + if (check_access(thd,SELECT_ACL,tables->db, + &tables->grant.privilege)) + goto error; /* purecov: inspected */ + if (grant_option && check_grant(thd,SELECT_ACL,tables)) + goto error; + res = mysql_check_table(thd, tables, &lex->check_opt); + break; + } + case SQLCOM_ANALYZE: + { + if (!tables->db) + tables->db=thd->db; + if (check_access(thd,SELECT_ACL|INSERT_ACL,tables->db, + &tables->grant.privilege)) + goto error; /* purecov: inspected */ + if (grant_option && check_grant(thd,SELECT_ACL|INSERT_ACL,tables)) + goto error; + res = mysql_analyze_table(thd, tables); + break; + } + case SQLCOM_OPTIMIZE: + { + HA_CREATE_INFO create_info; + /* This is now done with ALTER TABLE, but should be done with isamchk */ + if (!tables->db) + tables->db=thd->db; + if (check_access(thd,SELECT_ACL | INSERT_ACL,tables->db, + &tables->grant.privilege)) + goto error; /* purecov: inspected */ + if (grant_option && check_grant(thd,SELECT_ACL | INSERT_ACL,tables)) + goto error; + + lex->create_list.empty(); + lex->key_list.empty(); + lex->col_list.empty(); + lex->drop_list.empty(); + lex->alter_list.empty(); + bzero((char*) &create_info,sizeof(create_info)); + create_info.db_type=DB_TYPE_DEFAULT; + create_info.row_type=ROW_TYPE_DEFAULT; + res= mysql_alter_table(thd, NullS, NullS, &create_info, + tables, lex->create_list, + lex->key_list, lex->drop_list, lex->alter_list, + 0,DUP_ERROR); + break; + } + case SQLCOM_UPDATE: + 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 (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); + +#ifdef DELETE_ITEMS + delete lex->where; +#endif + break; + case SQLCOM_INSERT: + if (check_access(thd,INSERT_ACL,tables->db,&tables->grant.privilege)) + goto error; /* purecov: inspected */ + if (grant_option && check_grant(thd,INSERT_ACL,tables)) + goto error; + res = mysql_insert(thd,tables,lex->field_list,lex->many_values, + lex->duplicates, + lex->lock_option); + break; + case SQLCOM_REPLACE: + if (check_access(thd,INSERT_ACL | UPDATE_ACL | DELETE_ACL, + tables->db,&tables->grant.privilege)) + goto error; /* purecov: inspected */ + if (grant_option && check_grant(thd,INSERT_ACL | UPDATE_ACL | DELETE_ACL, + tables)) + + goto error; + res = mysql_insert(thd,tables,lex->field_list,lex->many_values, + DUP_REPLACE, + lex->lock_option); + break; + case SQLCOM_REPLACE_SELECT: + case SQLCOM_INSERT_SELECT: + { + // Check that we have modify privileges for the first table and + // select privileges for the rest + uint privilege= (lex->sql_command == SQLCOM_INSERT_SELECT ? + INSERT_ACL : INSERT_ACL | UPDATE_ACL | DELETE_ACL); + TABLE_LIST *save_next=tables->next; + tables->next=0; + if (check_access(thd, privilege, + tables->db,&tables->grant.privilege) || + (grant_option && check_grant(thd, privilege, tables))) + goto error; + tables->next=save_next; + if ((res=check_table_access(thd, SELECT_ACL, save_next))) + goto error; + + 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->select_limit= HA_POS_ERROR; // No limit + + if (check_dup(thd,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 + 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, + lex->ftfunc_list, + (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; + } +#ifdef DELETE_ITEMS + delete lex->having; + delete lex->where; +#endif + break; + } + case SQLCOM_DELETE: + { + if (check_access(thd,DELETE_ACL,tables->db,&tables->grant.privilege)) + goto error; /* purecov: inspected */ + if (grant_option && check_grant(thd,DELETE_ACL,tables)) + goto error; + // Set privilege for the WHERE clause + tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege); + res = mysql_delete(thd,tables,lex->where,lex->select_limit, + lex->lock_option); +#ifdef DELETE_ITEMS + delete lex->where; +#endif + break; + } + case SQLCOM_DROP_TABLE: + { + if (check_table_access(thd,DROP_ACL,tables)) + goto error; /* purecov: inspected */ + res = mysql_rm_table(thd,tables,lex->drop_if_exists); + } + break; + case SQLCOM_DROP_INDEX: + if (!tables->db) + tables->db=thd->db; + if (check_access(thd,INDEX_ACL,tables->db,&tables->grant.privilege)) + goto error; /* purecov: inspected */ + if (grant_option && check_grant(thd,INDEX_ACL,tables)) + goto error; + res = mysql_drop_index(thd, tables, lex->drop_list); + break; + case SQLCOM_SHOW_DATABASES: +#if defined(DONT_ALLOW_SHOW_COMMANDS) || defined(DEMO_VERSION) + send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + DBUG_VOID_RETURN; +#else + if ((specialflag & SPECIAL_SKIP_SHOW_DB) && + check_access(thd,PROCESS_ACL,any_db)) + goto error; + res= mysqld_show_dbs(thd, (lex->wild ? lex->wild->ptr() : NullS)); + break; +#endif + case SQLCOM_SHOW_PROCESSLIST: + if (!thd->priv_user[0] && check_access(thd,PROCESS_ACL,any_db)) + break; + mysqld_list_processes(thd,thd->master_access & PROCESS_ACL ? NullS : + thd->priv_user,lex->verbose); + break; + case SQLCOM_SHOW_STATUS: + res= mysqld_show(thd,(lex->wild ? lex->wild->ptr() : NullS),status_vars); + break; + case SQLCOM_SHOW_VARIABLES: + res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS), + init_vars); + break; + case SQLCOM_SHOW_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; + if (!db) + { + send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ + goto error; /* purecov: inspected */ + } + remove_escape(db); // Fix escaped '_' + if (strlen(db) > NAME_LEN) + { + net_printf(&thd->net,ER_WRONG_DB_NAME, db); + goto error; + } + if (check_access(thd,SELECT_ACL,db,&thd->col_access)) + goto error; /* purecov: inspected */ + /* grant is checked in mysqld_show_tables */ + if (lex->options & SELECT_DESCRIBE) + res= mysqld_extend_show_tables(thd,db, + (lex->wild ? lex->wild->ptr() : NullS)); + else + res= mysqld_show_tables(thd,db, + (lex->wild ? lex->wild->ptr() : NullS)); + break; + } +#endif + 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) + { + 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; + if (grant_option && check_grant(thd,SELECT_ACL,tables,2)) + goto error; + res= mysqld_show_fields(thd,tables, + (lex->wild ? lex->wild->ptr() : NullS)); + break; + } +#endif + case SQLCOM_SHOW_KEYS: +#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) + { + 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,db,&thd->col_access)) + goto error; /* purecov: inspected */ + tables->grant.privilege=thd->col_access; + if (grant_option && check_grant(thd,SELECT_ACL,tables,2)) + goto error; + res= mysqld_show_keys(thd,tables); + break; + } +#endif + case SQLCOM_CHANGE_DB: + mysql_change_db(thd,lex->db); + break; + case SQLCOM_LOAD: + { + uint privilege= (lex->duplicates == DUP_REPLACE ? + INSERT_ACL | UPDATE_ACL | DELETE_ACL : INSERT_ACL); + if (!(lex->local_file && (thd->client_capabilities & CLIENT_LOCAL_FILES))) + { + if (check_access(thd,privilege | FILE_ACL,tables->db)) + goto error; + } + else + { + if (check_access(thd,privilege,tables->db,&tables->grant.privilege) || + grant_option && check_grant(thd,privilege,tables)) + goto error; + } + res=mysql_load(thd, lex->exchange, tables, lex->field_list, + lex->duplicates, (bool) lex->local_file, lex->lock_option); + break; + } + case SQLCOM_SET_OPTION: + { + uint org_options=thd->options; + thd->options=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; + 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_AUTO_COMMIT) + { + if (org_options & OPTION_AUTO_COMMIT) + { + /* We changed to auto_commit mode */ + thd->options&= ~OPTION_BEGIN; + thd->server_status|= SERVER_STATUS_AUTOCOMMIT; + if (ha_commit(thd)) + { + res= -1; + break; + } + } + else + thd->server_status&= ~SERVER_STATUS_AUTOCOMMIT; + } + send_ok(&thd->net); + break; + } + case SQLCOM_UNLOCK_TABLES: + if (thd->locked_tables) + { + thd->lock=thd->locked_tables; + thd->locked_tables=0; // Will be automaticly closed + } + 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); + } + send_ok(&thd->net); + break; + case SQLCOM_LOCK_TABLES: + if (thd->locked_tables) + { + thd->lock=thd->locked_tables; + thd->locked_tables=0; // Will be automaticly closed + close_thread_tables(thd); + } + if (check_lock_tables(thd,tables)) + goto error; + thd->in_lock_tables=1; + if (!(res=open_and_lock_tables(thd,tables))) + { + thd->locked_tables=thd->lock; + thd->lock=0; + send_ok(&thd->net); + } + thd->in_lock_tables=0; + break; + case SQLCOM_CREATE_DB: + { + if (check_access(thd,CREATE_ACL,lex->name,0,1)) + break; + mysql_create_db(thd,lex->name,lex->create_info.options); + break; + } + case SQLCOM_DROP_DB: + { + if (check_access(thd,DROP_ACL,lex->name,0,1)) + break; + mysql_rm_db(thd,lex->name,lex->drop_if_exists); + break; + } + case SQLCOM_CREATE_FUNCTION: + if (check_access(thd,INSERT_ACL,"mysql",0,1)) + break; +#ifdef HAVE_DLOPEN + if (!(res = mysql_create_function(thd,&lex->udf))) + send_ok(&thd->net); +#else + res= -1; +#endif + break; + case SQLCOM_DROP_FUNCTION: + if (check_access(thd,DELETE_ACL,"mysql",0,1)) + break; +#ifdef HAVE_DLOPEN + if (!(res = mysql_drop_function(thd,lex->udf.name))) + send_ok(&thd->net); +#else + 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 */ + + List_iterator <LEX_USER> user_list(lex->users_list); + LEX_USER *user; + if(thd->user) + 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->query,thd->query_length); + 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->query,thd->query_length); + Query_log_event qinfo(thd, thd->query); + mysql_bin_log.write(&qinfo); + } + } + break; + } + case SQLCOM_FLUSH: + if (check_access(thd,RELOAD_ACL,any_db)) + goto error; + if (reload_acl_and_cache(lex->type)) + send_error(&thd->net,0); + else + send_ok(&thd->net); + break; + case SQLCOM_KILL: + kill_one_thread(thd,lex->thread_id); + break; + case SQLCOM_SHOW_GRANTS: + res=0; + if ((thd->user && !strcmp(thd->user,lex->grant_user->user.str)) || + !(check_access(thd, SELECT_ACL, "mysql"))) + { + res = mysql_show_grants(thd,lex->grant_user); + } + break; + case SQLCOM_BEGIN: + thd->options|= OPTION_BEGIN; + thd->server_status|= SERVER_STATUS_IN_TRANS; + break; + case SQLCOM_COMMIT: + thd->options&= ~OPTION_BEGIN; + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + if (!ha_commit(thd)) + send_ok(&thd->net); + else + res= -1; + break; + case SQLCOM_ROLLBACK: + thd->options&= ~OPTION_BEGIN; + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + if (!ha_rollback(thd)) + send_ok(&thd->net); + else + res= -1; + break; + default: /* Impossible */ + send_ok(&thd->net); + break; + } + thd->proc_info="query end"; // QQ + if (res < 0) + send_error(&thd->net,thd->killed ? ER_SERVER_SHUTDOWN : 0); + +error: + DBUG_VOID_RETURN; +} + + +/**************************************************************************** +** Get the user (global) and database privileges for all used tables +** Returns true (error) if we can't get the privileges and we don't use +** table/column grants. +****************************************************************************/ + +bool +check_access(THD *thd,uint want_access,const char *db, uint *save_priv, + bool no_grant) +{ + uint db_access,dummy; + if (save_priv) + *save_priv=0; + else + save_priv= &dummy; + + if (!db && !thd->db && !no_grant) + { + send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: tested */ + return TRUE; /* purecov: tested */ + } + + if ((thd->master_access & want_access) == want_access) + { + *save_priv=thd->master_access; + return FALSE; + } + if ((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL) || + ! db && no_grant) + { // 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 */ + 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 */ + else + db_access=thd->db_access; + want_access &= ~EXTRA_ACL; // Remove SHOW attribute + db_access= ((*save_priv=(db_access | thd->master_access)) & want_access); + if (db_access == want_access || + ((grant_option && !no_grant) && !(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 */ + return TRUE; /* purecov: tested */ +} + + +/* +** Check the privilege for all used tables. Table privileges are cached +** in the table list for GRANT checking +*/ + +static bool +check_table_access(THD *thd,uint want_access,TABLE_LIST *tables) +{ + uint found=0,found_access=0; + TABLE_LIST *org_tables=tables; + for (; tables ; tables=tables->next) + { + if ((thd->master_access & want_access) == want_access && thd->db) + tables->grant.privilege= want_access; + else if (tables->db) + { + if (found && !grant_option) // db already checked + tables->grant.privilege=found_access; + else + { + if (check_access(thd,want_access,tables->db,&tables->grant.privilege)) + return TRUE; // Access denied + found_access=tables->grant.privilege; + } + } + else if (check_access(thd,want_access,tables->db,&tables->grant.privilege)) + return TRUE; // Access denied + } + if (grant_option) + return check_grant(thd,want_access,org_tables); + return FALSE; +} + + +static bool check_lock_tables(THD *thd,TABLE_LIST *tables) +{ + for (; tables ; tables=tables->next) + { + if (!tables->db) + { + if (!(tables->db=thd->db)) + { + send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: tested */ + return TRUE; /* purecov: tested */ + } + } + } + return FALSE; +} + + +/**************************************************************************** + Check stack size; Send error if there isn't enough stack to continue +****************************************************************************/ + +#if STACK_DIRECTION < 0 +#define used_stack(A,B) (long) (A - B) +#else +#define used_stack(A,B) (long) (B - A) +#endif + +bool check_stack_overrun(THD *thd,char *buf __attribute__((unused))) +{ + long stack_used; + if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >= + (long) thread_stack_min) + { + sprintf(errbuff[0],ER(ER_STACK_OVERRUN),stack_used,thread_stack); + my_message(ER_STACK_OVERRUN,errbuff[0],MYF(0)); + thd->fatal_error=1; + return 1; + } + return 0; +} + +#define MY_YACC_INIT 1000 // Start with big alloc +#define MY_YACC_MAX 32000 // Because of 'short' + +bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, int *yystacksize) +{ + LEX *lex=current_lex; + int old_info=0; + if ((uint) *yystacksize >= MY_YACC_MAX) + return 1; + if (!lex->yacc_yyvs) + old_info= *yystacksize; + *yystacksize= set_zone((*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX); + if (!(lex->yacc_yyvs= (char*) + my_realloc((gptr) lex->yacc_yyvs, + *yystacksize*sizeof(**yyvs), + MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) || + !(lex->yacc_yyss= (char*) + my_realloc((gptr) lex->yacc_yyss, + *yystacksize*sizeof(**yyss), + MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR)))) + return 1; + if (old_info) + { // Copy old info from stack + memcpy(lex->yacc_yyss, (gptr) *yyss, old_info*sizeof(**yyss)); + memcpy(lex->yacc_yyvs, (gptr) *yyvs, old_info*sizeof(**yyvs)); + } + *yyss=(short*) lex->yacc_yyss; + *yyvs=(YYSTYPE*) lex->yacc_yyvs; + return 0; +} + + +/**************************************************************************** + Initialize global thd variables neaded for query +****************************************************************************/ + +static void +mysql_init_query(THD *thd) +{ + DBUG_ENTER("mysql_init_query"); + thd->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.proc_list.first=0; // Needed by sql_select + thd->fatal_error=0; // Safety + thd->last_insert_id_used=thd->query_start_used=thd->insert_id_used=0; + DBUG_VOID_RETURN; +} + + +void +mysql_parse(THD *thd,char *inBuf,uint length) +{ + DBUG_ENTER("mysql_parse"); + + 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); + DBUG_VOID_RETURN; +} + + +inline static void +link_in_list(SQL_LIST *list,byte *element,byte **next) +{ + list->elements++; + (*list->next)=element; + list->next=next; + *next=0; +} + + +/***************************************************************************** +** Store field definition for create +** Return 0 if ok +******************************************************************************/ + +bool add_field_to_list(char *field_name, enum_field_types type, + char *length, char *decimals, + uint type_modifier, Item *default_value,char *change, + TYPELIB *interval) +{ + register create_field *new_field; + THD *thd=current_thd; + LEX *lex= &thd->lex; + uint allowed_type_modifier=0; + DBUG_ENTER("add_field_to_list"); + + if (strlen(field_name) > NAME_LEN) + { + net_printf(&thd->net, ER_TOO_LONG_IDENT, field_name); /* purecov: inspected */ + DBUG_RETURN(1); /* purecov: inspected */ + } + if (type_modifier & PRI_KEY_FLAG) + { + lex->col_list.push_back(new key_part_spec(field_name,0)); + lex->key_list.push_back(new Key(Key::PRIMARY,NullS, + lex->col_list)); + lex->col_list.empty(); + } + if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG)) + { + lex->col_list.push_back(new key_part_spec(field_name,0)); + lex->key_list.push_back(new Key(Key::UNIQUE,NullS, + lex->col_list)); + lex->col_list.empty(); + } + + if (default_value && default_value->type() == Item::NULL_ITEM) + { + if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == + NOT_NULL_FLAG) + { + net_printf(&thd->net,ER_INVALID_DEFAULT,field_name); + DBUG_RETURN(1); + } + default_value=0; + } + if (!(new_field=new create_field())) + DBUG_RETURN(1); + new_field->field=0; + new_field->field_name=field_name; + new_field->def= (type_modifier & AUTO_INCREMENT_FLAG ? 0 : default_value); + new_field->flags= type_modifier; + new_field->unireg_check= (type_modifier & AUTO_INCREMENT_FLAG ? + Field::NEXT_NUMBER : Field::NONE); + new_field->decimals= decimals ? (uint) set_zone(atoi(decimals),0, + NOT_FIXED_DEC-1) : 0; + new_field->sql_type=type; + new_field->length=0; + new_field->change=change; + new_field->interval=0; + new_field->pack_length=0; + if (length) + if (!(new_field->length= (uint) atoi(length))) + length=0; /* purecov: inspected */ + uint sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1; + + if (new_field->length && new_field->decimals && + new_field->length < new_field->decimals+2 && + new_field->decimals != NOT_FIXED_DEC) + new_field->length=new_field->decimals+2; /* purecov: inspected */ + + switch (type) { + case FIELD_TYPE_TINY: + if (!length) new_field->length=3+sign_len; + allowed_type_modifier= AUTO_INCREMENT_FLAG; + break; + case FIELD_TYPE_SHORT: + if (!length) new_field->length=5+sign_len; + allowed_type_modifier= AUTO_INCREMENT_FLAG; + break; + case FIELD_TYPE_INT24: + if (!length) new_field->length=8+sign_len; + allowed_type_modifier= AUTO_INCREMENT_FLAG; + break; + case FIELD_TYPE_LONG: + if (!length) new_field->length=10+sign_len; + allowed_type_modifier= AUTO_INCREMENT_FLAG; + break; + case FIELD_TYPE_LONGLONG: + if (!length) new_field->length=20; + allowed_type_modifier= AUTO_INCREMENT_FLAG; + break; + case FIELD_TYPE_STRING: + case FIELD_TYPE_VAR_STRING: + case FIELD_TYPE_NULL: + break; + case FIELD_TYPE_DECIMAL: + if (!length) + new_field->length = 10; // Default length for DECIMAL + new_field->length+=sign_len; + if (new_field->decimals) + new_field->length++; + break; + case FIELD_TYPE_BLOB: + case FIELD_TYPE_TINY_BLOB: + case FIELD_TYPE_LONG_BLOB: + case FIELD_TYPE_MEDIUM_BLOB: + if (default_value) // Allow empty as default value + { + String str,*res; + res=default_value->val_str(&str); + if (res->length()) + { + net_printf(&thd->net,ER_BLOB_CANT_HAVE_DEFAULT,field_name); /* purecov: inspected */ + DBUG_RETURN(1); /* purecov: inspected */ + } + new_field->def=0; + } + new_field->flags|=BLOB_FLAG; + break; + case FIELD_TYPE_YEAR: + if (!length || new_field->length != 2) + new_field->length=4; // Default length + new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG; + break; + case FIELD_TYPE_FLOAT: + /* change FLOAT(precision) to FLOAT or DOUBLE */ + allowed_type_modifier= AUTO_INCREMENT_FLAG; + if (length && !decimals) + { + uint tmp_length=new_field->length; + if (tmp_length > PRECISION_FOR_DOUBLE) + { + net_printf(&thd->net,ER_WRONG_FIELD_SPEC,field_name); + DBUG_RETURN(1); + } + else if (tmp_length > PRECISION_FOR_FLOAT) + { + new_field->sql_type=FIELD_TYPE_DOUBLE; + new_field->length=DBL_DIG+7; // -[digits].E+### + } + else + new_field->length=FLT_DIG+6; // -[digits].E+## + new_field->decimals= NOT_FIXED_DEC; + break; + } + if (!length) + { + new_field->length = FLT_DIG+6; + new_field->decimals= NOT_FIXED_DEC; + } + break; + case FIELD_TYPE_DOUBLE: + allowed_type_modifier= AUTO_INCREMENT_FLAG; + if (!length) + { + new_field->length = DBL_DIG+7; + new_field->decimals=NOT_FIXED_DEC; + } + break; + case FIELD_TYPE_TIMESTAMP: + if (!length) + new_field->length= 14; // Full date YYYYMMDDHHMMSS + else + { + new_field->length=((new_field->length+1)/2)*2; /* purecov: inspected */ + new_field->length= min(new_field->length,14); /* purecov: inspected */ + } + new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG | NOT_NULL_FLAG; + break; + case FIELD_TYPE_DATE: // Old date type + if (protocol_version != PROTOCOL_VERSION-1) + new_field->sql_type=FIELD_TYPE_NEWDATE; + /* fall trough */ + case FIELD_TYPE_NEWDATE: + new_field->length=10; + break; + case FIELD_TYPE_TIME: + new_field->length=10; + break; + case FIELD_TYPE_DATETIME: + new_field->length=19; + break; + case FIELD_TYPE_SET: + { + if (interval->count > sizeof(longlong)*8) + { + net_printf(&thd->net,ER_TOO_BIG_SET,field_name); /* purecov: inspected */ + DBUG_RETURN(1); /* purecov: inspected */ + } + new_field->pack_length=(interval->count+7)/8; + if (new_field->pack_length > 4) + new_field->pack_length=8; + new_field->interval=interval; + new_field->length=0; + for (const char **pos=interval->type_names; *pos ; pos++) + new_field->length+=strlen(*pos)+1; + new_field->length--; + set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1); + if (default_value) + { + thd->cuted_fields=0; + String str,*res; + res=default_value->val_str(&str); + (void) find_set(interval,res->ptr(),res->length()); + if (thd->cuted_fields) + { + net_printf(&thd->net,ER_INVALID_DEFAULT,field_name); + DBUG_RETURN(1); + } + } + } + break; + case FIELD_TYPE_ENUM: + { + new_field->interval=interval; + new_field->pack_length=interval->count < 256 ? 1 : 2; // Should be safe + new_field->length=strlen(interval->type_names[0]); + for (const char **pos=interval->type_names+1; *pos ; pos++) + { + uint length=strlen(*pos); + set_if_bigger(new_field->length,length); + } + set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1); + if (default_value) + { + String str,*res; + res=default_value->val_str(&str); + if (!find_enum(interval,res->ptr(),res->length())) + { + net_printf(&thd->net,ER_INVALID_DEFAULT,field_name); + DBUG_RETURN(1); + } + } + break; + } + } + + if (new_field->length >= MAX_FIELD_WIDTH || + (!new_field->length && !(new_field->flags & BLOB_FLAG) && + type != FIELD_TYPE_STRING)) + { + net_printf(&thd->net,ER_TOO_BIG_FIELDLENGTH,field_name, + MAX_FIELD_WIDTH-1); /* purecov: inspected */ + DBUG_RETURN(1); /* purecov: inspected */ + } + type_modifier&= AUTO_INCREMENT_FLAG; + if ((~allowed_type_modifier) & type_modifier) + { + net_printf(&thd->net,ER_WRONG_FIELD_SPEC,field_name); + DBUG_RETURN(1); + } + if (!new_field->pack_length) + new_field->pack_length=calc_pack_length(new_field->sql_type == + FIELD_TYPE_VAR_STRING ? + FIELD_TYPE_STRING : + new_field->sql_type, + new_field->length); + lex->create_list.push_back(new_field); + lex->last_field=new_field; + DBUG_RETURN(0); +} + +/* Store position for column in ALTER TABLE .. ADD column */ + +void store_position_for_column(const char *name) +{ + current_lex->last_field->after=my_const_cast(char*) (name); +} + +bool +add_proc_to_list(Item *item) +{ + ORDER *order; + Item **item_ptr; + + if (!(order = (ORDER *) sql_alloc(sizeof(ORDER)+sizeof(Item*)))) + return 1; + item_ptr = (Item**) (order+1); + *item_ptr= item; + order->item=item_ptr; + order->free_me=0; + link_in_list(¤t_lex->proc_list,(byte*) order,(byte**) &order->next); + return 0; +} + + +/* Fix escaping of _, % and \ in database and table names (for ODBC) */ + +static void remove_escape(char *name) +{ + char *to; +#ifdef USE_MB + char *strend=name+strlen(name); +#endif + for (to=name; *name ; name++) + { +#ifdef USE_MB + int l; +/* if ((l = ismbchar(name, name+MBMAXLEN))) { Wei He: I think it's wrong */ + if (use_mb(default_charset_info) && + (l = my_ismbchar(default_charset_info, name, strend))) + { + while (l--) + *to++ = *name++; + name--; + continue; + } +#endif + if (*name == '\\' && name[1]) + name++; // Skipp '\\' + *to++= *name; + } + *to=0; +} + +/**************************************************************************** +** save order by and tables in own lists +****************************************************************************/ + + +bool add_to_list(SQL_LIST &list,Item *item,bool asc) +{ + ORDER *order; + Item **item_ptr; + DBUG_ENTER("add_to_list"); + if (!(order = (ORDER *) sql_alloc(sizeof(ORDER)+sizeof(Item*)))) + DBUG_RETURN(1); + item_ptr = (Item**) (order+1); + *item_ptr=item; + order->item= item_ptr; + order->asc = asc; + order->free_me=0; + order->used=0; + link_in_list(&list,(byte*) order,(byte**) &order->next); + DBUG_RETURN(0); +} + + +TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, + thr_lock_type flags, + List<String> *use_index, + List<String> *ignore_index) +{ + register TABLE_LIST *ptr; + THD *thd=current_thd; + char *alias_str; + const char *current_db; + DBUG_ENTER("add_table_to_list"); + + if (!table) + DBUG_RETURN(0); // End of memory + alias_str= alias ? alias->str : table->table.str; + if (table->table.length > NAME_LEN || + table->db.str && table->db.length > NAME_LEN || + check_table_name(table->table.str,table->table.length)) + { + net_printf(&thd->net,ER_WRONG_TABLE_NAME,table->table.str); + DBUG_RETURN(0); + } + +#ifdef FN_LOWER_CASE + if (!alias) /* Alias is case sensitive */ + if (!(alias_str=sql_strmake(alias_str,table->table.length))) + DBUG_RETURN(0); + if (lower_case_table_names) + casedn_str(table->table.str); +#endif + if (!(ptr = (TABLE_LIST *) sql_calloc(sizeof(TABLE_LIST)))) + DBUG_RETURN(0); /* purecov: inspected */ + ptr->db= table->db.str; + ptr->real_name=table->table.str; + ptr->name=alias_str; + ptr->lock_type=flags; + if (use_index) + ptr->use_index=(List<String> *) sql_memdup((gptr) use_index, + sizeof(*use_index)); + if (ignore_index) + ptr->ignore_index=(List<String> *) sql_memdup((gptr) ignore_index, + sizeof(*ignore_index)); + + /* check that used name is unique */ + current_db=thd->db ? thd->db : ""; + for (TABLE_LIST *tables=(TABLE_LIST*) thd->lex.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)) + { + 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); + DBUG_RETURN(ptr); +} + +void add_join_on(TABLE_LIST *b,Item *expr) +{ + b->on_expr=expr; +} + + +void add_join_natural(TABLE_LIST *a,TABLE_LIST *b) +{ + b->natural_join=a; +} + + /* Check if name is used in table list */ + +static bool check_dup(THD *thd,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)) + return 1; + return 0; +} + +bool reload_acl_and_cache(uint options) +{ + bool result=0; + + select_errors=0; /* Write if more errors */ + mysql_log.flush(); // Flush log + if (options & REFRESH_GRANT) + { + acl_reload(); + grant_reload(); + } + if (options & REFRESH_LOG) + { + mysql_log.new_file(); + mysql_update_log.new_file(); + mysql_bin_log.new_file(); + mysql_slow_log.new_file(); + if (ha_flush_logs()) + result=1; + } + if (options & (REFRESH_TABLES | REFRESH_READ_LOCK)) + { + if ((options & REFRESH_READ_LOCK) && ! current_thd->global_read_lock) + { + current_thd->global_read_lock=1; + thread_safe_increment(global_read_lock,&LOCK_open); + } + result=close_cached_tables((options & REFRESH_FAST) ? 0 : 1); + } + if (options & REFRESH_HOSTS) + hostname_cache_refresh(); + if (options & REFRESH_STATUS) + refresh_status(); + if (options & REFRESH_THREADS) + flush_thread_cache(); + if (options & REFRESH_MASTER) + reset_master(); + if (options & REFRESH_SLAVE) + reset_slave(); + + return result; +} + + +static void kill_one_thread(THD *thd, ulong id) +{ + VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list + I_List_iterator<THD> it(threads); + THD *tmp; + uint error=ER_NO_SUCH_THREAD; + while ((tmp=it++)) + { + if (tmp->thread_id == id) + { + if ((thd->master_access & PROCESS_ACL) || + !strcmp(thd->user,tmp->user)) + { + thr_alarm_kill(tmp->real_id); + tmp->killed=1; + error=0; + if (tmp->mysys_var) + { + pthread_mutex_lock(&tmp->mysys_var->mutex); + if (!tmp->system_thread) // Don't abort locks + tmp->mysys_var->abort=1; + if (tmp->mysys_var->current_mutex) + { + pthread_mutex_lock(tmp->mysys_var->current_mutex); + pthread_cond_broadcast(tmp->mysys_var->current_cond); + pthread_mutex_unlock(tmp->mysys_var->current_mutex); + } + pthread_mutex_unlock(&tmp->mysys_var->mutex); + } + } + else + error=ER_KILL_DENIED_ERROR; + break; // Found thread + } + } + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + if (!error) + send_ok(&thd->net); + else + net_printf(&thd->net,error,id); +} + +/* Clear most status variables */ + +static void refresh_status(void) +{ + pthread_mutex_lock(&THR_LOCK_keycache); + pthread_mutex_lock(&LOCK_status); + for (struct show_var_st *ptr=status_vars; ptr->name; ptr++) + { + if (ptr->type == SHOW_LONG) + *(ulong*) ptr->value=0; + } + pthread_mutex_unlock(&LOCK_status); + pthread_mutex_unlock(&THR_LOCK_keycache); +} + +static int start_slave(THD* thd , bool net_report) +{ + if(!thd) thd = current_thd; + NET* net = &thd->net; + const char* err = 0; + if(check_access(thd, PROCESS_ACL, any_db)) + return 1; + pthread_mutex_lock(&LOCK_slave); + if(!slave_running) + if(master_host) + { + pthread_t hThread; + if(pthread_create(&hThread, &connection_attrib, handle_slave, 0)) + { + err = "cannot create slave thread"; + } + } + else + err = "Master host not set"; + else + err = "Slave already running"; + + pthread_mutex_unlock(&LOCK_slave); + if(err) + { + if(net_report) send_error(net, 0, err); + return 1; + } + else if(net_report) + send_ok(net); + + return 0; +} + +static int stop_slave(THD* thd, bool net_report ) +{ + if(!thd) thd = current_thd; + NET* net = &thd->net; + const char* err = 0; + + if(check_access(thd, PROCESS_ACL, any_db)) + return 1; + + pthread_mutex_lock(&LOCK_slave); + if (slave_running) + { + abort_slave = 1; + thr_alarm_kill(slave_real_id); + // do not abort the slave in the middle of a query, so we do not set + // thd->killed for the slave thread + thd->proc_info = "waiting for slave to die"; + pthread_cond_wait(&COND_slave_stopped, &LOCK_slave); + } + else + err = "Slave is not running"; + + pthread_mutex_unlock(&LOCK_slave); + thd->proc_info = 0; + + if(err) + { + if(net_report) send_error(net, 0, err); + return 1; + } + else if(net_report) + send_ok(net); + + return 0; +} + +static void reset_slave() +{ + MY_STAT stat_area; + char fname[FN_REFLEN]; + bool slave_was_running = slave_running; + + if(slave_running) + stop_slave(0,0); + + fn_format(fname, master_info_file, mysql_data_home, "", 4+16+32); + if(my_stat(fname, &stat_area, MYF(0))) + if(my_delete(fname, MYF(MY_WME))) + return; + + if(slave_was_running) + start_slave(0,0); +} + +static int change_master(THD* thd) +{ + bool slave_was_running; + // kill slave thread + pthread_mutex_lock(&LOCK_slave); + if((slave_was_running = slave_running)) + { + abort_slave = 1; + thr_alarm_kill(slave_real_id); + thd->proc_info = "waiting for slave to die"; + pthread_cond_wait(&COND_slave_stopped, &LOCK_slave); // wait until done + } + pthread_mutex_unlock(&LOCK_slave); + thd->proc_info = "changing master"; + LEX_MASTER_INFO* lex_mi = &thd->lex.mi; + + pthread_mutex_lock(&glob_mi.lock); + if((lex_mi->host || lex_mi->port) && !lex_mi->log_file_name && !lex_mi->pos) + { + // if we change host or port, we must reset the postion + glob_mi.log_file_name[0] = 0; + glob_mi.pos = 0; + } + + if(lex_mi->log_file_name) + strmake(glob_mi.log_file_name, lex_mi->log_file_name, + sizeof(glob_mi.log_file_name)); + if(lex_mi->pos) + glob_mi.pos = lex_mi->pos; + + if(lex_mi->host) + strmake(glob_mi.host, lex_mi->host, sizeof(glob_mi.host)); + if(lex_mi->user) + strmake(glob_mi.user, lex_mi->user, sizeof(glob_mi.user)); + if(lex_mi->password) + strmake(glob_mi.password, lex_mi->password, sizeof(glob_mi.password)); + if(lex_mi->port) + glob_mi.port = lex_mi->port; + if(lex_mi->connect_retry) + glob_mi.connect_retry = lex_mi->connect_retry; + + flush_master_info(&glob_mi); + pthread_mutex_unlock(&glob_mi.lock); + thd->proc_info = "starting slave"; + if(slave_was_running) + start_slave(0,0); + thd->proc_info = 0; + + send_ok(&thd->net); + return 0; +} + +static void reset_master() +{ + if(!mysql_bin_log.is_open()) + { + my_error(ER_FLUSH_MASTER_BINLOG_CLOSED, MYF(ME_BELL+ME_WAITTANG)); + return; + } + + LOG_INFO linfo; + + if(mysql_bin_log.find_first_log(&linfo, "")) + return; + + for(;;) + { + my_delete(linfo.log_file_name, MYF(MY_WME)); + if(mysql_bin_log.find_next_log(&linfo)) + break; + } + mysql_bin_log.close(1); // exiting close + my_delete(mysql_bin_log.get_index_fname(), MYF(MY_WME)); + + char tmp[FN_REFLEN]; + if (!opt_bin_logname || !opt_bin_logname[0]) + { + char hostname[FN_REFLEN]; + if (gethostname(hostname,sizeof(hostname)-4) < 0) + strmov(hostname,"mysql"); + + strnmov(tmp,hostname,FN_REFLEN-5); + strmov(strcend(tmp,'.'),"-bin"); + opt_bin_logname=tmp; + } + + mysql_bin_log.open(opt_bin_logname,LOG_BIN); + +} + +int show_binlog_info(THD* thd) +{ + DBUG_ENTER("show_binlog_info"); + List<Item> field_list; + field_list.push_back(new Item_empty_string("File", FN_REFLEN)); + field_list.push_back(new Item_empty_string("Position",20)); + field_list.push_back(new Item_empty_string("Binlog_do_db",20)); + field_list.push_back(new Item_empty_string("Binlog_ignore_db",20)); + + if(send_fields(thd, field_list, 1)) + DBUG_RETURN(-1); + String* packet = &thd->packet; + packet->length(0); + + if(mysql_bin_log.is_open()) + { + LOG_INFO li; + mysql_bin_log.get_current_log(&li); + net_store_data(packet, li.log_file_name); + net_store_data(packet, (longlong)li.pos); + net_store_data(packet, &binlog_do_db); + net_store_data(packet, &binlog_ignore_db); + } + else + { + net_store_null(packet); + net_store_null(packet); + net_store_null(packet); + net_store_null(packet); + } + + if(my_net_write(&thd->net, (char*)thd->packet.ptr(), packet->length())) + DBUG_RETURN(-1); + + send_eof(&thd->net); + DBUG_RETURN(0); +} |