diff options
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r-- | sql/sql_parse.cc | 3551 |
1 files changed, 2438 insertions, 1113 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 8786304f893..5a06364cd22 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -25,6 +25,9 @@ #include "ha_innodb.h" #endif +#include "sp_head.h" +#include "sp.h" + #ifdef HAVE_OPENSSL /* Without SSL the handshake consists of one packet. This packet @@ -43,6 +46,16 @@ #define MIN_HANDSHAKE_SIZE 6 #endif /* HAVE_OPENSSL */ +/* Used in error handling only */ +#define SP_TYPE_STRING(LP) \ + ((LP)->sphead->m_type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE") +#define SP_COM_STRING(LP) \ + ((LP)->sql_command == SQLCOM_CREATE_SPFUNCTION || \ + (LP)->sql_command == SQLCOM_ALTER_FUNCTION || \ + (LP)->sql_command == SQLCOM_SHOW_CREATE_FUNC || \ + (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \ + "FUNCTION" : "PROCEDURE") + #ifdef SOLARIS extern "C" int gethostname(char *name, int namelen); #endif @@ -53,8 +66,7 @@ static int check_for_max_user_connections(THD *thd, USER_CONN *uc); #endif static void decrease_user_connections(USER_CONN *uc); static bool check_db_used(THD *thd,TABLE_LIST *tables); -static bool check_multi_update_lock(THD *thd, TABLE_LIST *tables, - List<Item> *fields, SELECT_LEX *select_lex); +static bool check_multi_update_lock(THD *thd); static void remove_escape(char *name); static void refresh_status(void); static bool append_file_to_dir(THD *thd, const char **filename_ptr, @@ -69,10 +81,14 @@ const char *command_name[]={ "Connect","Kill","Debug","Ping","Time","Delayed insert","Change user", "Binlog Dump","Table Dump", "Connect Out", "Register Slave", "Prepare", "Execute", "Long Data", "Close stmt", - "Reset stmt", "Set option", + "Reset stmt", "Set option", "Fetch", "Error" // Last command number }; +const char *xa_state_names[]={ + "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED" +}; + static char empty_c_string[1]= {0}; // Used for not defined 'db' #ifdef __WIN__ @@ -99,7 +115,7 @@ static void unlock_locked_tables(THD *thd) if (thd->locked_tables) { thd->lock=thd->locked_tables; - thd->locked_tables=0; // Will be automaticly closed + thd->locked_tables=0; // Will be automatically closed close_thread_tables(thd); // Free tables } } @@ -108,17 +124,44 @@ static void unlock_locked_tables(THD *thd) static bool end_active_trans(THD *thd) { int error=0; + DBUG_ENTER("end_active_trans"); if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN | OPTION_TABLE_LOCK)) { - thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); + DBUG_PRINT("info",("options: 0x%lx", (ulong) thd->options)); + /* Safety if one did "drop table" on locked tables */ + if (!thd->locked_tables) + thd->options&= ~OPTION_TABLE_LOCK; thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (ha_commit(thd)) error=1; + thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); } - return error; + DBUG_RETURN(error); } +static bool begin_trans(THD *thd) +{ + int error=0; + if (thd->locked_tables) + { + thd->lock=thd->locked_tables; + thd->locked_tables=0; // Will be automatically closed + close_thread_tables(thd); // Free tables + } + if (end_active_trans(thd)) + error= -1; + else + { + LEX *lex= thd->lex; + thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) | + OPTION_BEGIN); + thd->server_status|= SERVER_STATUS_IN_TRANS; + if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) + error= ha_start_consistent_snapshot(thd); + } + return error; +} #ifdef HAVE_REPLICATION inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables) @@ -156,25 +199,21 @@ 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(thd, 0, NullS); // Out of memory + net_send_error(thd, 0, NullS); // Out of memory return_val=1; goto end; } uc->user=(char*) (uc+1); memcpy(uc->user,temp_user,temp_len+1); - uc->user_len= user_len; - uc->host=uc->user + uc->user_len + 1; + uc->host= uc->user + user_len + 1; uc->len = temp_len; - uc->connections = 1; - uc->questions=uc->updates=uc->conn_per_hour=0; + uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0; uc->user_resources=*mqh; - if (max_user_connections && mqh->connections > max_user_connections) - uc->user_resources.connections = max_user_connections; uc->intime=thd->thr_create_time; if (my_hash_insert(&hash_user_connections, (byte*) uc)) { my_free((char*) uc,0); - send_error(thd, 0, NullS); // Out of memory + net_send_error(thd, 0, NullS); // Out of memory return_val=1; goto end; } @@ -196,13 +235,13 @@ end: command originator of the check: now check_user is called during connect and change user procedures; used for logging. - passwd scrambled password recieved from client + passwd scrambled password received from client passwd_len length of scrambled password db database name to connect to, may be NULL check_count dont know exactly Note, that host, user and passwd may point to communication buffer. - Current implementation does not depened on that, but future changes + Current implementation does not depend on that, but future changes should be done with this in mind; 'thd' is INOUT, all other params are 'IN'. @@ -221,13 +260,15 @@ int check_user(THD *thd, enum enum_server_command command, #ifdef NO_EMBEDDED_ACCESS_CHECKS thd->master_access= GLOBAL_ACLS; // Full rights - /* Change database if necessary: OK or FAIL is sent in mysql_change_db */ + /* Change database if necessary */ if (db && db[0]) { thd->db= 0; thd->db_length= 0; if (mysql_change_db(thd, db)) { + /* Send the error to the client */ + net_send_error(thd); if (thd->user_connect) decrease_user_connections(thd->user_connect); DBUG_RETURN(-1); @@ -249,7 +290,7 @@ int check_user(THD *thd, enum enum_server_command command, */ if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323) { - net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE); + net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE); mysql_log.write(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); DBUG_RETURN(-1); } @@ -260,7 +301,7 @@ int check_user(THD *thd, enum enum_server_command command, /* Clear thd->db as it points to something, that will be freed when - connection is closed. We don't want to accidently free a wrong pointer + connection is closed. We don't want to accidentally free a wrong pointer if connect failed. Also in case of 'CHANGE USER' failure, current database will be switched to 'no database selected'. */ @@ -281,15 +322,16 @@ int check_user(THD *thd, enum enum_server_command command, NET *net= &thd->net; if (opt_secure_auth_local) { - net_printf(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE, - thd->user, thd->host_or_ip); + net_printf_error(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE, + thd->user, thd->host_or_ip); mysql_log.write(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE), thd->user, thd->host_or_ip); DBUG_RETURN(-1); } + /* We have to read very specific packet size */ if (send_old_password_request(thd) || - my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) // We have to read very - { // specific packet size + my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) + { inc_host_errors(&thd->remote.sin_addr); DBUG_RETURN(ER_HANDSHAKE_ERROR); } @@ -301,7 +343,7 @@ int check_user(THD *thd, enum enum_server_command command, /* here res is always >= 0 */ if (res == 0) { - if (!(thd->master_access & NO_ACCESS)) // authentification is OK + if (!(thd->master_access & NO_ACCESS)) // authentication is OK { DBUG_PRINT("info", ("Capabilities: %d packet_length: %ld Host: '%s' " @@ -315,12 +357,12 @@ int check_user(THD *thd, enum enum_server_command command, if (check_count) { VOID(pthread_mutex_lock(&LOCK_thread_count)); - bool count_ok= thread_count < max_connections + delayed_insert_threads + bool count_ok= thread_count <= max_connections + delayed_insert_threads || (thd->master_access & SUPER_ACL); VOID(pthread_mutex_unlock(&LOCK_thread_count)); if (!count_ok) - { // too many connections - send_error(thd, ER_CON_COUNT_ERROR); + { // too many connections + net_send_error(thd, ER_CON_COUNT_ERROR); DBUG_RETURN(-1); } } @@ -341,21 +383,27 @@ int check_user(THD *thd, enum enum_server_command command, thd->db_access=0; /* Don't allow user to connect if he has done too many queries */ - if ((ur.questions || ur.updates || ur.connections || + if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn || max_user_connections) && - get_or_create_user_conn(thd,thd->user,thd->host_or_ip,&ur)) + get_or_create_user_conn(thd, + opt_old_style_user_limits ? thd->user : thd->priv_user, + opt_old_style_user_limits ? thd->host_or_ip : thd->priv_host, + &ur)) DBUG_RETURN(-1); if (thd->user_connect && - (thd->user_connect->user_resources.connections || + (thd->user_connect->user_resources.conn_per_hour || + thd->user_connect->user_resources.user_conn || max_user_connections) && check_for_max_user_connections(thd, thd->user_connect)) DBUG_RETURN(-1); - /* Change database if necessary: OK or FAIL is sent in mysql_change_db */ + /* Change database if necessary */ if (db && db[0]) { if (mysql_change_db(thd, db)) { + /* Send error to the client */ + net_send_error(thd); if (thd->user_connect) decrease_user_connections(thd->user_connect); DBUG_RETURN(-1); @@ -370,14 +418,14 @@ int check_user(THD *thd, enum enum_server_command command, } else if (res == 2) // client gave short hash, server has long hash { - net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE); + net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE); mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE)); DBUG_RETURN(-1); } - net_printf(thd, ER_ACCESS_DENIED_ERROR, - thd->user, - thd->host_or_ip, - passwd_len ? ER(ER_YES) : ER(ER_NO)); + net_printf_error(thd, ER_ACCESS_DENIED_ERROR, + thd->user, + thd->host_or_ip, + passwd_len ? ER(ER_YES) : ER(ER_NO)); mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR), thd->user, thd->host_or_ip, @@ -437,20 +485,29 @@ static int check_for_max_user_connections(THD *thd, USER_CONN *uc) DBUG_ENTER("check_for_max_user_connections"); (void) pthread_mutex_lock(&LOCK_user_conn); - if (max_user_connections && + if (max_user_connections && !uc->user_resources.user_conn && max_user_connections < (uint) uc->connections) { - net_printf(thd,ER_TOO_MANY_USER_CONNECTIONS, uc->user); + net_printf_error(thd, ER_TOO_MANY_USER_CONNECTIONS, uc->user); error=1; goto end; } time_out_user_resource_limits(thd, uc); - if (uc->user_resources.connections && - uc->user_resources.connections <= uc->conn_per_hour) + if (uc->user_resources.user_conn && + uc->user_resources.user_conn < uc->connections) { - net_printf(thd, ER_USER_LIMIT_REACHED, uc->user, - "max_connections_per_hour", - (long) uc->user_resources.connections); + net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, + "max_user_connections", + (long) uc->user_resources.user_conn); + error= 1; + goto end; + } + if (uc->user_resources.conn_per_hour && + uc->user_resources.conn_per_hour <= uc->conn_per_hour) + { + net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, + "max_connections_per_hour", + (long) uc->user_resources.conn_per_hour); error=1; goto end; } @@ -509,6 +566,12 @@ void free_max_user_conn(void) sql_command is actually set to SQLCOM_END sometimes so we need the +1 to include it in the array. + + numbers are: + 0 - read-only query + != 0 - query that may change a table + 2 - query that returns meaningful ROW_COUNT() - + a number of modified rows */ char uc_update_queries[SQLCOM_END+1]; @@ -520,23 +583,25 @@ void init_update_queries(void) uc_update_queries[SQLCOM_CREATE_TABLE]=1; uc_update_queries[SQLCOM_CREATE_INDEX]=1; uc_update_queries[SQLCOM_ALTER_TABLE]=1; - uc_update_queries[SQLCOM_UPDATE]=1; - uc_update_queries[SQLCOM_INSERT]=1; - uc_update_queries[SQLCOM_INSERT_SELECT]=1; - uc_update_queries[SQLCOM_DELETE]=1; + uc_update_queries[SQLCOM_UPDATE]=2; + uc_update_queries[SQLCOM_UPDATE_MULTI]=2; + uc_update_queries[SQLCOM_INSERT]=2; + uc_update_queries[SQLCOM_INSERT_SELECT]=2; + uc_update_queries[SQLCOM_DELETE]=2; + uc_update_queries[SQLCOM_DELETE_MULTI]=2; uc_update_queries[SQLCOM_TRUNCATE]=1; uc_update_queries[SQLCOM_DROP_TABLE]=1; uc_update_queries[SQLCOM_LOAD]=1; uc_update_queries[SQLCOM_CREATE_DB]=1; uc_update_queries[SQLCOM_DROP_DB]=1; - uc_update_queries[SQLCOM_REPLACE]=1; - uc_update_queries[SQLCOM_REPLACE_SELECT]=1; + uc_update_queries[SQLCOM_REPLACE]=2; + uc_update_queries[SQLCOM_REPLACE_SELECT]=2; uc_update_queries[SQLCOM_RENAME_TABLE]=1; uc_update_queries[SQLCOM_BACKUP_TABLE]=1; 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_UPDATE_MULTI]=1; + uc_update_queries[SQLCOM_CREATE_VIEW]=1; + uc_update_queries[SQLCOM_DROP_VIEW]=1; } bool is_update_query(enum enum_sql_command command) @@ -561,7 +626,6 @@ bool is_update_query(enum enum_sql_command command) static void time_out_user_resource_limits(THD *thd, USER_CONN *uc) { - bool error= 0; time_t check_time = thd->start_time ? thd->start_time : time(NULL); DBUG_ENTER("time_out_user_resource_limits"); @@ -600,8 +664,8 @@ static bool check_mqh(THD *thd, uint check_command) if (uc->user_resources.questions && uc->questions++ >= uc->user_resources.questions) { - net_printf(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions", - (long) uc->user_resources.questions); + net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions", + (long) uc->user_resources.questions); error=1; goto end; } @@ -611,8 +675,8 @@ 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, ER_USER_LIMIT_REACHED, uc->user, "max_updates", - (long) uc->user_resources.updates); + net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_updates", + (long) uc->user_resources.updates); error=1; goto end; } @@ -626,7 +690,7 @@ end: } -static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0) +static void reset_mqh(LEX_USER *lu, bool get_them= 0) { #ifndef NO_EMBEDDED_ACCESS_CHECKS (void) pthread_mutex_lock(&LOCK_user_conn); @@ -648,8 +712,9 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0) uc->conn_per_hour=0; } } - else // for FLUSH PRIVILEGES and FLUSH USER_RESOURCES + else { + /* for FLUSH PRIVILEGES and FLUSH USER_RESOURCES */ for (uint idx=0;idx < hash_user_connections.records; idx++) { USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections, @@ -750,7 +815,7 @@ static int check_connection(THD *thd) #endif /* HAVE_COMPRESS */ #ifdef HAVE_OPENSSL if (ssl_acceptor_fd) - client_flags |= CLIENT_SSL; /* Wow, SSL is avalaible! */ + client_flags |= CLIENT_SSL; /* Wow, SSL is available! */ #endif /* HAVE_OPENSSL */ end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1; @@ -798,17 +863,6 @@ static int check_connection(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; @@ -956,6 +1010,7 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var, */ save_vio= thd->net.vio; thd->net.vio= 0; + thd->net.no_send_error= 0; dispatch_command(COM_QUERY, thd, thd->query, thd->query_length+1); rw_unlock(var_mutex); thd->client_capabilities= save_client_capabilities; @@ -974,7 +1029,7 @@ pthread_handler_decl(handle_one_connection,arg) pthread_detach_this_thread(); #if !defined( __WIN__) && !defined(OS2) // Win32 calls this in pthread_create - // The following calls needs to be done before we call DBUG_ macros + /* The following calls needs to be done before we call DBUG_ macros */ if (!(test_flags & TEST_NO_THREADS) & my_thread_init()) { close_connection(thd, ER_OUT_OF_RESOURCES, 1); @@ -993,10 +1048,10 @@ pthread_handler_decl(handle_one_connection,arg) */ DBUG_PRINT("info", ("handle_one_connection called by thread %d\n", thd->thread_id)); - // now that we've called my_thread_init(), it is safe to call DBUG_* + /* now that we've called my_thread_init(), it is safe to call DBUG_* */ #if defined(__WIN__) - init_signals(); // IRENA; testing ? + init_signals(); #elif !defined(OS2) && !defined(__NETWARE__) sigset_t set; VOID(sigemptyset(&set)); // Get mask in use @@ -1015,11 +1070,12 @@ pthread_handler_decl(handle_one_connection,arg) int error; NET *net= &thd->net; thd->thread_stack= (char*) &thd; + net->no_send_error= 0; if ((error=check_connection(thd))) { // Wrong permissions if (error > 0) - net_printf(thd,error,thd->host_or_ip); + net_printf_error(thd, error, thd->host_or_ip); #ifdef __NT__ if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE) my_sleep(1000); /* must wait after eof() */ @@ -1037,32 +1093,39 @@ pthread_handler_decl(handle_one_connection,arg) thd->version= refresh_version; thd->proc_info= 0; + thd->command= COM_SLEEP; thd->set_time(); thd->init_for_queries(); + if (sys_init_connect.value_length && !(thd->master_access & SUPER_ACL)) { execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect); if (thd->query_error) - thd->killed= 1; + thd->killed= THD::KILL_CONNECTION; } - while (!net->error && net->vio != 0 && !thd->killed) + + thd->proc_info=0; + thd->set_time(); + thd->init_for_queries(); + while (!net->error && net->vio != 0 && + !(thd->killed == THD::KILL_CONNECTION)) { + net->no_send_error= 0; if (do_command(thd)) break; } if (thd->user_connect) decrease_user_connections(thd->user_connect); - free_root(thd->mem_root,MYF(0)); if (net->error && net->vio != 0 && net->report_error) { if (!thd->killed && thd->variables.log_warnings > 1) - sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), + sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), thd->thread_id,(thd->db ? thd->db : "unconnected"), thd->user ? thd->user : "unauthenticated", thd->host_or_ip, (net->last_errno ? ER(net->last_errno) : ER(ER_UNKNOWN_ERROR))); - send_error(thd,net->last_errno,NullS); + net_send_error(thd, net->last_errno, NullS); statistic_increment(aborted_threads,&LOCK_status); } else if (thd->killed) @@ -1128,25 +1191,26 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg) thd->init_for_queries(); while (fgets(buff, thd->net.max_packet, file)) { - ulong length= (ulong) strlen(buff); - while (buff[length-1] != '\n' && !feof(file)) - { - /* - We got only a part of the current string. Will try to increase - net buffer then read the rest of the current string. - */ - if (net_realloc(&(thd->net), 2 * thd->net.max_packet)) - { - send_error(thd, thd->net.last_errno, NullS); - thd->is_fatal_error= 1; - break; - } - buff= (char*) thd->net.buff; - fgets(buff + length, thd->net.max_packet - length, file); - length+= (ulong) strlen(buff + length); - } - if (thd->is_fatal_error) - break; + ulong length= (ulong) strlen(buff); + while (buff[length-1] != '\n' && !feof(file)) + { + /* + We got only a part of the current string. Will try to increase + net buffer then read the rest of the current string. + */ + if (net_realloc(&(thd->net), 2 * thd->net.max_packet)) + { + net_send_error(thd, ER_NET_PACKET_TOO_LARGE, NullS); + thd->fatal_error(); + break; + } + buff= (char*) thd->net.buff; + fgets(buff + length, thd->net.max_packet - length, file); + length+= (ulong) strlen(buff + length); + } + if (thd->is_fatal_error) + break; + while (length && (my_isspace(thd->charset(), buff[length-1]) || buff[length-1] == ';')) length--; @@ -1154,24 +1218,29 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg) thd->query_length=length; thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1); thd->query[length] = '\0'; - thd->query_id=query_id++; - if (mqh_used && thd->user_connect && check_mqh(thd, SQLCOM_END)) - { - thd->net.error = 0; - close_thread_tables(thd); // Free tables - free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); - break; - } + /* + We don't need to obtain LOCK_thread_count here because in bootstrap + mode we have only one thread. + */ + thd->query_id=next_query_id(); mysql_parse(thd,thd->query,length); close_thread_tables(thd); // Free tables if (thd->is_fatal_error) break; free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); +#ifdef USING_TRANSACTIONS free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC)); +#endif } /* thd->fatal_error should be set in case something went wrong */ end: + bootstrap_error= thd->is_fatal_error; + + net_end(&thd->net); + thd->cleanup(); + delete thd; + #ifndef EMBEDDED_LIBRARY (void) pthread_mutex_lock(&LOCK_thread_count); thread_count--; @@ -1180,23 +1249,31 @@ end: my_thread_end(); pthread_exit(0); #endif - DBUG_RETURN(0); // Never reached + DBUG_RETURN(0); } /* This works because items are allocated with sql_alloc() */ void free_items(Item *item) { - for (; item ; item=item->next) + Item *next; + DBUG_ENTER("free_items"); + for (; item ; item=next) + { + next=item->next; item->delete_self(); + } + DBUG_VOID_RETURN; } /* This works because items are allocated with sql_alloc() */ void cleanup_items(Item *item) { + DBUG_ENTER("cleanup_items"); for (; item ; item=item->next) item->cleanup(); + DBUG_VOID_RETURN; } int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) @@ -1208,19 +1285,19 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) db = (db && db[0]) ? db : thd->db; if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST)))) DBUG_RETURN(1); // out of memory - table_list->db = db; - table_list->real_name = table_list->alias = tbl_name; - table_list->lock_type = TL_READ_NO_INSERT; - table_list->next = 0; + table_list->db= db; + table_list->table_name= table_list->alias= tbl_name; + table_list->lock_type= TL_READ_NO_INSERT; + table_list->prev_global= &table_list; // can be removed after merge with 4.1 if (!db || check_db_name(db)) { - net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL"); + my_error(ER_WRONG_DB_NAME ,MYF(0), db ? db : "NULL"); goto err; } if (lower_case_table_names) my_casedn_str(files_charset_info, tbl_name); - remove_escape(table_list->real_name); + remove_escape(table_list->table_name); if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT))) DBUG_RETURN(1); @@ -1230,7 +1307,7 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) thd->free_list = 0; thd->query_length=(uint) strlen(tbl_name); thd->query = tbl_name; - if ((error = mysqld_dump_create_info(thd, table, -1))) + if ((error = mysqld_dump_create_info(thd, table_list, -1))) { my_error(ER_GET_ERRNO, MYF(0), my_errno); goto err; @@ -1240,10 +1317,71 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) my_error(ER_GET_ERRNO, MYF(0), error); err: - close_thread_tables(thd); DBUG_RETURN(error); } +/* + Ends the current transaction and (maybe) begin the next + + SYNOPSIS + end_trans() + thd Current thread + completion Completion type + + RETURN + 0 - OK +*/ + +int end_trans(THD *thd, enum enum_mysql_completiontype completion) +{ + bool do_release= 0; + int res= 0; + DBUG_ENTER("end_trans"); + + switch (completion) { + case COMMIT: + /* + We don't use end_active_trans() here to ensure that this works + even if there is a problem with the OPTION_AUTO_COMMIT flag + (Which of course should never happen...) + */ + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + res= ha_commit(thd); + thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); + break; + case COMMIT_RELEASE: + do_release= 1; /* fall through */ + case COMMIT_AND_CHAIN: + res= end_active_trans(thd); + if (!res && completion == COMMIT_AND_CHAIN) + res= begin_trans(thd); + break; + case ROLLBACK_RELEASE: + do_release= 1; /* fall through */ + case ROLLBACK: + case ROLLBACK_AND_CHAIN: + { + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + if (ha_rollback(thd)) + res= -1; + thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); + if (!res && (completion == ROLLBACK_AND_CHAIN)) + res= begin_trans(thd); + break; + } + default: + res= -1; + my_error(ER_UNKNOWN_COM_ERROR, MYF(0)); + DBUG_RETURN(-1); + } + + if (res < 0) + my_error(thd->killed_errno(), MYF(0)); + else if ((res == 0) && do_release) + thd->killed= THD::KILL_CONNECTION; + + DBUG_RETURN(res); +} #ifndef EMBEDDED_LIBRARY @@ -1275,7 +1413,7 @@ bool do_command(THD *thd) packet=0; old_timeout=net->read_timeout; - // Wait max for 8 hours + /* Wait max for 8 hours */ net->read_timeout=(uint) thd->variables.net_wait_timeout; thd->clear_error(); // Clear error message @@ -1291,7 +1429,7 @@ bool do_command(THD *thd) statistic_increment(aborted_threads,&LOCK_status); DBUG_RETURN(TRUE); // We have to close it. } - send_error(thd,net->last_errno,NullS); + net_send_error(thd, net->last_errno, NullS); net->error= 0; DBUG_RETURN(FALSE); } @@ -1319,6 +1457,7 @@ bool do_command(THD *thd) } #endif /* EMBEDDED_LIBRARY */ + /* Perform one connection-level (COM_XXXX) command. SYNOPSIS @@ -1342,17 +1481,21 @@ bool dispatch_command(enum enum_server_command command, THD *thd, bool error= 0; DBUG_ENTER("dispatch_command"); + if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA) + thd->killed= THD::NOT_KILLED; + thd->command=command; /* Commands which will always take a long time should be marked with this so that they will not get logged to the slow query log */ thd->slow_command=FALSE; + thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */ thd->set_time(); VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id=query_id; if (command != COM_STATISTICS && command != COM_PING) - query_id++; + next_query_id(); thread_running++; /* TODO: set thd->lex->sql_command to SQLCOM_END here */ VOID(pthread_mutex_unlock(&LOCK_thread_count)); @@ -1363,7 +1506,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_INIT_DB: { LEX_STRING tmp; - statistic_increment(com_stat[SQLCOM_CHANGE_DB],&LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB], + &LOCK_status); thd->convert_string(&tmp, system_charset_info, packet, strlen(packet), thd->charset()); if (!mysql_change_db(thd, tmp.str)) @@ -1384,13 +1528,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd, uint db_len= *(uchar*) packet; uint tbl_len= *(uchar*) (packet + db_len + 1); - statistic_increment(com_other, &LOCK_status); + statistic_increment(thd->status_var.com_other, &LOCK_status); thd->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 + mysql_table_dump(thd, db, tbl_name, -1); break; } case COM_CHANGE_USER: @@ -1398,7 +1541,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->change_user(); thd->clear_error(); // if errors from rollback - statistic_increment(com_other, &LOCK_status); + statistic_increment(thd->status_var.com_other, &LOCK_status); char *user= (char*) packet; char *passwd= strend(user)+1; /* @@ -1412,10 +1555,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, *passwd++ : strlen(passwd); db+= passwd_len + 1; #ifndef EMBEDDED_LIBRARY - /* Small check for incomming packet */ + /* Small check for incoming packet */ if ((uint) ((uchar*) db - net->read_pos) > packet_length) { - send_error(thd, ER_UNKNOWN_COM_ERROR); + my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); break; } #endif @@ -1438,7 +1581,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (!(thd->user= my_strdup(user, MYF(0)))) { thd->user= save_user; - send_error(thd, ER_OUT_OF_RESOURCES); + my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); break; } @@ -1448,9 +1591,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (res) { - /* authentification failure, we shall restore old user */ + /* authentication failure, we shall restore old user */ if (res > 0) - send_error(thd, ER_UNKNOWN_COM_ERROR); + my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); x_free(thd->user); thd->user= save_user; thd->priv_user= save_priv_user; @@ -1475,6 +1618,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, mysql_stmt_execute(thd, packet, packet_length); break; } + case COM_FETCH: + { + mysql_stmt_fetch(thd, packet, packet_length); + break; + } case COM_LONG_DATA: { mysql_stmt_get_longdata(thd, packet, packet_length); @@ -1482,7 +1630,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } case COM_PREPARE: { - mysql_stmt_prepare(thd, packet, packet_length); + mysql_stmt_prepare(thd, packet, packet_length, 0); break; } case COM_CLOSE_STMT: @@ -1504,15 +1652,16 @@ bool dispatch_command(enum enum_server_command command, THD *thd, DBUG_PRINT("query",("%-.4096s",thd->query)); mysql_parse(thd,thd->query, thd->query_length); - while (!thd->killed && !thd->is_fatal_error && thd->lex->found_colon) + while (!thd->killed && thd->lex->found_semicolon && !thd->net.report_error) { - char *packet= thd->lex->found_colon; + char *packet= thd->lex->found_semicolon; /* Multiple queries exits, execute them individually in embedded server - just store them to be executed later */ #ifndef EMBEDDED_LIBRARY - if (thd->lock || thd->open_tables || thd->derived_tables) + if (thd->lock || thd->open_tables || thd->derived_tables || + thd->prelocked_mode) close_thread_tables(thd); #endif ulong length= (ulong)(packet_end-packet); @@ -1528,7 +1677,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_length= length; thd->query= packet; - thd->query_id= query_id++; + thd->query_id= next_query_id(); thd->set_time(); /* Reset the query start time. */ /* TODO: set thd->lex->sql_command to SQLCOM_END here */ VOID(pthread_mutex_unlock(&LOCK_thread_count)); @@ -1560,34 +1709,50 @@ 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,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), + MYF(0)); /* purecov: inspected */ break; #else { char *fields, *pend; + /* Locked closure of all tables */ + TABLE_LIST *locked_tables= NULL; TABLE_LIST table_list; LEX_STRING conv_name; + /* Saved variable value */ + my_bool old_innodb_table_locks= + IF_INNOBASE_DB(thd->variables.innodb_table_locks, FALSE); + - statistic_increment(com_stat[SQLCOM_SHOW_FIELDS],&LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS], + &LOCK_status); bzero((char*) &table_list,sizeof(table_list)); if (!(table_list.db=thd->db)) { - send_error(thd,ER_NO_DB_ERROR); + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); break; } - thd->free_list=0; 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; + table_list.alias= table_list.table_name= conv_name.str; packet= pend+1; + + if (!my_strcasecmp(system_charset_info, table_list.db, + information_schema_name.str)) + { + ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, table_list.alias); + if (schema_table) + table_list.schema_table= schema_table; + } + thd->query_length= strlen(packet); // for simplicity: don't optimize if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1))) break; - mysql_log.write(thd,command,"%s %s",table_list.real_name,fields); + mysql_log.write(thd,command,"%s %s",table_list.table_name, fields); if (lower_case_table_names) - my_casedn_str(files_charset_info, table_list.real_name); - remove_escape(table_list.real_name); // This can't have wildcards + my_casedn_str(files_charset_info, table_list.table_name); + remove_escape(table_list.table_name); // This can't have wildcards if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege, 0, 0)) @@ -1595,9 +1760,19 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (grant_option && check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0)) break; + /* init structures for VIEW processing */ + table_list.select_lex= &(thd->lex->select_lex); + mysql_init_query(thd, (uchar*)"", 0); + thd->lex-> + select_lex.table_list.link_in_list((byte*) &table_list, + (byte**) &table_list.next_local); + thd->lex->add_to_query_tables(&table_list); + + /* switch on VIEW optimisation: do not fill temporary tables */ + thd->lex->sql_command= SQLCOM_SHOW_FIELDS; mysqld_list_fields(thd,&table_list,fields); - free_items(thd->free_list); - thd->free_list= 0; + thd->lex->unit.cleanup(); + thd->cleanup_after_query(); break; } #endif @@ -1613,43 +1788,43 @@ bool dispatch_command(enum enum_server_command command, THD *thd, char *db=thd->strdup(packet), *alias; HA_CREATE_INFO create_info; - statistic_increment(com_stat[SQLCOM_CREATE_DB],&LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB], + &LOCK_status); // null test to handle EOM if (!db || !(alias= thd->strdup(db)) || check_db_name(db)) { - net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL"); + my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL"); break; } if (check_access(thd,CREATE_ACL,db,0,1,0)) break; mysql_log.write(thd,command,packet); bzero(&create_info, sizeof(create_info)); - if (mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db), - &create_info, 0) < 0) - send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0); + mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db), + &create_info, 0); break; } case COM_DROP_DB: // QQ: To be removed { - statistic_increment(com_stat[SQLCOM_DROP_DB],&LOCK_status); - char *db=thd->strdup(packet), *alias; - // null test to handle EOM - if (!db || !(alias= thd->strdup(db)) || check_db_name(db)) + statistic_increment(thd->status_var.com_stat[SQLCOM_DROP_DB], + &LOCK_status); + char *db=thd->strdup(packet); + /* null test to handle EOM */ + if (!db || check_db_name(db)) { - net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL"); + my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL"); break; } if (check_access(thd,DROP_ACL,db,0,1,0)) break; if (thd->locked_tables || thd->active_transaction()) { - send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION); + my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, + ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); break; } mysql_log.write(thd,command,db); - if (mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : db), - 0, 0) < 0) - send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0); + mysql_rm_db(thd, db, 0, 0); break; } #ifndef EMBEDDED_LIBRARY @@ -1659,7 +1834,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ushort flags; uint32 slave_server_id; - statistic_increment(com_other,&LOCK_status); + statistic_increment(thd->status_var.com_other,&LOCK_status); thd->slow_command = TRUE; if (check_global_access(thd, REPL_SLAVE_ACL)) break; @@ -1676,7 +1851,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, (long) pos); mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags); unregister_slave(thd,1,1); - // fake COM_QUIT -- if we get here, the thread needs to terminate + /* fake COM_QUIT -- if we get here, the thread needs to terminate */ error = TRUE; net->error = 0; break; @@ -1684,21 +1859,20 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #endif case COM_REFRESH: { - statistic_increment(com_stat[SQLCOM_FLUSH],&LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_FLUSH], + &LOCK_status); ulong options= (ulong) (uchar) packet[0]; if (check_global_access(thd,RELOAD_ACL)) break; mysql_log.write(thd,command,NullS); - if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, NULL)) - send_error(thd, 0); - else + if (!reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, NULL)) send_ok(thd); break; } #ifndef EMBEDDED_LIBRARY case COM_SHUTDOWN: { - statistic_increment(com_other,&LOCK_status); + statistic_increment(thd->status_var.com_other, &LOCK_status); if (check_global_access(thd,SHUTDOWN_ACL)) break; /* purecov: inspected */ /* @@ -1715,7 +1889,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, else if (level != SHUTDOWN_WAIT_ALL_BUFFERS) { my_error(ER_NOT_SUPPORTED_YET, MYF(0), "this shutdown level"); - send_error(thd); break; } DBUG_PRINT("quit",("Got shutdown command for level %u", level)); @@ -1729,8 +1902,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #endif 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; @@ -1739,7 +1910,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_STATISTICS: { mysql_log.write(thd,command,NullS); - statistic_increment(com_stat[SQLCOM_SHOW_STATUS],&LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS], + &LOCK_status); #ifndef EMBEDDED_LIBRARY char buff[200]; #else @@ -1747,11 +1919,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #endif ulong uptime = (ulong) (thd->start_time - start_time); sprintf((char*) buff, - "Uptime: %ld Threads: %d Questions: %lu Slow queries: %ld Opens: %ld Flush tables: %ld Open tables: %u Queries per second avg: %.3f", + "Uptime: %lu Threads: %d Questions: %lu Slow queries: %lu Opens: %lu Flush tables: %lu Open tables: %u Queries per second avg: %.3f", uptime, - (int) thread_count,thd->query_id,long_query_count, - opened_tables,refresh_version, cached_tables(), - uptime ? (float)thd->query_id/(float)uptime : 0); + (int) thread_count, (ulong) thd->query_id, + (ulong) thd->status_var.long_query_count, + thd->status_var.opened_tables, refresh_version, cached_tables(), + (uptime ? (ulonglong2double(thd->query_id) / (double) uptime) : + (double) 0)); #ifdef SAFEMALLOC if (sf_malloc_cur_memory) // Using SAFEMALLOC sprintf(strend(buff), " Memory in use: %ldK Max memory used: %ldK", @@ -1765,11 +1939,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } case COM_PING: - statistic_increment(com_other,&LOCK_status); + statistic_increment(thd->status_var.com_other, &LOCK_status); send_ok(thd); // Tell client we are alive break; case COM_PROCESS_INFO: - statistic_increment(com_stat[SQLCOM_SHOW_PROCESSLIST],&LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST], + &LOCK_status); if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL)) break; mysql_log.write(thd,command,NullS); @@ -1779,14 +1954,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; case COM_PROCESS_KILL: { - statistic_increment(com_stat[SQLCOM_KILL],&LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_KILL], &LOCK_status); ulong id=(ulong) uint4korr(packet); - kill_one_thread(thd,id); + kill_one_thread(thd,id,false); break; } case COM_SET_OPTION: { - statistic_increment(com_stat[SQLCOM_SET_OPTION], &LOCK_status); + statistic_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION], + &LOCK_status); enum_mysql_set_option command= (enum_mysql_set_option) uint2korr(packet); switch (command) { case MYSQL_OPTION_MULTI_STATEMENTS_ON: @@ -1798,16 +1974,16 @@ bool dispatch_command(enum enum_server_command command, THD *thd, send_eof(thd); break; default: - send_error(thd, ER_UNKNOWN_COM_ERROR); + my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); break; } break; } case COM_DEBUG: - statistic_increment(com_other,&LOCK_status); + statistic_increment(thd->status_var.com_other, &LOCK_status); if (check_global_access(thd, SUPER_ACL)) break; /* purecov: inspected */ - mysql_print_status(thd); + mysql_print_status(); mysql_log.write(thd,command,NullS); send_eof(thd); break; @@ -1817,17 +1993,32 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_DELAYED_INSERT: case COM_END: default: - send_error(thd, ER_UNKNOWN_COM_ERROR); + my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); break; } - if (thd->lock || thd->open_tables || thd->derived_tables) + if (thd->lock || thd->open_tables || thd->derived_tables || + thd->prelocked_mode) { thd->proc_info="closing tables"; close_thread_tables(thd); /* Free tables */ } + /* + assume handlers auto-commit (if some doesn't - transaction handling + in MySQL should be redesigned to support it; it's a big change, + and it's not worth it - better to commit explicitly only writing + transactions, read-only ones should better take care of themselves. + saves some work in 2pc too) + see also sql_base.cc - close_thread_tables() + */ + bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt)); + if (!thd->active_transaction()) + thd->transaction.xid.null(); - if (thd->is_fatal_error) - send_error(thd,0); // End of memory ? + /* report error issued during command execution */ + if (thd->killed_errno() && !thd->net.report_error) + thd->send_kill_message(); + if (thd->net.report_error) + net_send_error(thd); log_slow_query(thd); @@ -1861,13 +2052,126 @@ static void log_slow_query(THD *thd) (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) && (specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES))) { - long_query_count++; + thd->status_var.long_query_count++; mysql_slow_log.write(thd, thd->query, thd->query_length, start_of_query); } } } +int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, + enum enum_schema_tables schema_table_idx) +{ + DBUG_ENTER("prepare_schema_table"); + SELECT_LEX *sel= 0; + switch (schema_table_idx) { + case SCH_SCHEMATA: +#if defined(DONT_ALLOW_SHOW_COMMANDS) + my_message(ER_NOT_ALLOWED_COMMAND, + ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */ + DBUG_RETURN(1); +#else + if ((specialflag & SPECIAL_SKIP_SHOW_DB) && + check_global_access(thd, SHOW_DB_ACL)) + DBUG_RETURN(1); + break; +#endif + case SCH_TABLE_NAMES: + case SCH_TABLES: + case SCH_VIEWS: +#ifdef DONT_ALLOW_SHOW_COMMANDS + my_message(ER_NOT_ALLOWED_COMMAND, + ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */ + DBUG_RETURN(1); +#else + { + char *db= lex->select_lex.db ? lex->select_lex.db : thd->db; + if (!db) + { + my_message(ER_NO_DB_ERROR, + ER(ER_NO_DB_ERROR), MYF(0)); /* purecov: inspected */ + DBUG_RETURN(1); /* purecov: inspected */ + } + remove_escape(db); // Fix escaped '_' + if (check_db_name(db)) + { + my_error(ER_WRONG_DB_NAME, MYF(0), db); + DBUG_RETURN(1); + } + if (check_access(thd,SELECT_ACL,db,&thd->col_access,0,0)) + DBUG_RETURN(1); /* purecov: inspected */ + if (!thd->col_access && check_grant_db(thd,db)) + { + my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), + thd->priv_user, thd->priv_host, db); + DBUG_RETURN(1); + } + /* + We need to do a copy to make this prepared statement safe if this + was thd->db + */ + lex->select_lex.db= thd->strdup(db); + break; + } +#endif + case SCH_COLUMNS: + case SCH_STATISTICS: +#ifdef DONT_ALLOW_SHOW_COMMANDS + my_message(ER_NOT_ALLOWED_COMMAND, + ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */ + DBUG_RETURN(1); +#else + if (table_ident) + { + TABLE_LIST **query_tables_last= lex->query_tables_last; + sel= new SELECT_LEX(); + sel->init_query(); + if(!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, + (List<String> *) 0, (List<String> *) 0)) + DBUG_RETURN(1); + lex->query_tables_last= query_tables_last; + TABLE_LIST *table_list= (TABLE_LIST*) sel->table_list.first; + char *db= table_list->db; + remove_escape(db); // Fix escaped '_' + remove_escape(table_list->table_name); + if (check_access(thd,SELECT_ACL | EXTRA_ACL,db, + &table_list->grant.privilege, 0, 0)) + DBUG_RETURN(1); /* purecov: inspected */ + if (grant_option && check_grant(thd, SELECT_ACL, table_list, 2, + UINT_MAX, 0)) + DBUG_RETURN(1); + break; + } +#endif + case SCH_OPEN_TABLES: + case SCH_VARIABLES: + case SCH_STATUS: + case SCH_PROCEDURES: + case SCH_CHARSETS: + case SCH_COLLATIONS: + case SCH_COLLATION_CHARACTER_SET_APPLICABILITY: + case SCH_USER_PRIVILEGES: + case SCH_SCHEMA_PRIVILEGES: + case SCH_TABLE_PRIVILEGES: + case SCH_COLUMN_PRIVILEGES: + case SCH_TABLE_CONSTRAINTS: + case SCH_KEY_COLUMN_USAGE: + default: + break; + } + + SELECT_LEX *select_lex= lex->current_select; + if (make_schema_select(thd, select_lex, schema_table_idx)) + { + DBUG_RETURN(1); + } + TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first; + table_list->schema_select_lex= sel; + table_list->schema_table_reformed= 1; + DBUG_RETURN(0); +} + + /* Read query from packet and store in thd->query Used in COM_QUERY and COM_PREPARE @@ -1878,8 +2182,8 @@ static void log_slow_query(THD *thd) query_length RETURN VALUES - 0 ok - 1 error; In this case thd->fatal_error is set + FALSE ok + TRUE error; In this case thd->fatal_error is set */ bool alloc_query(THD *thd, char *packet, ulong packet_length) @@ -1904,7 +2208,7 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length) packet_length, thd->db_length+ 1 + QUERY_CACHE_FLAGS_SIZE))) - return 1; + return TRUE; thd->query[packet_length]=0; thd->query_length= packet_length; @@ -1914,7 +2218,7 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length) if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); - return 0; + return FALSE; } /**************************************************************************** @@ -1922,26 +2226,56 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length) ** Execute command saved in thd and current_lex->sql_command ****************************************************************************/ -void +bool mysql_execute_command(THD *thd) { - int res= 0; + bool res= FALSE; + int result= 0; LEX *lex= thd->lex; + /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ + SELECT_LEX *select_lex= &lex->select_lex; bool slave_fake_lock= 0; MYSQL_LOCK *fake_prev_lock= 0; - SELECT_LEX *select_lex= &lex->select_lex; - TABLE_LIST *tables= (TABLE_LIST*) select_lex->table_list.first; + /* first table of first SELECT_LEX */ + TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first; + /* list of all tables in query */ + TABLE_LIST *all_tables; + /* most outer SELECT_LEX_UNIT of query */ SELECT_LEX_UNIT *unit= &lex->unit; + /* Saved variable value */ DBUG_ENTER("mysql_execute_command"); + thd->net.no_send_error= 0; + + /* + In many cases first table of main SELECT_LEX have special meaning => + check that it is first table in global list and relink it first in + queries_tables list if it is necessary (we need such relinking only + for queries with subqueries in select list, in this case tables of + subqueries will go to global list first) + + all_tables will differ from first_table only if most upper SELECT_LEX + do not contain tables. + + Because of above in place where should be at least one table in most + outer SELECT_LEX we have following check: + DBUG_ASSERT(first_table == all_tables); + DBUG_ASSERT(first_table == all_tables && first_table != 0); + */ + lex->first_lists_tables_same(); + /* should be assigned after making first tables same */ + all_tables= lex->query_tables; /* 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. + Don't reset warnings when executing a stored routine. */ - if (tables || &lex->select_lex != lex->all_selects_list) - mysql_reset_errors(thd); + if ((all_tables || &lex->select_lex != lex->all_selects_list || + lex->spfuns.records || lex->spprocs.records) && + !thd->spcont) + mysql_reset_errors(thd, 0); #ifdef HAVE_REPLICATION if (thd->slave_thread) @@ -1950,8 +2284,7 @@ mysql_execute_command(THD *thd) { DBUG_PRINT("info",("need faked locked tables")); - if (check_multi_update_lock(thd, tables, &select_lex->item_list, - select_lex)) + if (check_multi_update_lock(thd)) goto error; /* Fix for replication, the tables are opened and locked, @@ -1972,11 +2305,11 @@ mysql_execute_command(THD *thd) */ if (!(lex->sql_command == SQLCOM_DROP_TABLE && lex->drop_temporary && lex->drop_if_exists) && - all_tables_not_ok(thd,tables)) + all_tables_not_ok(thd, all_tables)) { /* we warn the slave SQL thread */ - my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); - DBUG_VOID_RETURN; + my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); + DBUG_RETURN(0); } #ifndef TO_BE_DELETED /* @@ -1993,13 +2326,9 @@ mysql_execute_command(THD *thd) } #endif /* !HAVE_REPLICATION */ - /* When subselects or time_zone info is used in a query - * we create a new TABLE_LIST containing all referenced tables - * and set local variable 'tables' to point to this list. */ - if ((&lex->select_lex != lex->all_selects_list || - lex->time_zone_tables_used) && - lex->unit.create_total_list(thd, lex, &tables)) - DBUG_VOID_RETURN; + + + /* When option readonly is set deny operations which change tables. @@ -2007,13 +2336,14 @@ mysql_execute_command(THD *thd) */ if (opt_readonly && !(thd->slave_thread || (thd->master_access & SUPER_ACL)) && - (uc_update_queries[lex->sql_command] > 0)) + uc_update_queries[lex->sql_command]) { - net_printf(thd, ER_OPTION_PREVENTS_STATEMENT, "--read-only"); - DBUG_VOID_RETURN; + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); + DBUG_RETURN(-1); } - statistic_increment(com_stat[lex->sql_command],&LOCK_status); + statistic_increment(thd->status_var.com_stat[lex->sql_command], + &LOCK_status); switch (lex->sql_command) { case SQLCOM_SELECT: { @@ -2025,43 +2355,26 @@ mysql_execute_command(THD *thd) } select_result *result=lex->result; - if (tables) + if (all_tables) { - res=check_table_access(thd, - lex->exchange ? SELECT_ACL | FILE_ACL : - SELECT_ACL, - tables,0); + res= check_table_access(thd, + lex->exchange ? SELECT_ACL | FILE_ACL : + SELECT_ACL, + all_tables, 0); } else - res=check_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, - any_db,0,0,0); + res= check_access(thd, + lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, + any_db, 0, 0, 0); if (res) - { - res=0; - break; // Error message is given - } - /* - In case of single SELECT unit->global_parameters points on first SELECT - TODO: move counters to SELECT_LEX - */ - 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->next_select()) - select_lex->options&= ~OPTION_FOUND_ROWS; + goto error; - if (!(res=open_and_lock_tables(thd,tables))) + if (!(res= open_and_lock_tables(thd, all_tables))) { if (lex->describe) { if (!(result= new select_send())) - { - send_error(thd, ER_OUT_OF_RESOURCES); - DBUG_VOID_RETURN; - } + goto error; else thd->send_explain_fields(result); res= mysql_explain_union(thd, &thd->lex->unit, result); @@ -2081,12 +2394,9 @@ mysql_execute_command(THD *thd) else { if (!result && !(result= new select_send())) - { - res= -1; - break; - } - query_cache_store_query(thd, tables); - res= handle_select(thd, lex, result); + goto error; + query_cache_store_query(thd, all_tables); + res= handle_select(thd, lex, result, 0); if (result != lex->result) delete result; } @@ -2125,10 +2435,7 @@ mysql_execute_command(THD *thd) */ DBUG_ASSERT(!is_var_null); if (!pstr) - { - res= -1; - break; // EOM (error should be reported by allocator) - } + goto error; } else { @@ -2146,11 +2453,8 @@ mysql_execute_command(THD *thd) query_len= need_conversion? (pstr->length() * to_cs->mbmaxlen) : pstr->length(); if (!(query_str= alloc_root(thd->mem_root, query_len+1))) - { - res= -1; - break; // EOM (error should be reported by allocator) - } - + goto error; + if (need_conversion) { uint dummy_errors; @@ -2172,8 +2476,8 @@ mysql_execute_command(THD *thd) query_len, query_str)); } thd->command= COM_PREPARE; - if (!mysql_stmt_prepare(thd, query_str, query_len + 1, - &lex->prepared_stmt_name)) + if (!(res= mysql_stmt_prepare(thd, query_str, query_len + 1, + &lex->prepared_stmt_name))) send_ok(thd, 0L, 0L, "Statement prepared"); break; } @@ -2199,21 +2503,20 @@ mysql_execute_command(THD *thd) } else { - res= -1; my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), - lex->prepared_stmt_name.length, lex->prepared_stmt_name.str, + lex->prepared_stmt_name.length, + lex->prepared_stmt_name.str, "DEALLOCATE PREPARE"); + goto error; } break; } case SQLCOM_DO: - if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) || - (res= open_and_lock_tables(thd,tables)))) - break; + if (check_table_access(thd, SELECT_ACL, all_tables, 0) || + open_and_lock_tables(thd, all_tables)) + goto error; res= mysql_do(thd, *lex->insert_list); - if (thd->net.report_error) - res= -1; break; case SQLCOM_EMPTY_QUERY: @@ -2229,16 +2532,31 @@ mysql_execute_command(THD *thd) { if (check_global_access(thd, SUPER_ACL)) goto error; - // PURGE MASTER LOGS TO 'file' + /* PURGE MASTER LOGS TO 'file' */ res = purge_master_logs(thd, lex->to_log); break; } case SQLCOM_PURGE_BEFORE: { + Item *it; + if (check_global_access(thd, SUPER_ACL)) goto error; - // PURGE MASTER LOGS BEFORE 'data' - res = purge_master_logs_before_date(thd, lex->purge_time); + /* PURGE MASTER LOGS BEFORE 'data' */ + it= (Item *)lex->value_list.head(); + if ((!it->fixed &&it->fix_fields(lex->thd, 0, &it)) || + it->check_cols(1)) + { + my_error(ER_WRONG_ARGUMENTS, MYF(0), "PURGE LOGS BEFORE"); + goto error; + } + it= new Item_func_unix_timestamp(it); + /* + it is OK only emulate fix_fieds, because we need only + value of constant + */ + it->quick_fix_field(); + res = purge_master_logs_before_date(thd, (ulong)it->val_int()); break; } #endif @@ -2263,12 +2581,12 @@ mysql_execute_command(THD *thd) goto error; /* This query don't work now. See comment in repl_failsafe.cc */ #ifndef WORKING_NEW_MASTER - net_printf(thd, ER_NOT_SUPPORTED_YET, "SHOW NEW MASTER"); - res= 1; + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "SHOW NEW MASTER"); + goto error; #else res = show_new_master(thd); -#endif break; +#endif } #ifdef HAVE_REPLICATION @@ -2283,48 +2601,52 @@ mysql_execute_command(THD *thd) { if (check_global_access(thd, REPL_SLAVE_ACL)) goto error; - res = show_binlog_events(thd); + res = mysql_show_binlog_events(thd); break; } #endif case SQLCOM_BACKUP_TABLE: { - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL, tables,0) || + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, SELECT_ACL, all_tables, 0) || check_global_access(thd, FILE_ACL)) goto error; /* purecov: inspected */ thd->slow_command=TRUE; - res = mysql_backup_table(thd, tables); + res = mysql_backup_table(thd, first_table); break; } case SQLCOM_RESTORE_TABLE: { - if (check_db_used(thd,tables) || - check_table_access(thd, INSERT_ACL, tables,0) || + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, INSERT_ACL, all_tables, 0) || check_global_access(thd, FILE_ACL)) goto error; /* purecov: inspected */ thd->slow_command=TRUE; - res = mysql_restore_table(thd, tables); + res = mysql_restore_table(thd, first_table); break; } case SQLCOM_ASSIGN_TO_KEYCACHE: { - if (check_db_used(thd, tables) || - check_access(thd, INDEX_ACL, tables->db, - &tables->grant.privilege, 0, 0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_access(thd, INDEX_ACL, first_table->db, + &first_table->grant.privilege, 0, 0)) goto error; - res= mysql_assign_to_keycache(thd, tables, &lex->name_and_length); + res= mysql_assign_to_keycache(thd, first_table, &lex->ident); break; } case SQLCOM_PRELOAD_KEYS: { - if (check_db_used(thd, tables) || - check_access(thd, INDEX_ACL, tables->db, - &tables->grant.privilege, 0, 0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_access(thd, INDEX_ACL, first_table->db, + &first_table->grant.privilege, 0, 0)) goto error; - res = mysql_preload_keys(thd, tables); + res = mysql_preload_keys(thd, first_table); break; } #ifdef HAVE_REPLICATION @@ -2360,7 +2682,7 @@ mysql_execute_command(THD *thd) if (check_global_access(thd, SUPER_ACL)) goto error; if (end_active_trans(thd)) - res= -1; + goto error; else res = load_master_data(thd); break; @@ -2373,23 +2695,32 @@ mysql_execute_command(THD *thd) res = innodb_show_status(thd); break; } + case SQLCOM_SHOW_MUTEX_STATUS: + { + if (check_global_access(thd, SUPER_ACL)) + goto error; + res = innodb_mutex_show_status(thd); + break; + } #endif #ifdef HAVE_REPLICATION case SQLCOM_LOAD_MASTER_TABLE: { - if (!tables->db) - tables->db=thd->db; - if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege,0,0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (!first_table->db) + first_table->db= thd->db; + if (check_access(thd, CREATE_ACL, first_table->db, + &first_table->grant.privilege, 0, 0)) goto error; /* purecov: inspected */ if (grant_option) { /* Check that the first table has CREATE privilege */ - if (check_grant(thd, CREATE_ACL, tables, 0, 1, 0)) + if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0)) goto error; } - if (strlen(tables->real_name) > NAME_LEN) + if (strlen(first_table->table_name) > NAME_LEN) { - net_printf(thd,ER_WRONG_TABLE_NAME, tables->real_name); + my_error(ER_WRONG_TABLE_NAME, MYF(0), first_table->table_name); break; } pthread_mutex_lock(&LOCK_active_mi); @@ -2397,7 +2728,7 @@ mysql_execute_command(THD *thd) fetch_master_table will send the error to the client on failure. Give error if the table already exists. */ - if (!fetch_master_table(thd, tables->db, tables->real_name, + if (!fetch_master_table(thd, first_table->db, first_table->table_name, active_mi, 0, 0)) { send_ok(thd); @@ -2409,12 +2740,13 @@ mysql_execute_command(THD *thd) case SQLCOM_CREATE_TABLE: { - /* Skip first table, which is the table we are creating */ - TABLE_LIST *create_table, *create_table_local; - tables= lex->unlink_first_table(tables, &create_table, - &create_table_local); + DBUG_ASSERT(first_table == all_tables && first_table != 0); + bool link_to_local; + // Skip first table, which is the table we are creating + TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local); + TABLE_LIST *select_tables= lex->query_tables; - if ((res= create_table_precheck(thd, tables, create_table))) + if ((res= create_table_precheck(thd, select_tables, create_table))) goto unsent_create_error; #ifndef HAVE_READLINK @@ -2422,16 +2754,13 @@ mysql_execute_command(THD *thd) #else /* Fix names if symlinked tables */ if (append_file_to_dir(thd, &lex->create_info.data_file_name, - create_table->real_name) || - append_file_to_dir(thd,&lex->create_info.index_file_name, - create_table->real_name)) - { - res=-1; + create_table->table_name) || + append_file_to_dir(thd, &lex->create_info.index_file_name, + create_table->table_name)) goto unsent_create_error; - } #endif /* - If we are using SET CHARSET without DEFAULT, add an implicite + If we are using SET CHARSET without DEFAULT, add an implicit DEFAULT to not confuse old users. (This may change). */ if ((lex->create_info.used_fields & @@ -2448,72 +2777,91 @@ mysql_execute_command(THD *thd) select_result *result; select_lex->options|= SELECT_NO_UNLOCK; - 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 + unit->set_limit(select_lex, select_lex); - if (!(res=open_and_lock_tables(thd,tables))) + if (!(res= open_and_lock_tables(thd, select_tables))) { - res= -1; // If error - if ((result=new select_create(create_table->db, - create_table->real_name, - &lex->create_info, - lex->create_list, - lex->key_list, - select_lex->item_list, lex->duplicates, - lex->ignore))) + /* + Is table which we are changing used somewhere in other parts + of query + */ + if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && + unique_table(create_table, select_tables)) + { + my_error(ER_UPDATE_TABLE_USED, MYF(0), create_table->table_name); + goto unsent_create_error; + } + /* If we create merge table, we have to test tables in merge, too */ + if (lex->create_info.used_fields & HA_CREATE_USED_UNION) + { + TABLE_LIST *tab; + for (tab= (TABLE_LIST*) lex->create_info.merge_list.first; + tab; + tab= tab->next_local) + { + if (unique_table(tab, select_tables)) + { + my_error(ER_UPDATE_TABLE_USED, MYF(0), tab->table_name); + goto unsent_create_error; + } + } + } + + if ((result= new select_create(create_table, + &lex->create_info, + lex->create_list, + lex->key_list, + select_lex->item_list, + lex->duplicates, + lex->ignore))) { /* CREATE from SELECT give its SELECT_LEX for SELECT, and item_list belong to SELECT */ select_lex->resolve_mode= SELECT_LEX::SELECT_MODE; - res=handle_select(thd, lex, result); + res= handle_select(thd, lex, result, 0); select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE; + delete result; } - //reset for PS + /* reset for PS */ lex->create_list.empty(); lex->key_list.empty(); } } - else // regular create + else { + /* regular create */ if (lex->name) res= mysql_create_like_table(thd, create_table, &lex->create_info, (Table_ident *)lex->name); else { - res= mysql_create_table(thd,create_table->db, - create_table->real_name, &lex->create_info, - lex->create_list, - lex->key_list,0,0); + res= mysql_create_table(thd, create_table->db, + create_table->table_name, &lex->create_info, + lex->create_list, + lex->key_list, 0, 0); } if (!res) send_ok(thd); } - - // put tables back for PS rexecuting - tables= lex->link_first_table_back(tables, create_table, - create_table_local); + lex->link_first_table_back(create_table, link_to_local); break; - res= 1; //error reported + /* put tables back for PS rexecuting */ unsent_create_error: - // put tables back for PS rexecuting - tables= lex->link_first_table_back(tables, create_table, - create_table_local); - break; + lex->link_first_table_back(create_table, link_to_local); + goto error; } case SQLCOM_CREATE_INDEX: - if (check_one_table_access(thd, INDEX_ACL, tables)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_one_table_access(thd, INDEX_ACL, all_tables)) goto error; /* purecov: inspected */ thd->slow_command=TRUE; if (end_active_trans(thd)) - res= -1; + goto error; else - res = mysql_create_index(thd, tables, lex->key_list); + res = mysql_create_index(thd, first_table, lex->key_list); break; #ifdef HAVE_REPLICATION @@ -2540,8 +2888,9 @@ unsent_create_error: */ if (thd->locked_tables || thd->active_transaction()) { - send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION); - break; + my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, ER(ER_LOCK_OR_ACTIVE_TRANSACTION), + MYF(0)); + goto error; } { pthread_mutex_lock(&LOCK_active_mi); @@ -2552,35 +2901,37 @@ unsent_create_error: #endif /* HAVE_REPLICATION */ case SQLCOM_ALTER_TABLE: + DBUG_ASSERT(first_table == all_tables && first_table != 0); #if defined(DONT_ALLOW_SHOW_COMMANDS) - send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - break; + my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), + MYF(0)); /* purecov: inspected */ + goto error; #else { ulong priv=0; if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN)) { - net_printf(thd, ER_WRONG_TABLE_NAME, lex->name); - res= 1; - break; + my_error(ER_WRONG_TABLE_NAME, MYF(0), lex->name); + goto error; } if (!select_lex->db) - select_lex->db=tables->db; - if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege,0,0) || + select_lex->db= first_table->db; + if (check_access(thd, ALTER_ACL, first_table->db, + &first_table->grant.privilege, 0, 0) || check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0)|| - check_merge_table_access(thd, tables->db, + check_merge_table_access(thd, first_table->db, (TABLE_LIST *) lex->create_info.merge_list.first)) goto error; /* purecov: inspected */ if (grant_option) { - if (check_grant(thd, ALTER_ACL, tables, 0, UINT_MAX, 0)) + if (check_grant(thd, ALTER_ACL, all_tables, 0, UINT_MAX, 0)) goto error; if (lex->name && !test_all_bits(priv,INSERT_ACL | CREATE_ACL)) { // Rename of table TABLE_LIST tmp_table; bzero((char*) &tmp_table,sizeof(tmp_table)); - tmp_table.real_name=lex->name; + tmp_table.table_name=lex->name; tmp_table.db=select_lex->db; tmp_table.grant.privilege=priv; if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0, @@ -2592,63 +2943,62 @@ unsent_create_error: lex->create_info.data_file_name=lex->create_info.index_file_name=0; /* ALTER TABLE ends previous transaction */ if (end_active_trans(thd)) - res= -1; + goto error; else { thd->slow_command=TRUE; res= mysql_alter_table(thd, select_lex->db, lex->name, &lex->create_info, - tables, lex->create_list, + first_table, lex->create_list, lex->key_list, select_lex->order_list.elements, (ORDER *) select_lex->order_list.first, - lex->duplicates, lex->ignore, &lex->alter_info); + lex->duplicates, lex->ignore, &lex->alter_info, + 1); } break; } #endif /*DONT_ALLOW_SHOW_COMMANDS*/ case SQLCOM_RENAME_TABLE: { + DBUG_ASSERT(first_table == all_tables && first_table != 0); TABLE_LIST *table; - if (check_db_used(thd,tables)) + if (check_db_used(thd, all_tables)) goto error; - for (table=tables ; table ; table=table->next->next) + for (table= first_table; table; table= table->next_local->next_local) { if (check_access(thd, ALTER_ACL | DROP_ACL, table->db, &table->grant.privilege,0,0) || - check_access(thd, INSERT_ACL | CREATE_ACL, table->next->db, - &table->next->grant.privilege,0,0)) + check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db, + &table->next_local->grant.privilege, 0, 0)) goto error; if (grant_option) { - TABLE_LIST old_list,new_list; + TABLE_LIST old_list, new_list; /* we do not need initialize old_list and new_list because we will come table[0] and table->next[0] there */ - old_list=table[0]; - new_list=table->next[0]; - old_list.next=new_list.next=0; - if (check_grant(thd, ALTER_ACL, &old_list, 0, UINT_MAX, 0) || - (!test_all_bits(table->next->grant.privilege, + old_list= table[0]; + new_list= table->next_local[0]; + if (check_grant(thd, ALTER_ACL, &old_list, 0, 1, 0) || + (!test_all_bits(table->next_local->grant.privilege, INSERT_ACL | CREATE_ACL) && - check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, - UINT_MAX, 0))) + check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0))) goto error; } } - query_cache_invalidate3(thd, tables, 0); - if (end_active_trans(thd)) - res= -1; - else if (mysql_rename_tables(thd,tables)) - res= -1; + query_cache_invalidate3(thd, first_table, 0); + if (end_active_trans(thd) || mysql_rename_tables(thd, first_table)) + goto error; break; } #ifndef EMBEDDED_LIBRARY case SQLCOM_SHOW_BINLOGS: #ifdef DONT_ALLOW_SHOW_COMMANDS - send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - DBUG_VOID_RETURN; + my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), + MYF(0)); /* purecov: inspected */ + goto error; #else { if (check_global_access(thd, SUPER_ACL)) @@ -2659,40 +3009,47 @@ unsent_create_error: #endif #endif /* EMBEDDED_LIBRARY */ case SQLCOM_SHOW_CREATE: + DBUG_ASSERT(first_table == all_tables && first_table != 0); #ifdef DONT_ALLOW_SHOW_COMMANDS - send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - DBUG_VOID_RETURN; + my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), + MYF(0)); /* purecov: inspected */ + goto error; #else { - if (check_db_used(thd, tables) || - check_access(thd, SELECT_ACL | EXTRA_ACL, tables->db, - &tables->grant.privilege,0,0)) + /* Ignore temporary tables if this is "SHOW CREATE VIEW" */ + if (lex->only_view) + first_table->skip_temporary= 1; + + if (check_db_used(thd, all_tables) || + check_access(thd, SELECT_ACL | EXTRA_ACL, first_table->db, + &first_table->grant.privilege, 0, 0)) goto error; - if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0)) + if (grant_option && check_grant(thd, SELECT_ACL, all_tables, 2, UINT_MAX, 0)) goto error; - res= mysqld_show_create(thd, tables); + res= mysqld_show_create(thd, first_table); break; } #endif case SQLCOM_CHECKSUM: { - if (check_db_used(thd,tables) || - check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, 0)) goto error; /* purecov: inspected */ - res = mysql_checksum_table(thd, tables, &lex->check_opt); + res = mysql_checksum_table(thd, first_table, &lex->check_opt); break; } case SQLCOM_REPAIR: { - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0)) goto error; /* purecov: inspected */ thd->slow_command=TRUE; - res = mysql_repair_table(thd, tables, &lex->check_opt); + res= mysql_repair_table(thd, first_table, &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()) { thd->clear_error(); // No binlog error generated @@ -2704,24 +3061,25 @@ unsent_create_error: } case SQLCOM_CHECK: { - if (check_db_used(thd,tables) || - check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, 0)) goto error; /* purecov: inspected */ thd->slow_command=TRUE; - res = mysql_check_table(thd, tables, &lex->check_opt); + res = mysql_check_table(thd, first_table, &lex->check_opt); break; } case SQLCOM_ANALYZE: { - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0)) goto error; /* purecov: inspected */ thd->slow_command=TRUE; - res = mysql_analyze_table(thd, tables, &lex->check_opt); + res = mysql_analyze_table(thd, first_table, &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()) { thd->clear_error(); // No binlog error generated @@ -2734,17 +3092,17 @@ unsent_create_error: case SQLCOM_OPTIMIZE: { - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0)) goto error; /* purecov: inspected */ thd->slow_command=TRUE; res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ? - mysql_recreate_table(thd, tables, 1) : - mysql_optimize_table(thd, tables, &lex->check_opt); + mysql_recreate_table(thd, first_table, 1) : + mysql_optimize_table(thd, first_table, &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()) { thd->clear_error(); // No binlog error generated @@ -2755,173 +3113,149 @@ unsent_create_error: break; } case SQLCOM_UPDATE: - if (update_precheck(thd, tables)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (update_precheck(thd, all_tables)) break; - 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, lex->ignore); - if (thd->net.report_error) - res= -1; - break; - case SQLCOM_UPDATE_MULTI: - { - if ((res= multi_update_precheck(thd, tables))) + res= (result= mysql_update(thd, all_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, lex->ignore)); + /* mysql_update return 2 if we need to switch to multi-update */ + if (result != 2) break; - res= mysql_multi_update(thd,tables, - &select_lex->item_list, - &lex->value_list, - select_lex->where, - select_lex->options, - lex->duplicates, lex->ignore, unit, select_lex); + case SQLCOM_UPDATE_MULTI: + { + DBUG_ASSERT(first_table == all_tables && first_table != 0); + /* if we switched from normal update, rights are checked */ + if (result != 2) + { + if ((res= multi_update_precheck(thd, all_tables))) + break; + } + else + res= 0; + + res= mysql_multi_update(thd, all_tables, + &select_lex->item_list, + &lex->value_list, + select_lex->where, + select_lex->options, + lex->duplicates, lex->ignore, unit, select_lex); break; } case SQLCOM_REPLACE: case SQLCOM_INSERT: { - if ((res= insert_precheck(thd, tables))) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if ((res= insert_precheck(thd, all_tables))) break; - res= mysql_insert(thd,tables,lex->field_list,lex->many_values, - lex->update_list, lex->value_list, + res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values, + lex->update_list, lex->value_list, lex->duplicates, lex->ignore); - if (thd->net.report_error) - res= -1; + if (first_table->view && !first_table->contain_auto_increment) + thd->last_insert_id= 0; // do not show last insert ID if VIEW have not it break; } case SQLCOM_REPLACE_SELECT: case SQLCOM_INSERT_SELECT: { - TABLE_LIST *first_local_table= (TABLE_LIST *) select_lex->table_list.first; - if ((res= insert_precheck(thd, tables))) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if ((res= insert_precheck(thd, all_tables))) break; /* Fix lock for first table */ - if (tables->lock_type == TL_WRITE_DELAYED) - tables->lock_type= TL_WRITE; + if (first_table->lock_type == TL_WRITE_DELAYED) + first_table->lock_type= TL_WRITE; /* Don't unlock tables until command is written to binary log */ select_lex->options|= SELECT_NO_UNLOCK; select_result *result; - 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 + unit->set_limit(select_lex, select_lex); - if (find_real_table_in_list(tables->next, tables->db, tables->real_name)) + if (!(res= open_and_lock_tables(thd, all_tables))) { - /* Using same table for INSERT and SELECT */ - select_lex->options |= OPTION_BUFFER_RESULT; - } + /* Skip first table, which is the table we are inserting in */ + select_lex->table_list.first= (byte*)first_table->next_local; - if ((res= open_and_lock_tables(thd, tables))) - break; - - TABLE *table= tables->table; - /* Skip first table, which is the table we are inserting in */ - select_lex->table_list.first= (byte*) first_local_table->next; - tables= (TABLE_LIST *) select_lex->table_list.first; - first_local_table->next= 0; - - if (!(res= mysql_prepare_insert(thd, tables, first_local_table, - table, lex->field_list, 0, - lex->update_list, lex->value_list, - lex->duplicates)) && - (result= new select_insert(table, &lex->field_list, - &lex->update_list, &lex->value_list, - lex->duplicates, lex->ignore))) - { - /* - insert/replace from SELECT give its SELECT_LEX for SELECT, - and item_list belong to SELECT - */ - lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE; - res= handle_select(thd, lex, result); + res= mysql_insert_select_prepare(thd); + if (!res && (result= new select_insert(first_table, first_table->table, + &lex->field_list, + &lex->update_list, &lex->value_list, + lex->duplicates, lex->ignore))) + { + /* + insert/replace from SELECT give its SELECT_LEX for SELECT, + and item_list belong to SELECT + */ + select_lex->resolve_mode= SELECT_LEX::SELECT_MODE; + res= handle_select(thd, lex, result, OPTION_SETUP_TABLES_DONE); + select_lex->resolve_mode= SELECT_LEX::INSERT_MODE; + delete result; + } /* revert changes for SP */ - lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE; - delete result; - table->insert_values= 0; - if (thd->net.report_error) - res= -1; + select_lex->table_list.first= (byte*) first_table; } - else - res= -1; - first_local_table->next= tables; - lex->select_lex.table_list.first= (byte*) first_local_table; + + if (first_table->view && !first_table->contain_auto_increment) + thd->last_insert_id= 0; // do not show last insert ID if VIEW have not it + break; } case SQLCOM_TRUNCATE: - if (check_one_table_access(thd, DELETE_ACL, tables)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_one_table_access(thd, DELETE_ACL, all_tables)) goto error; /* Don't allow this within a transaction because we want to use re-generate table */ - if (thd->locked_tables || thd->active_transaction()) + if ((thd->locked_tables && !lex->sphead) || thd->active_transaction()) { - send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION,NullS); + my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, + ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); goto error; } - res=mysql_truncate(thd, tables, 0); + + res= mysql_truncate(thd, first_table, 0); break; case SQLCOM_DELETE: { - if ((res= delete_precheck(thd, tables))) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if ((res= delete_precheck(thd, all_tables))) break; - res = mysql_delete(thd,tables, select_lex->where, + res = mysql_delete(thd, all_tables, select_lex->where, &select_lex->order_list, select_lex->select_limit, select_lex->options); - if (thd->net.report_error) - res= -1; break; } case SQLCOM_DELETE_MULTI: { + DBUG_ASSERT(first_table == all_tables && first_table != 0); TABLE_LIST *aux_tables= (TABLE_LIST *)thd->lex->auxilliary_table_list.first; - TABLE_LIST *target_tbl; uint table_count; multi_delete *result; - if ((res= multi_delete_precheck(thd, tables, &table_count))) + if ((res= multi_delete_precheck(thd, all_tables, &table_count))) break; /* condition will be TRUE on SP re-excuting */ if (select_lex->item_list.elements != 0) select_lex->item_list.empty(); if (add_item_to_list(thd, new Item_null())) - { - res= -1; - break; - } + goto error; thd->proc_info="init"; - if ((res=open_and_lock_tables(thd,tables))) + if ((res= open_and_lock_tables(thd, all_tables))) break; - /* Fix tables-to-be-deleted-from list to point at opened tables */ - for (target_tbl= (TABLE_LIST*) aux_tables; - target_tbl; - target_tbl= target_tbl->next) - { - TABLE_LIST *orig= target_tbl->table_list; - target_tbl->table= orig->table; - /* - Multi-delete can't be constructed over-union => we always have - single SELECT on top and have to check underlying SELECTs of it - */ - if (lex->select_lex.check_updateable_in_subqueries(orig->db, - orig->real_name)) - { - my_error(ER_UPDATE_TABLE_USED, MYF(0), - orig->real_name); - res= -1; - break; - } - } + + if ((res= mysql_multi_delete_prepare(thd))) + goto error; if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables, table_count))) @@ -2934,28 +3268,24 @@ unsent_create_error: 0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL, (ORDER *)NULL, select_lex->options | thd->options | - SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK, + SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | + OPTION_SETUP_TABLES_DONE, result, unit, select_lex); - if (thd->net.report_error) - res= -1; delete result; } else - res= -1; // Error is not sent - close_thread_tables(thd); + res= TRUE; // Error break; } case SQLCOM_DROP_TABLE: { + DBUG_ASSERT(first_table == all_tables && first_table != 0); if (!lex->drop_temporary) { - if (check_table_access(thd,DROP_ACL,tables,0)) + if (check_table_access(thd, DROP_ACL, all_tables, 0)) goto error; /* purecov: inspected */ if (end_active_trans(thd)) - { - res= -1; - break; - } + goto error; } else { @@ -2970,28 +3300,19 @@ unsent_create_error: if (thd->slave_thread) lex->drop_if_exists= 1; } - res= mysql_rm_table(thd,tables,lex->drop_if_exists, lex->drop_temporary); + res= mysql_rm_table(thd, first_table, lex->drop_if_exists, + lex->drop_temporary); } break; case SQLCOM_DROP_INDEX: - if (check_one_table_access(thd, INDEX_ACL, tables)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_one_table_access(thd, INDEX_ACL, all_tables)) goto error; /* purecov: inspected */ if (end_active_trans(thd)) - res= -1; - else - res = mysql_drop_index(thd, tables, &lex->alter_info); - break; - case SQLCOM_SHOW_DATABASES: -#if defined(DONT_ALLOW_SHOW_COMMANDS) - send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - DBUG_VOID_RETURN; -#else - if ((specialflag & SPECIAL_SKIP_SHOW_DB) && - check_global_access(thd, SHOW_DB_ACL)) goto error; - res= mysqld_show_dbs(thd, (lex->wild ? lex->wild->ptr() : NullS)); + else + res = mysql_drop_index(thd, first_table, &lex->alter_info); break; -#endif case SQLCOM_SHOW_PROCESSLIST: if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL)) break; @@ -3008,19 +3329,11 @@ unsent_create_error: 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); - break; - case SQLCOM_SHOW_VARIABLES: - res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS), - init_vars, lex->option_type, - &LOCK_global_system_variables); - break; case SQLCOM_SHOW_LOGS: #ifdef DONT_ALLOW_SHOW_COMMANDS - send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - DBUG_VOID_RETURN; + my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), + MYF(0)); /* purecov: inspected */ + goto error; #else { if (grant_option && check_access(thd, FILE_ACL, any_db,0,0,0)) @@ -3029,134 +3342,46 @@ unsent_create_error: break; } #endif - case SQLCOM_SHOW_TABLES: - /* FALL THROUGH */ -#ifdef DONT_ALLOW_SHOW_COMMANDS - 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,ER_NO_DB_ERROR); /* purecov: inspected */ - goto error; /* purecov: inspected */ - } - remove_escape(db); // Fix escaped '_' - if (check_db_name(db)) - { - net_printf(thd,ER_WRONG_DB_NAME, db); - goto error; - } - if (check_access(thd,SELECT_ACL,db,&thd->col_access,0,0)) - goto error; /* purecov: inspected */ - if (!thd->col_access && check_grant_db(thd,db)) - { - net_printf(thd, ER_DBACCESS_DENIED_ERROR, - thd->priv_user, - thd->priv_host, - db); - goto error; - } - /* grant is checked in mysqld_show_tables */ - if (lex->describe) - res= mysqld_extend_show_tables(thd,db, - (lex->wild ? lex->wild->ptr() : NullS)); - else - res= mysqld_show_tables(thd,db, - (lex->wild ? lex->wild->ptr() : NullS)); - break; - } -#endif - case SQLCOM_SHOW_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,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - DBUG_VOID_RETURN; -#else - { - char *db=tables->db; - remove_escape(db); // Fix escaped '_' - remove_escape(tables->real_name); - if (check_access(thd,SELECT_ACL | EXTRA_ACL,db, - &tables->grant.privilege, 0, 0)) - goto error; /* purecov: inspected */ - if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0)) - goto error; - res= mysqld_show_fields(thd,tables, - (lex->wild ? lex->wild->ptr() : NullS), - lex->verbose); - break; - } -#endif - case SQLCOM_SHOW_KEYS: -#ifdef DONT_ALLOW_SHOW_COMMANDS - send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - DBUG_VOID_RETURN; -#else - { - char *db=tables->db; - remove_escape(db); // Fix escaped '_' - remove_escape(tables->real_name); - if (check_access(thd,SELECT_ACL | EXTRA_ACL,db, - &tables->grant.privilege, 0, 0)) - goto error; /* purecov: inspected */ - if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0)) - goto error; - res= mysqld_show_keys(thd,tables); - break; - } -#endif case SQLCOM_CHANGE_DB: mysql_change_db(thd,select_lex->db); break; case SQLCOM_LOAD: { + DBUG_ASSERT(first_table == all_tables && first_table != 0); uint privilege= (lex->duplicates == DUP_REPLACE ? - INSERT_ACL | DELETE_ACL : INSERT_ACL); + INSERT_ACL | DELETE_ACL : INSERT_ACL) | + (lex->local_file ? 0 : FILE_ACL); - if (!lex->local_file) - { - if (check_access(thd,privilege | FILE_ACL,tables->db,0,0,0)) - goto error; - } - else + if (lex->local_file) { if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) || ! opt_local_infile) { - send_error(thd,ER_NOT_ALLOWED_COMMAND); + my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); goto error; } - if (check_one_table_access(thd, privilege, tables)) - goto error; } - res=mysql_load(thd, lex->exchange, tables, lex->field_list, - lex->duplicates, lex->ignore, (bool) lex->local_file, lex->lock_option); + + if (check_one_table_access(thd, privilege, all_tables)) + goto error; + + res= mysql_load(thd, lex->exchange, first_table, lex->field_list, + lex->update_list, lex->value_list, lex->duplicates, + lex->ignore, (bool) lex->local_file); break; } case SQLCOM_SET_OPTION: { List<set_var_base> *lex_var_list= &lex->var_list; - if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) || - (res= open_and_lock_tables(thd,tables)))) - break; + if ((check_table_access(thd, SELECT_ACL, all_tables, 0) || + open_and_lock_tables(thd, all_tables))) + goto error; if (lex->one_shot_set && not_all_support_one_shot(lex_var_list)) { - my_printf_error(0, "The SET ONE_SHOT syntax is reserved for \ -purposes internal to the MySQL server", MYF(0)); - res= -1; - break; + my_error(ER_RESERVED_SYNTAX, MYF(0), "SET ONE_SHOT"); + goto error; } if (!(res= sql_set_variables(thd, lex_var_list))) { @@ -3167,8 +3392,6 @@ purposes internal to the MySQL server", MYF(0)); thd->one_shot_set|= lex->one_shot_set; send_ok(thd); } - if (thd->net.report_error) - res= -1; break; } @@ -3191,17 +3414,18 @@ purposes internal to the MySQL server", MYF(0)); break; case SQLCOM_LOCK_TABLES: unlock_locked_tables(thd); - if (check_db_used(thd,tables) || end_active_trans(thd)) + if (check_db_used(thd, all_tables) || end_active_trans(thd)) goto error; - if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, tables,0)) + if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0)) goto error; thd->in_lock_tables=1; thd->options|= OPTION_TABLE_LOCK; - if (!(res= open_and_lock_tables(thd, tables))) + + if (!(res= simple_open_n_lock_tables(thd, all_tables))) { #ifdef HAVE_QUERY_CACHE if (thd->variables.query_cache_wlock_invalidate) - query_cache.invalidate_locked_for_write(tables); + query_cache.invalidate_locked_for_write(first_table); #endif /*HAVE_QUERY_CACHE*/ thd->locked_tables=thd->lock; thd->lock=0; @@ -3216,22 +3440,22 @@ purposes internal to the MySQL server", MYF(0)); char *alias; if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name)) { - net_printf(thd,ER_WRONG_DB_NAME, lex->name); + my_error(ER_WRONG_DB_NAME, MYF(0), lex->name); break; } /* If in a slave thread : CREATE DATABASE DB was certainly not preceded by USE DB. - For that reason, db_ok() in sql/slave.cc did not check the + For that reason, db_ok() in sql/slave.cc did not check the 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 && + if (thd->slave_thread && (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) || !db_ok_with_wild_table(lex->name))) { - my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); + my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); break; } #endif @@ -3243,10 +3467,9 @@ purposes internal to the MySQL server", MYF(0)); } case SQLCOM_DROP_DB: { - char *alias; - if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name)) + if (check_db_name(lex->name)) { - net_printf(thd, ER_WRONG_DB_NAME, lex->name); + my_error(ER_WRONG_DB_NAME, MYF(0), lex->name); break; } /* @@ -3261,7 +3484,7 @@ purposes internal to the MySQL server", MYF(0)); (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) || !db_ok_with_wild_table(lex->name))) { - my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); + my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); break; } #endif @@ -3269,11 +3492,11 @@ purposes internal to the MySQL server", MYF(0)); break; if (thd->locked_tables || thd->active_transaction()) { - send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION); + my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, + ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); goto error; } - res=mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : lex->name), - lex->drop_if_exists, 0); + res= mysql_rm_db(thd, lex->name, lex->drop_if_exists, 0); break; } case SQLCOM_ALTER_DB: @@ -3281,12 +3504,12 @@ purposes internal to the MySQL server", MYF(0)); char *db= lex->name ? lex->name : thd->db; if (!db) { - send_error(thd, ER_NO_DB_ERROR); - goto error; + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); + break; } if (!strip_sp(db) || check_db_name(db)) { - net_printf(thd, ER_WRONG_DB_NAME, db); + my_error(ER_WRONG_DB_NAME, MYF(0), lex->name); break; } /* @@ -3301,7 +3524,7 @@ purposes internal to the MySQL server", MYF(0)); (!db_ok(db, replicate_do_db, replicate_ignore_db) || !db_ok_with_wild_table(db))) { - my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); + my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); break; } #endif @@ -3309,7 +3532,8 @@ purposes internal to the MySQL server", MYF(0)); break; if (thd->locked_tables || thd->active_transaction()) { - send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION); + my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, + ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); goto error; } res= mysql_alter_db(thd, db, &lex->create_info); @@ -3319,7 +3543,7 @@ purposes internal to the MySQL server", MYF(0)); { if (!strip_sp(lex->name) || check_db_name(lex->name)) { - net_printf(thd,ER_WRONG_DB_NAME, lex->name); + my_error(ER_WRONG_DB_NAME, MYF(0), lex->name); break; } if (check_access(thd,SELECT_ACL,lex->name,0,1,0)) @@ -3327,38 +3551,67 @@ purposes internal to the MySQL server", MYF(0)); res=mysqld_show_create_db(thd,lex->name,&lex->create_info); break; } - case SQLCOM_CREATE_FUNCTION: + case SQLCOM_CREATE_FUNCTION: // UDF function + { if (check_access(thd,INSERT_ACL,"mysql",0,1,0)) break; #ifdef HAVE_DLOPEN - if (!(res = mysql_create_function(thd,&lex->udf))) + if (sp_find_function(thd, lex->spname)) + { + my_error(ER_UDF_EXISTS, MYF(0), lex->spname->m_name.str); + goto error; + } + if (!(res = mysql_create_function(thd, &lex->udf))) send_ok(thd); #else - res= -1; + res= TRUE; #endif break; - case SQLCOM_DROP_FUNCTION: - if (check_access(thd,DELETE_ACL,"mysql",0,1,0)) + } +#ifndef NO_EMBEDDED_ACCESS_CHECKS + case SQLCOM_CREATE_USER: + { + if (check_access(thd, INSERT_ACL, "mysql", 0, 1, 1) && + check_global_access(thd,CREATE_USER_ACL)) break; -#ifdef HAVE_DLOPEN - if (!(res = mysql_drop_function(thd,&lex->udf.name))) + if (!(res= mysql_create_user(thd, lex->users_list))) + { + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); + mysql_bin_log.write(&qinfo); + } send_ok(thd); -#else - res= -1; -#endif + } break; -#ifndef NO_EMBEDDED_ACCESS_CHECKS + } case SQLCOM_DROP_USER: { - if (check_access(thd, GRANT_ACL,"mysql",0,1,0)) + if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 1) && + check_global_access(thd,CREATE_USER_ACL)) 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, FALSE); - mysql_bin_log.write(&qinfo); + Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); + mysql_bin_log.write(&qinfo); + } + send_ok(thd); + } + break; + } + case SQLCOM_RENAME_USER: + { + if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1) && + check_global_access(thd,CREATE_USER_ACL)) + break; + if (!(res= mysql_rename_user(thd, lex->users_list))) + { + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); + mysql_bin_log.write(&qinfo); } send_ok(thd); } @@ -3366,11 +3619,11 @@ purposes internal to the MySQL server", MYF(0)); } case SQLCOM_REVOKE_ALL: { - if (check_access(thd, GRANT_ACL ,"mysql",0,1,0)) + if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1) && + check_global_access(thd,CREATE_USER_ACL)) 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, FALSE); @@ -3384,95 +3637,102 @@ purposes internal to the MySQL server", MYF(0)); case SQLCOM_GRANT: { if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL, - tables ? tables->db : select_lex->db, - tables ? &tables->grant.privilege : 0, - tables ? 0 : 1, 0)) + first_table ? first_table->db : select_lex->db, + first_table ? &first_table->grant.privilege : 0, + first_table ? 0 : 1, 0)) goto error; - /* - Check that the user isn't trying to change a password for another - user if he doesn't have UPDATE privilege to the MySQL database - */ - if (thd->user) // If not replication { LEX_USER *user; + uint counter; + List_iterator <LEX_USER> user_list(lex->users_list); - while ((user=user_list++)) + while ((user= user_list++)) { - if (user->password.str && - (strcmp(thd->user,user->user.str) || - user->host.str && - my_strcasecmp(&my_charset_latin1, - user->host.str, thd->host_or_ip))) - { - if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1)) - { - send_error(thd, ER_PASSWORD_NOT_ALLOWED); - goto error; - } - break; // We are allowed to do global changes - } + if (specialflag & SPECIAL_NO_RESOLVE && + hostname_requires_resolving(user->host.str)) + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_HOSTNAME_WONT_WORK, + ER(ER_WARN_HOSTNAME_WONT_WORK), + user->host.str); + // Are we trying to change a password of another user + DBUG_ASSERT(user->host.str != 0); + if (strcmp(thd->user, user->user.str) || + my_strcasecmp(system_charset_info, + user->host.str, thd->host_or_ip)) + { + // TODO: use check_change_password() + if (check_acl_user(user, &counter) && user->password.str && + check_access(thd, UPDATE_ACL,"mysql",0,1,1)) + { + my_message(ER_PASSWORD_NOT_ALLOWED, + ER(ER_PASSWORD_NOT_ALLOWED), MYF(0)); + goto error; + } + } } } - if (specialflag & SPECIAL_NO_RESOLVE) + if (first_table) { - LEX_USER *user; - List_iterator <LEX_USER> user_list(lex->users_list); - while ((user=user_list++)) + if (lex->type == TYPE_ENUM_PROCEDURE || + lex->type == TYPE_ENUM_FUNCTION) { - if (hostname_requires_resolving(user->host.str)) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_HOSTNAME_WONT_WORK, - ER(ER_WARN_HOSTNAME_WONT_WORK), - user->host.str); + uint grants= lex->all_privileges + ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL) + : lex->grant; + if (grant_option && + check_grant_routine(thd, grants | GRANT_ACL, all_tables, + lex->type == TYPE_ENUM_PROCEDURE, 0)) + goto error; + res= mysql_routine_grant(thd, all_tables, + lex->type == TYPE_ENUM_PROCEDURE, + lex->users_list, grants, + lex->sql_command == SQLCOM_REVOKE, 0); } - } - if (tables) - { - if (grant_option && check_grant(thd, - (lex->grant | lex->grant_tot_col | - GRANT_ACL), - tables, 0, UINT_MAX, 0)) - goto error; - if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns, - lex->grant, - lex->sql_command == SQLCOM_REVOKE))) + else { - mysql_update_log.write(thd, thd->query, thd->query_length); - if (mysql_bin_log.is_open()) - { - thd->clear_error(); - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - } + if (grant_option && check_grant(thd, + (lex->grant | lex->grant_tot_col | + GRANT_ACL), + all_tables, 0, UINT_MAX, 0)) + goto error; + res= mysql_table_grant(thd, all_tables, lex->users_list, + lex->columns, lex->grant, + lex->sql_command == SQLCOM_REVOKE); + } + if (!res && mysql_bin_log.is_open()) + { + thd->clear_error(); + Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); + mysql_bin_log.write(&qinfo); } } else { - if (lex->columns.elements) + if (lex->columns.elements || lex->type) { - send_error(thd,ER_ILLEGAL_GRANT_FOR_TABLE); - res=1; + my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE), + MYF(0)); + goto error; } else res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant, lex->sql_command == SQLCOM_REVOKE); if (!res) { - mysql_update_log.write(thd, thd->query, thd->query_length); if (mysql_bin_log.is_open()) { thd->clear_error(); Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); mysql_bin_log.write(&qinfo); } - if (mqh_used && lex->sql_command == SQLCOM_GRANT) + if (lex->sql_command == SQLCOM_GRANT) { List_iterator <LEX_USER> str_list(lex->users_list); LEX_USER *user; while ((user=str_list++)) - reset_mqh(thd,user); + reset_mqh(user); } } } @@ -3480,23 +3740,21 @@ purposes internal to the MySQL server", MYF(0)); } #endif /*!NO_EMBEDDED_ACCESS_CHECKS*/ 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 + /* + 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)) + if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, all_tables)) goto error; /* 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 + if (!reload_acl_and_cache(thd, lex->type, first_table, &write_to_binlog)) { /* We WANT to write and we CAN write. @@ -3504,7 +3762,6 @@ purposes internal to the MySQL server", MYF(0)); */ 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, FALSE); @@ -3516,11 +3773,20 @@ purposes internal to the MySQL server", MYF(0)); break; } case SQLCOM_KILL: - kill_one_thread(thd,lex->thread_id); + { + Item *it= (Item *)lex->value_list.head(); + + if ((!it->fixed && it->fix_fields(lex->thd, 0, &it)) || it->check_cols(1)) + { + my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY), + MYF(0)); + goto error; + } + kill_one_thread(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY); break; + } #ifndef NO_EMBEDDED_ACCESS_CHECKS case SQLCOM_SHOW_GRANTS: - res=0; if ((thd->priv_user && !strcmp(thd->priv_user,lex->grant_user->user.str)) || !check_access(thd, SELECT_ACL, "mysql",0,1,0)) @@ -3530,111 +3796,780 @@ purposes internal to the MySQL server", MYF(0)); break; #endif case SQLCOM_HA_OPEN: - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL, tables,0)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables) || + check_table_access(thd, SELECT_ACL, all_tables, 0)) goto error; - res = mysql_ha_open(thd, tables); + res= mysql_ha_open(thd, first_table, 0); break; case SQLCOM_HA_CLOSE: - if (check_db_used(thd,tables)) + DBUG_ASSERT(first_table == all_tables && first_table != 0); + if (check_db_used(thd, all_tables)) goto error; - res = mysql_ha_close(thd, tables); + res= mysql_ha_close(thd, first_table); break; case SQLCOM_HA_READ: + DBUG_ASSERT(first_table == all_tables && first_table != 0); /* There is no need to check for table permissions here, because if a user has no permissions to read a table, he won't be able to open it (with SQLCOM_HA_OPEN) in the first place. */ - if (check_db_used(thd,tables)) + if (check_db_used(thd, all_tables)) goto error; - res = mysql_ha_read(thd, tables, lex->ha_read_mode, lex->backup_dir, - lex->insert_list, lex->ha_rkey_mode, select_lex->where, - select_lex->select_limit, select_lex->offset_limit); + res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->ident.str, + lex->insert_list, lex->ha_rkey_mode, select_lex->where, + select_lex->select_limit, select_lex->offset_limit); break; case SQLCOM_BEGIN: - if (thd->locked_tables) + if (begin_trans(thd)) + goto error; + send_ok(thd); + break; + case SQLCOM_COMMIT: + if (end_trans(thd, lex->tx_release ? COMMIT_RELEASE : + lex->tx_chain ? COMMIT_AND_CHAIN : COMMIT)) + goto error; + send_ok(thd); + break; + case SQLCOM_ROLLBACK: + if (end_trans(thd, lex->tx_release ? ROLLBACK_RELEASE : + lex->tx_chain ? ROLLBACK_AND_CHAIN : ROLLBACK)) + goto error; + send_ok(thd); + break; + case SQLCOM_RELEASE_SAVEPOINT: + { + SAVEPOINT *sv; + for (sv=thd->transaction.savepoints; sv; sv=sv->prev) { - thd->lock=thd->locked_tables; - thd->locked_tables=0; // Will be automaticly closed - close_thread_tables(thd); // Free tables + if (my_strnncoll(system_charset_info, + (uchar *)lex->ident.str, lex->ident.length, + (uchar *)sv->name, sv->length) == 0) + break; } - if (end_active_trans(thd)) + if (sv) { - res= -1; + if (ha_release_savepoint(thd, sv)) + res= TRUE; // cannot happen + else + send_ok(thd); + thd->transaction.savepoints=sv->prev; } else + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str); + break; + } + case SQLCOM_ROLLBACK_TO_SAVEPOINT: + { + SAVEPOINT *sv; + for (sv=thd->transaction.savepoints; sv; sv=sv->prev) + { + if (my_strnncoll(system_charset_info, + (uchar *)lex->ident.str, lex->ident.length, + (uchar *)sv->name, sv->length) == 0) + break; + } + if (sv) { - thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) | - OPTION_BEGIN); - thd->server_status|= SERVER_STATUS_IN_TRANS; - if (!(lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) || - !(res= ha_start_consistent_snapshot(thd))) + if (ha_rollback_to_savepoint(thd, sv)) + res= TRUE; // cannot happen + else + { + if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && + !thd->slave_thread) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARNING_NOT_COMPLETE_ROLLBACK, + ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)); send_ok(thd); + } + thd->transaction.savepoints=sv; } + else + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str); break; - case SQLCOM_COMMIT: - /* - We don't use end_active_trans() here to ensure that this works - even if there is a problem with the OPTION_AUTO_COMMIT flag - (Which of course should never happen...) - */ + } + case SQLCOM_SAVEPOINT: + if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) || + !opt_using_transactions) + send_ok(thd); + else + { + SAVEPOINT **sv, *newsv; + for (sv=&thd->transaction.savepoints; *sv; sv=&(*sv)->prev) + { + if (my_strnncoll(system_charset_info, + (uchar *)lex->ident.str, lex->ident.length, + (uchar *)(*sv)->name, (*sv)->length) == 0) + break; + } + if (*sv) /* old savepoint of the same name exists */ + { + newsv=*sv; + ha_release_savepoint(thd, *sv); // it cannot fail + *sv=(*sv)->prev; + } + else if ((newsv=(SAVEPOINT *) alloc_root(&thd->transaction.mem_root, + savepoint_alloc_size)) == 0) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + break; + } + newsv->name=strmake_root(&thd->transaction.mem_root, + lex->ident.str, lex->ident.length); + newsv->length=lex->ident.length; + /* + if we'll get an error here, don't add new savepoint to the list. + we'll lose a little bit of memory in transaction mem_root, but it'll + be free'd when transaction ends anyway + */ + if (ha_savepoint(thd, newsv)) + res= TRUE; + else + { + newsv->prev=thd->transaction.savepoints; + thd->transaction.savepoints=newsv; + send_ok(thd); + } + } + break; + case SQLCOM_CREATE_PROCEDURE: + case SQLCOM_CREATE_SPFUNCTION: { - thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); - thd->server_status&= ~SERVER_STATUS_IN_TRANS; - if (!ha_commit(thd)) + uint namelen; + char *name, *db; + int result; + + DBUG_ASSERT(lex->sphead != 0); + + if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, 0, 0, 0)) { + delete lex->sphead; + lex->sphead= 0; + goto error; + } + + if (!lex->sphead->m_db.str || !lex->sphead->m_db.str[0]) + { + lex->sphead->m_db.length= strlen(thd->db); + lex->sphead->m_db.str= strmake_root(thd->mem_root, thd->db, + lex->sphead->m_db.length); + } + + name= lex->sphead->name(&namelen); +#ifdef HAVE_DLOPEN + if (lex->sphead->m_type == TYPE_ENUM_FUNCTION) + { + udf_func *udf = find_udf(name, namelen); + + if (udf) + { + my_error(ER_UDF_EXISTS, MYF(0), name); + delete lex->sphead; + lex->sphead= 0; + goto error; + } + } +#endif + if (lex->sphead->m_type == TYPE_ENUM_FUNCTION && + !lex->sphead->m_has_return) + { + my_error(ER_SP_NORETURN, MYF(0), name); + delete lex->sphead; + lex->sphead= 0; + goto error; + } + + name= thd->strdup(name); + db= thd->strmake(lex->sphead->m_db.str, lex->sphead->m_db.length); + res= (result= lex->sphead->create(thd)); + switch (result) { + case SP_OK: + lex->unit.cleanup(); + delete lex->sphead; + lex->sphead= 0; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + /* only add privileges if really neccessary */ + if (sp_automatic_privileges && + check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS, + db, name, + lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1)) + { + close_thread_tables(thd); + if (sp_grant_privileges(thd, db, name, + lex->sql_command == SQLCOM_CREATE_PROCEDURE)) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_PROC_AUTO_GRANT_FAIL, + ER(ER_PROC_AUTO_GRANT_FAIL)); + } +#endif send_ok(thd); + break; + case SP_WRITE_ROW_FAILED: + my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name); + lex->unit.cleanup(); + delete lex->sphead; + lex->sphead= 0; + goto error; + case SP_NO_DB_ERROR: + my_error(ER_BAD_DB_ERROR, MYF(0), lex->sphead->m_db.str); + lex->unit.cleanup(); + delete lex->sphead; + lex->sphead= 0; + goto error; + default: + my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name); + lex->unit.cleanup(); + delete lex->sphead; + lex->sphead= 0; + goto error; } - else - res= -1; break; } - case SQLCOM_ROLLBACK: - thd->server_status&= ~SERVER_STATUS_IN_TRANS; - if (!ha_rollback(thd)) + case SQLCOM_CALL: { + sp_head *sp; + /* - If a non-transactional table was updated, warn; don't warn if this is a - slave thread (because when a slave thread executes a ROLLBACK, it has - been read from the binary log, so it's 100% sure and normal to produce - error ER_WARNING_NOT_COMPLETE_ROLLBACK. If we sent the warning to the - slave SQL thread, it would not stop the thread but just be printed in - the error log; but we don't want users to wonder why they have this - message in the error log, so we don't send it. + This will cache all SP and SF and open and lock all tables + required for execution. */ - if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread) - send_warning(thd,ER_WARNING_NOT_COMPLETE_ROLLBACK,0); + if (check_table_access(thd, SELECT_ACL, all_tables, 0) || + open_and_lock_tables(thd, all_tables)) + goto error; + + /* + By this moment all needed SPs should be in cache so no need + to look into DB. Moreover we may be unable to do it becuase + we may don't have read lock on mysql.proc + */ + if (!(sp= sp_find_procedure(thd, lex->spname, TRUE))) + { + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE", + lex->spname->m_qname.str); + goto error; + } + else + { +#ifndef NO_EMBEDDED_ACCESS_CHECKS + st_sp_security_context save_ctx; +#endif + ha_rows select_limit; + /* bits that should be cleared in thd->server_status */ + uint bits_to_be_cleared= 0; + +#ifndef EMBEDDED_LIBRARY + my_bool nsok= thd->net.no_send_ok; + thd->net.no_send_ok= TRUE; +#endif + if (sp->m_multi_results) + { + if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS)) + { + my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str); +#ifndef EMBEDDED_LIBRARY + thd->net.no_send_ok= nsok; +#endif + goto error; + } + /* + If SERVER_MORE_RESULTS_EXISTS is not set, + then remember that it should be cleared + */ + bits_to_be_cleared= (~thd->server_status & + SERVER_MORE_RESULTS_EXISTS); + thd->server_status|= SERVER_MORE_RESULTS_EXISTS; + } + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_routine_access(thd, EXECUTE_ACL, + sp->m_db.str, sp->m_name.str, TRUE, 0)) + { +#ifndef EMBEDDED_LIBRARY + thd->net.no_send_ok= nsok; +#endif + goto error; + } + sp_change_security_context(thd, sp, &save_ctx); + if (save_ctx.changed && + check_routine_access(thd, EXECUTE_ACL, + sp->m_db.str, sp->m_name.str, TRUE, 0)) + { +#ifndef EMBEDDED_LIBRARY + thd->net.no_send_ok= nsok; +#endif + sp_restore_security_context(thd, sp, &save_ctx); + goto error; + } + +#endif + select_limit= thd->variables.select_limit; + thd->variables.select_limit= HA_POS_ERROR; + + thd->row_count_func= 0; + tmp_disable_binlog(thd); /* don't binlog the substatements */ + res= sp->execute_procedure(thd, &lex->value_list); + reenable_binlog(thd); + + /* + We write CALL to binlog; on the opposite we didn't write the + substatements. That choice is necessary because the substatements + may use local vars. + Binlogging should happen when all tables are locked. They are locked + just above, and unlocked by close_thread_tables(). All tables which + are to be updated are locked like with a table-level write lock, and + this also applies to InnoDB (I tested - note that it reduces + InnoDB's concurrency as we don't use row-level locks). So binlogging + below is safe. + Note the limitation: if the SP returned an error, but still did some + updates, we do NOT binlog it. This is because otherwise "permission + denied", "table does not exist" etc would stop the slave quite + often. There is no easy way to know if the SP updated something + (even no_trans_update is not suitable, as it may be a transactional + autocommit update which happened, and no_trans_update covers only + INSERT/UPDATE/LOAD). + */ + if (mysql_bin_log.is_open() && + (sp->m_chistics->daccess == SP_CONTAINS_SQL || + sp->m_chistics->daccess == SP_MODIFIES_SQL_DATA)) + { + if (res) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_FAILED_ROUTINE_BREAK_BINLOG, + ER(ER_FAILED_ROUTINE_BREAK_BINLOG)); + else + { + thd->clear_error(); + Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); + mysql_bin_log.write(&qinfo); + } + } + + /* + If warnings have been cleared, we have to clear total_warn_count + too, otherwise the clients get confused. + */ + if (thd->warn_list.is_empty()) + thd->total_warn_count= 0; + + thd->variables.select_limit= select_limit; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + sp_restore_security_context(thd, sp, &save_ctx); +#endif + +#ifndef EMBEDDED_LIBRARY + thd->net.no_send_ok= nsok; +#endif + thd->server_status&= ~bits_to_be_cleared; + + if (!res) + send_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 : + thd->row_count_func)); + else + goto error; // Substatement should already have sent error + } + break; + } + case SQLCOM_ALTER_PROCEDURE: + case SQLCOM_ALTER_FUNCTION: + { + int result; + sp_head *sp; + st_sp_chistics chistics; + + memcpy(&chistics, &lex->sp_chistics, sizeof(chistics)); + if (lex->sql_command == SQLCOM_ALTER_PROCEDURE) + sp= sp_find_procedure(thd, lex->spname); + else + sp= sp_find_function(thd, lex->spname); + mysql_reset_errors(thd, 0); + if (! sp) + { + if (lex->spname->m_db.str) + result= SP_KEY_NOT_FOUND; + else + { + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); + goto error; + } + } else + { + if (check_routine_access(thd, ALTER_PROC_ACL, sp->m_db.str, + sp->m_name.str, + lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0)) + goto error; + memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics)); + if (!trust_routine_creators && mysql_bin_log.is_open() && + !sp->m_chistics->detistic && + (chistics.daccess == SP_CONTAINS_SQL || + chistics.daccess == SP_MODIFIES_SQL_DATA)) + { + my_message(ER_BINLOG_UNSAFE_ROUTINE, + ER(ER_BINLOG_UNSAFE_ROUTINE), MYF(0)); + result= SP_INTERNAL_ERROR; + } + else + { + if (lex->sql_command == SQLCOM_ALTER_PROCEDURE) + result= sp_update_procedure(thd, lex->spname, &lex->sp_chistics); + else + result= sp_update_function(thd, lex->spname, &lex->sp_chistics); + } + } + switch (result) + { + case SP_OK: + if (mysql_bin_log.is_open()) + { + thd->clear_error(); + Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); + mysql_bin_log.write(&qinfo); + } send_ok(thd); + break; + case SP_KEY_NOT_FOUND: + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), + SP_COM_STRING(lex), lex->spname->m_qname.str); + goto error; + default: + my_error(ER_SP_CANT_ALTER, MYF(0), + SP_COM_STRING(lex), lex->spname->m_qname.str); + goto error; + } + break; + } + case SQLCOM_DROP_PROCEDURE: + case SQLCOM_DROP_FUNCTION: + { + sp_head *sp; + int result; + char *db, *name; + + if (lex->sql_command == SQLCOM_DROP_PROCEDURE) + sp= sp_find_procedure(thd, lex->spname); + else + sp= sp_find_function(thd, lex->spname); + mysql_reset_errors(thd, 0); + if (sp) + { + db= thd->strdup(sp->m_db.str); + name= thd->strdup(sp->m_name.str); + if (check_routine_access(thd, ALTER_PROC_ACL, db, name, + lex->sql_command == SQLCOM_DROP_PROCEDURE, 0)) + goto error; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (sp_automatic_privileges && + sp_revoke_privileges(thd, db, name, + lex->sql_command == SQLCOM_DROP_PROCEDURE)) + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_PROC_AUTO_REVOKE_FAIL, + ER(ER_PROC_AUTO_REVOKE_FAIL)); + } +#endif + if (lex->sql_command == SQLCOM_DROP_PROCEDURE) + result= sp_drop_procedure(thd, lex->spname); + else + result= sp_drop_function(thd, lex->spname); + } + else + { +#ifdef HAVE_DLOPEN + if (lex->sql_command == SQLCOM_DROP_FUNCTION) + { + udf_func *udf = find_udf(lex->spname->m_name.str, + lex->spname->m_name.length); + if (udf) + { + if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0)) + goto error; + if (!(res = mysql_drop_function(thd, &lex->spname->m_name))) + { + send_ok(thd); + break; + } + } + } +#endif + if (lex->spname->m_db.str) + result= SP_KEY_NOT_FOUND; + else + { + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); + goto error; + } + } + res= result; + switch (result) + { + case SP_OK: + if (mysql_bin_log.is_open()) + { + thd->clear_error(); + Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); + mysql_bin_log.write(&qinfo); + } + send_ok(thd); + break; + case SP_KEY_NOT_FOUND: + if (lex->drop_if_exists) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), + SP_COM_STRING(lex), lex->spname->m_name.str); + res= FALSE; + send_ok(thd); + break; + } + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), + SP_COM_STRING(lex), lex->spname->m_qname.str); + goto error; + default: + my_error(ER_SP_DROP_FAILED, MYF(0), + SP_COM_STRING(lex), lex->spname->m_qname.str); + goto error; + } + break; + } + case SQLCOM_SHOW_CREATE_PROC: + { + if (lex->spname->m_name.length > NAME_LEN) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str); + goto error; + } + if (sp_show_create_procedure(thd, lex->spname) != SP_OK) + { /* We don't distinguish between errors for now */ + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), + SP_COM_STRING(lex), lex->spname->m_name.str); + goto error; + } + break; + } + case SQLCOM_SHOW_CREATE_FUNC: + { + if (lex->spname->m_name.length > NAME_LEN) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str); + goto error; + } + if (sp_show_create_function(thd, lex->spname) != SP_OK) + { /* We don't distinguish between errors for now */ + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), + SP_COM_STRING(lex), lex->spname->m_name.str); + goto error; + } + break; + } + case SQLCOM_SHOW_STATUS_PROC: + { + res= sp_show_status_procedure(thd, (lex->wild ? + lex->wild->ptr() : NullS)); + break; + } + case SQLCOM_SHOW_STATUS_FUNC: + { + res= sp_show_status_function(thd, (lex->wild ? + lex->wild->ptr() : NullS)); + break; + } + case SQLCOM_CREATE_VIEW: + { + if (!(res= mysql_create_view(thd, thd->lex->create_view_mode)) && + mysql_bin_log.is_open()) + { + thd->clear_error(); + Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); + mysql_bin_log.write(&qinfo); + } + break; + } + case SQLCOM_DROP_VIEW: + { + if (check_table_access(thd, DROP_ACL, all_tables, 0) || + end_active_trans(thd)) + goto error; + if (!(res= mysql_drop_view(thd, first_table, thd->lex->drop_mode)) && + mysql_bin_log.is_open()) + { + thd->clear_error(); + Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); + mysql_bin_log.write(&qinfo); + } + break; + } + case SQLCOM_CREATE_TRIGGER: + { + res= mysql_create_or_drop_trigger(thd, all_tables, 1); + + /* We don't care about trigger body after this point */ + delete lex->sphead; + lex->sphead= 0; + break; + } + case SQLCOM_DROP_TRIGGER: + { + res= mysql_create_or_drop_trigger(thd, all_tables, 0); + break; + } + case SQLCOM_XA_START: + if (thd->transaction.xa_state == XA_IDLE && thd->lex->xa_opt == XA_RESUME) + { + if (! thd->transaction.xid.eq(thd->lex->xid)) + { + my_error(ER_XAER_NOTA, MYF(0)); + break; + } + thd->transaction.xa_state=XA_ACTIVE; + send_ok(thd); + break; + } + if (thd->lex->xa_opt != XA_NONE) + { // JOIN is not supported yet. TODO + my_error(ER_XAER_INVAL, MYF(0)); + break; + } + if (thd->transaction.xa_state != XA_NOTR) + { + my_error(ER_XAER_RMFAIL, MYF(0), + xa_state_names[thd->transaction.xa_state]); + break; + } + if (thd->active_transaction() || thd->locked_tables) + { + my_error(ER_XAER_OUTSIDE, MYF(0)); + break; + } + DBUG_ASSERT(thd->transaction.xid.is_null()); + thd->transaction.xa_state=XA_ACTIVE; + thd->transaction.xid.set(thd->lex->xid); + thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) | + OPTION_BEGIN); + thd->server_status|= SERVER_STATUS_IN_TRANS; + send_ok(thd); + break; + case SQLCOM_XA_END: + /* fake it */ + if (thd->lex->xa_opt != XA_NONE) + { // SUSPEND and FOR MIGRATE are not supported yet. TODO + my_error(ER_XAER_INVAL, MYF(0)); + break; + } + if (thd->transaction.xa_state != XA_ACTIVE) + { + my_error(ER_XAER_RMFAIL, MYF(0), + xa_state_names[thd->transaction.xa_state]); + break; + } + if (!thd->transaction.xid.eq(thd->lex->xid)) + { + my_error(ER_XAER_NOTA, MYF(0)); + break; + } + thd->transaction.xa_state=XA_IDLE; + send_ok(thd); + break; + case SQLCOM_XA_PREPARE: + if (thd->transaction.xa_state != XA_IDLE) + { + my_error(ER_XAER_RMFAIL, MYF(0), + xa_state_names[thd->transaction.xa_state]); + break; + } + if (!thd->transaction.xid.eq(thd->lex->xid)) + { + my_error(ER_XAER_NOTA, MYF(0)); + break; + } + if (ha_prepare(thd)) + { + my_error(ER_XA_RBROLLBACK, MYF(0)); + thd->transaction.xa_state=XA_NOTR; + break; + } + thd->transaction.xa_state=XA_PREPARED; + send_ok(thd); + break; + case SQLCOM_XA_COMMIT: + if (!thd->transaction.xid.eq(thd->lex->xid)) + { + if (!(res= !ha_commit_or_rollback_by_xid(thd->lex->xid, 1))) + my_error(ER_XAER_NOTA, MYF(0)); + else + send_ok(thd); + break; + } + if (thd->transaction.xa_state == XA_IDLE && thd->lex->xa_opt == XA_ONE_PHASE) + { + int r; + if ((r= ha_commit(thd))) + my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0)); + else + send_ok(thd); } else - res= -1; + if (thd->transaction.xa_state == XA_PREPARED && thd->lex->xa_opt == XA_NONE) + { + if (wait_if_global_read_lock(thd, 0, 0)) + { + ha_rollback(thd); + my_error(ER_XAER_RMERR, MYF(0)); + } + else + { + if (ha_commit_one_phase(thd, 1)) + my_error(ER_XAER_RMERR, MYF(0)); + else + send_ok(thd); + start_waiting_global_read_lock(thd); + } + } + else + { + my_error(ER_XAER_RMFAIL, MYF(0), + xa_state_names[thd->transaction.xa_state]); + break; + } thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + thd->transaction.xa_state=XA_NOTR; break; - case SQLCOM_ROLLBACK_TO_SAVEPOINT: - if (!ha_rollback_to_savepoint(thd, lex->savepoint_name)) + case SQLCOM_XA_ROLLBACK: + if (!thd->transaction.xid.eq(thd->lex->xid)) { - if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread) - send_warning(thd, ER_WARNING_NOT_COMPLETE_ROLLBACK, 0); + if (!(res= !ha_commit_or_rollback_by_xid(thd->lex->xid, 0))) + my_error(ER_XAER_NOTA, MYF(0)); else - send_ok(thd); + send_ok(thd); + break; } + if (thd->transaction.xa_state != XA_IDLE && + thd->transaction.xa_state != XA_PREPARED) + { + my_error(ER_XAER_RMFAIL, MYF(0), + xa_state_names[thd->transaction.xa_state]); + break; + } + if (ha_rollback(thd)) + my_error(ER_XAER_RMERR, MYF(0)); else - res= -1; - break; - case SQLCOM_SAVEPOINT: - if (!ha_savepoint(thd, lex->savepoint_name)) send_ok(thd); - else - res= -1; + thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + thd->transaction.xa_state=XA_NOTR; break; - default: /* Impossible */ + case SQLCOM_XA_RECOVER: + res= mysql_xa_recover(thd); + break; + default: + DBUG_ASSERT(0); /* Impossible */ send_ok(thd); break; } - thd->proc_info="query end"; // QQ + thd->proc_info="query end"; + /* Two binlog-related cleanups: */ if (thd->one_shot_set) { /* @@ -3659,10 +4594,20 @@ purposes internal to the MySQL server", MYF(0)); thd->one_shot_set= 0; } } - if (res < 0) - send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0); + + /* + The return value for ROW_COUNT() is "implementation dependent" if + the statement is not DELETE, INSERT or UPDATE (or a CALL executing + such a statement), but -1 is what JDBC and ODBC wants. + */ + if (lex->sql_command != SQLCOM_CALL && uc_update_queries[lex->sql_command]<2) + thd->row_count_func= -1; + goto cleanup; error: + res= 1; + +cleanup: if (unlikely(slave_fake_lock)) { DBUG_PRINT("info",("undoing faked lock")); @@ -3671,7 +4616,7 @@ error: if (thd->lock == thd->locked_tables) thd->lock= 0; } - DBUG_VOID_RETURN; + DBUG_RETURN(res || thd->net.report_error); } @@ -3682,28 +4627,29 @@ error: SYNOPSIS check_one_table_access() thd Thread handler - privilege requested privelage - tables table list of command + privilege requested privilege + all_tables global table list of query RETURN 0 - OK 1 - access denied, error is sent to client */ -int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables) +bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) { - if (check_access(thd, privilege, tables->db, &tables->grant.privilege,0,0)) + if (check_access(thd, privilege, all_tables->db, + &all_tables->grant.privilege, 0, 0)) return 1; /* Show only 1 table for check_grant */ - if (grant_option && check_grant(thd, privilege, tables, 0, 1, 0)) + if (grant_option && check_grant(thd, privilege, all_tables, 0, 1, 0)) return 1; /* Check rights on tables of subselects and implictly opened tables */ TABLE_LIST *subselects_tables; - if ((subselects_tables= tables->next)) + if ((subselects_tables= all_tables->next_global)) { - if ((check_table_access(thd, SELECT_ACL, subselects_tables,0))) + if ((check_table_access(thd, SELECT_ACL, subselects_tables, 0))) return 1; } return 0; @@ -3733,14 +4679,14 @@ bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, bool dont_check_global_grants, bool no_errors) { - DBUG_ENTER("check_access"); - DBUG_PRINT("enter",("db: '%s' want_access: %lu master_access: %lu", - db ? db : "", want_access, thd->master_access)); #ifndef NO_EMBEDDED_ACCESS_CHECKS ulong db_access; bool db_is_pattern= test(want_access & GRANT_ACL); #endif ulong dummy; + DBUG_ENTER("check_access"); + DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu", + db ? db : "", want_access, thd->master_access)); if (save_priv) *save_priv=0; else @@ -3748,8 +4694,10 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if ((!db || !db[0]) && !thd->db && !dont_check_global_grants) { + DBUG_PRINT("error",("No database")); if (!no_errors) - send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */ + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), + MYF(0)); /* purecov: tested */ DBUG_RETURN(TRUE); /* purecov: tested */ } @@ -3773,11 +4721,14 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if (((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL)) || ! db && dont_check_global_grants) { // We can never grant this + DBUG_PRINT("error",("No possible access")); if (!no_errors) - net_printf(thd,ER_ACCESS_DENIED_ERROR, - thd->priv_user, - thd->priv_host, - thd->password ? ER(ER_YES) : ER(ER_NO));/* purecov: tested */ + my_error(ER_ACCESS_DENIED_ERROR, MYF(0), + thd->priv_user, + thd->priv_host, + (thd->password ? + ER(ER_YES) : + ER(ER_NO))); /* purecov: tested */ DBUG_RETURN(TRUE); /* purecov: tested */ } @@ -3791,18 +4742,24 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, DBUG_PRINT("info",("db_access: %lu", db_access)); /* Remove SHOW attribute and access rights we already have */ want_access &= ~(thd->master_access | EXTRA_ACL); + DBUG_PRINT("info",("db_access: %lu want_access: %lu", + db_access, want_access)); db_access= ((*save_priv=(db_access | thd->master_access)) & want_access); /* grant_option is set if there exists a single table or column grant */ if (db_access == want_access || - ((grant_option && !dont_check_global_grants) && - !(want_access & ~(db_access | TABLE_ACLS)))) + (grant_option && !dont_check_global_grants && + !(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS)))) DBUG_RETURN(FALSE); /* Ok */ + + DBUG_PRINT("error",("Access denied")); if (!no_errors) - net_printf(thd,ER_DBACCESS_DENIED_ERROR, - thd->priv_user, - thd->priv_host, - db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */ + my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), + thd->priv_user, + thd->priv_host, + (db ? db : (thd->db ? + thd->db : + "unknown"))); /* purecov: tested */ DBUG_RETURN(TRUE); /* purecov: tested */ #endif /* NO_EMBEDDED_ACCESS_CHECKS */ } @@ -3817,7 +4774,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, want_access Use should have any of these global rights WARNING - One gets access rigth if one has ANY of the rights in want_access + One gets access right if one has ANY of the rights in want_access This is useful as one in most cases only need one global right, but in some case we want to check if the user has SUPER or REPL_CLIENT_ACL rights. @@ -3836,8 +4793,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,ER_SPECIFIC_ACCESS_DENIED_ERROR, - command); + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command); return 1; #endif /* NO_EMBEDDED_ACCESS_CHECKS */ } @@ -3855,10 +4811,10 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, uint found=0; ulong found_access=0; TABLE_LIST *org_tables=tables; - for (; tables ; tables=tables->next) + for (; tables; tables= tables->next_global) { - if (tables->derived || - (tables->table && (int)tables->table->tmp_table) || + if (tables->derived || tables->schema_table || tables->belong_to_view || + (tables->table && (int)tables->table->s->tmp_table) || my_tz_check_n_skip_implicit_tables(&tables, thd->lex->time_zone_tables_used)) continue; @@ -3888,6 +4844,94 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, return FALSE; } + +bool +check_routine_access(THD *thd, ulong want_access,char *db, char *name, + bool is_proc, bool no_errors) +{ + TABLE_LIST tables[1]; + + bzero((char *)tables, sizeof(TABLE_LIST)); + tables->db= db; + tables->table_name= tables->alias= name; + + if ((thd->master_access & want_access) == want_access && !thd->db) + tables->grant.privilege= want_access; + else if (check_access(thd,want_access,db,&tables->grant.privilege, + 0, no_errors)) + return TRUE; + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (grant_option) + return check_grant_routine(thd, want_access, tables, is_proc, no_errors); +#endif + + return FALSE; +} + + +/* + Check if the routine has any of the routine privileges + + SYNOPSIS + check_some_routine_access() + thd Thread handler + db Database name + name Routine name + + RETURN + 0 ok + 1 error +*/ + +bool check_some_routine_access(THD *thd, const char *db, const char *name, + bool is_proc) +{ + ulong save_priv; + if (thd->master_access & SHOW_PROC_ACLS) + return FALSE; + if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, 0, 1) || + (save_priv & SHOW_PROC_ACLS)) + return FALSE; + return check_routine_level_acl(thd, db, name, is_proc); +} + + +/* + Check if the given table has any of the asked privileges + + SYNOPSIS + check_some_access() + thd Thread handler + want_access Bitmap of possible privileges to check for + + RETURN + 0 ok + 1 error +*/ + + +bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) +{ + ulong access; + DBUG_ENTER("check_some_access"); + + /* This loop will work as long as we have less than 32 privileges */ + for (access= 1; access < want_access ; access<<= 1) + { + if (access & want_access) + { + if (!check_access(thd, access, table->db, + &table->grant.privilege, 0, 1) && + !grant_option || !check_grant(thd, access, table, 0, 1, 1)) + DBUG_RETURN(0); + } + } + DBUG_PRINT("exit",("no matching access rights")); + DBUG_RETURN(1); +} + + bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list) { @@ -3896,7 +4940,7 @@ bool check_merge_table_access(THD *thd, char *db, { /* Check that all tables use the current database */ TABLE_LIST *tmp; - for (tmp=table_list; tmp ; tmp=tmp->next) + for (tmp= table_list; tmp; tmp= tmp->next_local) { if (!tmp->db || !tmp->db[0]) tmp->db=db; @@ -3910,13 +4954,14 @@ bool check_merge_table_access(THD *thd, char *db, static bool check_db_used(THD *thd,TABLE_LIST *tables) { - for (; tables ; tables=tables->next) + for (; tables; tables= tables->next_global) { if (!tables->db) { if (!(tables->db=thd->db)) { - send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */ + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), + MYF(0)); /* purecov: tested */ return TRUE; /* purecov: tested */ } } @@ -3924,6 +4969,7 @@ static bool check_db_used(THD *thd,TABLE_LIST *tables) return FALSE; } + /**************************************************************************** Check stack size; Send error if there isn't enough stack to continue ****************************************************************************/ @@ -3988,6 +5034,7 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) return 0; } + /**************************************************************************** Initialize global thd variables needed for query ****************************************************************************/ @@ -4020,13 +5067,13 @@ void mysql_reset_thd_for_next_command(THD *thd) DBUG_ENTER("mysql_reset_thd_for_next_command"); thd->free_list= 0; thd->select_number= 1; - thd->total_warn_count= 0; // Warnings for this query + 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= thd->time_zone_used= 0; thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | - SERVER_QUERY_NO_INDEX_USED | - SERVER_QUERY_NO_GOOD_INDEX_USED); + SERVER_QUERY_NO_INDEX_USED | + SERVER_QUERY_NO_GOOD_INDEX_USED); thd->tmp_table_used= 0; if (opt_bin_log) reset_dynamic(&thd->user_var_events); @@ -4041,6 +5088,8 @@ mysql_init_select(LEX *lex) SELECT_LEX *select_lex= lex->current_select; select_lex->init_select(); select_lex->select_limit= HA_POS_ERROR; + lex->orig_sql_command= SQLCOM_END; + lex->wild= 0; if (select_lex == &lex->select_lex) { DBUG_ASSERT(lex->result == 0); @@ -4053,18 +5102,21 @@ bool mysql_new_select(LEX *lex, bool move_down) { SELECT_LEX *select_lex; + DBUG_ENTER("mysql_new_select"); + if (!(select_lex= new(lex->thd->mem_root) SELECT_LEX())) - return 1; + DBUG_RETURN(1); select_lex->select_number= ++lex->thd->select_number; select_lex->init_query(); select_lex->init_select(); + select_lex->parent_lex= lex; if (move_down) { + SELECT_LEX_UNIT *unit; lex->subqueries= TRUE; /* first select_lex of subselect or derived table */ - SELECT_LEX_UNIT *unit; if (!(unit= new(lex->thd->mem_root) SELECT_LEX_UNIT())) - return 1; + DBUG_RETURN(1); unit->init_query(); unit->init_select(); @@ -4074,10 +5126,15 @@ mysql_new_select(LEX *lex, bool move_down) 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 + /* TODO: assign resolve_mode for fake subquery after merging with new tree */ } else { + if (lex->current_select->order_list.first && !lex->current_select->braces) + { + my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY"); + DBUG_RETURN(1); + } select_lex->include_neighbour(lex->current_select); SELECT_LEX_UNIT *unit= select_lex->master_unit(); SELECT_LEX *fake= unit->fake_select_lex; @@ -4088,7 +5145,7 @@ mysql_new_select(LEX *lex, bool move_down) fake SELECT_LEX for UNION processing */ if (!(fake= unit->fake_select_lex= new(lex->thd->mem_root) SELECT_LEX())) - return 1; + DBUG_RETURN(1); fake->include_standalone(unit, (SELECT_LEX_NODE**)&unit->fake_select_lex); fake->select_number= INT_MAX; @@ -4102,7 +5159,7 @@ mysql_new_select(LEX *lex, bool move_down) 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; + DBUG_RETURN(0); } /* @@ -4146,6 +5203,8 @@ void mysql_init_multi_delete(LEX *lex) HA_POS_ERROR; lex->select_lex.table_list.save_and_clear(&lex->auxilliary_table_list); lex->lock_option= using_update_log ? TL_READ_NO_INSERT : TL_READ; + lex->query_tables= 0; + lex->query_tables_last= &lex->query_tables; } @@ -4174,7 +5233,13 @@ void mysql_parse(THD *thd, char *inBuf, uint length) #endif { if (thd->net.report_error) - send_error(thd, 0, NullS); + { + if (thd->lex->sphead) + { + delete thd->lex->sphead; + thd->lex->sphead= NULL; + } + } else { /* @@ -4187,23 +5252,32 @@ void mysql_parse(THD *thd, char *inBuf, uint length) PROCESSLIST. Note that we don't need LOCK_thread_count to modify query_length. */ - if (lex->found_colon && - (thd->query_length= (ulong)(lex->found_colon - thd->query))) + if (lex->found_semicolon && + (thd->query_length= (ulong)(lex->found_semicolon - thd->query))) thd->query_length--; /* Actually execute the query */ mysql_execute_command(thd); query_cache_end_of_result(thd); } } + lex->unit.cleanup(); } else { DBUG_PRINT("info",("Command aborted. Fatal_error: %d", thd->is_fatal_error)); query_cache_abort(&thd->net); + lex->unit.cleanup(); + if (thd->lex->sphead) + { + /* Clean up after failed stored procedure/function */ + delete thd->lex->sphead; + thd->lex->sphead= NULL; + } } thd->proc_info="freeing items"; thd->end_statement(); + thd->cleanup_after_query(); DBUG_ASSERT(thd->change_list.is_empty()); } DBUG_VOID_RETURN; @@ -4224,17 +5298,20 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) { LEX *lex= thd->lex; bool error= 0; + DBUG_ENTER("mysql_test_parse_for_slave"); mysql_init_query(thd, (uchar*) inBuf, length); if (!yyparse((void*) thd) && ! thd->is_fatal_error && all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) - error= 1; /* Ignore question */ + error= 1; /* Ignore question */ thd->end_statement(); - return error; + thd->cleanup_after_query(); + DBUG_RETURN(error); } #endif + /***************************************************************************** ** Store field definition for create ** Return 0 if ok @@ -4251,13 +5328,11 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, { register create_field *new_field; 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, ER_TOO_LONG_IDENT, field_name); /* purecov: inspected */ + my_error(ER_TOO_LONG_IDENT, MYF(0), field_name); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */ } if (type_modifier & PRI_KEY_FLAG) @@ -4288,7 +5363,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, !(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC && type == FIELD_TYPE_TIMESTAMP)) { - net_printf(thd, ER_INVALID_DEFAULT, field_name); + my_error(ER_INVALID_DEFAULT, MYF(0), field_name); DBUG_RETURN(1); } else if (default_value->type() == Item::NULL_ITEM) @@ -4297,25 +5372,54 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG) { - net_printf(thd,ER_INVALID_DEFAULT,field_name); + my_error(ER_INVALID_DEFAULT, MYF(0), field_name); DBUG_RETURN(1); } } else if (type_modifier & AUTO_INCREMENT_FLAG) { - net_printf(thd, ER_INVALID_DEFAULT, field_name); + my_error(ER_INVALID_DEFAULT, MYF(0), field_name); DBUG_RETURN(1); } } if (on_update_value && type != FIELD_TYPE_TIMESTAMP) { - net_printf(thd, ER_INVALID_ON_UPDATE, field_name); + my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name); DBUG_RETURN(1); } - - if (!(new_field=new create_field())) + + if (!(new_field= new_create_field(thd, field_name, type, length, decimals, + type_modifier, default_value, on_update_value, + comment, change, interval_list, cs, uint_geom_type))) DBUG_RETURN(1); + + lex->create_list.push_back(new_field); + lex->last_field=new_field; + DBUG_RETURN(0); +} + +/***************************************************************************** +** Create field definition for create +** Return 0 on failure, otherwise return create_field instance +******************************************************************************/ + +create_field * +new_create_field(THD *thd, char *field_name, enum_field_types type, + char *length, char *decimals, + uint type_modifier, + Item *default_value, Item *on_update_value, + LEX_STRING *comment, + char *change, List<String> *interval_list, CHARSET_INFO *cs, + uint uint_geom_type) +{ + register create_field *new_field; + uint sign_len, allowed_type_modifier=0; + ulong max_field_charlength= MAX_FIELD_CHARLENGTH; + DBUG_ENTER("new_create_field"); + + if (!(new_field=new create_field())) + DBUG_RETURN(NULL); new_field->field=0; new_field->field_name=field_name; new_field->def= default_value; @@ -4328,24 +5432,21 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, new_field->length=0; new_field->change=change; new_field->interval=0; - new_field->pack_length=0; + new_field->pack_length= new_field->key_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; - new_field->comment.length=comment->length; - } + new_field->comment=*comment; + /* + Set flag if this field doesn't have a default value + */ + if (!default_value && !(type_modifier & AUTO_INCREMENT_FLAG) && + (type_modifier & NOT_NULL_FLAG) && type != FIELD_TYPE_TIMESTAMP) + new_field->flags|= NO_DEFAULT_VALUE_FLAG; + if (length && !(new_field->length= (uint) atoi(length))) length=0; /* purecov: inspected */ - uint sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1; + sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1; if (new_field->length && new_field->decimals && new_field->length < new_field->decimals+1 && @@ -4375,59 +5476,47 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, break; case FIELD_TYPE_NULL: break; - case FIELD_TYPE_DECIMAL: + case FIELD_TYPE_NEWDECIMAL: if (!length) { - if ((new_field->length= new_field->decimals)) - new_field->length++; - else + if (!(new_field->length= new_field->decimals)) new_field->length= 10; // Default length for DECIMAL } - if (new_field->length < MAX_FIELD_WIDTH) // Skip wrong argument + new_field->pack_length= + my_decimal_get_binary_size(new_field->length, new_field->decimals); + if (new_field->length <= DECIMAL_MAX_PRECISION && + new_field->length >= new_field->decimals) { - new_field->length+=sign_len; - if (new_field->decimals) - new_field->length++; + new_field->length= + my_decimal_precision_to_length(new_field->length, new_field->decimals, + type_modifier & UNSIGNED_FLAG); + break; } + my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name); + DBUG_RETURN(NULL); + case MYSQL_TYPE_VARCHAR: + /* + Long VARCHAR's are automaticly converted to blobs in mysql_prepare_table + if they don't have a default value + */ + max_field_charlength= MAX_FIELD_VARCHARLENGTH; + break; + case MYSQL_TYPE_STRING: 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; - else 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,ER_BLOB_CANT_HAVE_DEFAULT,field_name); /* purecov: inspected */ - DBUG_RETURN(1); /* purecov: inspected */ + my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), + field_name); /* purecov: inspected */ + DBUG_RETURN(NULL); } new_field->def=0; } @@ -4446,8 +5535,8 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, uint tmp_length=new_field->length; if (tmp_length > PRECISION_FOR_DOUBLE) { - net_printf(thd,ER_WRONG_FIELD_SPEC,field_name); - DBUG_RETURN(1); + my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name); + DBUG_RETURN(NULL); } else if (tmp_length > PRECISION_FOR_FLOAT) { @@ -4519,11 +5608,11 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, If we have TIMESTAMP NULL column without explicit DEFAULT value we treat it as having DEFAULT NULL attribute. */ - new_field->unireg_check= on_update_value ? - Field::TIMESTAMP_UN_FIELD : - (new_field->flags & NOT_NULL_FLAG ? - Field::TIMESTAMP_OLD_FIELD: - Field::NONE); + new_field->unireg_check= (on_update_value ? + Field::TIMESTAMP_UN_FIELD : + (new_field->flags & NOT_NULL_FLAG ? + Field::TIMESTAMP_OLD_FIELD: + Field::NONE)); } break; case FIELD_TYPE_DATE: // Old date type @@ -4543,8 +5632,8 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, { if (interval_list->elements > sizeof(longlong)*8) { - net_printf(thd,ER_TOO_BIG_SET,field_name); /* purecov: inspected */ - DBUG_RETURN(1); /* purecov: inspected */ + my_error(ER_TOO_BIG_SET, MYF(0), field_name); /* purecov: inspected */ + DBUG_RETURN(NULL); } new_field->pack_length= get_set_pack_length(interval_list->elements); @@ -4558,8 +5647,8 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, when we know the character set of the column */ new_field->length= 1; + break; } - break; case FIELD_TYPE_ENUM: { // Should be safe @@ -4570,37 +5659,50 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, while ((tmp= it++)) new_field->interval_list.push_back(tmp); new_field->length= 1; // See comment for FIELD_TYPE_SET above. - } + break; + } + case MYSQL_TYPE_VAR_STRING: + DBUG_ASSERT(0); // Impossible break; + case MYSQL_TYPE_BIT: + { + if (!length) + new_field->length= 1; + if (new_field->length > MAX_BIT_FIELD_LENGTH) + { + my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), field_name, + MAX_BIT_FIELD_LENGTH); + DBUG_RETURN(NULL); + } + new_field->pack_length= (new_field->length + 7) / 8; + break; + } + case FIELD_TYPE_DECIMAL: + DBUG_ASSERT(0); /* Was obsolete */ } - 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_GEOMETRY)) + if (!(new_field->flags & BLOB_FLAG) && + ((new_field->length > max_field_charlength && type != FIELD_TYPE_SET && + type != FIELD_TYPE_ENUM && + (type != MYSQL_TYPE_VARCHAR || default_value)) || + (!new_field->length && + type != MYSQL_TYPE_STRING && + type != MYSQL_TYPE_VARCHAR && type != FIELD_TYPE_GEOMETRY))) { - net_printf(thd,ER_TOO_BIG_FIELDLENGTH,field_name, - MAX_FIELD_CHARLENGTH); /* purecov: inspected */ - DBUG_RETURN(1); /* purecov: inspected */ + my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), + field_name, max_field_charlength); /* purecov: inspected */ + DBUG_RETURN(NULL); } type_modifier&= AUTO_INCREMENT_FLAG; if ((~allowed_type_modifier) & type_modifier) { - net_printf(thd,ER_WRONG_FIELD_SPEC,field_name); - DBUG_RETURN(1); + my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name); + DBUG_RETURN(NULL); } - if (!new_field->pack_length) - new_field->pack_length=calc_pack_length(new_field->sql_type == - FIELD_TYPE_VAR_STRING ? - FIELD_TYPE_STRING : - new_field->sql_type, - new_field->length); - lex->create_list.push_back(new_field); - lex->last_field=new_field; - DBUG_RETURN(0); + DBUG_RETURN(new_field); } + /* Store position for column in ALTER TABLE .. ADD column */ void store_position_for_column(const char *name) @@ -4639,7 +5741,6 @@ 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(system_charset_info) && (l = my_ismbchar(system_charset_info, name, strend))) { @@ -4672,6 +5773,7 @@ bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc) order->asc = asc; order->free_me=0; order->used=0; + order->counter_used= 0; list.link_in_list((byte*) order,(byte**) &order->next); DBUG_RETURN(0); } @@ -4707,6 +5809,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, { register TABLE_LIST *ptr; char *alias_str; + LEX *lex= thd->lex; DBUG_ENTER("add_table_to_list"); if (!table) @@ -4715,7 +5818,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, if (check_table_name(table->table.str,table->table.length) || table->db.str && check_db_name(table->db.str)) { - net_printf(thd, ER_WRONG_TABLE_NAME, table->table.str); + my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str); DBUG_RETURN(0); } @@ -4723,7 +5826,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, { if (table->sel) { - net_printf(thd,ER_DERIVED_MUST_HAVE_ALIAS); + my_message(ER_DERIVED_MUST_HAVE_ALIAS, + ER(ER_DERIVED_MUST_HAVE_ALIAS), MYF(0)); DBUG_RETURN(0); } if (!(alias_str=thd->memdup(alias_str,table->table.length+1))) @@ -4753,13 +5857,29 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->alias= alias_str; 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->table_name=table->table.str; + ptr->table_name_length=table->table.length; 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 (!my_strcasecmp(system_charset_info, ptr->db, + information_schema_name.str)) + { + ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, ptr->table_name); + if (!schema_table || + (schema_table->hidden && + lex->orig_sql_command == SQLCOM_END)) // not a 'show' command + { + my_error(ER_UNKNOWN_TABLE, MYF(0), + ptr->table_name, information_schema_name.str); + DBUG_RETURN(0); + } + ptr->schema_table_name= ptr->table_name; + ptr->schema_table= schema_table; + } + ptr->select_lex= lex->current_select; ptr->cacheable_table= 1; if (use_index_arg) ptr->use_index=(List<String> *) thd->memdup((gptr) use_index_arg, @@ -4773,22 +5893,264 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, { for (TABLE_LIST *tables=(TABLE_LIST*) table_list.first ; tables ; - tables=tables->next) + tables=tables->next_local) { if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) && !strcmp(ptr->db, tables->db)) { - net_printf(thd,ER_NONUNIQ_TABLE,alias_str); /* purecov: tested */ + my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str); /* purecov: tested */ DBUG_RETURN(0); /* purecov: tested */ } } } - table_list.link_in_list((byte*) ptr, (byte**) &ptr->next); + /* Link table in local list (list for current select) */ + table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local); + /* Link table in global list (all used tables) */ + lex->add_to_query_tables(ptr); DBUG_RETURN(ptr); } /* + Initialize a new table list for a nested join + + SYNOPSIS + init_nested_join() + thd current thread + + DESCRIPTION + The function initializes a structure of the TABLE_LIST type + for a nested join. It sets up its nested join list as empty. + The created structure is added to the front of the current + join list in the st_select_lex object. Then the function + changes the current nest level for joins to refer to the newly + created empty list after having saved the info on the old level + in the initialized structure. + + RETURN VALUE + 0, if success + 1, otherwise +*/ + +bool st_select_lex::init_nested_join(THD *thd) +{ + TABLE_LIST *ptr; + NESTED_JOIN *nested_join; + DBUG_ENTER("init_nested_join"); + + if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))) || + !(nested_join= ptr->nested_join= + (NESTED_JOIN *) thd->calloc(sizeof(NESTED_JOIN)))) + DBUG_RETURN(1); + join_list->push_front(ptr); + ptr->embedding= embedding; + ptr->join_list= join_list; + embedding= ptr; + join_list= &nested_join->join_list; + join_list->empty(); + DBUG_RETURN(0); +} + + +/* + End a nested join table list + + SYNOPSIS + end_nested_join() + thd current thread + + DESCRIPTION + The function returns to the previous join nest level. + If the current level contains only one member, the function + moves it one level up, eliminating the nest. + + RETURN VALUE + Pointer to TABLE_LIST element added to the total table list, if success + 0, otherwise +*/ + +TABLE_LIST *st_select_lex::end_nested_join(THD *thd) +{ + TABLE_LIST *ptr; + NESTED_JOIN *nested_join; + DBUG_ENTER("end_nested_join"); + + DBUG_ASSERT(embedding); + ptr= embedding; + join_list= ptr->join_list; + embedding= ptr->embedding; + nested_join= ptr->nested_join; + if (nested_join->join_list.elements == 1) + { + TABLE_LIST *embedded= nested_join->join_list.head(); + join_list->pop(); + embedded->join_list= join_list; + embedded->embedding= embedding; + join_list->push_front(embedded); + ptr= embedded; + } + else if (nested_join->join_list.elements == 0) + { + join_list->pop(); + ptr= 0; // return value + } + DBUG_RETURN(ptr); +} + + +/* + Nest last join operation + + SYNOPSIS + nest_last_join() + thd current thread + + DESCRIPTION + The function nest last join operation as if it was enclosed in braces. + + RETURN VALUE + Pointer to TABLE_LIST element created for the new nested join, if success + 0, otherwise +*/ + +TABLE_LIST *st_select_lex::nest_last_join(THD *thd) +{ + TABLE_LIST *ptr; + NESTED_JOIN *nested_join; + DBUG_ENTER("nest_last_join"); + + if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))) || + !(nested_join= ptr->nested_join= + (NESTED_JOIN *) thd->calloc(sizeof(NESTED_JOIN)))) + DBUG_RETURN(0); + ptr->embedding= embedding; + ptr->join_list= join_list; + List<TABLE_LIST> *embedded_list= &nested_join->join_list; + embedded_list->empty(); + for (int i=0; i < 2; i++) + { + TABLE_LIST *table= join_list->pop(); + table->join_list= embedded_list; + table->embedding= ptr; + embedded_list->push_back(table); + } + join_list->push_front(ptr); + nested_join->used_tables= nested_join->not_null_tables= (table_map) 0; + DBUG_RETURN(ptr); +} + + +/* + Save names for a join with using clause + + SYNOPSIS + save_names_for_using_list + tab1 left table in join + tab2 right table in join + + DESCRIPTION + The function saves the full names of the tables in st_select_lex + to be able to build later an on expression to replace the using clause. + + RETURN VALUE + None +*/ + +void st_select_lex::save_names_for_using_list(TABLE_LIST *tab1, + TABLE_LIST *tab2) +{ + while (tab1->nested_join) + { + tab1= tab1->nested_join->join_list.head(); + } + db1= tab1->db; + table1= tab1->alias; + while (tab2->nested_join) + { + TABLE_LIST *next; + List_iterator_fast<TABLE_LIST> it(tab2->nested_join->join_list); + tab2= it++; + while ((next= it++)) + tab2= next; + } + db2= tab2->db; + table2= tab2->alias; +} + + +/* + Add a table to the current join list + + SYNOPSIS + add_joined_table() + table the table to add + + DESCRIPTION + The function puts a table in front of the current join list + of st_select_lex object. + Thus, joined tables are put into this list in the reverse order + (the most outer join operation follows first). + + RETURN VALUE + None +*/ + +void st_select_lex::add_joined_table(TABLE_LIST *table) +{ + DBUG_ENTER("add_joined_table"); + join_list->push_front(table); + table->join_list= join_list; + table->embedding= embedding; + DBUG_VOID_RETURN; +} + + +/* + Convert a right join into equivalent left join + + SYNOPSIS + convert_right_join() + thd current thread + + DESCRIPTION + The function takes the current join list t[0],t[1] ... and + effectively converts it into the list t[1],t[0] ... + Although the outer_join flag for the new nested table contains + JOIN_TYPE_RIGHT, it will be handled as the inner table of a left join + operation. + + EXAMPLES + SELECT * FROM t1 RIGHT JOIN t2 ON on_expr => + SELECT * FROM t2 LEFT JOIN t1 ON on_expr + + SELECT * FROM t1,t2 RIGHT JOIN t3 ON on_expr => + SELECT * FROM t1,t3 LEFT JOIN t2 ON on_expr + + SELECT * FROM t1,t2 RIGHT JOIN (t3,t4) ON on_expr => + SELECT * FROM t1,(t3,t4) LEFT JOIN t2 ON on_expr + + SELECT * FROM t1 LEFT JOIN t2 ON on_expr1 RIGHT JOIN t3 ON on_expr2 => + SELECT * FROM t3 LEFT JOIN (t1 LEFT JOIN t2 ON on_expr2) ON on_expr1 + + RETURN + Pointer to the table representing the inner table, if success + 0, otherwise +*/ + +TABLE_LIST *st_select_lex::convert_right_join() +{ + TABLE_LIST *tab2= join_list->pop(); + TABLE_LIST *tab1= join_list->pop(); + DBUG_ENTER("convert_right_join"); + + join_list->push_front(tab2); + join_list->push_front(tab1); + tab1->outer_join|= JOIN_TYPE_RIGHT; + + DBUG_RETURN(tab1); +} + +/* Set lock for all tables in current select level SYNOPSIS: @@ -4808,9 +6170,9 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type) DBUG_PRINT("enter", ("lock_type: %d for_update: %d", lock_type, for_update)); - for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first ; - tables ; - tables=tables->next) + for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first; + tables; + tables= tables->next_local) { tables->lock_type= lock_type; tables->updating= for_update; @@ -4827,7 +6189,7 @@ void add_join_on(TABLE_LIST *b,Item *expr) b->on_expr=expr; else { - // This only happens if you have both a right and left join + /* This only happens if you have both a right and left join */ b->on_expr=new Item_cond_and(b->on_expr,expr); } b->on_expr->top_level_item(); @@ -4842,7 +6204,7 @@ void add_join_on(TABLE_LIST *b,Item *expr) add_join_natural() a Table to do normal join with b Do normal join with this table - + IMPLEMENTATION This function just marks that table b should be joined with a. The function setup_cond() will create in b->on_expr a list @@ -4889,8 +6251,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, { acl_reload(thd); grant_reload(thd); - if (mqh_used) - reset_mqh(thd,(LEX_USER *) NULL,TRUE); + reset_mqh((LEX_USER *)NULL, TRUE); } #endif if (options & REFRESH_LOG) @@ -4900,23 +6261,16 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, 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); + mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE); #ifdef HAVE_REPLICATION - if (mysql_bin_log.is_open() && 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); - } pthread_mutex_lock(&LOCK_active_mi); rotate_relay_log(active_mi); pthread_mutex_unlock(&LOCK_active_mi); @@ -4930,7 +6284,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, if (options & REFRESH_QUERY_CACHE_FREE) { query_cache.pack(); // FLUSH QUERY CACHE - options &= ~REFRESH_QUERY_CACHE; //don't flush all cache, just free memory + options &= ~REFRESH_QUERY_CACHE; // Don't flush cache, just free memory } if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE)) { @@ -4954,7 +6308,12 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, return 1; result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables); - make_global_read_lock_block_commit(thd); + if (make_global_read_lock_block_commit(thd)) + { + /* Don't leave things in a half-locked state */ + unlock_global_read_lock(thd); + return 1; + } } else result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables); @@ -4962,7 +6321,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, } if (options & REFRESH_HOSTS) hostname_cache_refresh(); - if (options & REFRESH_STATUS) + if (thd && (options & REFRESH_STATUS)) refresh_status(); if (options & REFRESH_THREADS) flush_thread_cache(); @@ -4992,7 +6351,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, } #endif if (options & REFRESH_USER_RESOURCES) - reset_mqh(thd,(LEX_USER *) NULL); + reset_mqh((LEX_USER *) NULL); if (write_to_binlog) *write_to_binlog= tmp_write_to_binlog; return result; @@ -5010,7 +6369,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, This is written such that we have a short lock on LOCK_thread_count */ -void kill_one_thread(THD *thd, ulong id) +void kill_one_thread(THD *thd, ulong id, bool only_kill_query) { THD *tmp; uint error=ER_NO_SUCH_THREAD; @@ -5030,7 +6389,7 @@ void kill_one_thread(THD *thd, ulong id) if ((thd->master_access & SUPER_ACL) || !strcmp(thd->user,tmp->user)) { - tmp->awake(1 /*prepare to die*/); + tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION); error=0; } else @@ -5041,7 +6400,7 @@ void kill_one_thread(THD *thd, ulong id) if (!error) send_ok(thd); else - net_printf(thd,error,id); + my_error(error, MYF(0), id); } @@ -5054,6 +6413,13 @@ static void refresh_status(void) { if (ptr->type == SHOW_LONG) *(ulong*) ptr->value= 0; + else if (ptr->type == SHOW_LONG_STATUS) + { + THD *thd= current_thd; + /* We must update the global status before cleaning up the thread */ + add_to_status(&global_status_var, &thd->status_var); + bzero((char*) &thd->status_var, sizeof(thd->status_var)); + } } /* Reset the counters of all key caches (default and named). */ process_key_caches(reset_key_cache_counters); @@ -5102,12 +6468,13 @@ static bool append_file_to_dir(THD *thd, const char **filename_ptr, bool check_simple_select() { THD *thd= current_thd; - if (thd->lex->current_select != &thd->lex->select_lex) + LEX *lex= thd->lex; + if (lex->current_select != &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, ER_CANT_USE_OPTION_HERE, command); + strmake(command, lex->yylval->symbol.str, + min(lex->yylval->symbol.length, sizeof(command)-1)); + my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command); return 1; } return 0; @@ -5124,18 +6491,16 @@ bool check_simple_select() SYNOPSIS check_multi_update_lock() thd Current thread - tables List of user-supplied tables - fields List of fields requiring update RETURN VALUES 0 ok 1 error */ -static bool check_multi_update_lock(THD *thd, TABLE_LIST *tables, - List<Item> *fields, SELECT_LEX *select_lex) +static bool check_multi_update_lock(THD *thd) { bool res= 1; - TABLE_LIST *table; + LEX *lex= thd->lex; + TABLE_LIST *table, *tables= lex->query_tables; DBUG_ENTER("check_multi_update_lock"); if (check_db_used(thd, tables)) @@ -5145,18 +6510,18 @@ static bool check_multi_update_lock(THD *thd, TABLE_LIST *tables, Ensure that we have UPDATE or SELECT privilege for each table The exact privilege is checked in mysql_multi_update() */ - for (table= tables ; table ; table= table->next) + for (table= tables ; table ; table= table->next_local) { - TABLE_LIST *save= table->next; - table->next= 0; + TABLE_LIST *save= table->next_local; + table->next_local= 0; if ((check_access(thd, UPDATE_ACL, table->db, &table->grant.privilege,0,1) || (grant_option && check_grant(thd, UPDATE_ACL, table,0,1,1))) && check_one_table_access(thd, SELECT_ACL, table)) goto error; - table->next= save; + table->next_local= save; } - if (mysql_multi_update_lock(thd, tables, fields, select_lex)) + if (mysql_multi_update_prepare(thd)) goto error; res= 0; @@ -5247,25 +6612,24 @@ Item * all_any_subquery_creator(Item *left_expr, One should normally create all indexes with CREATE TABLE or ALTER TABLE. */ -int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys) +bool mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys) { List<create_field> fields; ALTER_INFO alter_info; alter_info.flags= ALTER_ADD_INDEX; - alter_info.is_simple= 0; HA_CREATE_INFO create_info; DBUG_ENTER("mysql_create_index"); bzero((char*) &create_info,sizeof(create_info)); create_info.db_type=DB_TYPE_DEFAULT; create_info.default_table_charset= thd->variables.collation_database; - DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name, + DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->table_name, &create_info, table_list, fields, keys, 0, (ORDER*)0, - DUP_ERROR, 0, &alter_info)); + DUP_ERROR, 0, &alter_info, 1)); } -int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info) +bool mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info) { List<create_field> fields; List<Key> keys; @@ -5276,11 +6640,10 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info) create_info.default_table_charset= thd->variables.collation_database; alter_info->clear(); alter_info->flags= ALTER_DROP_INDEX; - alter_info->is_simple= 0; - DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name, + DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->table_name, &create_info, table_list, fields, keys, 0, (ORDER*)0, - DUP_ERROR, 0, alter_info)); + DUP_ERROR, 0, alter_info, 1)); } @@ -5290,33 +6653,31 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info) SYNOPSIS multi_update_precheck() thd Thread handler - tables Global table list + tables Global/local table list (have to be the same) RETURN VALUE - 0 OK - 1 Error (message is sent to user) - -1 Error (message is not sent to user) + FALSE OK + TRUE Error */ -int multi_update_precheck(THD *thd, TABLE_LIST *tables) +bool multi_update_precheck(THD *thd, TABLE_LIST *tables) { - DBUG_ENTER("multi_update_precheck"); const char *msg= 0; TABLE_LIST *table; LEX *lex= thd->lex; SELECT_LEX *select_lex= &lex->select_lex; - TABLE_LIST *update_list= (TABLE_LIST*)select_lex->table_list.first; + DBUG_ENTER("multi_update_precheck"); if (select_lex->item_list.elements != lex->value_list.elements) { - my_error(ER_WRONG_VALUE_COUNT, MYF(0)); - DBUG_RETURN(-1); + my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0)); + DBUG_RETURN(TRUE); } /* Ensure that we have UPDATE or SELECT privilege for each table The exact privilege is checked in mysql_multi_update() */ - for (table= update_list; table; table= table->next) + for (table= tables; table; table= table->next_local) { if (table->derived) table->grant.privilege= SELECT_ACL; @@ -5324,17 +6685,12 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) &table->grant.privilege, 0, 1) || grant_option && check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) && - (check_access(thd, SELECT_ACL, table->db, - &table->grant.privilege, 0, 0) || - grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))) - DBUG_RETURN(1); + (check_access(thd, SELECT_ACL, table->db, + &table->grant.privilege, 0, 0) || + grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))) + DBUG_RETURN(TRUE); - /* - We assign following flag only to copy of table, because it will - be checked only if query contains subqueries i.e. only if copy exists - */ - if (table->table_list) - table->table_list->table_in_update_from_clause= 1; + table->table_in_first_from_clause= 1; } /* Is there tables of subqueries? @@ -5342,27 +6698,16 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) if (&lex->select_lex != lex->all_selects_list) { DBUG_PRINT("info",("Checking sub query list")); - for (table= tables; table; table= table->next) + for (table= tables; table; table= table->next_global) { - if (my_tz_check_n_skip_implicit_tables(&table, - lex->time_zone_tables_used)) - continue; - else if (table->table_in_update_from_clause) - { - /* - If we check table by local TABLE_LIST copy then we should copy - grants to global table list, because it will be used for table - opening. - */ - if (table->table_list) - table->grant= table->table_list->grant; - } - else if (!table->derived) + if (!my_tz_check_n_skip_implicit_tables(&table, + lex->time_zone_tables_used) && + !table->table_in_first_from_clause) { if (check_access(thd, SELECT_ACL, table->db, &table->grant.privilege, 0, 0) || grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)) - DBUG_RETURN(1); + DBUG_RETURN(TRUE); } } } @@ -5375,9 +6720,9 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) if (msg) { my_error(ER_WRONG_USAGE, MYF(0), "UPDATE", msg); - DBUG_RETURN(-1); + DBUG_RETURN(TRUE); } - DBUG_RETURN(0); + DBUG_RETURN(FALSE); } /* @@ -5386,23 +6731,21 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables) SYNOPSIS multi_delete_precheck() thd Thread handler - tables Global table list + tables Global/local table list table_count Pointer to table counter RETURN VALUE - 0 OK - 1 error (message is sent to user) - -1 error (message is not sent to user) + FALSE OK + TRUE error */ -int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) +bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) { - DBUG_ENTER("multi_delete_precheck"); SELECT_LEX *select_lex= &thd->lex->select_lex; TABLE_LIST *aux_tables= (TABLE_LIST *)thd->lex->auxilliary_table_list.first; - TABLE_LIST *delete_tables= (TABLE_LIST *)select_lex->table_list.first; TABLE_LIST *target_tbl; + DBUG_ENTER("multi_delete_precheck"); *table_count= 0; @@ -5411,18 +6754,19 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) if (check_db_used(thd, tables) || check_db_used(thd,aux_tables) || check_table_access(thd,SELECT_ACL, tables,0) || check_table_access(thd,DELETE_ACL, aux_tables,0)) - DBUG_RETURN(1); + DBUG_RETURN(TRUE); if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where) { - my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE, MYF(0)); - DBUG_RETURN(-1); + my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE, + ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0)); + DBUG_RETURN(TRUE); } - for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next) + for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next_local) { (*table_count)++; /* All tables in aux_tables must be found in FROM PART */ TABLE_LIST *walk; - for (walk= delete_tables; walk; walk= walk->next) + for (walk= tables; walk; walk= walk->next_local) { if (!my_strcasecmp(table_alias_charset, target_tbl->alias, walk->alias) && @@ -5431,28 +6775,22 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) } if (!walk) { - my_error(ER_UNKNOWN_TABLE, MYF(0), target_tbl->real_name, - "MULTI DELETE"); - DBUG_RETURN(-1); - } - if (walk->derived) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), target_tbl->real_name, - "DELETE"); - DBUG_RETURN(-1); + my_error(ER_UNKNOWN_TABLE, MYF(0), + target_tbl->table_name, "MULTI DELETE"); + DBUG_RETURN(TRUE); } walk->lock_type= target_tbl->lock_type; - target_tbl->table_list= walk; // Remember corresponding table + target_tbl->correspondent_table= walk; // Remember corresponding table /* in case of subselects, we need to set lock_type in * corresponding table in list of all tables */ - if (walk->table_list) + if (walk->correspondent_table) { - target_tbl->table_list= walk->table_list; - walk->table_list->lock_type= walk->lock_type; + target_tbl->correspondent_table= walk->correspondent_table; + walk->correspondent_table->lock_type= walk->lock_type; } } - DBUG_RETURN(0); + DBUG_RETURN(FALSE); } @@ -5465,21 +6803,20 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count) tables Global table list RETURN VALUE - 0 OK - 1 Error (message is sent to user) - -1 Error (message is not sent to user) + FALSE OK + TRUE Error */ -int update_precheck(THD *thd, TABLE_LIST *tables) +bool update_precheck(THD *thd, TABLE_LIST *tables) { DBUG_ENTER("update_precheck"); if (thd->lex->select_lex.item_list.elements != thd->lex->value_list.elements) { - my_error(ER_WRONG_VALUE_COUNT, MYF(0)); - DBUG_RETURN(-1); + my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0)); + DBUG_RETURN(TRUE); } - DBUG_RETURN((check_db_used(thd, tables) || - check_one_table_access(thd, UPDATE_ACL, tables)) ? 1 : 0); + DBUG_RETURN(check_db_used(thd, tables) || + check_one_table_access(thd, UPDATE_ACL, tables)); } @@ -5492,19 +6829,18 @@ int update_precheck(THD *thd, TABLE_LIST *tables) tables Global table list RETURN VALUE - 0 OK - 1 error (message is sent to user) - -1 error (message is not sent to user) + FALSE OK + TRUE error */ -int delete_precheck(THD *thd, TABLE_LIST *tables) +bool delete_precheck(THD *thd, TABLE_LIST *tables) { DBUG_ENTER("delete_precheck"); if (check_one_table_access(thd, DELETE_ACL, tables)) - DBUG_RETURN(1); + DBUG_RETURN(TRUE); /* Set privilege for the WHERE clause */ tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege); - DBUG_RETURN(0); + DBUG_RETURN(FALSE); } @@ -5517,12 +6853,11 @@ int delete_precheck(THD *thd, TABLE_LIST *tables) tables Global table list RETURN VALUE - 0 OK - 1 error (message is sent to user) - -1 error (message is not sent to user) + FALSE OK + TRUE error */ -int insert_precheck(THD *thd, TABLE_LIST *tables) +bool insert_precheck(THD *thd, TABLE_LIST *tables) { LEX *lex= thd->lex; DBUG_ENTER("insert_precheck"); @@ -5531,19 +6866,19 @@ int insert_precheck(THD *thd, TABLE_LIST *tables) Check that we have modify privileges for the first table and select privileges for the rest */ - ulong privilege= INSERT_ACL | - (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) | - (lex->duplicates == DUP_UPDATE ? UPDATE_ACL : 0); + ulong privilege= (INSERT_ACL | + (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) | + (lex->value_list.elements ? UPDATE_ACL : 0)); if (check_one_table_access(thd, privilege, tables)) - DBUG_RETURN(1); + DBUG_RETURN(TRUE); if (lex->update_list.elements != lex->value_list.elements) { - my_error(ER_WRONG_VALUE_COUNT, MYF(0)); - DBUG_RETURN(-1); + my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0)); + DBUG_RETURN(TRUE); } - DBUG_RETURN(0); + DBUG_RETURN(FALSE); } @@ -5557,17 +6892,17 @@ int insert_precheck(THD *thd, TABLE_LIST *tables) create_table Table which will be created RETURN VALUE - 0 OK - 1 Error (message is sent to user) + FALSE OK + TRUE Error */ -int create_table_precheck(THD *thd, TABLE_LIST *tables, - TABLE_LIST *create_table) +bool create_table_precheck(THD *thd, TABLE_LIST *tables, + TABLE_LIST *create_table) { LEX *lex= thd->lex; SELECT_LEX *select_lex= &lex->select_lex; ulong want_priv; - int error= 1; // Error message is given + bool error= TRUE; // Error message is given DBUG_ENTER("create_table_precheck"); want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ? @@ -5588,37 +6923,27 @@ int create_table_precheck(THD *thd, TABLE_LIST *tables, /* Check permissions for used tables in CREATE TABLE ... SELECT */ /* - For temporary tables or PREPARED STATEMETNS we don't have to check - if the created table exists + Only do the check for PS, becasue we on execute we have to check that + against the opened tables to ensure we don't use a table that is part + of the view (which can only be done after the table has been opened). */ - if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && - ! thd->current_arena->is_stmt_prepare() && - find_real_table_in_list(tables, create_table->db, - create_table->real_name)) - { - net_printf(thd,ER_UPDATE_TABLE_USED, create_table->real_name); - - goto err; - } - if (lex->create_info.used_fields & HA_CREATE_USED_UNION) + if (thd->current_arena->is_stmt_prepare()) { - TABLE_LIST *tab; - for (tab= tables; tab; tab= tab->next) + /* + For temporary tables we don't have to check if the created table exists + */ + if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && + find_table_in_global_list(tables, create_table->db, + create_table->table_name)) { - if (find_real_table_in_list((TABLE_LIST*) lex->create_info. - merge_list.first, - tables->db, tab->real_name)) - { - net_printf(thd, ER_UPDATE_TABLE_USED, tab->real_name); - goto err; - } - } - } - + error= FALSE; + goto err; + } + } if (tables && check_table_access(thd, SELECT_ACL, tables,0)) goto err; } - error= 0; + error= FALSE; err: DBUG_RETURN(error); @@ -5630,7 +6955,7 @@ err: SYNOPSIS negate_expression() - thd therad handler + thd thread handler expr expression for negation RETURN |