summaryrefslogtreecommitdiff
path: root/sql/sql_parse.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r--sql/sql_parse.cc2627
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(&current_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);
+}