summaryrefslogtreecommitdiff
path: root/sql/sql_parse.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r--sql/sql_parse.cc5445
1 files changed, 3573 insertions, 1872 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 66b68cfc2f1..1b8bfd38fc4 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2,8 +2,7 @@
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
+ the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -14,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+#define MYSQL_LEX 1
#include "mysql_priv.h"
#include "sql_repl.h"
#include "repl_failsafe.h"
@@ -29,6 +29,11 @@
#include "ha_ndbcluster.h"
#endif
+#include "sp_head.h"
+#include "sp.h"
+#include "sp_cache.h"
+#include "sql_trigger.h"
+
#ifdef HAVE_OPENSSL
/*
Without SSL the handshake consists of one packet. This packet
@@ -47,25 +52,29 @@
#define MIN_HANDSHAKE_SIZE 6
#endif /* HAVE_OPENSSL */
+/* Used in error handling only */
+#define SP_TYPE_STRING(LP) \
+ ((LP)->sphead->m_type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE")
+#define SP_COM_STRING(LP) \
+ ((LP)->sql_command == SQLCOM_CREATE_SPFUNCTION || \
+ (LP)->sql_command == SQLCOM_ALTER_FUNCTION || \
+ (LP)->sql_command == SQLCOM_SHOW_CREATE_FUNC || \
+ (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
+ "FUNCTION" : "PROCEDURE")
+
#ifdef SOLARIS
extern "C" int gethostname(char *name, int namelen);
#endif
-static void time_out_user_resource_limits(THD *thd, USER_CONN *uc);
#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 bool check_multi_update_lock(THD *thd, TABLE_LIST *tables,
- List<Item> *fields, SELECT_LEX *select_lex);
static void remove_escape(char *name);
-static void refresh_status(void);
static bool append_file_to_dir(THD *thd, const char **filename_ptr,
const char *table_name);
-
-static TABLE_LIST* get_table_by_alias(TABLE_LIST* tl, const char* db,
- const char* alias);
const char *any_db="*any*"; // Special symbol for check_access
@@ -75,11 +84,13 @@ const char *command_name[]={
"Connect","Kill","Debug","Ping","Time","Delayed insert","Change user",
"Binlog Dump","Table Dump", "Connect Out", "Register Slave",
"Prepare", "Execute", "Long Data", "Close stmt",
- "Reset stmt", "Set option",
+ "Reset stmt", "Set option", "Fetch",
"Error" // Last command number
};
-static char empty_c_string[1]= {0}; // Used for not defined 'db'
+const char *xa_state_names[]={
+ "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED"
+};
#ifdef __WIN__
static void test_signal(int sig_ptr)
@@ -105,7 +116,7 @@ static void unlock_locked_tables(THD *thd)
if (thd->locked_tables)
{
thd->lock=thd->locked_tables;
- thd->locked_tables=0; // Will be automaticly closed
+ thd->locked_tables=0; // Will be automatically closed
close_thread_tables(thd); // Free tables
}
}
@@ -114,17 +125,60 @@ static void unlock_locked_tables(THD *thd)
static bool end_active_trans(THD *thd)
{
int error=0;
+ DBUG_ENTER("end_active_trans");
+ if (unlikely(thd->in_sub_stmt))
+ {
+ my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
+ DBUG_RETURN(1);
+ }
+ if (thd->transaction.xid_state.xa_state != XA_NOTR)
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0),
+ xa_state_names[thd->transaction.xid_state.xa_state]);
+ DBUG_RETURN(1);
+ }
if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN |
OPTION_TABLE_LOCK))
{
- thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
+ DBUG_PRINT("info",("options: 0x%lx", (ulong) thd->options));
+ /* Safety if one did "drop table" on locked tables */
+ if (!thd->locked_tables)
+ thd->options&= ~OPTION_TABLE_LOCK;
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
if (ha_commit(thd))
error=1;
+ thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
}
- return error;
+ DBUG_RETURN(error);
}
+static bool begin_trans(THD *thd)
+{
+ int error=0;
+ if (unlikely(thd->in_sub_stmt))
+ {
+ my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
+ return 1;
+ }
+ if (thd->locked_tables)
+ {
+ thd->lock=thd->locked_tables;
+ thd->locked_tables=0; // Will be automatically closed
+ close_thread_tables(thd); // Free tables
+ }
+ if (end_active_trans(thd))
+ error= -1;
+ else
+ {
+ LEX *lex= thd->lex;
+ thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
+ OPTION_BEGIN);
+ thd->server_status|= SERVER_STATUS_IN_TRANS;
+ if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
+ error= ha_start_consistent_snapshot(thd);
+ }
+ return error;
+}
#ifdef HAVE_REPLICATION
/*
@@ -132,11 +186,23 @@ static bool end_active_trans(THD *thd)
*/
inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables)
{
- return (table_rules_on && tables && !tables_ok(thd,tables));
+ return table_rules_on && tables && !tables_ok(thd,tables);
}
#endif
+static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables)
+{
+ for (TABLE_LIST *table= tables; table; table= table->next_global)
+ {
+ DBUG_ASSERT(table->db && table->table_name);
+ if (table->updating &&
+ !find_temporary_table(thd, table->db, table->table_name))
+ return 1;
+ }
+ return 0;
+}
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
static HASH hash_user_connections;
@@ -146,7 +212,7 @@ static int get_or_create_user_conn(THD *thd, const char *user,
{
int return_val= 0;
uint temp_len, user_len;
- char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2];
+ char temp_user[USER_HOST_BUFF_SIZE];
struct user_conn *uc;
DBUG_ASSERT(user != 0);
@@ -163,23 +229,21 @@ static int get_or_create_user_conn(THD *thd, const char *user,
my_malloc(sizeof(struct user_conn) + temp_len+1,
MYF(MY_WME)))))
{
- send_error(thd, 0, NullS); // Out of memory
+ net_send_error(thd, 0, NullS); // Out of memory
return_val= 1;
goto end;
}
uc->user=(char*) (uc+1);
memcpy(uc->user,temp_user,temp_len+1);
- uc->user_len= user_len;
- uc->host= uc->user + uc->user_len + 1;
+ uc->host= uc->user + user_len + 1;
uc->len= temp_len;
- uc->connections= 0;
- uc->questions= uc->updates= uc->conn_per_hour=0;
+ 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);
- send_error(thd, 0, NullS); // Out of memory
+ net_send_error(thd, 0, NullS); // Out of memory
return_val= 1;
goto end;
}
@@ -195,26 +259,27 @@ end:
/*
- Check if user exist and password supplied is correct.
+ Check if user exist and password supplied is correct.
+
SYNOPSIS
check_user()
- thd thread handle, thd->{host,user,ip} are used
+ 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 recieved from client
+ passwd scrambled password received from client
passwd_len length of scrambled password
db database name to connect to, may be NULL
check_count dont know exactly
Note, that host, user and passwd may point to communication buffer.
- Current implementation does not depened on that, but future changes
+ Current implementation does not depend on that, but future changes
should be done with this in mind; 'thd' is INOUT, all other params
are 'IN'.
RETURN VALUE
- 0 OK; thd->user, thd->master_access, thd->priv_user, thd->db and
- thd->db_access are updated; OK is sent to client;
+ 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
*/
@@ -226,17 +291,23 @@ int check_user(THD *thd, enum enum_server_command command,
DBUG_ENTER("check_user");
#ifdef NO_EMBEDDED_ACCESS_CHECKS
- thd->master_access= GLOBAL_ACLS; // Full rights
- /* Change database if necessary: OK or FAIL is sent in mysql_change_db */
+ thd->main_security_ctx.master_access= GLOBAL_ACLS; // Full rights
+ /* Change database if necessary */
if (db && db[0])
{
- thd->db= 0;
- thd->db_length= 0;
- if (mysql_change_db(thd, db))
+ /*
+ 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, FALSE))
+ {
+ /* Send the error to the client */
+ net_send_error(thd);
DBUG_RETURN(-1);
+ }
}
- else
- send_ok(thd);
+ send_ok(thd);
DBUG_RETURN(0);
#else
@@ -251,7 +322,7 @@ int check_user(THD *thd, enum enum_server_command command,
*/
if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323)
{
- net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE);
+ net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE);
mysql_log.write(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
DBUG_RETURN(-1);
}
@@ -262,13 +333,12 @@ int check_user(THD *thd, enum enum_server_command command,
/*
Clear thd->db as it points to something, that will be freed when
- connection is closed. We don't want to accidently free a wrong pointer
+ connection is closed. We don't want to accidentally free a wrong pointer
if connect failed. Also in case of 'CHANGE USER' failure, current
database will be switched to 'no database selected'.
*/
- thd->db= 0;
- thd->db_length= 0;
-
+ thd->reset_db(NULL, 0);
+
USER_RESOURCES ur;
int res= acl_getroot(thd, &ur, passwd, passwd_len);
#ifndef EMBEDDED_LIBRARY
@@ -283,15 +353,18 @@ int check_user(THD *thd, enum enum_server_command command,
NET *net= &thd->net;
if (opt_secure_auth_local)
{
- net_printf(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE,
- thd->user, thd->host_or_ip);
+ net_printf_error(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE,
+ thd->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->user, thd->host_or_ip);
+ 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) // We have to read very
- { // specific packet size
+ my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
+ {
inc_host_errors(&thd->remote.sin_addr);
DBUG_RETURN(ER_HANDSHAKE_ERROR);
}
@@ -303,36 +376,43 @@ int check_user(THD *thd, enum enum_server_command command,
/* here res is always >= 0 */
if (res == 0)
{
- if (!(thd->master_access & NO_ACCESS)) // authentification is OK
+ if (!(thd->main_security_ctx.master_access &
+ NO_ACCESS)) // authentication is OK
{
DBUG_PRINT("info",
- ("Capabilities: %d packet_length: %ld Host: '%s' "
+ ("Capabilities: %lu packet_length: %ld Host: '%s' "
"Login user: '%s' Priv_user: '%s' Using password: %s "
- "Access: %u db: '%s'",
- thd->client_capabilities, thd->max_client_packet_length,
- thd->host_or_ip, thd->user, thd->priv_user,
+ "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->master_access, thd->db ? thd->db : "*none*"));
+ 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->master_access & SUPER_ACL);
+ 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
- send_error(thd, ER_CON_COUNT_ERROR);
+ { // 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->priv_user == thd->user ?
+ 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->user, thd->host_or_ip,
+ thd->main_security_ctx.user,
+ thd->main_security_ctx.host_or_ip,
db ? db : (char*) "");
/*
@@ -340,31 +420,38 @@ int check_user(THD *thd, enum enum_server_command command,
set to 0 here because we don't have an active database yet (and we
may not have an active database to set.
*/
- thd->db_access=0;
+ 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.connections ||
+ if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn ||
max_user_connections) &&
- get_or_create_user_conn(thd,thd->user,thd->host_or_ip,&ur))
+ get_or_create_user_conn(thd,
+ (opt_old_style_user_limits ? thd->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.connections ||
+ (thd->user_connect->user_resources.conn_per_hour ||
+ thd->user_connect->user_resources.user_conn ||
max_user_connections) &&
check_for_max_user_connections(thd, thd->user_connect))
DBUG_RETURN(-1);
- /* Change database if necessary: OK or FAIL is sent in mysql_change_db */
+ /* Change database if necessary */
if (db && db[0])
{
- if (mysql_change_db(thd, db))
+ if (mysql_change_db(thd, db, FALSE))
{
+ /* Send error to the client */
+ net_send_error(thd);
if (thd->user_connect)
decrease_user_connections(thd->user_connect);
DBUG_RETURN(-1);
}
}
- else
- send_ok(thd);
+ send_ok(thd);
thd->password= test(passwd_len); // remember for error messages
/* Ready to handle queries */
DBUG_RETURN(0);
@@ -372,17 +459,17 @@ int check_user(THD *thd, enum enum_server_command command,
}
else if (res == 2) // client gave short hash, server has long hash
{
- net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE);
+ net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE);
mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE));
DBUG_RETURN(-1);
}
- net_printf(thd, ER_ACCESS_DENIED_ERROR,
- thd->user,
- thd->host_or_ip,
- passwd_len ? ER(ER_YES) : ER(ER_NO));
+ net_printf_error(thd, ER_ACCESS_DENIED_ERROR,
+ thd->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->user,
- thd->host_or_ip,
+ 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 */
@@ -441,20 +528,29 @@ static int check_for_max_user_connections(THD *thd, USER_CONN *uc)
DBUG_ENTER("check_for_max_user_connections");
(void) pthread_mutex_lock(&LOCK_user_conn);
- if (max_user_connections &&
+ if (max_user_connections && !uc->user_resources.user_conn &&
max_user_connections < (uint) uc->connections)
{
- net_printf(thd,ER_TOO_MANY_USER_CONNECTIONS, uc->user);
+ net_printf_error(thd, ER_TOO_MANY_USER_CONNECTIONS, uc->user);
error=1;
goto end;
}
time_out_user_resource_limits(thd, uc);
- if (uc->user_resources.connections &&
- uc->user_resources.connections <= uc->conn_per_hour)
+ if (uc->user_resources.user_conn &&
+ uc->user_resources.user_conn < uc->connections)
{
- net_printf(thd, ER_USER_LIMIT_REACHED, uc->user,
- "max_connections_per_hour",
- (long) uc->user_resources.connections);
+ net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user,
+ "max_user_connections",
+ (long) uc->user_resources.user_conn);
+ error= 1;
+ goto end;
+ }
+ if (uc->user_resources.conn_per_hour &&
+ uc->user_resources.conn_per_hour <= uc->conn_per_hour)
+ {
+ net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user,
+ "max_connections_per_hour",
+ (long) uc->user_resources.conn_per_hour);
error=1;
goto end;
}
@@ -517,6 +613,12 @@ void free_max_user_conn(void)
sql_command is actually set to SQLCOM_END sometimes
so we need the +1 to include it in the array.
+
+ numbers are:
+ 0 - read-only query
+ != 0 - query that may change a table
+ 2 - query that returns meaningful ROW_COUNT() -
+ a number of modified rows
*/
char uc_update_queries[SQLCOM_END+1];
@@ -528,29 +630,31 @@ void init_update_queries(void)
uc_update_queries[SQLCOM_CREATE_TABLE]=1;
uc_update_queries[SQLCOM_CREATE_INDEX]=1;
uc_update_queries[SQLCOM_ALTER_TABLE]=1;
- uc_update_queries[SQLCOM_UPDATE]=1;
- uc_update_queries[SQLCOM_INSERT]=1;
- uc_update_queries[SQLCOM_INSERT_SELECT]=1;
- uc_update_queries[SQLCOM_DELETE]=1;
+ uc_update_queries[SQLCOM_UPDATE]=2;
+ uc_update_queries[SQLCOM_UPDATE_MULTI]=2;
+ uc_update_queries[SQLCOM_INSERT]=2;
+ uc_update_queries[SQLCOM_INSERT_SELECT]=2;
+ uc_update_queries[SQLCOM_DELETE]=2;
+ uc_update_queries[SQLCOM_DELETE_MULTI]=2;
uc_update_queries[SQLCOM_TRUNCATE]=1;
uc_update_queries[SQLCOM_DROP_TABLE]=1;
uc_update_queries[SQLCOM_LOAD]=1;
uc_update_queries[SQLCOM_CREATE_DB]=1;
uc_update_queries[SQLCOM_DROP_DB]=1;
- uc_update_queries[SQLCOM_REPLACE]=1;
- uc_update_queries[SQLCOM_REPLACE_SELECT]=1;
+ uc_update_queries[SQLCOM_REPLACE]=2;
+ uc_update_queries[SQLCOM_REPLACE_SELECT]=2;
uc_update_queries[SQLCOM_RENAME_TABLE]=1;
uc_update_queries[SQLCOM_BACKUP_TABLE]=1;
uc_update_queries[SQLCOM_RESTORE_TABLE]=1;
- uc_update_queries[SQLCOM_DELETE_MULTI]=1;
uc_update_queries[SQLCOM_DROP_INDEX]=1;
- uc_update_queries[SQLCOM_UPDATE_MULTI]=1;
+ uc_update_queries[SQLCOM_CREATE_VIEW]=1;
+ uc_update_queries[SQLCOM_DROP_VIEW]=1;
}
bool is_update_query(enum enum_sql_command command)
{
DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
- return uc_update_queries[command];
+ return uc_update_queries[command] != 0;
}
/*
@@ -567,6 +671,8 @@ bool is_update_query(enum enum_sql_command command)
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);
@@ -584,7 +690,6 @@ static void time_out_user_resource_limits(THD *thd, USER_CONN *uc)
DBUG_VOID_RETURN;
}
-
/*
Check if maximum queries per hour limit has been reached
returns 0 if OK.
@@ -592,7 +697,6 @@ static void time_out_user_resource_limits(THD *thd, USER_CONN *uc)
static bool check_mqh(THD *thd, uint check_command)
{
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
bool error= 0;
USER_CONN *uc=thd->user_connect;
DBUG_ENTER("check_mqh");
@@ -606,8 +710,8 @@ static bool check_mqh(THD *thd, uint check_command)
if (uc->user_resources.questions &&
uc->questions++ >= uc->user_resources.questions)
{
- net_printf(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions",
- (long) uc->user_resources.questions);
+ net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions",
+ (long) uc->user_resources.questions);
error=1;
goto end;
}
@@ -617,8 +721,8 @@ static bool check_mqh(THD *thd, uint check_command)
if (uc->user_resources.updates && uc_update_queries[check_command] &&
uc->updates++ >= uc->user_resources.updates)
{
- net_printf(thd, ER_USER_LIMIT_REACHED, uc->user, "max_updates",
- (long) uc->user_resources.updates);
+ net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_updates",
+ (long) uc->user_resources.updates);
error=1;
goto end;
}
@@ -626,13 +730,12 @@ static bool check_mqh(THD *thd, uint check_command)
end:
(void) pthread_mutex_unlock(&LOCK_user_conn);
DBUG_RETURN(error);
-#else
- return (0);
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
}
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+
-static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0)
+static void reset_mqh(LEX_USER *lu, bool get_them= 0)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
(void) pthread_mutex_lock(&LOCK_user_conn);
@@ -640,7 +743,7 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0)
{
USER_CONN *uc;
uint temp_len=lu->user.length+lu->host.length+2;
- char temp_user[USERNAME_LENGTH+HOSTNAME_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);
@@ -654,8 +757,9 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0)
uc->conn_per_hour=0;
}
}
- else // for FLUSH PRIVILEGES and FLUSH USER_RESOURCES
+ else
{
+ /* for FLUSH PRIVILEGES and FLUSH USER_RESOURCES */
for (uint idx=0;idx < hash_user_connections.records; idx++)
{
USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections,
@@ -728,41 +832,45 @@ static int check_connection(THD *thd)
thd->set_active_vio(net->vio);
#endif
- if (!thd->host) // If TCP/IP connection
+ 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->ip= my_strdup(ip,MYF(0))))
+ if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(0))))
return (ER_OUT_OF_RESOURCES);
- thd->host_or_ip= thd->ip;
+ 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->host=ip_to_hostname(&thd->remote.sin_addr,&connect_errors);
+ thd->main_security_ctx.host=
+ ip_to_hostname(&thd->remote.sin_addr, &connect_errors);
/* Cut very long hostnames to avoid possible overflows */
- if (thd->host)
+ if (thd->main_security_ctx.host)
{
- if (thd->host != my_localhost)
- thd->host[min(strlen(thd->host), HOSTNAME_LENGTH)]= 0;
- thd->host_or_ip= thd->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->host ? thd->host : "unknown host",
- thd->ip ? thd->ip : "unknown ip"));
- if (acl_check_host(thd->host,thd->ip))
+ (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->host));
- thd->host_or_ip= thd->host;
- thd->ip= 0;
+ 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));
}
@@ -780,7 +888,7 @@ static int check_connection(THD *thd)
#endif /* HAVE_COMPRESS */
#ifdef HAVE_OPENSSL
if (ssl_acceptor_fd)
- client_flags |= CLIENT_SSL; /* Wow, SSL is avalaible! */
+ client_flags |= CLIENT_SSL; /* Wow, SSL is available! */
#endif /* HAVE_OPENSSL */
end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1;
@@ -828,17 +936,6 @@ static int check_connection(THD *thd)
return(ER_OUT_OF_RESOURCES);
thd->client_capabilities=uint2korr(net->read_pos);
-#ifdef TO_BE_REMOVED_IN_4_1_RELEASE
- /*
- This is just a safety check against any client that would use the old
- CLIENT_CHANGE_USER flag
- */
- if ((thd->client_capabilities & CLIENT_PROTOCOL_41) &&
- !(thd->client_capabilities & (CLIENT_RESERVED |
- CLIENT_SECURE_CONNECTION |
- CLIENT_MULTI_RESULTS)))
- thd->client_capabilities&= ~CLIENT_PROTOCOL_41;
-#endif
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
{
thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
@@ -857,7 +954,7 @@ static int check_connection(THD *thd)
if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
thd->variables.sql_mode|= MODE_IGNORE_SPACE;
#ifdef HAVE_OPENSSL
- DBUG_PRINT("info", ("client capabilities: %d", thd->client_capabilities));
+ DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities));
if (thd->client_capabilities & CLIENT_SSL)
{
/* Do the SSL layering. */
@@ -869,8 +966,7 @@ static int check_connection(THD *thd)
DBUG_PRINT("info", ("IO layer change in progress..."));
if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout))
{
- DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
- pkt_len));
+ DBUG_PRINT("error", ("Failed to accept new SSL connection"));
inc_host_errors(&thd->remote.sin_addr);
return(ER_HANDSHAKE_ERROR);
}
@@ -900,6 +996,7 @@ static int check_connection(THD *thd)
char *user= end;
char *passwd= strend(user)+1;
+ uint user_len= passwd - user - 1;
char *db= passwd;
char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
@@ -932,14 +1029,22 @@ static int check_connection(THD *thd)
db= db_buff;
}
- user_buff[copy_and_convert(user_buff, sizeof(user_buff)-1,
- system_charset_info, user, strlen(user),
- thd->charset(), &dummy_errors)]= '\0';
+ user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1,
+ system_charset_info, user, user_len,
+ thd->charset(), &dummy_errors)]= '\0';
user= user_buff;
- if (thd->user)
- x_free(thd->user);
- if (!(thd->user= my_strdup(user, MYF(0))))
+ /* 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);
}
@@ -968,6 +1073,7 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var,
*/
save_vio= thd->net.vio;
thd->net.vio= 0;
+ thd->net.no_send_error= 0;
dispatch_command(COM_QUERY, thd, thd->query, thd->query_length+1);
rw_unlock(var_mutex);
thd->client_capabilities= save_client_capabilities;
@@ -975,7 +1081,7 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var,
}
-pthread_handler_decl(handle_one_connection,arg)
+pthread_handler_t handle_one_connection(void *arg)
{
THD *thd=(THD*) arg;
uint launch_time =
@@ -986,7 +1092,7 @@ pthread_handler_decl(handle_one_connection,arg)
pthread_detach_this_thread();
#if !defined( __WIN__) && !defined(OS2) // Win32 calls this in pthread_create
- // The following calls needs to be done before we call DBUG_ macros
+ /* The following calls needs to be done before we call DBUG_ macros */
if (!(test_flags & TEST_NO_THREADS) & my_thread_init())
{
close_connection(thd, ER_OUT_OF_RESOURCES, 1);
@@ -1003,17 +1109,18 @@ pthread_handler_decl(handle_one_connection,arg)
of handle_one_connection, which is thd. We need to know the
start of the stack so that we could check for stack overruns.
*/
- DBUG_PRINT("info", ("handle_one_connection called by thread %d\n",
+ 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_*
+ /* now that we've called my_thread_init(), it is safe to call DBUG_* */
#if defined(__WIN__)
- init_signals(); // IRENA; testing ?
+ init_signals();
#elif !defined(OS2) && !defined(__NETWARE__)
sigset_t set;
VOID(sigemptyset(&set)); // Get mask in use
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);
@@ -1026,7 +1133,8 @@ pthread_handler_decl(handle_one_connection,arg)
{
int error;
NET *net= &thd->net;
- thd->thread_stack= (char*) &thd;
+ Security_context *sctx= thd->security_ctx;
+ net->no_send_error= 0;
/* Use "connect_timeout" value during connection phase */
net_set_read_timeout(net, connect_timeout);
@@ -1035,7 +1143,7 @@ pthread_handler_decl(handle_one_connection,arg)
if ((error=check_connection(thd)))
{ // Wrong permissions
if (error > 0)
- net_printf(thd,error,thd->host_or_ip);
+ 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() */
@@ -1044,7 +1152,7 @@ pthread_handler_decl(handle_one_connection,arg)
goto end_thread;
}
#ifdef __NETWARE__
- netware_reg_user(thd->ip, thd->user, "MySQL");
+ netware_reg_user(sctx->ip, sctx->user, "MySQL");
#endif
if (thd->variables.max_join_size == HA_POS_ERROR)
thd->options |= OPTION_BIG_SELECTS;
@@ -1053,37 +1161,48 @@ pthread_handler_decl(handle_one_connection,arg)
thd->version= refresh_version;
thd->proc_info= 0;
- thd->set_time();
+ thd->command= COM_SLEEP;
thd->init_for_queries();
- if (sys_init_connect.value_length && !(thd->master_access & SUPER_ACL))
+
+ 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= 1;
+ {
+ thd->killed= THD::KILL_CONNECTION;
+ sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
+ thd->thread_id,(thd->db ? thd->db : "unconnected"),
+ sctx->user ? sctx->user : "unauthenticated",
+ sctx->host_or_ip, "init_connect command failed");
+ sql_print_warning("%s", net->last_error);
+ }
+ thd->proc_info=0;
+ thd->init_for_queries();
}
/* Connect completed, set read/write timeouts back to tdefault */
net_set_read_timeout(net, thd->variables.net_read_timeout);
net_set_write_timeout(net, thd->variables.net_write_timeout);
- while (!net->error && net->vio != 0 && !thd->killed)
+ while (!net->error && net->vio != 0 &&
+ !(thd->killed == THD::KILL_CONNECTION))
{
+ net->no_send_error= 0;
if (do_command(thd))
break;
}
if (thd->user_connect)
decrease_user_connections(thd->user_connect);
- free_root(thd->mem_root,MYF(0));
if (net->error && net->vio != 0 && net->report_error)
{
if (!thd->killed && thd->variables.log_warnings > 1)
- sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
+ sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
thd->thread_id,(thd->db ? thd->db : "unconnected"),
- thd->user ? thd->user : "unauthenticated",
- thd->host_or_ip,
+ sctx->user ? sctx->user : "unauthenticated",
+ sctx->host_or_ip,
(net->last_errno ? ER(net->last_errno) :
ER(ER_UNKNOWN_ERROR)));
- send_error(thd,net->last_errno,NullS);
+ net_send_error(thd, net->last_errno, NullS);
statistic_increment(aborted_threads,&LOCK_status);
}
else if (thd->killed)
@@ -1099,6 +1218,7 @@ end_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 */
@@ -1111,13 +1231,14 @@ end_thread:
Used when creating the initial grant tables
*/
-extern "C" pthread_handler_decl(handle_bootstrap,arg)
+pthread_handler_t handle_bootstrap(void *arg)
{
THD *thd=(THD*) arg;
FILE *file=bootstrap_file;
char *buff;
/* The following must be called before DBUG_ENTER */
+ thd->thread_stack= (char*) &thd;
if (my_thread_init() || thd->store_globals())
{
#ifndef EMBEDDED_LIBRARY
@@ -1143,57 +1264,82 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg)
thd->proc_info=0;
thd->version=refresh_version;
- thd->priv_user=thd->user=(char*) my_strdup("boot", MYF(MY_WME));
+ thd->security_ctx->priv_user=
+ thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
+ /*
+ Make the "client" handle multiple results. This is necessary
+ to enable stored procedures with SELECTs and Dynamic SQL
+ in init-file.
+ */
+ thd->client_capabilities|= CLIENT_MULTI_RESULTS;
buff= (char*) thd->net.buff;
thd->init_for_queries();
while (fgets(buff, thd->net.max_packet, file))
{
- ulong length= (ulong) strlen(buff);
- while (buff[length-1] != '\n' && !feof(file))
- {
- /*
- We got only a part of the current string. Will try to increase
- net buffer then read the rest of the current string.
- */
- if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
- {
- send_error(thd, thd->net.last_errno, NullS);
- thd->is_fatal_error= 1;
- break;
- }
- buff= (char*) thd->net.buff;
- fgets(buff + length, thd->net.max_packet - length, file);
- length+= (ulong) strlen(buff + length);
- }
- if (thd->is_fatal_error)
- break;
+ ulong length= (ulong) strlen(buff);
+ while (buff[length-1] != '\n' && !feof(file))
+ {
+ /*
+ We got only a part of the current string. Will try to increase
+ net buffer then read the rest of the current string.
+ */
+ if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
+ {
+ net_send_error(thd, ER_NET_PACKET_TOO_LARGE, NullS);
+ thd->fatal_error();
+ break;
+ }
+ buff= (char*) thd->net.buff;
+ fgets(buff + length, thd->net.max_packet - length, file);
+ length+= (ulong) strlen(buff + length);
+ }
+ if (thd->is_fatal_error)
+ break;
+
while (length && (my_isspace(thd->charset(), buff[length-1]) ||
buff[length-1] == ';'))
length--;
buff[length]=0;
thd->query_length=length;
- thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1);
+ thd->query= thd->memdup_w_gap(buff, length+1,
+ thd->db_length+1+QUERY_CACHE_FLAGS_SIZE);
thd->query[length] = '\0';
- thd->query_id=query_id++;
- if (mqh_used && thd->user_connect && check_mqh(thd, SQLCOM_END))
- {
- thd->net.error = 0;
- close_thread_tables(thd); // Free tables
- free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
- break;
- }
+ DBUG_PRINT("query",("%-.4096s",thd->query));
+ /*
+ We don't need to obtain LOCK_thread_count here because in bootstrap
+ mode we have only one thread.
+ */
+ thd->query_id=next_query_id();
thd->set_time();
mysql_parse(thd,thd->query,length);
close_thread_tables(thd); // Free tables
+
if (thd->is_fatal_error)
break;
+
+ if (thd->net.report_error)
+ {
+ /* The query failed, send error to log and abort bootstrap */
+ net_send_error(thd);
+ thd->fatal_error();
+ break;
+ }
+
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
+#ifdef USING_TRANSACTIONS
free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC));
+#endif
}
- /* thd->fatal_error should be set in case something went wrong */
end:
+ /* Remember the exit code of bootstrap */
+ bootstrap_error= thd->is_fatal_error;
+
+ net_end(&thd->net);
+ thd->cleanup();
+ delete thd;
+
#ifndef EMBEDDED_LIBRARY
(void) pthread_mutex_lock(&LOCK_thread_count);
thread_count--;
@@ -1202,26 +1348,40 @@ end:
my_thread_end();
pthread_exit(0);
#endif
- DBUG_RETURN(0); // Never reached
+ DBUG_RETURN(0);
}
- /* This works because items are allocated with sql_alloc() */
-
-void free_items(Item *item)
-{
- for (; item ; item=item->next)
- item->delete_self();
-}
/* This works because items are allocated with sql_alloc() */
void cleanup_items(Item *item)
{
+ DBUG_ENTER("cleanup_items");
for (; item ; item=item->next)
item->cleanup();
+ DBUG_VOID_RETURN;
}
-int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd)
+/*
+ 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
+
+ NOTES
+ This function is written to handle one specific command only.
+
+ RETURN VALUE
+ 0 success
+ 1 error, the error message is set in THD
+*/
+
+static
+int mysql_table_dump(THD* thd, char* db, char* tbl_name)
{
TABLE* table;
TABLE_LIST* table_list;
@@ -1230,19 +1390,19 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd)
db = (db && db[0]) ? db : thd->db;
if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST))))
DBUG_RETURN(1); // out of memory
- table_list->db = db;
- table_list->real_name = table_list->alias = tbl_name;
- table_list->lock_type = TL_READ_NO_INSERT;
- table_list->next = 0;
+ table_list->db= db;
+ table_list->table_name= table_list->alias= tbl_name;
+ table_list->lock_type= TL_READ_NO_INSERT;
+ table_list->prev_global= &table_list; // can be removed after merge with 4.1
if (!db || check_db_name(db))
{
- net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
+ my_error(ER_WRONG_DB_NAME ,MYF(0), db ? db : "NULL");
goto err;
}
if (lower_case_table_names)
my_casedn_str(files_charset_info, tbl_name);
- remove_escape(table_list->real_name);
+ remove_escape(table_list->table_name);
if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT)))
DBUG_RETURN(1);
@@ -1252,20 +1412,92 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd)
thd->free_list = 0;
thd->query_length=(uint) strlen(tbl_name);
thd->query = tbl_name;
- if ((error = mysqld_dump_create_info(thd, table, -1)))
+ if ((error = mysqld_dump_create_info(thd, table_list, -1)))
{
my_error(ER_GET_ERRNO, MYF(0), my_errno);
goto err;
}
net_flush(&thd->net);
- if ((error= table->file->dump(thd,fd)))
+ if ((error= table->file->dump(thd,-1)))
my_error(ER_GET_ERRNO, MYF(0), error);
err:
- close_thread_tables(thd);
DBUG_RETURN(error);
}
+/*
+ Ends the current transaction and (maybe) begin the next
+
+ SYNOPSIS
+ end_trans()
+ thd Current thread
+ completion Completion type
+
+ RETURN
+ 0 - OK
+*/
+
+int end_trans(THD *thd, enum enum_mysql_completiontype completion)
+{
+ bool do_release= 0;
+ int res= 0;
+ DBUG_ENTER("end_trans");
+
+ if (unlikely(thd->in_sub_stmt))
+ {
+ my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
+ DBUG_RETURN(1);
+ }
+ if (thd->transaction.xid_state.xa_state != XA_NOTR)
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0),
+ xa_state_names[thd->transaction.xid_state.xa_state]);
+ DBUG_RETURN(1);
+ }
+ switch (completion) {
+ case COMMIT:
+ /*
+ We don't use end_active_trans() here to ensure that this works
+ even if there is a problem with the OPTION_AUTO_COMMIT flag
+ (Which of course should never happen...)
+ */
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ res= ha_commit(thd);
+ thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
+ break;
+ case COMMIT_RELEASE:
+ do_release= 1; /* fall through */
+ case COMMIT_AND_CHAIN:
+ res= end_active_trans(thd);
+ if (!res && completion == COMMIT_AND_CHAIN)
+ res= begin_trans(thd);
+ break;
+ case ROLLBACK_RELEASE:
+ do_release= 1; /* fall through */
+ case ROLLBACK:
+ case ROLLBACK_AND_CHAIN:
+ {
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ if (ha_rollback(thd))
+ res= -1;
+ thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
+ if (!res && (completion == ROLLBACK_AND_CHAIN))
+ res= begin_trans(thd);
+ break;
+ }
+ default:
+ res= -1;
+ my_error(ER_UNKNOWN_COM_ERROR, MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ if (res < 0)
+ my_error(thd->killed_errno(), MYF(0));
+ else if ((res == 0) && do_release)
+ thd->killed= THD::KILL_CONNECTION;
+
+ DBUG_RETURN(res);
+}
#ifndef EMBEDDED_LIBRARY
@@ -1315,7 +1547,7 @@ bool do_command(THD *thd)
statistic_increment(aborted_threads,&LOCK_status);
DBUG_RETURN(TRUE); // We have to close it.
}
- send_error(thd,net->last_errno,NullS);
+ net_send_error(thd, net->last_errno, NullS);
net->error= 0;
DBUG_RETURN(FALSE);
}
@@ -1346,8 +1578,10 @@ bool do_command(THD *thd)
}
#endif /* EMBEDDED_LIBRARY */
+
/*
Perform one connection-level (COM_XXXX) command.
+
SYNOPSIS
dispatch_command()
thd connection handle
@@ -1369,17 +1603,24 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
bool error= 0;
DBUG_ENTER("dispatch_command");
+ if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA)
+ {
+ thd->killed= THD::NOT_KILLED;
+ thd->mysys_var->abort= 0;
+ }
+
thd->command=command;
/*
Commands which always take a long time are logged into
the slow log only if opt_log_slow_admin_statements is set.
*/
thd->enable_slow_log= TRUE;
+ thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
thd->set_time();
VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->query_id=query_id;
+ thd->query_id= global_query_id;
if (command != COM_STATISTICS && command != COM_PING)
- query_id++;
+ next_query_id();
thread_running++;
/* TODO: set thd->lex->sql_command to SQLCOM_END here */
VOID(pthread_mutex_unlock(&LOCK_thread_count));
@@ -1390,11 +1631,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
case COM_INIT_DB:
{
LEX_STRING tmp;
- statistic_increment(com_stat[SQLCOM_CHANGE_DB],&LOCK_status);
+ statistic_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB],
+ &LOCK_status);
thd->convert_string(&tmp, system_charset_info,
packet, strlen(packet), thd->charset());
- if (!mysql_change_db(thd, tmp.str))
+ if (!mysql_change_db(thd, tmp.str, FALSE))
+ {
mysql_log.write(thd,command,"%s",thd->db);
+ send_ok(thd);
+ }
break;
}
#ifdef HAVE_REPLICATION
@@ -1411,23 +1656,27 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
uint db_len= *(uchar*) packet;
if (db_len >= packet_length || db_len > NAME_LEN)
{
- send_error(thd, ER_UNKNOWN_COM_ERROR);
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
break;
}
uint tbl_len= *(uchar*) (packet + db_len + 1);
if (db_len+tbl_len+2 > packet_length || tbl_len > NAME_LEN)
{
- send_error(thd, ER_UNKNOWN_COM_ERROR);
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
break;
}
- statistic_increment(com_other, &LOCK_status);
+ statistic_increment(thd->status_var.com_other, &LOCK_status);
thd->enable_slow_log= opt_log_slow_admin_statements;
db= thd->alloc(db_len + tbl_len + 2);
+ if (!db)
+ {
+ my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
+ break;
+ }
tbl_name= strmake(db, packet + 1, db_len)+1;
strmake(tbl_name, packet + db_len + 2, tbl_len);
- if (mysql_table_dump(thd, db, tbl_name, -1))
- send_error(thd); // dump to NET
+ mysql_table_dump(thd, db, tbl_name);
break;
}
case COM_CHANGE_USER:
@@ -1435,24 +1684,24 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->change_user();
thd->clear_error(); // if errors from rollback
- statistic_increment(com_other, &LOCK_status);
+ statistic_increment(thd->status_var.com_other, &LOCK_status);
char *user= (char*) packet;
char *passwd= strend(user)+1;
- /*
+ /*
Old clients send null-terminated string ('\0' for empty string) for
password. New clients send the size (1 byte) + string (not null
terminated, so also '\0' for empty string).
*/
- char db_buff[NAME_LEN+1]; // buffer to store db in utf8
+ char db_buff[NAME_LEN+1]; // buffer to store db in utf8
char *db= passwd;
- uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
+ uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
*passwd++ : strlen(passwd);
db+= passwd_len + 1;
#ifndef EMBEDDED_LIBRARY
- /* Small check for incomming packet */
+ /* Small check for incoming packet */
if ((uint) ((uchar*) db - net->read_pos) > packet_length)
{
- send_error(thd, ER_UNKNOWN_COM_ERROR);
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
break;
}
#endif
@@ -1464,18 +1713,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
db= db_buff;
/* Save user and privileges */
- uint save_master_access= thd->master_access;
- uint save_db_access= thd->db_access;
uint save_db_length= thd->db_length;
- char *save_user= thd->user;
- char *save_priv_user= thd->priv_user;
char *save_db= thd->db;
+ Security_context save_security_ctx= *thd->security_ctx;
USER_CONN *save_user_connect= thd->user_connect;
-
- if (!(thd->user= my_strdup(user, MYF(0))))
+
+ if (!(thd->security_ctx->user= my_strdup(user, MYF(0))))
{
- thd->user= save_user;
- send_error(thd, ER_OUT_OF_RESOURCES);
+ thd->security_ctx->user= save_security_ctx.user;
+ my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
break;
}
@@ -1485,15 +1731,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (res)
{
- /* authentification failure, we shall restore old user */
+ /* authentication failure, we shall restore old user */
if (res > 0)
- send_error(thd, ER_UNKNOWN_COM_ERROR);
- x_free(thd->user);
- thd->user= save_user;
- thd->priv_user= save_priv_user;
+ 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;
- thd->master_access= save_master_access;
- thd->db_access= save_db_access;
thd->db= save_db;
thd->db_length= save_db_length;
}
@@ -1505,31 +1748,36 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
decrease_user_connections(save_user_connect);
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
x_free((gptr) save_db);
- x_free((gptr) save_user);
+ x_free((gptr) save_security_ctx.user);
}
break;
}
- case COM_EXECUTE:
+ case COM_STMT_EXECUTE:
{
mysql_stmt_execute(thd, packet, packet_length);
break;
}
- case COM_LONG_DATA:
+ case COM_STMT_FETCH:
+ {
+ mysql_stmt_fetch(thd, packet, packet_length);
+ break;
+ }
+ case COM_STMT_SEND_LONG_DATA:
{
mysql_stmt_get_longdata(thd, packet, packet_length);
break;
}
- case COM_PREPARE:
+ case COM_STMT_PREPARE:
{
mysql_stmt_prepare(thd, packet, packet_length);
break;
}
- case COM_CLOSE_STMT:
+ case COM_STMT_CLOSE:
{
- mysql_stmt_free(thd, packet);
+ mysql_stmt_close(thd, packet);
break;
}
- case COM_RESET_STMT:
+ case COM_STMT_RESET:
{
mysql_stmt_reset(thd, packet);
break;
@@ -1539,57 +1787,44 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (alloc_query(thd, packet, packet_length))
break; // fatal error is set
char *packet_end= thd->query + thd->query_length;
- mysql_log.write(thd,command,"%s",thd->query);
+ /* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */
+ const char *format= "%.*b";
+ mysql_log.write(thd,command, format, thd->query_length, thd->query);
DBUG_PRINT("query",("%-.4096s",thd->query));
+
+ if (!(specialflag & SPECIAL_NO_PRIOR))
+ my_pthread_setprio(pthread_self(),QUERY_PRIOR);
+
mysql_parse(thd,thd->query, thd->query_length);
- while (!thd->killed && !thd->is_fatal_error && thd->lex->found_colon)
+ while (!thd->killed && thd->lex->found_semicolon && !thd->net.report_error)
{
- char *packet= thd->lex->found_colon;
+ char *next_packet= thd->lex->found_semicolon;
+ net->no_send_error= 0;
/*
Multiple queries exits, execute them individually
- in embedded server - just store them to be executed later
*/
-#ifndef EMBEDDED_LIBRARY
- if (thd->lock || thd->open_tables || thd->derived_tables)
+ if (thd->lock || thd->open_tables || thd->derived_tables ||
+ thd->prelocked_mode)
close_thread_tables(thd);
-#endif
- ulong length= (ulong)(packet_end-packet);
+ ulong length= (ulong)(packet_end - next_packet);
log_slow_statement(thd);
/* Remove garbage at start of query */
- while (my_isspace(thd->charset(), *packet) && length > 0)
+ while (my_isspace(thd->charset(), *next_packet) && length > 0)
{
- packet++;
+ next_packet++;
length--;
}
VOID(pthread_mutex_lock(&LOCK_thread_count));
thd->query_length= length;
- thd->query= packet;
- thd->query_id= query_id++;
+ thd->query= next_packet;
+ 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));
-#ifndef EMBEDDED_LIBRARY
- mysql_parse(thd, packet, length);
-#else
- /*
- 'packet' can point inside the query_rest's buffer
- so we have to do memmove here
- */
- if (thd->query_rest.length() > length)
- {
- memmove(thd->query_rest.c_ptr(), packet, length);
- thd->query_rest.length(length);
- }
- else
- thd->query_rest.copy(packet, length, thd->query_rest.charset());
-
- thd->server_status&= ~ (SERVER_QUERY_NO_INDEX_USED |
- SERVER_QUERY_NO_GOOD_INDEX_USED);
- break;
-#endif /*EMBEDDED_LIBRARY*/
+ mysql_parse(thd, next_packet, length);
}
if (!(specialflag & SPECIAL_NO_PRIOR))
@@ -1599,44 +1834,65 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
case COM_FIELD_LIST: // This isn't actually needed
#ifdef DONT_ALLOW_SHOW_COMMANDS
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
+ my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
+ MYF(0)); /* purecov: inspected */
break;
#else
{
char *fields, *pend;
+ /* Locked closure of all tables */
TABLE_LIST table_list;
LEX_STRING conv_name;
- statistic_increment(com_stat[SQLCOM_SHOW_FIELDS],&LOCK_status);
+ /* used as fields initializator */
+ lex_start(thd, 0, 0);
+
+ statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS],
+ &LOCK_status);
bzero((char*) &table_list,sizeof(table_list));
- if (!(table_list.db=thd->db))
- {
- send_error(thd,ER_NO_DB_ERROR);
+ if (thd->copy_db_to(&table_list.db, 0))
break;
- }
- thd->free_list=0;
pend= strend(packet);
thd->convert_string(&conv_name, system_charset_info,
packet, (uint) (pend-packet), thd->charset());
- table_list.alias= table_list.real_name= conv_name.str;
+ table_list.alias= table_list.table_name= conv_name.str;
packet= pend+1;
+
+ if (!my_strcasecmp(system_charset_info, table_list.db,
+ information_schema_name.str))
+ {
+ ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, table_list.alias);
+ if (schema_table)
+ table_list.schema_table= schema_table;
+ }
+
thd->query_length= strlen(packet); // for simplicity: don't optimize
if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1)))
break;
- mysql_log.write(thd,command,"%s %s",table_list.real_name,fields);
+ mysql_log.write(thd,command,"%s %s",table_list.table_name, fields);
if (lower_case_table_names)
- my_casedn_str(files_charset_info, table_list.real_name);
- remove_escape(table_list.real_name); // This can't have wildcards
+ my_casedn_str(files_charset_info, table_list.table_name);
+ remove_escape(table_list.table_name); // This can't have wildcards
if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege,
- 0, 0))
+ 0, 0, test(table_list.schema_table)))
break;
if (grant_option &&
check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0))
break;
+ /* init structures for VIEW processing */
+ table_list.select_lex= &(thd->lex->select_lex);
+ mysql_init_query(thd, (uchar*)"", 0);
+ thd->lex->
+ select_lex.table_list.link_in_list((byte*) &table_list,
+ (byte**) &table_list.next_local);
+ thd->lex->add_to_query_tables(&table_list);
+
+ /* switch on VIEW optimisation: do not fill temporary tables */
+ thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
mysqld_list_fields(thd,&table_list,fields);
- free_items(thd->free_list);
- thd->free_list= 0;
+ thd->lex->unit.cleanup();
+ thd->cleanup_after_query();
break;
}
#endif
@@ -1652,43 +1908,43 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
char *db=thd->strdup(packet), *alias;
HA_CREATE_INFO create_info;
- statistic_increment(com_stat[SQLCOM_CREATE_DB],&LOCK_status);
+ statistic_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB],
+ &LOCK_status);
// null test to handle EOM
if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
{
- net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
+ my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL");
break;
}
- if (check_access(thd,CREATE_ACL,db,0,1,0))
+ if (check_access(thd,CREATE_ACL,db,0,1,0,is_schema_db(db)))
break;
mysql_log.write(thd,command,packet);
bzero(&create_info, sizeof(create_info));
- if (mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db),
- &create_info, 0) < 0)
- send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
+ mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db),
+ &create_info, 0);
break;
}
case COM_DROP_DB: // QQ: To be removed
{
- statistic_increment(com_stat[SQLCOM_DROP_DB],&LOCK_status);
- char *db=thd->strdup(packet), *alias;
- // null test to handle EOM
- if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
+ statistic_increment(thd->status_var.com_stat[SQLCOM_DROP_DB],
+ &LOCK_status);
+ char *db=thd->strdup(packet);
+ /* null test to handle EOM */
+ if (!db || check_db_name(db))
{
- net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
+ my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL");
break;
}
- if (check_access(thd,DROP_ACL,db,0,1,0))
+ if (check_access(thd,DROP_ACL,db,0,1,0,is_schema_db(db)))
break;
if (thd->locked_tables || thd->active_transaction())
{
- send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
+ my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
+ ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
break;
}
mysql_log.write(thd,command,db);
- if (mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : db),
- 0, 0) < 0)
- send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
+ mysql_rm_db(thd, db, 0, 0);
break;
}
#ifndef EMBEDDED_LIBRARY
@@ -1698,7 +1954,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
ushort flags;
uint32 slave_server_id;
- statistic_increment(com_other,&LOCK_status);
+ statistic_increment(thd->status_var.com_other,&LOCK_status);
thd->enable_slow_log= opt_log_slow_admin_statements;
if (check_global_access(thd, REPL_SLAVE_ACL))
break;
@@ -1715,29 +1971,29 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
(long) pos);
mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
unregister_slave(thd,1,1);
- // fake COM_QUIT -- if we get here, the thread needs to terminate
+ /* fake COM_QUIT -- if we get here, the thread needs to terminate */
error = TRUE;
net->error = 0;
break;
}
#endif
case COM_REFRESH:
- {
- statistic_increment(com_stat[SQLCOM_FLUSH],&LOCK_status);
- ulong options= (ulong) (uchar) packet[0];
- if (check_global_access(thd,RELOAD_ACL))
- break;
- mysql_log.write(thd,command,NullS);
- if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, NULL))
- send_error(thd, 0);
- else
- send_ok(thd);
+ {
+ bool not_used;
+ statistic_increment(thd->status_var.com_stat[SQLCOM_FLUSH],
+ &LOCK_status);
+ ulong options= (ulong) (uchar) packet[0];
+ if (check_global_access(thd,RELOAD_ACL))
break;
- }
+ mysql_log.write(thd,command,NullS);
+ if (!reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
+ send_ok(thd);
+ break;
+ }
#ifndef EMBEDDED_LIBRARY
case COM_SHUTDOWN:
{
- statistic_increment(com_other,&LOCK_status);
+ statistic_increment(thd->status_var.com_other, &LOCK_status);
if (check_global_access(thd,SHUTDOWN_ACL))
break; /* purecov: inspected */
/*
@@ -1754,7 +2010,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
else if (level != SHUTDOWN_WAIT_ALL_BUFFERS)
{
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "this shutdown level");
- send_error(thd);
break;
}
DBUG_PRINT("quit",("Got shutdown command for level %u", level));
@@ -1768,8 +2023,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#endif
close_connection(thd, 0, 1);
close_thread_tables(thd); // Free before kill
- free_root(thd->mem_root,MYF(0));
- free_root(&thd->transaction.mem_root,MYF(0));
kill_mysql();
error=TRUE;
break;
@@ -1778,19 +2031,26 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
case COM_STATISTICS:
{
mysql_log.write(thd,command,NullS);
- statistic_increment(com_stat[SQLCOM_SHOW_STATUS],&LOCK_status);
+ statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS],
+ &LOCK_status);
#ifndef EMBEDDED_LIBRARY
char buff[200];
#else
char *buff= thd->net.last_error;
#endif
- ulong uptime = (ulong) (thd->start_time - start_time);
+
+ STATUS_VAR current_global_status_var;
+ calc_sum_of_all_status(&current_global_status_var);
+
+ ulong uptime = (ulong) (thd->start_time - server_start_time);
sprintf((char*) buff,
- "Uptime: %ld Threads: %d Questions: %lu Slow queries: %ld Opens: %ld Flush tables: %ld Open tables: %u Queries per second avg: %.3f",
+ "Uptime: %lu Threads: %d Questions: %lu Slow queries: %lu Opens: %lu Flush tables: %lu Open tables: %u Queries per second avg: %.3f",
uptime,
- (int) thread_count,thd->query_id,long_query_count,
- opened_tables,refresh_version, cached_tables(),
- uptime ? (float)thd->query_id/(float)uptime : 0);
+ (int) thread_count, (ulong) thd->query_id,
+ 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));
#ifdef SAFEMALLOC
if (sf_malloc_cur_memory) // Using SAFEMALLOC
sprintf(strend(buff), " Memory in use: %ldK Max memory used: %ldK",
@@ -1804,49 +2064,53 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
}
case COM_PING:
- statistic_increment(com_other,&LOCK_status);
+ statistic_increment(thd->status_var.com_other, &LOCK_status);
send_ok(thd); // Tell client we are alive
break;
case COM_PROCESS_INFO:
- statistic_increment(com_stat[SQLCOM_SHOW_PROCESSLIST],&LOCK_status);
- if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL))
+ statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST],
+ &LOCK_status);
+ if (!thd->security_ctx->priv_user[0] &&
+ check_global_access(thd, PROCESS_ACL))
break;
mysql_log.write(thd,command,NullS);
mysqld_list_processes(thd,
- thd->master_access & PROCESS_ACL ?
- NullS : thd->priv_user, 0);
+ thd->security_ctx->master_access & PROCESS_ACL ?
+ NullS : thd->security_ctx->priv_user, 0);
break;
case COM_PROCESS_KILL:
{
- statistic_increment(com_stat[SQLCOM_KILL],&LOCK_status);
+ statistic_increment(thd->status_var.com_stat[SQLCOM_KILL], &LOCK_status);
ulong id=(ulong) uint4korr(packet);
- kill_one_thread(thd,id);
+ kill_one_thread(thd,id,false);
break;
}
case COM_SET_OPTION:
{
- statistic_increment(com_stat[SQLCOM_SET_OPTION], &LOCK_status);
- enum_mysql_set_option command= (enum_mysql_set_option) uint2korr(packet);
- switch (command) {
- case MYSQL_OPTION_MULTI_STATEMENTS_ON:
+ statistic_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION],
+ &LOCK_status);
+ uint opt_command= uint2korr(packet);
+
+ switch (opt_command) {
+ case (int) MYSQL_OPTION_MULTI_STATEMENTS_ON:
thd->client_capabilities|= CLIENT_MULTI_STATEMENTS;
send_eof(thd);
break;
- case MYSQL_OPTION_MULTI_STATEMENTS_OFF:
+ case (int) MYSQL_OPTION_MULTI_STATEMENTS_OFF:
thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS;
send_eof(thd);
break;
default:
- send_error(thd, ER_UNKNOWN_COM_ERROR);
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
break;
}
break;
}
case COM_DEBUG:
- statistic_increment(com_other,&LOCK_status);
+ statistic_increment(thd->status_var.com_other, &LOCK_status);
if (check_global_access(thd, SUPER_ACL))
break; /* purecov: inspected */
- mysql_print_status(thd);
+ mysql_print_status();
mysql_log.write(thd,command,NullS);
send_eof(thd);
break;
@@ -1856,17 +2120,32 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
case COM_DELAYED_INSERT:
case COM_END:
default:
- send_error(thd, ER_UNKNOWN_COM_ERROR);
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
break;
}
- if (thd->lock || thd->open_tables || thd->derived_tables)
+ if (thd->lock || thd->open_tables || thd->derived_tables ||
+ thd->prelocked_mode)
{
thd->proc_info="closing tables";
close_thread_tables(thd); /* Free tables */
}
+ /*
+ assume handlers auto-commit (if some doesn't - transaction handling
+ in MySQL should be redesigned to support it; it's a big change,
+ and it's not worth it - better to commit explicitly only writing
+ transactions, read-only ones should better take care of themselves.
+ saves some work in 2pc too)
+ see also sql_base.cc - close_thread_tables()
+ */
+ bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt));
+ if (!thd->active_transaction())
+ thd->transaction.xid_state.xid.null();
- if (thd->is_fatal_error)
- send_error(thd,0); // End of memory ?
+ /* report error issued during command execution */
+ if (thd->killed_errno() && !thd->net.report_error)
+ thd->send_kill_message();
+ if (thd->net.report_error)
+ net_send_error(thd);
log_slow_statement(thd);
@@ -1886,7 +2165,17 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
void log_slow_statement(THD *thd)
{
- time_t start_of_query=thd->start_time;
+ time_t start_of_query;
+
+ /*
+ The following should never be true with our current code base,
+ but better to keep this here so we don't accidently try to log a
+ 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
/*
@@ -1899,11 +2188,13 @@ void log_slow_statement(THD *thd)
if ((ulong) (thd->start_time - thd->time_after_lock) >
thd->variables.long_query_time ||
- ((thd->server_status &
+ (thd->server_status &
(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
- (specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES)))
+ (specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES) &&
+ /* == SQLCOM_END unless this is a SHOW command */
+ thd->lex->orig_sql_command == SQLCOM_END)
{
- long_query_count++;
+ thd->status_var.long_query_count++;
mysql_slow_log.write(thd, thd->query, thd->query_length, start_of_query);
}
}
@@ -1911,8 +2202,152 @@ void log_slow_statement(THD *thd)
/*
+ 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
+ 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
+ 0 success
+ 1 out of memory or SHOW commands are not allowed
+ in this version of the server.
+*/
+
+int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
+ enum enum_schema_tables schema_table_idx)
+{
+ DBUG_ENTER("prepare_schema_table");
+ SELECT_LEX *sel= 0;
+ switch (schema_table_idx) {
+ case SCH_SCHEMATA:
+#if defined(DONT_ALLOW_SHOW_COMMANDS)
+ my_message(ER_NOT_ALLOWED_COMMAND,
+ ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
+ DBUG_RETURN(1);
+#else
+ if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
+ check_global_access(thd, SHOW_DB_ACL))
+ DBUG_RETURN(1);
+ break;
+#endif
+ case SCH_TABLE_NAMES:
+ case SCH_TABLES:
+ case SCH_VIEWS:
+ case SCH_TRIGGERS:
+#ifdef DONT_ALLOW_SHOW_COMMANDS
+ my_message(ER_NOT_ALLOWED_COMMAND,
+ ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
+ DBUG_RETURN(1);
+#else
+ {
+ char *db;
+ if (lex->select_lex.db == NULL &&
+ thd->copy_db_to(&lex->select_lex.db, 0))
+ {
+ DBUG_RETURN(1);
+ }
+ db= lex->select_lex.db;
+ remove_escape(db); // Fix escaped '_'
+ if (check_db_name(db))
+ {
+ my_error(ER_WRONG_DB_NAME, MYF(0), db);
+ DBUG_RETURN(1);
+ }
+ if (check_access(thd, SELECT_ACL, db, &thd->col_access, 0, 0,
+ is_schema_db(db)))
+ DBUG_RETURN(1); /* purecov: inspected */
+ if (!thd->col_access && check_grant_db(thd,db))
+ {
+ my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
+ thd->security_ctx->priv_user, thd->security_ctx->priv_host,
+ db);
+ DBUG_RETURN(1);
+ }
+ break;
+ }
+#endif
+ case SCH_COLUMNS:
+ case SCH_STATISTICS:
+#ifdef DONT_ALLOW_SHOW_COMMANDS
+ my_message(ER_NOT_ALLOWED_COMMAND,
+ ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
+ DBUG_RETURN(1);
+#else
+ if (table_ident)
+ {
+ TABLE_LIST **query_tables_last= lex->query_tables_last;
+ sel= new SELECT_LEX();
+ /* 'parent_lex' is used in init_query() so it must be before it. */
+ sel->parent_lex= lex;
+ sel->init_query();
+ if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ,
+ (List<String> *) 0, (List<String> *) 0))
+ DBUG_RETURN(1);
+ lex->query_tables_last= query_tables_last;
+ TABLE_LIST *table_list= (TABLE_LIST*) sel->table_list.first;
+ char *db= table_list->db;
+ remove_escape(db); // Fix escaped '_'
+ remove_escape(table_list->table_name);
+ if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
+ &table_list->grant.privilege, 0, 0,
+ test(table_list->schema_table)))
+ DBUG_RETURN(1); /* purecov: inspected */
+ if (grant_option && check_grant(thd, SELECT_ACL, table_list, 2,
+ UINT_MAX, 0))
+ DBUG_RETURN(1);
+ break;
+ }
+#endif
+ case SCH_OPEN_TABLES:
+ case SCH_VARIABLES:
+ case SCH_STATUS:
+ case SCH_PROCEDURES:
+ case SCH_CHARSETS:
+ case SCH_COLLATIONS:
+ case SCH_COLLATION_CHARACTER_SET_APPLICABILITY:
+ case SCH_USER_PRIVILEGES:
+ case SCH_SCHEMA_PRIVILEGES:
+ case SCH_TABLE_PRIVILEGES:
+ case SCH_COLUMN_PRIVILEGES:
+ case SCH_TABLE_CONSTRAINTS:
+ case SCH_KEY_COLUMN_USAGE:
+ default:
+ break;
+ }
+
+ SELECT_LEX *select_lex= lex->current_select;
+ if (make_schema_select(thd, select_lex, schema_table_idx))
+ {
+ DBUG_RETURN(1);
+ }
+ TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first;
+ table_list->schema_select_lex= sel;
+ table_list->schema_table_reformed= 1;
+ 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_PREPARE
+ Used in COM_QUERY and COM_STMT_PREPARE
DESCRIPTION
Sets the following THD variables:
@@ -1920,11 +2355,11 @@ void log_slow_statement(THD *thd)
query_length
RETURN VALUES
- 0 ok
- 1 error; In this case thd->fatal_error is set
+ FALSE ok
+ TRUE error; In this case thd->fatal_error is set
*/
-bool alloc_query(THD *thd, char *packet, ulong packet_length)
+bool alloc_query(THD *thd, const char *packet, uint packet_length)
{
packet_length--; // Remove end null
/* Remove garbage at start and end of query */
@@ -1933,7 +2368,7 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length)
packet++;
packet_length--;
}
- char *pos=packet+packet_length; // Point at end null
+ const char *pos= packet + packet_length; // Point at end null
while (packet_length > 0 &&
(pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1])))
{
@@ -1946,7 +2381,7 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length)
packet_length,
thd->db_length+ 1 +
QUERY_CACHE_FLAGS_SIZE)))
- return 1;
+ return TRUE;
thd->query[packet_length]=0;
thd->query_length= packet_length;
@@ -1954,9 +2389,7 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length)
thd->packet.shrink(thd->variables.net_buffer_length);
thd->convert_buffer.shrink(thd->variables.net_buffer_length);
- if (!(specialflag & SPECIAL_NO_PRIOR))
- my_pthread_setprio(pthread_self(),QUERY_PRIOR);
- return 0;
+ return FALSE;
}
static void reset_one_shot_variables(THD *thd)
@@ -1977,50 +2410,125 @@ static void reset_one_shot_variables(THD *thd)
}
-/****************************************************************************
-** mysql_execute_command
-** Execute command saved in thd and current_lex->sql_command
-****************************************************************************/
+/*
+ Execute command saved in thd and lex->sql_command
-void
+ SYNOPSIS
+ mysql_execute_command()
+ thd Thread handle
+
+ IMPLEMENTATION
+
+ 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
+ thread has locked tables already. No new locks can be requested
+ until the other locks are released. The thread that requests the
+ global read lock waits for write locked tables to become unlocked.
+
+ Note that wait_if_global_read_lock() sets a protection against a new
+ global read lock when it succeeds. This needs to be released by
+ start_waiting_global_read_lock() after the operation.
+
+ RETURN
+ FALSE OK
+ TRUE Error
+*/
+
+bool
mysql_execute_command(THD *thd)
{
- int res= 0;
- LEX *lex= thd->lex;
+ bool res= FALSE;
+ bool need_start_waiting= FALSE; // have protection against global read lock
+ int up_result= 0;
+ LEX *lex= thd->lex;
+ /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
SELECT_LEX *select_lex= &lex->select_lex;
- TABLE_LIST *tables= (TABLE_LIST*) select_lex->table_list.first;
+ /* first table of first SELECT_LEX */
+ TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first;
+ /* list of all tables in query */
+ TABLE_LIST *all_tables;
+ /* most outer SELECT_LEX_UNIT of query */
SELECT_LEX_UNIT *unit= &lex->unit;
+ /* Saved variable value */
DBUG_ENTER("mysql_execute_command");
+ thd->net.no_send_error= 0;
/*
Remember first generated insert id value of the previous
- statement.
+ 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;
/*
+ In many cases first table of main SELECT_LEX have special meaning =>
+ check that it is first table in global list and relink it first in
+ queries_tables list if it is necessary (we need such relinking only
+ for queries with subqueries in select list, in this case tables of
+ subqueries will go to global list first)
+
+ all_tables will differ from first_table only if most upper SELECT_LEX
+ do not contain tables.
+
+ Because of above in place where should be at least one table in most
+ outer SELECT_LEX we have following check:
+ DBUG_ASSERT(first_table == all_tables);
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ */
+ lex->first_lists_tables_same();
+ /* should be assigned after making first tables same */
+ all_tables= lex->query_tables;
+ /* set context for commands which do not use setup_tables */
+ select_lex->
+ context.resolve_in_table_list_only((TABLE_LIST*)select_lex->
+ table_list.first);
+
+ /*
Reset warning count for each query that uses tables
A better approach would be to reset this for any commands
that is not a SHOW command or a select that only access local
variables, but for now this is probably good enough.
+ Don't reset warnings when executing a stored routine.
*/
- if (tables || &lex->select_lex != lex->all_selects_list ||
+ if ((all_tables || &lex->select_lex != lex->all_selects_list ||
+ lex->sroutines.records) && !thd->spcont ||
lex->time_zone_tables_used)
- mysql_reset_errors(thd);
-
- /*
- When subselects or time_zone info is used in a query
- we create a new TABLE_LIST containing all referenced tables
- and set local variable 'tables' to point to this list.
- */
- if ((&lex->select_lex != lex->all_selects_list ||
- lex->time_zone_tables_used) &&
- lex->unit.create_total_list(thd, lex, &tables))
- DBUG_VOID_RETURN;
+ mysql_reset_errors(thd, 0);
#ifdef HAVE_REPLICATION
- if (thd->slave_thread)
+ if (unlikely(thd->slave_thread))
{
+ if (lex->sql_command == SQLCOM_DROP_TRIGGER)
+ {
+ /*
+ When dropping a trigger, we need to load its table name
+ before checking slave filter rules.
+ */
+ add_table_for_trigger(thd, thd->lex->spname, 1, &all_tables);
+
+ if (!all_tables)
+ {
+ /*
+ If table name cannot be loaded,
+ it means the trigger does not exists possibly because
+ CREATE TRIGGER was previously skipped for this trigger
+ according to slave filtering rules.
+ Returning success without producing any errors in this case.
+ */
+ DBUG_RETURN(0);
+ }
+
+ // force searching in slave.cc:tables_ok()
+ all_tables->updating= 1;
+ }
+
/*
Check if statment should be skipped because of slave filtering
rules
@@ -2038,41 +2546,56 @@ mysql_execute_command(THD *thd)
!(lex->sql_command == SQLCOM_SET_OPTION) &&
!(lex->sql_command == SQLCOM_DROP_TABLE &&
lex->drop_temporary && lex->drop_if_exists) &&
- all_tables_not_ok(thd,tables))
+ all_tables_not_ok(thd, all_tables))
{
/* we warn the slave SQL thread */
- my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
- reset_one_shot_variables(thd);
- DBUG_VOID_RETURN;
+ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
+ if (thd->one_shot_set)
+ {
+ /*
+ It's ok to check thd->one_shot_set here:
+
+ The charsets in a MySQL 5.0 slave can change by both a binlogged
+ SET ONE_SHOT statement and the event-internal charset setting,
+ and these two ways to change charsets do not seems to work
+ together.
+
+ At least there seems to be problems in the rli cache for
+ charsets if we are using ONE_SHOT. Note that this is normally no
+ problem because either the >= 5.0 slave reads a 4.1 binlog (with
+ ONE_SHOT) *or* or 5.0 binlog (without ONE_SHOT) but never both."
+ */
+ reset_one_shot_variables(thd);
+ }
+ DBUG_RETURN(0);
}
-#ifndef TO_BE_DELETED
+ }
+ else
+ {
+#endif /* HAVE_REPLICATION */
/*
- This is a workaround to deal with the shortcoming in 3.23.44-3.23.46
- masters in RELEASE_LOCK() logging. We re-write SELECT RELEASE_LOCK()
- as DO RELEASE_LOCK()
+ When option readonly is set deny operations which change non-temporary
+ tables. Except for the replication thread and the 'super' users.
*/
- if (lex->sql_command == SQLCOM_SELECT)
- {
- lex->sql_command = SQLCOM_DO;
- lex->insert_list = &select_lex->item_list;
+ if (opt_readonly &&
+ !(thd->security_ctx->master_access & SUPER_ACL) &&
+ uc_update_queries[lex->sql_command] &&
+ !((lex->sql_command == SQLCOM_CREATE_TABLE) &&
+ (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) &&
+ !((lex->sql_command == SQLCOM_DROP_TABLE) && lex->drop_temporary) &&
+ ((lex->sql_command != SQLCOM_UPDATE_MULTI) &&
+ some_non_temp_table_to_be_updated(thd, all_tables)))
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
+ DBUG_RETURN(-1);
}
+#ifdef HAVE_REPLICATION
+ } /* endif unlikely slave */
#endif
- }
-#endif /* HAVE_REPLICATION */
+ if(lex->orig_sql_command == SQLCOM_END)
+ statistic_increment(thd->status_var.com_stat[lex->sql_command],
+ &LOCK_status);
- /*
- When option readonly is set deny operations which change tables.
- Except for the replication thread and the 'super' users.
- */
- if (opt_readonly &&
- !(thd->slave_thread || (thd->master_access & SUPER_ACL)) &&
- (uc_update_queries[lex->sql_command] > 0))
- {
- net_printf(thd, ER_OPTION_PREVENTS_STATEMENT, "--read-only");
- DBUG_VOID_RETURN;
- }
-
- statistic_increment(com_stat[lex->sql_command],&LOCK_status);
switch (lex->sql_command) {
case SQLCOM_SELECT:
{
@@ -2080,50 +2603,42 @@ mysql_execute_command(THD *thd)
{
SELECT_LEX *param= lex->unit.global_parameters;
if (!param->explicit_limit)
- param->select_limit= thd->variables.select_limit;
+ param->select_limit=
+ new Item_int((ulonglong)thd->variables.select_limit);
}
- select_result *result=lex->result;
- if (tables)
+ select_result *sel_result=lex->result;
+ if (all_tables)
{
- res=check_table_access(thd,
- lex->exchange ? SELECT_ACL | FILE_ACL :
- SELECT_ACL,
- tables,0);
+ 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);
}
else
- res=check_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
- any_db,0,0,0);
+ res= check_access(thd,
+ lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
+ any_db, 0, 0, 0, 0);
if (res)
- {
- res=0;
- break; // Error message is given
- }
- /*
- In case of single SELECT unit->global_parameters points on first SELECT
- TODO: move counters to SELECT_LEX
- */
- unit->offset_limit_cnt= (ha_rows) unit->global_parameters->offset_limit;
- unit->select_limit_cnt= (ha_rows) (unit->global_parameters->select_limit+
- unit->global_parameters->offset_limit);
- if (unit->select_limit_cnt <
- (ha_rows) unit->global_parameters->select_limit)
- unit->select_limit_cnt= HA_POS_ERROR; // no limit
- if (unit->select_limit_cnt == HA_POS_ERROR && !select_lex->next_select())
- select_lex->options&= ~OPTION_FOUND_ROWS;
+ goto error;
- if (!(res=open_and_lock_tables(thd,tables)))
+ if (!(res= open_and_lock_tables(thd, all_tables)))
{
if (lex->describe)
{
- if (!(result= new select_send()))
- {
- send_error(thd, ER_OUT_OF_RESOURCES);
- DBUG_VOID_RETURN;
- }
+ /*
+ 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(result);
- res= mysql_explain_union(thd, &thd->lex->unit, result);
+ thd->send_explain_fields(sel_result);
+ res= mysql_explain_union(thd, &thd->lex->unit, sel_result);
if (lex->describe & DESCRIBE_EXTENDED)
{
char buff[1024];
@@ -2134,147 +2649,42 @@ mysql_execute_command(THD *thd)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_YES, str.ptr());
}
- result->send_eof();
- delete result;
+ sel_result->send_eof();
+ delete sel_result;
}
else
{
- if (!result && !(result= new select_send()))
- {
- res= -1;
- break;
- }
- query_cache_store_query(thd, tables);
- res= handle_select(thd, lex, result);
- if (result != lex->result)
- delete result;
+ 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;
}
}
break;
}
case SQLCOM_PREPARE:
{
- char *query_str;
- uint query_len;
- if (lex->prepared_stmt_code_is_varref)
- {
- /* This is PREPARE stmt FROM @var. */
- String str;
- CHARSET_INFO *to_cs= thd->variables.collation_connection;
- bool need_conversion;
- user_var_entry *entry;
- String *pstr= &str;
- uint32 unused;
- /*
- Convert @var contents to string in connection character set. Although
- it is known that int/real/NULL value cannot be a valid query we still
- convert it for error messages to uniform.
- */
- if ((entry=
- (user_var_entry*)hash_search(&thd->user_vars,
- (byte*)lex->prepared_stmt_code.str,
- lex->prepared_stmt_code.length))
- && entry->value)
- {
- my_bool is_var_null;
- pstr= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC);
- /*
- NULL value of variable checked early as entry->value so here
- we can't get NULL in normal conditions
- */
- DBUG_ASSERT(!is_var_null);
- if (!pstr)
- {
- res= -1;
- break; // EOM (error should be reported by allocator)
- }
- }
- else
- {
- /*
- variable absent or equal to NULL, so we need to set variable to
- something reasonable to get readable error message during parsing
- */
- str.set("NULL", 4, &my_charset_latin1);
- }
-
- need_conversion=
- String::needs_conversion(pstr->length(), pstr->charset(),
- to_cs, &unused);
-
- query_len= need_conversion? (pstr->length() * to_cs->mbmaxlen) :
- pstr->length();
- if (!(query_str= alloc_root(thd->mem_root, query_len+1)))
- {
- res= -1;
- break; // EOM (error should be reported by allocator)
- }
-
- if (need_conversion)
- {
- uint dummy_errors;
- query_len= copy_and_convert(query_str, query_len, to_cs,
- pstr->ptr(), pstr->length(),
- pstr->charset(), &dummy_errors);
- }
- else
- memcpy(query_str, pstr->ptr(), pstr->length());
- query_str[query_len]= 0;
- }
- else
- {
- query_str= lex->prepared_stmt_code.str;
- query_len= lex->prepared_stmt_code.length;
- DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n",
- lex->prepared_stmt_name.length,
- lex->prepared_stmt_name.str,
- query_len, query_str));
- }
- thd->command= COM_PREPARE;
- if (!mysql_stmt_prepare(thd, query_str, query_len + 1,
- &lex->prepared_stmt_name))
- send_ok(thd, 0L, 0L, "Statement prepared");
+ mysql_sql_stmt_prepare(thd);
break;
}
case SQLCOM_EXECUTE:
{
- DBUG_PRINT("info", ("EXECUTE: %.*s\n",
- lex->prepared_stmt_name.length,
- lex->prepared_stmt_name.str));
- mysql_sql_stmt_execute(thd, &lex->prepared_stmt_name);
- lex->prepared_stmt_params.empty();
+ mysql_sql_stmt_execute(thd);
break;
}
case SQLCOM_DEALLOCATE_PREPARE:
{
- Statement* stmt;
- DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n",
- lex->prepared_stmt_name.length,
- lex->prepared_stmt_name.str));
- /* We account deallocate in the same manner as mysql_stmt_close */
- statistic_increment(com_stmt_close, &LOCK_status);
- if ((stmt= thd->stmt_map.find_by_name(&lex->prepared_stmt_name)))
- {
- thd->stmt_map.erase(stmt);
- send_ok(thd);
- }
- else
- {
- res= -1;
- my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0),
- lex->prepared_stmt_name.length, lex->prepared_stmt_name.str,
- "DEALLOCATE PREPARE");
- }
+ mysql_sql_stmt_close(thd);
break;
}
case SQLCOM_DO:
- if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
- (res= open_and_lock_tables(thd,tables))))
- break;
+ if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+ open_and_lock_tables(thd, all_tables))
+ goto error;
res= mysql_do(thd, *lex->insert_list);
- if (thd->net.report_error)
- res= -1;
break;
case SQLCOM_EMPTY_QUERY:
@@ -2290,16 +2700,31 @@ mysql_execute_command(THD *thd)
{
if (check_global_access(thd, SUPER_ACL))
goto error;
- // PURGE MASTER LOGS TO 'file'
+ /* PURGE MASTER LOGS TO 'file' */
res = purge_master_logs(thd, lex->to_log);
break;
}
case SQLCOM_PURGE_BEFORE:
{
+ Item *it;
+
if (check_global_access(thd, SUPER_ACL))
goto error;
- // PURGE MASTER LOGS BEFORE 'data'
- res = purge_master_logs_before_date(thd, lex->purge_time);
+ /* PURGE MASTER LOGS BEFORE 'data' */
+ it= (Item *)lex->value_list.head();
+ if ((!it->fixed && it->fix_fields(lex->thd, &it)) ||
+ it->check_cols(1))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "PURGE LOGS BEFORE");
+ goto error;
+ }
+ it= new Item_func_unix_timestamp(it);
+ /*
+ it is OK only emulate fix_fieds, because we need only
+ value of constant
+ */
+ it->quick_fix_field();
+ res = purge_master_logs_before_date(thd, (ulong)it->val_int());
break;
}
#endif
@@ -2324,12 +2749,12 @@ mysql_execute_command(THD *thd)
goto error;
/* This query don't work now. See comment in repl_failsafe.cc */
#ifndef WORKING_NEW_MASTER
- net_printf(thd, ER_NOT_SUPPORTED_YET, "SHOW NEW MASTER");
- res= 1;
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "SHOW NEW MASTER");
+ goto error;
#else
res = show_new_master(thd);
-#endif
break;
+#endif
}
#ifdef HAVE_REPLICATION
@@ -2344,48 +2769,57 @@ mysql_execute_command(THD *thd)
{
if (check_global_access(thd, REPL_SLAVE_ACL))
goto error;
- res = show_binlog_events(thd);
+ res = mysql_show_binlog_events(thd);
break;
}
#endif
case SQLCOM_BACKUP_TABLE:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL, tables,0) ||
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL, all_tables, 0) ||
check_global_access(thd, FILE_ACL))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
- res = mysql_backup_table(thd, tables);
-
+ res = mysql_backup_table(thd, first_table);
+ select_lex->table_list.first= (byte*) first_table;
+ lex->query_tables=all_tables;
break;
}
case SQLCOM_RESTORE_TABLE:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd, INSERT_ACL, tables,0) ||
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, INSERT_ACL, all_tables, 0) ||
check_global_access(thd, FILE_ACL))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
- res = mysql_restore_table(thd, tables);
+ res = mysql_restore_table(thd, first_table);
+ select_lex->table_list.first= (byte*) first_table;
+ lex->query_tables=all_tables;
break;
}
case SQLCOM_ASSIGN_TO_KEYCACHE:
{
- if (check_db_used(thd, tables) ||
- check_access(thd, INDEX_ACL, tables->db,
- &tables->grant.privilege, 0, 0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_access(thd, INDEX_ACL, first_table->db,
+ &first_table->grant.privilege, 0, 0,
+ test(first_table->schema_table)))
goto error;
- res= mysql_assign_to_keycache(thd, tables, &lex->name_and_length);
+ res= mysql_assign_to_keycache(thd, first_table, &lex->ident);
break;
}
case SQLCOM_PRELOAD_KEYS:
{
- if (check_db_used(thd, tables) ||
- check_access(thd, INDEX_ACL, tables->db,
- &tables->grant.privilege, 0, 0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_access(thd, INDEX_ACL, first_table->db,
+ &first_table->grant.privilege, 0, 0,
+ test(first_table->schema_table)))
goto error;
- res = mysql_preload_keys(thd, tables);
+ res = mysql_preload_keys(thd, first_table);
break;
}
#ifdef HAVE_REPLICATION
@@ -2421,7 +2855,7 @@ mysql_execute_command(THD *thd)
if (check_global_access(thd, SUPER_ACL))
goto error;
if (end_active_trans(thd))
- res= -1;
+ goto error;
else
res = load_master_data(thd);
break;
@@ -2441,23 +2875,33 @@ mysql_execute_command(THD *thd)
res = innodb_show_status(thd);
break;
}
+ case SQLCOM_SHOW_MUTEX_STATUS:
+ {
+ if (check_global_access(thd, SUPER_ACL))
+ goto error;
+ res = innodb_mutex_show_status(thd);
+ break;
+ }
#endif
#ifdef HAVE_REPLICATION
case SQLCOM_LOAD_MASTER_TABLE:
{
- if (!tables->db)
- tables->db=thd->db;
- if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege,0,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ DBUG_ASSERT(first_table->db); /* Must be set in the parser */
+
+ if (check_access(thd, CREATE_ACL, first_table->db,
+ &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, tables, 0, 1, 0))
+ if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0))
goto error;
}
- if (strlen(tables->real_name) > NAME_LEN)
+ if (strlen(first_table->table_name) > NAME_LEN)
{
- net_printf(thd,ER_WRONG_TABLE_NAME, tables->real_name);
+ my_error(ER_WRONG_TABLE_NAME, MYF(0), first_table->table_name);
break;
}
pthread_mutex_lock(&LOCK_active_mi);
@@ -2465,7 +2909,7 @@ mysql_execute_command(THD *thd)
fetch_master_table will send the error to the client on failure.
Give error if the table already exists.
*/
- if (!fetch_master_table(thd, tables->db, tables->real_name,
+ if (!fetch_master_table(thd, first_table->db, first_table->table_name,
active_mi, 0, 0))
{
send_ok(thd);
@@ -2491,8 +2935,11 @@ mysql_execute_command(THD *thd)
/* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
thd->options|= OPTION_STATUS_NO_TRANS_UPDATE;
}
- /* Skip first table, which is the table we are creating */
- TABLE_LIST *create_table, *create_table_local;
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ bool link_to_local;
+ // Skip first table, which is the table we are creating
+ TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local);
+ TABLE_LIST *select_tables= lex->query_tables;
/*
Code below (especially in mysql_create_table() and select_create
methods) may modify HA_CREATE_INFO structure in LEX, so we have to
@@ -2507,15 +2954,14 @@ mysql_execute_command(THD *thd)
{
/* out of memory when creating a copy of alter_info */
res= 1;
- goto unsent_create_error;
+ goto end_with_restore_list;
}
- tables= lex->unlink_first_table(tables, &create_table,
- &create_table_local);
- if ((res= create_table_precheck(thd, tables, create_table)))
- goto unsent_create_error;
+ if ((res= create_table_precheck(thd, select_tables, create_table)))
+ goto end_with_restore_list;
create_info.alias= create_table->alias;
+
#ifndef HAVE_READLINK
if (create_info.data_file_name)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
@@ -2527,16 +2973,13 @@ mysql_execute_command(THD *thd)
#else
/* Fix names if symlinked tables */
if (append_file_to_dir(thd, &create_info.data_file_name,
- create_table->real_name) ||
+ create_table->table_name) ||
append_file_to_dir(thd, &create_info.index_file_name,
- create_table->real_name))
- {
- res=-1;
- goto unsent_create_error;
- }
+ create_table->table_name))
+ goto end_with_restore_list;
#endif
/*
- If we are using SET CHARSET without DEFAULT, add an implicite
+ If we are using SET CHARSET without DEFAULT, add an implicit
DEFAULT to not confuse old users. (This may change).
*/
if ((create_info.used_fields &
@@ -2561,70 +3004,91 @@ mysql_execute_command(THD *thd)
TABLE in the same way. That way we avoid that a new table is
created during a gobal read lock.
*/
- if (wait_if_global_read_lock(thd, 0, 1))
+ if (!thd->locked_tables &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
{
- res= -1;
- goto unsent_create_error;
+ res= 1;
+ goto end_with_restore_list;
}
if (select_lex->item_list.elements) // With select
{
- select_result *result;
+ select_result *sel_result;
select_lex->options|= SELECT_NO_UNLOCK;
- unit->offset_limit_cnt= select_lex->offset_limit;
- unit->select_limit_cnt= select_lex->select_limit+
- select_lex->offset_limit;
- if (unit->select_limit_cnt < select_lex->select_limit)
- unit->select_limit_cnt= HA_POS_ERROR; // No limit
+ unit->set_limit(select_lex);
- if (!(res=open_and_lock_tables(thd,tables)))
+ if (!(res= open_and_lock_tables(thd, select_tables)))
{
- res= -1; // If error
+ /*
+ Is table which we are changing used somewhere in other parts
+ of query
+ */
+ if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
+ {
+ TABLE_LIST *duplicate;
+ if ((duplicate= unique_table(thd, create_table, select_tables, 0)))
+ {
+ update_non_unique_table_error(create_table, "CREATE", duplicate);
+ res= 1;
+ goto end_with_restore_list;
+ }
+ }
+ /* If we create merge table, we have to test tables in merge, too */
+ if (create_info.used_fields & HA_CREATE_USED_UNION)
+ {
+ TABLE_LIST *tab;
+ for (tab= (TABLE_LIST*) create_info.merge_list.first;
+ tab;
+ tab= tab->next_local)
+ {
+ TABLE_LIST *duplicate;
+ if ((duplicate= unique_table(thd, tab, select_tables, 0)))
+ {
+ update_non_unique_table_error(tab, "CREATE", duplicate);
+ res= 1;
+ goto end_with_restore_list;
+ }
+ }
+ }
/*
select_create is currently not re-execution friendly and
needs to be created for every execution of a PS/SP.
*/
- if ((result=new select_create(create_table->db,
- create_table->real_name,
- &create_info,
- &alter_info,
- select_lex->item_list, lex->duplicates,
- lex->ignore)))
+ if ((sel_result= new select_create(create_table,
+ &create_info,
+ &alter_info,
+ select_lex->item_list,
+ lex->duplicates,
+ lex->ignore)))
{
/*
CREATE from SELECT give its SELECT_LEX for SELECT,
and item_list belong to SELECT
*/
- select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
- res=handle_select(thd, lex, result);
- select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE;
+ res= handle_select(thd, lex, sel_result, 0);
+ delete sel_result;
}
}
}
- else // regular create
+ else
{
+ /* regular create */
if (lex->name)
res= mysql_create_like_table(thd, create_table, &create_info,
- (Table_ident *)lex->name);
+ (Table_ident *)lex->name);
else
{
res= mysql_create_table(thd, create_table->db,
- create_table->real_name, &create_info,
+ create_table->table_name, &create_info,
&alter_info, 0, 0);
}
if (!res)
send_ok(thd);
}
- /*
- Release the protection against the global read lock and wake
- everyone, who might want to set a global read lock.
- */
- start_waiting_global_read_lock(thd);
-unsent_create_error:
- // put tables back for PS rexecuting
- tables= lex->link_first_table_back(tables, create_table,
- create_table_local);
+ /* put tables back for PS rexecuting */
+end_with_restore_list:
+ lex->link_first_table_back(create_table, link_to_local);
break;
}
case SQLCOM_CREATE_INDEX:
@@ -2649,9 +3113,9 @@ unsent_create_error:
if (thd->is_fatal_error) /* out of memory creating a copy of alter_info*/
goto error;
- if (check_one_table_access(thd, INDEX_ACL, tables))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_one_table_access(thd, INDEX_ACL, first_table))
goto error; /* purecov: inspected */
- thd->enable_slow_log= opt_log_slow_admin_statements;
if (end_active_trans(thd))
goto error;
/*
@@ -2666,9 +3130,9 @@ unsent_create_error:
create_info.row_type= ROW_TYPE_NOT_USED;
create_info.default_table_charset= thd->variables.collation_database;
- res= mysql_alter_table(thd, tables->db, tables->real_name,
- &create_info, tables, &alter_info,
- 0, (ORDER*)0, DUP_ERROR, 0);
+ res= mysql_alter_table(thd, first_table->db, first_table->table_name,
+ &create_info, first_table, &alter_info,
+ 0, (ORDER*) 0, 0);
break;
}
#ifdef HAVE_REPLICATION
@@ -2695,8 +3159,9 @@ unsent_create_error:
*/
if (thd->locked_tables || thd->active_transaction() || thd->global_read_lock)
{
- send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
- break;
+ my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
+ ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
+ goto error;
}
{
pthread_mutex_lock(&LOCK_active_mi);
@@ -2707,10 +3172,7 @@ unsent_create_error:
#endif /* HAVE_REPLICATION */
case SQLCOM_ALTER_TABLE:
-#if defined(DONT_ALLOW_SHOW_COMMANDS)
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- break;
-#else
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
{
ulong priv=0;
/*
@@ -2726,44 +3188,29 @@ unsent_create_error:
goto error;
if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN))
{
- net_printf(thd, ER_WRONG_TABLE_NAME, lex->name);
- res= 1;
- break;
- }
- if (!select_lex->db)
- {
- /*
- In the case of ALTER TABLE ... RENAME we should supply the
- default database if the new name is not explicitly qualified
- by a database. (Bug #11493)
- */
- if (alter_info.flags & ALTER_RENAME)
- {
- if (! thd->db)
- {
- send_error(thd,ER_NO_DB_ERROR);
- goto error;
- }
- select_lex->db= thd->db;
- }
- else
- select_lex->db=tables->db;
+ my_error(ER_WRONG_TABLE_NAME, MYF(0), lex->name);
+ goto error;
}
- if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege,0,0) ||
- check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0)||
- check_merge_table_access(thd, tables->db,
+ /* Must be set in the parser */
+ DBUG_ASSERT(select_lex->db);
+ if (check_access(thd, ALTER_ACL, 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,
+ is_schema_db(select_lex->db))||
+ check_merge_table_access(thd, first_table->db,
(TABLE_LIST *)
create_info.merge_list.first))
goto error; /* purecov: inspected */
if (grant_option)
{
- if (check_grant(thd, ALTER_ACL, tables, 0, UINT_MAX, 0))
+ if (check_grant(thd, ALTER_ACL, all_tables, 0, UINT_MAX, 0))
goto error;
if (lex->name && !test_all_bits(priv,INSERT_ACL | CREATE_ACL))
{ // Rename of table
TABLE_LIST tmp_table;
bzero((char*) &tmp_table,sizeof(tmp_table));
- tmp_table.real_name=lex->name;
+ tmp_table.table_name=lex->name;
tmp_table.db=select_lex->db;
tmp_table.grant.privilege=priv;
if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0,
@@ -2781,63 +3228,68 @@ unsent_create_error:
create_info.data_file_name= create_info.index_file_name= NULL;
/* ALTER TABLE ends previous transaction */
if (end_active_trans(thd))
- res= -1;
+ 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,
- tables,
+ first_table,
&alter_info,
select_lex->order_list.elements,
(ORDER *) select_lex->order_list.first,
- lex->duplicates, lex->ignore);
+ lex->ignore);
}
break;
}
-#endif /*DONT_ALLOW_SHOW_COMMANDS*/
case SQLCOM_RENAME_TABLE:
{
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
TABLE_LIST *table;
- if (check_db_used(thd,tables))
+ if (check_db_used(thd, all_tables))
goto error;
- for (table=tables ; table ; table=table->next->next)
+ for (table= first_table; table; table= table->next_local->next_local)
{
if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
- &table->grant.privilege,0,0) ||
- check_access(thd, INSERT_ACL | CREATE_ACL, table->next->db,
- &table->next->grant.privilege,0,0))
+ &table->grant.privilege,0,0, test(table->schema_table)) ||
+ check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db,
+ &table->next_local->grant.privilege, 0, 0,
+ test(table->next_local->schema_table)))
goto error;
if (grant_option)
{
- TABLE_LIST old_list,new_list;
+ TABLE_LIST old_list, new_list;
/*
we do not need initialize old_list and new_list because we will
come table[0] and table->next[0] there
*/
- old_list=table[0];
- new_list=table->next[0];
- old_list.next=new_list.next=0;
- if (check_grant(thd, ALTER_ACL, &old_list, 0, UINT_MAX, 0) ||
- (!test_all_bits(table->next->grant.privilege,
+ old_list= table[0];
+ new_list= table->next_local[0];
+ if (check_grant(thd, ALTER_ACL, &old_list, 0, 1, 0) ||
+ (!test_all_bits(table->next_local->grant.privilege,
INSERT_ACL | CREATE_ACL) &&
- check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0,
- UINT_MAX, 0)))
+ check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0)))
goto error;
}
}
- query_cache_invalidate3(thd, tables, 0);
- if (end_active_trans(thd))
- res= -1;
- else if (mysql_rename_tables(thd,tables))
- res= -1;
+ query_cache_invalidate3(thd, first_table, 0);
+ if (end_active_trans(thd) || mysql_rename_tables(thd, first_table))
+ goto error;
break;
}
#ifndef EMBEDDED_LIBRARY
case SQLCOM_SHOW_BINLOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
+ MYF(0)); /* purecov: inspected */
+ goto error;
#else
{
if (check_global_access(thd, SUPER_ACL))
@@ -2848,40 +3300,49 @@ unsent_create_error:
#endif
#endif /* EMBEDDED_LIBRARY */
case SQLCOM_SHOW_CREATE:
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
#ifdef DONT_ALLOW_SHOW_COMMANDS
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
+ MYF(0)); /* purecov: inspected */
+ goto error;
#else
{
- if (check_db_used(thd, tables) ||
- check_access(thd, SELECT_ACL | EXTRA_ACL, tables->db,
- &tables->grant.privilege,0,0))
+ /* Ignore temporary tables if this is "SHOW CREATE VIEW" */
+ if (lex->only_view)
+ first_table->skip_temporary= 1;
+
+ if (check_db_used(thd, all_tables) ||
+ check_access(thd, SELECT_ACL | EXTRA_ACL, first_table->db,
+ &first_table->grant.privilege, 0, 0,
+ test(first_table->schema_table)))
goto error;
- if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
+ if (grant_option && check_grant(thd, SELECT_ACL, all_tables, 2, UINT_MAX, 0))
goto error;
- res= mysqld_show_create(thd, tables);
+ res= mysqld_show_create(thd, first_table);
break;
}
#endif
case SQLCOM_CHECKSUM:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, 0))
goto error; /* purecov: inspected */
- res = mysql_checksum_table(thd, tables, &lex->check_opt);
+ res = mysql_checksum_table(thd, first_table, &lex->check_opt);
break;
}
case SQLCOM_REPAIR:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
- res = mysql_repair_table(thd, tables, &lex->check_opt);
+ res= mysql_repair_table(thd, first_table, &lex->check_opt);
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
+ /* Presumably, REPAIR and binlog writing doesn't require synchronization */
if (mysql_bin_log.is_open())
{
thd->clear_error(); // No binlog error generated
@@ -2889,28 +3350,34 @@ unsent_create_error:
mysql_bin_log.write(&qinfo);
}
}
+ select_lex->table_list.first= (byte*) first_table;
+ lex->query_tables=all_tables;
break;
}
case SQLCOM_CHECK:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, 0))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
- res = mysql_check_table(thd, tables, &lex->check_opt);
+ res = mysql_check_table(thd, first_table, &lex->check_opt);
+ select_lex->table_list.first= (byte*) first_table;
+ lex->query_tables=all_tables;
break;
}
case SQLCOM_ANALYZE:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
- res = mysql_analyze_table(thd, tables, &lex->check_opt);
+ res = mysql_analyze_table(thd, first_table, &lex->check_opt);
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
+ /* Presumably, ANALYZE and binlog writing doesn't require synchronization */
if (mysql_bin_log.is_open())
{
thd->clear_error(); // No binlog error generated
@@ -2918,22 +3385,25 @@ unsent_create_error:
mysql_bin_log.write(&qinfo);
}
}
+ select_lex->table_list.first= (byte*) first_table;
+ lex->query_tables=all_tables;
break;
}
case SQLCOM_OPTIMIZE:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
- mysql_recreate_table(thd, tables) :
- mysql_optimize_table(thd, tables, &lex->check_opt);
+ mysql_recreate_table(thd, first_table) :
+ mysql_optimize_table(thd, first_table, &lex->check_opt);
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
+ /* Presumably, OPTIMIZE and binlog writing doesn't require synchronization */
if (mysql_bin_log.is_open())
{
thd->clear_error(); // No binlog error generated
@@ -2941,141 +3411,186 @@ unsent_create_error:
mysql_bin_log.write(&qinfo);
}
}
+ select_lex->table_list.first= (byte*) first_table;
+ lex->query_tables=all_tables;
break;
}
case SQLCOM_UPDATE:
- if (update_precheck(thd, tables))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (update_precheck(thd, all_tables))
break;
- res= mysql_update(thd,tables,
- select_lex->item_list,
- lex->value_list,
- select_lex->where,
- select_lex->order_list.elements,
- (ORDER *) select_lex->order_list.first,
- select_lex->select_limit,
- lex->duplicates, lex->ignore);
- if (thd->net.report_error)
- res= -1;
- break;
+ DBUG_ASSERT(select_lex->offset_limit == 0);
+ unit->set_limit(select_lex);
+ res= (up_result= mysql_update(thd, all_tables,
+ select_lex->item_list,
+ lex->value_list,
+ select_lex->where,
+ select_lex->order_list.elements,
+ (ORDER *) select_lex->order_list.first,
+ unit->select_limit_cnt,
+ lex->duplicates, lex->ignore));
+ /* mysql_update return 2 if we need to switch to multi-update */
+ if (up_result != 2)
+ break;
+ /* Fall through */
case SQLCOM_UPDATE_MULTI:
{
- if ((res= multi_update_precheck(thd, tables)))
- break;
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ /* if we switched from normal update, rights are checked */
+ if (up_result != 2)
+ {
+ if ((res= multi_update_precheck(thd, all_tables)))
+ break;
+ }
+ else
+ res= 0;
+
+ res= mysql_multi_update_prepare(thd);
- res= mysql_multi_update_lock(thd, tables, &select_lex->item_list,
- select_lex);
#ifdef HAVE_REPLICATION
/* Check slave filtering rules */
- if (thd->slave_thread)
- if (all_tables_not_ok(thd,tables))
+ if (unlikely(thd->slave_thread))
+ {
+ if (all_tables_not_ok(thd, all_tables))
{
if (res!= 0)
{
res= 0; /* don't care of prev failure */
thd->clear_error(); /* filters are of highest prior */
}
- /* we warn the slave SQL thread */
- my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
- break;
+ /* we warn the slave SQL thread */
+ my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
+ break;
}
+ if (res)
+ break;
+ }
+ else
+ {
#endif /* HAVE_REPLICATION */
- if (res)
- break;
-
- res= mysql_multi_update(thd,tables,
- &select_lex->item_list,
- &lex->value_list,
- select_lex->where,
- select_lex->options,
- lex->duplicates, lex->ignore, unit, select_lex);
+ if (res)
+ break;
+ if (opt_readonly &&
+ !(thd->security_ctx->master_access & SUPER_ACL) &&
+ some_non_temp_table_to_be_updated(thd, all_tables))
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
+ break;
+ }
+#ifdef HAVE_REPLICATION
+ } /* unlikely */
+#endif
+
+ res= mysql_multi_update(thd, all_tables,
+ &select_lex->item_list,
+ &lex->value_list,
+ select_lex->where,
+ select_lex->options,
+ lex->duplicates, lex->ignore, unit, select_lex);
break;
}
case SQLCOM_REPLACE:
case SQLCOM_INSERT:
{
- if ((res= insert_precheck(thd, tables)))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if ((res= insert_precheck(thd, all_tables)))
break;
- res= mysql_insert(thd,tables,lex->field_list,lex->many_values,
- lex->update_list, lex->value_list,
+
+ if (!thd->locked_tables &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ {
+ res= 1;
+ break;
+ }
+
+ res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
+ lex->update_list, lex->value_list,
lex->duplicates, lex->ignore);
- if (thd->net.report_error)
- res= -1;
+
+ /*
+ If we have inserted into a VIEW, and the base table has
+ AUTO_INCREMENT column, but this column is not accessible through
+ a view, then we should restore LAST_INSERT_ID to the value it
+ had before the statement.
+ */
+ if (first_table->view && !first_table->contain_auto_increment)
+ thd->last_insert_id= thd->current_insert_id;
+
break;
}
case SQLCOM_REPLACE_SELECT:
case SQLCOM_INSERT_SELECT:
{
- TABLE_LIST *first_local_table= (TABLE_LIST *) select_lex->table_list.first;
- TABLE_LIST dup_tables;
- TABLE *insert_table;
- if ((res= insert_precheck(thd, tables)))
+ select_result *sel_result;
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if ((res= insert_precheck(thd, all_tables)))
break;
/* Fix lock for first table */
- if (tables->lock_type == TL_WRITE_DELAYED)
- tables->lock_type= TL_WRITE;
+ if (first_table->lock_type == TL_WRITE_DELAYED)
+ first_table->lock_type= TL_WRITE;
/* Don't unlock tables until command is written to binary log */
select_lex->options|= SELECT_NO_UNLOCK;
- select_result *result;
- unit->offset_limit_cnt= select_lex->offset_limit;
- unit->select_limit_cnt= select_lex->select_limit+select_lex->offset_limit;
- if (unit->select_limit_cnt < select_lex->select_limit)
- unit->select_limit_cnt= HA_POS_ERROR; // No limit
-
- if ((res= open_and_lock_tables(thd, tables)))
- break;
+ unit->set_limit(select_lex);
- insert_table= tables->table;
- /* MERGE sub-tables can only be detected after open. */
- if (mysql_lock_have_duplicate(thd, insert_table, tables->next))
+ if (! thd->locked_tables &&
+ ! (need_start_waiting= ! wait_if_global_read_lock(thd, 0, 1)))
{
- /* Using same table for INSERT and SELECT */
- select_lex->options |= OPTION_BUFFER_RESULT;
+ res= 1;
+ break;
}
- /* Skip first table, which is the table we are inserting in */
- select_lex->table_list.first= (byte*) first_local_table->next;
- tables= (TABLE_LIST *) select_lex->table_list.first;
- dup_tables= *first_local_table;
- first_local_table->next= 0;
- if (select_lex->group_list.elements != 0)
- {
- /*
- When we are using GROUP BY we can't refere to other tables in the
- ON DUPLICATE KEY part
- */
- dup_tables.next= 0;
- }
-
- if (!(res= mysql_prepare_insert(thd, tables, first_local_table,
- &dup_tables, insert_table,
- lex->field_list, 0,
- lex->update_list, lex->value_list,
- lex->duplicates)) &&
- (result= new select_insert(insert_table, first_local_table,
- &dup_tables, &lex->field_list,
- &lex->update_list, &lex->value_list,
- lex->duplicates, lex->ignore)))
- {
- /*
- insert/replace from SELECT give its SELECT_LEX for SELECT,
- and item_list belong to SELECT
- */
- lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
- res= handle_select(thd, lex, result);
+ if (!(res= open_and_lock_tables(thd, all_tables)))
+ {
+ /* 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->context.table_list=
+ select_lex->context.first_name_resolution_table= second_table;
+ res= mysql_insert_select_prepare(thd);
+ if (!res && (sel_result= new select_insert(first_table,
+ first_table->table,
+ &lex->field_list,
+ &lex->update_list,
+ &lex->value_list,
+ lex->duplicates,
+ lex->ignore)))
+ {
+ res= handle_select(thd, lex, sel_result, OPTION_SETUP_TABLES_DONE);
+ /*
+ 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.
+ */
+ if (first_table->lock_type == TL_WRITE_CONCURRENT_INSERT &&
+ thd->lock)
+ {
+ /* 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 */
- lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
- delete result;
- if (thd->net.report_error)
- res= -1;
+ select_lex->table_list.first= (byte*) first_table;
}
- else
- res= -1;
- insert_table->insert_values= 0; // Set by mysql_prepare_insert()
- first_local_table->next= tables;
- lex->select_lex.table_list.first= (byte*) first_local_table;
+
+ /*
+ If we have inserted into a VIEW, and the base table has
+ AUTO_INCREMENT column, but this column is not accessible through
+ a view, then we should restore LAST_INSERT_ID to the value it
+ had before the statement.
+ */
+ if (first_table->view && !first_table->contain_auto_increment)
+ thd->last_insert_id= thd->current_insert_id;
+
break;
}
case SQLCOM_TRUNCATE:
@@ -3084,7 +3599,8 @@ unsent_create_error:
res= -1;
break;
}
- if (check_one_table_access(thd, DELETE_ACL, tables))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_one_table_access(thd, DELETE_ACL, all_tables))
goto error;
/*
Don't allow this within a transaction because we want to use
@@ -3092,68 +3608,66 @@ unsent_create_error:
*/
if (thd->locked_tables || thd->active_transaction())
{
- send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION,NullS);
+ my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
+ ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
goto error;
}
- res=mysql_truncate(thd, tables, 0);
+
+ res= mysql_truncate(thd, first_table, 0);
break;
case SQLCOM_DELETE:
{
- if ((res= delete_precheck(thd, tables)))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if ((res= delete_precheck(thd, all_tables)))
break;
- res = mysql_delete(thd,tables, select_lex->where,
+ DBUG_ASSERT(select_lex->offset_limit == 0);
+ unit->set_limit(select_lex);
+
+ if (!thd->locked_tables &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ {
+ res= 1;
+ break;
+ }
+
+ res = mysql_delete(thd, all_tables, select_lex->where,
&select_lex->order_list,
- select_lex->select_limit, select_lex->options);
- if (thd->net.report_error)
- res= -1;
+ unit->select_limit_cnt, select_lex->options,
+ FALSE);
break;
}
case SQLCOM_DELETE_MULTI:
{
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
TABLE_LIST *aux_tables=
- (TABLE_LIST *)thd->lex->auxilliary_table_list.first;
- TABLE_LIST *target_tbl;
- uint table_count;
- multi_delete *result;
+ (TABLE_LIST *)thd->lex->auxiliary_table_list.first;
+ multi_delete *del_result;
- if ((res= multi_delete_precheck(thd, tables, &table_count)))
+ if (!thd->locked_tables &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ {
+ res= 1;
+ break;
+ }
+
+ if ((res= multi_delete_precheck(thd, all_tables)))
break;
/* condition will be TRUE on SP re-excuting */
if (select_lex->item_list.elements != 0)
select_lex->item_list.empty();
if (add_item_to_list(thd, new Item_null()))
- {
- res= -1;
- break;
- }
+ goto error;
thd->proc_info="init";
- if ((res=open_and_lock_tables(thd,tables)))
+ if ((res= open_and_lock_tables(thd, all_tables)))
break;
- /* Fix tables-to-be-deleted-from list to point at opened tables */
- for (target_tbl= (TABLE_LIST*) aux_tables;
- target_tbl;
- target_tbl= target_tbl->next)
- {
- TABLE_LIST *orig= target_tbl->table_list;
- target_tbl->table= orig->table;
- /*
- Multi-delete can't be constructed over-union => we always have
- single SELECT on top and have to check underlying SELECTs of it
- */
- if (lex->select_lex.check_updateable_in_subqueries(orig->db,
- orig->real_name))
- {
- my_error(ER_UPDATE_TABLE_USED, MYF(0),
- orig->real_name);
- res= -1;
- break;
- }
- }
- if (!res && !thd->is_fatal_error &&
- (result= new multi_delete(thd,aux_tables, table_count)))
+ if ((res= mysql_multi_delete_prepare(thd)))
+ goto error;
+
+ if (!thd->is_fatal_error &&
+ (del_result= new multi_delete(aux_tables, lex->table_count)))
{
res= mysql_select(thd, &select_lex->ref_pointer_array,
select_lex->get_table_list(),
@@ -3163,28 +3677,24 @@ unsent_create_error:
0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
(ORDER *)NULL,
select_lex->options | thd->options |
- SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK,
- result, unit, select_lex);
- if (thd->net.report_error)
- res= -1;
- delete result;
+ SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
+ OPTION_SETUP_TABLES_DONE,
+ del_result, unit, select_lex);
+ delete del_result;
}
else
- res= -1; // Error is not sent
- close_thread_tables(thd);
+ res= TRUE; // Error
break;
}
case SQLCOM_DROP_TABLE:
{
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (!lex->drop_temporary)
{
- if (check_table_access(thd,DROP_ACL,tables,0))
+ if (check_table_access(thd, DROP_ACL, all_tables, 0))
goto error; /* purecov: inspected */
if (end_active_trans(thd))
- {
- res= -1;
- break;
- }
+ goto error;
}
else
{
@@ -3202,26 +3712,20 @@ unsent_create_error:
/* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
thd->options|= OPTION_STATUS_NO_TRANS_UPDATE;
}
- res= mysql_rm_table(thd,tables,lex->drop_if_exists, lex->drop_temporary);
+ /* DDL and binlog write order protected by LOCK_open */
+ res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
+ lex->drop_temporary);
}
break;
- case SQLCOM_SHOW_DATABASES:
-#if defined(DONT_ALLOW_SHOW_COMMANDS)
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
-#else
- if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
- check_global_access(thd, SHOW_DB_ACL))
- goto error;
- res= mysqld_show_dbs(thd, (lex->wild ? lex->wild->ptr() : NullS));
- break;
-#endif
case SQLCOM_SHOW_PROCESSLIST:
- if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL))
+ if (!thd->security_ctx->priv_user[0] &&
+ check_global_access(thd,PROCESS_ACL))
break;
mysqld_list_processes(thd,
- thd->master_access & PROCESS_ACL ? NullS :
- thd->priv_user,lex->verbose);
+ (thd->security_ctx->master_access & PROCESS_ACL ?
+ NullS :
+ thd->security_ctx->priv_user),
+ lex->verbose);
break;
case SQLCOM_SHOW_STORAGE_ENGINES:
res= mysqld_show_storage_engines(thd);
@@ -3232,155 +3736,60 @@ unsent_create_error:
case SQLCOM_SHOW_COLUMN_TYPES:
res= mysqld_show_column_types(thd);
break;
- case SQLCOM_SHOW_STATUS:
- res= mysqld_show(thd,(lex->wild ? lex->wild->ptr() : NullS),status_vars,
- OPT_GLOBAL, &LOCK_status);
- break;
- case SQLCOM_SHOW_VARIABLES:
- res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS),
- init_vars, lex->option_type,
- &LOCK_global_system_variables);
- break;
case SQLCOM_SHOW_LOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
+ MYF(0)); /* purecov: inspected */
+ goto error;
#else
{
- if (grant_option && check_access(thd, FILE_ACL, any_db,0,0,0))
+ if (grant_option && check_access(thd, FILE_ACL, any_db,0,0,0,0))
goto error;
res= mysqld_show_logs(thd);
break;
}
#endif
- case SQLCOM_SHOW_TABLES:
- /* FALL THROUGH */
-#ifdef DONT_ALLOW_SHOW_COMMANDS
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
-#else
- {
- char *db=select_lex->db ? select_lex->db : thd->db;
- if (!db)
- {
- send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */
- goto error; /* purecov: inspected */
- }
- remove_escape(db); // Fix escaped '_'
- if (check_db_name(db))
- {
- net_printf(thd,ER_WRONG_DB_NAME, db);
- goto error;
- }
- if (check_access(thd,SELECT_ACL,db,&thd->col_access,0,0))
- goto error; /* purecov: inspected */
- if (!thd->col_access && check_grant_db(thd,db))
- {
- net_printf(thd, ER_DBACCESS_DENIED_ERROR,
- thd->priv_user,
- thd->priv_host,
- db);
- goto error;
- }
- /* grant is checked in mysqld_show_tables */
- if (lex->describe)
- res= mysqld_extend_show_tables(thd,db,
- (lex->wild ? lex->wild->ptr() : NullS));
- else
- res= mysqld_show_tables(thd,db,
- (lex->wild ? lex->wild->ptr() : NullS));
- break;
- }
-#endif
- case SQLCOM_SHOW_OPEN_TABLES:
- res= mysqld_show_open_tables(thd,(lex->wild ? lex->wild->ptr() : NullS));
- break;
- case SQLCOM_SHOW_CHARSETS:
- res= mysqld_show_charsets(thd,(lex->wild ? lex->wild->ptr() : NullS));
- break;
- case SQLCOM_SHOW_COLLATIONS:
- res= mysqld_show_collations(thd,(lex->wild ? lex->wild->ptr() : NullS));
- break;
- case SQLCOM_SHOW_FIELDS:
-#ifdef DONT_ALLOW_SHOW_COMMANDS
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
-#else
- {
- char *db=tables->db;
- remove_escape(db); // Fix escaped '_'
- remove_escape(tables->real_name);
- if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
- &tables->grant.privilege, 0, 0))
- goto error; /* purecov: inspected */
- if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
- goto error;
- res= mysqld_show_fields(thd,tables,
- (lex->wild ? lex->wild->ptr() : NullS),
- lex->verbose);
- break;
- }
-#endif
- case SQLCOM_SHOW_KEYS:
-#ifdef DONT_ALLOW_SHOW_COMMANDS
- send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
-#else
- {
- char *db=tables->db;
- remove_escape(db); // Fix escaped '_'
- remove_escape(tables->real_name);
- if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
- &tables->grant.privilege, 0, 0))
- goto error; /* purecov: inspected */
- if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
- goto error;
- res= mysqld_show_keys(thd,tables);
- break;
- }
-#endif
case SQLCOM_CHANGE_DB:
- mysql_change_db(thd,select_lex->db);
+ if (!mysql_change_db(thd,select_lex->db,FALSE))
+ send_ok(thd);
break;
case SQLCOM_LOAD:
{
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
uint privilege= (lex->duplicates == DUP_REPLACE ?
- INSERT_ACL | DELETE_ACL : INSERT_ACL);
+ INSERT_ACL | DELETE_ACL : INSERT_ACL) |
+ (lex->local_file ? 0 : FILE_ACL);
- if (!lex->local_file)
- {
- if (check_access(thd,privilege | FILE_ACL,tables->db,0,0,0))
- goto error;
- }
- else
+ if (lex->local_file)
{
if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) ||
- ! opt_local_infile)
+ !opt_local_infile)
{
- send_error(thd,ER_NOT_ALLOWED_COMMAND);
+ my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), MYF(0));
goto error;
}
- if (check_one_table_access(thd, privilege, tables))
- goto error;
}
- res=mysql_load(thd, lex->exchange, tables, lex->field_list,
- lex->duplicates, lex->ignore, (bool) lex->local_file, lex->lock_option);
+
+ if (check_one_table_access(thd, privilege, all_tables))
+ goto error;
+
+ res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
+ lex->update_list, lex->value_list, lex->duplicates,
+ lex->ignore, (bool) lex->local_file);
break;
}
case SQLCOM_SET_OPTION:
{
List<set_var_base> *lex_var_list= &lex->var_list;
- if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
- (res= open_and_lock_tables(thd,tables))))
- break;
+ if ((check_table_access(thd, SELECT_ACL, all_tables, 0) ||
+ open_and_lock_tables(thd, all_tables)))
+ goto error;
if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
{
- my_printf_error(0, "The SET ONE_SHOT syntax is reserved for \
-purposes internal to the MySQL server", MYF(0));
- res= -1;
- break;
+ my_error(ER_RESERVED_SYNTAX, MYF(0), "SET ONE_SHOT");
+ goto error;
}
if (!(res= sql_set_variables(thd, lex_var_list)))
{
@@ -3391,8 +3800,6 @@ purposes internal to the MySQL server", MYF(0));
thd->one_shot_set|= lex->one_shot_set;
send_ok(thd);
}
- if (thd->net.report_error)
- res= -1;
break;
}
@@ -3415,17 +3822,18 @@ purposes internal to the MySQL server", MYF(0));
break;
case SQLCOM_LOCK_TABLES:
unlock_locked_tables(thd);
- if (check_db_used(thd,tables) || end_active_trans(thd))
+ if (check_db_used(thd, all_tables) || end_active_trans(thd))
goto error;
- if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, tables,0))
+ if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0))
goto error;
thd->in_lock_tables=1;
thd->options|= OPTION_TABLE_LOCK;
- if (!(res= open_and_lock_tables(thd, tables)))
+
+ if (!(res= simple_open_n_lock_tables(thd, all_tables)))
{
#ifdef HAVE_QUERY_CACHE
if (thd->variables.query_cache_wlock_invalidate)
- query_cache.invalidate_locked_for_write(tables);
+ query_cache.invalidate_locked_for_write(first_table);
#endif /*HAVE_QUERY_CACHE*/
thd->locked_tables=thd->lock;
thd->lock=0;
@@ -3451,26 +3859,27 @@ purposes internal to the MySQL server", MYF(0));
char *alias;
if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name))
{
- net_printf(thd,ER_WRONG_DB_NAME, lex->name);
+ my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
break;
}
/*
If in a slave thread :
CREATE DATABASE DB was certainly not preceded by USE DB.
- For that reason, db_ok() in sql/slave.cc did not check the
+ For that reason, db_ok() in sql/slave.cc did not check the
do_db/ignore_db. And as this query involves no tables, tables_ok()
above was not called. So we have to check rules again here.
*/
#ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
+ if (thd->slave_thread &&
(!db_ok(lex->name, replicate_do_db, replicate_ignore_db) ||
!db_ok_with_wild_table(lex->name)))
{
- my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
+ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
break;
}
#endif
- if (check_access(thd,CREATE_ACL,lex->name,0,1,0))
+
+ if (check_access(thd,CREATE_ACL,lex->name,0,1,0,is_schema_db(lex->name)))
break;
res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name),
&create_info, 0);
@@ -3483,10 +3892,9 @@ purposes internal to the MySQL server", MYF(0));
res= -1;
break;
}
- char *alias;
- if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name))
+ if (check_db_name(lex->name))
{
- net_printf(thd, ER_WRONG_DB_NAME, lex->name);
+ my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
break;
}
/*
@@ -3501,32 +3909,28 @@ purposes internal to the MySQL server", MYF(0));
(!db_ok(lex->name, replicate_do_db, replicate_ignore_db) ||
!db_ok_with_wild_table(lex->name)))
{
- my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
+ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
break;
}
#endif
- if (check_access(thd,DROP_ACL,lex->name,0,1,0))
+ if (check_access(thd,DROP_ACL,lex->name,0,1,0,is_schema_db(lex->name)))
break;
if (thd->locked_tables || thd->active_transaction())
{
- send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
+ my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
+ ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
goto error;
}
- res=mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : lex->name),
- lex->drop_if_exists, 0);
+ res= mysql_rm_db(thd, lex->name, lex->drop_if_exists, 0);
break;
}
case SQLCOM_ALTER_DB:
{
- char *db= lex->name ? lex->name : thd->db;
- if (!db)
- {
- send_error(thd, ER_NO_DB_ERROR);
- goto error;
- }
+ char *db= lex->name;
+ DBUG_ASSERT(db); /* Must be set in the parser */
if (!strip_sp(db) || check_db_name(db))
{
- net_printf(thd, ER_WRONG_DB_NAME, db);
+ my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
break;
}
/*
@@ -3541,15 +3945,16 @@ purposes internal to the MySQL server", MYF(0));
(!db_ok(db, replicate_do_db, replicate_ignore_db) ||
!db_ok_with_wild_table(db)))
{
- my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
+ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
break;
}
#endif
- if (check_access(thd, ALTER_ACL, db, 0, 1, 0))
+ if (check_access(thd, ALTER_ACL, db, 0, 1, 0, is_schema_db(db)))
break;
if (thd->locked_tables || thd->active_transaction())
{
- send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
+ my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
+ ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
goto error;
}
res= mysql_alter_db(thd, db, &lex->create_info);
@@ -3559,161 +3964,179 @@ purposes internal to the MySQL server", MYF(0));
{
if (!strip_sp(lex->name) || check_db_name(lex->name))
{
- net_printf(thd,ER_WRONG_DB_NAME, lex->name);
+ my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
break;
}
- if (check_access(thd,SELECT_ACL,lex->name,0,1,0))
+ 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);
break;
}
- case SQLCOM_CREATE_FUNCTION:
- if (check_access(thd,INSERT_ACL,"mysql",0,1,0))
+ 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)))
+ if (sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
+ &thd->sp_func_cache, FALSE))
+ {
+ my_error(ER_UDF_EXISTS, MYF(0), lex->spname->m_name.str);
+ goto error;
+ }
+ if (!(res = mysql_create_function(thd, &lex->udf)))
send_ok(thd);
#else
- net_printf(thd, ER_CANT_OPEN_LIBRARY, lex->udf.dl, 0, "feature disabled");
- res= -1;
+ my_error(ER_CANT_OPEN_LIBRARY, MYF(0), lex->udf.dl, 0, "feature disabled");
+ res= TRUE;
#endif
break;
- case SQLCOM_DROP_FUNCTION:
- if (check_access(thd,DELETE_ACL,"mysql",0,1,0))
+ }
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ case SQLCOM_CREATE_USER:
+ {
+ if (check_access(thd, INSERT_ACL, "mysql", 0, 1, 1, 0) &&
+ check_global_access(thd,CREATE_USER_ACL))
break;
-#ifdef HAVE_DLOPEN
- if (!(res = mysql_drop_function(thd,&lex->udf.name)))
+ if (end_active_trans(thd))
+ goto error;
+ /* Conditionally writes to binlog */
+ if (!(res= mysql_create_user(thd, lex->users_list)))
send_ok(thd);
-#else
- res= -1;
-#endif
break;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ }
case SQLCOM_DROP_USER:
{
- if (check_access(thd, GRANT_ACL,"mysql",0,1,0))
+ if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 1, 0) &&
+ check_global_access(thd,CREATE_USER_ACL))
break;
+ if (end_active_trans(thd))
+ goto error;
+ /* Conditionally writes to binlog */
if (!(res= mysql_drop_user(thd, lex->users_list)))
- {
- mysql_update_log.write(thd, thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
send_ok(thd);
- }
+ break;
+ }
+ case SQLCOM_RENAME_USER:
+ {
+ if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) &&
+ check_global_access(thd,CREATE_USER_ACL))
+ break;
+ if (end_active_trans(thd))
+ goto error;
+ /* Conditionally writes to binlog */
+ if (!(res= mysql_rename_user(thd, lex->users_list)))
+ send_ok(thd);
break;
}
case SQLCOM_REVOKE_ALL:
{
- if (check_access(thd, GRANT_ACL ,"mysql",0,1,0))
+ 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)))
- {
- mysql_update_log.write(thd, thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
send_ok(thd);
- }
break;
}
case SQLCOM_REVOKE:
case SQLCOM_GRANT:
{
if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
- tables ? tables->db : select_lex->db,
- tables ? &tables->grant.privilege : 0,
- tables ? 0 : 1, 0))
+ first_table ? first_table->db : select_lex->db,
+ first_table ? &first_table->grant.privilege : 0,
+ first_table ? 0 : 1, 0,
+ first_table ? (bool) first_table->schema_table :
+ select_lex->db ? is_schema_db(select_lex->db) : 0))
goto error;
- /*
- Check that the user isn't trying to change a password for another
- user if he doesn't have UPDATE privilege to the MySQL database
- */
-
- if (thd->user) // If not replication
+ if (thd->security_ctx->user) // If not replication
{
- LEX_USER *user;
+ LEX_USER *user, *tmp_user;
+
List_iterator <LEX_USER> user_list(lex->users_list);
- while ((user=user_list++))
+ while ((tmp_user= user_list++))
{
- if (user->password.str &&
- (strcmp(thd->user,user->user.str) ||
- user->host.str &&
- my_strcasecmp(&my_charset_latin1,
- user->host.str, thd->host_or_ip)))
- {
- if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1))
- {
- send_error(thd, ER_PASSWORD_NOT_ALLOWED);
- goto error;
- }
- break; // We are allowed to do global changes
- }
+ if (!(user= get_current_user(thd, tmp_user)))
+ goto error;
+ if (specialflag & SPECIAL_NO_RESOLVE &&
+ hostname_requires_resolving(user->host.str))
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_HOSTNAME_WONT_WORK,
+ ER(ER_WARN_HOSTNAME_WONT_WORK),
+ user->host.str);
+ // Are we trying to change a password of another user
+ DBUG_ASSERT(user->host.str != 0);
+ if (strcmp(thd->security_ctx->user, user->user.str) ||
+ my_strcasecmp(system_charset_info,
+ user->host.str, thd->security_ctx->host_or_ip))
+ {
+ // TODO: use check_change_password()
+ if (is_acl_user(user->host.str, user->user.str) &&
+ user->password.str &&
+ check_access(thd, UPDATE_ACL,"mysql",0,1,1,0))
+ {
+ my_message(ER_PASSWORD_NOT_ALLOWED,
+ ER(ER_PASSWORD_NOT_ALLOWED), MYF(0));
+ goto error;
+ }
+ }
}
}
- if (specialflag & SPECIAL_NO_RESOLVE)
+ if (first_table)
{
- LEX_USER *user;
- List_iterator <LEX_USER> user_list(lex->users_list);
- while ((user=user_list++))
+ if (lex->type == TYPE_ENUM_PROCEDURE ||
+ lex->type == TYPE_ENUM_FUNCTION)
{
- if (hostname_requires_resolving(user->host.str))
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_HOSTNAME_WONT_WORK,
- ER(ER_WARN_HOSTNAME_WONT_WORK),
- user->host.str);
+ uint grants= lex->all_privileges
+ ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL)
+ : lex->grant;
+ if (grant_option &&
+ check_grant_routine(thd, grants | GRANT_ACL, all_tables,
+ lex->type == TYPE_ENUM_PROCEDURE, 0))
+ goto error;
+ /* 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);
}
- }
- if (tables)
- {
- if (grant_option && check_grant(thd,
- (lex->grant | lex->grant_tot_col |
- GRANT_ACL),
- tables, 0, UINT_MAX, 0))
- goto error;
- if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns,
- lex->grant,
- lex->sql_command == SQLCOM_REVOKE)))
+ else
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
+ if (grant_option && check_grant(thd,
+ (lex->grant | lex->grant_tot_col |
+ GRANT_ACL),
+ all_tables, 0, UINT_MAX, 0))
+ goto error;
+ /* Conditionally writes to binlog */
+ res= mysql_table_grant(thd, all_tables, lex->users_list,
+ lex->columns, lex->grant,
+ lex->sql_command == SQLCOM_REVOKE);
}
}
else
{
- if (lex->columns.elements)
+ if (lex->columns.elements || lex->type)
{
- send_error(thd,ER_ILLEGAL_GRANT_FOR_TABLE);
- res=1;
+ my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
+ MYF(0));
+ goto error;
}
else
+ /* Conditionally writes to binlog */
res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
lex->sql_command == SQLCOM_REVOKE);
if (!res)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
- mysql_bin_log.write(&qinfo);
- }
- if (mqh_used && lex->sql_command == SQLCOM_GRANT)
+ if (lex->sql_command == SQLCOM_GRANT)
{
List_iterator <LEX_USER> str_list(lex->users_list);
- LEX_USER *user;
- while ((user=str_list++))
- reset_mqh(thd,user);
+ LEX_USER *user, *tmp_user;
+ while ((tmp_user=str_list++))
+ {
+ if (!(user= get_current_user(thd, tmp_user)))
+ goto error;
+ reset_mqh(user);
+ }
}
}
}
@@ -3721,31 +4144,30 @@ purposes internal to the MySQL server", MYF(0));
}
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
case SQLCOM_RESET:
- /*
- RESET commands are never written to the binary log, so we have to
- initialize this variable because RESET shares the same code as FLUSH
+ /*
+ RESET commands are never written to the binary log, so we have to
+ initialize this variable because RESET shares the same code as FLUSH
*/
lex->no_write_to_binlog= 1;
case SQLCOM_FLUSH:
{
- if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, tables))
+ bool write_to_binlog;
+ if (check_global_access(thd,RELOAD_ACL))
goto error;
+
/*
reload_acl_and_cache() will tell us if we are allowed to write to the
binlog or not.
*/
- bool write_to_binlog;
- if (reload_acl_and_cache(thd, lex->type, tables, &write_to_binlog))
- send_error(thd, 0);
- else
+ if (!reload_acl_and_cache(thd, lex->type, first_table, &write_to_binlog))
{
/*
We WANT to write and we CAN write.
! we write after unlocking the table.
*/
+ /* Presumably, RESET and binlog writing doesn't require synchronization */
if (!lex->no_write_to_binlog && write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
@@ -3753,129 +4175,911 @@ purposes internal to the MySQL server", MYF(0));
}
}
send_ok(thd);
- }
+ }
+
break;
}
case SQLCOM_KILL:
- kill_one_thread(thd,lex->thread_id);
+ {
+ Item *it= (Item *)lex->value_list.head();
+
+ if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
+ {
+ my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
+ MYF(0));
+ goto error;
+ }
+ kill_one_thread(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY);
break;
+ }
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_SHOW_GRANTS:
- res=0;
- if ((thd->priv_user &&
- !strcmp(thd->priv_user,lex->grant_user->user.str)) ||
- !check_access(thd, SELECT_ACL, "mysql",0,1,0))
+ {
+ LEX_USER *grant_user= get_current_user(thd, lex->grant_user);
+ if (!grant_user)
+ goto error;
+ if ((thd->security_ctx->priv_user &&
+ !strcmp(thd->security_ctx->priv_user, grant_user->user.str)) ||
+ !check_access(thd, SELECT_ACL, "mysql",0,1,0,0))
{
- res = mysql_show_grants(thd,lex->grant_user);
+ res = mysql_show_grants(thd, grant_user);
}
break;
+ }
#endif
case SQLCOM_HA_OPEN:
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL, tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL, all_tables, 0))
goto error;
- res = mysql_ha_open(thd, tables);
+ res= mysql_ha_open(thd, first_table, 0);
break;
case SQLCOM_HA_CLOSE:
- if (check_db_used(thd,tables))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables))
goto error;
- res = mysql_ha_close(thd, tables);
+ res= mysql_ha_close(thd, first_table);
break;
case SQLCOM_HA_READ:
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
/*
There is no need to check for table permissions here, because
if a user has no permissions to read a table, he won't be
able to open it (with SQLCOM_HA_OPEN) in the first place.
*/
- if (check_db_used(thd,tables))
+ if (check_db_used(thd, all_tables))
goto error;
- res = mysql_ha_read(thd, tables, lex->ha_read_mode, lex->backup_dir,
- lex->insert_list, lex->ha_rkey_mode, select_lex->where,
- select_lex->select_limit, select_lex->offset_limit);
+ 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,
+ unit->select_limit_cnt, unit->offset_limit_cnt);
break;
case SQLCOM_BEGIN:
- if (thd->locked_tables)
+ if (thd->transaction.xid_state.xa_state != XA_NOTR)
{
- thd->lock=thd->locked_tables;
- thd->locked_tables=0; // Will be automaticly closed
- close_thread_tables(thd); // Free tables
+ my_error(ER_XAER_RMFAIL, MYF(0),
+ xa_state_names[thd->transaction.xid_state.xa_state]);
+ break;
}
- if (end_active_trans(thd))
+ if (begin_trans(thd))
+ goto error;
+ send_ok(thd);
+ break;
+ case SQLCOM_COMMIT:
+ if (end_trans(thd, lex->tx_release ? COMMIT_RELEASE :
+ lex->tx_chain ? COMMIT_AND_CHAIN : COMMIT))
+ goto error;
+ send_ok(thd);
+ break;
+ case SQLCOM_ROLLBACK:
+ if (end_trans(thd, lex->tx_release ? ROLLBACK_RELEASE :
+ lex->tx_chain ? ROLLBACK_AND_CHAIN : ROLLBACK))
+ goto error;
+ send_ok(thd);
+ break;
+ case SQLCOM_RELEASE_SAVEPOINT:
+ {
+ SAVEPOINT *sv;
+ for (sv=thd->transaction.savepoints; sv; sv=sv->prev)
{
- res= -1;
+ if (my_strnncoll(system_charset_info,
+ (uchar *)lex->ident.str, lex->ident.length,
+ (uchar *)sv->name, sv->length) == 0)
+ break;
}
- else
+ if (sv)
{
- thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
- OPTION_BEGIN);
- thd->server_status|= SERVER_STATUS_IN_TRANS;
- if (!(lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) ||
- !(res= ha_start_consistent_snapshot(thd)))
+ if (ha_release_savepoint(thd, sv))
+ res= TRUE; // cannot happen
+ else
send_ok(thd);
+ thd->transaction.savepoints=sv->prev;
}
+ else
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str);
break;
- case SQLCOM_COMMIT:
- /*
- We don't use end_active_trans() here to ensure that this works
- even if there is a problem with the OPTION_AUTO_COMMIT flag
- (Which of course should never happen...)
- */
+ }
+ case SQLCOM_ROLLBACK_TO_SAVEPOINT:
{
- thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
- if (!ha_commit(thd))
+ SAVEPOINT *sv;
+ for (sv=thd->transaction.savepoints; sv; sv=sv->prev)
{
- send_ok(thd);
+ if (my_strnncoll(system_charset_info,
+ (uchar *)lex->ident.str, lex->ident.length,
+ (uchar *)sv->name, sv->length) == 0)
+ break;
+ }
+ if (sv)
+ {
+ if (ha_rollback_to_savepoint(thd, sv))
+ res= TRUE; // cannot happen
+ else
+ {
+ if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) &&
+ !thd->slave_thread)
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARNING_NOT_COMPLETE_ROLLBACK,
+ ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
+ send_ok(thd);
+ }
+ thd->transaction.savepoints=sv;
}
else
- res= -1;
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str);
break;
}
- case SQLCOM_ROLLBACK:
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
- if (!ha_rollback(thd))
+ case SQLCOM_SAVEPOINT:
+ if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
+ thd->in_sub_stmt) || !opt_using_transactions)
+ send_ok(thd);
+ else
{
+ SAVEPOINT **sv, *newsv;
+ for (sv=&thd->transaction.savepoints; *sv; sv=&(*sv)->prev)
+ {
+ if (my_strnncoll(system_charset_info,
+ (uchar *)lex->ident.str, lex->ident.length,
+ (uchar *)(*sv)->name, (*sv)->length) == 0)
+ break;
+ }
+ if (*sv) /* old savepoint of the same name exists */
+ {
+ newsv=*sv;
+ ha_release_savepoint(thd, *sv); // it cannot fail
+ *sv=(*sv)->prev;
+ }
+ else if ((newsv=(SAVEPOINT *) alloc_root(&thd->transaction.mem_root,
+ savepoint_alloc_size)) == 0)
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ break;
+ }
+ newsv->name=strmake_root(&thd->transaction.mem_root,
+ lex->ident.str, lex->ident.length);
+ newsv->length=lex->ident.length;
/*
- If a non-transactional table was updated, warn; don't warn if this is a
- slave thread (because when a slave thread executes a ROLLBACK, it has
- been read from the binary log, so it's 100% sure and normal to produce
- error ER_WARNING_NOT_COMPLETE_ROLLBACK. If we sent the warning to the
- slave SQL thread, it would not stop the thread but just be printed in
- the error log; but we don't want users to wonder why they have this
- message in the error log, so we don't send it.
+ if we'll get an error here, don't add new savepoint to the list.
+ we'll lose a little bit of memory in transaction mem_root, but it'll
+ be free'd when transaction ends anyway
*/
- if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread)
- send_warning(thd,ER_WARNING_NOT_COMPLETE_ROLLBACK,0);
+ if (ha_savepoint(thd, newsv))
+ res= TRUE;
else
+ {
+ newsv->prev=thd->transaction.savepoints;
+ thd->transaction.savepoints=newsv;
+ send_ok(thd);
+ }
+ }
+ break;
+ case SQLCOM_CREATE_PROCEDURE:
+ case SQLCOM_CREATE_SPFUNCTION:
+ {
+ uint namelen;
+ char *name;
+ int sp_result= SP_INTERNAL_ERROR;
+
+ DBUG_ASSERT(lex->sphead != 0);
+ DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */
+ /*
+ Verify that the database name is allowed, optionally
+ lowercase it.
+ */
+ if (check_db_name(lex->sphead->m_db.str))
+ {
+ my_error(ER_WRONG_DB_NAME, MYF(0), lex->sphead->m_db.str);
+ goto create_sp_error;
+ }
+
+ /*
+ Check that a database directory with this name
+ exists. Design note: This won't work on virtual databases
+ like information_schema.
+ */
+ if (check_db_dir_existence(lex->sphead->m_db.str))
+ {
+ my_error(ER_BAD_DB_ERROR, MYF(0), lex->sphead->m_db.str);
+ goto create_sp_error;
+ }
+
+ if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, 0, 0, 0,
+ is_schema_db(lex->sphead->m_db.str)))
+ goto create_sp_error;
+
+ if (end_active_trans(thd))
+ goto create_sp_error;
+
+ name= lex->sphead->name(&namelen);
+#ifdef HAVE_DLOPEN
+ if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
+ {
+ udf_func *udf = find_udf(name, namelen);
+
+ if (udf)
+ {
+ my_error(ER_UDF_EXISTS, MYF(0), name);
+ goto create_sp_error;
+ }
+ }
+#endif
+
+ /*
+ If the definer is not specified, this means that CREATE-statement missed
+ DEFINER-clause. DEFINER-clause can be missed in two cases:
+
+ - The user submitted a statement w/o the clause. This is a normal
+ case, we should assign CURRENT_USER as definer.
+
+ - Our slave received an updated from the master, that does not
+ replicate definer for stored rountines. We should also assign
+ CURRENT_USER as definer here, but also we should mark this routine
+ as NON-SUID. This is essential for the sake of backward
+ compatibility.
+
+ The problem is the slave thread is running under "special" user (@),
+ that actually does not exist. In the older versions we do not fail
+ execution of a stored routine if its definer does not exist and
+ continue the execution under the authorization of the invoker
+ (BUG#13198). And now if we try to switch to slave-current-user (@),
+ we will fail.
+
+ Actually, this leads to the inconsistent state of master and
+ slave (different definers, different SUID behaviour), but it seems,
+ this is the best we can do.
+ */
+
+ if (!lex->definer)
+ {
+ bool local_res= FALSE;
+ Query_arena original_arena;
+ Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena);
+
+ if (!(lex->definer= create_default_definer(thd)))
+ local_res= TRUE;
+
+ if (ps_arena)
+ thd->restore_active_arena(ps_arena, &original_arena);
+
+ /* Error has been already reported. */
+ if (local_res)
+ goto create_sp_error;
+
+ if (thd->slave_thread)
+ lex->sphead->m_chistics->suid= SP_IS_NOT_SUID;
+ }
+
+ /*
+ If the specified definer differs from the current user, we should check
+ that the current user has SUPER privilege (in order to create a stored
+ routine under another user one must have SUPER privilege).
+ */
+
+ else if (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
+ my_strcasecmp(system_charset_info,
+ lex->definer->host.str,
+ thd->security_ctx->priv_host))
+ {
+ if (check_global_access(thd, SUPER_ACL))
+ {
+ my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
+ goto create_sp_error;
+ }
+ }
+
+ /* Check that the specified definer exists. Emit a warning if not. */
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (!is_acl_user(lex->definer->host.str,
+ lex->definer->user.str))
+ {
+ push_warning_printf(thd,
+ MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_NO_SUCH_USER,
+ ER(ER_NO_SUCH_USER),
+ lex->definer->user.str,
+ lex->definer->host.str);
+ }
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+
+ 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,
+ lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1))
+ {
+ if (sp_grant_privileges(thd, lex->sphead->m_db.str, name,
+ lex->sql_command == SQLCOM_CREATE_PROCEDURE))
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_PROC_AUTO_GRANT_FAIL,
+ ER(ER_PROC_AUTO_GRANT_FAIL));
+ close_thread_tables(thd);
+ }
+#endif
+ break;
+ case SP_WRITE_ROW_FAILED:
+ my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name);
+ break;
+ case SP_BAD_IDENTIFIER:
+ my_error(ER_TOO_LONG_IDENT, MYF(0), name);
+ break;
+ case SP_BODY_TOO_LONG:
+ my_error(ER_TOO_LONG_BODY, MYF(0), name);
+ break;
+ default:
+ my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name);
+ break;
+ } /* end switch */
+
+ /*
+ Capture all errors within this CASE and
+ clean up the environment.
+ */
+create_sp_error:
+ if (sp_result != SP_OK )
+ goto error;
+ send_ok(thd);
+ break; /* break super switch */
+ } /* end case group bracket */
+ case SQLCOM_CALL:
+ {
+ sp_head *sp;
+
+ /*
+ 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) ||
+ open_and_lock_tables(thd, all_tables))
+ goto error;
+
+ /*
+ By this moment all needed SPs should be in cache so no need to look
+ into DB.
+ */
+ if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
+ &thd->sp_proc_cache, TRUE)))
+ {
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
+ lex->spname->m_qname.str);
+ goto error;
+ }
+ else
+ {
+ ha_rows select_limit;
+ /* bits that should be cleared in thd->server_status */
+ uint bits_to_be_cleared= 0;
+ /*
+ Check that the stored procedure doesn't contain Dynamic SQL
+ and doesn't return result sets: such stored procedures can't
+ be called from a function or trigger.
+ */
+ if (thd->in_sub_stmt)
+ {
+ const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ?
+ "trigger" : "function");
+ if (sp->is_not_allowed_in_function(where))
+ 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))
+ {
+ /*
+ The client does not support multiple result sets being sent
+ back
+ */
+ my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str);
+ thd->net.no_send_ok= save_no_send_ok;
+ goto error;
+ }
+ /*
+ If SERVER_MORE_RESULTS_EXISTS is not set,
+ then remember that it should be cleared
+ */
+ bits_to_be_cleared= (~thd->server_status &
+ SERVER_MORE_RESULTS_EXISTS);
+ thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
+ }
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (check_routine_access(thd, EXECUTE_ACL,
+ sp->m_db.str, sp->m_name.str, TRUE, 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;
+
+ /*
+ We never write CALL statements into binlog:
+ - If the mode is non-prelocked, each statement will be logged
+ separately.
+ - If the mode is prelocked, the invoking statement will care
+ about writing into binlog.
+ So just execute the statement.
+ */
+ res= sp->execute_procedure(thd, &lex->value_list);
+ /*
+ If warnings have been cleared, we have to clear total_warn_count
+ too, otherwise the clients get confused.
+ */
+ if (thd->warn_list.is_empty())
+ thd->total_warn_count= 0;
+
+ thd->variables.select_limit= select_limit;
+
+ 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));
+ else
+ goto error; // Substatement should already have sent error
+ }
+ break;
+ }
+ case SQLCOM_ALTER_PROCEDURE:
+ case SQLCOM_ALTER_FUNCTION:
+ {
+ int sp_result;
+ sp_head *sp;
+ st_sp_chistics chistics;
+
+ memcpy(&chistics, &lex->sp_chistics, sizeof(chistics));
+ if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
+ sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
+ &thd->sp_proc_cache, FALSE);
+ else
+ sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
+ &thd->sp_func_cache, FALSE);
+ mysql_reset_errors(thd, 0);
+ if (! sp)
+ {
+ if (lex->spname->m_db.str)
+ sp_result= SP_KEY_NOT_FOUND;
+ else
+ {
+ my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
+ goto error;
+ }
+ }
+ else
+ {
+ if (check_routine_access(thd, ALTER_PROC_ACL, sp->m_db.str,
+ sp->m_name.str,
+ lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0))
+ goto error;
+
+ if (end_active_trans(thd))
+ goto error;
+ memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics));
+ if ((sp->m_type == TYPE_ENUM_FUNCTION) &&
+ !trust_function_creators && mysql_bin_log.is_open() &&
+ !sp->m_chistics->detistic &&
+ (chistics.daccess == SP_CONTAINS_SQL ||
+ chistics.daccess == SP_MODIFIES_SQL_DATA))
+ {
+ my_message(ER_BINLOG_UNSAFE_ROUTINE,
+ ER(ER_BINLOG_UNSAFE_ROUTINE), MYF(0));
+ sp_result= SP_INTERNAL_ERROR;
+ }
+ else
+ {
+ /*
+ Note that if you implement the capability of ALTER FUNCTION to
+ alter the body of the function, this command should be made to
+ follow the restrictions that log-bin-trust-function-creators=0
+ 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);
+ }
+ }
+ switch (sp_result)
+ {
+ case SP_OK:
send_ok(thd);
+ break;
+ case SP_KEY_NOT_FOUND:
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
+ SP_COM_STRING(lex), lex->spname->m_qname.str);
+ goto error;
+ default:
+ my_error(ER_SP_CANT_ALTER, MYF(0),
+ SP_COM_STRING(lex), lex->spname->m_qname.str);
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_DROP_PROCEDURE:
+ case SQLCOM_DROP_FUNCTION:
+ {
+ int sp_result;
+ int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
+ TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
+
+ sp_result= sp_routine_exists_in_table(thd, type, lex->spname);
+ mysql_reset_errors(thd, 0);
+ if (sp_result == SP_OK)
+ {
+ char *db= lex->spname->m_db.str;
+ char *name= lex->spname->m_name.str;
+
+ if (check_routine_access(thd, ALTER_PROC_ACL, db, name,
+ lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
+ goto error;
+
+ if (end_active_trans(thd))
+ goto error;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (sp_automatic_privileges && !opt_noacl &&
+ sp_revoke_privileges(thd, db, name,
+ lex->sql_command == SQLCOM_DROP_PROCEDURE))
+ {
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_PROC_AUTO_REVOKE_FAIL,
+ ER(ER_PROC_AUTO_REVOKE_FAIL));
+ }
+#endif
+ /* 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);
+ }
+ else
+ {
+#ifdef HAVE_DLOPEN
+ if (lex->sql_command == SQLCOM_DROP_FUNCTION)
+ {
+ udf_func *udf = find_udf(lex->spname->m_name.str,
+ lex->spname->m_name.length);
+ if (udf)
+ {
+ if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0, 0))
+ goto error;
+
+ /* Does NOT write to binlog */
+ if (!(res = mysql_drop_function(thd, &lex->spname->m_name)))
+ {
+ send_ok(thd);
+ break;
+ }
+ }
+ }
+#endif
+ if (lex->spname->m_db.str)
+ sp_result= SP_KEY_NOT_FOUND;
+ else
+ {
+ my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
+ goto error;
+ }
+ }
+ res= sp_result;
+ switch (sp_result) {
+ case SP_OK:
+ send_ok(thd);
+ break;
+ case SP_KEY_NOT_FOUND:
+ if (lex->drop_if_exists)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
+ SP_COM_STRING(lex), lex->spname->m_name.str);
+ res= FALSE;
+ send_ok(thd);
+ break;
+ }
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
+ SP_COM_STRING(lex), lex->spname->m_qname.str);
+ goto error;
+ default:
+ my_error(ER_SP_DROP_FAILED, MYF(0),
+ SP_COM_STRING(lex), lex->spname->m_qname.str);
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_SHOW_CREATE_PROC:
+ {
+ if (lex->spname->m_name.length > NAME_LEN)
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
+ goto error;
+ }
+ if (sp_show_create_procedure(thd, lex->spname) != SP_OK)
+ { /* We don't distinguish between errors for now */
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
+ SP_COM_STRING(lex), lex->spname->m_name.str);
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_SHOW_CREATE_FUNC:
+ {
+ if (lex->spname->m_name.length > NAME_LEN)
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
+ goto error;
+ }
+ if (sp_show_create_function(thd, lex->spname) != SP_OK)
+ { /* We don't distinguish between errors for now */
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
+ SP_COM_STRING(lex), lex->spname->m_name.str);
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_SHOW_STATUS_PROC:
+ {
+ res= sp_show_status_procedure(thd, (lex->wild ?
+ lex->wild->ptr() : NullS));
+ break;
+ }
+ case SQLCOM_SHOW_STATUS_FUNC:
+ {
+ res= sp_show_status_function(thd, (lex->wild ?
+ lex->wild->ptr() : NullS));
+ break;
+ }
+#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);
+ else
+ sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
+ &thd->sp_func_cache, FALSE);
+ if (!sp || sp->show_routine_code(thd))
+ {
+ /* 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;
+ }
+#endif // ifndef DBUG_OFF
+ case SQLCOM_CREATE_VIEW:
+ {
+ if (end_active_trans(thd))
+ goto error;
+
+ res= mysql_create_view(thd, first_table, thd->lex->create_view_mode);
+ break;
+ }
+ case SQLCOM_DROP_VIEW:
+ {
+ if (check_table_access(thd, DROP_ACL, all_tables, 0) ||
+ end_active_trans(thd))
+ goto error;
+ /* Conditionally writes to binlog. */
+ res= mysql_drop_view(thd, first_table, thd->lex->drop_mode);
+ break;
+ }
+ case SQLCOM_CREATE_TRIGGER:
+ {
+ if (end_active_trans(thd))
+ goto error;
+
+ /* Conditionally writes to binlog. */
+ res= mysql_create_or_drop_trigger(thd, all_tables, 1);
+
+ break;
+ }
+ case SQLCOM_DROP_TRIGGER:
+ {
+ if (end_active_trans(thd))
+ goto error;
+
+ /* Conditionally writes to binlog. */
+ res= mysql_create_or_drop_trigger(thd, all_tables, 0);
+ break;
+ }
+ case SQLCOM_XA_START:
+ if (thd->transaction.xid_state.xa_state == XA_IDLE &&
+ thd->lex->xa_opt == XA_RESUME)
+ {
+ if (! thd->transaction.xid_state.xid.eq(thd->lex->xid))
+ {
+ my_error(ER_XAER_NOTA, MYF(0));
+ break;
+ }
+ thd->transaction.xid_state.xa_state=XA_ACTIVE;
+ send_ok(thd);
+ break;
+ }
+ if (thd->lex->xa_opt != XA_NONE)
+ { // JOIN is not supported yet. TODO
+ my_error(ER_XAER_INVAL, MYF(0));
+ break;
+ }
+ if (thd->transaction.xid_state.xa_state != XA_NOTR)
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0),
+ xa_state_names[thd->transaction.xid_state.xa_state]);
+ break;
+ }
+ if (thd->active_transaction() || thd->locked_tables)
+ {
+ my_error(ER_XAER_OUTSIDE, MYF(0));
+ break;
+ }
+ if (xid_cache_search(thd->lex->xid))
+ {
+ my_error(ER_XAER_DUPID, MYF(0));
+ break;
+ }
+ DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
+ thd->transaction.xid_state.xa_state=XA_ACTIVE;
+ thd->transaction.xid_state.xid.set(thd->lex->xid);
+ xid_cache_insert(&thd->transaction.xid_state);
+ thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
+ OPTION_BEGIN);
+ thd->server_status|= SERVER_STATUS_IN_TRANS;
+ send_ok(thd);
+ break;
+ case SQLCOM_XA_END:
+ /* fake it */
+ if (thd->lex->xa_opt != XA_NONE)
+ { // SUSPEND and FOR MIGRATE are not supported yet. TODO
+ my_error(ER_XAER_INVAL, MYF(0));
+ break;
+ }
+ if (thd->transaction.xid_state.xa_state != XA_ACTIVE)
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0),
+ xa_state_names[thd->transaction.xid_state.xa_state]);
+ break;
+ }
+ if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
+ {
+ my_error(ER_XAER_NOTA, MYF(0));
+ break;
+ }
+ thd->transaction.xid_state.xa_state=XA_IDLE;
+ send_ok(thd);
+ break;
+ case SQLCOM_XA_PREPARE:
+ if (thd->transaction.xid_state.xa_state != XA_IDLE)
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0),
+ xa_state_names[thd->transaction.xid_state.xa_state]);
+ break;
+ }
+ if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
+ {
+ my_error(ER_XAER_NOTA, MYF(0));
+ break;
+ }
+ if (ha_prepare(thd))
+ {
+ my_error(ER_XA_RBROLLBACK, MYF(0));
+ xid_cache_delete(&thd->transaction.xid_state);
+ thd->transaction.xid_state.xa_state=XA_NOTR;
+ break;
+ }
+ thd->transaction.xid_state.xa_state=XA_PREPARED;
+ send_ok(thd);
+ break;
+ case SQLCOM_XA_COMMIT:
+ if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
+ {
+ XID_STATE *xs=xid_cache_search(thd->lex->xid);
+ if (!xs || xs->in_thd)
+ my_error(ER_XAER_NOTA, MYF(0));
+ else
+ {
+ ha_commit_or_rollback_by_xid(thd->lex->xid, 1);
+ xid_cache_delete(xs);
+ send_ok(thd);
+ }
+ break;
+ }
+ if (thd->transaction.xid_state.xa_state == XA_IDLE &&
+ thd->lex->xa_opt == XA_ONE_PHASE)
+ {
+ int r;
+ if ((r= ha_commit(thd)))
+ my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
+ else
+ send_ok(thd);
+ }
+ else if (thd->transaction.xid_state.xa_state == XA_PREPARED &&
+ thd->lex->xa_opt == XA_NONE)
+ {
+ if (wait_if_global_read_lock(thd, 0, 0))
+ {
+ ha_rollback(thd);
+ my_error(ER_XAER_RMERR, MYF(0));
+ }
+ else
+ {
+ if (ha_commit_one_phase(thd, 1))
+ my_error(ER_XAER_RMERR, MYF(0));
+ else
+ send_ok(thd);
+ start_waiting_global_read_lock(thd);
+ }
}
else
- res= -1;
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0),
+ xa_state_names[thd->transaction.xid_state.xa_state]);
+ break;
+ }
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ xid_cache_delete(&thd->transaction.xid_state);
+ thd->transaction.xid_state.xa_state=XA_NOTR;
break;
- case SQLCOM_ROLLBACK_TO_SAVEPOINT:
- if (!ha_rollback_to_savepoint(thd, lex->savepoint_name))
+ case SQLCOM_XA_ROLLBACK:
+ if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
{
- if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread)
- send_warning(thd, ER_WARNING_NOT_COMPLETE_ROLLBACK, 0);
+ XID_STATE *xs=xid_cache_search(thd->lex->xid);
+ if (!xs || xs->in_thd)
+ my_error(ER_XAER_NOTA, MYF(0));
else
- send_ok(thd);
+ {
+ ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
+ xid_cache_delete(xs);
+ send_ok(thd);
+ }
+ break;
+ }
+ if (thd->transaction.xid_state.xa_state != XA_IDLE &&
+ thd->transaction.xid_state.xa_state != XA_PREPARED)
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0),
+ xa_state_names[thd->transaction.xid_state.xa_state]);
+ break;
}
+ if (ha_rollback(thd))
+ my_error(ER_XAER_RMERR, MYF(0));
else
- res= -1;
- break;
- case SQLCOM_SAVEPOINT:
- if (!ha_savepoint(thd, lex->savepoint_name))
send_ok(thd);
- else
- res= -1;
+ thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ xid_cache_delete(&thd->transaction.xid_state);
+ thd->transaction.xid_state.xa_state=XA_NOTR;
break;
- default: /* Impossible */
+ case SQLCOM_XA_RECOVER:
+ res= mysql_xa_recover(thd);
+ break;
+ default:
+#ifndef EMBEDDED_LIBRARY
+ DBUG_ASSERT(0); /* Impossible */
+#endif
send_ok(thd);
break;
}
- thd->proc_info="query end"; // QQ
+ thd->proc_info="query end";
+ /* Two binlog-related cleanups: */
/*
Reset system variables temporarily modified by SET ONE SHOT.
@@ -3889,43 +5093,119 @@ purposes internal to the MySQL server", MYF(0));
if (thd->one_shot_set && lex->sql_command != SQLCOM_SET_OPTION)
reset_one_shot_variables(thd);
- if (res < 0)
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
+ /*
+ The return value for ROW_COUNT() is "implementation dependent" if the
+ statement is not DELETE, INSERT or UPDATE, 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)
+ thd->row_count_func= -1;
+
+ goto end;
error:
- DBUG_VOID_RETURN;
+ res= TRUE;
+
+end:
+ if (need_start_waiting)
+ {
+ /*
+ Release the protection against the global read lock and wake
+ everyone, who might want to set a global read lock.
+ */
+ start_waiting_global_read_lock(thd);
+ }
+ DBUG_RETURN(res || thd->net.report_error);
}
/*
+ 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
+
+ RETURN
+ 0 - OK
+ 1 - access denied, error is sent to client
+*/
+
+bool check_single_table_access(THD *thd, ulong privilege,
+ TABLE_LIST *all_tables)
+{
+ Security_context * backup_ctx= thd->security_ctx;
+
+ /* we need to switch to the saved context (if any) */
+ if (all_tables->security_ctx)
+ thd->security_ctx= all_tables->security_ctx;
+
+ const char *db_name;
+ if ((all_tables->view || all_tables->field_translation) &&
+ !all_tables->schema_table)
+ db_name= all_tables->view_db.str;
+ else
+ db_name= all_tables->db;
+
+ if (check_access(thd, privilege, db_name,
+ &all_tables->grant.privilege, 0, 0,
+ test(all_tables->schema_table)))
+ goto deny;
+
+ /* Show only 1 table for check_grant */
+ if (grant_option && check_grant(thd, privilege, all_tables, 0, 1, 0))
+ goto deny;
+
+ thd->security_ctx= backup_ctx;
+ return 0;
+
+deny:
+ thd->security_ctx= backup_ctx;
+ 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 privelage
- tables table list of command
+ privilege requested privilege
+ all_tables global table list of query
RETURN
0 - OK
1 - access denied, error is sent to client
*/
-int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables)
+bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
{
- if (check_access(thd, privilege, tables->db, &tables->grant.privilege,0,0))
- return 1;
-
- /* Show only 1 table for check_grant */
- if (grant_option && check_grant(thd, privilege, tables, 0, 1, 0))
+ if (check_single_table_access (thd,privilege,all_tables))
return 1;
/* Check rights on tables of subselects and implictly opened tables */
- TABLE_LIST *subselects_tables;
- if ((subselects_tables= tables->next))
+ TABLE_LIST *subselects_tables, *view= all_tables->view ? all_tables : 0;
+ if ((subselects_tables= all_tables->next_global))
{
- if ((check_table_access(thd, SELECT_ACL, subselects_tables,0)))
+ /*
+ Access rights asked for the first table of a view should be the same
+ as for the view
+ */
+ if (view && subselects_tables->belong_to_view == view)
+ {
+ if (check_single_table_access (thd, privilege, subselects_tables))
+ return 1;
+ subselects_tables= subselects_tables->next_global;
+ }
+ if (subselects_tables &&
+ (check_table_access(thd, SELECT_ACL, subselects_tables, 0)))
return 1;
}
return 0;
@@ -3953,16 +5233,17 @@ int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables)
bool
check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
- bool dont_check_global_grants, bool no_errors)
+ bool dont_check_global_grants, bool no_errors, bool schema_db)
{
- DBUG_ENTER("check_access");
- DBUG_PRINT("enter",("db: '%s' want_access: %lu master_access: %lu",
- db ? db : "", want_access, thd->master_access));
+ Security_context *sctx= thd->security_ctx;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
ulong db_access;
bool db_is_pattern= test(want_access & GRANT_ACL);
#endif
ulong dummy;
+ DBUG_ENTER("check_access");
+ DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu",
+ db ? db : "", want_access, sctx->master_access));
if (save_priv)
*save_priv=0;
else
@@ -3970,36 +5251,61 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
{
+ DBUG_PRINT("error",("No database"));
if (!no_errors)
- send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */
+ my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR),
+ MYF(0)); /* purecov: tested */
DBUG_RETURN(TRUE); /* purecov: tested */
}
+ if (schema_db)
+ {
+ if (want_access & ~(SELECT_ACL | EXTRA_ACL))
+ {
+ if (!no_errors)
+ {
+ const char *db_name= db ? db : thd->db;
+ my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
+ sctx->priv_user, sctx->priv_host, db_name);
+ }
+ DBUG_RETURN(TRUE);
+ }
+ else
+ {
+ *save_priv= SELECT_ACL;
+ DBUG_RETURN(FALSE);
+ }
+ }
+
#ifdef NO_EMBEDDED_ACCESS_CHECKS
DBUG_RETURN(0);
#else
- if ((thd->master_access & want_access) == want_access)
+ if ((sctx->master_access & want_access) == want_access)
{
/*
If we don't have a global SELECT privilege, we have to get the database
specific access rights to be able to handle queries of type
UPDATE t1 SET a=1 WHERE b > 0
*/
- db_access= thd->db_access;
- if (!(thd->master_access & SELECT_ACL) &&
+ db_access= sctx->db_access;
+ if (!(sctx->master_access & SELECT_ACL) &&
(db && (!thd->db || db_is_pattern || strcmp(db,thd->db))))
- db_access=acl_get(thd->host, thd->ip, thd->priv_user, db, db_is_pattern);
- *save_priv=thd->master_access | db_access;
+ db_access=acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
+ db_is_pattern);
+ *save_priv=sctx->master_access | db_access;
DBUG_RETURN(FALSE);
}
- if (((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
+ if (((want_access & ~sctx->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
! db && dont_check_global_grants)
{ // We can never grant this
+ DBUG_PRINT("error",("No possible access"));
if (!no_errors)
- net_printf(thd,ER_ACCESS_DENIED_ERROR,
- thd->priv_user,
- thd->priv_host,
- thd->password ? ER(ER_YES) : ER(ER_NO));/* purecov: tested */
+ my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
+ sctx->priv_user,
+ sctx->priv_host,
+ (thd->password ?
+ ER(ER_YES) :
+ ER(ER_NO))); /* purecov: tested */
DBUG_RETURN(TRUE); /* purecov: tested */
}
@@ -4007,24 +5313,30 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
DBUG_RETURN(FALSE); // Allow select on anything
if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
- db_access=acl_get(thd->host, thd->ip, thd->priv_user, db, db_is_pattern);
+ db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
+ db_is_pattern);
else
- db_access=thd->db_access;
+ db_access= sctx->db_access;
DBUG_PRINT("info",("db_access: %lu", db_access));
/* Remove SHOW attribute and access rights we already have */
- want_access &= ~(thd->master_access | EXTRA_ACL);
- db_access= ((*save_priv=(db_access | thd->master_access)) & want_access);
+ want_access &= ~(sctx->master_access | EXTRA_ACL);
+ DBUG_PRINT("info",("db_access: %lu want_access: %lu",
+ 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) &&
- !(want_access & ~(db_access | TABLE_ACLS))))
+ (grant_option && !dont_check_global_grants &&
+ !(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS))))
DBUG_RETURN(FALSE); /* Ok */
+
+ DBUG_PRINT("error",("Access denied"));
if (!no_errors)
- net_printf(thd,ER_DBACCESS_DENIED_ERROR,
- thd->priv_user,
- thd->priv_host,
- db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */
+ my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
+ sctx->priv_user, sctx->priv_host,
+ (db ? db : (thd->db ?
+ thd->db :
+ "unknown"))); /* purecov: tested */
DBUG_RETURN(TRUE); /* purecov: tested */
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
}
@@ -4039,7 +5351,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
want_access Use should have any of these global rights
WARNING
- One gets access rigth if one has ANY of the rights in want_access
+ One gets access right if one has ANY of the rights in want_access
This is useful as one in most cases only need one global right,
but in some case we want to check if the user has SUPER or
REPL_CLIENT_ACL rights.
@@ -4055,19 +5367,36 @@ bool check_global_access(THD *thd, ulong want_access)
return 0;
#else
char command[128];
- if ((thd->master_access & want_access))
+ if ((thd->security_ctx->master_access & want_access))
return 0;
get_privilege_desc(command, sizeof(command), want_access);
- net_printf(thd,ER_SPECIFIC_ACCESS_DENIED_ERROR,
- command);
+ my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
return 1;
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
}
/*
- Check the privilege for all used tables. Table privileges are cached
- in the table list for GRANT checking
+ 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).
+
+ NOTES
+ 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
*/
bool
@@ -4076,40 +5405,173 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
{
uint found=0;
ulong found_access=0;
- TABLE_LIST *org_tables=tables;
- for (; tables ; tables=tables->next)
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ TABLE_LIST *org_tables= tables;
+#endif
+ TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
+ 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)
{
- if (tables->derived ||
- (tables->table && (int)tables->table->tmp_table) ||
+ if (tables->security_ctx)
+ sctx= tables->security_ctx;
+ else
+ sctx= backup_ctx;
+
+ if (tables->schema_table &&
+ (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
+ {
+ if (!no_errors)
+ my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
+ sctx->priv_user, sctx->priv_host,
+ information_schema_name.str);
+ return TRUE;
+ }
+ /*
+ Register access for view underlying table.
+ Remove SHOW_VIEW_ACL, because it will be checked during making view
+ */
+ tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
+ if (tables->derived || tables->schema_table ||
+ (tables->table && (int)tables->table->s->tmp_table) ||
my_tz_check_n_skip_implicit_tables(&tables,
thd->lex->time_zone_tables_used))
continue;
- if ((thd->master_access & want_access) == (want_access & ~EXTRA_ACL) &&
+ thd->security_ctx= sctx;
+ if ((sctx->master_access & want_access) ==
+ (want_access & ~EXTRA_ACL) &&
thd->db)
tables->grant.privilege= want_access;
- else if (tables->db && tables->db == thd->db)
+ 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))
- return TRUE; // Access denied
+ 0, no_errors, test(tables->schema_table)))
+ goto deny; // Access denied
found_access=tables->grant.privilege;
found=1;
}
}
else if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
- 0, no_errors))
- return TRUE;
+ 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;
+deny:
+ thd->security_ctx= backup_ctx;
+ return TRUE;
+}
+
+
+bool
+check_routine_access(THD *thd, ulong want_access,char *db, char *name,
+ bool is_proc, bool no_errors)
+{
+ TABLE_LIST tables[1];
+
+ bzero((char *)tables, sizeof(TABLE_LIST));
+ tables->db= db;
+ tables->table_name= tables->alias= name;
+
+ /*
+ The following test is just a shortcut for check_access() (to avoid
+ calculating db_access) under the assumption that it's common to
+ give persons global right to execute all stored SP (but not
+ necessary to create them).
+ */
+ if ((thd->security_ctx->master_access & want_access) == want_access)
+ tables->grant.privilege= want_access;
+ else if (check_access(thd,want_access,db,&tables->grant.privilege,
+ 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
+
+ SYNOPSIS
+ check_some_routine_access()
+ thd Thread handler
+ db Database name
+ name Routine name
+
+ RETURN
+ 0 ok
+ 1 error
+*/
+
+bool check_some_routine_access(THD *thd, const char *db, const char *name,
+ bool is_proc)
+{
+ ulong save_priv;
+ if (thd->security_ctx->master_access & SHOW_PROC_ACLS)
+ return FALSE;
+ /*
+ There are no routines in information_schema db. So we can safely
+ pass zero to last paramter of check_access function
+ */
+ if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, 0, 1, 0) ||
+ (save_priv & SHOW_PROC_ACLS))
+ return FALSE;
+ return check_routine_level_acl(thd, db, name, is_proc);
+}
+
+
+/*
+ Check if the given table has any of the asked privileges
+
+ SYNOPSIS
+ check_some_access()
+ thd Thread handler
+ want_access Bitmap of possible privileges to check for
+
+ RETURN
+ 0 ok
+ 1 error
+*/
+
+
+bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
+{
+ ulong access;
+ DBUG_ENTER("check_some_access");
+
+ /* This loop will work as long as we have less than 32 privileges */
+ for (access= 1; access < want_access ; access<<= 1)
+ {
+ if (access & want_access)
+ {
+ if (!check_access(thd, access, table->db,
+ &table->grant.privilege, 0, 1,
+ test(table->schema_table)) &&
+ !grant_option || !check_grant(thd, access, table, 0, 1, 1))
+ DBUG_RETURN(0);
+ }
+ }
+ DBUG_PRINT("exit",("no matching access rights"));
+ DBUG_RETURN(1);
}
+
bool check_merge_table_access(THD *thd, char *db,
TABLE_LIST *table_list)
{
@@ -4118,7 +5580,7 @@ bool check_merge_table_access(THD *thd, char *db,
{
/* Check that all tables use the current database */
TABLE_LIST *tmp;
- for (tmp=table_list; tmp ; tmp=tmp->next)
+ for (tmp= table_list; tmp; tmp= tmp->next_local)
{
if (!tmp->db || !tmp->db[0])
tmp->db=db;
@@ -4132,15 +5594,20 @@ bool check_merge_table_access(THD *thd, char *db,
static bool check_db_used(THD *thd,TABLE_LIST *tables)
{
- for (; tables ; tables=tables->next)
+ char *current_db= NULL;
+ for (; tables; tables= tables->next_global)
{
- if (!tables->db)
+ if (tables->db == NULL)
{
- if (!(tables->db=thd->db))
- {
- send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */
- return TRUE; /* purecov: tested */
- }
+ /*
+ 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(&current_db, 0))
+ return TRUE;
+ tables->db= current_db;
}
}
return FALSE;
@@ -4161,14 +5628,23 @@ long max_stack_used;
#endif
#ifndef EMBEDDED_LIBRARY
-bool check_stack_overrun(THD *thd,char *buf __attribute__((unused)))
+/*
+ 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)))
{
long stack_used;
+ DBUG_ASSERT(thd == current_thd);
if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
- (long) thread_stack_min)
+ (long) (thread_stack - margin))
{
- sprintf(errbuff[0],ER(ER_STACK_OVERRUN),stack_used,thread_stack);
- my_message(ER_STACK_OVERRUN,errbuff[0],MYF(0));
+ 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));
thd->fatal_error();
return 1;
}
@@ -4184,7 +5660,7 @@ bool check_stack_overrun(THD *thd,char *buf __attribute__((unused)))
bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
{
- LEX *lex=current_lex;
+ LEX *lex= current_thd->lex;
ulong old_info=0;
if ((uint) *yystacksize >= MY_YACC_MAX)
return 1;
@@ -4210,6 +5686,7 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
return 0;
}
+
/****************************************************************************
Initialize global thd variables needed for query
****************************************************************************/
@@ -4242,17 +5719,26 @@ void mysql_reset_thd_for_next_command(THD *thd)
DBUG_ENTER("mysql_reset_thd_for_next_command");
thd->free_list= 0;
thd->select_number= 1;
- thd->total_warn_count= 0; // Warnings for this query
- thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
- thd->sent_row_count= thd->examined_row_count= 0;
- thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0;
+ thd->query_start_used= thd->insert_id_used=0;
+ thd->last_insert_id_used_bin_log= FALSE;
+ 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);
+ SERVER_QUERY_NO_INDEX_USED |
+ SERVER_QUERY_NO_GOOD_INDEX_USED);
+ DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx);
thd->tmp_table_used= 0;
- if (opt_bin_log)
- reset_dynamic(&thd->user_var_events);
- thd->clear_error();
+ if (!thd->in_sub_stmt)
+ {
+ 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;
+ }
DBUG_VOID_RETURN;
}
@@ -4262,7 +5748,7 @@ mysql_init_select(LEX *lex)
{
SELECT_LEX *select_lex= lex->current_select;
select_lex->init_select();
- select_lex->select_limit= HA_POS_ERROR;
+ lex->wild= 0;
if (select_lex == &lex->select_lex)
{
DBUG_ASSERT(lex->result == 0);
@@ -4275,50 +5761,70 @@ bool
mysql_new_select(LEX *lex, bool move_down)
{
SELECT_LEX *select_lex;
- if (!(select_lex= new(lex->thd->mem_root) SELECT_LEX()))
- return 1;
- select_lex->select_number= ++lex->thd->select_number;
+ THD *thd= lex->thd;
+ DBUG_ENTER("mysql_new_select");
+
+ if (!(select_lex= new (thd->mem_root) SELECT_LEX()))
+ DBUG_RETURN(1);
+ select_lex->select_number= ++thd->select_number;
+ select_lex->parent_lex= lex; /* Used in init_query. */
select_lex->init_query();
select_lex->init_select();
+ lex->nest_level++;
+ select_lex->nest_level= lex->nest_level;
/*
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.
*/
- if (lex->thd->current_arena->is_stmt_prepare())
+ if (thd->stmt_arena->is_stmt_prepare())
select_lex->uncacheable|= UNCACHEABLE_PREPARE;
-
if (move_down)
{
+ SELECT_LEX_UNIT *unit;
lex->subqueries= TRUE;
/* first select_lex of subselect or derived table */
- SELECT_LEX_UNIT *unit;
- if (!(unit= new(lex->thd->mem_root) SELECT_LEX_UNIT()))
- return 1;
+ if (!(unit= new (thd->mem_root) SELECT_LEX_UNIT()))
+ DBUG_RETURN(1);
unit->init_query();
unit->init_select();
- unit->thd= lex->thd;
+ unit->thd= thd;
unit->include_down(lex->current_select);
unit->link_next= 0;
unit->link_prev= 0;
unit->return_to= lex->current_select;
select_lex->include_down(unit);
- // TODO: assign resolve_mode for fake subquery after merging with new tree
+ /*
+ By default we assume that it is usual subselect and we have outer name
+ resolution context, if no we will assign it to 0 later
+ */
+ select_lex->context.outer_context= &select_lex->outer_select()->context;
}
else
{
+ if (lex->current_select->order_list.first && !lex->current_select->braces)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY");
+ DBUG_RETURN(1);
+ }
select_lex->include_neighbour(lex->current_select);
- if (!select_lex->master_unit()->fake_select_lex &&
- select_lex->master_unit()->add_fake_select_lex(lex->thd))
- return 1;
+ SELECT_LEX_UNIT *unit= select_lex->master_unit();
+ if (!unit->fake_select_lex && unit->add_fake_select_lex(lex->thd))
+ DBUG_RETURN(1);
+ select_lex->context.outer_context=
+ unit->first_select()->context.outer_context;
}
select_lex->master_unit()->global_parameters= select_lex;
select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
lex->current_select= select_lex;
- select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
- return 0;
+ /*
+ in subquery is SELECT query and we allow resolution of names in SELECT
+ list
+ */
+ select_lex->context.resolve_in_select_list= TRUE;
+ DBUG_RETURN(0);
}
/*
@@ -4363,52 +5869,19 @@ void create_select_for_variable(const char *var_name)
DBUG_VOID_RETURN;
}
-static TABLE_LIST* get_table_by_alias(TABLE_LIST* tl, const char* db,
- const char* alias)
-{
- for (;tl;tl= tl->next)
- {
- if (!strcmp(db,tl->db) &&
- tl->alias && !my_strcasecmp(table_alias_charset,tl->alias,alias))
- return tl;
- }
-
- return 0;
-}
-
-/* Sets up lex->auxilliary_table_list */
-void fix_multi_delete_lex(LEX* lex)
-{
- TABLE_LIST *tl;
- TABLE_LIST *good_list= (TABLE_LIST*)lex->select_lex.table_list.first;
-
- for (tl= (TABLE_LIST*)lex->auxilliary_table_list.first; tl; tl= tl->next)
- {
- TABLE_LIST* good_table= get_table_by_alias(good_list,tl->db,tl->alias);
- if (good_table && !good_table->derived)
- {
- /*
- real_name points to a member of Table_ident which is
- allocated via thd->strmake() from THD memroot
- */
- tl->real_name= good_table->real_name;
- tl->real_name_length= good_table->real_name_length;
- good_table->updating= tl->updating;
- }
- }
-}
void mysql_init_multi_delete(LEX *lex)
{
lex->sql_command= SQLCOM_DELETE_MULTI;
mysql_init_select(lex);
- lex->select_lex.select_limit= lex->unit.select_limit_cnt=
- HA_POS_ERROR;
- lex->select_lex.table_list.save_and_clear(&lex->auxilliary_table_list);
+ 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->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.
@@ -4418,11 +5891,17 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
{
DBUG_ENTER("mysql_parse");
+ DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););
+
mysql_init_query(thd, (uchar*) inBuf, length);
if (query_cache_send_result_to_client(thd, inBuf, length) <= 0)
{
LEX *lex= thd->lex;
- if (!yyparse((void *)thd) && ! thd->is_fatal_error)
+
+ sp_cache_flush_obsolete(&thd->sp_proc_cache);
+ sp_cache_flush_obsolete(&thd->sp_func_cache);
+
+ if (!MYSQLparse((void *)thd) && ! thd->is_fatal_error)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (mqh_used && thd->user_connect &&
@@ -4433,9 +5912,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
else
#endif
{
- if (thd->net.report_error)
- send_error(thd, 0, NullS);
- else
+ if (! thd->net.report_error)
{
/*
Binlog logs a string starting from thd->query and having length
@@ -4447,8 +5924,8 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
PROCESSLIST.
Note that we don't need LOCK_thread_count to modify query_length.
*/
- if (lex->found_colon &&
- (thd->query_length= (ulong)(lex->found_colon - thd->query)))
+ if (lex->found_semicolon &&
+ (thd->query_length= (ulong)(lex->found_semicolon - thd->query)))
thd->query_length--;
/* Actually execute the query */
mysql_execute_command(thd);
@@ -4458,12 +5935,21 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
}
else
{
+ DBUG_ASSERT(thd->net.report_error);
DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
thd->is_fatal_error));
+
query_cache_abort(&thd->net);
}
+ if (thd->lex->sphead)
+ {
+ delete thd->lex->sphead;
+ thd->lex->sphead= 0;
+ }
+ lex->unit.cleanup();
thd->proc_info="freeing items";
thd->end_statement();
+ thd->cleanup_after_query();
DBUG_ASSERT(thd->change_list.is_empty());
}
DBUG_VOID_RETURN;
@@ -4484,17 +5970,20 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
{
LEX *lex= thd->lex;
bool error= 0;
+ DBUG_ENTER("mysql_test_parse_for_slave");
mysql_init_query(thd, (uchar*) inBuf, length);
- if (!yyparse((void*) thd) && ! thd->is_fatal_error &&
+ if (!MYSQLparse((void*) thd) && ! thd->is_fatal_error &&
all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
- error= 1; /* Ignore question */
+ error= 1; /* Ignore question */
thd->end_statement();
- return error;
+ thd->cleanup_after_query();
+ DBUG_RETURN(error);
}
#endif
+
/*****************************************************************************
** Store field definition for create
** Return 0 if ok
@@ -4511,13 +6000,11 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
{
register create_field *new_field;
LEX *lex= thd->lex;
- uint allowed_type_modifier=0;
- char warn_buff[MYSQL_ERRMSG_SIZE];
DBUG_ENTER("add_field_to_list");
if (strlen(field_name) > NAME_LEN)
{
- net_printf(thd, ER_TOO_LONG_IDENT, field_name); /* purecov: inspected */
+ my_error(ER_TOO_LONG_IDENT, MYF(0), field_name); /* purecov: inspected */
DBUG_RETURN(1); /* purecov: inspected */
}
if (type_modifier & PRI_KEY_FLAG)
@@ -4550,7 +6037,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
!(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC &&
type == FIELD_TYPE_TIMESTAMP))
{
- net_printf(thd, ER_INVALID_DEFAULT, field_name);
+ my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
DBUG_RETURN(1);
}
else if (default_value->type() == Item::NULL_ITEM)
@@ -4559,323 +6046,54 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
NOT_NULL_FLAG)
{
- net_printf(thd,ER_INVALID_DEFAULT,field_name);
+ my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
DBUG_RETURN(1);
}
}
else if (type_modifier & AUTO_INCREMENT_FLAG)
{
- net_printf(thd, ER_INVALID_DEFAULT, field_name);
+ my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
DBUG_RETURN(1);
}
}
if (on_update_value && type != FIELD_TYPE_TIMESTAMP)
{
- net_printf(thd, ER_INVALID_ON_UPDATE, field_name);
- DBUG_RETURN(1);
- }
-
- if (!(new_field=new create_field()))
+ my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name);
DBUG_RETURN(1);
- new_field->field=0;
- new_field->field_name=field_name;
- new_field->def= default_value;
- new_field->flags= type_modifier;
- new_field->unireg_check= (type_modifier & AUTO_INCREMENT_FLAG ?
- Field::NEXT_NUMBER : Field::NONE);
- new_field->decimals= decimals ? (uint) set_zone(atoi(decimals),0,
- NOT_FIXED_DEC-1) : 0;
- new_field->sql_type=type;
- new_field->length=0;
- new_field->char_length= 0;
- new_field->change=change;
- new_field->interval=0;
- new_field->pack_length=0;
- new_field->charset=cs;
- new_field->geom_type= (Field::geometry_type) uint_geom_type;
-
- if (!comment)
- {
- new_field->comment.str=0;
- new_field->comment.length=0;
- }
- else
- {
- /* In this case comment is always of type Item_string */
- new_field->comment.str= (char*) comment->str;
- new_field->comment.length=comment->length;
- }
- if (length && !(new_field->length= (uint) atoi(length)))
- length=0; /* purecov: inspected */
- uint sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1;
-
- if (new_field->length && new_field->decimals &&
- new_field->length < new_field->decimals+1 &&
- new_field->decimals != NOT_FIXED_DEC)
- new_field->length=new_field->decimals+1; /* purecov: inspected */
-
- switch (type) {
- case FIELD_TYPE_TINY:
- if (!length) new_field->length=MAX_TINYINT_WIDTH+sign_len;
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- break;
- case FIELD_TYPE_SHORT:
- if (!length) new_field->length=MAX_SMALLINT_WIDTH+sign_len;
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- break;
- case FIELD_TYPE_INT24:
- if (!length) new_field->length=MAX_MEDIUMINT_WIDTH+sign_len;
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- break;
- case FIELD_TYPE_LONG:
- if (!length) new_field->length=MAX_INT_WIDTH+sign_len;
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- break;
- case FIELD_TYPE_LONGLONG:
- if (!length) new_field->length=MAX_BIGINT_WIDTH;
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- break;
- case FIELD_TYPE_NULL:
- break;
- case FIELD_TYPE_DECIMAL:
- if (!length)
- {
- if ((new_field->length= new_field->decimals))
- new_field->length++;
- else
- new_field->length= 10; // Default length for DECIMAL
- }
- if (new_field->length < MAX_FIELD_WIDTH) // Skip wrong argument
- {
- new_field->length+=sign_len;
- if (new_field->decimals)
- new_field->length++;
- }
- break;
- case FIELD_TYPE_VAR_STRING:
- if (new_field->length < 4)
- {
- new_field->sql_type= FIELD_TYPE_STRING;
- break;
- }
- /* fall through */
- case FIELD_TYPE_STRING:
- if (new_field->length <= MAX_FIELD_CHARLENGTH || default_value)
- break;
- /* Convert long CHAR() and VARCHAR columns to TEXT or BLOB */
- new_field->sql_type= FIELD_TYPE_BLOB;
- sprintf(warn_buff, ER(ER_AUTO_CONVERT), field_name, "CHAR",
- (cs == &my_charset_bin) ? "BLOB" : "TEXT");
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_AUTO_CONVERT,
- warn_buff);
- /* fall through */
- case FIELD_TYPE_BLOB:
- case FIELD_TYPE_TINY_BLOB:
- case FIELD_TYPE_LONG_BLOB:
- case FIELD_TYPE_MEDIUM_BLOB:
- case FIELD_TYPE_GEOMETRY:
- if (new_field->length)
- {
- /* The user has given a length to the blob column */
- if (new_field->length < 256)
- type= FIELD_TYPE_TINY_BLOB;
- else if (new_field->length < 65536)
- type= FIELD_TYPE_BLOB;
- else if (new_field->length < 256L*256L*256L)
- type= FIELD_TYPE_MEDIUM_BLOB;
- else
- type= FIELD_TYPE_LONG_BLOB;
- new_field->length= 0;
- }
- new_field->sql_type= type;
- if (default_value) // Allow empty as default value
- {
- String str,*res;
- res=default_value->val_str(&str);
- if (res->length())
- {
- net_printf(thd,ER_BLOB_CANT_HAVE_DEFAULT,field_name); /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
- new_field->def=0;
- }
- new_field->flags|=BLOB_FLAG;
- break;
- case FIELD_TYPE_YEAR:
- if (!length || new_field->length != 2)
- new_field->length=4; // Default length
- new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
- break;
- case FIELD_TYPE_FLOAT:
- /* change FLOAT(precision) to FLOAT or DOUBLE */
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- if (length && !decimals)
- {
- uint tmp_length=new_field->length;
- if (tmp_length > PRECISION_FOR_DOUBLE)
- {
- net_printf(thd,ER_WRONG_FIELD_SPEC,field_name);
- DBUG_RETURN(1);
- }
- else if (tmp_length > PRECISION_FOR_FLOAT)
- {
- new_field->sql_type=FIELD_TYPE_DOUBLE;
- new_field->length=DBL_DIG+7; // -[digits].E+###
- }
- else
- new_field->length=FLT_DIG+6; // -[digits].E+##
- new_field->decimals= NOT_FIXED_DEC;
- break;
- }
- if (!length)
- {
- new_field->length = FLT_DIG+6;
- new_field->decimals= NOT_FIXED_DEC;
- }
- break;
- case FIELD_TYPE_DOUBLE:
- allowed_type_modifier= AUTO_INCREMENT_FLAG;
- if (!length)
- {
- new_field->length = DBL_DIG+7;
- new_field->decimals=NOT_FIXED_DEC;
- }
- break;
- case FIELD_TYPE_TIMESTAMP:
- if (!length)
- new_field->length= 14; // Full date YYYYMMDDHHMMSS
- else if (new_field->length != 19)
- {
- /*
- We support only even TIMESTAMP lengths less or equal than 14
- and 19 as length of 4.1 compatible representation.
- */
- new_field->length=((new_field->length+1)/2)*2; /* purecov: inspected */
- new_field->length= min(new_field->length,14); /* purecov: inspected */
- }
- new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
- if (default_value)
- {
- /* Grammar allows only NOW() value for ON UPDATE clause */
- if (default_value->type() == Item::FUNC_ITEM &&
- ((Item_func*)default_value)->functype() == Item_func::NOW_FUNC)
- {
- new_field->unireg_check= (on_update_value?Field::TIMESTAMP_DNUN_FIELD:
- Field::TIMESTAMP_DN_FIELD);
- /*
- We don't need default value any longer moreover it is dangerous.
- Everything handled by unireg_check further.
- */
- new_field->def= 0;
- }
- else
- new_field->unireg_check= (on_update_value?Field::TIMESTAMP_UN_FIELD:
- Field::NONE);
- }
- else
- {
- /*
- If we have default TIMESTAMP NOT NULL column without explicit DEFAULT
- or ON UPDATE values then for the sake of compatiblity we should treat
- this column as having DEFAULT NOW() ON UPDATE NOW() (when we don't
- have another TIMESTAMP column with auto-set option before this one)
- or DEFAULT 0 (in other cases).
- So here we are setting TIMESTAMP_OLD_FIELD only temporary, and will
- replace this value by TIMESTAMP_DNUN_FIELD or NONE later when
- information about all TIMESTAMP fields in table will be availiable.
-
- If we have TIMESTAMP NULL column without explicit DEFAULT value
- we treat it as having DEFAULT NULL attribute.
- */
- new_field->unireg_check= on_update_value ?
- Field::TIMESTAMP_UN_FIELD :
- (new_field->flags & NOT_NULL_FLAG ?
- Field::TIMESTAMP_OLD_FIELD:
- Field::NONE);
- }
- break;
- case FIELD_TYPE_DATE: // Old date type
- if (protocol_version != PROTOCOL_VERSION-1)
- new_field->sql_type=FIELD_TYPE_NEWDATE;
- /* fall trough */
- case FIELD_TYPE_NEWDATE:
- new_field->length=10;
- break;
- case FIELD_TYPE_TIME:
- new_field->length=10;
- break;
- case FIELD_TYPE_DATETIME:
- new_field->length=19;
- break;
- case FIELD_TYPE_SET:
- {
- if (interval_list->elements > sizeof(longlong)*8)
- {
- net_printf(thd,ER_TOO_BIG_SET,field_name); /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
- new_field->pack_length= get_set_pack_length(interval_list->elements);
-
- List_iterator<String> it(*interval_list);
- String *tmp;
- while ((tmp= it++))
- new_field->interval_list.push_back(tmp);
- /*
- Set fake length to 1 to pass the below conditions.
- Real length will be set in mysql_prepare_table()
- when we know the character set of the column
- */
- new_field->length= 1;
- }
- break;
- case FIELD_TYPE_ENUM:
- {
- // Should be safe
- new_field->pack_length= get_enum_pack_length(interval_list->elements);
-
- List_iterator<String> it(*interval_list);
- String *tmp;
- while ((tmp= it++))
- new_field->interval_list.push_back(tmp);
- new_field->length= 1; // See comment for FIELD_TYPE_SET above.
- }
- break;
}
- if ((new_field->length > MAX_FIELD_CHARLENGTH && type != FIELD_TYPE_SET &&
- type != FIELD_TYPE_ENUM) ||
- (!new_field->length && !(new_field->flags & BLOB_FLAG) &&
- type != FIELD_TYPE_STRING &&
- type != FIELD_TYPE_VAR_STRING && type != FIELD_TYPE_GEOMETRY))
- {
- net_printf(thd,ER_TOO_BIG_FIELDLENGTH,field_name,
- MAX_FIELD_CHARLENGTH); /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
- type_modifier&= AUTO_INCREMENT_FLAG;
- if ((~allowed_type_modifier) & type_modifier)
+ if (type == FIELD_TYPE_TIMESTAMP && length)
{
- net_printf(thd,ER_WRONG_FIELD_SPEC,field_name);
+ /* Display widths are no longer supported for TIMSTAMP as of MySQL 4.1.
+ In other words, for declarations such as TIMESTAMP(2), TIMESTAMP(4),
+ and so on, the display width is ignored.
+ */
+ 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");
+ }
+
+ if (!(new_field= new create_field()) ||
+ new_field->init(thd, field_name, type, length, decimals, type_modifier,
+ default_value, on_update_value, comment, change,
+ interval_list, cs, uint_geom_type))
DBUG_RETURN(1);
- }
- if (!new_field->pack_length)
- new_field->pack_length=calc_pack_length(new_field->sql_type ==
- FIELD_TYPE_VAR_STRING ?
- FIELD_TYPE_STRING :
- new_field->sql_type,
- new_field->length);
- new_field->char_length= new_field->length;
+
lex->alter_info.create_list.push_back(new_field);
lex->last_field=new_field;
DBUG_RETURN(0);
}
+
/* Store position for column in ALTER TABLE .. ADD column */
void store_position_for_column(const char *name)
{
- current_lex->last_field->after=my_const_cast(char*) (name);
+ current_thd->lex->last_field->after=my_const_cast(char*) (name);
}
bool
@@ -4909,7 +6127,6 @@ static void remove_escape(char *name)
{
#ifdef USE_MB
int l;
-/* if ((l = ismbchar(name, name+MBMAXLEN))) { Wei He: I think it's wrong */
if (use_mb(system_charset_info) &&
(l = my_ismbchar(system_charset_info, name, strend)))
{
@@ -4942,6 +6159,7 @@ bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc)
order->asc = asc;
order->free_me=0;
order->used=0;
+ order->counter_used= 0;
list.link_in_list((byte*) order,(byte**) &order->next);
DBUG_RETURN(0);
}
@@ -4977,17 +6195,19 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
LEX_STRING *option)
{
register TABLE_LIST *ptr;
+ TABLE_LIST *previous_table_ref; /* The table preceding the current one. */
char *alias_str;
+ LEX *lex= thd->lex;
DBUG_ENTER("add_table_to_list");
+ LINT_INIT(previous_table_ref);
if (!table)
DBUG_RETURN(0); // End of memory
alias_str= alias ? alias->str : table->table.str;
- if (!test(table_options & TL_OPTION_ALIAS) &&
- check_table_name(table->table.str,table->table.length) ||
- table->db.str && check_db_name(table->db.str))
+ if (!test(table_options & TL_OPTION_ALIAS) &&
+ check_table_name(table->table.str, table->table.length))
{
- net_printf(thd, ER_WRONG_TABLE_NAME, table->table.str);
+ my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str);
DBUG_RETURN(0);
}
@@ -4995,7 +6215,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
{
if (table->sel)
{
- net_printf(thd,ER_DERIVED_MUST_HAVE_ALIAS);
+ my_message(ER_DERIVED_MUST_HAVE_ALIAS,
+ ER(ER_DERIVED_MUST_HAVE_ALIAS), MYF(0));
DBUG_RETURN(0);
}
if (!(alias_str=thd->memdup(alias_str,table->table.length+1)))
@@ -5005,33 +6226,43 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
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;
}
- else if (thd->db)
- {
- ptr->db= thd->db;
- ptr->db_length= thd->db_length;
- }
- else
- {
- /* The following can't be "" as we may do 'casedn_str()' on it */
- ptr->db= empty_c_string;
- ptr->db_length= 0;
- }
- if (thd->current_arena->is_stmt_prepare())
- ptr->db= thd->strdup(ptr->db);
+ else if (thd->copy_db_to(&ptr->db, &ptr->db_length))
+ DBUG_RETURN(0);
ptr->alias= alias_str;
if (lower_case_table_names && table->table.length)
- my_casedn_str(files_charset_info, table->table.str);
- ptr->real_name=table->table.str;
- ptr->real_name_length=table->table.length;
+ table->table.length= my_casedn_str(files_charset_info, table->table.str);
+ ptr->table_name=table->table.str;
+ ptr->table_name_length=table->table.length;
ptr->lock_type= lock_type;
ptr->updating= test(table_options & TL_OPTION_UPDATING);
ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
ptr->derived= table->sel;
+ if (!ptr->derived && !my_strcasecmp(system_charset_info, ptr->db,
+ information_schema_name.str))
+ {
+ ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, ptr->table_name);
+ if (!schema_table ||
+ (schema_table->hidden &&
+ lex->orig_sql_command == SQLCOM_END)) // not a 'show' command
+ {
+ my_error(ER_UNKNOWN_TABLE, MYF(0),
+ ptr->table_name, information_schema_name.str);
+ DBUG_RETURN(0);
+ }
+ ptr->schema_table_name= ptr->table_name;
+ ptr->schema_table= schema_table;
+ }
+ ptr->select_lex= lex->current_select;
ptr->cacheable_table= 1;
if (use_index_arg)
ptr->use_index=(List<String> *) thd->memdup((gptr) use_index_arg,
@@ -5043,24 +6274,275 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
/* check that used name is unique */
if (lock_type != TL_IGNORE)
{
- for (TABLE_LIST *tables=(TABLE_LIST*) table_list.first ;
+ TABLE_LIST *first_table= (TABLE_LIST*) table_list.first;
+ if (lex->sql_command == SQLCOM_CREATE_VIEW)
+ first_table= first_table ? first_table->next_local : NULL;
+ for (TABLE_LIST *tables= first_table ;
tables ;
- tables=tables->next)
+ tables=tables->next_local)
{
if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
!strcmp(ptr->db, tables->db))
{
- net_printf(thd,ER_NONUNIQ_TABLE,alias_str); /* purecov: tested */
+ my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str); /* purecov: tested */
DBUG_RETURN(0); /* purecov: tested */
}
}
}
- table_list.link_in_list((byte*) ptr, (byte**) &ptr->next);
+ /* Store the table reference preceding the current one. */
+ if (table_list.elements > 0)
+ {
+ /*
+ table_list.next points to the last inserted TABLE_LIST->next_local'
+ element
+ We don't use the offsetof() macro here to avoid warnings from gcc
+ */
+ previous_table_ref= (TABLE_LIST*) ((char*) table_list.next -
+ ((char*) &(ptr->next_local) -
+ (char*) ptr));
+ /*
+ Set next_name_resolution_table of the previous table reference to point
+ to the current table reference. In effect the list
+ TABLE_LIST::next_name_resolution_table coincides with
+ TABLE_LIST::next_local. Later this may be changed in
+ store_top_level_join_columns() for NATURAL/USING joins.
+ */
+ previous_table_ref->next_name_resolution_table= ptr;
+ }
+
+ /*
+ Link the current table reference in a local list (list for current select).
+ Notice that as a side effect here we set the next_local field of the
+ 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);
+ ptr->next_name_resolution_table= NULL;
+ /* Link table in global list (all used tables) */
+ lex->add_to_query_tables(ptr);
+ DBUG_RETURN(ptr);
+}
+
+
+/*
+ Initialize a new table list for a nested join
+
+ SYNOPSIS
+ init_nested_join()
+ thd current thread
+
+ DESCRIPTION
+ The function initializes a structure of the TABLE_LIST type
+ for a nested join. It sets up its nested join list as empty.
+ The created structure is added to the front of the current
+ join list in the st_select_lex object. Then the function
+ changes the current nest level for joins to refer to the newly
+ created empty list after having saved the info on the old level
+ in the initialized structure.
+
+ RETURN VALUE
+ 0, if success
+ 1, otherwise
+*/
+
+bool st_select_lex::init_nested_join(THD *thd)
+{
+ TABLE_LIST *ptr;
+ NESTED_JOIN *nested_join;
+ DBUG_ENTER("init_nested_join");
+
+ if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
+ sizeof(NESTED_JOIN))))
+ DBUG_RETURN(1);
+ nested_join= ptr->nested_join=
+ ((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
+
+ join_list->push_front(ptr);
+ ptr->embedding= embedding;
+ ptr->join_list= join_list;
+ embedding= ptr;
+ join_list= &nested_join->join_list;
+ join_list->empty();
+ DBUG_RETURN(0);
+}
+
+
+/*
+ End a nested join table list
+
+ SYNOPSIS
+ end_nested_join()
+ thd current thread
+
+ DESCRIPTION
+ The function returns to the previous join nest level.
+ If the current level contains only one member, the function
+ moves it one level up, eliminating the nest.
+
+ RETURN VALUE
+ Pointer to TABLE_LIST element added to the total table list, if success
+ 0, otherwise
+*/
+
+TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
+{
+ TABLE_LIST *ptr;
+ NESTED_JOIN *nested_join;
+ DBUG_ENTER("end_nested_join");
+
+ DBUG_ASSERT(embedding);
+ ptr= embedding;
+ join_list= ptr->join_list;
+ embedding= ptr->embedding;
+ nested_join= ptr->nested_join;
+ if (nested_join->join_list.elements == 1)
+ {
+ TABLE_LIST *embedded= nested_join->join_list.head();
+ join_list->pop();
+ embedded->join_list= join_list;
+ embedded->embedding= embedding;
+ join_list->push_front(embedded);
+ ptr= embedded;
+ }
+ else if (nested_join->join_list.elements == 0)
+ {
+ join_list->pop();
+ ptr= 0; // return value
+ }
+ DBUG_RETURN(ptr);
+}
+
+
+/*
+ Nest last join operation
+
+ SYNOPSIS
+ nest_last_join()
+ thd current thread
+
+ DESCRIPTION
+ The function nest last join operation as if it was enclosed in braces.
+
+ RETURN VALUE
+ 0 Error
+ # Pointer to TABLE_LIST element created for the new nested join
+
+*/
+
+TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
+{
+ TABLE_LIST *ptr;
+ NESTED_JOIN *nested_join;
+ List<TABLE_LIST> *embedded_list;
+ DBUG_ENTER("nest_last_join");
+
+ if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
+ sizeof(NESTED_JOIN))))
+ DBUG_RETURN(0);
+ nested_join= ptr->nested_join=
+ ((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
+
+ ptr->embedding= embedding;
+ ptr->join_list= join_list;
+ embedded_list= &nested_join->join_list;
+ embedded_list->empty();
+
+ for (uint i=0; i < 2; i++)
+ {
+ TABLE_LIST *table= join_list->pop();
+ table->join_list= embedded_list;
+ table->embedding= ptr;
+ embedded_list->push_back(table);
+ if (table->natural_join)
+ {
+ ptr->is_natural_join= TRUE;
+ /*
+ If this is a JOIN ... USING, move the list of joined fields to the
+ table reference that describes the join.
+ */
+ if (prev_join_using)
+ ptr->join_using_fields= prev_join_using;
+ }
+ }
+ join_list->push_front(ptr);
+ nested_join->used_tables= nested_join->not_null_tables= (table_map) 0;
DBUG_RETURN(ptr);
}
/*
+ Add a table to the current join list
+
+ SYNOPSIS
+ add_joined_table()
+ table the table to add
+
+ DESCRIPTION
+ The function puts a table in front of the current join list
+ of st_select_lex object.
+ Thus, joined tables are put into this list in the reverse order
+ (the most outer join operation follows first).
+
+ RETURN VALUE
+ None
+*/
+
+void st_select_lex::add_joined_table(TABLE_LIST *table)
+{
+ DBUG_ENTER("add_joined_table");
+ join_list->push_front(table);
+ table->join_list= join_list;
+ table->embedding= embedding;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Convert a right join into equivalent left join
+
+ SYNOPSIS
+ convert_right_join()
+ thd current thread
+
+ DESCRIPTION
+ The function takes the current join list t[0],t[1] ... and
+ effectively converts it into the list t[1],t[0] ...
+ Although the outer_join flag for the new nested table contains
+ JOIN_TYPE_RIGHT, it will be handled as the inner table of a left join
+ operation.
+
+ EXAMPLES
+ SELECT * FROM t1 RIGHT JOIN t2 ON on_expr =>
+ SELECT * FROM t2 LEFT JOIN t1 ON on_expr
+
+ SELECT * FROM t1,t2 RIGHT JOIN t3 ON on_expr =>
+ SELECT * FROM t1,t3 LEFT JOIN t2 ON on_expr
+
+ SELECT * FROM t1,t2 RIGHT JOIN (t3,t4) ON on_expr =>
+ SELECT * FROM t1,(t3,t4) LEFT JOIN t2 ON on_expr
+
+ SELECT * FROM t1 LEFT JOIN t2 ON on_expr1 RIGHT JOIN t3 ON on_expr2 =>
+ SELECT * FROM t3 LEFT JOIN (t1 LEFT JOIN t2 ON on_expr2) ON on_expr1
+
+ RETURN
+ Pointer to the table representing the inner table, if success
+ 0, otherwise
+*/
+
+TABLE_LIST *st_select_lex::convert_right_join()
+{
+ TABLE_LIST *tab2= join_list->pop();
+ TABLE_LIST *tab1= join_list->pop();
+ DBUG_ENTER("convert_right_join");
+
+ join_list->push_front(tab2);
+ join_list->push_front(tab1);
+ tab1->outer_join|= JOIN_TYPE_RIGHT;
+
+ DBUG_RETURN(tab1);
+}
+
+/*
Set lock for all tables in current select level
SYNOPSIS:
@@ -5080,9 +6562,9 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
DBUG_PRINT("enter", ("lock_type: %d for_update: %d", lock_type,
for_update));
- for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first ;
- tables ;
- tables=tables->next)
+ for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first;
+ tables;
+ tables= tables->next_local)
{
tables->lock_type= lock_type;
tables->updating= for_update;
@@ -5115,20 +6597,26 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
0 on success
*/
-bool st_select_lex_unit::add_fake_select_lex(THD *thd)
+bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
{
SELECT_LEX *first_sl= first_select();
DBUG_ENTER("add_fake_select_lex");
DBUG_ASSERT(!fake_select_lex);
-
- if (!(fake_select_lex= new (thd->mem_root) SELECT_LEX()))
+
+ if (!(fake_select_lex= new (thd_arg->mem_root) SELECT_LEX()))
DBUG_RETURN(1);
fake_select_lex->include_standalone(this,
(SELECT_LEX_NODE**)&fake_select_lex);
fake_select_lex->select_number= INT_MAX;
+ fake_select_lex->parent_lex= thd_arg->lex; /* Used in init_query. */
fake_select_lex->make_empty_select();
fake_select_lex->linkage= GLOBAL_OPTIONS_TYPE;
- fake_select_lex->select_limit= HA_POS_ERROR;
+ fake_select_lex->select_limit= 0;
+
+ fake_select_lex->context.outer_context=first_sl->context.outer_context;
+ /* allow item list resolving in fake select for ORDER BY */
+ fake_select_lex->context.resolve_in_select_list= TRUE;
+ fake_select_lex->context.select_lex= fake_select_lex;
if (!first_sl->next_select())
{
@@ -5140,21 +6628,80 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd)
*/
global_parameters= fake_select_lex;
fake_select_lex->no_table_names_allowed= 1;
- thd->lex->current_select= fake_select_lex;
+ thd_arg->lex->current_select= fake_select_lex;
}
+ thd_arg->lex->pop_context();
DBUG_RETURN(0);
}
-void add_join_on(TABLE_LIST *b,Item *expr)
+
+/*
+ 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
+ FALSE if all is OK
+ TRUE if a memory allocation error occured
+*/
+
+bool
+push_new_name_resolution_context(THD *thd,
+ TABLE_LIST *left_op, TABLE_LIST *right_op)
+{
+ Name_resolution_context *on_context;
+ if (!(on_context= new (thd->mem_root) Name_resolution_context))
+ return TRUE;
+ on_context->init();
+ on_context->first_name_resolution_table=
+ left_op->first_leaf_for_name_resolution();
+ on_context->last_name_resolution_table=
+ right_op->last_leaf_for_name_resolution();
+ return thd->lex->push_context(on_context);
+}
+
+
+/*
+ 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
+ FALSE if there was some error
+ TRUE if all is OK
+*/
+
+void add_join_on(TABLE_LIST *b, Item *expr)
{
if (expr)
{
if (!b->on_expr)
- b->on_expr=expr;
+ b->on_expr= expr;
else
{
- // This only happens if you have both a right and left join
- b->on_expr=new Item_cond_and(b->on_expr,expr);
+ /*
+ If called from the parser, this happens if you have both a
+ right and left join. If called later, it happens if we add more
+ than one condition to the ON clause.
+ */
+ b->on_expr= new Item_cond_and(b->on_expr,expr);
}
b->on_expr->top_level_item();
}
@@ -5162,46 +6709,69 @@ void add_join_on(TABLE_LIST *b,Item *expr)
/*
- Mark that we have a NATURAL JOIN between two tables
+ Mark that there is a NATURAL JOIN or JOIN ... USING between two
+ tables.
SYNOPSIS
add_join_natural()
- a Table to do normal join with
- b Do normal join with this table
+ a Left join argument
+ b Right join argument
+ using_fields Field names from USING clause
+ lex The current st_select_lex
IMPLEMENTATION
- This function just marks that table b should be joined with a.
- The function setup_cond() will create in b->on_expr a list
- of equal condition between all fields of the same name.
-
+ 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
+ setup_conds() creates a list of equal condition between all fields
+ of the same name for NATURAL JOIN or the fields in 'using_fields'
+ for JOIN ... USING. The list of equality conditions is stored
+ either in b->on_expr, or in JOIN::conds, depending on whether there
+ was an outer join.
+
+ EXAMPLE
SELECT * FROM t1 NATURAL LEFT JOIN t2
<=>
SELECT * FROM t1 LEFT JOIN t2 ON (t1.i=t2.i and t1.j=t2.j ... )
+
+ SELECT * FROM t1 NATURAL JOIN t2 WHERE <some_cond>
+ <=>
+ SELECT * FROM t1, t2 WHERE (t1.i=t2.i and t1.j=t2.j and <some_cond>)
+
+ SELECT * FROM t1 JOIN t2 USING(j) WHERE <some_cond>
+ <=>
+ SELECT * FROM t1, t2 WHERE (t1.j=t2.j and <some_cond>)
+
+ RETURN
+ None
*/
-void add_join_natural(TABLE_LIST *a,TABLE_LIST *b)
+void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
+ SELECT_LEX *lex)
{
- b->natural_join=a;
+ b->natural_join= a;
+ lex->prev_join_using= using_fields;
}
+
/*
Reload/resets privileges and the different caches.
SYNOPSIS
reload_acl_and_cache()
- thd Thread handler
+ 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, if it is not NULL, reload_acl_and_cache()
- will put 0 if it thinks we really should not write to
- the binlog. Otherwise it will put 1.
+ 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
+ !=0 error. thd->killed or thd->net.report_error is set
*/
bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
@@ -5210,6 +6780,9 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
bool result=0;
select_errors=0; /* Write if more errors */
bool tmp_write_to_binlog= 1;
+
+ DBUG_ASSERT(!thd || !thd->in_sub_stmt);
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (options & REFRESH_GRANT)
{
@@ -5219,13 +6792,14 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
allocate temporary THD for execution of acl_reload()/grant_reload().
*/
if (!thd && (thd= (tmp_thd= new THD)))
+ {
+ thd->thread_stack= (char*) &tmp_thd;
thd->store_globals();
+ }
if (thd)
{
(void)acl_reload(thd);
(void)grant_reload(thd);
- if (mqh_used)
- reset_mqh(thd, (LEX_USER *) NULL, TRUE);
}
if (tmp_thd)
{
@@ -5234,6 +6808,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
my_pthread_setspecific_ptr(THR_THD, 0);
thd= 0;
}
+ reset_mqh((LEX_USER *)NULL, TRUE);
}
#endif
if (options & REFRESH_LOG)
@@ -5244,23 +6819,19 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
*/
/*
- Writing this command to the binlog may result in infinite loops when
- doing mysqlbinlog|mysql, and anyway it does not really make sense to
- log it automatically (would cause more trouble to users than it would
- help them)
+ Writing this command to the binlog may result in infinite loops
+ when doing mysqlbinlog|mysql, and anyway it does not really make
+ sense to log it automatically (would cause more trouble to users
+ than it would help them)
*/
tmp_write_to_binlog= 0;
mysql_log.new_file(1);
- mysql_update_log.new_file(1);
- mysql_bin_log.new_file(1);
mysql_slow_log.new_file(1);
-#ifdef HAVE_REPLICATION
- if (mysql_bin_log.is_open() && expire_logs_days)
+ if( mysql_bin_log.is_open() )
{
- long purge_time= time(0) - expire_logs_days*24*60*60;
- if (purge_time >= 0)
- mysql_bin_log.purge_logs_before_date(purge_time);
+ mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
}
+#ifdef HAVE_REPLICATION
pthread_mutex_lock(&LOCK_active_mi);
rotate_relay_log(active_mi);
pthread_mutex_unlock(&LOCK_active_mi);
@@ -5274,7 +6845,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
if (options & REFRESH_QUERY_CACHE_FREE)
{
query_cache.pack(); // FLUSH QUERY CACHE
- options &= ~REFRESH_QUERY_CACHE; //don't flush all cache, just free memory
+ options &= ~REFRESH_QUERY_CACHE; // Don't flush cache, just free memory
}
if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
{
@@ -5314,10 +6885,15 @@ 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;
+ return 1; // Killed
result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1,
tables);
- make_global_read_lock_block_commit(thd);
+ if (make_global_read_lock_block_commit(thd)) // Killed
+ {
+ /* Don't leave things in a half-locked state */
+ unlock_global_read_lock(thd);
+ return 1;
+ }
}
else
result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables);
@@ -5325,16 +6901,20 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
}
if (options & REFRESH_HOSTS)
hostname_cache_refresh();
- if (options & REFRESH_STATUS)
- refresh_status();
+ if (thd && (options & REFRESH_STATUS))
+ refresh_status(thd);
if (options & REFRESH_THREADS)
flush_thread_cache();
#ifdef HAVE_REPLICATION
if (options & REFRESH_MASTER)
{
+ DBUG_ASSERT(thd);
tmp_write_to_binlog= 0;
if (reset_master(thd))
+ {
result=1;
+ thd->fatal_error(); // Ensure client get error
+ }
}
#endif
#ifdef OPENSSL
@@ -5355,9 +6935,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
}
#endif
if (options & REFRESH_USER_RESOURCES)
- reset_mqh(thd,(LEX_USER *) NULL);
- if (write_to_binlog)
- *write_to_binlog= tmp_write_to_binlog;
+ reset_mqh((LEX_USER *) NULL);
+ *write_to_binlog= tmp_write_to_binlog;
return result;
}
@@ -5373,7 +6952,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
This is written such that we have a short lock on LOCK_thread_count
*/
-void kill_one_thread(THD *thd, ulong id)
+void kill_one_thread(THD *thd, ulong id, bool only_kill_query)
{
THD *tmp;
uint error=ER_NO_SUCH_THREAD;
@@ -5390,10 +6969,10 @@ void kill_one_thread(THD *thd, ulong id)
VOID(pthread_mutex_unlock(&LOCK_thread_count));
if (tmp)
{
- if ((thd->master_access & SUPER_ACL) ||
- !strcmp(thd->user,tmp->user))
+ if ((thd->security_ctx->master_access & SUPER_ACL) ||
+ !strcmp(thd->security_ctx->user, tmp->security_ctx->user))
{
- tmp->awake(1 /*prepare to die*/);
+ tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
error=0;
}
else
@@ -5404,23 +6983,7 @@ void kill_one_thread(THD *thd, ulong id)
if (!error)
send_ok(thd);
else
- net_printf(thd,error,id);
-}
-
-
-/* Clear most status variables */
-
-static void refresh_status(void)
-{
- pthread_mutex_lock(&LOCK_status);
- for (struct show_var_st *ptr=status_vars; ptr->name; ptr++)
- {
- if (ptr->type == SHOW_LONG)
- *(ulong*) ptr->value= 0;
- }
- /* Reset the counters of all key caches (default and named). */
- process_key_caches(reset_key_cache_counters);
- pthread_mutex_unlock(&LOCK_status);
+ my_error(error, MYF(0), id);
}
@@ -5465,12 +7028,13 @@ static bool append_file_to_dir(THD *thd, const char **filename_ptr,
bool check_simple_select()
{
THD *thd= current_thd;
- if (thd->lex->current_select != &thd->lex->select_lex)
+ LEX *lex= thd->lex;
+ if (lex->current_select != &lex->select_lex)
{
char command[80];
- strmake(command, thd->lex->yylval->symbol.str,
- min(thd->lex->yylval->symbol.length, sizeof(command)-1));
- net_printf(thd, ER_CANT_USE_OPTION_HERE, command);
+ strmake(command, lex->yylval->symbol.str,
+ min(lex->yylval->symbol.length, sizeof(command)-1));
+ my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command);
return 1;
}
return 0;
@@ -5552,51 +7116,46 @@ Item * all_any_subquery_creator(Item *left_expr,
SYNOPSIS
multi_update_precheck()
thd Thread handler
- tables Global table list
+ tables Global/local table list (have to be the same)
RETURN VALUE
- 0 OK
- 1 Error (message is sent to user)
- -1 Error (message is not sent to user)
+ FALSE OK
+ TRUE Error
*/
-int multi_update_precheck(THD *thd, TABLE_LIST *tables)
+bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
{
- DBUG_ENTER("multi_update_precheck");
const char *msg= 0;
TABLE_LIST *table;
LEX *lex= thd->lex;
SELECT_LEX *select_lex= &lex->select_lex;
- TABLE_LIST *update_list= (TABLE_LIST*)select_lex->table_list.first;
+ DBUG_ENTER("multi_update_precheck");
if (select_lex->item_list.elements != lex->value_list.elements)
{
- my_error(ER_WRONG_VALUE_COUNT, MYF(0));
- DBUG_RETURN(-1);
+ my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
+ DBUG_RETURN(TRUE);
}
/*
Ensure that we have UPDATE or SELECT privilege for each table
The exact privilege is checked in mysql_multi_update()
*/
- for (table= update_list; table; table= table->next)
+ for (table= tables; table; table= table->next_local)
{
if (table->derived)
table->grant.privilege= SELECT_ACL;
else if ((check_access(thd, UPDATE_ACL, table->db,
- &table->grant.privilege, 0, 1) ||
+ &table->grant.privilege, 0, 1,
+ test(table->schema_table)) ||
grant_option &&
check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) &&
- (check_access(thd, SELECT_ACL, table->db,
- &table->grant.privilege, 0, 0) ||
- grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
- DBUG_RETURN(1);
+ (check_access(thd, SELECT_ACL, table->db,
+ &table->grant.privilege, 0, 0,
+ test(table->schema_table)) ||
+ grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
+ DBUG_RETURN(TRUE);
- /*
- We assign following flag only to copy of table, because it will
- be checked only if query contains subqueries i.e. only if copy exists
- */
- if (table->table_list)
- table->table_list->table_in_update_from_clause= 1;
+ table->table_in_first_from_clause= 1;
}
/*
Is there tables of subqueries?
@@ -5604,42 +7163,31 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables)
if (&lex->select_lex != lex->all_selects_list || lex->time_zone_tables_used)
{
DBUG_PRINT("info",("Checking sub query list"));
- for (table= tables; table; table= table->next)
+ for (table= tables; table; table= table->next_global)
{
- if (my_tz_check_n_skip_implicit_tables(&table,
- lex->time_zone_tables_used))
- continue;
- else if (table->table_in_update_from_clause)
- {
- /*
- If we check table by local TABLE_LIST copy then we should copy
- grants to global table list, because it will be used for table
- opening.
- */
- if (table->table_list)
- table->grant= table->table_list->grant;
- }
- else if (!table->derived)
+ if (!my_tz_check_n_skip_implicit_tables(&table,
+ lex->time_zone_tables_used) &&
+ !table->table_in_first_from_clause)
{
if (check_access(thd, SELECT_ACL, table->db,
- &table->grant.privilege, 0, 0) ||
+ &table->grant.privilege, 0, 0,
+ test(table->schema_table)) ||
grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))
- DBUG_RETURN(1);
+ DBUG_RETURN(TRUE);
}
}
}
if (select_lex->order_list.elements)
msg= "ORDER BY";
- else if (select_lex->select_limit && select_lex->select_limit !=
- HA_POS_ERROR)
+ else if (select_lex->select_limit)
msg= "LIMIT";
if (msg)
{
my_error(ER_WRONG_USAGE, MYF(0), "UPDATE", msg);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
- DBUG_RETURN(0);
+ DBUG_RETURN(FALSE);
}
/*
@@ -5648,67 +7196,100 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables)
SYNOPSIS
multi_delete_precheck()
thd Thread handler
- tables Global table list
- table_count Pointer to table counter
+ tables Global/local table list
RETURN VALUE
- 0 OK
- 1 error (message is sent to user)
- -1 error (message is not sent to user)
+ FALSE OK
+ TRUE error
*/
-int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
+bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
{
- DBUG_ENTER("multi_delete_precheck");
SELECT_LEX *select_lex= &thd->lex->select_lex;
TABLE_LIST *aux_tables=
- (TABLE_LIST *)thd->lex->auxilliary_table_list.first;
- TABLE_LIST *delete_tables= (TABLE_LIST *)select_lex->table_list.first;
- TABLE_LIST *target_tbl;
-
- *table_count= 0;
+ (TABLE_LIST *)thd->lex->auxiliary_table_list.first;
+ TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
+ DBUG_ENTER("multi_delete_precheck");
/* 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) ||
- check_table_access(thd,DELETE_ACL, aux_tables,0))
- DBUG_RETURN(1);
+ check_table_access(thd, SELECT_ACL, tables, 0))
+ DBUG_RETURN(TRUE);
+
+ /*
+ Since aux_tables list is not part of LEX::query_tables list we
+ have to juggle with LEX::query_tables_own_last value to be able
+ call check_table_access() safely.
+ */
+ thd->lex->query_tables_own_last= 0;
+ if (check_table_access(thd, DELETE_ACL, aux_tables, 0))
+ {
+ thd->lex->query_tables_own_last= save_query_tables_own_last;
+ DBUG_RETURN(TRUE);
+ }
+ thd->lex->query_tables_own_last= save_query_tables_own_last;
+
if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where)
{
- my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE, MYF(0));
- DBUG_RETURN(-1);
+ my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
+ ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
+ DBUG_RETURN(TRUE);
}
- for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next)
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ 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
+
+ RETURN VALUE
+ FALSE - success
+ TRUE - error
+*/
+
+bool multi_delete_set_locks_and_link_aux_tables(LEX *lex)
+{
+ TABLE_LIST *tables= (TABLE_LIST*)lex->select_lex.table_list.first;
+ TABLE_LIST *target_tbl;
+ DBUG_ENTER("multi_delete_set_locks_and_link_aux_tables");
+
+ lex->table_count= 0;
+
+ for (target_tbl= (TABLE_LIST *)lex->auxiliary_table_list.first;
+ target_tbl; target_tbl= target_tbl->next_local)
{
- (*table_count)++;
+ lex->table_count++;
/* All tables in aux_tables must be found in FROM PART */
TABLE_LIST *walk;
- walk= get_table_by_alias(delete_tables,target_tbl->db,target_tbl->alias);
- if (!walk)
+ for (walk= tables; walk; walk= walk->next_local)
{
- my_error(ER_UNKNOWN_TABLE, MYF(0), target_tbl->real_name,
- "MULTI DELETE");
- DBUG_RETURN(-1);
+ if (!my_strcasecmp(table_alias_charset,
+ target_tbl->alias, walk->alias) &&
+ !strcmp(walk->db, target_tbl->db))
+ break;
}
- if (walk->derived)
+ if (!walk)
{
- my_error(ER_NON_UPDATABLE_TABLE, MYF(0), target_tbl->real_name,
- "DELETE");
- DBUG_RETURN(-1);
+ my_error(ER_UNKNOWN_TABLE, MYF(0),
+ target_tbl->table_name, "MULTI DELETE");
+ DBUG_RETURN(TRUE);
}
- walk->lock_type= target_tbl->lock_type;
- target_tbl->table_list= walk; // Remember corresponding table
-
- /* in case of subselects, we need to set lock_type in
- * corresponding table in list of all tables */
- if (walk->table_list)
+ if (!walk->derived)
{
- target_tbl->table_list= walk->table_list;
- walk->table_list->lock_type= walk->lock_type;
+ target_tbl->table_name= walk->table_name;
+ target_tbl->table_name_length= walk->table_name_length;
}
+ walk->updating= target_tbl->updating;
+ walk->lock_type= target_tbl->lock_type;
+ target_tbl->correspondent_table= walk; // Remember corresponding table
}
- DBUG_RETURN(0);
+ DBUG_RETURN(FALSE);
}
@@ -5721,21 +7302,20 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
tables Global table list
RETURN VALUE
- 0 OK
- 1 Error (message is sent to user)
- -1 Error (message is not sent to user)
+ FALSE OK
+ TRUE Error
*/
-int update_precheck(THD *thd, TABLE_LIST *tables)
+bool update_precheck(THD *thd, TABLE_LIST *tables)
{
DBUG_ENTER("update_precheck");
if (thd->lex->select_lex.item_list.elements != thd->lex->value_list.elements)
{
- my_error(ER_WRONG_VALUE_COUNT, MYF(0));
- DBUG_RETURN(-1);
+ my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
+ DBUG_RETURN(TRUE);
}
- DBUG_RETURN((check_db_used(thd, tables) ||
- check_one_table_access(thd, UPDATE_ACL, tables)) ? 1 : 0);
+ DBUG_RETURN(check_db_used(thd, tables) ||
+ check_one_table_access(thd, UPDATE_ACL, tables));
}
@@ -5748,19 +7328,18 @@ int update_precheck(THD *thd, TABLE_LIST *tables)
tables Global table list
RETURN VALUE
- 0 OK
- 1 error (message is sent to user)
- -1 error (message is not sent to user)
+ FALSE OK
+ TRUE error
*/
-int delete_precheck(THD *thd, TABLE_LIST *tables)
+bool delete_precheck(THD *thd, TABLE_LIST *tables)
{
DBUG_ENTER("delete_precheck");
if (check_one_table_access(thd, DELETE_ACL, tables))
- DBUG_RETURN(1);
+ DBUG_RETURN(TRUE);
/* Set privilege for the WHERE clause */
tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
- DBUG_RETURN(0);
+ DBUG_RETURN(FALSE);
}
@@ -5773,12 +7352,11 @@ int delete_precheck(THD *thd, TABLE_LIST *tables)
tables Global table list
RETURN VALUE
- 0 OK
- 1 error (message is sent to user)
- -1 error (message is not sent to user)
+ FALSE OK
+ TRUE error
*/
-int insert_precheck(THD *thd, TABLE_LIST *tables)
+bool insert_precheck(THD *thd, TABLE_LIST *tables)
{
LEX *lex= thd->lex;
DBUG_ENTER("insert_precheck");
@@ -5787,19 +7365,21 @@ int insert_precheck(THD *thd, TABLE_LIST *tables)
Check that we have modify privileges for the first table and
select privileges for the rest
*/
- ulong privilege= INSERT_ACL |
- (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
- (lex->duplicates == DUP_UPDATE ? UPDATE_ACL : 0);
+ ulong privilege= (INSERT_ACL |
+ (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
+ (lex->value_list.elements ? UPDATE_ACL : 0));
if (check_one_table_access(thd, privilege, tables))
- DBUG_RETURN(1);
+ DBUG_RETURN(TRUE);
if (lex->update_list.elements != lex->value_list.elements)
{
- my_error(ER_WRONG_VALUE_COUNT, MYF(0));
- DBUG_RETURN(-1);
+ my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
+ DBUG_RETURN(TRUE);
}
- DBUG_RETURN(0);
+ if (check_db_used(thd, tables))
+ DBUG_RETURN(TRUE);
+ DBUG_RETURN(FALSE);
}
@@ -5813,67 +7393,61 @@ int insert_precheck(THD *thd, TABLE_LIST *tables)
create_table Table which will be created
RETURN VALUE
- 0 OK
- 1 Error (message is sent to user)
+ FALSE OK
+ TRUE Error
*/
-int create_table_precheck(THD *thd, TABLE_LIST *tables,
- TABLE_LIST *create_table)
+bool create_table_precheck(THD *thd, TABLE_LIST *tables,
+ TABLE_LIST *create_table)
{
LEX *lex= thd->lex;
SELECT_LEX *select_lex= &lex->select_lex;
ulong want_priv;
- int error= 1; // Error message is given
+ bool error= TRUE; // Error message is given
DBUG_ENTER("create_table_precheck");
want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
CREATE_TMP_ACL : CREATE_ACL);
if (check_access(thd, want_priv, create_table->db,
- &create_table->grant.privilege, 0, 0) ||
+ &create_table->grant.privilege, 0, 0,
+ test(create_table->schema_table)) ||
check_merge_table_access(thd, create_table->db,
(TABLE_LIST *)
lex->create_info.merge_list.first))
goto err;
if (grant_option && want_priv != CREATE_TMP_ACL &&
- check_grant(thd, want_priv, create_table, 0, UINT_MAX, 0))
+ check_grant(thd, want_priv, create_table, 0, 1, 0))
goto err;
if (select_lex->item_list.elements)
{
/* Check permissions for used tables in CREATE TABLE ... SELECT */
+#ifdef NOT_NECESSARY_TO_CHECK_CREATE_TABLE_EXIST_WHEN_PREPARING_STATEMENT
+ /* This code throws an ill error for CREATE TABLE t1 SELECT * FROM t1 */
/*
- For temporary tables or PREPARED STATEMETNS we don't have to check
- if the created table exists
+ Only do the check for PS, becasue we on execute we have to check that
+ against the opened tables to ensure we don't use a table that is part
+ of the view (which can only be done after the table has been opened).
*/
- if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
- ! thd->current_arena->is_stmt_prepare() &&
- find_real_table_in_list(tables, create_table->db,
- create_table->real_name))
+ if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute())
{
- net_printf(thd,ER_UPDATE_TABLE_USED, create_table->real_name);
-
- goto err;
- }
- if (lex->create_info.used_fields & HA_CREATE_USED_UNION)
- {
- TABLE_LIST *tab;
- for (tab= tables; tab; tab= tab->next)
+ /*
+ For temporary tables we don't have to check if the created table exists
+ */
+ if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
+ find_table_in_global_list(tables, create_table->db,
+ create_table->table_name))
{
- if (find_real_table_in_list((TABLE_LIST*) lex->create_info.
- merge_list.first,
- tables->db, tab->real_name))
- {
- net_printf(thd, ER_UPDATE_TABLE_USED, tab->real_name);
- goto err;
- }
- }
- }
-
+ error= FALSE;
+ goto err;
+ }
+ }
+#endif
if (tables && check_table_access(thd, SELECT_ACL, tables,0))
goto err;
}
- error= 0;
+ error= FALSE;
err:
DBUG_RETURN(error);
@@ -5885,7 +7459,7 @@ err:
SYNOPSIS
negate_expression()
- thd therad handler
+ thd thread handler
expr expression for negation
RETURN
@@ -5914,3 +7488,130 @@ Item *negate_expression(THD *thd, Item *expr)
return negated;
return new Item_func_not(expr);
}
+
+/*
+ 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
+*/
+
+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= strlen(definer->user.str);
+
+ definer->host.str= (char *) sctx->priv_host;
+ definer->host.length= strlen(definer->host.str);
+}
+
+
+/*
+ Create default definer for the specified THD.
+
+ SYNOPSIS
+ create_default_definer()
+ thd [in] thread handler
+
+ RETURN
+ On success, return a valid pointer to the created and initialized
+ LEX_USER, which contains definer information.
+ On error, return 0.
+*/
+
+LEX_USER *create_default_definer(THD *thd)
+{
+ LEX_USER *definer;
+
+ if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
+ return 0;
+
+ get_default_definer(thd, definer);
+
+ return definer;
+}
+
+
+/*
+ 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
+
+ RETURN
+ On success, return a valid pointer to the created and initialized
+ LEX_USER, which contains definer information.
+ On error, return 0.
+*/
+
+LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
+{
+ LEX_USER *definer;
+
+ /* Create and initialize. */
+
+ if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
+ return 0;
+
+ definer->user= *user_name;
+ definer->host= *host_name;
+
+ return definer;
+}
+
+
+/*
+ Retuns information about user or current user.
+
+ SYNOPSIS
+ get_current_user()
+ thd [in] thread handler
+ user [in] user
+
+ RETURN
+ On success, return a valid pointer to initialized
+ LEX_USER, which contains user information.
+ On error, return 0.
+*/
+
+LEX_USER *get_current_user(THD *thd, LEX_USER *user)
+{
+ if (!user->user.str) // current_user
+ return create_default_definer(thd);
+
+ return user;
+}
+
+
+/*
+ Check that 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
+
+ RETURN
+ FALSE the passed string is not longer than max_length
+ TRUE the passed string is longer than max_length
+*/
+
+bool check_string_length(LEX_STRING *str, const char *err_msg,
+ uint max_length)
+{
+ if (str->length <= max_length)
+ return FALSE;
+
+ my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_length);
+
+ return TRUE;
+}