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
|
|
|
|
2002-03-15 22:57:31 +01:00
|
|
|
#define MEM_ROOT_BLOCK_SIZE 8192
|
|
|
|
#define MEM_ROOT_PREALLOC 8192
|
|
|
|
#define TRANS_MEM_ROOT_BLOCK_SIZE 4096
|
|
|
|
#define TRANS_MEM_ROOT_PREALLOC 4096
|
2001-12-05 12:03:00 +01: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")
|
|
|
|
|
2002-11-26 14:18:16 +01:00
|
|
|
extern int yyparse(void *thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
extern "C" pthread_mutex_t THR_LOCK_keycache;
|
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
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
static int check_for_max_user_connections(THD *thd, USER_CONN *uc);
|
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-09-14 01:39:07 +02:00
|
|
|
static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *tables);
|
2000-07-31 21:29:14 +02:00
|
|
|
static void remove_escape(char *name);
|
|
|
|
static void refresh_status(void);
|
2001-12-05 12:03:00 +01:00
|
|
|
static bool append_file_to_dir(THD *thd, char **filename_ptr,
|
|
|
|
char *table_name);
|
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",
|
|
|
|
"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",
|
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;
|
|
|
|
thd->locked_tables=0; // Will be automaticly closed
|
|
|
|
close_thread_tables(thd); // Free tables
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
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
|
|
|
{
|
2000-11-24 00:51:18 +01:00
|
|
|
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
|
|
|
|
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
|
|
|
}
|
2000-12-24 14:19:00 +01:00
|
|
|
return error;
|
2000-11-13 22:55:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-12-26 15:49:10 +01:00
|
|
|
static HASH hash_user_connections;
|
|
|
|
extern pthread_mutex_t LOCK_user_conn;
|
|
|
|
|
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;
|
2002-05-15 12:50:38 +02:00
|
|
|
uint temp_len, user_len, host_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);
|
|
|
|
host_len=strlen(host);
|
|
|
|
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)))))
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
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;
|
|
|
|
if (hash_insert(&hash_user_connections, (byte*) uc))
|
|
|
|
{
|
|
|
|
my_free((char*) uc,0);
|
2002-10-02 12:33:08 +02:00
|
|
|
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;
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
/*
|
2002-01-29 17:32:16 +01:00
|
|
|
Check if user is ok
|
2003-02-14 10:47:41 +01:00
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
check_user()
|
|
|
|
thd Thread handle
|
|
|
|
command Command for connection (for log)
|
|
|
|
user Name of user trying to connect
|
|
|
|
passwd Scrambled password sent from client
|
|
|
|
db Database to connect to
|
|
|
|
check_count If set to 1, don't allow too many connection
|
|
|
|
simple_connect If 1 then client is of old type and we should connect
|
|
|
|
using the old method (no challange)
|
|
|
|
do_send_error Set to 1 if we should send error to user
|
|
|
|
prepared_scramble Buffer to store hash password of new connection
|
|
|
|
had_password Set to 1 if the user gave a password
|
|
|
|
cur_priv_version Check flag to know if someone flushed the privileges
|
|
|
|
since last code
|
|
|
|
hint_user Pointer used by acl_getroot() to remmeber user for
|
|
|
|
next call
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
thd->user, thd->master_access, thd->priv_user, thd->db and
|
|
|
|
thd->db_access are updated
|
|
|
|
1 Access denied; Error sent to client
|
|
|
|
-1 If do_send_error == 1: Failed connect, error sent to client
|
|
|
|
If do_send_error == 0: Prepare for stage of connect
|
2000-07-31 21:29:14 +02:00
|
|
|
*/
|
|
|
|
|
2002-11-30 14:31:58 +01:00
|
|
|
static int check_user(THD *thd,enum_server_command command, const char *user,
|
2002-11-30 18:58:53 +01:00
|
|
|
const char *passwd, const char *db, bool check_count,
|
2002-12-26 17:12:57 +01:00
|
|
|
bool simple_connect, bool do_send_error,
|
2003-02-14 10:47:41 +01:00
|
|
|
char *prepared_scramble, bool had_password,
|
2002-12-26 17:12:57 +01:00
|
|
|
uint *cur_priv_version, ACL_USER** hint_user)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
thd->db=0;
|
2001-12-21 06:00:58 +01:00
|
|
|
thd->db_length=0;
|
2002-05-15 12:50:38 +02:00
|
|
|
USER_RESOURCES ur;
|
2003-02-14 10:47:41 +01:00
|
|
|
DBUG_ENTER("check_user");
|
2002-11-30 18:58:53 +01:00
|
|
|
|
|
|
|
/* We shall avoid dupplicate user allocations here */
|
2002-12-06 20:15:05 +01:00
|
|
|
if (!thd->user && !(thd->user = my_strdup(user, MYF(0))))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_OUT_OF_RESOURCES);
|
2003-02-14 10:47:41 +01:00
|
|
|
DBUG_RETURN(1);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2001-09-30 04:46:20 +02:00
|
|
|
thd->master_access=acl_getroot(thd, thd->host, thd->ip, thd->user,
|
2000-07-31 21:29:14 +02:00
|
|
|
passwd, thd->scramble, &thd->priv_user,
|
2003-01-18 15:39:21 +01:00
|
|
|
(protocol_version == 9 ||
|
|
|
|
!(thd->client_capabilities &
|
|
|
|
CLIENT_LONG_PASSWORD)),
|
2003-02-14 10:47:41 +01:00
|
|
|
&ur,prepared_scramble,
|
2003-01-18 15:39:21 +01:00
|
|
|
cur_priv_version,hint_user);
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2001-10-11 17:58:40 +02:00
|
|
|
DBUG_PRINT("info",
|
2003-01-04 14:17:16 +01:00
|
|
|
("Capabilities: %d packet_length: %ld Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'",
|
2002-07-23 17:31:22 +02:00
|
|
|
thd->client_capabilities, thd->max_client_packet_length,
|
2001-08-14 19:33:49 +02:00
|
|
|
thd->host_or_ip, thd->priv_user,
|
2002-11-24 15:07:53 +01:00
|
|
|
had_password ? "yes": "no",
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->master_access, thd->db ? thd->db : "*none*"));
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2003-01-18 18:31:38 +01:00
|
|
|
/*
|
|
|
|
In case we're going to retry we should not send error message at this
|
|
|
|
point
|
|
|
|
*/
|
2000-07-31 21:29:14 +02:00
|
|
|
if (thd->master_access & NO_ACCESS)
|
|
|
|
{
|
2003-02-14 10:47:41 +01:00
|
|
|
if (do_send_error || !had_password || !*hint_user)
|
2002-11-24 15:07:53 +01:00
|
|
|
{
|
2003-02-14 10:47:41 +01:00
|
|
|
DBUG_PRINT("info",("Access denied"));
|
2003-01-18 18:31:38 +01:00
|
|
|
/*
|
|
|
|
Old client should get nicer error message if password version is
|
|
|
|
not supported
|
|
|
|
*/
|
2002-12-26 17:12:57 +01:00
|
|
|
if (simple_connect && *hint_user && (*hint_user)->pversion)
|
|
|
|
{
|
|
|
|
net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE);
|
|
|
|
mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
net_printf(thd, ER_ACCESS_DENIED_ERROR,
|
|
|
|
thd->user,
|
|
|
|
thd->host_or_ip,
|
|
|
|
had_password ? ER(ER_YES) : ER(ER_NO));
|
|
|
|
mysql_log.write(thd,COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR),
|
|
|
|
thd->user,
|
|
|
|
thd->host_or_ip,
|
|
|
|
had_password ? ER(ER_YES) : ER(ER_NO));
|
|
|
|
}
|
2003-02-14 10:47:41 +01:00
|
|
|
DBUG_RETURN(1); // Error already given
|
2002-11-30 18:58:53 +01:00
|
|
|
}
|
2003-02-14 10:47:41 +01:00
|
|
|
DBUG_PRINT("info",("Prepare for second part of handshake"));
|
|
|
|
DBUG_RETURN(-1); // no report error in special handshake
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
if (check_count)
|
|
|
|
{
|
|
|
|
VOID(pthread_mutex_lock(&LOCK_thread_count));
|
|
|
|
bool tmp=(thread_count - delayed_insert_threads >= max_connections &&
|
2002-06-12 14:04:18 +02:00
|
|
|
!(thd->master_access & SUPER_ACL));
|
2000-07-31 21:29:14 +02:00
|
|
|
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
|
|
|
if (tmp)
|
|
|
|
{ // Too many connections
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd, ER_CON_COUNT_ERROR);
|
2003-02-14 10:47:41 +01:00
|
|
|
DBUG_RETURN(1);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
2000-09-16 03:27:21 +02:00
|
|
|
mysql_log.write(thd,command,
|
2000-07-31 21:29:14 +02:00
|
|
|
(thd->priv_user == thd->user ?
|
|
|
|
(char*) "%s@%s on %s" :
|
|
|
|
(char*) "%s@%s as anonymous on %s"),
|
|
|
|
user,
|
2001-08-14 19:33:49 +02:00
|
|
|
thd->host_or_ip,
|
2000-07-31 21:29:14 +02:00
|
|
|
db ? db : (char*) "");
|
|
|
|
thd->db_access=0;
|
2002-01-29 17:32:16 +01:00
|
|
|
/* Don't allow user to connect if he has done too many queries */
|
2002-05-15 17:41:36 +02:00
|
|
|
if ((ur.questions || ur.updates || ur.connections) &&
|
|
|
|
get_or_create_user_conn(thd,user,thd->host_or_ip,&ur))
|
2003-02-14 10:47:41 +01:00
|
|
|
DBUG_RETURN(1);
|
2002-11-30 18:58:53 +01:00
|
|
|
if (thd->user_connect && thd->user_connect->user_resources.connections &&
|
2002-10-02 12:33:08 +02:00
|
|
|
check_for_max_user_connections(thd, thd->user_connect))
|
2003-02-14 10:47:41 +01:00
|
|
|
DBUG_RETURN(1);
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
if (db && db[0])
|
2001-02-21 00:11:32 +01:00
|
|
|
{
|
2003-02-14 10:47:41 +01:00
|
|
|
int error= test(mysql_change_db(thd,db));
|
2002-02-01 19:53:24 +01:00
|
|
|
if (error && thd->user_connect)
|
|
|
|
decrease_user_connections(thd->user_connect);
|
2003-02-14 10:47:41 +01:00
|
|
|
DBUG_RETURN(error);
|
2001-02-21 00:11:32 +01:00
|
|
|
}
|
2003-02-14 10:47:41 +01:00
|
|
|
send_ok(thd); // Ready to handle questions
|
|
|
|
DBUG_RETURN(0); // ok
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2002-05-15 17:41:36 +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
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
|
2002-06-16 16:06:12 +02:00
|
|
|
if (max_user_connections &&
|
|
|
|
(max_user_connections <= (uint) uc->connections))
|
2001-02-21 00:11:32 +01:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(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-11-30 18:58:53 +01:00
|
|
|
uc->connections++;
|
2002-06-16 16:06:12 +02:00
|
|
|
if (uc->user_resources.connections &&
|
|
|
|
uc->conn_per_hour++ >= uc->user_resources.connections)
|
2002-05-15 12:50:38 +02:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd, ER_USER_LIMIT_REACHED, uc->user,
|
2002-06-16 16:06:12 +02:00
|
|
|
"max_connections",
|
2002-05-15 12:50:38 +02:00
|
|
|
(long) uc->user_resources.connections);
|
|
|
|
error=1;
|
|
|
|
}
|
2001-03-06 14:24:08 +01:00
|
|
|
end:
|
|
|
|
DBUG_RETURN(error);
|
2001-02-21 00:11:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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");
|
2002-06-16 16:06:12 +02:00
|
|
|
if ((uc->connections && !--uc->connections) && !mqh_used)
|
2001-02-21 00:11:32 +01:00
|
|
|
{
|
|
|
|
/* Last connection for user; Delete it */
|
2002-02-01 19:53:24 +01:00
|
|
|
(void) pthread_mutex_lock(&LOCK_user_conn);
|
2001-03-07 22:50:44 +01:00
|
|
|
(void) hash_delete(&hash_user_connections,(byte*) uc);
|
2002-02-01 19:53:24 +01:00
|
|
|
(void) pthread_mutex_unlock(&LOCK_user_conn);
|
2001-02-21 00:11:32 +01:00
|
|
|
}
|
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
|
|
|
|
*/
|
|
|
|
|
|
|
|
char uc_update_queries[SQLCOM_END];
|
|
|
|
|
|
|
|
void init_update_queries(void)
|
|
|
|
{
|
|
|
|
uc_update_queries[SQLCOM_CREATE_TABLE]=1;
|
|
|
|
uc_update_queries[SQLCOM_CREATE_INDEX]=1;
|
|
|
|
uc_update_queries[SQLCOM_ALTER_TABLE]=1;
|
|
|
|
uc_update_queries[SQLCOM_UPDATE]=1;
|
|
|
|
uc_update_queries[SQLCOM_INSERT]=1;
|
|
|
|
uc_update_queries[SQLCOM_INSERT_SELECT]=1;
|
|
|
|
uc_update_queries[SQLCOM_DELETE]=1;
|
|
|
|
uc_update_queries[SQLCOM_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;
|
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)
|
|
|
|
{
|
|
|
|
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
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions",
|
2002-06-20 15:46:25 +02:00
|
|
|
(long) uc->user_resources.questions);
|
|
|
|
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)
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd, ER_USER_LIMIT_REACHED, uc->user, "max_updates",
|
2002-06-27 10:27:04 +02:00
|
|
|
(long) uc->user_resources.updates);
|
|
|
|
error=1;
|
|
|
|
goto end;
|
|
|
|
}
|
2002-01-29 17:32:16 +01:00
|
|
|
}
|
|
|
|
end:
|
2002-02-01 19:53:24 +01:00
|
|
|
DBUG_RETURN(error);
|
2002-01-29 17:32:16 +01:00
|
|
|
}
|
|
|
|
|
2002-02-13 21:37:19 +01:00
|
|
|
|
2002-11-29 15:40:18 +01:00
|
|
|
static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0)
|
2002-02-01 19:53:24 +01:00
|
|
|
{
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
2002-05-15 12:50:38 +02:00
|
|
|
else // for FLUSH PRIVILEGES and FLUSH USER_RESOURCES
|
2002-02-01 19:53:24 +01:00
|
|
|
{
|
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
|
|
|
{
|
2002-05-15 12:50:38 +02:00
|
|
|
USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections, idx);
|
|
|
|
if (get_them)
|
|
|
|
get_mqh(uc->user,uc->host,uc);
|
|
|
|
uc->questions=0;
|
|
|
|
uc->updates=0;
|
|
|
|
uc->conn_per_hour=0;
|
2002-02-01 19:53:24 +01:00
|
|
|
}
|
|
|
|
}
|
2002-02-13 21:37:19 +01:00
|
|
|
(void) pthread_mutex_unlock(&LOCK_user_conn);
|
2002-02-01 19:53:24 +01:00
|
|
|
}
|
2002-01-29 17:32:16 +01:00
|
|
|
|
2002-02-13 21:37:19 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/*
|
2003-02-14 10:47:41 +01:00
|
|
|
Check connnectionn and get priviliges
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
check_connections
|
|
|
|
thd Thread handle
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
-1 Error, which is sent to user
|
|
|
|
> 0 Error code (not sent to user)
|
2000-07-31 21:29:14 +02:00
|
|
|
*/
|
|
|
|
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-07-31 21:29:14 +02:00
|
|
|
static int
|
|
|
|
check_connections(THD *thd)
|
|
|
|
{
|
2003-02-14 10:47:41 +01:00
|
|
|
int res;
|
2000-07-31 21:29:14 +02:00
|
|
|
uint connect_errors=0;
|
2003-02-14 10:47:41 +01:00
|
|
|
uint cur_priv_version;
|
|
|
|
bool using_password;
|
2000-07-31 21:29:14 +02:00
|
|
|
NET *net= &thd->net;
|
2003-01-18 18:31:38 +01:00
|
|
|
char *end, *user, *passwd, *db;
|
|
|
|
char prepared_scramble[SCRAMBLE41_LENGTH+4]; /* Buffer for scramble&hash */
|
|
|
|
ACL_USER* cached_user=NULL; /* Initialise to NULL for first stage */
|
2001-10-11 17:58:40 +02:00
|
|
|
DBUG_PRINT("info",("New connection received on %s",
|
2003-01-18 15:39:21 +01:00
|
|
|
vio_description(net->vio)));
|
2003-02-14 10:47:41 +01:00
|
|
|
|
2003-02-12 20:55:37 +01:00
|
|
|
/* Remove warning from valgrind. TODO: Fix it in password.c */
|
2003-02-14 10:47:41 +01:00
|
|
|
bzero((char*) &prepared_scramble[0], sizeof(prepared_scramble));
|
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
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
if (vio_peer_addr(net->vio,ip))
|
|
|
|
return (ER_BAD_HOST_ERROR);
|
|
|
|
if (!(thd->ip = my_strdup(ip,MYF(0))))
|
|
|
|
return (ER_OUT_OF_RESOURCES);
|
2001-08-14 19:33:49 +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"))
|
2000-09-02 06:58:42 +02:00
|
|
|
thd->host=(char*) localhost;
|
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)
|
|
|
|
thd->host[min(strlen(thd->host), HOSTNAME_LENGTH)]= 0;
|
|
|
|
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));
|
2001-08-14 19:33:49 +02:00
|
|
|
thd->host_or_ip=thd->host;
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->ip=0;
|
|
|
|
bzero((char*) &thd->remote,sizeof(struct sockaddr));
|
|
|
|
}
|
2002-12-26 17:26:37 +01:00
|
|
|
/* Ensure that wrong hostnames doesn't cause buffer overflows */
|
2000-07-31 21:29:14 +02:00
|
|
|
vio_keepalive(net->vio, TRUE);
|
|
|
|
|
2001-12-05 12:03:00 +01:00
|
|
|
ulong pkt_len=0;
|
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-01-18 15:39:21 +01:00
|
|
|
char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH+64];
|
2003-02-12 20:55:37 +01:00
|
|
|
int 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)
|
|
|
|
client_flags |= CLIENT_SSL; /* Wow, SSL is avalaible! */
|
|
|
|
#endif /* HAVE_OPENSSL */
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2001-12-05 12:03:00 +01:00
|
|
|
end=strnmov(buff,server_version,SERVER_VERSION_LENGTH)+1;
|
2000-07-31 21:29:14 +02:00
|
|
|
int4store((uchar*) end,thd->thread_id);
|
|
|
|
end+=4;
|
|
|
|
memcpy(end,thd->scramble,SCRAMBLE_LENGTH+1);
|
|
|
|
end+=SCRAMBLE_LENGTH +1;
|
|
|
|
int2store(end,client_flags);
|
2001-12-05 12:03:00 +01:00
|
|
|
end[2]=(char) MY_CHARSET_CURRENT;
|
2000-07-31 21:29:14 +02:00
|
|
|
int2store(end+3,thd->server_status);
|
|
|
|
bzero(end+5,13);
|
|
|
|
end+=18;
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2002-11-24 15:07:53 +01:00
|
|
|
// At this point we write connection message and read reply
|
2002-10-02 12:33:08 +02:00
|
|
|
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-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);
|
|
|
|
end= (char*) net->read_pos+8;
|
|
|
|
}
|
|
|
|
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. */
|
|
|
|
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"));
|
|
|
|
if ((pkt_len=my_net_read(net)) == packet_error ||
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2003-01-18 18:31:38 +01:00
|
|
|
user= end;
|
|
|
|
passwd= strend(user)+1;
|
|
|
|
db=0;
|
2003-02-14 10:47:41 +01:00
|
|
|
using_password= test(passwd[0]);
|
2002-11-30 18:58:53 +01:00
|
|
|
if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB)
|
2003-02-12 20:55:37 +01:00
|
|
|
db=strend(passwd)+1;
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2002-11-24 15:07:53 +01:00
|
|
|
/* We can get only old hash at this point */
|
2003-02-14 10:47:41 +01:00
|
|
|
if (using_password && strlen(passwd) != SCRAMBLE_LENGTH)
|
2003-02-12 20:55:37 +01:00
|
|
|
return ER_HANDSHAKE_ERROR;
|
2002-11-30 18:58:53 +01:00
|
|
|
|
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
|
|
|
|
2002-11-24 15:07:53 +01:00
|
|
|
/* Simple connect only for old clients. New clients always use secure auth */
|
|
|
|
bool simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION));
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2002-11-24 15:07:53 +01:00
|
|
|
/* Check user permissions. If password failure we'll get scramble back */
|
2003-02-14 10:47:41 +01:00
|
|
|
if ((res=check_user(thd, COM_CONNECT, user, passwd, db, 1, simple_connect,
|
|
|
|
simple_connect, prepared_scramble, using_password,
|
|
|
|
&cur_priv_version,
|
|
|
|
&cached_user)) < 0)
|
2002-11-30 18:58:53 +01:00
|
|
|
{
|
2002-11-24 15:07:53 +01:00
|
|
|
/* Store current used and database as they are erased with next packet */
|
|
|
|
char tmp_user[USERNAME_LENGTH+1];
|
|
|
|
char tmp_db[NAME_LEN+1];
|
|
|
|
|
2003-02-14 10:47:41 +01:00
|
|
|
/* If the client is old we just have to return error */
|
2003-01-18 18:31:38 +01:00
|
|
|
if (simple_connect)
|
|
|
|
return -1;
|
|
|
|
|
2003-02-14 10:47:41 +01:00
|
|
|
DBUG_PRINT("info",("password challenge"));
|
|
|
|
|
|
|
|
tmp_user[0]= tmp_db[0]= 0;
|
2002-11-24 15:07:53 +01:00
|
|
|
if (user)
|
2002-11-30 18:58:53 +01:00
|
|
|
strmake(tmp_user,user,USERNAME_LENGTH);
|
2002-11-24 15:07:53 +01:00
|
|
|
if (db)
|
2002-11-30 18:58:53 +01:00
|
|
|
strmake(tmp_db,db,NAME_LEN);
|
|
|
|
|
2002-11-24 15:07:53 +01:00
|
|
|
/* Write hash and encrypted scramble to client */
|
2002-12-05 01:55:29 +01:00
|
|
|
if (my_net_write(net,prepared_scramble,SCRAMBLE41_LENGTH+4) ||
|
|
|
|
net_flush(net))
|
2003-02-12 20:55:37 +01:00
|
|
|
{
|
|
|
|
inc_host_errors(&thd->remote.sin_addr);
|
|
|
|
return ER_HANDSHAKE_ERROR;
|
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
/* Reading packet back */
|
2003-01-18 18:31:38 +01:00
|
|
|
if ((pkt_len= my_net_read(net)) == packet_error)
|
|
|
|
{
|
|
|
|
inc_host_errors(&thd->remote.sin_addr);
|
|
|
|
return ER_HANDSHAKE_ERROR;
|
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
/* We have to get very specific packet size */
|
2003-01-18 18:31:38 +01:00
|
|
|
if (pkt_len != SCRAMBLE41_LENGTH)
|
|
|
|
{
|
|
|
|
inc_host_errors(&thd->remote.sin_addr);
|
|
|
|
return ER_HANDSHAKE_ERROR;
|
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
/* Final attempt to check the user based on reply */
|
|
|
|
if (check_user(thd,COM_CONNECT, tmp_user, (char*)net->read_pos,
|
2003-02-12 20:55:37 +01:00
|
|
|
tmp_db, 1, 0, 1, prepared_scramble, using_password,
|
|
|
|
&cur_priv_version,
|
|
|
|
&cached_user))
|
2002-11-24 15:07:53 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2003-02-14 10:47:41 +01:00
|
|
|
else if (res)
|
|
|
|
return -1; // Error sent from check_user()
|
2002-11-24 15:07:53 +01:00
|
|
|
thd->password=using_password;
|
2000-07-31 21:29:14 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-02-14 10:47:41 +01:00
|
|
|
|
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
|
|
|
|
// 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
|
|
|
{
|
|
|
|
close_connection(&thd->net,ER_OUT_OF_RESOURCES);
|
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));
|
|
|
|
// now that we've called my_thread_init(), it is safe to call DBUG_*
|
|
|
|
|
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())
|
|
|
|
{
|
|
|
|
close_connection(&thd->net,ER_OUT_OF_RESOURCES);
|
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;
|
|
|
|
|
|
|
|
if ((error=check_connections(thd)))
|
|
|
|
{ // Wrong permissions
|
|
|
|
if (error > 0)
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(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
|
|
|
|
|
2000-08-21 02:07:54 +02:00
|
|
|
thd->proc_info=0; // Remove 'login'
|
2000-09-20 03:54:10 +02:00
|
|
|
thd->command=COM_SLEEP;
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->version=refresh_version;
|
|
|
|
thd->set_time();
|
2002-03-15 22:57:31 +01:00
|
|
|
init_sql_alloc(&thd->mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
|
|
|
|
init_sql_alloc(&thd->transaction.mem_root,
|
|
|
|
TRANS_MEM_ROOT_BLOCK_SIZE, TRANS_MEM_ROOT_PREALLOC);
|
2000-07-31 21:29:14 +02:00
|
|
|
while (!net->error && net->vio != 0 && !thd->killed)
|
|
|
|
{
|
|
|
|
if (do_command(thd))
|
|
|
|
break;
|
|
|
|
}
|
2002-02-01 19:53:24 +01:00
|
|
|
if (thd->user_connect)
|
|
|
|
decrease_user_connections(thd->user_connect);
|
2000-09-12 02:02:33 +02:00
|
|
|
free_root(&thd->mem_root,MYF(0));
|
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
|
|
|
{
|
2002-07-23 17:31:22 +02:00
|
|
|
if (!thd->killed && thd->variables.log_warnings)
|
2002-01-15 23:42:52 +01:00
|
|
|
sql_print_error(ER(ER_NEW_ABORTING_CONNECTION),
|
|
|
|
thd->thread_id,(thd->db ? thd->db : "unconnected"),
|
|
|
|
thd->user ? thd->user : "unauthenticated",
|
|
|
|
thd->host_or_ip,
|
|
|
|
(net->last_errno ? ER(net->last_errno) :
|
|
|
|
ER(ER_UNKNOWN_ERROR)));
|
2002-10-02 12:33:08 +02:00
|
|
|
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
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
end_thread:
|
|
|
|
close_connection(net);
|
|
|
|
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 */
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
|
|
|
close_connection(&thd->net,ER_OUT_OF_RESOURCES);
|
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");
|
|
|
|
|
|
|
|
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
|
|
|
|
|
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;
|
2002-03-15 22:57:31 +01:00
|
|
|
init_sql_alloc(&thd->mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
|
|
|
|
init_sql_alloc(&thd->transaction.mem_root,
|
|
|
|
TRANS_MEM_ROOT_BLOCK_SIZE, TRANS_MEM_ROOT_PREALLOC);
|
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);
|
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;
|
|
|
|
thd->current_tablenr=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';
|
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
|
|
|
|
free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
|
|
|
|
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;
|
2000-09-12 02:02:33 +02: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:
|
|
|
|
(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);
|
|
|
|
DBUG_RETURN(0); // Never reached
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
|
|
|
|
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
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
for (; item ; item=item->next)
|
2000-07-31 21:29:14 +02:00
|
|
|
delete item;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2000-07-31 21:29:14 +02:00
|
|
|
table_list->db = db;
|
2002-09-20 13:05:18 +02:00
|
|
|
table_list->real_name = table_list->alias = tbl_name;
|
2000-07-31 21:29:14 +02:00
|
|
|
table_list->lock_type = TL_READ_NO_INSERT;
|
|
|
|
table_list->next = 0;
|
|
|
|
|
2001-01-31 03:47:25 +01:00
|
|
|
if (!db || check_db_name(db))
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_WRONG_DB_NAME, 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);
|
|
|
|
|
2000-09-14 01:39:07 +02:00
|
|
|
if (check_access(thd, SELECT_ACL, db, &table_list->grant.privilege))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto err;
|
2000-09-14 01:39:07 +02:00
|
|
|
if (grant_option && check_grant(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;
|
2001-10-08 22:20:19 +02:00
|
|
|
if ((error = mysqld_dump_create_info(thd, table, -1)))
|
|
|
|
{
|
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)))
|
2001-08-14 19:33:49 +02:00
|
|
|
my_error(ER_GET_ERRNO, MYF(0));
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-17 13:24:51 +02:00
|
|
|
/* Execute one command from socket (query or simple command) */
|
2003-02-07 14:47:24 +01:00
|
|
|
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
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;
|
|
|
|
thd->current_tablenr=0;
|
|
|
|
|
|
|
|
packet=0;
|
2002-07-23 17:31:22 +02:00
|
|
|
old_timeout=net->read_timeout;
|
|
|
|
// Wait max for 8 hours
|
|
|
|
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)
|
|
|
|
DBUG_RETURN(TRUE); // We have to close it.
|
2003-01-04 14:37:20 +01:00
|
|
|
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
|
|
|
|
{
|
|
|
|
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
|
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
|
|
|
|
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;
|
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
|
|
|
|
*/
|
2001-04-11 13:04:03 +02:00
|
|
|
bool slow_command=FALSE;
|
|
|
|
DBUG_ENTER("dispatch_command");
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->command=command;
|
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++;
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
2002-07-23 17:31:22 +02:00
|
|
|
|
2001-08-14 19:33:49 +02:00
|
|
|
thd->lex.select_lex.options=0; // We store status here
|
2001-04-11 13:04:03 +02:00
|
|
|
switch (command) {
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_INIT_DB:
|
2002-11-16 19:19:10 +01:00
|
|
|
statistic_increment(com_stat[SQLCOM_CHANGE_DB],&LOCK_status);
|
2001-04-11 13:04:03 +02:00
|
|
|
if (!mysql_change_db(thd,packet))
|
2000-09-16 03:27:21 +02:00
|
|
|
mysql_log.write(thd,command,"%s",thd->db);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
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:
|
|
|
|
{
|
2002-11-16 19:19:10 +01:00
|
|
|
statistic_increment(com_other, &LOCK_status);
|
2001-02-16 22:46:52 +01:00
|
|
|
slow_command = TRUE;
|
2001-04-11 13:04:03 +02:00
|
|
|
uint db_len = *(uchar*)packet;
|
|
|
|
uint tbl_len = *(uchar*)(packet + db_len + 1);
|
2001-07-11 09:36:22 +02:00
|
|
|
char* db = thd->alloc(db_len + tbl_len + 2);
|
2001-04-11 13:04:03 +02:00
|
|
|
memcpy(db, packet + 1, db_len);
|
2000-07-31 21:29:14 +02:00
|
|
|
char* tbl_name = db + db_len;
|
|
|
|
*tbl_name++ = 0;
|
2001-04-11 13:04:03 +02:00
|
|
|
memcpy(tbl_name, packet + db_len + 2, tbl_len);
|
2000-07-31 21:29:14 +02:00
|
|
|
tbl_name[tbl_len] = 0;
|
2001-11-06 23:13:29 +01:00
|
|
|
if (mysql_table_dump(thd, db, tbl_name, -1))
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd); // dump to NET
|
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_CHANGE_USER:
|
|
|
|
{
|
2002-11-16 19:19:10 +01:00
|
|
|
thd->change_user();
|
2003-02-04 02:19:19 +01:00
|
|
|
thd->clear_error(); // If errors from rollback
|
2002-11-16 19:19:10 +01:00
|
|
|
|
|
|
|
statistic_increment(com_other,&LOCK_status);
|
2001-04-11 13:04:03 +02:00
|
|
|
char *user= (char*) packet;
|
2000-07-31 21:29:14 +02:00
|
|
|
char *passwd= strend(user)+1;
|
|
|
|
char *db= strend(passwd)+1;
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/* Save user and privileges */
|
|
|
|
uint save_master_access=thd->master_access;
|
|
|
|
uint save_db_access= thd->db_access;
|
2001-12-21 06:00:58 +01:00
|
|
|
uint save_db_length= thd->db_length;
|
2000-07-31 21:29:14 +02:00
|
|
|
char *save_user= thd->user;
|
2002-11-30 14:31:58 +01:00
|
|
|
thd->user=NULL; /* Needed for check_user to allocate new user */
|
2000-07-31 21:29:14 +02:00
|
|
|
char *save_priv_user= thd->priv_user;
|
|
|
|
char *save_db= thd->db;
|
2002-11-30 14:31:58 +01:00
|
|
|
USER_CONN *save_uc= thd->user_connect;
|
|
|
|
bool simple_connect;
|
|
|
|
bool using_password;
|
2003-01-18 18:31:38 +01:00
|
|
|
char prepared_scramble[SCRAMBLE41_LENGTH+4];/* Buffer for scramble,hash */
|
|
|
|
char tmp_user[USERNAME_LENGTH+1];
|
|
|
|
char tmp_db[NAME_LEN+1];
|
|
|
|
ACL_USER* cached_user ; /* Cached user */
|
|
|
|
uint cur_priv_version; /* Cached grant version */
|
2003-03-04 14:02:49 +01:00
|
|
|
int res;
|
|
|
|
ulong pkt_len= 0; /* Length of reply packet */
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2003-02-14 10:47:41 +01:00
|
|
|
bzero((char*) prepared_scramble, sizeof(prepared_scramble));
|
2002-11-30 14:31:58 +01:00
|
|
|
/* Small check for incomming packet */
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
if ((uint) ((uchar*) db - net->read_pos) > packet_length)
|
2002-11-30 14:31:58 +01:00
|
|
|
goto restore_user_err;
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2002-11-30 14:31:58 +01:00
|
|
|
/* Now we shall basically perform authentication again */
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2002-11-30 14:31:58 +01:00
|
|
|
/* We can get only old hash at this point */
|
2002-11-30 18:58:53 +01:00
|
|
|
if (passwd[0] && strlen(passwd)!=SCRAMBLE_LENGTH)
|
2002-11-30 14:31:58 +01:00
|
|
|
goto restore_user_err;
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2002-12-05 01:55:29 +01:00
|
|
|
cached_user= NULL;
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2002-11-30 14:31:58 +01:00
|
|
|
/* Simple connect only for old clients. New clients always use sec. auth*/
|
|
|
|
simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION));
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2002-11-30 14:31:58 +01:00
|
|
|
/* Store information if we used password. passwd will be dammaged */
|
|
|
|
using_password=test(passwd[0]);
|
2002-11-30 18:58:53 +01:00
|
|
|
|
|
|
|
if (simple_connect) /* Restore scramble for old clients */
|
|
|
|
memcpy(thd->scramble,thd->old_scramble,9);
|
|
|
|
|
|
|
|
/*
|
2003-01-18 18:31:38 +01:00
|
|
|
Check user permissions. If password failure we'll get scramble back
|
|
|
|
Do not retry if we already have sent error (result>0)
|
2002-11-30 18:58:53 +01:00
|
|
|
*/
|
2003-02-14 10:47:41 +01:00
|
|
|
if ((res=check_user(thd,COM_CHANGE_USER, user, passwd, db, 0,
|
|
|
|
simple_connect, simple_connect, prepared_scramble,
|
|
|
|
using_password, &cur_priv_version, &cached_user)) < 0)
|
2002-11-30 14:31:58 +01:00
|
|
|
{
|
2003-02-14 10:47:41 +01:00
|
|
|
/* If the client is old we just have to have auth failure */
|
2002-11-30 14:31:58 +01:00
|
|
|
if (simple_connect)
|
2003-02-14 10:47:41 +01:00
|
|
|
goto restore_user; /* Error is already reported */
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2002-11-30 14:31:58 +01:00
|
|
|
/* Store current used and database as they are erased with next packet */
|
2003-02-12 20:55:37 +01:00
|
|
|
tmp_user[0]= tmp_db[0]= 0;
|
2002-11-30 14:31:58 +01:00
|
|
|
if (user)
|
2002-12-05 01:55:29 +01:00
|
|
|
strmake(tmp_user,user,USERNAME_LENGTH);
|
2002-11-30 14:31:58 +01:00
|
|
|
if (db)
|
2002-12-05 01:55:29 +01:00
|
|
|
strmake(tmp_db,db,NAME_LEN);
|
|
|
|
|
2002-11-30 14:31:58 +01:00
|
|
|
/* Write hash and encrypted scramble to client */
|
2002-12-05 01:55:29 +01:00
|
|
|
if (my_net_write(net,prepared_scramble,SCRAMBLE41_LENGTH+4) ||
|
|
|
|
net_flush(net))
|
2002-11-30 14:31:58 +01:00
|
|
|
goto restore_user_err;
|
2002-11-30 18:58:53 +01:00
|
|
|
|
|
|
|
/* Reading packet back */
|
|
|
|
if ((pkt_len=my_net_read(net)) == packet_error)
|
|
|
|
goto restore_user_err;
|
|
|
|
|
|
|
|
/* We have to get very specific packet size */
|
2003-02-14 10:47:41 +01:00
|
|
|
if (pkt_len != SCRAMBLE41_LENGTH)
|
2002-11-30 14:31:58 +01:00
|
|
|
goto restore_user;
|
2002-11-30 18:58:53 +01:00
|
|
|
|
|
|
|
/* Final attempt to check the user based on reply */
|
2003-01-18 18:31:38 +01:00
|
|
|
if (check_user(thd,COM_CHANGE_USER, tmp_user, (char*) net->read_pos,
|
2003-02-14 10:47:41 +01:00
|
|
|
tmp_db, 0, 0, 1, prepared_scramble, using_password,
|
|
|
|
&cur_priv_version, &cached_user))
|
2002-11-30 14:31:58 +01:00
|
|
|
goto restore_user;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2003-02-14 10:47:41 +01:00
|
|
|
else if (res)
|
|
|
|
goto restore_user;
|
|
|
|
|
2002-11-30 14:31:58 +01:00
|
|
|
/* Finally we've authenticated new user */
|
2002-02-01 19:53:24 +01:00
|
|
|
if (max_connections && save_uc)
|
2002-02-13 21:37:19 +01:00
|
|
|
decrease_user_connections(save_uc);
|
2000-07-31 21:29:14 +02:00
|
|
|
x_free((gptr) save_db);
|
|
|
|
x_free((gptr) save_user);
|
2002-11-30 14:31:58 +01:00
|
|
|
thd->password=using_password;
|
|
|
|
break;
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2002-11-30 14:31:58 +01:00
|
|
|
/* Bad luck we shall restore old user */
|
2003-02-14 10:47:41 +01:00
|
|
|
restore_user_err:
|
2002-11-30 14:31:58 +01:00
|
|
|
send_error(thd, ER_UNKNOWN_COM_ERROR);
|
2002-11-30 18:58:53 +01:00
|
|
|
|
2003-02-14 10:47:41 +01:00
|
|
|
restore_user:
|
2002-11-30 14:31:58 +01:00
|
|
|
x_free(thd->user);
|
|
|
|
thd->master_access=save_master_access;
|
|
|
|
thd->db_access=save_db_access;
|
|
|
|
thd->db=save_db;
|
|
|
|
thd->db_length=save_db_length;
|
|
|
|
thd->user=save_user;
|
2002-11-30 18:58:53 +01:00
|
|
|
thd->priv_user=save_priv_user;
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
2002-06-12 23:13:12 +02:00
|
|
|
case COM_EXECUTE:
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
mysql_stmt_execute(thd, packet);
|
2002-06-12 23:13:12 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
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
|
|
|
|
2003-02-04 05:44:29 +01:00
|
|
|
while (!thd->killed && !thd->is_fatal_error && thd->lex.found_colon)
|
2003-01-20 23:00:50 +01:00
|
|
|
{
|
2003-01-23 20:49:28 +01:00
|
|
|
char *packet= thd->lex.found_colon;
|
2003-01-20 23:00:50 +01:00
|
|
|
/*
|
|
|
|
Multiple queries exits, execute them individually
|
|
|
|
*/
|
|
|
|
if (thd->lock || thd->open_tables || thd->derived_tables)
|
|
|
|
close_thread_tables(thd);
|
|
|
|
|
2003-01-23 20:49:28 +01:00
|
|
|
ulong length= thd->query_length-(ulong)(thd->lex.found_colon-thd->query);
|
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--;
|
|
|
|
}
|
|
|
|
thd->query_length= length;
|
2003-01-23 20:49:28 +01:00
|
|
|
thd->query= packet;
|
|
|
|
VOID(pthread_mutex_lock(&LOCK_thread_count));
|
|
|
|
thd->query_id= query_id++;
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
2003-01-20 23:00:50 +01:00
|
|
|
mysql_parse(thd, packet, length);
|
|
|
|
}
|
|
|
|
|
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
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
#else
|
|
|
|
{
|
|
|
|
char *fields;
|
|
|
|
TABLE_LIST table_list;
|
2002-11-16 19:19:10 +01:00
|
|
|
statistic_increment(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))
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NO_DB_ERROR);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
thd->free_list=0;
|
2002-09-20 13:05:18 +02:00
|
|
|
table_list.alias= table_list.real_name= thd->strdup(packet);
|
2001-12-17 02:02:58 +01:00
|
|
|
packet=strend(packet)+1;
|
2001-12-21 06:00:58 +01: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
|
|
|
|
|
|
|
|
if (check_access(thd,SELECT_ACL,table_list.db,&thd->col_access))
|
|
|
|
break;
|
|
|
|
table_list.grant.privilege=thd->col_access;
|
|
|
|
if (grant_option && check_grant(thd,SELECT_ACL,&table_list,2))
|
|
|
|
break;
|
|
|
|
mysqld_list_fields(thd,&table_list,fields);
|
2002-10-02 12:33:08 +02:00
|
|
|
free_items(thd->free_list);
|
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
|
|
|
{
|
2002-11-16 19:19:10 +01:00
|
|
|
statistic_increment(com_stat[SQLCOM_CREATE_DB],&LOCK_status);
|
2001-04-11 13:04:03 +02:00
|
|
|
char *db=thd->strdup(packet);
|
2001-01-31 03:47:25 +01:00
|
|
|
// null test to handle EOM
|
2002-05-27 22:21:49 +02:00
|
|
|
if (!db || !strip_sp(db) || check_db_name(db))
|
2001-01-31 03:47:25 +01:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
|
2001-01-31 03:47:25 +01:00
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
if (check_access(thd,CREATE_ACL,db,0,1))
|
|
|
|
break;
|
2001-04-11 13:04:03 +02:00
|
|
|
mysql_log.write(thd,command,packet);
|
2001-09-14 18:50:56 +02:00
|
|
|
mysql_create_db(thd,db,0,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
|
|
|
{
|
2002-11-16 19:19:10 +01:00
|
|
|
statistic_increment(com_stat[SQLCOM_DROP_DB],&LOCK_status);
|
2001-04-11 13:04:03 +02:00
|
|
|
char *db=thd->strdup(packet);
|
2001-01-31 03:47:25 +01:00
|
|
|
// null test to handle EOM
|
2002-05-27 22:21:49 +02:00
|
|
|
if (!db || !strip_sp(db) || check_db_name(db))
|
2001-01-31 03:47:25 +01:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
|
2001-01-31 03:47:25 +01:00
|
|
|
break;
|
|
|
|
}
|
2002-12-20 13:58:27 +01:00
|
|
|
if (check_access(thd,DROP_ACL,db,0,1))
|
|
|
|
break;
|
2001-09-03 04:16:15 +02:00
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
|
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);
|
2001-09-14 18:50:56 +02:00
|
|
|
mysql_rm_db(thd,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:
|
|
|
|
{
|
2002-11-16 19:19:10 +01:00
|
|
|
statistic_increment(com_other,&LOCK_status);
|
2001-02-16 22:46:52 +01:00
|
|
|
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-09-16 03:27:21 +02:00
|
|
|
mysql_log.write(thd,command, 0);
|
2000-08-21 23:39:08 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
ulong pos;
|
|
|
|
ushort flags;
|
2000-09-30 01:20:26 +02:00
|
|
|
uint32 slave_server_id;
|
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 */
|
2001-04-11 13:04:03 +02:00
|
|
|
kill_zombie_dump_threads(slave_server_id = uint4korr(packet+6));
|
2000-09-30 01:20:26 +02:00
|
|
|
thd->server_id = slave_server_id;
|
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);
|
2000-10-03 01:59:12 +02:00
|
|
|
// fake COM_QUIT -- if we get here, the thread needs to terminate
|
|
|
|
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:
|
|
|
|
{
|
2002-11-16 19:19:10 +01:00
|
|
|
statistic_increment(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);
|
2002-12-13 11:05:24 +01:00
|
|
|
/* error sending is deferred to reload_acl_and_cache */
|
|
|
|
reload_acl_and_cache(thd, options, (TABLE_LIST*) 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_SHUTDOWN:
|
2002-11-16 19:19:10 +01:00
|
|
|
statistic_increment(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 */
|
|
|
|
DBUG_PRINT("quit",("Got shutdown command"));
|
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
|
2000-07-31 21:29:14 +02:00
|
|
|
close_connection(net);
|
|
|
|
close_thread_tables(thd); // Free before kill
|
2000-09-12 02:02:33 +02:00
|
|
|
free_root(&thd->mem_root,MYF(0));
|
2002-03-15 22:57:31 +01:00
|
|
|
free_root(&thd->transaction.mem_root,MYF(0));
|
2000-07-31 21:29:14 +02:00
|
|
|
kill_mysql();
|
|
|
|
error=TRUE;
|
|
|
|
break;
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
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);
|
2002-11-16 19:19:10 +01:00
|
|
|
statistic_increment(com_stat[SQLCOM_SHOW_STATUS],&LOCK_status);
|
2000-07-31 21:29:14 +02:00
|
|
|
char buff[200];
|
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,
|
|
|
|
(int) thread_count,thd->query_id,long_query_count,
|
|
|
|
opened_tables,refresh_version, cached_tables(),
|
|
|
|
uptime ? (float)thd->query_id/(float)uptime : 0);
|
|
|
|
#ifdef SAFEMALLOC
|
|
|
|
if (lCurMemory) // Using SAFEMALLOC
|
|
|
|
sprintf(strend(buff), " Memory in use: %ldK Max memory used: %ldK",
|
|
|
|
(lCurMemory+1023L)/1024L,(lMaxMemory+1023L)/1024L);
|
|
|
|
#endif
|
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));
|
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
case COM_PING:
|
2002-11-16 19:19:10 +01:00
|
|
|
statistic_increment(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:
|
2002-11-16 19:19:10 +01:00
|
|
|
statistic_increment(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);
|
2000-07-31 21:29:14 +02:00
|
|
|
mysqld_list_processes(thd,thd->master_access & PROCESS_ACL ? NullS :
|
|
|
|
thd->priv_user,0);
|
|
|
|
break;
|
|
|
|
case COM_PROCESS_KILL:
|
|
|
|
{
|
2002-11-16 19:19:10 +01:00
|
|
|
statistic_increment(com_stat[SQLCOM_KILL],&LOCK_status);
|
2001-04-11 13:04:03 +02:00
|
|
|
ulong id=(ulong) uint4korr(packet);
|
2000-07-31 21:29:14 +02:00
|
|
|
kill_one_thread(thd,id);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case COM_DEBUG:
|
2002-11-16 19:19:10 +01:00
|
|
|
statistic_increment(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:
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd, ER_UNKNOWN_COM_ERROR);
|
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 */
|
|
|
|
}
|
|
|
|
|
2003-01-30 21:15:44 +01:00
|
|
|
if (thd->is_fatal_error)
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,0); // End of memory ?
|
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 */
|
2001-02-16 22:46:52 +01:00
|
|
|
if (!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 ||
|
2001-06-03 16:07:26 +02:00
|
|
|
((thd->lex.select_lex.options &
|
2000-11-24 00:51:18 +01:00
|
|
|
(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED)) &&
|
2000-11-20 01:57:02 +01:00
|
|
|
(specialflag & SPECIAL_LONG_LOG_FORMAT)))
|
2000-10-14 02:16:35 +02:00
|
|
|
{
|
|
|
|
long_query_count++;
|
|
|
|
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;
|
|
|
|
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
|
2000-09-12 02:02:33 +02: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
|
|
|
|
|
|
|
/*
|
|
|
|
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
|
|
|
|
0 ok
|
|
|
|
1 error; In this case thd->fatal_error is set
|
|
|
|
*/
|
|
|
|
|
|
|
|
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 */
|
|
|
|
if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet),
|
|
|
|
packet_length,
|
|
|
|
thd->db_length+2)))
|
|
|
|
return 1;
|
|
|
|
thd->query[packet_length]=0;
|
|
|
|
thd->query_length= packet_length;
|
|
|
|
thd->packet.shrink(thd->variables.net_buffer_length);// Reclaim some memory
|
|
|
|
|
|
|
|
if (!(specialflag & SPECIAL_NO_PRIOR))
|
|
|
|
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
/****************************************************************************
|
|
|
|
** mysql_execute_command
|
|
|
|
** Execute command saved in thd and current_lex->sql_command
|
|
|
|
****************************************************************************/
|
|
|
|
|
2003-01-15 15:39:36 +01:00
|
|
|
int
|
2002-10-02 12:33:08 +02:00
|
|
|
mysql_execute_command(THD *thd)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-05-08 22:14:40 +02:00
|
|
|
int res= 0;
|
2000-11-22 03:24:54 +01:00
|
|
|
LEX *lex= &thd->lex;
|
2002-05-08 22:14:40 +02:00
|
|
|
TABLE_LIST *tables= (TABLE_LIST*) lex->select_lex.table_list.first;
|
2002-10-03 15:35:08 +02:00
|
|
|
SELECT_LEX *select_lex= &lex->select_lex;
|
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");
|
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
/*
|
2003-03-02 19:17:41 +01:00
|
|
|
Clear the SP function cache before each statement (QQ this is a temporary
|
|
|
|
solution; caching will be rehacked later), and the new ones.
|
|
|
|
*/
|
|
|
|
sp_clear_function_cache(thd);
|
|
|
|
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.
|
|
|
|
*/
|
2002-11-27 00:12:16 +01:00
|
|
|
if (tables || &lex->select_lex != lex->all_selects_list)
|
2002-10-02 12:33:08 +02:00
|
|
|
mysql_reset_errors(thd);
|
|
|
|
/*
|
|
|
|
Save old warning count to be able to send to client how many warnings we
|
|
|
|
got
|
|
|
|
*/
|
|
|
|
thd->old_total_warn_count= thd->total_warn_count;
|
|
|
|
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
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
|
|
|
|
*/
|
|
|
|
if (table_rules_on && tables && !tables_ok(thd,tables))
|
2003-02-12 16:17:03 +01:00
|
|
|
DBUG_RETURN(0);
|
2001-12-20 12:52:04 +01:00
|
|
|
#ifndef TO_BE_DELETED
|
|
|
|
/*
|
|
|
|
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-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
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
2001-08-14 19:33:49 +02:00
|
|
|
/*
|
2002-09-26 22:08:22 +02:00
|
|
|
TODO: make derived tables processing 'inside' SELECT processing.
|
|
|
|
TODO: solve problem with depended derived tables in subselects
|
2001-08-14 19:33:49 +02:00
|
|
|
*/
|
2002-11-11 13:04:50 +01:00
|
|
|
if (lex->derived_tables)
|
2002-03-26 14:06:05 +01:00
|
|
|
{
|
2002-11-27 00:12:16 +01:00
|
|
|
for (SELECT_LEX *sl= lex->all_selects_list;
|
|
|
|
sl;
|
|
|
|
sl= sl->next_select_in_list())
|
2003-01-07 10:45:06 +01:00
|
|
|
{
|
2002-11-27 00:12:16 +01:00
|
|
|
for (TABLE_LIST *cursor= sl->get_table_list();
|
|
|
|
cursor;
|
|
|
|
cursor= cursor->next)
|
2003-01-07 10:45:06 +01:00
|
|
|
{
|
2002-11-27 00:12:16 +01:00
|
|
|
if (cursor->derived && (res=mysql_derived(thd, lex,
|
|
|
|
cursor->derived,
|
|
|
|
cursor)))
|
2002-11-30 18:58:53 +01:00
|
|
|
{
|
2002-11-27 00:12:16 +01:00
|
|
|
if (res < 0 || thd->net.report_error)
|
|
|
|
send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
|
2003-02-12 16:17:03 +01:00
|
|
|
DBUG_RETURN(res);
|
2002-11-27 00:12:16 +01:00
|
|
|
}
|
2003-01-07 10:45:06 +01:00
|
|
|
}
|
|
|
|
}
|
2002-11-27 00:12:16 +01:00
|
|
|
}
|
2002-11-30 18:58:53 +01:00
|
|
|
if ((&lex->select_lex != lex->all_selects_list &&
|
2003-01-26 20:30:35 +01:00
|
|
|
lex->unit.create_total_list(thd, lex, &tables, 0))
|
2003-02-12 22:17:22 +01:00
|
|
|
#ifdef HAVE_REPLICATION
|
2002-12-16 14:33:29 +01:00
|
|
|
||
|
2001-08-19 13:43:51 +02:00
|
|
|
(table_rules_on && tables && thd->slave_thread &&
|
2002-12-16 14:33:29 +01:00
|
|
|
!tables_ok(thd,tables))
|
|
|
|
#endif
|
|
|
|
)
|
2003-02-12 16:17:03 +01:00
|
|
|
DBUG_RETURN(0);
|
2001-08-18 14:29:21 +02:00
|
|
|
|
2002-11-16 19:19:10 +01:00
|
|
|
statistic_increment(com_stat[lex->sql_command],&LOCK_status);
|
2000-07-31 21:29:14 +02:00
|
|
|
switch (lex->sql_command) {
|
|
|
|
case SQLCOM_SELECT:
|
|
|
|
{
|
2002-10-16 15:55:08 +02:00
|
|
|
select_result *result=lex->result;
|
2000-07-31 21:29:14 +02:00
|
|
|
if (tables)
|
|
|
|
{
|
|
|
|
res=check_table_access(thd,
|
|
|
|
lex->exchange ? SELECT_ACL | FILE_ACL :
|
|
|
|
SELECT_ACL,
|
|
|
|
tables);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
res=check_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
|
|
|
|
any_db);
|
|
|
|
if (res)
|
|
|
|
{
|
|
|
|
res=0;
|
|
|
|
break; // Error message is given
|
|
|
|
}
|
|
|
|
|
2002-11-11 16:28:58 +01:00
|
|
|
unit->offset_limit_cnt= (ha_rows) unit->global_parameters->offset_limit;
|
|
|
|
unit->select_limit_cnt= (ha_rows) (unit->global_parameters->select_limit+
|
|
|
|
unit->global_parameters->offset_limit);
|
2002-11-30 18:58:53 +01:00
|
|
|
if (unit->select_limit_cnt <
|
2002-11-11 16:28:58 +01:00
|
|
|
(ha_rows) unit->global_parameters->select_limit)
|
2002-05-08 22:14:40 +02:00
|
|
|
unit->select_limit_cnt= HA_POS_ERROR; // no limit
|
|
|
|
if (unit->select_limit_cnt == HA_POS_ERROR)
|
2001-06-07 13:10:58 +02:00
|
|
|
select_lex->options&= ~OPTION_FOUND_ROWS;
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
if (!(res=open_and_lock_tables(thd,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()))
|
|
|
|
{
|
|
|
|
send_error(thd, ER_OUT_OF_RESOURCES);
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2002-11-09 14:40:46 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
thd->send_explain_fields(result);
|
2002-11-27 00:12:16 +01:00
|
|
|
fix_tables_pointers(lex->all_selects_list);
|
2002-11-09 14:40:46 +01:00
|
|
|
res= mysql_explain_union(thd, &thd->lex.unit, result);
|
2002-09-26 22:08:22 +02:00
|
|
|
MYSQL_LOCK *save_lock= thd->lock;
|
|
|
|
thd->lock= (MYSQL_LOCK *)0;
|
2002-11-09 14:40:46 +01:00
|
|
|
result->send_eof();
|
2002-09-26 22:08:22 +02:00
|
|
|
thd->lock= save_lock;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2002-10-16 20:17:57 +02:00
|
|
|
if (!result)
|
|
|
|
{
|
2002-11-23 17:54:15 +01:00
|
|
|
if (!(result=new select_send()))
|
2002-10-16 20:17:57 +02:00
|
|
|
{
|
|
|
|
res= -1;
|
|
|
|
#ifdef DELETE_ITEMS
|
|
|
|
delete select_lex->having;
|
|
|
|
delete select_lex->where;
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2002-09-26 22:08:22 +02:00
|
|
|
query_cache_store_query(thd, tables);
|
|
|
|
res=handle_select(thd, lex, result);
|
|
|
|
}
|
2001-12-02 13:34:01 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2001-12-17 18:59:20 +01:00
|
|
|
case SQLCOM_DO:
|
2003-01-03 16:08:00 +01:00
|
|
|
if (tables && ((res= check_table_access(thd, SELECT_ACL, tables)) ||
|
|
|
|
(res= open_and_lock_tables(thd,tables))))
|
|
|
|
break;
|
|
|
|
|
|
|
|
fix_tables_pointers(lex->all_selects_list);
|
|
|
|
res= mysql_do(thd, *lex->insert_list);
|
|
|
|
if (thd->net.report_error)
|
|
|
|
res= -1;
|
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;
|
2003-02-16 17:39:12 +01: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:
|
|
|
|
{
|
|
|
|
if (check_global_access(thd, SUPER_ACL))
|
|
|
|
goto error;
|
|
|
|
// PURGE MASTER LOGS BEFORE 'data'
|
|
|
|
res = purge_master_logs_before_date(thd, lex->purge_time);
|
|
|
|
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;
|
2002-09-11 05:40:08 +02:00
|
|
|
#ifndef WORKING_NEW_MASTER
|
2002-10-03 15:54:26 +02:00
|
|
|
net_printf(thd, ER_NOT_SUPPORTED_YET, "SHOW NEW MASTER");
|
2002-09-11 05:40:08 +02:00
|
|
|
res= 1;
|
|
|
|
#else
|
2001-07-05 01:14:31 +02:00
|
|
|
res = show_new_master(thd);
|
2002-09-11 05:40:08 +02:00
|
|
|
#endif
|
2001-07-05 01:14:31 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
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
|
|
|
{
|
|
|
|
if (check_db_used(thd,tables) ||
|
|
|
|
check_table_access(thd,SELECT_ACL, tables) ||
|
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 */
|
|
|
|
res = mysql_backup_table(thd, tables);
|
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
|
|
|
{
|
|
|
|
if (check_db_used(thd,tables) ||
|
2002-06-12 14:04:18 +02:00
|
|
|
check_table_access(thd, INSERT_ACL, tables) ||
|
|
|
|
check_global_access(thd, FILE_ACL))
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
res = mysql_restore_table(thd, tables);
|
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
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;
|
2002-01-20 03:16:52 +01:00
|
|
|
LOCK_ACTIVE_MI;
|
|
|
|
res = change_master(thd,active_mi);
|
|
|
|
UNLOCK_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
|
|
|
{
|
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;
|
2002-01-20 03:16:52 +01:00
|
|
|
LOCK_ACTIVE_MI;
|
|
|
|
res = show_master_info(thd,active_mi);
|
|
|
|
UNLOCK_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
|
|
|
{
|
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;
|
|
|
|
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))
|
|
|
|
res= -1;
|
|
|
|
else
|
|
|
|
res = load_master_data(thd);
|
2001-05-29 03:18:23 +02:00
|
|
|
break;
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
2002-11-30 18:58:53 +01:00
|
|
|
|
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
|
2000-08-21 23:39:08 +02:00
|
|
|
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_LOAD_MASTER_TABLE:
|
2002-01-20 03:16:52 +01:00
|
|
|
{
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!tables->db)
|
|
|
|
tables->db=thd->db;
|
|
|
|
if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege))
|
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
if (grant_option)
|
|
|
|
{
|
|
|
|
/* Check that the first table has CREATE privilege */
|
|
|
|
TABLE_LIST *tmp_table_list=tables->next;
|
|
|
|
tables->next=0;
|
|
|
|
bool error=check_grant(thd,CREATE_ACL,tables);
|
|
|
|
tables->next=tmp_table_list;
|
|
|
|
if (error)
|
2001-08-14 19:33:49 +02:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2002-09-20 13:05:18 +02:00
|
|
|
if (strlen(tables->real_name) > NAME_LEN)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-10-02 16:55:12 +02:00
|
|
|
net_printf(thd,ER_WRONG_TABLE_NAME,tables->real_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-01-20 03:16:52 +01:00
|
|
|
LOCK_ACTIVE_MI;
|
|
|
|
// fetch_master_table will send the error to the client on failure
|
|
|
|
if (!fetch_master_table(thd, tables->db, tables->real_name,
|
|
|
|
active_mi, 0))
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2002-01-20 03:16:52 +01:00
|
|
|
}
|
|
|
|
UNLOCK_ACTIVE_MI;
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2002-01-20 03:16:52 +01:00
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_CREATE_TABLE:
|
2002-06-12 14:04:18 +02:00
|
|
|
{
|
|
|
|
ulong want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
|
|
|
|
CREATE_TMP_ACL : CREATE_ACL);
|
2000-09-14 01:39:07 +02:00
|
|
|
if (!tables->db)
|
|
|
|
tables->db=thd->db;
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_access(thd,want_priv,tables->db,&tables->grant.privilege) ||
|
2000-09-14 01:39:07 +02:00
|
|
|
check_merge_table_access(thd, tables->db,
|
|
|
|
(TABLE_LIST *)
|
|
|
|
lex->create_info.merge_list.first))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2002-08-03 14:59:41 +02:00
|
|
|
if (grant_option && want_priv != CREATE_TMP_ACL)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
/* Check that the first table has CREATE privilege */
|
|
|
|
TABLE_LIST *tmp_table_list=tables->next;
|
|
|
|
tables->next=0;
|
2002-08-08 02:14:44 +02:00
|
|
|
bool error=check_grant(thd, want_priv, tables);
|
2000-07-31 21:29:14 +02:00
|
|
|
tables->next=tmp_table_list;
|
|
|
|
if (error)
|
|
|
|
goto error;
|
|
|
|
}
|
2002-09-20 13:05:18 +02:00
|
|
|
if (strlen(tables->real_name) > NAME_LEN)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-10-02 16:55:12 +02:00
|
|
|
net_printf(thd, ER_WRONG_TABLE_NAME, tables->alias);
|
2000-07-31 21:29:14 +02:00
|
|
|
res=0;
|
|
|
|
break;
|
|
|
|
}
|
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,
|
2002-09-20 13:05:18 +02:00
|
|
|
tables->real_name) ||
|
2001-12-05 12:03:00 +01:00
|
|
|
append_file_to_dir(thd,&lex->create_info.index_file_name,
|
2002-09-20 13:05:18 +02:00
|
|
|
tables->real_name))
|
2001-06-01 03:27:59 +02:00
|
|
|
{
|
|
|
|
res=-1;
|
|
|
|
break;
|
|
|
|
}
|
2001-11-03 20:33:11 +01:00
|
|
|
#endif
|
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;
|
|
|
|
|
|
|
|
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
|
2002-11-30 18:26:18 +01:00
|
|
|
find_real_table_in_list(tables->next, tables->db, tables->real_name))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-12-04 00:32:45 +01:00
|
|
|
net_printf(thd,ER_UPDATE_TABLE_USED,tables->real_name);
|
2003-02-12 16:17:03 +01:00
|
|
|
DBUG_RETURN(-1);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
if (tables->next)
|
|
|
|
{
|
|
|
|
if (check_table_access(thd, SELECT_ACL, tables->next))
|
|
|
|
goto error; // Error message is given
|
|
|
|
}
|
2002-05-08 22:14:40 +02:00
|
|
|
unit->offset_limit_cnt= select_lex->offset_limit;
|
|
|
|
unit->select_limit_cnt= select_lex->select_limit+
|
|
|
|
select_lex->offset_limit;
|
|
|
|
if (unit->select_limit_cnt < select_lex->select_limit)
|
|
|
|
unit->select_limit_cnt= HA_POS_ERROR; // No limit
|
2001-08-14 19:33:49 +02:00
|
|
|
|
|
|
|
/* Skip first table, which is the table we are creating */
|
|
|
|
lex->select_lex.table_list.first=
|
|
|
|
(byte*) (((TABLE_LIST *) lex->select_lex.table_list.first)->next);
|
|
|
|
if (!(res=open_and_lock_tables(thd,tables->next)))
|
|
|
|
{
|
|
|
|
if ((result=new select_create(tables->db ? tables->db : thd->db,
|
|
|
|
tables->real_name, &lex->create_info,
|
|
|
|
lex->create_list,
|
|
|
|
lex->key_list,
|
|
|
|
select_lex->item_list,lex->duplicates)))
|
|
|
|
res=handle_select(thd, lex, result);
|
|
|
|
else
|
|
|
|
res= -1;
|
|
|
|
}
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
else // regular create
|
|
|
|
{
|
2002-12-28 10:38:29 +01:00
|
|
|
if (lex->name)
|
|
|
|
res= mysql_create_like_table(thd, tables, &lex->create_info,
|
|
|
|
(Table_ident *)lex->name);
|
|
|
|
else
|
|
|
|
res= mysql_create_table(thd,tables->db ? tables->db : thd->db,
|
|
|
|
tables->real_name, &lex->create_info,
|
|
|
|
lex->create_list,
|
|
|
|
lex->key_list,0,0,0); // do logging
|
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
|
|
|
}
|
|
|
|
break;
|
2002-06-12 14:04:18 +02:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_CREATE_INDEX:
|
|
|
|
if (!tables->db)
|
|
|
|
tables->db=thd->db;
|
|
|
|
if (check_access(thd,INDEX_ACL,tables->db,&tables->grant.privilege))
|
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
if (grant_option && check_grant(thd,INDEX_ACL,tables))
|
|
|
|
goto error;
|
2000-12-07 13:08:48 +01:00
|
|
|
if (end_active_trans(thd))
|
|
|
|
res= -1;
|
|
|
|
else
|
|
|
|
res = mysql_create_index(thd, tables, lex->key_list);
|
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 SQLCOM_SLAVE_START:
|
2002-01-20 03:16:52 +01:00
|
|
|
{
|
|
|
|
LOCK_ACTIVE_MI;
|
|
|
|
start_slave(thd,active_mi,1 /* net report*/);
|
|
|
|
UNLOCK_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())
|
|
|
|
{
|
2003-01-04 14:37:20 +01:00
|
|
|
send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
|
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
|
|
|
{
|
|
|
|
LOCK_ACTIVE_MI;
|
|
|
|
stop_slave(thd,active_mi,1/* net report*/);
|
|
|
|
UNLOCK_ACTIVE_MI;
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2002-01-20 03:16:52 +01:00
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_ALTER_TABLE:
|
|
|
|
#if defined(DONT_ALLOW_SHOW_COMMANDS)
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* 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
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_WRONG_TABLE_NAME,lex->name);
|
2000-07-31 21:29:14 +02:00
|
|
|
res=0;
|
|
|
|
break;
|
|
|
|
}
|
2000-09-14 01:39:07 +02:00
|
|
|
if (!tables->db)
|
|
|
|
tables->db=thd->db;
|
2001-06-07 13:10:58 +02:00
|
|
|
if (!select_lex->db)
|
|
|
|
select_lex->db=tables->db;
|
2000-07-31 21:29:14 +02:00
|
|
|
if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege) ||
|
2001-06-07 13:10:58 +02:00
|
|
|
check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv) ||
|
2002-11-30 18:58:53 +01:00
|
|
|
check_merge_table_access(thd, tables->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 (!tables->db)
|
|
|
|
tables->db=thd->db;
|
|
|
|
if (grant_option)
|
|
|
|
{
|
|
|
|
if (check_grant(thd,ALTER_ACL,tables))
|
|
|
|
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;
|
|
|
|
if (check_grant(thd,INSERT_ACL | CREATE_ACL,tables))
|
|
|
|
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))
|
2000-07-31 21:29:14 +02:00
|
|
|
res= -1;
|
|
|
|
else
|
2001-12-02 13:34:01 +01:00
|
|
|
{
|
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,
|
|
|
|
tables, lex->create_list,
|
|
|
|
lex->key_list, lex->drop_list, lex->alter_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,
|
2001-05-11 22:26:12 +02:00
|
|
|
lex->drop_primary, lex->duplicates,
|
|
|
|
lex->alter_keys_onoff, lex->simple_alter);
|
2001-12-02 13:34:01 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2000-08-21 02:07:54 +02:00
|
|
|
case SQLCOM_RENAME_TABLE:
|
2000-08-21 23:18:32 +02:00
|
|
|
{
|
|
|
|
TABLE_LIST *table;
|
|
|
|
if (check_db_used(thd,tables))
|
2000-08-21 02:07:54 +02:00
|
|
|
goto error;
|
2000-08-21 23:18:32 +02:00
|
|
|
for (table=tables ; table ; table=table->next->next)
|
|
|
|
{
|
2000-08-29 18:38:32 +02:00
|
|
|
if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
|
|
|
|
&table->grant.privilege) ||
|
2000-08-21 23:18:32 +02:00
|
|
|
check_access(thd, INSERT_ACL | CREATE_ACL, table->next->db,
|
|
|
|
&table->next->grant.privilege))
|
|
|
|
goto error;
|
|
|
|
if (grant_option)
|
|
|
|
{
|
2000-08-29 18:38:32 +02:00
|
|
|
TABLE_LIST old_list,new_list;
|
|
|
|
old_list=table[0];
|
|
|
|
new_list=table->next[0];
|
|
|
|
old_list.next=new_list.next=0;
|
|
|
|
if (check_grant(thd,ALTER_ACL,&old_list) ||
|
2000-08-21 23:18:32 +02:00
|
|
|
(!test_all_bits(table->next->grant.privilege,
|
2001-08-14 19:33:49 +02:00
|
|
|
INSERT_ACL | CREATE_ACL) &&
|
2000-08-29 18:38:32 +02:00
|
|
|
check_grant(thd,INSERT_ACL | CREATE_ACL, &new_list)))
|
2000-08-21 23:18:32 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
2002-03-22 21:55:08 +01:00
|
|
|
query_cache_invalidate3(thd, tables, 0);
|
2000-12-07 13:08:48 +01:00
|
|
|
if (end_active_trans(thd))
|
|
|
|
res= -1;
|
|
|
|
else if (mysql_rename_tables(thd,tables))
|
2000-08-21 02:07:54 +02:00
|
|
|
res= -1;
|
|
|
|
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
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* 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:
|
2000-08-21 02:07:54 +02:00
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* 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
|
|
|
{
|
2000-08-21 02:07:54 +02:00
|
|
|
if (check_db_used(thd, tables) ||
|
|
|
|
check_access(thd, SELECT_ACL | EXTRA_ACL, tables->db,
|
|
|
|
&tables->grant.privilege))
|
|
|
|
goto error;
|
2000-08-21 23:39:08 +02:00
|
|
|
res = mysqld_show_create(thd, tables);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-08-21 02:07:54 +02:00
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_REPAIR:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
|
|
|
if (check_db_used(thd,tables) ||
|
|
|
|
check_table_access(thd,SELECT_ACL | INSERT_ACL, tables))
|
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
res = mysql_repair_table(thd, tables, &lex->check_opt);
|
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_CHECK:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
|
|
|
if (check_db_used(thd,tables) ||
|
|
|
|
check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables))
|
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
res = mysql_check_table(thd, tables, &lex->check_opt);
|
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_ANALYZE:
|
|
|
|
{
|
2000-08-21 02:07:54 +02:00
|
|
|
if (check_db_used(thd,tables) ||
|
|
|
|
check_table_access(thd,SELECT_ACL | INSERT_ACL, tables))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2000-09-12 02:02:33 +02:00
|
|
|
res = mysql_analyze_table(thd, tables, &lex->check_opt);
|
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:
|
|
|
|
{
|
|
|
|
HA_CREATE_INFO create_info;
|
2000-09-12 02:02:33 +02:00
|
|
|
if (check_db_used(thd,tables) ||
|
|
|
|
check_table_access(thd,SELECT_ACL | INSERT_ACL, tables))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2000-09-12 02:02:33 +02:00
|
|
|
if (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC))
|
|
|
|
{
|
|
|
|
/* Use ALTER TABLE */
|
|
|
|
lex->create_list.empty();
|
|
|
|
lex->key_list.empty();
|
|
|
|
lex->col_list.empty();
|
|
|
|
lex->drop_list.empty();
|
|
|
|
lex->alter_list.empty();
|
|
|
|
bzero((char*) &create_info,sizeof(create_info));
|
|
|
|
create_info.db_type=DB_TYPE_DEFAULT;
|
|
|
|
create_info.row_type=ROW_TYPE_DEFAULT;
|
2002-06-04 07:23:57 +02:00
|
|
|
create_info.table_charset=default_charset_info;
|
2000-09-12 02:02:33 +02:00
|
|
|
res= mysql_alter_table(thd, NullS, NullS, &create_info,
|
|
|
|
tables, lex->create_list,
|
|
|
|
lex->key_list, lex->drop_list, lex->alter_list,
|
2003-01-25 01:25:52 +01:00
|
|
|
0, (ORDER *) 0,
|
|
|
|
0, DUP_ERROR);
|
2000-09-12 02:02:33 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
res = mysql_optimize_table(thd, tables, &lex->check_opt);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SQLCOM_UPDATE:
|
|
|
|
if (check_access(thd,UPDATE_ACL,tables->db,&tables->grant.privilege))
|
|
|
|
goto error;
|
|
|
|
if (grant_option && check_grant(thd,UPDATE_ACL,tables))
|
|
|
|
goto error;
|
2001-06-07 13:10:58 +02:00
|
|
|
if (select_lex->item_list.elements != lex->value_list.elements)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_WRONG_VALUE_COUNT);
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2002-11-29 13:17:54 +01:00
|
|
|
res= mysql_update(thd,tables,
|
|
|
|
select_lex->item_list,
|
|
|
|
lex->value_list,
|
|
|
|
select_lex->where,
|
2003-01-25 01:25:52 +01:00
|
|
|
select_lex->order_list.elements,
|
2002-11-29 13:17:54 +01:00
|
|
|
(ORDER *) select_lex->order_list.first,
|
|
|
|
select_lex->select_limit,
|
|
|
|
lex->duplicates);
|
2002-11-30 23:11:22 +01:00
|
|
|
if (thd->net.report_error)
|
|
|
|
res= -1;
|
2002-11-29 13:17:54 +01:00
|
|
|
break;
|
|
|
|
case SQLCOM_UPDATE_MULTI:
|
|
|
|
if (check_access(thd,UPDATE_ACL,tables->db,&tables->grant.privilege))
|
|
|
|
goto error;
|
|
|
|
if (grant_option && check_grant(thd,UPDATE_ACL,tables))
|
|
|
|
goto error;
|
|
|
|
if (select_lex->item_list.elements != lex->value_list.elements)
|
2002-01-16 21:45:47 +01:00
|
|
|
{
|
2002-11-29 13:17:54 +01:00
|
|
|
send_error(thd,ER_WRONG_VALUE_COUNT);
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2002-01-16 21:45:47 +01:00
|
|
|
}
|
|
|
|
{
|
2002-11-29 15:40:18 +01:00
|
|
|
const char *msg= 0;
|
2002-06-12 14:04:18 +02:00
|
|
|
if (select_lex->order_list.elements)
|
2002-12-05 18:38:42 +01:00
|
|
|
msg= "ORDER BY";
|
2002-06-12 14:04:18 +02:00
|
|
|
else if (select_lex->select_limit && select_lex->select_limit !=
|
|
|
|
HA_POS_ERROR)
|
2002-12-05 18:38:42 +01:00
|
|
|
msg= "LIMIT";
|
2002-06-12 14:04:18 +02:00
|
|
|
if (msg)
|
2002-01-16 21:45:47 +01:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd, ER_WRONG_USAGE, "UPDATE", msg);
|
2002-06-12 14:04:18 +02:00
|
|
|
res= 1;
|
|
|
|
break;
|
2002-01-16 21:45:47 +01:00
|
|
|
}
|
2002-11-29 15:40:18 +01:00
|
|
|
res= mysql_multi_update(thd,tables,
|
|
|
|
&select_lex->item_list,
|
|
|
|
&lex->value_list,
|
|
|
|
select_lex->where,
|
|
|
|
select_lex->options,
|
2002-12-05 18:38:42 +01:00
|
|
|
lex->duplicates, unit, select_lex);
|
2002-01-16 21:45:47 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_REPLACE:
|
2002-11-28 17:25:41 +01:00
|
|
|
case SQLCOM_INSERT:
|
|
|
|
{
|
2002-12-02 20:38:00 +01:00
|
|
|
my_bool update=(lex->value_list.elements ? UPDATE_ACL : 0);
|
2002-11-28 17:25:41 +01:00
|
|
|
ulong privilege= (lex->duplicates == DUP_REPLACE ?
|
2002-12-02 20:38:00 +01:00
|
|
|
INSERT_ACL | DELETE_ACL : INSERT_ACL | update);
|
2002-11-28 17:25:41 +01:00
|
|
|
if (check_access(thd,privilege,tables->db,&tables->grant.privilege))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
2002-11-28 17:25:41 +01:00
|
|
|
if (grant_option && check_grant(thd,privilege,tables))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error;
|
2002-12-02 20:38:00 +01:00
|
|
|
if (select_lex->item_list.elements != lex->value_list.elements)
|
|
|
|
{
|
|
|
|
send_error(thd,ER_WRONG_VALUE_COUNT);
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2002-12-02 20:38:00 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
res = mysql_insert(thd,tables,lex->field_list,lex->many_values,
|
2002-12-02 20:38:00 +01:00
|
|
|
select_lex->item_list, lex->value_list,
|
2002-12-02 20:41:34 +01:00
|
|
|
(update ? DUP_UPDATE : lex->duplicates));
|
2002-11-30 23:11:22 +01:00
|
|
|
if (thd->net.report_error)
|
|
|
|
res= -1;
|
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:
|
|
|
|
{
|
2001-08-14 19:33:49 +02:00
|
|
|
|
2002-06-12 14:04:18 +02:00
|
|
|
/*
|
|
|
|
Check that we have modify privileges for the first table and
|
|
|
|
select privileges for the rest
|
|
|
|
*/
|
2001-03-23 19:38:42 +01:00
|
|
|
{
|
2002-11-28 17:25:41 +01:00
|
|
|
ulong privilege= (lex->duplicates == DUP_REPLACE ?
|
|
|
|
INSERT_ACL | DELETE_ACL : INSERT_ACL);
|
2001-03-23 19:38:42 +01:00
|
|
|
TABLE_LIST *save_next=tables->next;
|
|
|
|
tables->next=0;
|
|
|
|
if (check_access(thd, privilege,
|
|
|
|
tables->db,&tables->grant.privilege) ||
|
|
|
|
(grant_option && check_grant(thd, privilege, tables)))
|
|
|
|
goto error;
|
|
|
|
tables->next=save_next;
|
|
|
|
if ((res=check_table_access(thd, SELECT_ACL, save_next)))
|
|
|
|
goto error;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
select_result *result;
|
2002-05-08 22:14:40 +02:00
|
|
|
unit->offset_limit_cnt= select_lex->offset_limit;
|
|
|
|
unit->select_limit_cnt= select_lex->select_limit+select_lex->offset_limit;
|
|
|
|
if (unit->select_limit_cnt < select_lex->select_limit)
|
|
|
|
unit->select_limit_cnt= HA_POS_ERROR; // No limit
|
2000-07-31 21:29:14 +02:00
|
|
|
|
2002-11-30 18:26:18 +01:00
|
|
|
if (find_real_table_in_list(tables->next, tables->db, tables->real_name))
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2003-01-28 17:55:52 +01:00
|
|
|
lex->select_lex.options |= OPTION_BUFFER_RESULT;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2001-08-14 19:33:49 +02:00
|
|
|
|
|
|
|
/* Skip first table, which is the table we are inserting in */
|
|
|
|
lex->select_lex.table_list.first=
|
|
|
|
(byte*) (((TABLE_LIST *) lex->select_lex.table_list.first)->next);
|
|
|
|
if (!(res=open_and_lock_tables(thd, tables)))
|
|
|
|
{
|
|
|
|
if ((result=new select_insert(tables->table,&lex->field_list,
|
2001-11-08 13:17:56 +01:00
|
|
|
lex->duplicates)))
|
2001-08-14 19:33:49 +02:00
|
|
|
res=handle_select(thd,lex,result);
|
2002-11-30 23:11:22 +01:00
|
|
|
if (thd->net.report_error)
|
|
|
|
res= -1;
|
2001-08-14 19:33:49 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
res= -1;
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
2000-11-13 22:55:10 +01:00
|
|
|
case SQLCOM_TRUNCATE:
|
2001-09-02 12:47:00 +02:00
|
|
|
if (check_access(thd,DELETE_ACL,tables->db,&tables->grant.privilege))
|
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
/*
|
|
|
|
Don't allow this within a transaction because we want to use
|
|
|
|
re-generate table
|
|
|
|
*/
|
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION,NullS);
|
2001-09-02 12:47:00 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
res=mysql_truncate(thd,tables);
|
|
|
|
break;
|
2000-11-29 04:09:28 +01:00
|
|
|
case SQLCOM_DELETE:
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
if (check_access(thd,DELETE_ACL,tables->db,&tables->grant.privilege))
|
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
if (grant_option && check_grant(thd,DELETE_ACL,tables))
|
|
|
|
goto error;
|
|
|
|
// Set privilege for the WHERE clause
|
|
|
|
tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
|
2002-09-02 22:37:18 +02:00
|
|
|
res = mysql_delete(thd,tables, select_lex->where,
|
|
|
|
(ORDER*) select_lex->order_list.first,
|
2002-11-16 19:19:10 +01:00
|
|
|
select_lex->select_limit, select_lex->options);
|
2002-11-30 23:11:22 +01:00
|
|
|
if (thd->net.report_error)
|
|
|
|
res= -1;
|
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
|
|
|
{
|
|
|
|
TABLE_LIST *aux_tables=(TABLE_LIST *)thd->lex.auxilliary_table_list.first;
|
|
|
|
TABLE_LIST *auxi;
|
|
|
|
uint table_count=0;
|
|
|
|
multi_delete *result;
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-06-15 04:03:15 +02:00
|
|
|
/* sql_yacc guarantees that tables and aux_tables are not zero */
|
|
|
|
if (check_db_used(thd, tables) || check_db_used(thd,aux_tables) ||
|
2002-11-29 12:29:27 +01:00
|
|
|
check_table_access(thd,SELECT_ACL, tables) ||
|
2001-06-15 04:03:15 +02:00
|
|
|
check_table_access(thd,DELETE_ACL, aux_tables))
|
|
|
|
goto error;
|
|
|
|
if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where)
|
2002-11-29 12:29:27 +01:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
|
2001-06-15 04:03:15 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
for (auxi=(TABLE_LIST*) aux_tables ; auxi ; auxi=auxi->next)
|
|
|
|
{
|
|
|
|
table_count++;
|
|
|
|
/* All tables in aux_tables must be found in FROM PART */
|
|
|
|
TABLE_LIST *walk;
|
|
|
|
for (walk=(TABLE_LIST*) tables ; walk ; walk=walk->next)
|
|
|
|
{
|
|
|
|
if (!strcmp(auxi->real_name,walk->real_name) &&
|
|
|
|
!strcmp(walk->db,auxi->db))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!walk)
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_NONUNIQ_TABLE,auxi->real_name);
|
2001-06-15 04:03:15 +02:00
|
|
|
goto error;
|
|
|
|
}
|
2002-11-16 19:19:10 +01:00
|
|
|
walk->lock_type= auxi->lock_type;
|
2002-05-09 14:23:57 +02:00
|
|
|
auxi->table_list= walk; // Remember corresponding table
|
2002-06-11 21:45:51 +02:00
|
|
|
}
|
2002-12-06 20:11:27 +01:00
|
|
|
if (add_item_to_list(thd, new Item_null()))
|
2002-06-11 21:45:51 +02:00
|
|
|
{
|
2001-06-15 04:03:15 +02:00
|
|
|
res= -1;
|
2002-06-11 21:45:51 +02:00
|
|
|
break;
|
2001-06-15 04:03:15 +02:00
|
|
|
}
|
|
|
|
thd->proc_info="init";
|
|
|
|
if ((res=open_and_lock_tables(thd,tables)))
|
|
|
|
break;
|
|
|
|
/* Fix tables-to-be-deleted-from list to point at opened tables */
|
|
|
|
for (auxi=(TABLE_LIST*) aux_tables ; auxi ; auxi=auxi->next)
|
2002-05-09 14:23:57 +02:00
|
|
|
auxi->table= auxi->table_list->table;
|
2002-11-30 18:26:18 +01:00
|
|
|
if (&lex->select_lex != lex->all_selects_list)
|
2003-01-06 01:04:52 +01:00
|
|
|
{
|
2002-11-30 18:26:18 +01:00
|
|
|
for (TABLE_LIST *t= select_lex->get_table_list();
|
|
|
|
t; t= t->next)
|
|
|
|
{
|
|
|
|
if (find_real_table_in_list(t->table_list->next, t->db, t->real_name))
|
|
|
|
{
|
2002-12-04 00:32:45 +01:00
|
|
|
my_error(ER_UPDATE_TABLE_USED, MYF(0), t->real_name);
|
2002-11-30 18:26:18 +01:00
|
|
|
res= -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2003-01-06 01:04:52 +01:00
|
|
|
}
|
2002-11-27 14:52:32 +01:00
|
|
|
fix_tables_pointers(lex->all_selects_list);
|
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-03-06 16:02:10 +01:00
|
|
|
result, unit, select_lex, 0);
|
2002-11-30 23:11:22 +01:00
|
|
|
if (thd->net.report_error)
|
|
|
|
res= -1;
|
2002-01-12 18:51:10 +01:00
|
|
|
delete result;
|
2001-06-15 04:03:15 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
res= -1; // Error is not sent
|
|
|
|
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
|
|
|
{
|
2003-01-04 14:37:20 +01:00
|
|
|
if (!lex->drop_temporary)
|
|
|
|
{
|
|
|
|
if (check_table_access(thd,DROP_ACL,tables))
|
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
if (end_active_trans(thd))
|
|
|
|
{
|
|
|
|
res= -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
res= mysql_rm_table(thd,tables,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:
|
|
|
|
if (!tables->db)
|
|
|
|
tables->db=thd->db;
|
|
|
|
if (check_access(thd,INDEX_ACL,tables->db,&tables->grant.privilege))
|
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
if (grant_option && check_grant(thd,INDEX_ACL,tables))
|
|
|
|
goto error;
|
2000-12-07 13:08:48 +01:00
|
|
|
if (end_active_trans(thd))
|
|
|
|
res= -1;
|
|
|
|
else
|
|
|
|
res = mysql_drop_index(thd, tables, lex->drop_list);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_SHOW_DATABASES:
|
2000-09-14 01:39:07 +02:00
|
|
|
#if defined(DONT_ALLOW_SHOW_COMMANDS)
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
#else
|
|
|
|
if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
|
2002-06-12 14:04:18 +02:00
|
|
|
check_global_access(thd, SHOW_DB_ACL))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error;
|
|
|
|
res= mysqld_show_dbs(thd, (lex->wild ? lex->wild->ptr() : NullS));
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
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;
|
|
|
|
mysqld_list_processes(thd,thd->master_access & PROCESS_ACL ? NullS :
|
|
|
|
thd->priv_user,lex->verbose);
|
|
|
|
break;
|
2002-06-12 23:13:12 +02:00
|
|
|
case SQLCOM_SHOW_TABLE_TYPES:
|
|
|
|
res= mysqld_show_table_types(thd);
|
|
|
|
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:
|
2002-06-28 18:30:09 +02:00
|
|
|
res= mysqld_show(thd,(lex->wild ? lex->wild->ptr() : NullS),status_vars,
|
2002-07-23 17:31:22 +02:00
|
|
|
OPT_GLOBAL);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_SHOW_VARIABLES:
|
|
|
|
res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS),
|
2002-07-23 17:31:22 +02:00
|
|
|
init_vars, lex->option_type);
|
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
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2000-12-15 12:18:52 +01:00
|
|
|
#else
|
|
|
|
{
|
|
|
|
if (grant_option && check_access(thd, FILE_ACL, any_db))
|
|
|
|
goto error;
|
|
|
|
res= mysqld_show_logs(thd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SHOW_TABLES:
|
2000-12-18 22:22:20 +01:00
|
|
|
/* FALL THROUGH */
|
2000-07-31 21:29:14 +02:00
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
#else
|
|
|
|
{
|
2001-06-07 13:10:58 +02:00
|
|
|
char *db=select_lex->db ? select_lex->db : thd->db;
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!db)
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
}
|
|
|
|
remove_escape(db); // Fix escaped '_'
|
2001-01-31 03:47:25 +01:00
|
|
|
if (check_db_name(db))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_WRONG_DB_NAME, db);
|
2001-01-31 03:47:25 +01:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
if (check_access(thd,SELECT_ACL,db,&thd->col_access))
|
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
/* grant is checked in mysqld_show_tables */
|
2001-07-11 09:36:22 +02:00
|
|
|
if (select_lex->options & SELECT_DESCRIBE)
|
2000-12-18 22:22:20 +01:00
|
|
|
res= mysqld_extend_show_tables(thd,db,
|
2001-04-11 13:04:03 +02:00
|
|
|
(lex->wild ? lex->wild->ptr() : NullS));
|
2000-07-31 21:29:14 +02:00
|
|
|
else
|
|
|
|
res= mysqld_show_tables(thd,db,
|
|
|
|
(lex->wild ? lex->wild->ptr() : NullS));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#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;
|
2002-06-11 14:40:06 +02:00
|
|
|
case SQLCOM_SHOW_CHARSETS:
|
|
|
|
res= mysqld_show_charsets(thd,(lex->wild ? lex->wild->ptr() : NullS));
|
2001-07-11 09:36:22 +02:00
|
|
|
break;
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SHOW_FIELDS:
|
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
#else
|
|
|
|
{
|
2001-08-15 19:09:20 +02:00
|
|
|
char *db=tables->db;
|
|
|
|
if (!*db)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
}
|
|
|
|
remove_escape(db); // Fix escaped '_'
|
2002-09-20 13:05:18 +02:00
|
|
|
remove_escape(tables->real_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,&thd->col_access))
|
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
tables->grant.privilege=thd->col_access;
|
|
|
|
if (grant_option && check_grant(thd,SELECT_ACL,tables,2))
|
|
|
|
goto error;
|
|
|
|
res= mysqld_show_fields(thd,tables,
|
2001-01-22 04:32:58 +01:00
|
|
|
(lex->wild ? lex->wild->ptr() : NullS),
|
|
|
|
lex->verbose);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
case SQLCOM_SHOW_KEYS:
|
|
|
|
#ifdef DONT_ALLOW_SHOW_COMMANDS
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2000-07-31 21:29:14 +02:00
|
|
|
#else
|
|
|
|
{
|
2001-08-15 19:09:20 +02:00
|
|
|
char *db=tables->db;
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!db)
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
}
|
|
|
|
remove_escape(db); // Fix escaped '_'
|
2002-09-20 13:05:18 +02:00
|
|
|
remove_escape(tables->real_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!tables->db)
|
|
|
|
tables->db=thd->db;
|
|
|
|
if (check_access(thd,SELECT_ACL,db,&thd->col_access))
|
|
|
|
goto error; /* purecov: inspected */
|
|
|
|
tables->grant.privilege=thd->col_access;
|
|
|
|
if (grant_option && check_grant(thd,SELECT_ACL,tables,2))
|
|
|
|
goto error;
|
|
|
|
res= mysqld_show_keys(thd,tables);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
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;
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_LOAD:
|
|
|
|
{
|
|
|
|
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
|
|
|
{
|
|
|
|
if (check_access(thd,privilege | FILE_ACL,tables->db))
|
|
|
|
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
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NOT_ALLOWED_COMMAND);
|
2002-02-13 20:53:26 +01:00
|
|
|
goto error;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
if (check_access(thd,privilege,tables->db,&tables->grant.privilege) ||
|
2001-08-14 19:33:49 +02:00
|
|
|
grant_option && check_grant(thd,privilege,tables))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
res=mysql_load(thd, lex->exchange, tables, lex->field_list,
|
|
|
|
lex->duplicates, (bool) lex->local_file, lex->lock_option);
|
|
|
|
break;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif /* EMBEDDED_LIBRARY */
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_SET_OPTION:
|
2003-01-04 15:33:42 +01:00
|
|
|
if (tables && ((res= check_table_access(thd, SELECT_ACL, tables)) ||
|
|
|
|
(res= open_and_lock_tables(thd,tables))))
|
|
|
|
break;
|
|
|
|
fix_tables_pointers(lex->all_selects_list);
|
|
|
|
if (!(res= sql_set_variables(thd, &lex->var_list)))
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2003-01-04 15:33:42 +01:00
|
|
|
if (thd->net.report_error)
|
|
|
|
res= -1;
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
2003-01-04 15:33:42 +01:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_UNLOCK_TABLES:
|
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);
|
2001-01-01 13:17:10 +01:00
|
|
|
if (check_db_used(thd,tables) || end_active_trans(thd))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error;
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, tables))
|
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;
|
2000-07-31 21:29:14 +02:00
|
|
|
if (!(res=open_and_lock_tables(thd,tables)))
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
case SQLCOM_CREATE_DB:
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2002-05-27 22:52:21 +02:00
|
|
|
if (!strip_sp(lex->name) || check_db_name(lex->name))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_WRONG_DB_NAME, 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)))
|
|
|
|
break;
|
2003-02-07 14:47:24 +01:00
|
|
|
#endif
|
2001-08-14 19:33:49 +02:00
|
|
|
if (check_access(thd,CREATE_ACL,lex->name,0,1))
|
|
|
|
break;
|
2002-06-21 13:55:55 +02:00
|
|
|
res=mysql_create_db(thd,lex->name,&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
|
|
|
{
|
2002-05-27 22:52:21 +02:00
|
|
|
if (!strip_sp(lex->name) || check_db_name(lex->name))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_WRONG_DB_NAME, 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)))
|
|
|
|
break;
|
2003-02-07 14:47:24 +01:00
|
|
|
#endif
|
2001-09-02 12:47:00 +02:00
|
|
|
if (check_access(thd,DROP_ACL,lex->name,0,1))
|
2001-08-14 19:33:49 +02:00
|
|
|
break;
|
2001-09-02 12:47:00 +02:00
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
|
2001-09-02 12:47:00 +02:00
|
|
|
goto error;
|
|
|
|
}
|
2001-09-14 18:50:56 +02:00
|
|
|
res=mysql_rm_db(thd,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))
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_WRONG_DB_NAME, lex->name);
|
2002-06-27 11:41:02 +02:00
|
|
|
break;
|
|
|
|
}
|
2002-07-02 11:31:54 +02:00
|
|
|
if (check_access(thd,ALTER_ACL,lex->name,0,1))
|
2002-06-27 11:41:02 +02:00
|
|
|
break;
|
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
|
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))
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_WRONG_DB_NAME, lex->name);
|
2002-07-02 11:31:54 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (check_access(thd,DROP_ACL,lex->name,0,1))
|
|
|
|
break;
|
|
|
|
if (thd->locked_tables || thd->active_transaction())
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
|
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;
|
|
|
|
}
|
2003-02-21 17:37:05 +01:00
|
|
|
case SQLCOM_CREATE_FUNCTION: // UDF function
|
|
|
|
{
|
|
|
|
if (check_access(thd,INSERT_ACL,"mysql",0,1))
|
|
|
|
break;
|
2000-07-31 21:29:14 +02:00
|
|
|
#ifdef HAVE_DLOPEN
|
2003-02-21 17:37:05 +01:00
|
|
|
sp_head *sph= sp_find_function(thd, &lex->udf.name);
|
|
|
|
if (sph)
|
|
|
|
{
|
|
|
|
net_printf(thd, ER_UDF_EXISTS, lex->udf.name.str);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (!(res = mysql_create_function(thd,&lex->udf)))
|
|
|
|
send_ok(thd);
|
2000-07-31 21:29:14 +02:00
|
|
|
#else
|
2003-02-21 17:37:05 +01:00
|
|
|
res= -1;
|
2000-07-31 21:29:14 +02:00
|
|
|
#endif
|
|
|
|
break;
|
2003-02-21 17:37:05 +01:00
|
|
|
}
|
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,
|
|
|
|
tables && tables->db ? tables->db : select_lex->db,
|
|
|
|
tables ? &tables->grant.privilege : 0,
|
|
|
|
tables ? 0 : 1))
|
|
|
|
goto error;
|
|
|
|
|
2002-06-12 14:04:18 +02:00
|
|
|
/*
|
|
|
|
Check that the user isn't trying to change a password for another
|
|
|
|
user if he doesn't have UPDATE privilege to the MySQL database
|
|
|
|
*/
|
2001-08-14 19:33:49 +02:00
|
|
|
|
|
|
|
if (thd->user) // If not replication
|
|
|
|
{
|
|
|
|
LEX_USER *user;
|
|
|
|
List_iterator <LEX_USER> user_list(lex->users_list);
|
|
|
|
while ((user=user_list++))
|
|
|
|
{
|
|
|
|
if (user->password.str &&
|
|
|
|
(strcmp(thd->user,user->user.str) ||
|
|
|
|
user->host.str &&
|
2003-01-29 14:31:20 +01:00
|
|
|
my_strcasecmp(&my_charset_latin1,
|
2002-03-12 18:37:58 +01:00
|
|
|
user->host.str, thd->host_or_ip)))
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
|
|
|
if (check_access(thd, UPDATE_ACL, "mysql",0,1))
|
|
|
|
goto error;
|
|
|
|
break; // We are allowed to do changes
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (tables)
|
|
|
|
{
|
|
|
|
if (grant_option && check_grant(thd,
|
|
|
|
(lex->grant | lex->grant_tot_col |
|
|
|
|
GRANT_ACL),
|
|
|
|
tables))
|
|
|
|
goto error;
|
2001-11-06 23:13:29 +01:00
|
|
|
if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns,
|
|
|
|
lex->grant,
|
|
|
|
lex->sql_command == SQLCOM_REVOKE)))
|
2001-08-14 19:33:49 +02:00
|
|
|
{
|
2001-10-08 22:20:19 +02:00
|
|
|
mysql_update_log.write(thd, thd->query, thd->query_length);
|
2001-08-14 19:33:49 +02:00
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
2002-11-07 03:02:37 +01:00
|
|
|
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
|
2001-08-14 19:33:49 +02:00
|
|
|
mysql_bin_log.write(&qinfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (lex->columns.elements)
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_ILLEGAL_GRANT_FOR_TABLE);
|
2001-08-14 19:33:49 +02:00
|
|
|
res=1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
|
|
|
|
lex->sql_command == SQLCOM_REVOKE);
|
|
|
|
if (!res)
|
|
|
|
{
|
2001-10-08 22:20:19 +02:00
|
|
|
mysql_update_log.write(thd, thd->query, thd->query_length);
|
2001-08-14 19:33:49 +02:00
|
|
|
if (mysql_bin_log.is_open())
|
|
|
|
{
|
2002-11-07 03:02:37 +01:00
|
|
|
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
|
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++))
|
2002-06-20 15:46:25 +02:00
|
|
|
reset_mqh(thd,user);
|
2002-02-01 19:53:24 +01:00
|
|
|
}
|
2001-08-14 19:33:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
case SQLCOM_FLUSH:
|
2000-10-14 10:16:17 +02:00
|
|
|
case SQLCOM_RESET:
|
2002-06-12 14:04:18 +02:00
|
|
|
if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, tables))
|
2000-07-31 21:29:14 +02:00
|
|
|
goto error;
|
2002-12-13 11:05:24 +01:00
|
|
|
/* error sending is deferred to reload_acl_and_cache */
|
2003-02-16 17:39:12 +01:00
|
|
|
reload_acl_and_cache(thd, lex->type, tables);
|
2000-07-31 21:29:14 +02:00
|
|
|
break;
|
|
|
|
case SQLCOM_KILL:
|
|
|
|
kill_one_thread(thd,lex->thread_id);
|
|
|
|
break;
|
|
|
|
case SQLCOM_SHOW_GRANTS:
|
|
|
|
res=0;
|
2001-08-14 19:33:49 +02:00
|
|
|
if ((thd->priv_user &&
|
|
|
|
!strcmp(thd->priv_user,lex->grant_user->user.str)) ||
|
2001-01-14 11:25:30 +01:00
|
|
|
!check_access(thd, SELECT_ACL, "mysql",0,1))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
res = mysql_show_grants(thd,lex->grant_user);
|
|
|
|
}
|
|
|
|
break;
|
2001-04-07 00:18:33 +02:00
|
|
|
case SQLCOM_HA_OPEN:
|
2001-08-14 19:33:49 +02:00
|
|
|
if (check_db_used(thd,tables) ||
|
|
|
|
check_table_access(thd,SELECT_ACL, tables))
|
2001-04-07 00:18:33 +02:00
|
|
|
goto error;
|
|
|
|
res = mysql_ha_open(thd, tables);
|
|
|
|
break;
|
|
|
|
case SQLCOM_HA_CLOSE:
|
|
|
|
if (check_db_used(thd,tables))
|
|
|
|
goto error;
|
|
|
|
res = mysql_ha_close(thd, tables);
|
|
|
|
break;
|
|
|
|
case SQLCOM_HA_READ:
|
2001-08-14 19:33:49 +02:00
|
|
|
if (check_db_used(thd,tables) ||
|
|
|
|
check_table_access(thd,SELECT_ACL, tables))
|
2001-04-07 00:18:33 +02:00
|
|
|
goto error;
|
2001-04-13 16:18:44 +02:00
|
|
|
res = mysql_ha_read(thd, tables, lex->ha_read_mode, lex->backup_dir,
|
2001-08-14 19:33:49 +02:00
|
|
|
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;
|
|
|
|
thd->locked_tables=0; // Will be automaticly closed
|
|
|
|
close_thread_tables(thd); // Free tables
|
|
|
|
}
|
2000-11-29 04:09:28 +01:00
|
|
|
if (end_active_trans(thd))
|
|
|
|
{
|
|
|
|
res= -1;
|
|
|
|
}
|
|
|
|
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;
|
2002-10-02 12:33:08 +02:00
|
|
|
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
|
|
|
|
res= -1;
|
|
|
|
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
|
|
|
{
|
|
|
|
if (thd->options & OPTION_STATUS_NO_TRANS_UPDATE)
|
2002-10-02 12:33:08 +02:00
|
|
|
send_warning(thd,ER_WARNING_NOT_COMPLETE_ROLLBACK,0);
|
2000-11-24 00:51:18 +01:00
|
|
|
else
|
2002-10-02 12:33:08 +02:00
|
|
|
send_ok(thd);
|
2000-11-24 00:51:18 +01:00
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
else
|
|
|
|
res= -1;
|
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-03-05 19:45:17 +01:00
|
|
|
case SQLCOM_CREATE_PROCEDURE:
|
|
|
|
case SQLCOM_CREATE_SPFUNCTION:
|
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
|
|
|
if (!lex->sphead)
|
2003-01-15 15:39:36 +01:00
|
|
|
{
|
2003-03-05 19:45:17 +01:00
|
|
|
res= -1; // Shouldn't happen
|
|
|
|
break;
|
2003-01-15 15:39:36 +01:00
|
|
|
}
|
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-02-21 17:37:05 +01:00
|
|
|
uint namelen;
|
|
|
|
char *name= lex->sphead->name(&namelen);
|
2003-02-26 19:22:29 +01:00
|
|
|
#ifdef HAVE_DLOPEN
|
2003-03-05 19:45:17 +01:00
|
|
|
if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
|
2003-02-21 17:37:05 +01:00
|
|
|
{
|
2003-03-05 19:45:17 +01:00
|
|
|
udf_func *udf = find_udf(name, namelen);
|
|
|
|
|
|
|
|
if (udf)
|
|
|
|
{
|
|
|
|
net_printf(thd, ER_UDF_EXISTS, name);
|
|
|
|
goto error;
|
|
|
|
}
|
2003-02-21 17:37:05 +01:00
|
|
|
}
|
2003-02-26 19:22:29 +01: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
|
|
|
res= lex->sphead->create(thd);
|
2003-02-21 17:37:05 +01:00
|
|
|
switch (res)
|
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-02-21 17:37:05 +01:00
|
|
|
case SP_OK:
|
|
|
|
send_ok(thd);
|
|
|
|
break;
|
|
|
|
case SP_WRITE_ROW_FAILED:
|
2003-03-05 19:45:17 +01:00
|
|
|
net_printf(thd, ER_SP_ALREADY_EXISTS, SP_TYPE_STRING(lex), name);
|
2003-01-15 15:39:36 +01:00
|
|
|
goto error;
|
2003-02-21 17:37:05 +01:00
|
|
|
default:
|
2003-03-05 19:45:17 +01:00
|
|
|
net_printf(thd, ER_SP_STORE_FAILED, SP_TYPE_STRING(lex), name);
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SQLCOM_CALL:
|
|
|
|
{
|
|
|
|
sp_head *sp;
|
|
|
|
|
2003-02-21 17:37:05 +01:00
|
|
|
sp= sp_find_procedure(thd, &lex->udf.name);
|
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
|
|
|
if (! sp)
|
|
|
|
{
|
2003-03-05 19:45:17 +01:00
|
|
|
net_printf(thd, ER_SP_DOES_NOT_EXIST, "PROCEDURE", lex->udf.name);
|
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-02-18 19:58:03 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2003-01-15 15:39:36 +01:00
|
|
|
// When executing substatements, they're assumed to send_error when
|
|
|
|
// it happens, but not to send_ok.
|
|
|
|
my_bool nsok= thd->net.no_send_ok;
|
|
|
|
|
|
|
|
thd->net.no_send_ok= TRUE;
|
2003-02-18 19:58:03 +01:00
|
|
|
#endif
|
2003-02-26 19:22:29 +01:00
|
|
|
res= sp->execute_procedure(thd, &lex->value_list);
|
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-01-15 15:39:36 +01:00
|
|
|
|
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
|
|
|
if (res == 0)
|
|
|
|
send_ok(thd);
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
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
|
|
|
{
|
|
|
|
sp_head *sp;
|
|
|
|
|
2003-02-21 17:37:05 +01:00
|
|
|
if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
|
|
|
|
sp= sp_find_procedure(thd, &lex->udf.name);
|
|
|
|
else
|
|
|
|
sp= sp_find_function(thd, &lex->udf.name);
|
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
|
|
|
if (! sp)
|
|
|
|
{
|
2003-03-05 19:45:17 +01:00
|
|
|
net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),lex->udf.name);
|
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
|
|
|
|
{
|
|
|
|
/* QQ This is an no-op right now, since we haven't
|
|
|
|
put the characteristics in yet. */
|
|
|
|
send_ok(thd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
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
|
|
|
{
|
2003-02-21 17:37:05 +01:00
|
|
|
if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
|
|
|
|
res= sp_drop_procedure(thd, lex->udf.name.str, lex->udf.name.length);
|
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-02-21 17:37:05 +01:00
|
|
|
res= sp_drop_function(thd, lex->udf.name.str, lex->udf.name.length);
|
|
|
|
#ifdef HAVE_DLOPEN
|
|
|
|
if (res == SP_KEY_NOT_FOUND)
|
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-02-21 17:37:05 +01:00
|
|
|
udf_func *udf = find_udf(lex->udf.name.str, lex->udf.name.length);
|
|
|
|
if (udf)
|
|
|
|
{
|
|
|
|
if (check_access(thd, DELETE_ACL, "mysql", 0, 1))
|
|
|
|
goto error;
|
|
|
|
if (!(res = mysql_drop_function(thd,&lex->udf.name)))
|
|
|
|
{
|
|
|
|
send_ok(thd);
|
|
|
|
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-02-21 17:37:05 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
switch (res)
|
|
|
|
{
|
|
|
|
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-05 19:45:17 +01:00
|
|
|
net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),
|
|
|
|
lex->udf.name.str);
|
2003-02-21 17:37:05 +01:00
|
|
|
goto error;
|
|
|
|
default:
|
2003-03-05 19:45:17 +01:00
|
|
|
net_printf(thd, ER_SP_DROP_FAILED, SP_COM_STRING(lex),
|
|
|
|
lex->udf.name.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
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
thd->proc_info="query end"; // QQ
|
2003-01-15 15:39:36 +01:00
|
|
|
|
|
|
|
// We end up here if res == 0 and send_ok() has been done,
|
|
|
|
// or res != 0 and no send_error() has yet been done.
|
2000-07-31 21:29:14 +02:00
|
|
|
if (res < 0)
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
|
2003-02-12 16:17:03 +01:00
|
|
|
DBUG_RETURN(res);
|
2000-07-31 21:29:14 +02:00
|
|
|
|
|
|
|
error:
|
2003-01-15 15:39:36 +01:00
|
|
|
// We end up here if send_error() has already been done.
|
2003-02-12 16:17:03 +01:00
|
|
|
DBUG_RETURN(-1);
|
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
|
|
|
|
Returns true (error) if we can't get the privileges and we don't use
|
|
|
|
table/column grants.
|
|
|
|
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.
|
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
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_ENTER("check_access");
|
|
|
|
DBUG_PRINT("enter",("want_access: %lu master_access: %lu", want_access,
|
|
|
|
thd->master_access));
|
|
|
|
ulong db_access,dummy;
|
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
|
|
|
{
|
2001-12-22 14:13:31 +01:00
|
|
|
if (!no_errors)
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_RETURN(TRUE); /* purecov: tested */
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((thd->master_access & want_access) == want_access)
|
|
|
|
{
|
|
|
|
*save_priv=thd->master_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
|
2001-12-22 14:13:31 +01:00
|
|
|
if (!no_errors)
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_ACCESS_DENIED_ERROR,
|
2001-12-22 14:13:31 +01:00
|
|
|
thd->priv_user,
|
|
|
|
thd->host_or_ip,
|
|
|
|
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)))
|
|
|
|
db_access=acl_get(thd->host, thd->ip, (char*) &thd->remote.sin_addr,
|
|
|
|
thd->priv_user, db); /* purecov: inspected */
|
|
|
|
else
|
|
|
|
db_access=thd->db_access;
|
2002-06-09 08:14:25 +02:00
|
|
|
// Remove SHOW attribute and access rights we already have
|
|
|
|
want_access &= ~(thd->master_access | EXTRA_ACL);
|
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 ||
|
2001-01-14 11:25:30 +01:00
|
|
|
((grant_option && !dont_check_global_grants) &&
|
|
|
|
!(want_access & ~TABLE_ACLS)))
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_RETURN(FALSE); /* Ok */
|
2001-12-22 14:13:31 +01:00
|
|
|
if (!no_errors)
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_DBACCESS_DENIED_ERROR,
|
2001-12-22 14:13:31 +01:00
|
|
|
thd->priv_user,
|
|
|
|
thd->host_or_ip,
|
|
|
|
db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */
|
2002-06-12 14:04:18 +02:00
|
|
|
DBUG_RETURN(TRUE); /* purecov: tested */
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-12 14:04:18 +02:00
|
|
|
/* check for global access and give descriptive error message if it fails */
|
|
|
|
|
|
|
|
bool check_global_access(THD *thd, ulong want_access)
|
2001-03-21 00:02:22 +01:00
|
|
|
{
|
2002-06-12 14:04:18 +02:00
|
|
|
char command[128];
|
|
|
|
if ((thd->master_access & want_access) == want_access)
|
|
|
|
return 0;
|
|
|
|
get_privilege_desc(command, sizeof(command), want_access);
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_SPECIFIC_ACCESS_DENIED_ERROR,
|
2002-06-12 14:04:18 +02:00
|
|
|
command);
|
|
|
|
return 1;
|
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;
|
|
|
|
for (; tables ; tables=tables->next)
|
|
|
|
{
|
2002-11-12 13:40:32 +01:00
|
|
|
if (tables->derived || (tables->table && (int)tables->table->tmp_table))
|
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,
|
2001-12-22 14:13:31 +01:00
|
|
|
test(want_access & EXTRA_ACL), no_errors);
|
2000-07-31 21:29:14 +02:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
{
|
|
|
|
for (; tables ; tables=tables->next)
|
|
|
|
{
|
|
|
|
if (!tables->db)
|
|
|
|
{
|
|
|
|
if (!(tables->db=thd->db))
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */
|
2000-07-31 21:29:14 +02:00
|
|
|
return TRUE; /* purecov: tested */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-01-31 03:47:25 +01:00
|
|
|
static 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;
|
|
|
|
for (tmp=table_list; tmp ; tmp=tmp->next)
|
2001-08-29 16:33:41 +02:00
|
|
|
{
|
|
|
|
if (!tmp->db || !tmp->db[0])
|
|
|
|
tmp->db=db;
|
2001-09-03 04:16:15 +02:00
|
|
|
else if (strcmp(tmp->db,db))
|
2001-08-29 16:33:41 +02:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
send_error(thd,ER_UNION_TABLES_IN_DIFFERENT_DIR);
|
2001-08-29 16:33:41 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2000-09-14 01:39:07 +02:00
|
|
|
error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
|
|
|
|
table_list);
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MY_YACC_INIT 1000 // Start with big alloc
|
|
|
|
#define MY_YACC_MAX 32000 // Because of 'short'
|
|
|
|
|
|
|
|
bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, int *yystacksize)
|
|
|
|
{
|
|
|
|
LEX *lex=current_lex;
|
|
|
|
int old_info=0;
|
|
|
|
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
|
2000-07-31 21:29:14 +02:00
|
|
|
mysql_init_query(THD *thd)
|
|
|
|
{
|
|
|
|
DBUG_ENTER("mysql_init_query");
|
2002-11-09 14:40:46 +01:00
|
|
|
LEX *lex=&thd->lex;
|
|
|
|
lex->unit.init_query();
|
|
|
|
lex->unit.init_select();
|
2003-01-25 01:25:52 +01:00
|
|
|
lex->unit.thd= thd;
|
2002-11-09 14:40:46 +01:00
|
|
|
lex->select_lex.init_query();
|
|
|
|
lex->value_list.empty();
|
|
|
|
lex->param_list.empty();
|
2002-12-15 21:01:09 +01:00
|
|
|
lex->unit.next= lex->unit.master= lex->unit.link_next= 0;
|
|
|
|
lex->unit.prev= lex->unit.link_prev= 0;
|
2002-11-30 18:58:53 +01:00
|
|
|
lex->unit.global_parameters= lex->unit.slave= lex->current_select=
|
2002-11-27 00:12:16 +01:00
|
|
|
lex->all_selects_list= &lex->select_lex;
|
2002-11-09 14:40:46 +01:00
|
|
|
lex->select_lex.master= &lex->unit;
|
|
|
|
lex->select_lex.prev= &lex->unit.slave;
|
2002-12-15 21:01:09 +01:00
|
|
|
lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0;
|
2002-11-27 00:12:16 +01:00
|
|
|
lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list);
|
2003-02-12 20:55:37 +01:00
|
|
|
lex->describe= 0;
|
|
|
|
lex->derived_tables= FALSE;
|
2003-01-30 13:35:07 +01:00
|
|
|
lex->lock_option= TL_READ;
|
|
|
|
lex->found_colon= 0;
|
2003-02-12 20:55:37 +01:00
|
|
|
lex->safe_to_cache_query= 1;
|
2003-01-30 13:35:07 +01:00
|
|
|
thd->select_number= lex->select_lex.select_number= 1;
|
2002-05-06 23:04:16 +02:00
|
|
|
thd->free_list= 0;
|
2002-10-02 12:33:08 +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;
|
2003-01-30 21:15:44 +01:00
|
|
|
thd->is_fatal_error= thd->rand_used= 0;
|
2003-01-18 20:53:38 +01:00
|
|
|
thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
|
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)
|
|
|
|
{
|
2002-11-04 21:12:45 +01:00
|
|
|
SELECT_LEX *select_lex= lex->current_select->select_lex();
|
2002-05-06 23:04:16 +02:00
|
|
|
select_lex->init_select();
|
2002-11-30 18:58:53 +01:00
|
|
|
select_lex->master_unit()->select_limit= select_lex->select_limit=
|
2002-08-30 11:40:40 +02:00
|
|
|
lex->thd->variables.select_limit;
|
2002-12-05 23:40:28 +01:00
|
|
|
if (select_lex == &lex->select_lex)
|
|
|
|
{
|
|
|
|
lex->exchange= 0;
|
|
|
|
lex->result= 0;
|
|
|
|
lex->proc_list.first= 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
|
|
|
{
|
2002-10-30 12:18:52 +01:00
|
|
|
SELECT_LEX *select_lex = new SELECT_LEX();
|
2002-09-26 22:08:22 +02:00
|
|
|
select_lex->select_number= ++lex->thd->select_number;
|
2001-12-13 14:53:18 +01:00
|
|
|
if (!select_lex)
|
|
|
|
return 1;
|
2002-05-06 23:04:16 +02:00
|
|
|
select_lex->init_query();
|
|
|
|
select_lex->init_select();
|
|
|
|
if (move_down)
|
|
|
|
{
|
|
|
|
/* first select_lex of subselect or derived table */
|
2002-10-30 12:18:52 +01:00
|
|
|
SELECT_LEX_UNIT *unit= new SELECT_LEX_UNIT();
|
2002-05-06 23:04:16 +02:00
|
|
|
if (!unit)
|
|
|
|
return 1;
|
|
|
|
unit->init_query();
|
|
|
|
unit->init_select();
|
2003-01-25 01:25:52 +01:00
|
|
|
unit->thd= lex->thd;
|
2002-10-30 12:18:52 +01:00
|
|
|
unit->include_down(lex->current_select);
|
2002-12-15 21:01:09 +01:00
|
|
|
unit->link_next= 0;
|
|
|
|
unit->link_prev= 0;
|
2002-05-06 23:04:16 +02:00
|
|
|
select_lex->include_down(unit);
|
|
|
|
}
|
|
|
|
else
|
2002-10-30 12:18:52 +01:00
|
|
|
select_lex->include_neighbour(lex->current_select);
|
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;
|
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)
|
|
|
|
{
|
|
|
|
LEX *lex;
|
|
|
|
LEX_STRING tmp;
|
|
|
|
DBUG_ENTER("create_select_for_variable");
|
|
|
|
lex= current_lex;
|
|
|
|
mysql_init_select(lex);
|
|
|
|
lex->sql_command= SQLCOM_SELECT;
|
|
|
|
tmp.str= (char*) var_name;
|
|
|
|
tmp.length=strlen(var_name);
|
2002-12-06 20:11:27 +01:00
|
|
|
add_item_to_list(lex->thd, get_system_var(OPT_SESSION, tmp));
|
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;
|
2002-05-06 23:04:16 +02:00
|
|
|
lex->auxilliary_table_list= lex->select_lex.table_list;
|
2002-10-30 12:18:52 +01:00
|
|
|
lex->select_lex.init_query();
|
2002-01-02 23:46:43 +01:00
|
|
|
}
|
2001-12-13 14:53:18 +01:00
|
|
|
|
2002-06-16 16:06:12 +02:00
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
void
|
2002-05-06 23:04:16 +02:00
|
|
|
mysql_parse(THD *thd, char *inBuf, uint length)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
DBUG_ENTER("mysql_parse");
|
|
|
|
|
|
|
|
mysql_init_query(thd);
|
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
|
|
|
{
|
|
|
|
LEX *lex=lex_start(thd, (uchar*) inBuf, length);
|
2003-01-30 21:15:44 +01:00
|
|
|
if (!yyparse((void *)thd) && ! thd->is_fatal_error)
|
2001-12-02 13:34:01 +01:00
|
|
|
{
|
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
|
|
|
|
{
|
2002-11-28 18:29:26 +01:00
|
|
|
if (thd->net.report_error)
|
|
|
|
send_error(thd, 0, NullS);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mysql_execute_command(thd);
|
2003-01-20 15:47:25 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY /* TODO query cache in embedded library*/
|
2002-11-28 18:29:26 +01:00
|
|
|
query_cache_end_of_result(&thd->net);
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2002-11-28 18:29:26 +01:00
|
|
|
}
|
2002-05-15 12:50:38 +02:00
|
|
|
}
|
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));
|
2003-01-20 15:47:25 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY /* TODO query cache in embedded library*/
|
2001-12-02 13:34:01 +01:00
|
|
|
query_cache_abort(&thd->net);
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2002-07-24 18:55:08 +02:00
|
|
|
}
|
2001-12-02 13:34:01 +01:00
|
|
|
thd->proc_info="freeing items";
|
2002-10-02 12:33:08 +02:00
|
|
|
free_items(thd->free_list); /* Free strings used by items */
|
2001-12-02 13:34:01 +01:00
|
|
|
lex_end(lex);
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
** 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,
|
|
|
|
Item *default_value, Item *comment,
|
2002-06-07 14:23:33 +02:00
|
|
|
char *change, TYPELIB *interval, CHARSET_INFO *cs)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
register create_field *new_field;
|
|
|
|
LEX *lex= &thd->lex;
|
|
|
|
uint allowed_type_modifier=0;
|
2002-11-21 01:07:14 +01:00
|
|
|
char warn_buff[MYSQL_ERRMSG_SIZE];
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_ENTER("add_field_to_list");
|
|
|
|
|
|
|
|
if (strlen(field_name) > NAME_LEN)
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd, ER_TOO_LONG_IDENT, 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,
|
2000-07-31 21:29:14 +02:00
|
|
|
lex->col_list));
|
|
|
|
lex->col_list.empty();
|
|
|
|
}
|
|
|
|
if (type_modifier & (UNIQUE_FLAG | UNIQUE_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::UNIQUE, NullS, HA_KEY_ALG_UNDEF,
|
2000-07-31 21:29:14 +02:00
|
|
|
lex->col_list));
|
|
|
|
lex->col_list.empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (default_value && default_value->type() == Item::NULL_ITEM)
|
|
|
|
{
|
|
|
|
if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
|
|
|
|
NOT_NULL_FLAG)
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_INVALID_DEFAULT,field_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
default_value=0;
|
|
|
|
}
|
|
|
|
if (!(new_field=new create_field()))
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
new_field->field=0;
|
|
|
|
new_field->field_name=field_name;
|
|
|
|
new_field->def= (type_modifier & AUTO_INCREMENT_FLAG ? 0 : default_value);
|
|
|
|
new_field->flags= type_modifier;
|
|
|
|
new_field->unireg_check= (type_modifier & AUTO_INCREMENT_FLAG ?
|
|
|
|
Field::NEXT_NUMBER : Field::NONE);
|
|
|
|
new_field->decimals= decimals ? (uint) set_zone(atoi(decimals),0,
|
|
|
|
NOT_FIXED_DEC-1) : 0;
|
|
|
|
new_field->sql_type=type;
|
|
|
|
new_field->length=0;
|
|
|
|
new_field->change=change;
|
|
|
|
new_field->interval=0;
|
|
|
|
new_field->pack_length=0;
|
2002-06-07 14:23:33 +02:00
|
|
|
new_field->charset=cs;
|
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 */
|
|
|
|
new_field->comment.str= (char*) comment->str_value.ptr();
|
|
|
|
new_field->comment.length=comment->str_value.length();
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
if (length)
|
|
|
|
if (!(new_field->length= (uint) atoi(length)))
|
|
|
|
length=0; /* purecov: inspected */
|
|
|
|
uint sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1;
|
|
|
|
|
|
|
|
if (new_field->length && new_field->decimals &&
|
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:
|
|
|
|
if (!length) new_field->length=3+sign_len;
|
|
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_SHORT:
|
|
|
|
if (!length) new_field->length=5+sign_len;
|
|
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_INT24:
|
|
|
|
if (!length) new_field->length=8+sign_len;
|
|
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_LONG:
|
|
|
|
if (!length) new_field->length=10+sign_len;
|
|
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_LONGLONG:
|
|
|
|
if (!length) new_field->length=20;
|
|
|
|
allowed_type_modifier= AUTO_INCREMENT_FLAG;
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_NULL:
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_DECIMAL:
|
|
|
|
if (!length)
|
|
|
|
new_field->length = 10; // Default length for DECIMAL
|
|
|
|
new_field->length+=sign_len;
|
|
|
|
if (new_field->decimals)
|
|
|
|
new_field->length++;
|
|
|
|
break;
|
2002-11-21 01:07:14 +01:00
|
|
|
case FIELD_TYPE_STRING:
|
|
|
|
case FIELD_TYPE_VAR_STRING:
|
|
|
|
if (new_field->length < MAX_FIELD_WIDTH || default_value)
|
|
|
|
break;
|
|
|
|
/* Convert long CHAR() and VARCHAR columns to TEXT or BLOB */
|
|
|
|
new_field->sql_type= FIELD_TYPE_BLOB;
|
|
|
|
sprintf(warn_buff, ER(ER_AUTO_CONVERT), field_name, "CHAR",
|
2003-01-29 14:31:20 +01:00
|
|
|
(cs == &my_charset_bin) ? "BLOB" : "TEXT");
|
2002-11-21 01:07:14 +01:00
|
|
|
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_AUTO_CONVERT,
|
|
|
|
warn_buff);
|
|
|
|
/* fall through */
|
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:
|
2002-11-21 01:07:14 +01:00
|
|
|
if (new_field->length)
|
|
|
|
{
|
|
|
|
/* The user has given a length to the blob column */
|
|
|
|
if (new_field->length < 256)
|
|
|
|
type= FIELD_TYPE_TINY_BLOB;
|
|
|
|
if (new_field->length < 65536)
|
|
|
|
type= FIELD_TYPE_BLOB;
|
|
|
|
else if (new_field->length < 256L*256L*256L)
|
|
|
|
type= FIELD_TYPE_MEDIUM_BLOB;
|
|
|
|
else
|
|
|
|
type= FIELD_TYPE_LONG_BLOB;
|
|
|
|
new_field->length= 0;
|
|
|
|
}
|
|
|
|
new_field->sql_type= type;
|
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())
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_BLOB_CANT_HAVE_DEFAULT,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)
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_WRONG_FIELD_SPEC,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
|
|
|
|
else
|
|
|
|
{
|
|
|
|
new_field->length=((new_field->length+1)/2)*2; /* purecov: inspected */
|
|
|
|
new_field->length= min(new_field->length,14); /* purecov: inspected */
|
|
|
|
}
|
|
|
|
new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG | NOT_NULL_FLAG;
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_DATE: // Old date type
|
|
|
|
if (protocol_version != PROTOCOL_VERSION-1)
|
|
|
|
new_field->sql_type=FIELD_TYPE_NEWDATE;
|
|
|
|
/* fall trough */
|
|
|
|
case FIELD_TYPE_NEWDATE:
|
|
|
|
new_field->length=10;
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_TIME:
|
|
|
|
new_field->length=10;
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_DATETIME:
|
|
|
|
new_field->length=19;
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_SET:
|
|
|
|
{
|
|
|
|
if (interval->count > sizeof(longlong)*8)
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_TOO_BIG_SET,field_name); /* purecov: inspected */
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1); /* purecov: inspected */
|
|
|
|
}
|
|
|
|
new_field->pack_length=(interval->count+7)/8;
|
|
|
|
if (new_field->pack_length > 4)
|
|
|
|
new_field->pack_length=8;
|
|
|
|
new_field->interval=interval;
|
|
|
|
new_field->length=0;
|
|
|
|
for (const char **pos=interval->type_names; *pos ; pos++)
|
2002-05-27 22:21:49 +02:00
|
|
|
{
|
|
|
|
new_field->length+=(uint) strip_sp((char*) *pos)+1;
|
|
|
|
}
|
2000-07-31 21:29:14 +02:00
|
|
|
new_field->length--;
|
|
|
|
set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1);
|
|
|
|
if (default_value)
|
|
|
|
{
|
2003-01-16 01:04:50 +01:00
|
|
|
char *not_used;
|
|
|
|
uint not_used2;
|
|
|
|
|
2000-07-31 21:29:14 +02:00
|
|
|
thd->cuted_fields=0;
|
|
|
|
String str,*res;
|
|
|
|
res=default_value->val_str(&str);
|
2003-01-16 01:04:50 +01:00
|
|
|
(void) find_set(interval, res->ptr(), res->length(), ¬_used,
|
|
|
|
¬_used2);
|
2000-07-31 21:29:14 +02:00
|
|
|
if (thd->cuted_fields)
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_INVALID_DEFAULT,field_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FIELD_TYPE_ENUM:
|
|
|
|
{
|
|
|
|
new_field->interval=interval;
|
|
|
|
new_field->pack_length=interval->count < 256 ? 1 : 2; // Should be safe
|
2002-05-27 22:21:49 +02:00
|
|
|
new_field->length=(uint) strip_sp((char*) interval->type_names[0]);
|
2000-07-31 21:29:14 +02:00
|
|
|
for (const char **pos=interval->type_names+1; *pos ; pos++)
|
|
|
|
{
|
2002-05-27 22:21:49 +02:00
|
|
|
uint length=(uint) strip_sp((char*) *pos);
|
2000-07-31 21:29:14 +02:00
|
|
|
set_if_bigger(new_field->length,length);
|
|
|
|
}
|
|
|
|
set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1);
|
|
|
|
if (default_value)
|
|
|
|
{
|
|
|
|
String str,*res;
|
|
|
|
res=default_value->val_str(&str);
|
|
|
|
if (!find_enum(interval,res->ptr(),res->length()))
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_INVALID_DEFAULT,field_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_field->length >= MAX_FIELD_WIDTH ||
|
|
|
|
(!new_field->length && !(new_field->flags & BLOB_FLAG) &&
|
2003-01-15 09:11:44 +01:00
|
|
|
type != FIELD_TYPE_STRING &&
|
|
|
|
type != FIELD_TYPE_VAR_STRING && type != FIELD_TYPE_GEOMETRY))
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_TOO_BIG_FIELDLENGTH,field_name,
|
2000-07-31 21:29:14 +02:00
|
|
|
MAX_FIELD_WIDTH-1); /* purecov: inspected */
|
|
|
|
DBUG_RETURN(1); /* purecov: inspected */
|
|
|
|
}
|
|
|
|
type_modifier&= AUTO_INCREMENT_FLAG;
|
|
|
|
if ((~allowed_type_modifier) & type_modifier)
|
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_WRONG_FIELD_SPEC,field_name);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(1);
|
|
|
|
}
|
|
|
|
if (!new_field->pack_length)
|
|
|
|
new_field->pack_length=calc_pack_length(new_field->sql_type ==
|
|
|
|
FIELD_TYPE_VAR_STRING ?
|
|
|
|
FIELD_TYPE_STRING :
|
|
|
|
new_field->sql_type,
|
|
|
|
new_field->length);
|
|
|
|
lex->create_list.push_back(new_field);
|
|
|
|
lex->last_field=new_field;
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Store position for column in ALTER TABLE .. ADD column */
|
|
|
|
|
|
|
|
void store_position_for_column(const char *name)
|
|
|
|
{
|
|
|
|
current_lex->last_field->after=my_const_cast(char*) (name);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2002-11-29 15:40:18 +01: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;
|
|
|
|
/* if ((l = ismbchar(name, name+MBMAXLEN))) { Wei He: I think it's wrong */
|
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;
|
|
|
|
Item **item_ptr;
|
|
|
|
DBUG_ENTER("add_to_list");
|
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
|
|
|
DBUG_RETURN(1);
|
|
|
|
item_ptr = (Item**) (order+1);
|
|
|
|
*item_ptr=item;
|
|
|
|
order->item= item_ptr;
|
|
|
|
order->asc = asc;
|
|
|
|
order->free_me=0;
|
|
|
|
order->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,
|
2002-10-30 12:18:52 +01:00
|
|
|
List<String> *use_index,
|
|
|
|
List<String> *ignore_index)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
register TABLE_LIST *ptr;
|
|
|
|
char *alias_str;
|
|
|
|
DBUG_ENTER("add_table_to_list");
|
|
|
|
|
|
|
|
if (!table)
|
|
|
|
DBUG_RETURN(0); // End of memory
|
|
|
|
alias_str= alias ? alias->str : table->table.str;
|
|
|
|
if (table->table.length > NAME_LEN ||
|
2002-11-21 21:25:53 +01:00
|
|
|
(table->table.length &&
|
|
|
|
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
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_WRONG_TABLE_NAME,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)
|
|
|
|
{
|
|
|
|
net_printf(thd,ER_DERIVED_MUST_HAVE_ALIAS);
|
|
|
|
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;
|
|
|
|
}
|
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-02-12 20:55:37 +01:00
|
|
|
ptr->derived= table->sel;
|
2000-07-31 21:29:14 +02:00
|
|
|
if (use_index)
|
2000-08-21 02:07:54 +02:00
|
|
|
ptr->use_index=(List<String> *) thd->memdup((gptr) use_index,
|
2000-07-31 21:29:14 +02:00
|
|
|
sizeof(*use_index));
|
|
|
|
if (ignore_index)
|
2000-08-21 02:07:54 +02:00
|
|
|
ptr->ignore_index=(List<String> *) thd->memdup((gptr) ignore_index,
|
|
|
|
sizeof(*ignore_index));
|
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 ;
|
2000-08-21 02:07:54 +02:00
|
|
|
tables=tables->next)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2002-09-20 13:05:18 +02:00
|
|
|
if (!strcmp(alias_str,tables->alias) && !strcmp(ptr->db, tables->db))
|
2000-08-21 02:07:54 +02:00
|
|
|
{
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,ER_NONUNIQ_TABLE,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
|
|
|
}
|
|
|
|
}
|
2002-12-05 18:38:42 +01:00
|
|
|
table_list.link_in_list((byte*) ptr, (byte**) &ptr->next);
|
2000-07-31 21:29:14 +02:00
|
|
|
DBUG_RETURN(ptr);
|
|
|
|
}
|
|
|
|
|
2001-08-02 05:29:50 +02:00
|
|
|
|
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));
|
|
|
|
|
2002-11-21 21:25:53 +01:00
|
|
|
for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first ;
|
2002-11-16 19:19:10 +01:00
|
|
|
tables ;
|
|
|
|
tables=tables->next)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
{
|
|
|
|
// This only happens if you have both a right and left join
|
|
|
|
b->on_expr=new Item_cond_and(b->on_expr,expr);
|
|
|
|
}
|
|
|
|
b->on_expr->top_level_item();
|
2000-09-25 23:33:25 +02:00
|
|
|
}
|
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
|
|
|
|
|
|
|
/*
|
|
|
|
Reload/resets privileges and the different caches
|
|
|
|
*/
|
|
|
|
|
2001-12-06 00:05:30 +01:00
|
|
|
bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
|
|
|
bool result=0;
|
2002-12-13 11:05:24 +01:00
|
|
|
bool error_already_sent=0;
|
2000-07-31 21:29:14 +02:00
|
|
|
select_errors=0; /* Write if more errors */
|
|
|
|
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)
|
2002-06-20 15:46:25 +02:00
|
|
|
reset_mqh(thd,(LEX_USER *) NULL,true);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
if (options & REFRESH_LOG)
|
|
|
|
{
|
2002-10-16 18:05:10 +02:00
|
|
|
mysql_log.new_file(1);
|
|
|
|
mysql_update_log.new_file(1);
|
|
|
|
mysql_bin_log.new_file(1);
|
2003-03-13 11:12:28 +01:00
|
|
|
#ifdef HAVE_REPLICATION
|
2003-03-11 10:49:06 +01:00
|
|
|
if (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)
|
|
|
|
mysql_bin_log.purge_logs_before_date(thd, purge_time);
|
|
|
|
}
|
2003-03-13 11:12:28 +01:00
|
|
|
#endif
|
2002-10-16 18:05:10 +02:00
|
|
|
mysql_slow_log.new_file(1);
|
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
|
2001-12-02 13:34:01 +01:00
|
|
|
options &= ~REFRESH_QUERY_CACHE; //don't flush all cache, just free memory
|
|
|
|
}
|
|
|
|
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*/
|
2000-07-31 21:29:14 +02:00
|
|
|
if (options & (REFRESH_TABLES | REFRESH_READ_LOCK))
|
|
|
|
{
|
2001-08-14 19:33:49 +02:00
|
|
|
if ((options & REFRESH_READ_LOCK) && thd)
|
2000-07-31 21:29:14 +02:00
|
|
|
{
|
2001-08-14 19:33:49 +02:00
|
|
|
if (lock_global_read_lock(thd))
|
|
|
|
return 1;
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
2000-08-21 23:18:32 +02:00
|
|
|
result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
if (options & REFRESH_HOSTS)
|
|
|
|
hostname_cache_refresh();
|
|
|
|
if (options & REFRESH_STATUS)
|
|
|
|
refresh_status();
|
|
|
|
if (options & REFRESH_THREADS)
|
|
|
|
flush_thread_cache();
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2000-07-31 21:29:14 +02:00
|
|
|
if (options & REFRESH_MASTER)
|
2002-01-20 03:16:52 +01:00
|
|
|
if (reset_master(thd))
|
|
|
|
result=1;
|
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
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2002-05-15 12:50:38 +02:00
|
|
|
if (options & REFRESH_SLAVE)
|
|
|
|
{
|
|
|
|
LOCK_ACTIVE_MI;
|
2002-08-08 02:12:02 +02:00
|
|
|
if (reset_slave(thd, active_mi))
|
2002-12-13 11:05:24 +01:00
|
|
|
{
|
2002-05-15 12:50:38 +02:00
|
|
|
result=1;
|
2002-12-13 11:05:24 +01:00
|
|
|
/*
|
|
|
|
reset_slave() sends error itself.
|
|
|
|
If it didn't, one would either change reset_slave()'s prototype, to
|
|
|
|
pass *errorcode and *errmsg to it when it's called or
|
|
|
|
change reset_slave to use my_error() to register the error.
|
|
|
|
*/
|
|
|
|
error_already_sent=1;
|
|
|
|
}
|
2002-05-15 12:50:38 +02:00
|
|
|
UNLOCK_ACTIVE_MI;
|
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2002-05-15 12:50:38 +02:00
|
|
|
if (options & REFRESH_USER_RESOURCES)
|
2002-06-20 15:46:25 +02:00
|
|
|
reset_mqh(thd,(LEX_USER *) NULL);
|
2002-12-13 11:05:24 +01:00
|
|
|
|
|
|
|
if (thd && !error_already_sent)
|
|
|
|
{
|
|
|
|
if (result)
|
2003-01-04 14:37:20 +01:00
|
|
|
send_error(thd,0);
|
2002-12-13 11:05:24 +01:00
|
|
|
else
|
2003-01-04 14:37:20 +01:00
|
|
|
send_ok(thd);
|
2002-12-13 11:05:24 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
2000-10-03 01:59:12 +02:00
|
|
|
void kill_one_thread(THD *thd, ulong id)
|
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))
|
|
|
|
{
|
|
|
|
tmp->awake(1 /*prepare to die*/);
|
|
|
|
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
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd,error,id);
|
2000-07-31 21:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear most status variables */
|
|
|
|
|
|
|
|
static void refresh_status(void)
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&THR_LOCK_keycache);
|
|
|
|
pthread_mutex_lock(&LOCK_status);
|
|
|
|
for (struct show_var_st *ptr=status_vars; ptr->name; ptr++)
|
|
|
|
{
|
|
|
|
if (ptr->type == SHOW_LONG)
|
|
|
|
*(ulong*) ptr->value=0;
|
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&LOCK_status);
|
|
|
|
pthread_mutex_unlock(&THR_LOCK_keycache);
|
|
|
|
}
|
2001-06-01 03:27:59 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* If pointer is not a null pointer, append filename to it */
|
|
|
|
|
2001-12-05 12:03:00 +01:00
|
|
|
static bool append_file_to_dir(THD *thd, char **filename_ptr, 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))
|
|
|
|
{
|
|
|
|
my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr);
|
|
|
|
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
|
|
|
|
|
|
|
/*
|
|
|
|
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;
|
2002-10-30 12:18:52 +01:00
|
|
|
if (thd->lex.current_select != &thd->lex.select_lex)
|
2002-07-24 18:55:08 +02:00
|
|
|
{
|
|
|
|
char command[80];
|
|
|
|
strmake(command, thd->lex.yylval->symbol.str,
|
|
|
|
min(thd->lex.yylval->symbol.length, sizeof(command)-1));
|
2002-10-02 12:33:08 +02:00
|
|
|
net_printf(thd, ER_CANT_USE_OPTION_HERE, command);
|
2002-07-24 18:55:08 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2002-11-07 22:45:19 +01:00
|
|
|
|
|
|
|
compare_func_creator comp_eq_creator(bool invert)
|
|
|
|
{
|
|
|
|
return invert?&Item_bool_func2::ne_creator:&Item_bool_func2::eq_creator;
|
|
|
|
}
|
|
|
|
|
|
|
|
compare_func_creator comp_ge_creator(bool invert)
|
|
|
|
{
|
|
|
|
return invert?&Item_bool_func2::lt_creator:&Item_bool_func2::ge_creator;
|
|
|
|
}
|
|
|
|
|
|
|
|
compare_func_creator comp_gt_creator(bool invert)
|
|
|
|
{
|
|
|
|
return invert?&Item_bool_func2::le_creator:&Item_bool_func2::gt_creator;
|
|
|
|
}
|
|
|
|
|
|
|
|
compare_func_creator comp_le_creator(bool invert)
|
|
|
|
{
|
|
|
|
return invert?&Item_bool_func2::gt_creator:&Item_bool_func2::le_creator;
|
|
|
|
}
|
|
|
|
|
|
|
|
compare_func_creator comp_lt_creator(bool invert)
|
|
|
|
{
|
|
|
|
return invert?&Item_bool_func2::ge_creator:&Item_bool_func2::lt_creator;
|
|
|
|
}
|
|
|
|
|
|
|
|
compare_func_creator comp_ne_creator(bool invert)
|
|
|
|
{
|
|
|
|
return invert?&Item_bool_func2::eq_creator:&Item_bool_func2::ne_creator;
|
|
|
|
}
|