sql_parse.cc 233 KB
Newer Older
1
/* Copyright (C) 2000-2003 MySQL AB
2

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3 4 5 6
   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.
7

bk@work.mysql.com's avatar
bk@work.mysql.com committed
8 9 10 11
   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.
12

bk@work.mysql.com's avatar
bk@work.mysql.com committed
13 14 15 16
   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 */

17
#define MYSQL_LEX 1
bk@work.mysql.com's avatar
bk@work.mysql.com committed
18
#include "mysql_priv.h"
19
#include "sql_repl.h"
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
20
#include "rpl_filter.h"
21
#include "repl_failsafe.h"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
22 23 24 25
#include <m_ctype.h>
#include <myisam.h>
#include <my_dir.h>

26
#include "sp_head.h"
27
#include "sp.h"
28
#include "sp_cache.h"
29
#include "events.h"
30
#include "event_data_objects.h"
31

32 33 34 35 36 37 38 39 40 41 42
#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?
*/
43 44 45
#define SSL_HANDSHAKE_SIZE      2
#define NORMAL_HANDSHAKE_SIZE   6
#define MIN_HANDSHAKE_SIZE      2
46
#else
47
#define MIN_HANDSHAKE_SIZE      6
48
#endif /* HAVE_OPENSSL */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
49

50 51 52 53 54 55
/* 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 || \
56
   (LP)->sql_command == SQLCOM_SHOW_CREATE_FUNC || \
57 58 59
   (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
   "FUNCTION" : "PROCEDURE")

60 61 62
#ifdef SOLARIS
extern "C" int gethostname(char *name, int namelen);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
63

64
static void time_out_user_resource_limits(THD *thd, USER_CONN *uc);
65
#ifndef NO_EMBEDDED_ACCESS_CHECKS
66
static int check_for_max_user_connections(THD *thd, USER_CONN *uc);
67
static void decrease_user_connections(USER_CONN *uc);
68
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
69
static bool check_multi_update_lock(THD *thd);
70
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
71

72
const char *any_db="*any*";	// Special symbol for check_access
bk@work.mysql.com's avatar
bk@work.mysql.com committed
73

andrey@example.com's avatar
andrey@example.com committed
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
const LEX_STRING command_name[]={
  C_STRING_WITH_LEN("Sleep"),
  C_STRING_WITH_LEN("Quit"),
  C_STRING_WITH_LEN("Init DB"),
  C_STRING_WITH_LEN("Query"),
  C_STRING_WITH_LEN("Field List"),
  C_STRING_WITH_LEN("Create DB"),
  C_STRING_WITH_LEN("Drop DB"),
  C_STRING_WITH_LEN("Refresh"),
  C_STRING_WITH_LEN("Shutdown"),
  C_STRING_WITH_LEN("Statistics"),
  C_STRING_WITH_LEN("Processlist"),
  C_STRING_WITH_LEN("Connect"),
  C_STRING_WITH_LEN("Kill"),
  C_STRING_WITH_LEN("Debug"),
  C_STRING_WITH_LEN("Ping"),
  C_STRING_WITH_LEN("Time"),
  C_STRING_WITH_LEN("Delayed insert"),
  C_STRING_WITH_LEN("Change user"),
  C_STRING_WITH_LEN("Binlog Dump"),
  C_STRING_WITH_LEN("Table Dump"),
  C_STRING_WITH_LEN("Connect Out"),
  C_STRING_WITH_LEN("Register Slave"),
  C_STRING_WITH_LEN("Prepare"),
  C_STRING_WITH_LEN("Execute"),
  C_STRING_WITH_LEN("Long Data"),
  C_STRING_WITH_LEN("Close stmt"),
  C_STRING_WITH_LEN("Reset stmt"),
  C_STRING_WITH_LEN("Set option"),
  C_STRING_WITH_LEN("Fetch"),
  C_STRING_WITH_LEN("Daemon"),
  C_STRING_WITH_LEN("Error")  // Last command number
bk@work.mysql.com's avatar
bk@work.mysql.com committed
106 107
};

108 109 110 111
const char *xa_state_names[]={
  "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED"
};

bk@work.mysql.com's avatar
bk@work.mysql.com committed
112 113 114
#ifdef __WIN__
static void  test_signal(int sig_ptr)
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
115
#if !defined( DBUG_OFF)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
116 117 118 119 120 121
  MessageBox(NULL,"Test signal","DBUG",MB_OK);
#endif
}
static void init_signals(void)
{
  int signals[7] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGBREAK,SIGABRT } ;
122
  for (int i=0 ; i < 7 ; i++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
123 124 125 126
    signal( signals[i], test_signal) ;
}
#endif

127 128 129 130 131
static void unlock_locked_tables(THD *thd)
{
  if (thd->locked_tables)
  {
    thd->lock=thd->locked_tables;
132
    thd->locked_tables=0;			// Will be automatically closed
133 134 135 136
    close_thread_tables(thd);			// Free tables
  }
}

137

138
bool end_active_trans(THD *thd)
139
{
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
140
  int error=0;
141
  DBUG_ENTER("end_active_trans");
142
  if (unlikely(thd->in_sub_stmt))
143 144 145 146
  {
    my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
    DBUG_RETURN(1);
  }
147 148 149 150 151 152
  if (thd->transaction.xid_state.xa_state != XA_NOTR)
  {
    my_error(ER_XAER_RMFAIL, MYF(0),
             xa_state_names[thd->transaction.xid_state.xa_state]);
    DBUG_RETURN(1);
  }
153
  if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN |
154
		      OPTION_TABLE_LOCK))
155
  {
156 157 158 159
    DBUG_PRINT("info",("options: 0x%lx", (ulong) thd->options));
    /* Safety if one did "drop table" on locked tables */
    if (!thd->locked_tables)
      thd->options&= ~OPTION_TABLE_LOCK;
160
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
161
    if (ha_commit(thd))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
162
      error=1;
163
  }
164 165
  thd->options&= ~(OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE |
                   OPTION_KEEP_LOG);
166
  DBUG_RETURN(error);
167 168
}

cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
169
bool begin_trans(THD *thd)
170 171
{
  int error=0;
172
  if (unlikely(thd->in_sub_stmt))
173 174 175 176
  {
    my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
    return 1;
  }
177 178 179 180 181 182 183 184 185 186
  if (thd->locked_tables)
  {
    thd->lock=thd->locked_tables;
    thd->locked_tables=0;			// Will be automatically closed
    close_thread_tables(thd);			// Free tables
  }
  if (end_active_trans(thd))
    error= -1;
  else
  {
187
    LEX *lex= thd->lex;
188
    thd->options|= OPTION_BEGIN;
189 190
    thd->server_status|= SERVER_STATUS_IN_TRANS;
    if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
serg@serg.mylan's avatar
serg@serg.mylan committed
191
      error= ha_start_consistent_snapshot(thd);
192 193 194
  }
  return error;
}
195

monty@mysql.com's avatar
monty@mysql.com committed
196
#ifdef HAVE_REPLICATION
197 198 199
/*
  Returns true if all tables should be ignored
*/
200 201
inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables)
{
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
202 203
  return rpl_filter->is_on() && tables && !thd->spcont &&
         !rpl_filter->tables_ok(thd->db, tables);
204
}
monty@mysql.com's avatar
monty@mysql.com committed
205
#endif
206 207


208 209 210 211 212 213 214 215 216 217 218 219
static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables)
{
  for (TABLE_LIST *table= tables; table; table= table->next_global)
  {
    DBUG_ASSERT(table->db && table->table_name);
    if (table->updating &&
        !find_temporary_table(thd, table->db, table->table_name))
      return 1;
  }
  return 0;
}

220
#ifndef NO_EMBEDDED_ACCESS_CHECKS
221 222
static HASH hash_user_connections;

223 224
static int get_or_create_user_conn(THD *thd, const char *user,
				   const char *host,
peter@mysql.com's avatar
peter@mysql.com committed
225
				   USER_RESOURCES *mqh)
226
{
227
  int return_val= 0;
228
  uint temp_len, user_len;
229
  char temp_user[USER_HOST_BUFF_SIZE];
230 231 232 233 234
  struct  user_conn *uc;

  DBUG_ASSERT(user != 0);
  DBUG_ASSERT(host != 0);

235
  user_len= strlen(user);
236
  temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1;
237
  (void) pthread_mutex_lock(&LOCK_user_conn);
238 239
  if (!(uc = (struct  user_conn *) hash_search(&hash_user_connections,
					       (byte*) temp_user, temp_len)))
240
  {
241 242 243
    /* First connection for user; Create a user connection object */
    if (!(uc= ((struct user_conn*)
	       my_malloc(sizeof(struct user_conn) + temp_len+1,
244 245
			 MYF(MY_WME)))))
    {
246
      net_send_error(thd, 0, NullS);		// Out of memory
247
      return_val= 1;
248
      goto end;
249
    }
250 251
    uc->user=(char*) (uc+1);
    memcpy(uc->user,temp_user,temp_len+1);
252
    uc->host= uc->user + user_len +  1;
253
    uc->len= temp_len;
254
    uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0;
255 256
    uc->user_resources= *mqh;
    uc->intime= thd->thr_create_time;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
257
    if (my_hash_insert(&hash_user_connections, (byte*) uc))
258 259
    {
      my_free((char*) uc,0);
260
      net_send_error(thd, 0, NullS);		// Out of memory
261
      return_val= 1;
262 263 264 265
      goto end;
    }
  }
  thd->user_connect=uc;
266
  uc->connections++;
267 268 269
end:
  (void) pthread_mutex_unlock(&LOCK_user_conn);
  return return_val;
peter@mysql.com's avatar
peter@mysql.com committed
270

271
}
272
#endif /* !NO_EMBEDDED_ACCESS_CHECKS */
273 274 275


/*
276 277
  Check if user exist and password supplied is correct. 

278 279
  SYNOPSIS
    check_user()
280
    thd          thread handle, thd->security_ctx->{host,user,ip} are used
281 282 283
    command      originator of the check: now check_user is called
                 during connect and change user procedures; used for 
                 logging.
284
    passwd       scrambled password received from client
285 286 287 288
    passwd_len   length of scrambled password
    db           database name to connect to, may be NULL
    check_count  dont know exactly

289
    Note, that host, user and passwd may point to communication buffer.
290
    Current implementation does not depend on that, but future changes
291 292 293
    should be done with this in mind; 'thd' is INOUT, all other params
    are 'IN'.

294
  RETURN VALUE
295 296
    0  OK; thd->security_ctx->user/master_access/priv_user/db_access and
       thd->db are updated; OK is sent to client;
297 298
   -1  access denied or handshake error; error is sent to client;
   >0  error, not sent to client
bk@work.mysql.com's avatar
bk@work.mysql.com committed
299 300
*/

hf@deer.(none)'s avatar
hf@deer.(none) committed
301 302 303
int check_user(THD *thd, enum enum_server_command command, 
	       const char *passwd, uint passwd_len, const char *db,
	       bool check_count)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
304
{
305
  DBUG_ENTER("check_user");
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
306
  
monty@mysql.com's avatar
monty@mysql.com committed
307
#ifdef NO_EMBEDDED_ACCESS_CHECKS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
308
  thd->main_security_ctx.master_access= GLOBAL_ACLS;       // Full rights
309
  /* Change database if necessary */
310 311
  if (db && db[0])
  {
312 313 314 315
    /*
      thd->db is saved in caller and needs to be freed by caller if this
      function returns 0
    */
316
    thd->reset_db(NULL, 0);
317
    if (mysql_change_db(thd, db, FALSE))
318
    {
319 320
      /* Send the error to the client */
      net_send_error(thd);
321 322 323
      DBUG_RETURN(-1);
    }
  }
324
  send_ok(thd);
325
  DBUG_RETURN(0);
monty@mysql.com's avatar
monty@mysql.com committed
326 327
#else

328 329 330 331 332
  my_bool opt_secure_auth_local;
  pthread_mutex_lock(&LOCK_global_system_variables);
  opt_secure_auth_local= opt_secure_auth;
  pthread_mutex_unlock(&LOCK_global_system_variables);
  
333
  /*
334 335
    If the server is running in secure auth mode, short scrambles are 
    forbidden.
336
  */
337
  if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
338
  {
339
    net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE);
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
340
    general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
341
    DBUG_RETURN(-1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
342
  }
343 344 345 346
  if (passwd_len != 0 &&
      passwd_len != SCRAMBLE_LENGTH &&
      passwd_len != SCRAMBLE_LENGTH_323)
    DBUG_RETURN(ER_HANDSHAKE_ERROR);
peter@mysql.com's avatar
peter@mysql.com committed
347

348
  /*
349
    Clear thd->db as it points to something, that will be freed when 
350
    connection is closed. We don't want to accidentally free a wrong pointer
351 352
    if connect failed. Also in case of 'CHANGE USER' failure, current
    database will be switched to 'no database selected'.
353
  */
354 355
  thd->reset_db(NULL, 0);

356
  USER_RESOURCES ur;
357
  int res= acl_getroot(thd, &ur, passwd, passwd_len);
hf@deer.(none)'s avatar
hf@deer.(none) committed
358
#ifndef EMBEDDED_LIBRARY
359
  if (res == -1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
360
  {
361 362 363 364 365 366
    /*
      This happens when client (new) sends password scrambled with
      scramble(), but database holds old value (scrambled with
      scramble_323()). Here we please client to send scrambled_password
      in old format.
    */
367
    NET *net= &thd->net;
368
    if (opt_secure_auth_local)
369
    {
370
      net_printf_error(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE,
371 372
                       thd->main_security_ctx.user,
                       thd->main_security_ctx.host_or_ip);
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
373 374 375
      general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
                        thd->main_security_ctx.user,
                        thd->main_security_ctx.host_or_ip);
376 377
      DBUG_RETURN(-1);
    }
monty@mysql.com's avatar
monty@mysql.com committed
378
    /* We have to read very specific packet size */
379
    if (send_old_password_request(thd) ||
monty@mysql.com's avatar
monty@mysql.com committed
380
        my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
381
    {
382 383 384 385 386
      inc_host_errors(&thd->remote.sin_addr);
      DBUG_RETURN(ER_HANDSHAKE_ERROR);
    }
    /* Final attempt to check the user based on reply */
    /* So as passwd is short, errcode is always >= 0 */
387
    res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323);
388
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
389
#endif /*EMBEDDED_LIBRARY*/
390 391
  /* here res is always >= 0 */
  if (res == 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
392
  {
393 394
    if (!(thd->main_security_ctx.master_access &
          NO_ACCESS)) // authentication is OK
395
    {
396
      DBUG_PRINT("info",
397
                 ("Capabilities: %lu  packet_length: %ld  Host: '%s'  "
398
                  "Login user: '%s' Priv_user: '%s'  Using password: %s "
399
                  "Access: %lu  db: '%s'",
400 401 402 403 404
                  thd->client_capabilities,
                  thd->max_client_packet_length,
                  thd->main_security_ctx.host_or_ip,
                  thd->main_security_ctx.user,
                  thd->main_security_ctx.priv_user,
405
                  passwd_len ? "yes": "no",
406 407
                  thd->main_security_ctx.master_access,
                  (thd->db ? thd->db : "*none*")));
408 409

      if (check_count)
410
      {
411
        VOID(pthread_mutex_lock(&LOCK_thread_count));
serg@serg.mylan's avatar
serg@serg.mylan committed
412
        bool count_ok= thread_count <= max_connections + delayed_insert_threads
413
                       || (thd->main_security_ctx.master_access & SUPER_ACL);
414 415
        VOID(pthread_mutex_unlock(&LOCK_thread_count));
        if (!count_ok)
serg@serg.mylan's avatar
serg@serg.mylan committed
416
        {                                         // too many connections
417
          net_send_error(thd, ER_CON_COUNT_ERROR);
418 419
          DBUG_RETURN(-1);
        }
420
      }
peter@mysql.com's avatar
peter@mysql.com committed
421

422
      /* Why logging is performed before all checks've passed? */
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
423 424 425 426 427 428 429 430
      general_log_print(thd, command,
                        (thd->main_security_ctx.priv_user ==
                         thd->main_security_ctx.user ?
                         (char*) "%s@%s on %s" :
                         (char*) "%s@%s as anonymous on %s"),
                        thd->main_security_ctx.user,
                        thd->main_security_ctx.host_or_ip,
                        db ? db : (char*) "");
431

432
      /*
433 434 435
        This is the default access rights for the current database.  It's
        set to 0 here because we don't have an active database yet (and we
        may not have an active database to set.
436
      */
437
      thd->main_security_ctx.db_access=0;
438 439

      /* Don't allow user to connect if he has done too many queries */
440
      if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn ||
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
441
	   max_user_connections) &&
442
	  get_or_create_user_conn(thd,
443 444 445 446
            (opt_old_style_user_limits ? thd->main_security_ctx.user :
             thd->main_security_ctx.priv_user),
            (opt_old_style_user_limits ? thd->main_security_ctx.host_or_ip :
             thd->main_security_ctx.priv_host),
447
            &ur))
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
448 449
	DBUG_RETURN(-1);
      if (thd->user_connect &&
450 451
	  (thd->user_connect->user_resources.conn_per_hour ||
	   thd->user_connect->user_resources.user_conn ||
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
452 453 454
	   max_user_connections) &&
	  check_for_max_user_connections(thd, thd->user_connect))
	DBUG_RETURN(-1);
455

456
      /* Change database if necessary */
457
      if (db && db[0])
458
      {
459
        if (mysql_change_db(thd, db, FALSE))
460
        {
461 462
          /* Send error to the client */
          net_send_error(thd);
463 464 465 466
          if (thd->user_connect)
            decrease_user_connections(thd->user_connect);
          DBUG_RETURN(-1);
        }
467
      }
468
      send_ok(thd);
469 470 471
      thd->password= test(passwd_len);          // remember for error messages 
      /* Ready to handle queries */
      DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
472 473
    }
  }
474
  else if (res == 2) // client gave short hash, server has long hash
475
  {
476
    net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE);
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
477
    general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
478
    DBUG_RETURN(-1);
479
  }
480
  net_printf_error(thd, ER_ACCESS_DENIED_ERROR,
481 482
                   thd->main_security_ctx.user,
                   thd->main_security_ctx.host_or_ip,
483
                   passwd_len ? ER(ER_YES) : ER(ER_NO));
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
484 485 486 487
  general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
                    thd->main_security_ctx.user,
                    thd->main_security_ctx.host_or_ip,
                    passwd_len ? ER(ER_YES) : ER(ER_NO));
488
  DBUG_RETURN(-1);
monty@mysql.com's avatar
monty@mysql.com committed
489
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
490 491
}

492
/*
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
493 494
  Check for maximum allowable user connections, if the mysqld server is
  started with corresponding variable that is greater then 0.
495 496
*/

497 498
extern "C" byte *get_key_conn(user_conn *buff, uint *length,
			      my_bool not_used __attribute__((unused)))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
499 500 501 502 503
{
  *length=buff->len;
  return (byte*) buff->user;
}

504
extern "C" void free_user(struct user_conn *uc)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
505 506 507 508
{
  my_free((char*) uc,MYF(0));
}

peter@mysql.com's avatar
peter@mysql.com committed
509
void init_max_user_conn(void)
510
{
511
#ifndef NO_EMBEDDED_ACCESS_CHECKS
512 513
  (void) hash_init(&hash_user_connections,system_charset_info,max_connections,
		   0,0,
514
		   (hash_get_key) get_key_conn, (hash_free_key) free_user,
515
		   0);
516
#endif
517 518 519
}


monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
/*
  check if user has already too many connections
  
  SYNOPSIS
  check_for_max_user_connections()
  thd			Thread handle
  uc			User connect object

  NOTES
    If check fails, we decrease user connection count, which means one
    shouldn't call decrease_user_connections() after this function.

  RETURN
    0	ok
    1	error
*/

537 538
#ifndef NO_EMBEDDED_ACCESS_CHECKS

539
static int check_for_max_user_connections(THD *thd, USER_CONN *uc)
540
{
541
  int error=0;
542
  DBUG_ENTER("check_for_max_user_connections");
peter@mysql.com's avatar
peter@mysql.com committed
543

544
  (void) pthread_mutex_lock(&LOCK_user_conn);
545
  if (max_user_connections && !uc->user_resources.user_conn &&
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
546
      max_user_connections < (uint) uc->connections)
547
  {
548
    net_printf_error(thd, ER_TOO_MANY_USER_CONNECTIONS, uc->user);
549 550
    error=1;
    goto end;
551
  }
552
  time_out_user_resource_limits(thd, uc);
553 554 555 556 557 558 559 560 561 562 563
  if (uc->user_resources.user_conn &&
      uc->user_resources.user_conn < uc->connections)
  {
    net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user,
                     "max_user_connections",
                     (long) uc->user_resources.user_conn);
    error= 1;
    goto end;
  }
  if (uc->user_resources.conn_per_hour &&
      uc->user_resources.conn_per_hour <= uc->conn_per_hour)
564
  {
565
    net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user,
jimw@mysql.com's avatar
jimw@mysql.com committed
566
                     "max_connections_per_hour",
567
                     (long) uc->user_resources.conn_per_hour);
568 569 570
    error=1;
    goto end;
  }
571
  uc->conn_per_hour++;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
572 573

  end:
574 575
  if (error)
    uc->connections--; // no need for decrease_user_connections() here
576
  (void) pthread_mutex_unlock(&LOCK_user_conn);
577
  DBUG_RETURN(error);
578 579
}

monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
/*
  Decrease user connection count

  SYNOPSIS
    decrease_user_connections()
    uc			User connection object

  NOTES
    If there is a n user connection object for a connection
    (which only happens if 'max_user_connections' is defined or
    if someone has created a resource grant for a user), then
    the connection count is always incremented on connect.

    The user connect object is not freed if some users has
    'max connections per hour' defined as we need to be able to hold
    count over the lifetime of the connection.
*/

598
static void decrease_user_connections(USER_CONN *uc)
599
{
600
  DBUG_ENTER("decrease_user_connections");
601 602 603
  (void) pthread_mutex_lock(&LOCK_user_conn);
  DBUG_ASSERT(uc->connections);
  if (!--uc->connections && !mqh_used)
604 605
  {
    /* Last connection for user; Delete it */
606
    (void) hash_delete(&hash_user_connections,(byte*) uc);
607
  }
608
  (void) pthread_mutex_unlock(&LOCK_user_conn);
609
  DBUG_VOID_RETURN;
610 611
}

612 613
#endif /* NO_EMBEDDED_ACCESS_CHECKS */

614

615 616
void free_max_user_conn(void)
{
617
#ifndef NO_EMBEDDED_ACCESS_CHECKS
618
  hash_free(&hash_user_connections);
619
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
620 621
}

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
622

623

624 625 626
/*
  Mark all commands that somehow changes a table
  This is used to check number of updates / hour
kent@mysql.com's avatar
kent@mysql.com committed
627 628 629

  sql_command is actually set to SQLCOM_END sometimes
  so we need the +1 to include it in the array.
630

631
  See COMMAND_FLAG_xxx for different type of commands
632 633
     2  - query that returns meaningful ROW_COUNT() -
          a number of modified rows
634 635
*/

636
uint sql_command_flags[SQLCOM_END+1];
637 638 639

void init_update_queries(void)
{
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
  bzero((gptr) &sql_command_flags, sizeof(sql_command_flags));

  sql_command_flags[SQLCOM_CREATE_TABLE]=   CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_CREATE_INDEX]=   CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_ALTER_TABLE]=    CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_TRUNCATE]=       CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_DROP_TABLE]=     CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_LOAD]=           CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_CREATE_DB]=      CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_DROP_DB]=        CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_RENAME_TABLE]=   CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_BACKUP_TABLE]=   CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_RESTORE_TABLE]=  CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_DROP_INDEX]=     CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_CREATE_VIEW]=    CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_DROP_VIEW]=      CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_CREATE_EVENT]=   CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_ALTER_EVENT]=    CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_DROP_EVENT]=     CF_CHANGES_DATA;  

  sql_command_flags[SQLCOM_UPDATE]=	    CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
  sql_command_flags[SQLCOM_UPDATE_MULTI]=   CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
  sql_command_flags[SQLCOM_INSERT]=	    CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
  sql_command_flags[SQLCOM_INSERT_SELECT]=  CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
  sql_command_flags[SQLCOM_DELETE]=         CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
  sql_command_flags[SQLCOM_DELETE_MULTI]=   CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
  sql_command_flags[SQLCOM_REPLACE]=        CF_CHANGES_DATA | CF_HAS_ROW_COUNT;
  sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT;

  sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_STATUS_FUNC]= CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_STATUS]=      CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_DATABASES]=   CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_TRIGGERS]=    CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_EVENTS]=      CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_OPEN_TABLES]= CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_PLUGINS]=     CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_FIELDS]=      CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_KEYS]=        CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_VARIABLES]=   CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_CHARSETS]=    CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_COLLATIONS]=  CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND;

  sql_command_flags[SQLCOM_SHOW_TABLES]=       (CF_STATUS_COMMAND |
                                                CF_SHOW_TABLE_COMMAND);
  sql_command_flags[SQLCOM_SHOW_TABLE_STATUS]= (CF_STATUS_COMMAND |
                                                CF_SHOW_TABLE_COMMAND);

  /*
    The following is used to preserver CF_ROW_COUNT during the
    a CALL or EXECUTE statement, so the value generated by the
    last called (or executed) statement is preserved.
    See mysql_execute_command() for how CF_ROW_COUNT is used.
  */
  sql_command_flags[SQLCOM_CALL]= 		CF_HAS_ROW_COUNT;
  sql_command_flags[SQLCOM_EXECUTE]= 		CF_HAS_ROW_COUNT;
697 698
}

699

700 701
bool is_update_query(enum enum_sql_command command)
{
kent@mysql.com's avatar
kent@mysql.com committed
702
  DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
703
  return (sql_command_flags[command] & CF_CHANGES_DATA) != 0;
704
}
705

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
706
/*
707 708
  Reset per-hour user resource limits when it has been more than
  an hour since they were last checked
709

710 711 712 713
  SYNOPSIS:
    time_out_user_resource_limits()
    thd			Thread handler
    uc			User connection details
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
714

715 716 717 718
  NOTE:
    This assumes that the LOCK_user_conn mutex has been acquired, so it is
    safe to test and modify members of the USER_CONN structure.
*/
719

720
static void time_out_user_resource_limits(THD *thd, USER_CONN *uc)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
721
{
722
  time_t check_time = thd->start_time ?  thd->start_time : time(NULL);
723
  DBUG_ENTER("time_out_user_resource_limits");
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
724

725
  /* If more than a hour since last check, reset resource checking */
726 727 728 729 730 731 732
  if (check_time  - uc->intime >= 3600)
  {
    uc->questions=1;
    uc->updates=0;
    uc->conn_per_hour=0;
    uc->intime=check_time;
  }
733 734 735 736 737 738 739 740 741 742 743 744

  DBUG_VOID_RETURN;
}


/*
  Check if maximum queries per hour limit has been reached
  returns 0 if OK.
*/

static bool check_mqh(THD *thd, uint check_command)
{
745 746
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  bool error= 0;
747 748 749 750 751 752 753 754
  USER_CONN *uc=thd->user_connect;
  DBUG_ENTER("check_mqh");
  DBUG_ASSERT(uc != 0);

  (void) pthread_mutex_lock(&LOCK_user_conn);

  time_out_user_resource_limits(thd, uc);

755
  /* Check that we have not done too many questions / hour */
756 757 758
  if (uc->user_resources.questions &&
      uc->questions++ >= uc->user_resources.questions)
  {
759 760
    net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions",
                     (long) uc->user_resources.questions);
761 762 763
    error=1;
    goto end;
  }
764
  if (check_command < (uint) SQLCOM_END)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
765
  {
766
    /* Check that we have not done too many updates / hour */
767 768
    if (uc->user_resources.updates &&
        (sql_command_flags[check_command] & CF_CHANGES_DATA) &&
769 770
	uc->updates++ >= uc->user_resources.updates)
    {
771 772
      net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_updates",
                       (long) uc->user_resources.updates);
773 774 775
      error=1;
      goto end;
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
776 777
  }
end:
778
  (void) pthread_mutex_unlock(&LOCK_user_conn);
779
  DBUG_RETURN(error);
780 781
#else
  return (0);
monty@mysql.com's avatar
monty@mysql.com committed
782
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
783 784
}

785

monty@mysql.com's avatar
monty@mysql.com committed
786
static void reset_mqh(LEX_USER *lu, bool get_them= 0)
787
{
monty@mysql.com's avatar
monty@mysql.com committed
788
#ifndef NO_EMBEDDED_ACCESS_CHECKS
789
  (void) pthread_mutex_lock(&LOCK_user_conn);
peter@mysql.com's avatar
peter@mysql.com committed
790
  if (lu)  // for GRANT
791
  {
792
    USER_CONN *uc;
793
    uint temp_len=lu->user.length+lu->host.length+2;
794
    char temp_user[USER_HOST_BUFF_SIZE];
795

796 797
    memcpy(temp_user,lu->user.str,lu->user.length);
    memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length);
798
    temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0;
799
    if ((uc = (struct  user_conn *) hash_search(&hash_user_connections,
800
						(byte*) temp_user, temp_len)))
801 802
    {
      uc->questions=0;
803
      get_mqh(temp_user,&temp_user[lu->user.length+1],uc);
804 805
      uc->updates=0;
      uc->conn_per_hour=0;
806 807
    }
  }
monty@mysql.com's avatar
monty@mysql.com committed
808
  else
809
  {
monty@mysql.com's avatar
monty@mysql.com committed
810
    /* for FLUSH PRIVILEGES and FLUSH USER_RESOURCES */
811
    for (uint idx=0;idx < hash_user_connections.records; idx++)
812
    {
monty@mysql.com's avatar
monty@mysql.com committed
813 814
      USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections,
						      idx);
815 816 817 818 819
      if (get_them)
	get_mqh(uc->user,uc->host,uc);
      uc->questions=0;
      uc->updates=0;
      uc->conn_per_hour=0;
820 821
    }
  }
822
  (void) pthread_mutex_unlock(&LOCK_user_conn);
monty@mysql.com's avatar
monty@mysql.com committed
823
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
824
}
825

826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
void thd_init_client_charset(THD *thd, uint cs_number)
{
  /*
   Use server character set and collation if
   - opt_character_set_client_handshake is not set
   - client has not specified a character set
   - client character set is the same as the servers
   - client character set doesn't exists in server
  */
  if (!opt_character_set_client_handshake ||
      !(thd->variables.character_set_client= get_charset(cs_number, MYF(0))) ||
      !my_strcasecmp(&my_charset_latin1,
                     global_system_variables.character_set_client->name,
                     thd->variables.character_set_client->name))
  {
    thd->variables.character_set_client=
      global_system_variables.character_set_client;
    thd->variables.collation_connection=
      global_system_variables.collation_connection;
    thd->variables.character_set_results=
      global_system_variables.character_set_results;
  }
  else
  {
    thd->variables.character_set_results=
      thd->variables.collation_connection= 
      thd->variables.character_set_client;
  }
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
857
/*
858
    Perform handshake, authorize client and update thd ACL variables.
859
  SYNOPSIS
860
    check_connection()
861
    thd  thread handle
862 863

  RETURN
864
     0  success, OK is sent to user, thd is updated.
865 866
    -1  error, which is sent to user
   > 0  error code (not sent to user)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
867 868
*/

hf@deer.(none)'s avatar
hf@deer.(none) committed
869 870
#ifndef EMBEDDED_LIBRARY
static int check_connection(THD *thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
871
{
872
  uint connect_errors= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
873
  NET *net= &thd->net;
monty@mysql.com's avatar
monty@mysql.com committed
874 875
  ulong pkt_len= 0;
  char *end;
876

877 878
  DBUG_PRINT("info",
             ("New connection received on %s", vio_description(net->vio)));
879
#ifdef SIGNAL_WITH_VIO_CLOSE
880
  thd->set_active_vio(net->vio);
881
#endif
882

883
  if (!thd->main_security_ctx.host)         // If TCP/IP connection
bk@work.mysql.com's avatar
bk@work.mysql.com committed
884
  {
885
    char ip[30];
886

887
    if (vio_peer_addr(net->vio, ip, &thd->peer_port))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
888
      return (ER_BAD_HOST_ERROR);
889
    if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(0))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
890
      return (ER_OUT_OF_RESOURCES);
891
    thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
monty@mysql.com's avatar
monty@mysql.com committed
892
    vio_in_addr(net->vio,&thd->remote.sin_addr);
893
    if (!(specialflag & SPECIAL_NO_RESOLVE))
894
    {
895
      vio_in_addr(net->vio,&thd->remote.sin_addr);
896 897
      thd->main_security_ctx.host=
        ip_to_hostname(&thd->remote.sin_addr, &connect_errors);
898
      /* Cut very long hostnames to avoid possible overflows */
899
      if (thd->main_security_ctx.host)
900
      {
901 902 903 904
        if (thd->main_security_ctx.host != my_localhost)
          thd->main_security_ctx.host[min(strlen(thd->main_security_ctx.host),
                                          HOSTNAME_LENGTH)]= 0;
        thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
905
      }
906 907
      if (connect_errors > max_connect_errors)
        return(ER_HOST_IS_BLOCKED);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
908
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
909
    DBUG_PRINT("info",("Host: %s  ip: %s",
910 911 912 913 914
		       (thd->main_security_ctx.host ?
                        thd->main_security_ctx.host : "unknown host"),
		       (thd->main_security_ctx.ip ?
                        thd->main_security_ctx.ip : "unknown ip")));
    if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
915 916
      return(ER_HOST_NOT_PRIVILEGED);
  }
917
  else /* Hostname given means that the connection was on a socket */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
918
  {
919 920 921
    DBUG_PRINT("info",("Host: %s", thd->main_security_ctx.host));
    thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
    thd->main_security_ctx.ip= 0;
922 923
    /* Reset sin_addr */
    bzero((char*) &thd->remote, sizeof(thd->remote));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
924 925 926
  }
  vio_keepalive(net->vio, TRUE);
  {
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
927
    /* buff[] needs to big enough to hold the server_version variable */
928
    char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
929 930
    ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB |
			  CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION);
931

932 933 934 935 936
    if (opt_using_transactions)
      client_flags|=CLIENT_TRANSACTIONS;
#ifdef HAVE_COMPRESS
    client_flags |= CLIENT_COMPRESS;
#endif /* HAVE_COMPRESS */
937 938
#ifdef HAVE_OPENSSL
    if (ssl_acceptor_fd)
939
      client_flags |= CLIENT_SSL;       /* Wow, SSL is available! */
940
#endif /* HAVE_OPENSSL */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
941

942 943 944 945 946 947 948 949 950 951 952
    end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1;
    int4store((uchar*) end, thd->thread_id);
    end+= 4;
    /*
      So as check_connection is the only entry point to authorization
      procedure, scramble is set here. This gives us new scramble for
      each handshake.
    */
    create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
    /*
      Old clients does not understand long scrambles, but can ignore packet
953
      tail: that's why first part of the scramble is placed here, and second
954 955
      part at the end of packet.
    */
956
    end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1;
957 958 959
   
    int2store(end, client_flags);
    /* write server characteristics: up to 16 bytes allowed */
960
    end[2]=(char) default_charset_info->number;
961 962 963 964 965 966 967 968 969
    int2store(end+3, thd->server_status);
    bzero(end+5, 13);
    end+= 18;
    /* write scramble tail */
    end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323, 
                 SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1;

    /* At this point we write connection message and read reply */
    if (net_write_command(net, (uchar) protocol_version, "", 0, buff,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
970
			  (uint) (end-buff)) ||
971
	(pkt_len= my_net_read(net)) == packet_error ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
972 973 974 975 976 977 978 979 980 981 982
	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);
983
  if (thd->packet.alloc(thd->variables.net_buffer_length))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
984 985 986
    return(ER_OUT_OF_RESOURCES);

  thd->client_capabilities=uint2korr(net->read_pos);
987 988 989 990
  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);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
991
    DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
992
    thd_init_client_charset(thd, (uint) net->read_pos[8]);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
993
    thd->update_charset();
994
    end= (char*) net->read_pos+32;
995 996 997 998 999 1000 1001
  }
  else
  {
    thd->max_client_packet_length= uint3korr(net->read_pos+2);
    end= (char*) net->read_pos+5;
  }

1002
  if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
1003
    thd->variables.sql_mode|= MODE_IGNORE_SPACE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1004
#ifdef HAVE_OPENSSL
1005
  DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1006 1007 1008
  if (thd->client_capabilities & CLIENT_SSL)
  {
    /* Do the SSL layering. */
1009 1010 1011 1012 1013
    if (!ssl_acceptor_fd)
    {
      inc_host_errors(&thd->remote.sin_addr);
      return(ER_HANDSHAKE_ERROR);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1014
    DBUG_PRINT("info", ("IO layer change in progress..."));
1015 1016
    if (sslaccept(ssl_acceptor_fd, net->vio, thd->variables.net_wait_timeout))
    {
1017
      DBUG_PRINT("error", ("Failed to accept new SSL connection"));
1018
      inc_host_errors(&thd->remote.sin_addr);
peter@mysql.com's avatar
peter@mysql.com committed
1019
      return(ER_HANDSHAKE_ERROR);
1020
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1021
    DBUG_PRINT("info", ("Reading user information over SSL layer"));
1022
    if ((pkt_len= my_net_read(net)) == packet_error ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1023 1024
	pkt_len < NORMAL_HANDSHAKE_SIZE)
    {
1025 1026
      DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
			   pkt_len));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1027 1028 1029 1030
      inc_host_errors(&thd->remote.sin_addr);
      return(ER_HANDSHAKE_ERROR);
    }
  }
1031 1032 1033
#endif

  if (end >= (char*) net->read_pos+ pkt_len +2)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1034
  {
1035 1036
    inc_host_errors(&thd->remote.sin_addr);
    return(ER_HANDSHAKE_ERROR);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1037 1038 1039
  }

  if (thd->client_capabilities & CLIENT_INTERACTIVE)
1040
    thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
1041
  if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
1042 1043
      opt_using_transactions)
    net->return_status= &thd->server_status;
