diff options
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r-- | sql/sql_parse.cc | 2117 |
1 files changed, 1469 insertions, 648 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d7d7469ceed..890334e8c94 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -21,7 +21,6 @@ #include <m_ctype.h> #include <myisam.h> #include <my_dir.h> -#include <assert.h> #ifdef HAVE_INNOBASE_DB #include "ha_innodb.h" @@ -44,30 +43,24 @@ #else #define MIN_HANDSHAKE_SIZE 6 #endif /* HAVE_OPENSSL */ -#define SCRAMBLE_LENGTH 8 -#define MEM_ROOT_BLOCK_SIZE 8192 -#define MEM_ROOT_PREALLOC 8192 -#define TRANS_MEM_ROOT_BLOCK_SIZE 4096 -#define TRANS_MEM_ROOT_PREALLOC 4096 - -extern int yyparse(void); +extern int yyparse(void *thd); extern "C" pthread_mutex_t THR_LOCK_keycache; #ifdef SOLARIS extern "C" int gethostname(char *name, int namelen); #endif -static int check_for_max_user_connections(USER_CONN *uc); +static int check_for_max_user_connections(THD *thd, USER_CONN *uc); static void decrease_user_connections(USER_CONN *uc); static bool check_db_used(THD *thd,TABLE_LIST *tables); static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *tables); -static bool check_dup(const char *db, const char *name, TABLE_LIST *tables); -static void mysql_init_query(THD *thd); static void remove_escape(char *name); static void refresh_status(void); static bool append_file_to_dir(THD *thd, char **filename_ptr, char *table_name); -static bool create_total_list(THD *thd, LEX *lex, TABLE_LIST **result); + +static bool single_table_command_access(THD *thd, ulong privilege, + TABLE_LIST *tables, int *res); const char *any_db="*any*"; // Special symbol for check_access @@ -76,10 +69,11 @@ const char *command_name[]={ "Drop DB", "Refresh", "Shutdown", "Statistics", "Processlist", "Connect","Kill","Debug","Ping","Time","Delayed_insert","Change user", "Binlog Dump","Table Dump", "Connect Out", "Register Slave", + "Prepare", "Prepare Execute", "Long Data", "Close stmt", "Error" // Last command number }; -bool volatile abort_slave = 0; +static char empty_c_string[1]= {0}; // Used for not defined 'db' #ifdef __WIN__ static void test_signal(int sig_ptr) @@ -130,7 +124,7 @@ extern pthread_mutex_t LOCK_user_conn; static int get_or_create_user_conn(THD *thd, const char *user, const char *host, - USER_RESOURCES *mqh) + USER_RESOURCES *mqh) { int return_val=0; uint temp_len, user_len, host_len; @@ -152,7 +146,7 @@ static int get_or_create_user_conn(THD *thd, const char *user, my_malloc(sizeof(struct user_conn) + temp_len+1, MYF(MY_WME))))) { - send_error(¤t_thd->net, 0, NullS); // Out of memory + send_error(thd, 0, NullS); // Out of memory return_val=1; goto end; } @@ -164,13 +158,13 @@ static int get_or_create_user_conn(THD *thd, const char *user, uc->connections = 1; uc->questions=uc->updates=uc->conn_per_hour=0; uc->user_resources=*mqh; - if (max_user_connections && mqh->connections > max_user_connections) + if (max_user_connections && mqh->connections > max_user_connections) uc->user_resources.connections = max_user_connections; uc->intime=thd->thr_create_time; if (hash_insert(&hash_user_connections, (byte*) uc)) { my_free((char*) uc,0); - send_error(¤t_thd->net, 0, NullS); // Out of memory + send_error(thd, 0, NullS); // Out of memory return_val=1; goto end; } @@ -179,63 +173,116 @@ static int get_or_create_user_conn(THD *thd, const char *user, end: (void) pthread_mutex_unlock(&LOCK_user_conn); return return_val; - + } /* Check if user is ok - Updates: - thd->{user,master_access,priv_user,priv_host,db,db_access} + + SYNOPSIS + check_user() + thd Thread handle + command Command for connection (for log) + user Name of user trying to connect + passwd Scrambled password sent from client + db Database to connect to + check_count If set to 1, don't allow too many connection + simple_connect If 1 then client is of old type and we should connect + using the old method (no challange) + do_send_error Set to 1 if we should send error to user + prepared_scramble Buffer to store hash password of new connection + had_password Set to 1 if the user gave a password + cur_priv_version Check flag to know if someone flushed the privileges + since last code + hint_user Pointer used by acl_getroot() to remmeber user for + next call + + RETURN + 0 ok + thd->user, thd->master_access, thd->priv_user, thd->db and + thd->db_access are updated + 1 Access denied; Error sent to client + -1 If do_send_error == 1: Failed connect, error sent to client + If do_send_error == 0: Prepare for stage of connect */ -static bool check_user(THD *thd,enum_server_command command, const char *user, - const char *passwd, const char *db, bool check_count) +static int check_user(THD *thd,enum_server_command command, const char *user, + const char *passwd, const char *db, bool check_count, + bool simple_connect, bool do_send_error, + char *prepared_scramble, bool had_password, + uint *cur_priv_version, ACL_USER** hint_user) { - NET *net= &thd->net; thd->db=0; thd->db_length=0; USER_RESOURCES ur; - char tmp_passwd[SCRAMBLE_LENGTH + 1]; - - if (passwd[0] && strlen(passwd) != SCRAMBLE_LENGTH) - return 1; + char tmp_passwd[SCRAMBLE41_LENGTH+1]; + DBUG_ENTER("check_user"); + /* Move password to temporary buffer as it may be stored in communication buffer */ - strmov(tmp_passwd, passwd); + strmake(tmp_passwd, passwd, sizeof(tmp_passwd)); passwd= tmp_passwd; // Use local copy - if (!(thd->user = my_strdup(user, MYF(0)))) + /* We shall avoid dupplicate user allocations here */ + if (!thd->user && !(thd->user = my_strdup(user, MYF(0)))) { - send_error(net,ER_OUT_OF_RESOURCES); - return 1; + send_error(thd,ER_OUT_OF_RESOURCES); + DBUG_RETURN(1); } thd->master_access=acl_getroot(thd, thd->host, thd->ip, thd->user, passwd, thd->scramble, - &thd->priv_user, thd->priv_host, - protocol_version == 9 || - !(thd->client_capabilities & - CLIENT_LONG_PASSWORD),&ur); + &thd->priv_user, thd->priv_host, + (protocol_version == 9 || + !(thd->client_capabilities & + CLIENT_LONG_PASSWORD)), + &ur,prepared_scramble, + cur_priv_version,hint_user); + DBUG_PRINT("info", ("Capabilities: %d packet_length: %ld Host: '%s' Login user: '%s' Priv_user: '%s' Using password: %s Access: %u db: '%s'", thd->client_capabilities, thd->max_client_packet_length, thd->host_or_ip, thd->user, thd->priv_user, - passwd[0] ? "yes": "no", + had_password ? "yes": "no", thd->master_access, thd->db ? thd->db : "*none*")); + + /* + In case we're going to retry we should not send error message at this + point + */ if (thd->master_access & NO_ACCESS) { - net_printf(net, ER_ACCESS_DENIED_ERROR, - thd->user, - thd->host_or_ip, - passwd[0] ? ER(ER_YES) : ER(ER_NO)); - mysql_log.write(thd,COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR), - thd->user, - thd->host_or_ip, - passwd[0] ? ER(ER_YES) : ER(ER_NO)); - return(1); // Error already given + if (do_send_error || !had_password || !*hint_user) + { + DBUG_PRINT("info",("Access denied")); + /* + Old client should get nicer error message if password version is + not supported + */ + if (simple_connect && *hint_user && (*hint_user)->pversion) + { + net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE); + mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE)); + } + else + { + net_printf(thd, ER_ACCESS_DENIED_ERROR, + thd->user, + thd->host_or_ip, + had_password ? ER(ER_YES) : ER(ER_NO)); + mysql_log.write(thd,COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR), + thd->user, + thd->host_or_ip, + had_password ? ER(ER_YES) : ER(ER_NO)); + } + DBUG_RETURN(1); // Error already given + } + DBUG_PRINT("info",("Prepare for second part of handshake")); + DBUG_RETURN(-1); // no report error in special handshake } + if (check_count) { VOID(pthread_mutex_lock(&LOCK_thread_count)); @@ -244,8 +291,8 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, VOID(pthread_mutex_unlock(&LOCK_thread_count)); if (tmp) { // Too many connections - send_error(net, ER_CON_COUNT_ERROR); - return(1); + send_error(thd, ER_CON_COUNT_ERROR); + DBUG_RETURN(1); } } mysql_log.write(thd,command, @@ -259,22 +306,22 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, /* Don't allow user to connect if he has done too many queries */ if ((ur.questions || ur.updates || ur.connections || max_user_connections) && get_or_create_user_conn(thd,user,thd->host_or_ip,&ur)) - return -1; + DBUG_RETURN(1); if (thd->user_connect && ((thd->user_connect->user_resources.connections) || max_user_connections) && - check_for_max_user_connections(thd->user_connect)) - return -1; + check_for_max_user_connections(thd, thd->user_connect)) + DBUG_RETURN(1); + if (db && db[0]) { - bool error=test(mysql_change_db(thd,db)); + int error= test(mysql_change_db(thd,db)); if (error && thd->user_connect) decrease_user_connections(thd->user_connect); - return error; + DBUG_RETURN(error); } - else - send_ok(net); // Ready to handle questions + send_ok(thd); // Ready to handle questions thd->password= test(passwd[0]); // Remember for error messages - return 0; // ok + DBUG_RETURN(0); // ok } @@ -295,35 +342,35 @@ extern "C" void free_user(struct user_conn *uc) my_free((char*) uc,MYF(0)); } -void init_max_user_conn(void) +void init_max_user_conn(void) { - (void) hash_init(&hash_user_connections,max_connections,0,0, + (void) hash_init(&hash_user_connections,system_charset_info,max_connections, + 0,0, (hash_get_key) get_key_conn, (hash_free_key) free_user, 0); } -static int check_for_max_user_connections(USER_CONN *uc) +static int check_for_max_user_connections(THD *thd, USER_CONN *uc) { int error=0; DBUG_ENTER("check_for_max_user_connections"); - + if (max_user_connections && (max_user_connections < (uint) uc->connections)) { - net_printf(&(current_thd->net),ER_TOO_MANY_USER_CONNECTIONS, uc->user); + net_printf(thd,ER_TOO_MANY_USER_CONNECTIONS, uc->user); error=1; goto end; } - uc->connections++; + uc->connections++; if (uc->user_resources.connections && uc->conn_per_hour++ >= uc->user_resources.connections) { - net_printf(¤t_thd->net, ER_USER_LIMIT_REACHED, uc->user, + net_printf(thd, ER_USER_LIMIT_REACHED, uc->user, "max_connections", (long) uc->user_resources.connections); error=1; - goto end; } end: DBUG_RETURN(error); @@ -378,9 +425,13 @@ void init_update_queries(void) uc_update_queries[SQLCOM_RESTORE_TABLE]=1; uc_update_queries[SQLCOM_DELETE_MULTI]=1; uc_update_queries[SQLCOM_DROP_INDEX]=1; - uc_update_queries[SQLCOM_MULTI_UPDATE]=1; + uc_update_queries[SQLCOM_UPDATE_MULTI]=1; } +bool is_update_query(enum enum_sql_command command) +{ + return uc_update_queries[command]; +} /* Check if maximum queries per hour limit has been reached @@ -414,7 +465,7 @@ static bool check_mqh(THD *thd, uint check_command) if (uc->user_resources.questions && uc->questions++ >= uc->user_resources.questions) { - net_printf(&thd->net, ER_USER_LIMIT_REACHED, uc->user, "max_questions", + net_printf(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions", (long) uc->user_resources.questions); error=1; goto end; @@ -425,7 +476,7 @@ static bool check_mqh(THD *thd, uint check_command) if (uc->user_resources.updates && uc_update_queries[check_command] && uc->updates++ >= uc->user_resources.updates) { - net_printf(&thd->net, ER_USER_LIMIT_REACHED, uc->user, "max_updates", + net_printf(thd, ER_USER_LIMIT_REACHED, uc->user, "max_updates", (long) uc->user_resources.updates); error=1; goto end; @@ -440,7 +491,7 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0) { (void) pthread_mutex_lock(&LOCK_user_conn); - if (lu) // for GRANT + if (lu) // for GRANT { USER_CONN *uc; uint temp_len=lu->user.length+lu->host.length+2; @@ -475,20 +526,36 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0) /* - Check connnetion and get priviliges - Returns 0 on ok, -1 < if error is given > 0 on error. + Check connnectionn and get priviliges + + SYNOPSIS + check_connections + thd Thread handle + + RETURN + 0 ok + -1 Error, which is sent to user + > 0 Error code (not sent to user) */ +#ifndef EMBEDDED_LIBRARY static int check_connections(THD *thd) { + int res; uint connect_errors=0; + uint cur_priv_version; + bool using_password; NET *net= &thd->net; - /* Store the connection details */ - DBUG_PRINT("info", (("check_connections called by thread %d"), - thd->thread_id)); + char *end, *user, *passwd, *db; + char prepared_scramble[SCRAMBLE41_LENGTH+4]; /* Buffer for scramble&hash */ + char db_buff[NAME_LEN+1]; + ACL_USER* cached_user=NULL; /* Initialise to NULL for first stage */ DBUG_PRINT("info",("New connection received on %s", - vio_description(net->vio))); + vio_description(net->vio))); + + /* Remove warning from valgrind. TODO: Fix it in password.c */ + bzero((char*) &prepared_scramble[0], sizeof(prepared_scramble)); if (!thd->host) // If TCP/IP connection { char ip[30]; @@ -507,18 +574,20 @@ check_connections(THD *thd) } 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); - /* Cut very long hostnames to avoid possible overflows */ - if (thd->host) + if (!(specialflag & SPECIAL_NO_RESOLVE)) { - thd->host[min(strlen(thd->host), HOSTNAME_LENGTH)]= 0; - thd->host_or_ip= thd->host; + vio_in_addr(net->vio,&thd->remote.sin_addr); + thd->host=ip_to_hostname(&thd->remote.sin_addr,&connect_errors); + /* Cut very long hostnames to avoid possible overflows */ + if (thd->host) + { + thd->host[min(strlen(thd->host), HOSTNAME_LENGTH)]= 0; + thd->host_or_ip= thd->host; + } + if (connect_errors > max_connect_errors) + return(ER_HOST_IS_BLOCKED); } - if (connect_errors > max_connect_errors) - return(ER_HOST_IS_BLOCKED); } DBUG_PRINT("info",("Host: %s ip: %s", thd->host ? thd->host : "unknown host", @@ -539,8 +608,9 @@ check_connections(THD *thd) ulong pkt_len=0; { /* buff[] needs to big enough to hold the server_version variable */ - char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH+32],*end; - int client_flags = CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB; + char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH+64]; + ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB | + CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION); if (opt_using_transactions) client_flags|=CLIENT_TRANSACTIONS; @@ -558,13 +628,15 @@ check_connections(THD *thd) memcpy(end,thd->scramble,SCRAMBLE_LENGTH+1); end+=SCRAMBLE_LENGTH +1; int2store(end,client_flags); - end[2]=(char) MY_CHARSET_CURRENT; + end[2]=(char) default_charset_info->number; int2store(end+3,thd->server_status); bzero(end+5,13); end+=18; - if (net_write_command(net,(uchar) protocol_version, buff, + + // At this point we write connection message and read reply + if (net_write_command(net,(uchar) protocol_version, "", 0, buff, (uint) (end-buff)) || - (pkt_len= my_net_read(net)) == packet_error || + (pkt_len= my_net_read(net)) == packet_error || pkt_len < MIN_HANDSHAKE_SIZE) { inc_host_errors(&thd->remote.sin_addr); @@ -580,8 +652,58 @@ check_connections(THD *thd) return(ER_OUT_OF_RESOURCES); thd->client_capabilities=uint2korr(net->read_pos); +#ifdef TO_BE_REMOVED_IN_4_1_RELEASE + /* + This is just a safety check against any client that would use the old + CLIENT_CHANGE_USER flag + */ + if ((thd->client_capabilities & CLIENT_PROTOCOL_41) && + !(thd->client_capabilities & (CLIENT_RESERVED | + CLIENT_SECURE_CONNECTION | + CLIENT_MULTI_RESULTS))) + thd->client_capabilities&= ~CLIENT_PROTOCOL_41; +#endif + if (thd->client_capabilities & CLIENT_PROTOCOL_41) + { + thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16; + thd->max_client_packet_length= uint4korr(net->read_pos+4); + DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8])); + /* + Use server character set and collation if + - client has not specified a character set + - client character set is the same as the servers + - client character set doesn't exists in server + */ + if (!(thd->variables.character_set_client= + get_charset((uint) net->read_pos[8], MYF(0))) || + !my_strcasecmp(&my_charset_latin1, + global_system_variables.character_set_client->name, + thd->variables.character_set_client->name)) + { + thd->variables.character_set_client= + global_system_variables.character_set_client; + thd->variables.collation_connection= + global_system_variables.collation_connection; + thd->variables.character_set_results= + global_system_variables.character_set_results; + } + else + { + thd->variables.character_set_results= + thd->variables.collation_connection= + thd->variables.character_set_client; + } + thd->update_charset(); + end= (char*) net->read_pos+32; + } + else + { + thd->max_client_packet_length= uint3korr(net->read_pos+2); + end= (char*) net->read_pos+5; + } + if (thd->client_capabilities & CLIENT_IGNORE_SPACE) - thd->sql_mode|= MODE_IGNORE_SPACE; + thd->variables.sql_mode|= MODE_IGNORE_SPACE; #ifdef HAVE_OPENSSL DBUG_PRINT("info", ("client capabilities: %d", thd->client_capabilities)); if (thd->client_capabilities & CLIENT_SSL) @@ -598,7 +720,7 @@ check_connections(THD *thd) DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)", pkt_len)); inc_host_errors(&thd->remote.sin_addr); - return(ER_HANDSHAKE_ERROR); + return(ER_HANDSHAKE_ERROR); } DBUG_PRINT("info", ("Reading user information over SSL layer")); if ((pkt_len=my_net_read(net)) == packet_error || @@ -610,31 +732,93 @@ check_connections(THD *thd) return(ER_HANDSHAKE_ERROR); } } - else +#endif + + if (end >= (char*) net->read_pos+ pkt_len +2) { - DBUG_PRINT("info", ("Leaving IO layer intact")); - if (pkt_len < NORMAL_HANDSHAKE_SIZE) - { - inc_host_errors(&thd->remote.sin_addr); - return ER_HANDSHAKE_ERROR; - } + inc_host_errors(&thd->remote.sin_addr); + return(ER_HANDSHAKE_ERROR); } -#endif - thd->max_client_packet_length=uint3korr(net->read_pos+2); - char *user= (char*) net->read_pos+5; - char *passwd= strend(user)+1; - char *db=0; + user= end; + passwd= strend(user)+1; + db=0; + using_password= test(passwd[0]); if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB) - db=strend(passwd)+1; + { + db= strend(passwd)+1; + uint32 length= copy_and_convert(db_buff, sizeof(db_buff)-1, + system_charset_info, + db, strlen(db), + thd->charset()); + db_buff[length]= 0; + db= db_buff; + } + + /* We can get only old hash at this point */ + if (using_password && strlen(passwd) != SCRAMBLE_LENGTH) + return ER_HANDSHAKE_ERROR; + if (thd->client_capabilities & CLIENT_INTERACTIVE) thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout; if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && opt_using_transactions) - thd->net.return_status= &thd->server_status; + net->return_status= &thd->server_status; net->read_timeout=(uint) thd->variables.net_read_timeout; - if (check_user(thd,COM_CONNECT, user, passwd, db, 1)) - return (-1); + + /* Simple connect only for old clients. New clients always use secure auth */ + bool simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION)); + + /* Check user permissions. If password failure we'll get scramble back */ + if ((res=check_user(thd, COM_CONNECT, user, passwd, db, 1, simple_connect, + simple_connect, prepared_scramble, using_password, + &cur_priv_version, + &cached_user)) < 0) + { + /* Store current used and database as they are erased with next packet */ + char tmp_user[USERNAME_LENGTH+1]; + char tmp_db[NAME_LEN+1]; + + /* If the client is old we just have to return error */ + if (simple_connect) + return -1; + + DBUG_PRINT("info",("password challenge")); + + tmp_user[0]= tmp_db[0]= 0; + if (user) + strmake(tmp_user,user,USERNAME_LENGTH); + if (db) + strmake(tmp_db,db,NAME_LEN); + + /* Write hash and encrypted scramble to client */ + if (my_net_write(net,prepared_scramble,SCRAMBLE41_LENGTH+4) || + net_flush(net)) + { + inc_host_errors(&thd->remote.sin_addr); + return ER_HANDSHAKE_ERROR; + } + /* Reading packet back */ + if ((pkt_len= my_net_read(net)) == packet_error) + { + inc_host_errors(&thd->remote.sin_addr); + return ER_HANDSHAKE_ERROR; + } + /* We have to get very specific packet size */ + if (pkt_len != SCRAMBLE41_LENGTH) + { + inc_host_errors(&thd->remote.sin_addr); + return ER_HANDSHAKE_ERROR; + } + /* Final attempt to check the user based on reply */ + if (check_user(thd,COM_CONNECT, tmp_user, (char*)net->read_pos, + tmp_db, 1, 0, 1, prepared_scramble, using_password, + &cur_priv_version, + &cached_user)) + return -1; + } + else if (res) + return -1; // Error sent from check_user() return 0; } @@ -653,7 +837,7 @@ pthread_handler_decl(handle_one_connection,arg) // The following calls needs to be done before we call DBUG_ macros if (!(test_flags & TEST_NO_THREADS) & my_thread_init()) { - close_connection(&thd->net,ER_OUT_OF_RESOURCES); + close_connection(thd, ER_OUT_OF_RESOURCES, 1); statistic_increment(aborted_connects,&LOCK_status); end_thread(thd,0); return 0; @@ -680,7 +864,7 @@ pthread_handler_decl(handle_one_connection,arg) #endif if (thd->store_globals()) { - close_connection(&thd->net,ER_OUT_OF_RESOURCES); + close_connection(thd, ER_OUT_OF_RESOURCES, 1); statistic_increment(aborted_connects,&LOCK_status); end_thread(thd,0); return 0; @@ -695,10 +879,10 @@ pthread_handler_decl(handle_one_connection,arg) if ((error=check_connections(thd))) { // Wrong permissions if (error > 0) - net_printf(net,error,thd->host_or_ip); + net_printf(thd,error,thd->host_or_ip); #ifdef __NT__ if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE) - sleep(1); /* must wait after eof() */ + my_sleep(1000); /* must wait after eof() */ #endif statistic_increment(aborted_connects,&LOCK_status); goto end_thread; @@ -715,9 +899,7 @@ pthread_handler_decl(handle_one_connection,arg) thd->command=COM_SLEEP; thd->version=refresh_version; thd->set_time(); - init_sql_alloc(&thd->mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); - init_sql_alloc(&thd->transaction.mem_root, - TRANS_MEM_ROOT_BLOCK_SIZE, TRANS_MEM_ROOT_PREALLOC); + thd->init_for_queries(); while (!net->error && net->vio != 0 && !thd->killed) { if (do_command(thd)) @@ -726,7 +908,7 @@ pthread_handler_decl(handle_one_connection,arg) if (thd->user_connect) decrease_user_connections(thd->user_connect); free_root(&thd->mem_root,MYF(0)); - if (net->error && net->vio != 0) + if (net->error && net->vio != 0 && net->report_error) { if (!thd->killed && thd->variables.log_warnings) sql_print_error(ER(ER_NEW_ABORTING_CONNECTION), @@ -735,7 +917,7 @@ pthread_handler_decl(handle_one_connection,arg) thd->host_or_ip, (net->last_errno ? ER(net->last_errno) : ER(ER_UNKNOWN_ERROR))); - send_error(net,net->last_errno,NullS); + send_error(thd,net->last_errno,NullS); statistic_increment(aborted_threads,&LOCK_status); } else if (thd->killed) @@ -744,7 +926,7 @@ pthread_handler_decl(handle_one_connection,arg) } end_thread: - close_connection(net); + close_connection(thd, 0, 1); end_thread(thd,1); /* If end_thread returns, we are either running with --one-thread @@ -770,8 +952,8 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg) /* The following must be called before DBUG_ENTER */ if (my_thread_init() || thd->store_globals()) { - close_connection(&thd->net,ER_OUT_OF_RESOURCES); - thd->fatal_error=1; + close_connection(thd, ER_OUT_OF_RESOURCES, 1); + thd->fatal_error(); goto end; } DBUG_ENTER("handle_bootstrap"); @@ -792,16 +974,14 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg) thd->priv_user=thd->user=(char*) my_strdup("boot", MYF(MY_WME)); buff= (char*) thd->net.buff; - init_sql_alloc(&thd->mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); - init_sql_alloc(&thd->transaction.mem_root, - TRANS_MEM_ROOT_BLOCK_SIZE, TRANS_MEM_ROOT_PREALLOC); + thd->init_for_queries(); while (fgets(buff, thd->net.max_packet, file)) { uint length=(uint) strlen(buff); - while (length && (isspace(buff[length-1]) || buff[length-1] == ';')) + while (length && (my_isspace(thd->charset(), buff[length-1]) || + buff[length-1] == ';')) length--; buff[length]=0; - thd->current_tablenr=0; thd->query_length=length; thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1); thd->query[length] = '\0'; @@ -815,7 +995,7 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg) } mysql_parse(thd,thd->query,length); close_thread_tables(thd); // Free tables - if (thd->fatal_error) + if (thd->is_fatal_error) break; free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC)); free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC)); @@ -832,11 +1012,13 @@ end: DBUG_RETURN(0); // Never reached } +#endif /* EMBEDDED_LIBRARY */ -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) + +void free_items(Item *item) +{ + for (; item ; item=item->next) delete item; } @@ -856,12 +1038,12 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) if (!db || check_db_name(db)) { - net_printf(&thd->net,ER_WRONG_DB_NAME, db ? db : "NULL"); + net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL"); goto err; } if (lower_case_table_names) - casedn_str(tbl_name); - remove_escape(tbl_name); + my_casedn_str(files_charset_info, tbl_name); + remove_escape(table_list->real_name); if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT))) DBUG_RETURN(1); @@ -876,7 +1058,7 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) thd->query = tbl_name; if ((error = mysqld_dump_create_info(thd, table, -1))) { - my_error(ER_GET_ERRNO, MYF(0)); + my_error(ER_GET_ERRNO, MYF(0), my_errno); goto err; } net_flush(&thd->net); @@ -891,6 +1073,7 @@ err: /* Execute one command from socket (query or simple command) */ +#ifndef EMBEDDED_LIBRARY bool do_command(THD *thd) { char *packet; @@ -901,14 +1084,17 @@ bool do_command(THD *thd) DBUG_ENTER("do_command"); net= &thd->net; - thd->current_tablenr=0; + /* + indicator of uninitialized lex => normal flow of errors handling + (see my_message_sql) + */ + thd->lex.current_select= 0; packet=0; old_timeout=net->read_timeout; // Wait max for 8 hours net->read_timeout=(uint) thd->variables.net_wait_timeout; - net->last_error[0]=0; // Clear error message - net->last_errno=0; + thd->clear_error(); // Clear error message net_new_transaction(net); if ((packet_length=my_net_read(net)) == packet_error) @@ -922,7 +1108,7 @@ bool do_command(THD *thd) statistic_increment(aborted_threads,&LOCK_status); DBUG_RETURN(TRUE); // We have to close it. } - send_error(net,net->last_errno,NullS); + send_error(thd,net->last_errno,NullS); net->error= 0; DBUG_RETURN(FALSE); } @@ -939,13 +1125,14 @@ bool do_command(THD *thd) net->read_timeout=old_timeout; // restore it DBUG_RETURN(dispatch_command(command,thd, packet+1, (uint) packet_length)); } +#endif /* EMBEDDED_LIBRARY */ bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length) { NET *net= &thd->net; - bool error=0; + bool error= 0; /* Commands which will always take a long time should be marked with this so that they will not get logged to the slow query log @@ -965,36 +1152,43 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->lex.select_lex.options=0; // We store status here switch (command) { case COM_INIT_DB: + { + LEX_STRING tmp; statistic_increment(com_stat[SQLCOM_CHANGE_DB],&LOCK_status); - if (!mysql_change_db(thd,packet)) + thd->convert_string(&tmp, system_charset_info, + packet, strlen(packet), thd->charset()); + if (!mysql_change_db(thd, tmp.str)) mysql_log.write(thd,command,"%s",thd->db); break; + } +#ifndef EMBEDDED_LIBRARY case COM_REGISTER_SLAVE: { if (!register_slave(thd, (uchar*)packet, packet_length)) - send_ok(&thd->net); + send_ok(thd); break; } +#endif case COM_TABLE_DUMP: - { - statistic_increment(com_other, &LOCK_status); - slow_command = TRUE; - uint db_len = *(uchar*)packet; - uint tbl_len = *(uchar*)(packet + db_len + 1); - char* db = thd->alloc(db_len + tbl_len + 2); - memcpy(db, packet + 1, db_len); - char* tbl_name = db + db_len; - *tbl_name++ = 0; - memcpy(tbl_name, packet + 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; - } + { + char *db, *tbl_name; + uint db_len= *(uchar*) packet; + uint tbl_len= *(uchar*) (packet + db_len + 1); + + statistic_increment(com_other, &LOCK_status); + slow_command= TRUE; + db= thd->alloc(db_len + tbl_len + 2); + tbl_name= strmake(db, packet + 1, db_len)+1; + strmake(tbl_name, packet + db_len + 2, tbl_len); + if (mysql_table_dump(thd, db, tbl_name, -1)) + send_error(thd); // dump to NET + break; + } +#ifndef EMBEDDED_LIBRARY case COM_CHANGE_USER: { thd->change_user(); - clear_error_message(thd); // If errors from rollback + thd->clear_error(); // If errors from rollback statistic_increment(com_other,&LOCK_status); char *user= (char*) packet; @@ -1006,63 +1200,165 @@ bool dispatch_command(enum enum_server_command command, THD *thd, uint save_db_access= thd->db_access; uint save_db_length= thd->db_length; char *save_user= thd->user; + thd->user=NULL; /* Needed for check_user to allocate new user */ char *save_priv_user= thd->priv_user; char *save_db= thd->db; - thd->user=0; - USER_CONN *save_uc= thd->user_connect; + USER_CONN *save_uc= thd->user_connect; + bool simple_connect; + bool using_password; + char prepared_scramble[SCRAMBLE41_LENGTH+4];/* Buffer for scramble,hash */ + char tmp_user[USERNAME_LENGTH+1]; + char tmp_db[NAME_LEN+1]; + ACL_USER* cached_user ; /* Cached user */ + uint cur_priv_version; /* Cached grant version */ + int res; + ulong pkt_len= 0; /* Length of reply packet */ + + bzero((char*) prepared_scramble, sizeof(prepared_scramble)); + /* Small check for incomming packet */ 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); - thd->master_access=save_master_access; - thd->db_access=save_db_access; - thd->db=save_db; - thd->db_length=save_db_length; - thd->user=save_user; - thd->priv_user=save_priv_user; - break; + goto restore_user_err; + + /* Now we shall basically perform authentication again */ + + /* We can get only old hash at this point */ + if (passwd[0] && strlen(passwd)!=SCRAMBLE_LENGTH) + goto restore_user_err; + + cached_user= NULL; + + /* Simple connect only for old clients. New clients always use sec. auth*/ + simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION)); + + /* Store information if we used password. passwd will be dammaged */ + using_password=test(passwd[0]); + + if (simple_connect) /* Restore scramble for old clients */ + memcpy(thd->scramble,thd->old_scramble,9); + + /* + Check user permissions. If password failure we'll get scramble back + Do not retry if we already have sent error (result>0) + */ + if ((res=check_user(thd,COM_CHANGE_USER, user, passwd, db, 0, + simple_connect, simple_connect, prepared_scramble, + using_password, &cur_priv_version, &cached_user)) < 0) + { + /* If the client is old we just have to have auth failure */ + if (simple_connect) + goto restore_user; /* Error is already reported */ + + /* Store current used and database as they are erased with next packet */ + tmp_user[0]= tmp_db[0]= 0; + if (user) + strmake(tmp_user,user,USERNAME_LENGTH); + if (db) + strmake(tmp_db,db,NAME_LEN); + + /* Write hash and encrypted scramble to client */ + if (my_net_write(net,prepared_scramble,SCRAMBLE41_LENGTH+4) || + net_flush(net)) + goto restore_user_err; + + /* Reading packet back */ + if ((pkt_len=my_net_read(net)) == packet_error) + goto restore_user_err; + + /* We have to get very specific packet size */ + if (pkt_len != SCRAMBLE41_LENGTH) + goto restore_user; + + /* Final attempt to check the user based on reply */ + if (check_user(thd,COM_CHANGE_USER, tmp_user, (char*) net->read_pos, + tmp_db, 0, 0, 1, prepared_scramble, using_password, + &cur_priv_version, &cached_user)) + goto restore_user; } + else if (res) + goto restore_user; + + /* Finally we've authenticated new user */ if (max_connections && save_uc) decrease_user_connections(save_uc); x_free((gptr) save_db); x_free((gptr) save_user); + thd->password=using_password; break; - } + /* Bad luck we shall restore old user */ +restore_user_err: + send_error(thd, ER_UNKNOWN_COM_ERROR); + +restore_user: + x_free(thd->user); + thd->master_access=save_master_access; + thd->db_access=save_db_access; + thd->db=save_db; + thd->db_length=save_db_length; + thd->user=save_user; + thd->priv_user=save_priv_user; + break; + } +#endif /* EMBEDDED_LIBRARY */ + case COM_EXECUTE: + { + mysql_stmt_execute(thd, packet); + break; + } + case COM_LONG_DATA: + { + mysql_stmt_get_longdata(thd, packet, packet_length); + break; + } + case COM_PREPARE: + { + mysql_stmt_prepare(thd, packet, packet_length); + break; + } + case COM_CLOSE_STMT: + { + mysql_stmt_free(thd, packet); + break; + } + case COM_RESET_STMT: + { + mysql_stmt_reset(thd, packet); + break; + } case COM_QUERY: { - packet_length--; // Remove end null - /* Remove garage at start and end of query */ - while (isspace(packet[0]) && packet_length > 0) - { - packet++; - packet_length--; - } - char *pos=packet+packet_length; // Point at end null - while (packet_length > 0 && (pos[-1] == ';' || isspace(pos[-1]))) - { - pos--; - packet_length--; - } - /* We must allocate some extra memory for query cache */ - if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet), - packet_length, - thd->db_length+2+ - sizeof(ha_rows)))) - break; - thd->query[packet_length]=0; - thd->packet.shrink(thd->variables.net_buffer_length);// Reclaim some memory - if (!(specialflag & SPECIAL_NO_PRIOR)) - my_pthread_setprio(pthread_self(),QUERY_PRIOR); + if (alloc_query(thd, packet, packet_length)) + break; // fatal error is set mysql_log.write(thd,command,"%s",thd->query); DBUG_PRINT("query",("%-.4096s",thd->query)); - /* thd->query_length is set by mysql_parse() */ - mysql_parse(thd,thd->query,packet_length); + mysql_parse(thd,thd->query, thd->query_length); + + while (!thd->killed && !thd->is_fatal_error && thd->lex.found_colon) + { + char *packet= thd->lex.found_colon; + /* + Multiple queries exits, execute them individually + */ + if (thd->lock || thd->open_tables || thd->derived_tables) + close_thread_tables(thd); + + ulong length= thd->query_length-(ulong)(thd->lex.found_colon-thd->query); + + /* Remove garbage at start of query */ + while (my_isspace(thd->charset(), *packet) && length > 0) + { + packet++; + length--; + } + thd->query_length= length; + thd->query= packet; + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->query_id= query_id++; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + mysql_parse(thd, packet, length); + } + if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),WAIT_PRIOR); DBUG_PRINT("info",("query ready")); @@ -1070,28 +1366,33 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } case COM_FIELD_LIST: // This isn't actually needed #ifdef DONT_ALLOW_SHOW_COMMANDS - send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ break; #else { - char *fields; + char *fields, *pend; TABLE_LIST table_list; + LEX_STRING conv_name; + statistic_increment(com_stat[SQLCOM_SHOW_FIELDS],&LOCK_status); bzero((char*) &table_list,sizeof(table_list)); if (!(table_list.db=thd->db)) { - send_error(net,ER_NO_DB_ERROR); + send_error(thd,ER_NO_DB_ERROR); break; } thd->free_list=0; - table_list.alias= table_list.real_name= thd->strdup(packet); - packet=strend(packet)+1; + pend= strend(packet); + thd->convert_string(&conv_name, system_charset_info, + packet, (uint) (pend-packet), thd->charset()); + table_list.alias= table_list.real_name= conv_name.str; + packet= pend+1; // command not cachable => no gap for data base name if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1))) break; mysql_log.write(thd,command,"%s %s",table_list.real_name,fields); if (lower_case_table_names) - casedn_str(table_list.real_name); + my_casedn_str(files_charset_info, table_list.real_name); remove_escape(table_list.real_name); // This can't have wildcards if (check_access(thd,SELECT_ACL,table_list.db,&thd->col_access)) @@ -1100,7 +1401,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (grant_option && check_grant(thd,SELECT_ACL,&table_list,2)) break; mysqld_list_fields(thd,&table_list,fields); - free_items(thd); + free_items(thd->free_list); break; } #endif @@ -1118,7 +1419,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, // null test to handle EOM if (!db || !strip_sp(db) || check_db_name(db)) { - net_printf(&thd->net,ER_WRONG_DB_NAME, db ? db : "NULL"); + net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL"); break; } if (check_access(thd,CREATE_ACL,db,0,1)) @@ -1134,20 +1435,21 @@ bool dispatch_command(enum enum_server_command command, THD *thd, // null test to handle EOM if (!db || !strip_sp(db) || check_db_name(db)) { - net_printf(&thd->net,ER_WRONG_DB_NAME, db ? db : "NULL"); + net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL"); break; } if (check_access(thd,DROP_ACL,db,0,1)) break; if (thd->locked_tables || thd->active_transaction()) { - send_error(&thd->net,ER_LOCK_OR_ACTIVE_TRANSACTION); + send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION); break; } mysql_log.write(thd,command,db); mysql_rm_db(thd,db,0,0); break; } +#ifndef EMBEDDED_LIBRARY case COM_BINLOG_DUMP: { statistic_increment(com_other,&LOCK_status); @@ -1173,6 +1475,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, net->error = 0; break; } +#endif case COM_REFRESH: { statistic_increment(com_stat[SQLCOM_FLUSH],&LOCK_status); @@ -1180,31 +1483,35 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (check_global_access(thd,RELOAD_ACL)) break; mysql_log.write(thd,command,NullS); - /* error sending is deferred to reload_acl_and_cache */ - reload_acl_and_cache(thd, options, (TABLE_LIST*) 0) ; + if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, NULL)) + send_error(thd, 0); + else + send_ok(thd); break; } +#ifndef EMBEDDED_LIBRARY case COM_SHUTDOWN: statistic_increment(com_other,&LOCK_status); if (check_global_access(thd,SHUTDOWN_ACL)) break; /* purecov: inspected */ DBUG_PRINT("quit",("Got shutdown command")); mysql_log.write(thd,command,NullS); - send_eof(net); + send_eof(thd); #ifdef __WIN__ sleep(1); // must wait after eof() #endif #ifndef OS2 - send_eof(net); // This is for 'quit request' + send_eof(thd); // This is for 'quit request' #endif - close_connection(net); + close_connection(thd, 0, 1); close_thread_tables(thd); // Free before kill free_root(&thd->mem_root,MYF(0)); free_root(&thd->transaction.mem_root,MYF(0)); kill_mysql(); error=TRUE; break; - +#endif +#ifndef EMBEDDED_LIBRARY case COM_STATISTICS: { mysql_log.write(thd,command,NullS); @@ -1227,9 +1534,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, VOID(net_flush(net)); break; } +#endif case COM_PING: statistic_increment(com_other,&LOCK_status); - send_ok(net); // Tell client we are alive + send_ok(thd); // Tell client we are alive break; case COM_PROCESS_INFO: statistic_increment(com_stat[SQLCOM_SHOW_PROCESSLIST],&LOCK_status); @@ -1252,7 +1560,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; /* purecov: inspected */ mysql_print_status(thd); mysql_log.write(thd,command,NullS); - send_eof(net); + send_eof(thd); break; case COM_SLEEP: case COM_CONNECT: // Impossible here @@ -1260,17 +1568,17 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_DELAYED_INSERT: case COM_END: default: - send_error(net, ER_UNKNOWN_COM_ERROR); + send_error(thd, ER_UNKNOWN_COM_ERROR); break; } - if (thd->lock || thd->open_tables) + if (thd->lock || thd->open_tables || thd->derived_tables) { thd->proc_info="closing tables"; close_thread_tables(thd); /* Free tables */ } - if (thd->fatal_error) - send_error(net,0); // End of memory ? + if (thd->is_fatal_error) + send_error(thd,0); // End of memory ? time_t start_of_query=thd->start_time; thd->end_time(); // Set start time @@ -1302,24 +1610,85 @@ bool dispatch_command(enum enum_server_command command, THD *thd, DBUG_RETURN(error); } + +/* + Read query from packet and store in thd->query + Used in COM_QUERY and COM_PREPARE + + DESCRIPTION + Sets the following THD variables: + query + query_length + + RETURN VALUES + 0 ok + 1 error; In this case thd->fatal_error is set +*/ + +bool alloc_query(THD *thd, char *packet, ulong packet_length) +{ + packet_length--; // Remove end null + /* Remove garbage at start and end of query */ + while (my_isspace(thd->charset(),packet[0]) && packet_length > 0) + { + packet++; + packet_length--; + } + char *pos=packet+packet_length; // Point at end null + while (packet_length > 0 && + (pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1]))) + { + pos--; + packet_length--; + } + /* We must allocate some extra memory for query cache */ + if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet), + packet_length, + thd->db_length+2+ + sizeof(ha_rows)))) + return 1; + thd->query[packet_length]=0; + thd->query_length= packet_length; + thd->packet.shrink(thd->variables.net_buffer_length);// Reclaim some memory + + if (!(specialflag & SPECIAL_NO_PRIOR)) + my_pthread_setprio(pthread_self(),QUERY_PRIOR); + return 0; +} + /**************************************************************************** ** mysql_execute_command ** Execute command saved in thd and current_lex->sql_command ****************************************************************************/ void -mysql_execute_command(void) +mysql_execute_command(THD *thd) { - int res=0; - THD *thd=current_thd; + int res= 0; LEX *lex= &thd->lex; - TABLE_LIST *tables=(TABLE_LIST*) lex->select_lex.table_list.first; - SELECT_LEX *select_lex = lex->select; + TABLE_LIST *tables= (TABLE_LIST*) lex->select_lex.table_list.first; + SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX_UNIT *unit= &lex->unit; DBUG_ENTER("mysql_execute_command"); + /* + Reset warning count for each query that uses tables + A better approach would be to reset this for any commands + that is not a SHOW command or a select that only access local + variables, but for now this is probably good enough. + */ + if (tables || &lex->select_lex != lex->all_selects_list) + mysql_reset_errors(thd); + /* + Save old warning count to be able to send to client how many warnings we + got + */ + thd->old_total_warn_count= thd->total_warn_count; + +#ifndef EMBEDDED_LIBRARY if (thd->slave_thread) { - /* + /* Skip if we are in the slave thread, some table rules have been given and the table list says the query should not be replicated */ @@ -1331,9 +1700,9 @@ mysql_execute_command(void) } #ifndef TO_BE_DELETED /* - This is a workaround to deal with the shortcoming in 3.23.44-3.23.46 - masters in RELEASE_LOCK() logging. We re-write SELECT RELEASE_LOCK() - as DO RELEASE_LOCK() + This is a workaround to deal with the shortcoming in 3.23.44-3.23.46 + masters in RELEASE_LOCK() logging. We re-write SELECT RELEASE_LOCK() + as DO RELEASE_LOCK() */ if (lex->sql_command == SQLCOM_SELECT) { @@ -1342,8 +1711,34 @@ mysql_execute_command(void) } #endif } - - if (lex->select_lex.next && create_total_list(thd,lex,&tables)) +#endif /* EMBEDDED_LIBRARY */ + /* + TODO: make derived tables processing 'inside' SELECT processing. + TODO: solve problem with depended derived tables in subselects + */ + if (lex->derived_tables) + { + for (SELECT_LEX *sl= lex->all_selects_list; + sl; + sl= sl->next_select_in_list()) + { + for (TABLE_LIST *cursor= sl->get_table_list(); + cursor; + cursor= cursor->next) + { + if (cursor->derived && (res=mysql_derived(thd, lex, + cursor->derived, + cursor))) + { + if (res < 0 || thd->net.report_error) + send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0); + DBUG_VOID_RETURN; + } + } + } + } + if (&lex->select_lex != lex->all_selects_list && + lex->unit.create_total_list(thd, lex, &tables, 0)) DBUG_VOID_RETURN; /* @@ -1354,7 +1749,7 @@ mysql_execute_command(void) !(thd->slave_thread || (thd->master_access & SUPER_ACL)) && (uc_update_queries[lex->sql_command] > 0)) { - send_error(&thd->net,ER_CANT_UPDATE_WITH_READLOCK); + send_error(thd, ER_CANT_UPDATE_WITH_READLOCK); DBUG_VOID_RETURN; } @@ -1362,9 +1757,7 @@ mysql_execute_command(void) switch (lex->sql_command) { case SQLCOM_SELECT: { - select_result *result; - if (select_lex->options & SELECT_DESCRIBE) - lex->exchange=0; + select_result *result=lex->result; if (tables) { res=check_table_access(thd, @@ -1381,92 +1774,121 @@ mysql_execute_command(void) break; // Error message is given } - thd->offset_limit=select_lex->offset_limit; - thd->select_limit=select_lex->select_limit+select_lex->offset_limit; - if (thd->select_limit < select_lex->select_limit) - thd->select_limit= HA_POS_ERROR; // no limit - if (thd->select_limit == HA_POS_ERROR) + unit->offset_limit_cnt= (ha_rows) unit->global_parameters->offset_limit; + unit->select_limit_cnt= (ha_rows) (unit->global_parameters->select_limit+ + unit->global_parameters->offset_limit); + if (unit->select_limit_cnt < + (ha_rows) unit->global_parameters->select_limit) + unit->select_limit_cnt= HA_POS_ERROR; // no limit + if (unit->select_limit_cnt == HA_POS_ERROR) select_lex->options&= ~OPTION_FOUND_ROWS; - if (lex->exchange) + if (!(res=open_and_lock_tables(thd,tables))) { - if (lex->exchange->dumpfile) + if (lex->describe) { - if (!(result=new select_dump(lex->exchange))) + if (!(result= new select_send())) { - res= -1; - break; + send_error(thd, ER_OUT_OF_RESOURCES); + DBUG_VOID_RETURN; } + else + thd->send_explain_fields(result); + fix_tables_pointers(lex->all_selects_list); + res= mysql_explain_union(thd, &thd->lex.unit, result); + MYSQL_LOCK *save_lock= thd->lock; + thd->lock= (MYSQL_LOCK *)0; + result->send_eof(); + thd->lock= save_lock; } else { - if (!(result=new select_export(lex->exchange))) + if (!result) { - res= -1; - break; - } - } - } - else if (!(result=new select_send())) - { - res= -1; + if (!(result=new select_send())) + { + res= -1; #ifdef DELETE_ITEMS - delete select_lex->having; - delete select_lex->where; + delete select_lex->having; + delete select_lex->where; #endif - break; - } - else - { - /* - Normal select: - Change lock if we are using SELECT HIGH PRIORITY, - FOR UPDATE or IN SHARE MODE - - TODO: Delete the following loop when locks is set by sql_yacc - */ - TABLE_LIST *table; - for (table = tables ; table ; table=table->next) - table->lock_type= lex->lock_option; - } - - if (!(res=open_and_lock_tables(thd,tables))) - { - query_cache_store_query(thd, tables); - res=handle_select(thd, lex, result); + break; + } + } + query_cache_store_query(thd, tables); + res=handle_select(thd, lex, result); + } } - else - delete result; break; } case SQLCOM_DO: - res=mysql_do(thd, *lex->insert_list); + if (tables && ((res= check_table_access(thd, SELECT_ACL, tables)) || + (res= open_and_lock_tables(thd,tables)))) + break; + + fix_tables_pointers(lex->all_selects_list); + res= mysql_do(thd, *lex->insert_list); + if (thd->net.report_error) + res= -1; break; case SQLCOM_EMPTY_QUERY: - send_ok(&thd->net); + send_ok(thd); break; + case SQLCOM_HELP: + res= mysqld_help(thd,lex->help_arg); + break; + +#ifndef EMBEDDED_LIBRARY case SQLCOM_PURGE: { if (check_global_access(thd, SUPER_ACL)) goto error; + // PURGE MASTER LOGS TO 'file' res = purge_master_logs(thd, lex->to_log); break; } + case SQLCOM_PURGE_BEFORE: + { + if (check_global_access(thd, SUPER_ACL)) + goto error; + // PURGE MASTER LOGS BEFORE 'data' + res = purge_master_logs_before_date(thd, lex->purge_time); + break; + } +#endif + + case SQLCOM_SHOW_WARNS: + { + res= mysqld_show_warnings(thd, (ulong) + ((1L << (uint) MYSQL_ERROR::WARN_LEVEL_NOTE) | + (1L << (uint) MYSQL_ERROR::WARN_LEVEL_WARN) | + (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR) + )); + break; + } + case SQLCOM_SHOW_ERRORS: + { + res= mysqld_show_warnings(thd, (ulong) + (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)); + break; + } case SQLCOM_SHOW_NEW_MASTER: { if (check_global_access(thd, REPL_SLAVE_ACL)) goto error; /* This query don't work now. See comment in repl_failsafe.cc */ #ifndef WORKING_NEW_MASTER - net_printf(&thd->net, ER_NOT_SUPPORTED_YET, "SHOW NEW MASTER"); + net_printf(thd, ER_NOT_SUPPORTED_YET, "SHOW NEW MASTER"); res= 1; #else res = show_new_master(thd); #endif break; } + +#ifndef EMBEDDED_LIBRARY case SQLCOM_SHOW_SLAVE_HOSTS: { if (check_global_access(thd, REPL_SLAVE_ACL)) @@ -1481,6 +1903,8 @@ mysql_execute_command(void) res = show_binlog_events(thd); break; } +#endif + case SQLCOM_BACKUP_TABLE: { if (check_db_used(thd,tables) || @@ -1500,6 +1924,17 @@ mysql_execute_command(void) res = mysql_restore_table(thd, tables); break; } + case SQLCOM_PRELOAD_KEYS: + { + if (check_db_used(thd, tables) || + check_access(thd, INDEX_ACL, tables->db, &tables->grant.privilege)) + goto error; + res = mysql_preload_keys(thd, tables); + break; + } + + +#ifndef EMBEDDED_LIBRARY case SQLCOM_CHANGE_MASTER: { if (check_global_access(thd, SUPER_ACL)) @@ -1527,7 +1962,7 @@ mysql_execute_command(void) res = show_binlog_info(thd); break; } - + case SQLCOM_LOAD_MASTER_DATA: // sync with master if (check_global_access(thd, SUPER_ACL)) goto error; @@ -1536,7 +1971,8 @@ mysql_execute_command(void) else res = load_master_data(thd); break; - +#endif /* EMBEDDED_LIBRARY */ + #ifdef HAVE_INNOBASE_DB case SQLCOM_SHOW_INNODB_STATUS: { @@ -1547,6 +1983,7 @@ mysql_execute_command(void) } #endif +#ifndef EMBEDDED_LIBRARY case SQLCOM_LOAD_MASTER_TABLE: { if (!tables->db) @@ -1563,16 +2000,23 @@ mysql_execute_command(void) if (error) goto error; } + if (strlen(tables->real_name) > NAME_LEN) + { + net_printf(thd,ER_WRONG_TABLE_NAME,tables->real_name); + break; + } LOCK_ACTIVE_MI; // fetch_master_table will send the error to the client on failure if (!fetch_master_table(thd, tables->db, tables->real_name, active_mi, 0)) { - send_ok(&thd->net); + send_ok(thd); } UNLOCK_ACTIVE_MI; break; } +#endif /* EMBEDDED_LIBRARY */ + case SQLCOM_CREATE_TABLE: { ulong want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ? @@ -1596,7 +2040,7 @@ mysql_execute_command(void) } if (strlen(tables->real_name) > NAME_LEN) { - net_printf(&thd->net, ER_WRONG_TABLE_NAME, tables->alias); + net_printf(thd, ER_WRONG_TABLE_NAME, tables->alias); res=0; break; } @@ -1618,25 +2062,22 @@ mysql_execute_command(void) select_result *result; if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && - check_dup(tables->db, tables->real_name, tables->next)) + find_real_table_in_list(tables->next, tables->db, tables->real_name)) { - net_printf(&thd->net,ER_INSERT_TABLE_USED,tables->real_name); + net_printf(thd,ER_UPDATE_TABLE_USED,tables->real_name); DBUG_VOID_RETURN; } if (tables->next) { - TABLE_LIST *table; if (check_table_access(thd, SELECT_ACL, tables->next)) goto error; // Error message is given - /* TODO: Delete the following loop when locks is set by sql_yacc */ - for (table = tables->next ; table ; table=table->next) - table->lock_type= lex->lock_option; } select_lex->options|= SELECT_NO_UNLOCK; - thd->offset_limit=select_lex->offset_limit; - thd->select_limit=select_lex->select_limit+select_lex->offset_limit; - if (thd->select_limit < select_lex->select_limit) - thd->select_limit= HA_POS_ERROR; // No limit + unit->offset_limit_cnt= select_lex->offset_limit; + unit->select_limit_cnt= select_lex->select_limit+ + select_lex->offset_limit; + if (unit->select_limit_cnt < select_lex->select_limit) + unit->select_limit_cnt= HA_POS_ERROR; // No limit /* Skip first table, which is the table we are creating */ lex->select_lex.table_list.first= @@ -1655,12 +2096,26 @@ mysql_execute_command(void) } 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 (lex->name) + res= mysql_create_like_table(thd, tables, &lex->create_info, + (Table_ident *)lex->name); + else + { + List_iterator<create_field> fields(lex->create_list); + create_field *field; + while ((field= fields++)) + { + if (!field->charset) + field->charset= lex->create_info.table_charset; + field->create_length_to_internal_length(); + } + 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,0); // do logging + } if (!res) - send_ok(&thd->net); + send_ok(thd); } break; } @@ -1677,6 +2132,7 @@ mysql_execute_command(void) res = mysql_create_index(thd, tables, lex->key_list); break; +#ifndef EMBEDDED_LIBRARY case SQLCOM_SLAVE_START: { LOCK_ACTIVE_MI; @@ -1700,7 +2156,7 @@ mysql_execute_command(void) */ if (thd->locked_tables || thd->active_transaction()) { - send_error(&thd->net,ER_LOCK_OR_ACTIVE_TRANSACTION); + send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION); break; } { @@ -1709,16 +2165,18 @@ mysql_execute_command(void) UNLOCK_ACTIVE_MI; break; } +#endif + case SQLCOM_ALTER_TABLE: #if defined(DONT_ALLOW_SHOW_COMMANDS) - send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ break; #else { ulong priv=0; if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN)) { - net_printf(&thd->net,ER_WRONG_TABLE_NAME,lex->name); + net_printf(thd,ER_WRONG_TABLE_NAME,lex->name); res=0; break; } @@ -1728,7 +2186,7 @@ mysql_execute_command(void) select_lex->db=tables->db; if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege) || check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv) || - check_merge_table_access(thd, tables->db, + check_merge_table_access(thd, tables->db, (TABLE_LIST *) lex->create_info.merge_list.first)) goto error; /* purecov: inspected */ @@ -1760,6 +2218,7 @@ mysql_execute_command(void) &lex->create_info, tables, lex->create_list, lex->key_list, lex->drop_list, lex->alter_list, + select_lex->order_list.elements, (ORDER *) select_lex->order_list.first, lex->drop_primary, lex->duplicates, lex->alter_keys_onoff, lex->simple_alter); @@ -1799,9 +2258,10 @@ mysql_execute_command(void) res= -1; break; } +#ifndef EMBEDDED_LIBRARY case SQLCOM_SHOW_BINLOGS: #ifdef DONT_ALLOW_SHOW_COMMANDS - send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ DBUG_VOID_RETURN; #else { @@ -1810,10 +2270,11 @@ mysql_execute_command(void) res = show_binlogs(thd); break; } -#endif +#endif +#endif /* EMBEDDED_LIBRARY */ case SQLCOM_SHOW_CREATE: #ifdef DONT_ALLOW_SHOW_COMMANDS - send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ DBUG_VOID_RETURN; #else { @@ -1831,6 +2292,16 @@ mysql_execute_command(void) check_table_access(thd,SELECT_ACL | INSERT_ACL, tables)) goto error; /* purecov: inspected */ res = mysql_repair_table(thd, tables, &lex->check_opt); + /* ! we write after unlocking the table */ + if (!res && !lex->no_write_to_binlog) + { + mysql_update_log.write(thd, thd->query, thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, 0); + mysql_bin_log.write(&qinfo); + } + } break; } case SQLCOM_CHECK: @@ -1847,6 +2318,16 @@ mysql_execute_command(void) check_table_access(thd,SELECT_ACL | INSERT_ACL, tables)) goto error; /* purecov: inspected */ res = mysql_analyze_table(thd, tables, &lex->check_opt); + /* ! we write after unlocking the table */ + if (!res && !lex->no_write_to_binlog) + { + mysql_update_log.write(thd, thd->query, thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, 0); + mysql_bin_log.write(&qinfo); + } + } break; } @@ -1867,54 +2348,70 @@ mysql_execute_command(void) bzero((char*) &create_info,sizeof(create_info)); create_info.db_type=DB_TYPE_DEFAULT; create_info.row_type=ROW_TYPE_DEFAULT; + create_info.table_charset=default_charset_info; res= mysql_alter_table(thd, NullS, NullS, &create_info, tables, lex->create_list, lex->key_list, lex->drop_list, lex->alter_list, - (ORDER *) 0, - 0,DUP_ERROR); + 0, (ORDER *) 0, + 0, DUP_ERROR); } else res = mysql_optimize_table(thd, tables, &lex->check_opt); + /* ! we write after unlocking the table */ + if (!res && !lex->no_write_to_binlog) + { + mysql_update_log.write(thd, thd->query, thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, 0); + mysql_bin_log.write(&qinfo); + } + } break; } case SQLCOM_UPDATE: - TABLE_LIST *table; if (check_db_used(thd,tables)) goto error; - for (table=tables ; table ; table=table->next) - { - if (check_access(thd,UPDATE_ACL,table->db,&table->grant.privilege)) + + if (single_table_command_access(thd, UPDATE_ACL, tables, &res)) goto error; + + if (select_lex->item_list.elements != lex->value_list.elements) + { + send_error(thd,ER_WRONG_VALUE_COUNT); + DBUG_VOID_RETURN; } + res= mysql_update(thd,tables, + select_lex->item_list, + lex->value_list, + select_lex->where, + select_lex->order_list.elements, + (ORDER *) select_lex->order_list.first, + select_lex->select_limit, + lex->duplicates); + if (thd->net.report_error) + res= -1; + break; + case SQLCOM_UPDATE_MULTI: + if (check_access(thd,UPDATE_ACL,tables->db,&tables->grant.privilege)) + goto error; if (grant_option && check_grant(thd,UPDATE_ACL,tables)) goto error; if (select_lex->item_list.elements != lex->value_list.elements) { - send_error(&thd->net,ER_WRONG_VALUE_COUNT); + send_error(thd,ER_WRONG_VALUE_COUNT); DBUG_VOID_RETURN; } - if (select_lex->table_list.elements == 1) - { - res= mysql_update(thd,tables, - select_lex->item_list, - lex->value_list, - select_lex->where, - (ORDER *) select_lex->order_list.first, - select_lex->select_limit, - lex->duplicates); - } - else { const char *msg= 0; - lex->sql_command= SQLCOM_MULTI_UPDATE; if (select_lex->order_list.elements) - msg="ORDER BY"; + msg= "ORDER BY"; else if (select_lex->select_limit && select_lex->select_limit != HA_POS_ERROR) - msg="LIMIT"; + msg= "LIMIT"; if (msg) { - net_printf(&thd->net, ER_WRONG_USAGE, "UPDATE", msg); + net_printf(thd, ER_WRONG_USAGE, "UPDATE", msg); res= 1; break; } @@ -1923,28 +2420,31 @@ mysql_execute_command(void) &lex->value_list, select_lex->where, select_lex->options, - lex->duplicates); + lex->duplicates, unit, select_lex); } 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); - break; case SQLCOM_REPLACE: - if (check_access(thd,INSERT_ACL | DELETE_ACL, - tables->db,&tables->grant.privilege)) - goto error; /* purecov: inspected */ - if (grant_option && check_grant(thd,INSERT_ACL | DELETE_ACL, - tables)) + case SQLCOM_INSERT: + { + my_bool update=(lex->value_list.elements ? UPDATE_ACL : 0); + ulong privilege= (lex->duplicates == DUP_REPLACE ? + INSERT_ACL | DELETE_ACL : INSERT_ACL | update); - goto error; + if (single_table_command_access(thd, privilege, tables, &res)) + goto error; + + if (select_lex->item_list.elements != lex->value_list.elements) + { + send_error(thd,ER_WRONG_VALUE_COUNT); + DBUG_VOID_RETURN; + } res = mysql_insert(thd,tables,lex->field_list,lex->many_values, - DUP_REPLACE); + select_lex->item_list, lex->value_list, + (update ? DUP_UPDATE : lex->duplicates)); + if (thd->net.report_error) + res= -1; break; + } case SQLCOM_REPLACE_SELECT: case SQLCOM_INSERT_SELECT: { @@ -1954,8 +2454,8 @@ mysql_execute_command(void) select privileges for the rest */ { - ulong privilege= (lex->sql_command == SQLCOM_INSERT_SELECT ? - INSERT_ACL : INSERT_ACL | DELETE_ACL); + ulong privilege= (lex->duplicates == DUP_REPLACE ? + INSERT_ACL | DELETE_ACL : INSERT_ACL); TABLE_LIST *save_next=tables->next; tables->next=0; if (check_access(thd, privilege, @@ -1970,31 +2470,29 @@ mysql_execute_command(void) select_lex->options|= SELECT_NO_UNLOCK; select_result *result; - thd->offset_limit=select_lex->offset_limit; - thd->select_limit=select_lex->select_limit+select_lex->offset_limit; - if (thd->select_limit < select_lex->select_limit) - thd->select_limit= HA_POS_ERROR; // No limit + unit->offset_limit_cnt= select_lex->offset_limit; + unit->select_limit_cnt= select_lex->select_limit+select_lex->offset_limit; + if (unit->select_limit_cnt < select_lex->select_limit) + unit->select_limit_cnt= HA_POS_ERROR; // No limit - if (check_dup(tables->db, tables->real_name, tables->next)) + if (find_real_table_in_list(tables->next, tables->db, tables->real_name)) { /* Using same table for INSERT and SELECT */ select_lex->options |= OPTION_BUFFER_RESULT; } - { - /* TODO: Delete the following loop when locks is set by sql_yacc */ - TABLE_LIST *table; - for (table = tables->next ; table ; table=table->next) - table->lock_type= lex->lock_option; - } /* Skip first table, which is the table we are inserting in */ lex->select_lex.table_list.first= (byte*) (((TABLE_LIST *) lex->select_lex.table_list.first)->next); + lex->select_lex.resolve_mode= SELECT_LEX::NOMATTER_MODE; + if (!(res=open_and_lock_tables(thd, tables))) { if ((result=new select_insert(tables->table,&lex->field_list, lex->duplicates))) res=handle_select(thd,lex,result); + if (thd->net.report_error) + res= -1; } else res= -1; @@ -2011,22 +2509,23 @@ mysql_execute_command(void) */ if (thd->locked_tables || thd->active_transaction()) { - send_error(&thd->net,ER_LOCK_OR_ACTIVE_TRANSACTION,NullS); + send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION,NullS); goto error; } res=mysql_truncate(thd,tables); break; case SQLCOM_DELETE: { - if (check_access(thd,DELETE_ACL,tables->db,&tables->grant.privilege)) - goto error; /* purecov: inspected */ - if (grant_option && check_grant(thd,DELETE_ACL,tables)) + if (single_table_command_access(thd, DELETE_ACL, tables, &res)) goto error; + // Set privilege for the WHERE clause tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege); res = mysql_delete(thd,tables, select_lex->where, (ORDER*) select_lex->order_list.first, select_lex->select_limit, select_lex->options); + if (thd->net.report_error) + res= -1; break; } case SQLCOM_DELETE_MULTI: @@ -2038,12 +2537,12 @@ mysql_execute_command(void) /* sql_yacc guarantees that tables and aux_tables are not zero */ if (check_db_used(thd, tables) || check_db_used(thd,aux_tables) || - check_table_access(thd,SELECT_ACL, tables) || + check_table_access(thd,SELECT_ACL, tables) || check_table_access(thd,DELETE_ACL, aux_tables)) goto error; if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where) - { - send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE); + { + send_error(thd,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE); goto error; } for (auxi=(TABLE_LIST*) aux_tables ; auxi ; auxi=auxi->next) @@ -2059,36 +2558,52 @@ mysql_execute_command(void) } if (!walk) { - net_printf(&thd->net,ER_NONUNIQ_TABLE,auxi->real_name); + net_printf(thd,ER_NONUNIQ_TABLE,auxi->real_name); goto error; } walk->lock_type= auxi->lock_type; - // Store address to table as we need it later - auxi->table= my_reinterpret_cast(TABLE *) (walk); + auxi->table_list= walk; // Remember corresponding table } - if (add_item_to_list(new Item_null())) + if (add_item_to_list(thd, new Item_null())) { res= -1; break; } - tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege); thd->proc_info="init"; if ((res=open_and_lock_tables(thd,tables))) break; /* Fix tables-to-be-deleted-from list to point at opened tables */ for (auxi=(TABLE_LIST*) aux_tables ; auxi ; auxi=auxi->next) - auxi->table= (my_reinterpret_cast(TABLE_LIST*) (auxi->table))->table; - - if (!thd->fatal_error && (result= new multi_delete(thd,aux_tables, - table_count))) - { - res=mysql_select(thd,tables,select_lex->item_list, - select_lex->where, - (ORDER *)NULL,(ORDER *)NULL,(Item *)NULL, - (ORDER *)NULL, - select_lex->options | thd->options | - SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK, - result); + auxi->table= auxi->table_list->table; + if (&lex->select_lex != lex->all_selects_list) + { + for (TABLE_LIST *t= select_lex->get_table_list(); + t; t= t->next) + { + if (find_real_table_in_list(t->table_list->next, t->db, t->real_name)) + { + my_error(ER_UPDATE_TABLE_USED, MYF(0), t->real_name); + res= -1; + break; + } + } + } + fix_tables_pointers(lex->all_selects_list); + if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables, + table_count))) + { + res= mysql_select(thd, &select_lex->ref_pointer_array, + select_lex->get_table_list(), + select_lex->with_wild, + select_lex->item_list, + select_lex->where, + 0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL, + (ORDER *)NULL, + select_lex->options | thd->options | + SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK, + result, unit, select_lex, 0); + if (thd->net.report_error) + res= -1; delete result; } else @@ -2098,22 +2613,30 @@ mysql_execute_command(void) } case SQLCOM_DROP_TABLE: { - if (check_table_access(thd,DROP_ACL,tables)) - goto error; /* purecov: inspected */ - /* - If this is a slave thread, we may sometimes execute some - DROP / * 40005 TEMPORARY * / TABLE - that come from parts of binlogs (likely if we use RESET SLAVE or CHANGE - MASTER TO), while the temporary table has already been dropped. - To not generate such irrelevant "table does not exist errors", we silently - add IF EXISTS if TEMPORARY was used. - */ - if (thd->slave_thread && lex->drop_temporary) - lex->drop_if_exists= 1; - if (end_active_trans(thd)) - res= -1; + if (!lex->drop_temporary) + { + if (check_table_access(thd,DROP_ACL,tables)) + goto error; /* purecov: inspected */ + if (end_active_trans(thd)) + { + res= -1; + break; + } + } else - res = mysql_rm_table(thd,tables,lex->drop_if_exists); + { + /* + If this is a slave thread, we may sometimes execute some + DROP / * 40005 TEMPORARY * / TABLE + that come from parts of binlogs (likely if we use RESET SLAVE or CHANGE + MASTER TO), while the temporary table has already been dropped. + To not generate such irrelevant "table does not exist errors", we + silently add IF EXISTS if TEMPORARY was used. + */ + if (thd->slave_thread) + lex->drop_if_exists= 1; + } + res= mysql_rm_table(thd,tables,lex->drop_if_exists, lex->drop_temporary); } break; case SQLCOM_DROP_INDEX: @@ -2130,7 +2653,7 @@ mysql_execute_command(void) break; case SQLCOM_SHOW_DATABASES: #if defined(DONT_ALLOW_SHOW_COMMANDS) - send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ DBUG_VOID_RETURN; #else if ((specialflag & SPECIAL_SKIP_SHOW_DB) && @@ -2145,6 +2668,15 @@ mysql_execute_command(void) mysqld_list_processes(thd,thd->master_access & PROCESS_ACL ? NullS : thd->priv_user,lex->verbose); break; + case SQLCOM_SHOW_TABLE_TYPES: + res= mysqld_show_table_types(thd); + break; + case SQLCOM_SHOW_PRIVILEGES: + res= mysqld_show_privileges(thd); + break; + case SQLCOM_SHOW_COLUMN_TYPES: + res= mysqld_show_column_types(thd); + break; case SQLCOM_SHOW_STATUS: res= mysqld_show(thd,(lex->wild ? lex->wild->ptr() : NullS),status_vars, OPT_GLOBAL, &LOCK_status); @@ -2155,34 +2687,41 @@ mysql_execute_command(void) &LOCK_global_system_variables); break; case SQLCOM_SHOW_LOGS: - { - res= mysqld_show_logs(thd); - break; - } +#ifdef DONT_ALLOW_SHOW_COMMANDS + send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + DBUG_VOID_RETURN; +#else + { + if (grant_option && check_access(thd, FILE_ACL, any_db)) + goto error; + res= mysqld_show_logs(thd); + break; + } +#endif case SQLCOM_SHOW_TABLES: /* FALL THROUGH */ #ifdef DONT_ALLOW_SHOW_COMMANDS - send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ DBUG_VOID_RETURN; #else { char *db=select_lex->db ? select_lex->db : thd->db; if (!db) { - send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ + send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */ goto error; /* purecov: inspected */ } remove_escape(db); // Fix escaped '_' if (check_db_name(db)) { - net_printf(&thd->net,ER_WRONG_DB_NAME, db); + net_printf(thd,ER_WRONG_DB_NAME, db); goto error; } if (check_access(thd,SELECT_ACL,db,&thd->col_access)) goto error; /* purecov: inspected */ if (!thd->col_access && check_grant_db(thd,db)) { - net_printf(&thd->net,ER_DBACCESS_DENIED_ERROR, + net_printf(thd, ER_DBACCESS_DENIED_ERROR, thd->priv_user, thd->priv_host, db); @@ -2201,16 +2740,22 @@ mysql_execute_command(void) case SQLCOM_SHOW_OPEN_TABLES: res= mysqld_show_open_tables(thd,(lex->wild ? lex->wild->ptr() : NullS)); break; + case SQLCOM_SHOW_CHARSETS: + res= mysqld_show_charsets(thd,(lex->wild ? lex->wild->ptr() : NullS)); + break; + case SQLCOM_SHOW_COLLATIONS: + res= mysqld_show_collations(thd,(lex->wild ? lex->wild->ptr() : NullS)); + break; case SQLCOM_SHOW_FIELDS: #ifdef DONT_ALLOW_SHOW_COMMANDS - send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ DBUG_VOID_RETURN; #else { char *db=tables->db; if (!*db) { - send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ + send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */ goto error; /* purecov: inspected */ } remove_escape(db); // Fix escaped '_' @@ -2228,14 +2773,14 @@ mysql_execute_command(void) #endif case SQLCOM_SHOW_KEYS: #ifdef DONT_ALLOW_SHOW_COMMANDS - send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ DBUG_VOID_RETURN; #else { char *db=tables->db; if (!db) { - send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ + send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */ goto error; /* purecov: inspected */ } remove_escape(db); // Fix escaped '_' @@ -2254,10 +2799,11 @@ mysql_execute_command(void) case SQLCOM_CHANGE_DB: mysql_change_db(thd,select_lex->db); break; +#ifndef EMBEDDED_LIBRARY case SQLCOM_LOAD: { uint privilege= (lex->duplicates == DUP_REPLACE ? - INSERT_ACL | UPDATE_ACL | DELETE_ACL : INSERT_ACL); + INSERT_ACL | DELETE_ACL : INSERT_ACL); if (!lex->local_file) { @@ -2269,7 +2815,7 @@ mysql_execute_command(void) if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) || ! opt_local_infile) { - send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); + send_error(thd,ER_NOT_ALLOWED_COMMAND); goto error; } if (check_access(thd,privilege,tables->db,&tables->grant.privilege) || @@ -2280,10 +2826,18 @@ mysql_execute_command(void) lex->duplicates, (bool) lex->local_file, lex->lock_option); break; } +#endif /* EMBEDDED_LIBRARY */ case SQLCOM_SET_OPTION: - if (!(res=sql_set_variables(thd, &lex->var_list))) - send_ok(&thd->net); + if (tables && ((res= check_table_access(thd, SELECT_ACL, tables)) || + (res= open_and_lock_tables(thd,tables)))) + break; + fix_tables_pointers(lex->all_selects_list); + if (!(res= sql_set_variables(thd, &lex->var_list))) + send_ok(thd); + if (thd->net.report_error) + res= -1; break; + case SQLCOM_UNLOCK_TABLES: unlock_locked_tables(thd); if (thd->options & OPTION_TABLE_LOCK) @@ -2293,7 +2847,7 @@ mysql_execute_command(void) } if (thd->global_read_lock) unlock_global_read_lock(thd); - send_ok(&thd->net); + send_ok(thd); break; case SQLCOM_LOCK_TABLES: unlock_locked_tables(thd); @@ -2307,7 +2861,7 @@ mysql_execute_command(void) { thd->locked_tables=thd->lock; thd->lock=0; - send_ok(&thd->net); + send_ok(thd); } else thd->options&= ~(ulong) (OPTION_TABLE_LOCK); @@ -2317,7 +2871,7 @@ mysql_execute_command(void) { if (!strip_sp(lex->name) || check_db_name(lex->name)) { - net_printf(&thd->net,ER_WRONG_DB_NAME, lex->name); + net_printf(thd,ER_WRONG_DB_NAME, lex->name); break; } /* @@ -2327,6 +2881,7 @@ mysql_execute_command(void) do_db/ignore_db. And as this query involves no tables, tables_ok() above was not called. So we have to check rules again here. */ +#ifdef HAVE_REPLICATION if (thd->slave_thread && (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) || !db_ok_with_wild_table(lex->name))) @@ -2334,17 +2889,17 @@ mysql_execute_command(void) my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); break; } - +#endif if (check_access(thd,CREATE_ACL,lex->name,0,1)) break; - res=mysql_create_db(thd,lex->name,lex->create_info.options,0); + res=mysql_create_db(thd,lex->name,&lex->create_info,0); break; } case SQLCOM_DROP_DB: { if (!strip_sp(lex->name) || check_db_name(lex->name)) { - net_printf(&thd->net,ER_WRONG_DB_NAME, lex->name); + net_printf(thd,ER_WRONG_DB_NAME, lex->name); break; } /* @@ -2354,6 +2909,7 @@ mysql_execute_command(void) do_db/ignore_db. And as this query involves no tables, tables_ok() above was not called. So we have to check rules again here. */ +#ifdef HAVE_REPLICATION if (thd->slave_thread && (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) || !db_ok_with_wild_table(lex->name))) @@ -2361,22 +2917,57 @@ mysql_execute_command(void) my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); break; } +#endif if (check_access(thd,DROP_ACL,lex->name,0,1)) break; if (thd->locked_tables || thd->active_transaction()) { - send_error(&thd->net,ER_LOCK_OR_ACTIVE_TRANSACTION); + send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION); goto error; } res=mysql_rm_db(thd,lex->name,lex->drop_if_exists,0); break; } + case SQLCOM_ALTER_DB: + { + if (!strip_sp(lex->name) || check_db_name(lex->name)) + { + net_printf(thd,ER_WRONG_DB_NAME, lex->name); + break; + } + if (check_access(thd,ALTER_ACL,lex->name,0,1)) + break; + if (thd->locked_tables || thd->active_transaction()) + { + send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION); + goto error; + } + res=mysql_alter_db(thd,lex->name,&lex->create_info); + break; + } + case SQLCOM_SHOW_CREATE_DB: + { + if (!strip_sp(lex->name) || check_db_name(lex->name)) + { + net_printf(thd,ER_WRONG_DB_NAME, lex->name); + break; + } + if (check_access(thd,DROP_ACL,lex->name,0,1)) + break; + if (thd->locked_tables || thd->active_transaction()) + { + send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION); + goto error; + } + res=mysqld_show_create_db(thd,lex->name,&lex->create_info); + 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); + send_ok(thd); #else res= -1; #endif @@ -2385,12 +2976,44 @@ mysql_execute_command(void) 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); + if (!(res = mysql_drop_function(thd,&lex->udf.name))) + send_ok(thd); #else res= -1; #endif break; + case SQLCOM_DROP_USER: + { + if (check_access(thd, GRANT_ACL,"mysql",0,1)) + break; + if (!(res= mysql_drop_user(thd, lex->users_list))) + { + mysql_update_log.write(thd, thd->query, thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, 0); + mysql_bin_log.write(&qinfo); + } + send_ok(thd); + } + break; + } + case SQLCOM_REVOKE_ALL: + { + if (check_access(thd, GRANT_ACL ,"mysql",0,1)) + break; + if (!(res = mysql_revoke_all(thd, lex->users_list))) + { + mysql_update_log.write(thd, thd->query, thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, 0); + mysql_bin_log.write(&qinfo); + } + send_ok(thd); + } + break; + } case SQLCOM_REVOKE: case SQLCOM_GRANT: { @@ -2414,7 +3037,8 @@ mysql_execute_command(void) if (user->password.str && (strcmp(thd->user,user->user.str) || user->host.str && - my_strcasecmp(user->host.str, thd->host_or_ip))) + my_strcasecmp(&my_charset_latin1, + user->host.str, thd->host_or_ip))) { if (check_access(thd, UPDATE_ACL, "mysql",0,1)) goto error; @@ -2445,7 +3069,7 @@ mysql_execute_command(void) { if (lex->columns.elements) { - send_error(&thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); + send_error(thd,ER_ILLEGAL_GRANT_FOR_TABLE); res=1; } else @@ -2470,13 +3094,42 @@ mysql_execute_command(void) } break; } - case SQLCOM_FLUSH: case SQLCOM_RESET: + /* + RESET commands are never written to the binary log, so we have to + initialize this variable because RESET shares the same code as FLUSH + */ + lex->no_write_to_binlog= 1; + case SQLCOM_FLUSH: + { if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, tables)) goto error; - /* error sending is deferred to reload_acl_and_cache */ - reload_acl_and_cache(thd, lex->type, tables) ; + /* + reload_acl_and_cache() will tell us if we are allowed to write to the + binlog or not. + */ + bool write_to_binlog; + if (reload_acl_and_cache(thd, lex->type, tables, &write_to_binlog)) + send_error(thd, 0); + else + { + /* + We WANT to write and we CAN write. + ! we write after unlocking the table. + */ + if (!lex->no_write_to_binlog && write_to_binlog) + { + mysql_update_log.write(thd, thd->query, thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, 0); + mysql_bin_log.write(&qinfo); + } + } + send_ok(thd); + } break; + } case SQLCOM_KILL: kill_one_thread(thd,lex->thread_id); break; @@ -2529,7 +3182,7 @@ mysql_execute_command(void) thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) | OPTION_BEGIN); thd->server_status|= SERVER_STATUS_IN_TRANS; - send_ok(&thd->net); + send_ok(thd); } break; case SQLCOM_COMMIT: @@ -2543,7 +3196,7 @@ mysql_execute_command(void) thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (!ha_commit(thd)) { - send_ok(&thd->net); + send_ok(thd); } else res= -1; @@ -2563,9 +3216,9 @@ mysql_execute_command(void) message in the error log, so we don't send it. */ if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread) - send_warning(&thd->net,ER_WARNING_NOT_COMPLETE_ROLLBACK,0); + send_warning(thd,ER_WARNING_NOT_COMPLETE_ROLLBACK,0); else - send_ok(&thd->net); + send_ok(thd); } else res= -1; @@ -2575,32 +3228,72 @@ mysql_execute_command(void) if (!ha_rollback_to_savepoint(thd, lex->savepoint_name)) { if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread) - send_warning(&thd->net,ER_WARNING_NOT_COMPLETE_ROLLBACK,0); + send_warning(thd, ER_WARNING_NOT_COMPLETE_ROLLBACK, 0); else - send_ok(&thd->net); + send_ok(thd); } else res= -1; break; case SQLCOM_SAVEPOINT: if (!ha_savepoint(thd, lex->savepoint_name)) - send_ok(&thd->net); + send_ok(thd); else res= -1; break; default: /* Impossible */ - send_ok(&thd->net); + send_ok(thd); break; } thd->proc_info="query end"; // QQ if (res < 0) - send_error(&thd->net,thd->killed ? ER_SERVER_SHUTDOWN : 0); + send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0); error: DBUG_VOID_RETURN; } +/* + Check grants for commands which work only with one table and all other + tables belong to subselects. + + SYNOPSYS + single_table_command_access() + thd - Thread handler + privilege - asked privelage + tables - table list of command + res - pointer on result code variable + + RETURN + 0 - OK + 1 - access denied +*/ + +static bool single_table_command_access(THD *thd, ulong privilege, + TABLE_LIST *tables, int *res) + +{ + if (check_access(thd, privilege, tables->db, &tables->grant.privilege)) + return 1; + + // Show only 1 table for check_grant + TABLE_LIST *subselects_tables= tables->next; + tables->next= 0; + if (grant_option && check_grant(thd, privilege, tables)) + return 1; + + // check rights on tables of subselect (if exists) + if (subselects_tables) + { + tables->next= subselects_tables; + if ((*res= check_table_access(thd, SELECT_ACL, subselects_tables))) + return 1; + } + return 0; +} + + /**************************************************************************** Get the user (global) and database privileges for all used tables @@ -2636,7 +3329,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if ((!db || !db[0]) && !thd->db && !dont_check_global_grants) { if (!no_errors) - send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: tested */ + send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */ DBUG_RETURN(TRUE); /* purecov: tested */ } @@ -2651,7 +3344,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if (!(thd->master_access & SELECT_ACL) && (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 */ + thd->priv_user, db, test(want_access & GRANT_ACL)); *save_priv=thd->master_access | db_access; DBUG_RETURN(FALSE); } @@ -2659,7 +3352,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, ! db && dont_check_global_grants) { // We can never grant this if (!no_errors) - net_printf(&thd->net,ER_ACCESS_DENIED_ERROR, + net_printf(thd,ER_ACCESS_DENIED_ERROR, thd->priv_user, thd->priv_host, thd->password ? ER(ER_YES) : ER(ER_NO));/* purecov: tested */ @@ -2671,7 +3364,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, 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 */ + thd->priv_user, db, test(want_access & GRANT_ACL)); else db_access=thd->db_access; // Remove SHOW attribute and access rights we already have @@ -2684,7 +3377,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, !(want_access & ~TABLE_ACLS))) DBUG_RETURN(FALSE); /* Ok */ if (!no_errors) - net_printf(&thd->net,ER_DBACCESS_DENIED_ERROR, + net_printf(thd,ER_DBACCESS_DENIED_ERROR, thd->priv_user, thd->priv_host, db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */ @@ -2717,7 +3410,7 @@ bool check_global_access(THD *thd, ulong want_access) if ((thd->master_access & want_access)) return 0; get_privilege_desc(command, sizeof(command), want_access); - net_printf(&thd->net,ER_SPECIFIC_ACCESS_DENIED_ERROR, + net_printf(thd,ER_SPECIFIC_ACCESS_DENIED_ERROR, command); return 1; } @@ -2737,6 +3430,8 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, TABLE_LIST *org_tables=tables; for (; tables ; tables=tables->next) { + if (tables->derived || (tables->table && (int)tables->table->tmp_table)) + continue; if ((thd->master_access & want_access) == (want_access & ~EXTRA_ACL) && thd->db) tables->grant.privilege= want_access; @@ -2772,7 +3467,7 @@ static bool check_db_used(THD *thd,TABLE_LIST *tables) { if (!(tables->db=thd->db)) { - send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: tested */ + send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */ return TRUE; /* purecov: tested */ } } @@ -2793,11 +3488,6 @@ static bool check_merge_table_access(THD *thd, char *db, { if (!tmp->db || !tmp->db[0]) tmp->db=db; - else if (strcmp(tmp->db,db)) - { - send_error(&thd->net,ER_UNION_TABLES_IN_DIFFERENT_DIR); - return 1; - } } error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL, table_list); @@ -2825,7 +3515,7 @@ bool check_stack_overrun(THD *thd,char *buf __attribute__((unused))) { sprintf(errbuff[0],ER(ER_STACK_OVERRUN),stack_used,thread_stack); my_message(ER_STACK_OVERRUN,errbuff[0],MYF(0)); - thd->fatal_error=1; + thd->fatal_error(); return 1; } return 0; @@ -2865,108 +3555,199 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, int *yystacksize) /**************************************************************************** - Initialize global thd variables needed for query + Initialize global thd variables needed for query ****************************************************************************/ -static void +void mysql_init_query(THD *thd) { DBUG_ENTER("mysql_init_query"); - thd->lex.select_lex.item_list.empty(); - thd->lex.value_list.empty(); - thd->lex.select_lex.table_list.elements=0; - thd->free_list=0; thd->lex.union_option=0; - thd->lex.select = &thd->lex.select_lex; - thd->lex.select_lex.table_list.first=0; - thd->lex.select_lex.table_list.next= (byte**) &thd->lex.select_lex.table_list.first; - thd->lex.select_lex.next=0; - thd->lex.olap=0; - thd->lex.select->olap= UNSPECIFIED_OLAP_TYPE; - thd->fatal_error=0; // Safety - thd->last_insert_id_used=thd->query_start_used=thd->insert_id_used=0; - thd->rand_used=0; - thd->sent_row_count=thd->examined_row_count=0; - thd->safe_to_cache_query=1; + LEX *lex=&thd->lex; + lex->unit.init_query(); + lex->unit.init_select(); + lex->unit.thd= thd; + lex->select_lex.init_query(); + lex->value_list.empty(); + lex->param_list.empty(); + lex->unit.next= lex->unit.master= + lex->unit.link_next= lex->unit.return_to=0; + lex->unit.prev= lex->unit.link_prev= 0; + lex->unit.slave= lex->unit.global_parameters= lex->current_select= + lex->all_selects_list= &lex->select_lex; + lex->select_lex.master= &lex->unit; + lex->select_lex.prev= &lex->unit.slave; + lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0; + lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list); + lex->describe= 0; + lex->derived_tables= FALSE; + lex->lock_option= TL_READ; + lex->found_colon= 0; + lex->safe_to_cache_query= 1; + thd->select_number= lex->select_lex.select_number= 1; + thd->free_list= 0; + thd->total_warn_count=0; // Warnings for this query + thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0; + thd->sent_row_count= thd->examined_row_count= 0; + thd->is_fatal_error= thd->rand_used= 0; + thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS; + thd->tmp_table_used= 0; + if (opt_bin_log) + reset_dynamic(&thd->user_var_events); + thd->clear_error(); DBUG_VOID_RETURN; } + void mysql_init_select(LEX *lex) { - SELECT_LEX *select_lex = lex->select; - select_lex->where=select_lex->having=0; + SELECT_LEX *select_lex= lex->current_select; + select_lex->init_select(); select_lex->select_limit= lex->thd->variables.select_limit; - select_lex->offset_limit=0; - select_lex->options=0; - select_lex->linkage=UNSPECIFIED_TYPE; - select_lex->olap= UNSPECIFIED_OLAP_TYPE; - lex->exchange = 0; - lex->proc_list.first=0; - select_lex->order_list.empty(); - select_lex->group_list.empty(); - select_lex->next = (SELECT_LEX *)NULL; + if (select_lex == &lex->select_lex) + { + lex->exchange= 0; + lex->result= 0; + lex->proc_list.first= 0; + } } bool -mysql_new_select(LEX *lex) +mysql_new_select(LEX *lex, bool move_down) { - SELECT_LEX *select_lex = (SELECT_LEX *) lex->thd->calloc(sizeof(SELECT_LEX)); + SELECT_LEX *select_lex = new(&lex->thd->mem_root) SELECT_LEX(); if (!select_lex) return 1; - lex->select->next=select_lex; - lex->select=select_lex; - select_lex->table_list.next= (byte**) &select_lex->table_list.first; - select_lex->item_list.empty(); - select_lex->when_list.empty(); - select_lex->expr_list.empty(); - select_lex->interval_list.empty(); - select_lex->use_index.empty(); - select_lex->ftfunc_list.empty(); + select_lex->select_number= ++lex->thd->select_number; + select_lex->init_query(); + select_lex->init_select(); + if (move_down) + { + /* first select_lex of subselect or derived table */ + SELECT_LEX_UNIT *unit= new(&lex->thd->mem_root) SELECT_LEX_UNIT(); + if (!unit) + return 1; + unit->init_query(); + unit->init_select(); + unit->thd= lex->thd; + unit->include_down(lex->current_select); + unit->link_next= 0; + unit->link_prev= 0; + unit->return_to= lex->current_select; + select_lex->include_down(unit); + // TODO: assign resolve_mode for fake subquery after merging with new tree + } + else + { + select_lex->include_neighbour(lex->current_select); + SELECT_LEX_UNIT *unit= select_lex->master_unit(); + SELECT_LEX *fake= unit->fake_select_lex; + if (!fake) + { + /* + as far as we included SELECT_LEX for UNION unit should have + fake SELECT_LEX for UNION processing + */ + fake= unit->fake_select_lex= new(&lex->thd->mem_root) SELECT_LEX(); + fake->include_standalone(unit, + (SELECT_LEX_NODE**)&unit->fake_select_lex); + fake->select_number= INT_MAX; + fake->make_empty_select(); + fake->linkage= GLOBAL_OPTIONS_TYPE; + fake->select_limit= lex->thd->variables.select_limit; + } + } + + select_lex->master_unit()->global_parameters= select_lex; + select_lex->include_global((st_select_lex_node**)&lex->all_selects_list); + lex->current_select= select_lex; + select_lex->resolve_mode= SELECT_LEX::SELECT_MODE; return 0; } +/* + Create a select to return the same output as 'SELECT @@var_name'. + + SYNOPSIS + create_select_for_variable() + var_name Variable name + + DESCRIPTION + Used for SHOW COUNT(*) [ WARNINGS | ERROR] + + This will crash with a core dump if the variable doesn't exists +*/ + +void create_select_for_variable(const char *var_name) +{ + THD *thd; + LEX *lex; + LEX_STRING tmp, null_lex_string; + DBUG_ENTER("create_select_for_variable"); + + thd= current_thd; + lex= &thd->lex; + mysql_init_select(lex); + lex->sql_command= SQLCOM_SELECT; + tmp.str= (char*) var_name; + tmp.length=strlen(var_name); + bzero((char*) &null_lex_string.str, sizeof(null_lex_string)); + add_item_to_list(thd, get_system_var(thd, OPT_SESSION, tmp, + null_lex_string)); + DBUG_VOID_RETURN; +} + void mysql_init_multi_delete(LEX *lex) { - lex->sql_command = SQLCOM_DELETE_MULTI; + lex->sql_command= SQLCOM_DELETE_MULTI; mysql_init_select(lex); - lex->select->select_limit=lex->thd->select_limit=HA_POS_ERROR; - lex->select->table_list.save_and_clear(&lex->auxilliary_table_list); + lex->select_lex.select_limit= lex->unit.select_limit_cnt= + HA_POS_ERROR; + lex->select_lex.table_list.save_and_clear(&lex->auxilliary_table_list); } void -mysql_parse(THD *thd,char *inBuf,uint length) +mysql_parse(THD *thd, char *inBuf, uint length) { DBUG_ENTER("mysql_parse"); mysql_init_query(thd); - thd->query_length = length; if (query_cache_send_result_to_client(thd, inBuf, length) <= 0) { LEX *lex=lex_start(thd, (uchar*) inBuf, length); - if (!yyparse() && ! thd->fatal_error) + if (!yyparse((void *)thd) && ! thd->is_fatal_error) { if (mqh_used && thd->user_connect && - check_mqh(thd, thd->lex.sql_command)) + check_mqh(thd, lex->sql_command)) { thd->net.error = 0; } else { - mysql_execute_command(); - query_cache_end_of_result(&thd->net); + if (thd->net.report_error) + send_error(thd, 0, NullS); + else + { + mysql_execute_command(thd); +#ifndef EMBEDDED_LIBRARY /* TODO query cache in embedded library*/ + query_cache_end_of_result(&thd->net); +#endif + } } } else { DBUG_PRINT("info",("Command aborted. Fatal_error: %d", - thd->fatal_error)); + thd->is_fatal_error)); +#ifndef EMBEDDED_LIBRARY /* TODO query cache in embedded library*/ query_cache_abort(&thd->net); +#endif } thd->proc_info="freeing items"; - free_items(thd); /* Free strings used by items */ + free_items(thd->free_list); /* Free strings used by items */ lex_end(lex); } DBUG_VOID_RETURN; @@ -2978,33 +3759,35 @@ mysql_parse(THD *thd,char *inBuf,uint length) ** Return 0 if ok ******************************************************************************/ -bool add_field_to_list(char *field_name, enum_field_types type, +bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, char *length, char *decimals, - uint type_modifier, Item *default_value,char *change, - TYPELIB *interval) + uint type_modifier, + Item *default_value, Item *comment, + char *change, TYPELIB *interval, CHARSET_INFO *cs, + uint uint_geom_type) { register create_field *new_field; - THD *thd=current_thd; LEX *lex= &thd->lex; uint allowed_type_modifier=0; + char warn_buff[MYSQL_ERRMSG_SIZE]; DBUG_ENTER("add_field_to_list"); if (strlen(field_name) > NAME_LEN) { - net_printf(&thd->net, ER_TOO_LONG_IDENT, field_name); /* purecov: inspected */ + net_printf(thd, 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->key_list.push_back(new Key(Key::PRIMARY, NullS, HA_KEY_ALG_UNDEF, 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->key_list.push_back(new Key(Key::UNIQUE, NullS, HA_KEY_ALG_UNDEF, lex->col_list)); lex->col_list.empty(); } @@ -3017,27 +3800,21 @@ bool add_field_to_list(char *field_name, enum_field_types type, if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG) { - net_printf(&thd->net,ER_INVALID_DEFAULT,field_name); + net_printf(thd,ER_INVALID_DEFAULT,field_name); DBUG_RETURN(1); } } -#ifdef MYSQL41000 else if (type_modifier & AUTO_INCREMENT_FLAG) { - net_printf(&thd->net, ER_INVALID_DEFAULT, field_name); + net_printf(thd, ER_INVALID_DEFAULT, field_name); DBUG_RETURN(1); } -#endif } if (!(new_field=new create_field())) DBUG_RETURN(1); new_field->field=0; new_field->field_name=field_name; -#ifdef MYSQL41000 new_field->def= default_value; -#else - new_field->def= (type_modifier & AUTO_INCREMENT_FLAG ? 0 : default_value); -#endif new_field->flags= type_modifier; new_field->unireg_check= (type_modifier & AUTO_INCREMENT_FLAG ? Field::NEXT_NUMBER : Field::NONE); @@ -3048,6 +3825,20 @@ bool add_field_to_list(char *field_name, enum_field_types type, new_field->change=change; new_field->interval=0; new_field->pack_length=0; + new_field->charset=cs; + new_field->geom_type= (Field::geometry_type) uint_geom_type; + + if (!comment) + { + new_field->comment.str=0; + new_field->comment.length=0; + } + else + { + /* In this case comment is always of type Item_string */ + new_field->comment.str= (char*) comment->str_value.ptr(); + new_field->comment.length=comment->str_value.length(); + } if (length && !(new_field->length= (uint) atoi(length))) length=0; /* purecov: inspected */ uint sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1; @@ -3078,10 +3869,7 @@ bool add_field_to_list(char *field_name, enum_field_types type, 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: - case FIELD_TYPE_GEOMETRY: break; case FIELD_TYPE_DECIMAL: if (!length) @@ -3093,17 +3881,43 @@ bool add_field_to_list(char *field_name, enum_field_types type, new_field->length++; } break; + case FIELD_TYPE_STRING: + case FIELD_TYPE_VAR_STRING: + if (new_field->length <= MAX_FIELD_CHARLENGTH || default_value) + break; + /* Convert long CHAR() and VARCHAR columns to TEXT or BLOB */ + new_field->sql_type= FIELD_TYPE_BLOB; + sprintf(warn_buff, ER(ER_AUTO_CONVERT), field_name, "CHAR", + (cs == &my_charset_bin) ? "BLOB" : "TEXT"); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_AUTO_CONVERT, + warn_buff); + /* fall through */ case FIELD_TYPE_BLOB: case FIELD_TYPE_TINY_BLOB: case FIELD_TYPE_LONG_BLOB: case FIELD_TYPE_MEDIUM_BLOB: + case FIELD_TYPE_GEOMETRY: + if (new_field->length) + { + /* The user has given a length to the blob column */ + if (new_field->length < 256) + type= FIELD_TYPE_TINY_BLOB; + if (new_field->length < 65536) + type= FIELD_TYPE_BLOB; + else if (new_field->length < 256L*256L*256L) + type= FIELD_TYPE_MEDIUM_BLOB; + else + type= FIELD_TYPE_LONG_BLOB; + new_field->length= 0; + } + new_field->sql_type= type; 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 */ + net_printf(thd,ER_BLOB_CANT_HAVE_DEFAULT,field_name); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */ } new_field->def=0; @@ -3123,7 +3937,7 @@ bool add_field_to_list(char *field_name, enum_field_types type, uint tmp_length=new_field->length; if (tmp_length > PRECISION_FOR_DOUBLE) { - net_printf(&thd->net,ER_WRONG_FIELD_SPEC,field_name); + net_printf(thd,ER_WRONG_FIELD_SPEC,field_name); DBUG_RETURN(1); } else if (tmp_length > PRECISION_FOR_FLOAT) @@ -3177,7 +3991,7 @@ bool add_field_to_list(char *field_name, enum_field_types type, { if (interval->count > sizeof(longlong)*8) { - net_printf(&thd->net,ER_TOO_BIG_SET,field_name); /* purecov: inspected */ + net_printf(thd,ER_TOO_BIG_SET,field_name); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */ } new_field->pack_length=(interval->count+7)/8; @@ -3193,13 +4007,18 @@ bool add_field_to_list(char *field_name, enum_field_types type, set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1); if (default_value) { + char *not_used; + uint not_used2; + bool not_used3; + thd->cuted_fields=0; String str,*res; res=default_value->val_str(&str); - (void) find_set(interval,res->ptr(),res->length()); + (void) find_set(interval, res->ptr(), res->length(), ¬_used, + ¬_used2, ¬_used3); if (thd->cuted_fields) { - net_printf(&thd->net,ER_INVALID_DEFAULT,field_name); + net_printf(thd,ER_INVALID_DEFAULT,field_name); DBUG_RETURN(1); } } @@ -3222,7 +4041,7 @@ bool add_field_to_list(char *field_name, enum_field_types type, res=default_value->val_str(&str); if (!find_enum(interval,res->ptr(),res->length())) { - net_printf(&thd->net,ER_INVALID_DEFAULT,field_name); + net_printf(thd,ER_INVALID_DEFAULT,field_name); DBUG_RETURN(1); } } @@ -3230,18 +4049,20 @@ bool add_field_to_list(char *field_name, enum_field_types type, } } - if (new_field->length >= MAX_FIELD_WIDTH || + if ((new_field->length > MAX_FIELD_CHARLENGTH && type != FIELD_TYPE_SET && + type != FIELD_TYPE_ENUM) || (!new_field->length && !(new_field->flags & BLOB_FLAG) && - type != FIELD_TYPE_STRING && type != FIELD_TYPE_VAR_STRING)) + type != FIELD_TYPE_STRING && + type != FIELD_TYPE_VAR_STRING && type != FIELD_TYPE_GEOMETRY)) { - net_printf(&thd->net,ER_TOO_BIG_FIELDLENGTH,field_name, - MAX_FIELD_WIDTH-1); /* purecov: inspected */ + net_printf(thd,ER_TOO_BIG_FIELDLENGTH,field_name, + MAX_FIELD_CHARLENGTH); /* 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); + net_printf(thd,ER_WRONG_FIELD_SPEC,field_name); DBUG_RETURN(1); } if (!new_field->pack_length) @@ -3268,7 +4089,7 @@ add_proc_to_list(THD* thd, Item *item) ORDER *order; Item **item_ptr; - if (!(order = (ORDER *) sql_alloc(sizeof(ORDER)+sizeof(Item*)))) + if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*)))) return 1; item_ptr = (Item**) (order+1); *item_ptr= item; @@ -3294,8 +4115,8 @@ static void remove_escape(char *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))) + if (use_mb(system_charset_info) && + (l = my_ismbchar(system_charset_info, name, strend))) { while (l--) *to++ = *name++; @@ -3315,12 +4136,12 @@ static void remove_escape(char *name) ****************************************************************************/ -bool add_to_list(SQL_LIST &list,Item *item,bool asc) +bool add_to_list(THD *thd, 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*)))) + if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*)))) DBUG_RETURN(1); item_ptr = (Item**) (order+1); *item_ptr=item; @@ -3352,14 +4173,15 @@ bool add_to_list(SQL_LIST &list,Item *item,bool asc) # Pointer to TABLE_LIST element added to the total table list */ -TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, - ulong table_options, - thr_lock_type lock_type, - List<String> *use_index, - List<String> *ignore_index) +TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, + Table_ident *table, + LEX_STRING *alias, + ulong table_options, + thr_lock_type lock_type, + List<String> *use_index, + List<String> *ignore_index) { register TABLE_LIST *ptr; - THD *thd=current_thd; char *alias_str; DBUG_ENTER("add_table_to_list"); @@ -3369,17 +4191,23 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, if (check_table_name(table->table.str,table->table.length) || table->db.str && check_db_name(table->db.str)) { - net_printf(&thd->net,ER_WRONG_TABLE_NAME,table->table.str); + net_printf(thd,ER_WRONG_TABLE_NAME,table->table.str); DBUG_RETURN(0); } if (!alias) /* Alias is case sensitive */ + { + if (table->sel) + { + net_printf(thd,ER_DERIVED_MUST_HAVE_ALIAS); + DBUG_RETURN(0); + } if (!(alias_str=thd->memdup(alias_str,table->table.length+1))) DBUG_RETURN(0); - + } if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST)))) DBUG_RETURN(0); /* purecov: inspected */ - if (table->db.str) + if (table->db.str) { ptr->db= table->db.str; ptr->db_length= table->db.length; @@ -3391,17 +4219,21 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, } else { - ptr->db= (char*) ""; + /* The following can't be "" as we may do 'casedn_str()' on it */ + ptr->db= empty_c_string; ptr->db_length= 0; } - + ptr->alias= alias_str; - table_case_convert(table->table.str, table->table.length); + if (lower_case_table_names && table->table.length) + my_casedn_str(files_charset_info, table->table.str); ptr->real_name=table->table.str; ptr->real_name_length=table->table.length; - ptr->lock_type= lock_type; + ptr->lock_type= lock_type; ptr->updating= test(table_options & TL_OPTION_UPDATING); ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX); + ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES); + ptr->derived= table->sel; if (use_index) ptr->use_index=(List<String> *) thd->memdup((gptr) use_index, sizeof(*use_index)); @@ -3412,21 +4244,22 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, /* check that used name is unique */ if (lock_type != TL_IGNORE) { - for (TABLE_LIST *tables=(TABLE_LIST*) thd->lex.select->table_list.first ; + for (TABLE_LIST *tables=(TABLE_LIST*) table_list.first ; tables ; tables=tables->next) { if (!strcmp(alias_str,tables->alias) && !strcmp(ptr->db, tables->db)) { - net_printf(&thd->net,ER_NONUNIQ_TABLE,alias_str); /* purecov: tested */ + net_printf(thd,ER_NONUNIQ_TABLE,alias_str); /* purecov: tested */ DBUG_RETURN(0); /* purecov: tested */ } } } - thd->lex.select->table_list.link_in_list((byte*) ptr,(byte**) &ptr->next); + table_list.link_in_list((byte*) ptr, (byte**) &ptr->next); DBUG_RETURN(ptr); } + /* Set lock for all tables in current select level @@ -3440,15 +4273,14 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, query */ -void set_lock_for_tables(thr_lock_type lock_type) +void st_select_lex::set_lock_for_tables(thr_lock_type lock_type) { - THD *thd=current_thd; bool for_update= lock_type >= TL_READ_NO_INSERT; DBUG_ENTER("set_lock_for_tables"); DBUG_PRINT("enter", ("lock_type: %d for_update: %d", lock_type, for_update)); - for (TABLE_LIST *tables= (TABLE_LIST*) thd->lex.select->table_list.first ; + for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first ; tables ; tables=tables->next) { @@ -3459,67 +4291,6 @@ void set_lock_for_tables(thr_lock_type lock_type) } -/* -** This is used for UNION to create a new table list of all used tables -** The table_list->table entry in all used tables are set to point -** to the entries in this list. -*/ - -static bool create_total_list(THD *thd, LEX *lex, TABLE_LIST **result) -{ - /* Handle the case when we are not using union */ - if (!lex->select_lex.next) - { - *result= (TABLE_LIST*) lex->select_lex.table_list.first; - return 0; - } - - SELECT_LEX *sl; - TABLE_LIST **new_table_list= result, *aux; - - *new_table_list=0; // end result list - for (sl= &lex->select_lex; sl; sl=sl->next) - { - if (sl->order_list.first && sl->next && !sl->braces) - { - net_printf(&thd->net,ER_WRONG_USAGE,"UNION","ORDER BY"); - return 1; - } - if ((aux= (TABLE_LIST*) sl->table_list.first)) - { - TABLE_LIST *next; - for (; aux; aux=next) - { - TABLE_LIST *cursor; - next= aux->next; - for (cursor= *result; cursor; cursor=cursor->next) - if (!strcmp(cursor->db,aux->db) && - !strcmp(cursor->real_name,aux->real_name) && - !strcmp(cursor->alias, aux->alias)) - break; - if (!cursor) - { - /* Add not used table to the total table list */ - if (!(cursor = (TABLE_LIST *) thd->memdup((char*) aux, - sizeof(*aux)))) - { - send_error(&thd->net,0); - return 1; - } - *new_table_list= cursor; - new_table_list= &cursor->next; - *new_table_list=0; // end result list - } - else - aux->shared=1; // Mark that it's used twice - aux->table= my_reinterpret_cast(TABLE *) (cursor); - } - } - } - return 0; -} - - void add_join_on(TABLE_LIST *b,Item *expr) { if (expr) @@ -3559,26 +4330,33 @@ 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(const char *db, const char *name, TABLE_LIST *tables) -{ - for (; tables ; tables=tables->next) - if (!strcmp(name,tables->real_name) && !strcmp(db,tables->db)) - return 1; - return 0; -} +/* + Reload/resets privileges and the different caches. + SYNOPSIS + reload_acl_and_cache() + thd Thread handler + options What should be reset/reloaded (tables, privileges, + slave...) + tables Tables to flush (if any) + write_to_binlog Depending on 'options', it may be very bad to write the + query to the binlog (e.g. FLUSH SLAVE); this is a + pointer where, if it is not NULL, reload_acl_and_cache() + will put 0 if it thinks we really should not write to + the binlog. Otherwise it will put 1. -/* - Reload/resets privileges and the different caches + RETURN + 0 ok + !=0 error */ -bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables) +bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, + bool *write_to_binlog) { bool result=0; - bool error_already_sent=0; select_errors=0; /* Write if more errors */ + bool tmp_write_to_binlog= 1; if (options & REFRESH_GRANT) { acl_reload(thd); @@ -3592,14 +4370,28 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables) Flush the normal query log, the update log, the binary log, the slow query log, and the relay log (if it exists). */ + + /* + Writing this command to the binlog may result in infinite loops when doing + mysqlbinlog|mysql, and anyway it does not really make sense to log it + automatically (would cause more trouble to users than it would help them) + */ + tmp_write_to_binlog= 0; mysql_log.new_file(1); mysql_update_log.new_file(1); mysql_bin_log.new_file(1); mysql_slow_log.new_file(1); +#ifdef HAVE_REPLICATION + if (expire_logs_days) + { + long purge_time= time(0) - expire_logs_days*24*60*60; + if (purge_time >= 0) + mysql_bin_log.purge_logs_before_date(purge_time); + } LOCK_ACTIVE_MI; rotate_relay_log(active_mi); UNLOCK_ACTIVE_MI; - +#endif if (ha_flush_logs()) result=1; if (flush_error_log()) @@ -3616,10 +4408,16 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables) query_cache.flush(); // RESET QUERY CACHE } #endif /*HAVE_QUERY_CACHE*/ - if (options & (REFRESH_TABLES | REFRESH_READ_LOCK)) + /* + Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too + (see sql_yacc.yy) + */ + if (options & (REFRESH_TABLES | REFRESH_READ_LOCK)) { if ((options & REFRESH_READ_LOCK) && thd) { + // writing to the binlog could cause deadlocks, as we don't log UNLOCK TABLES + tmp_write_to_binlog= 0; if (lock_global_read_lock(thd)) return 1; } @@ -3631,9 +4429,14 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables) refresh_status(); if (options & REFRESH_THREADS) flush_thread_cache(); +#ifndef EMBEDDED_LIBRARY if (options & REFRESH_MASTER) + { + tmp_write_to_binlog= 0; if (reset_master(thd)) result=1; + } +#endif #ifdef OPENSSL if (options & REFRESH_DES_KEY_FILE) { @@ -3641,33 +4444,20 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables) result=load_des_key_file(des_key_file); } #endif +#ifndef EMBEDDED_LIBRARY if (options & REFRESH_SLAVE) { + tmp_write_to_binlog= 0; LOCK_ACTIVE_MI; if (reset_slave(thd, active_mi)) - { result=1; - /* - reset_slave() sends error itself. - If it didn't, one would either change reset_slave()'s prototype, to - pass *errorcode and *errmsg to it when it's called or - change reset_slave to use my_error() to register the error. - */ - error_already_sent=1; - } UNLOCK_ACTIVE_MI; } +#endif if (options & REFRESH_USER_RESOURCES) reset_mqh(thd,(LEX_USER *) NULL); - - if (thd && !error_already_sent) - { - if (result) - send_error(&thd->net,0); - else - send_ok(&thd->net); - } - + if (write_to_binlog) + *write_to_binlog= tmp_write_to_binlog; return result; } @@ -3713,9 +4503,9 @@ void kill_one_thread(THD *thd, ulong id) } if (!error) - send_ok(&thd->net); + send_ok(thd); else - net_printf(&thd->net,error,id); + net_printf(thd,error,id); } /* Clear most status variables */ @@ -3759,6 +4549,7 @@ static bool append_file_to_dir(THD *thd, char **filename_ptr, char *table_name) return 0; } + /* Check if the select is a simple select (not an union) @@ -3773,13 +4564,43 @@ static bool append_file_to_dir(THD *thd, char **filename_ptr, char *table_name) bool check_simple_select() { THD *thd= current_thd; - if (thd->lex.select != &thd->lex.select_lex) + if (thd->lex.current_select != &thd->lex.select_lex) { char command[80]; strmake(command, thd->lex.yylval->symbol.str, min(thd->lex.yylval->symbol.length, sizeof(command)-1)); - net_printf(&thd->net, ER_CANT_USE_OPTION_HERE, command); + net_printf(thd, ER_CANT_USE_OPTION_HERE, command); return 1; } return 0; } + +compare_func_creator comp_eq_creator(bool invert) +{ + return invert?&Item_bool_func2::ne_creator:&Item_bool_func2::eq_creator; +} + +compare_func_creator comp_ge_creator(bool invert) +{ + return invert?&Item_bool_func2::lt_creator:&Item_bool_func2::ge_creator; +} + +compare_func_creator comp_gt_creator(bool invert) +{ + return invert?&Item_bool_func2::le_creator:&Item_bool_func2::gt_creator; +} + +compare_func_creator comp_le_creator(bool invert) +{ + return invert?&Item_bool_func2::gt_creator:&Item_bool_func2::le_creator; +} + +compare_func_creator comp_lt_creator(bool invert) +{ + return invert?&Item_bool_func2::ge_creator:&Item_bool_func2::lt_creator; +} + +compare_func_creator comp_ne_creator(bool invert) +{ + return invert?&Item_bool_func2::eq_creator:&Item_bool_func2::ne_creator; +} |