2003-01-29 18:22:22 +01:00
|
|
|
/* Copyright (C) 2000-2003 MySQL AB
|
2000-08-21 13:35:27 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
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.
|
2000-08-21 13:35:27 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
2000-08-21 13:35:27 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
|
|
|
|
|
#include "mysql_priv.h"
|
|
|
|
#include "sql_acl.h"
|
2000-09-30 01:20:26 +02:00
|
|
|
#include "sql_repl.h"
|
2001-11-11 06:24:12 +01:00
|
|
|
#include "repl_failsafe.h"
|
2000-07-31 21:29:14 +02:00
|
|
|
#include <m_ctype.h>
|
|
|
|
#include <myisam.h>
|
|
|
|
#include <my_dir.h>
|
|
|
|
|
2002-07-08 18:34:49 +02:00
|
|
|
#ifdef HAVE_INNOBASE_DB
|
2002-08-08 02:12:02 +02:00
|
|
|
#include "ha_innodb.h"
|
2002-07-08 18:34:49 +02:00
|
|
|
#endif
|
|
|
|
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
#include "sp_head.h"
|
2002-12-12 13:14:23 +01:00
|
|
|
#include "sp.h"
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
|
2001-12-05 12:03:00 +01:00
|
|
|
#ifdef HAVE_OPENSSL
|
|
|
|
/*
|
|
|
|
Without SSL the handshake consists of one packet. This packet
|
|
|
|
has both client capabilites and scrambled password.
|
|
|
|
With SSL the handshake might consist of two packets. If the first
|
|
|
|
packet (client capabilities) has CLIENT_SSL flag set, we have to
|
|
|
|
switch to SSL and read the second packet. The scrambled password
|
|
|
|
is in the second packet and client_capabilites field will be ignored.
|
|
|
|
Maybe it is better to accept flags other than CLIENT_SSL from the
|
|
|
|
second packet?
|
|
|
|
*/
|
2002-06-12 14:04:18 +02:00
|
|
|
#define SSL_HANDSHAKE_SIZE 2
|
|
|
|
#define NORMAL_HANDSHAKE_SIZE 6
|
|
|
|
#define MIN_HANDSHAKE_SIZE 2
|
2001-12-05 12:03:00 +01:00
|
|
|
#else
|
2002-06-12 14:04:18 +02:00
|
|
|
#define MIN_HANDSHAKE_SIZE 6
|
2001-12-05 12:03:00 +01:00
|
|
|
#endif /* HAVE_OPENSSL */
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2003-03-05 19:45:17 +01:00
|
|
|
/* 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_DROP_FUNCTION ? \
|
|
|
|
"FUNCTION" : "PROCEDURE")
|
|
|
|
|
2000-08-15 19:09:37 +02:00
|
|
|
#ifdef SOLARIS
|
|
|
|
extern "C" int gethostname(char *name, int namelen);
|
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2004-04-05 12:56:05 +02:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2002-10-02 12:33:08 +02:00
|
|
|
static int check_for_max_user_connections(THD *thd, USER_CONN *uc);
|
2004-04-05 12:56:05 +02:00
|
|
|
#endif
|
2002-05-15 12:50:38 +02:00
|
|
|
static void decrease_user_connections(USER_CONN *uc);
|
2000-08-21 02:07:54 +02:00
|
|
|
static bool check_db_used(THD *thd,TABLE_LIST *tables);
|
2000-07-31 21:29:14 +02:00
|
|
|
static void remove_escape(char *name);
|
|
|
|
static void refresh_status(void);
|
2003-12-30 12:14:21 +01:00
|
|
|
static bool append_file_to_dir(THD *thd, const char **filename_ptr,
|
|
|
|
const char *table_name);
|
2004-10-22 20:29:06 +02:00
|
|
|
static bool check_sp_definer_access(THD *thd, sp_head *sp);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2000-09-30 01:20:26 +02:00
|
|
|
const char *any_db="*any*"; // Special symbol for check_access
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
const char *command_name[]={
|
|
|
|
"Sleep", "Quit", "Init DB", "Query", "Field List", "Create DB",
|
|
|
|
"Drop DB", "Refresh", "Shutdown", "Statistics", "Processlist",
|
2004-10-22 20:33:10 +02:00
|
|
|
"Connect","Kill","Debug","Ping","Time","Delayed insert","Change user",
|
2002-06-12 23:13:12 +02:00
|
|
|
"Binlog Dump","Table Dump", "Connect Out", "Register Slave",
|
2003-01-06 01:04:52 +01:00
|
|
|
"Prepare", "Prepare Execute", "Long Data", "Close stmt",
|
2004-09-04 14:02:57 +02:00
|
|
|
"Reset stmt", "Set option", "Fetch",
|
2003-01-04 14:17:16 +01:00
|
|
|
"Error" // Last command number
|
2000-07-31 21:29:14 +02:00
|
|
|
};
|
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
static char empty_c_string[1]= {0}; // Used for not defined 'db'
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
#ifdef __WIN__
|
|
|
|
static void test_signal(int sig_ptr)
|
|
|
|
{
|
2001-08-22 00:45:07 +02:00
|
|
|
#if !defined( DBUG_OFF)
|
2000-07-31 21:29:14 +02:00
|
|
|
MessageBox(NULL,"Test signal","DBUG",MB_OK);
|
|
|
|
#endif
|
2001-08-22 00:45:07 +02:00
|
|
|
#if defined(OS2)
|
2002-01-02 20:29:41 +01:00
|
|
|
fprintf(stderr, "Test signal %d\n", sig_ptr);
|
|
|
|
fflush(stderr);
|
2001-08-22 00:45:07 +02:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
static void init_signals(void)
|
|
|
|
{
|
|
|
|
int signals[7] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGBREAK,SIGABRT } ;
|
2002-06-11 10:20:31 +02:00
|
|
|
for (int i=0 ; i < 7 ; i++)
|
2000-07-31 21:29:14 +02:00
|
|
|
signal( signals[i], test_signal) ;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2002-11-16 19:19:10 +01:00
|
|
|
static void unlock_locked_tables(THD *thd)
|
|
|
|
{
|
|
|
|
if (thd->locked_tables)
|
|
|
|
{
|
|
|
|
thd->lock=thd->locked_tables;
|
2004-10-07 00:45:06 +02:00
|
|
|
thd->locked_tables=0; // Will be automatically closed
|
2002-11-16 19:19:10 +01:00
|
|
|
close_thread_tables(thd); // Free tables
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-03-12 08:15:08 +01:00
|
|
|
|
2002-11-16 19:19:10 +01:00
|
|
|
static bool end_active_trans(THD *thd)
|
2000-11-13 22:55:10 +01:00
|
|
|
{
|
2000-12-24 14:19:00 +01:00
|
|
|
int error=0;
|
2004-12-06 01:00:37 +01:00
|
|
|
DBUG_ENTER("end_active_trans");
|
2002-07-23 17:31:22 +02:00
|
|
|
if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN |
|
2001-09-08 00:02:41 +02:00
|
|
|
OPTION_TABLE_LOCK))
|
2000-11-13 22:55:10 +01:00
|
|
|
{
|
2004-12-06 01:00:37 +01:00
|
|
|
DBUG_PRINT("info",("options: 0x%lx", (ulong) thd->options));
|
2000-11-24 00:51:18 +01:00
|
|
|
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
|
2004-12-06 01:00:37 +01:00
|
|
|
/* Safety if one did "drop table" on locked tables */
|
|
|
|
if (!thd->locked_tables)
|
|
|
|
thd->options&= ~OPTION_TABLE_LOCK;
|
2000-11-24 00:51:18 +01:00
|
|
|
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
2000-11-13 22:55:10 +01:00
|
|
|
if (ha_commit(thd))
|
2000-12-24 14:19:00 +01:00
|
|
|
error=1;
|
2000-11-13 22:55:10 +01:00
|
|
|
}
|
2004-12-06 01:00:37 +01:00
|
|
|
DBUG_RETURN(error);
|
2000-11-13 22:55:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-17 23:09:13 +01:00
|
|
|
#ifdef HAVE_REPLICATION
|
2004-03-12 08:15:08 +01:00
|
|
|
inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables)
|
|
|
|
{
|
|
|
|
return (table_rules_on && tables && !tables_ok(thd,tables) &&
|
2004-03-17 09:30:40 +01:00
|
|
|
((thd->lex->sql_command != SQLCOM_DELETE_MULTI) ||
|
2004-03-16 21:41:30 +01:00
|
|
|
!tables_ok(thd,
|
|
|
|
(TABLE_LIST *)thd->lex->auxilliary_table_list.first)));
|
2004-03-12 08:15:08 +01:00
|
|
|
}
|
2004-03-17 23:09:13 +01:00
|
|
|
#endif
|
2004-03-12 08:15:08 +01:00
|
|
|
|
|
|
|
|
2001-12-26 15:49:10 +01:00
|
|
|
static HASH hash_user_connections;
|
|
|
|
|
2002-02-13 21:37:19 +01:00
|
|
|
static int get_or_create_user_conn(THD *thd, const char *user,
|
|
|
|
const char *host,
|
2002-11-30 18:58:53 +01:00
|
|
|
USER_RESOURCES *mqh)
|
2002-02-01 19:53:24 +01:00
|
|
|
{
|
|
|
|
int return_val=0;
|
2003-11-20 21:06:25 +01:00
|
|
|
uint temp_len, user_len;
|
2002-02-01 19:53:24 +01:00
|
|
|
char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2];
|
|
|
|
struct user_conn *uc;
|
|
|
|
|
|
|
|
DBUG_ASSERT(user != 0);
|
|
|
|
DBUG_ASSERT(host != 0);
|
|
|
|
|
2002-05-15 12:50:38 +02:00
|
|
|
user_len=strlen(user);
|
|
|
|
temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1;
|
2002-02-01 19:53:24 +01:00
|
|
|
(void) pthread_mutex_lock(&LOCK_user_conn);
|
2002-02-13 21:37:19 +01:00
|
|
|
if (!(uc = (struct user_conn *) hash_search(&hash_user_connections,
|
|
|
|
(byte*) temp_user, temp_len)))
|
2002-02-01 19:53:24 +01:00
|
|
|
{
|
2002-02-13 21:37:19 +01:00
|
|
|
/* First connection for user; Create a user connection object */
|
|
|
|
if (!(uc= ((struct user_conn*)
|
|
|
|
my_malloc(sizeof(struct user_conn) + temp_len+1,
|
2002-02-15 01:49:02 +01:00
|
|
|
MYF(MY_WME)))))
|
|
|
|
{
|
2004-10-20 15:06:54 +02:00
|
|
|
net_send_error(thd, 0, NullS); // Out of memory
|
2002-02-01 19:53:24 +01:00
|
|
|
return_val=1;
|
|
|
|
goto end;
|
2002-02-15 01:49:02 +01:00
|
|
|
}
|
2002-02-01 19:53:24 +01:00
|
|
|
uc->user=(char*) (uc+1);
|
|
|
|
memcpy(uc->user,temp_user,temp_len+1);
|
2002-05-15 12:50:38 +02:00
|
|
|
uc->user_len= user_len;
|
|
|
|
uc->host=uc->user + uc->user_len + 1;
|
2002-02-01 19:53:24 +01:00
|
|
|
uc->len = temp_len;
|
2002-05-15 12:50:38 +02:00
|
|
|
uc->connections = 1;
|
|
|
|
uc->questions=uc->updates=uc->conn_per_hour=0;
|
|
|
|
uc->user_resources=*mqh;
|
2002-11-30 18:58:53 +01:00
|
|
|
if (max_user_connections && mqh->connections > max_user_connections)
|
2002-05-15 12:50:38 +02:00
|
|
|
uc->user_resources.connections = max_user_connections;
|
2002-02-01 19:53:24 +01:00
|
|
|
uc->intime=thd->thr_create_time;
|
2003-09-19 11:44:31 +02:00
|
|
|
if (my_hash_insert(&hash_user_connections, (byte*) uc))
|
2002-02-01 19:53:24 +01:00
|
|
|
{
|
|
|
|
my_free((char*) uc,0);
|
2004-10-20 15:06:54 +02:00
|
|
|
net_send_error(thd, 0, NullS); // Out of memory
|
2002-02-01 19:53:24 +01:00
|
|
|
return_val=1;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
thd->user_connect=uc;
|
2003-10-27 15:14:03 +01:00
|
|
|
uc->connections++;
|
2002-02-01 19:53:24 +01:00
|
|
|
end:
|
|
|
|
(void) pthread_mutex_unlock(&LOCK_user_conn);
|
|
|
|
return return_val;
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2002-02-01 19:53:24 +01:00
|
|
|
}
|
2001-12-26 15:49:10 +01:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
2003-07-01 21:40:59 +02:00
|
|
|
Check if user exist and password supplied is correct.
|
2003-02-14 10:47:41 +01:00
|
|
|
SYNOPSIS
|
|
|
|
check_user()
|
2003-07-18 16:25:54 +02:00
|
|
|
thd thread handle, thd->{host,user,ip} are used
|
|
|
|
command originator of the check: now check_user is called
|
|
|
|
during connect and change user procedures; used for
|
|
|
|
logging.
|
2004-10-07 00:45:06 +02:00
|
|
|
passwd scrambled password received from client
|
2003-07-18 16:25:54 +02:00
|
|
|
passwd_len length of scrambled password
|
|
|
|
db database name to connect to, may be NULL
|
|
|
|
check_count dont know exactly
|
|
|
|
|
2003-07-01 21:40:59 +02:00
|
|
|
Note, that host, user and passwd may point to communication buffer.
|
2004-10-07 00:45:06 +02:00
|
|
|
Current implementation does not depend on that, but future changes
|
2003-07-18 16:25:54 +02:00
|
|
|
should be done with this in mind; 'thd' is INOUT, all other params
|
|
|
|
are 'IN'.
|
|
|
|
|
2003-07-01 21:40:59 +02:00
|
|
|
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;
|
2003-07-04 18:52:04 +02:00
|
|
|
-1 access denied or handshake error; error is sent to client;
|
|
|
|
>0 error, not sent to client
|
2000-07-31 21:29:14 +02:00
|
|
|
*/
|
|
|
|
|
2003-09-26 12:33:13 +02:00
|
|
|
int check_user(THD *thd, enum enum_server_command command,
|
|
|
|
const char *passwd, uint passwd_len, const char *db,
|
|
|
|
bool check_count)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2003-02-14 10:47:41 +01:00
|
|
|
DBUG_ENTER("check_user");
|
2003-05-19 15:35:49 +02:00
|
|
|
|
2003-12-19 15:25:50 +01:00
|
|
|
#ifdef NO_EMBEDDED_ACCESS_CHECKS
|
|
|
|
thd->master_access= GLOBAL_ACLS; // Full rights
|
2004-02-14 17:26:21 +01:00
|
|
|
/* Change database if necessary: OK or FAIL is sent in mysql_change_db */
|
|
|
|
if (db && db[0])
|
|
|
|
{
|
|
|
|
thd->db= 0;
|
|
|
|
thd->db_length= 0;
|
|
|
|
if (mysql_change_db(thd, db))
|
|
|
|
{
|
|
|
|
if (thd->user_connect)
|
|
|
|
decrease_user_connections(thd->user_connect);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
send_ok(thd);
|
|
|
|
DBUG_RETURN(0);
|
2003-12-19 15:25:50 +01:00
|
|
|
#else
|
|
|
|
|
2003-07-08 00:36:14 +02:00
|
|
|
my_bool opt_secure_auth_local;
|
|
|
|
pthread_mutex_lock(&LOCK_global_system_variables);
|
|
|
|
opt_secure_auth_local= opt_secure_auth;
|
|
|
|
pthread_mutex_unlock(&LOCK_global_system_variables);
|
|
|
|
|
2003-05-13 17:58:26 +02:00
|
|
|
/*
|
2003-07-08 00:36:14 +02:00
|
|
|
If the server is running in secure auth mode, short scrambles are
|
|
|
|
forbidden.
|
2003-05-13 17:58:26 +02:00
|
|
|
*/
|
2003-07-08 00:36:14 +02:00
|
|
|
if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-10-20 15:06:54 +02:00
|
|
|
net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE);
|
2003-07-08 00:36:14 +02:00
|
|
|
mysql_log.write(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
|
|
|
|
DBUG_RETURN(-1);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2003-07-04 18:52:04 +02:00
|
|
|
if (passwd_len != 0 &&
|
|
|
|
passwd_len != SCRAMBLE_LENGTH &&
|
|
|
|
passwd_len != SCRAMBLE_LENGTH_323)
|
|
|
|
DBUG_RETURN(ER_HANDSHAKE_ERROR);
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2003-01-18 18:31:38 +01:00
|
|
|
/*
|
2003-07-18 16:25:54 +02:00
|
|
|
Clear thd->db as it points to something, that will be freed when
|
2004-10-07 00:45:06 +02:00
|
|
|
connection is closed. We don't want to accidentally free a wrong pointer
|
2003-07-18 16:25:54 +02:00
|
|
|
if connect failed. Also in case of 'CHANGE USER' failure, current
|
|
|
|
database will be switched to 'no database selected'.
|
2003-01-18 18:31:38 +01:00
|
|
|
*/
|
2003-07-01 21:40:59 +02:00
|
|
|
thd->db= 0;
|
|
|
|
thd->db_length= 0;
|
2003-07-04 18:52:04 +02:00
|
|
|
|
2003-07-01 21:40:59 +02:00
|
|
|
USER_RESOURCES ur;
|
2003-07-08 00:36:14 +02:00
|
|
|
int res= acl_getroot(thd, &ur, passwd, passwd_len);
|
2003-09-26 12:33:13 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2003-07-04 18:52:04 +02:00
|
|
|
if (res == -1)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2003-07-04 18:52:04 +02:00
|
|
|
/*
|
|
|
|
This happens when client (new) sends password scrambled with
|
|
|
|
scramble(), but database holds old value (scrambled with
|
|
|
|
scramble_323()). Here we please client to send scrambled_password
|
|
|
|
in old format.
|
|
|
|
*/
|
2003-07-18 16:25:54 +02:00
|
|
|
NET *net= &thd->net;
|
2003-07-08 00:36:14 +02:00
|
|
|
if (opt_secure_auth_local)
|
2002-11-24 15:07:53 +01:00
|
|
|
{
|
2004-10-20 15:06:54 +02:00
|
|
|
net_printf_error(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE,
|
|
|
|
thd->user, thd->host_or_ip);
|
2003-07-08 00:36:14 +02:00
|
|
|
mysql_log.write(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
|
|
|
|
thd->user, thd->host_or_ip);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2004-07-07 10:29:39 +02:00
|
|
|
/* We have to read very specific packet size */
|
2003-07-18 16:25:54 +02:00
|
|
|
if (send_old_password_request(thd) ||
|
2004-07-07 10:29:39 +02:00
|
|
|
my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
|
|
|
|
{
|
2003-07-04 18:52:04 +02:00
|
|
|
inc_host_errors(&thd->remote.sin_addr);
|
|
|
|
DBUG_RETURN(ER_HANDSHAKE_ERROR);
|
|
|
|
}
|
|
|
|
/* Final attempt to check the user based on reply */
|
|
|
|
/* So as passwd is short, errcode is always >= 0 */
|
2003-07-08 00:36:14 +02:00
|
|
|
res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323);
|
2003-07-04 18:52:04 +02:00
|
|
|
}
|
2003-09-26 12:33:13 +02:00
|
|
|
#endif /*EMBEDDED_LIBRARY*/
|
2003-07-04 18:52:04 +02:00
|
|
|
/* here res is always >= 0 */
|
|
|
|
if (res == 0)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-10-07 00:45:06 +02:00
|
|
|
if (!(thd->master_access & NO_ACCESS)) // authentication is OK
|
2002-11-24 15:07:53 +01:00
|
|
|
{
|
2003-07-04 18:52:04 +02:00
|
|
|
DBUG_PRINT("info",
|
|
|
|
("Capabilities: %d packet_length: %ld Host: '%s' "
|
|
|
|
"Login user: '%s' Priv_user: '%s' Using password: %s "
|
|
|
|
"Access: %u db: '%s'",
|
|
|
|
thd->client_capabilities, thd->max_client_packet_length,
|
|
|
|
thd->host_or_ip, thd->user, thd->priv_user,
|
|
|
|
passwd_len ? "yes": "no",
|
|
|
|
thd->master_access, thd->db ? thd->db : "*none*"));
|
|
|
|
|
|
|
|
if (check_count)
|
2002-12-26 17:12:57 +01:00
|
|
|
{
|
2003-07-04 18:52:04 +02:00
|
|
|
VOID(pthread_mutex_lock(&LOCK_thread_count));
|
|
|
|
bool count_ok= thread_count < max_connections + delayed_insert_threads
|
2003-07-18 16:25:54 +02:00
|
|
|
|| (thd->master_access & SUPER_ACL);
|
2003-07-04 18:52:04 +02:00
|
|
|
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
|
|
|
if (!count_ok)
|
|
|
|
{ // too many connections
|
2004-10-20 15:06:54 +02:00
|
|
|
net_send_error(thd, ER_CON_COUNT_ERROR);
|
2003-07-04 18:52:04 +02:00
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2002-12-26 17:12:57 +01:00
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2003-07-04 18:52:04 +02:00
|
|
|
/* Why logging is performed before all checks've passed? */
|
|
|
|
mysql_log.write(thd,command,
|
|
|
|
(thd->priv_user == thd->user ?
|
|
|
|
(char*) "%s@%s on %s" :
|
|
|
|
(char*) "%s@%s as anonymous on %s"),
|
|
|
|
thd->user, thd->host_or_ip,
|
|
|
|
db ? db : (char*) "");
|
|
|
|
|
2003-01-18 18:31:38 +01:00
|
|
|
/*
|
2003-07-18 16:25:54 +02:00
|
|
|
This is the default access rights for the current database. It's
|
|
|
|
set to 0 here because we don't have an active database yet (and we
|
|
|
|
may not have an active database to set.
|
2003-01-18 18:31:38 +01:00
|
|
|
*/
|
2003-07-04 18:52:04 +02:00
|
|
|
thd->db_access=0;
|
|
|
|
|
|
|
|
/* Don't allow user to connect if he has done too many queries */
|
2003-11-04 08:40:36 +01:00
|
|
|
if ((ur.questions || ur.updates || ur.connections ||
|
|
|
|
max_user_connections) &&
|
|
|
|
get_or_create_user_conn(thd,thd->user,thd->host_or_ip,&ur))
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
if (thd->user_connect &&
|
|
|
|
(thd->user_connect->user_resources.connections ||
|
|
|
|
max_user_connections) &&
|
|
|
|
check_for_max_user_connections(thd, thd->user_connect))
|
|
|
|
DBUG_RETURN(-1);
|
2003-07-04 18:52:04 +02:00
|
|
|
|
|
|
|
/* Change database if necessary: OK or FAIL is sent in mysql_change_db */
|
|
|
|
if (db && db[0])
|
2002-12-26 17:12:57 +01:00
|
|
|
{
|
2003-07-04 18:52:04 +02:00
|
|
|
if (mysql_change_db(thd, db))
|
|
|
|
{
|
|
|
|
if (thd->user_connect)
|
|
|
|
decrease_user_connections(thd->user_connect);
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
2002-12-26 17:12:57 +01:00
|
|
|
}
|
|
|
|
else
|
2003-12-19 15:25:50 +01:00
|
|
|
send_ok(thd);
|
2003-07-04 18:52:04 +02:00
|
|
|
thd->password= test(passwd_len); // remember for error messages
|
|
|
|
/* Ready to handle queries */
|
|
|
|
DBUG_RETURN(0);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
2003-07-04 18:52:04 +02:00
|
|
|
else if (res == 2) // client gave short hash, server has long hash
|
2001-02-21 00:11:32 +01:00
|
|
|
{
|
2004-10-20 15:06:54 +02:00
|
|
|
net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE);
|
2003-07-04 18:52:04 +02:00
|
|
|
mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE));
|
|
|
|
DBUG_RETURN(-1);
|
2001-02-21 00:11:32 +01:00
|
|
|
}
|
2004-10-20 15:06:54 +02:00
|
|
|
net_printf_error(thd, ER_ACCESS_DENIED_ERROR,
|
|
|
|
thd->user,
|
|
|
|
thd->host_or_ip,
|
|
|
|
passwd_len ? ER(ER_YES) : ER(ER_NO));
|
2003-07-04 18:52:04 +02:00
|
|
|
mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
|
|
|
|
thd->user,
|
|
|
|
thd->host_or_ip,
|
|
|
|
passwd_len ? ER(ER_YES) : ER(ER_NO));
|
|
|
|
DBUG_RETURN(-1);
|
2003-12-19 15:25:50 +01:00
|
|
|
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2001-02-21 00:11:32 +01:00
|
|
|
/*
|
2002-01-29 17:32:16 +01:00
|
|
|
Check for maximum allowable user connections, if the mysqld server is
|
|
|
|
started with corresponding variable that is greater then 0.
|
2001-02-21 00:11:32 +01:00
|
|
|
*/
|
|
|
|
|
2002-11-07 11:49:02 +01:00
|
|
|
extern "C" byte *get_key_conn(user_conn *buff, uint *length,
|
|
|
|
my_bool not_used __attribute__((unused)))
|
2002-01-29 17:32:16 +01:00
|
|
|
{
|
|
|
|
*length=buff->len;
|
|
|
|
return (byte*) buff->user;
|
|
|
|
}
|
|
|
|
|
2002-11-07 11:49:02 +01:00
|
|
|
extern "C" void free_user(struct user_conn *uc)
|
2002-01-29 17:32:16 +01:00
|
|
|
{
|
|
|
|
my_free((char*) uc,MYF(0));
|
|
|
|
}
|
|
|
|
|
2002-11-30 18:58:53 +01:00
|
|
|
void init_max_user_conn(void)
|
2001-02-21 00:11:32 +01:00
|
|
|
{
|
2002-03-14 18:44:42 +01:00
|
|
|
(void) hash_init(&hash_user_connections,system_charset_info,max_connections,
|
|
|
|
0,0,
|
2002-11-07 02:54:00 +01:00
|
|
|
(hash_get_key) get_key_conn, (hash_free_key) free_user,
|
2001-03-06 14:24:08 +01:00
|
|
|
0);
|
2001-02-21 00:11:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-11-04 08:40:36 +01:00
|
|
|
/*
|
|
|
|
check if user has already too many connections
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
check_for_max_user_connections()
|
|
|
|
thd Thread handle
|
|
|
|
uc User connect object
|
|
|
|
|
|
|
|
NOTES
|
|
|
|
If check fails, we decrease user connection count, which means one
|
|
|
|
shouldn't call decrease_user_connections() after this function.
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
1 error
|
|
|
|
*/
|
|
|
|
|
2004-04-05 12:56:05 +02:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
static int check_for_max_user_connections(THD *thd, USER_CONN *uc)
|
2001-02-21 00:11:32 +01:00
|
|
|
{
|
2002-02-01 19:53:24 +01:00
|
|
|
int error=0;
|
2001-03-06 14:24:08 +01:00
|
|
|
DBUG_ENTER("check_for_max_user_connections");
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2003-10-27 15:14:03 +01:00
|
|
|
(void) pthread_mutex_lock(&LOCK_user_conn);
|
2002-06-16 16:06:12 +02:00
|
|
|
if (max_user_connections &&
|
2003-11-04 08:40:36 +01:00
|
|
|
max_user_connections < (uint) uc->connections)
|
2001-02-21 00:11:32 +01:00
|
|
|
{
|
2004-10-20 15:06:54 +02:00
|
|
|
net_printf_error(thd, ER_TOO_MANY_USER_CONNECTIONS, uc->user);
|
2002-02-01 19:53:24 +01:00
|
|
|
error=1;
|
|
|
|
goto end;
|
2001-02-21 00:11:32 +01:00
|
|
|
}
|
2002-06-16 16:06:12 +02:00
|
|
|
if (uc->user_resources.connections &&
|
2003-10-27 15:14:03 +01:00
|
|
|
uc->user_resources.connections <= uc->conn_per_hour)
|
2002-05-15 12:50:38 +02:00
|
|
|
{
|
2004-10-20 15:06:54 +02:00
|
|
|
net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user,
|
|
|
|
"max_connections",
|
|
|
|
(long) uc->user_resources.connections);
|
2002-05-15 12:50:38 +02:00
|
|
|
error=1;
|
|
|
|
goto end;
|
|
|
|
}
|
2003-10-27 15:14:03 +01:00
|
|
|
uc->conn_per_hour++;
|
2003-11-04 08:40:36 +01:00
|
|
|
|
|
|
|
end:
|
2003-10-29 08:33:31 +01:00
|
|
|
if (error)
|
|
|
|
uc->connections--; // no need for decrease_user_connections() here
|
2003-10-27 15:14:03 +01:00
|
|
|
(void) pthread_mutex_unlock(&LOCK_user_conn);
|
2001-03-06 14:24:08 +01:00
|
|
|
DBUG_RETURN(error);
|
2001-02-21 00:11:32 +01:00
|
|
|
}
|
2004-04-05 12:56:05 +02:00
|
|
|
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
|
2001-02-21 00:11:32 +01:00
|
|
|
|
2003-11-04 08:40:36 +01:00
|
|
|
/*
|
|
|
|
Decrease user connection count
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
decrease_user_connections()
|
|
|
|
uc User connection object
|
|
|
|
|
|
|
|
NOTES
|
|
|
|
If there is a n user connection object for a connection
|
|
|
|
(which only happens if 'max_user_connections' is defined or
|
|
|
|
if someone has created a resource grant for a user), then
|
|
|
|
the connection count is always incremented on connect.
|
|
|
|
|
|
|
|
The user connect object is not freed if some users has
|
|
|
|
'max connections per hour' defined as we need to be able to hold
|
|
|
|
count over the lifetime of the connection.
|
|
|
|
*/
|
|
|
|
|
2002-05-15 12:50:38 +02:00
|
|
|
static void decrease_user_connections(USER_CONN *uc)
|
2001-02-21 00:11:32 +01:00
|
|
|
{
|
2002-02-01 19:53:24 +01:00
|
|
|
DBUG_ENTER("decrease_user_connections");
|
2003-10-27 15:14:03 +01:00
|
|
|
(void) pthread_mutex_lock(&LOCK_user_conn);
|
|
|
|
DBUG_ASSERT(uc->connections);
|
|
|
|
if (!--uc->connections && !mqh_used)
|
2001-02-21 00:11:32 +01:00
|
|
|
{
|
|
|
|
/* Last connection for user; Delete it */
|
2001-03-07 22:50:44 +01:00
|
|
|
(void) hash_delete(&hash_user_connections,(byte*) uc);
|
2001-02-21 00:11:32 +01:00
|
|
|
}
|
2003-10-27 15:14:03 +01:00
|
|
|
(void) pthread_mutex_unlock(&LOCK_user_conn);
|
2001-03-06 14:24:08 +01:00
|
|
|
DBUG_VOID_RETURN;
|
2001-02-21 00:11:32 +01:00
|
|
|
}
|
|
|
|
|
2001-03-06 14:24:08 +01:00
|
|
|
|
2001-02-21 00:11:32 +01:00
|
|
|
void free_max_user_conn(void)
|
|
|
|
{
|
|
|
|
hash_free(&hash_user_connections);
|
|
|
|
}
|
|
|
|
|
2002-01-29 17:32:16 +01:00
|
|
|
|
2002-09-16 14:55:19 +02:00
|
|
|
/*
|
|
|
|
Mark all commands that somehow changes a table
|
|
|
|
This is used to check number of updates / hour
|
2004-10-22 08:54:14 +02:00
|
|
|
|
|
|
|
sql_command is actually set to SQLCOM_END sometimes
|
|
|
|
so we need the +1 to include it in the array.
|
2002-09-16 14:55:19 +02:00
|
|
|
*/
|
|
|
|
|
2004-10-22 08:54:14 +02:00
|
|
|
char uc_update_queries[SQLCOM_END+1];
|
2002-09-16 14:55:19 +02:00
|
|
|
|
|
|
|
void init_update_queries(void)
|
|
|
|
{
|
2004-10-22 08:54:14 +02:00
|
|
|
bzero((gptr) &uc_update_queries, sizeof(uc_update_queries));
|
|
|
|
|
2002-09-16 14:55:19 +02:00
|
|
|
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_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_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;
|
2002-11-29 12:29:27 +01:00
|
|
|
uc_update_queries[SQLCOM_UPDATE_MULTI]=1;
|
2004-07-16 00:15:55 +02:00
|
|
|
uc_update_queries[SQLCOM_CREATE_VIEW]=1;
|
|
|
|
uc_update_queries[SQLCOM_DROP_VIEW]=1;
|
2002-09-16 14:55:19 +02:00
|
|
|
}
|
|
|
|
|
2003-01-30 18:39:54 +01:00
|
|
|
bool is_update_query(enum enum_sql_command command)
|
|
|
|
{
|
2004-10-22 08:54:14 +02:00
|
|
|
DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
|
2003-01-30 18:39:54 +01:00
|
|
|
return uc_update_queries[command];
|
|
|
|
}
|
2002-09-16 14:55:19 +02:00
|
|
|
|
2002-01-29 17:32:16 +01:00
|
|
|
/*
|
|
|
|
Check if maximum queries per hour limit has been reached
|
|
|
|
returns 0 if OK.
|
2002-02-13 21:37:19 +01:00
|
|
|
|
2002-06-16 16:06:12 +02:00
|
|
|
In theory we would need a mutex in the USER_CONN structure for this to
|
|
|
|
be 100 % safe, but as the worst scenario is that we would miss counting
|
|
|
|
a couple of queries, this isn't critical.
|
2002-01-29 17:32:16 +01:00
|
|
|
*/
|
|
|
|
|
2002-05-15 12:50:38 +02:00
|
|
|
|
2002-06-16 16:06:12 +02:00
|
|
|
static bool check_mqh(THD *thd, uint check_command)
|
2002-01-29 17:32:16 +01:00
|
|
|
{
|
2004-04-05 12:56:05 +02:00
|
|
|
#ifdef NO_EMBEDDED_ACCESS_CHECKS
|
|
|
|
return(0);
|
|
|
|
#else
|
2002-01-29 17:32:16 +01:00
|
|
|
bool error=0;
|
2002-06-27 10:27:04 +02:00
|
|
|
time_t check_time = thd->start_time ? thd->start_time : time(NULL);
|
2002-05-15 12:50:38 +02:00
|
|
|
USER_CONN *uc=thd->user_connect;
|
2002-06-27 10:27:04 +02:00
|
|
|
DBUG_ENTER("check_mqh");
|
2002-02-01 19:53:24 +01:00
|
|
|
DBUG_ASSERT(uc != 0);
|
2002-01-29 17:32:16 +01:00
|
|
|
|
2002-06-27 10:27:04 +02:00
|
|
|
/* If more than a hour since last check, reset resource checking */
|
2002-06-20 15:46:25 +02:00
|
|
|
if (check_time - uc->intime >= 3600)
|
|
|
|
{
|
|
|
|
(void) pthread_mutex_lock(&LOCK_user_conn);
|
|
|
|
uc->questions=1;
|
|
|
|
uc->updates=0;
|
|
|
|
uc->conn_per_hour=0;
|
|
|
|
uc->intime=check_time;
|
|
|
|
(void) pthread_mutex_unlock(&LOCK_user_conn);
|
|
|
|
}
|
2002-06-27 10:27:04 +02:00
|
|
|
/* Check that we have not done too many questions / hour */
|
2002-06-20 15:46:25 +02:00
|
|
|
if (uc->user_resources.questions &&
|
|
|
|
uc->questions++ >= uc->user_resources.questions)
|
|
|
|
{
|
2004-10-20 15:06:54 +02:00
|
|
|
net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions",
|
|
|
|
(long) uc->user_resources.questions);
|
2002-06-20 15:46:25 +02:00
|
|
|
error=1;
|
|
|
|
goto end;
|
|
|
|
}
|
2002-05-15 12:50:38 +02:00
|
|
|
if (check_command < (uint) SQLCOM_END)
|
2002-01-29 17:32:16 +01:00
|
|
|
{
|
2002-06-27 10:27:04 +02:00
|
|
|
/* Check that we have not done too many updates / hour */
|
|
|
|
if (uc->user_resources.updates && uc_update_queries[check_command] &&
|
|
|
|
uc->updates++ >= uc->user_resources.updates)
|
|
|
|
{
|
2004-10-20 15:06:54 +02:00
|
|
|
net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_updates",
|
|
|
|
(long) uc->user_resources.updates);
|
2002-06-27 10:27:04 +02:00
|
|
|
error=1;
|
|
|
|
goto end;
|
|
|
|
}
|
2002-01-29 17:32:16 +01:00
|
|
|
}
|
|
|
|
end:
|
2002-02-01 19:53:24 +01:00
|
|
|
DBUG_RETURN(error);
|
2003-12-19 15:25:50 +01:00
|
|
|
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
|
2002-01-29 17:32:16 +01:00
|
|
|
}
|
|
|
|
|
2002-02-13 21:37:19 +01:00
|
|
|
|
2004-12-06 16:15:54 +01:00
|
|
|
static void reset_mqh(LEX_USER *lu, bool get_them= 0)
|
2002-02-01 19:53:24 +01:00
|
|
|
{
|
2003-12-19 15:25:50 +01:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2002-02-13 21:37:19 +01:00
|
|
|
(void) pthread_mutex_lock(&LOCK_user_conn);
|
2002-11-30 18:58:53 +01:00
|
|
|
if (lu) // for GRANT
|
2002-02-01 19:53:24 +01:00
|
|
|
{
|
2002-05-15 12:50:38 +02:00
|
|
|
USER_CONN *uc;
|
2002-06-25 20:20:10 +02:00
|
|
|
uint temp_len=lu->user.length+lu->host.length+2;
|
2002-02-01 19:53:24 +01:00
|
|
|
char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2];
|
|
|
|
|
2002-02-13 21:37:19 +01:00
|
|
|
memcpy(temp_user,lu->user.str,lu->user.length);
|
|
|
|
memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length);
|
2002-05-15 12:50:38 +02:00
|
|
|
temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0;
|
2002-02-13 21:37:19 +01:00
|
|
|
if ((uc = (struct user_conn *) hash_search(&hash_user_connections,
|
2002-06-20 15:46:25 +02:00
|
|
|
(byte*) temp_user, temp_len)))
|
2002-02-01 19:53:24 +01:00
|
|
|
{
|
|
|
|
uc->questions=0;
|
2002-06-20 15:46:25 +02:00
|
|
|
get_mqh(temp_user,&temp_user[lu->user.length+1],uc);
|
2002-05-15 12:50:38 +02:00
|
|
|
uc->updates=0;
|
|
|
|
uc->conn_per_hour=0;
|
2002-02-01 19:53:24 +01:00
|
|
|
}
|
|
|
|
}
|
2004-07-07 10:29:39 +02:00
|
|
|
else
|
2002-02-01 19:53:24 +01:00
|
|
|
{
|
2004-07-07 10:29:39 +02:00
|
|
|
/* for FLUSH PRIVILEGES and FLUSH USER_RESOURCES */
|
2002-02-13 21:37:19 +01:00
|
|
|
for (uint idx=0;idx < hash_user_connections.records; idx++)
|
2002-02-01 19:53:24 +01:00
|
|
|
{
|
2003-12-19 15:25:50 +01:00
|
|
|
USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections,
|
|
|
|
idx);
|
2002-05-15 12:50:38 +02:00
|
|
|
if (get_them)
|
|
|
|
get_mqh(uc->user,uc->host,uc);
|
|
|
|
uc->questions=0;
|
|
|
|
uc->updates=0;
|
|
|
|
uc->conn_per_hour=0;
|
2002-02-01 19:53:24 +01:00
|
|
|
}
|
|
|
|
}
|
2002-02-13 21:37:19 +01:00
|
|
|
(void) pthread_mutex_unlock(&LOCK_user_conn);
|
2003-12-19 15:25:50 +01:00
|
|
|
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
|
2002-02-01 19:53:24 +01:00
|
|
|
}
|
2002-02-13 21:37:19 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/*
|
2003-07-01 21:40:59 +02:00
|
|
|
Perform handshake, authorize client and update thd ACL variables.
|
2003-02-14 10:47:41 +01:00
|
|
|
SYNOPSIS
|
2003-07-01 21:40:59 +02:00
|
|
|
check_connection()
|
2003-07-18 16:25:54 +02:00
|
|
|
thd thread handle
|
2003-02-14 10:47:41 +01:00
|
|
|
|
|
|
|
RETURN
|
2003-07-18 16:25:54 +02:00
|
|
|
0 success, OK is sent to user, thd is updated.
|
2003-07-01 21:40:59 +02:00
|
|
|
-1 error, which is sent to user
|
|
|
|
> 0 error code (not sent to user)
|
2000-07-31 21:29:14 +02:00
|
|
|
*/
|
|
|
|
|
2003-09-26 12:33:13 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
|
|
|
static int check_connection(THD *thd)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2003-07-01 21:40:59 +02:00
|
|
|
uint connect_errors= 0;
|
2000-07-31 21:29:14 +02:00
|
|
|
NET *net= &thd->net;
|
2003-02-14 10:47:41 +01:00
|
|
|
|
2003-07-01 21:40:59 +02:00
|
|
|
DBUG_PRINT("info",
|
|
|
|
("New connection received on %s", vio_description(net->vio)));
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!thd->host) // If TCP/IP connection
|
|
|
|
{
|
2000-12-07 13:08:48 +01:00
|
|
|
char ip[30];
|
2000-08-21 13:35:27 +02:00
|
|
|
|
2003-02-17 21:07:26 +01:00
|
|
|
if (vio_peer_addr(net->vio, ip, &thd->peer_port))
|
2000-07-31 21:29:14 +02:00
|
|
|
return (ER_BAD_HOST_ERROR);
|
2003-07-01 21:40:59 +02:00
|
|
|
if (!(thd->ip= my_strdup(ip,MYF(0))))
|
2000-07-31 21:29:14 +02:00
|
|
|
return (ER_OUT_OF_RESOURCES);
|
2003-07-01 21:40:59 +02:00
|
|
|
thd->host_or_ip= thd->ip;
|
2000-07-31 21:29:14 +02:00
|
|
|
#if !defined(HAVE_SYS_UN_H) || defined(HAVE_mit_thread)
|
|
|
|
/* Fast local hostname resolve for Win32 */
|
|
|
|
if (!strcmp(thd->ip,"127.0.0.1"))
|
2003-07-04 22:06:19 +02:00
|
|
|
{
|
2003-09-26 12:33:13 +02:00
|
|
|
thd->host= (char*) my_localhost;
|
|
|
|
thd->host_or_ip= my_localhost;
|
2003-07-04 22:06:19 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
2003-02-12 20:55:37 +01:00
|
|
|
if (!(specialflag & SPECIAL_NO_RESOLVE))
|
|
|
|
{
|
|
|
|
vio_in_addr(net->vio,&thd->remote.sin_addr);
|
|
|
|
thd->host=ip_to_hostname(&thd->remote.sin_addr,&connect_errors);
|
|
|
|
/* Cut very long hostnames to avoid possible overflows */
|
|
|
|
if (thd->host)
|
2003-03-19 21:25:44 +01:00
|
|
|
{
|
2003-02-12 20:55:37 +01:00
|
|
|
thd->host[min(strlen(thd->host), HOSTNAME_LENGTH)]= 0;
|
2003-03-19 21:25:44 +01:00
|
|
|
thd->host_or_ip= thd->host;
|
|
|
|
}
|
2003-02-12 20:55:37 +01:00
|
|
|
if (connect_errors > max_connect_errors)
|
|
|
|
return(ER_HOST_IS_BLOCKED);
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2001-10-11 17:58:40 +02:00
|
|
|
DBUG_PRINT("info",("Host: %s ip: %s",
|
|
|
|
thd->host ? thd->host : "unknown host",
|
|
|
|
thd->ip ? thd->ip : "unknown ip"));
|
2000-07-31 21:29:14 +02:00
|
|
|
if (acl_check_host(thd->host,thd->ip))
|
|
|
|
return(ER_HOST_NOT_PRIVILEGED);
|
|
|
|
}
|
2000-09-02 06:58:42 +02:00
|
|
|
else /* Hostname given means that the connection was on a socket */
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2001-10-11 17:58:40 +02:00
|
|
|
DBUG_PRINT("info",("Host: %s",thd->host));
|
2003-02-17 21:07:26 +01:00
|
|
|
thd->host_or_ip= thd->host;
|
|
|
|
thd->ip= 0;
|
2003-07-01 21:40:59 +02:00
|
|
|
bzero((char*) &thd->remote, sizeof(struct sockaddr));
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
vio_keepalive(net->vio, TRUE);
|
2003-07-01 21:40:59 +02:00
|
|
|
ulong pkt_len= 0;
|
|
|
|
char *end;
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2001-02-15 17:00:30 +01:00
|
|
|
/* buff[] needs to big enough to hold the server_version variable */
|
2003-07-01 21:40:59 +02:00
|
|
|
char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
|
2003-05-26 18:01:20 +02:00
|
|
|
ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB |
|
|
|
|
CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION);
|
2001-12-05 12:03:00 +01:00
|
|
|
|
2001-03-06 14:24:08 +01:00
|
|
|
if (opt_using_transactions)
|
|
|
|
client_flags|=CLIENT_TRANSACTIONS;
|
|
|
|
#ifdef HAVE_COMPRESS
|
|
|
|
client_flags |= CLIENT_COMPRESS;
|
|
|
|
#endif /* HAVE_COMPRESS */
|
2001-12-05 12:03:00 +01:00
|
|
|
#ifdef HAVE_OPENSSL
|
|
|
|
if (ssl_acceptor_fd)
|
2004-10-07 00:45:06 +02:00
|
|
|
client_flags |= CLIENT_SSL; /* Wow, SSL is available! */
|
2001-12-05 12:03:00 +01:00
|
|
|
#endif /* HAVE_OPENSSL */
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2003-07-01 21:40:59 +02:00
|
|
|
end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1;
|
|
|
|
int4store((uchar*) end, thd->thread_id);
|
|
|
|
end+= 4;
|
|
|
|
/*
|
|
|
|
So as check_connection is the only entry point to authorization
|
|
|
|
procedure, scramble is set here. This gives us new scramble for
|
|
|
|
each handshake.
|
|
|
|
*/
|
|
|
|
create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
|
|
|
|
/*
|
|
|
|
Old clients does not understand long scrambles, but can ignore packet
|
2003-07-04 18:52:04 +02:00
|
|
|
tail: that's why first part of the scramble is placed here, and second
|
2003-07-01 21:40:59 +02:00
|
|
|
part at the end of packet.
|
|
|
|
*/
|
2003-07-18 16:25:54 +02:00
|
|
|
end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1;
|
2003-07-01 21:40:59 +02:00
|
|
|
|
|
|
|
int2store(end, client_flags);
|
|
|
|
/* write server characteristics: up to 16 bytes allowed */
|
2003-06-03 11:59:17 +02:00
|
|
|
end[2]=(char) default_charset_info->number;
|
2003-07-01 21:40:59 +02:00
|
|
|
int2store(end+3, thd->server_status);
|
|
|
|
bzero(end+5, 13);
|
|
|
|
end+= 18;
|
|
|
|
/* write scramble tail */
|
|
|
|
end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323,
|
|
|
|
SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1;
|
|
|
|
|
|
|
|
/* At this point we write connection message and read reply */
|
|
|
|
if (net_write_command(net, (uchar) protocol_version, "", 0, buff,
|
2000-07-31 21:29:14 +02:00
|
|
|
(uint) (end-buff)) ||
|
2003-02-12 20:55:37 +01:00
|
|
|
(pkt_len= my_net_read(net)) == packet_error ||
|
2000-07-31 21:29:14 +02:00
|
|
|
pkt_len < MIN_HANDSHAKE_SIZE)
|
|
|
|
{
|
|
|
|
inc_host_errors(&thd->remote.sin_addr);
|
|
|
|
return(ER_HANDSHAKE_ERROR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef _CUSTOMCONFIG_
|
|
|
|
#include "_cust_sql_parse.h"
|
|
|
|
#endif
|
|
|
|
if (connect_errors)
|
|
|
|
reset_host_errors(&thd->remote.sin_addr);
|
2002-07-23 17:31:22 +02:00
|
|
|
if (thd->packet.alloc(thd->variables.net_buffer_length))
|
2000-07-31 21:29:14 +02:00
|
|
|
return(ER_OUT_OF_RESOURCES);
|
|
|
|
|
|
|
|
thd->client_capabilities=uint2korr(net->read_pos);
|
2003-05-26 18:01:20 +02:00
|
|
|
#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
|
2003-01-18 15:39:21 +01:00
|
|
|
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
|
|
|
|
{
|
|
|
|
thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
|
|
|
|
thd->max_client_packet_length= uint4korr(net->read_pos+4);
|
2003-08-18 23:08:08 +02:00
|
|
|
DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
|
|
|
|
/*
|
|
|
|
Use server character set and collation if
|
|
|
|
- client has not specified a character set
|
|
|
|
- client character set is the same as the servers
|
|
|
|
- client character set doesn't exists in server
|
|
|
|
*/
|
2003-05-26 18:01:20 +02:00
|
|
|
if (!(thd->variables.character_set_client=
|
2003-08-18 23:08:08 +02:00
|
|
|
get_charset((uint) net->read_pos[8], MYF(0))) ||
|
|
|
|
!my_strcasecmp(&my_charset_latin1,
|
|
|
|
global_system_variables.character_set_client->name,
|
|
|
|
thd->variables.character_set_client->name))
|
2003-05-28 10:54:18 +02:00
|
|
|
{
|
2003-05-26 18:01:20 +02:00
|
|
|
thd->variables.character_set_client=
|
|
|
|
global_system_variables.character_set_client;
|
2003-05-28 10:54:18 +02:00
|
|
|
thd->variables.collation_connection=
|
|
|
|
global_system_variables.collation_connection;
|
2003-05-30 20:09:35 +02:00
|
|
|
thd->variables.character_set_results=
|
|
|
|
global_system_variables.character_set_results;
|
2003-05-28 10:54:18 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-05-30 20:09:35 +02:00
|
|
|
thd->variables.character_set_results=
|
2003-05-28 10:54:18 +02:00
|
|
|
thd->variables.collation_connection=
|
|
|
|
thd->variables.character_set_client;
|
|
|
|
}
|
2003-08-18 23:08:08 +02:00
|
|
|
thd->update_charset();
|
2003-05-26 18:01:20 +02:00
|
|
|
end= (char*) net->read_pos+32;
|
2003-01-18 15:39:21 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
thd->max_client_packet_length= uint3korr(net->read_pos+2);
|
|
|
|
end= (char*) net->read_pos+5;
|
|
|
|
}
|
|
|
|
|
2001-08-09 17:51:38 +02:00
|
|
|
if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
|
2003-01-16 01:04:50 +01:00
|
|
|
thd->variables.sql_mode|= MODE_IGNORE_SPACE;
|
2000-07-31 21:29:14 +02:00
|
|
|
#ifdef HAVE_OPENSSL
|
2001-12-05 12:03:00 +01:00
|
|
|
DBUG_PRINT("info", ("client capabilities: %d", thd->client_capabilities));
|
2000-07-31 21:29:14 +02:00
|
|
|
if (thd->client_capabilities & CLIENT_SSL)
|
|
|
|
{
|
|
|
|
/* Do the SSL layering. */
|
2003-06-19 12:38:52 +02:00
|
|
|
if (!ssl_acceptor_fd)
|
|
|
|
{
|
|
|
|
inc_host_errors(&thd->remote.sin_addr);
|
|
|
|
return(ER_HANDSHAKE_ERROR);
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_PRINT("info", ("IO layer change in progress..."));
|
2002-11-05 09:05:58 +01:00
|
|
|
if (sslaccept(ssl_acceptor_fd, net->vio, thd->variables.net_wait_timeout))
|
|
|
|
{
|
|
|
|
DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
|
|
|
|
pkt_len));
|
|
|
|
inc_host_errors(&thd->remote.sin_addr);
|
2002-11-30 18:58:53 +01:00
|
|
|
return(ER_HANDSHAKE_ERROR);
|
2002-11-05 09:05:58 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_PRINT("info", ("Reading user information over SSL layer"));
|
2003-07-01 21:40:59 +02:00
|
|
|
if ((pkt_len= my_net_read(net)) == packet_error ||
|
2000-07-31 21:29:14 +02:00
|
|
|
pkt_len < NORMAL_HANDSHAKE_SIZE)
|
|
|
|
{
|
2001-12-05 12:03:00 +01:00
|
|
|
DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
|
|
|
|
pkt_len));
|
2000-07-31 21:29:14 +02:00
|
|
|
inc_host_errors(&thd->remote.sin_addr);
|
|
|
|
return(ER_HANDSHAKE_ERROR);
|
|
|
|
}
|
|
|
|
}
|
2003-01-18 15:39:21 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (end >= (char*) net->read_pos+ pkt_len +2)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2003-01-18 15:39:21 +01:00
|
|
|
inc_host_errors(&thd->remote.sin_addr);
|
|
|
|
return(ER_HANDSHAKE_ERROR);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (thd->client_capabilities & CLIENT_INTERACTIVE)
|
2003-02-12 20:55:37 +01:00
|
|
|
thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
|
2001-03-21 19:13:46 +01:00
|
|
|
if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
|
2003-02-12 20:55:37 +01:00
|
|
|
opt_using_transactions)
|
|
|
|
net->return_status= &thd->server_status;
|
2002-07-23 17:31:22 +02:00
|
|
|
net->read_timeout=(uint) thd->variables.net_read_timeout;
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2003-07-01 21:40:59 +02:00
|
|
|
char *user= end;
|
|
|
|
char *passwd= strend(user)+1;
|
2003-07-04 18:52:04 +02:00
|
|
|
char *db= passwd;
|
2003-09-03 12:12:10 +02:00
|
|
|
char db_buff[NAME_LEN+1]; // buffer to store db in utf8
|
2003-12-24 14:58:06 +01:00
|
|
|
char user_buff[USERNAME_LENGTH+1]; // buffer to store user in utf8
|
2003-07-04 18:52:04 +02:00
|
|
|
/*
|
|
|
|
Old clients send null-terminated string as password; new clients send
|
|
|
|
the size (1 byte) + string (not null-terminated). Hence in case of empty
|
|
|
|
password both send '\0'.
|
|
|
|
*/
|
|
|
|
uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
|
|
|
|
*passwd++ : strlen(passwd);
|
|
|
|
db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
|
|
|
|
db + passwd_len + 1 : 0;
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2003-07-31 15:11:52 +02:00
|
|
|
/* Since 4.1 all database names are stored in utf8 */
|
|
|
|
if (db)
|
2002-11-30 18:58:53 +01:00
|
|
|
{
|
2004-10-29 14:00:39 +02:00
|
|
|
uint dummy_errors;
|
2003-09-16 00:39:25 +02:00
|
|
|
db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
|
|
|
|
system_charset_info,
|
|
|
|
db, strlen(db),
|
2004-10-29 14:00:39 +02:00
|
|
|
thd->charset(), &dummy_errors)]= 0;
|
2003-09-03 12:12:10 +02:00
|
|
|
db= db_buff;
|
2003-07-31 15:11:52 +02:00
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2003-12-24 14:58:06 +01:00
|
|
|
if (user)
|
|
|
|
{
|
2004-10-29 14:00:39 +02:00
|
|
|
uint dummy_errors;
|
2003-12-24 14:58:06 +01:00
|
|
|
user_buff[copy_and_convert(user_buff, sizeof(user_buff)-1,
|
|
|
|
system_charset_info, user, strlen(user),
|
2004-10-29 14:00:39 +02:00
|
|
|
thd->charset(), &dummy_errors)]= '\0';
|
2003-12-24 14:58:06 +01:00
|
|
|
user= user_buff;
|
|
|
|
}
|
|
|
|
|
2003-07-01 21:40:59 +02:00
|
|
|
if (thd->user)
|
|
|
|
x_free(thd->user);
|
2003-07-18 16:25:54 +02:00
|
|
|
if (!(thd->user= my_strdup(user, MYF(0))))
|
|
|
|
return (ER_OUT_OF_RESOURCES);
|
2004-10-14 17:03:46 +02:00
|
|
|
return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2003-02-14 10:47:41 +01:00
|
|
|
|
2003-12-08 06:13:14 +01:00
|
|
|
void execute_init_command(THD *thd, sys_var_str *init_command_var,
|
|
|
|
rw_lock_t *var_mutex)
|
2003-07-18 11:11:01 +02:00
|
|
|
{
|
|
|
|
Vio* save_vio;
|
|
|
|
ulong save_client_capabilities;
|
|
|
|
|
2003-12-08 06:13:14 +01:00
|
|
|
thd->proc_info= "Execution of init_command";
|
|
|
|
/*
|
|
|
|
We need to lock init_command_var because
|
|
|
|
during execution of init_command_var query
|
|
|
|
values of init_command_var can't be changed
|
|
|
|
*/
|
|
|
|
rw_rdlock(var_mutex);
|
|
|
|
thd->query= init_command_var->value;
|
|
|
|
thd->query_length= init_command_var->value_length;
|
2003-07-18 11:11:01 +02:00
|
|
|
save_client_capabilities= thd->client_capabilities;
|
|
|
|
thd->client_capabilities|= CLIENT_MULTI_QUERIES;
|
2003-12-08 06:13:14 +01:00
|
|
|
/*
|
|
|
|
We don't need return result of execution to client side.
|
|
|
|
To forbid this we should set thd->net.vio to 0.
|
|
|
|
*/
|
2003-07-18 11:11:01 +02:00
|
|
|
save_vio= thd->net.vio;
|
|
|
|
thd->net.vio= 0;
|
|
|
|
dispatch_command(COM_QUERY, thd, thd->query, thd->query_length+1);
|
2003-12-08 06:13:14 +01:00
|
|
|
rw_unlock(var_mutex);
|
2003-07-18 11:11:01 +02:00
|
|
|
thd->client_capabilities= save_client_capabilities;
|
|
|
|
thd->net.vio= save_vio;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
pthread_handler_decl(handle_one_connection,arg)
|
|
|
|
{
|
|
|
|
THD *thd=(THD*) arg;
|
|
|
|
uint launch_time =
|
2000-08-23 14:02:27 +02:00
|
|
|
(uint) ((thd->thr_create_time = time(NULL)) - thd->connect_time);
|
2000-07-31 21:29:14 +02:00
|
|
|
if (launch_time >= slow_launch_time)
|
|
|
|
statistic_increment(slow_launch_threads,&LOCK_status );
|
|
|
|
|
|
|
|
pthread_detach_this_thread();
|
|
|
|
|
2002-01-15 23:42:52 +01:00
|
|
|
#if !defined( __WIN__) && !defined(OS2) // Win32 calls this in pthread_create
|
2004-07-07 10:29:39 +02:00
|
|
|
/* The following calls needs to be done before we call DBUG_ macros */
|
2002-06-03 23:40:27 +02:00
|
|
|
if (!(test_flags & TEST_NO_THREADS) & my_thread_init())
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2003-05-26 18:01:20 +02:00
|
|
|
close_connection(thd, ER_OUT_OF_RESOURCES, 1);
|
2002-08-22 15:50:58 +02:00
|
|
|
statistic_increment(aborted_connects,&LOCK_status);
|
2000-07-31 21:29:14 +02:00
|
|
|
end_thread(thd,0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2002-01-15 23:42:52 +01:00
|
|
|
/*
|
|
|
|
handle_one_connection() is the only way a thread would start
|
|
|
|
and would always be on top of the stack, therefore, the thread
|
|
|
|
stack always starts at the address of the first local variable
|
|
|
|
of handle_one_connection, which is thd. We need to know the
|
|
|
|
start of the stack so that we could check for stack overruns.
|
|
|
|
*/
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_PRINT("info", ("handle_one_connection called by thread %d\n",
|
|
|
|
thd->thread_id));
|
2004-07-07 10:29:39 +02:00
|
|
|
/* now that we've called my_thread_init(), it is safe to call DBUG_* */
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2001-08-22 00:45:07 +02:00
|
|
|
#if defined(__WIN__)
|
2000-07-31 21:29:14 +02:00
|
|
|
init_signals(); // IRENA; testing ?
|
2003-01-28 07:38:28 +01:00
|
|
|
#elif !defined(OS2) && !defined(__NETWARE__)
|
2000-07-31 21:29:14 +02:00
|
|
|
sigset_t set;
|
|
|
|
VOID(sigemptyset(&set)); // Get mask in use
|
|
|
|
VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
|
|
|
|
#endif
|
|
|
|
if (thd->store_globals())
|
|
|
|
{
|
2003-05-26 18:01:20 +02:00
|
|
|
close_connection(thd, ER_OUT_OF_RESOURCES, 1);
|
2002-08-22 15:50:58 +02:00
|
|
|
statistic_increment(aborted_connects,&LOCK_status);
|
2000-07-31 21:29:14 +02:00
|
|
|
end_thread(thd,0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
NET *net= &thd->net;
|
|
|
|
thd->thread_stack= (char*) &thd;
|
|
|
|
|
2003-07-01 21:40:59 +02:00
|
|
|
if ((error=check_connection(thd)))
|
2000-07-31 21:29:14 +02:00
|
|
|
{ // Wrong permissions
|
|
|
|
if (error > 0)
|
2004-10-20 15:06:54 +02:00
|
|
|
net_printf_error(thd, error, thd->host_or_ip);
|
2000-07-31 21:29:14 +02:00
|
|
|
#ifdef __NT__
|
|
|
|
if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
|
2003-02-12 20:55:37 +01:00
|
|
|
my_sleep(1000); /* must wait after eof() */
|
2000-07-31 21:29:14 +02:00
|
|
|
#endif
|
2002-08-22 15:50:58 +02:00
|
|
|
statistic_increment(aborted_connects,&LOCK_status);
|
2000-07-31 21:29:14 +02:00
|
|
|
goto end_thread;
|
|
|
|
}
|
2003-01-28 07:38:28 +01:00
|
|
|
#ifdef __NETWARE__
|
|
|
|
netware_reg_user(thd->ip, thd->user, "MySQL");
|
|
|
|
#endif
|
2002-12-20 13:58:27 +01:00
|
|
|
if (thd->variables.max_join_size == HA_POS_ERROR)
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->options |= OPTION_BIG_SELECTS;
|
|
|
|
if (thd->client_capabilities & CLIENT_COMPRESS)
|
|
|
|
net->compress=1; // Use compression
|
|
|
|
|
2003-07-18 11:11:01 +02:00
|
|
|
thd->version= refresh_version;
|
2004-10-14 00:53:59 +02:00
|
|
|
thd->proc_info= 0;
|
2004-11-12 13:38:01 +01:00
|
|
|
thd->command= COM_SLEEP;
|
2004-10-14 00:53:59 +02:00
|
|
|
thd->set_time();
|
|
|
|
thd->init_for_queries();
|
2004-10-29 18:26:52 +02:00
|
|
|
|
2004-03-20 15:56:20 +01:00
|
|
|
if (sys_init_connect.value_length && !(thd->master_access & SUPER_ACL))
|
2003-07-18 11:11:01 +02:00
|
|
|
{
|
2003-12-08 06:13:14 +01:00
|
|
|
execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect);
|
|
|
|
if (thd->query_error)
|
2003-12-09 19:05:41 +01:00
|
|
|
thd->killed= THD::KILL_CONNECTION;
|
2003-07-18 11:11:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
thd->proc_info=0;
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->set_time();
|
2003-12-21 20:26:45 +01:00
|
|
|
thd->init_for_queries();
|
2003-03-31 10:39:46 +02:00
|
|
|
while (!net->error && net->vio != 0 && !(thd->killed == THD::KILL_CONNECTION))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
if (do_command(thd))
|
|
|
|
break;
|
|
|
|
}
|
2002-02-01 19:53:24 +01:00
|
|
|
if (thd->user_connect)
|
|
|
|
decrease_user_connections(thd->user_connect);
|
2002-09-03 08:50:36 +02:00
|
|
|
if (net->error && net->vio != 0 && net->report_error)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-06-01 16:29:24 +02:00
|
|
|
if (!thd->killed && thd->variables.log_warnings > 1)
|
2004-10-29 18:26:52 +02:00
|
|
|
sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
|
2004-09-04 20:17:09 +02:00
|
|
|
thd->thread_id,(thd->db ? thd->db : "unconnected"),
|
|
|
|
thd->user ? thd->user : "unauthenticated",
|
|
|
|
thd->host_or_ip,
|
|
|
|
(net->last_errno ? ER(net->last_errno) :
|
|
|
|
ER(ER_UNKNOWN_ERROR)));
|
2004-10-20 15:06:54 +02:00
|
|
|
net_send_error(thd, net->last_errno, NullS);
|
2002-11-16 19:19:10 +01:00
|
|
|
statistic_increment(aborted_threads,&LOCK_status);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2003-06-05 11:29:13 +02:00
|
|
|
else if (thd->killed)
|
|
|
|
{
|
|
|
|
statistic_increment(aborted_threads,&LOCK_status);
|
|
|
|
}
|
2002-02-01 19:53:24 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
end_thread:
|
2003-05-26 18:01:20 +02:00
|
|
|
close_connection(thd, 0, 1);
|
2000-07-31 21:29:14 +02:00
|
|
|
end_thread(thd,1);
|
|
|
|
/*
|
|
|
|
If end_thread returns, we are either running with --one-thread
|
|
|
|
or this thread has been schedule to handle the next query
|
|
|
|
*/
|
|
|
|
thd= current_thd;
|
|
|
|
} while (!(test_flags & TEST_NO_THREADS));
|
|
|
|
/* The following is only executed if we are not using --one-thread */
|
|
|
|
return(0); /* purecov: deadcode */
|
|
|
|
}
|
|
|
|
|
2003-09-29 18:07:53 +02:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
|
|
|
|
2000-11-28 03:47:47 +01:00
|
|
|
/*
|
|
|
|
Execute commands from bootstrap_file.
|
|
|
|
Used when creating the initial grant tables
|
|
|
|
*/
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2002-11-07 02:54:00 +01:00
|
|
|
extern "C" pthread_handler_decl(handle_bootstrap,arg)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2000-11-28 03:47:47 +01:00
|
|
|
THD *thd=(THD*) arg;
|
|
|
|
FILE *file=bootstrap_file;
|
|
|
|
char *buff;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2001-03-06 14:24:08 +01:00
|
|
|
/* The following must be called before DBUG_ENTER */
|
2000-11-28 03:47:47 +01:00
|
|
|
if (my_thread_init() || thd->store_globals())
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2003-09-29 18:07:53 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2003-05-26 18:01:20 +02:00
|
|
|
close_connection(thd, ER_OUT_OF_RESOURCES, 1);
|
2003-09-29 18:07:53 +02:00
|
|
|
#endif
|
2003-01-30 21:15:44 +01:00
|
|
|
thd->fatal_error();
|
2000-11-28 03:47:47 +01:00
|
|
|
goto end;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2001-03-06 14:24:08 +01:00
|
|
|
DBUG_ENTER("handle_bootstrap");
|
|
|
|
|
2003-09-29 18:07:53 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2001-03-06 14:24:08 +01:00
|
|
|
pthread_detach_this_thread();
|
|
|
|
thd->thread_stack= (char*) &thd;
|
2003-01-28 07:38:28 +01:00
|
|
|
#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
|
2000-07-31 21:29:14 +02:00
|
|
|
sigset_t set;
|
2002-08-04 15:14:51 +02:00
|
|
|
VOID(sigemptyset(&set)); // Get mask in use
|
|
|
|
VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
|
2000-07-31 21:29:14 +02:00
|
|
|
#endif
|
2003-09-29 18:07:53 +02:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2002-12-20 13:58:27 +01:00
|
|
|
if (thd->variables.max_join_size == HA_POS_ERROR)
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->options |= OPTION_BIG_SELECTS;
|
|
|
|
|
|
|
|
thd->proc_info=0;
|
|
|
|
thd->version=refresh_version;
|
2002-08-08 19:49:06 +02:00
|
|
|
thd->priv_user=thd->user=(char*) my_strdup("boot", MYF(MY_WME));
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2000-11-28 03:47:47 +01:00
|
|
|
buff= (char*) thd->net.buff;
|
2003-12-21 20:26:45 +01:00
|
|
|
thd->init_for_queries();
|
2000-07-31 21:29:14 +02:00
|
|
|
while (fgets(buff, thd->net.max_packet, file))
|
|
|
|
{
|
2000-08-21 23:18:32 +02:00
|
|
|
uint length=(uint) strlen(buff);
|
2004-04-30 13:02:17 +02:00
|
|
|
if (buff[length-1]!='\n' && !feof(file))
|
|
|
|
{
|
2004-10-20 15:06:54 +02:00
|
|
|
net_send_error(thd, ER_NET_PACKET_TOO_LARGE, NullS);
|
2004-11-12 13:34:00 +01:00
|
|
|
thd->fatal_error();
|
2004-04-30 13:02:17 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-03-15 14:24:21 +01:00
|
|
|
while (length && (my_isspace(thd->charset(), buff[length-1]) ||
|
2002-03-12 18:37:58 +01:00
|
|
|
buff[length-1] == ';'))
|
2000-07-31 21:29:14 +02:00
|
|
|
length--;
|
|
|
|
buff[length]=0;
|
2001-10-08 22:20:19 +02:00
|
|
|
thd->query_length=length;
|
2001-12-21 06:00:58 +01:00
|
|
|
thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1);
|
|
|
|
thd->query[length] = '\0';
|
2004-03-01 07:15:33 +01:00
|
|
|
/*
|
|
|
|
We don't need to obtain LOCK_thread_count here because in bootstrap
|
|
|
|
mode we have only one thread.
|
|
|
|
*/
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->query_id=query_id++;
|
2002-06-16 16:06:12 +02:00
|
|
|
if (mqh_used && thd->user_connect && check_mqh(thd, SQLCOM_END))
|
2002-02-01 19:53:24 +01:00
|
|
|
{
|
|
|
|
thd->net.error = 0;
|
|
|
|
close_thread_tables(thd); // Free tables
|
2004-11-08 00:13:54 +01:00
|
|
|
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
|
2002-02-01 19:53:24 +01:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
mysql_parse(thd,thd->query,length);
|
|
|
|
close_thread_tables(thd); // Free tables
|
2003-01-30 21:15:44 +01:00
|
|
|
if (thd->is_fatal_error)
|
2000-11-28 03:47:47 +01:00
|
|
|
break;
|
2004-11-08 00:13:54 +01:00
|
|
|
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
|
2002-03-15 22:57:31 +01:00
|
|
|
free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC));
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2000-11-28 03:47:47 +01:00
|
|
|
|
|
|
|
/* thd->fatal_error should be set in case something went wrong */
|
|
|
|
end:
|
2003-09-29 18:07:53 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-11-28 03:47:47 +01:00
|
|
|
(void) pthread_mutex_lock(&LOCK_thread_count);
|
|
|
|
thread_count--;
|
|
|
|
(void) pthread_mutex_unlock(&LOCK_thread_count);
|
2002-08-22 15:50:58 +02:00
|
|
|
(void) pthread_cond_broadcast(&COND_thread_count);
|
2000-11-28 03:47:47 +01:00
|
|
|
my_thread_end();
|
|
|
|
pthread_exit(0);
|
2003-09-29 18:07:53 +02:00
|
|
|
#endif
|
2000-11-28 03:47:47 +01:00
|
|
|
DBUG_RETURN(0); // Never reached
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
/* This works because items are allocated with sql_alloc() */
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
void free_items(Item *item)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-28 15:53:17 +01:00
|
|
|
Item *next;
|
2004-07-15 03:19:07 +02:00
|
|
|
DBUG_ENTER("free_items");
|
2004-11-28 15:53:17 +01:00
|
|
|
for (; item ; item=next)
|
|
|
|
{
|
|
|
|
next=item->next;
|
2004-01-20 17:55:47 +01:00
|
|
|
item->delete_self();
|
2004-11-28 15:53:17 +01:00
|
|
|
}
|
2004-07-15 03:19:07 +02:00
|
|
|
DBUG_VOID_RETURN;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2003-11-18 12:47:27 +01:00
|
|
|
/* This works because items are allocated with sql_alloc() */
|
|
|
|
|
|
|
|
void cleanup_items(Item *item)
|
|
|
|
{
|
|
|
|
for (; item ; item=item->next)
|
|
|
|
item->cleanup();
|
|
|
|
}
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd)
|
|
|
|
{
|
|
|
|
TABLE* table;
|
|
|
|
TABLE_LIST* table_list;
|
|
|
|
int error = 0;
|
|
|
|
DBUG_ENTER("mysql_table_dump");
|
|
|
|
db = (db && db[0]) ? db : thd->db;
|
2001-07-11 09:36:22 +02:00
|
|
|
if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST))))
|
2001-02-21 00:11:32 +01:00
|
|
|
DBUG_RETURN(1); // out of memory
|
2004-07-16 00:15:55 +02:00
|
|
|
table_list->db= db;
|
|
|
|
table_list->real_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
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2001-01-31 03:47:25 +01:00
|
|
|
if (!db || check_db_name(db))
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_DB_NAME ,MYF(0), db ? db : "NULL");
|
2001-01-31 03:47:25 +01:00
|
|
|
goto err;
|
|
|
|
}
|
2003-01-29 17:56:34 +01:00
|
|
|
if (lower_case_table_names)
|
2003-02-07 14:47:24 +01:00
|
|
|
my_casedn_str(files_charset_info, tbl_name);
|
2003-02-04 20:52:14 +01:00
|
|
|
remove_escape(table_list->real_name);
|
2003-01-29 17:56:34 +01:00
|
|
|
|
|
|
|
if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT)))
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
|
2004-04-10 00:14:32 +02:00
|
|
|
if (check_one_table_access(thd, SELECT_ACL, table_list))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto err;
|
|
|
|
thd->free_list = 0;
|
2001-12-05 12:03:00 +01:00
|
|
|
thd->query_length=(uint) strlen(tbl_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->query = tbl_name;
|
2004-11-18 10:16:06 +01:00
|
|
|
if ((error = mysqld_dump_create_info(thd, table_list, -1)))
|
2001-10-08 22:20:19 +02:00
|
|
|
{
|
2003-02-07 14:47:24 +01:00
|
|
|
my_error(ER_GET_ERRNO, MYF(0), my_errno);
|
2001-10-08 22:20:19 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
net_flush(&thd->net);
|
2003-01-19 00:31:43 +01:00
|
|
|
if ((error= table->file->dump(thd,fd)))
|
2004-06-10 21:18:57 +02:00
|
|
|
my_error(ER_GET_ERRNO, MYF(0), error);
|
2000-08-21 23:39:08 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
err:
|
|
|
|
close_thread_tables(thd);
|
2000-08-21 23:39:08 +02:00
|
|
|
DBUG_RETURN(error);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-07 14:47:24 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2004-03-08 15:47:42 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Read one command from socket and execute it (query or simple command).
|
|
|
|
This function is called in loop from thread function.
|
|
|
|
SYNOPSIS
|
|
|
|
do_command()
|
|
|
|
RETURN VALUE
|
|
|
|
0 success
|
|
|
|
1 request of thread shutdown (see dispatch_command() description)
|
|
|
|
*/
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
bool do_command(THD *thd)
|
|
|
|
{
|
|
|
|
char *packet;
|
2001-12-05 12:03:00 +01:00
|
|
|
uint old_timeout;
|
|
|
|
ulong packet_length;
|
2000-07-31 21:29:14 +02:00
|
|
|
NET *net;
|
|
|
|
enum enum_server_command command;
|
|
|
|
DBUG_ENTER("do_command");
|
|
|
|
|
|
|
|
net= &thd->net;
|
2003-07-01 18:05:08 +02:00
|
|
|
/*
|
|
|
|
indicator of uninitialized lex => normal flow of errors handling
|
|
|
|
(see my_message_sql)
|
|
|
|
*/
|
2003-07-09 17:07:12 +02:00
|
|
|
thd->lex->current_select= 0;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
packet=0;
|
2002-07-23 17:31:22 +02:00
|
|
|
old_timeout=net->read_timeout;
|
2004-07-07 10:29:39 +02:00
|
|
|
/* Wait max for 8 hours */
|
2002-07-23 17:31:22 +02:00
|
|
|
net->read_timeout=(uint) thd->variables.net_wait_timeout;
|
2002-11-03 23:56:25 +01:00
|
|
|
thd->clear_error(); // Clear error message
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
net_new_transaction(net);
|
|
|
|
if ((packet_length=my_net_read(net)) == packet_error)
|
|
|
|
{
|
2003-01-04 14:17:16 +01:00
|
|
|
DBUG_PRINT("info",("Got error %d reading command from socket %s",
|
|
|
|
net->error,
|
|
|
|
vio_description(net->vio)));
|
|
|
|
/* Check if we can continue without closing the connection */
|
|
|
|
if (net->error != 3)
|
2003-06-05 11:29:13 +02:00
|
|
|
{
|
|
|
|
statistic_increment(aborted_threads,&LOCK_status);
|
2003-01-04 14:17:16 +01:00
|
|
|
DBUG_RETURN(TRUE); // We have to close it.
|
2003-06-05 11:29:13 +02:00
|
|
|
}
|
2004-10-20 15:06:54 +02:00
|
|
|
net_send_error(thd, net->last_errno, NullS);
|
2003-01-04 14:17:16 +01:00
|
|
|
net->error= 0;
|
2003-01-02 15:21:22 +01:00
|
|
|
DBUG_RETURN(FALSE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-09-28 19:08:00 +02:00
|
|
|
if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA)
|
2003-03-31 10:39:46 +02:00
|
|
|
thd->killed= THD::NOT_KILLED;
|
2003-03-21 06:37:01 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
packet=(char*) net->read_pos;
|
|
|
|
command = (enum enum_server_command) (uchar) packet[0];
|
2003-01-04 14:17:16 +01:00
|
|
|
if (command >= COM_END)
|
|
|
|
command= COM_END; // Wrong command
|
2001-10-11 17:58:40 +02:00
|
|
|
DBUG_PRINT("info",("Command on %s = %d (%s)",
|
|
|
|
vio_description(net->vio), command,
|
|
|
|
command_name[command]));
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2002-07-23 17:31:22 +02:00
|
|
|
net->read_timeout=old_timeout; // restore it
|
2004-03-02 20:39:50 +01:00
|
|
|
/*
|
|
|
|
packet_length contains length of data, as it was stored in packet
|
|
|
|
header. In case of malformed header, packet_length can be zero.
|
|
|
|
If packet_length is not zero, my_net_read ensures that this number
|
|
|
|
of bytes was actually read from network. Additionally my_net_read
|
|
|
|
sets packet[packet_length]= 0 (thus if packet_length == 0,
|
|
|
|
command == packet[0] == COM_SLEEP).
|
|
|
|
In dispatch_command packet[packet_length] points beyond the end of packet.
|
|
|
|
*/
|
2001-12-05 12:03:00 +01:00
|
|
|
DBUG_RETURN(dispatch_command(command,thd, packet+1, (uint) packet_length));
|
2001-04-11 13:04:03 +02:00
|
|
|
}
|
2002-06-17 13:24:51 +02:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
2001-04-11 13:04:03 +02:00
|
|
|
|
2004-03-02 20:39:50 +01:00
|
|
|
/*
|
|
|
|
Perform one connection-level (COM_XXXX) command.
|
|
|
|
SYNOPSIS
|
|
|
|
dispatch_command()
|
|
|
|
thd connection handle
|
|
|
|
command type of command to perform
|
|
|
|
packet data for the command, packet is always null-terminated
|
|
|
|
packet_length length of packet + 1 (to show that data is
|
|
|
|
null-terminated) except for COM_SLEEP, where it
|
|
|
|
can be zero.
|
|
|
|
RETURN VALUE
|
|
|
|
0 ok
|
|
|
|
1 request of thread shutdown, i. e. if command is
|
|
|
|
COM_QUIT/COM_SHUTDOWN
|
|
|
|
*/
|
2003-02-14 10:47:41 +01:00
|
|
|
|
2001-04-11 13:04:03 +02:00
|
|
|
bool dispatch_command(enum enum_server_command command, THD *thd,
|
|
|
|
char* packet, uint packet_length)
|
|
|
|
{
|
|
|
|
NET *net= &thd->net;
|
2003-02-14 10:47:41 +01:00
|
|
|
bool error= 0;
|
2003-11-25 13:28:43 +01:00
|
|
|
DBUG_ENTER("dispatch_command");
|
|
|
|
|
|
|
|
thd->command=command;
|
2002-02-13 21:37:19 +01:00
|
|
|
/*
|
|
|
|
Commands which will always take a long time should be marked with
|
|
|
|
this so that they will not get logged to the slow query log
|
|
|
|
*/
|
2003-11-14 13:50:19 +01:00
|
|
|
thd->slow_command=FALSE;
|
2004-09-07 14:50:56 +02:00
|
|
|
thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
|
2002-07-23 17:31:22 +02:00
|
|
|
thd->set_time();
|
2000-07-31 21:29:14 +02:00
|
|
|
VOID(pthread_mutex_lock(&LOCK_thread_count));
|
|
|
|
thd->query_id=query_id;
|
|
|
|
if (command != COM_STATISTICS && command != COM_PING)
|
|
|
|
query_id++;
|
|
|
|
thread_running++;
|
2004-06-18 23:50:04 +02:00
|
|
|
/* TODO: set thd->lex->sql_command to SQLCOM_END here */
|
2000-07-31 21:29:14 +02:00
|
|
|
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
2002-07-23 17:31:22 +02:00
|
|
|
|
2003-12-06 23:21:09 +01:00
|
|
|
thd->server_status&=
|
|
|
|
~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
|
2001-04-11 13:04:03 +02:00
|
|
|
switch (command) {
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_INIT_DB:
|
2003-08-18 23:08:08 +02:00
|
|
|
{
|
|
|
|
LEX_STRING tmp;
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB],
|
|
|
|
&LOCK_status);
|
2003-08-18 23:08:08 +02:00
|
|
|
thd->convert_string(&tmp, system_charset_info,
|
|
|
|
packet, strlen(packet), thd->charset());
|
|
|
|
if (!mysql_change_db(thd, tmp.str))
|
|
|
|
mysql_log.write(thd,command,"%s",thd->db);
|
|
|
|
break;
|
|
|
|
}
|
2003-09-08 12:08:53 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2001-05-31 02:50:56 +02:00
|
|
|
case COM_REGISTER_SLAVE:
|
|
|
|
{
|
2002-08-21 21:04:22 +02:00
|
|
|
if (!register_slave(thd, (uchar*)packet, packet_length))
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2001-05-31 02:50:56 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_TABLE_DUMP:
|
2003-08-19 15:00:12 +02:00
|
|
|
{
|
|
|
|
char *db, *tbl_name;
|
|
|
|
uint db_len= *(uchar*) packet;
|
|
|
|
uint tbl_len= *(uchar*) (packet + db_len + 1);
|
|
|
|
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_other, &LOCK_status);
|
2003-11-14 13:50:19 +01:00
|
|
|
thd->slow_command= TRUE;
|
2003-08-19 15:00:12 +02:00
|
|
|
db= thd->alloc(db_len + tbl_len + 2);
|
|
|
|
tbl_name= strmake(db, packet + 1, db_len)+1;
|
|
|
|
strmake(tbl_name, packet + db_len + 2, tbl_len);
|
2004-10-20 03:04:37 +02:00
|
|
|
mysql_table_dump(thd, db, tbl_name, -1);
|
2003-08-19 15:00:12 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_CHANGE_USER:
|
|
|
|
{
|
2002-11-16 19:19:10 +01:00
|
|
|
thd->change_user();
|
2003-07-01 21:40:59 +02:00
|
|
|
thd->clear_error(); // if errors from rollback
|
2002-11-16 19:19:10 +01:00
|
|
|
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_other, &LOCK_status);
|
2003-07-01 21:40:59 +02:00
|
|
|
char *user= (char*) packet;
|
2000-07-31 21:29:14 +02:00
|
|
|
char *passwd= strend(user)+1;
|
2003-07-04 18:52:04 +02:00
|
|
|
/*
|
|
|
|
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).
|
|
|
|
*/
|
2003-09-16 00:39:25 +02:00
|
|
|
char db_buff[NAME_LEN+1]; // buffer to store db in utf8
|
2003-07-04 18:52:04 +02:00
|
|
|
char *db= passwd;
|
|
|
|
uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
|
|
|
|
*passwd++ : strlen(passwd);
|
|
|
|
db+= passwd_len + 1;
|
2004-02-14 17:26:21 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2004-10-07 00:45:06 +02:00
|
|
|
/* Small check for incoming packet */
|
2000-07-31 21:29:14 +02:00
|
|
|
if ((uint) ((uchar*) db - net->read_pos) > packet_length)
|
2003-07-01 21:40:59 +02:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
|
2003-07-01 21:40:59 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-02-14 17:26:21 +01:00
|
|
|
#endif
|
2003-09-03 15:29:51 +02:00
|
|
|
/* Convert database name to utf8 */
|
2004-10-29 14:00:39 +02:00
|
|
|
uint dummy_errors;
|
2003-09-16 00:39:25 +02:00
|
|
|
db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
|
|
|
|
system_charset_info, db, strlen(db),
|
2004-10-29 14:00:39 +02:00
|
|
|
thd->charset(), &dummy_errors)]= 0;
|
2003-09-16 00:39:25 +02:00
|
|
|
db= db_buff;
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2003-07-01 21:40:59 +02:00
|
|
|
/* 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;
|
2003-10-30 00:01:53 +01:00
|
|
|
USER_CONN *save_user_connect= thd->user_connect;
|
2003-11-04 08:40:36 +01:00
|
|
|
|
|
|
|
if (!(thd->user= my_strdup(user, MYF(0))))
|
2003-07-01 21:40:59 +02:00
|
|
|
{
|
|
|
|
thd->user= save_user;
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
|
2003-07-01 21:40:59 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2003-10-30 00:01:53 +01:00
|
|
|
/* Clear variables that are allocated */
|
|
|
|
thd->user_connect= 0;
|
2004-03-30 01:32:41 +02:00
|
|
|
int res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE);
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2003-07-01 21:40:59 +02:00
|
|
|
if (res)
|
|
|
|
{
|
2004-10-07 00:45:06 +02:00
|
|
|
/* authentication failure, we shall restore old user */
|
2003-07-01 21:40:59 +02:00
|
|
|
if (res > 0)
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
|
2003-07-01 21:40:59 +02:00
|
|
|
x_free(thd->user);
|
|
|
|
thd->user= save_user;
|
|
|
|
thd->priv_user= save_priv_user;
|
2003-10-30 00:01:53 +01:00
|
|
|
thd->user_connect= save_user_connect;
|
2003-07-01 21:40:59 +02:00
|
|
|
thd->master_access= save_master_access;
|
|
|
|
thd->db_access= save_db_access;
|
|
|
|
thd->db= save_db;
|
|
|
|
thd->db_length= save_db_length;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* we've authenticated new user */
|
2003-11-04 08:40:36 +01:00
|
|
|
if (save_user_connect)
|
|
|
|
decrease_user_connections(save_user_connect);
|
2003-07-01 21:40:59 +02:00
|
|
|
x_free((gptr) save_db);
|
|
|
|
x_free((gptr) save_user);
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-06-12 23:13:12 +02:00
|
|
|
case COM_EXECUTE:
|
|
|
|
{
|
2004-03-15 18:20:47 +01:00
|
|
|
mysql_stmt_execute(thd, packet, packet_length);
|
2002-06-12 23:13:12 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-08-03 12:32:21 +02:00
|
|
|
case COM_FETCH:
|
|
|
|
{
|
|
|
|
mysql_stmt_fetch(thd, packet, packet_length);
|
|
|
|
break;
|
|
|
|
}
|
2002-06-12 23:13:12 +02:00
|
|
|
case COM_LONG_DATA:
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
mysql_stmt_get_longdata(thd, packet, packet_length);
|
2002-06-12 23:13:12 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case COM_PREPARE:
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
mysql_stmt_prepare(thd, packet, packet_length);
|
2002-06-12 23:13:12 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-11-27 03:51:38 +01:00
|
|
|
case COM_CLOSE_STMT:
|
|
|
|
{
|
|
|
|
mysql_stmt_free(thd, packet);
|
|
|
|
break;
|
|
|
|
}
|
2003-07-08 11:27:21 +02:00
|
|
|
case COM_RESET_STMT:
|
|
|
|
{
|
|
|
|
mysql_stmt_reset(thd, packet);
|
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_QUERY:
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
if (alloc_query(thd, packet, packet_length))
|
|
|
|
break; // fatal error is set
|
2000-09-16 03:27:21 +02:00
|
|
|
mysql_log.write(thd,command,"%s",thd->query);
|
2003-01-04 14:17:16 +01:00
|
|
|
DBUG_PRINT("query",("%-.4096s",thd->query));
|
2002-10-02 12:33:08 +02:00
|
|
|
mysql_parse(thd,thd->query, thd->query_length);
|
2003-01-20 23:00:50 +01:00
|
|
|
|
2004-11-13 18:35:51 +01:00
|
|
|
while (!thd->killed && thd->lex->found_colon && !thd->net.report_error)
|
2003-01-20 23:00:50 +01:00
|
|
|
{
|
2003-05-05 20:54:37 +02:00
|
|
|
char *packet= thd->lex->found_colon;
|
2003-11-14 13:50:19 +01:00
|
|
|
/*
|
2003-01-20 23:00:50 +01:00
|
|
|
Multiple queries exits, execute them individually
|
2004-02-10 14:09:59 +01:00
|
|
|
in embedded server - just store them to be executed later
|
2003-01-20 23:00:50 +01:00
|
|
|
*/
|
2004-02-10 14:09:59 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2003-01-20 23:00:50 +01:00
|
|
|
if (thd->lock || thd->open_tables || thd->derived_tables)
|
2003-11-14 13:50:19 +01:00
|
|
|
close_thread_tables(thd);
|
2004-02-10 14:09:59 +01:00
|
|
|
#endif
|
|
|
|
ulong length= thd->query_length-(ulong)(packet-thd->query);
|
2003-11-14 13:50:19 +01:00
|
|
|
|
2003-01-20 23:00:50 +01:00
|
|
|
/* Remove garbage at start of query */
|
2003-03-15 14:24:21 +01:00
|
|
|
while (my_isspace(thd->charset(), *packet) && length > 0)
|
2003-01-20 23:00:50 +01:00
|
|
|
{
|
|
|
|
packet++;
|
|
|
|
length--;
|
|
|
|
}
|
2004-05-17 00:52:13 +02:00
|
|
|
VOID(pthread_mutex_lock(&LOCK_thread_count));
|
2003-01-20 23:00:50 +01:00
|
|
|
thd->query_length= length;
|
2003-01-23 20:49:28 +01:00
|
|
|
thd->query= packet;
|
|
|
|
thd->query_id= query_id++;
|
2004-06-18 23:50:04 +02:00
|
|
|
/* TODO: set thd->lex->sql_command to SQLCOM_END here */
|
2003-01-23 20:49:28 +01:00
|
|
|
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
2004-02-10 14:09:59 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2003-01-20 23:00:50 +01:00
|
|
|
mysql_parse(thd, packet, length);
|
2004-02-10 14:09:59 +01:00
|
|
|
#else
|
2004-02-13 08:56:36 +01:00
|
|
|
/*
|
|
|
|
'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
|
2004-05-15 14:07:44 +02:00
|
|
|
thd->query_rest.copy(packet, length, thd->query_rest.charset());
|
2004-02-10 14:09:59 +01:00
|
|
|
break;
|
|
|
|
#endif /*EMBEDDED_LIBRARY*/
|
2003-01-20 23:00:50 +01:00
|
|
|
}
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!(specialflag & SPECIAL_NO_PRIOR))
|
|
|
|
my_pthread_setprio(pthread_self(),WAIT_PRIOR);
|
|
|
|
DBUG_PRINT("info",("query ready"));
|
|
|
|
break;
|
|
|
|
}
|
2001-09-27 20:45:48 +02:00
|
|
|
case COM_FIELD_LIST: // This isn't actually needed
|
2000-07-31 21:29:14 +02:00
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
|
|
|
|
MYF(0)); /* purecov: inspected */
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
#else
|
|
|
|
{
|
2003-05-30 20:09:35 +02:00
|
|
|
char *fields, *pend;
|
2000-07-31 21:29:14 +02:00
|
|
|
TABLE_LIST table_list;
|
2003-08-18 23:08:08 +02:00
|
|
|
LEX_STRING conv_name;
|
|
|
|
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS],
|
|
|
|
&LOCK_status);
|
2000-07-31 21:29:14 +02:00
|
|
|
bzero((char*) &table_list,sizeof(table_list));
|
|
|
|
if (!(table_list.db=thd->db))
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-05-30 20:09:35 +02:00
|
|
|
pend= strend(packet);
|
2003-08-18 23:08:08 +02:00
|
|
|
thd->convert_string(&conv_name, system_charset_info,
|
|
|
|
packet, (uint) (pend-packet), thd->charset());
|
|
|
|
table_list.alias= table_list.real_name= conv_name.str;
|
2003-05-30 20:09:35 +02:00
|
|
|
packet= pend+1;
|
2004-12-18 10:48:01 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2004-07-07 10:29:39 +02:00
|
|
|
/* command not cachable => no gap for data base name */
|
2001-12-17 02:02:58 +01:00
|
|
|
if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1)))
|
|
|
|
break;
|
2000-09-16 03:27:21 +02:00
|
|
|
mysql_log.write(thd,command,"%s %s",table_list.real_name,fields);
|
2003-01-29 17:56:34 +01:00
|
|
|
if (lower_case_table_names)
|
2003-02-04 20:52:14 +01:00
|
|
|
my_casedn_str(files_charset_info, table_list.real_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
remove_escape(table_list.real_name); // This can't have wildcards
|
|
|
|
|
2004-02-16 09:03:25 +01:00
|
|
|
if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege,
|
|
|
|
0, 0))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2004-04-10 00:14:32 +02:00
|
|
|
if (grant_option &&
|
|
|
|
check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2004-09-07 14:50:56 +02:00
|
|
|
/* 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);
|
|
|
|
|
2004-10-10 10:01:05 +02:00
|
|
|
/* switch on VIEW optimisation: do not fill temporary tables */
|
|
|
|
thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
|
2000-07-31 21:29:14 +02:00
|
|
|
mysqld_list_fields(thd,&table_list,fields);
|
2004-09-07 14:50:56 +02:00
|
|
|
thd->lex->unit.cleanup();
|
2004-09-15 21:10:31 +02:00
|
|
|
thd->cleanup_after_query();
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
case COM_QUIT:
|
2001-12-13 19:06:44 +01:00
|
|
|
/* We don't calculate statistics for this command */
|
2000-09-16 03:27:21 +02:00
|
|
|
mysql_log.write(thd,command,NullS);
|
2000-07-31 21:29:14 +02:00
|
|
|
net->error=0; // Don't give 'abort' message
|
|
|
|
error=TRUE; // End server
|
|
|
|
break;
|
|
|
|
|
2001-09-03 04:16:15 +02:00
|
|
|
case COM_CREATE_DB: // QQ: To be removed
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2003-12-30 12:14:21 +01:00
|
|
|
char *db=thd->strdup(packet), *alias;
|
2004-10-17 11:59:46 +02:00
|
|
|
HA_CREATE_INFO create_info;
|
2003-12-30 12:14:21 +01:00
|
|
|
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB],
|
|
|
|
&LOCK_status);
|
2001-01-31 03:47:25 +01:00
|
|
|
// null test to handle EOM
|
2004-03-05 19:13:33 +01:00
|
|
|
if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
|
2001-01-31 03:47:25 +01:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL");
|
2001-01-31 03:47:25 +01:00
|
|
|
break;
|
|
|
|
}
|
2003-09-26 12:33:13 +02:00
|
|
|
if (check_access(thd,CREATE_ACL,db,0,1,0))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2001-04-11 13:04:03 +02:00
|
|
|
mysql_log.write(thd,command,packet);
|
2004-10-17 11:59:46 +02:00
|
|
|
bzero(&create_info, sizeof(create_info));
|
2004-11-16 12:39:10 +01:00
|
|
|
mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db),
|
|
|
|
&create_info, 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2001-09-03 04:16:15 +02:00
|
|
|
case COM_DROP_DB: // QQ: To be removed
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_DROP_DB],
|
|
|
|
&LOCK_status);
|
2003-12-30 12:14:21 +01:00
|
|
|
char *db=thd->strdup(packet), *alias;
|
2004-07-07 10:29:39 +02:00
|
|
|
/* null test to handle EOM */
|
2004-03-05 19:13:33 +01:00
|
|
|
if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
|
2001-01-31 03:47:25 +01:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL");
|
2001-01-31 03:47:25 +01:00
|
|
|
break;
|
|
|
|
}
|
2003-09-26 12:33:13 +02:00
|
|
|
if (check_access(thd,DROP_ACL,db,0,1,0))
|
2002-12-20 13:58:27 +01:00
|
|
|
break;
|
2001-09-03 04:16:15 +02:00
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
|
|
|
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2001-09-03 04:16:15 +02:00
|
|
|
}
|
2000-09-16 03:27:21 +02:00
|
|
|
mysql_log.write(thd,command,db);
|
2004-11-16 12:39:10 +01:00
|
|
|
mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : db),
|
|
|
|
0, 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_BINLOG_DUMP:
|
|
|
|
{
|
2004-11-10 17:07:39 +01:00
|
|
|
ulong pos;
|
|
|
|
ushort flags;
|
|
|
|
uint32 slave_server_id;
|
|
|
|
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_other,&LOCK_status);
|
2003-11-14 13:50:19 +01:00
|
|
|
thd->slow_command = TRUE;
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, REPL_SLAVE_ACL))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2000-08-21 23:39:08 +02:00
|
|
|
|
2002-08-21 21:04:22 +02:00
|
|
|
/* TODO: The following has to be changed to an 8 byte integer */
|
2001-04-11 13:04:03 +02:00
|
|
|
pos = uint4korr(packet);
|
|
|
|
flags = uint2korr(packet + 4);
|
2001-10-12 17:37:25 +02:00
|
|
|
thd->server_id=0; /* avoid suicide */
|
2003-07-03 01:08:31 +02:00
|
|
|
if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
|
2003-07-02 22:56:27 +02:00
|
|
|
kill_zombie_dump_threads(slave_server_id);
|
2000-09-30 01:20:26 +02:00
|
|
|
thd->server_id = slave_server_id;
|
2004-11-10 17:07:39 +01:00
|
|
|
|
|
|
|
mysql_log.write(thd, command, "Log: '%s' Pos: %ld", packet+10,
|
|
|
|
(long) pos);
|
2002-08-21 21:04:22 +02:00
|
|
|
mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
|
2001-10-12 17:37:25 +02:00
|
|
|
unregister_slave(thd,1,1);
|
2004-07-07 10:29:39 +02:00
|
|
|
/* fake COM_QUIT -- if we get here, the thread needs to terminate */
|
2000-10-03 01:59:12 +02:00
|
|
|
error = TRUE;
|
|
|
|
net->error = 0;
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_REFRESH:
|
|
|
|
{
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_FLUSH],
|
|
|
|
&LOCK_status);
|
2001-12-06 00:05:30 +01:00
|
|
|
ulong options= (ulong) (uchar) packet[0];
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd,RELOAD_ACL))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2000-09-16 03:27:21 +02:00
|
|
|
mysql_log.write(thd,command,NullS);
|
2004-10-20 03:04:37 +02:00
|
|
|
if (!reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, NULL))
|
2003-05-15 18:35:39 +02:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_SHUTDOWN:
|
2004-06-15 11:35:23 +02:00
|
|
|
{
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_other, &LOCK_status);
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd,SHUTDOWN_ACL))
|
2000-07-31 21:29:14 +02:00
|
|
|
break; /* purecov: inspected */
|
2004-06-15 11:35:23 +02:00
|
|
|
/*
|
2004-06-18 23:50:04 +02:00
|
|
|
If the client is < 4.1.3, it is going to send us no argument; then
|
|
|
|
packet_length is 1, packet[0] is the end 0 of the packet. Note that
|
|
|
|
SHUTDOWN_DEFAULT is 0. If client is >= 4.1.3, the shutdown level is in
|
|
|
|
packet[0].
|
2004-06-15 11:35:23 +02:00
|
|
|
*/
|
2004-08-19 20:48:00 +02:00
|
|
|
enum mysql_enum_shutdown_level level=
|
|
|
|
(enum mysql_enum_shutdown_level) (uchar) packet[0];
|
2004-06-18 23:50:04 +02:00
|
|
|
DBUG_PRINT("quit",("Got shutdown command for level %u", level));
|
2004-06-15 11:35:23 +02:00
|
|
|
if (level == SHUTDOWN_DEFAULT)
|
|
|
|
level= SHUTDOWN_WAIT_ALL_BUFFERS; // soon default will be configurable
|
|
|
|
else if (level != SHUTDOWN_WAIT_ALL_BUFFERS)
|
|
|
|
{
|
|
|
|
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "this shutdown level");
|
|
|
|
break;
|
|
|
|
}
|
2004-06-18 23:50:04 +02:00
|
|
|
DBUG_PRINT("quit",("Got shutdown command for level %u", level));
|
2000-09-16 03:27:21 +02:00
|
|
|
mysql_log.write(thd,command,NullS);
|
2002-10-02 12:33:08 +02:00
|
|
|
send_eof(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
#ifdef __WIN__
|
|
|
|
sleep(1); // must wait after eof()
|
|
|
|
#endif
|
2001-08-22 00:45:07 +02:00
|
|
|
#ifndef OS2
|
2002-10-02 12:33:08 +02:00
|
|
|
send_eof(thd); // This is for 'quit request'
|
2001-08-22 00:45:07 +02:00
|
|
|
#endif
|
2003-05-26 18:01:20 +02:00
|
|
|
close_connection(thd, 0, 1);
|
2000-07-31 21:29:14 +02:00
|
|
|
close_thread_tables(thd); // Free before kill
|
|
|
|
kill_mysql();
|
|
|
|
error=TRUE;
|
|
|
|
break;
|
2004-06-15 11:35:23 +02:00
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_STATISTICS:
|
|
|
|
{
|
2000-09-16 03:27:21 +02:00
|
|
|
mysql_log.write(thd,command,NullS);
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS],
|
|
|
|
&LOCK_status);
|
2003-10-04 16:28:08 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-07-31 21:29:14 +02:00
|
|
|
char buff[200];
|
2003-10-04 16:28:08 +02:00
|
|
|
#else
|
|
|
|
char *buff= thd->net.last_error;
|
|
|
|
#endif
|
2000-09-20 03:54:10 +02:00
|
|
|
ulong uptime = (ulong) (thd->start_time - start_time);
|
2000-07-31 21:29:14 +02:00
|
|
|
sprintf((char*) buff,
|
2001-07-11 09:36:22 +02:00
|
|
|
"Uptime: %ld Threads: %d Questions: %lu Slow queries: %ld Opens: %ld Flush tables: %ld Open tables: %u Queries per second avg: %.3f",
|
2000-07-31 21:29:14 +02:00
|
|
|
uptime,
|
2004-09-13 15:48:01 +02:00
|
|
|
(int) thread_count,thd->query_id,thd->status_var.long_query_count,
|
|
|
|
thd->status_var.opened_tables,refresh_version, cached_tables(),
|
2000-07-31 21:29:14 +02:00
|
|
|
uptime ? (float)thd->query_id/(float)uptime : 0);
|
|
|
|
#ifdef SAFEMALLOC
|
2003-06-12 21:39:45 +02:00
|
|
|
if (sf_malloc_cur_memory) // Using SAFEMALLOC
|
2000-07-31 21:29:14 +02:00
|
|
|
sprintf(strend(buff), " Memory in use: %ldK Max memory used: %ldK",
|
2003-06-12 21:39:45 +02:00
|
|
|
(sf_malloc_cur_memory+1023L)/1024L,
|
|
|
|
(sf_malloc_max_memory+1023L)/1024L);
|
2003-10-04 16:28:08 +02:00
|
|
|
#endif
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-08-21 23:18:32 +02:00
|
|
|
VOID(my_net_write(net, buff,(uint) strlen(buff)));
|
2000-07-31 21:29:14 +02:00
|
|
|
VOID(net_flush(net));
|
2003-10-04 16:28:08 +02:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case COM_PING:
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_other, &LOCK_status);
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd); // Tell client we are alive
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case COM_PROCESS_INFO:
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST],
|
|
|
|
&LOCK_status);
|
2002-06-12 14:04:18 +02:00
|
|
|
if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2000-09-16 03:27:21 +02:00
|
|
|
mysql_log.write(thd,command,NullS);
|
2003-09-26 12:33:13 +02:00
|
|
|
mysqld_list_processes(thd,
|
|
|
|
thd->master_access & PROCESS_ACL ?
|
2003-12-19 15:25:50 +01:00
|
|
|
NullS : thd->priv_user, 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case COM_PROCESS_KILL:
|
|
|
|
{
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_KILL], &LOCK_status);
|
2001-04-11 13:04:03 +02:00
|
|
|
ulong id=(ulong) uint4korr(packet);
|
2003-03-21 06:37:01 +01:00
|
|
|
kill_one_thread(thd,id,false);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-11-18 12:47:27 +01:00
|
|
|
case COM_SET_OPTION:
|
|
|
|
{
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION],
|
|
|
|
&LOCK_status);
|
2003-11-18 12:47:27 +01:00
|
|
|
enum_mysql_set_option command= (enum_mysql_set_option) uint2korr(packet);
|
|
|
|
switch (command) {
|
|
|
|
case MYSQL_OPTION_MULTI_STATEMENTS_ON:
|
|
|
|
thd->client_capabilities|= CLIENT_MULTI_STATEMENTS;
|
2004-01-09 21:28:29 +01:00
|
|
|
send_eof(thd);
|
2003-11-18 12:47:27 +01:00
|
|
|
break;
|
|
|
|
case MYSQL_OPTION_MULTI_STATEMENTS_OFF:
|
|
|
|
thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS;
|
2004-01-09 21:28:29 +01:00
|
|
|
send_eof(thd);
|
2003-11-18 12:47:27 +01:00
|
|
|
break;
|
|
|
|
default:
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
|
2003-11-18 12:47:27 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_DEBUG:
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_other, &LOCK_status);
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
2000-07-31 21:29:14 +02:00
|
|
|
break; /* purecov: inspected */
|
|
|
|
mysql_print_status(thd);
|
2000-09-16 03:27:21 +02:00
|
|
|
mysql_log.write(thd,command,NullS);
|
2002-10-02 12:33:08 +02:00
|
|
|
send_eof(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case COM_SLEEP:
|
|
|
|
case COM_CONNECT: // Impossible here
|
|
|
|
case COM_TIME: // Impossible from client
|
|
|
|
case COM_DELAYED_INSERT:
|
2003-01-04 14:17:16 +01:00
|
|
|
case COM_END:
|
2000-07-31 21:29:14 +02:00
|
|
|
default:
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-01-07 10:45:06 +01:00
|
|
|
if (thd->lock || thd->open_tables || thd->derived_tables)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
thd->proc_info="closing tables";
|
|
|
|
close_thread_tables(thd); /* Free tables */
|
|
|
|
}
|
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
/* report error issued during command execution */
|
|
|
|
if (thd->killed_errno() && !thd->net.report_error)
|
|
|
|
thd->send_kill_message();
|
2004-11-12 13:34:00 +01:00
|
|
|
if (thd->net.report_error)
|
2004-10-20 15:06:54 +02:00
|
|
|
net_send_error(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
time_t start_of_query=thd->start_time;
|
2000-10-14 02:16:35 +02:00
|
|
|
thd->end_time(); // Set start time
|
2001-02-16 22:46:52 +01:00
|
|
|
|
2000-09-20 03:54:10 +02:00
|
|
|
/* If not reading from backup and if the query took too long */
|
2003-11-14 13:50:19 +01:00
|
|
|
if (!thd->slow_command && !thd->user_time) // do not log 'slow_command' queries
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2001-02-16 22:46:52 +01:00
|
|
|
thd->proc_info="logging slow query";
|
|
|
|
|
2002-06-28 18:30:09 +02:00
|
|
|
if ((ulong) (thd->start_time - thd->time_after_lock) >
|
|
|
|
thd->variables.long_query_time ||
|
2003-12-06 23:21:09 +01:00
|
|
|
((thd->server_status &
|
|
|
|
(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
|
2003-10-07 09:05:35 +02:00
|
|
|
(specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES)))
|
2000-10-14 02:16:35 +02:00
|
|
|
{
|
2004-09-13 15:48:01 +02:00
|
|
|
thd->status_var.long_query_count++;
|
2000-10-14 02:16:35 +02:00
|
|
|
mysql_slow_log.write(thd, thd->query, thd->query_length, start_of_query);
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2001-02-16 22:46:52 +01:00
|
|
|
thd->proc_info="cleaning up";
|
2000-07-31 21:29:14 +02:00
|
|
|
VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
|
|
|
|
thd->proc_info=0;
|
|
|
|
thd->command=COM_SLEEP;
|
|
|
|
thd->query=0;
|
2004-05-14 15:48:56 +02:00
|
|
|
thd->query_length=0;
|
2000-07-31 21:29:14 +02:00
|
|
|
thread_running--;
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
2002-07-23 17:31:22 +02:00
|
|
|
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
|
2003-04-03 20:00:52 +02:00
|
|
|
|
2004-11-08 00:13:54 +01:00
|
|
|
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(error);
|
|
|
|
}
|
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
|
2004-11-13 11:56:39 +01:00
|
|
|
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)
|
2004-11-13 22:26:15 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND,
|
|
|
|
ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
#else
|
|
|
|
if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
|
|
|
|
check_global_access(thd, SHOW_DB_ACL))
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case SCH_TABLE_NAMES:
|
|
|
|
case SCH_TABLES:
|
|
|
|
case SCH_VIEWS:
|
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2004-11-13 22:26:15 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND,
|
|
|
|
ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
#else
|
|
|
|
{
|
|
|
|
char *db= lex->select_lex.db ? lex->select_lex.db : thd->db;
|
|
|
|
if (!db)
|
|
|
|
{
|
2004-11-13 22:26:15 +01:00
|
|
|
my_message(ER_NO_DB_ERROR,
|
|
|
|
ER(ER_NO_DB_ERROR), MYF(0)); /* purecov: inspected */
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(1); /* purecov: inspected */
|
|
|
|
}
|
|
|
|
remove_escape(db); // Fix escaped '_'
|
|
|
|
if (check_db_name(db))
|
|
|
|
{
|
2004-11-13 22:26:15 +01:00
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), db);
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
if (check_access(thd,SELECT_ACL,db,&thd->col_access,0,0))
|
|
|
|
DBUG_RETURN(1); /* purecov: inspected */
|
|
|
|
if (!thd->col_access && check_grant_db(thd,db))
|
|
|
|
{
|
2004-11-13 22:26:15 +01:00
|
|
|
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
|
|
|
|
thd->priv_user, thd->priv_host, db);
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
lex->select_lex.db= db;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
case SCH_COLUMNS:
|
|
|
|
case SCH_STATISTICS:
|
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2004-11-13 22:26:15 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND,
|
|
|
|
ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
#else
|
|
|
|
if (table_ident)
|
|
|
|
{
|
|
|
|
TABLE_LIST **query_tables_last= lex->query_tables_last;
|
|
|
|
sel= new SELECT_LEX();
|
|
|
|
sel->init_query();
|
|
|
|
if(!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ,
|
|
|
|
(List<String> *) 0, (List<String> *) 0))
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
lex->query_tables_last= query_tables_last;
|
|
|
|
TABLE_LIST *table_list= (TABLE_LIST*) sel->table_list.first;
|
|
|
|
char *db= table_list->db;
|
|
|
|
remove_escape(db); // Fix escaped '_'
|
|
|
|
remove_escape(table_list->real_name);
|
|
|
|
if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
|
|
|
|
&table_list->grant.privilege, 0, 0))
|
|
|
|
DBUG_RETURN(1); /* purecov: inspected */
|
|
|
|
if (grant_option && check_grant(thd, SELECT_ACL, table_list, 2,
|
|
|
|
UINT_MAX, 0))
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
case SCH_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;
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
/*
|
|
|
|
Read query from packet and store in thd->query
|
|
|
|
Used in COM_QUERY and COM_PREPARE
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
Sets the following THD variables:
|
|
|
|
query
|
|
|
|
query_length
|
|
|
|
|
|
|
|
RETURN VALUES
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE ok
|
|
|
|
TRUE error; In this case thd->fatal_error is set
|
2002-10-02 12:33:08 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
bool alloc_query(THD *thd, char *packet, ulong packet_length)
|
|
|
|
{
|
|
|
|
packet_length--; // Remove end null
|
2003-01-20 23:00:50 +01:00
|
|
|
/* Remove garbage at start and end of query */
|
2003-03-15 14:24:21 +01:00
|
|
|
while (my_isspace(thd->charset(),packet[0]) && packet_length > 0)
|
2002-10-02 12:33:08 +02:00
|
|
|
{
|
|
|
|
packet++;
|
|
|
|
packet_length--;
|
|
|
|
}
|
|
|
|
char *pos=packet+packet_length; // Point at end null
|
2002-11-30 18:58:53 +01:00
|
|
|
while (packet_length > 0 &&
|
2003-03-15 14:24:21 +01:00
|
|
|
(pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1])))
|
2002-10-02 12:33:08 +02:00
|
|
|
{
|
|
|
|
pos--;
|
|
|
|
packet_length--;
|
|
|
|
}
|
|
|
|
/* We must allocate some extra memory for query cache */
|
2004-05-17 00:52:13 +02:00
|
|
|
thd->query_length= 0; // Extra safety: Avoid races
|
2002-10-02 12:33:08 +02:00
|
|
|
if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet),
|
|
|
|
packet_length,
|
2003-10-02 11:02:05 +02:00
|
|
|
thd->db_length+ 1 +
|
|
|
|
QUERY_CACHE_FLAGS_SIZE)))
|
2004-10-20 03:04:37 +02:00
|
|
|
return TRUE;
|
2002-10-02 12:33:08 +02:00
|
|
|
thd->query[packet_length]=0;
|
|
|
|
thd->query_length= packet_length;
|
2004-05-25 00:03:49 +02:00
|
|
|
|
|
|
|
/* Reclaim some memory */
|
|
|
|
thd->packet.shrink(thd->variables.net_buffer_length);
|
|
|
|
thd->convert_buffer.shrink(thd->variables.net_buffer_length);
|
2002-10-02 12:33:08 +02:00
|
|
|
|
|
|
|
if (!(specialflag & SPECIAL_NO_PRIOR))
|
|
|
|
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
2004-10-20 03:04:37 +02:00
|
|
|
return FALSE;
|
2002-10-02 12:33:08 +02:00
|
|
|
}
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/****************************************************************************
|
|
|
|
** mysql_execute_command
|
|
|
|
** Execute command saved in thd and current_lex->sql_command
|
|
|
|
****************************************************************************/
|
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool
|
2002-10-02 12:33:08 +02:00
|
|
|
mysql_execute_command(THD *thd)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-10-20 03:04:37 +02:00
|
|
|
bool res= FALSE;
|
2004-11-25 01:23:13 +01:00
|
|
|
int result= 0;
|
2003-05-05 20:54:37 +02:00
|
|
|
LEX *lex= thd->lex;
|
2004-10-29 18:26:52 +02:00
|
|
|
/* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
|
2002-10-03 15:35:08 +02:00
|
|
|
SELECT_LEX *select_lex= &lex->select_lex;
|
2004-07-16 00:15:55 +02:00
|
|
|
/* first table of first SELECT_LEX */
|
2004-10-29 18:26:52 +02:00
|
|
|
TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first;
|
2004-07-16 00:15:55 +02:00
|
|
|
/* list of all tables in query */
|
|
|
|
TABLE_LIST *all_tables;
|
|
|
|
/* most outer SELECT_LEX_UNIT of query */
|
2002-05-08 22:14:40 +02:00
|
|
|
SELECT_LEX_UNIT *unit= &lex->unit;
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_ENTER("mysql_execute_command");
|
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
/*
|
|
|
|
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();
|
2004-09-03 20:43:04 +02:00
|
|
|
/* should be assigned after making first tables same */
|
2004-07-16 00:15:55 +02:00
|
|
|
all_tables= lex->query_tables;
|
|
|
|
|
2003-03-02 19:17:41 +01:00
|
|
|
if (lex->sql_command != SQLCOM_CREATE_PROCEDURE &&
|
2003-03-05 19:45:17 +01:00
|
|
|
lex->sql_command != SQLCOM_CREATE_SPFUNCTION)
|
2003-03-02 19:17:41 +01:00
|
|
|
{
|
|
|
|
if (sp_cache_functions(thd, lex))
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
}
|
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
2004-07-16 00:15:55 +02:00
|
|
|
if (all_tables || &lex->select_lex != lex->all_selects_list)
|
2002-10-02 12:33:08 +02:00
|
|
|
mysql_reset_errors(thd);
|
|
|
|
|
2003-09-29 08:47:37 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2001-12-19 05:56:36 +01:00
|
|
|
if (thd->slave_thread)
|
|
|
|
{
|
2002-11-30 18:58:53 +01:00
|
|
|
/*
|
2001-12-20 12:52:04 +01:00
|
|
|
Skip if we are in the slave thread, some table rules have been
|
|
|
|
given and the table list says the query should not be replicated
|
|
|
|
*/
|
2004-07-16 00:15:55 +02:00
|
|
|
if (all_tables_not_ok(thd, all_tables))
|
2003-07-08 15:50:57 +02:00
|
|
|
{
|
|
|
|
/* we warn the slave SQL thread */
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
|
2003-02-12 16:17:03 +01:00
|
|
|
DBUG_RETURN(0);
|
2003-07-08 15:50:57 +02:00
|
|
|
}
|
2001-12-20 12:52:04 +01:00
|
|
|
#ifndef TO_BE_DELETED
|
|
|
|
/*
|
2003-08-19 15:00:12 +02:00
|
|
|
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()
|
2001-12-20 12:52:04 +01:00
|
|
|
*/
|
2001-12-19 05:56:36 +01:00
|
|
|
if (lex->sql_command == SQLCOM_SELECT)
|
|
|
|
{
|
|
|
|
lex->sql_command = SQLCOM_DO;
|
2001-12-21 06:00:58 +01:00
|
|
|
lex->insert_list = &select_lex->item_list;
|
2001-12-19 05:56:36 +01:00
|
|
|
}
|
2001-12-20 12:52:04 +01:00
|
|
|
#endif
|
2001-12-19 05:56:36 +01:00
|
|
|
}
|
2003-09-26 12:33:13 +02:00
|
|
|
#endif /* !HAVE_REPLICATION */
|
2004-05-05 20:21:41 +02:00
|
|
|
|
2003-05-19 11:01:38 +02:00
|
|
|
/*
|
|
|
|
When option readonly is set deny operations which change tables.
|
|
|
|
Except for the replication thread and the 'super' users.
|
|
|
|
*/
|
|
|
|
if (opt_readonly &&
|
2003-12-19 15:25:50 +01:00
|
|
|
!(thd->slave_thread || (thd->master_access & SUPER_ACL)) &&
|
2003-05-19 11:01:38 +02:00
|
|
|
(uc_update_queries[lex->sql_command] > 0))
|
|
|
|
{
|
2004-10-20 03:04:37 +02:00
|
|
|
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
|
2003-07-08 17:50:23 +02:00
|
|
|
DBUG_RETURN(-1);
|
2003-05-19 11:01:38 +02:00
|
|
|
}
|
2001-08-18 14:29:21 +02:00
|
|
|
|
2004-09-13 15:48:01 +02:00
|
|
|
statistic_increment(thd->status_var.com_stat[lex->sql_command],
|
|
|
|
&LOCK_status);
|
2000-07-31 21:29:14 +02:00
|
|
|
switch (lex->sql_command) {
|
|
|
|
case SQLCOM_SELECT:
|
|
|
|
{
|
2004-05-05 20:21:41 +02:00
|
|
|
/* assign global limit variable if limit is not given */
|
|
|
|
{
|
|
|
|
SELECT_LEX *param= lex->unit.global_parameters;
|
|
|
|
if (!param->explicit_limit)
|
|
|
|
param->select_limit= thd->variables.select_limit;
|
|
|
|
}
|
|
|
|
|
2002-10-16 15:55:08 +02:00
|
|
|
select_result *result=lex->result;
|
2004-07-16 00:15:55 +02:00
|
|
|
if (all_tables)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
res= check_table_access(thd,
|
|
|
|
lex->exchange ? SELECT_ACL | FILE_ACL :
|
|
|
|
SELECT_ACL,
|
|
|
|
all_tables, 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
else
|
2004-07-16 00:15:55 +02:00
|
|
|
res= check_access(thd,
|
|
|
|
lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
|
|
|
|
any_db, 0, 0, 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
if (res)
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
if (!(res= open_and_lock_tables(thd, all_tables)))
|
2001-12-02 13:34:01 +01:00
|
|
|
{
|
2002-10-16 20:17:57 +02:00
|
|
|
if (lex->describe)
|
2002-09-26 22:08:22 +02:00
|
|
|
{
|
2002-11-09 14:40:46 +01:00
|
|
|
if (!(result= new select_send()))
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2002-11-09 14:40:46 +01:00
|
|
|
else
|
|
|
|
thd->send_explain_fields(result);
|
2003-05-05 20:54:37 +02:00
|
|
|
res= mysql_explain_union(thd, &thd->lex->unit, result);
|
2003-10-16 14:54:47 +02:00
|
|
|
if (lex->describe & DESCRIBE_EXTENDED)
|
|
|
|
{
|
|
|
|
char buff[1024];
|
|
|
|
String str(buff,(uint32) sizeof(buff), system_charset_info);
|
|
|
|
str.length(0);
|
2003-11-19 16:59:35 +01:00
|
|
|
thd->lex->unit.print(&str);
|
2003-10-16 14:54:47 +02:00
|
|
|
str.append('\0');
|
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
|
|
|
ER_YES, str.ptr());
|
|
|
|
}
|
2002-11-09 14:40:46 +01:00
|
|
|
result->send_eof();
|
2004-08-24 18:17:11 +02:00
|
|
|
delete result;
|
2002-09-26 22:08:22 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-08-24 18:17:11 +02:00
|
|
|
if (!result && !(result= new select_send()))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2004-07-16 00:15:55 +02:00
|
|
|
query_cache_store_query(thd, all_tables);
|
2004-08-24 18:17:11 +02:00
|
|
|
res= handle_select(thd, lex, result);
|
|
|
|
if (result != lex->result)
|
|
|
|
delete result;
|
2002-09-26 22:08:22 +02:00
|
|
|
}
|
2001-12-02 13:34:01 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-04-05 17:43:37 +02:00
|
|
|
case SQLCOM_PREPARE:
|
2004-06-07 10:09:10 +02:00
|
|
|
{
|
2004-05-21 02:27:50 +02:00
|
|
|
char *query_str;
|
|
|
|
uint query_len;
|
|
|
|
if (lex->prepared_stmt_code_is_varref)
|
|
|
|
{
|
2004-05-24 19:08:22 +02:00
|
|
|
/* This is PREPARE stmt FROM @var. */
|
2004-05-21 02:27:50 +02:00
|
|
|
String str;
|
|
|
|
CHARSET_INFO *to_cs= thd->variables.collation_connection;
|
|
|
|
bool need_conversion;
|
|
|
|
user_var_entry *entry;
|
2004-10-14 14:47:00 +02:00
|
|
|
String *pstr= &str;
|
2004-05-21 02:27:50 +02:00
|
|
|
uint32 unused;
|
2004-05-24 19:08:22 +02:00
|
|
|
/*
|
2004-06-07 10:09:10 +02:00
|
|
|
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.
|
2004-05-24 19:08:22 +02:00
|
|
|
*/
|
2004-06-07 10:09:10 +02:00
|
|
|
if ((entry=
|
|
|
|
(user_var_entry*)hash_search(&thd->user_vars,
|
2004-05-21 02:27:50 +02:00
|
|
|
(byte*)lex->prepared_stmt_code.str,
|
|
|
|
lex->prepared_stmt_code.length))
|
|
|
|
&& entry->value)
|
|
|
|
{
|
2004-06-07 10:09:10 +02:00
|
|
|
my_bool is_var_null;
|
|
|
|
pstr= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC);
|
2004-10-14 14:47:00 +02:00
|
|
|
/*
|
|
|
|
NULL value of variable checked early as entry->value so here
|
|
|
|
we can't get NULL in normal conditions
|
|
|
|
*/
|
2004-06-07 10:09:10 +02:00
|
|
|
DBUG_ASSERT(!is_var_null);
|
|
|
|
if (!pstr)
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2004-05-21 02:27:50 +02:00
|
|
|
}
|
|
|
|
else
|
2004-10-14 14:47:00 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
variable absent or equal to NULL, so we need to set variable to
|
|
|
|
something reasonable to get readable error message during parsing
|
|
|
|
*/
|
2004-06-07 10:09:10 +02:00
|
|
|
str.set("NULL", 4, &my_charset_latin1);
|
2004-10-14 14:47:00 +02:00
|
|
|
}
|
|
|
|
|
2004-06-07 10:09:10 +02:00
|
|
|
need_conversion=
|
2004-10-14 14:47:00 +02:00
|
|
|
String::needs_conversion(pstr->length(), pstr->charset(),
|
|
|
|
to_cs, &unused);
|
2004-06-07 10:09:10 +02:00
|
|
|
|
2004-10-14 14:47:00 +02:00
|
|
|
query_len= need_conversion? (pstr->length() * to_cs->mbmaxlen) :
|
|
|
|
pstr->length();
|
2004-11-08 00:13:54 +01:00
|
|
|
if (!(query_str= alloc_root(thd->mem_root, query_len+1)))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2004-11-12 14:36:31 +01:00
|
|
|
|
2004-05-21 02:27:50 +02:00
|
|
|
if (need_conversion)
|
2004-10-29 14:00:39 +02:00
|
|
|
{
|
|
|
|
uint dummy_errors;
|
|
|
|
query_len= copy_and_convert(query_str, query_len, to_cs,
|
|
|
|
pstr->ptr(), pstr->length(),
|
|
|
|
pstr->charset(), &dummy_errors);
|
|
|
|
}
|
2004-05-21 02:27:50 +02:00
|
|
|
else
|
2004-10-14 14:47:00 +02:00
|
|
|
memcpy(query_str, pstr->ptr(), pstr->length());
|
2004-05-24 19:08:22 +02:00
|
|
|
query_str[query_len]= 0;
|
2004-05-21 02:27:50 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-05-24 19:08:22 +02:00
|
|
|
query_str= lex->prepared_stmt_code.str;
|
|
|
|
query_len= lex->prepared_stmt_code.length;
|
2004-06-07 10:09:10 +02:00
|
|
|
DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n",
|
2004-05-21 02:27:50 +02:00
|
|
|
lex->prepared_stmt_name.length,
|
|
|
|
lex->prepared_stmt_name.str,
|
2004-05-24 19:08:22 +02:00
|
|
|
query_len, query_str));
|
2004-05-21 02:27:50 +02:00
|
|
|
}
|
2004-04-05 17:43:37 +02:00
|
|
|
thd->command= COM_PREPARE;
|
2004-10-20 03:04:37 +02:00
|
|
|
if (!(res= mysql_stmt_prepare(thd, query_str, query_len + 1,
|
|
|
|
&lex->prepared_stmt_name)))
|
2004-04-05 17:43:37 +02:00
|
|
|
send_ok(thd, 0L, 0L, "Statement prepared");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_EXECUTE:
|
|
|
|
{
|
2004-06-07 10:09:10 +02:00
|
|
|
DBUG_PRINT("info", ("EXECUTE: %.*s\n",
|
2004-04-12 23:58:48 +02:00
|
|
|
lex->prepared_stmt_name.length,
|
|
|
|
lex->prepared_stmt_name.str));
|
|
|
|
mysql_sql_stmt_execute(thd, &lex->prepared_stmt_name);
|
2004-04-05 17:43:37 +02:00
|
|
|
lex->prepared_stmt_params.empty();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_DEALLOCATE_PREPARE:
|
|
|
|
{
|
2004-04-12 23:58:48 +02:00
|
|
|
Statement* stmt;
|
|
|
|
DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n",
|
|
|
|
lex->prepared_stmt_name.length,
|
|
|
|
lex->prepared_stmt_name.str));
|
|
|
|
if ((stmt= thd->stmt_map.find_by_name(&lex->prepared_stmt_name)))
|
2004-04-05 17:43:37 +02:00
|
|
|
{
|
2004-04-12 23:58:48 +02:00
|
|
|
thd->stmt_map.erase(stmt);
|
|
|
|
send_ok(thd);
|
2004-04-05 17:43:37 +02:00
|
|
|
}
|
2004-04-12 23:58:48 +02:00
|
|
|
else
|
2004-05-21 02:27:50 +02:00
|
|
|
{
|
|
|
|
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0),
|
2004-11-13 18:35:51 +01:00
|
|
|
lex->prepared_stmt_name.length,
|
|
|
|
lex->prepared_stmt_name.str,
|
2004-05-21 02:27:50 +02:00
|
|
|
"DEALLOCATE PREPARE");
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2004-05-21 02:27:50 +02:00
|
|
|
}
|
2004-04-05 17:43:37 +02:00
|
|
|
break;
|
|
|
|
}
|
2001-12-17 18:59:20 +01:00
|
|
|
case SQLCOM_DO:
|
2004-07-16 00:15:55 +02:00
|
|
|
if (all_tables &&
|
2004-10-20 03:04:37 +02:00
|
|
|
(check_table_access(thd, SELECT_ACL, all_tables, 0) ||
|
|
|
|
open_and_lock_tables(thd, all_tables)))
|
|
|
|
goto error;
|
2003-01-03 16:08:00 +01:00
|
|
|
|
|
|
|
res= mysql_do(thd, *lex->insert_list);
|
2001-12-17 18:59:20 +01:00
|
|
|
break;
|
|
|
|
|
2002-02-14 14:04:14 +01:00
|
|
|
case SQLCOM_EMPTY_QUERY:
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2002-02-14 14:04:14 +01:00
|
|
|
break;
|
|
|
|
|
2002-10-28 14:44:19 +01:00
|
|
|
case SQLCOM_HELP:
|
|
|
|
res= mysqld_help(thd,lex->help_arg);
|
|
|
|
break;
|
|
|
|
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-10-27 06:11:55 +02:00
|
|
|
case SQLCOM_PURGE:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error;
|
2004-07-07 10:29:39 +02:00
|
|
|
/* PURGE MASTER LOGS TO 'file' */
|
2001-08-14 19:33:49 +02:00
|
|
|
res = purge_master_logs(thd, lex->to_log);
|
|
|
|
break;
|
|
|
|
}
|
2003-02-16 17:39:12 +01:00
|
|
|
case SQLCOM_PURGE_BEFORE:
|
|
|
|
{
|
2004-11-25 16:13:06 +01:00
|
|
|
Item *it;
|
|
|
|
|
2003-02-16 17:39:12 +01:00
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
|
|
|
goto error;
|
2004-07-07 10:29:39 +02:00
|
|
|
/* PURGE MASTER LOGS BEFORE 'data' */
|
2004-11-25 16:13:06 +01:00
|
|
|
it= (Item *)lex->value_list.head();
|
|
|
|
if (it->check_cols(1) || it->fix_fields(lex->thd, 0, &it))
|
|
|
|
{
|
|
|
|
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());
|
2003-02-16 17:39:12 +01:00
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2002-06-12 23:13:12 +02:00
|
|
|
case SQLCOM_SHOW_WARNS:
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
res= mysqld_show_warnings(thd, (ulong)
|
|
|
|
((1L << (uint) MYSQL_ERROR::WARN_LEVEL_NOTE) |
|
2003-01-04 14:37:20 +01:00
|
|
|
(1L << (uint) MYSQL_ERROR::WARN_LEVEL_WARN) |
|
|
|
|
(1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)
|
|
|
|
));
|
2002-06-12 23:13:12 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_SHOW_ERRORS:
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
res= mysqld_show_warnings(thd, (ulong)
|
|
|
|
(1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
|
2002-06-12 23:13:12 +02:00
|
|
|
break;
|
|
|
|
}
|
2001-07-05 01:14:31 +02:00
|
|
|
case SQLCOM_SHOW_NEW_MASTER:
|
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, REPL_SLAVE_ACL))
|
2001-07-05 01:14:31 +02:00
|
|
|
goto error;
|
2003-08-20 01:38:31 +02:00
|
|
|
/* This query don't work now. See comment in repl_failsafe.cc */
|
2002-09-11 05:40:08 +02:00
|
|
|
#ifndef WORKING_NEW_MASTER
|
2004-10-20 03:04:37 +02:00
|
|
|
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "SHOW NEW MASTER");
|
|
|
|
goto error;
|
2002-09-11 05:40:08 +02:00
|
|
|
#else
|
2001-07-05 01:14:31 +02:00
|
|
|
res = show_new_master(thd);
|
|
|
|
break;
|
2004-10-20 03:04:37 +02:00
|
|
|
#endif
|
2001-07-05 01:14:31 +02:00
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
|
2003-09-08 12:08:53 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2001-05-31 02:50:56 +02:00
|
|
|
case SQLCOM_SHOW_SLAVE_HOSTS:
|
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, REPL_SLAVE_ACL))
|
2001-05-31 02:50:56 +02:00
|
|
|
goto error;
|
|
|
|
res = show_slave_hosts(thd);
|
|
|
|
break;
|
|
|
|
}
|
2001-06-21 21:19:24 +02:00
|
|
|
case SQLCOM_SHOW_BINLOG_EVENTS:
|
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, REPL_SLAVE_ACL))
|
2001-06-21 21:19:24 +02:00
|
|
|
goto error;
|
|
|
|
res = show_binlog_events(thd);
|
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
|
|
|
|
2000-09-15 00:34:50 +02:00
|
|
|
case SQLCOM_BACKUP_TABLE:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
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) ||
|
2002-06-12 14:04:18 +02:00
|
|
|
check_global_access(thd, FILE_ACL))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2003-11-14 13:50:19 +01:00
|
|
|
thd->slow_command=TRUE;
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysql_backup_table(thd, first_table);
|
2000-09-15 00:34:50 +02:00
|
|
|
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-09-15 00:34:50 +02:00
|
|
|
case SQLCOM_RESTORE_TABLE:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
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) ||
|
2002-06-12 14:04:18 +02:00
|
|
|
check_global_access(thd, FILE_ACL))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2003-11-14 13:50:19 +01:00
|
|
|
thd->slow_command=TRUE;
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysql_restore_table(thd, first_table);
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-07-16 21:30:49 +02:00
|
|
|
case SQLCOM_ASSIGN_TO_KEYCACHE:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
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))
|
2003-08-02 12:26:02 +02:00
|
|
|
goto error;
|
2004-07-16 00:15:55 +02:00
|
|
|
res= mysql_assign_to_keycache(thd, first_table, &lex->name_and_length);
|
2003-07-16 21:30:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-06-12 13:29:02 +02:00
|
|
|
case SQLCOM_PRELOAD_KEYS:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
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))
|
2003-11-14 13:50:19 +01:00
|
|
|
goto error;
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysql_preload_keys(thd, first_table);
|
2003-06-12 13:29:02 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-09-08 12:08:53 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_CHANGE_MASTER:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error;
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_lock(&LOCK_active_mi);
|
2002-01-20 03:16:52 +01:00
|
|
|
res = change_master(thd,active_mi);
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_unlock(&LOCK_active_mi);
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SHOW_SLAVE_STAT:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2003-05-14 00:55:23 +02:00
|
|
|
/* Accept one of two privileges */
|
|
|
|
if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error;
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_lock(&LOCK_active_mi);
|
2002-01-20 03:16:52 +01:00
|
|
|
res = show_master_info(thd,active_mi);
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_unlock(&LOCK_active_mi);
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SHOW_MASTER_STAT:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2003-05-14 00:55:23 +02:00
|
|
|
/* Accept one of two privileges */
|
|
|
|
if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error;
|
|
|
|
res = show_binlog_info(thd);
|
|
|
|
break;
|
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2001-05-29 03:18:23 +02:00
|
|
|
case SQLCOM_LOAD_MASTER_DATA: // sync with master
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
2001-05-29 03:18:23 +02:00
|
|
|
goto error;
|
2002-08-08 02:12:02 +02:00
|
|
|
if (end_active_trans(thd))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2002-08-08 02:12:02 +02:00
|
|
|
else
|
|
|
|
res = load_master_data(thd);
|
2001-05-29 03:18:23 +02:00
|
|
|
break;
|
2003-09-08 12:08:53 +02:00
|
|
|
#endif /* HAVE_REPLICATION */
|
2002-07-08 18:34:49 +02:00
|
|
|
#ifdef HAVE_INNOBASE_DB
|
|
|
|
case SQLCOM_SHOW_INNODB_STATUS:
|
|
|
|
{
|
2002-08-08 02:12:02 +02:00
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
2002-07-08 18:34:49 +02:00
|
|
|
goto error;
|
|
|
|
res = innodb_show_status(thd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2003-09-08 12:08:53 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_LOAD_MASTER_TABLE:
|
2002-01-20 03:16:52 +01:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
if (!first_table->db)
|
|
|
|
first_table->db= thd->db;
|
|
|
|
if (check_access(thd, CREATE_ACL, first_table->db,
|
|
|
|
&first_table->grant.privilege, 0, 0))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
if (grant_option)
|
|
|
|
{
|
|
|
|
/* Check that the first table has CREATE privilege */
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
if (strlen(first_table->real_name) > NAME_LEN)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_TABLE_NAME, MYF(0), first_table->real_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_lock(&LOCK_active_mi);
|
2003-09-11 23:17:28 +02:00
|
|
|
/*
|
|
|
|
fetch_master_table will send the error to the client on failure.
|
|
|
|
Give error if the table already exists.
|
|
|
|
*/
|
2004-07-16 00:15:55 +02:00
|
|
|
if (!fetch_master_table(thd, first_table->db, first_table->real_name,
|
2003-09-11 23:17:28 +02:00
|
|
|
active_mi, 0, 0))
|
2002-01-20 03:16:52 +01:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2002-01-20 03:16:52 +01:00
|
|
|
}
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_unlock(&LOCK_active_mi);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2002-01-20 03:16:52 +01:00
|
|
|
}
|
2003-09-08 12:08:53 +02:00
|
|
|
#endif /* HAVE_REPLICATION */
|
2002-12-16 14:33:29 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_CREATE_TABLE:
|
2002-06-12 14:04:18 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
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;
|
2003-12-19 15:25:50 +01:00
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
if ((res= create_table_precheck(thd, select_tables, create_table)))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto create_error;
|
2004-04-10 00:14:32 +02:00
|
|
|
|
2001-11-03 20:33:11 +01:00
|
|
|
#ifndef HAVE_READLINK
|
|
|
|
lex->create_info.data_file_name=lex->create_info.index_file_name=0;
|
|
|
|
#else
|
2001-06-01 03:27:59 +02:00
|
|
|
/* Fix names if symlinked tables */
|
2001-12-05 12:03:00 +01:00
|
|
|
if (append_file_to_dir(thd, &lex->create_info.data_file_name,
|
2003-12-19 15:25:50 +01:00
|
|
|
create_table->real_name) ||
|
2004-07-16 00:15:55 +02:00
|
|
|
append_file_to_dir(thd, &lex->create_info.index_file_name,
|
2003-12-19 15:25:50 +01:00
|
|
|
create_table->real_name))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto create_error;
|
2001-11-03 20:33:11 +01:00
|
|
|
#endif
|
2003-11-18 12:47:27 +01:00
|
|
|
/*
|
2004-10-07 00:45:06 +02:00
|
|
|
If we are using SET CHARSET without DEFAULT, add an implicit
|
2003-11-18 12:47:27 +01:00
|
|
|
DEFAULT to not confuse old users. (This may change).
|
|
|
|
*/
|
|
|
|
if ((lex->create_info.used_fields &
|
|
|
|
(HA_CREATE_USED_DEFAULT_CHARSET | HA_CREATE_USED_CHARSET)) ==
|
|
|
|
HA_CREATE_USED_CHARSET)
|
|
|
|
{
|
|
|
|
lex->create_info.used_fields&= ~HA_CREATE_USED_CHARSET;
|
|
|
|
lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
|
|
|
|
lex->create_info.default_table_charset= lex->create_info.table_charset;
|
|
|
|
lex->create_info.table_charset= 0;
|
|
|
|
}
|
2001-06-07 13:10:58 +02:00
|
|
|
if (select_lex->item_list.elements) // With select
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
select_result *result;
|
2004-09-04 14:59:49 +02:00
|
|
|
|
2003-03-12 06:51:08 +01:00
|
|
|
select_lex->options|= SELECT_NO_UNLOCK;
|
2004-03-29 21:40:49 +02:00
|
|
|
unit->set_limit(select_lex, select_lex);
|
2001-08-14 19:33:49 +02:00
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
if (!(res= open_and_lock_tables(thd, select_tables)))
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2004-09-08 09:18:04 +02:00
|
|
|
/*
|
|
|
|
Is table which we are changing used somewhere in other parts
|
|
|
|
of query
|
|
|
|
*/
|
|
|
|
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
|
|
|
|
unique_table(create_table, select_tables))
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_UPDATE_TABLE_USED, MYF(0), create_table->real_name);
|
2004-09-08 09:18:04 +02:00
|
|
|
goto create_error;
|
|
|
|
}
|
2004-09-10 01:22:44 +02:00
|
|
|
/* If we create merge table, we have to test tables in merge, too */
|
|
|
|
if (lex->create_info.used_fields & HA_CREATE_USED_UNION)
|
|
|
|
{
|
|
|
|
TABLE_LIST *tab;
|
|
|
|
for (tab= (TABLE_LIST*) lex->create_info.merge_list.first;
|
|
|
|
tab;
|
|
|
|
tab= tab->next_local)
|
|
|
|
{
|
|
|
|
if (unique_table(tab, select_tables))
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_UPDATE_TABLE_USED, MYF(0), tab->real_name);
|
2004-09-10 01:22:44 +02:00
|
|
|
goto create_error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-09-08 09:18:04 +02:00
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
if ((result= new select_create(create_table,
|
|
|
|
&lex->create_info,
|
|
|
|
lex->create_list,
|
|
|
|
lex->key_list,
|
|
|
|
select_lex->item_list,
|
|
|
|
lex->duplicates)))
|
2004-06-13 21:39:09 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
CREATE from SELECT give its SELECT_LEX for SELECT,
|
|
|
|
and item_list belong to SELECT
|
|
|
|
*/
|
|
|
|
select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
|
2001-08-14 19:33:49 +02:00
|
|
|
res=handle_select(thd, lex, result);
|
2004-06-13 21:39:09 +02:00
|
|
|
select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE;
|
2004-09-17 02:08:23 +02:00
|
|
|
delete result;
|
2004-06-13 21:39:09 +02:00
|
|
|
}
|
2004-07-07 10:29:39 +02:00
|
|
|
/* reset for PS */
|
2004-04-07 23:16:17 +02:00
|
|
|
lex->create_list.empty();
|
|
|
|
lex->key_list.empty();
|
2001-08-14 19:33:49 +02:00
|
|
|
}
|
|
|
|
}
|
2004-07-07 10:29:39 +02:00
|
|
|
else
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-07-07 10:29:39 +02:00
|
|
|
/* regular create */
|
2002-12-28 10:38:29 +01:00
|
|
|
if (lex->name)
|
2003-12-19 15:25:50 +01:00
|
|
|
res= mysql_create_like_table(thd, create_table, &lex->create_info,
|
2002-12-28 10:38:29 +01:00
|
|
|
(Table_ident *)lex->name);
|
|
|
|
else
|
2003-08-11 15:18:34 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
res= mysql_create_table(thd, create_table->db,
|
|
|
|
create_table->real_name, &lex->create_info,
|
|
|
|
lex->create_list,
|
2004-09-06 14:14:10 +02:00
|
|
|
lex->key_list, 0, 0);
|
2003-08-11 15:18:34 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!res)
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
lex->link_first_table_back(create_table, link_to_local);
|
2004-04-05 23:10:43 +02:00
|
|
|
break;
|
|
|
|
|
2004-04-07 23:16:17 +02:00
|
|
|
create_error:
|
2004-07-07 10:29:39 +02:00
|
|
|
/* put tables back for PS rexecuting */
|
2004-07-16 00:15:55 +02:00
|
|
|
lex->link_first_table_back(create_table, link_to_local);
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2002-06-12 14:04:18 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_CREATE_INDEX:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
if (check_one_table_access(thd, INDEX_ACL, all_tables))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2003-11-14 13:50:19 +01:00
|
|
|
thd->slow_command=TRUE;
|
2000-12-07 13:08:48 +01:00
|
|
|
if (end_active_trans(thd))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2000-12-07 13:08:48 +01:00
|
|
|
else
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysql_create_index(thd, first_table, lex->key_list);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
|
2003-09-08 12:08:53 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SLAVE_START:
|
2002-01-20 03:16:52 +01:00
|
|
|
{
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_lock(&LOCK_active_mi);
|
2002-01-20 03:16:52 +01:00
|
|
|
start_slave(thd,active_mi,1 /* net report*/);
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_unlock(&LOCK_active_mi);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2002-01-20 03:16:52 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SLAVE_STOP:
|
2002-12-13 15:36:08 +01:00
|
|
|
/*
|
|
|
|
If the client thread has locked tables, a deadlock is possible.
|
|
|
|
Assume that
|
|
|
|
- the client thread does LOCK TABLE t READ.
|
|
|
|
- then the master updates t.
|
|
|
|
- then the SQL slave thread wants to update t,
|
2002-12-13 15:01:51 +01:00
|
|
|
so it waits for the client thread because t is locked by it.
|
2002-12-13 15:36:08 +01:00
|
|
|
- then the client thread does SLAVE STOP.
|
2002-12-13 15:01:51 +01:00
|
|
|
SLAVE STOP waits for the SQL slave thread to terminate its
|
|
|
|
update t, which waits for the client thread because t is locked by it.
|
2002-12-13 15:36:08 +01:00
|
|
|
To prevent that, refuse SLAVE STOP if the
|
|
|
|
client thread has locked tables
|
|
|
|
*/
|
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, ER(ER_LOCK_OR_ACTIVE_TRANSACTION),
|
|
|
|
MYF(0));
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2002-12-13 15:36:08 +01:00
|
|
|
}
|
2002-01-20 03:16:52 +01:00
|
|
|
{
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_lock(&LOCK_active_mi);
|
2002-01-20 03:16:52 +01:00
|
|
|
stop_slave(thd,active_mi,1/* net report*/);
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_unlock(&LOCK_active_mi);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2002-01-20 03:16:52 +01:00
|
|
|
}
|
2003-09-08 12:08:53 +02:00
|
|
|
#endif /* HAVE_REPLICATION */
|
2002-12-16 14:33:29 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_ALTER_TABLE:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
#if defined(DONT_ALLOW_SHOW_COMMANDS)
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
|
|
|
|
MYF(0)); /* purecov: inspected */
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
#else
|
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
ulong priv=0;
|
2002-11-12 16:41:31 +01:00
|
|
|
if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_TABLE_NAME, MYF(0), lex->name);
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2001-06-07 13:10:58 +02:00
|
|
|
if (!select_lex->db)
|
2004-07-16 00:15:55 +02:00
|
|
|
select_lex->db= first_table->db;
|
|
|
|
if (check_access(thd, ALTER_ACL, first_table->db,
|
|
|
|
&first_table->grant.privilege, 0, 0) ||
|
2003-09-26 12:33:13 +02:00
|
|
|
check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0)||
|
2004-07-16 00:15:55 +02:00
|
|
|
check_merge_table_access(thd, first_table->db,
|
2000-09-14 01:39:07 +02:00
|
|
|
(TABLE_LIST *)
|
|
|
|
lex->create_info.merge_list.first))
|
|
|
|
goto error; /* purecov: inspected */
|
2000-07-31 21:29:14 +02:00
|
|
|
if (grant_option)
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_grant(thd, ALTER_ACL, all_tables, 0, UINT_MAX, 0))
|
2000-07-31 21:29:14 +02:00
|
|
|
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;
|
2001-06-07 13:10:58 +02:00
|
|
|
tmp_table.db=select_lex->db;
|
2000-07-31 21:29:14 +02:00
|
|
|
tmp_table.grant.privilege=priv;
|
2004-04-10 00:14:32 +02:00
|
|
|
if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0,
|
|
|
|
UINT_MAX, 0))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
2001-06-01 03:27:59 +02:00
|
|
|
/* Don't yet allow changing of symlinks with ALTER TABLE */
|
|
|
|
lex->create_info.data_file_name=lex->create_info.index_file_name=0;
|
2000-07-31 21:29:14 +02:00
|
|
|
/* ALTER TABLE ends previous transaction */
|
2000-11-13 22:55:10 +01:00
|
|
|
if (end_active_trans(thd))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
else
|
2001-12-02 13:34:01 +01:00
|
|
|
{
|
2003-11-14 13:50:19 +01:00
|
|
|
thd->slow_command=TRUE;
|
2001-06-07 13:10:58 +02:00
|
|
|
res= mysql_alter_table(thd, select_lex->db, lex->name,
|
2000-07-31 21:29:14 +02:00
|
|
|
&lex->create_info,
|
2004-07-16 00:15:55 +02:00
|
|
|
first_table, lex->create_list,
|
2004-05-21 16:57:03 +02:00
|
|
|
lex->key_list,
|
2003-01-25 01:25:52 +01:00
|
|
|
select_lex->order_list.elements,
|
2001-06-07 13:10:58 +02:00
|
|
|
(ORDER *) select_lex->order_list.first,
|
2004-05-21 16:57:03 +02:00
|
|
|
lex->duplicates, &lex->alter_info);
|
2001-12-02 13:34:01 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-09-26 12:33:13 +02:00
|
|
|
#endif /*DONT_ALLOW_SHOW_COMMANDS*/
|
2000-08-21 02:07:54 +02:00
|
|
|
case SQLCOM_RENAME_TABLE:
|
2000-08-21 23:18:32 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2000-08-21 23:18:32 +02:00
|
|
|
TABLE_LIST *table;
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_db_used(thd, all_tables))
|
2000-08-21 02:07:54 +02:00
|
|
|
goto error;
|
2004-07-16 00:15:55 +02:00
|
|
|
for (table= first_table; table; table= table->next_local->next_local)
|
2000-08-21 23:18:32 +02:00
|
|
|
{
|
2000-08-29 18:38:32 +02:00
|
|
|
if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
|
2003-09-26 12:33:13 +02:00
|
|
|
&table->grant.privilege,0,0) ||
|
2004-07-16 00:15:55 +02:00
|
|
|
check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db,
|
|
|
|
&table->next_local->grant.privilege, 0, 0))
|
2000-08-21 23:18:32 +02:00
|
|
|
goto error;
|
|
|
|
if (grant_option)
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
TABLE_LIST old_list, new_list;
|
2004-03-23 18:24:35 +01:00
|
|
|
/*
|
|
|
|
we do not need initialize old_list and new_list because we will
|
|
|
|
come table[0] and table->next[0] there
|
|
|
|
*/
|
2004-07-16 00:15:55 +02:00
|
|
|
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,
|
2001-08-14 19:33:49 +02:00
|
|
|
INSERT_ACL | CREATE_ACL) &&
|
2004-07-16 00:15:55 +02:00
|
|
|
check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0)))
|
2000-08-21 23:18:32 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
query_cache_invalidate3(thd, first_table, 0);
|
2004-10-20 03:04:37 +02:00
|
|
|
if (end_active_trans(thd) || mysql_rename_tables(thd, first_table))
|
|
|
|
goto error;
|
2000-08-21 02:07:54 +02:00
|
|
|
break;
|
2000-08-21 23:18:32 +02:00
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-10-27 06:11:55 +02:00
|
|
|
case SQLCOM_SHOW_BINLOGS:
|
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
|
|
|
|
MYF(0)); /* purecov: inspected */
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2000-10-27 06:11:55 +02:00
|
|
|
#else
|
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
2000-10-27 06:11:55 +02:00
|
|
|
goto error;
|
|
|
|
res = show_binlogs(thd);
|
|
|
|
break;
|
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
#endif
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SHOW_CREATE:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2000-08-21 02:07:54 +02:00
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
|
|
|
|
MYF(0)); /* purecov: inspected */
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2000-08-21 02:07:54 +02:00
|
|
|
#else
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_db_used(thd, all_tables) ||
|
|
|
|
check_access(thd, SELECT_ACL | EXTRA_ACL, first_table->db,
|
|
|
|
&first_table->grant.privilege, 0, 0))
|
2000-08-21 02:07:54 +02:00
|
|
|
goto error;
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysqld_show_create(thd, first_table);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-08-21 02:07:54 +02:00
|
|
|
#endif
|
2003-08-21 16:15:06 +02:00
|
|
|
case SQLCOM_CHECKSUM:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
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))
|
2003-08-21 16:15:06 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysql_checksum_table(thd, first_table, &lex->check_opt);
|
2003-08-21 16:15:06 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_REPAIR:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
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))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2003-11-14 13:50:19 +01:00
|
|
|
thd->slow_command=TRUE;
|
2004-07-16 00:15:55 +02:00
|
|
|
res= mysql_repair_table(thd, first_table, &lex->check_opt);
|
2003-05-15 18:35:39 +02:00
|
|
|
/* ! we write after unlocking the table */
|
|
|
|
if (!res && !lex->no_write_to_binlog)
|
|
|
|
{
|
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
2004-11-03 18:23:58 +01:00
|
|
|
thd->clear_error(); // No binlog error generated
|
2004-12-03 12:13:51 +01:00
|
|
|
Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
|
2003-05-15 18:35:39 +02:00
|
|
|
mysql_bin_log.write(&qinfo);
|
|
|
|
}
|
|
|
|
}
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_CHECK:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
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))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2003-11-14 13:50:19 +01:00
|
|
|
thd->slow_command=TRUE;
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysql_check_table(thd, first_table, &lex->check_opt);
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_ANALYZE:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
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))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2003-11-14 13:50:19 +01:00
|
|
|
thd->slow_command=TRUE;
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysql_analyze_table(thd, first_table, &lex->check_opt);
|
2003-05-15 18:35:39 +02:00
|
|
|
/* ! we write after unlocking the table */
|
|
|
|
if (!res && !lex->no_write_to_binlog)
|
|
|
|
{
|
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
2004-11-03 18:23:58 +01:00
|
|
|
thd->clear_error(); // No binlog error generated
|
2004-12-03 12:13:51 +01:00
|
|
|
Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
|
2003-05-15 18:35:39 +02:00
|
|
|
mysql_bin_log.write(&qinfo);
|
|
|
|
}
|
|
|
|
}
|
2000-08-21 02:07:54 +02:00
|
|
|
break;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2000-09-12 02:02:33 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_OPTIMIZE:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
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))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2003-11-14 13:50:19 +01:00
|
|
|
thd->slow_command=TRUE;
|
2004-06-10 16:41:24 +02:00
|
|
|
res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
|
2004-07-16 00:15:55 +02:00
|
|
|
mysql_recreate_table(thd, first_table, 1) :
|
|
|
|
mysql_optimize_table(thd, first_table, &lex->check_opt);
|
2003-05-15 18:35:39 +02:00
|
|
|
/* ! we write after unlocking the table */
|
|
|
|
if (!res && !lex->no_write_to_binlog)
|
|
|
|
{
|
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
2004-11-03 18:23:58 +01:00
|
|
|
thd->clear_error(); // No binlog error generated
|
2004-12-03 12:13:51 +01:00
|
|
|
Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
|
2003-05-15 18:35:39 +02:00
|
|
|
mysql_bin_log.write(&qinfo);
|
|
|
|
}
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_UPDATE:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
if (update_precheck(thd, all_tables))
|
2004-04-10 00:14:32 +02:00
|
|
|
break;
|
2004-11-25 01:23:13 +01:00
|
|
|
res= (result= mysql_update(thd, all_tables,
|
|
|
|
select_lex->item_list,
|
|
|
|
lex->value_list,
|
|
|
|
select_lex->where,
|
|
|
|
select_lex->order_list.elements,
|
|
|
|
(ORDER *) select_lex->order_list.first,
|
|
|
|
select_lex->select_limit,
|
|
|
|
lex->duplicates));
|
2004-11-25 08:28:32 +01:00
|
|
|
/* mysql_update return 2 if we need to switch to multi-update */
|
2004-11-25 01:23:13 +01:00
|
|
|
if (result != 2)
|
2004-09-15 22:42:56 +02:00
|
|
|
break;
|
2002-11-29 13:17:54 +01:00
|
|
|
case SQLCOM_UPDATE_MULTI:
|
2004-09-15 22:42:56 +02:00
|
|
|
{
|
2004-11-25 01:23:13 +01:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2004-11-25 08:28:32 +01:00
|
|
|
/* if we switched from normal update, rights are checked */
|
2004-11-25 01:23:13 +01:00
|
|
|
if (result != 2)
|
|
|
|
{
|
|
|
|
if ((res= multi_update_precheck(thd, all_tables)))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
res= 0;
|
|
|
|
|
|
|
|
res= mysql_multi_update(thd, all_tables,
|
|
|
|
&select_lex->item_list,
|
|
|
|
&lex->value_list,
|
|
|
|
select_lex->where,
|
|
|
|
select_lex->options,
|
|
|
|
lex->duplicates, unit, select_lex);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2004-02-11 00:06:46 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_REPLACE:
|
2002-11-28 17:25:41 +01:00
|
|
|
case SQLCOM_INSERT:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2004-10-29 18:26:52 +02:00
|
|
|
if ((res= insert_precheck(thd, all_tables)))
|
2004-04-10 00:14:32 +02:00
|
|
|
break;
|
2004-07-16 00:15:55 +02:00
|
|
|
res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
|
|
|
|
select_lex->item_list, lex->value_list,
|
2004-11-03 11:39:38 +01:00
|
|
|
(lex->value_list.elements ?
|
|
|
|
DUP_UPDATE : lex->duplicates));
|
2004-07-16 00:15:55 +02:00
|
|
|
if (first_table->view && !first_table->contain_auto_increment)
|
|
|
|
thd->last_insert_id= 0; // do not show last insert ID if VIEW have not it
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2002-11-28 17:25:41 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_REPLACE_SELECT:
|
|
|
|
case SQLCOM_INSERT_SELECT:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
if ((res= insert_select_precheck(thd, all_tables)))
|
2004-04-07 23:16:17 +02:00
|
|
|
break;
|
2003-12-17 16:35:34 +01:00
|
|
|
|
2003-12-14 05:39:52 +01:00
|
|
|
/* Fix lock for first table */
|
2004-07-16 00:15:55 +02:00
|
|
|
if (first_table->lock_type == TL_WRITE_DELAYED)
|
|
|
|
first_table->lock_type= TL_WRITE;
|
2003-12-14 05:39:52 +01:00
|
|
|
|
2003-03-12 06:51:08 +01:00
|
|
|
/* Don't unlock tables until command is written to binary log */
|
|
|
|
select_lex->options|= SELECT_NO_UNLOCK;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
select_result *result;
|
2004-03-29 21:40:49 +02:00
|
|
|
unit->set_limit(select_lex, select_lex);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
if (!(res= open_and_lock_tables(thd, all_tables)))
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2004-09-15 22:42:56 +02:00
|
|
|
/* Skip first table, which is the table we are inserting in */
|
|
|
|
lex->select_lex.table_list.first= (byte*)first_table->next_local;
|
2004-09-08 09:18:04 +02:00
|
|
|
|
2004-09-15 22:42:56 +02:00
|
|
|
res= mysql_insert_select_prepare(thd);
|
|
|
|
if (!res && (result= new select_insert(first_table, first_table->table,
|
|
|
|
&lex->field_list,
|
2004-10-05 12:41:51 +02:00
|
|
|
lex->duplicates,
|
|
|
|
lex->duplicates == DUP_IGNORE)))
|
2004-09-17 02:08:23 +02:00
|
|
|
{
|
2004-09-15 22:42:56 +02:00
|
|
|
TABLE_LIST *first_select_table;
|
2004-09-08 09:18:04 +02:00
|
|
|
|
2004-06-13 21:39:09 +02:00
|
|
|
/*
|
|
|
|
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;
|
2004-07-16 00:15:55 +02:00
|
|
|
res= handle_select(thd, lex, result);
|
2004-04-07 23:16:17 +02:00
|
|
|
lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
|
2004-09-17 02:08:23 +02:00
|
|
|
delete result;
|
|
|
|
}
|
2004-09-15 22:42:56 +02:00
|
|
|
/* revert changes for SP */
|
|
|
|
lex->select_lex.table_list.first= (byte*) first_table;
|
2001-08-14 19:33:49 +02:00
|
|
|
}
|
|
|
|
else
|
2004-10-20 03:04:37 +02:00
|
|
|
res= TRUE;
|
2004-07-16 00:15:55 +02:00
|
|
|
|
|
|
|
if (first_table->view && !first_table->contain_auto_increment)
|
|
|
|
thd->last_insert_id= 0; // do not show last insert ID if VIEW have not it
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-11-13 22:55:10 +01:00
|
|
|
case SQLCOM_TRUNCATE:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
if (check_one_table_access(thd, DELETE_ACL, all_tables))
|
2003-03-07 11:55:07 +01:00
|
|
|
goto error;
|
2001-09-02 12:47:00 +02:00
|
|
|
/*
|
|
|
|
Don't allow this within a transaction because we want to use
|
|
|
|
re-generate table
|
|
|
|
*/
|
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
|
|
|
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
2001-09-02 12:47:00 +02:00
|
|
|
goto error;
|
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
|
2004-09-06 14:14:10 +02:00
|
|
|
res= mysql_truncate(thd, first_table, 0);
|
2001-09-02 12:47:00 +02:00
|
|
|
break;
|
2000-11-29 04:09:28 +01:00
|
|
|
case SQLCOM_DELETE:
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
if ((res= delete_precheck(thd, all_tables)))
|
2004-04-10 00:14:32 +02:00
|
|
|
break;
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysql_delete(thd, all_tables, select_lex->where,
|
2003-12-19 19:16:26 +01:00
|
|
|
&select_lex->order_list,
|
2002-11-16 19:19:10 +01:00
|
|
|
select_lex->select_limit, select_lex->options);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2001-12-13 21:03:09 +01:00
|
|
|
case SQLCOM_DELETE_MULTI:
|
2001-06-15 04:03:15 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2004-04-07 23:16:17 +02:00
|
|
|
TABLE_LIST *aux_tables=
|
|
|
|
(TABLE_LIST *)thd->lex->auxilliary_table_list.first;
|
2004-04-10 00:14:32 +02:00
|
|
|
uint table_count;
|
2001-06-15 04:03:15 +02:00
|
|
|
multi_delete *result;
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
if ((res= multi_delete_precheck(thd, all_tables, &table_count)))
|
2004-04-07 23:16:17 +02:00
|
|
|
break;
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2004-04-12 02:26:32 +02:00
|
|
|
/* condition will be TRUE on SP re-excuting */
|
2004-04-07 23:16:17 +02:00
|
|
|
if (select_lex->item_list.elements != 0)
|
|
|
|
select_lex->item_list.empty();
|
2002-12-06 20:11:27 +01:00
|
|
|
if (add_item_to_list(thd, new Item_null()))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2004-04-07 23:16:17 +02:00
|
|
|
|
2001-06-15 04:03:15 +02:00
|
|
|
thd->proc_info="init";
|
2004-07-16 00:15:55 +02:00
|
|
|
if ((res= open_and_lock_tables(thd, all_tables)))
|
|
|
|
break;
|
|
|
|
|
2004-09-15 22:42:56 +02:00
|
|
|
if (!first_table->table)
|
|
|
|
{
|
|
|
|
DBUG_ASSERT(first_table->view &&
|
|
|
|
first_table->ancestor && first_table->ancestor->next_local);
|
|
|
|
my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
|
|
|
|
first_table->view_db.str, first_table->view_name.str);
|
|
|
|
res= -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
if ((res= mysql_multi_delete_prepare(thd)))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2004-02-01 14:30:32 +01:00
|
|
|
|
2003-01-30 21:15:44 +01:00
|
|
|
if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables,
|
|
|
|
table_count)))
|
2001-06-15 04:03:15 +02:00
|
|
|
{
|
2003-01-25 01:25:52 +01:00
|
|
|
res= mysql_select(thd, &select_lex->ref_pointer_array,
|
|
|
|
select_lex->get_table_list(),
|
|
|
|
select_lex->with_wild,
|
2002-11-26 21:33:33 +01:00
|
|
|
select_lex->item_list,
|
2002-09-03 08:50:36 +02:00
|
|
|
select_lex->where,
|
2003-01-25 01:25:52 +01:00
|
|
|
0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
|
2002-09-03 08:50:36 +02:00
|
|
|
(ORDER *)NULL,
|
|
|
|
select_lex->options | thd->options |
|
2003-02-26 00:03:47 +01:00
|
|
|
SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK,
|
2003-11-23 01:01:15 +01:00
|
|
|
result, unit, select_lex);
|
2002-01-12 18:51:10 +01:00
|
|
|
delete result;
|
2001-06-15 04:03:15 +02:00
|
|
|
}
|
|
|
|
else
|
2004-10-20 03:04:37 +02:00
|
|
|
res= TRUE;
|
2001-06-15 04:03:15 +02:00
|
|
|
close_thread_tables(thd);
|
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_DROP_TABLE:
|
2001-08-02 05:29:50 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2003-01-04 14:37:20 +01:00
|
|
|
if (!lex->drop_temporary)
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_table_access(thd, DROP_ACL, all_tables, 0))
|
2003-01-04 14:37:20 +01:00
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
if (end_active_trans(thd))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2003-01-04 14:37:20 +01:00
|
|
|
}
|
2001-08-02 05:29:50 +02:00
|
|
|
else
|
2003-04-03 20:19:12 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
If this is a slave thread, we may sometimes execute some
|
|
|
|
DROP / * 40005 TEMPORARY * / TABLE
|
|
|
|
that come from parts of binlogs (likely if we use RESET SLAVE or CHANGE
|
|
|
|
MASTER TO), while the temporary table has already been dropped.
|
2004-02-11 00:06:46 +01:00
|
|
|
To not generate such irrelevant "table does not exist errors",
|
|
|
|
we silently add IF EXISTS if TEMPORARY was used.
|
2003-04-03 20:19:12 +02:00
|
|
|
*/
|
|
|
|
if (thd->slave_thread)
|
|
|
|
lex->drop_if_exists= 1;
|
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
|
|
|
|
lex->drop_temporary);
|
2001-08-02 05:29:50 +02:00
|
|
|
}
|
|
|
|
break;
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_DROP_INDEX:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
if (check_one_table_access(thd, INDEX_ACL, all_tables))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2000-12-07 13:08:48 +01:00
|
|
|
if (end_active_trans(thd))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2000-12-07 13:08:48 +01:00
|
|
|
else
|
2004-07-16 00:15:55 +02:00
|
|
|
res = mysql_drop_index(thd, first_table, &lex->alter_info);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_SHOW_PROCESSLIST:
|
2002-06-12 14:04:18 +02:00
|
|
|
if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL))
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2003-09-26 12:33:13 +02:00
|
|
|
mysqld_list_processes(thd,
|
|
|
|
thd->master_access & PROCESS_ACL ? NullS :
|
2003-12-19 15:25:50 +01:00
|
|
|
thd->priv_user,lex->verbose);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2003-12-17 23:52:03 +01:00
|
|
|
case SQLCOM_SHOW_STORAGE_ENGINES:
|
|
|
|
res= mysqld_show_storage_engines(thd);
|
2002-06-12 23:13:12 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_SHOW_PRIVILEGES:
|
|
|
|
res= mysqld_show_privileges(thd);
|
|
|
|
break;
|
|
|
|
case SQLCOM_SHOW_COLUMN_TYPES:
|
|
|
|
res= mysqld_show_column_types(thd);
|
|
|
|
break;
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SHOW_STATUS:
|
2004-09-13 15:48:01 +02:00
|
|
|
STATUS_VAR tmp;
|
|
|
|
if (lex->option_type == OPT_GLOBAL)
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&LOCK_status);
|
|
|
|
calc_sum_of_all_status(&tmp);
|
|
|
|
}
|
|
|
|
res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS),
|
|
|
|
status_vars, OPT_GLOBAL, &LOCK_status,
|
|
|
|
(lex->option_type == OPT_GLOBAL ?
|
|
|
|
&tmp: &thd->status_var));
|
|
|
|
if (lex->option_type == OPT_GLOBAL)
|
|
|
|
pthread_mutex_unlock(&LOCK_status);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_SHOW_VARIABLES:
|
|
|
|
res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS),
|
2003-08-17 13:10:15 +02:00
|
|
|
init_vars, lex->option_type,
|
2004-09-13 15:48:01 +02:00
|
|
|
&LOCK_global_system_variables, 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2000-12-15 12:18:52 +01:00
|
|
|
case SQLCOM_SHOW_LOGS:
|
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
|
|
|
|
MYF(0)); /* purecov: inspected */
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2000-12-15 12:18:52 +01:00
|
|
|
#else
|
|
|
|
{
|
2003-09-26 12:33:13 +02:00
|
|
|
if (grant_option && check_access(thd, FILE_ACL, any_db,0,0,0))
|
2000-12-15 12:18:52 +01:00
|
|
|
goto error;
|
|
|
|
res= mysqld_show_logs(thd);
|
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
#endif
|
2001-07-11 09:36:22 +02:00
|
|
|
case SQLCOM_SHOW_OPEN_TABLES:
|
|
|
|
res= mysqld_show_open_tables(thd,(lex->wild ? lex->wild->ptr() : NullS));
|
|
|
|
break;
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_CHANGE_DB:
|
2001-06-07 13:10:58 +02:00
|
|
|
mysql_change_db(thd,select_lex->db);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2003-09-06 15:50:30 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_LOAD:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2000-07-31 21:29:14 +02:00
|
|
|
uint privilege= (lex->duplicates == DUP_REPLACE ?
|
2002-11-30 13:03:55 +01:00
|
|
|
INSERT_ACL | DELETE_ACL : INSERT_ACL);
|
2002-02-13 20:53:26 +01:00
|
|
|
|
|
|
|
if (!lex->local_file)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_access(thd, privilege | FILE_ACL, first_table->db, 0, 0, 0))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2002-02-13 20:53:26 +01:00
|
|
|
if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) ||
|
2002-07-23 17:31:22 +02:00
|
|
|
! opt_local_infile)
|
2002-02-13 20:53:26 +01:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), MYF(0));
|
2002-02-13 20:53:26 +01:00
|
|
|
goto error;
|
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_one_table_access(thd, privilege, all_tables))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error;
|
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
|
2004-10-21 20:53:27 +02:00
|
|
|
lex->duplicates, (bool) lex->local_file,
|
|
|
|
lex->lock_option, lex->duplicates == DUP_IGNORE);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-09-06 15:50:30 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SET_OPTION:
|
2004-06-03 23:17:18 +02:00
|
|
|
{
|
|
|
|
List<set_var_base> *lex_var_list= &lex->var_list;
|
2004-07-16 00:15:55 +02:00
|
|
|
if (all_tables &&
|
2004-10-20 03:04:37 +02:00
|
|
|
(check_table_access(thd, SELECT_ACL, all_tables, 0) ||
|
|
|
|
open_and_lock_tables(thd, all_tables)))
|
|
|
|
goto error;
|
2004-06-03 23:17:18 +02:00
|
|
|
if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
|
|
|
|
{
|
2004-10-20 03:04:37 +02:00
|
|
|
my_error(ER_RESERVED_SYNTAX, MYF(0), "SET ONE_SHOT");
|
|
|
|
goto error;
|
2004-06-03 23:17:18 +02:00
|
|
|
}
|
|
|
|
if (!(res= sql_set_variables(thd, lex_var_list)))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
If the previous command was a SET ONE_SHOT, we don't want to forget
|
|
|
|
about the ONE_SHOT property of that SET. So we use a |= instead of = .
|
|
|
|
*/
|
|
|
|
thd->one_shot_set|= lex->one_shot_set;
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2004-06-03 23:17:18 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2004-06-03 23:17:18 +02:00
|
|
|
}
|
2003-01-04 15:33:42 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_UNLOCK_TABLES:
|
2004-11-10 17:56:45 +01:00
|
|
|
/*
|
|
|
|
It is critical for mysqldump --single-transaction --master-data that
|
|
|
|
UNLOCK TABLES does not implicitely commit a connection which has only
|
|
|
|
done FLUSH TABLES WITH READ LOCK + BEGIN. If this assumption becomes
|
|
|
|
false, mysqldump will not work.
|
|
|
|
*/
|
2002-11-16 19:19:10 +01:00
|
|
|
unlock_locked_tables(thd);
|
2001-09-08 00:02:41 +02:00
|
|
|
if (thd->options & OPTION_TABLE_LOCK)
|
|
|
|
{
|
2001-04-19 19:41:19 +02:00
|
|
|
end_active_trans(thd);
|
2001-09-08 00:02:41 +02:00
|
|
|
thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
if (thd->global_read_lock)
|
2001-08-14 19:33:49 +02:00
|
|
|
unlock_global_read_lock(thd);
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_LOCK_TABLES:
|
2002-11-16 19:19:10 +01:00
|
|
|
unlock_locked_tables(thd);
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_db_used(thd, all_tables) || end_active_trans(thd))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error;
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0))
|
2001-06-03 16:07:26 +02:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->in_lock_tables=1;
|
2001-09-08 00:02:41 +02:00
|
|
|
thd->options|= OPTION_TABLE_LOCK;
|
2004-07-16 00:15:55 +02:00
|
|
|
|
|
|
|
if (!(res= open_and_lock_tables(thd, all_tables)))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-03-04 17:32:55 +01:00
|
|
|
#ifdef HAVE_QUERY_CACHE
|
|
|
|
if (thd->variables.query_cache_wlock_invalidate)
|
2004-07-16 00:15:55 +02:00
|
|
|
query_cache.invalidate_locked_for_write(first_table);
|
2004-03-04 17:32:55 +01:00
|
|
|
#endif /*HAVE_QUERY_CACHE*/
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->locked_tables=thd->lock;
|
|
|
|
thd->lock=0;
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2001-09-08 00:02:41 +02:00
|
|
|
else
|
|
|
|
thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->in_lock_tables=0;
|
|
|
|
break;
|
2004-12-09 10:10:45 +01:00
|
|
|
case SQLCOM_LOCK_TABLES_TRANSACTIONAL:
|
|
|
|
{
|
|
|
|
uint counter = 0;
|
|
|
|
|
|
|
|
if (check_db_used(thd, all_tables))
|
|
|
|
goto error;
|
|
|
|
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
thd->in_lock_tables=1;
|
|
|
|
thd->options|= OPTION_TABLE_LOCK;
|
|
|
|
|
|
|
|
if (open_tables(thd, all_tables, &counter) == 0 &&
|
|
|
|
transactional_lock_tables(thd, all_tables, counter) == 0)
|
|
|
|
send_ok(thd);
|
|
|
|
else
|
|
|
|
thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
|
|
|
|
|
|
|
|
thd->in_lock_tables=0;
|
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_CREATE_DB:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2003-12-30 12:14:21 +01:00
|
|
|
char *alias;
|
2004-03-05 19:13:33 +01:00
|
|
|
if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-01-25 14:07:51 +01:00
|
|
|
/*
|
|
|
|
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
|
|
|
|
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.
|
|
|
|
*/
|
2003-02-07 14:47:24 +01:00
|
|
|
#ifdef HAVE_REPLICATION
|
2003-01-25 14:07:51 +01:00
|
|
|
if (thd->slave_thread &&
|
|
|
|
(!db_ok(lex->name, replicate_do_db, replicate_ignore_db) ||
|
|
|
|
!db_ok_with_wild_table(lex->name)))
|
2003-07-08 15:50:57 +02:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
|
2003-01-25 14:07:51 +01:00
|
|
|
break;
|
2003-07-08 15:50:57 +02:00
|
|
|
}
|
2003-02-07 14:47:24 +01:00
|
|
|
#endif
|
2003-09-26 12:33:13 +02:00
|
|
|
if (check_access(thd,CREATE_ACL,lex->name,0,1,0))
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
2004-02-11 00:06:46 +01:00
|
|
|
res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name),
|
2004-02-16 09:03:25 +01:00
|
|
|
&lex->create_info, 0);
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_DROP_DB:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2003-12-30 12:14:21 +01:00
|
|
|
char *alias;
|
2004-03-05 19:13:33 +01:00
|
|
|
if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-01-25 14:07:51 +01:00
|
|
|
/*
|
|
|
|
If in a slave thread :
|
|
|
|
DROP DATABASE DB may not be preceded by USE DB.
|
|
|
|
For that reason, maybe 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.
|
|
|
|
*/
|
2003-02-07 14:47:24 +01:00
|
|
|
#ifdef HAVE_REPLICATION
|
2003-01-25 14:07:51 +01:00
|
|
|
if (thd->slave_thread &&
|
|
|
|
(!db_ok(lex->name, replicate_do_db, replicate_ignore_db) ||
|
|
|
|
!db_ok_with_wild_table(lex->name)))
|
2003-07-08 15:50:57 +02:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
|
2003-01-25 14:07:51 +01:00
|
|
|
break;
|
2003-07-08 15:50:57 +02:00
|
|
|
}
|
2003-02-07 14:47:24 +01:00
|
|
|
#endif
|
2003-09-26 12:33:13 +02:00
|
|
|
if (check_access(thd,DROP_ACL,lex->name,0,1,0))
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
2001-09-02 12:47:00 +02:00
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
|
|
|
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
2001-09-02 12:47:00 +02:00
|
|
|
goto error;
|
|
|
|
}
|
2004-06-17 10:53:17 +02:00
|
|
|
res=mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : lex->name),
|
|
|
|
lex->drop_if_exists, 0);
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-06-27 11:41:02 +02:00
|
|
|
case SQLCOM_ALTER_DB:
|
|
|
|
{
|
|
|
|
if (!strip_sp(lex->name) || check_db_name(lex->name))
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
|
2002-06-27 11:41:02 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-02-28 22:22:29 +01:00
|
|
|
/*
|
|
|
|
If in a slave thread :
|
|
|
|
ALTER DATABASE DB may not be preceded by USE DB.
|
|
|
|
For that reason, maybe 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 &&
|
|
|
|
(!db_ok(lex->name, replicate_do_db, replicate_ignore_db) ||
|
|
|
|
!db_ok_with_wild_table(lex->name)))
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
|
2004-02-28 22:22:29 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2003-09-26 12:33:13 +02:00
|
|
|
if (check_access(thd,ALTER_ACL,lex->name,0,1,0))
|
2002-06-27 11:41:02 +02:00
|
|
|
break;
|
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
|
|
|
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
2002-06-27 11:41:02 +02:00
|
|
|
goto error;
|
|
|
|
}
|
2002-10-02 12:33:08 +02:00
|
|
|
res=mysql_alter_db(thd,lex->name,&lex->create_info);
|
2002-06-27 11:41:02 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-07-02 11:31:54 +02:00
|
|
|
case SQLCOM_SHOW_CREATE_DB:
|
|
|
|
{
|
|
|
|
if (!strip_sp(lex->name) || check_db_name(lex->name))
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
|
2002-07-02 11:31:54 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-04-06 17:26:35 +02:00
|
|
|
if (check_access(thd,SELECT_ACL,lex->name,0,1,0))
|
2002-07-02 11:31:54 +02:00
|
|
|
break;
|
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
|
|
|
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
2002-07-02 11:31:54 +02:00
|
|
|
goto error;
|
|
|
|
}
|
2002-11-06 09:01:38 +01:00
|
|
|
res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
|
2002-07-02 11:31:54 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-07-07 10:29:39 +02:00
|
|
|
case SQLCOM_CREATE_FUNCTION: // UDF function
|
2003-12-21 11:48:03 +01:00
|
|
|
{
|
|
|
|
if (check_access(thd,INSERT_ACL,"mysql",0,1,0))
|
|
|
|
break;
|
2000-07-31 21:29:14 +02:00
|
|
|
#ifdef HAVE_DLOPEN
|
2004-11-23 19:19:09 +01:00
|
|
|
if (sp_find_function(thd, lex->spname))
|
2003-12-21 11:48:03 +01:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_UDF_EXISTS, MYF(0), lex->spname->m_name.str);
|
2003-12-21 11:48:03 +01:00
|
|
|
goto error;
|
|
|
|
}
|
2004-11-23 19:19:09 +01:00
|
|
|
if (!(res = mysql_create_function(thd, &lex->udf)))
|
2003-12-21 11:48:03 +01:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
#else
|
2004-10-20 03:04:37 +02:00
|
|
|
res= TRUE;
|
2000-07-31 21:29:14 +02:00
|
|
|
#endif
|
|
|
|
break;
|
2003-12-21 11:48:03 +01:00
|
|
|
}
|
2003-09-26 12:33:13 +02:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2004-11-25 21:55:49 +01:00
|
|
|
case SQLCOM_CREATE_USER:
|
|
|
|
{
|
|
|
|
if (check_access(thd, GRANT_ACL,"mysql",0,1,0))
|
|
|
|
break;
|
|
|
|
if (!(res= mysql_create_user(thd, lex->users_list)))
|
|
|
|
{
|
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
2004-12-06 16:15:54 +01:00
|
|
|
Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
|
2004-11-25 21:55:49 +01:00
|
|
|
mysql_bin_log.write(&qinfo);
|
|
|
|
}
|
|
|
|
send_ok(thd);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2003-06-06 14:43:23 +02:00
|
|
|
case SQLCOM_DROP_USER:
|
|
|
|
{
|
2003-09-26 12:33:13 +02:00
|
|
|
if (check_access(thd, GRANT_ACL,"mysql",0,1,0))
|
2003-06-06 14:43:23 +02:00
|
|
|
break;
|
|
|
|
if (!(res= mysql_drop_user(thd, lex->users_list)))
|
|
|
|
{
|
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
2004-12-06 10:38:56 +01:00
|
|
|
Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
|
2004-11-25 21:55:49 +01:00
|
|
|
mysql_bin_log.write(&qinfo);
|
|
|
|
}
|
|
|
|
send_ok(thd);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_RENAME_USER:
|
|
|
|
{
|
|
|
|
if (check_access(thd, GRANT_ACL,"mysql",0,1,0))
|
|
|
|
break;
|
|
|
|
if (!(res= mysql_rename_user(thd, lex->users_list)))
|
|
|
|
{
|
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
2004-12-06 10:38:56 +01:00
|
|
|
Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
|
2004-11-25 21:55:49 +01:00
|
|
|
mysql_bin_log.write(&qinfo);
|
2003-06-06 14:43:23 +02:00
|
|
|
}
|
|
|
|
send_ok(thd);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_REVOKE_ALL:
|
|
|
|
{
|
2003-09-26 12:33:13 +02:00
|
|
|
if (check_access(thd, GRANT_ACL ,"mysql",0,1,0))
|
2003-06-06 14:43:23 +02:00
|
|
|
break;
|
|
|
|
if (!(res = mysql_revoke_all(thd, lex->users_list)))
|
|
|
|
{
|
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
2004-12-03 12:13:51 +01:00
|
|
|
Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
|
2003-06-06 14:43:23 +02:00
|
|
|
mysql_bin_log.write(&qinfo);
|
|
|
|
}
|
|
|
|
send_ok(thd);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2001-08-14 19:33:49 +02:00
|
|
|
case SQLCOM_REVOKE:
|
|
|
|
case SQLCOM_GRANT:
|
|
|
|
{
|
|
|
|
if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
|
2004-07-16 00:15:55 +02:00
|
|
|
((first_table && first_table->db) ?
|
|
|
|
first_table->db : select_lex->db),
|
|
|
|
first_table ? &first_table->grant.privilege : 0,
|
|
|
|
first_table ? 0 : 1, 0))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error;
|
|
|
|
|
2003-11-20 09:55:48 +01:00
|
|
|
if (specialflag & SPECIAL_NO_RESOLVE)
|
|
|
|
{
|
|
|
|
LEX_USER *user;
|
|
|
|
List_iterator <LEX_USER> user_list(lex->users_list);
|
|
|
|
while ((user=user_list++))
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
if (first_table)
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
|
|
|
if (grant_option && check_grant(thd,
|
|
|
|
(lex->grant | lex->grant_tot_col |
|
|
|
|
GRANT_ACL),
|
2004-07-16 00:15:55 +02:00
|
|
|
all_tables, 0, UINT_MAX, 0))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error;
|
2004-07-16 00:15:55 +02:00
|
|
|
if (!(res = mysql_table_grant(thd, all_tables, lex->users_list,
|
|
|
|
lex->columns, lex->grant,
|
2003-04-02 00:15:20 +02:00
|
|
|
lex->sql_command == SQLCOM_REVOKE)) &&
|
|
|
|
mysql_bin_log.is_open())
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2003-12-19 18:03:27 +01:00
|
|
|
thd->clear_error();
|
2004-12-06 10:38:56 +01:00
|
|
|
Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
|
2003-04-02 00:15:20 +02:00
|
|
|
mysql_bin_log.write(&qinfo);
|
2001-08-14 19:33:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (lex->columns.elements)
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
|
|
|
|
MYF(0));
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2001-08-14 19:33:49 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
|
|
|
|
lex->sql_command == SQLCOM_REVOKE);
|
|
|
|
if (!res)
|
|
|
|
{
|
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
2003-12-16 11:10:50 +01:00
|
|
|
thd->clear_error();
|
2004-12-03 12:13:51 +01:00
|
|
|
Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
|
2001-08-14 19:33:49 +02:00
|
|
|
mysql_bin_log.write(&qinfo);
|
|
|
|
}
|
2002-06-20 15:46:25 +02:00
|
|
|
if (mqh_used && lex->sql_command == SQLCOM_GRANT)
|
2002-02-01 19:53:24 +01:00
|
|
|
{
|
2002-02-13 21:37:19 +01:00
|
|
|
List_iterator <LEX_USER> str_list(lex->users_list);
|
|
|
|
LEX_USER *user;
|
|
|
|
while ((user=str_list++))
|
2004-12-06 16:15:54 +01:00
|
|
|
reset_mqh(user);
|
2002-02-01 19:53:24 +01:00
|
|
|
}
|
2001-08-14 19:33:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2003-09-29 08:47:37 +02:00
|
|
|
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
|
2000-10-14 10:16:17 +02:00
|
|
|
case SQLCOM_RESET:
|
2003-05-15 18:35:39 +02:00
|
|
|
/*
|
|
|
|
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:
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, all_tables))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error;
|
2003-05-15 18:35:39 +02:00
|
|
|
/*
|
|
|
|
reload_acl_and_cache() will tell us if we are allowed to write to the
|
|
|
|
binlog or not.
|
|
|
|
*/
|
|
|
|
bool write_to_binlog;
|
2004-10-20 03:04:37 +02:00
|
|
|
if (!reload_acl_and_cache(thd, lex->type, first_table, &write_to_binlog))
|
2003-05-15 18:35:39 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
We WANT to write and we CAN write.
|
|
|
|
! we write after unlocking the table.
|
|
|
|
*/
|
|
|
|
if (!lex->no_write_to_binlog && write_to_binlog)
|
|
|
|
{
|
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
2004-12-03 12:13:51 +01:00
|
|
|
Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
|
2003-05-15 18:35:39 +02:00
|
|
|
mysql_bin_log.write(&qinfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
send_ok(thd);
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2003-05-15 18:35:39 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_KILL:
|
2004-11-25 16:13:06 +01:00
|
|
|
{
|
|
|
|
Item *it= (Item *)lex->value_list.head();
|
|
|
|
|
|
|
|
if (it->fix_fields(lex->thd, 0, &it) || it->check_cols(1))
|
|
|
|
{
|
|
|
|
my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
|
|
|
|
MYF(0));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
kill_one_thread(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2004-11-25 16:13:06 +01:00
|
|
|
}
|
2003-09-26 12:33:13 +02:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SHOW_GRANTS:
|
2001-08-14 19:33:49 +02:00
|
|
|
if ((thd->priv_user &&
|
|
|
|
!strcmp(thd->priv_user,lex->grant_user->user.str)) ||
|
2003-09-26 12:33:13 +02:00
|
|
|
!check_access(thd, SELECT_ACL, "mysql",0,1,0))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
res = mysql_show_grants(thd,lex->grant_user);
|
|
|
|
}
|
|
|
|
break;
|
2003-09-26 12:33:13 +02:00
|
|
|
#endif
|
2001-04-07 00:18:33 +02:00
|
|
|
case SQLCOM_HA_OPEN:
|
2004-07-16 00:15:55 +02:00
|
|
|
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))
|
2001-04-07 00:18:33 +02:00
|
|
|
goto error;
|
2004-07-16 00:15:55 +02:00
|
|
|
res= mysql_ha_open(thd, first_table);
|
2001-04-07 00:18:33 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_HA_CLOSE:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
|
|
|
if (check_db_used(thd, all_tables))
|
2001-04-07 00:18:33 +02:00
|
|
|
goto error;
|
2004-07-16 00:15:55 +02:00
|
|
|
res= mysql_ha_close(thd, first_table);
|
2001-04-07 00:18:33 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_HA_READ:
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ASSERT(first_table == all_tables && first_table != 0);
|
2003-07-03 10:55:36 +02:00
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_db_used(thd, all_tables))
|
2001-04-07 00:18:33 +02:00
|
|
|
goto error;
|
2004-07-16 00:15:55 +02:00
|
|
|
res= mysql_ha_read(thd, first_table, 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);
|
2001-04-07 00:18:33 +02:00
|
|
|
break;
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_BEGIN:
|
2001-09-08 00:01:10 +02:00
|
|
|
if (thd->locked_tables)
|
|
|
|
{
|
|
|
|
thd->lock=thd->locked_tables;
|
2004-10-07 00:45:06 +02:00
|
|
|
thd->locked_tables=0; // Will be automatically closed
|
2001-09-08 00:01:10 +02:00
|
|
|
close_thread_tables(thd); // Free tables
|
|
|
|
}
|
2000-11-29 04:09:28 +01:00
|
|
|
if (end_active_trans(thd))
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2000-11-29 04:09:28 +01:00
|
|
|
else
|
|
|
|
{
|
2000-12-12 03:34:56 +01:00
|
|
|
thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
|
2000-11-29 04:09:28 +01:00
|
|
|
OPTION_BEGIN);
|
|
|
|
thd->server_status|= SERVER_STATUS_IN_TRANS;
|
2004-11-10 17:56:45 +01:00
|
|
|
if (!(lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) ||
|
|
|
|
!(res= ha_start_consistent_snapshot(thd)))
|
|
|
|
send_ok(thd);
|
2000-11-29 04:09:28 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_COMMIT:
|
2000-11-13 22:55:10 +01:00
|
|
|
/*
|
|
|
|
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...)
|
|
|
|
*/
|
2001-12-02 13:34:01 +01:00
|
|
|
{
|
2000-11-24 00:51:18 +01:00
|
|
|
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
|
|
|
if (!ha_commit(thd))
|
2001-12-02 13:34:01 +01:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2001-12-02 13:34:01 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
else
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2001-12-02 13:34:01 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_ROLLBACK:
|
|
|
|
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
|
|
|
|
if (!ha_rollback(thd))
|
2000-11-24 00:51:18 +01:00
|
|
|
{
|
2 minor edits, plus
fix for BUG#1113 "INSERT into non-trans table SELECT ; ROLLBACK" does not send warning"
and
fix for BUG#873 "In transaction, INSERT to non-trans table is written too early to binlog".
Now we don't always write the non-trans update immediately to the binlog;
if there is something in the binlog cache we write it to the binlog cache
(because the non-trans update could depend on a trans table which was modified
earlier in the transaction); then in case of ROLLBACK, we write the binlog
cache to the binlog, wrapped with BEGIN/ROLLBACK.
This guarantees that the slave does the same updates.
For ROLLBACK TO SAVEPOINT: when we execute a SAVEPOINT command we write it
to the binlog cache. At ROLLBACK TO SAVEPOINT, if some non-trans table was updated,
we write ROLLBACK TO SAVEPOINT to the binlog cache; when the transaction
terminates (COMMIT/ROLLBACK), the binlog cache will be flushed to the binlog
(because of the non-trans update) so we'll have SAVEPOINT and ROLLBACK TO
SAVEPOINT in the binlog.
Apart from this rare case of updates of mixed table types in transaction, the
usual way is still clear the binlog cache at ROLLBACK, or chop it at
ROLLBACK TO SAVEPOINT (meaning the SAVEPOINT command is also chopped, which
is fine).
Note that BUG#873 encompasses subbugs 1) and 2) of BUG#333 "3 binlogging bugs when doing INSERT with mixed InnoDB/MyISAM".
2003-08-22 15:39:24 +02:00
|
|
|
/*
|
|
|
|
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 ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread)
|
2004-11-24 17:22:29 +01:00
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
|
|
ER_WARNING_NOT_COMPLETE_ROLLBACK,
|
|
|
|
ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
|
|
|
|
send_ok(thd);
|
2000-11-24 00:51:18 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
else
|
2004-10-20 03:04:37 +02:00
|
|
|
res= TRUE;
|
2000-11-24 00:51:18 +01:00
|
|
|
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2003-06-15 00:04:28 +02:00
|
|
|
case SQLCOM_ROLLBACK_TO_SAVEPOINT:
|
|
|
|
if (!ha_rollback_to_savepoint(thd, lex->savepoint_name))
|
|
|
|
{
|
2 minor edits, plus
fix for BUG#1113 "INSERT into non-trans table SELECT ; ROLLBACK" does not send warning"
and
fix for BUG#873 "In transaction, INSERT to non-trans table is written too early to binlog".
Now we don't always write the non-trans update immediately to the binlog;
if there is something in the binlog cache we write it to the binlog cache
(because the non-trans update could depend on a trans table which was modified
earlier in the transaction); then in case of ROLLBACK, we write the binlog
cache to the binlog, wrapped with BEGIN/ROLLBACK.
This guarantees that the slave does the same updates.
For ROLLBACK TO SAVEPOINT: when we execute a SAVEPOINT command we write it
to the binlog cache. At ROLLBACK TO SAVEPOINT, if some non-trans table was updated,
we write ROLLBACK TO SAVEPOINT to the binlog cache; when the transaction
terminates (COMMIT/ROLLBACK), the binlog cache will be flushed to the binlog
(because of the non-trans update) so we'll have SAVEPOINT and ROLLBACK TO
SAVEPOINT in the binlog.
Apart from this rare case of updates of mixed table types in transaction, the
usual way is still clear the binlog cache at ROLLBACK, or chop it at
ROLLBACK TO SAVEPOINT (meaning the SAVEPOINT command is also chopped, which
is fine).
Note that BUG#873 encompasses subbugs 1) and 2) of BUG#333 "3 binlogging bugs when doing INSERT with mixed InnoDB/MyISAM".
2003-08-22 15:39:24 +02:00
|
|
|
if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread)
|
2004-11-24 17:22:29 +01:00
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
|
|
ER_WARNING_NOT_COMPLETE_ROLLBACK,
|
|
|
|
ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
|
|
|
|
send_ok(thd);
|
2003-06-15 00:04:28 +02:00
|
|
|
}
|
|
|
|
else
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2003-06-15 00:04:28 +02:00
|
|
|
break;
|
2003-06-06 03:18:58 +02:00
|
|
|
case SQLCOM_SAVEPOINT:
|
2003-06-15 00:04:28 +02:00
|
|
|
if (!ha_savepoint(thd, lex->savepoint_name))
|
2003-08-18 23:08:08 +02:00
|
|
|
send_ok(thd);
|
2003-06-15 00:04:28 +02:00
|
|
|
else
|
2004-10-20 03:04:37 +02:00
|
|
|
goto error;
|
2003-06-06 03:18:58 +02:00
|
|
|
break;
|
2003-03-05 19:45:17 +01:00
|
|
|
case SQLCOM_CREATE_PROCEDURE:
|
|
|
|
case SQLCOM_CREATE_SPFUNCTION:
|
2003-12-21 11:48:03 +01:00
|
|
|
{
|
2004-10-07 15:32:36 +02:00
|
|
|
uint namelen;
|
|
|
|
char *name;
|
2004-10-20 03:04:37 +02:00
|
|
|
int result;
|
2004-10-07 15:32:36 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_ASSERT(lex->sphead);
|
2004-10-07 15:32:36 +02:00
|
|
|
|
|
|
|
if (! lex->sphead->m_db.str)
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
|
2004-10-07 15:32:36 +02:00
|
|
|
delete lex->sphead;
|
|
|
|
lex->sphead= 0;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
name= lex->sphead->name(&namelen);
|
2003-02-26 19:22:29 +01:00
|
|
|
#ifdef HAVE_DLOPEN
|
2003-12-21 11:48:03 +01:00
|
|
|
if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
|
|
|
|
{
|
|
|
|
udf_func *udf = find_udf(name, namelen);
|
2003-04-03 20:00:52 +02:00
|
|
|
|
2003-12-21 11:48:03 +01:00
|
|
|
if (udf)
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_UDF_EXISTS, MYF(0), name);
|
2003-12-15 13:24:16 +01:00
|
|
|
delete lex->sphead;
|
2004-10-07 15:32:36 +02:00
|
|
|
lex->sphead= 0;
|
2003-02-21 17:37:05 +01:00
|
|
|
goto error;
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
2003-12-21 11:48:03 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (lex->sphead->m_type == TYPE_ENUM_FUNCTION &&
|
|
|
|
!lex->sphead->m_has_return)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_NORETURN, MYF(0), name);
|
2003-12-21 11:48:03 +01:00
|
|
|
delete lex->sphead;
|
2004-10-07 15:32:36 +02:00
|
|
|
lex->sphead= 0;
|
2003-12-21 11:48:03 +01:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
res= (result= lex->sphead->create(thd));
|
|
|
|
switch (result) {
|
2003-12-21 11:48:03 +01:00
|
|
|
case SP_OK:
|
|
|
|
send_ok(thd);
|
2004-08-24 12:59:06 +02:00
|
|
|
lex->unit.cleanup();
|
2003-12-21 11:48:03 +01:00
|
|
|
delete lex->sphead;
|
|
|
|
lex->sphead= 0;
|
2003-04-23 09:22:54 +02:00
|
|
|
break;
|
2003-12-21 11:48:03 +01:00
|
|
|
case SP_WRITE_ROW_FAILED:
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name);
|
2004-08-24 12:59:06 +02:00
|
|
|
lex->unit.cleanup();
|
2003-12-21 11:48:03 +01:00
|
|
|
delete lex->sphead;
|
|
|
|
lex->sphead= 0;
|
|
|
|
goto error;
|
2004-06-15 15:42:28 +02:00
|
|
|
case SP_NO_DB_ERROR:
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_BAD_DB_ERROR, MYF(0), lex->sphead->m_db.str);
|
2004-08-24 12:59:06 +02:00
|
|
|
lex->unit.cleanup();
|
2004-06-15 15:42:28 +02:00
|
|
|
delete lex->sphead;
|
|
|
|
lex->sphead= 0;
|
|
|
|
goto error;
|
2003-12-21 11:48:03 +01:00
|
|
|
default:
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name);
|
2004-08-24 12:59:06 +02:00
|
|
|
lex->unit.cleanup();
|
2003-12-21 11:48:03 +01:00
|
|
|
delete lex->sphead;
|
|
|
|
lex->sphead= 0;
|
|
|
|
goto error;
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
2003-12-21 11:48:03 +01:00
|
|
|
break;
|
|
|
|
}
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
case SQLCOM_CALL:
|
|
|
|
{
|
|
|
|
sp_head *sp;
|
|
|
|
|
2004-02-17 17:36:53 +01:00
|
|
|
if (!(sp= sp_find_procedure(thd, lex->spname)))
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
|
2004-10-20 03:04:37 +02:00
|
|
|
lex->spname->m_qname.str);
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-12-16 14:15:27 +01:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2003-12-13 16:40:52 +01:00
|
|
|
st_sp_security_context save_ctx;
|
2003-12-16 14:15:27 +01:00
|
|
|
#endif
|
2004-05-14 16:00:57 +02:00
|
|
|
ha_rows select_limit;
|
2003-04-23 09:22:54 +02:00
|
|
|
uint smrx;
|
|
|
|
LINT_INIT(smrx);
|
|
|
|
|
2004-07-07 10:29:39 +02:00
|
|
|
/* In case the arguments are subselects... */
|
2004-07-16 00:15:55 +02:00
|
|
|
if (all_tables &&
|
2004-10-20 03:04:37 +02:00
|
|
|
(check_table_access(thd, SELECT_ACL, all_tables, 0) ||
|
|
|
|
open_and_lock_tables(thd, all_tables)))
|
|
|
|
goto error;
|
2003-04-23 21:31:47 +02:00
|
|
|
|
2003-02-18 19:58:03 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2003-01-15 15:39:36 +01:00
|
|
|
my_bool nsok= thd->net.no_send_ok;
|
|
|
|
thd->net.no_send_ok= TRUE;
|
2003-02-18 19:58:03 +01:00
|
|
|
#endif
|
2003-08-27 17:04:33 +02:00
|
|
|
if (sp->m_multi_results)
|
2003-04-23 09:22:54 +02:00
|
|
|
{
|
2003-08-27 17:04:33 +02:00
|
|
|
if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS))
|
2003-04-23 09:22:54 +02:00
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_SP_BADSELECT, ER(ER_SP_BADSELECT), MYF(0));
|
2003-04-23 09:22:54 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
|
|
|
thd->net.no_send_ok= nsok;
|
|
|
|
#endif
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
smrx= thd->server_status & SERVER_MORE_RESULTS_EXISTS;
|
|
|
|
thd->server_status |= SERVER_MORE_RESULTS_EXISTS;
|
|
|
|
}
|
|
|
|
|
2003-12-16 14:15:27 +01:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2003-12-13 16:40:52 +01:00
|
|
|
sp_change_security_context(thd, sp, &save_ctx);
|
2003-12-16 14:15:27 +01:00
|
|
|
#endif
|
2004-05-14 16:00:57 +02:00
|
|
|
select_limit= thd->variables.select_limit;
|
|
|
|
thd->variables.select_limit= HA_POS_ERROR;
|
2003-12-13 16:40:52 +01:00
|
|
|
|
2004-09-29 18:55:05 +02:00
|
|
|
thd->row_count_func= 0;
|
2003-02-26 19:22:29 +01:00
|
|
|
res= sp->execute_procedure(thd, &lex->value_list);
|
2003-04-23 09:22:54 +02:00
|
|
|
|
2004-05-14 16:00:57 +02:00
|
|
|
thd->variables.select_limit= select_limit;
|
2003-12-16 14:15:27 +01:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2003-12-13 16:40:52 +01:00
|
|
|
sp_restore_security_context(thd, sp, &save_ctx);
|
2003-12-16 14:15:27 +01:00
|
|
|
#endif
|
2003-12-13 16:40:52 +01:00
|
|
|
|
2003-02-18 19:58:03 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2003-01-15 15:39:36 +01:00
|
|
|
thd->net.no_send_ok= nsok;
|
2003-02-18 19:58:03 +01:00
|
|
|
#endif
|
2003-08-27 17:04:33 +02:00
|
|
|
if (sp->m_multi_results)
|
2003-04-23 09:22:54 +02:00
|
|
|
{
|
|
|
|
if (! smrx)
|
|
|
|
thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
|
|
|
|
}
|
2003-01-15 15:39:36 +01:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
if (!res)
|
2004-07-16 08:47:17 +02:00
|
|
|
send_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 : thd->row_count_func));
|
2003-01-15 15:39:36 +01:00
|
|
|
else
|
|
|
|
goto error; // Substatement should already have sent error
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
2003-04-23 09:22:54 +02:00
|
|
|
break;
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
|
|
|
case SQLCOM_ALTER_PROCEDURE:
|
2003-02-21 17:37:05 +01:00
|
|
|
case SQLCOM_ALTER_FUNCTION:
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
{
|
2004-10-20 03:04:37 +02:00
|
|
|
int result;
|
2004-10-22 20:29:06 +02:00
|
|
|
sp_head *sp;
|
|
|
|
st_sp_chistics chistics;
|
|
|
|
|
|
|
|
memcpy(&chistics, &lex->sp_chistics, sizeof(chistics));
|
2003-11-17 18:21:36 +01:00
|
|
|
if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
|
2004-10-22 20:29:06 +02:00
|
|
|
sp= sp_find_procedure(thd, lex->spname);
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
else
|
2004-10-22 20:29:06 +02:00
|
|
|
sp= sp_find_function(thd, lex->spname);
|
|
|
|
mysql_reset_errors(thd);
|
|
|
|
if (! sp)
|
2004-10-28 10:02:48 +02:00
|
|
|
result= SP_KEY_NOT_FOUND;
|
2004-10-22 20:29:06 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (check_sp_definer_access(thd, sp))
|
2004-10-28 10:02:48 +02:00
|
|
|
goto error;
|
2004-10-22 20:29:06 +02:00
|
|
|
memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics));
|
|
|
|
if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
|
2004-10-28 10:02:48 +02:00
|
|
|
result= sp_update_procedure(thd, lex->spname, &lex->sp_chistics);
|
2004-10-22 20:29:06 +02:00
|
|
|
else
|
2004-10-28 10:02:48 +02:00
|
|
|
result= sp_update_function(thd, lex->spname, &lex->sp_chistics);
|
2004-10-22 20:29:06 +02:00
|
|
|
}
|
2004-10-20 03:04:37 +02:00
|
|
|
switch (result)
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
{
|
2003-11-17 18:21:36 +01:00
|
|
|
case SP_OK:
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
send_ok(thd);
|
2003-11-17 18:21:36 +01:00
|
|
|
break;
|
|
|
|
case SP_KEY_NOT_FOUND:
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
|
|
|
|
SP_COM_STRING(lex), lex->spname->m_qname.str);
|
2003-11-17 18:21:36 +01:00
|
|
|
goto error;
|
|
|
|
default:
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_CANT_ALTER, MYF(0),
|
|
|
|
SP_COM_STRING(lex), lex->spname->m_qname.str);
|
2003-11-17 18:21:36 +01:00
|
|
|
goto error;
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
2003-04-23 09:22:54 +02:00
|
|
|
break;
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
|
|
|
case SQLCOM_DROP_PROCEDURE:
|
2003-02-21 17:37:05 +01:00
|
|
|
case SQLCOM_DROP_FUNCTION:
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
{
|
2004-10-22 20:29:06 +02:00
|
|
|
sp_head *sp;
|
2004-10-20 03:04:37 +02:00
|
|
|
int result;
|
2004-10-22 20:29:06 +02:00
|
|
|
|
2003-02-21 17:37:05 +01:00
|
|
|
if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
|
2004-10-22 20:29:06 +02:00
|
|
|
sp= sp_find_procedure(thd, lex->spname);
|
|
|
|
else
|
|
|
|
sp= sp_find_function(thd, lex->spname);
|
|
|
|
mysql_reset_errors(thd);
|
2004-11-23 19:19:09 +01:00
|
|
|
if (sp)
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
{
|
2004-10-22 20:29:06 +02:00
|
|
|
if (check_sp_definer_access(thd, sp))
|
2004-10-28 10:02:48 +02:00
|
|
|
goto error;
|
2004-10-22 20:29:06 +02:00
|
|
|
if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
|
2004-10-28 10:02:48 +02:00
|
|
|
result= sp_drop_procedure(thd, lex->spname);
|
2004-10-22 20:29:06 +02:00
|
|
|
else
|
2004-10-28 10:02:48 +02:00
|
|
|
result= sp_drop_function(thd, lex->spname);
|
2004-11-23 19:19:09 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-10-22 20:29:06 +02:00
|
|
|
#ifdef HAVE_DLOPEN
|
2004-11-23 19:19:09 +01:00
|
|
|
if (lex->sql_command == SQLCOM_DROP_FUNCTION)
|
|
|
|
{
|
|
|
|
udf_func *udf = find_udf(lex->spname->m_name.str,
|
|
|
|
lex->spname->m_name.length);
|
|
|
|
if (udf)
|
|
|
|
{
|
|
|
|
if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0))
|
|
|
|
goto error;
|
|
|
|
if (!(res = mysql_drop_function(thd, &lex->spname->m_name)))
|
2003-02-21 17:37:05 +01:00
|
|
|
{
|
2004-11-23 19:19:09 +01:00
|
|
|
send_ok(thd);
|
|
|
|
break;
|
2003-02-21 17:37:05 +01:00
|
|
|
}
|
|
|
|
}
|
2004-10-22 20:29:06 +02:00
|
|
|
}
|
2004-11-23 19:19:09 +01:00
|
|
|
#endif
|
|
|
|
result= SP_KEY_NOT_FOUND;
|
2003-02-21 17:37:05 +01:00
|
|
|
}
|
2004-10-20 03:04:37 +02:00
|
|
|
res= result;
|
|
|
|
switch (result)
|
2003-02-21 17:37:05 +01:00
|
|
|
{
|
|
|
|
case SP_OK:
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
send_ok(thd);
|
2003-02-21 17:37:05 +01:00
|
|
|
break;
|
|
|
|
case SP_KEY_NOT_FOUND:
|
2003-03-26 12:29:58 +01:00
|
|
|
if (lex->drop_if_exists)
|
|
|
|
{
|
2004-09-09 05:59:26 +02:00
|
|
|
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
2003-03-26 12:29:58 +01:00
|
|
|
ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
|
2004-02-17 17:36:53 +01:00
|
|
|
SP_COM_STRING(lex), lex->spname->m_name.str);
|
2004-10-20 03:04:37 +02:00
|
|
|
res= FALSE;
|
2003-03-26 12:29:58 +01:00
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
}
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
|
|
|
|
SP_COM_STRING(lex), lex->spname->m_qname.str);
|
2003-02-21 17:37:05 +01:00
|
|
|
goto error;
|
|
|
|
default:
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_DROP_FAILED, MYF(0),
|
|
|
|
SP_COM_STRING(lex), lex->spname->m_qname.str);
|
2003-02-21 17:37:05 +01:00
|
|
|
goto error;
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
2003-04-23 09:22:54 +02:00
|
|
|
break;
|
Simplistic, experimental framework for Stored Procedures (SPs).
Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
2002-12-08 19:59:22 +01:00
|
|
|
}
|
2003-11-17 18:21:36 +01:00
|
|
|
case SQLCOM_SHOW_CREATE_PROC:
|
|
|
|
{
|
2004-02-17 17:36:53 +01:00
|
|
|
if (lex->spname->m_name.length > NAME_LEN)
|
2003-11-17 18:21:36 +01:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
|
2003-11-17 18:21:36 +01:00
|
|
|
goto error;
|
|
|
|
}
|
2004-10-20 03:04:37 +02:00
|
|
|
if (sp_show_create_procedure(thd, lex->spname) != SP_OK)
|
2003-11-20 15:07:22 +01:00
|
|
|
{ /* We don't distinguish between errors for now */
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
|
|
|
|
SP_COM_STRING(lex), lex->spname->m_name.str);
|
2003-11-17 18:21:36 +01:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_SHOW_CREATE_FUNC:
|
|
|
|
{
|
2004-02-17 17:36:53 +01:00
|
|
|
if (lex->spname->m_name.length > NAME_LEN)
|
2003-11-17 18:21:36 +01:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
|
2003-11-17 18:21:36 +01:00
|
|
|
goto error;
|
|
|
|
}
|
2004-10-20 03:04:37 +02:00
|
|
|
if (sp_show_create_function(thd, lex->spname) != SP_OK)
|
2003-11-20 15:07:22 +01:00
|
|
|
{ /* We don't distinguish between errors for now */
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
|
|
|
|
SP_COM_STRING(lex), lex->spname->m_name.str);
|
2003-11-17 18:21:36 +01:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_SHOW_STATUS_PROC:
|
|
|
|
{
|
2003-11-20 15:07:22 +01:00
|
|
|
res= sp_show_status_procedure(thd, (lex->wild ?
|
2003-11-17 18:21:36 +01:00
|
|
|
lex->wild->ptr() : NullS));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_SHOW_STATUS_FUNC:
|
|
|
|
{
|
2003-11-20 15:07:22 +01:00
|
|
|
res= sp_show_status_function(thd, (lex->wild ?
|
2003-11-17 18:21:36 +01:00
|
|
|
lex->wild->ptr() : NullS));
|
|
|
|
break;
|
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
case SQLCOM_CREATE_VIEW:
|
|
|
|
{
|
|
|
|
res= mysql_create_view(thd, thd->lex->create_view_mode);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_DROP_VIEW:
|
|
|
|
{
|
2004-10-20 03:04:37 +02:00
|
|
|
if (check_table_access(thd, DROP_ACL, all_tables, 0) ||
|
|
|
|
end_active_trans(thd))
|
|
|
|
goto error;
|
2004-07-16 00:15:55 +02:00
|
|
|
res= mysql_drop_view(thd, first_table, thd->lex->drop_mode);
|
|
|
|
break;
|
|
|
|
}
|
2004-09-07 14:29:46 +02:00
|
|
|
case SQLCOM_CREATE_TRIGGER:
|
|
|
|
{
|
2004-11-24 10:24:02 +01:00
|
|
|
res= mysql_create_or_drop_trigger(thd, all_tables, 1);
|
|
|
|
|
|
|
|
/* We don't care about trigger body after this point */
|
2004-09-07 14:29:46 +02:00
|
|
|
delete lex->sphead;
|
|
|
|
lex->sphead= 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_DROP_TRIGGER:
|
|
|
|
{
|
|
|
|
res= mysql_create_or_drop_trigger(thd, all_tables, 0);
|
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
default: /* Impossible */
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-07-07 10:29:39 +02:00
|
|
|
thd->proc_info="query end";
|
2004-06-03 23:17:18 +02:00
|
|
|
if (thd->one_shot_set)
|
2004-07-07 10:29:39 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
If this is a SET, do nothing. This is to allow mysqlbinlog to print
|
|
|
|
many SET commands (in this case we want the charset temp setting to
|
|
|
|
live until the real query). This is also needed so that SET
|
|
|
|
CHARACTER_SET_CLIENT... does not cancel itself immediately.
|
|
|
|
*/
|
|
|
|
if (lex->sql_command != SQLCOM_SET_OPTION)
|
2004-06-03 23:17:18 +02:00
|
|
|
{
|
2004-07-07 10:29:39 +02:00
|
|
|
thd->variables.character_set_client=
|
|
|
|
global_system_variables.character_set_client;
|
|
|
|
thd->variables.collation_connection=
|
|
|
|
global_system_variables.collation_connection;
|
|
|
|
thd->variables.collation_database=
|
|
|
|
global_system_variables.collation_database;
|
|
|
|
thd->variables.collation_server=
|
|
|
|
global_system_variables.collation_server;
|
|
|
|
thd->update_charset();
|
|
|
|
thd->variables.time_zone=
|
|
|
|
global_system_variables.time_zone;
|
|
|
|
thd->one_shot_set= 0;
|
2004-06-03 23:17:18 +02:00
|
|
|
}
|
2004-07-07 10:29:39 +02:00
|
|
|
}
|
2003-01-15 15:39:36 +01:00
|
|
|
|
2004-05-04 17:18:47 +02:00
|
|
|
/*
|
|
|
|
The return value for ROW_COUNT() is "implementation dependent" if
|
|
|
|
the statement is not DELETE, INSERT or UPDATE (or a CALL executing
|
|
|
|
such a statement), but -1 is what JDBC and ODBC wants.
|
|
|
|
*/
|
|
|
|
switch (lex->sql_command) {
|
|
|
|
case SQLCOM_UPDATE:
|
|
|
|
case SQLCOM_UPDATE_MULTI:
|
|
|
|
case SQLCOM_REPLACE:
|
|
|
|
case SQLCOM_INSERT:
|
|
|
|
case SQLCOM_REPLACE_SELECT:
|
|
|
|
case SQLCOM_INSERT_SELECT:
|
|
|
|
case SQLCOM_DELETE:
|
|
|
|
case SQLCOM_DELETE_MULTI:
|
|
|
|
case SQLCOM_CALL:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
thd->row_count_func= -1;
|
|
|
|
}
|
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(res || thd->net.report_error);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
error:
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-05-28 01:06:56 +02:00
|
|
|
/*
|
|
|
|
Check grants for commands which work only with one table and all other
|
|
|
|
tables belong to subselects.
|
|
|
|
|
2003-07-01 21:40:59 +02:00
|
|
|
SYNOPSIS
|
2004-02-11 00:06:46 +01:00
|
|
|
check_one_table_access()
|
|
|
|
thd Thread handler
|
2004-10-07 00:45:06 +02:00
|
|
|
privilege requested privilege
|
2004-07-16 00:15:55 +02:00
|
|
|
all_tables global table list of query
|
2003-05-28 01:06:56 +02:00
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 - OK
|
2004-02-11 00:06:46 +01:00
|
|
|
1 - access denied, error is sent to client
|
2003-05-28 01:06:56 +02:00
|
|
|
*/
|
|
|
|
|
2004-09-03 20:43:04 +02:00
|
|
|
bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
|
2003-05-28 01:06:56 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
if (check_access(thd, privilege, all_tables->db,
|
|
|
|
&all_tables->grant.privilege, 0, 0))
|
2003-12-19 15:25:50 +01:00
|
|
|
return 1;
|
2003-05-28 01:06:56 +02:00
|
|
|
|
2004-04-12 02:26:32 +02:00
|
|
|
/* Show only 1 table for check_grant */
|
2004-07-16 00:15:55 +02:00
|
|
|
if (grant_option && check_grant(thd, privilege, all_tables, 0, 1, 0))
|
2003-12-19 15:25:50 +01:00
|
|
|
return 1;
|
2003-05-28 01:06:56 +02:00
|
|
|
|
2004-04-12 02:26:32 +02:00
|
|
|
/* Check rights on tables of subselect (if exists) */
|
2004-04-10 00:14:32 +02:00
|
|
|
TABLE_LIST *subselects_tables;
|
2004-07-16 00:15:55 +02:00
|
|
|
if ((subselects_tables= all_tables->next_global))
|
2003-12-19 15:25:50 +01:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
if ((check_table_access(thd, SELECT_ACL, subselects_tables, 0)))
|
2003-12-19 15:25:50 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
2003-05-28 01:06:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/****************************************************************************
|
2002-06-12 14:04:18 +02:00
|
|
|
Get the user (global) and database privileges for all used tables
|
2003-04-28 09:32:56 +02:00
|
|
|
|
|
|
|
NOTES
|
|
|
|
The idea of EXTRA_ACL is that one will be granted access to the table if
|
|
|
|
one has the asked privilege on any column combination of the table; For
|
|
|
|
example to be able to check a table one needs to have SELECT privilege on
|
|
|
|
any column of the table.
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
1 If we can't get the privileges and we don't use table/column grants.
|
|
|
|
|
|
|
|
save_priv In this we store global and db level grants for the table
|
|
|
|
Note that we don't store db level grants if the global grants
|
2003-05-07 22:15:46 +02:00
|
|
|
is enough to satisfy the request and the global grants contains
|
|
|
|
a SELECT grant.
|
2000-07-31 21:29:14 +02:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
bool
|
2002-06-12 14:04:18 +02:00
|
|
|
check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
|
2001-12-22 14:13:31 +01:00
|
|
|
bool dont_check_global_grants, bool no_errors)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-04-05 12:56:05 +02:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
|
|
|
ulong db_access;
|
|
|
|
#endif
|
|
|
|
ulong dummy;
|
2004-09-03 20:43:04 +02:00
|
|
|
DBUG_ENTER("check_access");
|
|
|
|
DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu",
|
|
|
|
db ? db : "", want_access, thd->master_access));
|
2000-07-31 21:29:14 +02:00
|
|
|
if (save_priv)
|
|
|
|
*save_priv=0;
|
|
|
|
else
|
|
|
|
save_priv= &dummy;
|
|
|
|
|
2001-09-18 05:05:55 +02:00
|
|
|
if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-09-03 20:43:04 +02:00
|
|
|
DBUG_PRINT("error",("No database"));
|
2001-12-22 14:13:31 +01:00
|
|
|
if (!no_errors)
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR),
|
|
|
|
MYF(0)); /* purecov: tested */
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_RETURN(TRUE); /* purecov: tested */
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2003-12-19 15:25:50 +01:00
|
|
|
#ifdef NO_EMBEDDED_ACCESS_CHECKS
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
#else
|
2000-07-31 21:29:14 +02:00
|
|
|
if ((thd->master_access & want_access) == want_access)
|
|
|
|
{
|
2003-05-07 22:59:24 +02:00
|
|
|
/*
|
|
|
|
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 && (!thd->db || strcmp(db,thd->db))))
|
2003-10-24 23:27:21 +02:00
|
|
|
db_access=acl_get(thd->host, thd->ip,
|
2003-07-22 22:21:23 +02:00
|
|
|
thd->priv_user, db, test(want_access & GRANT_ACL));
|
2003-05-07 22:59:24 +02:00
|
|
|
*save_priv=thd->master_access | db_access;
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2002-06-09 08:14:25 +02:00
|
|
|
if (((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
|
2001-01-14 11:25:30 +01:00
|
|
|
! db && dont_check_global_grants)
|
2000-07-31 21:29:14 +02:00
|
|
|
{ // We can never grant this
|
2004-09-03 20:43:04 +02:00
|
|
|
DBUG_PRINT("error",("No possible access"));
|
2001-12-22 14:13:31 +01:00
|
|
|
if (!no_errors)
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
|
|
|
|
thd->priv_user,
|
|
|
|
thd->priv_host,
|
|
|
|
(thd->password ?
|
|
|
|
ER(ER_YES) :
|
|
|
|
ER(ER_NO))); /* purecov: tested */
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_RETURN(TRUE); /* purecov: tested */
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (db == any_db)
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_RETURN(FALSE); // Allow select on anything
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
if (db && (!thd->db || strcmp(db,thd->db)))
|
2003-10-24 23:27:21 +02:00
|
|
|
db_access=acl_get(thd->host, thd->ip,
|
2003-07-22 22:21:23 +02:00
|
|
|
thd->priv_user, db, test(want_access & GRANT_ACL));
|
2000-07-31 21:29:14 +02:00
|
|
|
else
|
|
|
|
db_access=thd->db_access;
|
2004-10-26 18:30:01 +02:00
|
|
|
DBUG_PRINT("info",("db_access: %lu", db_access));
|
2004-07-07 10:29:39 +02:00
|
|
|
/* Remove SHOW attribute and access rights we already have */
|
2002-06-09 08:14:25 +02:00
|
|
|
want_access &= ~(thd->master_access | EXTRA_ACL);
|
2004-09-03 20:43:04 +02:00
|
|
|
DBUG_PRINT("info",("db_access: %lu want_access: %lu",
|
|
|
|
db_access, want_access));
|
2000-07-31 21:29:14 +02:00
|
|
|
db_access= ((*save_priv=(db_access | thd->master_access)) & want_access);
|
2001-01-14 11:25:30 +01:00
|
|
|
|
|
|
|
/* grant_option is set if there exists a single table or column grant */
|
2000-07-31 21:29:14 +02:00
|
|
|
if (db_access == want_access ||
|
2004-09-03 20:43:04 +02:00
|
|
|
(grant_option && !dont_check_global_grants &&
|
2003-10-14 13:10:41 +02:00
|
|
|
!(want_access & ~(db_access | TABLE_ACLS))))
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_RETURN(FALSE); /* Ok */
|
2004-09-03 20:43:04 +02:00
|
|
|
|
|
|
|
DBUG_PRINT("error",("Access denied"));
|
2001-12-22 14:13:31 +01:00
|
|
|
if (!no_errors)
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
|
|
|
|
thd->priv_user,
|
|
|
|
thd->priv_host,
|
|
|
|
(db ? db : (thd->db ?
|
|
|
|
thd->db :
|
|
|
|
"unknown"))); /* purecov: tested */
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_RETURN(TRUE); /* purecov: tested */
|
2003-12-19 15:25:50 +01:00
|
|
|
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-05-14 00:55:23 +02:00
|
|
|
/*
|
|
|
|
check for global access and give descriptive error message if it fails
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
check_global_access()
|
|
|
|
thd Thread handler
|
|
|
|
want_access Use should have any of these global rights
|
|
|
|
|
|
|
|
WARNING
|
2004-10-07 00:45:06 +02:00
|
|
|
One gets access right if one has ANY of the rights in want_access
|
2003-05-14 00:55:23 +02:00
|
|
|
This is useful as one in most cases only need one global right,
|
|
|
|
but in some case we want to check if the user has SUPER or
|
|
|
|
REPL_CLIENT_ACL rights.
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
1 Access denied. In this case an error is sent to the client
|
|
|
|
*/
|
2002-06-12 14:04:18 +02:00
|
|
|
|
|
|
|
bool check_global_access(THD *thd, ulong want_access)
|
2001-03-21 00:02:22 +01:00
|
|
|
{
|
2003-12-19 15:25:50 +01:00
|
|
|
#ifdef NO_EMBEDDED_ACCESS_CHECKS
|
|
|
|
return 0;
|
|
|
|
#else
|
2002-06-12 14:04:18 +02:00
|
|
|
char command[128];
|
2003-05-14 00:55:23 +02:00
|
|
|
if ((thd->master_access & want_access))
|
2002-06-12 14:04:18 +02:00
|
|
|
return 0;
|
|
|
|
get_privilege_desc(command, sizeof(command), want_access);
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
|
2002-06-12 14:04:18 +02:00
|
|
|
return 1;
|
2003-12-19 15:25:50 +01:00
|
|
|
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
|
2001-03-21 00:02:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/*
|
2002-06-12 14:04:18 +02:00
|
|
|
Check the privilege for all used tables. Table privileges are cached
|
|
|
|
in the table list for GRANT checking
|
2000-07-31 21:29:14 +02:00
|
|
|
*/
|
|
|
|
|
2001-04-11 13:04:03 +02:00
|
|
|
bool
|
2002-06-12 14:04:18 +02:00
|
|
|
check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
|
2001-12-22 14:13:31 +01:00
|
|
|
bool no_errors)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
uint found=0;
|
|
|
|
ulong found_access=0;
|
2000-07-31 21:29:14 +02:00
|
|
|
TABLE_LIST *org_tables=tables;
|
2004-07-16 00:15:55 +02:00
|
|
|
for (; tables; tables= tables->next_global)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 11:56:39 +01:00
|
|
|
if (tables->derived || tables->schema_table ||
|
2004-10-21 20:18:00 +02:00
|
|
|
(tables->table && (int)tables->table->tmp_table) ||
|
|
|
|
my_tz_check_n_skip_implicit_tables(&tables,
|
|
|
|
thd->lex->time_zone_tables_used))
|
2002-11-11 13:04:50 +01:00
|
|
|
continue;
|
2000-08-29 11:31:01 +02:00
|
|
|
if ((thd->master_access & want_access) == (want_access & ~EXTRA_ACL) &&
|
|
|
|
thd->db)
|
2000-07-31 21:29:14 +02:00
|
|
|
tables->grant.privilege= want_access;
|
2000-08-21 02:07:54 +02:00
|
|
|
else if (tables->db && tables->db == thd->db)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
if (found && !grant_option) // db already checked
|
|
|
|
tables->grant.privilege=found_access;
|
|
|
|
else
|
|
|
|
{
|
2001-12-22 14:13:31 +01:00
|
|
|
if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
|
|
|
|
0, no_errors))
|
2000-07-31 21:29:14 +02:00
|
|
|
return TRUE; // Access denied
|
|
|
|
found_access=tables->grant.privilege;
|
2000-08-21 02:07:54 +02:00
|
|
|
found=1;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
2002-11-12 13:40:32 +01:00
|
|
|
else if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
|
2002-06-09 14:41:19 +02:00
|
|
|
0, no_errors))
|
2002-06-09 08:14:25 +02:00
|
|
|
return TRUE;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
if (grant_option)
|
2001-04-11 13:04:03 +02:00
|
|
|
return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
|
2004-04-10 00:14:32 +02:00
|
|
|
test(want_access & EXTRA_ACL), UINT_MAX, no_errors);
|
2000-07-31 21:29:14 +02:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2004-09-03 20:43:04 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Check if the given table has any of the asked privileges
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
check_some_access()
|
|
|
|
thd Thread handler
|
|
|
|
want_access Bitmap of possible privileges to check for
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
1 error
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
|
|
|
|
{
|
|
|
|
ulong access;
|
|
|
|
DBUG_ENTER("check_some_access");
|
|
|
|
|
|
|
|
/* This loop will work as long as we have less than 32 privileges */
|
|
|
|
for (access= 1; access < want_access ; access<<= 1)
|
|
|
|
{
|
|
|
|
if (access & want_access)
|
|
|
|
{
|
|
|
|
if (!check_access(thd, access, table->db,
|
|
|
|
&table->grant.privilege, 0, 1) &&
|
|
|
|
!grant_option || !check_grant(thd, access, table, 0, 1, 1))
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DBUG_PRINT("exit",("no matching access rights"));
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-04-07 23:16:17 +02:00
|
|
|
bool check_merge_table_access(THD *thd, char *db,
|
|
|
|
TABLE_LIST *table_list)
|
2000-09-14 01:39:07 +02:00
|
|
|
{
|
|
|
|
int error=0;
|
|
|
|
if (table_list)
|
|
|
|
{
|
2001-08-29 16:33:41 +02:00
|
|
|
/* Check that all tables use the current database */
|
2000-09-14 01:39:07 +02:00
|
|
|
TABLE_LIST *tmp;
|
2004-07-16 00:15:55 +02:00
|
|
|
for (tmp= table_list; tmp; tmp= tmp->next_local)
|
2001-08-29 16:33:41 +02:00
|
|
|
{
|
|
|
|
if (!tmp->db || !tmp->db[0])
|
|
|
|
tmp->db=db;
|
|
|
|
}
|
2000-09-14 01:39:07 +02:00
|
|
|
error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
|
2003-09-26 12:33:13 +02:00
|
|
|
table_list,0);
|
2000-09-14 01:39:07 +02:00
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2003-09-26 12:33:13 +02:00
|
|
|
|
|
|
|
static bool check_db_used(THD *thd,TABLE_LIST *tables)
|
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
for (; tables; tables= tables->next_global)
|
2003-09-26 12:33:13 +02:00
|
|
|
{
|
|
|
|
if (!tables->db)
|
|
|
|
{
|
|
|
|
if (!(tables->db=thd->db))
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR),
|
|
|
|
MYF(0)); /* purecov: tested */
|
2003-09-26 12:33:13 +02:00
|
|
|
return TRUE; /* purecov: tested */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
2000-09-14 01:39:07 +02:00
|
|
|
|
2004-10-22 20:29:06 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Check if the given SP is owned by thd->priv_user/host, or priv_user is root.
|
|
|
|
QQ This is not quite complete, but it will do as a basic security check
|
|
|
|
for now. The question is exactly which rights should 'root' have?
|
|
|
|
Should root have access regardless of host for instance?
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
check_sp_definer_access()
|
|
|
|
thd Thread handler
|
|
|
|
sp The SP pointer
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
1 error Error message has been sent
|
|
|
|
*/
|
|
|
|
|
|
|
|
static bool
|
|
|
|
check_sp_definer_access(THD *thd, sp_head *sp)
|
|
|
|
{
|
|
|
|
LEX_STRING *usr, *hst;
|
|
|
|
|
|
|
|
if (strcmp("root", thd->priv_user) == 0)
|
|
|
|
return FALSE; /* QQ Any root is ok now */
|
|
|
|
usr= &sp->m_definer_user;
|
|
|
|
hst= &sp->m_definer_host;
|
|
|
|
if (strncmp(thd->priv_user, usr->str, usr->length) == 0 &&
|
|
|
|
strncmp(thd->priv_host, hst->str, hst->length) == 0)
|
|
|
|
return FALSE; /* Both user and host must match */
|
|
|
|
|
|
|
|
my_error(ER_SP_ACCESS_DENIED_ERROR, MYF(0), sp->m_qname.str);
|
|
|
|
return TRUE; /* Not definer or root */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/****************************************************************************
|
|
|
|
Check stack size; Send error if there isn't enough stack to continue
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#if STACK_DIRECTION < 0
|
|
|
|
#define used_stack(A,B) (long) (A - B)
|
|
|
|
#else
|
|
|
|
#define used_stack(A,B) (long) (B - A)
|
|
|
|
#endif
|
|
|
|
|
2004-04-07 03:33:58 +02:00
|
|
|
#ifndef DBUG_OFF
|
|
|
|
long max_stack_used;
|
|
|
|
#endif
|
|
|
|
|
2003-04-08 07:35:13 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-07-31 21:29:14 +02:00
|
|
|
bool check_stack_overrun(THD *thd,char *buf __attribute__((unused)))
|
|
|
|
{
|
|
|
|
long stack_used;
|
|
|
|
if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
|
|
|
|
(long) thread_stack_min)
|
|
|
|
{
|
|
|
|
sprintf(errbuff[0],ER(ER_STACK_OVERRUN),stack_used,thread_stack);
|
|
|
|
my_message(ER_STACK_OVERRUN,errbuff[0],MYF(0));
|
2003-01-30 21:15:44 +01:00
|
|
|
thd->fatal_error();
|
2000-07-31 21:29:14 +02:00
|
|
|
return 1;
|
|
|
|
}
|
2004-04-07 03:33:58 +02:00
|
|
|
#ifndef DBUG_OFF
|
|
|
|
max_stack_used= max(max_stack_used, stack_used);
|
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2003-04-08 07:35:13 +02:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
#define MY_YACC_INIT 1000 // Start with big alloc
|
|
|
|
#define MY_YACC_MAX 32000 // Because of 'short'
|
|
|
|
|
2004-06-24 01:57:57 +02:00
|
|
|
bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
LEX *lex=current_lex;
|
2004-06-24 01:57:57 +02:00
|
|
|
ulong old_info=0;
|
2000-07-31 21:29:14 +02:00
|
|
|
if ((uint) *yystacksize >= MY_YACC_MAX)
|
|
|
|
return 1;
|
|
|
|
if (!lex->yacc_yyvs)
|
|
|
|
old_info= *yystacksize;
|
|
|
|
*yystacksize= set_zone((*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX);
|
|
|
|
if (!(lex->yacc_yyvs= (char*)
|
|
|
|
my_realloc((gptr) lex->yacc_yyvs,
|
|
|
|
*yystacksize*sizeof(**yyvs),
|
|
|
|
MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) ||
|
|
|
|
!(lex->yacc_yyss= (char*)
|
|
|
|
my_realloc((gptr) lex->yacc_yyss,
|
|
|
|
*yystacksize*sizeof(**yyss),
|
|
|
|
MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))))
|
|
|
|
return 1;
|
|
|
|
if (old_info)
|
|
|
|
{ // Copy old info from stack
|
|
|
|
memcpy(lex->yacc_yyss, (gptr) *yyss, old_info*sizeof(**yyss));
|
|
|
|
memcpy(lex->yacc_yyvs, (gptr) *yyvs, old_info*sizeof(**yyvs));
|
|
|
|
}
|
|
|
|
*yyss=(short*) lex->yacc_yyss;
|
|
|
|
*yyvs=(YYSTYPE*) lex->yacc_yyvs;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
2003-02-12 20:55:37 +01:00
|
|
|
Initialize global thd variables needed for query
|
2000-07-31 21:29:14 +02:00
|
|
|
****************************************************************************/
|
|
|
|
|
2002-05-27 19:52:54 +02:00
|
|
|
void
|
2004-07-21 21:44:12 +02:00
|
|
|
mysql_init_query(THD *thd, uchar *buf, uint length)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("mysql_init_query");
|
2004-07-21 21:44:12 +02:00
|
|
|
lex_start(thd, buf, length);
|
2004-10-14 00:53:59 +02:00
|
|
|
mysql_reset_thd_for_next_command(thd);
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Reset THD part responsible for command processing state.
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
This needs to be called before execution of every statement
|
|
|
|
(prepared or conventional).
|
|
|
|
|
|
|
|
TODO
|
|
|
|
Make it a method of THD and align its name with the rest of
|
|
|
|
reset/end/start/init methods.
|
|
|
|
Call it after we use THD for queries, not before.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void mysql_reset_thd_for_next_command(THD *thd)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("mysql_reset_thd_for_next_command");
|
2004-10-14 01:37:20 +02:00
|
|
|
thd->free_list= 0;
|
2004-10-14 00:53:59 +02:00
|
|
|
thd->select_number= 1;
|
2004-10-29 18:26:52 +02:00
|
|
|
thd->total_warn_count=0; // Warnings for this query
|
2002-05-06 23:04:16 +02:00
|
|
|
thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
|
|
|
|
thd->sent_row_count= thd->examined_row_count= 0;
|
2004-06-18 08:11:31 +02:00
|
|
|
thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0;
|
2003-12-17 16:35:34 +01:00
|
|
|
thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS |
|
2004-10-29 18:26:52 +02:00
|
|
|
SERVER_QUERY_NO_INDEX_USED |
|
|
|
|
SERVER_QUERY_NO_GOOD_INDEX_USED);
|
2003-02-10 16:59:16 +01:00
|
|
|
thd->tmp_table_used= 0;
|
2003-01-30 18:39:54 +01:00
|
|
|
if (opt_bin_log)
|
|
|
|
reset_dynamic(&thd->user_var_events);
|
2003-02-12 20:55:37 +01:00
|
|
|
thd->clear_error();
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
2002-12-11 08:17:51 +01:00
|
|
|
|
2000-08-15 19:09:37 +02:00
|
|
|
void
|
|
|
|
mysql_init_select(LEX *lex)
|
|
|
|
{
|
2003-07-03 01:30:52 +02:00
|
|
|
SELECT_LEX *select_lex= lex->current_select;
|
2002-05-06 23:04:16 +02:00
|
|
|
select_lex->init_select();
|
2004-05-05 20:21:41 +02:00
|
|
|
select_lex->select_limit= HA_POS_ERROR;
|
2004-11-13 11:56:39 +01:00
|
|
|
lex->orig_sql_command= SQLCOM_END;
|
|
|
|
lex->wild= 0;
|
2002-12-05 23:40:28 +01:00
|
|
|
if (select_lex == &lex->select_lex)
|
|
|
|
{
|
2004-08-24 18:17:11 +02:00
|
|
|
DBUG_ASSERT(lex->result == 0);
|
2002-12-05 23:40:28 +01:00
|
|
|
lex->exchange= 0;
|
|
|
|
}
|
2000-08-15 19:09:37 +02:00
|
|
|
}
|
|
|
|
|
2002-06-16 16:06:12 +02:00
|
|
|
|
2001-12-13 14:53:18 +01:00
|
|
|
bool
|
2002-05-06 23:04:16 +02:00
|
|
|
mysql_new_select(LEX *lex, bool move_down)
|
2001-06-13 12:36:53 +02:00
|
|
|
{
|
2004-11-08 00:13:54 +01:00
|
|
|
SELECT_LEX *select_lex;
|
|
|
|
if (!(select_lex= new(lex->thd->mem_root) SELECT_LEX()))
|
2001-12-13 14:53:18 +01:00
|
|
|
return 1;
|
2003-03-11 00:06:28 +01:00
|
|
|
select_lex->select_number= ++lex->thd->select_number;
|
2002-05-06 23:04:16 +02:00
|
|
|
select_lex->init_query();
|
|
|
|
select_lex->init_select();
|
2004-07-16 00:15:55 +02:00
|
|
|
select_lex->parent_lex= lex;
|
2002-05-06 23:04:16 +02:00
|
|
|
if (move_down)
|
|
|
|
{
|
2004-11-30 20:41:12 +01:00
|
|
|
lex->subqueries= TRUE;
|
2002-05-06 23:04:16 +02:00
|
|
|
/* first select_lex of subselect or derived table */
|
2004-11-08 00:13:54 +01:00
|
|
|
SELECT_LEX_UNIT *unit;
|
|
|
|
if (!(unit= new(lex->thd->mem_root) SELECT_LEX_UNIT()))
|
2002-05-06 23:04:16 +02:00
|
|
|
return 1;
|
2004-11-08 00:13:54 +01:00
|
|
|
|
2002-05-06 23:04:16 +02:00
|
|
|
unit->init_query();
|
|
|
|
unit->init_select();
|
2003-01-25 01:25:52 +01:00
|
|
|
unit->thd= lex->thd;
|
2003-07-03 01:30:52 +02:00
|
|
|
unit->include_down(lex->current_select);
|
2002-12-15 21:01:09 +01:00
|
|
|
unit->link_next= 0;
|
|
|
|
unit->link_prev= 0;
|
2003-03-11 00:06:28 +01:00
|
|
|
unit->return_to= lex->current_select;
|
2002-05-06 23:04:16 +02:00
|
|
|
select_lex->include_down(unit);
|
2004-07-07 10:29:39 +02:00
|
|
|
/* TODO: assign resolve_mode for fake subquery after merging with new tree */
|
2002-05-06 23:04:16 +02:00
|
|
|
}
|
|
|
|
else
|
2003-07-03 01:30:52 +02:00
|
|
|
{
|
2004-07-16 00:15:55 +02:00
|
|
|
if (lex->current_select->order_list.first && !lex->current_select->braces)
|
|
|
|
{
|
2004-10-20 03:04:37 +02:00
|
|
|
my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY");
|
2004-07-16 00:15:55 +02:00
|
|
|
return 1;
|
|
|
|
}
|
2002-10-30 12:18:52 +01:00
|
|
|
select_lex->include_neighbour(lex->current_select);
|
2003-07-03 01:30:52 +02:00
|
|
|
SELECT_LEX_UNIT *unit= select_lex->master_unit();
|
|
|
|
SELECT_LEX *fake= unit->fake_select_lex;
|
|
|
|
if (!fake)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
as far as we included SELECT_LEX for UNION unit should have
|
|
|
|
fake SELECT_LEX for UNION processing
|
|
|
|
*/
|
2004-11-08 00:13:54 +01:00
|
|
|
if (!(fake= unit->fake_select_lex= new(lex->thd->mem_root) SELECT_LEX()))
|
|
|
|
return 1;
|
2003-07-03 01:30:52 +02:00
|
|
|
fake->include_standalone(unit,
|
|
|
|
(SELECT_LEX_NODE**)&unit->fake_select_lex);
|
|
|
|
fake->select_number= INT_MAX;
|
|
|
|
fake->make_empty_select();
|
|
|
|
fake->linkage= GLOBAL_OPTIONS_TYPE;
|
2004-05-05 20:21:41 +02:00
|
|
|
fake->select_limit= HA_POS_ERROR;
|
2003-07-03 01:30:52 +02:00
|
|
|
}
|
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2002-05-27 19:52:54 +02:00
|
|
|
select_lex->master_unit()->global_parameters= select_lex;
|
2002-11-27 00:12:16 +01:00
|
|
|
select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
|
2002-10-30 12:18:52 +01:00
|
|
|
lex->current_select= select_lex;
|
2003-07-29 15:59:46 +02:00
|
|
|
select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
|
2001-12-13 14:53:18 +01:00
|
|
|
return 0;
|
2001-06-13 12:36:53 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
/*
|
|
|
|
Create a select to return the same output as 'SELECT @@var_name'.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
create_select_for_variable()
|
|
|
|
var_name Variable name
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
Used for SHOW COUNT(*) [ WARNINGS | ERROR]
|
|
|
|
|
|
|
|
This will crash with a core dump if the variable doesn't exists
|
|
|
|
*/
|
|
|
|
|
|
|
|
void create_select_for_variable(const char *var_name)
|
|
|
|
{
|
2003-07-06 18:09:57 +02:00
|
|
|
THD *thd;
|
2002-10-02 12:33:08 +02:00
|
|
|
LEX *lex;
|
2003-07-06 18:09:57 +02:00
|
|
|
LEX_STRING tmp, null_lex_string;
|
2002-10-02 12:33:08 +02:00
|
|
|
DBUG_ENTER("create_select_for_variable");
|
2003-07-06 18:09:57 +02:00
|
|
|
|
|
|
|
thd= current_thd;
|
2003-08-26 17:41:40 +02:00
|
|
|
lex= thd->lex;
|
2002-10-02 12:33:08 +02:00
|
|
|
mysql_init_select(lex);
|
|
|
|
lex->sql_command= SQLCOM_SELECT;
|
|
|
|
tmp.str= (char*) var_name;
|
|
|
|
tmp.length=strlen(var_name);
|
2003-07-06 18:09:57 +02:00
|
|
|
bzero((char*) &null_lex_string.str, sizeof(null_lex_string));
|
|
|
|
add_item_to_list(thd, get_system_var(thd, OPT_SESSION, tmp,
|
|
|
|
null_lex_string));
|
2002-10-02 12:33:08 +02:00
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
2002-06-16 16:06:12 +02:00
|
|
|
|
2002-01-02 23:46:43 +01:00
|
|
|
void mysql_init_multi_delete(LEX *lex)
|
|
|
|
{
|
2002-05-06 23:04:16 +02:00
|
|
|
lex->sql_command= SQLCOM_DELETE_MULTI;
|
2002-01-02 23:46:43 +01:00
|
|
|
mysql_init_select(lex);
|
2002-10-30 12:18:52 +01:00
|
|
|
lex->select_lex.select_limit= lex->unit.select_limit_cnt=
|
2002-06-05 20:32:22 +02:00
|
|
|
HA_POS_ERROR;
|
2003-08-18 23:08:08 +02:00
|
|
|
lex->select_lex.table_list.save_and_clear(&lex->auxilliary_table_list);
|
2004-07-16 00:15:55 +02:00
|
|
|
lex->query_tables= 0;
|
|
|
|
lex->query_tables_last= &lex->query_tables;
|
2002-01-02 23:46:43 +01:00
|
|
|
}
|
2001-12-13 14:53:18 +01:00
|
|
|
|
2002-06-16 16:06:12 +02:00
|
|
|
|
2004-03-11 17:38:19 +01:00
|
|
|
/*
|
|
|
|
When you modify mysql_parse(), you may need to mofify
|
|
|
|
mysql_test_parse_for_slave() in this same file.
|
|
|
|
*/
|
2004-04-12 02:26:32 +02:00
|
|
|
|
2004-03-11 17:38:19 +01:00
|
|
|
void mysql_parse(THD *thd, char *inBuf, uint length)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("mysql_parse");
|
|
|
|
|
2004-07-21 21:44:12 +02:00
|
|
|
mysql_init_query(thd, (uchar*) inBuf, length);
|
2002-03-22 21:55:08 +01:00
|
|
|
if (query_cache_send_result_to_client(thd, inBuf, length) <= 0)
|
2001-12-02 13:34:01 +01:00
|
|
|
{
|
2004-07-21 21:44:12 +02:00
|
|
|
LEX *lex= thd->lex;
|
2003-01-30 21:15:44 +01:00
|
|
|
if (!yyparse((void *)thd) && ! thd->is_fatal_error)
|
2001-12-02 13:34:01 +01:00
|
|
|
{
|
2003-09-26 12:33:13 +02:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2002-06-16 16:06:12 +02:00
|
|
|
if (mqh_used && thd->user_connect &&
|
2002-11-09 14:40:46 +01:00
|
|
|
check_mqh(thd, lex->sql_command))
|
2002-05-15 12:50:38 +02:00
|
|
|
{
|
|
|
|
thd->net.error = 0;
|
|
|
|
}
|
|
|
|
else
|
2003-09-26 12:33:13 +02:00
|
|
|
#endif
|
2002-05-15 12:50:38 +02:00
|
|
|
{
|
2002-11-28 18:29:26 +01:00
|
|
|
if (thd->net.report_error)
|
2003-05-23 15:32:31 +02:00
|
|
|
{
|
|
|
|
if (thd->lex->sphead)
|
2003-06-29 18:15:17 +02:00
|
|
|
{
|
|
|
|
if (lex != thd->lex)
|
|
|
|
thd->lex->sphead->restore_lex(thd);
|
|
|
|
delete thd->lex->sphead;
|
|
|
|
thd->lex->sphead= NULL;
|
|
|
|
}
|
2003-05-23 15:32:31 +02:00
|
|
|
}
|
2002-11-28 18:29:26 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
mysql_execute_command(thd);
|
2003-12-01 14:19:10 +01:00
|
|
|
query_cache_end_of_result(thd);
|
2002-11-28 18:29:26 +01:00
|
|
|
}
|
2002-05-15 12:50:38 +02:00
|
|
|
}
|
2004-05-20 01:02:49 +02:00
|
|
|
lex->unit.cleanup();
|
2001-12-02 13:34:01 +01:00
|
|
|
}
|
|
|
|
else
|
2002-07-24 18:55:08 +02:00
|
|
|
{
|
|
|
|
DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
|
2003-01-30 21:15:44 +01:00
|
|
|
thd->is_fatal_error));
|
2001-12-02 13:34:01 +01:00
|
|
|
query_cache_abort(&thd->net);
|
2004-05-20 01:02:49 +02:00
|
|
|
lex->unit.cleanup();
|
2003-05-23 15:32:31 +02:00
|
|
|
if (thd->lex->sphead)
|
2003-06-29 18:15:17 +02:00
|
|
|
{
|
2003-12-21 11:48:03 +01:00
|
|
|
/* Clean up after failed stored procedure/function */
|
2003-06-29 18:15:17 +02:00
|
|
|
if (lex != thd->lex)
|
|
|
|
thd->lex->sphead->restore_lex(thd);
|
|
|
|
delete thd->lex->sphead;
|
|
|
|
thd->lex->sphead= NULL;
|
|
|
|
}
|
2002-07-24 18:55:08 +02:00
|
|
|
}
|
2001-12-02 13:34:01 +01:00
|
|
|
thd->proc_info="freeing items";
|
2004-08-24 18:17:11 +02:00
|
|
|
thd->end_statement();
|
2004-09-15 21:10:31 +02:00
|
|
|
thd->cleanup_after_query();
|
2004-10-08 00:21:19 +02:00
|
|
|
DBUG_ASSERT(thd->change_list.is_empty());
|
2001-12-02 13:34:01 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-03-17 23:09:13 +01:00
|
|
|
#ifdef HAVE_REPLICATION
|
2004-03-11 17:38:19 +01:00
|
|
|
/*
|
|
|
|
Usable by the replication SQL thread only: just parse a query to know if it
|
|
|
|
can be ignored because of replicate-*-table rules.
|
|
|
|
|
|
|
|
RETURN VALUES
|
|
|
|
0 cannot be ignored
|
|
|
|
1 can be ignored
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
|
|
|
|
{
|
2004-07-21 21:44:12 +02:00
|
|
|
LEX *lex= thd->lex;
|
2004-03-11 17:38:19 +01:00
|
|
|
bool error= 0;
|
2004-07-15 03:19:07 +02:00
|
|
|
DBUG_ENTER("mysql_test_parse_for_slave");
|
2004-03-11 17:38:19 +01:00
|
|
|
|
2004-07-21 21:44:12 +02:00
|
|
|
mysql_init_query(thd, (uchar*) inBuf, length);
|
2004-03-16 21:41:30 +01:00
|
|
|
if (!yyparse((void*) thd) && ! thd->is_fatal_error &&
|
2004-03-11 17:38:19 +01:00
|
|
|
all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
|
2004-07-15 03:19:07 +02:00
|
|
|
error= 1; /* Ignore question */
|
2004-08-24 18:17:11 +02:00
|
|
|
thd->end_statement();
|
2004-09-15 21:10:31 +02:00
|
|
|
thd->cleanup_after_query();
|
2004-07-15 03:19:07 +02:00
|
|
|
DBUG_RETURN(error);
|
2004-03-11 17:38:19 +01:00
|
|
|
}
|
2004-03-17 23:09:13 +01:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2004-09-15 21:10:31 +02:00
|
|
|
|
2004-10-29 18:26:52 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/*****************************************************************************
|
|
|
|
** Store field definition for create
|
|
|
|
** Return 0 if ok
|
|
|
|
******************************************************************************/
|
|
|
|
|
2002-12-06 20:11:27 +01:00
|
|
|
bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
|
2000-07-31 21:29:14 +02:00
|
|
|
char *length, char *decimals,
|
2002-06-02 20:22:20 +02:00
|
|
|
uint type_modifier,
|
2004-04-02 08:12:53 +02:00
|
|
|
Item *default_value, Item *on_update_value,
|
|
|
|
LEX_STRING *comment,
|
2004-12-02 09:48:43 +01:00
|
|
|
char *change,
|
|
|
|
List<String> *interval_list, CHARSET_INFO *cs,
|
2003-03-27 10:09:09 +01:00
|
|
|
uint uint_geom_type)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
register create_field *new_field;
|
2003-05-05 20:54:37 +02:00
|
|
|
LEX *lex= thd->lex;
|
2000-07-31 21:29:14 +02:00
|
|
|
uint allowed_type_modifier=0;
|
2004-09-28 19:08:00 +02:00
|
|
|
uint sign_len;
|
2004-12-06 01:00:37 +01:00
|
|
|
ulong max_field_charlength= MAX_FIELD_CHARLENGTH;
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_ENTER("add_field_to_list");
|
|
|
|
|
|
|
|
if (strlen(field_name) > NAME_LEN)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_TOO_LONG_IDENT, MYF(0), field_name); /* purecov: inspected */
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1); /* purecov: inspected */
|
|
|
|
}
|
|
|
|
if (type_modifier & PRI_KEY_FLAG)
|
|
|
|
{
|
|
|
|
lex->col_list.push_back(new key_part_spec(field_name,0));
|
2002-06-02 20:22:20 +02:00
|
|
|
lex->key_list.push_back(new Key(Key::PRIMARY, NullS, HA_KEY_ALG_UNDEF,
|
2004-05-11 23:29:52 +02:00
|
|
|
0, lex->col_list));
|
2000-07-31 21:29:14 +02:00
|
|
|
lex->col_list.empty();
|
|
|
|
}
|
|
|
|
if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
|
|
|
|
{
|
|
|
|
lex->col_list.push_back(new key_part_spec(field_name,0));
|
2004-05-11 23:29:52 +02:00
|
|
|
lex->key_list.push_back(new Key(Key::UNIQUE, NullS, HA_KEY_ALG_UNDEF, 0,
|
2000-07-31 21:29:14 +02:00
|
|
|
lex->col_list));
|
|
|
|
lex->col_list.empty();
|
|
|
|
}
|
|
|
|
|
2003-03-22 18:55:09 +01:00
|
|
|
if (default_value)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-04-02 08:12:53 +02:00
|
|
|
/*
|
2004-03-20 12:36:26 +01:00
|
|
|
Default value should be literal => basic constants =>
|
|
|
|
no need fix_fields()
|
2004-04-02 08:12:53 +02:00
|
|
|
|
|
|
|
We allow only one function as part of default value -
|
|
|
|
NOW() as default for TIMESTAMP type.
|
2004-01-30 13:13:19 +01:00
|
|
|
*/
|
2004-04-02 08:12:53 +02:00
|
|
|
if (default_value->type() == Item::FUNC_ITEM &&
|
|
|
|
!(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC &&
|
|
|
|
type == FIELD_TYPE_TIMESTAMP))
|
2003-12-09 09:36:57 +01:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
|
2003-12-09 09:36:57 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
else if (default_value->type() == Item::NULL_ITEM)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-10-04 15:13:31 +02:00
|
|
|
default_value= 0;
|
2003-03-22 18:55:09 +01:00
|
|
|
if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
|
|
|
|
NOT_NULL_FLAG)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
|
2003-03-22 18:55:09 +01:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type_modifier & AUTO_INCREMENT_FLAG)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
}
|
2004-04-02 08:12:53 +02:00
|
|
|
|
|
|
|
if (on_update_value && type != FIELD_TYPE_TIMESTAMP)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name);
|
2004-04-02 08:12:53 +02:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!(new_field=new create_field()))
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
new_field->field=0;
|
|
|
|
new_field->field_name=field_name;
|
2003-03-22 18:55:09 +01:00
|
|
|
new_field->def= default_value;
|
2000-07-31 21:29:14 +02:00
|
|
|
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->change=change;
|
|
|
|
new_field->interval=0;
|
2004-12-06 01:00:37 +01:00
|
|
|
new_field->pack_length= new_field->key_length= 0;
|
2002-06-07 14:23:33 +02:00
|
|
|
new_field->charset=cs;
|
2003-03-27 10:09:09 +01:00
|
|
|
new_field->geom_type= (Field::geometry_type) uint_geom_type;
|
2002-06-04 07:23:57 +02:00
|
|
|
|
2002-06-02 20:22:20 +02:00
|
|
|
if (!comment)
|
|
|
|
{
|
|
|
|
new_field->comment.str=0;
|
|
|
|
new_field->comment.length=0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* In this case comment is always of type Item_string */
|
2003-09-16 09:14:40 +02:00
|
|
|
new_field->comment.str= (char*) comment->str;
|
|
|
|
new_field->comment.length=comment->length;
|
2002-06-02 20:22:20 +02:00
|
|
|
}
|
2004-10-02 21:20:08 +02:00
|
|
|
/*
|
|
|
|
Set flag if this field doesn't have a default value
|
|
|
|
Enum values has always the first value as a default (set in
|
|
|
|
make_empty_rec().
|
|
|
|
*/
|
2004-09-28 19:08:00 +02:00
|
|
|
if (!default_value && !(type_modifier & AUTO_INCREMENT_FLAG) &&
|
2004-10-02 21:20:08 +02:00
|
|
|
(type_modifier & NOT_NULL_FLAG) && type != FIELD_TYPE_TIMESTAMP &&
|
|
|
|
type != FIELD_TYPE_ENUM)
|
2004-09-28 19:08:00 +02:00
|
|
|
new_field->flags|= NO_DEFAULT_VALUE_FLAG;
|
|
|
|
|
2003-05-14 21:12:55 +02:00
|
|
|
if (length && !(new_field->length= (uint) atoi(length)))
|
|
|
|
length=0; /* purecov: inspected */
|
2004-09-28 19:08:00 +02:00
|
|
|
sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
if (new_field->length && new_field->decimals &&
|
2002-04-26 12:56:32 +02:00
|
|
|
new_field->length < new_field->decimals+1 &&
|
2000-07-31 21:29:14 +02:00
|
|
|
new_field->decimals != NOT_FIXED_DEC)
|
2002-04-26 12:56:32 +02:00
|
|
|
new_field->length=new_field->decimals+1; /* purecov: inspected */
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case FIELD_TYPE_TINY:
|
2004-09-10 18:56:47 +02:00
|
|
|
if (!length) new_field->length=MAX_TINYINT_WIDTH+sign_len;
|
2000-07-31 21:29:14 +02:00
|
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_SHORT:
|
2004-09-10 18:56:47 +02:00
|
|
|
if (!length) new_field->length=MAX_SMALLINT_WIDTH+sign_len;
|
2000-07-31 21:29:14 +02:00
|
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_INT24:
|
2004-09-10 18:56:47 +02:00
|
|
|
if (!length) new_field->length=MAX_MEDIUMINT_WIDTH+sign_len;
|
2000-07-31 21:29:14 +02:00
|
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_LONG:
|
2004-09-10 18:56:47 +02:00
|
|
|
if (!length) new_field->length=MAX_INT_WIDTH+sign_len;
|
2000-07-31 21:29:14 +02:00
|
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_LONGLONG:
|
2004-09-10 18:56:47 +02:00
|
|
|
if (!length) new_field->length=MAX_BIGINT_WIDTH;
|
2000-07-31 21:29:14 +02:00
|
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_NULL:
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_DECIMAL:
|
|
|
|
if (!length)
|
2004-06-10 10:58:16 +02:00
|
|
|
{
|
|
|
|
if ((new_field->length= new_field->decimals))
|
2004-06-09 00:08:24 +02:00
|
|
|
new_field->length++;
|
|
|
|
else
|
2004-06-10 10:58:16 +02:00
|
|
|
new_field->length= 10; // Default length for DECIMAL
|
|
|
|
}
|
2003-05-14 21:12:55 +02:00
|
|
|
if (new_field->length < MAX_FIELD_WIDTH) // Skip wrong argument
|
|
|
|
{
|
|
|
|
new_field->length+=sign_len;
|
|
|
|
if (new_field->decimals)
|
|
|
|
new_field->length++;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2004-12-06 01:00:37 +01:00
|
|
|
case MYSQL_TYPE_VARCHAR:
|
|
|
|
/*
|
|
|
|
Long VARCHAR's are automaticly converted to blobs in mysql_prepare_table
|
|
|
|
if they don't have a default value
|
|
|
|
*/
|
|
|
|
max_field_charlength= MAX_FIELD_VARCHARLENGTH;
|
|
|
|
break;
|
|
|
|
case MYSQL_TYPE_STRING:
|
|
|
|
break;
|
2000-07-31 21:29:14 +02:00
|
|
|
case FIELD_TYPE_BLOB:
|
|
|
|
case FIELD_TYPE_TINY_BLOB:
|
|
|
|
case FIELD_TYPE_LONG_BLOB:
|
|
|
|
case FIELD_TYPE_MEDIUM_BLOB:
|
2002-10-04 09:48:32 +02:00
|
|
|
case FIELD_TYPE_GEOMETRY:
|
2000-07-31 21:29:14 +02:00
|
|
|
if (default_value) // Allow empty as default value
|
|
|
|
{
|
|
|
|
String str,*res;
|
|
|
|
res=default_value->val_str(&str);
|
|
|
|
if (res->length())
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0),
|
|
|
|
field_name); /* purecov: inspected */
|
2000-07-31 21:29:14 +02:00
|
|
|
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)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
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
|
2004-08-06 10:15:40 +02:00
|
|
|
else if (new_field->length != 19)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-08-06 10:15:40 +02:00
|
|
|
/*
|
|
|
|
We support only even TIMESTAMP lengths less or equal than 14
|
|
|
|
and 19 as length of 4.1 compatible representation.
|
|
|
|
*/
|
2000-07-31 21:29:14 +02:00
|
|
|
new_field->length=((new_field->length+1)/2)*2; /* purecov: inspected */
|
|
|
|
new_field->length= min(new_field->length,14); /* purecov: inspected */
|
|
|
|
}
|
2004-10-01 16:54:06 +02:00
|
|
|
new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
|
2004-04-02 08:12:53 +02:00
|
|
|
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
|
|
|
|
{
|
2004-10-04 15:13:31 +02:00
|
|
|
/*
|
|
|
|
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
|
2004-04-02 08:12:53 +02:00
|
|
|
information about all TIMESTAMP fields in table will be availiable.
|
2004-10-04 15:13:31 +02:00
|
|
|
|
|
|
|
If we have TIMESTAMP NULL column without explicit DEFAULT value
|
|
|
|
we treat it as having DEFAULT NULL attribute.
|
2004-04-02 08:12:53 +02:00
|
|
|
*/
|
2004-10-29 18:26:52 +02:00
|
|
|
new_field->unireg_check= (on_update_value ?
|
|
|
|
Field::TIMESTAMP_UN_FIELD :
|
|
|
|
(new_field->flags & NOT_NULL_FLAG ?
|
|
|
|
Field::TIMESTAMP_OLD_FIELD:
|
|
|
|
Field::NONE));
|
2004-04-02 08:12:53 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
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:
|
|
|
|
{
|
2004-12-02 09:48:43 +01:00
|
|
|
if (interval_list->elements > sizeof(longlong)*8)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_TOO_BIG_SET, MYF(0), field_name); /* purecov: inspected */
|
2004-12-06 10:38:56 +01:00
|
|
|
DBUG_RETURN(1); /* purecov: inspected */
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2004-12-02 09:48:43 +01:00
|
|
|
new_field->pack_length= (interval_list->elements + 7) / 8;
|
2000-07-31 21:29:14 +02:00
|
|
|
if (new_field->pack_length > 4)
|
2004-12-02 09:48:43 +01:00
|
|
|
new_field->pack_length=8;
|
|
|
|
|
|
|
|
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;
|
2004-12-06 18:18:35 +01:00
|
|
|
break;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
case FIELD_TYPE_ENUM:
|
|
|
|
{
|
2004-12-02 09:48:43 +01:00
|
|
|
// Should be safe
|
|
|
|
new_field->pack_length= interval_list->elements < 256 ? 1 : 2;
|
2004-10-26 06:13:52 +02:00
|
|
|
|
2004-12-02 09:48:43 +01:00
|
|
|
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.
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2004-12-06 18:18:35 +01:00
|
|
|
}
|
2004-12-06 01:00:37 +01:00
|
|
|
case MYSQL_TYPE_VAR_STRING:
|
|
|
|
DBUG_ASSERT(0); // Impossible
|
2004-12-02 09:48:43 +01:00
|
|
|
break;
|
2004-12-17 15:06:05 +01:00
|
|
|
case MYSQL_TYPE_BIT:
|
|
|
|
{
|
|
|
|
if (!length)
|
|
|
|
new_field->length= 1;
|
|
|
|
if (new_field->length > MAX_BIT_FIELD_LENGTH)
|
|
|
|
{
|
|
|
|
my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), field_name,
|
|
|
|
MAX_BIT_FIELD_LENGTH);
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
new_field->pack_length= (new_field->length + 7) / 8;
|
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2004-12-06 01:00:37 +01:00
|
|
|
if (!(new_field->flags & BLOB_FLAG) &&
|
|
|
|
((new_field->length > max_field_charlength && type != FIELD_TYPE_SET &&
|
|
|
|
type != FIELD_TYPE_ENUM &&
|
|
|
|
(type != MYSQL_TYPE_VARCHAR || default_value)) ||
|
|
|
|
(!new_field->length &&
|
|
|
|
type != MYSQL_TYPE_STRING &&
|
|
|
|
type != MYSQL_TYPE_VARCHAR && type != FIELD_TYPE_GEOMETRY)))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0),
|
2004-12-06 18:18:35 +01:00
|
|
|
field_name, max_field_charlength); /* purecov: inspected */
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1); /* purecov: inspected */
|
|
|
|
}
|
|
|
|
type_modifier&= AUTO_INCREMENT_FLAG;
|
|
|
|
if ((~allowed_type_modifier) & type_modifier)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
lex->create_list.push_back(new_field);
|
|
|
|
lex->last_field=new_field;
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
2004-12-18 04:19:21 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2002-11-29 15:40:18 +01:00
|
|
|
add_proc_to_list(THD* thd, Item *item)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
ORDER *order;
|
|
|
|
Item **item_ptr;
|
|
|
|
|
2002-12-06 20:11:27 +01:00
|
|
|
if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*))))
|
2000-07-31 21:29:14 +02:00
|
|
|
return 1;
|
|
|
|
item_ptr = (Item**) (order+1);
|
|
|
|
*item_ptr= item;
|
|
|
|
order->item=item_ptr;
|
|
|
|
order->free_me=0;
|
2003-05-05 20:54:37 +02:00
|
|
|
thd->lex->proc_list.link_in_list((byte*) order,(byte**) &order->next);
|
2000-07-31 21:29:14 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Fix escaping of _, % and \ in database and table names (for ODBC) */
|
|
|
|
|
|
|
|
static void remove_escape(char *name)
|
|
|
|
{
|
2001-08-15 19:09:20 +02:00
|
|
|
if (!*name) // For empty DB names
|
|
|
|
return;
|
2000-07-31 21:29:14 +02:00
|
|
|
char *to;
|
|
|
|
#ifdef USE_MB
|
2000-08-21 23:18:32 +02:00
|
|
|
char *strend=name+(uint) strlen(name);
|
2000-07-31 21:29:14 +02:00
|
|
|
#endif
|
|
|
|
for (to=name; *name ; name++)
|
|
|
|
{
|
|
|
|
#ifdef USE_MB
|
|
|
|
int l;
|
2002-03-14 17:52:48 +01:00
|
|
|
if (use_mb(system_charset_info) &&
|
|
|
|
(l = my_ismbchar(system_charset_info, name, strend)))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
while (l--)
|
|
|
|
*to++ = *name++;
|
|
|
|
name--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (*name == '\\' && name[1])
|
2001-11-06 23:13:29 +01:00
|
|
|
name++; // Skip '\\'
|
2000-07-31 21:29:14 +02:00
|
|
|
*to++= *name;
|
|
|
|
}
|
|
|
|
*to=0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
** save order by and tables in own lists
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
2002-12-06 20:11:27 +01:00
|
|
|
bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
ORDER *order;
|
|
|
|
DBUG_ENTER("add_to_list");
|
2004-04-03 10:13:51 +02:00
|
|
|
if (!(order = (ORDER *) thd->alloc(sizeof(ORDER))))
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1);
|
2004-04-03 10:13:51 +02:00
|
|
|
order->item_ptr= item;
|
|
|
|
order->item= &order->item_ptr;
|
2000-07-31 21:29:14 +02:00
|
|
|
order->asc = asc;
|
|
|
|
order->free_me=0;
|
|
|
|
order->used=0;
|
2004-08-31 10:58:45 +02:00
|
|
|
order->counter_used= 0;
|
2002-11-29 15:40:18 +01:00
|
|
|
list.link_in_list((byte*) order,(byte**) &order->next);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-01-09 21:42:31 +01:00
|
|
|
/*
|
|
|
|
Add a table to list of used tables
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
add_table_to_list()
|
|
|
|
table Table to add
|
|
|
|
alias alias for table (or null if no alias)
|
|
|
|
table_options A set of the following bits:
|
|
|
|
TL_OPTION_UPDATING Table will be updated
|
|
|
|
TL_OPTION_FORCE_INDEX Force usage of index
|
|
|
|
lock_type How table should be locked
|
|
|
|
use_index List of indexed used in USE INDEX
|
|
|
|
ignore_index List of indexed used in IGNORE INDEX
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 Error
|
|
|
|
# Pointer to TABLE_LIST element added to the total table list
|
|
|
|
*/
|
|
|
|
|
2002-12-06 20:11:27 +01:00
|
|
|
TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
|
|
|
|
Table_ident *table,
|
2002-10-30 12:18:52 +01:00
|
|
|
LEX_STRING *alias,
|
2003-01-09 21:42:31 +01:00
|
|
|
ulong table_options,
|
|
|
|
thr_lock_type lock_type,
|
2003-11-28 11:18:13 +01:00
|
|
|
List<String> *use_index_arg,
|
|
|
|
List<String> *ignore_index_arg,
|
2003-07-16 21:30:49 +02:00
|
|
|
LEX_STRING *option)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
register TABLE_LIST *ptr;
|
|
|
|
char *alias_str;
|
2004-09-03 20:43:04 +02:00
|
|
|
LEX *lex= thd->lex;
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_ENTER("add_table_to_list");
|
|
|
|
|
|
|
|
if (!table)
|
|
|
|
DBUG_RETURN(0); // End of memory
|
|
|
|
alias_str= alias ? alias->str : table->table.str;
|
2003-03-14 16:08:42 +01:00
|
|
|
if (check_table_name(table->table.str,table->table.length) ||
|
2001-01-31 03:47:25 +01:00
|
|
|
table->db.str && check_db_name(table->db.str))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!alias) /* Alias is case sensitive */
|
2002-11-28 18:19:21 +01:00
|
|
|
{
|
|
|
|
if (table->sel)
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_DERIVED_MUST_HAVE_ALIAS,
|
|
|
|
ER(ER_DERIVED_MUST_HAVE_ALIAS), MYF(0));
|
2002-11-28 18:19:21 +01:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
2001-08-14 19:33:49 +02:00
|
|
|
if (!(alias_str=thd->memdup(alias_str,table->table.length+1)))
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(0);
|
2002-11-28 18:19:21 +01:00
|
|
|
}
|
2000-08-21 02:07:54 +02:00
|
|
|
if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(0); /* purecov: inspected */
|
2002-11-30 18:58:53 +01:00
|
|
|
if (table->db.str)
|
2002-03-15 22:57:31 +01:00
|
|
|
{
|
|
|
|
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
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
/* The following can't be "" as we may do 'casedn_str()' on it */
|
|
|
|
ptr->db= empty_c_string;
|
2002-03-15 22:57:31 +01:00
|
|
|
ptr->db_length= 0;
|
|
|
|
}
|
2004-10-26 18:30:01 +02:00
|
|
|
if (thd->current_arena->is_stmt_prepare())
|
|
|
|
ptr->db= thd->strdup(ptr->db);
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2002-09-20 13:05:18 +02:00
|
|
|
ptr->alias= alias_str;
|
2003-02-27 11:45:00 +01:00
|
|
|
if (lower_case_table_names && table->table.length)
|
|
|
|
my_casedn_str(files_charset_info, table->table.str);
|
2001-12-13 01:31:19 +01:00
|
|
|
ptr->real_name=table->table.str;
|
2002-03-15 22:57:31 +01:00
|
|
|
ptr->real_name_length=table->table.length;
|
2003-02-12 20:55:37 +01:00
|
|
|
ptr->lock_type= lock_type;
|
2003-01-09 21:42:31 +01:00
|
|
|
ptr->updating= test(table_options & TL_OPTION_UPDATING);
|
|
|
|
ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
|
2003-06-12 13:29:02 +02:00
|
|
|
ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
|
2003-02-12 20:55:37 +01:00
|
|
|
ptr->derived= table->sel;
|
2004-11-13 11:56:39 +01:00
|
|
|
if (!my_strcasecmp(system_charset_info, ptr->db,
|
|
|
|
information_schema_name.str))
|
|
|
|
{
|
|
|
|
ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, ptr->real_name);
|
2004-12-18 11:49:13 +01:00
|
|
|
if (!schema_table ||
|
|
|
|
(schema_table->hidden &&
|
|
|
|
lex->orig_sql_command == SQLCOM_END)) // not a 'show' command
|
2004-11-13 11:56:39 +01:00
|
|
|
{
|
2004-11-13 22:26:15 +01:00
|
|
|
my_error(ER_UNKNOWN_TABLE, MYF(0),
|
|
|
|
ptr->real_name, information_schema_name.str);
|
2004-11-13 11:56:39 +01:00
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
ptr->schema_table= schema_table;
|
|
|
|
}
|
2004-09-03 20:43:04 +02:00
|
|
|
ptr->select_lex= lex->current_select;
|
2004-04-07 12:25:24 +02:00
|
|
|
ptr->cacheable_table= 1;
|
2003-11-28 11:18:13 +01:00
|
|
|
if (use_index_arg)
|
|
|
|
ptr->use_index=(List<String> *) thd->memdup((gptr) use_index_arg,
|
|
|
|
sizeof(*use_index_arg));
|
|
|
|
if (ignore_index_arg)
|
|
|
|
ptr->ignore_index=(List<String> *) thd->memdup((gptr) ignore_index_arg,
|
|
|
|
sizeof(*ignore_index_arg));
|
2003-07-16 21:30:49 +02:00
|
|
|
ptr->option= option ? option->str : 0;
|
2000-07-31 21:29:14 +02:00
|
|
|
/* check that used name is unique */
|
2003-01-09 01:19:14 +01:00
|
|
|
if (lock_type != TL_IGNORE)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-10-30 12:18:52 +01:00
|
|
|
for (TABLE_LIST *tables=(TABLE_LIST*) table_list.first ;
|
2001-12-13 01:31:19 +01:00
|
|
|
tables ;
|
2004-07-16 00:15:55 +02:00
|
|
|
tables=tables->next_local)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2004-03-22 14:43:13 +01:00
|
|
|
if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
|
|
|
|
!strcmp(ptr->db, tables->db))
|
2000-08-21 02:07:54 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str); /* purecov: tested */
|
2000-08-21 02:07:54 +02:00
|
|
|
DBUG_RETURN(0); /* purecov: tested */
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
2004-09-03 20:43:04 +02:00
|
|
|
/* Link table in local list (list for current select) */
|
2004-07-16 00:15:55 +02:00
|
|
|
table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local);
|
2004-09-03 20:43:04 +02:00
|
|
|
/* Link table in global list (all used tables) */
|
2004-09-11 22:52:55 +02:00
|
|
|
lex->add_to_query_tables(ptr);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(ptr);
|
|
|
|
}
|
|
|
|
|
2001-08-02 05:29:50 +02:00
|
|
|
|
2004-06-11 07:27:21 +02:00
|
|
|
/*
|
|
|
|
Initialize a new table list for a nested join
|
|
|
|
|
|
|
|
SYNOPSIS
|
2004-09-11 22:52:55 +02:00
|
|
|
init_table_list()
|
2004-06-11 07:27:21 +02:00
|
|
|
thd current thread
|
2004-09-11 22:52:55 +02:00
|
|
|
|
2004-06-11 07:27:21 +02:00
|
|
|
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");
|
2004-09-11 22:52:55 +02:00
|
|
|
|
2004-06-11 07:27:21 +02:00
|
|
|
if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))) ||
|
|
|
|
!(nested_join= ptr->nested_join=
|
|
|
|
(NESTED_JOIN *) thd->calloc(sizeof(NESTED_JOIN))))
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
join_list->push_front(ptr);
|
|
|
|
ptr->embedding= embedding;
|
|
|
|
ptr->join_list= join_list;
|
|
|
|
embedding= ptr;
|
|
|
|
join_list= &nested_join->join_list;
|
|
|
|
join_list->empty();
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
End a nested join table list
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
end_nested_join()
|
|
|
|
thd current thread
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
The function returns to the previous join nest level.
|
|
|
|
If the current level contains only one member, the function
|
2004-09-11 22:52:55 +02:00
|
|
|
moves it one level up, eliminating the nest.
|
2004-06-11 07:27:21 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
DBUG_ENTER("end_nested_join");
|
|
|
|
ptr= embedding;
|
|
|
|
join_list= ptr->join_list;
|
|
|
|
embedding= ptr->embedding;
|
|
|
|
NESTED_JOIN *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;
|
|
|
|
}
|
|
|
|
DBUG_RETURN(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Nest last join operation
|
|
|
|
|
|
|
|
SYNOPSIS
|
2004-09-11 22:52:55 +02:00
|
|
|
nest_last_join()
|
2004-06-11 07:27:21 +02:00
|
|
|
thd current thread
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
The function nest last join operation as if it was enclosed in braces.
|
|
|
|
|
|
|
|
RETURN VALUE
|
|
|
|
Pointer to TABLE_LIST element created for the new nested join, if success
|
|
|
|
0, otherwise
|
|
|
|
*/
|
|
|
|
|
|
|
|
TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
|
|
|
|
{
|
|
|
|
TABLE_LIST *ptr;
|
|
|
|
NESTED_JOIN *nested_join;
|
|
|
|
DBUG_ENTER("nest_last_join");
|
2004-09-11 22:52:55 +02:00
|
|
|
|
2004-06-11 07:27:21 +02:00
|
|
|
if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))) ||
|
|
|
|
!(nested_join= ptr->nested_join=
|
|
|
|
(NESTED_JOIN *) thd->calloc(sizeof(NESTED_JOIN))))
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
ptr->embedding= embedding;
|
|
|
|
ptr->join_list= join_list;
|
|
|
|
List<TABLE_LIST> *embedded_list= &nested_join->join_list;
|
|
|
|
embedded_list->empty();
|
|
|
|
for (int i=0; i < 2; i++)
|
|
|
|
{
|
|
|
|
TABLE_LIST *table= join_list->pop();
|
|
|
|
table->join_list= embedded_list;
|
|
|
|
table->embedding= ptr;
|
|
|
|
embedded_list->push_back(table);
|
|
|
|
}
|
|
|
|
join_list->push_front(ptr);
|
|
|
|
nested_join->used_tables= nested_join->not_null_tables= (table_map) 0;
|
|
|
|
DBUG_RETURN(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2004-10-07 00:45:06 +02:00
|
|
|
Save names for a join with using clause
|
2004-09-11 22:52:55 +02:00
|
|
|
|
2004-06-11 07:27:21 +02:00
|
|
|
SYNOPSIS
|
|
|
|
save_names_for_using_list
|
|
|
|
tab1 left table in join
|
|
|
|
tab2 right table in join
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
The function saves the full names of the tables in st_select_lex
|
2004-09-11 22:52:55 +02:00
|
|
|
to be able to build later an on expression to replace the using clause.
|
|
|
|
|
2004-06-11 07:27:21 +02:00
|
|
|
RETURN VALUE
|
2004-09-11 22:52:55 +02:00
|
|
|
None
|
|
|
|
*/
|
2004-06-11 07:27:21 +02:00
|
|
|
|
|
|
|
void st_select_lex::save_names_for_using_list(TABLE_LIST *tab1,
|
|
|
|
TABLE_LIST *tab2)
|
|
|
|
{
|
|
|
|
while (tab1->nested_join)
|
|
|
|
{
|
|
|
|
tab1= tab1->nested_join->join_list.head();
|
|
|
|
}
|
|
|
|
db1= tab1->db;
|
|
|
|
table1= tab1->alias;
|
|
|
|
while (tab2->nested_join)
|
|
|
|
{
|
|
|
|
TABLE_LIST *next;
|
|
|
|
List_iterator_fast<TABLE_LIST> it(tab2->nested_join->join_list);
|
|
|
|
tab2= it++;
|
|
|
|
while ((next= it++))
|
|
|
|
tab2= next;
|
|
|
|
}
|
|
|
|
db2= tab2->db;
|
|
|
|
table2= tab2->alias;
|
|
|
|
}
|
2004-09-11 22:52:55 +02:00
|
|
|
|
2004-06-11 07:27:21 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
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
|
2004-09-11 22:52:55 +02:00
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
The function takes the current join list t[0],t[1] ... and
|
2004-06-11 07:27:21 +02:00
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
2004-09-11 22:52:55 +02:00
|
|
|
TABLE_LIST *st_select_lex::convert_right_join()
|
2004-06-11 07:27:21 +02:00
|
|
|
{
|
|
|
|
TABLE_LIST *tab2= join_list->pop();
|
2004-09-11 22:52:55 +02:00
|
|
|
TABLE_LIST *tab1= join_list->pop();
|
2004-06-11 07:27:21 +02:00
|
|
|
DBUG_ENTER("convert_right_join");
|
|
|
|
|
|
|
|
join_list->push_front(tab2);
|
|
|
|
join_list->push_front(tab1);
|
|
|
|
tab1->outer_join|= JOIN_TYPE_RIGHT;
|
|
|
|
|
|
|
|
DBUG_RETURN(tab1);
|
|
|
|
}
|
|
|
|
|
2002-11-16 19:19:10 +01:00
|
|
|
/*
|
|
|
|
Set lock for all tables in current select level
|
|
|
|
|
|
|
|
SYNOPSIS:
|
|
|
|
set_lock_for_tables()
|
|
|
|
lock_type Lock to set for tables
|
|
|
|
|
|
|
|
NOTE:
|
|
|
|
If lock is a write lock, then tables->updating is set 1
|
|
|
|
This is to get tables_ok to know that the table is updated by the
|
|
|
|
query
|
|
|
|
*/
|
|
|
|
|
2002-11-21 21:25:53 +01:00
|
|
|
void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
|
2002-11-16 19:19:10 +01:00
|
|
|
{
|
|
|
|
bool for_update= lock_type >= TL_READ_NO_INSERT;
|
|
|
|
DBUG_ENTER("set_lock_for_tables");
|
|
|
|
DBUG_PRINT("enter", ("lock_type: %d for_update: %d", lock_type,
|
|
|
|
for_update));
|
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first;
|
|
|
|
tables;
|
|
|
|
tables= tables->next_local)
|
2002-11-16 19:19:10 +01:00
|
|
|
{
|
|
|
|
tables->lock_type= lock_type;
|
|
|
|
tables->updating= for_update;
|
|
|
|
}
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
2001-08-02 05:29:50 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
void add_join_on(TABLE_LIST *b,Item *expr)
|
|
|
|
{
|
2002-11-11 14:57:35 +01:00
|
|
|
if (expr)
|
2000-09-25 23:33:25 +02:00
|
|
|
{
|
2002-11-11 14:57:35 +01:00
|
|
|
if (!b->on_expr)
|
|
|
|
b->on_expr=expr;
|
|
|
|
else
|
|
|
|
{
|
2004-07-07 10:29:39 +02:00
|
|
|
/* This only happens if you have both a right and left join */
|
2002-11-11 14:57:35 +01:00
|
|
|
b->on_expr=new Item_cond_and(b->on_expr,expr);
|
|
|
|
}
|
|
|
|
b->on_expr->top_level_item();
|
2000-09-25 23:33:25 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-04-26 19:43:28 +02:00
|
|
|
/*
|
|
|
|
Mark that we have a NATURAL JOIN between two tables
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
add_join_natural()
|
|
|
|
a Table to do normal join with
|
|
|
|
b Do normal join with this table
|
2004-09-11 22:52:55 +02:00
|
|
|
|
2003-04-26 19:43:28 +02:00
|
|
|
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.
|
|
|
|
|
|
|
|
SELECT * FROM t1 NATURAL LEFT JOIN t2
|
|
|
|
<=>
|
|
|
|
SELECT * FROM t1 LEFT JOIN t2 ON (t1.i=t2.i and t1.j=t2.j ... )
|
|
|
|
*/
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
void add_join_natural(TABLE_LIST *a,TABLE_LIST *b)
|
|
|
|
{
|
|
|
|
b->natural_join=a;
|
|
|
|
}
|
|
|
|
|
2002-12-13 11:05:24 +01:00
|
|
|
/*
|
2003-05-15 18:35:39 +02:00
|
|
|
Reload/resets privileges and the different caches.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
reload_acl_and_cache()
|
|
|
|
thd Thread handler
|
|
|
|
options What should be reset/reloaded (tables, privileges,
|
|
|
|
slave...)
|
|
|
|
tables Tables to flush (if any)
|
|
|
|
write_to_binlog Depending on 'options', it may be very bad to write the
|
|
|
|
query to the binlog (e.g. FLUSH SLAVE); this is a
|
|
|
|
pointer where, if it is not NULL, reload_acl_and_cache()
|
|
|
|
will put 0 if it thinks we really should not write to
|
|
|
|
the binlog. Otherwise it will put 1.
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
!=0 error
|
2002-12-13 11:05:24 +01:00
|
|
|
*/
|
|
|
|
|
2003-05-15 18:35:39 +02:00
|
|
|
bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
|
|
|
|
bool *write_to_binlog)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
bool result=0;
|
|
|
|
select_errors=0; /* Write if more errors */
|
2003-05-15 18:35:39 +02:00
|
|
|
bool tmp_write_to_binlog= 1;
|
2003-09-29 08:47:37 +02:00
|
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
2000-07-31 21:29:14 +02:00
|
|
|
if (options & REFRESH_GRANT)
|
|
|
|
{
|
2002-09-05 15:17:08 +02:00
|
|
|
acl_reload(thd);
|
2002-10-29 20:59:03 +01:00
|
|
|
grant_reload(thd);
|
2002-05-15 12:50:38 +02:00
|
|
|
if (mqh_used)
|
2004-12-06 16:15:54 +01:00
|
|
|
reset_mqh((LEX_USER *) NULL,TRUE);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2003-09-29 08:47:37 +02:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
if (options & REFRESH_LOG)
|
|
|
|
{
|
2003-07-06 17:59:54 +02:00
|
|
|
/*
|
2003-08-11 21:44:43 +02:00
|
|
|
Flush the normal query log, the update log, the binary log,
|
|
|
|
the slow query log, and the relay log (if it exists).
|
2003-07-06 17:59:54 +02:00
|
|
|
*/
|
2003-08-11 21:44:43 +02:00
|
|
|
|
2003-05-15 18:35:39 +02:00
|
|
|
/*
|
|
|
|
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;
|
2002-10-16 18:05:10 +02:00
|
|
|
mysql_log.new_file(1);
|
|
|
|
mysql_bin_log.new_file(1);
|
|
|
|
mysql_slow_log.new_file(1);
|
2003-03-13 11:12:28 +01:00
|
|
|
#ifdef HAVE_REPLICATION
|
2004-05-18 10:29:20 +02:00
|
|
|
if (mysql_bin_log.is_open() && expire_logs_days)
|
2003-02-16 17:39:12 +01:00
|
|
|
{
|
|
|
|
long purge_time= time(0) - expire_logs_days*24*60*60;
|
|
|
|
if (purge_time >= 0)
|
2003-04-24 15:29:25 +02:00
|
|
|
mysql_bin_log.purge_logs_before_date(purge_time);
|
2003-02-16 17:39:12 +01:00
|
|
|
}
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_lock(&LOCK_active_mi);
|
2003-07-06 17:59:54 +02:00
|
|
|
rotate_relay_log(active_mi);
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_unlock(&LOCK_active_mi);
|
2003-03-13 11:12:28 +01:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
if (ha_flush_logs())
|
|
|
|
result=1;
|
2003-01-27 18:37:25 +01:00
|
|
|
if (flush_error_log())
|
|
|
|
result=1;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2002-03-22 21:55:08 +01:00
|
|
|
#ifdef HAVE_QUERY_CACHE
|
2001-12-02 13:34:01 +01:00
|
|
|
if (options & REFRESH_QUERY_CACHE_FREE)
|
|
|
|
{
|
2001-12-06 00:05:30 +01:00
|
|
|
query_cache.pack(); // FLUSH QUERY CACHE
|
2004-07-07 10:29:39 +02:00
|
|
|
options &= ~REFRESH_QUERY_CACHE; // Don't flush cache, just free memory
|
2001-12-02 13:34:01 +01:00
|
|
|
}
|
|
|
|
if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
|
|
|
|
{
|
2001-12-06 00:05:30 +01:00
|
|
|
query_cache.flush(); // RESET QUERY CACHE
|
2001-12-02 13:34:01 +01:00
|
|
|
}
|
2002-03-22 21:55:08 +01:00
|
|
|
#endif /*HAVE_QUERY_CACHE*/
|
2003-05-15 18:35:39 +02:00
|
|
|
/*
|
|
|
|
Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too
|
|
|
|
(see sql_yacc.yy)
|
|
|
|
*/
|
|
|
|
if (options & (REFRESH_TABLES | REFRESH_READ_LOCK))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2001-08-14 19:33:49 +02:00
|
|
|
if ((options & REFRESH_READ_LOCK) && thd)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2003-10-08 20:50:05 +02:00
|
|
|
/*
|
|
|
|
Writing to the binlog could cause deadlocks, as we don't log
|
|
|
|
UNLOCK TABLES
|
|
|
|
*/
|
2003-05-15 18:35:39 +02:00
|
|
|
tmp_write_to_binlog= 0;
|
2001-08-14 19:33:49 +02:00
|
|
|
if (lock_global_read_lock(thd))
|
|
|
|
return 1;
|
2004-08-20 16:35:23 +02:00
|
|
|
result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1,
|
|
|
|
tables);
|
2004-12-02 23:02:38 +01:00
|
|
|
if (make_global_read_lock_block_commit(thd))
|
|
|
|
{
|
|
|
|
/* Don't leave things in a half-locked state */
|
|
|
|
unlock_global_read_lock(thd);
|
|
|
|
return 1;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2004-08-20 16:35:23 +02:00
|
|
|
else
|
|
|
|
result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables);
|
2004-07-09 09:55:16 +02:00
|
|
|
my_dbopt_cleanup();
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
if (options & REFRESH_HOSTS)
|
|
|
|
hostname_cache_refresh();
|
2004-12-06 16:15:54 +01:00
|
|
|
if (thd && (options & REFRESH_STATUS))
|
2000-07-31 21:29:14 +02:00
|
|
|
refresh_status();
|
|
|
|
if (options & REFRESH_THREADS)
|
|
|
|
flush_thread_cache();
|
2003-09-08 12:08:53 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2000-07-31 21:29:14 +02:00
|
|
|
if (options & REFRESH_MASTER)
|
2003-05-15 18:35:39 +02:00
|
|
|
{
|
|
|
|
tmp_write_to_binlog= 0;
|
2002-01-20 03:16:52 +01:00
|
|
|
if (reset_master(thd))
|
|
|
|
result=1;
|
2003-05-15 18:35:39 +02:00
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2001-12-13 14:53:18 +01:00
|
|
|
#ifdef OPENSSL
|
2002-01-20 06:46:25 +01:00
|
|
|
if (options & REFRESH_DES_KEY_FILE)
|
|
|
|
{
|
|
|
|
if (des_key_file)
|
|
|
|
result=load_des_key_file(des_key_file);
|
|
|
|
}
|
|
|
|
#endif
|
2003-09-08 12:08:53 +02:00
|
|
|
#ifdef HAVE_REPLICATION
|
2002-05-15 12:50:38 +02:00
|
|
|
if (options & REFRESH_SLAVE)
|
|
|
|
{
|
2003-05-15 18:35:39 +02:00
|
|
|
tmp_write_to_binlog= 0;
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_lock(&LOCK_active_mi);
|
2002-08-08 02:12:02 +02:00
|
|
|
if (reset_slave(thd, active_mi))
|
2002-05-15 12:50:38 +02:00
|
|
|
result=1;
|
2004-03-11 16:23:35 +01:00
|
|
|
pthread_mutex_unlock(&LOCK_active_mi);
|
2002-05-15 12:50:38 +02:00
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2002-05-15 12:50:38 +02:00
|
|
|
if (options & REFRESH_USER_RESOURCES)
|
2004-12-06 16:15:54 +01:00
|
|
|
reset_mqh((LEX_USER *) NULL);
|
2003-05-15 18:35:39 +02:00
|
|
|
if (write_to_binlog)
|
|
|
|
*write_to_binlog= tmp_write_to_binlog;
|
2002-05-15 12:50:38 +02:00
|
|
|
return result;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2002-08-22 15:50:58 +02:00
|
|
|
/*
|
|
|
|
kill on thread
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
kill_one_thread()
|
|
|
|
thd Thread class
|
|
|
|
id Thread id
|
|
|
|
|
|
|
|
NOTES
|
|
|
|
This is written such that we have a short lock on LOCK_thread_count
|
|
|
|
*/
|
|
|
|
|
2003-03-21 06:37:01 +01:00
|
|
|
void kill_one_thread(THD *thd, ulong id, bool only_kill_query)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
THD *tmp;
|
|
|
|
uint error=ER_NO_SUCH_THREAD;
|
2002-08-22 15:50:58 +02:00
|
|
|
VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
|
|
|
|
I_List_iterator<THD> it(threads);
|
2000-07-31 21:29:14 +02:00
|
|
|
while ((tmp=it++))
|
|
|
|
{
|
|
|
|
if (tmp->thread_id == id)
|
|
|
|
{
|
2002-08-22 15:50:58 +02:00
|
|
|
pthread_mutex_lock(&tmp->LOCK_delete); // Lock from delete
|
|
|
|
break;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
2002-08-22 15:50:58 +02:00
|
|
|
if (tmp)
|
|
|
|
{
|
|
|
|
if ((thd->master_access & SUPER_ACL) ||
|
|
|
|
!strcmp(thd->user,tmp->user))
|
|
|
|
{
|
2003-03-31 10:39:46 +02:00
|
|
|
tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
|
2002-08-22 15:50:58 +02:00
|
|
|
error=0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
error=ER_KILL_DENIED_ERROR;
|
|
|
|
pthread_mutex_unlock(&tmp->LOCK_delete);
|
|
|
|
}
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!error)
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
else
|
2004-10-20 03:04:37 +02:00
|
|
|
my_error(error, MYF(0), id);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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)
|
2003-11-18 12:47:27 +01:00
|
|
|
*(ulong*) ptr->value= 0;
|
|
|
|
else if (ptr->type == SHOW_KEY_CACHE_LONG)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Reset value in 'default' key cache.
|
|
|
|
This needs to be recoded when we have thread specific key values
|
|
|
|
*/
|
|
|
|
char *value= (((char*) sql_key_cache) +
|
|
|
|
(uint) ((char*) (ptr->value) -
|
|
|
|
(char*) &dflt_key_cache_var));
|
|
|
|
*(ulong*) value= 0;
|
|
|
|
}
|
2004-09-13 15:48:01 +02:00
|
|
|
else if (ptr->type == SHOW_LONG_STATUS)
|
|
|
|
{
|
|
|
|
THD *thd= current_thd;
|
|
|
|
/* We must update the global status before cleaning up the thread */
|
|
|
|
add_to_status(&global_status_var, &thd->status_var);
|
|
|
|
bzero((char*) &thd->status_var, sizeof(thd->status_var));
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&LOCK_status);
|
|
|
|
}
|
2001-06-01 03:27:59 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* If pointer is not a null pointer, append filename to it */
|
|
|
|
|
2003-12-30 12:14:21 +01:00
|
|
|
static bool append_file_to_dir(THD *thd, const char **filename_ptr,
|
|
|
|
const char *table_name)
|
2001-06-01 03:27:59 +02:00
|
|
|
{
|
2001-10-08 03:58:07 +02:00
|
|
|
char buff[FN_REFLEN],*ptr, *end;
|
2001-06-01 03:27:59 +02:00
|
|
|
if (!*filename_ptr)
|
|
|
|
return 0; // nothing to do
|
|
|
|
|
|
|
|
/* Check that the filename is not too long and it's a hard path */
|
|
|
|
if (strlen(*filename_ptr)+strlen(table_name) >= FN_REFLEN-1 ||
|
|
|
|
!test_if_hard_path(*filename_ptr))
|
|
|
|
{
|
2003-11-18 16:28:00 +01:00
|
|
|
my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr);
|
2001-06-01 03:27:59 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
/* Fix is using unix filename format on dos */
|
|
|
|
strmov(buff,*filename_ptr);
|
2001-10-08 03:58:07 +02:00
|
|
|
end=convert_dirname(buff, *filename_ptr, NullS);
|
2001-12-05 12:03:00 +01:00
|
|
|
if (!(ptr=thd->alloc((uint) (end-buff)+(uint) strlen(table_name)+1)))
|
2001-06-01 03:27:59 +02:00
|
|
|
return 1; // End of memory
|
|
|
|
*filename_ptr=ptr;
|
2001-06-05 02:38:10 +02:00
|
|
|
strxmov(ptr,buff,table_name,NullS);
|
2001-06-01 03:27:59 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2002-07-24 18:55:08 +02:00
|
|
|
|
2003-06-27 15:29:10 +02:00
|
|
|
|
2002-07-24 18:55:08 +02:00
|
|
|
/*
|
|
|
|
Check if the select is a simple select (not an union)
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
check_simple_select()
|
|
|
|
|
|
|
|
RETURN VALUES
|
|
|
|
0 ok
|
|
|
|
1 error ; In this case the error messege is sent to the client
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool check_simple_select()
|
|
|
|
{
|
|
|
|
THD *thd= current_thd;
|
2004-11-11 20:37:48 +01:00
|
|
|
LEX *lex= thd->lex;
|
|
|
|
if (lex->current_select != &lex->select_lex)
|
2002-07-24 18:55:08 +02:00
|
|
|
{
|
|
|
|
char command[80];
|
2004-11-11 20:37:48 +01:00
|
|
|
strmake(command, lex->yylval->symbol.str,
|
|
|
|
min(lex->yylval->symbol.length, sizeof(command)-1));
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command);
|
2002-07-24 18:55:08 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2002-11-07 22:45:19 +01:00
|
|
|
|
2003-10-08 16:40:54 +02:00
|
|
|
|
2003-11-03 11:28:36 +01:00
|
|
|
Comp_creator *comp_eq_creator(bool invert)
|
2002-11-07 22:45:19 +01:00
|
|
|
{
|
2003-11-03 11:28:36 +01:00
|
|
|
return invert?(Comp_creator *)&ne_creator:(Comp_creator *)&eq_creator;
|
2002-11-07 22:45:19 +01:00
|
|
|
}
|
|
|
|
|
2003-10-08 16:40:54 +02:00
|
|
|
|
2003-11-03 11:28:36 +01:00
|
|
|
Comp_creator *comp_ge_creator(bool invert)
|
2002-11-07 22:45:19 +01:00
|
|
|
{
|
2003-11-03 11:28:36 +01:00
|
|
|
return invert?(Comp_creator *)<_creator:(Comp_creator *)&ge_creator;
|
2002-11-07 22:45:19 +01:00
|
|
|
}
|
|
|
|
|
2003-10-08 16:40:54 +02:00
|
|
|
|
2003-11-03 11:28:36 +01:00
|
|
|
Comp_creator *comp_gt_creator(bool invert)
|
2002-11-07 22:45:19 +01:00
|
|
|
{
|
2003-11-03 11:28:36 +01:00
|
|
|
return invert?(Comp_creator *)&le_creator:(Comp_creator *)>_creator;
|
2002-11-07 22:45:19 +01:00
|
|
|
}
|
|
|
|
|
2003-10-08 16:40:54 +02:00
|
|
|
|
2003-11-03 11:28:36 +01:00
|
|
|
Comp_creator *comp_le_creator(bool invert)
|
2002-11-07 22:45:19 +01:00
|
|
|
{
|
2003-11-03 11:28:36 +01:00
|
|
|
return invert?(Comp_creator *)>_creator:(Comp_creator *)&le_creator;
|
2002-11-07 22:45:19 +01:00
|
|
|
}
|
|
|
|
|
2003-10-08 16:40:54 +02:00
|
|
|
|
2003-11-03 11:28:36 +01:00
|
|
|
Comp_creator *comp_lt_creator(bool invert)
|
2002-11-07 22:45:19 +01:00
|
|
|
{
|
2003-11-03 11:28:36 +01:00
|
|
|
return invert?(Comp_creator *)&ge_creator:(Comp_creator *)<_creator;
|
2002-11-07 22:45:19 +01:00
|
|
|
}
|
|
|
|
|
2003-10-08 16:40:54 +02:00
|
|
|
|
2003-11-03 11:28:36 +01:00
|
|
|
Comp_creator *comp_ne_creator(bool invert)
|
2002-11-07 22:45:19 +01:00
|
|
|
{
|
2003-11-03 11:28:36 +01:00
|
|
|
return invert?(Comp_creator *)&eq_creator:(Comp_creator *)&ne_creator;
|
2002-11-07 22:45:19 +01:00
|
|
|
}
|
2003-10-08 16:40:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Construct ALL/ANY/SOME subquery Item
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
all_any_subquery_creator()
|
|
|
|
left_expr - pointer to left expression
|
|
|
|
cmp - compare function creator
|
|
|
|
all - true if we create ALL subquery
|
|
|
|
select_lex - pointer on parsed subquery structure
|
|
|
|
|
|
|
|
RETURN VALUE
|
|
|
|
constructed Item (or 0 if out of memory)
|
|
|
|
*/
|
|
|
|
Item * all_any_subquery_creator(Item *left_expr,
|
|
|
|
chooser_compare_func_creator cmp,
|
|
|
|
bool all,
|
|
|
|
SELECT_LEX *select_lex)
|
|
|
|
{
|
2003-10-25 12:00:53 +02:00
|
|
|
if ((cmp == &comp_eq_creator) && !all) // = ANY <=> IN
|
2003-10-08 16:40:54 +02:00
|
|
|
return new Item_in_subselect(left_expr, select_lex);
|
2003-10-25 12:00:53 +02:00
|
|
|
|
|
|
|
if ((cmp == &comp_ne_creator) && all) // <> ALL <=> NOT IN
|
2003-10-08 16:40:54 +02:00
|
|
|
return new Item_func_not(new Item_in_subselect(left_expr, select_lex));
|
|
|
|
|
|
|
|
Item_allany_subselect *it=
|
2003-10-30 11:57:26 +01:00
|
|
|
new Item_allany_subselect(left_expr, (*cmp)(all), select_lex, all);
|
2003-10-08 16:40:54 +02:00
|
|
|
if (all)
|
|
|
|
return it->upper_not= new Item_func_not_all(it); /* ALL */
|
|
|
|
|
|
|
|
return it; /* ANY/SOME */
|
|
|
|
}
|
2004-04-07 23:16:17 +02:00
|
|
|
|
|
|
|
|
2004-04-08 16:56:45 +02:00
|
|
|
/*
|
|
|
|
CREATE INDEX and DROP INDEX are implemented by calling ALTER TABLE with
|
|
|
|
the proper arguments. This isn't very fast but it should work for most
|
|
|
|
cases.
|
|
|
|
|
|
|
|
In the future ALTER TABLE will notice that only added indexes
|
|
|
|
and create these one by one for the existing table without having to do
|
|
|
|
a full rebuild.
|
|
|
|
|
|
|
|
One should normally create all indexes with CREATE TABLE or ALTER TABLE.
|
|
|
|
*/
|
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
|
2004-04-08 16:56:45 +02:00
|
|
|
{
|
|
|
|
List<create_field> fields;
|
2004-05-21 16:57:03 +02:00
|
|
|
ALTER_INFO alter_info;
|
|
|
|
alter_info.flags= ALTER_ADD_INDEX;
|
2004-04-08 16:56:45 +02:00
|
|
|
HA_CREATE_INFO create_info;
|
|
|
|
DBUG_ENTER("mysql_create_index");
|
|
|
|
bzero((char*) &create_info,sizeof(create_info));
|
|
|
|
create_info.db_type=DB_TYPE_DEFAULT;
|
|
|
|
create_info.default_table_charset= thd->variables.collation_database;
|
|
|
|
DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
|
|
|
|
&create_info, table_list,
|
2004-05-21 16:57:03 +02:00
|
|
|
fields, keys, 0, (ORDER*)0,
|
|
|
|
DUP_ERROR, &alter_info));
|
2004-04-08 16:56:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info)
|
2004-04-08 16:56:45 +02:00
|
|
|
{
|
|
|
|
List<create_field> fields;
|
|
|
|
List<Key> keys;
|
|
|
|
HA_CREATE_INFO create_info;
|
|
|
|
DBUG_ENTER("mysql_drop_index");
|
|
|
|
bzero((char*) &create_info,sizeof(create_info));
|
|
|
|
create_info.db_type=DB_TYPE_DEFAULT;
|
|
|
|
create_info.default_table_charset= thd->variables.collation_database;
|
2004-05-21 16:57:03 +02:00
|
|
|
alter_info->clear();
|
|
|
|
alter_info->flags= ALTER_DROP_INDEX;
|
2004-04-08 16:56:45 +02:00
|
|
|
DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
|
|
|
|
&create_info, table_list,
|
2004-05-21 16:57:03 +02:00
|
|
|
fields, keys, 0, (ORDER*)0,
|
|
|
|
DUP_ERROR, alter_info));
|
2004-04-08 16:56:45 +02:00
|
|
|
}
|
2004-04-08 22:50:10 +02:00
|
|
|
|
|
|
|
|
2004-04-07 23:16:17 +02:00
|
|
|
/*
|
|
|
|
Multi update query pre-check
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
multi_update_precheck()
|
2004-04-12 02:26:32 +02:00
|
|
|
thd Thread handler
|
2004-07-16 00:15:55 +02:00
|
|
|
tables Global/local table list (have to be the same)
|
2004-04-07 23:16:17 +02:00
|
|
|
|
2004-04-10 00:14:32 +02:00
|
|
|
RETURN VALUE
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK
|
|
|
|
TRUE Error
|
2004-04-07 23:16:17 +02:00
|
|
|
*/
|
2004-04-12 02:26:32 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
|
|
|
const char *msg= 0;
|
|
|
|
TABLE_LIST *table;
|
|
|
|
LEX *lex= thd->lex;
|
|
|
|
SELECT_LEX *select_lex= &lex->select_lex;
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ENTER("multi_update_precheck");
|
2004-04-07 23:16:17 +02:00
|
|
|
|
|
|
|
if (select_lex->item_list.elements != lex->value_list.elements)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
Ensure that we have UPDATE or SELECT privilege for each table
|
|
|
|
The exact privilege is checked in mysql_multi_update()
|
|
|
|
*/
|
2004-07-16 00:15:55 +02:00
|
|
|
for (table= tables; table; table= table->next_local)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
2004-10-26 18:30:01 +02:00
|
|
|
if (table->derived)
|
|
|
|
table->grant.privilege= SELECT_ACL;
|
|
|
|
else if ((check_access(thd, UPDATE_ACL, table->db,
|
|
|
|
&table->grant.privilege, 0, 1) ||
|
|
|
|
grant_option &&
|
|
|
|
check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) &&
|
2004-10-29 18:26:52 +02:00
|
|
|
(check_access(thd, SELECT_ACL, table->db,
|
|
|
|
&table->grant.privilege, 0, 0) ||
|
|
|
|
grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-10 00:14:32 +02:00
|
|
|
|
2004-07-16 00:15:55 +02:00
|
|
|
table->table_in_first_from_clause= 1;
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
2004-04-10 00:14:32 +02:00
|
|
|
/*
|
|
|
|
Is there tables of subqueries?
|
|
|
|
*/
|
2004-04-07 23:16:17 +02:00
|
|
|
if (&lex->select_lex != lex->all_selects_list)
|
|
|
|
{
|
2004-10-26 18:30:01 +02:00
|
|
|
DBUG_PRINT("info",("Checking sub query list"));
|
2004-07-16 00:15:55 +02:00
|
|
|
for (table= tables; table; table= table->next_global)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
2004-10-29 18:26:52 +02:00
|
|
|
if (!table->table_in_first_from_clause && table->derived)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
|
|
|
if (check_access(thd, SELECT_ACL, table->db,
|
|
|
|
&table->grant.privilege, 0, 0) ||
|
2004-04-10 00:14:32 +02:00
|
|
|
grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (select_lex->order_list.elements)
|
|
|
|
msg= "ORDER BY";
|
|
|
|
else if (select_lex->select_limit && select_lex->select_limit !=
|
|
|
|
HA_POS_ERROR)
|
|
|
|
msg= "LIMIT";
|
|
|
|
if (msg)
|
|
|
|
{
|
|
|
|
my_error(ER_WRONG_USAGE, MYF(0), "UPDATE", msg);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Multi delete query pre-check
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
multi_delete_precheck()
|
2004-04-12 02:26:32 +02:00
|
|
|
thd Thread handler
|
2004-07-16 00:15:55 +02:00
|
|
|
tables Global/local table list
|
2004-04-12 02:26:32 +02:00
|
|
|
table_count Pointer to table counter
|
2004-04-07 23:16:17 +02:00
|
|
|
|
2004-04-10 00:14:32 +02:00
|
|
|
RETURN VALUE
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK
|
|
|
|
TRUE error
|
2004-04-07 23:16:17 +02:00
|
|
|
*/
|
2004-10-20 03:04:37 +02:00
|
|
|
|
|
|
|
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
|
|
|
SELECT_LEX *select_lex= &thd->lex->select_lex;
|
|
|
|
TABLE_LIST *aux_tables=
|
|
|
|
(TABLE_LIST *)thd->lex->auxilliary_table_list.first;
|
2004-04-10 00:14:32 +02:00
|
|
|
TABLE_LIST *target_tbl;
|
2004-07-16 00:15:55 +02:00
|
|
|
DBUG_ENTER("multi_delete_precheck");
|
2004-04-10 00:14:32 +02:00
|
|
|
|
|
|
|
*table_count= 0;
|
2004-04-07 23:16:17 +02:00
|
|
|
|
|
|
|
/* 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))
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-07 23:16:17 +02:00
|
|
|
if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where)
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
|
|
|
|
ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
2004-07-16 00:15:55 +02:00
|
|
|
for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next_local)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
|
|
|
(*table_count)++;
|
|
|
|
/* All tables in aux_tables must be found in FROM PART */
|
|
|
|
TABLE_LIST *walk;
|
2004-07-16 00:15:55 +02:00
|
|
|
for (walk= tables; walk; walk= walk->next_local)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
2004-04-10 00:14:32 +02:00
|
|
|
if (!my_strcasecmp(table_alias_charset,
|
|
|
|
target_tbl->alias, walk->alias) &&
|
|
|
|
!strcmp(walk->db, target_tbl->db))
|
2004-04-07 23:16:17 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!walk)
|
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
my_error(ER_UNKNOWN_TABLE, MYF(0),
|
|
|
|
target_tbl->real_name, "MULTI DELETE");
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
2004-04-10 00:14:32 +02:00
|
|
|
walk->lock_type= target_tbl->lock_type;
|
2004-07-16 00:15:55 +02:00
|
|
|
target_tbl->correspondent_table= walk; // Remember corresponding table
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
INSERT ... SELECT query pre-check
|
|
|
|
|
|
|
|
SYNOPSIS
|
2004-10-22 17:44:51 +02:00
|
|
|
insert_delete_precheck()
|
2004-04-12 02:26:32 +02:00
|
|
|
thd Thread handler
|
|
|
|
tables Global table list
|
2004-04-07 23:16:17 +02:00
|
|
|
|
2004-04-10 00:14:32 +02:00
|
|
|
RETURN VALUE
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK
|
|
|
|
TRUE Error
|
2004-04-07 23:16:17 +02:00
|
|
|
*/
|
2004-04-12 02:26:32 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool insert_select_precheck(THD *thd, TABLE_LIST *tables)
|
2004-04-07 23:16:17 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("insert_select_precheck");
|
|
|
|
/*
|
|
|
|
Check that we have modify privileges for the first table and
|
|
|
|
select privileges for the rest
|
|
|
|
*/
|
|
|
|
ulong privilege= (thd->lex->duplicates == DUP_REPLACE ?
|
|
|
|
INSERT_ACL | DELETE_ACL : INSERT_ACL);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(check_one_table_access(thd, privilege, tables));
|
2004-04-10 00:14:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
simple UPDATE query pre-check
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
update_precheck()
|
2004-04-12 02:26:32 +02:00
|
|
|
thd Thread handler
|
|
|
|
tables Global table list
|
2004-04-10 00:14:32 +02:00
|
|
|
|
|
|
|
RETURN VALUE
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK
|
|
|
|
TRUE Error
|
2004-04-10 00:14:32 +02:00
|
|
|
*/
|
2004-04-12 02:26:32 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool update_precheck(THD *thd, TABLE_LIST *tables)
|
2004-04-10 00:14:32 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("update_precheck");
|
|
|
|
if (thd->lex->select_lex.item_list.elements != thd->lex->value_list.elements)
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-10 00:14:32 +02:00
|
|
|
}
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(check_db_used(thd, tables) ||
|
|
|
|
check_one_table_access(thd, UPDATE_ACL, tables));
|
2004-04-10 00:14:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
simple DELETE query pre-check
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
delete_precheck()
|
2004-04-12 02:26:32 +02:00
|
|
|
thd Thread handler
|
|
|
|
tables Global table list
|
2004-04-10 00:14:32 +02:00
|
|
|
|
|
|
|
RETURN VALUE
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK
|
|
|
|
TRUE error
|
2004-04-10 00:14:32 +02:00
|
|
|
*/
|
2004-04-12 02:26:32 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool delete_precheck(THD *thd, TABLE_LIST *tables)
|
2004-04-10 00:14:32 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("delete_precheck");
|
|
|
|
if (check_one_table_access(thd, DELETE_ACL, tables))
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-12 02:26:32 +02:00
|
|
|
/* Set privilege for the WHERE clause */
|
2004-04-10 00:14:32 +02:00
|
|
|
tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2004-04-10 00:14:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
simple INSERT query pre-check
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
insert_precheck()
|
2004-04-12 02:26:32 +02:00
|
|
|
thd Thread handler
|
|
|
|
tables Global table list
|
2004-04-10 00:14:32 +02:00
|
|
|
|
|
|
|
RETURN VALUE
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK
|
|
|
|
TRUE error
|
2004-04-10 00:14:32 +02:00
|
|
|
*/
|
2004-04-12 02:26:32 +02:00
|
|
|
|
2004-11-12 14:36:31 +01:00
|
|
|
bool insert_precheck(THD *thd, TABLE_LIST *tables)
|
2004-04-10 00:14:32 +02:00
|
|
|
{
|
|
|
|
LEX *lex= thd->lex;
|
|
|
|
DBUG_ENTER("insert_precheck");
|
|
|
|
|
2004-11-03 11:39:38 +01:00
|
|
|
ulong privilege= (INSERT_ACL |
|
|
|
|
(lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
|
|
|
|
(lex->value_list.elements ? UPDATE_ACL : 0));
|
2004-04-10 00:14:32 +02:00
|
|
|
|
|
|
|
if (check_one_table_access(thd, privilege, tables))
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-10 00:14:32 +02:00
|
|
|
|
|
|
|
if (lex->select_lex.item_list.elements != lex->value_list.elements)
|
|
|
|
{
|
2004-11-12 13:34:00 +01:00
|
|
|
my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(TRUE);
|
2004-04-10 00:14:32 +02:00
|
|
|
}
|
2004-10-20 03:04:37 +02:00
|
|
|
DBUG_RETURN(FALSE);
|
2004-04-07 23:16:17 +02:00
|
|
|
}
|
2004-04-10 00:14:32 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
CREATE TABLE query pre-check
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
create_table_precheck()
|
2004-04-12 02:26:32 +02:00
|
|
|
thd Thread handler
|
|
|
|
tables Global table list
|
|
|
|
create_table Table which will be created
|
2004-04-10 00:14:32 +02:00
|
|
|
|
|
|
|
RETURN VALUE
|
2004-10-20 03:04:37 +02:00
|
|
|
FALSE OK
|
|
|
|
TRUE Error
|
2004-04-10 00:14:32 +02:00
|
|
|
*/
|
2004-04-12 02:26:32 +02:00
|
|
|
|
2004-10-20 03:04:37 +02:00
|
|
|
bool create_table_precheck(THD *thd, TABLE_LIST *tables,
|
|
|
|
TABLE_LIST *create_table)
|
2004-04-10 00:14:32 +02:00
|
|
|
{
|
|
|
|
LEX *lex= thd->lex;
|
2004-10-22 17:44:51 +02:00
|
|
|
SELECT_LEX *select_lex= &lex->select_lex;
|
|
|
|
ulong want_priv;
|
2004-11-12 14:36:31 +01:00
|
|
|
bool error= TRUE; // Error message is given
|
2004-04-10 00:14:32 +02:00
|
|
|
DBUG_ENTER("create_table_precheck");
|
2004-10-22 17:44:51 +02:00
|
|
|
|
|
|
|
want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
|
|
|
|
CREATE_TMP_ACL : CREATE_ACL);
|
2004-04-10 00:14:32 +02:00
|
|
|
lex->create_info.alias= create_table->alias;
|
|
|
|
if (check_access(thd, want_priv, create_table->db,
|
|
|
|
&create_table->grant.privilege, 0, 0) ||
|
|
|
|
check_merge_table_access(thd, create_table->db,
|
|
|
|
(TABLE_LIST *)
|
|
|
|
lex->create_info.merge_list.first))
|
2004-10-22 17:44:51 +02:00
|
|
|
goto err;
|
|
|
|
if (grant_option && want_priv != CREATE_TMP_ACL &&
|
|
|
|
check_grant(thd, want_priv, create_table, 0, UINT_MAX, 0))
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (select_lex->item_list.elements)
|
|
|
|
{
|
|
|
|
/* Check permissions for used tables in CREATE TABLE ... SELECT */
|
|
|
|
|
|
|
|
/*
|
2004-10-29 18:26:52 +02:00
|
|
|
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).
|
2004-10-22 17:44:51 +02:00
|
|
|
*/
|
2004-10-29 18:26:52 +02:00
|
|
|
if (thd->current_arena->is_stmt_prepare())
|
2004-10-22 17:44:51 +02:00
|
|
|
{
|
2004-10-29 18:26:52 +02:00
|
|
|
/*
|
|
|
|
For temporary tables we don't have to check if the created table exists
|
|
|
|
*/
|
|
|
|
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
|
2004-11-03 11:39:38 +01:00
|
|
|
find_table_in_global_list(tables, create_table->db,
|
|
|
|
create_table->real_name))
|
2004-10-29 18:26:52 +02:00
|
|
|
{
|
2004-11-13 18:35:51 +01:00
|
|
|
error= FALSE;
|
2004-10-29 18:26:52 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
2004-10-22 17:44:51 +02:00
|
|
|
if (tables && check_table_access(thd, SELECT_ACL, tables,0))
|
|
|
|
goto err;
|
|
|
|
}
|
2004-11-12 14:36:31 +01:00
|
|
|
error= FALSE;
|
2004-10-22 17:44:51 +02:00
|
|
|
|
|
|
|
err:
|
|
|
|
DBUG_RETURN(error);
|
2004-04-10 00:14:32 +02:00
|
|
|
}
|
2004-08-31 20:10:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
negate given expression
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
negate_expression()
|
2004-10-07 00:45:06 +02:00
|
|
|
thd thread handler
|
2004-08-31 20:10:57 +02:00
|
|
|
expr expression for negation
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
negated expression
|
|
|
|
*/
|
|
|
|
|
|
|
|
Item *negate_expression(THD *thd, Item *expr)
|
|
|
|
{
|
|
|
|
Item *negated;
|
|
|
|
if (expr->type() == Item::FUNC_ITEM &&
|
|
|
|
((Item_func *) expr)->functype() == Item_func::NOT_FUNC)
|
|
|
|
{
|
|
|
|
/* it is NOT(NOT( ... )) */
|
|
|
|
Item *arg= ((Item_func *) expr)->arguments()[0];
|
|
|
|
enum_parsing_place place= thd->lex->current_select->parsing_place;
|
|
|
|
if (arg->is_bool_func() || place == IN_WHERE || place == IN_HAVING)
|
|
|
|
return arg;
|
|
|
|
/*
|
|
|
|
if it is not boolean function then we have to emulate value of
|
|
|
|
not(not(a)), it will be a != 0
|
|
|
|
*/
|
|
|
|
return new Item_func_ne(arg, new Item_int((char*) "0", 0, 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((negated= expr->neg_transformer(thd)) != 0)
|
|
|
|
return negated;
|
|
|
|
return new Item_func_not(expr);
|
|
|
|
}
|