1044
  net->read_timeout=(uint) thd->variables.net_read_timeout;
peter@mysql.com's avatar
peter@mysql.com committed
1045

1046 1047
  char *user= end;
  char *passwd= strend(user)+1;
1048
  uint user_len= passwd - user - 1;
1049
  char *db= passwd;
1050 1051
  char db_buff[NAME_LEN + 1];           // buffer to store db in utf8
  char user_buff[USERNAME_LENGTH + 1];	// buffer to store user in utf8
1052 1053 1054
  uint dummy_errors;

  /*
1055 1056 1057
    Old clients send null-terminated string as password; new clients send
    the size (1 byte) + string (not null-terminated). Hence in case of empty
    password both send '\0'.
1058 1059

    This strlen() can't be easily deleted without changing protocol.
1060
  */
1061
  uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
1062 1063 1064
    *passwd++ : strlen(passwd);
  db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
    db + passwd_len + 1 : 0;
1065
  /* strlen() can't be easily deleted without changing protocol */
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1066 1067
  uint db_len= db ? strlen(db) : 0;

serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1068
  if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len)
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1069 1070 1071 1072
  {
    inc_host_errors(&thd->remote.sin_addr);
    return ER_HANDSHAKE_ERROR;
  }
peter@mysql.com's avatar
peter@mysql.com committed
1073

kostja@oak.local's avatar
kostja@oak.local committed
1074 1075
  /* Since 4.1 all database names are stored in utf8 */
  if (db)
peter@mysql.com's avatar
peter@mysql.com committed
1076
  {
1077 1078
    db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
                             system_charset_info,
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1079
                             db, db_len,
1080
                             thd->charset(), &dummy_errors)]= 0;
1081
    db= db_buff;
kostja@oak.local's avatar
kostja@oak.local committed
1082
  }
peter@mysql.com's avatar
peter@mysql.com committed
1083

1084 1085 1086
  user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1,
                                       system_charset_info, user, user_len,
                                       thd->charset(), &dummy_errors)]= '\0';
1087
  user= user_buff;
1088

1089 1090 1091 1092 1093 1094 1095 1096
  /* If username starts and ends in "'", chop them off */
  if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
  {
    user[user_len-1]= 0;
    user++;
    user_len-= 2;
  }

1097 1098 1099
  if (thd->main_security_ctx.user)
    x_free(thd->main_security_ctx.user);
  if (!(thd->main_security_ctx.user= my_strdup(user, MYF(0))))
1100
    return (ER_OUT_OF_RESOURCES);
1101
  return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1102 1103
}

1104

1105 1106
void execute_init_command(THD *thd, sys_var_str *init_command_var,
			  rw_lock_t *var_mutex)
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1107 1108 1109 1110
{
  Vio* save_vio;
  ulong save_client_capabilities;

1111 1112 1113 1114 1115 1116 1117 1118 1119
  thd->proc_info= "Execution of init_command";
  /*
    We need to lock init_command_var because
    during execution of init_command_var query
    values of init_command_var can't be changed
  */
  rw_rdlock(var_mutex);
  thd->query= init_command_var->value;
  thd->query_length= init_command_var->value_length;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1120 1121
  save_client_capabilities= thd->client_capabilities;
  thd->client_capabilities|= CLIENT_MULTI_QUERIES;
1122 1123 1124 1125
  /*
    We don't need return result of execution to client side.
    To forbid this we should set thd->net.vio to 0.
  */
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1126 1127
  save_vio= thd->net.vio;
  thd->net.vio= 0;
1128
  thd->net.no_send_error= 0;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1129
  dispatch_command(COM_QUERY, thd, thd->query, thd->query_length+1);
1130
  rw_unlock(var_mutex);
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1131 1132 1133 1134 1135
  thd->client_capabilities= save_client_capabilities;
  thd->net.vio= save_vio;
}


1136
pthread_handler_t handle_one_connection(void *arg)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1137 1138 1139
{
  THD *thd=(THD*) arg;
  uint launch_time  =
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1140
    (uint) ((thd->thr_create_time = time(NULL)) - thd->connect_time);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1141 1142 1143 1144 1145
  if (launch_time >= slow_launch_time)
    statistic_increment(slow_launch_threads,&LOCK_status );

  pthread_detach_this_thread();

cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
1146
#if !defined( __WIN__) // Win32 calls this in pthread_create
monty@mysql.com's avatar
monty@mysql.com committed
1147
  /* The following calls needs to be done before we call DBUG_ macros */
1148
  if (!(test_flags & TEST_NO_THREADS) & my_thread_init())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1149
  {
1150
    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
1151
    statistic_increment(aborted_connects,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1152 1153 1154 1155 1156
    end_thread(thd,0);
    return 0;
  }
#endif

1157 1158 1159 1160 1161 1162 1163
  /*
    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.
  */
1164
  DBUG_PRINT("info", ("handle_one_connection called by thread %lu\n",
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1165
		      thd->thread_id));
monty@mysql.com's avatar
monty@mysql.com committed
1166
  /* now that we've called my_thread_init(), it is safe to call DBUG_* */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1167

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1168
#if defined(__WIN__)
1169
  init_signals();
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
1170
#elif !defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1171 1172 1173 1174
  sigset_t set;
  VOID(sigemptyset(&set));			// Get mask in use
  VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
#endif
1175
  thd->thread_stack= (char*) &thd;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1176 1177
  if (thd->store_globals())
  {
1178
    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
1179
    statistic_increment(aborted_connects,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1180 1181 1182 1183 1184 1185 1186 1187
    end_thread(thd,0);
    return 0;
  }

  do
  {
    int error;
    NET *net= &thd->net;
1188
    Security_context *sctx= thd->security_ctx;
1189
    net->no_send_error= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1190

1191
    if ((error=check_connection(thd)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1192 1193
    {						// Wrong permissions
      if (error > 0)
1194
	net_printf_error(thd, error, sctx->host_or_ip);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1195 1196
#ifdef __NT__
      if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
1197
	my_sleep(1000);				/* must wait after eof() */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1198
#endif
1199
      statistic_increment(aborted_connects,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1200 1201
      goto end_thread;
    }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1202
#ifdef __NETWARE__
1203
    netware_reg_user(sctx->ip, sctx->user, "MySQL");
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1204
#endif
1205
    if (thd->variables.max_join_size == HA_POS_ERROR)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1206 1207 1208 1209
      thd->options |= OPTION_BIG_SELECTS;
    if (thd->client_capabilities & CLIENT_COMPRESS)
      net->compress=1;				// Use compression

gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1210
    thd->version= refresh_version;
1211
    thd->proc_info= 0;
1212
    thd->command= COM_SLEEP;
1213 1214
    thd->set_time();
    thd->init_for_queries();
monty@mysql.com's avatar
monty@mysql.com committed
1215

1216
    if (sys_init_connect.value_length && !(sctx->master_access & SUPER_ACL))
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1217
    {
1218 1219
      execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect);
      if (thd->query_error)
1220
      {
pem@mysql.comhem.se's avatar
pem@mysql.comhem.se committed
1221
	thd->killed= THD::KILL_CONNECTION;
1222 1223 1224 1225 1226 1227
        sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
                          thd->thread_id,(thd->db ? thd->db : "unconnected"),
                          sctx->user ? sctx->user : "unauthenticated",
                          sctx->host_or_ip, "init_connect command failed");
        sql_print_warning("%s", net->last_error);
      }
1228 1229 1230
      thd->proc_info=0;
      thd->set_time();
      thd->init_for_queries();
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1231 1232
    }

1233 1234
    while (!net->error && net->vio != 0 &&
           !(thd->killed == THD::KILL_CONNECTION))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1235
    {
1236
      net->no_send_error= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1237 1238 1239
      if (do_command(thd))
	break;
    }
1240 1241
    if (thd->user_connect)
      decrease_user_connections(thd->user_connect);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1242
    if (net->error && net->vio != 0 && net->report_error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1243
    {
1244
      if (!thd->killed && thd->variables.log_warnings > 1)
monty@mysql.com's avatar
monty@mysql.com committed
1245
	sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
serg@serg.mylan's avatar
serg@serg.mylan committed
1246
                          thd->thread_id,(thd->db ? thd->db : "unconnected"),
1247 1248
                          sctx->user ? sctx->user : "unauthenticated",
                          sctx->host_or_ip,
serg@serg.mylan's avatar
serg@serg.mylan committed
1249 1250
                          (net->last_errno ? ER(net->last_errno) :
                           ER(ER_UNKNOWN_ERROR)));
1251
      net_send_error(thd, net->last_errno, NullS);
1252
      statistic_increment(aborted_threads,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1253
    }
1254 1255 1256 1257
    else if (thd->killed)
    {
      statistic_increment(aborted_threads,&LOCK_status);
    }
1258
    
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1259
end_thread:
1260
    close_connection(thd, 0, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1261 1262 1263 1264 1265 1266
    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;
1267
    thd->thread_stack= (char*) &thd;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1268 1269 1270 1271 1272
  } while (!(test_flags & TEST_NO_THREADS));
  /* The following is only executed if we are not using --one-thread */
  return(0);					/* purecov: deadcode */
}

hf@deer.(none)'s avatar
hf@deer.(none) committed
1273 1274
#endif /* EMBEDDED_LIBRARY */

1275 1276 1277 1278
/*
  Execute commands from bootstrap_file.
  Used when creating the initial grant tables
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1279

1280
pthread_handler_t handle_bootstrap(void *arg)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1281
{
1282 1283 1284
  THD *thd=(THD*) arg;
  FILE *file=bootstrap_file;
  char *buff;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1285

1286
  /* The following must be called before DBUG_ENTER */
1287
  thd->thread_stack= (char*) &thd;
1288
  if (my_thread_init() || thd->store_globals())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1289
  {
hf@deer.(none)'s avatar
hf@deer.(none) committed
1290
#ifndef EMBEDDED_LIBRARY
1291
    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1292
#endif
1293
    thd->fatal_error();
1294
    goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1295
  }
1296 1297
  DBUG_ENTER("handle_bootstrap");

hf@deer.(none)'s avatar
hf@deer.(none) committed
1298
#ifndef EMBEDDED_LIBRARY
1299 1300
  pthread_detach_this_thread();
  thd->thread_stack= (char*) &thd;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
1301
#if !defined(__WIN__) && !defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1302
  sigset_t set;
1303 1304
  VOID(sigemptyset(&set));			// Get mask in use
  VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1305
#endif
hf@deer.(none)'s avatar
hf@deer.(none) committed
1306
#endif /* EMBEDDED_LIBRARY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1307

1308
  if (thd->variables.max_join_size == HA_POS_ERROR)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1309 1310 1311 1312
    thd->options |= OPTION_BIG_SELECTS;

  thd->proc_info=0;
  thd->version=refresh_version;
1313 1314
  thd->security_ctx->priv_user=
    thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
1315
  thd->security_ctx->priv_host[0]=0;
1316 1317 1318 1319 1320 1321
  /*
    Make the "client" handle multiple results. This is necessary
    to enable stored procedures with SELECTs and Dynamic SQL
    in init-file.
  */
  thd->client_capabilities|= CLIENT_MULTI_RESULTS;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1322

1323
  buff= (char*) thd->net.buff;
1324
  thd->init_for_queries();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1325 1326
  while (fgets(buff, thd->net.max_packet, file))
  {
1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348
    /* strlen() can't be deleted because fgets() doesn't return length */
    ulong length= (ulong) strlen(buff);
    while (buff[length-1] != '\n' && !feof(file))
    {
      /*
        We got only a part of the current string. Will try to increase
        net buffer then read the rest of the current string.
      */
      /* purecov: begin tested */
      if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
      {
        net_send_error(thd, ER_NET_PACKET_TOO_LARGE, NullS);
        thd->fatal_error();
        break;
      }
      buff= (char*) thd->net.buff;
      fgets(buff + length, thd->net.max_packet - length, file);
      length+= (ulong) strlen(buff + length);
      /* purecov: end */
    }
    if (thd->is_fatal_error)
      break;                                    /* purecov: inspected */
monty@mishka.local's avatar
monty@mishka.local committed
1349

1350
    while (length && (my_isspace(thd->charset(), buff[length-1]) ||
1351
                      buff[length-1] == ';'))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1352 1353
      length--;
    buff[length]=0;
1354
    thd->query_length=length;
1355 1356
    thd->query= thd->memdup_w_gap(buff, length+1, 
				  thd->db_length+1+QUERY_CACHE_FLAGS_SIZE);
1357
    thd->query[length] = '\0';
1358 1359 1360 1361
    /*
      We don't need to obtain LOCK_thread_count here because in bootstrap
      mode we have only one thread.
    */
1362
    thd->query_id=next_query_id();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1363 1364
    mysql_parse(thd,thd->query,length);
    close_thread_tables(thd);			// Free tables
1365
    if (thd->is_fatal_error)
1366
      break;
1367
    free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
1368
#ifdef USING_TRANSACTIONS
1369
    free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC));
1370
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1371
  }
1372 1373 1374

  /* thd->fatal_error should be set in case something went wrong */
end:
1375 1376 1377 1378 1379 1380
  bootstrap_error= thd->is_fatal_error;

  net_end(&thd->net);
  thd->cleanup();
  delete thd;

hf@deer.(none)'s avatar
hf@deer.(none) committed
1381
#ifndef EMBEDDED_LIBRARY
1382 1383 1384
  (void) pthread_mutex_lock(&LOCK_thread_count);
  thread_count--;
  (void) pthread_mutex_unlock(&LOCK_thread_count);
1385
  (void) pthread_cond_broadcast(&COND_thread_count);
1386 1387
  my_thread_end();
  pthread_exit(0);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1388
#endif
1389
  DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1390 1391 1392
}


1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407
/* This works because items are allocated with sql_alloc() */

void free_items(Item *item)
{
  Item *next;
  DBUG_ENTER("free_items");
  for (; item ; item=next)
  {
    next=item->next;
    item->delete_self();
  }
  DBUG_VOID_RETURN;
}

/* This works because items are allocated with sql_alloc() */
1408 1409 1410

void cleanup_items(Item *item)
{
monty@mysql.com's avatar
monty@mysql.com committed
1411
  DBUG_ENTER("cleanup_items");  
1412 1413
  for (; item ; item=item->next)
    item->cleanup();
monty@mysql.com's avatar
monty@mysql.com committed
1414
  DBUG_VOID_RETURN;
1415 1416
}

1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435
/*
  Handle COM_TABLE_DUMP command

  SYNOPSIS
    mysql_table_dump
      thd           thread handle
      db            database name or an empty string. If empty,
                    the current database of the connection is used
      tbl_name      name of the table to dump

  NOTES
    This function is written to handle one specific command only.

  RETURN VALUE
    0               success
    1               error, the error message is set in THD
*/

static
1436
int mysql_table_dump(THD *thd, LEX_STRING *db, char *tbl_name)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1437 1438 1439 1440 1441
{
  TABLE* table;
  TABLE_LIST* table_list;
  int error = 0;
  DBUG_ENTER("mysql_table_dump");
1442 1443 1444 1445 1446
  if (db->length == 0)
  {
    db->str= thd->db;            /* purecov: inspected */
    db->length= thd->db_length;  /* purecov: inspected */
  }
1447
  if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST))))
1448
    DBUG_RETURN(1); // out of memory
1449
  table_list->db= db->str;
1450
  table_list->table_name= table_list->alias= tbl_name;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1451 1452
  table_list->lock_type= TL_READ_NO_INSERT;
  table_list->prev_global= &table_list;	// can be removed after merge with 4.1
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1453

1454
  if (check_db_name(db))
1455
  {
1456 1457
    /* purecov: begin inspected */
    my_error(ER_WRONG_DB_NAME ,MYF(0), db->str ? db->str : "NULL");
1458
    goto err;
1459
    /* purecov: end */
1460
  }
1461
  if (lower_case_table_names)
1462
    my_casedn_str(files_charset_info, tbl_name);
1463 1464 1465 1466

  if (!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT)))
    DBUG_RETURN(1);

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1467
  if (check_one_table_access(thd, SELECT_ACL, table_list))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1468 1469
    goto err;
  thd->free_list = 0;
1470
  thd->query_length=(uint) strlen(tbl_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1471
  thd->query = tbl_name;
1472
  if ((error = mysqld_dump_create_info(thd, table_list, -1)))
1473
  {
1474
    my_error(ER_GET_ERRNO, MYF(0), my_errno);
1475 1476
    goto err;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1477
  net_flush(&thd->net);
1478
  if ((error= table->file->dump(thd,-1)))
1479
    my_error(ER_GET_ERRNO, MYF(0), error);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1480

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1481
err:
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1482
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1483 1484
}

1485 1486 1487 1488
/*
  Ends the current transaction and (maybe) begin the next

  SYNOPSIS
1489
    end_trans()
1490 1491 1492 1493 1494 1495 1496
      thd            Current thread
      completion     Completion type

  RETURN
    0 - OK
*/

1497
int end_trans(THD *thd, enum enum_mysql_completiontype completion)
1498 1499 1500
{
  bool do_release= 0;
  int res= 0;
1501
  DBUG_ENTER("end_trans");
1502

1503
  if (unlikely(thd->in_sub_stmt))
1504 1505 1506 1507
  {
    my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
    DBUG_RETURN(1);
  }
1508 1509 1510 1511 1512 1513
  if (thd->transaction.xid_state.xa_state != XA_NOTR)
  {
    my_error(ER_XAER_RMFAIL, MYF(0),
             xa_state_names[thd->transaction.xid_state.xa_state]);
    DBUG_RETURN(1);
  }
1514 1515 1516 1517 1518 1519 1520 1521
  switch (completion) {
  case COMMIT:
    /*
     We don't use end_active_trans() here to ensure that this works
     even if there is a problem with the OPTION_AUTO_COMMIT flag
     (Which of course should never happen...)
    */
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
1522
    res= ha_commit(thd);
1523 1524
    thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE |
                             OPTION_KEEP_LOG);
1525 1526
    break;
  case COMMIT_RELEASE:
serg@serg.mylan's avatar
serg@serg.mylan committed
1527
    do_release= 1; /* fall through */
1528 1529 1530 1531 1532 1533
  case COMMIT_AND_CHAIN:
    res= end_active_trans(thd);
    if (!res && completion == COMMIT_AND_CHAIN)
      res= begin_trans(thd);
    break;
  case ROLLBACK_RELEASE:
serg@serg.mylan's avatar
serg@serg.mylan committed
1534
    do_release= 1; /* fall through */
1535 1536 1537 1538
  case ROLLBACK:
  case ROLLBACK_AND_CHAIN:
  {
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
serg@serg.mylan's avatar
serg@serg.mylan committed
1539
    if (ha_rollback(thd))
1540
      res= -1;
1541 1542
    thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE |
                             OPTION_KEEP_LOG);
1543 1544 1545 1546 1547 1548 1549 1550 1551
    if (!res && (completion == ROLLBACK_AND_CHAIN))
      res= begin_trans(thd);
    break;
  }
  default:
    res= -1;
    my_error(ER_UNKNOWN_COM_ERROR, MYF(0));
    DBUG_RETURN(-1);
  }
serg@serg.mylan's avatar
serg@serg.mylan committed
1552

1553 1554 1555
  if (res < 0)
    my_error(thd->killed_errno(), MYF(0));
  else if ((res == 0) && do_release)
serg@serg.mylan's avatar
serg@serg.mylan committed
1556 1557
    thd->killed= THD::KILL_CONNECTION;

1558 1559
  DBUG_RETURN(res);
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1560

1561
#ifndef EMBEDDED_LIBRARY
1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572

/*
  Read one command from socket and execute it (query or simple command).
  This function is called in loop from thread function.
  SYNOPSIS
    do_command()
  RETURN VALUE
    0  success
    1  request of thread shutdown (see dispatch_command() description)
*/

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1573 1574 1575
bool do_command(THD *thd)
{
  char *packet;
1576 1577
  uint old_timeout;
  ulong packet_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1578 1579 1580 1581 1582
  NET *net;
  enum enum_server_command command;
  DBUG_ENTER("do_command");

  net= &thd->net;
1583 1584 1585 1586
  /*
    indicator of uninitialized lex => normal flow of errors handling
    (see my_message_sql)
  */
1587
  thd->lex->current_select= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1588 1589

  packet=0;
1590
  old_timeout=net->read_timeout;
monty@mysql.com's avatar
monty@mysql.com committed
1591
  /* Wait max for 8 hours */
1592
  net->read_timeout=(uint) thd->variables.net_wait_timeout;
1593
  thd->clear_error();				// Clear error message
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1594 1595 1596 1597

  net_new_transaction(net);
  if ((packet_length=my_net_read(net)) == packet_error)
  {
1598 1599 1600 1601 1602
    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)
1603 1604
    {
      statistic_increment(aborted_threads,&LOCK_status);
1605
      DBUG_RETURN(TRUE);			// We have to close it.
1606
    }
1607
    net_send_error(thd, net->last_errno, NullS);
1608
    net->error= 0;
1609
    DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1610 1611 1612 1613 1614
  }
  else
  {
    packet=(char*) net->read_pos;
    command = (enum enum_server_command) (uchar) packet[0];
1615 1616
    if (command >= COM_END)
      command= COM_END;				// Wrong command
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1617 1618
    DBUG_PRINT("info",("Command on %s = %d (%s)",
		       vio_description(net->vio), command,
1619
		       command_name[command].str));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1620
  }
1621
  net->read_timeout=old_timeout;		// restore it
1622 1623 1624 1625 1626 1627 1628 1629 1630
  /*
    packet_length contains length of data, as it was stored in packet
    header. In case of malformed header, packet_length can be zero.
    If packet_length is not zero, my_net_read ensures that this number
    of bytes was actually read from network. Additionally my_net_read
    sets packet[packet_length]= 0 (thus if packet_length == 0,
    command == packet[0] == COM_SLEEP).
    In dispatch_command packet[packet_length] points beyond the end of packet.
  */
1631
  DBUG_RETURN(dispatch_command(command,thd, packet+1, (uint) packet_length));
1632
}
1633
#endif  /* EMBEDDED_LIBRARY */
1634

1635

1636 1637
/*
   Perform one connection-level (COM_XXXX) command.
1638

1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651
  SYNOPSIS
    dispatch_command()
    thd             connection handle
    command         type of command to perform 
    packet          data for the command, packet is always null-terminated
    packet_length   length of packet + 1 (to show that data is
                    null-terminated) except for COM_SLEEP, where it
                    can be zero.
  RETURN VALUE
    0   ok
    1   request of thread shutdown, i. e. if command is
        COM_QUIT/COM_SHUTDOWN
*/
1652

1653 1654 1655 1656
bool dispatch_command(enum enum_server_command command, THD *thd,
		      char* packet, uint packet_length)
{
  NET *net= &thd->net;
1657
  bool error= 0;
1658 1659
  DBUG_ENTER("dispatch_command");

1660 1661 1662
  if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA)
    thd->killed= THD::NOT_KILLED;

1663
  thd->command=command;
1664
  /*
1665 1666
    Commands which always take a long time are logged into
    the slow log only if opt_log_slow_admin_statements is set.
1667
  */
1668
  thd->enable_slow_log= TRUE;
1669
  thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
1670
  thd->set_time();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1671 1672 1673
  VOID(pthread_mutex_lock(&LOCK_thread_count));
  thd->query_id=query_id;
  if (command != COM_STATISTICS && command != COM_PING)
1674
    next_query_id();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1675
  thread_running++;
1676
  /* TODO: set thd->lex->sql_command to SQLCOM_END here */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1677
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
1678

1679 1680
  thd->server_status&=
           ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
1681
  switch (command) {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1682
  case COM_INIT_DB:
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1683 1684
  {
    LEX_STRING tmp;
1685 1686
    statistic_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB],
			&LOCK_status);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1687
    thd->convert_string(&tmp, system_charset_info,
1688
			packet, packet_length-1, thd->charset());
1689 1690
    if (!mysql_change_db(thd, tmp.str, FALSE))
    {
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
1691
      general_log_print(thd, command, "%s",thd->db);
1692 1693
      send_ok(thd);
    }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1694 1695
    break;
  }
1696
#ifdef HAVE_REPLICATION
1697 1698
  case COM_REGISTER_SLAVE:
  {
1699
    if (!register_slave(thd, (uchar*)packet, packet_length))
1700
      send_ok(thd);
1701 1702
    break;
  }
1703
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1704
  case COM_TABLE_DUMP:
1705
  {
1706 1707
    char *tbl_name;
    LEX_STRING db;
1708
    uint db_len= *(uchar*) packet;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1709
    if (db_len >= packet_length || db_len > NAME_LEN)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1710
    {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1711
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1712 1713
      break;
    }
1714
    uint tbl_len= *(uchar*) (packet + db_len + 1);
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1715 1716
    if (db_len+tbl_len+2 > packet_length || tbl_len > NAME_LEN)
    {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1717
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1718 1719
      break;
    }
1720

1721
    statistic_increment(thd->status_var.com_other, &LOCK_status);
1722
    thd->enable_slow_log= opt_log_slow_admin_statements;
1723 1724 1725
    db.str= thd->alloc(db_len + tbl_len + 2);
    db.length= db_len;
    if (!db.str)
1726 1727 1728 1729
    {
      my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
      break;
    }
1730
    tbl_name= strmake(db.str, packet + 1, db_len)+1;
1731
    strmake(tbl_name, packet + db_len + 2, tbl_len);
1732
    mysql_table_dump(thd, &db, tbl_name);
1733 1734
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1735 1736
  case COM_CHANGE_USER:
  {
1737 1738 1739 1740
    statistic_increment(thd->status_var.com_other, &LOCK_status);
    char *user= (char*) packet, *packet_end= packet+ packet_length;
    char *passwd= strend(user)+1;

1741
    thd->change_user();
1742
    thd->clear_error();                         // if errors from rollback
1743

1744
    /*
1745 1746 1747 1748
      Old clients send null-terminated string ('\0' for empty string) for
      password.  New clients send the size (1 byte) + string (not null
      terminated, so also '\0' for empty string).
    */
1749
    char db_buff[NAME_LEN+1];                 // buffer to store db in utf8
1750
    char *db= passwd;
1751 1752 1753 1754 1755 1756 1757
    char *save_db;
    uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
                      *passwd++ : strlen(passwd));
    uint dummy_errors, save_db_length, db_length, res;
    Security_context save_security_ctx= *thd->security_ctx;
    USER_CONN *save_user_connect;

1758
    db+= passwd_len + 1;
1759
#ifndef EMBEDDED_LIBRARY
1760
    /* Small check for incoming packet */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1761
    if ((uint) ((uchar*) db - net->read_pos) > packet_length)
1762
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1763
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
1764 1765
      break;
    }
1766
#endif
1767
    /* Convert database name to utf8 */
1768 1769 1770 1771 1772 1773 1774
    /*
      Handle problem with old bug in client protocol where db had an extra
      \0
    */
    db_length= (packet_end - db);
    if (db_length > 0 && db[db_length-1] == 0)
      db_length--;
1775
    db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
1776
                             system_charset_info, db, db_length,
1777
                             thd->charset(), &dummy_errors)]= 0;
1778
    db= db_buff;
peter@mysql.com's avatar
peter@mysql.com committed
1779

1780
    /* Save user and privileges */
1781 1782 1783
    save_db_length= thd->db_length;
    save_db= thd->db;
    save_user_connect= thd->user_connect;
1784 1785

    if (!(thd->security_ctx->user= my_strdup(user, MYF(0))))
1786
    {
1787
      thd->security_ctx->user= save_security_ctx.user;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1788
      my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
1789 1790
      break;
    }
peter@mysql.com's avatar
peter@mysql.com committed
1791

1792 1793
    /* Clear variables that are allocated */
    thd->user_connect= 0;
1794
    res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE);
peter@mysql.com's avatar
peter@mysql.com committed
1795

1796 1797
    if (res)
    {
1798
      /* authentication failure, we shall restore old user */
1799
      if (res > 0)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1800
        my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
1801 1802
      x_free(thd->security_ctx->user);
      *thd->security_ctx= save_security_ctx;
1803
      thd->user_connect= save_user_connect;
1804 1805 1806 1807 1808
      thd->db= save_db;
      thd->db_length= save_db_length;
    }
    else
    {
1809
#ifndef NO_EMBEDDED_ACCESS_CHECKS
1810
      /* we've authenticated new user */
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1811 1812
      if (save_user_connect)
	decrease_user_connections(save_user_connect);
1813
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
1814
      x_free((gptr) save_db);
1815
      x_free((gptr)  save_security_ctx.user);
1816
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1817 1818
    break;
  }
1819
  case COM_STMT_EXECUTE:
1820
  {
1821
    mysql_stmt_execute(thd, packet, packet_length);
1822 1823
    break;
  }
1824
  case COM_STMT_FETCH:
1825 1826 1827 1828
  {
    mysql_stmt_fetch(thd, packet, packet_length);
    break;
  }
1829
  case COM_STMT_SEND_LONG_DATA:
1830
  {
1831
    mysql_stmt_get_longdata(thd, packet, packet_length);
1832 1833
    break;
  }
1834
  case COM_STMT_PREPARE:
1835
  {
1836
    mysql_stmt_prepare(thd, packet, packet_length);
1837 1838
    break;
  }
1839
  case COM_STMT_CLOSE:
1840
  {
1841
    mysql_stmt_close(thd, packet);
1842 1843
    break;
  }
1844
  case COM_STMT_RESET:
1845 1846 1847 1848
  {
    mysql_stmt_reset(thd, packet);
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1849 1850
  case COM_QUERY:
  {
1851 1852
    if (alloc_query(thd, packet, packet_length))
      break;					// fatal error is set
1853
    char *packet_end= thd->query + thd->query_length;
1854 1855
    /* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */
    const char *format= "%.*b";
1856
    general_log_print(thd, command, format, thd->query_length, thd->query);
1857
    DBUG_PRINT("query",("%-.4096s",thd->query));
1858 1859 1860 1861

    if (!(specialflag & SPECIAL_NO_PRIOR))
      my_pthread_setprio(pthread_self(),QUERY_PRIOR);

1862
    mysql_parse(thd,thd->query, thd->query_length);
1863

1864
    while (!thd->killed && thd->lex->found_semicolon && !thd->net.report_error)
1865
    {
1866
      char *packet= thd->lex->found_semicolon;
1867
      net->no_send_error= 0;
1868
      /*
1869 1870
        Multiple queries exits, execute them individually
      */
1871 1872
      if (thd->lock || thd->open_tables || thd->derived_tables ||
          thd->prelocked_mode)
1873
        close_thread_tables(thd);
1874
      ulong length= (ulong)(packet_end-packet);
1875

1876
      log_slow_statement(thd);
1877

1878
      /* Remove garbage at start of query */
1879
      while (my_isspace(thd->charset(), *packet) && length > 0)
1880 1881 1882 1883
      {
        packet++;
        length--;
      }
monty@mysql.com's avatar
monty@mysql.com committed
1884
      VOID(pthread_mutex_lock(&LOCK_thread_count));
1885
      thd->query_length= length;
1886
      thd->query= packet;
1887
      thd->query_id= next_query_id();
1888
      thd->set_time(); /* Reset the query start time. */
1889
      /* TODO: set thd->lex->sql_command to SQLCOM_END here */
1890
      VOID(pthread_mutex_unlock(&LOCK_thread_count));
1891 1892 1893
      mysql_parse(thd, packet, length);
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1894 1895 1896 1897 1898
    if (!(specialflag & SPECIAL_NO_PRIOR))
      my_pthread_setprio(pthread_self(),WAIT_PRIOR);
    DBUG_PRINT("info",("query ready"));
    break;
  }
1899
  case COM_FIELD_LIST:				// This isn't actually needed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1900
#ifdef DONT_ALLOW_SHOW_COMMANDS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1901 1902
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0));	/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1903 1904 1905
    break;
#else
  {
1906
    char *fields, *packet_end= packet + packet_length - 1, *arg_end;
1907 1908
    /* Locked closure of all tables */
    TABLE_LIST *locked_tables= NULL;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1909
    TABLE_LIST table_list;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1910
    LEX_STRING conv_name;
1911
    /* Saved variable value */
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
1912
    my_bool old_innodb_table_locks=  thd->variables.innodb_table_locks;
1913
    uint dummy;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
1914

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1915 1916
    /* used as fields initializator */
    lex_start(thd, 0, 0);
1917

1918 1919
    statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS],
			&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1920
    bzero((char*) &table_list,sizeof(table_list));
1921
    if (thd->copy_db_to(&table_list.db, &dummy))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1922
      break;
1923 1924 1925 1926
    /*
      We have name + wildcard in packet, separated by endzero
    */
    arg_end= strend(packet);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1927
    thd->convert_string(&conv_name, system_charset_info,
1928
			packet, (uint) (arg_end - packet), thd->charset());
