diff options
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r-- | sql/sql_parse.cc | 4781 |
1 files changed, 2158 insertions, 2623 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 79b9b479a0c..9c621004cc3 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB +/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -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,31 +43,43 @@ (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[]={ @@ -141,14 +134,6 @@ static bool xa_trans_rollback(THD *thd) return status; } -#ifndef EMBEDDED_LIBRARY -static bool do_command(THD *thd); -#endif // EMBEDDED_LIBRARY - -#ifdef __WIN__ -extern void win_install_sigabrt_handler(void); -#endif - static void unlock_locked_tables(THD *thd) { if (thd->locked_tables) @@ -160,7 +145,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"); @@ -185,13 +170,14 @@ static bool end_active_trans(THD *thd) thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (ha_commit(thd)) error=1; - thd->options&= ~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)) @@ -209,23 +195,20 @@ static bool begin_trans(THD *thd) error= -1; else { - LEX *lex= thd->lex; - thd->transaction.all.modified_non_trans_table= FALSE; 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); } return error; } #ifdef HAVE_REPLICATION -/* - Returns true if all tables should be ignored +/** + Returns true if all tables should be ignored. */ 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 @@ -242,863 +225,161 @@ 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; - size_t 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, (uint) 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= (uint) 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 ? (uint) 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. -/* - Mark all commands that somehow changes a table - This is used to check number of updates / hour + This is used to check number of updates / hour. 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; -} - -/* - 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); -} + bzero((uchar*) &sql_command_flags, sizeof(sql_command_flags)); + + sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE; + 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 | CF_REEXECUTION_FRAGILE; + 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 | CF_REEXECUTION_FRAGILE; + 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 | + CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_UPDATE_MULTI]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | + CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_INSERT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | + CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_INSERT_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | + CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_DELETE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | + CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_DELETE_MULTI]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | + CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_REPLACE]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | + CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT | + CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SELECT]= CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SET_OPTION]= CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_DO]= CF_REEXECUTION_FRAGILE; + + sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_STATUS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_DATABASES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_TRIGGERS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_EVENTS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_OPEN_TABLES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_PLUGINS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_FIELDS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_KEYS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_VARIABLES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_CHARSETS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + sql_command_flags[SQLCOM_SHOW_COLLATIONS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE; + 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 | CF_REEXECUTION_FRAGILE; + 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_PROFILES]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND; + + sql_command_flags[SQLCOM_SHOW_TABLES]= (CF_STATUS_COMMAND | + CF_SHOW_TABLE_COMMAND | + CF_REEXECUTION_FRAGILE); + sql_command_flags[SQLCOM_SHOW_TABLE_STATUS]= (CF_STATUS_COMMAND | + CF_SHOW_TABLE_COMMAND | + CF_REEXECUTION_FRAGILE); -#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 | CF_REEXECUTION_FRAGILE; + 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; - size_t 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++) : (uint) strlen(passwd); - db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ? - db + passwd_len + 1 : 0; - size_t 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, (uint) 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, (uint) 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) { Vio* save_vio; ulong save_client_capabilities; +#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) + thd->profiling.start_new_query(); + thd->profiling.set_query_source(init_command_var->value, + init_command_var->value_length); +#endif + thd_proc_info(thd, "Execution of init_command"); /* We need to lock init_command_var because @@ -1106,8 +387,6 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var, values of init_command_var can't be changed */ rw_rdlock(var_mutex); - thd->query= init_command_var->value; - thd->query_length= init_command_var->value_length; save_client_capabilities= thd->client_capabilities; thd->client_capabilities|= CLIENT_MULTI_QUERIES; /* @@ -1116,167 +395,23 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var, */ save_vio= thd->net.vio; thd->net.vio= 0; - thd->net.no_send_error= 0; - dispatch_command(COM_QUERY, thd, thd->query, thd->query_length+1); + dispatch_command(COM_QUERY, thd, + init_command_var->value, + init_command_var->value_length); rw_unlock(var_mutex); thd->client_capabilities= save_client_capabilities; thd->net.vio= save_vio; -} - -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__) - win_install_sigabrt_handler(); -#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"); +#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) + thd->profiling.finish_current_query(); #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(thd, 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(thd, 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 (thd->killed || - (net->vio && net->error && net->report_error)) - { - statistic_increment(aborted_threads, &LOCK_status); - } - - 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); - } - -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 + + Used when creating the initial grant tables. */ pthread_handler_t handle_bootstrap(void *arg) @@ -1301,11 +436,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) @@ -1315,6 +445,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 @@ -1326,36 +457,46 @@ 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_end_statement(thd); + bootstrap_error= 1; + break; + } + buff= (char*) thd->net.buff; + fgets(buff + length, thd->net.max_packet - length, file); + length+= (ulong) strlen(buff + length); + /* purecov: end */ + } + if (bootstrap_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)); #if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) + thd->profiling.start_new_query(); thd->profiling.set_query_source(thd->query, length); #endif @@ -1368,16 +509,15 @@ pthread_handler_t handle_bootstrap(void *arg) mysql_parse(thd, thd->query, length, & found_semicolon); close_thread_tables(thd); // Free tables - if (thd->is_fatal_error) - break; + bootstrap_error= thd->is_error(); + net_end_statement(thd); - if (thd->net.report_error) - { - /* The query failed, send error to log and abort bootstrap */ - net_send_error(thd); - thd->fatal_error(); +#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) + thd->profiling.finish_current_query(); +#endif + + if (bootstrap_error) break; - } free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); #ifdef USING_TRANSACTIONS @@ -1386,9 +526,6 @@ pthread_handler_t handle_bootstrap(void *arg) } end: - /* Remember the exit code of bootstrap */ - bootstrap_error= thd->is_fatal_error; - net_end(&thd->net); thd->cleanup(); delete thd; @@ -1405,7 +542,61 @@ end: } - /* This works because items are allocated with sql_alloc() */ +/** + @brief Check access privs for a MERGE table and fix children lock types. + + @param[in] thd thread handle + @param[in] db database name + @param[in,out] table_list list of child tables (merge_list) + lock_type and optionally db set per table + + @return status + @retval 0 OK + @retval != 0 Error + + @detail + This function is used for write access to MERGE tables only + (CREATE TABLE, ALTER TABLE ... UNION=(...)). Set TL_WRITE for + every child. Set 'db' for every child if not present. +*/ +#ifndef NO_EMBEDDED_ACCESS_CHECKS +static bool check_merge_table_access(THD *thd, char *db, + TABLE_LIST *table_list) +{ + int error= 0; + + if (table_list) + { + /* Check that all tables use the current database */ + TABLE_LIST *tlist; + + for (tlist= table_list; tlist; tlist= tlist->next_local) + { + if (!tlist->db || !tlist->db[0]) + tlist->db= db; /* purecov: inspected */ + } + error= check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL, + table_list, UINT_MAX, FALSE); + } + return error; +} +#endif + +/* 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) { @@ -1415,49 +606,53 @@ void cleanup_items(Item *item) DBUG_VOID_RETURN; } -/* - Handle COM_TABLE_DUMP command +/** + Handle COM_TABLE_DUMP command. - SYNOPSIS - mysql_table_dump - thd thread handle - db database name or an empty string. If empty, - the current database of the connection is used - tbl_name name of the table to dump + @param thd thread handle + @param db database name or an empty string. If empty, + the current database of the connection is used + @param tbl_name name of the table to dump - NOTES + @note This function is written to handle one specific command only. - RETURN VALUE + @retval 0 success + @retval 1 error, the error message is set in THD */ 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)) @@ -1478,16 +673,14 @@ err: DBUG_RETURN(error); } -/* - Ends the current transaction and (maybe) begin the next +/** + Ends the current transaction and (maybe) begin the next. - SYNOPSIS - end_trans() - thd Current thread - completion Completion type + @param thd Current thread + @param completion Completion type - RETURN - 0 - OK + @retval + 0 OK */ int end_trans(THD *thd, enum enum_mysql_completiontype completion) @@ -1516,7 +709,7 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) */ thd->server_status&= ~SERVER_STATUS_IN_TRANS; res= ha_commit(thd); - thd->options&= ~OPTION_BEGIN; + thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); thd->transaction.all.modified_non_trans_table= FALSE; break; case COMMIT_RELEASE: @@ -1534,7 +727,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&= ~OPTION_BEGIN; + thd->options&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); thd->transaction.all.modified_non_trans_table= FALSE; if (!res && (completion == ROLLBACK_AND_CHAIN)) res= begin_trans(thd); @@ -1556,17 +749,19 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) #ifndef EMBEDDED_LIBRARY -/* +/** Read one command from connection and execute it (query or simple command). This function is called in loop from thread function. - SYNOPSIS - do_command() - RETURN VALUE + + For profiling to work, it must never be called recursively. + + @retval 0 success + @retval 1 request of thread shutdown (see dispatch_command() description) */ -static bool do_command(THD *thd) +bool do_command(THD *thd) { bool return_value; char *packet= 0; @@ -1589,7 +784,12 @@ static bool do_command(THD *thd) */ my_net_set_read_timeout(net, thd->variables.net_wait_timeout); + /* + XXX: this code is here only to clear possible errors of init_connect. + Consider moving to init_connect() instead. + */ thd->clear_error(); // Clear error message + thd->main_da.reset_diagnostics_area(); net_new_transaction(net); @@ -1605,41 +805,53 @@ static bool do_command(THD *thd) /* Check if we can continue without closing the connection */ + /* The error must be set. */ + DBUG_ASSERT(thd->is_error()); + net_end_statement(thd); + if (net->error != 3) { - return_value= TRUE; // We have to close it. + return_value= TRUE; // We have to close it. goto out; } - net_send_error(thd, net->last_errno, NullS); net->error= 0; return_value= FALSE; goto out; } - else + + packet= (char*) net->read_pos; + /* + 'packet_length' contains length of data, as it was stored in packet + header. In case of malformed header, my_net_read returns zero. + If packet_length is not zero, my_net_read ensures that the returned + number of bytes was actually read from network. + There is also an extra safety measure in my_net_read: + it sets packet[packet_length]= 0, but only for non-zero packets. + */ + if (packet_length == 0) /* safety */ { - packet=(char*) net->read_pos; - command = (enum enum_server_command) (uchar) packet[0]; - if (command >= COM_END) - command= COM_END; // Wrong command - DBUG_PRINT("info",("Command on %s = %d (%s)", - vio_description(net->vio), command, - command_name[command])); + /* Initialize with COM_SLEEP packet */ + packet[0]= (uchar) COM_SLEEP; + packet_length= 1; } + /* Do not rely on my_net_read, extra safety against programming errors. */ + packet[packet_length]= '\0'; /* safety */ + + command= (enum enum_server_command) (uchar) packet[0]; + + if (command >= COM_END) + command= COM_END; // Wrong command + + DBUG_PRINT("info",("Command on %s = %d (%s)", + vio_description(net->vio), command, + command_name[command].str)); /* Restore read timeout value */ my_net_set_read_timeout(net, thd->variables.net_read_timeout); - /* - packet_length contains length of data, as it was stored in packet - header. In case of malformed header, packet_length can be zero. - If packet_length is not zero, my_net_read ensures that this number - of bytes was actually read from network. Additionally my_net_read - sets packet[packet_length]= 0 (thus if packet_length == 0, - command == packet[0] == COM_SLEEP). - In dispatch_command packet[packet_length] points beyond the end of packet. - */ - return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length)); + DBUG_ASSERT(packet_length); + return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1)); out: #if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) @@ -1649,7 +861,6 @@ out: } #endif /* EMBEDDED_LIBRARY */ - /** @brief Determine if an attempt to update a non-temporary table while the read-only option was enabled has been made. @@ -1681,7 +892,7 @@ static my_bool deny_updates_if_read_only_option(THD *thd, if (user_is_super) DBUG_RETURN(FALSE); - if (!uc_update_queries[lex->sql_command]) + if (!(sql_command_flags[lex->sql_command] & CF_CHANGES_DATA)) DBUG_RETURN(FALSE); /* Multi update is an exception and is dealt with later. */ @@ -1718,35 +929,34 @@ static my_bool deny_updates_if_read_only_option(THD *thd, DBUG_RETURN(FALSE); } -/* - Perform one connection-level (COM_XXXX) command. +/** + Perform one connection-level (COM_XXXX) command. - SYNOPSIS - dispatch_command() - thd connection handle - command type of command to perform - packet data for the command, packet is always null-terminated - packet_length length of packet + 1 (to show that data is - null-terminated) except for COM_SLEEP, where it - can be zero. - RETURN VALUE + @param command type of command to perform + @param thd connection handle + @param packet data for the command, packet is always null-terminated + @param packet_length length of packet + 1 (to show that data is + null-terminated) except for COM_SLEEP, where it + can be zero. + + @todo + set thd->lex->sql_command to SQLCOM_END here. + @todo + The following has to be changed to an 8 byte integer + + @retval 0 ok + @retval 1 request of thread shutdown, i. e. if command is COM_QUIT/COM_SHUTDOWN */ - bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length) { NET *net= &thd->net; bool error= 0; DBUG_ENTER("dispatch_command"); - - if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA) - { - thd->killed= THD::NOT_KILLED; - thd->mysys_var->abort= 0; - } + DBUG_PRINT("info",("packet: '%*.s'; command: %d", packet_length, packet, command)); thd->command=command; /* @@ -1758,7 +968,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->set_time(); VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id= global_query_id; - + switch( command ) { /* Ignore these statements. */ case COM_STATISTICS: @@ -1775,25 +985,27 @@ bool dispatch_command(enum enum_server_command command, THD *thd, statistic_increment(thd->status_var.questions, &LOCK_status); next_query_id(); } - + thread_running++; /* TODO: set thd->lex->sql_command to SQLCOM_END here */ VOID(pthread_mutex_unlock(&LOCK_thread_count)); - thd->server_status&= - ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED); + /** + Clear the set of flags that are expected to be cleared at the + beginning of each command. + */ + thd->server_status&= ~SERVER_STATUS_CLEAR_SET; switch (command) { 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, (uint) strlen(packet), thd->charset()); + packet, packet_length, thd->charset()); if (!mysql_change_db(thd, &tmp, FALSE)) { - mysql_log.write(thd,command,"%s",thd->db); - send_ok(thd); + general_log_write(thd, command, thd->db, thd->db_length); + my_ok(thd); } break; } @@ -1801,47 +1013,54 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_REGISTER_SLAVE: { if (!register_slave(thd, (uchar*)packet, packet_length)) - send_ok(thd); + my_ok(thd); break; } #endif case COM_TABLE_DUMP: { - char *db, *tbl_name; + char *tbl_name; + LEX_STRING db; + /* Safe because there is always a trailing \0 at the end of the packet */ uint db_len= *(uchar*) packet; - if (db_len >= packet_length || db_len > NAME_LEN) + if (db_len + 1 > packet_length || db_len > NAME_LEN) { my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); break; } + /* Safe because there is always a trailing \0 at the end of the packet */ uint tbl_len= *(uchar*) (packet + db_len + 1); - if (db_len+tbl_len+2 > packet_length || tbl_len > NAME_LEN) + if (db_len + tbl_len + 2 > packet_length || tbl_len > NAME_LEN) { my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); 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); + if (mysql_table_dump(thd, &db, tbl_name) == 0) + thd->main_da.disable_status(); break; } case COM_CHANGE_USER: { + status_var_increment(thd->status_var.com_other); + char *user= (char*) packet, *packet_end= packet + packet_length; + /* Safe because there is always a trailing \0 at the end of the packet */ + 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 @@ -1850,31 +1069,61 @@ 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; - size_t passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? - (uchar)(*passwd++) : strlen(passwd); + char *save_db; + /* + If there is no password supplied, the packet must contain '\0', + in any type of handshake (4.1 or pre-4.1). + */ + if (passwd >= packet_end) + { + my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); + break; + } + 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 */ - if ((uint) ((uchar*) db - net->read_pos) > packet_length) + /* + Database name is always NUL-terminated, so in case of empty database + the packet must contain at least the trailing '\0'. + */ + if (db >= packet_end) { my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); break; } -#endif + db_length= strlen(db); + + char *ptr= db + db_length + 1; + uint cs_number= 0; + + if (ptr < packet_end) + { + if (ptr + 2 > packet_end) + { + my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); + break; + } + + cs_number= uint2korr(ptr); + } + /* Convert database name to utf8 */ - uint dummy_errors; db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1, - system_charset_info, db, (uint) 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)))) { @@ -1885,13 +1134,11 @@ 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, (uint) passwd_len, db, FALSE); + thd->security_ctx->priv_user= thd->security_ctx->user; + 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)); x_free(thd->security_ctx->user); *thd->security_ctx= save_security_ctx; thd->user_connect= save_user_connect; @@ -1905,19 +1152,25 @@ 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(save_db); + x_free(save_security_ctx.user); + + if (cs_number) + { + thd_init_client_charset(thd, cs_number); + thd->update_charset(); + } } break; } case COM_STMT_EXECUTE: { - mysql_stmt_execute(thd, packet, packet_length); + mysqld_stmt_execute(thd, packet, packet_length); break; } case COM_STMT_FETCH: { - mysql_stmt_fetch(thd, packet, packet_length); + mysqld_stmt_fetch(thd, packet, packet_length); break; } case COM_STMT_SEND_LONG_DATA: @@ -1927,17 +1180,17 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } case COM_STMT_PREPARE: { - mysql_stmt_prepare(thd, packet, packet_length); + mysqld_stmt_prepare(thd, packet, packet_length); break; } case COM_STMT_CLOSE: { - mysql_stmt_close(thd, packet); + mysqld_stmt_close(thd, packet); break; } case COM_STMT_RESET: { - mysql_stmt_reset(thd, packet); + mysqld_stmt_reset(thd, packet); break; } case COM_QUERY: @@ -1946,10 +1199,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; // fatal error is set char *packet_end= thd->query + thd->query_length; /* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */ - const char *format= "%.*b"; - const char* found_semicolon= NULL; + const char* end_of_stmt= NULL; - mysql_log.write(thd,command, format, thd->query_length, thd->query); + general_log_write(thd, command, thd->query, thd->query_length); DBUG_PRINT("query",("%-.4096s",thd->query)); #if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) thd->profiling.set_query_source(thd->query, thd->query_length); @@ -1958,48 +1210,47 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); - mysql_parse(thd, thd->query, thd->query_length, & found_semicolon); + mysql_parse(thd, thd->query, thd->query_length, &end_of_stmt); - while (!thd->killed && found_semicolon && !thd->net.report_error) + while (!thd->killed && (end_of_stmt != NULL) && ! thd->is_error()) { - char *next_packet= (char*) found_semicolon; - net->no_send_error= 0; + char *beginning_of_next_stmt= (char*) end_of_stmt; + + net_end_statement(thd); + query_cache_end_of_result(thd); /* Multiple queries exits, execute them individually */ - if (thd->lock || thd->open_tables || thd->derived_tables || - thd->prelocked_mode) - close_thread_tables(thd); - ulong length= (ulong)(packet_end - next_packet); + close_thread_tables(thd); + ulong length= (ulong)(packet_end - beginning_of_next_stmt); log_slow_statement(thd); /* Remove garbage at start of query */ - while (my_isspace(thd->charset(), *next_packet) && length > 0) + while (length > 0 && my_isspace(thd->charset(), *beginning_of_next_stmt)) { - next_packet++; + beginning_of_next_stmt++; length--; } #if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) thd->profiling.finish_current_query(); thd->profiling.start_new_query("continuing"); - thd->profiling.set_query_source(next_packet, length); + thd->profiling.set_query_source(beginning_of_next_stmt, length); #endif VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_length= length; - thd->query= next_packet; + thd->query= beginning_of_next_stmt; /* Count each statement from the client. */ statistic_increment(thd->status_var.questions, &LOCK_status); - thd->query_id= next_query_id(); thd->set_time(); /* Reset the query start time. */ /* TODO: set thd->lex->sql_command to SQLCOM_END here */ VOID(pthread_mutex_unlock(&LOCK_thread_count)); - mysql_parse(thd, next_packet, length, & found_semicolon); + mysql_parse(thd, beginning_of_next_stmt, length, &end_of_stmt); } if (!(specialflag & SPECIAL_NO_PRIOR)) @@ -2014,7 +1265,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; #else { - char *fields, *pend; + char *fields, *packet_end= packet + packet_length, *arg_end; /* Locked closure of all tables */ TABLE_LIST table_list; LEX_STRING conv_name; @@ -2022,16 +1273,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)) @@ -2041,19 +1294,17 @@ bool dispatch_command(enum enum_server_command command, THD *thd, table_list.schema_table= schema_table; } - thd->query_length= (uint) 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); @@ -2062,8 +1313,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 */ @@ -2076,44 +1327,47 @@ 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 + thd->main_da.disable_status(); // Don't send anything back 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, 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, 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()) { @@ -2121,10 +1375,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_write(thd, command, db.str, db.length); + mysql_rm_db(thd, db.str, 0, 0); break; } +#endif #ifndef EMBEDDED_LIBRARY case COM_BINLOG_DUMP: { @@ -2132,7 +1387,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; @@ -2145,7 +1400,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); @@ -2157,31 +1412,29 @@ 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); + my_ok(thd); break; } #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 */ /* If the client is < 4.1.3, it is going to send us no argument; then - packet_length is 1, packet[0] is the end 0 of the packet. Note that + packet_length is 0, packet[0] is the end 0 of the packet. Note that SHUTDOWN_DEFAULT is 0. If client is >= 4.1.3, the shutdown level is in packet[0]. */ 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) @@ -2190,15 +1443,8 @@ 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); - send_eof(thd); -#ifdef __WIN__ - sleep(1); // must wait after eof() -#endif -#ifndef OS2 - send_eof(thd); // This is for 'quit request' -#endif - close_connection(thd, 0, 1); + general_log_print(thd, command, NullS); + my_eof(thd); close_thread_tables(thd); // Free before kill kill_mysql(); error=TRUE; @@ -2207,75 +1453,88 @@ 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); -#ifndef EMBEDDED_LIBRARY - char buff[200]; -#else - char *buff= thd->net.last_error; -#endif - STATUS_VAR current_global_status_var; + ulong uptime; + uint length; + ulonglong queries_per_second1000; + char buff[250]; + uint buff_len= sizeof(buff); + + 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 EMBEDDED_LIBRARY + /* Store the buffer in permanent memory */ + my_ok(thd, 0, 0, buff); +#endif #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(my_net_write(net, (uchar*) buff, length)); VOID(net_flush(net)); + thd->main_da.disable_status(); #endif break; } case COM_PING: - statistic_increment(thd->status_var.com_other, &LOCK_status); - send_ok(thd); // Tell client we are alive + status_var_increment(thd->status_var.com_other); + my_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) { case (int) MYSQL_OPTION_MULTI_STATEMENTS_ON: thd->client_capabilities|= CLIENT_MULTI_STATEMENTS; - send_eof(thd); + my_eof(thd); break; case (int) MYSQL_OPTION_MULTI_STATEMENTS_OFF: thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS; - send_eof(thd); + my_eof(thd); break; default: my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); @@ -2284,12 +1543,12 @@ 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); - send_eof(thd); + general_log_print(thd, command, NullS); + my_eof(thd); break; case COM_SLEEP: case COM_CONNECT: // Impossible here @@ -2300,29 +1559,33 @@ bool dispatch_command(enum enum_server_command command, THD *thd, my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); break; } - if (thd->lock || thd->open_tables || thd->derived_tables || - thd->prelocked_mode) + + /* If commit fails, we should be able to reset the OK status. */ + thd->main_da.can_overwrite_status= TRUE; + ha_autocommit_or_rollback(thd, thd->is_error()); + thd->main_da.can_overwrite_status= FALSE; + + thd->transaction.stmt.reset(); + + + /* report error issued during command execution */ + if (thd->killed_errno()) { - thd_proc_info(thd, "closing tables"); - close_thread_tables(thd); /* Free tables */ + if (! thd->main_da.is_set()) + thd->send_kill_message(); + } + if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA) + { + thd->killed= THD::NOT_KILLED; + thd->mysys_var->abort= 0; } - /* - assume handlers auto-commit (if some doesn't - transaction handling - in MySQL should be redesigned to support it; it's a big change, - and it's not worth it - better to commit explicitly only writing - transactions, read-only ones should better take care of themselves. - saves some work in 2pc too) - see also sql_base.cc - close_thread_tables() - */ - bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt)); - if (!thd->active_transaction()) - thd->transaction.xid_state.xid.null(); - /* report error issued during command execution */ - if (thd->killed_errno() && !thd->net.report_error) - thd->send_kill_message(); - if (thd->net.report_error) - net_send_error(thd); + net_end_statement(thd); + query_cache_end_of_result(thd); + + thd->proc_info= "closing tables"; + /* Free tables */ + close_thread_tables(thd); log_slow_statement(thd); @@ -2342,7 +1605,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, @@ -2350,10 +1613,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 @@ -2361,49 +1621,48 @@ void log_slow_statement(THD *thd) */ if (thd->enable_slow_log && !thd->user_time) { + ulonglong end_utime_of_query= thd->current_utime(); thd_proc_info(thd, "logging slow query"); - if ((thd->start_time > thd->time_after_lock && - (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)) + 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_proc_info(thd, "logging slow query"); 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; } -/* +/** Create a TABLE_LIST object for an INFORMATION_SCHEMA table. - SYNOPSIS - prepare_schema_table() - thd thread handle - lex current lex - table_ident table alias if it's used - schema_table_idx the type of the INFORMATION_SCHEMA table to be - created - - DESCRIPTION This function is used in the parser to convert a SHOW or DESCRIBE table_name command to a SELECT from INFORMATION_SCHEMA. It prepares a SELECT_LEX and a TABLE_LIST object to represent the given command as a SELECT parse tree. - NOTES + @param thd thread handle + @param lex current lex + @param table_ident table alias if it's used + @param schema_table_idx the type of the INFORMATION_SCHEMA table to be + created + + @note Due to the way this function works with memory and LEX it cannot be used outside the parser (parse tree transformations outside the parser break PS and SP). - RETURN VALUE + @retval 0 success + @retval 1 out of memory or SHOW commands are not allowed in this version of the server. */ @@ -2411,8 +1670,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: @@ -2428,57 +1687,52 @@ 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_PROFILES: /* @@ -2494,6 +1748,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, 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: @@ -2514,31 +1769,28 @@ 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); } -/* - Read query from packet and store in thd->query - Used in COM_QUERY and COM_STMT_PREPARE +/** + Read query from packet and store in thd->query. + Used in COM_QUERY and COM_STMT_PREPARE. - DESCRIPTION Sets the following THD variables: - query - query_length + - query + - query_length - RETURN VALUES + @retval FALSE ok + @retval TRUE error; In this case thd->fatal_error is set */ bool alloc_query(THD *thd, const char *packet, uint packet_length) { - packet_length--; // Remove end null /* Remove garbage at start and end of query */ - while (my_isspace(thd->charset(),packet[0]) && packet_length > 0) + while (packet_length > 0 && my_isspace(thd->charset(), packet[0])) { packet++; packet_length--; @@ -2552,7 +1804,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))) @@ -2585,14 +1837,93 @@ static void reset_one_shot_variables(THD *thd) } -/* - Execute command saved in thd and lex->sql_command +static +bool sp_process_definer(THD *thd) +{ + DBUG_ENTER("sp_process_definer"); - SYNOPSIS - mysql_execute_command() - thd Thread handle + 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); + } + } - IMPLEMENTATION + /* 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. Before every operation that can request a write lock for a table wait if a global read lock exists. However do not wait if this @@ -2604,8 +1935,20 @@ static void reset_one_shot_variables(THD *thd) global read lock when it succeeds. This needs to be released by start_waiting_global_read_lock() after the operation. - RETURN + @param thd Thread handle + + @todo + - Invalidate the table in the query cache if something changed + after unlocking when changes become visible. + TODO: this is workaround. right way will be move invalidating in + the unlock procedure. + - TODO: use check_change_password() + - JOIN is not supported yet. TODO + - SUSPEND and FOR MIGRATE are not supported yet. TODO + + @retval FALSE OK + @retval TRUE Error */ @@ -2630,21 +1973,9 @@ mysql_execute_command(THD *thd) #endif /* 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 => @@ -2676,9 +2007,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 @@ -2806,84 +2135,78 @@ 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_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; + case SQLCOM_SHOW_EVENTS: +#ifndef HAVE_EVENT_SCHEDULER + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server"); + break; +#endif + case SQLCOM_SHOW_STATUS_PROC: + case SQLCOM_SHOW_STATUS_FUNC: + if (!(res= check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE))) + 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; + if (!(res= check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE))) + 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_SHOW_PROFILE: + case SQLCOM_SELECT: + 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, UINT_MAX, FALSE); } else res= check_access(thd, - lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, - any_db, 0, 0, 0, 0); + lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, + any_db, 0, 0, 0, 0); + if (res) - goto error; + break; if (!thd->locked_tables && lex->protect_against_global_read_lock && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) - goto error; + break; - 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; - } - } + res= execute_sqlcom_select(thd, all_tables); break; - } case SQLCOM_PREPARE: { mysql_sql_stmt_prepare(thd); @@ -2900,7 +2223,7 @@ mysql_execute_command(THD *thd) break; } case SQLCOM_DO: - if (check_table_access(thd, SELECT_ACL, all_tables, 0) || + if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) || open_and_lock_tables(thd, all_tables)) goto error; @@ -2908,7 +2231,7 @@ mysql_execute_command(THD *thd) break; case SQLCOM_EMPTY_QUERY: - send_ok(thd); + my_ok(thd); break; case SQLCOM_HELP: @@ -3010,34 +2333,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, UINT_MAX, FALSE) || 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, UINT_MAX, FALSE) || 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; @@ -3047,8 +2367,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; @@ -3077,9 +2396,9 @@ mysql_execute_command(THD *thd) } else { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, - "the master info structure does not exist"); - send_ok(thd); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + WARN_NO_MASTER_INFO, ER(WARN_NO_MASTER_INFO)); + my_ok(thd); } pthread_mutex_unlock(&LOCK_active_mi); break; @@ -3098,33 +2417,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); + if (check_global_access(thd, PROCESS_ACL)) + 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)) + if (check_global_access(thd, PROCESS_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: { @@ -3135,17 +2444,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. @@ -3154,7 +2456,7 @@ mysql_execute_command(THD *thd) if (!fetch_master_table(thd, first_table->db, first_table->table_name, active_mi, 0, 0)) { - send_ok(thd); + my_ok(thd); } pthread_mutex_unlock(&LOCK_active_mi); break; @@ -3172,11 +2474,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 @@ -3190,11 +2487,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; } @@ -3202,31 +2504,10 @@ 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 - if (create_info.data_file_name) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, - "DATA DIRECTORY option ignored"); - if (create_info.index_file_name) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, - "INDEX DIRECTORY option ignored"); - create_info.data_file_name= create_info.index_file_name= NULL; -#else - - if (test_if_data_home_dir(lex->create_info.data_file_name)) - { - my_error(ER_WRONG_ARGUMENTS,MYF(0),"DATA DIRECTORY"); - res= -1; - break; - } - if (test_if_data_home_dir(lex->create_info.index_file_name)) - { - my_error(ER_WRONG_ARGUMENTS,MYF(0),"INDEX DIRECTORY"); - res= -1; - break; - } - +#ifdef HAVE_READLINK /* Fix names if symlinked tables */ if (append_file_to_dir(thd, &create_info.data_file_name, create_table->table_name) || @@ -3266,18 +2547,32 @@ 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 *result; + /* If: a) we inside an SP and there was NAME_CONST substitution, - b) binlogging is on, + b) binlogging is on (STMT mode), c) we log the SP as separate statements raise a warning, as it may cause problems (see 'NAME_CONST issues' in 'Binary Logging of Stored Programs') */ if (thd->query_name_consts && mysql_bin_log.is_open() && + thd->variables.binlog_format == BINLOG_FORMAT_STMT && !mysql_bin_log.is_query_in_union(thd, thd->query_id)) { List_iterator_fast<Item> it(select_lex->item_list); @@ -3304,12 +2599,23 @@ mysql_execute_command(THD *thd) "section of the manual."); } - select_result *sel_result; - select_lex->options|= SELECT_NO_UNLOCK; unit->set_limit(select_lex); - if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) + /* + Disable non-empty MERGE tables with CREATE...SELECT. Too + complicated. See Bug #26379. Empty MERGE tables are read-only + and don't allow CREATE...SELECT anyway. + */ + if (create_info.used_fields & HA_CREATE_USED_UNION) + { + my_error(ER_WRONG_OBJECT, MYF(0), create_table->db, + create_table->table_name, "BASE TABLE"); + res= 1; + goto end_with_restore_list; + } + + if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE)) { lex->link_first_table_back(create_table, link_to_local); create_table->create= TRUE; @@ -3349,33 +2655,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 @@ -3385,7 +2696,7 @@ mysql_execute_command(THD *thd) &alter_info, 0, 0); } if (!res) - send_ok(thd); + my_ok(thd); } /* put tables back for PS rexecuting */ @@ -3398,25 +2709,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. - - 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. + TABLE with proper arguments. - 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; @@ -3428,7 +2736,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; @@ -3477,6 +2785,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 @@ -3488,14 +2797,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, @@ -3504,59 +2815,55 @@ 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, - "DATA DIRECTORY option ignored"); + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED), + "DATA DIRECTORY"); if (create_info.index_file_name) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, - "INDEX DIRECTORY option ignored"); + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED), + "INDEX DIRECTORY"); create_info.data_file_name= create_info.index_file_name= NULL; /* 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, @@ -3565,24 +2872,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; } @@ -3612,9 +2916,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; @@ -3623,8 +2925,8 @@ 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, + UINT_MAX, FALSE)) goto error; /* purecov: inspected */ res = mysql_checksum_table(thd, first_table, &lex->check_opt); break; @@ -3632,60 +2934,52 @@ 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, + UINT_MAX, FALSE)) 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, THD::NOT_KILLED); - 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, + UINT_MAX, FALSE)) 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, + UINT_MAX, FALSE)) 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, THD::NOT_KILLED); - 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; } @@ -3693,8 +2987,8 @@ 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, + UINT_MAX, FALSE)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ? @@ -3703,16 +2997,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, THD::NOT_KILLED); - 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; } @@ -3803,6 +3093,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); @@ -3827,7 +3147,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; } @@ -3859,7 +3180,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); @@ -3884,15 +3205,13 @@ end_with_restore_list: /* INSERT ... SELECT should invalidate only the very first table */ TABLE_LIST *save_table= first_table->next_local; first_table->next_local= 0; - mysql_unlock_tables(thd, thd->lock); query_cache_invalidate3(thd, first_table, 1); first_table->next_local= save_table; - thd->lock=0; } delete sel_result; } /* revert changes for SP */ - select_lex->table_list.first= (byte*) first_table; + select_lex->table_list.first= (uchar*) first_table; } /* @@ -3902,7 +3221,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; } @@ -3913,7 +3233,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 @@ -3994,13 +3314,9 @@ end_with_restore_list: SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE, del_result, unit, select_lex); - res|= thd->net.report_error; - if (unlikely(res)) - { - /* If we had a another error reported earlier then this will be ignored */ - del_result->send_error(ER_UNKNOWN_ERROR, "Execution of the query failed"); + res|= thd->is_error(); + if (res) del_result->abort(); - } delete del_result; } else @@ -4012,7 +3328,7 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); if (!lex->drop_temporary) { - if (check_table_access(thd, DROP_ACL, all_tables, 0)) + if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ if (end_active_trans(thd)) goto error; @@ -4031,7 +3347,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, @@ -4048,8 +3364,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); @@ -4057,25 +3376,25 @@ 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 case SQLCOM_CHANGE_DB: { - LEX_STRING db_str= { (char *) select_lex->db, (uint) strlen(select_lex->db) }; + LEX_STRING db_str= { (char *) select_lex->db, strlen(select_lex->db) }; if (!mysql_change_db(thd, &db_str, FALSE)) - send_ok(thd); + my_ok(thd); break; } @@ -4113,7 +3432,11 @@ end_with_restore_list: case SQLCOM_SET_OPTION: { List<set_var_base> *lex_var_list= &lex->var_list; - if ((check_table_access(thd, SELECT_ACL, all_tables, 0) || + + if (lex->autocommit && end_active_trans(thd)) + goto error; + + if ((check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) || open_and_lock_tables(thd, all_tables))) goto error; if (lex->one_shot_set && not_all_support_one_shot(lex_var_list)) @@ -4128,8 +3451,20 @@ end_with_restore_list: about the ONE_SHOT property of that SET. So we use a |= instead of = . */ thd->one_shot_set|= lex->one_shot_set; - send_ok(thd); + my_ok(thd); + } + else + { + /* + We encountered some sort of error, but no message was sent. + Send something semi-generic here since we don't know which + assignment in the list caused the error. + */ + if (!thd->is_error()) + my_error(ER_WRONG_ARGUMENTS,MYF(0),"SET"); + goto error; } + break; } @@ -4148,16 +3483,15 @@ end_with_restore_list: } if (thd->global_read_lock) unlock_global_read_lock(thd); - send_ok(thd); + my_ok(thd); break; case SQLCOM_LOCK_TABLES: unlock_locked_tables(thd); /* 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)) + if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, + UINT_MAX, FALSE)) goto error; if (lex->protect_against_global_read_lock && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) @@ -4173,7 +3507,7 @@ end_with_restore_list: #endif /*HAVE_QUERY_CACHE*/ thd->locked_tables=thd->lock; thd->lock=0; - send_ok(thd); + my_ok(thd); } else { @@ -4182,6 +3516,7 @@ end_with_restore_list: can free its locks if LOCK TABLES locked some tables before finding that it can't lock a table in its list */ + ha_autocommit_or_rollback(thd, 1); end_active_trans(thd); thd->options&= ~(OPTION_TABLE_LOCK); } @@ -4201,9 +3536,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; } /* @@ -4214,19 +3550,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: @@ -4236,9 +3572,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; } /* @@ -4250,14 +3586,15 @@ 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,0,1,0,is_schema_db(lex->name))) + 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()) { @@ -4265,16 +3602,59 @@ end_with_restore_list: ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); goto error; } - res= mysql_rm_db(thd, lex->name, lex->drop_if_exists, 0); + res= mysql_rm_db(thd, lex->name.str, lex->drop_if_exists, 0); + break; + } + case SQLCOM_ALTER_DB_UPGRADE: + { + LEX_STRING *db= & lex->name; + if (end_active_trans(thd)) + { + res= 1; + break; + } +#ifdef HAVE_REPLICATION + if (thd->slave_thread && + (!rpl_filter->db_ok(db->str) || + !rpl_filter->db_ok_with_wild_table(db->str))) + { + res= 1; + my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); + break; + } +#endif + if (check_db_name(db)) + { + my_error(ER_WRONG_DB_NAME, MYF(0), db->str); + break; + } + if (check_access(thd, ALTER_ACL, db->str, 0, 1, 0, is_schema_db(db->str)) || + check_access(thd, DROP_ACL, db->str, 0, 1, 0, is_schema_db(db->str)) || + check_access(thd, CREATE_ACL, db->str, 0, 1, 0, is_schema_db(db->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_upgrade_db(thd, db); + if (!res) + my_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; } /* @@ -4286,14 +3666,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()) { @@ -4301,30 +3681,88 @@ 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: + #ifdef HAVE_EVENT_SCHEDULER + 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) + my_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))) + my_ok(thd); + break; +#else + my_error(ER_NOT_SUPPORTED_YET,MYF(0),"embedded server"); + break; +#endif case SQLCOM_CREATE_FUNCTION: // UDF function { if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0)) break; #ifdef HAVE_DLOPEN if (!(res = mysql_create_function(thd, &lex->udf))) - send_ok(thd); + my_ok(thd); #else my_error(ER_CANT_OPEN_LIBRARY, MYF(0), lex->udf.dl, 0, "feature disabled"); res= TRUE; @@ -4341,7 +3779,7 @@ end_with_restore_list: goto error; /* Conditionally writes to binlog */ if (!(res= mysql_create_user(thd, lex->users_list))) - send_ok(thd); + my_ok(thd); break; } case SQLCOM_DROP_USER: @@ -4353,7 +3791,7 @@ end_with_restore_list: goto error; /* Conditionally writes to binlog */ if (!(res= mysql_drop_user(thd, lex->users_list))) - send_ok(thd); + my_ok(thd); break; } case SQLCOM_RENAME_USER: @@ -4365,22 +3803,27 @@ end_with_restore_list: goto error; /* Conditionally writes to binlog */ if (!(res= mysql_rename_user(thd, lex->users_list))) - send_ok(thd); + my_ok(thd); break; } 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; /* Conditionally writes to binlog */ if (!(res = mysql_revoke_all(thd, lex->users_list))) - send_ok(thd); + my_ok(thd); break; } 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, @@ -4430,22 +3873,21 @@ 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 */ res= mysql_routine_grant(thd, all_tables, lex->type == TYPE_ENUM_PROCEDURE, lex->users_list, grants, - lex->sql_command == SQLCOM_REVOKE, 0); + lex->sql_command == SQLCOM_REVOKE, TRUE); + if (!res) + my_ok(thd); } 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, @@ -4475,7 +3917,7 @@ end_with_restore_list: { if (!(user= get_current_user(thd, tmp_user))) goto error; - reset_mqh(user); + reset_mqh(user, 0); } } } @@ -4505,17 +3947,14 @@ 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, THD::NOT_KILLED); - mysql_bin_log.write(&qinfo); - } + write_bin_log(thd, FALSE, thd->query, thd->query_length); } - send_ok(thd); + my_ok(thd); } break; @@ -4524,13 +3963,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 @@ -4550,15 +3996,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, UINT_MAX, FALSE)) 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: @@ -4568,8 +4011,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, @@ -4585,19 +4026,24 @@ end_with_restore_list: } if (begin_trans(thd)) goto error; - send_ok(thd); + if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) + { + if (ha_start_consistent_snapshot(thd)) + goto error; + } + my_ok(thd); break; case SQLCOM_COMMIT: if (end_trans(thd, lex->tx_release ? COMMIT_RELEASE : lex->tx_chain ? COMMIT_AND_CHAIN : COMMIT)) goto error; - send_ok(thd); + my_ok(thd); break; case SQLCOM_ROLLBACK: if (end_trans(thd, lex->tx_release ? ROLLBACK_RELEASE : lex->tx_chain ? ROLLBACK_AND_CHAIN : ROLLBACK)) goto error; - send_ok(thd); + my_ok(thd); break; case SQLCOM_RELEASE_SAVEPOINT: { @@ -4614,7 +4060,7 @@ end_with_restore_list: if (ha_release_savepoint(thd, sv)) res= TRUE; // cannot happen else - send_ok(thd); + my_ok(thd); thd->transaction.savepoints=sv->prev; } else @@ -4637,12 +4083,13 @@ 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, ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)); - send_ok(thd); + my_ok(thd); } thd->transaction.savepoints=sv; } @@ -4653,7 +4100,7 @@ end_with_restore_list: case SQLCOM_SAVEPOINT: if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) || thd->in_sub_stmt) || !opt_using_transactions) - send_ok(thd); + my_ok(thd); else { SAVEPOINT **sv, *newsv; @@ -4690,7 +4137,7 @@ end_with_restore_list: { newsv->prev=thd->transaction.savepoints; thd->transaction.savepoints=newsv; - send_ok(thd); + my_ok(thd); } } break; @@ -4707,7 +4154,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; @@ -4745,89 +4192,37 @@ 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 (sp_process_definer(thd)) + goto create_sp_error; - if (thd->slave_thread) - lex->sphead->m_chistics->suid= SP_IS_NOT_SUID; - } + res= (sp_result= lex->sphead->create(thd)); + switch (sp_result) { + case SP_OK: { +#ifndef NO_EMBEDDED_ACCESS_CHECKS + /* only add privileges if really neccessary */ - /* - 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). - */ + Security_context security_context; + bool restore_backup_context= false; + Security_context *backup= NULL; + LEX_USER *definer= thd->lex->definer; + /* + Check if the definer exists on slave, + then use definer privilege to insert routine privileges to mysql.procs_priv. - 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)) + For current user of SQL thread has GLOBAL_ACL privilege, + which doesn't any check routine privileges, + so no routine privilege record will insert into mysql.procs_priv. + */ + if (thd->slave_thread && is_acl_user(definer->host.str, definer->user.str)) { - my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); - goto create_sp_error; + security_context.change_security_context(thd, + &thd->lex->definer->user, + &thd->lex->definer->host, + &thd->lex->sphead->m_db, + &backup); + restore_backup_context= 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 */ - - res= (sp_result= lex->sphead->create(thd)); - switch (sp_result) { - case SP_OK: -#ifndef NO_EMBEDDED_ACCESS_CHECKS - /* only add privileges if really neccessary */ if (sp_automatic_privileges && !opt_noacl && check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS, lex->sphead->m_db.str, name, @@ -4838,10 +4233,20 @@ end_with_restore_list: push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_PROC_AUTO_GRANT_FAIL, ER(ER_PROC_AUTO_GRANT_FAIL)); - close_thread_tables(thd); } + + /* + Restore current user with GLOBAL_ACL privilege of SQL thread + */ + if (restore_backup_context) + { + DBUG_ASSERT(thd->slave_thread == 1); + thd->security_ctx->restore_security_context(thd, backup); + } + #endif break; + } case SP_WRITE_ROW_FAILED: my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name); break; @@ -4851,6 +4256,9 @@ end_with_restore_list: case SP_BODY_TOO_LONG: my_error(ER_TOO_LONG_BODY, MYF(0), name); break; + case SP_FLD_STORE_FAILED: + my_error(ER_CANT_CREATE_SROUTINE, MYF(0), name); + break; default: my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name); break; @@ -4863,7 +4271,7 @@ end_with_restore_list: create_sp_error: if (sp_result != SP_OK ) goto error; - send_ok(thd); + my_ok(thd); break; /* break super switch */ } /* end case group bracket */ case SQLCOM_CALL: @@ -4874,7 +4282,7 @@ create_sp_error: This will cache all SP and SF and open and lock all tables required for execution. */ - if (check_table_access(thd, SELECT_ACL, all_tables, 0) || + if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) || open_and_lock_tables(thd, all_tables)) goto error; @@ -4907,8 +4315,6 @@ create_sp_error: goto error; } - my_bool save_no_send_ok= thd->net.no_send_ok; - thd->net.no_send_ok= TRUE; if (sp->m_flags & sp_head::MULTI_RESULTS) { if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS)) @@ -4918,7 +4324,6 @@ create_sp_error: back */ my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str); - thd->net.no_send_ok= save_no_send_ok; goto error; } /* @@ -4930,14 +4335,11 @@ create_sp_error: thd->server_status|= SERVER_MORE_RESULTS_EXISTS; } -#ifndef NO_EMBEDDED_ACCESS_CHECKS if (check_routine_access(thd, EXECUTE_ACL, sp->m_db.str, sp->m_name.str, TRUE, FALSE)) { - thd->net.no_send_ok= save_no_send_ok; goto error; } -#endif select_limit= thd->variables.select_limit; thd->variables.select_limit= HA_POS_ERROR; @@ -4959,14 +4361,16 @@ create_sp_error: thd->variables.select_limit= select_limit; - thd->net.no_send_ok= save_no_send_ok; thd->server_status&= ~bits_to_be_cleared; if (!res) - send_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 : - thd->row_count_func)); + my_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 : + thd->row_count_func)); else + { + DBUG_ASSERT(thd->is_error() || thd->killed); goto error; // Substatement should already have sent error + } } break; } @@ -5024,17 +4428,21 @@ 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) { case SP_OK: - send_ok(thd); + my_ok(thd); break; case SP_KEY_NOT_FOUND: my_error(ER_SP_DOES_NOT_EXIST, MYF(0), @@ -5077,11 +4485,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 { @@ -5095,10 +4505,9 @@ 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); + my_ok(thd); break; } } @@ -5115,24 +4524,17 @@ create_sp_error: res= sp_result; switch (sp_result) { case SP_OK: - send_ok(thd); + my_ok(thd); break; case SP_KEY_NOT_FOUND: if (lex->drop_if_exists) { - if (mysql_bin_log.is_open()) - { - Query_log_event qinfo(thd, thd->query, thd->query_length, - /* using_trans */ 0, - /* suppress use */ FALSE, - THD::NOT_KILLED); - mysql_bin_log.write(&qinfo); - } + write_bin_log(thd, TRUE, thd->query, thd->query_length); push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), SP_COM_STRING(lex), lex->spname->m_name.str); res= FALSE; - send_ok(thd); + my_ok(thd); break; } my_error(ER_SP_DOES_NOT_EXIST, MYF(0), @@ -5147,13 +4549,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; @@ -5162,42 +4559,20 @@ 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; } - case SQLCOM_SHOW_STATUS_PROC: - { - res= sp_show_status_procedure(thd, (lex->wild ? - lex->wild->ptr() : NullS)); - break; - } - case SQLCOM_SHOW_STATUS_FUNC: - { - res= sp_show_status_function(thd, (lex->wild ? - lex->wild->ptr() : NullS)); - break; - } #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); @@ -5214,6 +4589,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: { /* @@ -5228,7 +4616,7 @@ create_sp_error: } case SQLCOM_DROP_VIEW: { - if (check_table_access(thd, DROP_ACL, all_tables, 0) || + if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE) || end_active_trans(thd)) goto error; /* Conditionally writes to binlog. */ @@ -5264,7 +4652,7 @@ create_sp_error: break; } thd->transaction.xid_state.xa_state=XA_ACTIVE; - send_ok(thd); + my_ok(thd); break; } if (thd->lex->xa_opt != XA_NONE) @@ -5294,9 +4682,9 @@ 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|= OPTION_BEGIN; + thd->options= ((thd->options & ~(OPTION_KEEP_LOG)) | OPTION_BEGIN); thd->server_status|= SERVER_STATUS_IN_TRANS; - send_ok(thd); + my_ok(thd); break; case SQLCOM_XA_END: /* fake it */ @@ -5319,7 +4707,7 @@ create_sp_error: if (xa_trans_rolled_back(&thd->transaction.xid_state)) break; thd->transaction.xid_state.xa_state=XA_IDLE; - send_ok(thd); + my_ok(thd); break; case SQLCOM_XA_PREPARE: if (thd->transaction.xid_state.xa_state != XA_IDLE) @@ -5341,7 +4729,7 @@ create_sp_error: break; } thd->transaction.xid_state.xa_state=XA_PREPARED; - send_ok(thd); + my_ok(thd); break; case SQLCOM_XA_COMMIT: if (!thd->transaction.xid_state.xid.eq(thd->lex->xid)) @@ -5359,7 +4747,7 @@ create_sp_error: { ha_commit_or_rollback_by_xid(thd->lex->xid, 1); xid_cache_delete(xs); - send_ok(thd); + my_ok(thd); } break; } @@ -5375,7 +4763,7 @@ create_sp_error: if ((r= ha_commit(thd))) my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0)); else - send_ok(thd); + my_ok(thd); } else if (thd->transaction.xid_state.xa_state == XA_PREPARED && thd->lex->xa_opt == XA_NONE) @@ -5390,7 +4778,7 @@ create_sp_error: if (ha_commit_one_phase(thd, 1)) my_error(ER_XAER_RMERR, MYF(0)); else - send_ok(thd); + my_ok(thd); start_waiting_global_read_lock(thd); } } @@ -5400,7 +4788,7 @@ create_sp_error: xa_state_names[thd->transaction.xid_state.xa_state]); break; } - thd->options&= ~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); @@ -5418,7 +4806,7 @@ create_sp_error: ha_commit_or_rollback_by_xid(thd->lex->xid, 0); xid_cache_delete(xs); if (ok) - send_ok(thd); + my_ok(thd); } break; } @@ -5433,22 +4821,110 @@ create_sp_error: if (xa_trans_rollback(thd)) my_error(ER_XAER_RMERR, MYF(0)); else - send_ok(thd); + my_ok(thd); break; 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))) + my_ok(thd); + break; + case SQLCOM_INSTALL_PLUGIN: + if (! (res= mysql_install_plugin(thd, &thd->lex->comment, + &thd->lex->ident))) + my_ok(thd); + break; + case SQLCOM_UNINSTALL_PLUGIN: + if (! (res= mysql_uninstall_plugin(thd, &thd->lex->comment))) + my_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; + } + my_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; + } + my_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 + { + my_ok(thd, 0); + } + break; + } + my_ok(thd, 1); + break; + } default: #ifndef EMBEDDED_LIBRARY DBUG_ASSERT(0); /* Impossible */ #endif - send_ok(thd); + my_ok(thd); break; } thd_proc_info(thd, "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 @@ -5463,21 +4939,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) { /* @@ -5486,26 +4959,84 @@ end: */ start_waiting_global_read_lock(thd); } - DBUG_RETURN(res || thd->net.report_error); + DBUG_RETURN(res || thd->is_error()); } -/* +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, QT_ORDINARY); + str.append('\0'); + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_YES, str.ptr()); + } + if (res) + result->abort(); + else + 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; +} + + +#ifndef NO_EMBEDDED_ACCESS_CHECKS +/** Check grants for commands which work only with one table. - SYNOPSIS - check_single_table_access() - thd Thread handler - privilege requested privilege - all_tables global table list of query + @param thd Thread handler + @param privilege requested privilege + @param all_tables global table list of query + @param no_errors FALSE/TRUE - report/don't report error to + the client (using my_error() call). - RETURN - 0 - OK - 1 - access denied, error is sent to client + @retval + 0 OK + @retval + 1 access denied, error is sent to client */ 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; @@ -5521,15 +5052,16 @@ 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)) + !(all_tables->view && + all_tables->effective_algorithm == VIEW_ALGORITHM_TMPTABLE) && + check_grant(thd, privilege, all_tables, 0, 1, no_errors)) goto deny; thd->security_ctx= backup_ctx; @@ -5540,24 +5072,23 @@ deny: return 1; } -/* +/** Check grants for commands which work only with one table and all other tables belonging to subselects or implicitly opened tables. - SYNOPSIS - check_one_table_access() - thd Thread handler - privilege requested privilege - all_tables global table list of query + @param thd Thread handler + @param privilege requested privilege + @param all_tables global table list of query - RETURN - 0 - OK - 1 - access denied, error is sent to client + @retval + 0 OK + @retval + 1 access denied, error is sent to client */ 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 */ @@ -5570,43 +5101,43 @@ 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; } if (subselects_tables && - (check_table_access(thd, SELECT_ACL, subselects_tables, 0))) + (check_table_access(thd, SELECT_ACL, subselects_tables, UINT_MAX, FALSE))) return 1; } return 0; } -/**************************************************************************** - Get the user (global) and database privileges for all used tables +/** + Get the user (global) and database privileges for all used tables. - NOTES + @param save_priv In this we store global and db level grants for the + table. Note that we don't store db level grants if the + global grants is enough to satisfy the request and the + global grants contains a SELECT grant. + + @note The idea of EXTRA_ACL is that one will be granted access to the table if one has the asked privilege on any column combination of the table; For example to be able to check a table one needs to have SELECT privilege on any column of the table. - RETURN + @retval 0 ok - 1 If we can't get the privileges and we don't use table/column grants. - - save_priv In this we store global and db level grants for the table - Note that we don't store db level grants if the global grants - is enough to satisfy the request and the global grants contains - a SELECT grant. -****************************************************************************/ - + @retval + 1 If we can't get the privileges and we don't use table/column + grants. +*/ bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, bool dont_check_global_grants, bool no_errors, bool schema_db) { Security_context *sctx= thd->security_ctx; -#ifndef NO_EMBEDDED_ACCESS_CHECKS ulong db_access; /* GRANT command: @@ -5619,7 +5150,6 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, */ bool db_is_pattern= (test(want_access & GRANT_ACL) && dont_check_global_grants); -#endif ulong dummy; DBUG_ENTER("check_access"); DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu", @@ -5659,9 +5189,6 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, } } -#ifdef NO_EMBEDDED_ACCESS_CHECKS - DBUG_RETURN(0); -#else if ((sctx->master_access & want_access) == want_access) { /* @@ -5706,9 +5233,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 */ @@ -5720,115 +5246,61 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, thd->db : "unknown"))); /* purecov: tested */ DBUG_RETURN(TRUE); /* purecov: tested */ -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ -} - - -/* - check for global access and give descriptive error message if it fails - - SYNOPSIS - check_global_access() - thd Thread handler - want_access Use should have any of these global rights - - WARNING - One gets access right if one has ANY of the rights in want_access - This is useful as one in most cases only need one global right, - but in some case we want to check if the user has SUPER or - REPL_CLIENT_ACL rights. - - RETURN - 0 ok - 1 Access denied. In this case an error is sent to the client -*/ - -bool check_global_access(THD *thd, ulong want_access) -{ -#ifdef NO_EMBEDDED_ACCESS_CHECKS - return 0; -#else - char command[128]; - if ((thd->security_ctx->master_access & want_access)) - return 0; - get_privilege_desc(command, sizeof(command), want_access); - my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command); - return 1; -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ } 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; + case SCH_EVENTS: + { + const char *dst_db_name= table->schema_select_lex->db; - DBUG_ASSERT(dst_db_name); + DBUG_ASSERT(dst_db_name); - if (check_access(thd, SELECT_ACL, dst_db_name, - &thd->col_access, FALSE, FALSE, - is_schema_db(dst_db_name))) - { - return TRUE; - } - - 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: - case SCH_PROFILES: + return (check_grant(thd, SELECT_ACL, dst_table, 2, UINT_MAX, FALSE)); + } + default: break; } @@ -5836,46 +5308,42 @@ static bool check_show_access(THD *thd, TABLE_LIST *table) } -/* +/** Check the privilege for all used tables. - SYNOPSYS - check_table_access() - thd Thread context - want_access Privileges requested - tables List of tables to be checked - no_errors FALSE/TRUE - report/don't report error to - the client (using my_error() call). + @param thd Thread context + @param want_access Privileges requested + @param tables List of tables to be checked + @param number Check at most this number of tables. + @param no_errors FALSE/TRUE - report/don't report error to + the client (using my_error() call). - NOTES + @note Table privileges are cached in the table list for GRANT checking. This functions assumes that table list used and thd->lex->query_tables_own_last value correspond to each other (the latter should be either 0 or point to next_global member of one of elements of this table list). - RETURN VALUE - FALSE - OK - TRUE - Access denied + @retval FALSE OK + @retval TRUE Access denied */ bool check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, - bool no_errors) + uint number, bool no_errors) { - uint found=0; - ulong found_access=0; -#ifndef NO_EMBEDDED_ACCESS_CHECKS TABLE_LIST *org_tables= tables; -#endif TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); + uint i= 0; Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx; /* The check that first_not_own_table is not reached is for the case when the given table list refers to the list for prelocking (contains tables of other queries). For simple queries first_not_own_table is 0. */ - for (; tables != first_not_own_table; tables= tables->next_global) + for (; i < number && tables != first_not_own_table; + tables= tables->next_global, i++) { if (tables->security_ctx) sctx= tables->security_ctx; @@ -5905,10 +5373,8 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, continue; } - 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)) + if (tables->is_anonymous_derived_table() || + (tables->table && (int)tables->table->s->tmp_table)) continue; thd->security_ctx= sctx; if ((sctx->master_access & want_access) == @@ -5917,26 +5383,19 @@ 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, - 0, no_errors, test(tables->schema_table))) - goto deny; // Access denied - found_access=tables->grant.privilege; - found=1; - } + if (check_access(thd, want_access, tables->get_db_name(), + &tables->grant.privilege, 0, no_errors, + test(tables->schema_table))) + goto deny; // Access denied } - else if (check_access(thd,want_access,tables->db,&tables->grant.privilege, - 0, no_errors, test(tables->schema_table))) + else if (check_access(thd, want_access, tables->get_db_name(), + &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, - test(want_access & EXTRA_ACL), UINT_MAX, no_errors); - return FALSE; + return check_grant(thd,want_access & ~EXTRA_ACL,org_tables, + test(want_access & EXTRA_ACL), number, no_errors); deny: thd->security_ctx= backup_ctx; return TRUE; @@ -5965,26 +5424,20 @@ check_routine_access(THD *thd, ulong want_access,char *db, char *name, 0, no_errors, 0)) return TRUE; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - if (grant_option) return check_grant_routine(thd, want_access, tables, is_proc, no_errors); -#endif - - return FALSE; } -/* - Check if the routine has any of the routine privileges +/** + Check if the routine has any of the routine privileges. - SYNOPSIS - check_some_routine_access() - thd Thread handler - db Database name - name Routine name + @param thd Thread handler + @param db Database name + @param name Routine name - RETURN + @retval 0 ok + @retval 1 error */ @@ -6008,17 +5461,15 @@ bool check_some_routine_access(THD *thd, const char *db, const char *name, /* Check if the given table has any of the asked privileges - SYNOPSIS - check_some_access() - thd Thread handler - want_access Bitmap of possible privileges to check for + @param thd Thread handler + @param want_access Bitmap of possible privileges to check for - RETURN + @retval 0 ok + @retval 1 error */ - bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) { ulong access; @@ -6029,10 +5480,10 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) { if (access & want_access) { - if ((!check_access(thd, access, table->db, + 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); } } @@ -6040,52 +5491,47 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) DBUG_RETURN(1); } +#endif /*NO_EMBEDDED_ACCESS_CHECKS*/ -bool check_merge_table_access(THD *thd, char *db, - TABLE_LIST *table_list) -{ - int error=0; - if (table_list) - { - /* Check that all tables use the current database */ - TABLE_LIST *tmp; - for (tmp= table_list; tmp; tmp= tmp->next_local) - { - if (!tmp->db || !tmp->db[0]) - tmp->db=db; - } - error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL, - table_list,0); - } - return error; -} +/** + check for global access and give descriptive error message if it fails. + + @param thd Thread handler + @param want_access Use should have any of these global rights + + @warning + One gets access right if one has ANY of the rights in want_access. + This is useful as one in most cases only need one global right, + but in some case we want to check if the user has SUPER or + REPL_CLIENT_ACL rights. -static bool check_db_used(THD *thd,TABLE_LIST *tables) + @retval + 0 ok + @retval + 1 Access denied. In this case an error is sent to the client +*/ + +bool check_global_access(THD *thd, ulong want_access) { - 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; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + char command[128]; + if ((thd->security_ctx->master_access & want_access)) + return 0; + get_privilege_desc(command, sizeof(command), want_access); + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command); + return 1; +#else + return 0; +#endif } /**************************************************************************** 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 @@ -6096,24 +5542,25 @@ static bool check_db_used(THD *thd,TABLE_LIST *tables) long max_stack_used; #endif -#ifndef EMBEDDED_LIBRARY -/* +/** + @note Note: The 'buf' parameter is necessary, even if it is unused here. - fix_fields functions has a "dummy" buffer large enough for the corresponding exec. (Thus we only have to check in fix_fields.) - 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); if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >= - (long) (thread_stack - margin)) + (long) (my_thread_stack_size - margin)) { - sprintf(errbuff[0],ER(ER_STACK_OVERRUN_NEED_MORE), - stack_used,thread_stack,margin); - my_message(ER_STACK_OVERRUN_NEED_MORE,errbuff[0],MYF(0)); + char ebuff[MYSQL_ERRMSG_SIZE]; + my_snprintf(ebuff, sizeof(ebuff), ER(ER_STACK_OVERRUN_NEED_MORE), + stack_used, my_thread_stack_size, margin); + my_message(ER_STACK_OVERRUN_NEED_MORE, ebuff, MYF(ME_FATALERROR)); thd->fatal_error(); return 1; } @@ -6137,14 +5584,14 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) if (!state->yacc_yyvs) old_info= *yystacksize; *yystacksize= set_zone((*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX); - if (!(state->yacc_yyvs= (char*) + if (!(state->yacc_yyvs= (uchar*) my_realloc(state->yacc_yyvs, - *yystacksize*sizeof(**yyvs), - MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) || - !(state->yacc_yyss= (char*) + *yystacksize*sizeof(**yyvs), + MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) || + !(state->yacc_yyss= (uchar*) my_realloc(state->yacc_yyss, - *yystacksize*sizeof(**yyss), - MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR)))) + *yystacksize*sizeof(**yyss), + MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR)))) return 1; if (old_info) { @@ -6162,49 +5609,87 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) } -/* +/** Reset THD part responsible for command processing state. - DESCRIPTION This needs to be called before execution of every statement (prepared or conventional). + It is not called by substatements of routines. - TODO + @todo Make it a method of THD and align its name with the rest of reset/end/start/init methods. + @todo Call it after we use THD for queries, not before. */ 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 */ + DBUG_ASSERT(! thd->in_sub_stmt); 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); + /* + Clear the status flag that are expected to be cleared at the + beginning of each SQL statement. + */ + thd->server_status&= ~SERVER_STATUS_CLEAR_SET; + /* + 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) + + if (opt_bin_log) { - if (opt_bin_log) - { - reset_dynamic(&thd->user_var_events); - thd->user_var_events_alloc= thd->mem_root; - } - thd->clear_error(); - thd->total_warn_count=0; // Warnings for this query - thd->rand_used= 0; - thd->sent_row_count= thd->examined_row_count= 0; + reset_dynamic(&thd->user_var_events); + thd->user_var_events_alloc= thd->mem_root; } + thd->clear_error(); + thd->main_da.reset_diagnostics_area(); + thd->total_warn_count=0; // Warnings for this query + 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_PRINT("debug", + ("current_stmt_binlog_row_based: %d", + thd->current_stmt_binlog_row_based)); + DBUG_VOID_RETURN; } +/** + Resets the lex->current_select object. + @note It is assumed that lex->current_select != NULL + + This function is a wrapper around select_lex->init_select() with an added + check for the special situation when using INTO OUTFILE and LOAD DATA. +*/ + void mysql_init_select(LEX *lex) { @@ -6219,6 +5704,18 @@ mysql_init_select(LEX *lex) } +/** + Used to allocate a new SELECT_LEX object on the current thd mem_root and + link it into the relevant lists. + + This function is always followed by mysql_init_select. + + @see mysql_init_select + + @retval TRUE An error occurred + @retval FALSE The new SELECT_LEX was successfully allocated. +*/ + bool mysql_new_select(LEX *lex, bool move_down) { @@ -6242,7 +5739,7 @@ mysql_new_select(LEX *lex, bool move_down) /* Don't evaluate this subquery during statement prepare even if it's a constant one. The flag is switched off in the end of - mysql_stmt_prepare. + mysqld_stmt_prepare. */ if (thd->stmt_arena->is_stmt_prepare()) select_lex->uncacheable|= UNCACHEABLE_PREPARE; @@ -6294,17 +5791,14 @@ mysql_new_select(LEX *lex, bool move_down) DBUG_RETURN(0); } -/* +/** Create a select to return the same output as 'SELECT @@var_name'. - SYNOPSIS - create_select_for_variable() - var_name Variable name + Used for SHOW COUNT(*) [ WARNINGS | ERROR]. - DESCRIPTION - Used for SHOW COUNT(*) [ WARNINGS | ERROR] + This will crash with a core dump if the variable doesn't exists. - This will crash with a core dump if the variable doesn't exists + @param var_name Variable name */ void create_select_for_variable(const char *var_name) @@ -6321,7 +5815,7 @@ void create_select_for_variable(const char *var_name) mysql_init_select(lex); lex->sql_command= SQLCOM_SELECT; tmp.str= (char*) var_name; - tmp.length=(uint) strlen(var_name); + tmp.length=strlen(var_name); bzero((char*) &null_lex_string.str, sizeof(null_lex_string)); /* We set the name of Item to @@session.var_name because that then is used @@ -6330,7 +5824,7 @@ void create_select_for_variable(const char *var_name) if ((var= get_system_var(thd, OPT_SESSION, tmp, null_lex_string))) { end= strxmov(buff, "@@session.", var_name, NullS); - var->set_name(buff, (uint) (end - buff), system_charset_info); + var->set_name(buff, end-buff, system_charset_info); add_item_to_list(thd, var); } DBUG_VOID_RETURN; @@ -6344,11 +5838,12 @@ void mysql_init_multi_delete(LEX *lex) lex->select_lex.select_limit= 0; lex->unit.select_limit_cnt= HA_POS_ERROR; lex->select_lex.table_list.save_and_clear(&lex->auxiliary_table_list); - lex->lock_option= using_update_log ? TL_READ_NO_INSERT : TL_READ; + lex->lock_option= TL_READ_DEFAULT; lex->query_tables= 0; 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. @@ -6356,11 +5851,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, @@ -6397,13 +5893,11 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, sp_cache_flush_obsolete(&thd->sp_func_cache); Parser_state parser_state(thd, inBuf, length); - thd->m_parser_state= &parser_state; - int err= MYSQLparse(thd); + bool err= parse_sql(thd, & parser_state, NULL); *found_semicolon= parser_state.m_lip.found_semicolon; - thd->m_parser_state= NULL; - if (!err && ! thd->is_fatal_error) + if (!err) { #ifndef NO_EMBEDDED_ACCESS_CHECKS if (mqh_used && thd->user_connect && @@ -6414,7 +5908,7 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, else #endif { - if (! thd->net.report_error) + if (! thd->is_error()) { /* Binlog logs a string starting from thd->query and having length @@ -6426,9 +5920,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 (parser_state.m_lip.found_semicolon && - (thd->query_length= (ulong)(parser_state.m_lip.found_semicolon - - thd->query))) + if (*found_semicolon && + (thd->query_length= (ulong)(*found_semicolon - thd->query))) thd->query_length--; /* Actually execute the query */ if (*found_semicolon) @@ -6438,13 +5931,12 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, } lex->set_trg_event_type_for_tables(); mysql_execute_command(thd); - query_cache_end_of_result(thd); } } } else { - DBUG_ASSERT(thd->net.report_error); + DBUG_ASSERT(thd->is_error()); DBUG_PRINT("info",("Command aborted. Fatal_error: %d", thd->is_fatal_error)); @@ -6476,8 +5968,9 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, Usable by the replication SQL thread only: just parse a query to know if it can be ignored because of replicate-*-table rules. - RETURN VALUES + @retval 0 cannot be ignored + @retval 1 can be ignored */ @@ -6488,14 +5981,10 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) DBUG_ENTER("mysql_test_parse_for_slave"); Parser_state parser_state(thd, inBuf, length); - thd->m_parser_state= &parser_state; - lex_start(thd); mysql_reset_thd_for_next_command(thd); - int err= MYSQLparse((void*) thd); - thd->m_parser_state= NULL; - if (!err && ! thd->is_fatal_error && + if (!parse_sql(thd, & parser_state, NULL) && all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) error= 1; /* Ignore question */ thd->end_statement(); @@ -6506,12 +5995,14 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) -/***************************************************************************** -** Store field definition for create -** Return 0 if ok -******************************************************************************/ +/** + Store field definition for create. -bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, + @return + Return 0 if ok +*/ + +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, @@ -6520,29 +6011,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(); } @@ -6557,9 +6053,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) @@ -6568,24 +6064,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), @@ -6593,14 +6089,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, "6.0", 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); @@ -6611,7 +6104,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, } -/* Store position for column in ALTER TABLE .. ADD column */ +/** Store position for column in ALTER TABLE .. ADD column. */ void store_position_for_column(const char *name) { @@ -6630,45 +6123,14 @@ 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 -****************************************************************************/ - +/** + save order by and tables in own lists. +*/ bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc) { @@ -6682,29 +6144,28 @@ 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); } -/* - Add a table to list of used tables - - SYNOPSIS - add_table_to_list() - table Table to add - alias alias for table (or null if no alias) - table_options A set of the following bits: - TL_OPTION_UPDATING Table will be updated - TL_OPTION_FORCE_INDEX Force usage of index - TL_OPTION_ALIAS an alias in multi table DELETE - lock_type How table should be locked - use_index List of indexed used in USE INDEX - ignore_index List of indexed used in IGNORE INDEX - - RETURN +/** + Add a table to list of used tables. + + @param table Table to add + @param alias alias for table (or null if no alias) + @param table_options A set of the following bits: + - TL_OPTION_UPDATING : Table will be updated + - TL_OPTION_FORCE_INDEX : Force usage of index + - TL_OPTION_ALIAS : an alias in multi table DELETE + @param lock_type How table should be locked + @param use_index List of indexed used in USE INDEX + @param ignore_index List of indexed used in IGNORE INDEX + + @retval 0 Error - # Pointer to TABLE_LIST element added to the total table list + @retval + \# Pointer to TABLE_LIST element added to the total table list */ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, @@ -6712,8 +6173,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; @@ -6733,6 +6193,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) @@ -6741,18 +6208,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; } @@ -6775,12 +6237,12 @@ 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 || /* this check is used for show columns|keys from I_S hidden table */ - lex->orig_sql_command == SQLCOM_SHOW_FIELDS || - lex->orig_sql_command == SQLCOM_SHOW_KEYS))) + lex->sql_command == SQLCOM_SHOW_FIELDS || + lex->sql_command == SQLCOM_SHOW_KEYS))) { my_error(ER_UNKNOWN_TABLE, MYF(0), ptr->table_name, INFORMATION_SCHEMA_NAME.str); @@ -6791,12 +6253,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) @@ -6843,7 +6300,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); @@ -6851,14 +6308,9 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, } -/* - Initialize a new table list for a nested join - - SYNOPSIS - init_nested_join() - thd current thread +/** + Initialize a new table list for a nested join. - DESCRIPTION The function initializes a structure of the TABLE_LIST type for a nested join. It sets up its nested join list as empty. The created structure is added to the front of the current @@ -6867,9 +6319,12 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, created empty list after having saved the info on the old level in the initialized structure. - RETURN VALUE - 0, if success - 1, otherwise + @param thd current thread + + @retval + 0 if success + @retval + 1 otherwise */ bool st_select_lex::init_nested_join(THD *thd) @@ -6882,11 +6337,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(); @@ -6894,21 +6350,18 @@ bool st_select_lex::init_nested_join(THD *thd) } -/* - End a nested join table list - - SYNOPSIS - end_nested_join() - thd current thread +/** + End a nested join table list. - DESCRIPTION The function returns to the previous join nest level. If the current level contains only one member, the function moves it one level up, eliminating the nest. - RETURN VALUE - Pointer to TABLE_LIST element added to the total table list, if success - 0, otherwise + @param thd current thread + + @return + - Pointer to TABLE_LIST element added to the total table list, if success + - 0, otherwise */ TABLE_LIST *st_select_lex::end_nested_join(THD *thd) @@ -6940,20 +6393,17 @@ TABLE_LIST *st_select_lex::end_nested_join(THD *thd) } -/* - Nest last join operation - - SYNOPSIS - nest_last_join() - thd current thread +/** + Nest last join operation. - DESCRIPTION The function nest last join operation as if it was enclosed in braces. - RETURN VALUE - 0 Error - # Pointer to TABLE_LIST element created for the new nested join + @param thd current thread + @retval + 0 Error + @retval + \# Pointer to TABLE_LIST element created for the new nested join */ TABLE_LIST *st_select_lex::nest_last_join(THD *thd) @@ -6967,10 +6417,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(); @@ -6997,20 +6448,17 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd) } -/* - Add a table to the current join list - - SYNOPSIS - add_joined_table() - table the table to add +/** + Add a table to the current join list. - DESCRIPTION The function puts a table in front of the current join list of st_select_lex object. Thus, joined tables are put into this list in the reverse order (the most outer join operation follows first). - RETURN VALUE + @param table the table to add + + @return None */ @@ -7024,14 +6472,9 @@ void st_select_lex::add_joined_table(TABLE_LIST *table) } -/* - Convert a right join into equivalent left join - - SYNOPSIS - convert_right_join() - thd current thread +/** + Convert a right join into equivalent left join. - DESCRIPTION The function takes the current join list t[0],t[1] ... and effectively converts it into the list t[1],t[0] ... Although the outer_join flag for the new nested table contains @@ -7039,6 +6482,7 @@ void st_select_lex::add_joined_table(TABLE_LIST *table) operation. EXAMPLES + @verbatim SELECT * FROM t1 RIGHT JOIN t2 ON on_expr => SELECT * FROM t2 LEFT JOIN t1 ON on_expr @@ -7050,10 +6494,13 @@ void st_select_lex::add_joined_table(TABLE_LIST *table) SELECT * FROM t1 LEFT JOIN t2 ON on_expr1 RIGHT JOIN t3 ON on_expr2 => SELECT * FROM t3 LEFT JOIN (t1 LEFT JOIN t2 ON on_expr2) ON on_expr1 + @endverbatim - RETURN - Pointer to the table representing the inner table, if success - 0, otherwise + @param thd current thread + + @return + - Pointer to the table representing the inner table, if success + - 0, otherwise */ TABLE_LIST *st_select_lex::convert_right_join() @@ -7069,14 +6516,12 @@ TABLE_LIST *st_select_lex::convert_right_join() DBUG_RETURN(tab1); } -/* - Set lock for all tables in current select level +/** + Set lock for all tables in current select level. - SYNOPSIS: - set_lock_for_tables() - lock_type Lock to set for tables + @param lock_type Lock to set for tables - NOTE: + @note If lock is a write lock, then tables->updating is set 1 This is to get tables_ok to know that the table is updated by the query @@ -7088,7 +6533,6 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type) DBUG_ENTER("set_lock_for_tables"); DBUG_PRINT("enter", ("lock_type: %d for_update: %d", lock_type, for_update)); - for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first; tables; tables= tables->next_local) @@ -7100,27 +6544,29 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type) } -/* - Create a fake SELECT_LEX for a unit - - SYNOPSIS: - add_fake_select_lex() - thd thread handle +/** + Create a fake SELECT_LEX for a unit. - DESCRIPTION The method create a fake SELECT_LEX object for a unit. This object is created for any union construct containing a union operation and also for any single select union construct of the form + @verbatim (SELECT ... ORDER BY order_list [LIMIT n]) ORDER BY ... + @endvarbatim or of the form + @varbatim (SELECT ... ORDER BY LIMIT n) ORDER BY ... + @endvarbatim - NOTES + @param thd_arg thread handle + + @note The object is used to retrieve rows from the temporary table where the result on the union is obtained. - RETURN VALUES + @retval 1 on failure to create the object + @retval 0 on success */ @@ -7145,7 +6591,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 @@ -7162,24 +6608,22 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg) } -/* +/** Push a new name resolution context for a JOIN ... ON clause to the context stack of a query block. - SYNOPSIS - push_new_name_resolution_context() - thd pointer to current thread - left_op left operand of the JOIN - right_op rigth operand of the JOIN - - DESCRIPTION Create a new name resolution context for a JOIN ... ON clause, set the first and last leaves of the list of table references to be used for name resolution, and push the newly created context to the stack of contexts of the query. - RETURN + @param thd pointer to current thread + @param left_op left operand of the JOIN + @param right_op rigth operand of the JOIN + + @retval FALSE if all is OK + @retval TRUE if a memory allocation error occured */ @@ -7199,19 +6643,17 @@ push_new_name_resolution_context(THD *thd, } -/* +/** Add an ON condition to the second operand of a JOIN ... ON. - SYNOPSIS - add_join_on - b the second operand of a JOIN ... ON - expr the condition to be added to the ON clause - - DESCRIPTION Add an ON condition to the right operand of a JOIN ... ON clause. - RETURN + @param b the second operand of a JOIN ... ON + @param expr the condition to be added to the ON clause + + @retval FALSE if there was some error + @retval TRUE if all is OK */ @@ -7235,18 +6677,10 @@ void add_join_on(TABLE_LIST *b, Item *expr) } -/* +/** Mark that there is a NATURAL JOIN or JOIN ... USING between two tables. - SYNOPSIS - add_join_natural() - a Left join argument - b Right join argument - using_fields Field names from USING clause - lex The current st_select_lex - - IMPLEMENTATION This function marks that table b should be joined with a either via a NATURAL JOIN or via JOIN ... USING. Both join types are special cases of each other, so we treat them together. The function @@ -7257,6 +6691,7 @@ void add_join_on(TABLE_LIST *b, Item *expr) was an outer join. EXAMPLE + @verbatim SELECT * FROM t1 NATURAL LEFT JOIN t2 <=> SELECT * FROM t1 LEFT JOIN t2 ON (t1.i=t2.i and t1.j=t2.j ... ) @@ -7268,9 +6703,11 @@ void add_join_on(TABLE_LIST *b, Item *expr) SELECT * FROM t1 JOIN t2 USING(j) WHERE <some_cond> <=> SELECT * FROM t1, t2 WHERE (t1.j=t2.j and <some_cond>) + @endverbatim - RETURN - None + @param a Left join argument + @param b Right join argument + @param using_fields Field names from USING clause */ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields, @@ -7281,24 +6718,23 @@ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields, } -/* +/** Reload/resets privileges and the different caches. - SYNOPSIS - reload_acl_and_cache() - thd Thread handler (can be NULL!) - options What should be reset/reloaded (tables, privileges, - slave...) - tables Tables to flush (if any) - write_to_binlog Depending on 'options', it may be very bad to write the - query to the binlog (e.g. FLUSH SLAVE); this is a - pointer where reload_acl_and_cache() will put 0 if - it thinks we really should not write to the binlog. - Otherwise it will put 1. - - RETURN - 0 ok - !=0 error. thd->killed or thd->net.report_error is set + @param thd Thread handler (can be NULL!) + @param options What should be reset/reloaded (tables, privileges, slave...) + @param tables Tables to flush (if any) + @param write_to_binlog True if we can write to the binlog. + + @note Depending on 'options', it may be very bad to write the + query to the binlog (e.g. FLUSH SLAVE); this is a + pointer where reload_acl_and_cache() will put 0 if + it thinks we really should not write to the binlog. + Otherwise it will put 1. + + @return Error status code + @retval 0 Ok + @retval !=0 Error; thd->killed is set or thd->is_error() is true */ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, @@ -7322,14 +6758,16 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, { thd->thread_stack= (char*) &tmp_thd; thd->store_globals(); + lex_start(thd); } if (thd) { bool reload_acl_failed= acl_reload(thd); bool reload_grants_failed= grant_reload(thd); - - if (reload_acl_failed || reload_grants_failed) + bool reload_servers_failed= servers_reload(thd); + + if (reload_acl_failed || reload_grants_failed || reload_servers_failed) { result= 1; /* @@ -7354,7 +6792,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. */ /* @@ -7364,8 +6803,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); @@ -7375,7 +6812,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; @@ -7425,8 +6866,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, tmp_write_to_binlog= 0; if (lock_global_read_lock(thd)) return 1; // Killed - if (close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, - tables)) + if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ? + FALSE : TRUE, TRUE)) result= 1; if (make_global_read_lock_block_commit(thd)) // Killed @@ -7438,7 +6879,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, } else { - if (close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables)) + if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ? + FALSE : TRUE, FALSE)) result= 1; } my_dbopt_cleanup(); @@ -7457,7 +6899,6 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, if (reset_master(thd)) { result=1; - thd->fatal_error(); // Ensure client get error } } #endif @@ -7479,31 +6920,35 @@ 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 - SYNOPSIS - kill_one_thread() - thd Thread class - id Thread id +/** + kill on thread. - NOTES + @param thd Thread class + @param id Thread id + @param only_kill_query Should it kill the query or the connection + + @note 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 @@ -7541,18 +6986,35 @@ 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) - send_ok(thd); +/* + 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))) + my_ok(thd); else my_error(error, MYF(0), id); } - /* If pointer is not a null pointer, append filename to it */ +/** 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) @@ -7568,7 +7030,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); @@ -7576,14 +7038,12 @@ static bool append_file_to_dir(THD *thd, const char **filename_ptr, } -/* - Check if the select is a simple select (not an union) - - SYNOPSIS - check_simple_select() +/** + Check if the select is a simple select (not an union). - RETURN VALUES + @retval 0 ok + @retval 1 error ; In this case the error messege is sent to the client */ @@ -7640,17 +7100,15 @@ Comp_creator *comp_ne_creator(bool invert) } -/* - Construct ALL/ANY/SOME subquery Item +/** + Construct ALL/ANY/SOME subquery Item. - SYNOPSIS - all_any_subquery_creator() - left_expr - pointer to left expression - cmp - compare function creator - all - true if we create ALL subquery - select_lex - pointer on parsed subquery structure + @param left_expr pointer to left expression + @param cmp compare function creator + @param all true if we create ALL subquery + @param select_lex pointer on parsed subquery structure - RETURN VALUE + @return constructed Item (or 0 if out of memory) */ Item * all_any_subquery_creator(Item *left_expr, @@ -7673,16 +7131,15 @@ Item * all_any_subquery_creator(Item *left_expr, } -/* - Multi update query pre-check +/** + Multi update query pre-check. - SYNOPSIS - multi_update_precheck() - thd Thread handler - tables Global/local table list (have to be the same) + @param thd Thread handler + @param tables Global/local table list (have to be the same) - RETURN VALUE + @retval FALSE OK + @retval TRUE Error */ @@ -7710,12 +7167,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_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; @@ -7723,19 +7179,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); } } @@ -7753,16 +7207,15 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) DBUG_RETURN(FALSE); } -/* - Multi delete query pre-check +/** + Multi delete query pre-check. - SYNOPSIS - multi_delete_precheck() - thd Thread handler - tables Global/local table list + @param thd Thread handler + @param tables Global/local table list - RETURN VALUE + @retval FALSE OK + @retval TRUE error */ @@ -7776,8 +7229,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, UINT_MAX, FALSE)) DBUG_RETURN(TRUE); /* @@ -7786,7 +7238,7 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables) call check_table_access() safely. */ thd->lex->query_tables_own_last= 0; - if (check_table_access(thd, DELETE_ACL, aux_tables, 0)) + if (check_table_access(thd, DELETE_ACL, aux_tables, UINT_MAX, FALSE)) { thd->lex->query_tables_own_last= save_query_tables_own_last; DBUG_RETURN(TRUE); @@ -7803,17 +7255,16 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables) } -/* +/** Link tables in auxilary table list of multi-delete with corresponding elements in main table list, and set proper locks for them. - SYNOPSIS - multi_delete_set_locks_and_link_aux_tables() - lex - pointer to LEX representing multi-delete + @param lex pointer to LEX representing multi-delete - RETURN VALUE - FALSE - success - TRUE - error + @retval + FALSE success + @retval + TRUE error */ bool multi_delete_set_locks_and_link_aux_tables(LEX *lex) @@ -7856,16 +7307,15 @@ bool multi_delete_set_locks_and_link_aux_tables(LEX *lex) } -/* - simple UPDATE query pre-check +/** + simple UPDATE query pre-check. - SYNOPSIS - update_precheck() - thd Thread handler - tables Global table list + @param thd Thread handler + @param tables Global table list - RETURN VALUE + @retval FALSE OK + @retval TRUE Error */ @@ -7877,21 +7327,19 @@ 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)); } -/* - simple DELETE query pre-check +/** + simple DELETE query pre-check. - SYNOPSIS - delete_precheck() - thd Thread handler - tables Global table list + @param thd Thread handler + @param tables Global table list - RETURN VALUE + @retval FALSE OK + @retval TRUE error */ @@ -7906,16 +7354,15 @@ bool delete_precheck(THD *thd, TABLE_LIST *tables) } -/* - simple INSERT query pre-check +/** + simple INSERT query pre-check. - SYNOPSIS - insert_precheck() - thd Thread handler - tables Global table list + @param thd Thread handler + @param tables Global table list - RETURN VALUE + @retval FALSE OK + @retval TRUE error */ @@ -7940,20 +7387,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) @@ -7961,21 +7406,20 @@ 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); } -/* - CREATE TABLE query pre-check +/** + CREATE TABLE query pre-check. - SYNOPSIS - create_table_precheck() - thd Thread handler - tables Global table list - create_table Table which will be created + @param thd Thread handler + @param tables Global table list + @param create_table Table which will be created - RETURN VALUE + @retval FALSE OK + @retval TRUE Error */ @@ -7988,8 +7432,15 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, bool error= TRUE; // Error message is given DBUG_ENTER("create_table_precheck"); + /* + Require CREATE [TEMPORARY] privilege on new table; for + CREATE TABLE ... SELECT, also require INSERT. + */ + want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ? - CREATE_TMP_ACL : CREATE_ACL); + CREATE_TMP_ACL : CREATE_ACL) | + (select_lex->item_list.elements ? INSERT_ACL : 0); + if (check_access(thd, want_priv, create_table->db, &create_table->grant.privilege, 0, 0, test(create_table->schema_table)) || @@ -7997,7 +7448,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; @@ -8008,7 +7459,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). */ @@ -8026,7 +7477,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, } } #endif - if (tables && check_table_access(thd, SELECT_ACL, tables,0)) + if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) goto err; } else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) @@ -8041,15 +7492,13 @@ err: } -/* - negate given expression +/** + negate given expression. - SYNOPSIS - negate_expression() - thd thread handler - expr expression for negation + @param thd thread handler + @param expr expression for negation - RETURN + @return negated expression */ @@ -8076,14 +7525,12 @@ Item *negate_expression(THD *thd, Item *expr) return new Item_func_not(expr); } -/* - Set the specified definer to the default value, which is the current user in - the thread. +/** + Set the specified definer to the default value, which is the + current user in the thread. - SYNOPSIS - get_default_definer() - thd [in] thread handler - definer [out] definer + @param[in] thd thread handler + @param[out] definer definer */ void get_default_definer(THD *thd, LEX_USER *definer) @@ -8091,24 +7538,22 @@ void get_default_definer(THD *thd, LEX_USER *definer) const Security_context *sctx= thd->security_ctx; definer->user.str= (char *) sctx->priv_user; - definer->user.length= (uint) strlen(definer->user.str); + definer->user.length= strlen(definer->user.str); definer->host.str= (char *) sctx->priv_host; - definer->host.length= (uint) strlen(definer->host.str); + definer->host.length= strlen(definer->host.str); } -/* +/** Create default definer for the specified THD. - SYNOPSIS - create_default_definer() - thd [in] thread handler + @param[in] thd thread handler - RETURN - On success, return a valid pointer to the created and initialized + @return + - On success, return a valid pointer to the created and initialized LEX_USER, which contains definer information. - On error, return 0. + - On error, return 0. */ LEX_USER *create_default_definer(THD *thd) @@ -8124,19 +7569,17 @@ LEX_USER *create_default_definer(THD *thd) } -/* +/** Create definer with the given user and host names. - SYNOPSIS - create_definer() - thd [in] thread handler - user_name [in] user name - host_name [in] host name + @param[in] thd thread handler + @param[in] user_name user name + @param[in] host_name host name - RETURN - On success, return a valid pointer to the created and initialized + @return + - On success, return a valid pointer to the created and initialized LEX_USER, which contains definer information. - On error, return 0. + - On error, return 0. */ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name) @@ -8155,18 +7598,16 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name) } -/* +/** Retuns information about user or current user. - SYNOPSIS - get_current_user() - thd [in] thread handler - user [in] user + @param[in] thd thread handler + @param[in] user user - RETURN - On success, return a valid pointer to initialized + @return + - On success, return a valid pointer to initialized LEX_USER, which contains user information. - On error, return 0. + - On error, return 0. */ LEX_USER *get_current_user(THD *thd, LEX_USER *user) @@ -8178,36 +7619,70 @@ 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 + @param str string to be checked + @param err_msg error message to be displayed if the string is too long + @param max_length max length - RETURN + @retval FALSE the passed string is not longer than max_length + @retval 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 if path does not contain mysql data home directory + 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; +} + + +/* + Check if path does not contain mysql data home directory + SYNOPSIS test_if_data_home_dir() dir directory conv_home_dir converted data home directory @@ -8217,7 +7692,6 @@ bool check_string_length(LEX_STRING *str, const char *err_msg, 0 ok 1 error */ - C_MODE_START int test_if_data_home_dir(const char *dir) @@ -8271,7 +7745,7 @@ bool check_host_name(LEX_STRING *str) { const char *name= str->str; const char *end= str->str + str->length; - if (check_string_length(str, ER(ER_HOSTNAME), HOSTNAME_LENGTH)) + if (check_string_byte_length(str, ER(ER_HOSTNAME), HOSTNAME_LENGTH)) return TRUE; while (name != end) @@ -8287,3 +7761,64 @@ bool check_host_name(LEX_STRING *str) } return FALSE; } + + +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 parser_state Parser state. + @param creation_ctx Object creation context. + + @return Error status. + @retval FALSE on success. + @retval TRUE on parsing error. +*/ + +bool parse_sql(THD *thd, + Parser_state *parser_state, + Object_creation_ctx *creation_ctx) +{ + DBUG_ASSERT(thd->m_parser_state == NULL); + + /* Backup creation context. */ + + Object_creation_ctx *backup_ctx= NULL; + + if (creation_ctx) + backup_ctx= creation_ctx->set_n_backup(thd); + + /* Set parser state. */ + + thd->m_parser_state= parser_state; + + /* Parse the query. */ + + bool mysql_parse_status= MYSQLparse(thd) != 0; + + /* Check that if MYSQLparse() failed, thd->is_error() is set. */ + + DBUG_ASSERT(!mysql_parse_status || + (mysql_parse_status && thd->is_error())); + + /* Reset parser state. */ + + thd->m_parser_state= NULL; + + /* Restore creation context. */ + + if (creation_ctx) + creation_ctx->restore_env(thd, backup_ctx); + + /* That's it. */ + + return mysql_parse_status || thd->is_fatal_error; +} + +/** + @} (end of group Runtime_Environment) +*/ |