diff options
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r-- | sql/sql_parse.cc | 3251 |
1 files changed, 1349 insertions, 1902 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 084bcfc3c76..ae347bebb47 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -16,41 +16,22 @@ #define MYSQL_LEX 1 #include "mysql_priv.h" #include "sql_repl.h" +#include "rpl_filter.h" #include "repl_failsafe.h" #include <m_ctype.h> #include <myisam.h> #include <my_dir.h> -#ifdef HAVE_INNOBASE_DB -#include "ha_innodb.h" -#endif - -#ifdef HAVE_NDBCLUSTER_DB -#include "ha_ndbcluster.h" -#endif - #include "sp_head.h" #include "sp.h" #include "sp_cache.h" +#include "events.h" #include "sql_trigger.h" -#ifdef HAVE_OPENSSL -/* - Without SSL the handshake consists of one packet. This packet - has both client capabilites and scrambled password. - With SSL the handshake might consist of two packets. If the first - packet (client capabilities) has CLIENT_SSL flag set, we have to - switch to SSL and read the second packet. The scrambled password - is in the second packet and client_capabilites field will be ignored. - Maybe it is better to accept flags other than CLIENT_SSL from the - second packet? +/** + @defgroup Runtime_Environment Runtime Environment + @{ */ -#define SSL_HANDSHAKE_SIZE 2 -#define NORMAL_HANDSHAKE_SIZE 6 -#define MIN_HANDSHAKE_SIZE 2 -#else -#define MIN_HANDSHAKE_SIZE 6 -#endif /* HAVE_OPENSSL */ /* Used in error handling only */ #define SP_TYPE_STRING(LP) \ @@ -62,55 +43,49 @@ (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \ "FUNCTION" : "PROCEDURE") -#ifdef SOLARIS -extern "C" int gethostname(char *name, int namelen); -#endif - -#ifndef NO_EMBEDDED_ACCESS_CHECKS -static void time_out_user_resource_limits(THD *thd, USER_CONN *uc); -static int check_for_max_user_connections(THD *thd, USER_CONN *uc); -static void decrease_user_connections(USER_CONN *uc); -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ -static bool check_db_used(THD *thd,TABLE_LIST *tables); -static void remove_escape(char *name); -static bool append_file_to_dir(THD *thd, const char **filename_ptr, - const char *table_name); +static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables); static bool check_show_create_table_access(THD *thd, TABLE_LIST *table); const char *any_db="*any*"; // Special symbol for check_access -const char *command_name[]={ - "Sleep", "Quit", "Init DB", "Query", "Field List", "Create DB", - "Drop DB", "Refresh", "Shutdown", "Statistics", "Processlist", - "Connect","Kill","Debug","Ping","Time","Delayed insert","Change user", - "Binlog Dump","Table Dump", "Connect Out", "Register Slave", - "Prepare", "Execute", "Long Data", "Close stmt", - "Reset stmt", "Set option", "Fetch", - "Error" // Last command number +const LEX_STRING command_name[]={ + { C_STRING_WITH_LEN("Sleep") }, + { C_STRING_WITH_LEN("Quit") }, + { C_STRING_WITH_LEN("Init DB") }, + { C_STRING_WITH_LEN("Query") }, + { C_STRING_WITH_LEN("Field List") }, + { C_STRING_WITH_LEN("Create DB") }, + { C_STRING_WITH_LEN("Drop DB") }, + { C_STRING_WITH_LEN("Refresh") }, + { C_STRING_WITH_LEN("Shutdown") }, + { C_STRING_WITH_LEN("Statistics") }, + { C_STRING_WITH_LEN("Processlist") }, + { C_STRING_WITH_LEN("Connect") }, + { C_STRING_WITH_LEN("Kill") }, + { C_STRING_WITH_LEN("Debug") }, + { C_STRING_WITH_LEN("Ping") }, + { C_STRING_WITH_LEN("Time") }, + { C_STRING_WITH_LEN("Delayed insert") }, + { C_STRING_WITH_LEN("Change user") }, + { C_STRING_WITH_LEN("Binlog Dump") }, + { C_STRING_WITH_LEN("Table Dump") }, + { C_STRING_WITH_LEN("Connect Out") }, + { C_STRING_WITH_LEN("Register Slave") }, + { C_STRING_WITH_LEN("Prepare") }, + { C_STRING_WITH_LEN("Execute") }, + { C_STRING_WITH_LEN("Long Data") }, + { C_STRING_WITH_LEN("Close stmt") }, + { C_STRING_WITH_LEN("Reset stmt") }, + { C_STRING_WITH_LEN("Set option") }, + { C_STRING_WITH_LEN("Fetch") }, + { C_STRING_WITH_LEN("Daemon") }, + { C_STRING_WITH_LEN("Error") } // Last command number }; const char *xa_state_names[]={ "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED" }; -#ifdef __WIN__ -static void test_signal(int sig_ptr) -{ -#if !defined( DBUG_OFF) - MessageBox(NULL,"Test signal","DBUG",MB_OK); -#endif -#if defined(OS2) - fprintf(stderr, "Test signal %d\n", sig_ptr); - fflush(stderr); -#endif -} -static void init_signals(void) -{ - int signals[7] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGBREAK,SIGABRT } ; - for (int i=0 ; i < 7 ; i++) - signal( signals[i], test_signal) ; -} -#endif static void unlock_locked_tables(THD *thd) { @@ -123,7 +98,7 @@ static void unlock_locked_tables(THD *thd) } -static bool end_active_trans(THD *thd) +bool end_active_trans(THD *thd) { int error=0; DBUG_ENTER("end_active_trans"); @@ -148,13 +123,14 @@ static bool end_active_trans(THD *thd) thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (ha_commit(thd)) error=1; - thd->options&= ~(ulong) OPTION_BEGIN; - thd->transaction.all.modified_non_trans_table= FALSE; } + thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); + thd->transaction.all.modified_non_trans_table= FALSE; DBUG_RETURN(error); } -static bool begin_trans(THD *thd) + +bool begin_trans(THD *thd) { int error=0; if (unlikely(thd->in_sub_stmt)) @@ -173,8 +149,7 @@ static bool begin_trans(THD *thd) else { LEX *lex= thd->lex; - thd->transaction.all.modified_non_trans_table= FALSE; - thd->options|= (ulong) OPTION_BEGIN; + thd->options|= 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); @@ -188,7 +163,8 @@ static bool begin_trans(THD *thd) */ inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables) { - return table_rules_on && tables && !tables_ok(thd,tables); + return rpl_filter->is_on() && tables && !thd->spcont && + !rpl_filter->tables_ok(thd->db, tables); } #endif @@ -205,410 +181,6 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables) return 0; } -#ifndef NO_EMBEDDED_ACCESS_CHECKS -static HASH hash_user_connections; - -static int get_or_create_user_conn(THD *thd, const char *user, - const char *host, - USER_RESOURCES *mqh) -{ - int return_val= 0; - uint temp_len, user_len; - char temp_user[USER_HOST_BUFF_SIZE]; - struct user_conn *uc; - - DBUG_ASSERT(user != 0); - DBUG_ASSERT(host != 0); - - user_len= strlen(user); - temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1; - (void) pthread_mutex_lock(&LOCK_user_conn); - if (!(uc = (struct user_conn *) hash_search(&hash_user_connections, - (byte*) temp_user, temp_len))) - { - /* First connection for user; Create a user connection object */ - if (!(uc= ((struct user_conn*) - my_malloc(sizeof(struct user_conn) + temp_len+1, - MYF(MY_WME))))) - { - 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->host= uc->user + user_len + 1; - uc->len= temp_len; - uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0; - uc->user_resources= *mqh; - uc->intime= thd->thr_create_time; - if (my_hash_insert(&hash_user_connections, (byte*) uc)) - { - my_free((char*) uc,0); - net_send_error(thd, 0, NullS); // Out of memory - return_val= 1; - goto end; - } - } - thd->user_connect=uc; - uc->connections++; -end: - (void) pthread_mutex_unlock(&LOCK_user_conn); - return return_val; - -} -#endif /* !NO_EMBEDDED_ACCESS_CHECKS */ - - -/* - Check if user exist and password supplied is correct. - - SYNOPSIS - check_user() - thd thread handle, thd->security_ctx->{host,user,ip} are used - command originator of the check: now check_user is called - during connect and change user procedures; used for - logging. - 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 depend on that, but future changes - should be done with this in mind; 'thd' is INOUT, all other params - are 'IN'. - - RETURN VALUE - 0 OK; thd->security_ctx->user/master_access/priv_user/db_access and - thd->db are updated; OK is sent to client; - -1 access denied or handshake error; error is sent to client; - >0 error, not sent to client -*/ - -int check_user(THD *thd, enum enum_server_command command, - const char *passwd, uint passwd_len, const char *db, - bool check_count) -{ - DBUG_ENTER("check_user"); - LEX_STRING db_str= { (char *) db, db ? strlen(db) : 0 }; - -#ifdef NO_EMBEDDED_ACCESS_CHECKS - thd->main_security_ctx.master_access= GLOBAL_ACLS; // Full rights - /* Change database if necessary */ - if (db && db[0]) - { - /* - thd->db is saved in caller and needs to be freed by caller if this - function returns 0 - */ - thd->reset_db(NULL, 0); - if (mysql_change_db(thd, &db_str, FALSE)) - { - /* Send the error to the client */ - net_send_error(thd); - DBUG_RETURN(-1); - } - } - send_ok(thd); - DBUG_RETURN(0); -#else - - my_bool opt_secure_auth_local; - pthread_mutex_lock(&LOCK_global_system_variables); - opt_secure_auth_local= opt_secure_auth; - pthread_mutex_unlock(&LOCK_global_system_variables); - - /* - If the server is running in secure auth mode, short scrambles are - forbidden. - */ - if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323) - { - net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE); - mysql_log.write(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); - DBUG_RETURN(-1); - } - if (passwd_len != 0 && - passwd_len != SCRAMBLE_LENGTH && - passwd_len != SCRAMBLE_LENGTH_323) - DBUG_RETURN(ER_HANDSHAKE_ERROR); - - /* - Clear thd->db as it points to something, that will be freed when - 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'. - */ - thd->reset_db(NULL, 0); - - USER_RESOURCES ur; - int res= acl_getroot(thd, &ur, passwd, passwd_len); -#ifndef EMBEDDED_LIBRARY - if (res == -1) - { - /* - This happens when client (new) sends password scrambled with - scramble(), but database holds old value (scrambled with - scramble_323()). Here we please client to send scrambled_password - in old format. - */ - NET *net= &thd->net; - if (opt_secure_auth_local) - { - net_printf_error(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE, - thd->main_security_ctx.user, - thd->main_security_ctx.host_or_ip); - mysql_log.write(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE), - thd->main_security_ctx.user, - thd->main_security_ctx.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) - { - inc_host_errors(&thd->remote.sin_addr); - DBUG_RETURN(ER_HANDSHAKE_ERROR); - } - /* Final attempt to check the user based on reply */ - /* So as passwd is short, errcode is always >= 0 */ - res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323); - } -#endif /*EMBEDDED_LIBRARY*/ - /* here res is always >= 0 */ - if (res == 0) - { - if (!(thd->main_security_ctx.master_access & - NO_ACCESS)) // authentication is OK - { - DBUG_PRINT("info", - ("Capabilities: %lu packet_length: %ld Host: '%s' " - "Login user: '%s' Priv_user: '%s' Using password: %s " - "Access: %lu db: '%s'", - thd->client_capabilities, - thd->max_client_packet_length, - thd->main_security_ctx.host_or_ip, - thd->main_security_ctx.user, - thd->main_security_ctx.priv_user, - passwd_len ? "yes": "no", - thd->main_security_ctx.master_access, - (thd->db ? thd->db : "*none*"))); - - if (check_count) - { - VOID(pthread_mutex_lock(&LOCK_thread_count)); - bool count_ok= thread_count <= max_connections + delayed_insert_threads - || (thd->main_security_ctx.master_access & SUPER_ACL); - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - if (!count_ok) - { // too many connections - net_send_error(thd, ER_CON_COUNT_ERROR); - DBUG_RETURN(-1); - } - } - - /* Why logging is performed before all checks've passed? */ - mysql_log.write(thd, command, - (thd->main_security_ctx.priv_user == - thd->main_security_ctx.user ? - (char*) "%s@%s on %s" : - (char*) "%s@%s as anonymous on %s"), - thd->main_security_ctx.user, - thd->main_security_ctx.host_or_ip, - db ? db : (char*) ""); - - /* - This is the default access rights for the current database. It's - set to 0 here because we don't have an active database yet (and we - may not have an active database to set. - */ - thd->main_security_ctx.db_access=0; - - /* Don't allow user to connect if he has done too many queries */ - if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn || - max_user_connections) && - get_or_create_user_conn(thd, - (opt_old_style_user_limits ? thd->main_security_ctx.user : - thd->main_security_ctx.priv_user), - (opt_old_style_user_limits ? thd->main_security_ctx.host_or_ip : - thd->main_security_ctx.priv_host), - &ur)) - DBUG_RETURN(-1); - if (thd->user_connect && - (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 */ - if (db && db[0]) - { - if (mysql_change_db(thd, &db_str, FALSE)) - { - /* Send error to the client */ - net_send_error(thd); - if (thd->user_connect) - decrease_user_connections(thd->user_connect); - DBUG_RETURN(-1); - } - } - send_ok(thd); - thd->password= test(passwd_len); // remember for error messages - /* Ready to handle queries */ - DBUG_RETURN(0); - } - } - else if (res == 2) // client gave short hash, server has long hash - { - 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_error(thd, ER_ACCESS_DENIED_ERROR, - thd->main_security_ctx.user, - thd->main_security_ctx.host_or_ip, - passwd_len ? ER(ER_YES) : ER(ER_NO)); - mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR), - thd->main_security_ctx.user, - thd->main_security_ctx.host_or_ip, - passwd_len ? ER(ER_YES) : ER(ER_NO)); - DBUG_RETURN(-1); -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ -} - -/* - Check for maximum allowable user connections, if the mysqld server is - started with corresponding variable that is greater then 0. -*/ - -extern "C" byte *get_key_conn(user_conn *buff, uint *length, - my_bool not_used __attribute__((unused))) -{ - *length=buff->len; - return (byte*) buff->user; -} - -extern "C" void free_user(struct user_conn *uc) -{ - my_free((char*) uc,MYF(0)); -} - -void init_max_user_conn(void) -{ -#ifndef NO_EMBEDDED_ACCESS_CHECKS - (void) hash_init(&hash_user_connections,system_charset_info,max_connections, - 0,0, - (hash_get_key) get_key_conn, (hash_free_key) free_user, - 0); -#endif -} - - -/* - check if user has already too many connections - - SYNOPSIS - check_for_max_user_connections() - thd Thread handle - uc User connect object - - NOTES - If check fails, we decrease user connection count, which means one - shouldn't call decrease_user_connections() after this function. - - RETURN - 0 ok - 1 error -*/ - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - -static int check_for_max_user_connections(THD *thd, USER_CONN *uc) -{ - int error=0; - DBUG_ENTER("check_for_max_user_connections"); - - (void) pthread_mutex_lock(&LOCK_user_conn); - if (max_user_connections && !uc->user_resources.user_conn && - max_user_connections < (uint) uc->connections) - { - 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.user_conn && - uc->user_resources.user_conn < uc->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; - } - uc->conn_per_hour++; - - end: - if (error) - uc->connections--; // no need for decrease_user_connections() here - (void) pthread_mutex_unlock(&LOCK_user_conn); - DBUG_RETURN(error); -} - -/* - Decrease user connection count - - SYNOPSIS - decrease_user_connections() - uc User connection object - - NOTES - If there is a n user connection object for a connection - (which only happens if 'max_user_connections' is defined or - if someone has created a resource grant for a user), then - the connection count is always incremented on connect. - - The user connect object is not freed if some users has - 'max connections per hour' defined as we need to be able to hold - count over the lifetime of the connection. -*/ - -static void decrease_user_connections(USER_CONN *uc) -{ - DBUG_ENTER("decrease_user_connections"); - (void) pthread_mutex_lock(&LOCK_user_conn); - DBUG_ASSERT(uc->connections); - if (!--uc->connections && !mqh_used) - { - /* Last connection for user; Delete it */ - (void) hash_delete(&hash_user_connections,(byte*) uc); - } - (void) pthread_mutex_unlock(&LOCK_user_conn); - DBUG_VOID_RETURN; -} - -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ - - -void free_max_user_conn(void) -{ -#ifndef NO_EMBEDDED_ACCESS_CHECKS - hash_free(&hash_user_connections); -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ -} - - /* Mark all commands that somehow changes a table @@ -617,445 +189,125 @@ 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 + See COMMAND_FLAG_xxx for different type of commands 2 - query that returns meaningful ROW_COUNT() - a number of modified rows */ -char uc_update_queries[SQLCOM_END+1]; +uint sql_command_flags[SQLCOM_END+1]; void init_update_queries(void) { - bzero((gptr) &uc_update_queries, sizeof(uc_update_queries)); - - 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]=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]=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_DROP_INDEX]=1; - uc_update_queries[SQLCOM_CREATE_VIEW]=1; - uc_update_queries[SQLCOM_DROP_VIEW]=1; -} - -bool is_update_query(enum enum_sql_command command) -{ - DBUG_ASSERT(command >= 0 && command <= SQLCOM_END); - return uc_update_queries[command] != 0; -} - -/* - Reset per-hour user resource limits when it has been more than - an hour since they were last checked - - SYNOPSIS: - time_out_user_resource_limits() - thd Thread handler - uc User connection details - - NOTE: - This assumes that the LOCK_user_conn mutex has been acquired, so it is - safe to test and modify members of the USER_CONN structure. -*/ - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - -static void time_out_user_resource_limits(THD *thd, USER_CONN *uc) -{ - time_t check_time = thd->start_time ? thd->start_time : time(NULL); - DBUG_ENTER("time_out_user_resource_limits"); - - /* If more than a hour since last check, reset resource checking */ - if (check_time - uc->intime >= 3600) - { - uc->questions=1; - uc->updates=0; - uc->conn_per_hour=0; - uc->intime=check_time; - } - - DBUG_VOID_RETURN; -} + bzero((uchar*) &sql_command_flags, sizeof(sql_command_flags)); + + sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND; + sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND; + sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_BACKUP_TABLE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_RESTORE_TABLE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_DROP_INDEX]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_CREATE_VIEW]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_DROP_VIEW]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_CREATE_EVENT]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_ALTER_EVENT]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA; + + sql_command_flags[SQLCOM_UPDATE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT; + sql_command_flags[SQLCOM_UPDATE_MULTI]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT; + sql_command_flags[SQLCOM_INSERT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT; + sql_command_flags[SQLCOM_INSERT_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT; + sql_command_flags[SQLCOM_DELETE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT; + sql_command_flags[SQLCOM_DELETE_MULTI]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT; + sql_command_flags[SQLCOM_REPLACE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT; + sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT; + + sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_STATUS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_DATABASES]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_TRIGGERS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_EVENTS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_OPEN_TABLES]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_PLUGINS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_FIELDS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_KEYS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_VARIABLES]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CHARSETS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_COLLATIONS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_NEW_MASTER]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_BINLOGS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_SLAVE_HOSTS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_BINLOG_EVENTS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_COLUMN_TYPES]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_STORAGE_ENGINES]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_AUTHORS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CONTRIBUTORS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_PRIVILEGES]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_WARNS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_ERRORS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_ENGINE_STATUS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_ENGINE_MUTEX]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_ENGINE_LOGS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_SLAVE_STAT]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE_PROC]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE_FUNC]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE_TRIGGER]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_STATUS_FUNC]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_PROC_CODE]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_FUNC_CODE]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND; + + sql_command_flags[SQLCOM_SHOW_TABLES]= (CF_STATUS_COMMAND | + CF_SHOW_TABLE_COMMAND); + sql_command_flags[SQLCOM_SHOW_TABLE_STATUS]= (CF_STATUS_COMMAND | + CF_SHOW_TABLE_COMMAND); -/* - Check if maximum queries per hour limit has been reached - returns 0 if OK. -*/ - -static bool check_mqh(THD *thd, uint check_command) -{ - bool error= 0; - USER_CONN *uc=thd->user_connect; - DBUG_ENTER("check_mqh"); - DBUG_ASSERT(uc != 0); - - (void) pthread_mutex_lock(&LOCK_user_conn); - - time_out_user_resource_limits(thd, uc); - - /* Check that we have not done too many questions / hour */ - if (uc->user_resources.questions && - uc->questions++ >= 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; - } - if (check_command < (uint) SQLCOM_END) - { - /* Check that we have not done too many updates / hour */ - if (uc->user_resources.updates && uc_update_queries[check_command] && - uc->updates++ >= 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; - } - } -end: - (void) pthread_mutex_unlock(&LOCK_user_conn); - DBUG_RETURN(error); -} - -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ - - -static void reset_mqh(LEX_USER *lu, bool get_them= 0) -{ -#ifndef NO_EMBEDDED_ACCESS_CHECKS - (void) pthread_mutex_lock(&LOCK_user_conn); - if (lu) // for GRANT - { - USER_CONN *uc; - uint temp_len=lu->user.length+lu->host.length+2; - char temp_user[USER_HOST_BUFF_SIZE]; - - memcpy(temp_user,lu->user.str,lu->user.length); - memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length); - temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0; - if ((uc = (struct user_conn *) hash_search(&hash_user_connections, - (byte*) temp_user, temp_len))) - { - uc->questions=0; - get_mqh(temp_user,&temp_user[lu->user.length+1],uc); - uc->updates=0; - uc->conn_per_hour=0; - } - } - 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, - idx); - if (get_them) - get_mqh(uc->user,uc->host,uc); - uc->questions=0; - uc->updates=0; - uc->conn_per_hour=0; - } - } - (void) pthread_mutex_unlock(&LOCK_user_conn); -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ -} + /* + The following is used to preserver CF_ROW_COUNT during the + a CALL or EXECUTE statement, so the value generated by the + last called (or executed) statement is preserved. + See mysql_execute_command() for how CF_ROW_COUNT is used. + */ + sql_command_flags[SQLCOM_CALL]= CF_HAS_ROW_COUNT; + sql_command_flags[SQLCOM_EXECUTE]= CF_HAS_ROW_COUNT; -void thd_init_client_charset(THD *thd, uint cs_number) -{ /* - Use server character set and collation if - - opt_character_set_client_handshake is not set - - client has not specified a character set - - client character set is the same as the servers - - client character set doesn't exists in server + The following admin table operations are allowed + on log tables. */ - if (!opt_character_set_client_handshake || - !(thd->variables.character_set_client= get_charset(cs_number, MYF(0))) || - !my_strcasecmp(&my_charset_latin1, - global_system_variables.character_set_client->name, - thd->variables.character_set_client->name)) - { - thd->variables.character_set_client= - global_system_variables.character_set_client; - thd->variables.collation_connection= - global_system_variables.collation_connection; - thd->variables.character_set_results= - global_system_variables.character_set_results; - } - else - { - thd->variables.character_set_results= - thd->variables.collation_connection= - thd->variables.character_set_client; - } + sql_command_flags[SQLCOM_REPAIR]= CF_WRITE_LOGS_COMMAND; + sql_command_flags[SQLCOM_OPTIMIZE]= CF_WRITE_LOGS_COMMAND; + sql_command_flags[SQLCOM_ANALYZE]= CF_WRITE_LOGS_COMMAND; } -/* - Perform handshake, authorize client and update thd ACL variables. - SYNOPSIS - check_connection() - thd thread handle +bool is_update_query(enum enum_sql_command command) +{ + DBUG_ASSERT(command >= 0 && command <= SQLCOM_END); + return (sql_command_flags[command] & CF_CHANGES_DATA) != 0; +} - RETURN - 0 success, OK is sent to user, thd is updated. - -1 error, which is sent to user - > 0 error code (not sent to user) +/** + Check if a sql command is allowed to write to log tables. + @param command The SQL command + @return true if writing is allowed */ - -#ifndef EMBEDDED_LIBRARY -static int check_connection(THD *thd) +bool is_log_table_write_query(enum enum_sql_command command) { - uint connect_errors= 0; - NET *net= &thd->net; - ulong pkt_len= 0; - char *end; - - DBUG_PRINT("info", - ("New connection received on %s", vio_description(net->vio))); -#ifdef SIGNAL_WITH_VIO_CLOSE - thd->set_active_vio(net->vio); -#endif - - if (!thd->main_security_ctx.host) // If TCP/IP connection - { - char ip[30]; - - if (vio_peer_addr(net->vio, ip, &thd->peer_port)) - return (ER_BAD_HOST_ERROR); - if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(0)))) - return (ER_OUT_OF_RESOURCES); - thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip; - vio_in_addr(net->vio,&thd->remote.sin_addr); - if (!(specialflag & SPECIAL_NO_RESOLVE)) - { - vio_in_addr(net->vio,&thd->remote.sin_addr); - thd->main_security_ctx.host= - ip_to_hostname(&thd->remote.sin_addr, &connect_errors); - /* Cut very long hostnames to avoid possible overflows */ - if (thd->main_security_ctx.host) - { - if (thd->main_security_ctx.host != my_localhost) - thd->main_security_ctx.host[min(strlen(thd->main_security_ctx.host), - HOSTNAME_LENGTH)]= 0; - thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host; - } - if (connect_errors > max_connect_errors) - return(ER_HOST_IS_BLOCKED); - } - DBUG_PRINT("info",("Host: %s ip: %s", - (thd->main_security_ctx.host ? - thd->main_security_ctx.host : "unknown host"), - (thd->main_security_ctx.ip ? - thd->main_security_ctx.ip : "unknown ip"))); - if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip)) - return(ER_HOST_NOT_PRIVILEGED); - } - else /* Hostname given means that the connection was on a socket */ - { - DBUG_PRINT("info",("Host: %s", thd->main_security_ctx.host)); - thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host; - thd->main_security_ctx.ip= 0; - /* Reset sin_addr */ - bzero((char*) &thd->remote, sizeof(thd->remote)); - } - vio_keepalive(net->vio, TRUE); - { - /* buff[] needs to big enough to hold the server_version variable */ - char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64]; - ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB | - CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION); - - if (opt_using_transactions) - client_flags|=CLIENT_TRANSACTIONS; -#ifdef HAVE_COMPRESS - client_flags |= CLIENT_COMPRESS; -#endif /* HAVE_COMPRESS */ -#ifdef HAVE_OPENSSL - if (ssl_acceptor_fd) - client_flags |= CLIENT_SSL; /* Wow, SSL is available! */ -#endif /* HAVE_OPENSSL */ - - end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1; - int4store((uchar*) end, thd->thread_id); - end+= 4; - /* - So as check_connection is the only entry point to authorization - procedure, scramble is set here. This gives us new scramble for - each handshake. - */ - create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand); - /* - Old clients does not understand long scrambles, but can ignore packet - tail: that's why first part of the scramble is placed here, and second - part at the end of packet. - */ - end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1; - - int2store(end, client_flags); - /* write server characteristics: up to 16 bytes allowed */ - end[2]=(char) default_charset_info->number; - int2store(end+3, thd->server_status); - bzero(end+5, 13); - end+= 18; - /* write scramble tail */ - end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323, - SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1; - - /* At this point we write connection message and read reply */ - if (net_write_command(net, (uchar) protocol_version, "", 0, buff, - (uint) (end-buff)) || - (pkt_len= my_net_read(net)) == packet_error || - pkt_len < MIN_HANDSHAKE_SIZE) - { - inc_host_errors(&thd->remote.sin_addr); - return(ER_HANDSHAKE_ERROR); - } - } -#ifdef _CUSTOMCONFIG_ -#include "_cust_sql_parse.h" -#endif - if (connect_errors) - reset_host_errors(&thd->remote.sin_addr); - if (thd->packet.alloc(thd->variables.net_buffer_length)) - return(ER_OUT_OF_RESOURCES); - - thd->client_capabilities=uint2korr(net->read_pos); - if (thd->client_capabilities & CLIENT_PROTOCOL_41) - { - thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16; - thd->max_client_packet_length= uint4korr(net->read_pos+4); - DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8])); - thd_init_client_charset(thd, (uint) net->read_pos[8]); - thd->update_charset(); - end= (char*) net->read_pos+32; - } - else - { - thd->max_client_packet_length= uint3korr(net->read_pos+2); - end= (char*) net->read_pos+5; - } - - if (thd->client_capabilities & CLIENT_IGNORE_SPACE) - thd->variables.sql_mode|= MODE_IGNORE_SPACE; -#ifdef HAVE_OPENSSL - DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities)); - if (thd->client_capabilities & CLIENT_SSL) - { - /* Do the SSL layering. */ - if (!ssl_acceptor_fd) - { - inc_host_errors(&thd->remote.sin_addr); - return(ER_HANDSHAKE_ERROR); - } - DBUG_PRINT("info", ("IO layer change in progress...")); - if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout)) - { - DBUG_PRINT("error", ("Failed to accept new SSL connection")); - inc_host_errors(&thd->remote.sin_addr); - return(ER_HANDSHAKE_ERROR); - } - DBUG_PRINT("info", ("Reading user information over SSL layer")); - if ((pkt_len= my_net_read(net)) == packet_error || - pkt_len < NORMAL_HANDSHAKE_SIZE) - { - DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)", - pkt_len)); - inc_host_errors(&thd->remote.sin_addr); - return(ER_HANDSHAKE_ERROR); - } - } -#endif - - if (end >= (char*) net->read_pos+ pkt_len +2) - { - inc_host_errors(&thd->remote.sin_addr); - return(ER_HANDSHAKE_ERROR); - } - - if (thd->client_capabilities & CLIENT_INTERACTIVE) - thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout; - if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && - opt_using_transactions) - net->return_status= &thd->server_status; - - char *user= end; - char *passwd= strend(user)+1; - uint user_len= passwd - user - 1; - char *db= passwd; - char db_buff[NAME_LEN + 1]; // buffer to store db in utf8 - char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8 - uint dummy_errors; - - /* - Old clients send null-terminated string as password; new clients send - the size (1 byte) + string (not null-terminated). Hence in case of empty - password both send '\0'. - - Cast *passwd to an unsigned char, so that it doesn't extend the sign for - *passwd > 127 and become 2**32-127 after casting to uint. - */ - uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? - (uchar)(*passwd++) : strlen(passwd); - db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ? - db + passwd_len + 1 : 0; - uint db_len= db ? strlen(db) : 0; - - if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len) - { - inc_host_errors(&thd->remote.sin_addr); - return ER_HANDSHAKE_ERROR; - } - - /* Since 4.1 all database names are stored in utf8 */ - if (db) - { - db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1, - system_charset_info, - db, db_len, - thd->charset(), &dummy_errors)]= 0; - db= db_buff; - } - - user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1, - system_charset_info, user, user_len, - thd->charset(), &dummy_errors)]= '\0'; - user= user_buff; - - /* If username starts and ends in "'", chop them off */ - if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'') - { - user[user_len-1]= 0; - user++; - user_len-= 2; - } - - if (thd->main_security_ctx.user) - x_free(thd->main_security_ctx.user); - if (!(thd->main_security_ctx.user= my_strdup(user, MYF(0)))) - return (ER_OUT_OF_RESOURCES); - return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE); + DBUG_ASSERT(command >= 0 && command <= SQLCOM_END); + return (sql_command_flags[command] & CF_WRITE_LOGS_COMMAND) != 0; } - void execute_init_command(THD *thd, sys_var_str *init_command_var, rw_lock_t *var_mutex) { @@ -1087,151 +339,6 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var, } -pthread_handler_t handle_one_connection(void *arg) -{ - THD *thd=(THD*) arg; - uint launch_time = - (uint) ((thd->thr_create_time = time(NULL)) - thd->connect_time); - if (launch_time >= slow_launch_time) - statistic_increment(slow_launch_threads,&LOCK_status ); - - pthread_detach_this_thread(); - -#if !defined( __WIN__) && !defined(OS2) // Win32 calls this in pthread_create - /* 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); - statistic_increment(aborted_connects,&LOCK_status); - end_thread(thd,0); - return 0; - } -#endif - - /* - handle_one_connection() is the only way a thread would start - and would always be on top of the stack, therefore, the thread - stack always starts at the address of the first local variable - of handle_one_connection, which is thd. We need to know the - start of the stack so that we could check for stack overruns. - */ - DBUG_PRINT("info", ("handle_one_connection called by thread %lu\n", - thd->thread_id)); - /* now that we've called my_thread_init(), it is safe to call DBUG_* */ - -#if defined(__WIN__) - init_signals(); -#elif !defined(OS2) && !defined(__NETWARE__) - sigset_t set; - VOID(sigemptyset(&set)); // Get mask in use - VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); -#endif - thd->thread_stack= (char*) &thd; - if (thd->store_globals()) - { - close_connection(thd, ER_OUT_OF_RESOURCES, 1); - statistic_increment(aborted_connects,&LOCK_status); - end_thread(thd,0); - return 0; - } - - do - { - int error; - NET *net= &thd->net; - Security_context *sctx= thd->security_ctx; - net->no_send_error= 0; - - /* Use "connect_timeout" value during connection phase */ - my_net_set_read_timeout(net, connect_timeout); - my_net_set_write_timeout(net, connect_timeout); - - if ((error=check_connection(thd))) - { // Wrong permissions - if (error > 0) - net_printf_error(thd, error, sctx->host_or_ip); -#ifdef __NT__ - if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE) - my_sleep(1000); /* must wait after eof() */ -#endif - statistic_increment(aborted_connects,&LOCK_status); - goto end_thread; - } -#ifdef __NETWARE__ - netware_reg_user(sctx->ip, sctx->user, "MySQL"); -#endif - if (thd->variables.max_join_size == HA_POS_ERROR) - thd->options |= OPTION_BIG_SELECTS; - if (thd->client_capabilities & CLIENT_COMPRESS) - net->compress=1; // Use compression - - thd->version= refresh_version; - thd->proc_info= 0; - thd->command= COM_SLEEP; - thd->init_for_queries(); - - if (sys_init_connect.value_length && !(sctx->master_access & SUPER_ACL)) - { - execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect); - if (thd->query_error) - { - thd->killed= THD::KILL_CONNECTION; - sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), - thd->thread_id,(thd->db ? thd->db : "unconnected"), - sctx->user ? sctx->user : "unauthenticated", - sctx->host_or_ip, "init_connect command failed"); - sql_print_warning("%s", net->last_error); - } - thd->proc_info=0; - thd->init_for_queries(); - } - - /* Connect completed, set read/write timeouts back to tdefault */ - my_net_set_read_timeout(net, thd->variables.net_read_timeout); - my_net_set_write_timeout(net, thd->variables.net_write_timeout); - - 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); - 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), - thd->thread_id,(thd->db ? thd->db : "unconnected"), - sctx->user ? sctx->user : "unauthenticated", - sctx->host_or_ip, - (net->last_errno ? ER(net->last_errno) : - ER(ER_UNKNOWN_ERROR))); - net_send_error(thd, net->last_errno, NullS); - statistic_increment(aborted_threads,&LOCK_status); - } - else if (thd->killed) - { - statistic_increment(aborted_threads,&LOCK_status); - } - -end_thread: - close_connection(thd, 0, 1); - end_thread(thd,1); - /* - If end_thread returns, we are either running with --one-thread - or this thread has been schedule to handle the next query - */ - thd= current_thd; - thd->thread_stack= (char*) &thd; - } while (!(test_flags & TEST_NO_THREADS)); - /* The following is only executed if we are not using --one-thread */ - return(0); /* purecov: deadcode */ -} - -#endif /* EMBEDDED_LIBRARY */ - /* Execute commands from bootstrap_file. Used when creating the initial grant tables @@ -1259,11 +366,6 @@ pthread_handler_t handle_bootstrap(void *arg) #ifndef EMBEDDED_LIBRARY pthread_detach_this_thread(); thd->thread_stack= (char*) &thd; -#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) - sigset_t set; - VOID(sigemptyset(&set)); // Get mask in use - VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); -#endif #endif /* EMBEDDED_LIBRARY */ if (thd->variables.max_join_size == HA_POS_ERROR) @@ -1273,6 +375,7 @@ pthread_handler_t handle_bootstrap(void *arg) thd->version=refresh_version; thd->security_ctx->priv_user= thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME)); + thd->security_ctx->priv_host[0]=0; /* Make the "client" handle multiple results. This is necessary to enable stored procedures with SELECTs and Dynamic SQL @@ -1284,33 +387,42 @@ pthread_handler_t handle_bootstrap(void *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)) - { - 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; + /* strlen() can't be deleted because fgets() doesn't return length */ + 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. + */ + /* purecov: begin tested */ + 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); + /* purecov: end */ + } + if (thd->is_fatal_error) + break; /* purecov: inspected */ while (length && (my_isspace(thd->charset(), buff[length-1]) || - buff[length-1] == ';')) + buff[length-1] == ';')) length--; buff[length]=0; + + /* Skip lines starting with delimiter */ + if (strncmp(buff, STRING_WITH_LEN("delimiter")) == 0) + continue; + thd->query_length=length; - thd->query= thd->memdup_w_gap(buff, length+1, - thd->db_length+1+QUERY_CACHE_FLAGS_SIZE); + thd->query= (char*) thd->memdup_w_gap(buff, length+1, + thd->db_length+1+ + QUERY_CACHE_FLAGS_SIZE); thd->query[length] = '\0'; DBUG_PRINT("query",("%-.4096s",thd->query)); /* @@ -1359,7 +471,21 @@ end: } - /* This works because items are allocated with sql_alloc() */ +/* This works because items are allocated with sql_alloc() */ + +void free_items(Item *item) +{ + 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) { @@ -1388,30 +514,35 @@ void cleanup_items(Item *item) */ static -int mysql_table_dump(THD* thd, char* db, char* tbl_name) +int mysql_table_dump(THD *thd, LEX_STRING *db, char *tbl_name) { TABLE* table; TABLE_LIST* table_list; int error = 0; DBUG_ENTER("mysql_table_dump"); - db = (db && db[0]) ? db : thd->db; + if (db->length == 0) + { + db->str= thd->db; /* purecov: inspected */ + db->length= thd->db_length; /* purecov: inspected */ + } if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST)))) DBUG_RETURN(1); // out of memory - table_list->db= db; + table_list->db= db->str; 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)) + if (check_db_name(db)) { - my_error(ER_WRONG_DB_NAME ,MYF(0), db ? db : "NULL"); + /* purecov: begin inspected */ + my_error(ER_WRONG_DB_NAME ,MYF(0), db->str ? db->str : "NULL"); goto err; + /* purecov: end */ } if (lower_case_table_names) my_casedn_str(files_charset_info, tbl_name); - remove_escape(table_list->table_name); - if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT))) + if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT, 0))) DBUG_RETURN(1); if (check_one_table_access(thd, SELECT_ACL, table_list)) @@ -1470,7 +601,7 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) */ thd->server_status&= ~SERVER_STATUS_IN_TRANS; res= ha_commit(thd); - thd->options&= ~(ulong) OPTION_BEGIN; + thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_KEEP_LOG); thd->transaction.all.modified_non_trans_table= FALSE; break; case COMMIT_RELEASE: @@ -1488,7 +619,7 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (ha_rollback(thd)) res= -1; - thd->options&= ~(ulong) OPTION_BEGIN; + thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_KEEP_LOG); thd->transaction.all.modified_non_trans_table= FALSE; if (!res && (completion == ROLLBACK_AND_CHAIN)) res= begin_trans(thd); @@ -1568,7 +699,7 @@ bool do_command(THD *thd) command= COM_END; // Wrong command DBUG_PRINT("info",("Command on %s = %d (%s)", vio_description(net->vio), command, - command_name[command])); + command_name[command].str)); } /* Restore read timeout value */ @@ -1640,13 +771,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_INIT_DB: { LEX_STRING tmp; - statistic_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB], - &LOCK_status); + status_var_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB]); thd->convert_string(&tmp, system_charset_info, - packet, strlen(packet), thd->charset()); + packet, packet_length-1, thd->charset()); if (!mysql_change_db(thd, &tmp, FALSE)) { - mysql_log.write(thd,command,"%s",thd->db); + general_log_print(thd, command, "%s",thd->db); send_ok(thd); } break; @@ -1661,7 +791,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #endif case COM_TABLE_DUMP: { - char *db, *tbl_name; + char *tbl_name; + LEX_STRING db; uint db_len= *(uchar*) packet; if (db_len >= packet_length || db_len > NAME_LEN) { @@ -1675,27 +806,29 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } - statistic_increment(thd->status_var.com_other, &LOCK_status); + status_var_increment(thd->status_var.com_other); thd->enable_slow_log= opt_log_slow_admin_statements; - db= thd->alloc(db_len + tbl_len + 2); - if (!db) + db.str= (char*) thd->alloc(db_len + tbl_len + 2); + if (!db.str) { my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); break; } - tbl_name= strmake(db, packet + 1, db_len)+1; + db.length= db_len; + tbl_name= strmake(db.str, packet + 1, db_len)+1; strmake(tbl_name, packet + db_len + 2, tbl_len); - mysql_table_dump(thd, db, tbl_name); + mysql_table_dump(thd, &db, tbl_name); break; } case COM_CHANGE_USER: { + status_var_increment(thd->status_var.com_other); + char *user= (char*) packet, *packet_end= packet+ packet_length; + char *passwd= strend(user)+1; + thd->change_user(); thd->clear_error(); // if errors from rollback - statistic_increment(thd->status_var.com_other, &LOCK_status); - char *user= (char*) packet; - char *passwd= strend(user)+1; /* Old clients send null-terminated string ('\0' for empty string) for password. New clients send the size (1 byte) + string (not null @@ -1704,10 +837,16 @@ bool dispatch_command(enum enum_server_command command, THD *thd, Cast *passwd to an unsigned char, so that it doesn't extend the sign for *passwd > 127 and become 2**32-127 after casting to uint. */ - char db_buff[NAME_LEN+1]; // buffer to store db in utf8 + char db_buff[NAME_LEN+1]; // buffer to store db in utf8 char *db= passwd; - uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? - (uchar)(*passwd++) : strlen(passwd); + char *save_db; + uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ? + (uchar)(*passwd++) : strlen(passwd)); + uint dummy_errors, save_db_length, db_length; + int res; + Security_context save_security_ctx= *thd->security_ctx; + USER_CONN *save_user_connect; + db+= passwd_len + 1; #ifndef EMBEDDED_LIBRARY /* Small check for incoming packet */ @@ -1718,17 +857,22 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } #endif /* Convert database name to utf8 */ - uint dummy_errors; + /* + Handle problem with old bug in client protocol where db had an extra + \0 + */ + db_length= (packet_end - db); + if (db_length > 0 && db[db_length-1] == 0) + db_length--; db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1, - system_charset_info, db, strlen(db), + system_charset_info, db, db_length, thd->charset(), &dummy_errors)]= 0; db= db_buff; /* Save user and privileges */ - uint save_db_length= thd->db_length; - char *save_db= thd->db; - Security_context save_security_ctx= *thd->security_ctx; - USER_CONN *save_user_connect= thd->user_connect; + save_db_length= thd->db_length; + save_db= thd->db; + save_user_connect= thd->user_connect; if (!(thd->security_ctx->user= my_strdup(user, MYF(0)))) { @@ -1739,13 +883,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* Clear variables that are allocated */ thd->user_connect= 0; - int res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE); + res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE); if (res) { /* authentication failure, we shall restore old user */ if (res > 0) my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); + else + thd->clear_error(); // Error already sent to client x_free(thd->security_ctx->user); *thd->security_ctx= save_security_ctx; thd->user_connect= save_user_connect; @@ -1759,8 +905,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (save_user_connect) decrease_user_connections(save_user_connect); #endif /* NO_EMBEDDED_ACCESS_CHECKS */ - x_free((gptr) save_db); - x_free((gptr) save_security_ctx.user); + x_free((uchar*) save_db); + x_free((uchar*) save_security_ctx.user); } break; } @@ -1803,7 +949,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, const char *format= "%.*b"; const char* found_semicolon= NULL; - mysql_log.write(thd,command, format, thd->query_length, thd->query); + general_log_print(thd, command, format, thd->query_length, thd->query); DBUG_PRINT("query",("%-.4096s",thd->query)); if (!(specialflag & SPECIAL_NO_PRIOR)) @@ -1853,7 +999,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; #else { - char *fields, *pend; + char *fields, *packet_end= packet + packet_length - 1, *arg_end; /* Locked closure of all tables */ TABLE_LIST table_list; LEX_STRING conv_name; @@ -1861,16 +1007,18 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* used as fields initializator */ lex_start(thd); - statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS], - &LOCK_status); + status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS]); bzero((char*) &table_list,sizeof(table_list)); if (thd->copy_db_to(&table_list.db, &table_list.db_length)) break; - pend= strend(packet); + /* + We have name + wildcard in packet, separated by endzero + */ + arg_end= strend(packet); thd->convert_string(&conv_name, system_charset_info, - packet, (uint) (pend-packet), thd->charset()); + packet, (uint) (arg_end - packet), thd->charset()); table_list.alias= table_list.table_name= conv_name.str; - packet= pend+1; + packet= arg_end + 1; if (!my_strcasecmp(system_charset_info, table_list.db, INFORMATION_SCHEMA_NAME.str)) @@ -1880,19 +1028,17 @@ bool dispatch_command(enum enum_server_command command, THD *thd, 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))) + thd->query_length= (uint) (packet_end - packet); // Don't count end \0 + if (!(thd->query=fields= (char*) thd->memdup(packet,thd->query_length+1))) break; - mysql_log.write(thd,command,"%s %s",table_list.table_name, fields); + general_log_print(thd, command, "%s %s", table_list.table_name, fields); if (lower_case_table_names) 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, test(table_list.schema_table))) break; - if (grant_option && - check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0)) + if (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); @@ -1901,8 +1047,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, mysql_reset_thd_for_next_command(thd); thd->lex-> - select_lex.table_list.link_in_list((byte*) &table_list, - (byte**) &table_list.next_local); + select_lex.table_list.link_in_list((uchar*) &table_list, + (uchar**) &table_list.next_local); thd->lex->add_to_query_tables(&table_list); /* switch on VIEW optimisation: do not fill temporary tables */ @@ -1915,44 +1061,46 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #endif case COM_QUIT: /* We don't calculate statistics for this command */ - mysql_log.write(thd,command,NullS); + general_log_print(thd, command, NullS); net->error=0; // Don't give 'abort' message error=TRUE; // End server break; +#ifdef REMOVED case COM_CREATE_DB: // QQ: To be removed { - char *db=thd->strdup(packet), *alias; + LEX_STRING db, alias; HA_CREATE_INFO create_info; - 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)) + status_var_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB]); + if (thd->make_lex_string(&db, packet, packet_length - 1, FALSE) || + thd->make_lex_string(&alias, db.str, db.length, FALSE) || + check_db_name(&db)) { - my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL"); + my_error(ER_WRONG_DB_NAME, MYF(0), db.str ? db.str : "NULL"); break; } - if (check_access(thd,CREATE_ACL,db,0,1,0,is_schema_db(db))) + if (check_access(thd, CREATE_ACL, db.str , 0, 1, 0, + is_schema_db(db.str))) break; - mysql_log.write(thd,command,packet); + general_log_print(thd, command, packet); bzero(&create_info, sizeof(create_info)); - mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db), + mysql_create_db(thd, (lower_case_table_names == 2 ? alias.str : db.str), &create_info, 0); break; } case COM_DROP_DB: // QQ: To be removed { - 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)) + status_var_increment(thd->status_var.com_stat[SQLCOM_DROP_DB]); + LEX_STRING db; + + if (thd->make_lex_string(&db, packet, packet_length - 1, FALSE) || + check_db_name(&db)) { - my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL"); + my_error(ER_WRONG_DB_NAME, MYF(0), db.str ? db.str : "NULL"); break; } - if (check_access(thd,DROP_ACL,db,0,1,0,is_schema_db(db))) + if (check_access(thd, DROP_ACL, db.str, 0, 1, 0, is_schema_db(db.str))) break; if (thd->locked_tables || thd->active_transaction()) { @@ -1960,10 +1108,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); break; } - mysql_log.write(thd,command,db); - mysql_rm_db(thd, db, 0, 0); + general_log_print(thd, command, db.str); + mysql_rm_db(thd, db.str, 0, 0); break; } +#endif #ifndef EMBEDDED_LIBRARY case COM_BINLOG_DUMP: { @@ -1971,7 +1120,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ushort flags; uint32 slave_server_id; - statistic_increment(thd->status_var.com_other,&LOCK_status); + status_var_increment(thd->status_var.com_other); thd->enable_slow_log= opt_log_slow_admin_statements; if (check_global_access(thd, REPL_SLAVE_ACL)) break; @@ -1984,7 +1133,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, kill_zombie_dump_threads(slave_server_id); thd->server_id = slave_server_id; - mysql_log.write(thd, command, "Log: '%s' Pos: %ld", packet+10, + general_log_print(thd, command, "Log: '%s' Pos: %ld", packet+10, (long) pos); mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags); unregister_slave(thd,1,1); @@ -1997,12 +1146,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_REFRESH: { bool not_used; - statistic_increment(thd->status_var.com_stat[SQLCOM_FLUSH], - &LOCK_status); + status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]); ulong options= (ulong) (uchar) packet[0]; if (check_global_access(thd,RELOAD_ACL)) break; - mysql_log.write(thd,command,NullS); + general_log_print(thd, command, NullS); if (!reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, ¬_used)) send_ok(thd); break; @@ -2010,7 +1158,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #ifndef EMBEDDED_LIBRARY case COM_SHUTDOWN: { - statistic_increment(thd->status_var.com_other, &LOCK_status); + status_var_increment(thd->status_var.com_other); if (check_global_access(thd,SHUTDOWN_ACL)) break; /* purecov: inspected */ /* @@ -2021,7 +1169,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, */ enum mysql_enum_shutdown_level level= (enum mysql_enum_shutdown_level) (uchar) packet[0]; - DBUG_PRINT("quit",("Got shutdown command for level %u", level)); if (level == SHUTDOWN_DEFAULT) level= SHUTDOWN_WAIT_ALL_BUFFERS; // soon default will be configurable else if (level != SHUTDOWN_WAIT_ALL_BUFFERS) @@ -2030,14 +1177,17 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } DBUG_PRINT("quit",("Got shutdown command for level %u", level)); - mysql_log.write(thd,command,NullS); + general_log_print(thd, command, NullS); send_eof(thd); #ifdef __WIN__ sleep(1); // must wait after eof() #endif -#ifndef OS2 - send_eof(thd); // This is for 'quit request' -#endif + /* + The client is next going to send a COM_QUIT request (as part of + mysql_close()). Make the life simpler for the client by sending + the response for the coming COM_QUIT in advance + */ + send_eof(thd); close_connection(thd, 0, 1); close_thread_tables(thd); // Free before kill kill_mysql(); @@ -2047,65 +1197,78 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #endif case COM_STATISTICS: { - mysql_log.write(thd,command,NullS); - statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS], - &LOCK_status); + STATUS_VAR current_global_status_var; + ulong uptime; + uint length; + ulonglong queries_per_second1000; #ifndef EMBEDDED_LIBRARY - char buff[200]; + char buff[250]; + uint buff_len= sizeof(buff); #else char *buff= thd->net.last_error; + uint buff_len= sizeof(thd->net.last_error); #endif - STATUS_VAR current_global_status_var; + general_log_print(thd, command, NullS); + status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS]); calc_sum_of_all_status(¤t_global_status_var); - - ulong uptime = (ulong) (thd->start_time - server_start_time); - sprintf((char*) buff, - "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, (ulong) thd->query_id, - current_global_status_var.long_query_count, - current_global_status_var.opened_tables, refresh_version, cached_tables(), - (uptime ? (ulonglong2double(thd->query_id) / (double) uptime) : - (double) 0)); + if (!(uptime= (ulong) (thd->start_time - server_start_time))) + queries_per_second1000= 0; + else + queries_per_second1000= thd->query_id * LL(1000) / uptime; + + length= my_snprintf((char*) buff, buff_len - 1, + "Uptime: %lu Threads: %d Questions: %lu " + "Slow queries: %lu Opens: %lu Flush tables: %lu " + "Open tables: %u Queries per second avg: %u.%u", + uptime, + (int) thread_count, (ulong) thd->query_id, + current_global_status_var.long_query_count, + current_global_status_var.opened_tables, + refresh_version, + cached_open_tables(), + (uint) (queries_per_second1000 / 1000), + (uint) (queries_per_second1000 % 1000)); #ifdef SAFEMALLOC if (sf_malloc_cur_memory) // Using SAFEMALLOC - sprintf(strend(buff), " Memory in use: %ldK Max memory used: %ldK", - (sf_malloc_cur_memory+1023L)/1024L, - (sf_malloc_max_memory+1023L)/1024L); + { + char *end= buff + length; + length+= my_snprintf(end, buff_len - length - 1, + end," Memory in use: %ldK Max memory used: %ldK", + (sf_malloc_cur_memory+1023L)/1024L, + (sf_malloc_max_memory+1023L)/1024L); + } #endif #ifndef EMBEDDED_LIBRARY - VOID(my_net_write(net, buff,(uint) strlen(buff))); - VOID(net_flush(net)); + VOID(my_net_write(net, (uchar*) buff, length)); + VOID(net_flush(net)); #endif break; } case COM_PING: - statistic_increment(thd->status_var.com_other, &LOCK_status); + status_var_increment(thd->status_var.com_other); send_ok(thd); // Tell client we are alive break; case COM_PROCESS_INFO: - statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST], - &LOCK_status); + status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST]); if (!thd->security_ctx->priv_user[0] && check_global_access(thd, PROCESS_ACL)) break; - mysql_log.write(thd,command,NullS); + general_log_print(thd, command, NullS); mysqld_list_processes(thd, thd->security_ctx->master_access & PROCESS_ACL ? NullS : thd->security_ctx->priv_user, 0); break; case COM_PROCESS_KILL: { - statistic_increment(thd->status_var.com_stat[SQLCOM_KILL], &LOCK_status); + status_var_increment(thd->status_var.com_stat[SQLCOM_KILL]); ulong id=(ulong) uint4korr(packet); - kill_one_thread(thd,id,false); + sql_kill(thd,id,false); break; } case COM_SET_OPTION: { - statistic_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION], - &LOCK_status); + status_var_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION]); uint opt_command= uint2korr(packet); switch (opt_command) { @@ -2124,11 +1287,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } case COM_DEBUG: - statistic_increment(thd->status_var.com_other, &LOCK_status); + status_var_increment(thd->status_var.com_other); if (check_global_access(thd, SUPER_ACL)) break; /* purecov: inspected */ mysql_print_status(); - mysql_log.write(thd,command,NullS); + general_log_print(thd, command, NullS); send_eof(thd); break; case COM_SLEEP: @@ -2182,7 +1345,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, void log_slow_statement(THD *thd) { - time_t start_of_query; + DBUG_ENTER("log_slow_statement"); /* The following should never be true with our current code base, @@ -2190,10 +1353,7 @@ void log_slow_statement(THD *thd) statement in a trigger or stored function */ if (unlikely(thd->in_sub_stmt)) - return; // Don't set time for sub stmt - - start_of_query= thd->start_time; - thd->end_time(); // Set start time + DBUG_VOID_RETURN; // Don't set time for sub stmt /* Do not log administrative statements unless the appropriate option is @@ -2201,20 +1361,22 @@ void log_slow_statement(THD *thd) */ if (thd->enable_slow_log && !thd->user_time) { - thd->proc_info="logging slow query"; + ulonglong end_utime_of_query= thd->current_utime(); - if ((ulong) (thd->start_time - thd->time_after_lock) > - thd->variables.long_query_time || - (thd->server_status & - (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) && - opt_log_queries_not_using_indexes && - /* == SQLCOM_END unless this is a SHOW command */ - thd->lex->orig_sql_command == SQLCOM_END) + thd->proc_info="logging slow query"; + if (((end_utime_of_query - thd->utime_after_lock) > + thd->variables.long_query_time || + ((thd->server_status & + (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) && + opt_log_queries_not_using_indexes && + !(sql_command_flags[thd->lex->sql_command] & CF_STATUS_COMMAND))) && + thd->examined_row_count >= thd->variables.min_examined_row_limit) { thd->status_var.long_query_count++; - mysql_slow_log.write(thd, thd->query, thd->query_length, start_of_query); + slow_log_print(thd, thd->query, thd->query_length, end_utime_of_query); } } + DBUG_VOID_RETURN; } @@ -2249,8 +1411,8 @@ void log_slow_statement(THD *thd) 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 *schema_select_lex= NULL; + DBUG_ENTER("prepare_schema_table"); switch (schema_table_idx) { case SCH_SCHEMATA: @@ -2266,63 +1428,59 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, case SCH_TABLES: case SCH_VIEWS: case SCH_TRIGGERS: + case SCH_EVENTS: #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 (lex->select_lex.db == NULL && - lex->copy_db_to(&lex->select_lex.db, NULL)) { - DBUG_RETURN(1); - } - - schema_select_lex= new SELECT_LEX(); - schema_select_lex->db= lex->select_lex.db; - schema_select_lex->table_list.first= NULL; - remove_escape(schema_select_lex->db); // Fix escaped '_' + LEX_STRING db; + size_t dummy; + if (lex->select_lex.db == NULL && + lex->copy_db_to(&lex->select_lex.db, &dummy)) + { + DBUG_RETURN(1); + } + schema_select_lex= new SELECT_LEX(); + db.str= schema_select_lex->db= lex->select_lex.db; + schema_select_lex->table_list.first= NULL; + db.length= strlen(db.str); - if (check_db_name(schema_select_lex->db)) - { - my_error(ER_WRONG_DB_NAME, MYF(0), schema_select_lex->db); - DBUG_RETURN(1); + if (check_db_name(&db)) + { + my_error(ER_WRONG_DB_NAME, MYF(0), db.str); + DBUG_RETURN(1); + } + break; } - - - 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 - { - DBUG_ASSERT(table_ident); - - TABLE_LIST **query_tables_last= lex->query_tables_last; - schema_select_lex= new SELECT_LEX(); - /* 'parent_lex' is used in init_query() so it must be before it. */ - schema_select_lex->parent_lex= lex; - schema_select_lex->init_query(); - if (!schema_select_lex->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 *dst_table= (TABLE_LIST*) schema_select_lex->table_list.first; - remove_escape(dst_table->db); // Fix escaped '_' - remove_escape(dst_table->table_name); - - break; - } + DBUG_ASSERT(table_ident); + TABLE_LIST **query_tables_last= lex->query_tables_last; + schema_select_lex= new SELECT_LEX(); + /* 'parent_lex' is used in init_query() so it must be before it. */ + schema_select_lex->parent_lex= lex; + schema_select_lex->init_query(); + if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ)) + DBUG_RETURN(1); + lex->query_tables_last= query_tables_last; + break; + } #endif case SCH_OPEN_TABLES: case SCH_VARIABLES: case SCH_STATUS: case SCH_PROCEDURES: case SCH_CHARSETS: + case SCH_ENGINES: case SCH_COLLATIONS: case SCH_COLLATION_CHARACTER_SET_APPLICABILITY: case SCH_USER_PRIVILEGES: @@ -2343,8 +1501,6 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first; table_list->schema_select_lex= schema_select_lex; table_list->schema_table_reformed= 1; - statistic_increment(thd->status_var.com_stat[lex->orig_sql_command], - &LOCK_status); DBUG_RETURN(0); } @@ -2381,7 +1537,7 @@ bool alloc_query(THD *thd, const char *packet, uint packet_length) } /* We must allocate some extra memory for query cache */ thd->query_length= 0; // Extra safety: Avoid races - if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet), + if (!(thd->query= (char*) thd->memdup_w_gap((uchar*) (packet), packet_length, thd->db_length+ 1 + QUERY_CACHE_FLAGS_SIZE))) @@ -2414,6 +1570,91 @@ static void reset_one_shot_variables(THD *thd) } +static +bool sp_process_definer(THD *thd) +{ + DBUG_ENTER("sp_process_definer"); + + LEX *lex= thd->lex; + + /* + If the definer is not specified, this means that CREATE-statement missed + DEFINER-clause. DEFINER-clause can be missed in two cases: + + - The user submitted a statement w/o the clause. This is a normal + case, we should assign CURRENT_USER as definer. + + - Our slave received an updated from the master, that does not + replicate definer for stored rountines. We should also assign + CURRENT_USER as definer here, but also we should mark this routine + as NON-SUID. This is essential for the sake of backward + compatibility. + + The problem is the slave thread is running under "special" user (@), + that actually does not exist. In the older versions we do not fail + execution of a stored routine if its definer does not exist and + continue the execution under the authorization of the invoker + (BUG#13198). And now if we try to switch to slave-current-user (@), + we will fail. + + Actually, this leads to the inconsistent state of master and + slave (different definers, different SUID behaviour), but it seems, + this is the best we can do. + */ + + if (!lex->definer) + { + Query_arena original_arena; + Query_arena *ps_arena= thd->activate_stmt_arena_if_needed(&original_arena); + + lex->definer= create_default_definer(thd); + + if (ps_arena) + thd->restore_active_arena(ps_arena, &original_arena); + + /* Error has been already reported. */ + if (lex->definer == NULL) + DBUG_RETURN(TRUE); + + if (thd->slave_thread && lex->sphead) + lex->sphead->m_chistics->suid= SP_IS_NOT_SUID; + } + else + { + /* + If the specified definer differs from the current user, we + should check that the current user has SUPER privilege (in order + to create a stored routine under another user one must have + SUPER privilege). + */ + if ((strcmp(lex->definer->user.str, thd->security_ctx->priv_user) || + my_strcasecmp(system_charset_info, lex->definer->host.str, + thd->security_ctx->priv_host)) && + check_global_access(thd, SUPER_ACL)) + { + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); + DBUG_RETURN(TRUE); + } + } + + /* Check that the specified definer exists. Emit a warning if not. */ + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (!is_acl_user(lex->definer->host.str, lex->definer->user.str)) + { + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_NO_SUCH_USER, + ER(ER_NO_SUCH_USER), + lex->definer->user.str, + lex->definer->host.str); + } +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ + + DBUG_RETURN(FALSE); +} + + /* Execute command saved in thd and lex->sql_command @@ -2456,20 +1697,9 @@ mysql_execute_command(THD *thd) /* Saved variable value */ DBUG_ENTER("mysql_execute_command"); thd->net.no_send_error= 0; - - /* - Remember first generated insert id value of the previous - statement. We remember it here at the beginning of the statement, - and also in Item_func_last_insert_id::fix_fields() and - sys_var_last_insert_id::value_ptr(). Last two places are required - because LAST_INSERT_ID() and @@LAST_INSERT_ID may also be used in - expression that is not executed with mysql_execute_command(). - - And we remember it here because some statements read - @@LAST_INSERT_ID indirectly, like "SELECT * FROM t1 WHERE id IS - NULL", that may replace "id IS NULL" with "id = <LAST_INSERT_ID>". - */ - thd->current_insert_id= thd->last_insert_id; +#ifdef WITH_PARTITION_STORAGE_ENGINE + thd->work_part_info= 0; +#endif /* In many cases first table of main SELECT_LEX have special meaning => @@ -2501,9 +1731,7 @@ mysql_execute_command(THD *thd) variables, but for now this is probably good enough. Don't reset warnings when executing a stored routine. */ - if ((all_tables || &lex->select_lex != lex->all_selects_list || - lex->sroutines.records) && !thd->spcont || - lex->time_zone_tables_used) + if ((all_tables || !lex->is_single_level_stmt()) && !thd->spcont) mysql_reset_errors(thd, 0); #ifdef HAVE_REPLICATION @@ -2582,8 +1810,8 @@ mysql_execute_command(THD *thd) tables. Except for the replication thread and the 'super' users. */ if (opt_readonly && - !(thd->security_ctx->master_access & SUPER_ACL) && - uc_update_queries[lex->sql_command] && + !(thd->security_ctx->master_access & SUPER_ACL) && + (sql_command_flags[lex->sql_command] & CF_CHANGES_DATA) && !((lex->sql_command == SQLCOM_CREATE_TABLE) && (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) && !((lex->sql_command == SQLCOM_DROP_TABLE) && lex->drop_temporary) && @@ -2596,80 +1824,67 @@ mysql_execute_command(THD *thd) #ifdef HAVE_REPLICATION } /* endif unlikely slave */ #endif - if(lex->orig_sql_command == SQLCOM_END) - statistic_increment(thd->status_var.com_stat[lex->sql_command], - &LOCK_status); + status_var_increment(thd->status_var.com_stat[lex->sql_command]); DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE); switch (lex->sql_command) { + case SQLCOM_SHOW_EVENTS: + if ((res= check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0, + is_schema_db(thd->lex->select_lex.db)))) + break; + /* fall through */ + case SQLCOM_SHOW_STATUS_PROC: + case SQLCOM_SHOW_STATUS_FUNC: + res= execute_sqlcom_select(thd, all_tables); + break; + case SQLCOM_SHOW_STATUS: + { + system_status_var old_status_var= thd->status_var; + thd->initial_status_var= &old_status_var; + res= execute_sqlcom_select(thd, all_tables); + /* Don't log SHOW STATUS commands to slow query log */ + thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | + SERVER_QUERY_NO_GOOD_INDEX_USED); + /* + restore status variables, as we don't want 'show status' to cause + changes + */ + pthread_mutex_lock(&LOCK_status); + add_diff_to_status(&global_status_var, &thd->status_var, + &old_status_var); + thd->status_var= old_status_var; + pthread_mutex_unlock(&LOCK_status); + break; + } + case SQLCOM_SHOW_DATABASES: + case SQLCOM_SHOW_TABLES: + case SQLCOM_SHOW_TRIGGERS: + case SQLCOM_SHOW_TABLE_STATUS: + case SQLCOM_SHOW_OPEN_TABLES: + case SQLCOM_SHOW_PLUGINS: + case SQLCOM_SHOW_FIELDS: + case SQLCOM_SHOW_KEYS: + case SQLCOM_SHOW_VARIABLES: + case SQLCOM_SHOW_CHARSETS: + case SQLCOM_SHOW_COLLATIONS: + case SQLCOM_SHOW_STORAGE_ENGINES: case SQLCOM_SELECT: - { - /* assign global limit variable if limit is not given */ - { - SELECT_LEX *param= lex->unit.global_parameters; - if (!param->explicit_limit) - param->select_limit= - new Item_int((ulonglong)thd->variables.select_limit); - } - - select_result *sel_result=lex->result; + thd->status_var.last_query_cost= 0.0; if (all_tables) { - if (lex->orig_sql_command != SQLCOM_SHOW_STATUS_PROC && - lex->orig_sql_command != SQLCOM_SHOW_STATUS_FUNC) - res= check_table_access(thd, - lex->exchange ? SELECT_ACL | FILE_ACL : - SELECT_ACL, - all_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, 0); - if (res) - goto error; - - if (!(res= open_and_lock_tables(thd, all_tables))) - { - if (lex->describe) - { - /* - We always use select_send for EXPLAIN, even if it's an EXPLAIN - for SELECT ... INTO OUTFILE: a user application should be able - to prepend EXPLAIN to any query and receive output for it, - even if the query itself redirects the output. - */ - if (!(sel_result= new select_send())) - goto error; - else - thd->send_explain_fields(sel_result); - res= mysql_explain_union(thd, &thd->lex->unit, sel_result); - if (lex->describe & DESCRIBE_EXTENDED) - { - char buff[1024]; - String str(buff,(uint32) sizeof(buff), system_charset_info); - str.length(0); - thd->lex->unit.print(&str); - str.append('\0'); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_YES, str.ptr()); - } - sel_result->send_eof(); - delete sel_result; - } - else - { - if (!sel_result && !(sel_result= new select_send())) - goto error; - query_cache_store_query(thd, all_tables); - res= handle_select(thd, lex, sel_result, 0); - if (sel_result != lex->result) - delete sel_result; - } - } + lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, + any_db, 0, 0, 0, 0); + if (!res) + res= execute_sqlcom_select(thd, all_tables); break; - } case SQLCOM_PREPARE: { mysql_sql_stmt_prepare(thd); @@ -2783,34 +1998,31 @@ mysql_execute_command(THD *thd) case SQLCOM_BACKUP_TABLE: { 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) || + if (check_table_access(thd, SELECT_ACL, all_tables, 0) || check_global_access(thd, FILE_ACL)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res = mysql_backup_table(thd, first_table); - select_lex->table_list.first= (byte*) first_table; + select_lex->table_list.first= (uchar*) first_table; lex->query_tables=all_tables; break; } case SQLCOM_RESTORE_TABLE: { 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) || + if (check_table_access(thd, INSERT_ACL, all_tables, 0) || check_global_access(thd, FILE_ACL)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res = mysql_restore_table(thd, first_table); - select_lex->table_list.first= (byte*) first_table; + select_lex->table_list.first= (uchar*) first_table; lex->query_tables=all_tables; break; } case SQLCOM_ASSIGN_TO_KEYCACHE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_db_used(thd, all_tables) || - check_access(thd, INDEX_ACL, first_table->db, + if (check_access(thd, INDEX_ACL, first_table->db, &first_table->grant.privilege, 0, 0, test(first_table->schema_table))) goto error; @@ -2820,8 +2032,7 @@ mysql_execute_command(THD *thd) case SQLCOM_PRELOAD_KEYS: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_db_used(thd, all_tables) || - check_access(thd, INDEX_ACL, first_table->db, + if (check_access(thd, INDEX_ACL, first_table->db, &first_table->grant.privilege, 0, 0, test(first_table->schema_table))) goto error; @@ -2862,33 +2073,23 @@ mysql_execute_command(THD *thd) goto error; if (end_active_trans(thd)) goto error; - else - res = load_master_data(thd); + res = load_master_data(thd); break; #endif /* HAVE_REPLICATION */ -#ifdef HAVE_NDBCLUSTER_DB - case SQLCOM_SHOW_NDBCLUSTER_STATUS: - { - res = ndbcluster_show_status(thd); - break; - } -#endif -#ifdef HAVE_INNOBASE_DB - case SQLCOM_SHOW_INNODB_STATUS: + case SQLCOM_SHOW_ENGINE_STATUS: { if (check_global_access(thd, SUPER_ACL)) - goto error; - res = innodb_show_status(thd); + goto error; + res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_STATUS); break; } - case SQLCOM_SHOW_MUTEX_STATUS: + case SQLCOM_SHOW_ENGINE_MUTEX: { if (check_global_access(thd, SUPER_ACL)) goto error; - res = innodb_mutex_show_status(thd); + res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_MUTEX); break; } -#endif #ifdef HAVE_REPLICATION case SQLCOM_LOAD_MASTER_TABLE: { @@ -2899,17 +2100,10 @@ mysql_execute_command(THD *thd) &first_table->grant.privilege, 0, 0, test(first_table->schema_table))) goto error; /* purecov: inspected */ - if (grant_option) - { - /* Check that the first table has CREATE privilege */ - if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0)) - goto error; - } - if (strlen(first_table->table_name) > NAME_LEN) - { - my_error(ER_WRONG_TABLE_NAME, MYF(0), first_table->table_name); - break; - } + /* Check that the first table has CREATE privilege */ + if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0)) + goto error; + pthread_mutex_lock(&LOCK_active_mi); /* fetch_master_table will send the error to the client on failure. @@ -2936,11 +2130,6 @@ mysql_execute_command(THD *thd) break; } } - else - { - /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */ - thd->transaction.all.modified_non_trans_table= TRUE; - } DBUG_ASSERT(first_table == all_tables && first_table != 0); bool link_to_local; // Skip first table, which is the table we are creating @@ -2954,11 +2143,16 @@ mysql_execute_command(THD *thd) referenced from this structure. */ HA_CREATE_INFO create_info(lex->create_info); + /* + We need to copy alter_info for the same reasons of re-execution + safety, only in case of Alter_info we have to do (almost) a deep + copy. + */ Alter_info alter_info(lex->alter_info, thd->mem_root); if (thd->is_fatal_error) { - /* out of memory when creating a copy of alter_info */ + /* If out of memory when creating a copy of alter_info. */ res= 1; goto end_with_restore_list; } @@ -2966,6 +2160,7 @@ mysql_execute_command(THD *thd) if ((res= create_table_precheck(thd, select_tables, create_table))) goto end_with_restore_list; + /* Might have been updated in create_table_precheck */ create_info.alias= create_table->alias; #ifndef HAVE_READLINK @@ -3016,14 +2211,25 @@ mysql_execute_command(THD *thd) res= 1; goto end_with_restore_list; } +#ifdef WITH_PARTITION_STORAGE_ENGINE + { + partition_info *part_info= thd->lex->part_info; + if (part_info && !(part_info= thd->lex->part_info->get_clone())) + { + res= -1; + goto end_with_restore_list; + } + thd->work_part_info= part_info; + } +#endif if (select_lex->item_list.elements) // With select { - select_result *sel_result; + select_result *result; select_lex->options|= SELECT_NO_UNLOCK; unit->set_limit(select_lex); - if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) + if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE)) { lex->link_first_table_back(create_table, link_to_local); create_table->create= TRUE; @@ -3063,33 +2269,38 @@ mysql_execute_command(THD *thd) } } } + /* select_create is currently not re-execution friendly and needs to be created for every execution of a PS/SP. */ - if ((sel_result= new select_create(create_table, - &create_info, - &alter_info, - select_lex->item_list, - lex->duplicates, - lex->ignore))) + if ((result= new select_create(create_table, + &create_info, + &alter_info, + select_lex->item_list, + lex->duplicates, + lex->ignore, + select_tables))) { /* CREATE from SELECT give its SELECT_LEX for SELECT, and item_list belong to SELECT */ - res= handle_select(thd, lex, sel_result, 0); - delete sel_result; + res= handle_select(thd, lex, result, 0); + delete result; } } - else if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) + else if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE)) create_table= lex->unlink_first_table(&link_to_local); } else { + /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */ + if (create_info.options & HA_LEX_CREATE_TMP_TABLE) + thd->options|= OPTION_KEEP_LOG; /* regular create */ - if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) + if (create_info.options & HA_LEX_CREATE_TABLE_LIKE) res= mysql_create_like_table(thd, create_table, select_tables, &create_info); else @@ -3112,25 +2323,22 @@ end_with_restore_list: case SQLCOM_DROP_INDEX: /* CREATE INDEX and DROP INDEX are implemented by calling ALTER - TABLE with proper arguments. This isn't very fast but it - should work for most cases. + TABLE with proper arguments. - In the future ALTER TABLE will notice that only added - indexes and create these one by one for the existing table - without having to do a full rebuild. - - One should normally create all indexes with CREATE TABLE or - ALTER TABLE. + In the future ALTER TABLE will notice that the request is to + only add indexes and create these one by one for the existing + table without having to do a full rebuild. */ { - Alter_info alter_info(lex->alter_info, thd->mem_root); + /* Prepare stack copies to be re-execution safe */ HA_CREATE_INFO create_info; + Alter_info alter_info(lex->alter_info, thd->mem_root); - if (thd->is_fatal_error) /* out of memory creating a copy of alter_info*/ + if (thd->is_fatal_error) /* out of memory creating a copy of alter_info */ goto error; DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_one_table_access(thd, INDEX_ACL, first_table)) + if (check_one_table_access(thd, INDEX_ACL, all_tables)) goto error; /* purecov: inspected */ if (end_active_trans(thd)) goto error; @@ -3142,7 +2350,7 @@ end_with_restore_list: thd->enable_slow_log= opt_log_slow_admin_statements; bzero((char*) &create_info, sizeof(create_info)); - create_info.db_type= DB_TYPE_DEFAULT; + create_info.db_type= 0; create_info.row_type= ROW_TYPE_NOT_USED; create_info.default_table_charset= thd->variables.collation_database; @@ -3191,6 +2399,7 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); { ulong priv=0; + ulong priv_needed= ALTER_ACL; /* Code in mysql_alter_table() may modify its HA_CREATE_INFO argument, so we have to use a copy of this structure to make execution @@ -3202,14 +2411,16 @@ end_with_restore_list: if (thd->is_fatal_error) /* out of memory creating a copy of alter_info */ goto error; - if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN)) - { - my_error(ER_WRONG_TABLE_NAME, MYF(0), lex->name); - goto error; - } + /* + We also require DROP priv for ALTER TABLE ... DROP PARTITION, as well + as for RENAME TO, as being done by SQLCOM_RENAME_TABLE + */ + if (alter_info.flags & (ALTER_DROP_PARTITION | ALTER_RENAME)) + priv_needed|= DROP_ACL; + /* Must be set in the parser */ DBUG_ASSERT(select_lex->db); - if (check_access(thd, ALTER_ACL, first_table->db, + if (check_access(thd, priv_needed, first_table->db, &first_table->grant.privilege, 0, 0, test(first_table->schema_table)) || check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0, @@ -3218,22 +2429,20 @@ end_with_restore_list: (TABLE_LIST *) create_info.merge_list.first)) goto error; /* purecov: inspected */ - if (grant_option) - { - 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.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, - UINT_MAX, 0)) - goto error; - } + if (check_grant(thd, priv_needed, all_tables, 0, UINT_MAX, 0)) + goto error; + if (lex->name.str && !test_all_bits(priv,INSERT_ACL | CREATE_ACL)) + { // Rename of table + TABLE_LIST tmp_table; + bzero((char*) &tmp_table,sizeof(tmp_table)); + tmp_table.table_name= lex->name.str; + tmp_table.db=select_lex->db; + tmp_table.grant.privilege=priv; + if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0, + UINT_MAX, 0)) + goto error; } + /* Don't yet allow changing of symlinks with ALTER TABLE */ if (create_info.data_file_name) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, @@ -3245,32 +2454,28 @@ end_with_restore_list: /* ALTER TABLE ends previous transaction */ if (end_active_trans(thd)) goto error; - else - { - if (!thd->locked_tables && - !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) - { - res= 1; - break; - } - thd->enable_slow_log= opt_log_slow_admin_statements; - res= mysql_alter_table(thd, select_lex->db, lex->name, - &create_info, - first_table, - &alter_info, - select_lex->order_list.elements, - (ORDER *) select_lex->order_list.first, - lex->ignore); + if (!thd->locked_tables && + !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) + { + res= 1; + break; } + + thd->enable_slow_log= opt_log_slow_admin_statements; + res= mysql_alter_table(thd, select_lex->db, lex->name.str, + &create_info, + first_table, + &alter_info, + select_lex->order_list.elements, + (ORDER *) select_lex->order_list.first, + lex->ignore); break; } case SQLCOM_RENAME_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); TABLE_LIST *table; - if (check_db_used(thd, all_tables)) - goto error; for (table= first_table; table; table= table->next_local->next_local) { if (check_access(thd, ALTER_ACL | DROP_ACL, table->db, @@ -3279,24 +2484,21 @@ end_with_restore_list: &table->next_local->grant.privilege, 0, 0, test(table->next_local->schema_table))) goto error; - if (grant_option) - { - 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_local[0]; - if (check_grant(thd, ALTER_ACL | DROP_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, 1, 0))) - goto error; - } + 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_local[0]; + if (check_grant(thd, ALTER_ACL | DROP_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, 1, 0))) + goto error; } - query_cache_invalidate3(thd, first_table, 0); - if (end_active_trans(thd) || mysql_rename_tables(thd, first_table)) + + if (end_active_trans(thd) || mysql_rename_tables(thd, first_table, 0)) goto error; break; } @@ -3326,9 +2528,7 @@ end_with_restore_list: /* 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_show_create_table_access(thd, first_table)) + if (check_show_create_table_access(thd, first_table)) goto error; res= mysqld_show_create(thd, first_table); break; @@ -3337,8 +2537,7 @@ end_with_restore_list: case SQLCOM_CHECKSUM: { 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)) + if (check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, 0)) goto error; /* purecov: inspected */ res = mysql_checksum_table(thd, first_table, &lex->check_opt); break; @@ -3346,58 +2545,49 @@ end_with_restore_list: case SQLCOM_REPAIR: { 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)) + if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res= mysql_repair_table(thd, first_table, &lex->check_opt); /* ! we write after unlocking the table */ if (!res && !lex->no_write_to_binlog) { - /* Presumably, REPAIR and binlog writing doesn't require synchronization */ - if (mysql_bin_log.is_open()) - { - thd->clear_error(); // No binlog error generated - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - } + /* + Presumably, REPAIR and binlog writing doesn't require synchronization + */ + write_bin_log(thd, TRUE, thd->query, thd->query_length); } - select_lex->table_list.first= (byte*) first_table; + select_lex->table_list.first= (uchar*) first_table; lex->query_tables=all_tables; break; } case SQLCOM_CHECK: { 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)) + if (check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, 0)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res = mysql_check_table(thd, first_table, &lex->check_opt); - select_lex->table_list.first= (byte*) first_table; + select_lex->table_list.first= (uchar*) first_table; lex->query_tables=all_tables; break; } case SQLCOM_ANALYZE: { 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)) + if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; - res = mysql_analyze_table(thd, first_table, &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) { - /* Presumably, ANALYZE and binlog writing doesn't require synchronization */ - if (mysql_bin_log.is_open()) - { - thd->clear_error(); // No binlog error generated - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - } + /* + Presumably, ANALYZE and binlog writing doesn't require synchronization + */ + write_bin_log(thd, TRUE, thd->query, thd->query_length); } - select_lex->table_list.first= (byte*) first_table; + select_lex->table_list.first= (uchar*) first_table; lex->query_tables=all_tables; break; } @@ -3405,8 +2595,7 @@ end_with_restore_list: case SQLCOM_OPTIMIZE: { 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)) + if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ? @@ -3415,15 +2604,12 @@ end_with_restore_list: /* ! we write after unlocking the table */ if (!res && !lex->no_write_to_binlog) { - /* Presumably, OPTIMIZE and binlog writing doesn't require synchronization */ - if (mysql_bin_log.is_open()) - { - thd->clear_error(); // No binlog error generated - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - } + /* + Presumably, OPTIMIZE and binlog writing doesn't require synchronization + */ + write_bin_log(thd, TRUE, thd->query, thd->query_length); } - select_lex->table_list.first= (byte*) first_table; + select_lex->table_list.first= (uchar*) first_table; lex->query_tables=all_tables; break; } @@ -3502,6 +2688,36 @@ end_with_restore_list: break; } case SQLCOM_REPLACE: +#ifndef DBUG_OFF + if (mysql_bin_log.is_open()) + { + /* + Generate an incident log event before writing the real event + to the binary log. We put this event is before the statement + since that makes it simpler to check that the statement was + not executed on the slave (since incidents usually stop the + slave). + + Observe that any row events that are generated will be + generated before. + + This is only for testing purposes and will not be present in a + release build. + */ + + Incident incident= INCIDENT_NONE; + DBUG_PRINT("debug", ("Just before generate_incident()")); + DBUG_EXECUTE_IF("incident_database_resync_on_replace", + incident= INCIDENT_LOST_EVENTS;); + if (incident) + { + Incident_log_event ev(thd, incident); + mysql_bin_log.write(&ev); + mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE); + } + DBUG_PRINT("debug", ("Just after generate_incident()")); + } +#endif case SQLCOM_INSERT: { DBUG_ASSERT(first_table == all_tables && first_table != 0); @@ -3526,7 +2742,8 @@ end_with_restore_list: had before the statement. */ if (first_table->view && !first_table->contain_auto_increment) - thd->last_insert_id= thd->current_insert_id; + thd->first_successful_insert_id_in_cur_stmt= + thd->first_successful_insert_id_in_prev_stmt; break; } @@ -3558,7 +2775,7 @@ end_with_restore_list: { /* Skip first table, which is the table we are inserting in */ TABLE_LIST *second_table= first_table->next_local; - select_lex->table_list.first= (byte*) second_table; + select_lex->table_list.first= (uchar*) second_table; select_lex->context.table_list= select_lex->context.first_name_resolution_table= second_table; res= mysql_insert_select_prepare(thd); @@ -3591,7 +2808,7 @@ end_with_restore_list: delete sel_result; } /* revert changes for SP */ - select_lex->table_list.first= (byte*) first_table; + select_lex->table_list.first= (uchar*) first_table; } /* @@ -3601,7 +2818,8 @@ end_with_restore_list: had before the statement. */ if (first_table->view && !first_table->contain_auto_increment) - thd->last_insert_id= thd->current_insert_id; + thd->first_successful_insert_id_in_cur_stmt= + thd->first_successful_insert_id_in_prev_stmt; break; } @@ -3612,7 +2830,7 @@ end_with_restore_list: break; } DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_one_table_access(thd, DELETE_ACL, all_tables)) + if (check_one_table_access(thd, DROP_ACL, all_tables)) goto error; /* Don't allow this within a transaction because we want to use @@ -3722,7 +2940,7 @@ end_with_restore_list: lex->drop_if_exists= 1; /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */ - thd->transaction.all.modified_non_trans_table= TRUE; + thd->options|= OPTION_KEEP_LOG; } /* DDL and binlog write order protected by LOCK_open */ res= mysql_rm_table(thd, first_table, lex->drop_if_exists, @@ -3739,8 +2957,11 @@ end_with_restore_list: thd->security_ctx->priv_user), lex->verbose); break; - case SQLCOM_SHOW_STORAGE_ENGINES: - res= mysqld_show_storage_engines(thd); + case SQLCOM_SHOW_AUTHORS: + res= mysqld_show_authors(thd); + break; + case SQLCOM_SHOW_CONTRIBUTORS: + res= mysqld_show_contributors(thd); break; case SQLCOM_SHOW_PRIVILEGES: res= mysqld_show_privileges(thd); @@ -3748,16 +2969,16 @@ end_with_restore_list: case SQLCOM_SHOW_COLUMN_TYPES: res= mysqld_show_column_types(thd); break; - case SQLCOM_SHOW_LOGS: + case SQLCOM_SHOW_ENGINE_LOGS: #ifdef DONT_ALLOW_SHOW_COMMANDS 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,0)) + if (check_access(thd, FILE_ACL, any_db,0,0,0,0)) goto error; - res= mysqld_show_logs(thd); + res= ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_LOGS); break; } #endif @@ -3842,8 +3063,6 @@ end_with_restore_list: /* we must end the trasaction first, regardless of anything */ if (end_active_trans(thd)) goto error; - if (check_db_used(thd, all_tables)) - goto error; if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0)) goto error; thd->in_lock_tables=1; @@ -3885,9 +3104,10 @@ end_with_restore_list: break; } char *alias; - if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name)) + if (!(alias=thd->strmake(lex->name.str, lex->name.length)) || + check_db_name(&lex->name)) { - my_error(ER_WRONG_DB_NAME, MYF(0), lex->name); + my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str); break; } /* @@ -3898,19 +3118,19 @@ end_with_restore_list: above was not called. So we have to check rules again here. */ #ifdef HAVE_REPLICATION - if (thd->slave_thread && - (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) || - !db_ok_with_wild_table(lex->name))) + if (thd->slave_thread && + (!rpl_filter->db_ok(lex->name.str) || + !rpl_filter->db_ok_with_wild_table(lex->name.str))) { my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); break; } #endif - - if (check_access(thd,CREATE_ACL,lex->name,0,1,0,is_schema_db(lex->name))) + if (check_access(thd,CREATE_ACL,lex->name.str, 0, 1, 0, + is_schema_db(lex->name.str))) break; - res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name), - &create_info, 0); + res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : + lex->name.str), &create_info, 0); break; } case SQLCOM_DROP_DB: @@ -3920,9 +3140,9 @@ end_with_restore_list: res= -1; break; } - if (check_db_name(lex->name)) + if (check_db_name(&lex->name)) { - my_error(ER_WRONG_DB_NAME, MYF(0), lex->name); + my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str); break; } /* @@ -3934,31 +3154,79 @@ end_with_restore_list: */ #ifdef HAVE_REPLICATION if (thd->slave_thread && - (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) || - !db_ok_with_wild_table(lex->name))) + (!rpl_filter->db_ok(lex->name.str) || + !rpl_filter->db_ok_with_wild_table(lex->name.str))) + { + my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); + break; + } +#endif + if (check_access(thd,DROP_ACL,lex->name.str,0,1,0, + is_schema_db(lex->name.str))) + break; + if (thd->locked_tables || thd->active_transaction()) + { + my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, + ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); + goto error; + } + res= mysql_rm_db(thd, lex->name.str, lex->drop_if_exists, 0); + break; + } + case SQLCOM_RENAME_DB: + { + LEX_STRING *olddb, *newdb; + List_iterator <LEX_STRING> db_list(lex->db_list); + olddb= db_list++; + newdb= db_list++; + if (end_active_trans(thd)) + { + res= 1; + break; + } +#ifdef HAVE_REPLICATION + if (thd->slave_thread && + (!rpl_filter->db_ok(olddb->str) || + !rpl_filter->db_ok(newdb->str) || + !rpl_filter->db_ok_with_wild_table(olddb->str) || + !rpl_filter->db_ok_with_wild_table(newdb->str))) { + res= 1; my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); break; } #endif - if (check_access(thd,DROP_ACL,lex->name,0,1,0,is_schema_db(lex->name))) + if (check_db_name(newdb)) + { + my_error(ER_WRONG_DB_NAME, MYF(0), newdb->str); break; + } + if (check_access(thd,ALTER_ACL,olddb->str,0,1,0,is_schema_db(olddb->str)) || + check_access(thd,DROP_ACL,olddb->str,0,1,0,is_schema_db(olddb->str)) || + check_access(thd,CREATE_ACL,newdb->str,0,1,0,is_schema_db(newdb->str))) + { + res= 1; + break; + } if (thd->locked_tables || thd->active_transaction()) { + res= 1; my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); goto error; } - res= mysql_rm_db(thd, lex->name, lex->drop_if_exists, 0); + res= mysql_rename_db(thd, olddb, newdb); + if (!res) + send_ok(thd); break; } case SQLCOM_ALTER_DB: { - char *db= lex->name; - DBUG_ASSERT(db); /* Must be set in the parser */ - if (!strip_sp(db) || check_db_name(db)) + LEX_STRING *db= &lex->name; + HA_CREATE_INFO create_info(lex->create_info); + if (check_db_name(db)) { - my_error(ER_WRONG_DB_NAME, MYF(0), lex->name); + my_error(ER_WRONG_DB_NAME, MYF(0), db->str); break; } /* @@ -3970,14 +3238,14 @@ end_with_restore_list: */ #ifdef HAVE_REPLICATION if (thd->slave_thread && - (!db_ok(db, replicate_do_db, replicate_ignore_db) || - !db_ok_with_wild_table(db))) + (!rpl_filter->db_ok(db->str) || + !rpl_filter->db_ok_with_wild_table(db->str))) { my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); break; } #endif - if (check_access(thd, ALTER_ACL, db, 0, 1, 0, is_schema_db(db))) + if (check_access(thd, ALTER_ACL, db->str, 0, 1, 0, is_schema_db(db->str))) break; if (thd->locked_tables || thd->active_transaction()) { @@ -3985,23 +3253,76 @@ end_with_restore_list: ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); goto error; } - res= mysql_alter_db(thd, db, &lex->create_info); + res= mysql_alter_db(thd, db->str, &create_info); break; } case SQLCOM_SHOW_CREATE_DB: { DBUG_EXECUTE_IF("4x_server_emul", my_error(ER_UNKNOWN_ERROR, MYF(0)); goto error;); - if (!strip_sp(lex->name) || check_db_name(lex->name)) + if (check_db_name(&lex->name)) { - my_error(ER_WRONG_DB_NAME, MYF(0), lex->name); + my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str); break; } - if (check_access(thd,SELECT_ACL,lex->name,0,1,0,is_schema_db(lex->name))) - break; - res=mysqld_show_create_db(thd,lex->name,&lex->create_info); + res= mysqld_show_create_db(thd, lex->name.str, &lex->create_info); break; } + case SQLCOM_CREATE_EVENT: + case SQLCOM_ALTER_EVENT: + do + { + DBUG_ASSERT(lex->event_parse_data); + if (lex->table_or_sp_used()) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored " + "function calls as part of this statement"); + break; + } + + res= sp_process_definer(thd); + if (res) + break; + + switch (lex->sql_command) { + case SQLCOM_CREATE_EVENT: + { + bool if_not_exists= (lex->create_info.options & + HA_LEX_CREATE_IF_NOT_EXISTS); + res= Events::create_event(thd, lex->event_parse_data, if_not_exists); + break; + } + case SQLCOM_ALTER_EVENT: + res= Events::update_event(thd, lex->event_parse_data, + lex->spname ? &lex->spname->m_db : NULL, + lex->spname ? &lex->spname->m_name : NULL); + break; + default: + DBUG_ASSERT(0); + } + DBUG_PRINT("info",("DDL error code=%d", res)); + if (!res) + send_ok(thd); + + } while (0); + /* Don't do it, if we are inside a SP */ + if (!thd->spcont) + { + delete lex->sphead; + lex->sphead= NULL; + } + /* lex->unit.cleanup() is called outside, no need to call it here */ + break; + case SQLCOM_SHOW_CREATE_EVENT: + res= Events::show_create_event(thd, lex->spname->m_db, + lex->spname->m_name); + break; + case SQLCOM_DROP_EVENT: + if (!(res= Events::drop_event(thd, + lex->spname->m_db, lex->spname->m_name, + lex->drop_if_exists))) + send_ok(thd); + break; case SQLCOM_CREATE_FUNCTION: // UDF function { if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0)) @@ -4060,6 +3381,8 @@ end_with_restore_list: } case SQLCOM_REVOKE_ALL: { + if (end_active_trans(thd)) + goto error; if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) && check_global_access(thd,CREATE_USER_ACL)) break; @@ -4071,6 +3394,9 @@ end_with_restore_list: case SQLCOM_REVOKE: case SQLCOM_GRANT: { + if (end_active_trans(thd)) + goto error; + if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL, first_table ? first_table->db : select_lex->db, first_table ? &first_table->grant.privilege : 0, @@ -4120,8 +3446,7 @@ end_with_restore_list: 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, + if (check_grant_routine(thd, grants | GRANT_ACL, all_tables, lex->type == TYPE_ENUM_PROCEDURE, 0)) goto error; /* Conditionally writes to binlog */ @@ -4132,10 +3457,8 @@ end_with_restore_list: } else { - if (grant_option && check_grant(thd, - (lex->grant | lex->grant_tot_col | - GRANT_ACL), - all_tables, 0, UINT_MAX, 0)) + if (check_grant(thd,(lex->grant | lex->grant_tot_col | GRANT_ACL), + all_tables, 0, UINT_MAX, 0)) goto error; /* Conditionally writes to binlog */ res= mysql_table_grant(thd, all_tables, lex->users_list, @@ -4165,7 +3488,7 @@ end_with_restore_list: { if (!(user= get_current_user(thd, tmp_user))) goto error; - reset_mqh(user); + reset_mqh(user, 0); } } } @@ -4195,14 +3518,12 @@ end_with_restore_list: We WANT to write and we CAN write. ! we write after unlocking the table. */ - /* Presumably, RESET and binlog writing doesn't require synchronization */ + /* + Presumably, RESET and binlog writing doesn't require synchronization + */ if (!lex->no_write_to_binlog && write_to_binlog) { - if (mysql_bin_log.is_open()) - { - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - } + write_bin_log(thd, FALSE, thd->query, thd->query_length); } send_ok(thd); } @@ -4213,13 +3534,20 @@ end_with_restore_list: { Item *it= (Item *)lex->value_list.head(); + if (lex->table_or_sp_used()) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored " + "function calls as part of this statement"); + break; + } + if ((!it->fixed && it->fix_fields(lex->thd, &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); + sql_kill(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY); break; } #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -4239,15 +3567,12 @@ end_with_restore_list: #endif case SQLCOM_HA_OPEN: 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)) + if (check_table_access(thd, SELECT_ACL, all_tables, 0)) goto error; res= mysql_ha_open(thd, first_table, 0); break; case SQLCOM_HA_CLOSE: DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_db_used(thd, all_tables)) - goto error; res= mysql_ha_close(thd, first_table); break; case SQLCOM_HA_READ: @@ -4257,8 +3582,6 @@ end_with_restore_list: 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, all_tables)) - goto error; unit->set_limit(select_lex); res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->ident.str, lex->insert_list, lex->ha_rkey_mode, select_lex->where, @@ -4326,7 +3649,8 @@ end_with_restore_list: res= TRUE; // cannot happen else { - if (thd->transaction.all.modified_non_trans_table && + if (((thd->options & OPTION_KEEP_LOG) || + thd->transaction.all.modified_non_trans_table) && !thd->slave_thread) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARNING_NOT_COMPLETE_ROLLBACK, @@ -4396,7 +3720,7 @@ end_with_restore_list: Verify that the database name is allowed, optionally lowercase it. */ - if (check_db_name(lex->sphead->m_db.str)) + if (check_db_name(&lex->sphead->m_db)) { my_error(ER_WRONG_DB_NAME, MYF(0), lex->sphead->m_db.str); goto create_sp_error; @@ -4434,83 +3758,8 @@ end_with_restore_list: } #endif - /* - If the definer is not specified, this means that CREATE-statement missed - DEFINER-clause. DEFINER-clause can be missed in two cases: - - - The user submitted a statement w/o the clause. This is a normal - case, we should assign CURRENT_USER as definer. - - - Our slave received an updated from the master, that does not - replicate definer for stored rountines. We should also assign - CURRENT_USER as definer here, but also we should mark this routine - as NON-SUID. This is essential for the sake of backward - compatibility. - - The problem is the slave thread is running under "special" user (@), - that actually does not exist. In the older versions we do not fail - execution of a stored routine if its definer does not exist and - continue the execution under the authorization of the invoker - (BUG#13198). And now if we try to switch to slave-current-user (@), - we will fail. - - Actually, this leads to the inconsistent state of master and - slave (different definers, different SUID behaviour), but it seems, - this is the best we can do. - */ - - if (!lex->definer) - { - bool local_res= FALSE; - Query_arena original_arena; - Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena); - - if (!(lex->definer= create_default_definer(thd))) - local_res= TRUE; - - if (ps_arena) - thd->restore_active_arena(ps_arena, &original_arena); - - /* Error has been already reported. */ - if (local_res) - goto create_sp_error; - - if (thd->slave_thread) - lex->sphead->m_chistics->suid= SP_IS_NOT_SUID; - } - - /* - If the specified definer differs from the current user, we should check - that the current user has SUPER privilege (in order to create a stored - routine under another user one must have SUPER privilege). - */ - - else if (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) || - my_strcasecmp(system_charset_info, - lex->definer->host.str, - thd->security_ctx->priv_host)) - { - if (check_global_access(thd, SUPER_ACL)) - { - my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); - goto create_sp_error; - } - } - - /* Check that the specified definer exists. Emit a warning if not. */ - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - if (!is_acl_user(lex->definer->host.str, - lex->definer->user.str)) - { - push_warning_printf(thd, - MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_NO_SUCH_USER, - ER(ER_NO_SUCH_USER), - lex->definer->user.str, - lex->definer->host.str); - } -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ + if (sp_process_definer(thd)) + goto create_sp_error; res= (sp_result= lex->sphead->create(thd)); switch (sp_result) { @@ -4655,7 +3904,10 @@ create_sp_error: send_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 : thd->row_count_func)); else + { + DBUG_ASSERT(thd->net.report_error == 1 || thd->killed); goto error; // Substatement should already have sent error + } } break; } @@ -4713,11 +3965,15 @@ create_sp_error: already puts on CREATE FUNCTION. */ /* Conditionally writes to binlog */ - if (lex->sql_command == SQLCOM_ALTER_PROCEDURE) - sp_result= sp_update_procedure(thd, lex->spname, - &lex->sp_chistics); - else - sp_result= sp_update_function(thd, lex->spname, &lex->sp_chistics); + + int type= lex->sql_command == SQLCOM_ALTER_PROCEDURE ? + TYPE_ENUM_PROCEDURE : + TYPE_ENUM_FUNCTION; + + sp_result= sp_update_routine(thd, + type, + lex->spname, + &lex->sp_chistics); } } switch (sp_result) @@ -4766,11 +4022,13 @@ create_sp_error: ER(ER_PROC_AUTO_REVOKE_FAIL)); } #endif - /* Conditionally writes to binlog */ - if (lex->sql_command == SQLCOM_DROP_PROCEDURE) - sp_result= sp_drop_procedure(thd, lex->spname); - else - sp_result= sp_drop_function(thd, lex->spname); + /* Conditionally writes to binlog */ + + int type= lex->sql_command == SQLCOM_DROP_PROCEDURE ? + TYPE_ENUM_PROCEDURE : + TYPE_ENUM_FUNCTION; + + sp_result= sp_drop_routine(thd, type, lex->spname); } else { @@ -4784,7 +4042,6 @@ create_sp_error: if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0, 0)) goto error; - /* Does NOT write to binlog */ if (!(res = mysql_drop_function(thd, &lex->spname->m_name))) { send_ok(thd); @@ -4828,13 +4085,8 @@ create_sp_error: } case SQLCOM_SHOW_CREATE_PROC: { - if (lex->spname->m_name.length > NAME_LEN) + if (sp_show_create_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname)) { - 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; @@ -4843,42 +4095,34 @@ create_sp_error: } case SQLCOM_SHOW_CREATE_FUNC: { - if (lex->spname->m_name.length > NAME_LEN) + if (sp_show_create_routine(thd, TYPE_ENUM_FUNCTION, lex->spname)) { - 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; } +#ifdef NOT_USED case SQLCOM_SHOW_STATUS_PROC: { - res= sp_show_status_procedure(thd, (lex->wild ? - lex->wild->ptr() : NullS)); + res= sp_show_status_routine(thd, TYPE_ENUM_PROCEDURE, + (lex->wild ? lex->wild->ptr() : NullS)); break; } case SQLCOM_SHOW_STATUS_FUNC: { - res= sp_show_status_function(thd, (lex->wild ? - lex->wild->ptr() : NullS)); + res= sp_show_status_routine(thd, TYPE_ENUM_FUNCTION, + (lex->wild ? lex->wild->ptr() : NullS)); break; } +#endif #ifndef DBUG_OFF case SQLCOM_SHOW_PROC_CODE: case SQLCOM_SHOW_FUNC_CODE: { sp_head *sp; - if (lex->spname->m_name.length > NAME_LEN) - { - my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str); - goto error; - } if (lex->sql_command == SQLCOM_SHOW_PROC_CODE) sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname, &thd->sp_proc_cache, FALSE); @@ -4895,6 +4139,19 @@ create_sp_error: break; } #endif // ifndef DBUG_OFF + case SQLCOM_SHOW_CREATE_TRIGGER: + { + if (lex->spname->m_name.length > NAME_LEN) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str); + goto error; + } + + if (show_create_trigger(thd, lex->spname)) + goto error; /* Error has been already logged. */ + + break; + } case SQLCOM_CREATE_VIEW: { /* @@ -4974,7 +4231,7 @@ create_sp_error: thd->transaction.xid_state.xid.set(thd->lex->xid); xid_cache_insert(&thd->transaction.xid_state); thd->transaction.all.modified_non_trans_table= FALSE; - thd->options|= (ulong) OPTION_BEGIN; + thd->options= ((thd->options & ~(OPTION_KEEP_LOG)) | OPTION_BEGIN); thd->server_status|= SERVER_STATUS_IN_TRANS; send_ok(thd); break; @@ -5067,7 +4324,7 @@ create_sp_error: xa_state_names[thd->transaction.xid_state.xa_state]); break; } - thd->options&= ~(ulong) OPTION_BEGIN; + thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); thd->transaction.all.modified_non_trans_table= FALSE; thd->server_status&= ~SERVER_STATUS_IN_TRANS; xid_cache_delete(&thd->transaction.xid_state); @@ -5098,7 +4355,7 @@ create_sp_error: my_error(ER_XAER_RMERR, MYF(0)); else send_ok(thd); - thd->options&= ~(ulong) OPTION_BEGIN; + thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); thd->transaction.all.modified_non_trans_table= FALSE; thd->server_status&= ~SERVER_STATUS_IN_TRANS; xid_cache_delete(&thd->transaction.xid_state); @@ -5107,6 +4364,94 @@ create_sp_error: case SQLCOM_XA_RECOVER: res= mysql_xa_recover(thd); break; + case SQLCOM_ALTER_TABLESPACE: + if (check_access(thd, ALTER_ACL, thd->db, 0, 1, 0, thd->db ? is_schema_db(thd->db) : 0)) + break; + if (!(res= mysql_alter_tablespace(thd, lex->alter_tablespace_info))) + send_ok(thd); + break; + case SQLCOM_INSTALL_PLUGIN: + if (! (res= mysql_install_plugin(thd, &thd->lex->comment, + &thd->lex->ident))) + send_ok(thd); + break; + case SQLCOM_UNINSTALL_PLUGIN: + if (! (res= mysql_uninstall_plugin(thd, &thd->lex->comment))) + send_ok(thd); + break; + case SQLCOM_BINLOG_BASE64_EVENT: + { +#ifndef EMBEDDED_LIBRARY + mysql_client_binlog_statement(thd); +#else /* EMBEDDED_LIBRARY */ + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "embedded"); +#endif /* EMBEDDED_LIBRARY */ + break; + } + case SQLCOM_CREATE_SERVER: + { + int error; + LEX *lex= thd->lex; + DBUG_PRINT("info", ("case SQLCOM_CREATE_SERVER")); + + if (check_global_access(thd, SUPER_ACL)) + break; + + if ((error= create_server(thd, &lex->server_options))) + { + DBUG_PRINT("info", ("problem creating server <%s>", + lex->server_options.server_name)); + my_error(error, MYF(0), lex->server_options.server_name); + break; + } + send_ok(thd, 1); + break; + } + case SQLCOM_ALTER_SERVER: + { + int error; + LEX *lex= thd->lex; + DBUG_PRINT("info", ("case SQLCOM_ALTER_SERVER")); + + if (check_global_access(thd, SUPER_ACL)) + break; + + if ((error= alter_server(thd, &lex->server_options))) + { + DBUG_PRINT("info", ("problem altering server <%s>", + lex->server_options.server_name)); + my_error(error, MYF(0), lex->server_options.server_name); + break; + } + send_ok(thd, 1); + break; + } + case SQLCOM_DROP_SERVER: + { + int err_code; + LEX *lex= thd->lex; + DBUG_PRINT("info", ("case SQLCOM_DROP_SERVER")); + + if (check_global_access(thd, SUPER_ACL)) + break; + + if ((err_code= drop_server(thd, &lex->server_options))) + { + if (! lex->drop_if_exists && err_code == ER_FOREIGN_SERVER_DOESNT_EXIST) + { + DBUG_PRINT("info", ("problem dropping server %s", + lex->server_options.server_name)); + my_error(err_code, MYF(0), lex->server_options.server_name); + } + else + { + send_ok(thd, 0); + } + break; + } + send_ok(thd, 1); + break; + } default: #ifndef EMBEDDED_LIBRARY DBUG_ASSERT(0); /* Impossible */ @@ -5114,10 +4459,11 @@ create_sp_error: send_ok(thd); break; } + thd->proc_info="query end"; - /* Two binlog-related cleanups: */ /* + Binlog-related cleanup: Reset system variables temporarily modified by SET ONE SHOT. Exception: If this is a SET, do nothing. This is to allow @@ -5132,21 +4478,18 @@ create_sp_error: /* The return value for ROW_COUNT() is "implementation dependent" if the statement is not DELETE, INSERT or UPDATE, but -1 is what JDBC and ODBC - wants. - - We do not change the value for a CALL or EXECUTE statement, so the value - generated by the last called (or executed) statement is preserved. - */ - if (lex->sql_command != SQLCOM_CALL && lex->sql_command != SQLCOM_EXECUTE && - uc_update_queries[lex->sql_command]<2) + wants. We also keep the last value in case of SQLCOM_CALL or + SQLCOM_EXECUTE. + */ + if (!(sql_command_flags[lex->sql_command] & CF_HAS_ROW_COUNT)) thd->row_count_func= -1; - goto end; + goto finish; error: res= TRUE; -end: +finish: if (need_start_waiting) { /* @@ -5159,6 +4502,59 @@ end: } +static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) +{ + LEX *lex= thd->lex; + select_result *result=lex->result; + bool res; + /* assign global limit variable if limit is not given */ + { + SELECT_LEX *param= lex->unit.global_parameters; + if (!param->explicit_limit) + param->select_limit= + new Item_int((ulonglong) thd->variables.select_limit); + } + if (!(res= open_and_lock_tables(thd, all_tables))) + { + if (lex->describe) + { + /* + We always use select_send for EXPLAIN, even if it's an EXPLAIN + for SELECT ... INTO OUTFILE: a user application should be able + to prepend EXPLAIN to any query and receive output for it, + even if the query itself redirects the output. + */ + if (!(result= new select_send())) + return 1; /* purecov: inspected */ + thd->send_explain_fields(result); + res= mysql_explain_union(thd, &thd->lex->unit, result); + if (lex->describe & DESCRIBE_EXTENDED) + { + char buff[1024]; + String str(buff,(uint32) sizeof(buff), system_charset_info); + str.length(0); + thd->lex->unit.print(&str); + str.append('\0'); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_YES, str.ptr()); + } + result->send_eof(); + delete result; + } + else + { + if (!result && !(result= new select_send())) + return 1; /* purecov: inspected */ + query_cache_store_query(thd, all_tables); + res= handle_select(thd, lex, result, 0); + if (result != lex->result) + delete result; + } + } + return res; +} + + /* Check grants for commands which work only with one table. @@ -5167,6 +4563,8 @@ end: thd Thread handler privilege requested privilege all_tables global table list of query + no_errors FALSE/TRUE - report/don't report error to + the client (using my_error() call). RETURN 0 - OK @@ -5174,7 +4572,7 @@ end: */ bool check_single_table_access(THD *thd, ulong privilege, - TABLE_LIST *all_tables) + TABLE_LIST *all_tables, bool no_errors) { Security_context * backup_ctx= thd->security_ctx; @@ -5190,15 +4588,14 @@ bool check_single_table_access(THD *thd, ulong privilege, db_name= all_tables->db; if (check_access(thd, privilege, db_name, - &all_tables->grant.privilege, 0, 0, + &all_tables->grant.privilege, 0, no_errors, test(all_tables->schema_table))) goto deny; /* Show only 1 table for check_grant */ - if (grant_option && - !(all_tables->belong_to_view && + if (!(all_tables->belong_to_view && (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) && - check_grant(thd, privilege, all_tables, 0, 1, 0)) + check_grant(thd, privilege, all_tables, 0, 1, no_errors)) goto deny; thd->security_ctx= backup_ctx; @@ -5226,7 +4623,7 @@ deny: bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) { - if (check_single_table_access (thd,privilege,all_tables)) + if (check_single_table_access (thd,privilege,all_tables, FALSE)) return 1; /* Check rights on tables of subselects and implictly opened tables */ @@ -5239,7 +4636,7 @@ bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) */ if (view && subselects_tables->belong_to_view == view) { - if (check_single_table_access (thd, privilege, subselects_tables)) + if (check_single_table_access (thd, privilege, subselects_tables, FALSE)) return 1; subselects_tables= subselects_tables->next_global; } @@ -5374,9 +4771,8 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, db_access, want_access)); db_access= ((*save_priv=(db_access | sctx->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 && + (!dont_check_global_grants && !(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS)))) DBUG_RETURN(FALSE); /* Ok */ @@ -5428,74 +4824,56 @@ bool check_global_access(THD *thd, ulong want_access) static bool check_show_access(THD *thd, TABLE_LIST *table) { - switch (get_schema_table_idx(table->schema_table)) - { + switch (get_schema_table_idx(table->schema_table)) { case SCH_SCHEMATA: return (specialflag & SPECIAL_SKIP_SHOW_DB) && - check_global_access(thd, SHOW_DB_ACL); + check_global_access(thd, SHOW_DB_ACL); case SCH_TABLE_NAMES: case SCH_TABLES: case SCH_VIEWS: case SCH_TRIGGERS: - { - const char *dst_db_name= table->schema_select_lex->db; - - DBUG_ASSERT(dst_db_name); + case SCH_EVENTS: + { + const char *dst_db_name= table->schema_select_lex->db; - if (check_access(thd, SELECT_ACL, dst_db_name, - &thd->col_access, FALSE, FALSE, - is_schema_db(dst_db_name))) - { - return TRUE; - } + DBUG_ASSERT(dst_db_name); - if (!thd->col_access && check_grant_db(thd, dst_db_name)) - { - my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), - thd->security_ctx->priv_user, - thd->security_ctx->priv_host, - dst_db_name); - return TRUE; - } + if (check_access(thd, SELECT_ACL, dst_db_name, + &thd->col_access, FALSE, FALSE, + is_schema_db(dst_db_name))) + return TRUE; - return FALSE; + if (!thd->col_access && check_grant_db(thd, dst_db_name)) + { + my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), + thd->security_ctx->priv_user, + thd->security_ctx->priv_host, + dst_db_name); + return TRUE; } + return FALSE; + } + case SCH_COLUMNS: case SCH_STATISTICS: - { - TABLE_LIST *dst_table= - (TABLE_LIST *) table->schema_select_lex->table_list.first; + { + TABLE_LIST *dst_table; + dst_table= (TABLE_LIST *) table->schema_select_lex->table_list.first; - DBUG_ASSERT(dst_table); + DBUG_ASSERT(dst_table); - if (check_access(thd, SELECT_ACL | EXTRA_ACL, - dst_table->db, - &dst_table->grant.privilege, - FALSE, FALSE, - test(dst_table->schema_table))) - { - return FALSE; - } - - return grant_option && - check_grant(thd, SELECT_ACL, dst_table, 2, UINT_MAX, FALSE); - } + if (check_access(thd, SELECT_ACL | EXTRA_ACL, + dst_table->db, + &dst_table->grant.privilege, + FALSE, FALSE, + test(dst_table->schema_table))) + return FALSE; - 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: + return (check_grant(thd, SELECT_ACL, dst_table, 2, UINT_MAX, FALSE)); + } + default: break; } @@ -5530,8 +4908,6 @@ bool check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, bool no_errors) { - uint found=0; - ulong found_access=0; #ifndef NO_EMBEDDED_ACCESS_CHECKS TABLE_LIST *org_tables= tables; #endif @@ -5573,9 +4949,7 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, } if (tables->derived || - (tables->table && (int)tables->table->s->tmp_table) || - my_tz_check_n_skip_implicit_tables(&tables, - thd->lex->time_zone_tables_used)) + (tables->table && (int)tables->table->s->tmp_table)) continue; thd->security_ctx= sctx; if ((sctx->master_access & want_access) == @@ -5584,26 +4958,17 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, tables->grant.privilege= want_access; else if (tables->db && thd->db && strcmp(tables->db, thd->db) == 0) { - if (found && !grant_option) // db already checked - tables->grant.privilege=found_access; - else - { - if (check_access(thd,want_access,tables->db,&tables->grant.privilege, + if (check_access(thd,want_access,tables->db,&tables->grant.privilege, 0, no_errors, test(tables->schema_table))) - goto deny; // Access denied - found_access=tables->grant.privilege; - found=1; - } + goto deny; // Access denied } else if (check_access(thd,want_access,tables->db,&tables->grant.privilege, 0, no_errors, test(tables->schema_table))) goto deny; } thd->security_ctx= backup_ctx; - if (grant_option) - return check_grant(thd,want_access & ~EXTRA_ACL,org_tables, + return check_grant(thd,want_access & ~EXTRA_ACL,org_tables, test(want_access & EXTRA_ACL), UINT_MAX, no_errors); - return FALSE; deny: thd->security_ctx= backup_ctx; return TRUE; @@ -5633,11 +4998,10 @@ check_routine_access(THD *thd, ulong want_access,char *db, char *name, return TRUE; #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (grant_option) return check_grant_routine(thd, want_access, tables, is_proc, no_errors); -#endif - +#else return FALSE; +#endif } @@ -5699,7 +5063,7 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) if (!check_access(thd, access, table->db, &table->grant.privilege, 0, 1, test(table->schema_table)) && - !grant_option || !check_grant(thd, access, table, 0, 1, 1)) + !check_grant(thd, access, table, 0, 1, 1)) DBUG_RETURN(0); } } @@ -5728,31 +5092,12 @@ bool check_merge_table_access(THD *thd, char *db, } -static bool check_db_used(THD *thd,TABLE_LIST *tables) -{ - char *current_db= NULL; - for (; tables; tables= tables->next_global) - { - if (tables->db == NULL) - { - /* - This code never works and should be removed in 5.1. All tables - that are added to the list of tables should already have its - database field initialized properly (see st_lex::add_table_to_list). - */ - DBUG_ASSERT(0); - if (thd->copy_db_to(¤t_db, 0)) - return TRUE; - tables->db= current_db; - } - } - return FALSE; -} - /**************************************************************************** Check stack size; Send error if there isn't enough stack to continue ****************************************************************************/ +#ifndef EMBEDDED_LIBRARY + #if STACK_DIRECTION < 0 #define used_stack(A,B) (long) (A - B) #else @@ -5763,7 +5108,6 @@ static bool check_db_used(THD *thd,TABLE_LIST *tables) long max_stack_used; #endif -#ifndef EMBEDDED_LIBRARY /* Note: The 'buf' parameter is necessary, even if it is unused here. - fix_fields functions has a "dummy" buffer large enough for the @@ -5771,7 +5115,7 @@ long max_stack_used; - Passing to check_stack_overrun() prevents the compiler from removing it. */ bool check_stack_overrun(THD *thd, long margin, - char *buf __attribute__((unused))) + uchar *buf __attribute__((unused))) { long stack_used; DBUG_ASSERT(thd == current_thd); @@ -5803,19 +5147,19 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) if (!lex->yacc_yyvs) old_info= *yystacksize; *yystacksize= set_zone((*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX); - if (!(lex->yacc_yyvs= (char*) - my_realloc((gptr) lex->yacc_yyvs, + if (!(lex->yacc_yyvs= (uchar*) + my_realloc(lex->yacc_yyvs, *yystacksize*sizeof(**yyvs), MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) || - !(lex->yacc_yyss= (char*) - my_realloc((gptr) lex->yacc_yyss, + !(lex->yacc_yyss= (uchar*) + my_realloc(lex->yacc_yyss, *yystacksize*sizeof(**yyss), MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR)))) return 1; if (old_info) { // Copy old info from stack - memcpy(lex->yacc_yyss, (gptr) *yyss, old_info*sizeof(**yyss)); - memcpy(lex->yacc_yyvs, (gptr) *yyvs, old_info*sizeof(**yyvs)); + memcpy(lex->yacc_yyss, (uchar*) *yyss, old_info*sizeof(**yyss)); + memcpy(lex->yacc_yyvs, (uchar*) *yyvs, old_info*sizeof(**yyvs)); } *yyss=(short*) lex->yacc_yyss; *yyvs=(YYSTYPE*) lex->yacc_yyvs; @@ -5829,6 +5173,7 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) DESCRIPTION This needs to be called before execution of every statement (prepared or conventional). + It is not called by substatements of routines. TODO Make it a method of THD and align its name with the rest of @@ -5839,16 +5184,32 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) void mysql_reset_thd_for_next_command(THD *thd) { DBUG_ENTER("mysql_reset_thd_for_next_command"); + DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */ thd->free_list= 0; thd->select_number= 1; - thd->query_start_used= thd->insert_id_used=0; - thd->last_insert_id_used_bin_log= FALSE; + /* + Those two lines below are theoretically unneeded as + THD::cleanup_after_query() should take care of this already. + */ + thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty(); + thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0; + + thd->query_start_used= 0; thd->is_fatal_error= thd->time_zone_used= 0; thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED); + /* + If in autocommit mode and not in a transaction, reset + OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG to not get warnings + in ha_rollback_trans() about some tables couldn't be rolled back. + */ + if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) + { + thd->options&= ~OPTION_KEEP_LOG; + thd->transaction.all.modified_non_trans_table= FALSE; + } DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx); - thd->tmp_table_used= 0; thd->thread_specific_used= FALSE; if (!thd->in_sub_stmt) { @@ -5862,6 +5223,12 @@ void mysql_reset_thd_for_next_command(THD *thd) thd->rand_used= 0; thd->sent_row_count= thd->examined_row_count= 0; } + /* + Because we come here only for start of top-statements, binlog format is + constant inside a complex statement (using stored functions) etc. + */ + thd->reset_current_stmt_binlog_row_based(); + DBUG_VOID_RETURN; } @@ -6010,6 +5377,7 @@ void mysql_init_multi_delete(LEX *lex) lex->query_tables_last= &lex->query_tables; } + /* When you modify mysql_parse(), you may need to mofify mysql_test_parse_for_slave() in this same file. @@ -6017,11 +5385,12 @@ void mysql_init_multi_delete(LEX *lex) /** Parse a query. - @param thd Current thread - @param inBuf Begining of the query text - @param length Length of the query text - @param [out] semicolon For multi queries, position of the character of - the next query in the query text. + + @param thd Current thread + @param inBuf Begining of the query text + @param length Length of the query text + @param[out] found_semicolon For multi queries, position of the character of + the next query in the query text. */ void mysql_parse(THD *thd, const char *inBuf, uint length, @@ -6058,12 +5427,11 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, sp_cache_flush_obsolete(&thd->sp_func_cache); Lex_input_stream lip(thd, inBuf, length); - thd->m_lip= &lip; - int err= MYSQLparse(thd); + bool err= parse_sql(thd, &lip, NULL); *found_semicolon= lip.found_semicolon; - if (!err && ! thd->is_fatal_error) + if (!err) { #ifndef NO_EMBEDDED_ACCESS_CHECKS if (mqh_used && thd->user_connect && @@ -6086,8 +5454,8 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, PROCESSLIST. Note that we don't need LOCK_thread_count to modify query_length. */ - if (lip.found_semicolon && - (thd->query_length= (ulong)(lip.found_semicolon - thd->query))) + if (*found_semicolon && + (thd->query_length= (ulong)(*found_semicolon - thd->query))) thd->query_length--; /* Actually execute the query */ lex->set_trg_event_type_for_tables(); @@ -6142,12 +5510,10 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) DBUG_ENTER("mysql_test_parse_for_slave"); Lex_input_stream lip(thd, inBuf, length); - thd->m_lip= &lip; lex_start(thd); mysql_reset_thd_for_next_command(thd); - int err= MYSQLparse((void*) thd); - if (!err && ! thd->is_fatal_error && + if (!parse_sql(thd, &lip, NULL) && all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) error= 1; /* Ignore question */ thd->end_statement(); @@ -6163,7 +5529,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) ** Return 0 if ok ******************************************************************************/ -bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, +bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, char *length, char *decimals, uint type_modifier, Item *default_value, Item *on_update_value, @@ -6172,29 +5538,34 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, List<String> *interval_list, CHARSET_INFO *cs, uint uint_geom_type) { - register create_field *new_field; + register Create_field *new_field; LEX *lex= thd->lex; DBUG_ENTER("add_field_to_list"); - if (strlen(field_name) > NAME_LEN) + if (check_string_char_length(field_name, "", NAME_CHAR_LEN, + system_charset_info, 1)) { - my_error(ER_TOO_LONG_IDENT, MYF(0), field_name); /* purecov: inspected */ + my_error(ER_TOO_LONG_IDENT, MYF(0), field_name->str); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */ } if (type_modifier & PRI_KEY_FLAG) { - lex->col_list.push_back(new key_part_spec(field_name,0)); - lex->alter_info.key_list.push_back(new Key(Key::PRIMARY, NullS, - HA_KEY_ALG_UNDEF, 0, - lex->col_list)); + Key *key; + lex->col_list.push_back(new Key_part_spec(field_name->str, 0)); + key= new Key(Key::PRIMARY, NullS, + &default_key_create_info, + 0, lex->col_list); + lex->alter_info.key_list.push_back(key); lex->col_list.empty(); } if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG)) { - lex->col_list.push_back(new key_part_spec(field_name,0)); - lex->alter_info.key_list.push_back(new Key(Key::UNIQUE, NullS, - HA_KEY_ALG_UNDEF, 0, - lex->col_list)); + Key *key; + lex->col_list.push_back(new Key_part_spec(field_name->str, 0)); + key= new Key(Key::UNIQUE, NullS, + &default_key_create_info, 0, + lex->col_list); + lex->alter_info.key_list.push_back(key); lex->col_list.empty(); } @@ -6209,9 +5580,9 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, */ if (default_value->type() == Item::FUNC_ITEM && !(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC && - type == FIELD_TYPE_TIMESTAMP)) + type == MYSQL_TYPE_TIMESTAMP)) { - my_error(ER_INVALID_DEFAULT, MYF(0), field_name); + my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str); DBUG_RETURN(1); } else if (default_value->type() == Item::NULL_ITEM) @@ -6220,24 +5591,24 @@ 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) { - my_error(ER_INVALID_DEFAULT, MYF(0), field_name); + my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str); DBUG_RETURN(1); } } else if (type_modifier & AUTO_INCREMENT_FLAG) { - my_error(ER_INVALID_DEFAULT, MYF(0), field_name); + my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str); DBUG_RETURN(1); } } - if (on_update_value && type != FIELD_TYPE_TIMESTAMP) + if (on_update_value && type != MYSQL_TYPE_TIMESTAMP) { - my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name); + my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name->str); DBUG_RETURN(1); } - if (type == FIELD_TYPE_TIMESTAMP && length) + if (type == MYSQL_TYPE_TIMESTAMP && length) { /* Display widths are no longer supported for TIMSTAMP as of MySQL 4.1. In other words, for declarations such as TIMESTAMP(2), TIMESTAMP(4), @@ -6245,14 +5616,11 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, */ char buf[32]; my_snprintf(buf, sizeof(buf), "TIMESTAMP(%s)", length); - push_warning_printf(thd,MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DEPRECATED_SYNTAX, - ER(ER_WARN_DEPRECATED_SYNTAX), - buf, "TIMESTAMP"); + WARN_DEPRECATED(thd, "5.2", buf, "'TIMESTAMP'"); } - if (!(new_field= new create_field()) || - new_field->init(thd, field_name, type, length, decimals, type_modifier, + if (!(new_field= new Create_field()) || + new_field->init(thd, field_name->str, type, length, decimals, type_modifier, default_value, on_update_value, comment, change, interval_list, cs, uint_geom_type)) DBUG_RETURN(1); @@ -6282,41 +5650,11 @@ add_proc_to_list(THD* thd, Item *item) *item_ptr= item; order->item=item_ptr; order->free_me=0; - thd->lex->proc_list.link_in_list((byte*) order,(byte**) &order->next); + thd->lex->proc_list.link_in_list((uchar*) order,(uchar**) &order->next); return 0; } -/* Fix escaping of _, % and \ in database and table names (for ODBC) */ - -static void remove_escape(char *name) -{ - if (!*name) // For empty DB names - return; - char *to; -#ifdef USE_MB - char *strend=name+(uint) strlen(name); -#endif - for (to=name; *name ; name++) - { -#ifdef USE_MB - int l; - if (use_mb(system_charset_info) && - (l = my_ismbchar(system_charset_info, name, strend))) - { - while (l--) - *to++ = *name++; - name--; - continue; - } -#endif - if (*name == '\\' && name[1]) - name++; // Skip '\\' - *to++= *name; - } - *to=0; -} - /**************************************************************************** ** save order by and tables in own lists ****************************************************************************/ @@ -6334,7 +5672,7 @@ bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc) order->free_me=0; order->used=0; order->counter_used= 0; - list.link_in_list((byte*) order,(byte**) &order->next); + list.link_in_list((uchar*) order,(uchar**) &order->next); DBUG_RETURN(0); } @@ -6364,8 +5702,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, LEX_STRING *alias, ulong table_options, thr_lock_type lock_type, - List<String> *use_index_arg, - List<String> *ignore_index_arg, + List<Index_hint> *index_hints_arg, LEX_STRING *option) { register TABLE_LIST *ptr; @@ -6385,6 +5722,13 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, DBUG_RETURN(0); } + if (table->is_derived_table() == FALSE && table->db.str && + check_db_name(&table->db)) + { + my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str); + DBUG_RETURN(0); + } + if (!alias) /* Alias is case sensitive */ { if (table->sel) @@ -6393,18 +5737,13 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ER(ER_DERIVED_MUST_HAVE_ALIAS), MYF(0)); DBUG_RETURN(0); } - if (!(alias_str=thd->memdup(alias_str,table->table.length+1))) + if (!(alias_str= (char*) thd->memdup(alias_str,table->table.length+1))) DBUG_RETURN(0); } if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST)))) DBUG_RETURN(0); /* purecov: inspected */ if (table->db.str) { - if (table->is_derived_table() == FALSE && check_db_name(table->db.str)) - { - my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str); - DBUG_RETURN(0); - } ptr->db= table->db.str; ptr->db_length= table->db.length; } @@ -6427,7 +5766,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, 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 + (sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0)) { my_error(ER_UNKNOWN_TABLE, MYF(0), ptr->table_name, INFORMATION_SCHEMA_NAME.str); @@ -6438,12 +5777,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, } 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, - sizeof(*use_index_arg)); - if (ignore_index_arg) - ptr->ignore_index=(List<String> *) thd->memdup((gptr) ignore_index_arg, - sizeof(*ignore_index_arg)); + ptr->index_hints= index_hints_arg; ptr->option= option ? option->str : 0; /* check that used name is unique */ if (lock_type != TL_IGNORE) @@ -6490,7 +5824,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, previous table reference to 'ptr'. Here we also add one element to the list 'table_list'. */ - table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local); + table_list.link_in_list((uchar*) ptr, (uchar**) &ptr->next_local); ptr->next_name_resolution_table= NULL; /* Link table in global list (all used tables) */ lex->add_to_query_tables(ptr); @@ -6529,11 +5863,12 @@ bool st_select_lex::init_nested_join(THD *thd) sizeof(NESTED_JOIN)))) DBUG_RETURN(1); nested_join= ptr->nested_join= - ((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST)))); + ((NESTED_JOIN*) ((uchar*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST)))); join_list->push_front(ptr); ptr->embedding= embedding; ptr->join_list= join_list; + ptr->alias= (char*) "(nested_join)"; embedding= ptr; join_list= &nested_join->join_list; join_list->empty(); @@ -6614,10 +5949,11 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd) sizeof(NESTED_JOIN)))) DBUG_RETURN(0); nested_join= ptr->nested_join= - ((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST)))); + ((NESTED_JOIN*) ((uchar*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST)))); ptr->embedding= embedding; ptr->join_list= join_list; + ptr->alias= (char*) "(nest_last_join)"; embedded_list= &nested_join->join_list; embedded_list->empty(); @@ -6792,7 +6128,7 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg) fake_select_lex->context.resolve_in_select_list= TRUE; fake_select_lex->context.select_lex= fake_select_lex; - if (!first_sl->next_select()) + if (!is_union()) { /* This works only for @@ -6989,7 +6325,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, { /* Flush the normal query log, the update log, the binary log, - the slow query log, and the relay log (if it exists). + the slow query log, the relay log (if it exists) and the log + tables. */ /* @@ -6999,8 +6336,6 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, than it would help them) */ tmp_write_to_binlog= 0; - mysql_log.new_file(1); - mysql_slow_log.new_file(1); if( mysql_bin_log.is_open() ) { mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE); @@ -7010,7 +6345,11 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, rotate_relay_log(active_mi); pthread_mutex_unlock(&LOCK_active_mi); #endif - if (ha_flush_logs()) + + /* flush slow and general logs */ + logger.flush_logs(thd); + + if (ha_flush_logs(NULL)) result=1; if (flush_error_log()) result=1; @@ -7109,31 +6448,37 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, } #endif if (options & REFRESH_USER_RESOURCES) - reset_mqh((LEX_USER *) NULL); + reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */ *write_to_binlog= tmp_write_to_binlog; return result; } + /* - kill on thread + kills a thread SYNOPSIS kill_one_thread() thd Thread class id Thread id + only_kill_query Should it kill the query or the connection NOTES This is written such that we have a short lock on LOCK_thread_count */ -void kill_one_thread(THD *thd, ulong id, bool only_kill_query) +uint kill_one_thread(THD *thd, ulong id, bool only_kill_query) { THD *tmp; uint error=ER_NO_SUCH_THREAD; + DBUG_ENTER("kill_one_thread"); + DBUG_PRINT("enter", ("id=%lu only_kill=%d", id, only_kill_query)); VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list I_List_iterator<THD> it(threads); while ((tmp=it++)) { + if (tmp->command == COM_DAEMON) + continue; if (tmp->thread_id == id) { pthread_mutex_lock(&tmp->LOCK_delete); // Lock from delete @@ -7153,8 +6498,25 @@ void kill_one_thread(THD *thd, ulong id, bool only_kill_query) error=ER_KILL_DENIED_ERROR; pthread_mutex_unlock(&tmp->LOCK_delete); } + DBUG_PRINT("exit", ("%d", error)); + DBUG_RETURN(error); +} - if (!error) + +/* + kills a thread and sends response + + SYNOPSIS + sql_kill() + thd Thread class + id Thread id + only_kill_query Should it kill the query or the connection +*/ + +void sql_kill(THD *thd, ulong id, bool only_kill_query) +{ + uint error; + if (!(error= kill_one_thread(thd, id, only_kill_query))) send_ok(thd); else my_error(error, MYF(0), id); @@ -7163,8 +6525,8 @@ void kill_one_thread(THD *thd, ulong id, bool only_kill_query) /* If pointer is not a null pointer, append filename to it */ -static bool append_file_to_dir(THD *thd, const char **filename_ptr, - const char *table_name) +bool append_file_to_dir(THD *thd, const char **filename_ptr, + const char *table_name) { char buff[FN_REFLEN],*ptr, *end; if (!*filename_ptr) @@ -7180,7 +6542,7 @@ static bool append_file_to_dir(THD *thd, const char **filename_ptr, /* Fix is using unix filename format on dos */ strmov(buff,*filename_ptr); end=convert_dirname(buff, *filename_ptr, NullS); - if (!(ptr=thd->alloc((uint) (end-buff)+(uint) strlen(table_name)+1))) + if (!(ptr= (char*) thd->alloc((size_t) (end-buff) + strlen(table_name)+1))) return 1; // End of memory *filename_ptr=ptr; strxmov(ptr,buff,table_name,NullS); @@ -7322,12 +6684,11 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) else if ((check_access(thd, UPDATE_ACL, table->db, &table->grant.privilege, 0, 1, test(table->schema_table)) || - grant_option && check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) && (check_access(thd, SELECT_ACL, table->db, &table->grant.privilege, 0, 0, test(table->schema_table)) || - grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))) + check_grant(thd, SELECT_ACL, table, 0, 1, 0))) DBUG_RETURN(TRUE); table->table_in_first_from_clause= 1; @@ -7335,19 +6696,17 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) /* Is there tables of subqueries? */ - if (&lex->select_lex != lex->all_selects_list || lex->time_zone_tables_used) + if (&lex->select_lex != lex->all_selects_list) { DBUG_PRINT("info",("Checking sub query list")); for (table= tables; table; table= table->next_global) { - if (!my_tz_check_n_skip_implicit_tables(&table, - lex->time_zone_tables_used) && - !table->table_in_first_from_clause) + if (!table->table_in_first_from_clause) { if (check_access(thd, SELECT_ACL, table->db, &table->grant.privilege, 0, 0, test(table->schema_table)) || - grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)) + check_grant(thd, SELECT_ACL, table, 0, 1, 0)) DBUG_RETURN(TRUE); } } @@ -7388,8 +6747,7 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables) /* sql_yacc guarantees that tables and aux_tables are not zero */ DBUG_ASSERT(aux_tables != 0); - if (check_db_used(thd, tables) || check_db_used(thd,aux_tables) || - check_table_access(thd, SELECT_ACL, tables, 0)) + if (check_table_access(thd, SELECT_ACL, tables, 0)) DBUG_RETURN(TRUE); /* @@ -7489,8 +6847,7 @@ bool update_precheck(THD *thd, TABLE_LIST *tables) 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)); + DBUG_RETURN(check_one_table_access(thd, UPDATE_ACL, tables)); } @@ -7552,20 +6909,18 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables) my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0)); DBUG_RETURN(TRUE); } - if (check_db_used(thd, tables)) - DBUG_RETURN(TRUE); DBUG_RETURN(FALSE); } /** - @brief Check privileges for SHOW CREATE TABLE statement. + @brief Check privileges for SHOW CREATE TABLE statement. - @param thd Thread context - @param table Target table + @param thd Thread context + @param table Target table - @retval TRUE Failure - @retval FALSE Success + @retval TRUE Failure + @retval FALSE Success */ static bool check_show_create_table_access(THD *thd, TABLE_LIST *table) @@ -7573,7 +6928,7 @@ static bool check_show_create_table_access(THD *thd, TABLE_LIST *table) return check_access(thd, SELECT_ACL | EXTRA_ACL, table->db, &table->grant.privilege, 0, 0, test(table->schema_table)) || - grant_option && check_grant(thd, SELECT_ACL, table, 2, UINT_MAX, 0); + check_grant(thd, SELECT_ACL, table, 2, UINT_MAX, 0); } @@ -7609,7 +6964,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, (TABLE_LIST *) lex->create_info.merge_list.first)) goto err; - if (grant_option && want_priv != CREATE_TMP_ACL && + if (want_priv != CREATE_TMP_ACL && check_grant(thd, want_priv, create_table, 0, 1, 0)) goto err; @@ -7620,7 +6975,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, #ifdef NOT_NECESSARY_TO_CHECK_CREATE_TABLE_EXIST_WHEN_PREPARING_STATEMENT /* This code throws an ill error for CREATE TABLE t1 SELECT * FROM t1 */ /* - Only do the check for PS, becasue we on execute we have to check that + Only do the check for PS, because 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). */ @@ -7791,26 +7146,118 @@ LEX_USER *get_current_user(THD *thd, LEX_USER *user) /* - Check that length of a string does not exceed some limit. + Check that byte length of a string does not exceed some limit. SYNOPSIS - check_string_length() - str string to be checked - err_msg error message to be displayed if the string is too long - max_length max length + check_string_byte_length() + str string to be checked + err_msg error message to be displayed if the string is too long + max_byte_length max length in bytes RETURN FALSE the passed string is not longer than max_length TRUE the passed string is longer than max_length + + NOTE + The function is not used in existing code but can be useful later? */ -bool check_string_length(LEX_STRING *str, const char *err_msg, - uint max_length) +bool check_string_byte_length(LEX_STRING *str, const char *err_msg, + uint max_byte_length) { - if (str->length <= max_length) + if (str->length <= max_byte_length) return FALSE; - my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_length); + my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_byte_length); + + return TRUE; +} + + +/* + Check that char length of a string does not exceed some limit. + + SYNOPSIS + check_string_char_length() + str string to be checked + err_msg error message to be displayed if the string is too long + max_char_length max length in symbols + cs string charset + + RETURN + FALSE the passed string is not longer than max_char_length + TRUE the passed string is longer than max_char_length +*/ + + +bool check_string_char_length(LEX_STRING *str, const char *err_msg, + uint max_char_length, CHARSET_INFO *cs, + bool no_error) +{ + int well_formed_error; + uint res= cs->cset->well_formed_len(cs, str->str, str->str + str->length, + max_char_length, &well_formed_error); + + if (!well_formed_error && str->length == res) + return FALSE; + if (!no_error) + my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_char_length); return TRUE; } + + +extern int MYSQLparse(void *thd); // from sql_yacc.cc + + +/** + This is a wrapper of MYSQLparse(). All the code should call parse_sql() + instead of MYSQLparse(). + + @param thd Thread context. + @param lip Lexer context. + @param creation_ctx Object creation context. + + @return Error status. + @retval FALSE on success. + @retval TRUE on parsing error. +*/ + +bool parse_sql(THD *thd, + Lex_input_stream *lip, + Object_creation_ctx *creation_ctx) +{ + DBUG_ASSERT(thd->m_lip == NULL); + + /* Backup creation context. */ + + Object_creation_ctx *backup_ctx= NULL; + + if (creation_ctx) + backup_ctx= creation_ctx->set_n_backup(thd); + + /* Set Lex_input_stream. */ + + thd->m_lip= lip; + + /* Parse the query. */ + + bool err_status= MYSQLparse(thd) != 0 || thd->is_fatal_error; + + /* Reset Lex_input_stream. */ + + thd->m_lip= NULL; + + /* Restore creation context. */ + + if (creation_ctx) + creation_ctx->restore_env(thd, backup_ctx); + + /* That's it. */ + + return err_status; +} + +/** + @} (end of group Runtime_Environment) +*/ |