1929
    table_list.alias= table_list.table_name= conv_name.str;
1930
    packet= arg_end + 1;
1931 1932 1933 1934 1935 1936 1937 1938 1939

    if (!my_strcasecmp(system_charset_info, table_list.db,
                       information_schema_name.str))
    {
      ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, table_list.alias);
      if (schema_table)
        table_list.schema_table= schema_table;
    }

1940
    thd->query_length= (uint) (packet_end - packet); // Don't count end \0
1941 1942
    if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1)))
      break;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
1943
    general_log_print(thd, command, "%s %s", table_list.table_name, fields);
1944
    if (lower_case_table_names)
1945
      my_casedn_str(files_charset_info, table_list.table_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1946

monty@mysql.com's avatar
monty@mysql.com committed
1947
    if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege,
1948
		     0, 0, test(table_list.schema_table)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1949
      break;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1950 1951
    if (grant_option &&
	check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1952
      break;
1953 1954 1955 1956 1957 1958
    /* init structures for VIEW processing */
    table_list.select_lex= &(thd->lex->select_lex);
    mysql_init_query(thd, (uchar*)"", 0);
    thd->lex->
      select_lex.table_list.link_in_list((byte*) &table_list,
                                         (byte**) &table_list.next_local);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
1959
    thd->lex->add_to_query_tables(&table_list);
1960

1961 1962
    /* switch on VIEW optimisation: do not fill temporary tables */
    thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1963
    mysqld_list_fields(thd,&table_list,fields);
1964
    thd->lex->unit.cleanup();
1965
    thd->cleanup_after_query();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1966 1967 1968 1969
    break;
  }
#endif
  case COM_QUIT:
1970
    /* We don't calculate statistics for this command */
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
1971
    general_log_print(thd, command, NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1972 1973 1974 1975
    net->error=0;				// Don't give 'abort' message
    error=TRUE;					// End server
    break;

1976
#ifdef REMOVED
1977
  case COM_CREATE_DB:				// QQ: To be removed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1978
    {
1979
      LEX_STRING db, alias;
1980
      HA_CREATE_INFO create_info;
1981

1982 1983
      statistic_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB],
			  &LOCK_status);
1984 1985 1986
      if (thd->LEX_STRING_make(&db, packet, packet_length -1) ||
          thd->LEX_STRING_make(&alias, db.str, db.length) ||
          check_db_name(&db))
1987
      {
1988
	my_error(ER_WRONG_DB_NAME, MYF(0), db.str ? db.str : "NULL");
1989 1990
	break;
      }
1991 1992
      if (check_access(thd, CREATE_ACL, db.str , 0, 1, 0,
                       is_schema_db(db.str)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1993
	break;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
1994
      general_log_print(thd, command, packet);
1995
      bzero(&create_info, sizeof(create_info));
1996
      mysql_create_db(thd, (lower_case_table_names == 2 ? alias.str : db.str),
1997
                      &create_info, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1998 1999
      break;
    }
2000
  case COM_DROP_DB:				// QQ: To be removed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2001
    {
2002 2003
      statistic_increment(thd->status_var.com_stat[SQLCOM_DROP_DB],
			  &LOCK_status);
2004 2005 2006 2007
      LEX_STRING db;

      if (thd->LEX_STRING_make(&db, packet, packet_length - 1) ||
          check_db_name(&db))
2008
      {
2009
	my_error(ER_WRONG_DB_NAME, MYF(0), db.str ? db.str : "NULL");
2010 2011
	break;
      }
2012
      if (check_access(thd, DROP_ACL, db.str, 0, 1, 0, is_schema_db(db.str)))
2013
	break;
2014 2015
      if (thd->locked_tables || thd->active_transaction())
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2016 2017
	my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                   ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2018
	break;
2019
      }
2020 2021
      general_log_print(thd, command, db.str);
      mysql_rm_db(thd, db.str, 0, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2022 2023
      break;
    }
2024
#endif
2025
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2026 2027
  case COM_BINLOG_DUMP:
    {
monty@mysql.com's avatar
monty@mysql.com committed
2028 2029 2030 2031
      ulong pos;
      ushort flags;
      uint32 slave_server_id;

2032
      statistic_increment(thd->status_var.com_other,&LOCK_status);
2033
      thd->enable_slow_log= opt_log_slow_admin_statements;
2034
      if (check_global_access(thd, REPL_SLAVE_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2035
	break;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2036

2037
      /* TODO: The following has to be changed to an 8 byte integer */
2038 2039
      pos = uint4korr(packet);
      flags = uint2korr(packet + 4);
2040
      thd->server_id=0; /* avoid suicide */
2041
      if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
2042
	kill_zombie_dump_threads(slave_server_id);
2043
      thd->server_id = slave_server_id;
monty@mysql.com's avatar
monty@mysql.com committed
2044

cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2045
      general_log_print(thd, command, "Log: '%s'  Pos: %ld", packet+10,
monty@mysql.com's avatar
monty@mysql.com committed
2046
                      (long) pos);
2047
      mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
2048
      unregister_slave(thd,1,1);
monty@mysql.com's avatar
monty@mysql.com committed
2049
      /*  fake COM_QUIT -- if we get here, the thread needs to terminate */
2050 2051
      error = TRUE;
      net->error = 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2052 2053
      break;
    }
2054
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2055
  case COM_REFRESH:
2056 2057 2058 2059 2060 2061
  {
    bool not_used;
    statistic_increment(thd->status_var.com_stat[SQLCOM_FLUSH],
                        &LOCK_status);
    ulong options= (ulong) (uchar) packet[0];
    if (check_global_access(thd,RELOAD_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2062
      break;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2063
    general_log_print(thd, command, NullS);
2064 2065 2066 2067
    if (!reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
      send_ok(thd);
    break;
  }
2068
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2069
  case COM_SHUTDOWN:
2070
  {
2071
    statistic_increment(thd->status_var.com_other, &LOCK_status);
2072
    if (check_global_access(thd,SHUTDOWN_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2073
      break; /* purecov: inspected */
2074
    /*
2075 2076 2077 2078
      If the client is < 4.1.3, it is going to send us no argument; then
      packet_length is 1, packet[0] is the end 0 of the packet. Note that
      SHUTDOWN_DEFAULT is 0. If client is >= 4.1.3, the shutdown level is in
      packet[0].
2079
    */
2080 2081
    enum mysql_enum_shutdown_level level=
      (enum mysql_enum_shutdown_level) (uchar) packet[0];
2082
    DBUG_PRINT("quit",("Got shutdown command for level %u", level));
2083 2084 2085 2086 2087 2088 2089
    if (level == SHUTDOWN_DEFAULT)
      level= SHUTDOWN_WAIT_ALL_BUFFERS; // soon default will be configurable
    else if (level != SHUTDOWN_WAIT_ALL_BUFFERS)
    {
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "this shutdown level");
      break;
    }
2090
    DBUG_PRINT("quit",("Got shutdown command for level %u", level));
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2091
    general_log_print(thd, command, NullS);
2092
    send_eof(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2093 2094 2095
#ifdef __WIN__
    sleep(1);					// must wait after eof()
#endif
2096
    send_eof(thd);				// This is for 'quit request'
2097
    close_connection(thd, 0, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2098 2099 2100 2101
    close_thread_tables(thd);			// Free before kill
    kill_mysql();
    error=TRUE;
    break;
2102
  }
2103
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2104 2105
  case COM_STATISTICS:
  {
2106 2107 2108
    STATUS_VAR current_global_status_var;
    ulong uptime;
    uint length;
hf@deer.(none)'s avatar
hf@deer.(none) committed
2109
#ifndef EMBEDDED_LIBRARY
2110 2111
    char buff[250];
    uint buff_len= sizeof(buff);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2112 2113
#else
    char *buff= thd->net.last_error;
2114
    uint buff_len= sizeof(thd->net.last_error);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2115
#endif
2116

2117 2118 2119
    general_log_print(thd, command, NullS);
    statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS],
			&LOCK_status);
2120
    calc_sum_of_all_status(&current_global_status_var);
2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133
    uptime= (ulong) (thd->start_time - start_time);
    length= my_snprintf((char*) buff, buff_len - 1,
                        "Uptime: %lu  Threads: %d  Questions: %lu  "
                        "Slow queries: %lu  Opens: %lu  Flush tables: %lu  "
                        "Open tables: %u  Queries per second avg: %.3f",
                        uptime,
                        (int) thread_count, (ulong) thd->query_id,
                        current_global_status_var.long_query_count,
                        current_global_status_var.opened_tables,
                        refresh_version,
                        cached_open_tables(),
                        (uptime ? (ulonglong2double(thd->query_id) /
                                   (double) uptime) : (double) 0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2134
#ifdef SAFEMALLOC
2135
    if (sf_malloc_cur_memory)				// Using SAFEMALLOC
2136 2137 2138 2139 2140 2141 2142
    {
      char *end= buff + length;
      length+= my_snprintf(end, buff_len - length - 1,
                           end,"  Memory in use: %ldK  Max memory used: %ldK",
                           (sf_malloc_cur_memory+1023L)/1024L,
                           (sf_malloc_max_memory+1023L)/1024L);
    }
hf@deer.(none)'s avatar
hf@deer.(none) committed
2143 2144
#endif
#ifndef EMBEDDED_LIBRARY
2145 2146
    VOID(my_net_write(net, buff, length));
      VOID(net_flush(net));
hf@deer.(none)'s avatar
hf@deer.(none) committed
2147
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2148 2149 2150
    break;
  }
  case COM_PING:
2151
    statistic_increment(thd->status_var.com_other, &LOCK_status);
2152
    send_ok(thd);				// Tell client we are alive
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2153 2154
    break;
  case COM_PROCESS_INFO:
2155 2156
    statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST],
			&LOCK_status);
2157 2158
    if (!thd->security_ctx->priv_user[0] &&
        check_global_access(thd, PROCESS_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2159
      break;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2160
    general_log_print(thd, command, NullS);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2161
    mysqld_list_processes(thd,
2162 2163
			  thd->security_ctx->master_access & PROCESS_ACL ? 
			  NullS : thd->security_ctx->priv_user, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2164 2165 2166
    break;
  case COM_PROCESS_KILL:
  {
2167
    statistic_increment(thd->status_var.com_stat[SQLCOM_KILL], &LOCK_status);
2168
    ulong id=(ulong) uint4korr(packet);
2169
    sql_kill(thd,id,false);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2170 2171
    break;
  }
2172 2173
  case COM_SET_OPTION:
  {
2174 2175
    statistic_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION],
			&LOCK_status);
2176 2177 2178 2179
    enum_mysql_set_option command= (enum_mysql_set_option) uint2korr(packet);
    switch (command) {
    case MYSQL_OPTION_MULTI_STATEMENTS_ON:
      thd->client_capabilities|= CLIENT_MULTI_STATEMENTS;
2180
      send_eof(thd);
2181 2182 2183
      break;
    case MYSQL_OPTION_MULTI_STATEMENTS_OFF:
      thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS;
2184
      send_eof(thd);
2185 2186
      break;
    default:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2187
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
2188 2189 2190 2191
      break;
    }
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2192
  case COM_DEBUG:
2193
    statistic_increment(thd->status_var.com_other, &LOCK_status);
2194
    if (check_global_access(thd, SUPER_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2195
      break;					/* purecov: inspected */
2196
    mysql_print_status();
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2197
    general_log_print(thd, command, NullS);
2198
    send_eof(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2199 2200 2201 2202 2203
    break;
  case COM_SLEEP:
  case COM_CONNECT:				// Impossible here
  case COM_TIME:				// Impossible from client
  case COM_DELAYED_INSERT:
2204
  case COM_END:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2205
  default:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2206
    my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2207 2208
    break;
  }
2209 2210
  if (thd->lock || thd->open_tables || thd->derived_tables ||
      thd->prelocked_mode)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2211 2212 2213 2214
  {
    thd->proc_info="closing tables";
    close_thread_tables(thd);			/* Free tables */
  }
2215 2216 2217 2218 2219 2220 2221 2222 2223 2224
  /*
    assume handlers auto-commit (if some doesn't - transaction handling
    in MySQL should be redesigned to support it; it's a big change,
    and it's not worth it - better to commit explicitly only writing
    transactions, read-only ones should better take care of themselves.
    saves some work in 2pc too)
    see also sql_base.cc - close_thread_tables()
  */
  bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt));
  if (!thd->active_transaction())
2225
    thd->transaction.xid_state.xid.null();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2226

2227 2228 2229
  /* report error issued during command execution */
  if (thd->killed_errno() && !thd->net.report_error)
    thd->send_kill_message();
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2230
  if (thd->net.report_error)
2231
    net_send_error(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2232

2233
  log_slow_statement(thd);
2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248

  thd->proc_info="cleaning up";
  VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
  thd->proc_info=0;
  thd->command=COM_SLEEP;
  thd->query=0;
  thd->query_length=0;
  thread_running--;
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
  thd->packet.shrink(thd->variables.net_buffer_length);	// Reclaim some memory
  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
  DBUG_RETURN(error);
}


2249
void log_slow_statement(THD *thd)
2250
{
2251
  time_t start_of_query;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2252
  DBUG_ENTER("log_slow_statement");
2253 2254 2255 2256 2257 2258 2259

  /*
    The following should never be true with our current code base,
    but better to keep this here so we don't accidently try to log a
    statement in a trigger or stored function
  */
  if (unlikely(thd->in_sub_stmt))
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2260
    DBUG_VOID_RETURN;                           // Don't set time for sub stmt
2261 2262

  start_of_query= thd->start_time;
2263
  thd->end_time();				// Set start time
2264

2265 2266 2267 2268 2269
  /*
    Do not log administrative statements unless the appropriate option is
    set; do not log into slow log if reading from backup.
  */
  if (thd->enable_slow_log && !thd->user_time)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2270
  {
2271 2272
    thd->proc_info="logging slow query";

2273 2274
    if ((ulong) (thd->start_time - thd->time_after_lock) >
	thd->variables.long_query_time ||
2275 2276
	((thd->server_status &
	  (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
2277
	 (specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES)))
2278
    {
2279
      thd->status_var.long_query_count++;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2280
      slow_log_print(thd, thd->query, thd->query_length, start_of_query);
2281
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2282
  }
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2283
  DBUG_VOID_RETURN;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2284 2285
}

2286

2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314
/*
  Create a TABLE_LIST object for an INFORMATION_SCHEMA table.

  SYNOPSIS
    prepare_schema_table()
      thd              thread handle
      lex              current lex
      table_ident      table alias if it's used
      schema_table_idx the type of the INFORMATION_SCHEMA table to be
                       created

  DESCRIPTION
    This function is used in the parser to convert a SHOW or DESCRIBE
    table_name command to a SELECT from INFORMATION_SCHEMA.
    It prepares a SELECT_LEX and a TABLE_LIST object to represent the
    given command as a SELECT parse tree.

  NOTES
    Due to the way this function works with memory and LEX it cannot
    be used outside the parser (parse tree transformations outside
    the parser break PS and SP).

  RETURN VALUE
    0                 success
    1                 out of memory or SHOW commands are not allowed
                      in this version of the server.
*/

2315 2316 2317 2318 2319
int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
                         enum enum_schema_tables schema_table_idx)
{
  DBUG_ENTER("prepare_schema_table");
  SELECT_LEX *sel= 0;
2320
  switch (schema_table_idx) {
2321 2322
  case SCH_SCHEMATA:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2323 2324
    my_message(ER_NOT_ALLOWED_COMMAND,
               ER(ER_NOT_ALLOWED_COMMAND), MYF(0));   /* purecov: inspected */
2325 2326 2327 2328 2329 2330 2331 2332 2333 2334
    DBUG_RETURN(1);
#else
    if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
	check_global_access(thd, SHOW_DB_ACL))
      DBUG_RETURN(1);
    break;
#endif
  case SCH_TABLE_NAMES:
  case SCH_TABLES:
  case SCH_VIEWS:
2335
  case SCH_TRIGGERS:
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2336
  case SCH_EVENTS:
2337
#ifdef DONT_ALLOW_SHOW_COMMANDS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2338 2339
    my_message(ER_NOT_ALLOWED_COMMAND,
               ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
2340 2341 2342
    DBUG_RETURN(1);
#else
    {
2343 2344
      LEX_STRING db;
      uint dummy;
2345
      if (lex->select_lex.db == NULL &&
2346
          thd->copy_db_to(&lex->select_lex.db, &dummy))
2347
      {
2348
        DBUG_RETURN(1);
2349
      }
2350 2351 2352
      db.str= lex->select_lex.db;
      db.length= strlen(db.str);
      if (check_db_name(&db))
2353
      {
2354
        my_error(ER_WRONG_DB_NAME, MYF(0), db.str);
2355 2356
        DBUG_RETURN(1);
      }
2357 2358
      if (check_access(thd, SELECT_ACL, db.str, &thd->col_access, 0, 0,
                       is_schema_db(db.str)))
2359
        DBUG_RETURN(1);			        /* purecov: inspected */
2360
      if (!thd->col_access && check_grant_db(thd, db.str))
2361
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2362
	my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
2363
                 thd->security_ctx->priv_user, thd->security_ctx->priv_host,
2364
                 db.str);
2365 2366 2367 2368 2369 2370 2371 2372
	DBUG_RETURN(1);
      }
      break;
    }
#endif
  case SCH_COLUMNS:
  case SCH_STATISTICS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2373 2374
    my_message(ER_NOT_ALLOWED_COMMAND,
               ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
2375 2376 2377 2378 2379 2380
    DBUG_RETURN(1);
#else
    if (table_ident)
    {
      TABLE_LIST **query_tables_last= lex->query_tables_last;
      sel= new SELECT_LEX();
2381 2382
      /* 'parent_lex' is used in init_query() so it must be before it. */
      sel->parent_lex= lex;
2383
      sel->init_query();
monty@mishka.local's avatar
monty@mishka.local committed
2384
      if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, 
2385 2386 2387 2388 2389 2390
                                 (List<String> *) 0, (List<String> *) 0))
        DBUG_RETURN(1);
      lex->query_tables_last= query_tables_last;
      TABLE_LIST *table_list= (TABLE_LIST*) sel->table_list.first;
      char *db= table_list->db;
      if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
2391 2392
                       &table_list->grant.privilege, 0, 0,
                       test(table_list->schema_table)))
2393 2394 2395 2396 2397 2398 2399
        DBUG_RETURN(1);				/* purecov: inspected */
      if (grant_option && check_grant(thd, SELECT_ACL, table_list, 2,
                                      UINT_MAX, 0))
        DBUG_RETURN(1);
      break;
    }
#endif
2400 2401 2402
  case SCH_OPEN_TABLES:
  case SCH_VARIABLES:
  case SCH_STATUS:
2403 2404
  case SCH_PROCEDURES:
  case SCH_CHARSETS:
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2405
  case SCH_ENGINES:
2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424
  case SCH_COLLATIONS:
  case SCH_COLLATION_CHARACTER_SET_APPLICABILITY:
  case SCH_USER_PRIVILEGES:
  case SCH_SCHEMA_PRIVILEGES:
  case SCH_TABLE_PRIVILEGES:
  case SCH_COLUMN_PRIVILEGES:
  case SCH_TABLE_CONSTRAINTS:
  case SCH_KEY_COLUMN_USAGE:
  default:
    break;
  }
  
  SELECT_LEX *select_lex= lex->current_select;
  if (make_schema_select(thd, select_lex, schema_table_idx))
  {
    DBUG_RETURN(1);
  }
  TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first;
  table_list->schema_select_lex= sel;
2425
  table_list->schema_table_reformed= 1;
2426 2427 2428 2429
  DBUG_RETURN(0);
}


2430 2431
/*
  Read query from packet and store in thd->query
2432
  Used in COM_QUERY and COM_STMT_PREPARE
2433 2434 2435 2436 2437 2438 2439

  DESCRIPTION
    Sets the following THD variables:
      query
      query_length

  RETURN VALUES
2440 2441
    FALSE ok
    TRUE  error;  In this case thd->fatal_error is set
2442 2443
*/

2444
bool alloc_query(THD *thd, const char *packet, uint packet_length)
2445 2446
{
  packet_length--;				// Remove end null
2447
  /* Remove garbage at start and end of query */
2448
  while (my_isspace(thd->charset(),packet[0]) && packet_length > 0)
2449 2450 2451 2452
  {
    packet++;
    packet_length--;
  }
2453
  const char *pos= packet + packet_length;     // Point at end null
peter@mysql.com's avatar
peter@mysql.com committed
2454
  while (packet_length > 0 &&
2455
	 (pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1])))
2456 2457 2458 2459 2460
  {
    pos--;
    packet_length--;
  }
  /* We must allocate some extra memory for query cache */
monty@mysql.com's avatar
monty@mysql.com committed
2461
  thd->query_length= 0;                        // Extra safety: Avoid races
2462 2463
  if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet),
					      packet_length,
2464 2465
					      thd->db_length+ 1 +
					      QUERY_CACHE_FLAGS_SIZE)))
2466
    return TRUE;
2467 2468
  thd->query[packet_length]=0;
  thd->query_length= packet_length;
2469 2470 2471 2472

  /* Reclaim some memory */
  thd->packet.shrink(thd->variables.net_buffer_length);
  thd->convert_buffer.shrink(thd->variables.net_buffer_length);
2473

2474
  return FALSE;
2475 2476
}

2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492
static void reset_one_shot_variables(THD *thd) 
{
  thd->variables.character_set_client=
    global_system_variables.character_set_client;
  thd->variables.collation_connection=
    global_system_variables.collation_connection;
  thd->variables.collation_database=
    global_system_variables.collation_database;
  thd->variables.collation_server=
    global_system_variables.collation_server;
  thd->update_charset();
  thd->variables.time_zone=
    global_system_variables.time_zone;
  thd->one_shot_set= 0;
}

2493

2494
/*
2495
  Execute command saved in thd and lex->sql_command
2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516

  SYNOPSIS
    mysql_execute_command()
      thd                       Thread handle

  IMPLEMENTATION

    Before every operation that can request a write lock for a table
    wait if a global read lock exists. However do not wait if this
    thread has locked tables already. No new locks can be requested
    until the other locks are released. The thread that requests the
    global read lock waits for write locked tables to become unlocked.

    Note that wait_if_global_read_lock() sets a protection against a new
    global read lock when it succeeds. This needs to be released by
    start_waiting_global_read_lock() after the operation.

  RETURN
    FALSE       OK
    TRUE        Error
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2517

2518
bool
2519
mysql_execute_command(THD *thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2520
{
2521 2522 2523 2524
  bool res= FALSE;
  bool need_start_waiting= FALSE; // have protection against global read lock
  int  result= 0;
  LEX  *lex= thd->lex;
monty@mysql.com's avatar
monty@mysql.com committed
2525
  /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2526
  SELECT_LEX *select_lex= &lex->select_lex;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2527
  /* first table of first SELECT_LEX */
monty@mysql.com's avatar
monty@mysql.com committed
2528
  TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2529 2530 2531
  /* list of all tables in query */
  TABLE_LIST *all_tables;
  /* most outer SELECT_LEX_UNIT of query */
2532
  SELECT_LEX_UNIT *unit= &lex->unit;
2533
  /* Saved variable value */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2534
  DBUG_ENTER("mysql_execute_command");
2535
  thd->net.no_send_error= 0;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2536 2537 2538
#ifdef WITH_PARTITION_STORAGE_ENGINE
  thd->work_part_info= 0;
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2539

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555
  /*
    In many cases first table of main SELECT_LEX have special meaning =>
    check that it is first table in global list and relink it first in 
    queries_tables list if it is necessary (we need such relinking only
    for queries with subqueries in select list, in this case tables of
    subqueries will go to global list first)

    all_tables will differ from first_table only if most upper SELECT_LEX
    do not contain tables.

    Because of above in place where should be at least one table in most
    outer SELECT_LEX we have following check:
    DBUG_ASSERT(first_table == all_tables);
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
  */
  lex->first_lists_tables_same();
2556
  /* should be assigned after making first tables same */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2557
  all_tables= lex->query_tables;
2558 2559 2560 2561
  /* set context for commands which do not use setup_tables */
  select_lex->
    context.resolve_in_table_list_only((TABLE_LIST*)select_lex->
                                       table_list.first);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2562

2563 2564 2565 2566 2567
  /*
    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.
2568
    Don't reset warnings when executing a stored routine.
2569
  */
2570
  if ((all_tables || &lex->select_lex != lex->all_selects_list ||
2571
       lex->sroutines.records) && !thd->spcont ||
2572
      lex->time_zone_tables_used)
monty@mysql.com's avatar
monty@mysql.com committed
2573
    mysql_reset_errors(thd, 0);
2574

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2575
#ifdef HAVE_REPLICATION
2576
  if (unlikely(thd->slave_thread))
2577
  {
peter@mysql.com's avatar
peter@mysql.com committed
2578
    /*
2579 2580
      Check if statment should be skipped because of slave filtering
      rules
2581 2582

      Exceptions are:
2583 2584
      - UPDATE MULTI: For this statement, we want to check the filtering
        rules later in the code
2585
      - SET: we always execute it (Not that many SET commands exists in
lars@mysql.com's avatar
lars@mysql.com committed
2586 2587
        the binary log anyway -- only 4.1 masters write SET statements,
	in 5.0 there are no SET statements in the binary log)
2588 2589
      - DROP TEMPORARY TABLE IF EXISTS: we always execute it (otherwise we
        have stale files on slave caused by exclusion of one tmp table).
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
2590
    */
2591 2592
    if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) &&
	!(lex->sql_command == SQLCOM_SET_OPTION) &&
2593
	!(lex->sql_command == SQLCOM_DROP_TABLE &&
2594
          lex->drop_temporary && lex->drop_if_exists) &&
guilhem@mysql.com's avatar
Merge  
guilhem@mysql.com committed
2595
        all_tables_not_ok(thd, all_tables))
guilhem@mysql.com's avatar
guilhem@mysql.com committed
2596 2597
    {
      /* we warn the slave SQL thread */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2598
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615
      if (thd->one_shot_set)
      {
        /*
          It's ok to check thd->one_shot_set here:

          The charsets in a MySQL 5.0 slave can change by both a binlogged
          SET ONE_SHOT statement and the event-internal charset setting, 
          and these two ways to change charsets do not seems to work
          together.

          At least there seems to be problems in the rli cache for
          charsets if we are using ONE_SHOT.  Note that this is normally no
          problem because either the >= 5.0 slave reads a 4.1 binlog (with
          ONE_SHOT) *or* or 5.0 binlog (without ONE_SHOT) but never both."
        */
        reset_one_shot_variables(thd);
      }
2616
      DBUG_RETURN(0);
guilhem@mysql.com's avatar
guilhem@mysql.com committed
2617
    }
2618
  }
2619
  else
2620
  {
2621
#endif /* HAVE_REPLICATION */
2622 2623 2624 2625 2626 2627
    /*
      When option readonly is set deny operations which change non-temporary
      tables. Except for the replication thread and the 'super' users.
    */
    if (opt_readonly &&
	!(thd->security_ctx->master_access & SUPER_ACL) &&
2628
	(sql_command_flags[lex->sql_command] & CF_CHANGES_DATA) &&
2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639
	!((lex->sql_command == SQLCOM_CREATE_TABLE) &&
	  (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) &&
	((lex->sql_command != SQLCOM_UPDATE_MULTI) &&
	 some_non_temp_table_to_be_updated(thd, all_tables)))
    {
      my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
      DBUG_RETURN(-1);
    }
#ifdef HAVE_REPLICATION
  } /* endif unlikely slave */
#endif
2640 2641
  statistic_increment(thd->status_var.com_stat[lex->sql_command],
                      &LOCK_status);
2642

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2643
  switch (lex->sql_command) {
2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682
  case SQLCOM_SHOW_EVENTS:
    if ((res= check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0,
                           is_schema_db(thd->lex->select_lex.db))))
      break;
    /* fall through */
  case SQLCOM_SHOW_STATUS_PROC:
  case SQLCOM_SHOW_STATUS_FUNC:
    res= execute_sqlcom_select(thd, all_tables);
    break;
  case SQLCOM_SHOW_STATUS:
  {
    system_status_var old_status_var= thd->status_var;
    thd->initial_status_var= &old_status_var;
    res= execute_sqlcom_select(thd, all_tables);
    /* Don't log SHOW STATUS commands to slow query log */
    thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED |
                           SERVER_QUERY_NO_GOOD_INDEX_USED);
    /*
      restore status variables, as we don't want 'show status' to cause
      changes
    */
    pthread_mutex_lock(&LOCK_status);
    add_diff_to_status(&global_status_var, &thd->status_var,
                       &old_status_var);
    thd->status_var= old_status_var;
    pthread_mutex_unlock(&LOCK_status);
    break;
  }
  case SQLCOM_SHOW_DATABASES:
  case SQLCOM_SHOW_TABLES:
  case SQLCOM_SHOW_TRIGGERS:
  case SQLCOM_SHOW_TABLE_STATUS:
  case SQLCOM_SHOW_OPEN_TABLES:
  case SQLCOM_SHOW_PLUGINS:
  case SQLCOM_SHOW_FIELDS:
  case SQLCOM_SHOW_KEYS:
  case SQLCOM_SHOW_VARIABLES:
  case SQLCOM_SHOW_CHARSETS:
  case SQLCOM_SHOW_COLLATIONS:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2683
  case SQLCOM_SELECT:
2684
    thd->status_var.last_query_cost= 0.0;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2685
    if (all_tables)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2686
    {
2687 2688 2689 2690
      res= check_table_access(thd,
                              lex->exchange ? SELECT_ACL | FILE_ACL :
                              SELECT_ACL,
                              all_tables, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2691 2692
    }
    else
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2693
      res= check_access(thd,
2694 2695 2696 2697
                        lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
                        any_db, 0, 0, 0, 0);
    if (!res)
      res= execute_sqlcom_select(thd, all_tables);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2698
    break;
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2699
  case SQLCOM_PREPARE:
2700
  {
2701
    mysql_sql_stmt_prepare(thd);
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2702 2703 2704 2705
    break;
  }
  case SQLCOM_EXECUTE:
  {
2706
    mysql_sql_stmt_execute(thd);
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2707 2708 2709 2710
    break;
  }
  case SQLCOM_DEALLOCATE_PREPARE:
  {
2711
    mysql_sql_stmt_close(thd);
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2712 2713
    break;
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2714
  case SQLCOM_DO:
2715 2716
    if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
        open_and_lock_tables(thd, all_tables))
2717
      goto error;
2718 2719

    res= mysql_do(thd, *lex->insert_list);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2720 2721
    break;

2722
  case SQLCOM_EMPTY_QUERY:
2723
    send_ok(thd);
2724 2725
    break;

2726 2727 2728 2729
  case SQLCOM_HELP:
    res= mysqld_help(thd,lex->help_arg);
    break;

2730
#ifndef EMBEDDED_LIBRARY
2731
  case SQLCOM_PURGE:
2732
  {
2733
    if (check_global_access(thd, SUPER_ACL))
2734
      goto error;
monty@mysql.com's avatar
monty@mysql.com committed
2735
    /* PURGE MASTER LOGS TO 'file' */
2736 2737 2738
    res = purge_master_logs(thd, lex->to_log);
    break;
  }
2739 2740
  case SQLCOM_PURGE_BEFORE:
  {
2741 2742
    Item *it;

2743 2744
    if (check_global_access(thd, SUPER_ACL))
      goto error;
monty@mysql.com's avatar
monty@mysql.com committed
2745
    /* PURGE MASTER LOGS BEFORE 'data' */
2746
    it= (Item *)lex->value_list.head();
2747
    if ((!it->fixed && it->fix_fields(lex->thd, &it)) ||
2748
        it->check_cols(1))
2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759
    {
      my_error(ER_WRONG_ARGUMENTS, MYF(0), "PURGE LOGS BEFORE");
      goto error;
    }
    it= new Item_func_unix_timestamp(it);
    /*
      it is OK only emulate fix_fieds, because we need only
      value of constant
    */
    it->quick_fix_field();
    res = purge_master_logs_before_date(thd, (ulong)it->val_int());
2760 2761
    break;
  }
2762
#endif
2763 2764
  case SQLCOM_SHOW_WARNS:
  {
2765 2766
    res= mysqld_show_warnings(thd, (ulong)
			      ((1L << (uint) MYSQL_ERROR::WARN_LEVEL_NOTE) |
2767 2768 2769
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_WARN) |
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)
			       ));
2770 2771 2772 2773
    break;
  }
  case SQLCOM_SHOW_ERRORS:
  {
2774 2775
    res= mysqld_show_warnings(thd, (ulong)
			      (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
2776 2777
    break;
  }
2778 2779
  case SQLCOM_SHOW_NEW_MASTER:
  {
2780
    if (check_global_access(thd, REPL_SLAVE_ACL))
2781
      goto error;
2782
    /* This query don't work now. See comment in repl_failsafe.cc */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2783
#ifndef WORKING_NEW_MASTER
2784 2785
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "SHOW NEW MASTER");
    goto error;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2786
#else
2787 2788
    res = show_new_master(thd);
    break;
2789
#endif
2790
  }
2791

2792
#ifdef HAVE_REPLICATION
2793 2794
  case SQLCOM_SHOW_SLAVE_HOSTS:
  {
2795
    if (check_global_access(thd, REPL_SLAVE_ACL))
2796 2797 2798 2799
      goto error;
    res = show_slave_hosts(thd);
    break;
  }
2800 2801
  case SQLCOM_SHOW_BINLOG_EVENTS:
  {
2802
    if (check_global_access(thd, REPL_SLAVE_ACL))
2803
      goto error;
2804
    res = mysql_show_binlog_events(thd);
2805 2806
    break;
  }
2807 2808
#endif

2809
  case SQLCOM_BACKUP_TABLE:
2810
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2811
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
2812
    if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
2813
	check_global_access(thd, FILE_ACL))
2814
      goto error; /* purecov: inspected */
2815
    thd->enable_slow_log= opt_log_slow_admin_statements;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2816
    res = mysql_backup_table(thd, first_table);
2817
    select_lex->table_list.first= (byte*) first_table;
2818
    lex->query_tables=all_tables;
2819 2820
    break;
  }
2821
  case SQLCOM_RESTORE_TABLE:
2822
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2823
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
2824
    if (check_table_access(thd, INSERT_ACL, all_tables, 0) ||
2825
	check_global_access(thd, FILE_ACL))
2826
      goto error; /* purecov: inspected */
2827
    thd->enable_slow_log= opt_log_slow_admin_statements;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2828
    res = mysql_restore_table(thd, first_table);
2829
    select_lex->table_list.first= (byte*) first_table;
2830
    lex->query_tables=all_tables;
2831 2832
    break;
  }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2833 2834
  case SQLCOM_ASSIGN_TO_KEYCACHE:
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2835
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
2836
    if (check_access(thd, INDEX_ACL, first_table->db,
2837 2838
                     &first_table->grant.privilege, 0, 0,
                     test(first_table->schema_table)))
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2839
      goto error;
2840
    res= mysql_assign_to_keycache(thd, first_table, &lex->ident);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2841 2842
    break;
  }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2843 2844
  case SQLCOM_PRELOAD_KEYS:
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2845
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
2846
    if (check_access(thd, INDEX_ACL, first_table->db,
2847 2848
                     &first_table->grant.privilege, 0, 0,
                     test(first_table->schema_table)))
2849
      goto error;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2850
    res = mysql_preload_keys(thd, first_table);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2851 2852
    break;
  }
2853
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2854
  case SQLCOM_CHANGE_MASTER:
2855
  {
2856
    if (check_global_access(thd, SUPER_ACL))
2857
      goto error;
2858
    pthread_mutex_lock(&LOCK_active_mi);
2859
    res = change_master(thd,active_mi);
2860
    pthread_mutex_unlock(&LOCK_active_mi);
2861 2862
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2863
  case SQLCOM_SHOW_SLAVE_STAT:
2864
  {
2865 2866
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2867
      goto error;
2868
    pthread_mutex_lock(&LOCK_active_mi);
2869
    res = show_master_info(thd,active_mi);
2870
    pthread_mutex_unlock(&LOCK_active_mi);
2871 2872
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2873
  case SQLCOM_SHOW_MASTER_STAT:
2874
  {
2875 2876
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2877 2878 2879 2880
      goto error;
    res = show_binlog_info(thd);
    break;
  }
peter@mysql.com's avatar
peter@mysql.com committed
2881

2882
  case SQLCOM_LOAD_MASTER_DATA: // sync with master
2883
    if (check_global_access(thd, SUPER_ACL))
2884
      goto error;
2885
    if (end_active_trans(thd))
2886
      goto error;
2887
    res = load_master_data(thd);
2888
    break;
2889
#endif /* HAVE_REPLICATION */
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2890
  case SQLCOM_SHOW_ENGINE_STATUS:
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2891
    {
2892
      if (check_global_access(thd, SUPER_ACL))
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2893 2894
        goto error;
      res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_STATUS);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2895 2896
      break;
    }
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2897
  case SQLCOM_SHOW_ENGINE_MUTEX:
vtkachenko@intelp4d.mysql.com's avatar
vtkachenko@intelp4d.mysql.com committed
2898 2899
    {
      if (check_global_access(thd, SUPER_ACL))
vtkachenko@intelp4d.mysql.com's avatar
vtkachenko@intelp4d.mysql.com committed
2900
        goto error;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
2901
      res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_MUTEX);
vtkachenko@intelp4d.mysql.com's avatar
vtkachenko@intelp4d.mysql.com committed
2902 2903
      break;
    }
2904
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2905
  case SQLCOM_LOAD_MASTER_TABLE:
2906
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2907
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
2908 2909
    DBUG_ASSERT(first_table->db); /* Must be set in the parser */

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2910
    if (check_access(thd, CREATE_ACL, first_table->db,
2911 2912
		     &first_table->grant.privilege, 0, 0,
                     test(first_table->schema_table)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2913 2914 2915 2916
      goto error;				/* purecov: inspected */
    if (grant_option)
    {
      /* Check that the first table has CREATE privilege */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2917
      if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0))
2918
	goto error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2919
    }
2920
    pthread_mutex_lock(&LOCK_active_mi);
2921 2922 2923 2924
    /*
      fetch_master_table will send the error to the client on failure.
      Give error if the table already exists.
    */
2925
    if (!fetch_master_table(thd, first_table->db, first_table->table_name,
2926
			    active_mi, 0, 0))
2927
    {
2928
      send_ok(thd);
2929
    }
2930
    pthread_mutex_unlock(&LOCK_active_mi);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2931
    break;
2932
  }
2933
#endif /* HAVE_REPLICATION */
2934

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2935
  case SQLCOM_CREATE_TABLE:
2936
  {
2937
    /* If CREATE TABLE of non-temporary table, do implicit commit */
2938 2939 2940 2941 2942 2943 2944 2945
    if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
    {
      if (end_active_trans(thd))
      {
	res= -1;
	break;
      }
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2946 2947 2948 2949 2950
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    bool link_to_local;
    // Skip first table, which is the table we are creating
    TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local);
    TABLE_LIST *select_tables= lex->query_tables;
monty@mysql.com's avatar
monty@mysql.com committed
2951

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2952
    if ((res= create_table_precheck(thd, select_tables, create_table)))
monty@mysql.com's avatar
monty@mysql.com committed
2953
      goto end_with_restore_list;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2954

2955 2956 2957
#ifndef HAVE_READLINK
    lex->create_info.data_file_name=lex->create_info.index_file_name=0;
#else
2958
    /* Fix names if symlinked tables */
2959
    if (append_file_to_dir(thd, &lex->create_info.data_file_name,
2960
			   create_table->table_name) ||
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2961
	append_file_to_dir(thd, &lex->create_info.index_file_name,
2962
			   create_table->table_name))
monty@mysql.com's avatar
monty@mysql.com committed
2963
      goto end_with_restore_list;
2964
#endif
2965
    /*
2966
      If we are using SET CHARSET without DEFAULT, add an implicit
2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977
      DEFAULT to not confuse old users. (This may change).
    */
    if ((lex->create_info.used_fields & 
	 (HA_CREATE_USED_DEFAULT_CHARSET | HA_CREATE_USED_CHARSET)) ==
	HA_CREATE_USED_CHARSET)
    {
      lex->create_info.used_fields&= ~HA_CREATE_USED_CHARSET;
      lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
      lex->create_info.default_table_charset= lex->create_info.table_charset;
      lex->create_info.table_charset= 0;
    }
2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990
    /*
      The create-select command will open and read-lock the select table
      and then create, open and write-lock the new table. If a global
      read lock steps in, we get a deadlock. The write lock waits for
      the global read lock, while the global read lock waits for the
      select table to be closed. So we wait until the global readlock is
      gone before starting both steps. Note that
      wait_if_global_read_lock() sets a protection against a new global
      read lock when it succeeds. This needs to be released by
      start_waiting_global_read_lock(). We protect the normal CREATE
      TABLE in the same way. That way we avoid that a new table is
      created during a gobal read lock.
    */
2991 2992
    if (!thd->locked_tables &&
        !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
2993
    {
monty@mysql.com's avatar
monty@mysql.com committed
2994 2995
      res= 1;
      goto end_with_restore_list;
2996
    }
2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007
#ifdef WITH_PARTITION_STORAGE_ENGINE
    {
      partition_info *part_info= thd->lex->part_info;
      if (part_info && !(part_info= thd->lex->part_info->get_clone()))
      {
        res= -1;
        goto end_with_restore_list;
      }
      thd->work_part_info= part_info;
    }
#endif
3008
    if (select_lex->item_list.elements)		// With select
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3009 3010
    {
      select_result *result;
3011

3012
      select_lex->options|= SELECT_NO_UNLOCK;
3013
      unit->set_limit(select_lex);
3014

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3015
      if (!(res= open_and_lock_tables(thd, select_tables)))
3016
      {
3017 3018 3019 3020
        /*
          Is table which we are changing used somewhere in other parts
          of query
        */
3021
        if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
3022
        {
3023
          TABLE_LIST *duplicate;
3024
          if ((duplicate= unique_table(thd, create_table, select_tables)))
3025 3026 3027
          {
            update_non_unique_table_error(create_table, "CREATE", duplicate);
            res= 1;
3028
            goto end_with_restore_list;
3029
          }
3030
        }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3031 3032 3033 3034 3035 3036 3037 3038
        /* If we create merge table, we have to test tables in merge, too */
        if (lex->create_info.used_fields & HA_CREATE_USED_UNION)
        {
          TABLE_LIST *tab;
          for (tab= (TABLE_LIST*) lex->create_info.merge_list.first;
               tab;
               tab= tab->next_local)
          {
3039
            TABLE_LIST *duplicate;
3040
            if ((duplicate= unique_table(thd, tab, select_tables)))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3041
            {
3042
              update_non_unique_table_error(tab, "CREATE", duplicate);
monty@mysql.com's avatar
monty@mysql.com committed
3043
              res= 1;
3044
              goto end_with_restore_list;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3045 3046 3047
            }
          }
        }
3048

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3049 3050 3051 3052 3053
        if ((result= new select_create(create_table,
				       &lex->create_info,
				       lex->create_list,
				       lex->key_list,
				       select_lex->item_list,
3054 3055
				       lex->duplicates,
				       lex->ignore)))
3056 3057 3058 3059 3060
        {
          /*
            CREATE from SELECT give its SELECT_LEX for SELECT,
            and item_list belong to SELECT
          */
3061
          res= handle_select(thd, lex, result, 0);
3062
          delete result;
3063
        }
monty@mysql.com's avatar
monty@mysql.com committed
3064
	/* reset for PS */
3065 3066
	lex->create_list.empty();
	lex->key_list.empty();
3067 3068
      }
    }
monty@mysql.com's avatar
monty@mysql.com committed
3069
    else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3070
    {
3071 3072 3073
      /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
      if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)
        thd->options|= OPTION_KEEP_LOG;
monty@mysql.com's avatar
monty@mysql.com committed
3074
      /* regular create */
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3075
      if (lex->like_name)
monty@mysql.com's avatar
monty@mysql.com committed
3076
        res= mysql_create_like_table(thd, create_table, &lex->create_info, 
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3077
                                     lex->like_name); 
venu@myvenu.com's avatar
venu@myvenu.com committed
3078
      else
3079
      {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3080
        res= mysql_create_table(thd, create_table->db,
3081
				create_table->table_name, &lex->create_info,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3082
				lex->create_list,
3083
				lex->key_list, 0, 0, 1);
3084
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3085
      if (!res)
3086
	send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3087
    }
3088

monty@mysql.com's avatar
monty@mysql.com committed
3089
    /* put tables back for PS rexecuting */
monty@mysql.com's avatar
monty@mysql.com committed
3090
end_with_restore_list:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3091
    lex->link_first_table_back(create_table, link_to_local);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3092
    break;
3093
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3094
  case SQLCOM_CREATE_INDEX:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3095 3096
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_one_table_access(thd, INDEX_ACL, all_tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3097
      goto error; /* purecov: inspected */
3098
    thd->enable_slow_log= opt_log_slow_admin_statements;
3099
    if (end_active_trans(thd))
3100
      goto error;
3101
    res= mysql_create_index(thd, first_table, lex->key_list);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3102 3103
    break;

3104
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3105
  case SQLCOM_SLAVE_START:
3106
  {
3107
    pthread_mutex_lock(&LOCK_active_mi);
3108
    start_slave(thd,active_mi,1 /* net report*/);
3109
    pthread_mutex_unlock(&LOCK_active_mi);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3110
    break;
3111
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3112
  case SQLCOM_SLAVE_STOP:
3113 3114 3115 3116 3117 3118
  /*
    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,
3119
      so it waits for the client thread because t is locked by it.
3120
    - then the client thread does SLAVE STOP.
3121 3122
      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.
3123 3124 3125
    To prevent that, refuse SLAVE STOP if the
    client thread has locked tables
  */
3126
  if (thd->locked_tables || thd->active_transaction() || thd->global_read_lock)
3127
  {
3128 3129
    my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
               ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
3130
    goto error;
3131
  }
3132
  {
3133
    pthread_mutex_lock(&LOCK_active_mi);
3134
    stop_slave(thd,active_mi,1/* net report*/);
3135
    pthread_mutex_unlock(&LOCK_active_mi);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3136
    break;
3137
  }
3138
#endif /* HAVE_REPLICATION */
3139

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3140
  case SQLCOM_ALTER_TABLE:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3141
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3142
    {
3143
      ulong priv=0;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3144 3145 3146 3147 3148
      ulong priv_needed= ALTER_ACL;
      /* We also require DROP priv for ALTER TABLE ... DROP PARTITION */
      if (lex->alter_info.flags & ALTER_DROP_PARTITION)
        priv_needed|= DROP_ACL;

3149 3150
      /* Must be set in the parser */
      DBUG_ASSERT(select_lex->db);
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3151
      if (check_access(thd, priv_needed, first_table->db,
3152 3153 3154 3155
		       &first_table->grant.privilege, 0, 0,
                       test(first_table->schema_table)) ||
	  check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0,
                       is_schema_db(select_lex->db))||
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3156
	  check_merge_table_access(thd, first_table->db,
3157 3158 3159
				   (TABLE_LIST *)
				   lex->create_info.merge_list.first))
	goto error;				/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3160 3161
      if (grant_option)
      {
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3162
	if (check_grant(thd, priv_needed, all_tables, 0, UINT_MAX, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3163
	  goto error;
3164
	if (lex->name.str && !test_all_bits(priv,INSERT_ACL | CREATE_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3165 3166 3167
	{					// Rename of table
	  TABLE_LIST tmp_table;
	  bzero((char*) &tmp_table,sizeof(tmp_table));
3168
	  tmp_table.table_name= lex->name.str;
3169
	  tmp_table.db=select_lex->db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3170
	  tmp_table.grant.privilege=priv;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3171 3172
	  if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0,
			  UINT_MAX, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3173 3174 3175
	    goto error;
	}
      }
3176
      /* Don't yet allow changing of symlinks with ALTER TABLE */
3177 3178 3179 3180 3181 3182
      if (lex->create_info.data_file_name)
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
                     "DATA DIRECTORY option ignored");
      if (lex->create_info.index_file_name)
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
                     "INDEX DIRECTORY option ignored");
3183
      lex->create_info.data_file_name=lex->create_info.index_file_name=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3184
      /* ALTER TABLE ends previous transaction */
3185
      if (end_active_trans(thd))
3186
	goto error;
3187

3188 3189 3190 3191 3192
      if (!thd->locked_tables &&
          !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
      {
        res= 1;
        break;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3193
      }
3194

3195
      thd->enable_slow_log= opt_log_slow_admin_statements;
3196
      res= mysql_alter_table(thd, select_lex->db, lex->name.str,
3197 3198 3199 3200 3201
                             &lex->create_info,
                             first_table, lex->create_list,
                             lex->key_list,
                             select_lex->order_list.elements,
                             (ORDER *) select_lex->order_list.first,
3202
                             lex->ignore, &lex->alter_info, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3203 3204
      break;
    }
3205
  case SQLCOM_RENAME_TABLE:
3206
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3207
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3208
    TABLE_LIST *table;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3209
    for (table= first_table; table; table= table->next_local->next_local)
3210
    {
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3211
      if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
3212
		       &table->grant.privilege,0,0, test(table->schema_table)) ||
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3213
	  check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db,
3214 3215
		       &table->next_local->grant.privilege, 0, 0,
                       test(table->next_local->schema_table)))
3216 3217 3218
	goto error;
      if (grant_option)
      {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3219
	TABLE_LIST old_list, new_list;
3220 3221 3222 3223
	/*
	  we do not need initialize old_list and new_list because we will
	  come table[0] and table->next[0] there
	*/
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3224 3225 3226 3227
	old_list= table[0];
	new_list= table->next_local[0];
	if (check_grant(thd, ALTER_ACL, &old_list, 0, 1, 0) ||
	    (!test_all_bits(table->next_local->grant.privilege,
3228
			    INSERT_ACL | CREATE_ACL) &&
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3229
	     check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0)))
3230 3231 3232
	  goto error;
      }
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3233
    query_cache_invalidate3(thd, first_table, 0);
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3234
    if (end_active_trans(thd) || mysql_rename_tables(thd, first_table, 0))
3235
      goto error;
3236
    break;
3237
  }
3238
#ifndef EMBEDDED_LIBRARY
3239 3240
  case SQLCOM_SHOW_BINLOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3241 3242
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0)); /* purecov: inspected */
3243
    goto error;
3244 3245
#else
    {
3246
      if (check_global_access(thd, SUPER_ACL))
3247 3248 3249 3250
	goto error;
      res = show_binlogs(thd);
      break;
    }
peter@mysql.com's avatar
peter@mysql.com committed
3251
#endif
3252
#endif /* EMBEDDED_LIBRARY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3253
  case SQLCOM_SHOW_CREATE:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3254
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3255
#ifdef DONT_ALLOW_SHOW_COMMANDS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3256 3257
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0)); /* purecov: inspected */
3258
    goto error;
3259
#else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3260
    {
3261 3262 3263 3264
      /* Ignore temporary tables if this is "SHOW CREATE VIEW" */
      if (lex->only_view)
        first_table->skip_temporary= 1;

3265
      if (check_access(thd, SELECT_ACL | EXTRA_ACL, first_table->db,
3266 3267
		       &first_table->grant.privilege, 0, 0, 
                       test(first_table->schema_table)))
3268
	goto error;
monty@mishka.local's avatar
monty@mishka.local committed
3269
      if (grant_option && check_grant(thd, SELECT_ACL, all_tables, 2, UINT_MAX, 0))
3270
	goto error;
monty@mishka.local's avatar
monty@mishka.local committed
3271
      res= mysqld_show_create(thd, first_table);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3272 3273
      break;
    }
3274
#endif
3275 3276
  case SQLCOM_CHECKSUM:
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3277
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3278
    if (check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, 0))
3279
      goto error; /* purecov: inspected */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3280
    res = mysql_checksum_table(thd, first_table, &lex->check_opt);
3281 3282
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3283
  case SQLCOM_REPAIR:
3284
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3285
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3286
    if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
3287
      goto error; /* purecov: inspected */
3288
    thd->enable_slow_log= opt_log_slow_admin_statements;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3289
    res= mysql_repair_table(thd, first_table, &lex->check_opt);
3290 3291 3292
    /* ! we write after unlocking the table */
    if (!res && !lex->no_write_to_binlog)
    {
3293
      /* Presumably, REPAIR and binlog writing doesn't require synchronization */
3294 3295
      if (mysql_bin_log.is_open())
      {
3296
	thd->clear_error(); // No binlog error generated
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3297 3298
        thd->binlog_query(THD::STMT_QUERY_TYPE,
                          thd->query, thd->query_length, 0, FALSE);
3299 3300
      }
    }
3301
    select_lex->table_list.first= (byte*) first_table;
3302
    lex->query_tables=all_tables;
3303 3304
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3305
  case SQLCOM_CHECK:
3306
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3307
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3308
    if (check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, 0))
3309
      goto error; /* purecov: inspected */
3310
    thd->enable_slow_log= opt_log_slow_admin_statements;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3311
    res = mysql_check_table(thd, first_table, &lex->check_opt);
3312
    select_lex->table_list.first= (byte*) first_table;
3313
    lex->query_tables=all_tables;
3314 3315
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3316 3317
  case SQLCOM_ANALYZE:
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3318
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3319
    if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3320
      goto error; /* purecov: inspected */
3321
    thd->enable_slow_log= opt_log_slow_admin_statements;
3322
    res= mysql_analyze_table(thd, first_table, &lex->check_opt);
3323 3324 3325
    /* ! we write after unlocking the table */
    if (!res && !lex->no_write_to_binlog)
    {
3326
      /* Presumably, ANALYZE and binlog writing doesn't require synchronization */
3327 3328
      if (mysql_bin_log.is_open())
      {
3329
	thd->clear_error(); // No binlog error generated
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3330 3331
        thd->binlog_query(THD::STMT_QUERY_TYPE,
                          thd->query, thd->query_length, 0, FALSE);
3332 3333
      }
    }
3334
    select_lex->table_list.first= (byte*) first_table;
3335
    lex->query_tables=all_tables;
3336
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3337
  }
3338

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3339 3340
  case SQLCOM_OPTIMIZE:
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3341
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3342
    if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3343
      goto error; /* purecov: inspected */
3344
    thd->enable_slow_log= opt_log_slow_admin_statements;
3345
    res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3346 3347
      mysql_recreate_table(thd, first_table, 1) :
      mysql_optimize_table(thd, first_table, &lex->check_opt);
3348 3349 3350
    /* ! we write after unlocking the table */
    if (!res && !lex->no_write_to_binlog)
    {
3351
      /* Presumably, OPTIMIZE and binlog writing doesn't require synchronization */
3352 3353
      if (mysql_bin_log.is_open())
      {
3354
	thd->clear_error(); // No binlog error generated
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3355 3356
        thd->binlog_query(THD::STMT_QUERY_TYPE,
                          thd->query, thd->query_length, 0, FALSE);
3357 3358
      }
    }
3359
    select_lex->table_list.first= (byte*) first_table;
3360
    lex->query_tables=all_tables;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3361 3362 3363
    break;
  }
  case SQLCOM_UPDATE:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3364 3365
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (update_precheck(thd, all_tables))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3366
      break;
3367 3368
    DBUG_ASSERT(select_lex->offset_limit == 0);
    unit->set_limit(select_lex);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3369 3370 3371 3372 3373 3374
    res= (result= mysql_update(thd, all_tables,
                               select_lex->item_list,
                               lex->value_list,
                               select_lex->where,
                               select_lex->order_list.elements,
                               (ORDER *) select_lex->order_list.first,
3375
                               unit->select_limit_cnt,
3376
                               lex->duplicates, lex->ignore));
3377
    /* mysql_update return 2 if we need to switch to multi-update */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3378
    if (result != 2)
3379
      break;
3380
  case SQLCOM_UPDATE_MULTI:
3381 3382 3383 3384
  {
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    /* if we switched from normal update, rights are checked */
    if (result != 2)
3385
    {
3386 3387 3388 3389 3390
      if ((res= multi_update_precheck(thd, all_tables)))
        break;
    }
    else
      res= 0;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3391

3392
    res= mysql_multi_update_prepare(thd);
3393

3394
#ifdef HAVE_REPLICATION
3395
    /* Check slave filtering rules */
3396
    if (unlikely(thd->slave_thread))
3397
    {
3398 3399
      if (all_tables_not_ok(thd, all_tables))
      {
3400 3401 3402 3403 3404
        if (res!= 0)
        {
          res= 0;             /* don't care of prev failure  */
          thd->clear_error(); /* filters are of highest prior */
        }
3405 3406 3407 3408
        /* we warn the slave SQL thread */
        my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
        break;
      }
3409 3410
      if (res)
        break;
3411
    }
3412 3413
    else
    {
3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426
#endif /* HAVE_REPLICATION */
      if (res)
        break;
      if (opt_readonly &&
	  !(thd->security_ctx->master_access & SUPER_ACL) &&
	  some_non_temp_table_to_be_updated(thd, all_tables))
      {
	my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
	break;
      }
#ifdef HAVE_REPLICATION
    }  /* unlikely */
#endif
3427

3428 3429 3430 3431 3432 3433
    res= mysql_multi_update(thd, all_tables,
                            &select_lex->item_list,
                            &lex->value_list,
                            select_lex->where,
                            select_lex->options,
                            lex->duplicates, lex->ignore, unit, select_lex);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3434
    break;
monty@mysql.com's avatar
monty@mysql.com committed
3435
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3436
  case SQLCOM_REPLACE:
3437 3438
  case SQLCOM_INSERT:
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3439
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
monty@mysql.com's avatar
monty@mysql.com committed
3440
    if ((res= insert_precheck(thd, all_tables)))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3441
      break;
3442 3443 3444 3445 3446 3447 3448 3449

    if (!thd->locked_tables &&
        !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
    {
      res= 1;
      break;
    }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3450
    res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
monty@mishka.local's avatar
monty@mishka.local committed
3451
		      lex->update_list, lex->value_list,
3452
                      lex->duplicates, lex->ignore);
3453 3454 3455 3456 3457 3458 3459

    /*
      If we have inserted into a VIEW, and the base table has
      AUTO_INCREMENT column, but this column is not accessible through
      a view, then we should restore LAST_INSERT_ID to the value it
      had before the statement.
    */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3460
    if (first_table->view && !first_table->contain_auto_increment)
3461 3462
      thd->first_successful_insert_id_in_cur_stmt=
        thd->first_successful_insert_id_in_prev_stmt;
3463

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3464
    break;
3465
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3466 3467 3468
  case SQLCOM_REPLACE_SELECT:
  case SQLCOM_INSERT_SELECT:
  {
monty@mysql.com's avatar
monty@mysql.com committed
3469
    select_result *result;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3470
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
monty@mishka.local's avatar
monty@mishka.local committed
3471
    if ((res= insert_precheck(thd, all_tables)))
3472
      break;
monty@mysql.com's avatar
monty@mysql.com committed
3473

3474
    /* Fix lock for first table */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3475 3476
    if (first_table->lock_type == TL_WRITE_DELAYED)
      first_table->lock_type= TL_WRITE;
3477

3478 3479
    /* Don't unlock tables until command is written to binary log */
    select_lex->options|= SELECT_NO_UNLOCK;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3480

3481
    unit->set_limit(select_lex);
3482 3483 3484 3485 3486 3487 3488 3489

    if (! thd->locked_tables &&
        ! (need_start_waiting= ! wait_if_global_read_lock(thd, 0, 1)))
    {
      res= 1;
      break;
    }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3490
    if (!(res= open_and_lock_tables(thd, all_tables)))
3491
    {
3492
      /* Skip first table, which is the table we are inserting in */
3493 3494
      TABLE_LIST *second_table= first_table->next_local;
      select_lex->table_list.first= (byte*) second_table;
3495 3496
      select_lex->context.table_list= 
        select_lex->context.first_name_resolution_table= second_table;
3497 3498 3499
      res= mysql_insert_select_prepare(thd);
      if (!res && (result= new select_insert(first_table, first_table->table,
                                             &lex->field_list,
3500 3501
                                             &lex->update_list,
                                             &lex->value_list,
3502
                                             lex->duplicates, lex->ignore)))
3503
      {
3504
	res= handle_select(thd, lex, result, OPTION_SETUP_TABLES_DONE);
3505 3506 3507 3508 3509 3510 3511 3512 3513
        /*
          Invalidate the table in the query cache if something changed
          after unlocking when changes become visible.
          TODO: this is workaround. right way will be move invalidating in
          the unlock procedure.
        */
        if (first_table->lock_type ==  TL_WRITE_CONCURRENT_INSERT &&
            thd->lock)
        {
3514 3515 3516
          /* INSERT ... SELECT should invalidate only the very first table */
          TABLE_LIST *save_table= first_table->next_local;
          first_table->next_local= 0;
3517 3518
          mysql_unlock_tables(thd, thd->lock);
          query_cache_invalidate3(thd, first_table, 1);
3519
          first_table->next_local= save_table;
3520 3521
          thd->lock=0;
        }
3522 3523
        delete result;
      }
3524
      /* revert changes for SP */
msvensson@neptunus.(none)'s avatar
msvensson@neptunus.(none) committed
3525
      select_lex->table_list.first= (byte*) first_table;
3526
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3527

3528 3529 3530 3531 3532 3533
    /*
      If we have inserted into a VIEW, and the base table has
      AUTO_INCREMENT column, but this column is not accessible through
      a view, then we should restore LAST_INSERT_ID to the value it
      had before the statement.
    */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3534
    if (first_table->view && !first_table->contain_auto_increment)
3535 3536
      thd->first_successful_insert_id_in_cur_stmt=
        thd->first_successful_insert_id_in_prev_stmt;
3537

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3538 3539
    break;
  }
3540
  case SQLCOM_TRUNCATE:
3541 3542 3543 3544 3545
    if (end_active_trans(thd))
    {
      res= -1;
      break;
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3546 3547
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_one_table_access(thd, DELETE_ACL, all_tables))
3548
      goto error;
3549 3550 3551 3552
    /*
      Don't allow this within a transaction because we want to use
      re-generate table
    */
3553
    if (thd->locked_tables || thd->active_transaction())
3554
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3555 3556
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
3557 3558
      goto error;
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3559

monty@mysql.com's avatar
monty@mysql.com committed
3560
    res= mysql_truncate(thd, first_table, 0);
3561
    break;
3562
  case SQLCOM_DELETE:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3563
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3564 3565
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if ((res= delete_precheck(thd, all_tables)))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3566
      break;
3567 3568
    DBUG_ASSERT(select_lex->offset_limit == 0);
    unit->set_limit(select_lex);
3569 3570 3571 3572 3573 3574 3575 3576

    if (!thd->locked_tables &&
        !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
    {
      res= 1;
      break;
    }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3577
    res = mysql_delete(thd, all_tables, select_lex->where,
3578
                       &select_lex->order_list,
osku@127.(none)'s avatar
osku@127.(none) committed
3579 3580
                       unit->select_limit_cnt, select_lex->options,
                       FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3581 3582
    break;
  }
3583
  case SQLCOM_DELETE_MULTI:
3584
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3585
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3586
    TABLE_LIST *aux_tables=
3587
      (TABLE_LIST *)thd->lex->auxiliary_table_list.first;
3588
    multi_delete *result;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3589

3590 3591 3592 3593 3594 3595 3596
    if (!thd->locked_tables &&
        !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
    {
      res= 1;
      break;
    }

3597
    if ((res= multi_delete_precheck(thd, all_tables)))
3598
      break;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3599

3600
    /* condition will be TRUE on SP re-excuting */
3601 3602
    if (select_lex->item_list.elements != 0)
      select_lex->item_list.empty();
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3603
    if (add_item_to_list(thd, new Item_null()))
3604
      goto error;
3605

3606
    thd->proc_info="init";
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3607 3608 3609 3610
    if ((res= open_and_lock_tables(thd, all_tables)))
      break;

    if ((res= mysql_multi_delete_prepare(thd)))
3611
      goto error;
3612

3613
    if (!thd->is_fatal_error && (result= new multi_delete(aux_tables,
3614
							  lex->table_count)))
3615
    {
3616 3617 3618
      res= mysql_select(thd, &select_lex->ref_pointer_array,
			select_lex->get_table_list(),
			select_lex->with_wild,
3619
			select_lex->item_list,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3620
			select_lex->where,
3621
			0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3622 3623
			(ORDER *)NULL,
			select_lex->options | thd->options |
3624 3625
			SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
                        OPTION_SETUP_TABLES_DONE,
3626
			result, unit, select_lex);
3627
      delete result;
3628 3629
    }
    else
3630
      res= TRUE;                                // Error
3631 3632
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3633
  case SQLCOM_DROP_TABLE:
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3634
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3635
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3636 3637
    if (!lex->drop_temporary)
    {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3638
      if (check_table_access(thd, DROP_ACL, all_tables, 0))
3639 3640
	goto error;				/* purecov: inspected */
      if (end_active_trans(thd))
3641
        goto error;
3642
    }
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3643
    else
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3644 3645 3646 3647 3648 3649
    {
      /*
	If this is a slave thread, we may sometimes execute some 
	DROP / * 40005 TEMPORARY * / TABLE
	that come from parts of binlogs (likely if we use RESET SLAVE or CHANGE
	MASTER TO), while the temporary table has already been dropped.
monty@mysql.com's avatar
monty@mysql.com committed
3650 3651
	To not generate such irrelevant "table does not exist errors",
	we silently add IF EXISTS if TEMPORARY was used.
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3652 3653 3654
      */
      if (thd->slave_thread)
	lex->drop_if_exists= 1;
3655

3656
      /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
3657
      thd->options|= OPTION_KEEP_LOG;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3658
    }
3659
    /* DDL and binlog write order protected by LOCK_open */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3660 3661
    res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
			lex->drop_temporary);
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3662 3663
  }
  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3664
  case SQLCOM_DROP_INDEX:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3665 3666
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_one_table_access(thd, INDEX_ACL, all_tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3667
      goto error;				/* purecov: inspected */
3668
    if (end_active_trans(thd))
3669
      goto error;
3670
    res= mysql_drop_index(thd, first_table, &lex->alter_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3671 3672
    break;
  case SQLCOM_SHOW_PROCESSLIST:
3673 3674
    if (!thd->security_ctx->priv_user[0] &&
        check_global_access(thd,PROCESS_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3675
      break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3676
    mysqld_list_processes(thd,
3677 3678 3679 3680
			  (thd->security_ctx->master_access & PROCESS_ACL ?
                           NullS :
                           thd->security_ctx->priv_user),
                          lex->verbose);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3681
    break;
3682 3683
  case SQLCOM_SHOW_STORAGE_ENGINES:
    res= mysqld_show_storage_engines(thd);
3684
    break;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3685 3686 3687
  case SQLCOM_SHOW_AUTHORS:
    res= mysqld_show_authors(thd);
    break;
3688 3689 3690
  case SQLCOM_SHOW_CONTRIBUTORS:
    res= mysqld_show_contributors(thd);
    break;
3691 3692 3693 3694 3695 3696
  case SQLCOM_SHOW_PRIVILEGES:
    res= mysqld_show_privileges(thd);
    break;
  case SQLCOM_SHOW_COLUMN_TYPES:
    res= mysqld_show_column_types(thd);
    break;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3697
  case SQLCOM_SHOW_ENGINE_LOGS:
tim@cane.mysql.fi's avatar
tim@cane.mysql.fi committed
3698
#ifdef DONT_ALLOW_SHOW_COMMANDS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3699 3700
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0));	/* purecov: inspected */
3701
    goto error;
tim@cane.mysql.fi's avatar
tim@cane.mysql.fi committed
3702 3703
#else
    {
3704
      if (grant_option && check_access(thd, FILE_ACL, any_db,0,0,0,0))
tim@cane.mysql.fi's avatar
tim@cane.mysql.fi committed
3705
	goto error;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3706
      res= ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_LOGS);
tim@cane.mysql.fi's avatar
tim@cane.mysql.fi committed
3707 3708
      break;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3709 3710
#endif
  case SQLCOM_CHANGE_DB:
3711 3712
    if (!mysql_change_db(thd,select_lex->db,FALSE))
      send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3713
    break;
3714

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3715 3716
  case SQLCOM_LOAD:
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3717
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3718
    uint privilege= (lex->duplicates == DUP_REPLACE ?
3719 3720
		     INSERT_ACL | DELETE_ACL : INSERT_ACL) |
                    (lex->local_file ? 0 : FILE_ACL);
3721

3722
    if (lex->local_file)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3723
    {
3724
      if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) ||
3725
          !opt_local_infile)
3726
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3727
	my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), MYF(0));
3728 3729
	goto error;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3730
    }
3731 3732 3733 3734

    if (check_one_table_access(thd, privilege, all_tables))
      goto error;

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3735
    res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
3736
                    lex->update_list, lex->value_list, lex->duplicates,
3737
                    lex->ignore, (bool) lex->local_file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3738 3739
    break;
  }
3740

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3741
  case SQLCOM_SET_OPTION:
3742 3743
  {
    List<set_var_base> *lex_var_list= &lex->var_list;
3744
    if ((check_table_access(thd, SELECT_ACL, all_tables, 0) ||
3745 3746
	 open_and_lock_tables(thd, all_tables)))
      goto error;
3747 3748
    if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
    {
3749 3750
      my_error(ER_RESERVED_SYNTAX, MYF(0), "SET ONE_SHOT");
      goto error;
3751 3752 3753 3754 3755 3756 3757 3758
    }
    if (!(res= sql_set_variables(thd, lex_var_list)))
    {
      /*
        If the previous command was a SET ONE_SHOT, we don't want to forget
        about the ONE_SHOT property of that SET. So we use a |= instead of = .
      */
      thd->one_shot_set|= lex->one_shot_set;
3759
      send_ok(thd);
3760
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3761
    break;
3762
  }
3763

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3764
  case SQLCOM_UNLOCK_TABLES:
3765 3766 3767 3768 3769 3770
    /*
      It is critical for mysqldump --single-transaction --master-data that
      UNLOCK TABLES does not implicitely commit a connection which has only
      done FLUSH TABLES WITH READ LOCK + BEGIN. If this assumption becomes
      false, mysqldump will not work.
    */
3771
    unlock_locked_tables(thd);
3772 3773
    if (thd->options & OPTION_TABLE_LOCK)
    {
3774
      end_active_trans(thd);
3775
      thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3776 3777
    }
    if (thd->global_read_lock)
3778
      unlock_global_read_lock(thd);
3779
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3780 3781
    break;
  case SQLCOM_LOCK_TABLES:
3782
    unlock_locked_tables(thd);
3783
    if (end_active_trans(thd))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3784
      goto error;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3785
    if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0))
3786
      goto error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3787
    thd->in_lock_tables=1;
3788
    thd->options|= OPTION_TABLE_LOCK;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3789

3790
    if (!(res= simple_open_n_lock_tables(thd, all_tables)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3791
    {
3792 3793
#ifdef HAVE_QUERY_CACHE
      if (thd->variables.query_cache_wlock_invalidate)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3794
	query_cache.invalidate_locked_for_write(first_table);
3795
#endif /*HAVE_QUERY_CACHE*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3796 3797
      thd->locked_tables=thd->lock;
      thd->lock=0;
3798
      send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3799
    }
3800 3801
    else
      thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3802 3803 3804
    thd->in_lock_tables=0;
    break;
  case SQLCOM_CREATE_DB:
3805
  {
3806 3807 3808 3809 3810
    if (end_active_trans(thd))
    {
      res= -1;
      break;
    }
3811
    char *alias;
3812 3813
    if (!(alias=thd->strmake(lex->name.str, lex->name.length)) ||
        check_db_name(&lex->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3814
    {
3815
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3816 3817
      break;
    }
3818 3819 3820
    /*
      If in a slave thread :
      CREATE DATABASE DB was certainly not preceded by USE DB.
3821
      For that reason, db_ok() in sql/slave.cc did not check the
3822 3823 3824
      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.
    */
3825
#ifdef HAVE_REPLICATION
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3826
    if (thd->slave_thread && 
3827 3828
	(!rpl_filter->db_ok(lex->name.str) ||
	 !rpl_filter->db_ok_with_wild_table(lex->name.str)))
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3829
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3830
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
3831
      break;
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3832
    }
3833
#endif
3834 3835
    if (check_access(thd,CREATE_ACL,lex->name.str, 0, 1, 0,
                     is_schema_db(lex->name.str)))
3836
      break;
3837 3838
    res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias :
                              lex->name.str), &lex->create_info, 0);
3839 3840
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3841
  case SQLCOM_DROP_DB:
3842
  {
3843 3844 3845 3846 3847
    if (end_active_trans(thd))
    {
      res= -1;
      break;
    }
3848
    if (check_db_name(&lex->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3849
    {
3850
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3851 3852
      break;
    }
3853 3854 3855 3856 3857 3858 3859
    /*
      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.
    */
3860
#ifdef HAVE_REPLICATION
3861
    if (thd->slave_thread && 
3862 3863
	(!rpl_filter->db_ok(lex->name.str) ||
	 !rpl_filter->db_ok_with_wild_table(lex->name.str)))
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3864
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3865
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
3866
      break;
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3867
    }
3868
#endif
3869 3870
    if (check_access(thd,DROP_ACL,lex->name.str,0,1,0,
                     is_schema_db(lex->name.str)))
3871
      break;
3872 3873
    if (thd->locked_tables || thd->active_transaction())
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3874 3875
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
3876 3877
      goto error;
    }
3878
    res= mysql_rm_db(thd, lex->name.str, lex->drop_if_exists, 0);
3879 3880
    break;
  }
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903
  case SQLCOM_RENAME_DB:
  {
    LEX_STRING *olddb, *newdb;
    List_iterator <LEX_STRING> db_list(lex->db_list);
    olddb= db_list++;
    newdb= db_list++;
    if (end_active_trans(thd))
    {
      res= 1;
      break;
    }
#ifdef HAVE_REPLICATION
    if (thd->slave_thread && 
       (!rpl_filter->db_ok(olddb->str) ||
        !rpl_filter->db_ok(newdb->str) ||
        !rpl_filter->db_ok_with_wild_table(olddb->str) ||
        !rpl_filter->db_ok_with_wild_table(newdb->str)))
    {
      res= 1;
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
      break;
    }
#endif
3904 3905 3906 3907 3908
    if (check_db_name(newdb))
    {
      my_error(ER_WRONG_DB_NAME, MYF(0), newdb->str);
      break;
    }
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927
    if (check_access(thd,ALTER_ACL,olddb->str,0,1,0,is_schema_db(olddb->str)) ||
        check_access(thd,DROP_ACL,olddb->str,0,1,0,is_schema_db(olddb->str)) ||
        check_access(thd,CREATE_ACL,newdb->str,0,1,0,is_schema_db(newdb->str)))
    {
      res= 1;
      break;
    }
    if (thd->locked_tables || thd->active_transaction())
    {
      res= 1;
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
      goto error;
    }
    res= mysql_rename_db(thd, olddb, newdb);
    if (!res)
      send_ok(thd);
    break;
  }
3928 3929
  case SQLCOM_ALTER_DB:
  {
3930 3931
    LEX_STRING *db= &lex->name;
    if (check_db_name(db))
3932
    {
3933
      my_error(ER_WRONG_DB_NAME, MYF(0), db->str);
3934 3935
      break;
    }
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3936 3937 3938
    /*
      If in a slave thread :
      ALTER DATABASE DB may not be preceded by USE DB.
3939
      For that reason, maybe db_ok() in sql/slave.cc did not check the
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3940 3941 3942 3943
      do_db/ignore_db. And as this query involves no tables, tables_ok()
      above was not called. So we have to check rules again here.
    */
#ifdef HAVE_REPLICATION
3944
    if (thd->slave_thread &&
3945 3946
	(!rpl_filter->db_ok(db->str) ||
	 !rpl_filter->db_ok_with_wild_table(db->str)))
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3947
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3948
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3949 3950 3951
      break;
    }
#endif
3952
    if (check_access(thd, ALTER_ACL, db->str, 0, 1, 0, is_schema_db(db->str)))
3953 3954 3955
      break;
    if (thd->locked_tables || thd->active_transaction())
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3956 3957
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
3958 3959
      goto error;
    }
3960
    res= mysql_alter_db(thd, db->str, &lex->create_info);
3961 3962
    break;
  }
3963 3964
  case SQLCOM_SHOW_CREATE_DB:
  {
3965
    if (check_db_name(&lex->name))
3966
    {
3967
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
3968 3969
      break;
    }
3970
    res= mysqld_show_create_db(thd, lex->name.str, &lex->create_info);
3971 3972
    break;
  }
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3973 3974
  case SQLCOM_CREATE_EVENT:
  case SQLCOM_ALTER_EVENT:
3975
  do
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
3976
  {
3977
    DBUG_ASSERT(lex->event_parse_data);
3978 3979 3980 3981 3982 3983
    if (lex->table_or_sp_used())
    {
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored "
               "function calls as part of this statement");
      break;
    }
3984 3985
    switch (lex->sql_command) {
    case SQLCOM_CREATE_EVENT:
3986 3987
      res= Events::get_instance()->
            create_event(thd, lex->event_parse_data,
3988
                         lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS);
3989 3990
      break;
    case SQLCOM_ALTER_EVENT:
3991 3992
      res= Events::get_instance()->update_event(thd, lex->event_parse_data,
                                                lex->spname);
3993
      break;
3994 3995
    default:
      DBUG_ASSERT(0);
3996
    }
3997
    DBUG_PRINT("info",("DDL error code=%d", res));
3998
    if (!res)
3999
      send_ok(thd);
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
4000

4001 4002 4003 4004 4005 4006
  } while (0);
  /* Don't do it, if we are inside a SP */
  if (!thd->spcont)
  {
    delete lex->sphead;
    lex->sphead= NULL;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
4007
  }
4008 4009
  /* lex->unit.cleanup() is called outside, no need to call it here */
  break;
4010
  case SQLCOM_DROP_EVENT:
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
4011 4012 4013 4014 4015 4016
  case SQLCOM_SHOW_CREATE_EVENT:
  {
    DBUG_ASSERT(lex->spname);
    if (! lex->spname->m_db.str)
    {
      my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
4017
      goto error;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
4018 4019 4020 4021 4022 4023 4024 4025
    }
    if (check_access(thd, EVENT_ACL, lex->spname->m_db.str, 0, 0, 0,
                     is_schema_db(lex->spname->m_db.str)))
      break;

    if (lex->spname->m_name.length > NAME_LEN)
    {
      my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
4026
      /* this jumps to the end of the function and skips own messaging */
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
4027 4028
      goto error;
    }
4029 4030

    if (lex->sql_command == SQLCOM_SHOW_CREATE_EVENT)
4031 4032
      res= Events::get_instance()->show_create_event(thd, lex->spname->m_db,
                                                     lex->spname->m_name);
4033 4034
    else
    {
4035
      uint affected= 1;
4036 4037 4038 4039 4040
      if (!(res= Events::get_instance()->drop_event(thd,
                                                    lex->spname->m_db,
                                                    lex->spname->m_name,
                                                    lex->drop_if_exists,
                                                    FALSE)))
4041
        send_ok(thd);
4042
    }
4043 4044
    break;
  }
monty@mysql.com's avatar
monty@mysql.com committed
4045
  case SQLCOM_CREATE_FUNCTION:                  // UDF function
monty@mysql.com's avatar
monty@mysql.com committed
4046
  {
4047
    if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0))
monty@mysql.com's avatar
monty@mysql.com committed
4048
      break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4049
#ifdef HAVE_DLOPEN
4050 4051
    if (sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
                        &thd->sp_func_cache, FALSE))
monty@mysql.com's avatar
monty@mysql.com committed
4052
    {
4053
      my_error(ER_UDF_EXISTS, MYF(0), lex->spname->m_name.str);
monty@mysql.com's avatar
monty@mysql.com committed
4054 4055
      goto error;
    }
4056
    if (!(res = mysql_create_function(thd, &lex->udf)))
monty@mysql.com's avatar
monty@mysql.com committed
4057
      send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4058
#else
hf@deer.(none)'s avatar
hf@deer.(none) committed
4059
    my_error(ER_CANT_OPEN_LIBRARY, MYF(0), lex->udf.dl, 0, "feature disabled");
4060
    res= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4061 4062
#endif
    break;
monty@mysql.com's avatar
monty@mysql.com committed
4063
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
4064
#ifndef NO_EMBEDDED_ACCESS_CHECKS
4065 4066
  case SQLCOM_CREATE_USER:
  {
4067
    if (check_access(thd, INSERT_ACL, "mysql", 0, 1, 1, 0) &&
4068
        check_global_access(thd,CREATE_USER_ACL))
4069
      break;
4070 4071
    if (end_active_trans(thd))
      goto error;
4072
    /* Conditionally writes to binlog */
4073 4074 4075 4076
    if (!(res= mysql_create_user(thd, lex->users_list)))
      send_ok(thd);
    break;
  }
4077 4078
  case SQLCOM_DROP_USER:
  {
4079
    if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 1, 0) &&
4080
        check_global_access(thd,CREATE_USER_ACL))
4081
      break;
4082 4083
    if (end_active_trans(thd))
      goto error;
4084
    /* Conditionally writes to binlog */
4085
    if (!(res= mysql_drop_user(thd, lex->users_list)))
4086 4087 4088 4089 4090
      send_ok(thd);
    break;
  }
  case SQLCOM_RENAME_USER:
  {
4091
    if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) &&
4092
        check_global_access(thd,CREATE_USER_ACL))
4093
      break;
4094 4095
    if (end_active_trans(thd))
      goto error;
4096
    /* Conditionally writes to binlog */
4097
    if (!(res= mysql_rename_user(thd, lex->users_list)))
4098 4099 4100 4101 4102
      send_ok(thd);
    break;
  }
  case SQLCOM_REVOKE_ALL:
  {
4103
    if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) &&
4104
        check_global_access(thd,CREATE_USER_ACL))
4105
      break;
4106
    /* Conditionally writes to binlog */
4107 4108 4109 4110
    if (!(res = mysql_revoke_all(thd, lex->users_list)))
      send_ok(thd);
    break;
  }
4111 4112 4113 4114
  case SQLCOM_REVOKE:
  case SQLCOM_GRANT:
  {
    if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
serg@serg.mylan's avatar
serg@serg.mylan committed
4115
		     first_table ?  first_table->db : select_lex->db,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4116
		     first_table ? &first_table->grant.privilege : 0,
4117 4118 4119
		     first_table ? 0 : 1, 0,
                     first_table ? (bool) first_table->schema_table :
                     select_lex->db ? is_schema_db(select_lex->db) : 0))
4120 4121
      goto error;

4122
    if (thd->security_ctx->user)              // If not replication
4123
    {
4124
      LEX_USER *user, *tmp_user;
4125

4126
      List_iterator <LEX_USER> user_list(lex->users_list);
4127
      while ((tmp_user= user_list++))
4128
      {
4129 4130
        if (!(user= get_current_user(thd, tmp_user)))
          goto error;
4131 4132 4133 4134 4135 4136 4137 4138
        if (specialflag & SPECIAL_NO_RESOLVE &&
            hostname_requires_resolving(user->host.str))
          push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                              ER_WARN_HOSTNAME_WONT_WORK,
                              ER(ER_WARN_HOSTNAME_WONT_WORK),
                              user->host.str);
        // Are we trying to change a password of another user
        DBUG_ASSERT(user->host.str != 0);
4139
        if (strcmp(thd->security_ctx->user, user->user.str) ||
4140
            my_strcasecmp(system_charset_info,
4141
                          user->host.str, thd->security_ctx->host_or_ip))
4142 4143
        {
          // TODO: use check_change_password()
4144 4145
          if (is_acl_user(user->host.str, user->user.str) &&
              user->password.str &&
4146
              check_access(thd, UPDATE_ACL,"mysql",0,1,1,0))
4147 4148 4149 4150 4151 4152
          {
            my_message(ER_PASSWORD_NOT_ALLOWED,
                       ER(ER_PASSWORD_NOT_ALLOWED), MYF(0));
            goto error;
          }
        }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
4153 4154
      }
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4155
    if (first_table)
4156
    {
4157 4158
      if (lex->type == TYPE_ENUM_PROCEDURE ||
          lex->type == TYPE_ENUM_FUNCTION)
4159 4160 4161 4162 4163
      {
        uint grants= lex->all_privileges 
		   ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL)
		   : lex->grant;
        if (grant_option && 
4164 4165
	    check_grant_routine(thd, grants | GRANT_ACL, all_tables,
                                lex->type == TYPE_ENUM_PROCEDURE, 0))
4166
	  goto error;
4167
        /* Conditionally writes to binlog */
4168 4169 4170 4171
        res= mysql_routine_grant(thd, all_tables,
                                 lex->type == TYPE_ENUM_PROCEDURE, 
                                 lex->users_list, grants,
                                 lex->sql_command == SQLCOM_REVOKE, 0);
4172 4173 4174 4175 4176 4177 4178 4179
      }
      else
      {
	if (grant_option && check_grant(thd,
					(lex->grant | lex->grant_tot_col |
					 GRANT_ACL),
					all_tables, 0, UINT_MAX, 0))
	  goto error;
4180
        /* Conditionally writes to binlog */
4181 4182 4183 4184
        res= mysql_table_grant(thd, all_tables, lex->users_list,
			       lex->columns, lex->grant,
			       lex->sql_command == SQLCOM_REVOKE);
      }
4185 4186 4187
    }
    else
    {
4188
      if (lex->columns.elements || lex->type)
4189
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4190 4191
	my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
                   MYF(0));
4192
        goto error;
4193 4194
      }
      else
4195
	/* Conditionally writes to binlog */
4196 4197 4198 4199
	res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
			  lex->sql_command == SQLCOM_REVOKE);
      if (!res)
      {
4200
	if (lex->sql_command == SQLCOM_GRANT)
4201
	{
4202
	  List_iterator <LEX_USER> str_list(lex->users_list);
4203 4204 4205 4206 4207
	  LEX_USER *user, *tmp_user;
	  while ((tmp_user=str_list++))
          {
            if (!(user= get_current_user(thd, tmp_user)))
              goto error;
monty@mysql.com's avatar
monty@mysql.com committed
4208
	    reset_mqh(user);
4209
          }
4210
	}
4211 4212 4213 4214
      }
    }
    break;
  }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
4215
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4216
  case SQLCOM_RESET:
4217 4218 4219
    /*
      RESET commands are never written to the binary log, so we have to
      initialize this variable because RESET shares the same code as FLUSH
4220 4221 4222 4223
    */
    lex->no_write_to_binlog= 1;
  case SQLCOM_FLUSH:
  {
4224
    bool write_to_binlog;
4225
    if (check_global_access(thd,RELOAD_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4226
      goto error;
4227 4228 4229 4230
    /*
      reload_acl_and_cache() will tell us if we are allowed to write to the
      binlog or not.
    */
4231
    if (!reload_acl_and_cache(thd, lex->type, first_table, &write_to_binlog))
4232 4233 4234 4235 4236
    {
      /*
        We WANT to write and we CAN write.
        ! we write after unlocking the table.
      */
4237
      /* Presumably, RESET and binlog writing doesn't require synchronization */
4238 4239 4240 4241
      if (!lex->no_write_to_binlog && write_to_binlog)
      {
        if (mysql_bin_log.is_open())
        {
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
4242 4243
          thd->binlog_query(THD::STMT_QUERY_TYPE,
                            thd->query, thd->query_length, 0, FALSE);
4244 4245 4246 4247
        }
      }
      send_ok(thd);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4248
    break;
4249
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4250
  case SQLCOM_KILL:
4251 4252 4253
  {
    Item *it= (Item *)lex->value_list.head();

4254 4255 4256 4257 4258 4259 4260
    if (lex->table_or_sp_used())
    {
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored "
               "function calls as part of this statement");
      break;
    }

4261
    if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
4262 4263 4264 4265 4266
    {
      my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
		 MYF(0));
      goto error;
    }
4267
    sql_kill(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4268
    break;
4269
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
4270
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4271
  case SQLCOM_SHOW_GRANTS:
4272 4273 4274 4275
  {
    LEX_USER *grant_user= get_current_user(thd, lex->grant_user);
    if (!grant_user)
      goto error;
4276
    if ((thd->security_ctx->priv_user &&
4277
	 !strcmp(thd->security_ctx->priv_user, grant_user->user.str)) ||
4278
	!check_access(thd, SELECT_ACL, "mysql",0,1,0,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4279
    {
4280
      res = mysql_show_grants(thd, grant_user);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4281 4282
    }
    break;
4283
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
4284
#endif
4285
  case SQLCOM_HA_OPEN:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4286
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
4287
    if (check_table_access(thd, SELECT_ACL, all_tables, 0))
4288
      goto error;
monty@mysql.com's avatar
monty@mysql.com committed
4289
    res= mysql_ha_open(thd, first_table, 0);
4290 4291
    break;
  case SQLCOM_HA_CLOSE:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4292 4293
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    res= mysql_ha_close(thd, first_table);
4294 4295
    break;
  case SQLCOM_HA_READ:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4296
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
4297 4298 4299 4300 4301
    /*
      There is no need to check for table permissions here, because
      if a user has no permissions to read a table, he won't be
      able to open it (with SQLCOM_HA_OPEN) in the first place.
    */
4302
    unit->set_limit(select_lex);
4303
    res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->ident.str,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4304
                       lex->insert_list, lex->ha_rkey_mode, select_lex->where,
4305
                       unit->select_limit_cnt, unit->offset_limit_cnt);
4306 4307
    break;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4308
  case SQLCOM_BEGIN:
4309 4310 4311 4312 4313 4314
    if (thd->transaction.xid_state.xa_state != XA_NOTR)
    {
      my_error(ER_XAER_RMFAIL, MYF(0),
               xa_state_names[thd->transaction.xid_state.xa_state]);
      break;
    }
4315
    if (begin_trans(thd))
4316
      goto error;
serg@serg.mylan's avatar
serg@serg.mylan committed
4317
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4318 4319
    break;
  case SQLCOM_COMMIT:
4320
    if (end_trans(thd, lex->tx_release ? COMMIT_RELEASE :
serg@serg.mylan's avatar
serg@serg.mylan committed
4321
                              lex->tx_chain ? COMMIT_AND_CHAIN : COMMIT))
4322
      goto error;
4323
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4324 4325
    break;
  case SQLCOM_ROLLBACK:
4326
    if (end_trans(thd, lex->tx_release ? ROLLBACK_RELEASE :
serg@serg.mylan's avatar
serg@serg.mylan committed
4327
                              lex->tx_chain ? ROLLBACK_AND_CHAIN : ROLLBACK))
4328
      goto error;
4329
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4330
    break;
serg@serg.mylan's avatar
serg@serg.mylan committed
4331
  case SQLCOM_RELEASE_SAVEPOINT:
serg@serg.mylan's avatar
serg@serg.mylan committed
4332
  {
4333 4334
    SAVEPOINT *sv;
    for (sv=thd->transaction.savepoints; sv; sv=sv->prev)
serg@serg.mylan's avatar
serg@serg.mylan committed
4335 4336 4337
    {
      if (my_strnncoll(system_charset_info,
                       (uchar *)lex->ident.str, lex->ident.length,
4338
                       (uchar *)sv->name, sv->length) == 0)
serg@serg.mylan's avatar
serg@serg.mylan committed
4339 4340
        break;
    }
4341
    if (sv)
serg@serg.mylan's avatar
serg@serg.mylan committed
4342
    {
4343
      if (ha_release_savepoint(thd, sv))
serg@serg.mylan's avatar
serg@serg.mylan committed
4344
        res= TRUE; // cannot happen
serg@serg.mylan's avatar
serg@serg.mylan committed
4345 4346
      else
        send_ok(thd);
4347
      thd->transaction.savepoints=sv->prev;
serg@serg.mylan's avatar
serg@serg.mylan committed
4348
    }
4349
    else
serg@serg.mylan's avatar
serg@serg.mylan committed
4350
      my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4351
    break;
serg@serg.mylan's avatar
serg@serg.mylan committed
4352
  }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4353
  case SQLCOM_ROLLBACK_TO_SAVEPOINT:
serg@serg.mylan's avatar
serg@serg.mylan committed
4354
  {
4355 4356
    SAVEPOINT *sv;
    for (sv=thd->transaction.savepoints; sv; sv=sv->prev)
4357 4358 4359
    {
      if (my_strnncoll(system_charset_info,
                       (uchar *)lex->ident.str, lex->ident.length,
4360
                       (uchar *)sv->name, sv->length) == 0)
4361 4362
        break;
    }
4363
    if (sv)
4364
    {
4365
      if (ha_rollback_to_savepoint(thd, sv))
4366 4367
        res= TRUE; // cannot happen
      else
serg@serg.mylan's avatar
serg@serg.mylan committed
4368
      {
4369 4370
        if ((thd->options &
             (OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG)) &&
serg@serg.mylan's avatar
serg@serg.mylan committed
4371 4372 4373 4374 4375 4376
            !thd->slave_thread)
          push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                       ER_WARNING_NOT_COMPLETE_ROLLBACK,
                       ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
        send_ok(thd);
      }
4377
      thd->transaction.savepoints=sv;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4378 4379
    }
    else
4380
      my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4381
    break;
4382
  }
4383
  case SQLCOM_SAVEPOINT:
4384 4385
    if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN) ||
          thd->in_sub_stmt) || !opt_using_transactions)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4386
      send_ok(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4387
    else
4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425
    {
      SAVEPOINT **sv, *newsv;
      for (sv=&thd->transaction.savepoints; *sv; sv=&(*sv)->prev)
      {
        if (my_strnncoll(system_charset_info,
                         (uchar *)lex->ident.str, lex->ident.length,
                         (uchar *)(*sv)->name, (*sv)->length) == 0)
          break;
      }
      if (*sv) /* old savepoint of the same name exists */
      {
        newsv=*sv;
        ha_release_savepoint(thd, *sv); // it cannot fail
        *sv=(*sv)->prev;
      }
      else if ((newsv=(SAVEPOINT *) alloc_root(&thd->transaction.mem_root,
                                               savepoint_alloc_size)) == 0)
      {
        my_error(ER_OUT_OF_RESOURCES, MYF(0));
        break;
      }
      newsv->name=strmake_root(&thd->transaction.mem_root,
                               lex->ident.str, lex->ident.length);
      newsv->length=lex->ident.length;
      /*
        if we'll get an error here, don't add new savepoint to the list.
        we'll lose a little bit of memory in transaction mem_root, but it'll
        be free'd when transaction ends anyway
      */
      if (ha_savepoint(thd, newsv))
        res= TRUE;
      else
      {
        newsv->prev=thd->transaction.savepoints;
        thd->transaction.savepoints=newsv;
        send_ok(thd);
      }
    }
4426
    break;
4427 4428
  case SQLCOM_CREATE_PROCEDURE:
  case SQLCOM_CREATE_SPFUNCTION:
monty@mysql.com's avatar
monty@mysql.com committed
4429
  {
4430
    uint namelen;
4431
    char *name;
4432
    int result;
4433

4434
    DBUG_ASSERT(lex->sphead != 0);
4435
    DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */
4436

4437 4438
    if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, 0, 0, 0,
                     is_schema_db(lex->sphead->m_db.str)))
4439 4440 4441 4442 4443 4444
    {
      delete lex->sphead;
      lex->sphead= 0;
      goto error;
    }

4445
    if (end_active_trans(thd)) 
4446
    {
4447 4448 4449
      delete lex->sphead;
      lex->sphead= 0;
      goto error;
4450
    }
4451 4452

    name= lex->sphead->name(&namelen);
4453
#ifdef HAVE_DLOPEN
monty@mysql.com's avatar
monty@mysql.com committed
4454 4455 4456
    if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
    {
      udf_func *udf = find_udf(name, namelen);
4457

monty@mysql.com's avatar
monty@mysql.com committed
4458
      if (udf)
4459
      {
4460
	my_error(ER_UDF_EXISTS, MYF(0), name);
4461
	delete lex->sphead;
4462
	lex->sphead= 0;
4463
	goto error;
4464
      }
monty@mysql.com's avatar
monty@mysql.com committed
4465 4466 4467
    }
#endif

4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551
    /*
      If the definer is not specified, this means that CREATE-statement missed
      DEFINER-clause. DEFINER-clause can be missed in two cases:
      
        - The user submitted a statement w/o the clause. This is a normal
          case, we should assign CURRENT_USER as definer.

        - Our slave received an updated from the master, that does not
          replicate definer for stored rountines. We should also assign
          CURRENT_USER as definer here, but also we should mark this routine
          as NON-SUID. This is essential for the sake of backward
          compatibility.
          
          The problem is the slave thread is running under "special" user (@),
          that actually does not exist. In the older versions we do not fail
          execution of a stored routine if its definer does not exist and
          continue the execution under the authorization of the invoker
          (BUG#13198). And now if we try to switch to slave-current-user (@),
          we will fail.

          Actually, this leads to the inconsistent state of master and
          slave (different definers, different SUID behaviour), but it seems,
          this is the best we can do.
    */

    if (!lex->definer)
    {
      bool res= FALSE;
      Query_arena original_arena;
      Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena);

      if (!(lex->definer= create_default_definer(thd)))
        res= TRUE;

      if (ps_arena)
        thd->restore_active_arena(ps_arena, &original_arena);

      if (res)
      {
        /* Error has been already reported. */
        delete lex->sphead;
        lex->sphead= 0;
        goto error;
      }

      if (thd->slave_thread)
        lex->sphead->m_chistics->suid= SP_IS_NOT_SUID;
    }

    /*
      If the specified definer differs from the current user, we should check
      that the current user has SUPER privilege (in order to create a stored
      routine under another user one must have SUPER privilege).
    */
    
    else if (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
        my_strcasecmp(system_charset_info,
                      lex->definer->host.str,
                      thd->security_ctx->priv_host))
    {
      if (check_global_access(thd, SUPER_ACL))
      {
        my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
        delete lex->sphead;
        lex->sphead= 0;
        goto error;
      }
    }

    /* Check that the specified definer exists. Emit a warning if not. */

#ifndef NO_EMBEDDED_ACCESS_CHECKS
    if (!is_acl_user(lex->definer->host.str,
                     lex->definer->user.str))
    {
      push_warning_printf(thd,
                          MYSQL_ERROR::WARN_LEVEL_NOTE,
                          ER_NO_SUCH_USER,
                          ER(ER_NO_SUCH_USER),
                          lex->definer->user.str,
                          lex->definer->host.str);
    }
#endif /* NO_EMBEDDED_ACCESS_CHECKS */

4552
    res= (result= lex->sphead->create(thd));
4553 4554
    if (result == SP_OK)
    {
4555
#ifndef NO_EMBEDDED_ACCESS_CHECKS
4556
      /* only add privileges if really neccessary */
4557
      if (sp_automatic_privileges && !opt_noacl &&
4558
          check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS,
4559
      			       lex->sphead->m_db.str, name,
4560
                               lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1))
4561
      {
4562
        if (sp_grant_privileges(thd, lex->sphead->m_db.str, name,
4563
                                lex->sql_command == SQLCOM_CREATE_PROCEDURE))
4564 4565 4566
          push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
	  	       ER_PROC_AUTO_GRANT_FAIL,
		       ER(ER_PROC_AUTO_GRANT_FAIL));
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
4567
        close_thread_tables(thd);
4568
      }
4569
#endif
4570 4571 4572
      lex->unit.cleanup();
      delete lex->sphead;
      lex->sphead= 0;
4573
      send_ok(thd);
4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593
    }
    else
    {
      switch (result) {
      case SP_WRITE_ROW_FAILED:
	my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name);
	break;
      case SP_NO_DB_ERROR:
	my_error(ER_BAD_DB_ERROR, MYF(0), lex->sphead->m_db.str);
	break;
      case SP_BAD_IDENTIFIER:
	my_error(ER_TOO_LONG_IDENT, MYF(0), name);
	break;
      case SP_BODY_TOO_LONG:
	my_error(ER_TOO_LONG_BODY, MYF(0), name);
	break;
      default:
	my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name);
	break;
      }
4594
      lex->unit.cleanup();
monty@mysql.com's avatar
monty@mysql.com committed
4595 4596 4597
      delete lex->sphead;
      lex->sphead= 0;
      goto error;
4598
    }
monty@mysql.com's avatar
monty@mysql.com committed
4599 4600
    break;
  }
4601 4602 4603 4604
  case SQLCOM_CALL:
    {
      sp_head *sp;

4605 4606 4607 4608 4609 4610 4611 4612 4613
      /*
        This will cache all SP and SF and open and lock all tables
        required for execution.
      */
      if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
	  open_and_lock_tables(thd, all_tables))
       goto error;

      /*
4614 4615
        By this moment all needed SPs should be in cache so no need to look 
        into DB. 
4616
      */
4617 4618
      if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
                                &thd->sp_proc_cache, TRUE)))
4619
      {
4620
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
4621
                 lex->spname->m_qname.str);
4622
	goto error;
4623 4624 4625
      }
      else
      {
pem@mysql.comhem.se's avatar
pem@mysql.comhem.se committed
4626
	ha_rows select_limit;
monty@mysql.com's avatar
monty@mysql.com committed
4627 4628
        /* bits that should be cleared in thd->server_status */
	uint bits_to_be_cleared= 0;
4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640
        /*
          Check that the stored procedure doesn't contain Dynamic SQL
          and doesn't return result sets: such stored procedures can't
          be called from a function or trigger.
        */
        if (thd->in_sub_stmt)
        {
          const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ?
                              "trigger" : "function");
          if (sp->is_not_allowed_in_function(where))
            goto error;
        }
4641

4642 4643
	my_bool nsok= thd->net.no_send_ok;
	thd->net.no_send_ok= TRUE;
4644
	if (sp->m_flags & sp_head::MULTI_RESULTS)
4645
	{
4646
	  if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS))
4647
	  {
4648 4649 4650 4651
            /*
              The client does not support multiple result sets being sent
              back
            */
4652
	    my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str);
4653 4654 4655
	    thd->net.no_send_ok= nsok;
	    goto error;
	  }
monty@mysql.com's avatar
monty@mysql.com committed
4656 4657 4658 4659 4660 4661 4662
          /*
            If SERVER_MORE_RESULTS_EXISTS is not set,
            then remember that it should be cleared
          */
	  bits_to_be_cleared= (~thd->server_status &
                               SERVER_MORE_RESULTS_EXISTS);
	  thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
4663 4664
	}

4665
#ifndef NO_EMBEDDED_ACCESS_CHECKS
4666
	if (check_routine_access(thd, EXECUTE_ACL,
4667
				 sp->m_db.str, sp->m_name.str, TRUE, FALSE))
4668 4669 4670 4671
	{
	  thd->net.no_send_ok= nsok;
	  goto error;
	}
4672
#endif
pem@mysql.comhem.se's avatar
pem@mysql.comhem.se committed
4673 4674
	select_limit= thd->variables.select_limit;
	thd->variables.select_limit= HA_POS_ERROR;
4675

4676
        thd->row_count_func= 0;
4677 4678
        
        /* 
4679
          We never write CALL statements into binlog:
4680 4681 4682 4683 4684
           - If the mode is non-prelocked, each statement will be logged
             separately.
           - If the mode is prelocked, the invoking statement will care
             about writing into binlog.
          So just execute the statement.
4685
        */
4686
	res= sp->execute_procedure(thd, &lex->value_list);
4687 4688 4689
	/*
          If warnings have been cleared, we have to clear total_warn_count
          too, otherwise the clients get confused.
4690 4691 4692 4693
	 */
	if (thd->warn_list.is_empty())
	  thd->total_warn_count= 0;

pem@mysql.comhem.se's avatar
pem@mysql.comhem.se committed
4694
	thd->variables.select_limit= select_limit;
4695

4696
	thd->net.no_send_ok= nsok;
monty@mysql.com's avatar
monty@mysql.com committed
4697
        thd->server_status&= ~bits_to_be_cleared;
4698

4699
	if (!res)
monty@mysql.com's avatar
monty@mysql.com committed
4700 4701
	  send_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 :
                                thd->row_count_func));
4702
	else
4703 4704
        {
          DBUG_ASSERT(thd->net.report_error == 1 || thd->killed);
4705
	  goto error;		// Substatement should already have sent error
4706
        }
4707
      }
4708
      break;
4709 4710
    }
  case SQLCOM_ALTER_PROCEDURE:
4711
  case SQLCOM_ALTER_FUNCTION:
4712
    {
4713
      int result;
4714 4715 4716 4717
      sp_head *sp;
      st_sp_chistics chistics;

      memcpy(&chistics, &lex->sp_chistics, sizeof(chistics));
4718
      if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
4719 4720
        sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
                            &thd->sp_proc_cache, FALSE);
4721
      else
4722 4723
        sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
                            &thd->sp_func_cache, FALSE);
monty@mysql.com's avatar
monty@mysql.com committed
4724
      mysql_reset_errors(thd, 0);
4725
      if (! sp)
4726 4727 4728 4729 4730 4731 4732 4733 4734
      {
	if (lex->spname->m_db.str)
	  result= SP_KEY_NOT_FOUND;
	else
	{
	  my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
	  goto error;
	}
      }
4735 4736
      else
      {
4737 4738 4739
        if (check_routine_access(thd, ALTER_PROC_ACL, sp->m_db.str, 
				 sp->m_name.str,
                                 lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0))
4740
	  goto error;
4741 4742 4743

        if (end_active_trans(thd)) 
          goto error;
4744
	memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics));
4745 4746
        if ((sp->m_type == TYPE_ENUM_FUNCTION) &&
            !trust_function_creators &&  mysql_bin_log.is_open() &&
4747 4748 4749 4750 4751 4752 4753 4754 4755 4756
            !sp->m_chistics->detistic &&
            (chistics.daccess == SP_CONTAINS_SQL ||
             chistics.daccess == SP_MODIFIES_SQL_DATA))
        {
          my_message(ER_BINLOG_UNSAFE_ROUTINE,
		     ER(ER_BINLOG_UNSAFE_ROUTINE), MYF(0));
          result= SP_INTERNAL_ERROR;
        }
        else
        {
4757 4758 4759 4760 4761 4762
          /*
            Note that if you implement the capability of ALTER FUNCTION to
            alter the body of the function, this command should be made to
            follow the restrictions that log-bin-trust-function-creators=0
            already puts on CREATE FUNCTION.
          */
4763
          if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
4764
            /* Conditionally writes to binlog */
4765 4766
            result= sp_update_procedure(thd, lex->spname, &lex->sp_chistics);
          else
4767
            /* Conditionally writes to binlog */
4768 4769
            result= sp_update_function(thd, lex->spname, &lex->sp_chistics);
        }
4770
      }
4771
      switch (result)
4772
      {
4773
      case SP_OK:
4774
	send_ok(thd);
4775 4776
	break;
      case SP_KEY_NOT_FOUND:
4777 4778
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
4779 4780
	goto error;
      default:
4781 4782
	my_error(ER_SP_CANT_ALTER, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
4783
	goto error;
4784
      }
4785
      break;
4786 4787
    }
  case SQLCOM_DROP_PROCEDURE:
4788
  case SQLCOM_DROP_FUNCTION:
4789
    {
4790
      int result;
4791 4792
      int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
                 TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
4793

4794
      result= sp_routine_exists_in_table(thd, type, lex->spname);
monty@mysql.com's avatar
monty@mysql.com committed
4795
      mysql_reset_errors(thd, 0);
4796
      if (result == SP_OK)
4797
      {
4798 4799 4800
        char *db= lex->spname->m_db.str;
	char *name= lex->spname->m_name.str;

4801 4802
	if (check_routine_access(thd, ALTER_PROC_ACL, db, name,
                                 lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
4803
          goto error;
4804 4805 4806

        if (end_active_trans(thd)) 
          goto error;
4807
#ifndef NO_EMBEDDED_ACCESS_CHECKS
4808
	if (sp_automatic_privileges && !opt_noacl &&
4809 4810
	    sp_revoke_privileges(thd, db, name, 
                                 lex->sql_command == SQLCOM_DROP_PROCEDURE))
4811 4812 4813 4814 4815
	{
	  push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
		       ER_PROC_AUTO_REVOKE_FAIL,
		       ER(ER_PROC_AUTO_REVOKE_FAIL));
	}
4816
#endif
4817
	if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
4818 4819
          /* Conditionally writes to binlog */
	  result= sp_drop_procedure(thd, lex->spname); /* Conditionally writes to binlog */
4820
	else
4821 4822
          /* Conditionally writes to binlog */
	  result= sp_drop_function(thd, lex->spname); /* Conditionally writes to binlog */
4823 4824 4825
      }
      else
      {
4826
#ifdef HAVE_DLOPEN
4827 4828 4829 4830 4831 4832
	if (lex->sql_command == SQLCOM_DROP_FUNCTION)
	{
          udf_func *udf = find_udf(lex->spname->m_name.str,
                                   lex->spname->m_name.length);
          if (udf)
          {
4833
	    if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0, 0))
4834
	      goto error;
4835 4836

	    /* Does NOT write to binlog */
4837
	    if (!(res = mysql_drop_function(thd, &lex->spname->m_name)))
4838
	    {
4839 4840
	      send_ok(thd);
	      break;
4841 4842
	    }
	  }
4843
	}
4844
#endif
4845 4846 4847 4848 4849 4850 4851
	if (lex->spname->m_db.str)
	  result= SP_KEY_NOT_FOUND;
	else
	{
	  my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
	  goto error;
	}
4852
      }
4853 4854
      res= result;
      switch (result)
4855 4856
      {
      case SP_OK:
4857
	send_ok(thd);
4858 4859
	break;
      case SP_KEY_NOT_FOUND:
4860 4861
	if (lex->drop_if_exists)
	{
4862
	  push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
4863
			      ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
4864
			      SP_COM_STRING(lex), lex->spname->m_name.str);
4865
	  res= FALSE;
4866 4867 4868
	  send_ok(thd);
	  break;
	}
4869 4870
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
4871 4872
	goto error;
      default:
4873 4874
	my_error(ER_SP_DROP_FAILED, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
4875
	goto error;
4876
      }
4877
      break;
4878
    }
4879 4880
  case SQLCOM_SHOW_CREATE_PROC:
    {
4881
      if (lex->spname->m_name.length > NAME_LEN)
4882
      {
4883
	my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
4884 4885
	goto error;
      }
4886
      if (sp_show_create_procedure(thd, lex->spname) != SP_OK)
4887
      {			/* We don't distinguish between errors for now */
4888 4889
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_name.str);
4890 4891 4892 4893 4894 4895
	goto error;
      }
      break;
    }
  case SQLCOM_SHOW_CREATE_FUNC:
    {
4896
      if (lex->spname->m_name.length > NAME_LEN)
4897
      {
4898
	my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
4899 4900
	goto error;
      }
4901
      if (sp_show_create_function(thd, lex->spname) != SP_OK)
4902
      {			/* We don't distinguish between errors for now */
4903 4904
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_name.str);
4905 4906 4907 4908
	goto error;
      }
      break;
    }
4909
#ifdef NOT_USED
4910 4911
  case SQLCOM_SHOW_STATUS_PROC:
    {
4912
      res= sp_show_status_procedure(thd, (lex->wild ?
4913 4914 4915 4916 4917
					  lex->wild->ptr() : NullS));
      break;
    }
  case SQLCOM_SHOW_STATUS_FUNC:
    {
4918
      res= sp_show_status_function(thd, (lex->wild ? 
4919 4920 4921
					 lex->wild->ptr() : NullS));
      break;
    }
4922
#endif
pem@mysql.com's avatar
pem@mysql.com committed
4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934
#ifndef DBUG_OFF
  case SQLCOM_SHOW_PROC_CODE:
  case SQLCOM_SHOW_FUNC_CODE:
    {
      sp_head *sp;

      if (lex->spname->m_name.length > NAME_LEN)
      {
	my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
	goto error;
      }
      if (lex->sql_command == SQLCOM_SHOW_PROC_CODE)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4935 4936
        sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
                            &thd->sp_proc_cache, FALSE);
pem@mysql.com's avatar
pem@mysql.com committed
4937
      else
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4938 4939
        sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
                            &thd->sp_func_cache, FALSE);
4940
      if (!sp || sp->show_routine_code(thd))
4941 4942
      {
        /* We don't distinguish between errors for now */
pem@mysql.com's avatar
pem@mysql.com committed
4943 4944 4945 4946 4947 4948 4949
        my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_name.str);
        goto error;
      }
      break;
    }
#endif // ifndef DBUG_OFF
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4950 4951
  case SQLCOM_CREATE_VIEW:
    {
4952 4953 4954
      if (end_active_trans(thd))
        goto error;

4955
      res= mysql_create_view(thd, first_table, thd->lex->create_view_mode);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4956 4957 4958 4959
      break;
    }
  case SQLCOM_DROP_VIEW:
    {
4960 4961 4962
      if (check_table_access(thd, DROP_ACL, all_tables, 0) ||
          end_active_trans(thd))
        goto error;
4963 4964
      /* Conditionally writes to binlog. */
      res= mysql_drop_view(thd, first_table, thd->lex->drop_mode);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4965 4966
      break;
    }
4967 4968
  case SQLCOM_CREATE_TRIGGER:
  {
4969 4970 4971
    if (end_active_trans(thd))
      goto error;

4972
    /* Conditionally writes to binlog. */
4973 4974 4975
    res= mysql_create_or_drop_trigger(thd, all_tables, 1);

    /* We don't care about trigger body after this point */
4976 4977 4978 4979 4980 4981
    delete lex->sphead;
    lex->sphead= 0;
    break;
  }
  case SQLCOM_DROP_TRIGGER:
  {
4982 4983 4984
    if (end_active_trans(thd))
      goto error;

4985
    /* Conditionally writes to binlog. */
4986 4987 4988
    res= mysql_create_or_drop_trigger(thd, all_tables, 0);
    break;
  }
4989
  case SQLCOM_XA_START:
4990 4991
    if (thd->transaction.xid_state.xa_state == XA_IDLE &&
        thd->lex->xa_opt == XA_RESUME)
4992
    {
4993
      if (! thd->transaction.xid_state.xid.eq(thd->lex->xid))
4994 4995 4996 4997
      {
        my_error(ER_XAER_NOTA, MYF(0));
        break;
      }
4998
      thd->transaction.xid_state.xa_state=XA_ACTIVE;
4999 5000 5001
      send_ok(thd);
      break;
    }
serg@serg.mylan's avatar
serg@serg.mylan committed
5002
    if (thd->lex->xa_opt != XA_NONE)
5003 5004 5005 5006
    { // JOIN is not supported yet. TODO
      my_error(ER_XAER_INVAL, MYF(0));
      break;
    }
5007
    if (thd->transaction.xid_state.xa_state != XA_NOTR)
5008
    {
5009
      my_error(ER_XAER_RMFAIL, MYF(0),
5010
               xa_state_names[thd->transaction.xid_state.xa_state]);
5011 5012 5013 5014 5015 5016 5017
      break;
    }
    if (thd->active_transaction() || thd->locked_tables)
    {
      my_error(ER_XAER_OUTSIDE, MYF(0));
      break;
    }
5018 5019 5020 5021 5022 5023 5024 5025 5026
    if (xid_cache_search(thd->lex->xid))
    {
      my_error(ER_XAER_DUPID, MYF(0));
      break;
    }
    DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
    thd->transaction.xid_state.xa_state=XA_ACTIVE;
    thd->transaction.xid_state.xid.set(thd->lex->xid);
    xid_cache_insert(&thd->transaction.xid_state);
5027 5028
    thd->options= ((thd->options & ~(OPTION_STATUS_NO_TRANS_UPDATE |
                                     OPTION_KEEP_LOG)) |
5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039
                   OPTION_BEGIN);
    thd->server_status|= SERVER_STATUS_IN_TRANS;
    send_ok(thd);
    break;
  case SQLCOM_XA_END:
    /* fake it */
    if (thd->lex->xa_opt != XA_NONE)
    { // SUSPEND and FOR MIGRATE are not supported yet. TODO
      my_error(ER_XAER_INVAL, MYF(0));
      break;
    }
5040
    if (thd->transaction.xid_state.xa_state != XA_ACTIVE)
5041
    {
5042
      my_error(ER_XAER_RMFAIL, MYF(0),
5043
               xa_state_names[thd->transaction.xid_state.xa_state]);
5044 5045
      break;
    }
5046
    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
5047 5048 5049 5050
    {
      my_error(ER_XAER_NOTA, MYF(0));
      break;
    }
5051
    thd->transaction.xid_state.xa_state=XA_IDLE;
5052 5053 5054
    send_ok(thd);
    break;
  case SQLCOM_XA_PREPARE:
5055
    if (thd->transaction.xid_state.xa_state != XA_IDLE)
5056
    {
5057
      my_error(ER_XAER_RMFAIL, MYF(0),
5058
               xa_state_names[thd->transaction.xid_state.xa_state]);
5059 5060
      break;
    }
5061
    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
5062 5063 5064 5065 5066 5067 5068
    {
      my_error(ER_XAER_NOTA, MYF(0));
      break;
    }
    if (ha_prepare(thd))
    {
      my_error(ER_XA_RBROLLBACK, MYF(0));
5069 5070
      xid_cache_delete(&thd->transaction.xid_state);
      thd->transaction.xid_state.xa_state=XA_NOTR;
5071 5072
      break;
    }
5073
    thd->transaction.xid_state.xa_state=XA_PREPARED;
5074 5075 5076
    send_ok(thd);
    break;
  case SQLCOM_XA_COMMIT:
5077
    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
5078
    {
5079 5080
      XID_STATE *xs=xid_cache_search(thd->lex->xid);
      if (!xs || xs->in_thd)
5081
        my_error(ER_XAER_NOTA, MYF(0));
serg@serg.mylan's avatar
serg@serg.mylan committed
5082
      else
5083 5084 5085
      {
        ha_commit_or_rollback_by_xid(thd->lex->xid, 1);
        xid_cache_delete(xs);
serg@serg.mylan's avatar
serg@serg.mylan committed
5086
        send_ok(thd);
5087
      }
5088 5089
      break;
    }
5090
    if (thd->transaction.xid_state.xa_state == XA_IDLE &&
monty@mysql.com's avatar
monty@mysql.com committed
5091
        thd->lex->xa_opt == XA_ONE_PHASE)
5092
    {
5093 5094 5095
      int r;
      if ((r= ha_commit(thd)))
        my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
5096 5097 5098
      else
        send_ok(thd);
    }
5099
    else if (thd->transaction.xid_state.xa_state == XA_PREPARED &&
monty@mysql.com's avatar
monty@mysql.com committed
5100
             thd->lex->xa_opt == XA_NONE)
5101
    {
5102 5103 5104
      if (wait_if_global_read_lock(thd, 0, 0))
      {
        ha_rollback(thd);
5105
        my_error(ER_XAER_RMERR, MYF(0));
5106
      }
5107
      else
5108 5109 5110 5111 5112 5113 5114
      {
        if (ha_commit_one_phase(thd, 1))
          my_error(ER_XAER_RMERR, MYF(0));
        else
          send_ok(thd);
        start_waiting_global_read_lock(thd);
      }
5115 5116 5117
    }
    else
    {
5118
      my_error(ER_XAER_RMFAIL, MYF(0),
5119
               xa_state_names[thd->transaction.xid_state.xa_state]);
5120 5121
      break;
    }
5122 5123
    thd->options&= ~(OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE |
                     OPTION_KEEP_LOG);
5124
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
5125 5126
    xid_cache_delete(&thd->transaction.xid_state);
    thd->transaction.xid_state.xa_state=XA_NOTR;
5127 5128
    break;
  case SQLCOM_XA_ROLLBACK:
5129
    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
5130
    {
5131 5132
      XID_STATE *xs=xid_cache_search(thd->lex->xid);
      if (!xs || xs->in_thd)
5133
        my_error(ER_XAER_NOTA, MYF(0));
serg@serg.mylan's avatar
serg@serg.mylan committed
5134
      else
5135 5136 5137
      {
        ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
        xid_cache_delete(xs);
serg@serg.mylan's avatar
serg@serg.mylan committed
5138
        send_ok(thd);
5139
      }
5140 5141
      break;
    }
5142 5143
    if (thd->transaction.xid_state.xa_state != XA_IDLE &&
        thd->transaction.xid_state.xa_state != XA_PREPARED)
5144
    {
5145
      my_error(ER_XAER_RMFAIL, MYF(0),
5146
               xa_state_names[thd->transaction.xid_state.xa_state]);
5147 5148 5149 5150 5151 5152
      break;
    }
    if (ha_rollback(thd))
      my_error(ER_XAER_RMERR, MYF(0));
    else
      send_ok(thd);
5153 5154
    thd->options&= ~(OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE |
                     OPTION_KEEP_LOG);
5155
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
5156 5157
    xid_cache_delete(&thd->transaction.xid_state);
    thd->transaction.xid_state.xa_state=XA_NOTR;
5158 5159
    break;
  case SQLCOM_XA_RECOVER:
5160
    res= mysql_xa_recover(thd);
5161
    break;
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185
  case SQLCOM_ALTER_TABLESPACE:
    if (check_access(thd, ALTER_ACL, thd->db, 0, 1, 0, thd->db ? is_schema_db(thd->db) : 0))
      break;
    if (!(res= mysql_alter_tablespace(thd, lex->alter_tablespace_info)))
      send_ok(thd);
    break;
  case SQLCOM_INSTALL_PLUGIN:
    if (! (res= mysql_install_plugin(thd, &thd->lex->comment,
                                     &thd->lex->ident)))
      send_ok(thd);
    break;
  case SQLCOM_UNINSTALL_PLUGIN:
    if (! (res= mysql_uninstall_plugin(thd, &thd->lex->comment)))
      send_ok(thd);
    break;
  case SQLCOM_BINLOG_BASE64_EVENT:
  {
#ifndef EMBEDDED_LIBRARY
    mysql_client_binlog_statement(thd);
#else /* EMBEDDED_LIBRARY */
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "embedded");
#endif /* EMBEDDED_LIBRARY */
    break;
  }
patg@radha.tangent.org's avatar
patg@radha.tangent.org committed
5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237
  case SQLCOM_CREATE_SERVER:
  {
    int error;
    LEX *lex= thd->lex;
    DBUG_PRINT("info", ("case SQLCOM_CREATE_SERVER"));
    if ((error= create_server(thd, &lex->server_options)))
    {
      DBUG_PRINT("info", ("problem creating server",
                          lex->server_options.server_name));
      my_error(error, MYF(0), lex->server_options.server_name);
      break;
    }
    send_ok(thd, 1);
    break;
  }
  case SQLCOM_ALTER_SERVER:
  {
    int error;
    LEX *lex= thd->lex;
    DBUG_PRINT("info", ("case SQLCOM_ALTER_SERVER"));
    if ((error= alter_server(thd, &lex->server_options)))
    {
      DBUG_PRINT("info", ("problem altering server",
                          lex->server_options.server_name));
      my_error(error, MYF(0), lex->server_options.server_name);
      break;
    }
    send_ok(thd, 1);
    break;
  }
  case SQLCOM_DROP_SERVER:
  {
    int err_code;
    LEX *lex= thd->lex;
    DBUG_PRINT("info", ("case SQLCOM_DROP_SERVER"));
    if ((err_code= drop_server(thd, &lex->server_options)))
    {
      if (! lex->drop_if_exists && err_code == ER_FOREIGN_SERVER_EXISTS)
      {
        DBUG_PRINT("info", ("problem dropping server %s",
                            lex->server_options.server_name));
        my_error(err_code, MYF(0), lex->server_options.server_name);
      }
      else
      {
        send_ok(thd, 0);
      }
      break;
    }
    send_ok(thd, 1);
    break;
  }
5238
  default:
5239
#ifndef EMBEDDED_LIBRARY
5240
    DBUG_ASSERT(0);                             /* Impossible */
5241
#endif
5242
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5243 5244
    break;
  }
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
5245 5246

end:
monty@mysql.com's avatar
monty@mysql.com committed
5247
  thd->proc_info="query end";
5248 5249

  /*
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
5250
    Binlog-related cleanup:
5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261
    Reset system variables temporarily modified by SET ONE SHOT.

    Exception: If this is a SET, do nothing. This is to allow
    mysqlbinlog to print many SET commands (in this case we want the
    charset temp setting to live until the real query). This is also
    needed so that SET CHARACTER_SET_CLIENT... does not cancel itself
    immediately.
  */
  if (thd->one_shot_set && lex->sql_command != SQLCOM_SET_OPTION)
    reset_one_shot_variables(thd);

5262
  /*
5263 5264
    The return value for ROW_COUNT() is "implementation dependent" if the
    statement is not DELETE, INSERT or UPDATE, but -1 is what JDBC and ODBC
5265 5266 5267 5268
    wants. We also keep the last value in case of SQLCOM_CALL or
    SQLCOM_EXECUTE.
  */
  if (!(sql_command_flags[lex->sql_command] & CF_HAS_ROW_COUNT))
5269
    thd->row_count_func= -1;
5270

5271
  goto finish;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5272 5273

error:
5274 5275
  res= TRUE;

5276
finish:
5277 5278 5279 5280 5281 5282 5283 5284 5285
  if (need_start_waiting)
  {
    /*
      Release the protection against the global read lock and wake
      everyone, who might want to set a global read lock.
    */
    start_waiting_global_read_lock(thd);
  }
  DBUG_RETURN(res || thd->net.report_error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5286 5287 5288
}


5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
{
  LEX	*lex= thd->lex;
  select_result *result=lex->result;
  bool res;
  /* assign global limit variable if limit is not given */
  {
    SELECT_LEX *param= lex->unit.global_parameters;
    if (!param->explicit_limit)
      param->select_limit=
        new Item_int((ulonglong) thd->variables.select_limit);
  }
  if (!(res= open_and_lock_tables(thd, all_tables)))
  {
    if (lex->describe)
    {
      /*
        We always use select_send for EXPLAIN, even if it's an EXPLAIN
        for SELECT ... INTO OUTFILE: a user application should be able
        to prepend EXPLAIN to any query and receive output for it,
        even if the query itself redirects the output.
      */
      if (!(result= new select_send()))
5312
        return 1;                               /* purecov: inspected */
5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330
      thd->send_explain_fields(result);
      res= mysql_explain_union(thd, &thd->lex->unit, result);
      if (lex->describe & DESCRIBE_EXTENDED)
      {
        char buff[1024];
        String str(buff,(uint32) sizeof(buff), system_charset_info);
        str.length(0);
        thd->lex->unit.print(&str);
        str.append('\0');
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                     ER_YES, str.ptr());
      }
      result->send_eof();
      delete result;
    }
    else
    {
      if (!result && !(result= new select_send()))
5331
        return 1;                               /* purecov: inspected */
5332 5333 5334 5335 5336 5337 5338 5339 5340 5341
      query_cache_store_query(thd, all_tables);
      res= handle_select(thd, lex, result, 0);
      if (result != lex->result)
        delete result;
    }
  }
  return res;
}


bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5342
/*
5343
  Check grants for commands which work only with one table.
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5344

5345
  SYNOPSIS
5346
    check_single_table_access()
monty@mysql.com's avatar
monty@mysql.com committed
5347
    thd			Thread handler
5348
    privilege		requested privilege
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
5349
    all_tables		global table list of query
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5350 5351 5352

  RETURN
    0 - OK
monty@mysql.com's avatar
monty@mysql.com committed
5353
    1 - access denied, error is sent to client
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5354 5355
*/

5356 5357
bool check_single_table_access(THD *thd, ulong privilege, 
                               TABLE_LIST *all_tables)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5358
{
5359 5360 5361 5362 5363 5364
  Security_context * backup_ctx= thd->security_ctx;

  /* we need to switch to the saved context (if any) */
  if (all_tables->security_ctx)
    thd->security_ctx= all_tables->security_ctx;

5365 5366 5367 5368 5369 5370 5371 5372
  const char *db_name;
  if ((all_tables->view || all_tables->field_translation) &&
      !all_tables->schema_table)
    db_name= all_tables->view_db.str;
  else
    db_name= all_tables->db;

  if (check_access(thd, privilege, db_name,
5373 5374
		   &all_tables->grant.privilege, 0, 0,
                   test(all_tables->schema_table)))
5375
    goto deny;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5376

5377
  /* Show only 1 table for check_grant */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
5378
  if (grant_option && check_grant(thd, privilege, all_tables, 0, 1, 0))
5379 5380 5381
    goto deny;

  thd->security_ctx= backup_ctx;
5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407
  return 0;

deny:
  thd->security_ctx= backup_ctx;
  return 1;
}

/*
  Check grants for commands which work only with one table and all other
  tables belonging to subselects or implicitly opened tables.

  SYNOPSIS
    check_one_table_access()
    thd			Thread handler
    privilege		requested privilege
    all_tables		global table list of query

  RETURN
    0 - OK
    1 - access denied, error is sent to client
*/

bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
{
  if (check_single_table_access (thd,privilege,all_tables))
    return 1;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5408

5409
  /* Check rights on tables of subselects and implictly opened tables */
5410
  TABLE_LIST *subselects_tables, *view= all_tables->view ? all_tables : 0;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
5411
  if ((subselects_tables= all_tables->next_global))
monty@mysql.com's avatar
monty@mysql.com committed
5412
  {
5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424
    /*
      Access rights asked for the first table of a view should be the same
      as for the view
    */
    if (view && subselects_tables->belong_to_view == view)
    {
      if (check_single_table_access (thd, privilege, subselects_tables))
        return 1;
      subselects_tables= subselects_tables->next_global;
    }
    if (subselects_tables &&
        (check_table_access(thd, SELECT_ACL, subselects_tables, 0)))
5425
      return 1;
monty@mysql.com's avatar
monty@mysql.com committed
5426 5427
  }
  return 0;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5428 5429 5430
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
5431
/****************************************************************************
5432
  Get the user (global) and database privileges for all used tables
5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445

  NOTES
    The idea of EXTRA_ACL is that one will be granted access to the table if
    one has the asked privilege on any column combination of the table; For
    example to be able to check a table one needs to have SELECT privilege on
    any column of the table.

  RETURN
    0  ok
    1  If we can't get the privileges and we don't use table/column grants.

    save_priv	In this we store global and db level grants for the table
		Note that we don't store db level grants if the global grants
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
5446 5447
                is enough to satisfy the request and the global grants contains
                a SELECT grant.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5448 5449 5450
****************************************************************************/

bool
5451
check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
5452
	     bool dont_check_global_grants, bool no_errors, bool schema_db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5453
{
5454
  Security_context *sctx= thd->security_ctx;
5455 5456
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  ulong db_access;
5457
  bool  db_is_pattern= test(want_access & GRANT_ACL);
5458 5459
#endif
  ulong dummy;
5460 5461
  DBUG_ENTER("check_access");
  DBUG_PRINT("enter",("db: %s  want_access: %lu  master_access: %lu",
5462
                      db ? db : "", want_access, sctx->master_access));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5463 5464 5465 5466 5467
  if (save_priv)
    *save_priv=0;
  else
    save_priv= &dummy;

5468
  if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5469
  {
5470
    DBUG_PRINT("error",("No database"));
5471
    if (!no_errors)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5472 5473
      my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR),
                 MYF(0));                       /* purecov: tested */
5474
    DBUG_RETURN(TRUE);				/* purecov: tested */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5475 5476
  }

5477 5478 5479 5480 5481
  if (schema_db)
  {
    if (want_access & ~(SELECT_ACL | EXTRA_ACL))
    {
      if (!no_errors)
5482 5483
      {
        const char *db_name= db ? db : thd->db;
5484
        my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
5485 5486
                 sctx->priv_user, sctx->priv_host, db_name);
      }
5487 5488 5489 5490 5491 5492 5493 5494 5495
      DBUG_RETURN(TRUE);
    }
    else
    {
      *save_priv= SELECT_ACL;
      DBUG_RETURN(FALSE);
    }
  }

monty@mysql.com's avatar
monty@mysql.com committed
5496 5497 5498
#ifdef NO_EMBEDDED_ACCESS_CHECKS
  DBUG_RETURN(0);
#else
5499
  if ((sctx->master_access & want_access) == want_access)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5500
  {
5501 5502 5503 5504 5505
    /*
      If we don't have a global SELECT privilege, we have to get the database
      specific access rights to be able to handle queries of type
      UPDATE t1 SET a=1 WHERE b > 0
    */
5506 5507
    db_access= sctx->db_access;
    if (!(sctx->master_access & SELECT_ACL) &&
5508
	(db && (!thd->db || db_is_pattern || strcmp(db,thd->db))))
5509 5510 5511
      db_access=acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
                        db_is_pattern);
    *save_priv=sctx->master_access | db_access;
5512
    DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5513
  }
5514
  if (((want_access & ~sctx->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
5515
      ! db && dont_check_global_grants)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5516
  {						// We can never grant this
5517
    DBUG_PRINT("error",("No possible access"));
5518
    if (!no_errors)
5519
      my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
5520 5521
               sctx->priv_user,
               sctx->priv_host,
5522 5523 5524
               (thd->password ?
                ER(ER_YES) :
                ER(ER_NO)));                    /* purecov: tested */
5525
    DBUG_RETURN(TRUE);				/* purecov: tested */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5526 5527 5528
  }

  if (db == any_db)
5529
    DBUG_RETURN(FALSE);				// Allow select on anything
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5530

5531
  if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
5532 5533
    db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
                       db_is_pattern);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5534
  else
5535
    db_access= sctx->db_access;
5536
  DBUG_PRINT("info",("db_access: %lu", db_access));
monty@mysql.com's avatar
monty@mysql.com committed
5537
  /* Remove SHOW attribute and access rights we already have */
5538
  want_access &= ~(sctx->master_access | EXTRA_ACL);
5539 5540
  DBUG_PRINT("info",("db_access: %lu  want_access: %lu",
                     db_access, want_access));
5541
  db_access= ((*save_priv=(db_access | sctx->master_access)) & want_access);
5542 5543

  /* grant_option is set if there exists a single table or column grant */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5544
  if (db_access == want_access ||
5545
      (grant_option && !dont_check_global_grants &&
5546
       !(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS))))
5547
    DBUG_RETURN(FALSE);				/* Ok */
5548 5549

  DBUG_PRINT("error",("Access denied"));
5550
  if (!no_errors)
5551
    my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
5552
             sctx->priv_user, sctx->priv_host,
5553 5554 5555
             (db ? db : (thd->db ?
                         thd->db :
                         "unknown")));          /* purecov: tested */
5556
  DBUG_RETURN(TRUE);				/* purecov: tested */
monty@mysql.com's avatar
monty@mysql.com committed
5557
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5558 5559 5560
}


5561 5562 5563 5564 5565 5566 5567 5568 5569
/*
  check for global access and give descriptive error message if it fails

  SYNOPSIS
    check_global_access()
    thd			Thread handler
    want_access		Use should have any of these global rights

  WARNING
5570
    One gets access right if one has ANY of the rights in want_access
5571 5572 5573 5574 5575 5576 5577 5578
    This is useful as one in most cases only need one global right,
    but in some case we want to check if the user has SUPER or
    REPL_CLIENT_ACL rights.

  RETURN
    0	ok
    1	Access denied.  In this case an error is sent to the client
*/
5579 5580

bool check_global_access(THD *thd, ulong want_access)
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
5581
{
monty@mysql.com's avatar
monty@mysql.com committed
5582 5583 5584
#ifdef NO_EMBEDDED_ACCESS_CHECKS
  return 0;
#else
5585
  char command[128];
5586
  if ((thd->security_ctx->master_access & want_access))
5587 5588
    return 0;
  get_privilege_desc(command, sizeof(command), want_access);
5589
  my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
5590
  return 1;
monty@mysql.com's avatar
monty@mysql.com committed
5591
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
5592 5593 5594
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
5595
/*
5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615
  Check the privilege for all used tables.

  SYNOPSYS
    check_table_access()
      thd          Thread context
      want_access  Privileges requested
      tables       List of tables to be checked
      no_errors    FALSE/TRUE - report/don't report error to
                   the client (using my_error() call).

  NOTES
    Table privileges are cached in the table list for GRANT checking.
    This functions assumes that table list used and
    thd->lex->query_tables_own_last value correspond to each other
    (the latter should be either 0 or point to next_global member
    of one of elements of this table list).

  RETURN VALUE
    FALSE - OK
    TRUE  - Access denied
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5616 5617
*/

5618
bool
5619
check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
5620
		   bool no_errors)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5621
{
5622 5623
  uint found=0;
  ulong found_access=0;
5624 5625
  TABLE_LIST *org_tables= tables;
  TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
5626
  Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx;
5627
  /*
5628 5629 5630
    The check that first_not_own_table is not reached is for the case when
    the given table list refers to the list for prelocking (contains tables
    of other queries). For simple queries first_not_own_table is 0.
5631
  */
5632
  for (; tables != first_not_own_table; tables= tables->next_global)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5633
  {
5634 5635 5636 5637 5638
    if (tables->security_ctx)
      sctx= tables->security_ctx;
    else
      sctx= backup_ctx;

5639
    if (tables->schema_table && 
5640
        (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
5641 5642 5643
    {
      if (!no_errors)
        my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
5644
                 sctx->priv_user, sctx->priv_host,
5645 5646 5647
                 information_schema_name.str);
      return TRUE;
    }
5648 5649 5650 5651 5652
    /*
       Register access for view underlying table.
       Remove SHOW_VIEW_ACL, because it will be checked during making view
     */
    tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
5653
    if (tables->derived || tables->schema_table ||
5654
        (tables->table && (int)tables->table->s->tmp_table) ||
5655 5656
        my_tz_check_n_skip_implicit_tables(&tables,
                                           thd->lex->time_zone_tables_used))
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
5657
      continue;
5658 5659
    thd->security_ctx= sctx;
    if ((sctx->master_access & want_access) ==
5660
        (want_access & ~EXTRA_ACL) &&
5661
	thd->db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5662
      tables->grant.privilege= want_access;
5663
    else if (tables->db && thd->db && strcmp(tables->db, thd->db) == 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5664 5665 5666 5667 5668
    {
      if (found && !grant_option)		// db already checked
	tables->grant.privilege=found_access;
      else
      {
5669
	if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
5670
			 0, no_errors, test(tables->schema_table)))
5671
	  goto deny;                            // Access denied
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5672
	found_access=tables->grant.privilege;
5673
	found=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5674 5675
      }
    }
5676
    else if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
5677
			  0, no_errors, test(tables->schema_table)))
5678
      goto deny;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5679
  }
5680
  thd->security_ctx= backup_ctx;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5681
  if (grant_option)
5682
    return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5683
		       test(want_access & EXTRA_ACL), UINT_MAX, no_errors);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5684
  return FALSE;
5685 5686 5687
deny:
  thd->security_ctx= backup_ctx;
  return TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5688 5689
}

5690

5691
bool
5692 5693
check_routine_access(THD *thd, ulong want_access,char *db, char *name,
		     bool is_proc, bool no_errors)
5694 5695 5696 5697 5698
{
  TABLE_LIST tables[1];
  
  bzero((char *)tables, sizeof(TABLE_LIST));
  tables->db= db;
5699
  tables->table_name= tables->alias= name;
5700
  
monty@mysql.com's avatar
monty@mysql.com committed
5701 5702 5703 5704 5705 5706 5707
  /*
    The following test is just a shortcut for check_access() (to avoid
    calculating db_access) under the assumption that it's common to
    give persons global right to execute all stored SP (but not
    necessary to create them).
  */
  if ((thd->security_ctx->master_access & want_access) == want_access)
5708 5709
    tables->grant.privilege= want_access;
  else if (check_access(thd,want_access,db,&tables->grant.privilege,
monty@mysql.com's avatar
monty@mysql.com committed
5710
			0, no_errors, 0))
5711 5712
    return TRUE;
  
5713
#ifndef NO_EMBEDDED_ACCESS_CHECKS
5714
  if (grant_option)
5715
    return check_grant_routine(thd, want_access, tables, is_proc, no_errors);
5716
#endif
5717 5718 5719 5720

  return FALSE;
}

5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735

/*
  Check if the routine has any of the routine privileges

  SYNOPSIS
    check_some_routine_access()
    thd		 Thread handler
    db           Database name
    name         Routine name

  RETURN
    0            ok
    1            error
*/

5736 5737
bool check_some_routine_access(THD *thd, const char *db, const char *name,
                               bool is_proc)
5738 5739
{
  ulong save_priv;
5740
  if (thd->security_ctx->master_access & SHOW_PROC_ACLS)
5741
    return FALSE;
5742 5743 5744 5745 5746
  /*
    There are no routines in information_schema db. So we can safely
    pass zero to last paramter of check_access function
  */
  if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, 0, 1, 0) ||
5747 5748
      (save_priv & SHOW_PROC_ACLS))
    return FALSE;
5749
  return check_routine_level_acl(thd, db, name, is_proc);
5750 5751 5752
}


5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777
/*
  Check if the given table has any of the asked privileges

  SYNOPSIS
    check_some_access()
    thd		 Thread handler
    want_access	 Bitmap of possible privileges to check for

  RETURN
    0  ok
    1  error
*/


bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
{
  ulong access;
  DBUG_ENTER("check_some_access");

  /* This loop will work as long as we have less than 32 privileges */
  for (access= 1; access < want_access ; access<<= 1)
  {
    if (access & want_access)
    {
      if (!check_access(thd, access, table->db,
5778 5779
                        &table->grant.privilege, 0, 1,
                        test(table->schema_table)) &&
5780 5781 5782 5783 5784 5785 5786 5787 5788
          !grant_option || !check_grant(thd, access, table, 0, 1, 1))
        DBUG_RETURN(0);
    }
  }
  DBUG_PRINT("exit",("no matching access rights"));
  DBUG_RETURN(1);
}


5789 5790
bool check_merge_table_access(THD *thd, char *db,
			      TABLE_LIST *table_list)
5791 5792 5793 5794
{
  int error=0;
  if (table_list)
  {
5795
    /* Check that all tables use the current database */
5796
    TABLE_LIST *tmp;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
5797
    for (tmp= table_list; tmp; tmp= tmp->next_local)
5798 5799 5800 5801
    {
      if (!tmp->db || !tmp->db[0])
	tmp->db=db;
    }
5802
    error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
hf@deer.(none)'s avatar
hf@deer.(none) committed
5803
			     table_list,0);
5804 5805 5806 5807
  }
  return error;
}

hf@deer.(none)'s avatar
hf@deer.(none) committed
5808

bk@work.mysql.com's avatar
bk@work.mysql.com committed
5809 5810 5811 5812 5813 5814 5815 5816 5817 5818
/****************************************************************************
	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

monty@mysql.com's avatar
monty@mysql.com committed
5819 5820 5821 5822
#ifndef DBUG_OFF
long max_stack_used;
#endif

5823
#ifndef EMBEDDED_LIBRARY
5824 5825 5826 5827 5828 5829 5830 5831
/*
  Note: The 'buf' parameter is necessary, even if it is unused here.
  - fix_fields functions has a "dummy" buffer large enough for the
    corresponding exec. (Thus we only have to check in fix_fields.)
  - Passing to check_stack_overrun() prevents the compiler from removing it.
 */
bool check_stack_overrun(THD *thd, long margin,
			 char *buf __attribute__((unused)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5832 5833
{
  long stack_used;
5834
  DBUG_ASSERT(thd == current_thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5835
  if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
monty@mysql.com's avatar
monty@mysql.com committed
5836
      (long) (thread_stack - margin))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5837
  {
5838 5839 5840
    sprintf(errbuff[0],ER(ER_STACK_OVERRUN_NEED_MORE),
            stack_used,thread_stack,margin);
    my_message(ER_STACK_OVERRUN_NEED_MORE,errbuff[0],MYF(0));
5841
    thd->fatal_error();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5842 5843
    return 1;
  }
monty@mysql.com's avatar
monty@mysql.com committed
5844 5845 5846
#ifndef DBUG_OFF
  max_stack_used= max(max_stack_used, stack_used);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5847 5848
  return 0;
}
5849
#endif /* EMBEDDED_LIBRARY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5850 5851 5852 5853

#define MY_YACC_INIT 1000			// Start with big alloc
#define MY_YACC_MAX  32000			// Because of 'short'

5854
bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5855
{
5856
  LEX	*lex= current_thd->lex;
5857
  ulong old_info=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883
  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;
}


/****************************************************************************
5884
  Initialize global thd variables needed for query
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5885 5886
****************************************************************************/

5887
void
5888
mysql_init_query(THD *thd, uchar *buf, uint length)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5889 5890
{
  DBUG_ENTER("mysql_init_query");
5891
  lex_start(thd, buf, length);
5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902
  mysql_reset_thd_for_next_command(thd);
  DBUG_VOID_RETURN;
}


/*
 Reset THD part responsible for command processing state.

 DESCRIPTION
   This needs to be called before execution of every statement
   (prepared or conventional).
5903
   It is not called by substatements of routines.
5904 5905 5906 5907 5908 5909 5910 5911 5912 5913

 TODO
   Make it a method of THD and align its name with the rest of
   reset/end/start/init methods.
   Call it after we use THD for queries, not before.
*/

void mysql_reset_thd_for_next_command(THD *thd)
{
  DBUG_ENTER("mysql_reset_thd_for_next_command");
5914
  DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */
5915
  thd->free_list= 0;
5916
  thd->select_number= 1;
5917 5918 5919 5920
  /*
    Those two lines below are theoretically unneeded as
    THD::cleanup_after_query() should take care of this already.
  */
5921
  thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty();
5922 5923 5924
  thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;

  thd->query_start_used= 0;
5925
  thd->is_fatal_error= thd->time_zone_used= 0;
monty@mysql.com's avatar
monty@mysql.com committed
5926
  thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | 
monty@mysql.com's avatar
monty@mysql.com committed
5927 5928
                          SERVER_QUERY_NO_INDEX_USED |
                          SERVER_QUERY_NO_GOOD_INDEX_USED);
5929 5930 5931 5932 5933 5934 5935 5936
  /*
    If in autocommit mode and not in a transaction, reset
    OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG to not get warnings
    in ha_rollback_trans() about some tables couldn't be rolled back.
  */
  if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
    thd->options&= ~(OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG);

5937
  DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx);
5938
  thd->tmp_table_used= 0;
5939 5940
  if (!thd->in_sub_stmt)
  {
5941
    if (opt_bin_log)
5942
    {
5943
      reset_dynamic(&thd->user_var_events);
5944 5945
      thd->user_var_events_alloc= thd->mem_root;
    }
5946
    thd->clear_error();
5947 5948 5949 5950
    thd->total_warn_count=0;			// Warnings for this query
    thd->rand_used= 0;
    thd->sent_row_count= thd->examined_row_count= 0;
  }
5951 5952 5953 5954
  /*
    Because we come here only for start of top-statements, binlog format is
    constant inside a complex statement (using stored functions) etc.
  */
5955 5956
  thd->reset_current_stmt_binlog_row_based();

bk@work.mysql.com's avatar
bk@work.mysql.com committed
5957 5958 5959
  DBUG_VOID_RETURN;
}

5960

5961 5962 5963
void
mysql_init_select(LEX *lex)
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5964
  SELECT_LEX *select_lex= lex->current_select;
5965
  select_lex->init_select();
5966
  lex->wild= 0;
5967 5968
  if (select_lex == &lex->select_lex)
  {
5969
    DBUG_ASSERT(lex->result == 0);
5970 5971
    lex->exchange= 0;
  }
5972 5973
}

5974

5975
bool
5976
mysql_new_select(LEX *lex, bool move_down)
5977
{
5978
  SELECT_LEX *select_lex;
5979
  THD *thd= lex->thd;
5980 5981
  DBUG_ENTER("mysql_new_select");

5982
  if (!(select_lex= new (thd->mem_root) SELECT_LEX()))
5983
    DBUG_RETURN(1);
5984
  select_lex->select_number= ++thd->select_number;
5985
  select_lex->parent_lex= lex; /* Used in init_query. */
5986 5987
  select_lex->init_query();
  select_lex->init_select();
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
5988 5989
  lex->nest_level++;
  select_lex->nest_level= lex->nest_level;
5990 5991 5992 5993 5994
  /*
    Don't evaluate this subquery during statement prepare even if
    it's a constant one. The flag is switched off in the end of
    mysql_stmt_prepare.
  */
konstantin@mysql.com's avatar
konstantin@mysql.com committed
5995
  if (thd->stmt_arena->is_stmt_prepare())
5996
    select_lex->uncacheable|= UNCACHEABLE_PREPARE;
5997 5998
  if (move_down)
  {
5999
    SELECT_LEX_UNIT *unit;
6000
    lex->subqueries= TRUE;
6001
    /* first select_lex of subselect or derived table */
6002
    if (!(unit= new (thd->mem_root) SELECT_LEX_UNIT()))
6003
      DBUG_RETURN(1);
6004

6005 6006
    unit->init_query();
    unit->init_select();
6007
    unit->thd= thd;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
6008
    unit->include_down(lex->current_select);
6009 6010
    unit->link_next= 0;
    unit->link_prev= 0;
6011
    unit->return_to= lex->current_select;
6012
    select_lex->include_down(unit);
6013 6014 6015 6016 6017
    /*
      By default we assume that it is usual subselect and we have outer name
      resolution context, if no we will assign it to 0 later
    */
    select_lex->context.outer_context= &select_lex->outer_select()->context;
6018 6019
  }
  else
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
6020
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6021 6022
    if (lex->current_select->order_list.first && !lex->current_select->braces)
    {
6023
      my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY");
6024
      DBUG_RETURN(1);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6025
    }
6026
    select_lex->include_neighbour(lex->current_select);
6027 6028 6029 6030 6031
    SELECT_LEX_UNIT *unit= select_lex->master_unit();                              
    if (!unit->fake_select_lex && unit->add_fake_select_lex(lex->thd))
      DBUG_RETURN(1);
    select_lex->context.outer_context= 
                unit->first_select()->context.outer_context;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
6032
  }
peter@mysql.com's avatar
peter@mysql.com committed
6033

6034
  select_lex->master_unit()->global_parameters= select_lex;
6035
  select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
6036
  lex->current_select= select_lex;
6037 6038 6039 6040 6041
  /*
    in subquery is SELECT query and we allow resolution of names in SELECT
    list
  */
  select_lex->context.resolve_in_select_list= TRUE;
6042
  DBUG_RETURN(0);
6043
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6044

6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059
/*
  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)
{
6060
  THD *thd;
6061
  LEX *lex;
6062
  LEX_STRING tmp, null_lex_string;
6063 6064
  Item *var;
  char buff[MAX_SYS_VAR_LENGTH*2+4+8], *end;
6065
  DBUG_ENTER("create_select_for_variable");
6066 6067

  thd= current_thd;
pem@mysql.telia.com's avatar
pem@mysql.telia.com committed
6068
  lex= thd->lex;
6069 6070 6071 6072
  mysql_init_select(lex);
  lex->sql_command= SQLCOM_SELECT;
  tmp.str= (char*) var_name;
  tmp.length=strlen(var_name);
6073
  bzero((char*) &null_lex_string.str, sizeof(null_lex_string));
6074 6075 6076 6077
  /*
    We set the name of Item to @@session.var_name because that then is used
    as the column name in the output.
  */
monty@mysql.com's avatar
monty@mysql.com committed
6078 6079 6080 6081 6082 6083
  if ((var= get_system_var(thd, OPT_SESSION, tmp, null_lex_string)))
  {
    end= strxmov(buff, "@@session.", var_name, NullS);
    var->set_name(buff, end-buff, system_charset_info);
    add_item_to_list(thd, var);
  }
6084 6085 6086
  DBUG_VOID_RETURN;
}

6087

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6088 6089
void mysql_init_multi_delete(LEX *lex)
{
6090
  lex->sql_command=  SQLCOM_DELETE_MULTI;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6091
  mysql_init_select(lex);
6092 6093
  lex->select_lex.select_limit= 0;
  lex->unit.select_limit_cnt= HA_POS_ERROR;
6094
  lex->select_lex.table_list.save_and_clear(&lex->auxiliary_table_list);
6095
  lex->lock_option= using_update_log ? TL_READ_NO_INSERT : TL_READ;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6096 6097
  lex->query_tables= 0;
  lex->query_tables_last= &lex->query_tables;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6098
}
6099

6100 6101 6102 6103
/*
  When you modify mysql_parse(), you may need to mofify
  mysql_test_parse_for_slave() in this same file.
*/
6104

6105
void mysql_parse(THD *thd, char *inBuf, uint length)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6106 6107
{
  DBUG_ENTER("mysql_parse");
6108 6109 6110

  DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););

6111
  mysql_init_query(thd, (uchar*) inBuf, length);
6112
  if (query_cache_send_result_to_client(thd, inBuf, length) <= 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6113
  {
6114
    LEX *lex= thd->lex;
6115
    
6116 6117
    sp_cache_flush_obsolete(&thd->sp_proc_cache);
    sp_cache_flush_obsolete(&thd->sp_func_cache);
6118
    
6119
    if (!MYSQLparse((void *)thd) && ! thd->is_fatal_error)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6120
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
6121
#ifndef NO_EMBEDDED_ACCESS_CHECKS
6122
      if (mqh_used && thd->user_connect &&
6123
	  check_mqh(thd, lex->sql_command))
6124 6125 6126 6127
      {
	thd->net.error = 0;
      }
      else
hf@deer.(none)'s avatar
hf@deer.(none) committed
6128
#endif
6129
      {
6130
	if (thd->net.report_error)
6131
	{
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
6132 6133
          delete lex->sphead;
          lex->sphead= NULL;
6134
	}
6135 6136
	else
	{
6137 6138 6139 6140 6141 6142 6143 6144 6145 6146
          /*
            Binlog logs a string starting from thd->query and having length
            thd->query_length; so we set thd->query_length correctly (to not
            log several statements in one event, when we executed only first).
            We set it to not see the ';' (otherwise it would get into binlog
            and Query_log_event::print() would give ';;' output).
            This also helps display only the current query in SHOW
            PROCESSLIST.
            Note that we don't need LOCK_thread_count to modify query_length.
          */
serg@serg.mylan's avatar
serg@serg.mylan committed
6147 6148
          if (lex->found_semicolon &&
              (thd->query_length= (ulong)(lex->found_semicolon - thd->query)))
6149 6150
            thd->query_length--;
          /* Actually execute the query */
6151
	  mysql_execute_command(thd);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
6152
	  query_cache_end_of_result(thd);
6153
	}
6154
      }
6155
      lex->unit.cleanup();
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6156 6157
    }
    else
6158
    {
6159
      DBUG_ASSERT(thd->net.report_error);
6160
      DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
6161
			 thd->is_fatal_error));
6162 6163 6164 6165 6166

      /*
        The first thing we do after parse error is freeing sp_head to
        ensure that we have restored original memroot.
      */
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
6167
      if (lex->sphead)
6168
      {
monty@mysql.com's avatar
monty@mysql.com committed
6169
	/* Clean up after failed stored procedure/function */
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
6170 6171 6172
	delete lex->sphead;
	lex->sphead= NULL;
      }
6173 6174
      query_cache_abort(&thd->net);
      lex->unit.cleanup();
6175
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6176
    thd->proc_info="freeing items";
6177
    thd->end_statement();
6178
    thd->cleanup_after_query();
6179
    DBUG_ASSERT(thd->change_list.is_empty());
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6180
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6181 6182 6183 6184
  DBUG_VOID_RETURN;
}


monty@mysql.com's avatar
monty@mysql.com committed
6185
#ifdef HAVE_REPLICATION
6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196
/*
  Usable by the replication SQL thread only: just parse a query to know if it
  can be ignored because of replicate-*-table rules.

  RETURN VALUES
    0	cannot be ignored
    1	can be ignored
*/

bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
{
6197
  LEX *lex= thd->lex;
6198
  bool error= 0;
monty@mysql.com's avatar
monty@mysql.com committed
6199
  DBUG_ENTER("mysql_test_parse_for_slave");
6200

6201
  mysql_init_query(thd, (uchar*) inBuf, length);
6202
  if (!MYSQLparse((void*) thd) && ! thd->is_fatal_error &&
6203
      all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
monty@mysql.com's avatar
monty@mysql.com committed
6204
    error= 1;                  /* Ignore question */
6205
  thd->end_statement();
6206
  thd->cleanup_after_query();
monty@mysql.com's avatar
monty@mysql.com committed
6207
  DBUG_RETURN(error);
6208
}
monty@mysql.com's avatar
monty@mysql.com committed
6209
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6210

6211

monty@mysql.com's avatar
monty@mysql.com committed
6212

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6213 6214 6215 6216 6217
/*****************************************************************************
** Store field definition for create
** Return 0 if ok
******************************************************************************/

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6218
bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6219
		       char *length, char *decimals,
6220
		       uint type_modifier,
6221 6222
		       Item *default_value, Item *on_update_value,
                       LEX_STRING *comment,
6223 6224
		       char *change,
                       List<String> *interval_list, CHARSET_INFO *cs,
6225
		       uint uint_geom_type)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6226 6227
{
  register create_field *new_field;
6228
  LEX  *lex= thd->lex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6229 6230 6231 6232
  DBUG_ENTER("add_field_to_list");

  if (strlen(field_name) > NAME_LEN)
  {
6233
    my_error(ER_TOO_LONG_IDENT, MYF(0), field_name); /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6234 6235 6236 6237 6238
    DBUG_RETURN(1);				/* purecov: inspected */
  }
  if (type_modifier & PRI_KEY_FLAG)
  {
    lex->col_list.push_back(new key_part_spec(field_name,0));
6239 6240
    lex->key_list.push_back(new Key(Key::PRIMARY, NullS,
                                    &default_key_create_info,
6241
				    0, lex->col_list));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6242 6243 6244 6245 6246
    lex->col_list.empty();
  }
  if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
  {
    lex->col_list.push_back(new key_part_spec(field_name,0));
6247 6248
    lex->key_list.push_back(new Key(Key::UNIQUE, NullS,
                                    &default_key_create_info, 0,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6249 6250 6251 6252
				    lex->col_list));
    lex->col_list.empty();
  }

6253
  if (default_value)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6254
  {
6255
    /* 
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
6256 6257
      Default value should be literal => basic constants =>
      no need fix_fields()
6258 6259 6260
      
      We allow only one function as part of default value - 
      NOW() as default for TIMESTAMP type.
6261
    */
6262 6263 6264
    if (default_value->type() == Item::FUNC_ITEM && 
        !(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC &&
         type == FIELD_TYPE_TIMESTAMP))
6265
    {
6266
      my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
6267 6268 6269
      DBUG_RETURN(1);
    }
    else if (default_value->type() == Item::NULL_ITEM)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6270
    {
6271
      default_value= 0;
6272 6273 6274
      if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
	  NOT_NULL_FLAG)
      {
6275
	my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
6276 6277 6278 6279 6280
	DBUG_RETURN(1);
      }
    }
    else if (type_modifier & AUTO_INCREMENT_FLAG)
    {
6281
      my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6282 6283 6284
      DBUG_RETURN(1);
    }
  }
6285 6286 6287

  if (on_update_value && type != FIELD_TYPE_TIMESTAMP)
  {
6288
    my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name);
6289 6290
    DBUG_RETURN(1);
  }
6291

6292 6293 6294 6295 6296 6297
  if (type == FIELD_TYPE_TIMESTAMP && length)
  {
    /* Display widths are no longer supported for TIMSTAMP as of MySQL 4.1.
       In other words, for declarations such as TIMESTAMP(2), TIMESTAMP(4),
       and so on, the display width is ignored.
    */
msvensson@neptunus.(none)'s avatar
msvensson@neptunus.(none) committed
6298
    char buf[32];
6299
    my_snprintf(buf, sizeof(buf), "TIMESTAMP(%s)", length);
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
6300
    WARN_DEPRECATED(thd, "5.2", buf, "'TIMESTAMP'");
6301 6302
  }

6303 6304 6305 6306
  if (!(new_field= new create_field()) ||
      new_field->init(thd, field_name, type, length, decimals, type_modifier,
                      default_value, on_update_value, comment, change,
                      interval_list, cs, uint_geom_type))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6307
    DBUG_RETURN(1);
6308 6309 6310 6311 6312 6313

  lex->create_list.push_back(new_field);
  lex->last_field=new_field;
  DBUG_RETURN(0);
}

6314

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6315 6316 6317 6318
/* Store position for column in ALTER TABLE .. ADD column */

void store_position_for_column(const char *name)
{
6319
  current_thd->lex->last_field->after=my_const_cast(char*) (name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6320 6321 6322
}

bool
6323
add_proc_to_list(THD* thd, Item *item)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6324 6325 6326 6327
{
  ORDER *order;
  Item	**item_ptr;

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6328
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6329 6330 6331 6332 6333
    return 1;
  item_ptr = (Item**) (order+1);
  *item_ptr= item;
  order->item=item_ptr;
  order->free_me=0;
6334
  thd->lex->proc_list.link_in_list((byte*) order,(byte**) &order->next);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6335 6336 6337 6338 6339 6340 6341 6342 6343
  return 0;
}


/****************************************************************************
** save order by and tables in own lists
****************************************************************************/


monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6344
bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6345 6346 6347
{
  ORDER *order;
  DBUG_ENTER("add_to_list");
6348
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6349
    DBUG_RETURN(1);
6350 6351
  order->item_ptr= item;
  order->item= &order->item_ptr;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6352 6353 6354
  order->asc = asc;
  order->free_me=0;
  order->used=0;
6355
  order->counter_used= 0;
6356
  list.link_in_list((byte*) order,(byte**) &order->next);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6357 6358 6359 6360
  DBUG_RETURN(0);
}


monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6361 6362 6363 6364 6365 6366 6367 6368 6369 6370
/*
  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
6371
			TL_OPTION_ALIAS	        an alias in multi table DELETE
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6372 6373 6374 6375 6376 6377 6378 6379 6380
    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
*/

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6381 6382
TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
					     Table_ident *table,
6383
					     LEX_STRING *alias,
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6384 6385
					     ulong table_options,
					     thr_lock_type lock_type,
6386 6387
					     List<String> *use_index_arg,
					     List<String> *ignore_index_arg,
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6388
                                             LEX_STRING *option)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6389 6390
{
  register TABLE_LIST *ptr;
6391
  TABLE_LIST *previous_table_ref; /* The table preceding the current one. */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6392
  char *alias_str;
6393
  LEX *lex= thd->lex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6394
  DBUG_ENTER("add_table_to_list");
6395
  LINT_INIT(previous_table_ref);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6396 6397 6398 6399

  if (!table)
    DBUG_RETURN(0);				// End of memory
  alias_str= alias ? alias->str : table->table.str;
6400 6401
  if (!test(table_options & TL_OPTION_ALIAS) && 
      check_table_name(table->table.str, table->table.length))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6402
  {
6403
    my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6404 6405
    DBUG_RETURN(0);
  }
6406 6407

  if (table->is_derived_table() == FALSE && table->db.str &&
6408
      check_db_name(&table->db))
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
6409 6410 6411 6412
  {
    my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str);
    DBUG_RETURN(0);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6413 6414

  if (!alias)					/* Alias is case sensitive */
6415 6416 6417
  {
    if (table->sel)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
6418 6419
      my_message(ER_DERIVED_MUST_HAVE_ALIAS,
                 ER(ER_DERIVED_MUST_HAVE_ALIAS), MYF(0));
6420 6421
      DBUG_RETURN(0);
    }
6422
    if (!(alias_str=thd->memdup(alias_str,table->table.length+1)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6423
      DBUG_RETURN(0);
6424
  }
6425
  if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6426
    DBUG_RETURN(0);				/* purecov: inspected */
peter@mysql.com's avatar
peter@mysql.com committed
6427
  if (table->db.str)
6428 6429 6430 6431
  {
    ptr->db= table->db.str;
    ptr->db_length= table->db.length;
  }
6432 6433
  else if (thd->copy_db_to(&ptr->db, &ptr->db_length))
    DBUG_RETURN(0);
peter@mysql.com's avatar
peter@mysql.com committed
6434

6435
  ptr->alias= alias_str;
6436
  if (lower_case_table_names && table->table.length)
6437
    table->table.length= my_casedn_str(files_charset_info, table->table.str);
6438 6439
  ptr->table_name=table->table.str;
  ptr->table_name_length=table->table.length;
6440
  ptr->lock_type=   lock_type;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6441 6442
  ptr->updating=    test(table_options & TL_OPTION_UPDATING);
  ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6443
  ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
6444
  ptr->derived=	    table->sel;
6445 6446
  if (!ptr->derived && !my_strcasecmp(system_charset_info, ptr->db,
                                      information_schema_name.str))
6447
  {
6448
    ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, ptr->table_name);
6449 6450
    if (!schema_table ||
        (schema_table->hidden && 
6451
         (sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0))
6452
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
6453
      my_error(ER_UNKNOWN_TABLE, MYF(0),
6454
               ptr->table_name, information_schema_name.str);
6455 6456
      DBUG_RETURN(0);
    }
6457
    ptr->schema_table_name= ptr->table_name;
6458 6459
    ptr->schema_table= schema_table;
  }
6460
  ptr->select_lex=  lex->current_select;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
6461
  ptr->cacheable_table= 1;
6462 6463 6464 6465 6466 6467
  if (use_index_arg)
    ptr->use_index=(List<String> *) thd->memdup((gptr) use_index_arg,
						sizeof(*use_index_arg));
  if (ignore_index_arg)
    ptr->ignore_index=(List<String> *) thd->memdup((gptr) ignore_index_arg,
						   sizeof(*ignore_index_arg));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6468
  ptr->option= option ? option->str : 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6469
  /* check that used name is unique */
6470
  if (lock_type != TL_IGNORE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6471
  {
timour@mysql.com's avatar
timour@mysql.com committed
6472 6473 6474 6475
    TABLE_LIST *first_table= (TABLE_LIST*) table_list.first;
    if (lex->sql_command == SQLCOM_CREATE_VIEW)
      first_table= first_table ? first_table->next_local : NULL;
    for (TABLE_LIST *tables= first_table ;
6476
	 tables ;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6477
	 tables=tables->next_local)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6478
    {
6479 6480
      if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
	  !strcmp(ptr->db, tables->db))
6481
      {
6482
	my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str); /* purecov: tested */
6483 6484
	DBUG_RETURN(0);				/* purecov: tested */
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6485 6486
    }
  }
6487 6488 6489
  /* Store the table reference preceding the current one. */
  if (table_list.elements > 0)
  {
6490 6491 6492
    /*
      table_list.next points to the last inserted TABLE_LIST->next_local'
      element
6493
      We don't use the offsetof() macro here to avoid warnings from gcc
6494
    */
6495 6496 6497
    previous_table_ref= (TABLE_LIST*) ((char*) table_list.next -
                                       ((char*) &(ptr->next_local) -
                                        (char*) ptr));
6498 6499 6500 6501 6502 6503 6504 6505
    /*
      Set next_name_resolution_table of the previous table reference to point
      to the current table reference. In effect the list
      TABLE_LIST::next_name_resolution_table coincides with
      TABLE_LIST::next_local. Later this may be changed in
      store_top_level_join_columns() for NATURAL/USING joins.
    */
    previous_table_ref->next_name_resolution_table= ptr;
6506
  }
6507

6508 6509 6510 6511 6512 6513
  /*
    Link the current table reference in a local list (list for current select).
    Notice that as a side effect here we set the next_local field of the
    previous table reference to 'ptr'. Here we also add one element to the
    list 'table_list'.
  */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6514
  table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local);
6515
  ptr->next_name_resolution_table= NULL;
6516
  /* Link table in global list (all used tables) */
6517
  lex->add_to_query_tables(ptr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6518 6519 6520
  DBUG_RETURN(ptr);
}

monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
6521

6522 6523 6524 6525
/*
  Initialize a new table list for a nested join

  SYNOPSIS
6526
    init_nested_join()
6527
    thd         current thread
6528

6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547
  DESCRIPTION
    The function initializes a structure of the TABLE_LIST type
    for a nested join. It sets up its nested join list as empty.
    The created structure is added to the front of the current
    join list in the st_select_lex object. Then the function
    changes the current nest level for joins to refer to the newly
    created empty list after having saved the info on the old level
    in the initialized structure.

  RETURN VALUE
    0,  if success
    1,  otherwise
*/

bool st_select_lex::init_nested_join(THD *thd)
{
  TABLE_LIST *ptr;
  NESTED_JOIN *nested_join;
  DBUG_ENTER("init_nested_join");
6548

6549 6550
  if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
                                       sizeof(NESTED_JOIN))))
6551
    DBUG_RETURN(1);
6552 6553 6554
  nested_join= ptr->nested_join=
    ((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));

6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574
  join_list->push_front(ptr);
  ptr->embedding= embedding;
  ptr->join_list= join_list;
  embedding= ptr;
  join_list= &nested_join->join_list;
  join_list->empty();
  DBUG_RETURN(0);
}


/*
  End a nested join table list

  SYNOPSIS
    end_nested_join()
    thd         current thread

  DESCRIPTION
    The function returns to the previous join nest level.
    If the current level contains only one member, the function
6575
    moves it one level up, eliminating the nest.
6576 6577 6578 6579 6580 6581 6582 6583 6584

  RETURN VALUE
    Pointer to TABLE_LIST element added to the total table list, if success
    0, otherwise
*/

TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
{
  TABLE_LIST *ptr;
6585
  NESTED_JOIN *nested_join;
6586
  DBUG_ENTER("end_nested_join");
6587

6588
  DBUG_ASSERT(embedding);
6589 6590 6591
  ptr= embedding;
  join_list= ptr->join_list;
  embedding= ptr->embedding;
6592
  nested_join= ptr->nested_join;
6593 6594 6595 6596 6597 6598 6599 6600 6601
  if (nested_join->join_list.elements == 1)
  {
    TABLE_LIST *embedded= nested_join->join_list.head();
    join_list->pop();
    embedded->join_list= join_list;
    embedded->embedding= embedding;
    join_list->push_front(embedded);
    ptr= embedded;
  }
6602
  else if (nested_join->join_list.elements == 0)
6603 6604
  {
    join_list->pop();
6605
    ptr= 0;                                     // return value
6606
  }
6607 6608 6609 6610 6611 6612 6613 6614
  DBUG_RETURN(ptr);
}


/*
  Nest last join operation

  SYNOPSIS
6615
    nest_last_join()
6616 6617 6618 6619 6620 6621
    thd         current thread

  DESCRIPTION
    The function nest last join operation as if it was enclosed in braces.

  RETURN VALUE
6622 6623 6624
    0  Error
    #  Pointer to TABLE_LIST element created for the new nested join

6625 6626 6627 6628 6629 6630
*/

TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
{
  TABLE_LIST *ptr;
  NESTED_JOIN *nested_join;
6631
  List<TABLE_LIST> *embedded_list;
6632
  DBUG_ENTER("nest_last_join");
6633

6634 6635
  if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
                                       sizeof(NESTED_JOIN))))
6636
    DBUG_RETURN(0);
6637 6638 6639
  nested_join= ptr->nested_join=
    ((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));

6640 6641
  ptr->embedding= embedding;
  ptr->join_list= join_list;
6642
  embedded_list= &nested_join->join_list;
6643
  embedded_list->empty();
6644 6645

  for (uint i=0; i < 2; i++)
6646 6647 6648 6649 6650
  {
    TABLE_LIST *table= join_list->pop();
    table->join_list= embedded_list;
    table->embedding= ptr;
    embedded_list->push_back(table);
6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663
    if (table->natural_join)
    {
      ptr->is_natural_join= TRUE;
      /*
        If this is a JOIN ... USING, move the list of joined fields to the
        table reference that describes the join.
      */
      if (table->join_using_fields)
      {
        ptr->join_using_fields= table->join_using_fields;
        table->join_using_fields= NULL;
      }
    }
6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703
  }
  join_list->push_front(ptr);
  nested_join->used_tables= nested_join->not_null_tables= (table_map) 0;
  DBUG_RETURN(ptr);
}


/*
  Add a table to the current join list

  SYNOPSIS
    add_joined_table()
    table       the table to add

  DESCRIPTION
    The function puts a table in front of the current join list
    of st_select_lex object.
    Thus, joined tables are put into this list in the reverse order
    (the most outer join operation follows first).

  RETURN VALUE
    None
*/

void st_select_lex::add_joined_table(TABLE_LIST *table)
{
  DBUG_ENTER("add_joined_table");
  join_list->push_front(table);
  table->join_list= join_list;
  table->embedding= embedding;
  DBUG_VOID_RETURN;
}


/*
  Convert a right join into equivalent left join

  SYNOPSIS
    convert_right_join()
    thd         current thread
6704 6705 6706

  DESCRIPTION
    The function takes the current join list t[0],t[1] ... and
6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729
    effectively converts it into the list t[1],t[0] ...
    Although the outer_join flag for the new nested table contains
    JOIN_TYPE_RIGHT, it will be handled as the inner table of a left join
    operation.

  EXAMPLES
    SELECT * FROM t1 RIGHT JOIN t2 ON on_expr =>
      SELECT * FROM t2 LEFT JOIN t1 ON on_expr

    SELECT * FROM t1,t2 RIGHT JOIN t3 ON on_expr =>
      SELECT * FROM t1,t3 LEFT JOIN t2 ON on_expr

    SELECT * FROM t1,t2 RIGHT JOIN (t3,t4) ON on_expr =>
      SELECT * FROM t1,(t3,t4) LEFT JOIN t2 ON on_expr

    SELECT * FROM t1 LEFT JOIN t2 ON on_expr1 RIGHT JOIN t3  ON on_expr2 =>
      SELECT * FROM t3 LEFT JOIN (t1 LEFT JOIN t2 ON on_expr2) ON on_expr1

  RETURN
    Pointer to the table representing the inner table, if success
    0, otherwise
*/

6730
TABLE_LIST *st_select_lex::convert_right_join()
6731 6732
{
  TABLE_LIST *tab2= join_list->pop();
6733
  TABLE_LIST *tab1= join_list->pop();
6734 6735 6736 6737 6738 6739 6740 6741 6742
  DBUG_ENTER("convert_right_join");

  join_list->push_front(tab2);
  join_list->push_front(tab1);
  tab1->outer_join|= JOIN_TYPE_RIGHT;

  DBUG_RETURN(tab1);
}

6743 6744 6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755
/*
  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
*/

6756
void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
6757 6758 6759 6760 6761 6762
{
  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));

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6763 6764 6765
  for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first;
       tables;
       tables= tables->next_local)
6766 6767 6768 6769 6770 6771 6772
  {
    tables->lock_type= lock_type;
    tables->updating=  for_update;
  }
  DBUG_VOID_RETURN;
}

monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
6773

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802
/*
  Create a fake SELECT_LEX for a unit

  SYNOPSIS:
    add_fake_select_lex()
    thd			   thread handle

  DESCRIPTION
    The method create a fake SELECT_LEX object for a unit.
    This object is created for any union construct containing a union
    operation and also for any single select union construct of the form
    (SELECT ... ORDER BY order_list [LIMIT n]) ORDER BY ... 
    or of the form
    (SELECT ... ORDER BY LIMIT n) ORDER BY ...
  
  NOTES
    The object is used to retrieve rows from the temporary table
    where the result on the union is obtained.

  RETURN VALUES
    1     on failure to create the object
    0     on success
*/

bool st_select_lex_unit::add_fake_select_lex(THD *thd)
{
  SELECT_LEX *first_sl= first_select();
  DBUG_ENTER("add_fake_select_lex");
  DBUG_ASSERT(!fake_select_lex);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6803

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6804 6805 6806 6807 6808
  if (!(fake_select_lex= new (thd->mem_root) SELECT_LEX()))
      DBUG_RETURN(1);
  fake_select_lex->include_standalone(this, 
                                      (SELECT_LEX_NODE**)&fake_select_lex);
  fake_select_lex->select_number= INT_MAX;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6809
  fake_select_lex->parent_lex= thd->lex; /* Used in init_query. */
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6810 6811
  fake_select_lex->make_empty_select();
  fake_select_lex->linkage= GLOBAL_OPTIONS_TYPE;
6812 6813
  fake_select_lex->select_limit= 0;

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6814
  fake_select_lex->context.outer_context=first_sl->context.outer_context;
6815 6816 6817
  /* allow item list resolving in fake select for ORDER BY */
  fake_select_lex->context.resolve_in_select_list= TRUE;
  fake_select_lex->context.select_lex= fake_select_lex;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830

  if (!first_sl->next_select())
  {
    /* 
      This works only for 
      (SELECT ... ORDER BY list [LIMIT n]) ORDER BY order_list [LIMIT m],
      (SELECT ... LIMIT n) ORDER BY order_list [LIMIT m]
      just before the parser starts processing order_list
    */ 
    global_parameters= fake_select_lex;
    fake_select_lex->no_table_names_allowed= 1;
    thd->lex->current_select= fake_select_lex;
  }
6831
  thd->lex->pop_context();
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6832 6833 6834
  DBUG_RETURN(0);
}

6835

6836
/*
6837 6838
  Push a new name resolution context for a JOIN ... ON clause to the
  context stack of a query block.
6839 6840

  SYNOPSIS
6841
    push_new_name_resolution_context()
6842
    thd       pointer to current thread
timour@mysql.com's avatar
timour@mysql.com committed
6843
    left_op   left  operand of the JOIN
6844 6845 6846 6847
    right_op  rigth operand of the JOIN

  DESCRIPTION
    Create a new name resolution context for a JOIN ... ON clause,
6848 6849 6850
    set the first and last leaves of the list of table references
    to be used for name resolution, and push the newly created
    context to the stack of contexts of the query.
6851 6852

  RETURN
6853 6854
    FALSE  if all is OK
    TRUE   if a memory allocation error occured
6855 6856
*/

6857 6858 6859
bool
push_new_name_resolution_context(THD *thd,
                                 TABLE_LIST *left_op, TABLE_LIST *right_op)
6860 6861
{
  Name_resolution_context *on_context;
6862
  if (!(on_context= new (thd->mem_root) Name_resolution_context))
6863
    return TRUE;
6864 6865 6866 6867 6868
  on_context->init();
  on_context->first_name_resolution_table=
    left_op->first_leaf_for_name_resolution();
  on_context->last_name_resolution_table=
    right_op->last_leaf_for_name_resolution();
6869
  return thd->lex->push_context(on_context);
6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889
}


/*
  Add an ON condition to the second operand of a JOIN ... ON.

  SYNOPSIS
    add_join_on
    b     the second operand of a JOIN ... ON
    expr  the condition to be added to the ON clause

  DESCRIPTION
    Add an ON condition to the right operand of a JOIN ... ON clause.

  RETURN
    FALSE  if there was some error
    TRUE   if all is OK
*/

void add_join_on(TABLE_LIST *b, Item *expr)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6890
{
6891
  if (expr)
6892
  {
6893
    if (!b->on_expr)
6894
      b->on_expr= expr;
6895 6896
    else
    {
6897 6898 6899 6900 6901 6902
      /*
        If called from the parser, this happens if you have both a
        right and left join. If called later, it happens if we add more
        than one condition to the ON clause.
      */
      b->on_expr= new Item_cond_and(b->on_expr,expr);
6903 6904
    }
    b->on_expr->top_level_item();
6905
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6906 6907 6908
}


6909
/*
6910 6911
  Mark that there is a NATURAL JOIN or JOIN ... USING between two
  tables.
6912 6913 6914

  SYNOPSIS
    add_join_natural()
6915 6916 6917 6918
    a			Left join argument
    b			Right join argument
    using_fields        Field names from USING clause
  
6919
  IMPLEMENTATION
6920 6921 6922 6923 6924 6925 6926 6927 6928 6929
    This function marks that table b should be joined with a either via
    a NATURAL JOIN or via JOIN ... USING. Both join types are special
    cases of each other, so we treat them together. The function
    setup_conds() creates a list of equal condition between all fields
    of the same name for NATURAL JOIN or the fields in 'using_fields'
    for JOIN ... USING. The list of equality conditions is stored
    either in b->on_expr, or in JOIN::conds, depending on whether there
    was an outer join.

  EXAMPLE
6930 6931 6932
    SELECT * FROM t1 NATURAL LEFT JOIN t2
     <=>
    SELECT * FROM t1 LEFT JOIN t2 ON (t1.i=t2.i and t1.j=t2.j ... )
6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943

    SELECT * FROM t1 NATURAL JOIN t2 WHERE <some_cond>
     <=>
    SELECT * FROM t1, t2 WHERE (t1.i=t2.i and t1.j=t2.j and <some_cond>)

    SELECT * FROM t1 JOIN t2 USING(j) WHERE <some_cond>
     <=>
    SELECT * FROM t1, t2 WHERE (t1.j=t2.j and <some_cond>)

  RETURN
    None
6944 6945
*/

6946
void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6947
{
6948 6949
  b->natural_join= a;
  b->join_using_fields= using_fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6950 6951
}

6952

6953
/*
6954 6955 6956 6957
  Reload/resets privileges and the different caches.

  SYNOPSIS
    reload_acl_and_cache()
6958
    thd			Thread handler (can be NULL!)
6959 6960 6961 6962 6963
    options             What should be reset/reloaded (tables, privileges,
    slave...)
    tables              Tables to flush (if any)
    write_to_binlog     Depending on 'options', it may be very bad to write the
                        query to the binlog (e.g. FLUSH SLAVE); this is a
6964 6965 6966
                        pointer where reload_acl_and_cache() will put 0 if
                        it thinks we really should not write to the binlog.
                        Otherwise it will put 1.
6967 6968 6969

  RETURN
    0	 ok
6970
    !=0  error.  thd->killed or thd->net.report_error is set
6971 6972
*/

6973 6974
bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
                          bool *write_to_binlog)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6975 6976 6977
{
  bool result=0;
  select_errors=0;				/* Write if more errors */
6978
  bool tmp_write_to_binlog= 1;
6979

6980
  DBUG_ASSERT(!thd || !thd->in_sub_stmt);
6981

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
6982
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6983 6984
  if (options & REFRESH_GRANT)
  {
6985 6986 6987 6988 6989 6990
    THD *tmp_thd= 0;
    /*
      If reload_acl_and_cache() is called from SIGHUP handler we have to
      allocate temporary THD for execution of acl_reload()/grant_reload().
    */
    if (!thd && (thd= (tmp_thd= new THD)))
6991 6992
    {
      thd->thread_stack= (char*) &tmp_thd;
6993
      thd->store_globals();
6994
    }
6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006
    if (thd)
    {
      (void)acl_reload(thd);
      (void)grant_reload(thd);
    }
    if (tmp_thd)
    {
      delete tmp_thd;
      /* Remember that we don't have a THD */
      my_pthread_setspecific_ptr(THR_THD,  0);
      thd= 0;
    }
7007
    reset_mqh((LEX_USER *)NULL, TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7008
  }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
7009
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7010 7011
  if (options & REFRESH_LOG)
  {
7012
    /*
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
7013
      Flush the normal query log, the update log, the binary log,
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
7014 7015
      the slow query log, the relay log (if it exists) and the log
      tables.
7016
    */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
7017

7018
    /*
monty@mysql.com's avatar
monty@mysql.com committed
7019 7020 7021 7022
      Writing this command to the binlog may result in infinite loops
      when doing mysqlbinlog|mysql, and anyway it does not really make
      sense to log it automatically (would cause more trouble to users
      than it would help them)
7023 7024
    */
    tmp_write_to_binlog= 0;
7025
    mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
7026
#ifdef HAVE_REPLICATION
7027
    pthread_mutex_lock(&LOCK_active_mi);
7028
    rotate_relay_log(active_mi);
7029
    pthread_mutex_unlock(&LOCK_active_mi);
7030
#endif
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
7031 7032 7033 7034 7035

    /* flush slow and general logs */
    logger.flush_logs(thd);

    if (ha_flush_logs(NULL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7036
      result=1;
7037 7038
    if (flush_error_log())
      result=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7039
  }
7040
#ifdef HAVE_QUERY_CACHE
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
7041 7042
  if (options & REFRESH_QUERY_CACHE_FREE)
  {
7043
    query_cache.pack();				// FLUSH QUERY CACHE
7044
    options &= ~REFRESH_QUERY_CACHE;    // Don't flush cache, just free memory
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
7045 7046 7047
  }
  if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
  {
7048
    query_cache.flush();			// RESET QUERY CACHE
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
7049
  }
7050
#endif /*HAVE_QUERY_CACHE*/
7051 7052 7053 7054 7055
  /*
    Note that if REFRESH_READ_LOCK bit is set then REFRESH_TABLES is set too
    (see sql_yacc.yy)
  */
  if (options & (REFRESH_TABLES | REFRESH_READ_LOCK)) 
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7056
  {
7057
    if ((options & REFRESH_READ_LOCK) && thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7058
    {
7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069
      /*
        We must not try to aspire a global read lock if we have a write
        locked table. This would lead to a deadlock when trying to
        reopen (and re-lock) the table after the flush.
      */
      if (thd->locked_tables)
      {
        THR_LOCK_DATA **lock_p= thd->locked_tables->locks;
        THR_LOCK_DATA **end_p= lock_p + thd->locked_tables->lock_count;

        for (; lock_p < end_p; lock_p++)
monty@mysql.com's avatar
monty@mysql.com committed
7070
        {
7071 7072 7073 7074 7075
          if ((*lock_p)->type == TL_WRITE)
          {
            my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
            return 1;
          }
monty@mysql.com's avatar
monty@mysql.com committed
7076
        }
7077
      }
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
7078 7079 7080 7081
      /*
	Writing to the binlog could cause deadlocks, as we don't log
	UNLOCK TABLES
      */
7082
      tmp_write_to_binlog= 0;
7083
      if (lock_global_read_lock(thd))
7084
	return 1;                               // Killed
7085 7086
      result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1,
                                 tables);
7087
      if (make_global_read_lock_block_commit(thd)) // Killed
7088 7089 7090 7091 7092
      {
        /* Don't leave things in a half-locked state */
        unlock_global_read_lock(thd);
        return 1;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7093
    }
7094 7095
    else
      result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables);
7096
    my_dbopt_cleanup();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7097 7098 7099
  }
  if (options & REFRESH_HOSTS)
    hostname_cache_refresh();
monty@mysql.com's avatar
monty@mysql.com committed
7100
  if (thd && (options & REFRESH_STATUS))
7101
    refresh_status(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7102 7103
  if (options & REFRESH_THREADS)
    flush_thread_cache();
7104
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7105
  if (options & REFRESH_MASTER)
7106
  {
7107
    DBUG_ASSERT(thd);
7108
    tmp_write_to_binlog= 0;
7109
    if (reset_master(thd))
7110
    {
7111
      result=1;
7112 7113
      thd->fatal_error();                       // Ensure client get error
    }
7114
  }
7115
#endif
7116
#ifdef OPENSSL
7117 7118 7119 7120 7121 7122
   if (options & REFRESH_DES_KEY_FILE)
   {
     if (des_key_file)
       result=load_des_key_file(des_key_file);
   }
#endif
7123
#ifdef HAVE_REPLICATION
7124 7125
 if (options & REFRESH_SLAVE)
 {
7126
   tmp_write_to_binlog= 0;
7127
   pthread_mutex_lock(&LOCK_active_mi);
7128
   if (reset_slave(thd, active_mi))
7129
     result=1;
7130
   pthread_mutex_unlock(&LOCK_active_mi);
7131
 }
7132
#endif
7133
 if (options & REFRESH_USER_RESOURCES)
monty@mysql.com's avatar
monty@mysql.com committed
7134
   reset_mqh((LEX_USER *) NULL);
7135
 *write_to_binlog= tmp_write_to_binlog;
7136
 return result;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7137 7138
}

7139

7140
/*
7141
  kills a thread
7142 7143 7144 7145 7146

  SYNOPSIS
    kill_one_thread()
    thd			Thread class
    id			Thread id
7147
    only_kill_query     Should it kill the query or the connection
7148 7149 7150 7151 7152

  NOTES
    This is written such that we have a short lock on LOCK_thread_count
*/

7153
uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7154 7155 7156
{
  THD *tmp;
  uint error=ER_NO_SUCH_THREAD;
7157 7158
  DBUG_ENTER("kill_one_thread");
  DBUG_PRINT("enter", ("id=%lu only_kill=%d", id, only_kill_query));
7159 7160
  VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
  I_List_iterator<THD> it(threads);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7161 7162
  while ((tmp=it++))
  {
cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
7163 7164
    if (tmp->command == COM_DAEMON)
      continue;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7165 7166
    if (tmp->thread_id == id)
    {
7167 7168
      pthread_mutex_lock(&tmp->LOCK_delete);	// Lock from delete
      break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7169 7170 7171
    }
  }
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
7172 7173
  if (tmp)
  {
7174 7175
    if ((thd->security_ctx->master_access & SUPER_ACL) ||
	!strcmp(thd->security_ctx->user, tmp->security_ctx->user))
7176
    {
hf@genie.(none)'s avatar
SCRUM  
hf@genie.(none) committed
7177
      tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
7178 7179 7180 7181 7182 7183
      error=0;
    }
    else
      error=ER_KILL_DENIED_ERROR;
    pthread_mutex_unlock(&tmp->LOCK_delete);
  }
7184 7185 7186 7187
  DBUG_PRINT("exit", ("%d", error));
  DBUG_RETURN(error);
}

7188

7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201 7202
/*
  kills a thread and sends response

  SYNOPSIS
    sql_kill()
    thd			Thread class
    id			Thread id
    only_kill_query     Should it kill the query or the connection
*/

void sql_kill(THD *thd, ulong id, bool only_kill_query)
{
  uint error;
  if (!(error= kill_one_thread(thd, id, only_kill_query)))
7203
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7204
  else
7205
    my_error(error, MYF(0), id);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7206 7207
}

timour@mysql.com's avatar
timour@mysql.com committed
7208

7209 7210
	/* If pointer is not a null pointer, append filename to it */

cmiller@zippy.(none)'s avatar
cmiller@zippy.(none) committed
7211 7212
bool append_file_to_dir(THD *thd, const char **filename_ptr,
                        const char *table_name)
7213
{
7214
  char buff[FN_REFLEN],*ptr, *end;
7215 7216 7217 7218 7219 7220 7221
  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))
  {
7222
    my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr);
7223 7224 7225 7226
    return 1;
  }
  /* Fix is using unix filename format on dos */
  strmov(buff,*filename_ptr);
7227
  end=convert_dirname(buff, *filename_ptr, NullS);
7228
  if (!(ptr=thd->alloc((uint) (end-buff)+(uint) strlen(table_name)+1)))
7229 7230
    return 1;					// End of memory
  *filename_ptr=ptr;
7231
  strxmov(ptr,buff,table_name,NullS);
7232 7233
  return 0;
}
7234

7235

7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249
/*
  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;
7250 7251
  LEX *lex= thd->lex;
  if (lex->current_select != &lex->select_lex)
7252 7253
  {
    char command[80];
7254 7255
    strmake(command, lex->yylval->symbol.str,
	    min(lex->yylval->symbol.length, sizeof(command)-1));
7256
    my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command);
7257 7258 7259 7260
    return 1;
  }
  return 0;
}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7261

7262

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7263
Comp_creator *comp_eq_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7264
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7265
  return invert?(Comp_creator *)&ne_creator:(Comp_creator *)&eq_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7266 7267
}

7268

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7269
Comp_creator *comp_ge_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7270
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7271
  return invert?(Comp_creator *)&lt_creator:(Comp_creator *)&ge_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7272 7273
}

7274

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7275
Comp_creator *comp_gt_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7276
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7277
  return invert?(Comp_creator *)&le_creator:(Comp_creator *)&gt_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7278 7279
}

7280

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7281
Comp_creator *comp_le_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7282
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7283
  return invert?(Comp_creator *)&gt_creator:(Comp_creator *)&le_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7284 7285
}

7286

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7287
Comp_creator *comp_lt_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7288
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7289
  return invert?(Comp_creator *)&ge_creator:(Comp_creator *)&lt_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7290 7291
}

7292

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7293
Comp_creator *comp_ne_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7294
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7295
  return invert?(Comp_creator *)&eq_creator:(Comp_creator *)&ne_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7296
}
7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 7316


/*
  Construct ALL/ANY/SOME subquery Item

  SYNOPSIS
    all_any_subquery_creator()
    left_expr - pointer to left expression
    cmp - compare function creator
    all - true if we create ALL subquery
    select_lex - pointer on parsed subquery structure

  RETURN VALUE
    constructed Item (or 0 if out of memory)
*/
Item * all_any_subquery_creator(Item *left_expr,
				chooser_compare_func_creator cmp,
				bool all,
				SELECT_LEX *select_lex)
{
serg@serg.mylan's avatar
serg@serg.mylan committed
7317
  if ((cmp == &comp_eq_creator) && !all)       //  = ANY <=> IN
7318
    return new Item_in_subselect(left_expr, select_lex);
serg@serg.mylan's avatar
serg@serg.mylan committed
7319 7320

  if ((cmp == &comp_ne_creator) && all)        // <> ALL <=> NOT IN
7321 7322 7323
    return new Item_func_not(new Item_in_subselect(left_expr, select_lex));

  Item_allany_subselect *it=
7324
    new Item_allany_subselect(left_expr, cmp, select_lex, all);
7325
  if (all)
7326
    return it->upper_item= new Item_func_not_all(it);	/* ALL */
7327

7328
  return it->upper_item= new Item_func_nop_all(it);      /* ANY/SOME */
7329
}
7330 7331


7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343
/*
  CREATE INDEX and DROP INDEX are implemented by calling ALTER TABLE with
  the proper arguments.  This isn't very fast but it should work for most
  cases.

  In the future ALTER TABLE will notice that only added indexes
  and create these one by one for the existing table without having to do
  a full rebuild.

  One should normally create all indexes with CREATE TABLE or ALTER TABLE.
*/

7344
bool mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
7345 7346
{
  List<create_field> fields;
7347 7348
  ALTER_INFO alter_info;
  alter_info.flags= ALTER_ADD_INDEX;
7349 7350 7351
  HA_CREATE_INFO create_info;
  DBUG_ENTER("mysql_create_index");
  bzero((char*) &create_info,sizeof(create_info));
7352
  create_info.db_type= 0;
7353
  create_info.default_table_charset= thd->variables.collation_database;
7354
  DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->table_name,
7355
				&create_info, table_list,
7356
				fields, keys, 0, (ORDER*)0,
7357
                                0, &alter_info, 1));
7358 7359 7360
}


7361
bool mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info)
7362 7363 7364 7365 7366 7367
{
  List<create_field> fields;
  List<Key> keys;
  HA_CREATE_INFO create_info;
  DBUG_ENTER("mysql_drop_index");
  bzero((char*) &create_info,sizeof(create_info));
7368
  create_info.db_type= 0;
7369
  create_info.default_table_charset= thd->variables.collation_database;
7370 7371
  alter_info->clear();
  alter_info->flags= ALTER_DROP_INDEX;
7372
  DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->table_name,
7373
				&create_info, table_list,
7374
				fields, keys, 0, (ORDER*)0,
7375
                                0, alter_info, 1));
7376
}
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
7377 7378


7379 7380 7381 7382 7383
/*
  Multi update query pre-check

  SYNOPSIS
    multi_update_precheck()
7384
    thd		Thread handler
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
7385
    tables	Global/local table list (have to be the same)
7386

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7387
  RETURN VALUE
7388 7389
    FALSE OK
    TRUE  Error
7390
*/
7391

7392
bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
7393 7394 7395 7396 7397
{
  const char *msg= 0;
  TABLE_LIST *table;
  LEX *lex= thd->lex;
  SELECT_LEX *select_lex= &lex->select_lex;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
7398
  DBUG_ENTER("multi_update_precheck");
7399 7400 7401

  if (select_lex->item_list.elements != lex->value_list.elements)
  {
7402
    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
7403
    DBUG_RETURN(TRUE);
7404 7405 7406 7407 7408
  }
  /*
    Ensure that we have UPDATE or SELECT privilege for each table
    The exact privilege is checked in mysql_multi_update()
  */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
7409
  for (table= tables; table; table= table->next_local)
7410
  {
7411 7412 7413
    if (table->derived)
      table->grant.privilege= SELECT_ACL;
    else if ((check_access(thd, UPDATE_ACL, table->db,
7414 7415
                           &table->grant.privilege, 0, 1,
                           test(table->schema_table)) ||
7416 7417
              grant_option &&
              check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) &&
monty@mysql.com's avatar
monty@mysql.com committed
7418
             (check_access(thd, SELECT_ACL, table->db,
7419 7420
                           &table->grant.privilege, 0, 0,
                           test(table->schema_table)) ||
monty@mysql.com's avatar
monty@mysql.com committed
7421
              grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
7422
      DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7423

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
7424
    table->table_in_first_from_clause= 1;
7425
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7426 7427 7428
  /*
    Is there tables of subqueries?
  */
7429
  if (&lex->select_lex != lex->all_selects_list || lex->time_zone_tables_used)
7430
  {
7431
    DBUG_PRINT("info",("Checking sub query list"));
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
7432
    for (table= tables; table; table= table->next_global)
7433
    {
monty@mysql.com's avatar
monty@mysql.com committed
7434 7435 7436
      if (!my_tz_check_n_skip_implicit_tables(&table,
                                              lex->time_zone_tables_used) &&
          !table->table_in_first_from_clause)
7437 7438
      {
	if (check_access(thd, SELECT_ACL, table->db,
7439 7440
			 &table->grant.privilege, 0, 0,
                         test(table->schema_table)) ||
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7441
	    grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))
7442
	  DBUG_RETURN(TRUE);
7443 7444 7445 7446 7447 7448
      }
    }
  }

  if (select_lex->order_list.elements)
    msg= "ORDER BY";
7449
  else if (select_lex->select_limit)
7450 7451 7452 7453
    msg= "LIMIT";
  if (msg)
  {
    my_error(ER_WRONG_USAGE, MYF(0), "UPDATE", msg);
7454
    DBUG_RETURN(TRUE);
7455
  }
7456
  DBUG_RETURN(FALSE);
7457 7458 7459 7460 7461 7462 7463
}

/*
  Multi delete query pre-check

  SYNOPSIS
    multi_delete_precheck()
7464
    thd			Thread handler
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
7465
    tables		Global/local table list
7466

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7467
  RETURN VALUE
7468 7469
    FALSE OK
    TRUE  error
7470
*/
7471

7472
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
7473 7474 7475
{
  SELECT_LEX *select_lex= &thd->lex->select_lex;
  TABLE_LIST *aux_tables=
7476
    (TABLE_LIST *)thd->lex->auxiliary_table_list.first;
7477
  TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
7478
  DBUG_ENTER("multi_delete_precheck");
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7479

7480 7481
  /* sql_yacc guarantees that tables and aux_tables are not zero */
  DBUG_ASSERT(aux_tables != 0);
7482
  if (check_table_access(thd, SELECT_ACL, tables, 0))
7483 7484 7485 7486 7487 7488 7489 7490 7491 7492 7493
    DBUG_RETURN(TRUE);

  /*
    Since aux_tables list is not part of LEX::query_tables list we
    have to juggle with LEX::query_tables_own_last value to be able
    call check_table_access() safely.
  */
  thd->lex->query_tables_own_last= 0;
  if (check_table_access(thd, DELETE_ACL, aux_tables, 0))
  {
    thd->lex->query_tables_own_last= save_query_tables_own_last;
7494
    DBUG_RETURN(TRUE);
7495 7496 7497
  }
  thd->lex->query_tables_own_last= save_query_tables_own_last;

7498 7499
  if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7500 7501
    my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
               ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
7502
    DBUG_RETURN(TRUE);
7503
  }
7504 7505 7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 7525 7526 7527 7528
  DBUG_RETURN(FALSE);
}


/*
  Link tables in auxilary table list of multi-delete with corresponding
  elements in main table list, and set proper locks for them.

  SYNOPSIS
    multi_delete_set_locks_and_link_aux_tables()
      lex - pointer to LEX representing multi-delete

  RETURN VALUE
    FALSE - success
    TRUE  - error
*/

bool multi_delete_set_locks_and_link_aux_tables(LEX *lex)
{
  TABLE_LIST *tables= (TABLE_LIST*)lex->select_lex.table_list.first;
  TABLE_LIST *target_tbl;
  DBUG_ENTER("multi_delete_set_locks_and_link_aux_tables");

  lex->table_count= 0;

7529
  for (target_tbl= (TABLE_LIST *)lex->auxiliary_table_list.first;
7530
       target_tbl; target_tbl= target_tbl->next_local)
7531
  {
7532
    lex->table_count++;
7533 7534
    /* All tables in aux_tables must be found in FROM PART */
    TABLE_LIST *walk;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
7535
    for (walk= tables; walk; walk= walk->next_local)
7536
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7537 7538 7539
      if (!my_strcasecmp(table_alias_charset,
			 target_tbl->alias, walk->alias) &&
	  !strcmp(walk->db, target_tbl->db))
7540 7541 7542 7543
	break;
    }
    if (!walk)
    {
7544
      my_error(ER_UNKNOWN_TABLE, MYF(0),
7545
               target_tbl->table_name, "MULTI DELETE");
7546
      DBUG_RETURN(TRUE);
7547
    }
serg@serg.mylan's avatar
serg@serg.mylan committed
7548 7549 7550 7551 7552
    if (!walk->derived)
    {
      target_tbl->table_name= walk->table_name;
      target_tbl->table_name_length= walk->table_name_length;
    }
serg@serg.mylan's avatar
serg@serg.mylan committed
7553
    walk->updating= target_tbl->updating;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7554
    walk->lock_type= target_tbl->lock_type;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
7555
    target_tbl->correspondent_table= walk;	// Remember corresponding table
7556
  }
7557
  DBUG_RETURN(FALSE);
7558 7559 7560
}


bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7561 7562 7563 7564 7565
/*
  simple UPDATE query pre-check

  SYNOPSIS
    update_precheck()
7566 7567
    thd		Thread handler
    tables	Global table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7568 7569

  RETURN VALUE
7570 7571
    FALSE OK
    TRUE  Error
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7572
*/
7573

7574
bool update_precheck(THD *thd, TABLE_LIST *tables)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7575 7576 7577 7578
{
  DBUG_ENTER("update_precheck");
  if (thd->lex->select_lex.item_list.elements != thd->lex->value_list.elements)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7579
    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
7580
    DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7581
  }
7582
  DBUG_RETURN(check_one_table_access(thd, UPDATE_ACL, tables));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7583 7584 7585 7586 7587 7588 7589 7590
}


/*
  simple DELETE query pre-check

  SYNOPSIS
    delete_precheck()
7591 7592
    thd		Thread handler
    tables	Global table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7593 7594

  RETURN VALUE
7595 7596
    FALSE  OK
    TRUE   error
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7597
*/
7598

7599
bool delete_precheck(THD *thd, TABLE_LIST *tables)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7600 7601 7602
{
  DBUG_ENTER("delete_precheck");
  if (check_one_table_access(thd, DELETE_ACL, tables))
7603
    DBUG_RETURN(TRUE);
7604
  /* Set privilege for the WHERE clause */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7605
  tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
7606
  DBUG_RETURN(FALSE);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7607 7608 7609 7610 7611 7612 7613 7614
}


/*
  simple INSERT query pre-check

  SYNOPSIS
    insert_precheck()
7615 7616
    thd		Thread handler
    tables	Global table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7617 7618

  RETURN VALUE
7619 7620
    FALSE  OK
    TRUE   error
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7621
*/
7622

bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
7623
bool insert_precheck(THD *thd, TABLE_LIST *tables)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7624 7625 7626 7627
{
  LEX *lex= thd->lex;
  DBUG_ENTER("insert_precheck");

7628 7629 7630 7631
  /*
    Check that we have modify privileges for the first table and
    select privileges for the rest
  */
monty@mysql.com's avatar
monty@mysql.com committed
7632 7633 7634
  ulong privilege= (INSERT_ACL |
                    (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
                    (lex->value_list.elements ? UPDATE_ACL : 0));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7635 7636

  if (check_one_table_access(thd, privilege, tables))
7637
    DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7638

7639
  if (lex->update_list.elements != lex->value_list.elements)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7640
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7641
    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
7642
    DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7643
  }
7644
  DBUG_RETURN(FALSE);
7645
}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7646 7647 7648 7649 7650 7651 7652


/*
  CREATE TABLE query pre-check

  SYNOPSIS
    create_table_precheck()
7653 7654 7655
    thd			Thread handler
    tables		Global table list
    create_table	Table which will be created
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7656 7657

  RETURN VALUE
7658 7659
    FALSE   OK
    TRUE   Error
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7660
*/
7661

7662 7663
bool create_table_precheck(THD *thd, TABLE_LIST *tables,
                           TABLE_LIST *create_table)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7664 7665
{
  LEX *lex= thd->lex;
7666 7667
  SELECT_LEX *select_lex= &lex->select_lex;
  ulong want_priv;
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
7668
  bool error= TRUE;                                 // Error message is given
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7669
  DBUG_ENTER("create_table_precheck");
7670 7671 7672

  want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
              CREATE_TMP_ACL : CREATE_ACL);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7673 7674
  lex->create_info.alias= create_table->alias;
  if (check_access(thd, want_priv, create_table->db,
7675 7676
		   &create_table->grant.privilege, 0, 0,
                   test(create_table->schema_table)) ||
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7677 7678 7679
      check_merge_table_access(thd, create_table->db,
			       (TABLE_LIST *)
			       lex->create_info.merge_list.first))
7680 7681
    goto err;
  if (grant_option && want_priv != CREATE_TMP_ACL &&
7682
      check_grant(thd, want_priv, create_table, 0, 1, 0))
7683 7684 7685 7686 7687 7688
    goto err;

  if (select_lex->item_list.elements)
  {
    /* Check permissions for used tables in CREATE TABLE ... SELECT */

7689 7690
#ifdef NOT_NECESSARY_TO_CHECK_CREATE_TABLE_EXIST_WHEN_PREPARING_STATEMENT
    /* This code throws an ill error for CREATE TABLE t1 SELECT * FROM t1 */
7691
    /*
7692
      Only do the check for PS, because we on execute we have to check that
monty@mysql.com's avatar
monty@mysql.com committed
7693 7694
      against the opened tables to ensure we don't use a table that is part
      of the view (which can only be done after the table has been opened).
7695
    */
konstantin@mysql.com's avatar
konstantin@mysql.com committed
7696
    if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute())
7697
    {
monty@mysql.com's avatar
monty@mysql.com committed
7698 7699 7700 7701
      /*
        For temporary tables we don't have to check if the created table exists
      */
      if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
monty@mysql.com's avatar
monty@mysql.com committed
7702
          find_table_in_global_list(tables, create_table->db,
7703
                                    create_table->table_name))
monty@mysql.com's avatar
monty@mysql.com committed
7704
      {
7705
	error= FALSE;
monty@mysql.com's avatar
monty@mysql.com committed
7706 7707 7708
        goto err;
      }
    }
7709
#endif
7710 7711 7712
    if (tables && check_table_access(thd, SELECT_ACL, tables,0))
      goto err;
  }
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
7713
  error= FALSE;
7714 7715 7716

err:
  DBUG_RETURN(error);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7717
}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7718 7719 7720 7721 7722 7723 7724


/*
  negate given expression

  SYNOPSIS
    negate_expression()
7725
    thd  thread handler
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7726 7727 7728 7729 7730 7731 7732 7733 7734 7735 7736 7737 7738 7739 7740 7741 7742 7743 7744 7745 7746 7747 7748 7749 7750 7751 7752 7753
    expr expression for negation

  RETURN
    negated expression
*/

Item *negate_expression(THD *thd, Item *expr)
{
  Item *negated;
  if (expr->type() == Item::FUNC_ITEM &&
      ((Item_func *) expr)->functype() == Item_func::NOT_FUNC)
  {
    /* it is NOT(NOT( ... )) */
    Item *arg= ((Item_func *) expr)->arguments()[0];
    enum_parsing_place place= thd->lex->current_select->parsing_place;
    if (arg->is_bool_func() || place == IN_WHERE || place == IN_HAVING)
      return arg;
    /*
      if it is not boolean function then we have to emulate value of
      not(not(a)), it will be a != 0
    */
    return new Item_func_ne(arg, new Item_int((char*) "0", 0, 1));
  }

  if ((negated= expr->neg_transformer(thd)) != 0)
    return negated;
  return new Item_func_not(expr);
}
7754

7755 7756
/*
  Set the specified definer to the default value, which is the current user in
7757
  the thread.
7758 7759 7760 7761 7762 7763 7764
 
  SYNOPSIS
    get_default_definer()
    thd       [in] thread handler
    definer   [out] definer
*/
 
7765
void get_default_definer(THD *thd, LEX_USER *definer)
7766 7767 7768 7769 7770 7771 7772 7773 7774 7775
{
  const Security_context *sctx= thd->security_ctx;

  definer->user.str= (char *) sctx->priv_user;
  definer->user.length= strlen(definer->user.str);

  definer->host.str= (char *) sctx->priv_host;
  definer->host.length= strlen(definer->host.str);
}

7776

7777
/*
7778
  Create default definer for the specified THD.
7779 7780 7781 7782 7783 7784 7785 7786 7787 7788 7789 7790 7791 7792 7793 7794 7795 7796

  SYNOPSIS
    create_default_definer()
    thd         [in] thread handler

  RETURN
    On success, return a valid pointer to the created and initialized
    LEX_USER, which contains definer information.
    On error, return 0.
*/

LEX_USER *create_default_definer(THD *thd)
{
  LEX_USER *definer;

  if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
    return 0;

7797
  get_default_definer(thd, definer);
7798 7799 7800 7801 7802

  return definer;
}


7803
/*
7804
  Create definer with the given user and host names.
7805 7806

  SYNOPSIS
7807 7808 7809 7810
    create_definer()
    thd         [in] thread handler
    user_name   [in] user name
    host_name   [in] host name
7811 7812

  RETURN
7813
    On success, return a valid pointer to the created and initialized
7814
    LEX_USER, which contains definer information.
7815
    On error, return 0.
7816 7817
*/

7818
LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
7819
{
7820 7821 7822 7823
  LEX_USER *definer;

  /* Create and initialize. */

7824
  if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
7825 7826 7827 7828 7829 7830
    return 0;

  definer->user= *user_name;
  definer->host= *host_name;

  return definer;
7831
}
7832 7833 7834 7835 7836 7837 7838 7839 7840 7841 7842 7843 7844 7845 7846 7847 7848 7849 7850


/*
  Retuns information about user or current user.

  SYNOPSIS
    get_current_user()
    thd         [in] thread handler
    user        [in] user

  RETURN
    On success, return a valid pointer to initialized
    LEX_USER, which contains user information.
    On error, return 0.
*/

LEX_USER *get_current_user(THD *thd, LEX_USER *user)
{
  if (!user->user.str)  // current_user
7851 7852
    return create_default_definer(thd);

7853 7854
  return user;
}
7855 7856 7857 7858 7859 7860 7861 7862 7863 7864 7865 7866 7867 7868 7869 7870


/*
  Check that length of a string does not exceed some limit.

  SYNOPSIS
    check_string_length()
      str         string to be checked
      err_msg     error message to be displayed if the string is too long
      max_length  max length

  RETURN
    FALSE   the passed string is not longer than max_length
    TRUE    the passed string is longer than max_length
*/

7871 7872
bool check_string_length(LEX_STRING *str, const char *err_msg,
                         uint max_length)
7873
{
7874 7875
  if (str->length <= max_length)
    return FALSE;
7876 7877

  my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_length);
7878

7879 7880
  return TRUE;
}