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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3 4
   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
5
   the Free Software Foundation; version 2 of the License.
6

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

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

16
#define MYSQL_LEX 1
bk@work.mysql.com's avatar
bk@work.mysql.com committed
17
#include "mysql_priv.h"
18
#include "sql_repl.h"
19
#include "repl_failsafe.h"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
20 21 22 23
#include <m_ctype.h>
#include <myisam.h>
#include <my_dir.h>

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
24
#ifdef HAVE_INNOBASE_DB
25
#include "ha_innodb.h"
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
26 27
#endif

28 29 30 31
#ifdef HAVE_NDBCLUSTER_DB
#include "ha_ndbcluster.h"
#endif

32
#include "sp_head.h"
33
#include "sp.h"
34
#include "sp_cache.h"
35
#include "sql_trigger.h"
36

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

55 56 57 58 59 60
/* 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 || \
61
   (LP)->sql_command == SQLCOM_SHOW_CREATE_FUNC || \
62 63 64
   (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
   "FUNCTION" : "PROCEDURE")

65 66 67
#ifdef SOLARIS
extern "C" int gethostname(char *name, int namelen);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
68

69
#ifndef NO_EMBEDDED_ACCESS_CHECKS
70
static void time_out_user_resource_limits(THD *thd, USER_CONN *uc);
71
static int check_for_max_user_connections(THD *thd, USER_CONN *uc);
72
static void decrease_user_connections(USER_CONN *uc);
73
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
74
static bool check_db_used(THD *thd,TABLE_LIST *tables);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
75
static void remove_escape(char *name);
76 77
static bool append_file_to_dir(THD *thd, const char **filename_ptr,
			       const char *table_name);
78
static bool check_show_create_table_access(THD *thd, TABLE_LIST *table);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
79

80
const char *any_db="*any*";	// Special symbol for check_access
bk@work.mysql.com's avatar
bk@work.mysql.com committed
81 82 83 84

const char *command_name[]={
  "Sleep", "Quit", "Init DB", "Query", "Field List", "Create DB",
  "Drop DB", "Refresh", "Shutdown", "Statistics", "Processlist",
85
  "Connect","Kill","Debug","Ping","Time","Delayed insert","Change user",
86
  "Binlog Dump","Table Dump",  "Connect Out", "Register Slave",
87
  "Prepare", "Execute", "Long Data", "Close stmt",
88
  "Reset stmt", "Set option", "Fetch",
89
  "Error"					// Last command number
bk@work.mysql.com's avatar
bk@work.mysql.com committed
90 91
};

92 93 94 95
const char *xa_state_names[]={
  "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED"
};

bk@work.mysql.com's avatar
bk@work.mysql.com committed
96 97 98
#ifdef __WIN__
static void  test_signal(int sig_ptr)
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
99
#if !defined( DBUG_OFF)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
100 101
  MessageBox(NULL,"Test signal","DBUG",MB_OK);
#endif
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
102
#if defined(OS2)
103 104
  fprintf(stderr, "Test signal %d\n", sig_ptr);
  fflush(stderr);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
105
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
106 107 108 109
}
static void init_signals(void)
{
  int signals[7] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGBREAK,SIGABRT } ;
110
  for (int i=0 ; i < 7 ; i++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
111 112 113 114
    signal( signals[i], test_signal) ;
}
#endif

115 116 117 118 119
static void unlock_locked_tables(THD *thd)
{
  if (thd->locked_tables)
  {
    thd->lock=thd->locked_tables;
120
    thd->locked_tables=0;			// Will be automatically closed
121 122 123 124
    close_thread_tables(thd);			// Free tables
  }
}

125

126
static bool end_active_trans(THD *thd)
127
{
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
128
  int error=0;
129
  DBUG_ENTER("end_active_trans");
130
  if (unlikely(thd->in_sub_stmt))
131 132 133 134
  {
    my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
    DBUG_RETURN(1);
  }
135 136 137 138 139 140
  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);
  }
141
  if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN |
142
		      OPTION_TABLE_LOCK))
143
  {
144 145 146 147
    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;
148
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
149
    if (ha_commit(thd))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
150
      error=1;
151
    thd->options&= ~(ulong) OPTION_BEGIN;
152
    thd->transaction.all.modified_non_trans_table= FALSE;
153
  }
154
  DBUG_RETURN(error);
155 156
}

157 158 159
static bool begin_trans(THD *thd)
{
  int error=0;
160
  if (unlikely(thd->in_sub_stmt))
161 162 163 164
  {
    my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
    return 1;
  }
165 166 167 168 169 170 171 172 173 174
  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
  {
175
    LEX *lex= thd->lex;
176
    thd->transaction.all.modified_non_trans_table= FALSE;
177
    thd->options|= (ulong) OPTION_BEGIN;
178 179
    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
180
      error= ha_start_consistent_snapshot(thd);
181 182 183
  }
  return error;
}
184

monty@mysql.com's avatar
monty@mysql.com committed
185
#ifdef HAVE_REPLICATION
186 187 188
/*
  Returns true if all tables should be ignored
*/
189 190
inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables)
{
serg@serg.mylan's avatar
serg@serg.mylan committed
191
  return table_rules_on && tables && !tables_ok(thd,tables);
192
}
monty@mysql.com's avatar
monty@mysql.com committed
193
#endif
194 195


196 197 198 199 200 201 202 203 204 205 206 207
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;
}

208
#ifndef NO_EMBEDDED_ACCESS_CHECKS
209 210
static HASH hash_user_connections;

211 212
static int get_or_create_user_conn(THD *thd, const char *user,
				   const char *host,
peter@mysql.com's avatar
peter@mysql.com committed
213
				   USER_RESOURCES *mqh)
214
{
215
  int return_val= 0;
216
  uint temp_len, user_len;
217
  char temp_user[USER_HOST_BUFF_SIZE];
218 219 220 221 222
  struct  user_conn *uc;

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

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

259
}
260
#endif /* !NO_EMBEDDED_ACCESS_CHECKS */
261 262 263


/*
264 265
  Check if user exist and password supplied is correct. 

266 267
  SYNOPSIS
    check_user()
268
    thd          thread handle, thd->security_ctx->{host,user,ip} are used
269 270 271
    command      originator of the check: now check_user is called
                 during connect and change user procedures; used for 
                 logging.
272
    passwd       scrambled password received from client
273 274 275 276
    passwd_len   length of scrambled password
    db           database name to connect to, may be NULL
    check_count  dont know exactly

277
    Note, that host, user and passwd may point to communication buffer.
278
    Current implementation does not depend on that, but future changes
279 280 281
    should be done with this in mind; 'thd' is INOUT, all other params
    are 'IN'.

282
  RETURN VALUE
283 284
    0  OK; thd->security_ctx->user/master_access/priv_user/db_access and
       thd->db are updated; OK is sent to client;
285 286
   -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
287 288
*/

hf@deer.(none)'s avatar
hf@deer.(none) committed
289 290 291
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
292
{
293
  DBUG_ENTER("check_user");
294 295
  LEX_STRING db_str= { (char *) db, db ? strlen(db) : 0 };

monty@mysql.com's avatar
monty@mysql.com committed
296
#ifdef NO_EMBEDDED_ACCESS_CHECKS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
297
  thd->main_security_ctx.master_access= GLOBAL_ACLS;       // Full rights
298
  /* Change database if necessary */
299 300
  if (db && db[0])
  {
301 302 303 304
    /*
      thd->db is saved in caller and needs to be freed by caller if this
      function returns 0
    */
305
    thd->reset_db(NULL, 0);
306
    if (mysql_change_db(thd, &db_str, FALSE))
307
    {
308 309
      /* Send the error to the client */
      net_send_error(thd);
310
      DBUG_RETURN(-1);
holyfoot/hf@mysql.com/deer.(none)'s avatar
holyfoot/hf@mysql.com/deer.(none) committed
311
    }
312
  }
313
  send_ok(thd);
314
  DBUG_RETURN(0);
monty@mysql.com's avatar
monty@mysql.com committed
315 316
#else

317 318 319 320 321
  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);
  
322
  /*
323 324
    If the server is running in secure auth mode, short scrambles are 
    forbidden.
325
  */
326
  if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
327
  {
328
    net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE);
329 330
    mysql_log.write(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
    DBUG_RETURN(-1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
331
  }
332 333 334 335
  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
336

337
  /*
338
    Clear thd->db as it points to something, that will be freed when 
339
    connection is closed. We don't want to accidentally free a wrong pointer
340 341
    if connect failed. Also in case of 'CHANGE USER' failure, current
    database will be switched to 'no database selected'.
342
  */
343 344
  thd->reset_db(NULL, 0);

345
  USER_RESOURCES ur;
346
  int res= acl_getroot(thd, &ur, passwd, passwd_len);
hf@deer.(none)'s avatar
hf@deer.(none) committed
347
#ifndef EMBEDDED_LIBRARY
348
  if (res == -1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
349
  {
350 351 352 353 354 355
    /*
      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.
    */
356
    NET *net= &thd->net;
357
    if (opt_secure_auth_local)
358
    {
359
      net_printf_error(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE,
360 361
                       thd->main_security_ctx.user,
                       thd->main_security_ctx.host_or_ip);
362
      mysql_log.write(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
363 364
                      thd->main_security_ctx.user,
                      thd->main_security_ctx.host_or_ip);
365 366
      DBUG_RETURN(-1);
    }
monty@mysql.com's avatar
monty@mysql.com committed
367
    /* We have to read very specific packet size */
368
    if (send_old_password_request(thd) ||
monty@mysql.com's avatar
monty@mysql.com committed
369
        my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
370
    {
371 372 373 374 375
      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 */
376
    res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323);
377
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
378
#endif /*EMBEDDED_LIBRARY*/
379 380
  /* here res is always >= 0 */
  if (res == 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
381
  {
382 383
    if (!(thd->main_security_ctx.master_access &
          NO_ACCESS)) // authentication is OK
384
    {
385
      DBUG_PRINT("info",
386
                 ("Capabilities: %lu  packet_length: %ld  Host: '%s'  "
387
                  "Login user: '%s' Priv_user: '%s'  Using password: %s "
388
                  "Access: %lu  db: '%s'",
389 390 391 392 393
                  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,
394
                  passwd_len ? "yes": "no",
395 396
                  thd->main_security_ctx.master_access,
                  (thd->db ? thd->db : "*none*")));
397 398

      if (check_count)
399
      {
400
        VOID(pthread_mutex_lock(&LOCK_thread_count));
serg@serg.mylan's avatar
serg@serg.mylan committed
401
        bool count_ok= thread_count <= max_connections + delayed_insert_threads
402
                       || (thd->main_security_ctx.master_access & SUPER_ACL);
403 404
        VOID(pthread_mutex_unlock(&LOCK_thread_count));
        if (!count_ok)
serg@serg.mylan's avatar
serg@serg.mylan committed
405
        {                                         // too many connections
406
          net_send_error(thd, ER_CON_COUNT_ERROR);
407 408
          DBUG_RETURN(-1);
        }
409
      }
peter@mysql.com's avatar
peter@mysql.com committed
410

411
      /* Why logging is performed before all checks've passed? */
412
      mysql_log.write(thd, command,
413 414
                      (thd->main_security_ctx.priv_user ==
                       thd->main_security_ctx.user ?
415 416
                       (char*) "%s@%s on %s" :
                       (char*) "%s@%s as anonymous on %s"),
417 418
                      thd->main_security_ctx.user,
                      thd->main_security_ctx.host_or_ip,
419 420
                      db ? db : (char*) "");

421
      /*
422 423 424
        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.
425
      */
426
      thd->main_security_ctx.db_access=0;
427 428

      /* Don't allow user to connect if he has done too many queries */
429
      if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn ||
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
430
	   max_user_connections) &&
431
	  get_or_create_user_conn(thd,
432 433 434 435
            (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),
436
            &ur))
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
437 438
	DBUG_RETURN(-1);
      if (thd->user_connect &&
439 440
	  (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
441 442 443
	   max_user_connections) &&
	  check_for_max_user_connections(thd, thd->user_connect))
	DBUG_RETURN(-1);
444

445
      /* Change database if necessary */
446
      if (db && db[0])
447
      {
448
        if (mysql_change_db(thd, &db_str, FALSE))
449
        {
450 451
          /* Send error to the client */
          net_send_error(thd);
452 453 454 455
          if (thd->user_connect)
            decrease_user_connections(thd->user_connect);
          DBUG_RETURN(-1);
        }
456
      }
457
      send_ok(thd);
458 459 460
      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
461 462
    }
  }
463
  else if (res == 2) // client gave short hash, server has long hash
464
  {
465
    net_printf_error(thd, ER_NOT_SUPPORTED_AUTH_MODE);
466 467
    mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE));
    DBUG_RETURN(-1);
468
  }
469
  net_printf_error(thd, ER_ACCESS_DENIED_ERROR,
470 471
                   thd->main_security_ctx.user,
                   thd->main_security_ctx.host_or_ip,
472
                   passwd_len ? ER(ER_YES) : ER(ER_NO));
473
  mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
474 475
                  thd->main_security_ctx.user,
                  thd->main_security_ctx.host_or_ip,
476 477
                  passwd_len ? ER(ER_YES) : ER(ER_NO));
  DBUG_RETURN(-1);
monty@mysql.com's avatar
monty@mysql.com committed
478
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
479 480
}

481
/*
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
482 483
  Check for maximum allowable user connections, if the mysqld server is
  started with corresponding variable that is greater then 0.
484 485
*/

486 487
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
488 489 490 491 492
{
  *length=buff->len;
  return (byte*) buff->user;
}

493
extern "C" void free_user(struct user_conn *uc)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
494 495 496 497
{
  my_free((char*) uc,MYF(0));
}

peter@mysql.com's avatar
peter@mysql.com committed
498
void init_max_user_conn(void)
499
{
500
#ifndef NO_EMBEDDED_ACCESS_CHECKS
501 502
  (void) hash_init(&hash_user_connections,system_charset_info,max_connections,
		   0,0,
503
		   (hash_get_key) get_key_conn, (hash_free_key) free_user,
504
		   0);
505
#endif
506 507 508
}


monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
/*
  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
*/

526 527
#ifndef NO_EMBEDDED_ACCESS_CHECKS

528
static int check_for_max_user_connections(THD *thd, USER_CONN *uc)
529
{
530
  int error=0;
531
  DBUG_ENTER("check_for_max_user_connections");
peter@mysql.com's avatar
peter@mysql.com committed
532

533
  (void) pthread_mutex_lock(&LOCK_user_conn);
534
  if (max_user_connections && !uc->user_resources.user_conn &&
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
535
      max_user_connections < (uint) uc->connections)
536
  {
537
    net_printf_error(thd, ER_TOO_MANY_USER_CONNECTIONS, uc->user);
538 539
    error=1;
    goto end;
540
  }
541
  time_out_user_resource_limits(thd, uc);
542 543 544 545 546 547 548 549 550 551 552
  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)
553
  {
554
    net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user,
jimw@mysql.com's avatar
jimw@mysql.com committed
555
                     "max_connections_per_hour",
556
                     (long) uc->user_resources.conn_per_hour);
557 558 559
    error=1;
    goto end;
  }
560
  uc->conn_per_hour++;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
561 562

  end:
563 564
  if (error)
    uc->connections--; // no need for decrease_user_connections() here
565
  (void) pthread_mutex_unlock(&LOCK_user_conn);
566
  DBUG_RETURN(error);
567 568
}

monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
/*
  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.
*/

587
static void decrease_user_connections(USER_CONN *uc)
588
{
589
  DBUG_ENTER("decrease_user_connections");
590 591 592
  (void) pthread_mutex_lock(&LOCK_user_conn);
  DBUG_ASSERT(uc->connections);
  if (!--uc->connections && !mqh_used)
593 594
  {
    /* Last connection for user; Delete it */
595
    (void) hash_delete(&hash_user_connections,(byte*) uc);
596
  }
597
  (void) pthread_mutex_unlock(&LOCK_user_conn);
598
  DBUG_VOID_RETURN;
599 600
}

601 602
#endif /* NO_EMBEDDED_ACCESS_CHECKS */

603

604 605
void free_max_user_conn(void)
{
606
#ifndef NO_EMBEDDED_ACCESS_CHECKS
607
  hash_free(&hash_user_connections);
608
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
609 610
}

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

612

613 614 615
/*
  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
616 617 618

  sql_command is actually set to SQLCOM_END sometimes
  so we need the +1 to include it in the array.
619 620 621 622 623 624

  numbers are:
     0  - read-only query
  != 0  - query that may change a table
     2  - query that returns meaningful ROW_COUNT() -
          a number of modified rows
625 626
*/

kent@mysql.com's avatar
kent@mysql.com committed
627
char  uc_update_queries[SQLCOM_END+1];
628 629 630

void init_update_queries(void)
{
kent@mysql.com's avatar
kent@mysql.com committed
631 632
  bzero((gptr) &uc_update_queries, sizeof(uc_update_queries));

633 634 635
  uc_update_queries[SQLCOM_CREATE_TABLE]=1;
  uc_update_queries[SQLCOM_CREATE_INDEX]=1;
  uc_update_queries[SQLCOM_ALTER_TABLE]=1;
636 637 638 639 640 641
  uc_update_queries[SQLCOM_UPDATE]=2;
  uc_update_queries[SQLCOM_UPDATE_MULTI]=2;
  uc_update_queries[SQLCOM_INSERT]=2;
  uc_update_queries[SQLCOM_INSERT_SELECT]=2;
  uc_update_queries[SQLCOM_DELETE]=2;
  uc_update_queries[SQLCOM_DELETE_MULTI]=2;
642 643 644 645 646
  uc_update_queries[SQLCOM_TRUNCATE]=1;
  uc_update_queries[SQLCOM_DROP_TABLE]=1;
  uc_update_queries[SQLCOM_LOAD]=1;
  uc_update_queries[SQLCOM_CREATE_DB]=1;
  uc_update_queries[SQLCOM_DROP_DB]=1;
647 648
  uc_update_queries[SQLCOM_REPLACE]=2;
  uc_update_queries[SQLCOM_REPLACE_SELECT]=2;
649 650 651 652
  uc_update_queries[SQLCOM_RENAME_TABLE]=1;
  uc_update_queries[SQLCOM_BACKUP_TABLE]=1;
  uc_update_queries[SQLCOM_RESTORE_TABLE]=1;
  uc_update_queries[SQLCOM_DROP_INDEX]=1;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
653 654
  uc_update_queries[SQLCOM_CREATE_VIEW]=1;
  uc_update_queries[SQLCOM_DROP_VIEW]=1;
655 656
}

657 658
bool is_update_query(enum enum_sql_command command)
{
kent@mysql.com's avatar
kent@mysql.com committed
659
  DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
660
  return uc_update_queries[command] != 0;
661
}
662

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
663
/*
664 665
  Reset per-hour user resource limits when it has been more than
  an hour since they were last checked
666

667 668 669 670
  SYNOPSIS:
    time_out_user_resource_limits()
    thd			Thread handler
    uc			User connection details
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
671

672 673 674 675
  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.
*/
676

677 678
#ifndef NO_EMBEDDED_ACCESS_CHECKS

679
static void time_out_user_resource_limits(THD *thd, USER_CONN *uc)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
680
{
681
  time_t check_time = thd->start_time ?  thd->start_time : time(NULL);
682
  DBUG_ENTER("time_out_user_resource_limits");
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
683

684
  /* If more than a hour since last check, reset resource checking */
685 686 687 688 689 690 691
  if (check_time  - uc->intime >= 3600)
  {
    uc->questions=1;
    uc->updates=0;
    uc->conn_per_hour=0;
    uc->intime=check_time;
  }
692 693 694 695 696 697 698 699 700 701 702

  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)
{
703
  bool error= 0;
704 705 706 707 708 709 710 711
  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);

712
  /* Check that we have not done too many questions / hour */
713 714 715
  if (uc->user_resources.questions &&
      uc->questions++ >= uc->user_resources.questions)
  {
716 717
    net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions",
                     (long) uc->user_resources.questions);
718 719 720
    error=1;
    goto end;
  }
721
  if (check_command < (uint) SQLCOM_END)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
722
  {
723 724 725 726
    /* Check that we have not done too many updates / hour */
    if (uc->user_resources.updates && uc_update_queries[check_command] &&
	uc->updates++ >= uc->user_resources.updates)
    {
727 728
      net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user, "max_updates",
                       (long) uc->user_resources.updates);
729 730 731
      error=1;
      goto end;
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
732 733
  }
end:
734
  (void) pthread_mutex_unlock(&LOCK_user_conn);
735
  DBUG_RETURN(error);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
736 737
}

738 739
#endif /* NO_EMBEDDED_ACCESS_CHECKS */

740

monty@mysql.com's avatar
monty@mysql.com committed
741
static void reset_mqh(LEX_USER *lu, bool get_them= 0)
742
{
monty@mysql.com's avatar
monty@mysql.com committed
743
#ifndef NO_EMBEDDED_ACCESS_CHECKS
744
  (void) pthread_mutex_lock(&LOCK_user_conn);
peter@mysql.com's avatar
peter@mysql.com committed
745
  if (lu)  // for GRANT
746
  {
747
    USER_CONN *uc;
748
    uint temp_len=lu->user.length+lu->host.length+2;
749
    char temp_user[USER_HOST_BUFF_SIZE];
750

751 752
    memcpy(temp_user,lu->user.str,lu->user.length);
    memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length);
753
    temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0;
754
    if ((uc = (struct  user_conn *) hash_search(&hash_user_connections,
755
						(byte*) temp_user, temp_len)))
756 757
    {
      uc->questions=0;
758
      get_mqh(temp_user,&temp_user[lu->user.length+1],uc);
759 760
      uc->updates=0;
      uc->conn_per_hour=0;
761 762
    }
  }
monty@mysql.com's avatar
monty@mysql.com committed
763
  else
764
  {
monty@mysql.com's avatar
monty@mysql.com committed
765
    /* for FLUSH PRIVILEGES and FLUSH USER_RESOURCES */
766
    for (uint idx=0;idx < hash_user_connections.records; idx++)
767
    {
monty@mysql.com's avatar
monty@mysql.com committed
768 769
      USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections,
						      idx);
770 771 772 773 774
      if (get_them)
	get_mqh(uc->user,uc->host,uc);
      uc->questions=0;
      uc->updates=0;
      uc->conn_per_hour=0;
775 776
    }
  }
777
  (void) pthread_mutex_unlock(&LOCK_user_conn);
monty@mysql.com's avatar
monty@mysql.com committed
778
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
779
}
780

781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
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
812
/*
813
    Perform handshake, authorize client and update thd ACL variables.
814
  SYNOPSIS
815
    check_connection()
816
    thd  thread handle
817 818

  RETURN
819
     0  success, OK is sent to user, thd is updated.
820 821
    -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
822 823
*/

hf@deer.(none)'s avatar
hf@deer.(none) committed
824 825
#ifndef EMBEDDED_LIBRARY
static int check_connection(THD *thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
826
{
827
  uint connect_errors= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
828
  NET *net= &thd->net;
monty@mysql.com's avatar
monty@mysql.com committed
829 830
  ulong pkt_len= 0;
  char *end;
831

832 833
  DBUG_PRINT("info",
             ("New connection received on %s", vio_description(net->vio)));
834
#ifdef SIGNAL_WITH_VIO_CLOSE
835
  thd->set_active_vio(net->vio);
836
#endif
837

838
  if (!thd->main_security_ctx.host)         // If TCP/IP connection
bk@work.mysql.com's avatar
bk@work.mysql.com committed
839
  {
840
    char ip[30];
841

842
    if (vio_peer_addr(net->vio, ip, &thd->peer_port))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
843
      return (ER_BAD_HOST_ERROR);
844
    if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(0))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
845
      return (ER_OUT_OF_RESOURCES);
846
    thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
monty@mysql.com's avatar
monty@mysql.com committed
847
    vio_in_addr(net->vio,&thd->remote.sin_addr);
848
    if (!(specialflag & SPECIAL_NO_RESOLVE))
849
    {
850
      vio_in_addr(net->vio,&thd->remote.sin_addr);
851 852
      thd->main_security_ctx.host=
        ip_to_hostname(&thd->remote.sin_addr, &connect_errors);
853
      /* Cut very long hostnames to avoid possible overflows */
854
      if (thd->main_security_ctx.host)
855
      {
856 857 858 859
        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;
860
      }
861 862
      if (connect_errors > max_connect_errors)
        return(ER_HOST_IS_BLOCKED);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
863
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
864
    DBUG_PRINT("info",("Host: %s  ip: %s",
865 866 867 868 869
		       (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
870 871
      return(ER_HOST_NOT_PRIVILEGED);
  }
872
  else /* Hostname given means that the connection was on a socket */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
873
  {
874 875 876
    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;
877 878
    /* Reset sin_addr */
    bzero((char*) &thd->remote, sizeof(thd->remote));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
879 880 881
  }
  vio_keepalive(net->vio, TRUE);
  {
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
882
    /* buff[] needs to big enough to hold the server_version variable */
883
    char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
884 885
    ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB |
			  CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION);
886

887 888 889 890 891
    if (opt_using_transactions)
      client_flags|=CLIENT_TRANSACTIONS;
#ifdef HAVE_COMPRESS
    client_flags |= CLIENT_COMPRESS;
#endif /* HAVE_COMPRESS */
892 893
#ifdef HAVE_OPENSSL
    if (ssl_acceptor_fd)
894
      client_flags |= CLIENT_SSL;       /* Wow, SSL is available! */
895
#endif /* HAVE_OPENSSL */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
896

897 898 899 900 901 902 903 904 905 906 907
    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
908
      tail: that's why first part of the scramble is placed here, and second
909 910
      part at the end of packet.
    */
911
    end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1;
912 913 914
   
    int2store(end, client_flags);
    /* write server characteristics: up to 16 bytes allowed */
915
    end[2]=(char) default_charset_info->number;
916 917 918 919 920 921 922 923 924
    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
925
			  (uint) (end-buff)) ||
926
	(pkt_len= my_net_read(net)) == packet_error ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
927 928 929 930 931 932 933 934 935 936 937
	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);
938
  if (thd->packet.alloc(thd->variables.net_buffer_length))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
939 940 941
    return(ER_OUT_OF_RESOURCES);

  thd->client_capabilities=uint2korr(net->read_pos);
942 943 944 945
  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
946
    DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
947
    thd_init_client_charset(thd, (uint) net->read_pos[8]);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
948
    thd->update_charset();
949
    end= (char*) net->read_pos+32;
950 951 952 953 954 955 956
  }
  else
  {
    thd->max_client_packet_length= uint3korr(net->read_pos+2);
    end= (char*) net->read_pos+5;
  }

957
  if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
958
    thd->variables.sql_mode|= MODE_IGNORE_SPACE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
959
#ifdef HAVE_OPENSSL
960
  DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
961 962 963
  if (thd->client_capabilities & CLIENT_SSL)
  {
    /* Do the SSL layering. */
964 965 966 967 968
    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
969
    DBUG_PRINT("info", ("IO layer change in progress..."));
970
    if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout))
971
    {
972
      DBUG_PRINT("error", ("Failed to accept new SSL connection"));
973
      inc_host_errors(&thd->remote.sin_addr);
peter@mysql.com's avatar
peter@mysql.com committed
974
      return(ER_HANDSHAKE_ERROR);
975
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
976
    DBUG_PRINT("info", ("Reading user information over SSL layer"));
977
    if ((pkt_len= my_net_read(net)) == packet_error ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
978 979
	pkt_len < NORMAL_HANDSHAKE_SIZE)
    {
980 981
      DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
			   pkt_len));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
982 983 984 985
      inc_host_errors(&thd->remote.sin_addr);
      return(ER_HANDSHAKE_ERROR);
    }
  }
986 987 988
#endif

  if (end >= (char*) net->read_pos+ pkt_len +2)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
989
  {
990 991
    inc_host_errors(&thd->remote.sin_addr);
    return(ER_HANDSHAKE_ERROR);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
992 993 994
  }

  if (thd->client_capabilities & CLIENT_INTERACTIVE)
995
    thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
996
  if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
997 998
      opt_using_transactions)
    net->return_status= &thd->server_status;
peter@mysql.com's avatar
peter@mysql.com committed
999

1000 1001
  char *user= end;
  char *passwd= strend(user)+1;
1002
  uint user_len= passwd - user - 1;
1003
  char *db= passwd;
1004 1005
  char db_buff[NAME_LEN + 1];           // buffer to store db in utf8
  char user_buff[USERNAME_LENGTH + 1];	// buffer to store user in utf8
1006 1007 1008
  uint dummy_errors;

  /*
1009 1010 1011
    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'.
1012 1013 1014

    Cast *passwd to an unsigned char, so that it doesn't extend the sign for
    *passwd > 127 and become 2**32-127 after casting to uint.
1015
  */
1016
  uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
1017
    (uchar)(*passwd++) : strlen(passwd);
1018 1019
  db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
    db + passwd_len + 1 : 0;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1020 1021
  uint db_len= db ? strlen(db) : 0;

serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1022
  if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len)
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1023 1024 1025 1026
  {
    inc_host_errors(&thd->remote.sin_addr);
    return ER_HANDSHAKE_ERROR;
  }
peter@mysql.com's avatar
peter@mysql.com committed
1027

kostja@oak.local's avatar
kostja@oak.local committed
1028 1029
  /* Since 4.1 all database names are stored in utf8 */
  if (db)
peter@mysql.com's avatar
peter@mysql.com committed
1030
  {
1031 1032
    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
1033
                             db, db_len,
1034
                             thd->charset(), &dummy_errors)]= 0;
1035
    db= db_buff;
kostja@oak.local's avatar
kostja@oak.local committed
1036
  }
peter@mysql.com's avatar
peter@mysql.com committed
1037

1038 1039 1040
  user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1,
                                       system_charset_info, user, user_len,
                                       thd->charset(), &dummy_errors)]= '\0';
1041
  user= user_buff;
1042

1043 1044 1045 1046 1047 1048 1049 1050
  /* 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;
  }

1051 1052 1053
  if (thd->main_security_ctx.user)
    x_free(thd->main_security_ctx.user);
  if (!(thd->main_security_ctx.user= my_strdup(user, MYF(0))))
1054
    return (ER_OUT_OF_RESOURCES);
1055
  return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1056 1057
}

1058

1059 1060
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
1061 1062 1063 1064
{
  Vio* save_vio;
  ulong save_client_capabilities;

1065 1066 1067 1068 1069 1070 1071 1072 1073
  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
1074 1075
  save_client_capabilities= thd->client_capabilities;
  thd->client_capabilities|= CLIENT_MULTI_QUERIES;
1076 1077 1078 1079
  /*
    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
1080 1081
  save_vio= thd->net.vio;
  thd->net.vio= 0;
1082
  thd->net.no_send_error= 0;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1083
  dispatch_command(COM_QUERY, thd, thd->query, thd->query_length+1);
1084
  rw_unlock(var_mutex);
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1085 1086 1087 1088 1089
  thd->client_capabilities= save_client_capabilities;
  thd->net.vio= save_vio;
}


1090
pthread_handler_t handle_one_connection(void *arg)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1091 1092 1093
{
  THD *thd=(THD*) arg;
  uint launch_time  =
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1094
    (uint) ((thd->thr_create_time = time(NULL)) - thd->connect_time);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1095 1096 1097 1098 1099
  if (launch_time >= slow_launch_time)
    statistic_increment(slow_launch_threads,&LOCK_status );

  pthread_detach_this_thread();

1100
#if !defined( __WIN__) && !defined(OS2)	// Win32 calls this in pthread_create
monty@mysql.com's avatar
monty@mysql.com committed
1101
  /* The following calls needs to be done before we call DBUG_ macros */
1102
  if (!(test_flags & TEST_NO_THREADS) & my_thread_init())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1103
  {
1104
    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
1105
    statistic_increment(aborted_connects,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1106 1107 1108 1109 1110
    end_thread(thd,0);
    return 0;
  }
#endif

1111 1112 1113 1114 1115 1116 1117
  /*
    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.
  */
1118
  DBUG_PRINT("info", ("handle_one_connection called by thread %lu\n",
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1119
		      thd->thread_id));
monty@mysql.com's avatar
monty@mysql.com committed
1120
  /* 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
1121

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1122
#if defined(__WIN__)
1123
  init_signals();
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1124
#elif !defined(OS2) && !defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1125 1126 1127 1128
  sigset_t set;
  VOID(sigemptyset(&set));			// Get mask in use
  VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
#endif
1129
  thd->thread_stack= (char*) &thd;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1130 1131
  if (thd->store_globals())
  {
1132
    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
1133
    statistic_increment(aborted_connects,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1134 1135 1136 1137 1138 1139 1140 1141
    end_thread(thd,0);
    return 0;
  }

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

1145
    /* Use "connect_timeout" value during connection phase */
1146 1147
    my_net_set_read_timeout(net, connect_timeout);
    my_net_set_write_timeout(net, connect_timeout);
1148

1149
    if ((error=check_connection(thd)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1150 1151
    {						// Wrong permissions
      if (error > 0)
1152
	net_printf_error(thd, error, sctx->host_or_ip);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1153 1154
#ifdef __NT__
      if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
1155
	my_sleep(1000);				/* must wait after eof() */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1156
#endif
1157
      statistic_increment(aborted_connects,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1158 1159
      goto end_thread;
    }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1160
#ifdef __NETWARE__
1161
    netware_reg_user(sctx->ip, sctx->user, "MySQL");
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1162
#endif
1163
    if (thd->variables.max_join_size == HA_POS_ERROR)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1164 1165 1166 1167
      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
1168
    thd->version= refresh_version;
1169
    thd->proc_info= 0;
1170
    thd->command= COM_SLEEP;
1171
    thd->init_for_queries();
monty@mysql.com's avatar
monty@mysql.com committed
1172

1173
    if (sys_init_connect.value_length && !(sctx->master_access & SUPER_ACL))
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1174
    {
1175 1176
      execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect);
      if (thd->query_error)
1177
      {
pem@mysql.comhem.se's avatar
pem@mysql.comhem.se committed
1178
	thd->killed= THD::KILL_CONNECTION;
1179 1180 1181 1182 1183 1184
        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);
      }
1185 1186
      thd->proc_info=0;
      thd->init_for_queries();
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1187 1188
    }

1189
    /* Connect completed, set read/write timeouts back to tdefault */
1190 1191
    my_net_set_read_timeout(net, thd->variables.net_read_timeout);
    my_net_set_write_timeout(net, thd->variables.net_write_timeout);
1192

1193 1194
    while (!net->error && net->vio != 0 &&
           !(thd->killed == THD::KILL_CONNECTION))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1195
    {
1196
      net->no_send_error= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1197 1198 1199
      if (do_command(thd))
	break;
    }
1200 1201
    if (thd->user_connect)
      decrease_user_connections(thd->user_connect);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1202
    if (net->error && net->vio != 0 && net->report_error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1203
    {
1204
      if (!thd->killed && thd->variables.log_warnings > 1)
monty@mysql.com's avatar
monty@mysql.com committed
1205
	sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
serg@serg.mylan's avatar
serg@serg.mylan committed
1206
                          thd->thread_id,(thd->db ? thd->db : "unconnected"),
1207 1208
                          sctx->user ? sctx->user : "unauthenticated",
                          sctx->host_or_ip,
serg@serg.mylan's avatar
serg@serg.mylan committed
1209 1210
                          (net->last_errno ? ER(net->last_errno) :
                           ER(ER_UNKNOWN_ERROR)));
1211
      net_send_error(thd, net->last_errno, NullS);
1212
      statistic_increment(aborted_threads,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1213
    }
1214 1215 1216 1217
    else if (thd->killed)
    {
      statistic_increment(aborted_threads,&LOCK_status);
    }
1218
    
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1219
end_thread:
1220
    close_connection(thd, 0, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1221 1222 1223 1224 1225 1226
    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;
1227
    thd->thread_stack= (char*) &thd;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1228 1229 1230 1231 1232
  } 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
1233 1234
#endif /* EMBEDDED_LIBRARY */

1235 1236 1237 1238
/*
  Execute commands from bootstrap_file.
  Used when creating the initial grant tables
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1239

1240
pthread_handler_t handle_bootstrap(void *arg)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1241
{
1242 1243 1244
  THD *thd=(THD*) arg;
  FILE *file=bootstrap_file;
  char *buff;
1245
  const char* found_semicolon= NULL;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1246

1247
  /* The following must be called before DBUG_ENTER */
1248
  thd->thread_stack= (char*) &thd;
1249
  if (my_thread_init() || thd->store_globals())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1250
  {
hf@deer.(none)'s avatar
hf@deer.(none) committed
1251
#ifndef EMBEDDED_LIBRARY
1252
    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1253
#endif
1254
    thd->fatal_error();
1255
    goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1256
  }
1257 1258
  DBUG_ENTER("handle_bootstrap");

hf@deer.(none)'s avatar
hf@deer.(none) committed
1259
#ifndef EMBEDDED_LIBRARY
1260 1261
  pthread_detach_this_thread();
  thd->thread_stack= (char*) &thd;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1262
#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1263
  sigset_t set;
1264 1265
  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
1266
#endif
hf@deer.(none)'s avatar
hf@deer.(none) committed
1267
#endif /* EMBEDDED_LIBRARY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1268

1269
  if (thd->variables.max_join_size == HA_POS_ERROR)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1270 1271 1272 1273
    thd->options |= OPTION_BIG_SELECTS;

  thd->proc_info=0;
  thd->version=refresh_version;
1274 1275
  thd->security_ctx->priv_user=
    thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
1276 1277 1278 1279 1280 1281
  /*
    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
1282

1283
  buff= (char*) thd->net.buff;
1284
  thd->init_for_queries();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1285 1286
  while (fgets(buff, thd->net.max_packet, file))
  {
monty@mishka.local's avatar
monty@mishka.local committed
1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306
   ulong length= (ulong) strlen(buff);
   while (buff[length-1] != '\n' && !feof(file))
   {
     /*
       We got only a part of the current string. Will try to increase
       net buffer then read the rest of the current string.
     */
     if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
     {
       net_send_error(thd, ER_NET_PACKET_TOO_LARGE, NullS);
       thd->fatal_error();
       break;
     }
     buff= (char*) thd->net.buff;
     fgets(buff + length, thd->net.max_packet - length, file);
     length+= (ulong) strlen(buff + length);
   }
   if (thd->is_fatal_error)
     break;

1307
    while (length && (my_isspace(thd->charset(), buff[length-1]) ||
1308
           buff[length-1] == ';'))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1309 1310
      length--;
    buff[length]=0;
1311
    thd->query_length=length;
1312 1313
    thd->query= thd->memdup_w_gap(buff, length+1, 
				  thd->db_length+1+QUERY_CACHE_FLAGS_SIZE);
1314
    thd->query[length] = '\0';
1315
    DBUG_PRINT("query",("%-.4096s",thd->query));
1316 1317 1318 1319
    /*
      We don't need to obtain LOCK_thread_count here because in bootstrap
      mode we have only one thread.
    */
1320
    thd->query_id=next_query_id();
1321
    thd->set_time();
1322
    mysql_parse(thd, thd->query, length, & found_semicolon);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1323
    close_thread_tables(thd);			// Free tables
1324

1325
    if (thd->is_fatal_error)
1326
      break;
1327 1328 1329 1330 1331 1332 1333 1334 1335

    if (thd->net.report_error)
    {
      /* The query failed, send error to log and abort bootstrap */
      net_send_error(thd);
      thd->fatal_error();
      break;
    }

1336
    free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
1337
#ifdef USING_TRANSACTIONS
1338
    free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC));
1339
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1340
  }
1341 1342

end:
1343
  /* Remember the exit code of bootstrap */
1344 1345 1346 1347 1348 1349
  bootstrap_error= thd->is_fatal_error;

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

hf@deer.(none)'s avatar
hf@deer.(none) committed
1350
#ifndef EMBEDDED_LIBRARY
1351 1352 1353
  (void) pthread_mutex_lock(&LOCK_thread_count);
  thread_count--;
  (void) pthread_mutex_unlock(&LOCK_thread_count);
1354
  (void) pthread_cond_broadcast(&COND_thread_count);
1355 1356
  my_thread_end();
  pthread_exit(0);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1357
#endif
1358
  DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1359 1360 1361
}


1362 1363 1364 1365
    /* This works because items are allocated with sql_alloc() */

void cleanup_items(Item *item)
{
monty@mysql.com's avatar
monty@mysql.com committed
1366
  DBUG_ENTER("cleanup_items");  
1367 1368
  for (; item ; item=item->next)
    item->cleanup();
monty@mysql.com's avatar
monty@mysql.com committed
1369
  DBUG_VOID_RETURN;
1370 1371
}

1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391
/*
  Handle COM_TABLE_DUMP command

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

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

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

static
int mysql_table_dump(THD* thd, char* db, char* tbl_name)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1392 1393 1394 1395 1396 1397
{
  TABLE* table;
  TABLE_LIST* table_list;
  int error = 0;
  DBUG_ENTER("mysql_table_dump");
  db = (db && db[0]) ? db : thd->db;
1398
  if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST))))
1399
    DBUG_RETURN(1); // out of memory
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1400
  table_list->db= db;
1401
  table_list->table_name= table_list->alias= tbl_name;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1402 1403
  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
1404

1405 1406
  if (!db || check_db_name(db))
  {
1407
    my_error(ER_WRONG_DB_NAME ,MYF(0), db ? db : "NULL");
1408 1409
    goto err;
  }
1410
  if (lower_case_table_names)
1411
    my_casedn_str(files_charset_info, tbl_name);
1412
  remove_escape(table_list->table_name);
1413 1414 1415 1416

  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
1417
  if (check_one_table_access(thd, SELECT_ACL, table_list))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1418 1419
    goto err;
  thd->free_list = 0;
1420
  thd->query_length=(uint) strlen(tbl_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1421
  thd->query = tbl_name;
1422
  if ((error = mysqld_dump_create_info(thd, table_list, -1)))
1423
  {
1424
    my_error(ER_GET_ERRNO, MYF(0), my_errno);
1425 1426
    goto err;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1427
  net_flush(&thd->net);
1428
  if ((error= table->file->dump(thd,-1)))
1429
    my_error(ER_GET_ERRNO, MYF(0), error);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1430

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1431
err:
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1432
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1433 1434
}

1435 1436 1437 1438
/*
  Ends the current transaction and (maybe) begin the next

  SYNOPSIS
1439
    end_trans()
1440 1441 1442 1443 1444 1445 1446
      thd            Current thread
      completion     Completion type

  RETURN
    0 - OK
*/

1447
int end_trans(THD *thd, enum enum_mysql_completiontype completion)
1448 1449 1450
{
  bool do_release= 0;
  int res= 0;
1451
  DBUG_ENTER("end_trans");
1452

1453
  if (unlikely(thd->in_sub_stmt))
1454 1455 1456 1457
  {
    my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
    DBUG_RETURN(1);
  }
1458 1459 1460 1461 1462 1463
  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);
  }
1464 1465 1466 1467 1468 1469 1470 1471
  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;
1472
    res= ha_commit(thd);
1473
    thd->options&= ~(ulong) OPTION_BEGIN;
1474
    thd->transaction.all.modified_non_trans_table= FALSE;
1475 1476
    break;
  case COMMIT_RELEASE:
serg@serg.mylan's avatar
serg@serg.mylan committed
1477
    do_release= 1; /* fall through */
1478 1479 1480 1481 1482 1483
  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
1484
    do_release= 1; /* fall through */
1485 1486 1487 1488
  case ROLLBACK:
  case ROLLBACK_AND_CHAIN:
  {
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
serg@serg.mylan's avatar
serg@serg.mylan committed
1489
    if (ha_rollback(thd))
1490
      res= -1;
1491
    thd->options&= ~(ulong) OPTION_BEGIN;
1492
    thd->transaction.all.modified_non_trans_table= FALSE;
1493 1494 1495 1496 1497 1498 1499 1500 1501
    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
1502

1503 1504 1505
  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
1506 1507
    thd->killed= THD::KILL_CONNECTION;

1508 1509
  DBUG_RETURN(res);
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1510

1511
#ifndef EMBEDDED_LIBRARY
1512 1513

/*
1514
  Read one command from connection and execute it (query or simple command).
1515 1516 1517 1518 1519 1520 1521 1522
  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
1523 1524
bool do_command(THD *thd)
{
1525
  char *packet= 0;
1526
  ulong packet_length;
1527
  NET *net= &thd->net;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1528 1529 1530
  enum enum_server_command command;
  DBUG_ENTER("do_command");

1531 1532 1533 1534
  /*
    indicator of uninitialized lex => normal flow of errors handling
    (see my_message_sql)
  */
1535
  thd->lex->current_select= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1536

1537 1538 1539 1540 1541 1542
  /*
    This thread will do a blocking read from the client which
    will be interrupted when the next command is received from
    the client, the connection is closed or "net_wait_timeout"
    number of seconds has passed
  */
1543
  my_net_set_read_timeout(net, thd->variables.net_wait_timeout);
1544

1545
  thd->clear_error();				// Clear error message
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1546 1547 1548 1549

  net_new_transaction(net);
  if ((packet_length=my_net_read(net)) == packet_error)
  {
1550 1551 1552 1553 1554
    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)
1555 1556
    {
      statistic_increment(aborted_threads,&LOCK_status);
1557
      DBUG_RETURN(TRUE);			// We have to close it.
1558
    }
1559
    net_send_error(thd, net->last_errno, NullS);
1560
    net->error= 0;
1561
    DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1562 1563 1564 1565 1566
  }
  else
  {
    packet=(char*) net->read_pos;
    command = (enum enum_server_command) (uchar) packet[0];
1567 1568
    if (command >= COM_END)
      command= COM_END;				// Wrong command
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1569 1570 1571
    DBUG_PRINT("info",("Command on %s = %d (%s)",
		       vio_description(net->vio), command,
		       command_name[command]));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1572
  }
1573 1574

  /* Restore read timeout value */
1575
  my_net_set_read_timeout(net, thd->variables.net_read_timeout);
1576

1577 1578 1579 1580 1581 1582 1583 1584 1585
  /*
    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.
  */
1586
  DBUG_RETURN(dispatch_command(command,thd, packet+1, (uint) packet_length));
1587
}
1588
#endif  /* EMBEDDED_LIBRARY */
1589

1590

1591 1592
/*
   Perform one connection-level (COM_XXXX) command.
1593

1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606
  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
*/
1607

1608 1609 1610 1611
bool dispatch_command(enum enum_server_command command, THD *thd,
		      char* packet, uint packet_length)
{
  NET *net= &thd->net;
1612
  bool error= 0;
1613 1614
  DBUG_ENTER("dispatch_command");

1615
  if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA)
1616
  {
1617
    thd->killed= THD::NOT_KILLED;
1618 1619
    thd->mysys_var->abort= 0;
  }
1620

1621
  thd->command=command;
1622
  /*
1623 1624
    Commands which always take a long time are logged into
    the slow log only if opt_log_slow_admin_statements is set.
1625
  */
1626
  thd->enable_slow_log= TRUE;
1627
  thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
1628
  thd->set_time();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1629
  VOID(pthread_mutex_lock(&LOCK_thread_count));
1630
  thd->query_id= global_query_id;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1631
  if (command != COM_STATISTICS && command != COM_PING)
1632
    next_query_id();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1633
  thread_running++;
1634
  /* TODO: set thd->lex->sql_command to SQLCOM_END here */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1635
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
1636

1637 1638
  thd->server_status&=
           ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
1639
  switch (command) {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1640
  case COM_INIT_DB:
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1641 1642
  {
    LEX_STRING tmp;
1643 1644
    statistic_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB],
			&LOCK_status);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1645 1646
    thd->convert_string(&tmp, system_charset_info,
			packet, strlen(packet), thd->charset());
1647
    if (!mysql_change_db(thd, &tmp, FALSE))
1648
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1649
      mysql_log.write(thd,command,"%s",thd->db);
1650 1651
      send_ok(thd);
    }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1652 1653
    break;
  }
1654
#ifdef HAVE_REPLICATION
1655 1656
  case COM_REGISTER_SLAVE:
  {
1657
    if (!register_slave(thd, (uchar*)packet, packet_length))
1658
      send_ok(thd);
1659 1660
    break;
  }
1661
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1662
  case COM_TABLE_DUMP:
1663 1664 1665
  {
    char *db, *tbl_name;
    uint db_len= *(uchar*) packet;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1666
    if (db_len >= packet_length || db_len > NAME_LEN)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1667
    {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1668
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1669 1670
      break;
    }
1671
    uint tbl_len= *(uchar*) (packet + db_len + 1);
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1672 1673
    if (db_len+tbl_len+2 > packet_length || tbl_len > NAME_LEN)
    {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1674
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1675 1676
      break;
    }
1677

1678
    statistic_increment(thd->status_var.com_other, &LOCK_status);
1679
    thd->enable_slow_log= opt_log_slow_admin_statements;
1680
    db= thd->alloc(db_len + tbl_len + 2);
1681 1682 1683 1684 1685
    if (!db)
    {
      my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
      break;
    }
1686 1687
    tbl_name= strmake(db, packet + 1, db_len)+1;
    strmake(tbl_name, packet + db_len + 2, tbl_len);
1688
    mysql_table_dump(thd, db, tbl_name);
1689 1690
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1691 1692
  case COM_CHANGE_USER:
  {
1693
    thd->change_user();
1694
    thd->clear_error();                         // if errors from rollback
1695

1696
    statistic_increment(thd->status_var.com_other, &LOCK_status);
1697
    char *user= (char*) packet;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1698
    char *passwd= strend(user)+1;
1699
    /*
1700 1701 1702
      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).
1703 1704 1705

      Cast *passwd to an unsigned char, so that it doesn't extend the sign
      for *passwd > 127 and become 2**32-127 after casting to uint.
1706
    */
gluh@mysql.com/gluh.(none)'s avatar
gluh@mysql.com/gluh.(none) committed
1707
    char db_buff[NAME_LEN+1];               // buffer to store db in utf8
1708
    char *db= passwd;
1709
    uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
1710
      (uchar)(*passwd++) : strlen(passwd);
1711
    db+= passwd_len + 1;
1712
#ifndef EMBEDDED_LIBRARY
1713
    /* Small check for incoming packet */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1714
    if ((uint) ((uchar*) db - net->read_pos) > packet_length)
1715
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1716
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
1717 1718
      break;
    }
1719
#endif
1720
    /* Convert database name to utf8 */
1721
    uint dummy_errors;
1722 1723
    db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
                             system_charset_info, db, strlen(db),
1724
                             thd->charset(), &dummy_errors)]= 0;
1725
    db= db_buff;
peter@mysql.com's avatar
peter@mysql.com committed
1726

1727 1728 1729
    /* Save user and privileges */
    uint save_db_length= thd->db_length;
    char *save_db= thd->db;
1730
    Security_context save_security_ctx= *thd->security_ctx;
1731
    USER_CONN *save_user_connect= thd->user_connect;
1732 1733

    if (!(thd->security_ctx->user= my_strdup(user, MYF(0))))
1734
    {
1735
      thd->security_ctx->user= save_security_ctx.user;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1736
      my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
1737 1738
      break;
    }
peter@mysql.com's avatar
peter@mysql.com committed
1739

1740 1741
    /* Clear variables that are allocated */
    thd->user_connect= 0;
monty@mysql.com's avatar
monty@mysql.com committed
1742
    int res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE);
peter@mysql.com's avatar
peter@mysql.com committed
1743

1744 1745
    if (res)
    {
1746
      /* authentication failure, we shall restore old user */
1747
      if (res > 0)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1748
        my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
1749 1750
      x_free(thd->security_ctx->user);
      *thd->security_ctx= save_security_ctx;
1751
      thd->user_connect= save_user_connect;
1752 1753 1754 1755 1756
      thd->db= save_db;
      thd->db_length= save_db_length;
    }
    else
    {
1757
#ifndef NO_EMBEDDED_ACCESS_CHECKS
1758
      /* we've authenticated new user */
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1759 1760
      if (save_user_connect)
	decrease_user_connections(save_user_connect);
1761
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
1762
      x_free((gptr) save_db);
1763
      x_free((gptr)  save_security_ctx.user);
1764
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1765 1766
    break;
  }
1767
  case COM_STMT_EXECUTE:
1768
  {
1769
    mysql_stmt_execute(thd, packet, packet_length);
1770 1771
    break;
  }
1772
  case COM_STMT_FETCH:
1773 1774 1775 1776
  {
    mysql_stmt_fetch(thd, packet, packet_length);
    break;
  }
1777
  case COM_STMT_SEND_LONG_DATA:
1778
  {
1779
    mysql_stmt_get_longdata(thd, packet, packet_length);
1780 1781
    break;
  }
1782
  case COM_STMT_PREPARE:
1783
  {
1784
    mysql_stmt_prepare(thd, packet, packet_length);
1785 1786
    break;
  }
1787
  case COM_STMT_CLOSE:
1788
  {
1789
    mysql_stmt_close(thd, packet);
1790 1791
    break;
  }
1792
  case COM_STMT_RESET:
1793 1794 1795 1796
  {
    mysql_stmt_reset(thd, packet);
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1797 1798
  case COM_QUERY:
  {
1799 1800
    if (alloc_query(thd, packet, packet_length))
      break;					// fatal error is set
1801
    char *packet_end= thd->query + thd->query_length;
1802 1803
    /* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */
    const char *format= "%.*b";
1804 1805
    const char* found_semicolon= NULL;

1806
    mysql_log.write(thd,command, format, thd->query_length, thd->query);
1807
    DBUG_PRINT("query",("%-.4096s",thd->query));
1808 1809 1810 1811

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

1812
    mysql_parse(thd, thd->query, thd->query_length, & found_semicolon);
1813

1814
    while (!thd->killed && found_semicolon && !thd->net.report_error)
1815
    {
1816
      char *next_packet= (char*) found_semicolon;
1817
      net->no_send_error= 0;
1818
      /*
1819 1820
        Multiple queries exits, execute them individually
      */
1821 1822
      if (thd->lock || thd->open_tables || thd->derived_tables ||
          thd->prelocked_mode)
1823
        close_thread_tables(thd);
1824
      ulong length= (ulong)(packet_end - next_packet);
1825

1826
      log_slow_statement(thd);
1827

1828
      /* Remove garbage at start of query */
1829
      while (my_isspace(thd->charset(), *next_packet) && length > 0)
1830
      {
1831
        next_packet++;
1832 1833
        length--;
      }
monty@mysql.com's avatar
monty@mysql.com committed
1834
      VOID(pthread_mutex_lock(&LOCK_thread_count));
1835
      thd->query_length= length;
1836
      thd->query= next_packet;
1837
      thd->query_id= next_query_id();
1838
      thd->set_time(); /* Reset the query start time. */
1839
      /* TODO: set thd->lex->sql_command to SQLCOM_END here */
1840
      VOID(pthread_mutex_unlock(&LOCK_thread_count));
1841
      mysql_parse(thd, next_packet, length, & found_semicolon);
1842 1843
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1844 1845 1846 1847 1848
    if (!(specialflag & SPECIAL_NO_PRIOR))
      my_pthread_setprio(pthread_self(),WAIT_PRIOR);
    DBUG_PRINT("info",("query ready"));
    break;
  }
1849
  case COM_FIELD_LIST:				// This isn't actually needed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1850
#ifdef DONT_ALLOW_SHOW_COMMANDS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1851 1852
    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
1853 1854 1855
    break;
#else
  {
1856
    char *fields, *pend;
1857
    /* Locked closure of all tables */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1858
    TABLE_LIST table_list;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1859
    LEX_STRING conv_name;
1860

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1861
    /* used as fields initializator */
1862
    lex_start(thd);
1863

1864 1865
    statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS],
			&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1866
    bzero((char*) &table_list,sizeof(table_list));
1867
    if (thd->copy_db_to(&table_list.db, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1868
      break;
1869
    pend= strend(packet);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1870 1871
    thd->convert_string(&conv_name, system_charset_info,
			packet, (uint) (pend-packet), thd->charset());
1872
    table_list.alias= table_list.table_name= conv_name.str;
1873
    packet= pend+1;
1874 1875

    if (!my_strcasecmp(system_charset_info, table_list.db,
1876
                       INFORMATION_SCHEMA_NAME.str))
1877 1878 1879 1880 1881 1882
    {
      ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, table_list.alias);
      if (schema_table)
        table_list.schema_table= schema_table;
    }

1883
    thd->query_length= strlen(packet);       // for simplicity: don't optimize
1884 1885
    if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1)))
      break;
1886
    mysql_log.write(thd,command,"%s %s",table_list.table_name, fields);
1887
    if (lower_case_table_names)
1888 1889
      my_casedn_str(files_charset_info, table_list.table_name);
    remove_escape(table_list.table_name);	// This can't have wildcards
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1890

monty@mysql.com's avatar
monty@mysql.com committed
1891
    if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege,
1892
		     0, 0, test(table_list.schema_table)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1893
      break;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1894 1895
    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
1896
      break;
1897 1898
    /* init structures for VIEW processing */
    table_list.select_lex= &(thd->lex->select_lex);
1899 1900 1901 1902

    lex_start(thd);
    mysql_reset_thd_for_next_command(thd);

1903 1904 1905
    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
1906
    thd->lex->add_to_query_tables(&table_list);
1907

1908 1909
    /* 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
1910
    mysqld_list_fields(thd,&table_list,fields);
1911
    thd->lex->unit.cleanup();
1912
    thd->cleanup_after_query();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1913 1914 1915 1916
    break;
  }
#endif
  case COM_QUIT:
1917
    /* We don't calculate statistics for this command */
1918
    mysql_log.write(thd,command,NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1919 1920 1921 1922
    net->error=0;				// Don't give 'abort' message
    error=TRUE;					// End server
    break;

1923
  case COM_CREATE_DB:				// QQ: To be removed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1924
    {
1925
      char *db=thd->strdup(packet), *alias;
1926
      HA_CREATE_INFO create_info;
1927

1928 1929
      statistic_increment(thd->status_var.com_stat[SQLCOM_CREATE_DB],
			  &LOCK_status);
1930
      // null test to handle EOM
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1931
      if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
1932
      {
1933
	my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL");
1934 1935
	break;
      }
1936
      if (check_access(thd,CREATE_ACL,db,0,1,0,is_schema_db(db)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1937
	break;
1938
      mysql_log.write(thd,command,packet);
1939
      bzero(&create_info, sizeof(create_info));
1940 1941
      mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db),
                      &create_info, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1942 1943
      break;
    }
1944
  case COM_DROP_DB:				// QQ: To be removed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1945
    {
1946 1947
      statistic_increment(thd->status_var.com_stat[SQLCOM_DROP_DB],
			  &LOCK_status);
1948
      char *db=thd->strdup(packet);
monty@mysql.com's avatar
monty@mysql.com committed
1949
      /*  null test to handle EOM */
1950
      if (!db || check_db_name(db))
1951
      {
1952
	my_error(ER_WRONG_DB_NAME, MYF(0), db ? db : "NULL");
1953 1954
	break;
      }
1955
      if (check_access(thd,DROP_ACL,db,0,1,0,is_schema_db(db)))
1956
	break;
1957 1958
      if (thd->locked_tables || thd->active_transaction())
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1959 1960
	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
1961
	break;
1962
      }
1963
      mysql_log.write(thd,command,db);
1964
      mysql_rm_db(thd, db, 0, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1965 1966
      break;
    }
1967
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1968 1969
  case COM_BINLOG_DUMP:
    {
monty@mysql.com's avatar
monty@mysql.com committed
1970 1971 1972 1973
      ulong pos;
      ushort flags;
      uint32 slave_server_id;

1974
      statistic_increment(thd->status_var.com_other,&LOCK_status);
1975
      thd->enable_slow_log= opt_log_slow_admin_statements;
1976
      if (check_global_access(thd, REPL_SLAVE_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1977
	break;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1978

1979
      /* TODO: The following has to be changed to an 8 byte integer */
1980 1981
      pos = uint4korr(packet);
      flags = uint2korr(packet + 4);
1982
      thd->server_id=0; /* avoid suicide */
1983
      if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1984
	kill_zombie_dump_threads(slave_server_id);
1985
      thd->server_id = slave_server_id;
monty@mysql.com's avatar
monty@mysql.com committed
1986 1987 1988

      mysql_log.write(thd, command, "Log: '%s'  Pos: %ld", packet+10,
                      (long) pos);
1989
      mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
1990
      unregister_slave(thd,1,1);
monty@mysql.com's avatar
monty@mysql.com committed
1991
      /*  fake COM_QUIT -- if we get here, the thread needs to terminate */
1992 1993
      error = TRUE;
      net->error = 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1994 1995
      break;
    }
1996
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1997
  case COM_REFRESH:
1998 1999 2000 2001 2002 2003
  {
    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
2004
      break;
2005 2006 2007 2008 2009
    mysql_log.write(thd,command,NullS);
    if (!reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
      send_ok(thd);
    break;
  }
2010
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2011
  case COM_SHUTDOWN:
2012
  {
2013
    statistic_increment(thd->status_var.com_other, &LOCK_status);
2014
    if (check_global_access(thd,SHUTDOWN_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2015
      break; /* purecov: inspected */
2016
    /*
2017 2018 2019 2020
      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].
2021
    */
2022 2023
    enum mysql_enum_shutdown_level level=
      (enum mysql_enum_shutdown_level) (uchar) packet[0];
2024
    DBUG_PRINT("quit",("Got shutdown command for level %u", level));
2025 2026 2027 2028 2029 2030 2031
    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;
    }
2032
    DBUG_PRINT("quit",("Got shutdown command for level %u", level));
2033
    mysql_log.write(thd,command,NullS);
2034
    send_eof(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2035 2036 2037
#ifdef __WIN__
    sleep(1);					// must wait after eof()
#endif
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2038
#ifndef OS2
2039
    send_eof(thd);				// This is for 'quit request'
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2040
#endif
2041
    close_connection(thd, 0, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2042 2043 2044 2045
    close_thread_tables(thd);			// Free before kill
    kill_mysql();
    error=TRUE;
    break;
2046
  }
2047
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2048 2049
  case COM_STATISTICS:
  {
2050
    mysql_log.write(thd,command,NullS);
2051 2052
    statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS],
			&LOCK_status);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2053
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2054
    char buff[200];
hf@deer.(none)'s avatar
hf@deer.(none) committed
2055 2056 2057
#else
    char *buff= thd->net.last_error;
#endif
2058 2059 2060 2061

    STATUS_VAR current_global_status_var;
    calc_sum_of_all_status(&current_global_status_var);

2062
    ulong uptime = (ulong) (thd->start_time - server_start_time);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2063
    sprintf((char*) buff,
2064
	    "Uptime: %lu  Threads: %d  Questions: %lu  Slow queries: %lu  Opens: %lu  Flush tables: %lu  Open tables: %u  Queries per second avg: %.3f",
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2065
	    uptime,
2066
	    (int) thread_count, (ulong) thd->query_id,
2067 2068
	    current_global_status_var.long_query_count,
	    current_global_status_var.opened_tables, refresh_version, cached_tables(),
2069 2070
	    (uptime ? (ulonglong2double(thd->query_id) / (double) uptime) :
	     (double) 0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2071
#ifdef SAFEMALLOC
2072
    if (sf_malloc_cur_memory)				// Using SAFEMALLOC
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2073
      sprintf(strend(buff), "  Memory in use: %ldK  Max memory used: %ldK",
2074 2075
	      (sf_malloc_cur_memory+1023L)/1024L,
	      (sf_malloc_max_memory+1023L)/1024L);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2076 2077
#endif
#ifndef EMBEDDED_LIBRARY
2078
    VOID(my_net_write(net, buff,(uint) strlen(buff)));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2079
    VOID(net_flush(net));
hf@deer.(none)'s avatar
hf@deer.(none) committed
2080
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2081 2082 2083
    break;
  }
  case COM_PING:
2084
    statistic_increment(thd->status_var.com_other, &LOCK_status);
2085
    send_ok(thd);				// Tell client we are alive
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2086 2087
    break;
  case COM_PROCESS_INFO:
2088 2089
    statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST],
			&LOCK_status);
2090 2091
    if (!thd->security_ctx->priv_user[0] &&
        check_global_access(thd, PROCESS_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2092
      break;
2093
    mysql_log.write(thd,command,NullS);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2094
    mysqld_list_processes(thd,
2095 2096
			  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
2097 2098 2099
    break;
  case COM_PROCESS_KILL:
  {
2100
    statistic_increment(thd->status_var.com_stat[SQLCOM_KILL], &LOCK_status);
2101
    ulong id=(ulong) uint4korr(packet);
hf@genie.(none)'s avatar
SCRUM  
hf@genie.(none) committed
2102
    kill_one_thread(thd,id,false);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2103 2104
    break;
  }
2105 2106
  case COM_SET_OPTION:
  {
2107 2108
    statistic_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION],
			&LOCK_status);
2109 2110 2111 2112
    uint opt_command= uint2korr(packet);

    switch (opt_command) {
    case (int) MYSQL_OPTION_MULTI_STATEMENTS_ON:
2113
      thd->client_capabilities|= CLIENT_MULTI_STATEMENTS;
2114
      send_eof(thd);
2115
      break;
2116
    case (int) MYSQL_OPTION_MULTI_STATEMENTS_OFF:
2117
      thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS;
2118
      send_eof(thd);
2119 2120
      break;
    default:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2121
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
2122 2123 2124 2125
      break;
    }
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2126
  case COM_DEBUG:
2127
    statistic_increment(thd->status_var.com_other, &LOCK_status);
2128
    if (check_global_access(thd, SUPER_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2129
      break;					/* purecov: inspected */
2130
    mysql_print_status();
2131
    mysql_log.write(thd,command,NullS);
2132
    send_eof(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2133 2134 2135 2136 2137
    break;
  case COM_SLEEP:
  case COM_CONNECT:				// Impossible here
  case COM_TIME:				// Impossible from client
  case COM_DELAYED_INSERT:
2138
  case COM_END:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2139
  default:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2140
    my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2141 2142
    break;
  }
2143 2144
  if (thd->lock || thd->open_tables || thd->derived_tables ||
      thd->prelocked_mode)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2145 2146 2147 2148
  {
    thd->proc_info="closing tables";
    close_thread_tables(thd);			/* Free tables */
  }
2149 2150 2151 2152 2153 2154 2155 2156 2157 2158
  /*
    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())
2159
    thd->transaction.xid_state.xid.null();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2160

2161 2162 2163
  /* 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
2164
  if (thd->net.report_error)
2165
    net_send_error(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2166

2167
  log_slow_statement(thd);
2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182

  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);
}


2183
void log_slow_statement(THD *thd)
2184
{
2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195
  time_t start_of_query;

  /*
    The following should never be true with our current code base,
    but better to keep this here so we don't accidently try to log a
    statement in a trigger or stored function
  */
  if (unlikely(thd->in_sub_stmt))
    return;                                     // Don't set time for sub stmt

  start_of_query= thd->start_time;
2196
  thd->end_time();				// Set start time
2197

2198 2199 2200 2201 2202
  /*
    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
2203
  {
2204 2205
    thd->proc_info="logging slow query";

2206 2207
    if ((ulong) (thd->start_time - thd->time_after_lock) >
	thd->variables.long_query_time ||
2208
        (thd->server_status &
2209
	  (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
2210
        opt_log_queries_not_using_indexes &&
2211 2212
        /* == SQLCOM_END unless this is a SHOW command */
        thd->lex->orig_sql_command == SQLCOM_END)
2213
    {
2214
      thd->status_var.long_query_count++;
2215 2216
      mysql_slow_log.write(thd, thd->query, thd->query_length, start_of_query);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2217 2218 2219
  }
}

2220

2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248
/*
  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.
*/

2249 2250 2251 2252
int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
                         enum enum_schema_tables schema_table_idx)
{
  DBUG_ENTER("prepare_schema_table");
2253 2254
  SELECT_LEX *schema_select_lex= NULL;

2255
  switch (schema_table_idx) {
2256 2257
  case SCH_SCHEMATA:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2258 2259
    my_message(ER_NOT_ALLOWED_COMMAND,
               ER(ER_NOT_ALLOWED_COMMAND), MYF(0));   /* purecov: inspected */
2260 2261 2262 2263
    DBUG_RETURN(1);
#else
    break;
#endif
2264

2265 2266 2267
  case SCH_TABLE_NAMES:
  case SCH_TABLES:
  case SCH_VIEWS:
2268
  case SCH_TRIGGERS:
2269
#ifdef DONT_ALLOW_SHOW_COMMANDS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2270 2271
    my_message(ER_NOT_ALLOWED_COMMAND,
               ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
2272 2273
    DBUG_RETURN(1);
#else
2274
    if (lex->select_lex.db == NULL &&
2275
        lex->copy_db_to(&lex->select_lex.db, NULL))
2276
    {
2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288
      DBUG_RETURN(1);
    }

    schema_select_lex= new SELECT_LEX();
    schema_select_lex->db= lex->select_lex.db;
    schema_select_lex->table_list.first= NULL;
    remove_escape(schema_select_lex->db); // Fix escaped '_'

    if (check_db_name(schema_select_lex->db))
    {
      my_error(ER_WRONG_DB_NAME, MYF(0), schema_select_lex->db);
      DBUG_RETURN(1);
2289
    }
2290 2291 2292


    break;
2293 2294 2295 2296
#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
2297 2298
    my_message(ER_NOT_ALLOWED_COMMAND,
               ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
2299 2300 2301
    DBUG_RETURN(1);
#else
    {
2302 2303
      DBUG_ASSERT(table_ident);

2304
      TABLE_LIST **query_tables_last= lex->query_tables_last;
2305
      schema_select_lex= new SELECT_LEX();
2306
      /* 'parent_lex' is used in init_query() so it must be before it. */
2307 2308 2309 2310
      schema_select_lex->parent_lex= lex;
      schema_select_lex->init_query();
      if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ,
                                  (List<String> *) 0, (List<String> *) 0))
2311 2312
        DBUG_RETURN(1);
      lex->query_tables_last= query_tables_last;
2313 2314 2315 2316 2317

      TABLE_LIST *dst_table= (TABLE_LIST*) schema_select_lex->table_list.first;
      remove_escape(dst_table->db);			// Fix escaped '_'
      remove_escape(dst_table->table_name);

2318 2319 2320
      break;
    }
#endif
2321 2322 2323
  case SCH_OPEN_TABLES:
  case SCH_VARIABLES:
  case SCH_STATUS:
2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343
  case SCH_PROCEDURES:
  case SCH_CHARSETS:
  case SCH_COLLATIONS:
  case SCH_COLLATION_CHARACTER_SET_APPLICABILITY:
  case SCH_USER_PRIVILEGES:
  case SCH_SCHEMA_PRIVILEGES:
  case SCH_TABLE_PRIVILEGES:
  case SCH_COLUMN_PRIVILEGES:
  case SCH_TABLE_CONSTRAINTS:
  case SCH_KEY_COLUMN_USAGE:
  default:
    break;
  }
  
  SELECT_LEX *select_lex= lex->current_select;
  if (make_schema_select(thd, select_lex, schema_table_idx))
  {
    DBUG_RETURN(1);
  }
  TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first;
2344
  table_list->schema_select_lex= schema_select_lex;
2345
  table_list->schema_table_reformed= 1;
2346 2347
  statistic_increment(thd->status_var.com_stat[lex->orig_sql_command],
                      &LOCK_status);
2348 2349 2350 2351
  DBUG_RETURN(0);
}


2352 2353
/*
  Read query from packet and store in thd->query
2354
  Used in COM_QUERY and COM_STMT_PREPARE
2355 2356 2357 2358 2359 2360 2361

  DESCRIPTION
    Sets the following THD variables:
      query
      query_length

  RETURN VALUES
2362 2363
    FALSE ok
    TRUE  error;  In this case thd->fatal_error is set
2364 2365
*/

2366
bool alloc_query(THD *thd, const char *packet, uint packet_length)
2367 2368
{
  packet_length--;				// Remove end null
2369
  /* Remove garbage at start and end of query */
2370
  while (my_isspace(thd->charset(),packet[0]) && packet_length > 0)
2371 2372 2373 2374
  {
    packet++;
    packet_length--;
  }
2375
  const char *pos= packet + packet_length;     // Point at end null
peter@mysql.com's avatar
peter@mysql.com committed
2376
  while (packet_length > 0 &&
2377
	 (pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1])))
2378 2379 2380 2381 2382
  {
    pos--;
    packet_length--;
  }
  /* We must allocate some extra memory for query cache */
monty@mysql.com's avatar
monty@mysql.com committed
2383
  thd->query_length= 0;                        // Extra safety: Avoid races
2384 2385
  if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet),
					      packet_length,
2386 2387
					      thd->db_length+ 1 +
					      QUERY_CACHE_FLAGS_SIZE)))
2388
    return TRUE;
2389 2390
  thd->query[packet_length]=0;
  thd->query_length= packet_length;
2391 2392 2393 2394

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

2396
  return FALSE;
2397 2398
}

2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411
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;
2412
  thd->variables.lc_time_names= &my_locale_en_US;
2413 2414 2415
  thd->one_shot_set= 0;
}

2416

2417
/*
2418
  Execute command saved in thd and lex->sql_command
2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439

  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
2440

2441
bool
2442
mysql_execute_command(THD *thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2443
{
2444 2445
  bool res= FALSE;
  bool need_start_waiting= FALSE; // have protection against global read lock
2446
  int  up_result= 0;
2447
  LEX  *lex= thd->lex;
monty@mysql.com's avatar
monty@mysql.com committed
2448
  /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2449
  SELECT_LEX *select_lex= &lex->select_lex;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2450
  /* first table of first SELECT_LEX */
monty@mysql.com's avatar
monty@mysql.com committed
2451
  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
2452 2453 2454
  /* list of all tables in query */
  TABLE_LIST *all_tables;
  /* most outer SELECT_LEX_UNIT of query */
2455
  SELECT_LEX_UNIT *unit= &lex->unit;
2456
  /* Saved variable value */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2457
  DBUG_ENTER("mysql_execute_command");
2458
  thd->net.no_send_error= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2459

2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473
  /*
    Remember first generated insert id value of the previous
    statement.  We remember it here at the beginning of the statement,
    and also in Item_func_last_insert_id::fix_fields() and
    sys_var_last_insert_id::value_ptr().  Last two places are required
    because LAST_INSERT_ID() and @@LAST_INSERT_ID may also be used in
    expression that is not executed with mysql_execute_command().

    And we remember it here because some statements read
    @@LAST_INSERT_ID indirectly, like "SELECT * FROM t1 WHERE id IS
    NULL", that may replace "id IS NULL" with "id = <LAST_INSERT_ID>".
  */
  thd->current_insert_id= thd->last_insert_id;

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489
  /*
    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();
2490
  /* should be assigned after making first tables same */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2491
  all_tables= lex->query_tables;
2492 2493 2494 2495
  /* 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
2496

2497 2498 2499 2500 2501
  /*
    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.
2502
    Don't reset warnings when executing a stored routine.
2503
  */
2504
  if ((all_tables || &lex->select_lex != lex->all_selects_list ||
2505
       lex->sroutines.records) && !thd->spcont ||
2506
      lex->time_zone_tables_used)
monty@mysql.com's avatar
monty@mysql.com committed
2507
    mysql_reset_errors(thd, 0);
2508

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2509
#ifdef HAVE_REPLICATION
2510
  if (unlikely(thd->slave_thread))
2511
  {
2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535
    if (lex->sql_command == SQLCOM_DROP_TRIGGER)
    {
      /*
        When dropping a trigger, we need to load its table name
        before checking slave filter rules.
      */
      add_table_for_trigger(thd, thd->lex->spname, 1, &all_tables);
      
      if (!all_tables)
      {
        /*
          If table name cannot be loaded,
          it means the trigger does not exists possibly because
          CREATE TRIGGER was previously skipped for this trigger
          according to slave filtering rules.
          Returning success without producing any errors in this case.
        */
        DBUG_RETURN(0);
      }
      
      // force searching in slave.cc:tables_ok() 
      all_tables->updating= 1;
    }
    
peter@mysql.com's avatar
peter@mysql.com committed
2536
    /*
2537 2538
      Check if statment should be skipped because of slave filtering
      rules
2539 2540

      Exceptions are:
2541 2542
      - UPDATE MULTI: For this statement, we want to check the filtering
        rules later in the code
2543
      - SET: we always execute it (Not that many SET commands exists in
lars@mysql.com's avatar
lars@mysql.com committed
2544 2545
        the binary log anyway -- only 4.1 masters write SET statements,
	in 5.0 there are no SET statements in the binary log)
2546 2547
      - 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
2548
    */
2549 2550
    if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) &&
	!(lex->sql_command == SQLCOM_SET_OPTION) &&
2551
	!(lex->sql_command == SQLCOM_DROP_TABLE &&
2552
          lex->drop_temporary && lex->drop_if_exists) &&
guilhem@mysql.com's avatar
Merge  
guilhem@mysql.com committed
2553
        all_tables_not_ok(thd, all_tables))
guilhem@mysql.com's avatar
guilhem@mysql.com committed
2554 2555
    {
      /* we warn the slave SQL thread */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2556
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573
      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);
      }
2574
      DBUG_RETURN(0);
guilhem@mysql.com's avatar
guilhem@mysql.com committed
2575
    }
2576
  }
2577
  else
2578
  {
2579
#endif /* HAVE_REPLICATION */
2580 2581 2582 2583 2584
    /*
      When option readonly is set deny operations which change non-temporary
      tables. Except for the replication thread and the 'super' users.
    */
    if (opt_readonly &&
2585 2586 2587 2588 2589 2590 2591
        !(thd->security_ctx->master_access & SUPER_ACL) &&
        uc_update_queries[lex->sql_command] &&
        !((lex->sql_command == SQLCOM_CREATE_TABLE) &&
          (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) &&
        !((lex->sql_command == SQLCOM_DROP_TABLE) && lex->drop_temporary) &&
        ((lex->sql_command != SQLCOM_UPDATE_MULTI) &&
          some_non_temp_table_to_be_updated(thd, all_tables)))
2592 2593 2594 2595 2596 2597 2598
    {
      my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
      DBUG_RETURN(-1);
    }
#ifdef HAVE_REPLICATION
  } /* endif unlikely slave */
#endif
2599 2600 2601
  if(lex->orig_sql_command == SQLCOM_END)
    statistic_increment(thd->status_var.com_stat[lex->sql_command],
                        &LOCK_status);
2602

2603 2604
  DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE);
  
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2605 2606 2607
  switch (lex->sql_command) {
  case SQLCOM_SELECT:
  {
2608 2609 2610 2611
    /* assign global limit variable if limit is not given */
    {
      SELECT_LEX *param= lex->unit.global_parameters;
      if (!param->explicit_limit)
2612 2613
	param->select_limit=
          new Item_int((ulonglong)thd->variables.select_limit);
2614 2615
    }

2616
    select_result *sel_result=lex->result;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2617
    if (all_tables)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2618
    {
2619 2620 2621 2622 2623 2624
      if (lex->orig_sql_command != SQLCOM_SHOW_STATUS_PROC &&
          lex->orig_sql_command != SQLCOM_SHOW_STATUS_FUNC)
        res= check_table_access(thd,
                                lex->exchange ? SELECT_ACL | FILE_ACL :
                                SELECT_ACL,
                                all_tables, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2625 2626
    }
    else
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2627 2628
      res= check_access(thd,
			lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
2629
			any_db, 0, 0, 0, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2630
    if (res)
2631
      goto error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2632

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2633
    if (!(res= open_and_lock_tables(thd, all_tables)))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2634
    {
2635
      if (lex->describe)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2636
      {
2637 2638 2639 2640 2641 2642
        /*
          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.
        */
2643
	if (!(sel_result= new select_send()))
2644
	  goto error;
2645
	else
2646 2647
	  thd->send_explain_fields(sel_result);
	res= mysql_explain_union(thd, &thd->lex->unit, sel_result);
2648 2649 2650 2651 2652
	if (lex->describe & DESCRIBE_EXTENDED)
	{
	  char buff[1024];
	  String str(buff,(uint32) sizeof(buff), system_charset_info);
	  str.length(0);
pem@mysql.comhem.se's avatar
pem@mysql.comhem.se committed
2653
	  thd->lex->unit.print(&str);
2654 2655 2656 2657
	  str.append('\0');
	  push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
		       ER_YES, str.ptr());
	}
2658 2659
	sel_result->send_eof();
        delete sel_result;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2660 2661 2662
      }
      else
      {
2663
	if (!sel_result && !(sel_result= new select_send()))
2664
          goto error;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2665
	query_cache_store_query(thd, all_tables);
2666 2667 2668
	res= handle_select(thd, lex, sel_result, 0);
        if (sel_result != lex->result)
          delete sel_result;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2669
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2670
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2671 2672
    break;
  }
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2673
  case SQLCOM_PREPARE:
2674
  {
2675
    mysql_sql_stmt_prepare(thd);
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2676 2677 2678 2679
    break;
  }
  case SQLCOM_EXECUTE:
  {
2680
    mysql_sql_stmt_execute(thd);
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2681 2682 2683 2684
    break;
  }
  case SQLCOM_DEALLOCATE_PREPARE:
  {
2685
    mysql_sql_stmt_close(thd);
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2686 2687
    break;
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2688
  case SQLCOM_DO:
2689 2690
    if (check_table_access(thd, SELECT_ACL, all_tables, 0) ||
        open_and_lock_tables(thd, all_tables))
2691
      goto error;
2692 2693

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

2696
  case SQLCOM_EMPTY_QUERY:
2697
    send_ok(thd);
2698 2699
    break;

2700 2701 2702 2703
  case SQLCOM_HELP:
    res= mysqld_help(thd,lex->help_arg);
    break;

2704
#ifndef EMBEDDED_LIBRARY
2705
  case SQLCOM_PURGE:
2706
  {
2707
    if (check_global_access(thd, SUPER_ACL))
2708
      goto error;
monty@mysql.com's avatar
monty@mysql.com committed
2709
    /* PURGE MASTER LOGS TO 'file' */
2710 2711 2712
    res = purge_master_logs(thd, lex->to_log);
    break;
  }
2713 2714
  case SQLCOM_PURGE_BEFORE:
  {
2715 2716
    Item *it;

2717 2718
    if (check_global_access(thd, SUPER_ACL))
      goto error;
monty@mysql.com's avatar
monty@mysql.com committed
2719
    /* PURGE MASTER LOGS BEFORE 'data' */
2720
    it= (Item *)lex->value_list.head();
2721
    if ((!it->fixed && it->fix_fields(lex->thd, &it)) ||
2722
        it->check_cols(1))
2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733
    {
      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());
2734 2735
    break;
  }
2736
#endif
2737 2738
  case SQLCOM_SHOW_WARNS:
  {
2739 2740
    res= mysqld_show_warnings(thd, (ulong)
			      ((1L << (uint) MYSQL_ERROR::WARN_LEVEL_NOTE) |
2741 2742 2743
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_WARN) |
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)
			       ));
2744 2745 2746 2747
    break;
  }
  case SQLCOM_SHOW_ERRORS:
  {
2748 2749
    res= mysqld_show_warnings(thd, (ulong)
			      (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
2750 2751
    break;
  }
2752 2753
  case SQLCOM_SHOW_NEW_MASTER:
  {
2754
    if (check_global_access(thd, REPL_SLAVE_ACL))
2755
      goto error;
2756
    /* This query don't work now. See comment in repl_failsafe.cc */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2757
#ifndef WORKING_NEW_MASTER
2758 2759
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "SHOW NEW MASTER");
    goto error;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2760
#else
2761 2762
    res = show_new_master(thd);
    break;
2763
#endif
2764
  }
2765

2766
#ifdef HAVE_REPLICATION
2767 2768
  case SQLCOM_SHOW_SLAVE_HOSTS:
  {
2769
    if (check_global_access(thd, REPL_SLAVE_ACL))
2770 2771 2772 2773
      goto error;
    res = show_slave_hosts(thd);
    break;
  }
2774 2775
  case SQLCOM_SHOW_BINLOG_EVENTS:
  {
2776
    if (check_global_access(thd, REPL_SLAVE_ACL))
2777
      goto error;
2778
    res = mysql_show_binlog_events(thd);
2779 2780
    break;
  }
2781 2782
#endif

2783
  case SQLCOM_BACKUP_TABLE:
2784
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2785 2786 2787
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_db_used(thd, all_tables) ||
	check_table_access(thd, SELECT_ACL, all_tables, 0) ||
2788
	check_global_access(thd, FILE_ACL))
2789
      goto error; /* purecov: inspected */
2790
    thd->enable_slow_log= opt_log_slow_admin_statements;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2791
    res = mysql_backup_table(thd, first_table);
2792
    select_lex->table_list.first= (byte*) first_table;
2793
    lex->query_tables=all_tables;
2794 2795
    break;
  }
2796
  case SQLCOM_RESTORE_TABLE:
2797
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2798 2799 2800
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_db_used(thd, all_tables) ||
	check_table_access(thd, INSERT_ACL, all_tables, 0) ||
2801
	check_global_access(thd, FILE_ACL))
2802
      goto error; /* purecov: inspected */
2803
    thd->enable_slow_log= opt_log_slow_admin_statements;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2804
    res = mysql_restore_table(thd, first_table);
2805
    select_lex->table_list.first= (byte*) first_table;
2806
    lex->query_tables=all_tables;
2807 2808
    break;
  }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2809 2810
  case SQLCOM_ASSIGN_TO_KEYCACHE:
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2811 2812 2813
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_db_used(thd, all_tables) ||
        check_access(thd, INDEX_ACL, first_table->db,
2814 2815
                     &first_table->grant.privilege, 0, 0,
                     test(first_table->schema_table)))
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2816
      goto error;
2817
    res= mysql_assign_to_keycache(thd, first_table, &lex->ident);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2818 2819
    break;
  }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2820 2821
  case SQLCOM_PRELOAD_KEYS:
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2822 2823 2824
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_db_used(thd, all_tables) ||
	check_access(thd, INDEX_ACL, first_table->db,
2825 2826
                     &first_table->grant.privilege, 0, 0,
                     test(first_table->schema_table)))
2827
      goto error;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2828
    res = mysql_preload_keys(thd, first_table);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2829 2830
    break;
  }
2831
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2832
  case SQLCOM_CHANGE_MASTER:
2833
  {
2834
    if (check_global_access(thd, SUPER_ACL))
2835
      goto error;
2836
    pthread_mutex_lock(&LOCK_active_mi);
2837
    res = change_master(thd,active_mi);
2838
    pthread_mutex_unlock(&LOCK_active_mi);
2839 2840
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2841
  case SQLCOM_SHOW_SLAVE_STAT:
2842
  {
2843 2844
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2845
      goto error;
2846
    pthread_mutex_lock(&LOCK_active_mi);
2847
    res = show_master_info(thd,active_mi);
2848
    pthread_mutex_unlock(&LOCK_active_mi);
2849 2850
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2851
  case SQLCOM_SHOW_MASTER_STAT:
2852
  {
2853 2854
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2855 2856 2857 2858
      goto error;
    res = show_binlog_info(thd);
    break;
  }
peter@mysql.com's avatar
peter@mysql.com committed
2859

2860
  case SQLCOM_LOAD_MASTER_DATA: // sync with master
2861
    if (check_global_access(thd, SUPER_ACL))
2862
      goto error;
2863
    if (end_active_trans(thd))
2864
      goto error;
2865 2866
    else
      res = load_master_data(thd);
2867
    break;
2868
#endif /* HAVE_REPLICATION */
2869 2870 2871 2872 2873 2874 2875
#ifdef HAVE_NDBCLUSTER_DB
  case SQLCOM_SHOW_NDBCLUSTER_STATUS:
    {
      res = ndbcluster_show_status(thd);
      break;
    }
#endif
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2876 2877 2878
#ifdef HAVE_INNOBASE_DB
  case SQLCOM_SHOW_INNODB_STATUS:
    {
2879
      if (check_global_access(thd, SUPER_ACL))
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2880 2881 2882 2883
	goto error;
      res = innodb_show_status(thd);
      break;
    }
vtkachenko@intelp4d.mysql.com's avatar
vtkachenko@intelp4d.mysql.com committed
2884 2885 2886
  case SQLCOM_SHOW_MUTEX_STATUS:
    {
      if (check_global_access(thd, SUPER_ACL))
vtkachenko@intelp4d.mysql.com's avatar
vtkachenko@intelp4d.mysql.com committed
2887
        goto error;
vtkachenko@intelp4d.mysql.com's avatar
vtkachenko@intelp4d.mysql.com committed
2888 2889 2890
      res = innodb_mutex_show_status(thd);
      break;
    }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2891
#endif
2892
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2893
  case SQLCOM_LOAD_MASTER_TABLE:
2894
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2895
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
2896 2897
    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
2898
    if (check_access(thd, CREATE_ACL, first_table->db,
2899 2900
		     &first_table->grant.privilege, 0, 0,
                     test(first_table->schema_table)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2901 2902 2903 2904
      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
2905
      if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0))
2906
	goto error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2907
    }
2908
    if (strlen(first_table->table_name) > NAME_LEN)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2909
    {
2910
      my_error(ER_WRONG_TABLE_NAME, MYF(0), first_table->table_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2911 2912
      break;
    }
2913
    pthread_mutex_lock(&LOCK_active_mi);
2914 2915 2916 2917
    /*
      fetch_master_table will send the error to the client on failure.
      Give error if the table already exists.
    */
2918
    if (!fetch_master_table(thd, first_table->db, first_table->table_name,
2919
			    active_mi, 0, 0))
2920
    {
2921
      send_ok(thd);
2922
    }
2923
    pthread_mutex_unlock(&LOCK_active_mi);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2924
    break;
2925
  }
2926
#endif /* HAVE_REPLICATION */
2927

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2928
  case SQLCOM_CREATE_TABLE:
2929
  {
2930
    /* If CREATE TABLE of non-temporary table, do implicit commit */
2931 2932 2933 2934 2935 2936 2937 2938 2939 2940
    if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
    {
      if (end_active_trans(thd))
      {
	res= -1;
	break;
      }
    }
    else 
    {
2941
      /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
2942
      thd->transaction.all.modified_non_trans_table= TRUE;
2943
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2944 2945 2946 2947 2948
    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;
2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962
    /*
      Code below (especially in mysql_create_table() and select_create
      methods) may modify HA_CREATE_INFO structure in LEX, so we have to
      use a copy of this structure to make execution prepared statement-
      safe. A shallow copy is enough as this code won't modify any memory
      referenced from this structure.
    */
    HA_CREATE_INFO create_info(lex->create_info);
    Alter_info alter_info(lex->alter_info, thd->mem_root);

    if (thd->is_fatal_error)
    {
      /* out of memory when creating a copy of alter_info */
      res= 1;
2963
      goto end_with_restore_list;
2964
    }
monty@mysql.com's avatar
monty@mysql.com committed
2965

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

2969
    create_info.alias= create_table->alias;
2970

2971
#ifndef HAVE_READLINK
2972
    if (create_info.data_file_name)
2973 2974
      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
                   "DATA DIRECTORY option ignored");
2975
    if (create_info.index_file_name)
2976 2977
      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
                   "INDEX DIRECTORY option ignored");
2978
    create_info.data_file_name= create_info.index_file_name= NULL;
2979
#else
2980
    /* Fix names if symlinked tables */
2981
    if (append_file_to_dir(thd, &create_info.data_file_name,
2982
			   create_table->table_name) ||
2983
	append_file_to_dir(thd, &create_info.index_file_name,
2984
			   create_table->table_name))
monty@mysql.com's avatar
monty@mysql.com committed
2985
      goto end_with_restore_list;
2986
#endif
2987
    /*
2988
      If we are using SET CHARSET without DEFAULT, add an implicit
2989 2990
      DEFAULT to not confuse old users. (This may change).
    */
2991
    if ((create_info.used_fields &
2992 2993 2994
	 (HA_CREATE_USED_DEFAULT_CHARSET | HA_CREATE_USED_CHARSET)) ==
	HA_CREATE_USED_CHARSET)
    {
2995 2996 2997 2998
      create_info.used_fields&= ~HA_CREATE_USED_CHARSET;
      create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
      create_info.default_table_charset= create_info.table_charset;
      create_info.table_charset= 0;
2999
    }
3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012
    /*
      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.
    */
3013 3014
    if (!thd->locked_tables &&
        !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
3015
    {
monty@mysql.com's avatar
monty@mysql.com committed
3016 3017
      res= 1;
      goto end_with_restore_list;
3018
    }
3019
    if (select_lex->item_list.elements)		// With select
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3020
    {
3021
      select_result *sel_result;
3022

3023
      select_lex->options|= SELECT_NO_UNLOCK;
3024
      unit->set_limit(select_lex);
3025

dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3026 3027 3028 3029 3030 3031 3032
      if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
      {
        lex->link_first_table_back(create_table, link_to_local);
        create_table->create= TRUE;
      }

      if (!(res= open_and_lock_tables(thd, lex->query_tables)))
3033
      {
3034 3035 3036 3037
        /*
          Is table which we are changing used somewhere in other parts
          of query
        */
3038
        if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
3039
        {
3040
          TABLE_LIST *duplicate;
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3041
          create_table= lex->unlink_first_table(&link_to_local);
3042
          if ((duplicate= unique_table(thd, create_table, select_tables, 0)))
3043 3044 3045
          {
            update_non_unique_table_error(create_table, "CREATE", duplicate);
            res= 1;
3046
            goto end_with_restore_list;
3047
          }
3048
        }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3049
        /* If we create merge table, we have to test tables in merge, too */
3050
        if (create_info.used_fields & HA_CREATE_USED_UNION)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3051 3052
        {
          TABLE_LIST *tab;
3053
          for (tab= (TABLE_LIST*) create_info.merge_list.first;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3054 3055 3056
               tab;
               tab= tab->next_local)
          {
3057
            TABLE_LIST *duplicate;
3058
            if ((duplicate= unique_table(thd, tab, select_tables, 0)))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3059
            {
3060
              update_non_unique_table_error(tab, "CREATE", duplicate);
monty@mysql.com's avatar
monty@mysql.com committed
3061
              res= 1;
3062
              goto end_with_restore_list;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3063 3064 3065
            }
          }
        }
3066 3067 3068 3069
        /*
          select_create is currently not re-execution friendly and
          needs to be created for every execution of a PS/SP.
        */
3070
        if ((sel_result= new select_create(create_table,
3071 3072
                                           &create_info,
                                           &alter_info,
3073 3074 3075
                                           select_lex->item_list,
                                           lex->duplicates,
                                           lex->ignore)))
3076 3077 3078 3079 3080
        {
          /*
            CREATE from SELECT give its SELECT_LEX for SELECT,
            and item_list belong to SELECT
          */
3081 3082
          res= handle_select(thd, lex, sel_result, 0);
          delete sel_result;
3083
        }
3084
      }
dlenev@mockturtle.local's avatar
dlenev@mockturtle.local committed
3085 3086 3087
      else if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
        create_table= lex->unlink_first_table(&link_to_local);

3088
    }
monty@mysql.com's avatar
monty@mysql.com committed
3089
    else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3090
    {
monty@mysql.com's avatar
monty@mysql.com committed
3091
      /* regular create */
3092 3093 3094
      if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
        res= mysql_create_like_table(thd, create_table, select_tables,
                                     &create_info);
venu@myvenu.com's avatar
venu@myvenu.com committed
3095
      else
3096
      {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3097
        res= mysql_create_table(thd, create_table->db,
3098
                                create_table->table_name, &create_info,
3099
                                &alter_info, 0, 0);
3100
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3101
      if (!res)
3102
	send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3103
    }
3104

monty@mysql.com's avatar
monty@mysql.com committed
3105
    /* put tables back for PS rexecuting */
monty@mysql.com's avatar
monty@mysql.com committed
3106
end_with_restore_list:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3107
    lex->link_first_table_back(create_table, link_to_local);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3108
    break;
3109
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3110
  case SQLCOM_CREATE_INDEX:
3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131
    /* Fall through */
  case SQLCOM_DROP_INDEX:
  /*
    CREATE INDEX and DROP INDEX are implemented by calling ALTER
    TABLE with proper arguments. This isn't very fast but it
    should work for most cases.

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

    One should normally create all indexes with CREATE TABLE or
    ALTER TABLE.
  */
  {
    Alter_info alter_info(lex->alter_info, thd->mem_root);
    HA_CREATE_INFO create_info;

    if (thd->is_fatal_error) /* out of memory creating a copy of alter_info*/
      goto error;

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3132
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3133
    if (check_one_table_access(thd, INDEX_ACL, first_table))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3134
      goto error; /* purecov: inspected */
3135
    if (end_active_trans(thd))
3136
      goto error;
3137 3138 3139 3140 3141 3142
    /*
      Currently CREATE INDEX or DROP INDEX cause a full table rebuild
      and thus classify as slow administrative statements just like
      ALTER TABLE.
    */
    thd->enable_slow_log= opt_log_slow_admin_statements;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3143

3144 3145
    bzero((char*) &create_info, sizeof(create_info));
    create_info.db_type= DB_TYPE_DEFAULT;
kostja@bodhi.local's avatar
kostja@bodhi.local committed
3146
    create_info.row_type= ROW_TYPE_NOT_USED;
3147
    create_info.default_table_charset= thd->variables.collation_database;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3148

3149
    res= mysql_alter_table(thd, first_table->db, first_table->table_name,
3150
                           &create_info, first_table, &alter_info,
3151
                           0, (ORDER*) 0, 0);
3152 3153
    break;
  }
3154
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3155
  case SQLCOM_SLAVE_START:
3156
  {
3157
    pthread_mutex_lock(&LOCK_active_mi);
3158
    start_slave(thd,active_mi,1 /* net report*/);
3159
    pthread_mutex_unlock(&LOCK_active_mi);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3160
    break;
3161
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3162
  case SQLCOM_SLAVE_STOP:
3163 3164 3165 3166 3167 3168
  /*
    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,
3169
      so it waits for the client thread because t is locked by it.
3170
    - then the client thread does SLAVE STOP.
3171 3172
      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.
3173 3174 3175
    To prevent that, refuse SLAVE STOP if the
    client thread has locked tables
  */
3176
  if (thd->locked_tables || thd->active_transaction() || thd->global_read_lock)
3177
  {
3178 3179
    my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
               ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
3180
    goto error;
3181
  }
3182
  {
3183
    pthread_mutex_lock(&LOCK_active_mi);
3184
    stop_slave(thd,active_mi,1/* net report*/);
3185
    pthread_mutex_unlock(&LOCK_active_mi);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3186
    break;
3187
  }
3188
#endif /* HAVE_REPLICATION */
3189

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3190
  case SQLCOM_ALTER_TABLE:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3191
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3192
    {
3193
      ulong priv=0;
3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204
      /*
        Code in mysql_alter_table() may modify its HA_CREATE_INFO argument,
        so we have to use a copy of this structure to make execution
        prepared statement- safe. A shallow copy is enough as no memory
        referenced from this structure will be modified.
      */
      HA_CREATE_INFO create_info(lex->create_info);
      Alter_info alter_info(lex->alter_info, thd->mem_root);

      if (thd->is_fatal_error) /* out of memory creating a copy of alter_info */
        goto error;
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
3205
      if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3206
      {
3207
	my_error(ER_WRONG_TABLE_NAME, MYF(0), lex->name);
3208
        goto error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3209
      }
3210 3211
      /* Must be set in the parser */
      DBUG_ASSERT(select_lex->db);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3212
      if (check_access(thd, ALTER_ACL, first_table->db,
3213 3214 3215 3216
		       &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
3217
	  check_merge_table_access(thd, first_table->db,
3218
				   (TABLE_LIST *)
3219
				   create_info.merge_list.first))
3220
	goto error;				/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3221 3222
      if (grant_option)
      {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3223
	if (check_grant(thd, ALTER_ACL, all_tables, 0, UINT_MAX, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3224 3225 3226 3227 3228
	  goto error;
	if (lex->name && !test_all_bits(priv,INSERT_ACL | CREATE_ACL))
	{					// Rename of table
	  TABLE_LIST tmp_table;
	  bzero((char*) &tmp_table,sizeof(tmp_table));
3229
	  tmp_table.table_name=lex->name;
3230
	  tmp_table.db=select_lex->db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3231
	  tmp_table.grant.privilege=priv;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3232 3233
	  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
3234 3235 3236
	    goto error;
	}
      }
3237
      /* Don't yet allow changing of symlinks with ALTER TABLE */
3238
      if (create_info.data_file_name)
3239 3240
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
                     "DATA DIRECTORY option ignored");
3241
      if (create_info.index_file_name)
3242 3243
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
                     "INDEX DIRECTORY option ignored");
3244
      create_info.data_file_name= create_info.index_file_name= NULL;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3245
      /* ALTER TABLE ends previous transaction */
3246
      if (end_active_trans(thd))
3247
	goto error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3248
      else
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3249
      {
3250 3251 3252 3253 3254 3255 3256
        if (!thd->locked_tables &&
            !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
        {
          res= 1;
          break;
        }

3257
        thd->enable_slow_log= opt_log_slow_admin_statements;
3258 3259
        res= mysql_alter_table(thd, select_lex->db, lex->name,
                               &create_info,
3260
                               first_table,
3261 3262
                               &alter_info,
                               select_lex->order_list.elements,
3263
                               (ORDER *) select_lex->order_list.first,
3264
                               lex->ignore);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3265
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3266 3267
      break;
    }
3268
  case SQLCOM_RENAME_TABLE:
3269
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3270
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3271
    TABLE_LIST *table;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3272
    if (check_db_used(thd, all_tables))
3273
      goto error;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3274
    for (table= first_table; table; table= table->next_local->next_local)
3275
    {
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3276
      if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
3277
		       &table->grant.privilege,0,0, test(table->schema_table)) ||
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3278
	  check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db,
3279 3280
		       &table->next_local->grant.privilege, 0, 0,
                       test(table->next_local->schema_table)))
3281 3282 3283
	goto error;
      if (grant_option)
      {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3284
	TABLE_LIST old_list, new_list;
3285 3286 3287 3288
	/*
	  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
3289 3290
	old_list= table[0];
	new_list= table->next_local[0];
3291
	if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, 0, 1, 0) ||
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3292
	    (!test_all_bits(table->next_local->grant.privilege,
3293
			    INSERT_ACL | CREATE_ACL) &&
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3294
	     check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0)))
3295 3296 3297
	  goto error;
      }
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3298
    query_cache_invalidate3(thd, first_table, 0);
3299 3300
    if (end_active_trans(thd) || mysql_rename_tables(thd, first_table))
      goto error;
3301
    break;
3302
  }
3303
#ifndef EMBEDDED_LIBRARY
3304 3305
  case SQLCOM_SHOW_BINLOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3306 3307
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0)); /* purecov: inspected */
3308
    goto error;
3309 3310
#else
    {
3311
      if (check_global_access(thd, SUPER_ACL))
3312 3313 3314 3315
	goto error;
      res = show_binlogs(thd);
      break;
    }
peter@mysql.com's avatar
peter@mysql.com committed
3316
#endif
3317
#endif /* EMBEDDED_LIBRARY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3318
  case SQLCOM_SHOW_CREATE:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3319
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3320
#ifdef DONT_ALLOW_SHOW_COMMANDS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3321 3322
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0)); /* purecov: inspected */
3323
    goto error;
3324
#else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3325
    {
3326 3327 3328 3329
      /* Ignore temporary tables if this is "SHOW CREATE VIEW" */
      if (lex->only_view)
        first_table->skip_temporary= 1;

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3330
      if (check_db_used(thd, all_tables) ||
3331
          check_show_create_table_access(thd, first_table))
3332
	goto error;
monty@mishka.local's avatar
monty@mishka.local committed
3333
      res= mysqld_show_create(thd, first_table);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3334 3335
      break;
    }
3336
#endif
3337 3338
  case SQLCOM_CHECKSUM:
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3339 3340 3341
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_db_used(thd, all_tables) ||
	check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, 0))
3342
      goto error; /* purecov: inspected */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3343
    res = mysql_checksum_table(thd, first_table, &lex->check_opt);
3344 3345
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3346
  case SQLCOM_REPAIR:
3347
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3348 3349 3350
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_db_used(thd, all_tables) ||
	check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
3351
      goto error; /* purecov: inspected */
3352
    thd->enable_slow_log= opt_log_slow_admin_statements;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3353
    res= mysql_repair_table(thd, first_table, &lex->check_opt);
3354 3355 3356
    /* ! we write after unlocking the table */
    if (!res && !lex->no_write_to_binlog)
    {
3357
      /* Presumably, REPAIR and binlog writing doesn't require synchronization */
3358 3359
      if (mysql_bin_log.is_open())
      {
3360
	thd->clear_error(); // No binlog error generated
3361
        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3362 3363 3364
        mysql_bin_log.write(&qinfo);
      }
    }
3365
    select_lex->table_list.first= (byte*) first_table;
3366
    lex->query_tables=all_tables;
3367 3368
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3369
  case SQLCOM_CHECK:
3370
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3371 3372 3373
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_db_used(thd, all_tables) ||
	check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, 0))
3374
      goto error; /* purecov: inspected */
3375
    thd->enable_slow_log= opt_log_slow_admin_statements;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3376
    res = mysql_check_table(thd, first_table, &lex->check_opt);
3377
    select_lex->table_list.first= (byte*) first_table;
3378
    lex->query_tables=all_tables;
3379 3380
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3381 3382
  case SQLCOM_ANALYZE:
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3383 3384 3385
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_db_used(thd, all_tables) ||
	check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3386
      goto error; /* purecov: inspected */
3387
    thd->enable_slow_log= opt_log_slow_admin_statements;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3388
    res = mysql_analyze_table(thd, first_table, &lex->check_opt);
3389 3390 3391
    /* ! we write after unlocking the table */
    if (!res && !lex->no_write_to_binlog)
    {
3392
      /* Presumably, ANALYZE and binlog writing doesn't require synchronization */
3393 3394
      if (mysql_bin_log.is_open())
      {
3395
	thd->clear_error(); // No binlog error generated
3396
        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3397 3398 3399
        mysql_bin_log.write(&qinfo);
      }
    }
3400
    select_lex->table_list.first= (byte*) first_table;
3401
    lex->query_tables=all_tables;
3402
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3403
  }
3404

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3405 3406
  case SQLCOM_OPTIMIZE:
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3407 3408 3409
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_db_used(thd, all_tables) ||
	check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3410
      goto error; /* purecov: inspected */
3411
    thd->enable_slow_log= opt_log_slow_admin_statements;
3412
    res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
3413
      mysql_recreate_table(thd, first_table) :
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3414
      mysql_optimize_table(thd, first_table, &lex->check_opt);
3415 3416 3417
    /* ! we write after unlocking the table */
    if (!res && !lex->no_write_to_binlog)
    {
3418
      /* Presumably, OPTIMIZE and binlog writing doesn't require synchronization */
3419 3420
      if (mysql_bin_log.is_open())
      {
3421
	thd->clear_error(); // No binlog error generated
3422
        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3423 3424 3425
        mysql_bin_log.write(&qinfo);
      }
    }
3426
    select_lex->table_list.first= (byte*) first_table;
3427
    lex->query_tables=all_tables;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3428 3429 3430
    break;
  }
  case SQLCOM_UPDATE:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3431 3432
    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
3433
      break;
3434 3435
    DBUG_ASSERT(select_lex->offset_limit == 0);
    unit->set_limit(select_lex);
3436 3437 3438 3439 3440 3441 3442 3443
    res= (up_result= mysql_update(thd, all_tables,
                                  select_lex->item_list,
                                  lex->value_list,
                                  select_lex->where,
                                  select_lex->order_list.elements,
                                  (ORDER *) select_lex->order_list.first,
                                  unit->select_limit_cnt,
                                  lex->duplicates, lex->ignore));
3444
    /* mysql_update return 2 if we need to switch to multi-update */
3445
    if (up_result != 2)
3446
      break;
3447
    /* Fall through */
3448
  case SQLCOM_UPDATE_MULTI:
3449 3450 3451
  {
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    /* if we switched from normal update, rights are checked */
3452
    if (up_result != 2)
3453
    {
3454 3455 3456 3457 3458
      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
3459

3460
    res= mysql_multi_update_prepare(thd);
3461

3462
#ifdef HAVE_REPLICATION
3463
    /* Check slave filtering rules */
3464
    if (unlikely(thd->slave_thread))
3465
    {
3466 3467
      if (all_tables_not_ok(thd, all_tables))
      {
3468 3469 3470 3471 3472
        if (res!= 0)
        {
          res= 0;             /* don't care of prev failure  */
          thd->clear_error(); /* filters are of highest prior */
        }
3473 3474 3475 3476
        /* we warn the slave SQL thread */
        my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
        break;
      }
3477 3478
      if (res)
        break;
3479
    }
3480 3481
    else
    {
3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494
#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
3495

3496 3497 3498 3499 3500 3501
    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
3502
    break;
monty@mysql.com's avatar
monty@mysql.com committed
3503
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3504
  case SQLCOM_REPLACE:
3505 3506
  case SQLCOM_INSERT:
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3507
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
monty@mysql.com's avatar
monty@mysql.com committed
3508
    if ((res= insert_precheck(thd, all_tables)))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3509
      break;
3510 3511 3512 3513 3514 3515 3516 3517

    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
3518
    res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
monty@mishka.local's avatar
monty@mishka.local committed
3519
		      lex->update_list, lex->value_list,
3520
                      lex->duplicates, lex->ignore);
3521 3522 3523 3524 3525 3526 3527

    /*
      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
3528
    if (first_table->view && !first_table->contain_auto_increment)
3529 3530
      thd->last_insert_id= thd->current_insert_id;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3531
    break;
3532
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3533 3534 3535
  case SQLCOM_REPLACE_SELECT:
  case SQLCOM_INSERT_SELECT:
  {
3536
    select_result *sel_result;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3537
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
monty@mishka.local's avatar
monty@mishka.local committed
3538
    if ((res= insert_precheck(thd, all_tables)))
3539
      break;
monty@mysql.com's avatar
monty@mysql.com committed
3540

3541
    /* Fix lock for first table */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3542 3543
    if (first_table->lock_type == TL_WRITE_DELAYED)
      first_table->lock_type= TL_WRITE;
3544

3545 3546
    /* 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
3547

3548
    unit->set_limit(select_lex);
3549 3550 3551 3552 3553 3554 3555 3556

    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
3557
    if (!(res= open_and_lock_tables(thd, all_tables)))
3558
    {
3559
      /* Skip first table, which is the table we are inserting in */
3560 3561
      TABLE_LIST *second_table= first_table->next_local;
      select_lex->table_list.first= (byte*) second_table;
3562 3563
      select_lex->context.table_list= 
        select_lex->context.first_name_resolution_table= second_table;
3564
      res= mysql_insert_select_prepare(thd);
3565 3566 3567 3568 3569 3570 3571
      if (!res && (sel_result= new select_insert(first_table,
                                                 first_table->table,
                                                 &lex->field_list,
                                                 &lex->update_list,
                                                 &lex->value_list,
                                                 lex->duplicates,
                                                 lex->ignore)))
3572
      {
3573
	res= handle_select(thd, lex, sel_result, OPTION_SETUP_TABLES_DONE);
3574 3575 3576 3577 3578 3579 3580 3581 3582
        /*
          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)
        {
3583 3584 3585
          /* INSERT ... SELECT should invalidate only the very first table */
          TABLE_LIST *save_table= first_table->next_local;
          first_table->next_local= 0;
3586 3587
          mysql_unlock_tables(thd, thd->lock);
          query_cache_invalidate3(thd, first_table, 1);
3588
          first_table->next_local= save_table;
3589 3590
          thd->lock=0;
        }
3591
        delete sel_result;
3592
      }
3593
      /* revert changes for SP */
msvensson@neptunus.(none)'s avatar
msvensson@neptunus.(none) committed
3594
      select_lex->table_list.first= (byte*) first_table;
3595
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3596

3597 3598 3599 3600 3601 3602
    /*
      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
3603
    if (first_table->view && !first_table->contain_auto_increment)
3604 3605
      thd->last_insert_id= thd->current_insert_id;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3606 3607
    break;
  }
3608
  case SQLCOM_TRUNCATE:
3609 3610 3611 3612 3613
    if (end_active_trans(thd))
    {
      res= -1;
      break;
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3614 3615
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_one_table_access(thd, DELETE_ACL, all_tables))
3616
      goto error;
3617 3618 3619 3620
    /*
      Don't allow this within a transaction because we want to use
      re-generate table
    */
3621
    if (thd->locked_tables || thd->active_transaction())
3622
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3623 3624
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
3625 3626
      goto error;
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3627

monty@mysql.com's avatar
monty@mysql.com committed
3628
    res= mysql_truncate(thd, first_table, 0);
3629
    break;
3630
  case SQLCOM_DELETE:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3631
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3632 3633
    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
3634
      break;
3635 3636
    DBUG_ASSERT(select_lex->offset_limit == 0);
    unit->set_limit(select_lex);
3637 3638 3639 3640 3641 3642 3643 3644

    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
3645
    res = mysql_delete(thd, all_tables, select_lex->where,
3646
                       &select_lex->order_list,
osku@127.(none)'s avatar
osku@127.(none) committed
3647 3648
                       unit->select_limit_cnt, select_lex->options,
                       FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3649 3650
    break;
  }
3651
  case SQLCOM_DELETE_MULTI:
3652
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3653
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3654
    TABLE_LIST *aux_tables=
3655
      (TABLE_LIST *)thd->lex->auxiliary_table_list.first;
3656
    multi_delete *del_result;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3657

3658 3659 3660 3661 3662 3663 3664
    if (!thd->locked_tables &&
        !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
    {
      res= 1;
      break;
    }

3665
    if ((res= multi_delete_precheck(thd, all_tables)))
3666
      break;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3667

3668
    /* condition will be TRUE on SP re-excuting */
3669 3670
    if (select_lex->item_list.elements != 0)
      select_lex->item_list.empty();
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3671
    if (add_item_to_list(thd, new Item_null()))
3672
      goto error;
3673

3674
    thd->proc_info="init";
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3675 3676 3677 3678
    if ((res= open_and_lock_tables(thd, all_tables)))
      break;

    if ((res= mysql_multi_delete_prepare(thd)))
3679
      goto error;
3680

3681 3682
    if (!thd->is_fatal_error &&
        (del_result= new multi_delete(aux_tables, lex->table_count)))
3683
    {
3684 3685 3686
      res= mysql_select(thd, &select_lex->ref_pointer_array,
			select_lex->get_table_list(),
			select_lex->with_wild,
3687
			select_lex->item_list,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3688
			select_lex->where,
3689
			0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3690 3691
			(ORDER *)NULL,
			select_lex->options | thd->options |
3692 3693
			SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
                        OPTION_SETUP_TABLES_DONE,
3694 3695
			del_result, unit, select_lex);
      delete del_result;
3696 3697
    }
    else
3698
      res= TRUE;                                // Error
3699 3700
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3701
  case SQLCOM_DROP_TABLE:
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3702
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3703
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3704 3705
    if (!lex->drop_temporary)
    {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3706
      if (check_table_access(thd, DROP_ACL, all_tables, 0))
3707 3708
	goto error;				/* purecov: inspected */
      if (end_active_trans(thd))
3709
        goto error;
3710
    }
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3711
    else
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3712 3713 3714 3715 3716 3717
    {
      /*
	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
3718 3719
	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
3720 3721
      */
      if (thd->slave_thread)
3722
        lex->drop_if_exists= 1;
3723

3724
      /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
3725
      thd->transaction.all.modified_non_trans_table= TRUE;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3726
    }
3727
    /* DDL and binlog write order protected by LOCK_open */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3728 3729
    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
3730 3731
  }
  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3732
  case SQLCOM_SHOW_PROCESSLIST:
3733 3734
    if (!thd->security_ctx->priv_user[0] &&
        check_global_access(thd,PROCESS_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3735
      break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3736
    mysqld_list_processes(thd,
3737 3738 3739 3740
			  (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
3741
    break;
3742 3743
  case SQLCOM_SHOW_STORAGE_ENGINES:
    res= mysqld_show_storage_engines(thd);
3744 3745 3746 3747 3748 3749 3750
    break;
  case SQLCOM_SHOW_PRIVILEGES:
    res= mysqld_show_privileges(thd);
    break;
  case SQLCOM_SHOW_COLUMN_TYPES:
    res= mysqld_show_column_types(thd);
    break;
tim@cane.mysql.fi's avatar
tim@cane.mysql.fi committed
3751 3752
  case SQLCOM_SHOW_LOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3753 3754
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0));	/* purecov: inspected */
3755
    goto error;
tim@cane.mysql.fi's avatar
tim@cane.mysql.fi committed
3756 3757
#else
    {
3758
      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
3759 3760 3761 3762
	goto error;
      res= mysqld_show_logs(thd);
      break;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3763 3764
#endif
  case SQLCOM_CHANGE_DB:
3765 3766 3767 3768
  {
    LEX_STRING db_str= { (char *) select_lex->db, strlen(select_lex->db) };

    if (!mysql_change_db(thd, &db_str, FALSE))
3769
      send_ok(thd);
3770

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3771
    break;
3772
  }
3773

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3774 3775
  case SQLCOM_LOAD:
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3776
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3777
    uint privilege= (lex->duplicates == DUP_REPLACE ?
3778 3779
		     INSERT_ACL | DELETE_ACL : INSERT_ACL) |
                    (lex->local_file ? 0 : FILE_ACL);
3780

3781
    if (lex->local_file)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3782
    {
3783
      if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) ||
3784
          !opt_local_infile)
3785
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3786
	my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), MYF(0));
3787 3788
	goto error;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3789
    }
3790 3791 3792 3793

    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
3794
    res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
3795
                    lex->update_list, lex->value_list, lex->duplicates,
3796
                    lex->ignore, (bool) lex->local_file);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3797 3798
    break;
  }
3799

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3800
  case SQLCOM_SET_OPTION:
3801 3802
  {
    List<set_var_base> *lex_var_list= &lex->var_list;
3803
    if ((check_table_access(thd, SELECT_ACL, all_tables, 0) ||
3804 3805
	 open_and_lock_tables(thd, all_tables)))
      goto error;
3806 3807
    if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
    {
3808 3809
      my_error(ER_RESERVED_SYNTAX, MYF(0), "SET ONE_SHOT");
      goto error;
3810 3811 3812 3813 3814 3815 3816 3817
    }
    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;
3818
      send_ok(thd);
3819
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3820
    break;
3821
  }
3822

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3823
  case SQLCOM_UNLOCK_TABLES:
3824 3825 3826 3827 3828 3829
    /*
      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.
    */
3830
    unlock_locked_tables(thd);
3831 3832
    if (thd->options & OPTION_TABLE_LOCK)
    {
3833
      end_active_trans(thd);
3834
      thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3835 3836
    }
    if (thd->global_read_lock)
3837
      unlock_global_read_lock(thd);
3838
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3839 3840
    break;
  case SQLCOM_LOCK_TABLES:
3841
    unlock_locked_tables(thd);
3842 3843 3844 3845
    /* we must end the trasaction first, regardless of anything */
    if (end_active_trans(thd))
      goto error;
    if (check_db_used(thd, all_tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3846
      goto error;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3847
    if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0))
3848
      goto error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3849
    thd->in_lock_tables=1;
3850
    thd->options|= OPTION_TABLE_LOCK;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3851

3852
    if (!(res= simple_open_n_lock_tables(thd, all_tables)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3853
    {
3854 3855
#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
3856
	query_cache.invalidate_locked_for_write(first_table);
3857
#endif /*HAVE_QUERY_CACHE*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3858 3859
      thd->locked_tables=thd->lock;
      thd->lock=0;
3860
      send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3861
    }
3862
    else
3863 3864 3865 3866 3867 3868 3869
    {
      /* 
        Need to end the current transaction, so the storage engine (InnoDB)
        can free its locks if LOCK TABLES locked some tables before finding
        that it can't lock a table in its list
      */
      end_active_trans(thd);
3870
      thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
3871
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3872 3873 3874
    thd->in_lock_tables=0;
    break;
  case SQLCOM_CREATE_DB:
3875
  {
3876 3877 3878 3879 3880 3881
    /*
      As mysql_create_db() may modify HA_CREATE_INFO structure passed to
      it, we need to use a copy of LEX::create_info to make execution
      prepared statement- safe.
    */
    HA_CREATE_INFO create_info(lex->create_info);
3882 3883 3884 3885 3886
    if (end_active_trans(thd))
    {
      res= -1;
      break;
    }
3887
    char *alias;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
3888
    if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3889
    {
3890
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3891 3892
      break;
    }
3893 3894 3895
    /*
      If in a slave thread :
      CREATE DATABASE DB was certainly not preceded by USE DB.
3896
      For that reason, db_ok() in sql/slave.cc did not check the
3897 3898 3899
      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.
    */
3900
#ifdef HAVE_REPLICATION
3901
    if (thd->slave_thread &&
3902 3903
	(!db_ok(lex->name, replicate_do_db, replicate_ignore_db) ||
	 !db_ok_with_wild_table(lex->name)))
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3904
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3905
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
3906
      break;
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3907
    }
3908
#endif
3909

3910
    if (check_access(thd,CREATE_ACL,lex->name,0,1,0,is_schema_db(lex->name)))
3911
      break;
monty@mysql.com's avatar
monty@mysql.com committed
3912
    res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name),
3913
			 &create_info, 0);
3914 3915
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3916
  case SQLCOM_DROP_DB:
3917
  {
3918 3919 3920 3921 3922
    if (end_active_trans(thd))
    {
      res= -1;
      break;
    }
3923
    if (check_db_name(lex->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3924
    {
3925
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3926 3927
      break;
    }
3928 3929 3930 3931 3932 3933 3934
    /*
      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.
    */
3935
#ifdef HAVE_REPLICATION
3936 3937 3938
    if (thd->slave_thread && 
	(!db_ok(lex->name, replicate_do_db, replicate_ignore_db) ||
	 !db_ok_with_wild_table(lex->name)))
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3939
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3940
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
3941
      break;
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3942
    }
3943
#endif
3944
    if (check_access(thd,DROP_ACL,lex->name,0,1,0,is_schema_db(lex->name)))
3945
      break;
3946 3947
    if (thd->locked_tables || thd->active_transaction())
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3948 3949
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
3950 3951
      goto error;
    }
3952
    res= mysql_rm_db(thd, lex->name, lex->drop_if_exists, 0);
3953 3954
    break;
  }
3955 3956
  case SQLCOM_ALTER_DB:
  {
3957 3958
    char *db= lex->name;
    DBUG_ASSERT(db); /* Must be set in the parser */
3959
    if (!strip_sp(db) || check_db_name(db))
3960
    {
3961
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
3962 3963
      break;
    }
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3964 3965 3966
    /*
      If in a slave thread :
      ALTER DATABASE DB may not be preceded by USE DB.
3967
      For that reason, maybe db_ok() in sql/slave.cc did not check the
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3968 3969 3970 3971
      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
3972
    if (thd->slave_thread &&
3973 3974
	(!db_ok(db, replicate_do_db, replicate_ignore_db) ||
	 !db_ok_with_wild_table(db)))
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3975
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3976
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3977 3978 3979
      break;
    }
#endif
3980
    if (check_access(thd, ALTER_ACL, db, 0, 1, 0, is_schema_db(db)))
3981 3982 3983
      break;
    if (thd->locked_tables || thd->active_transaction())
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3984 3985
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
3986 3987
      goto error;
    }
3988
    res= mysql_alter_db(thd, db, &lex->create_info);
3989 3990
    break;
  }
3991 3992 3993 3994
  case SQLCOM_SHOW_CREATE_DB:
  {
    if (!strip_sp(lex->name) || check_db_name(lex->name))
    {
3995
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
3996 3997
      break;
    }
3998
    if (check_access(thd,SELECT_ACL,lex->name,0,1,0,is_schema_db(lex->name)))
3999
      break;
4000
    res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
4001 4002
    break;
  }
monty@mysql.com's avatar
monty@mysql.com committed
4003
  case SQLCOM_CREATE_FUNCTION:                  // UDF function
monty@mysql.com's avatar
monty@mysql.com committed
4004
  {
4005
    if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0))
monty@mysql.com's avatar
monty@mysql.com committed
4006
      break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4007
#ifdef HAVE_DLOPEN
4008 4009
    if (sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
                        &thd->sp_func_cache, FALSE))
monty@mysql.com's avatar
monty@mysql.com committed
4010
    {
4011
      my_error(ER_UDF_EXISTS, MYF(0), lex->spname->m_name.str);
monty@mysql.com's avatar
monty@mysql.com committed
4012 4013
      goto error;
    }
4014
    if (!(res = mysql_create_function(thd, &lex->udf)))
monty@mysql.com's avatar
monty@mysql.com committed
4015
      send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4016
#else
hf@deer.(none)'s avatar
hf@deer.(none) committed
4017
    my_error(ER_CANT_OPEN_LIBRARY, MYF(0), lex->udf.dl, 0, "feature disabled");
4018
    res= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4019 4020
#endif
    break;
monty@mysql.com's avatar
monty@mysql.com committed
4021
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
4022
#ifndef NO_EMBEDDED_ACCESS_CHECKS
4023 4024
  case SQLCOM_CREATE_USER:
  {
4025
    if (check_access(thd, INSERT_ACL, "mysql", 0, 1, 1, 0) &&
4026
        check_global_access(thd,CREATE_USER_ACL))
4027
      break;
4028 4029
    if (end_active_trans(thd))
      goto error;
4030
    /* Conditionally writes to binlog */
4031 4032 4033 4034
    if (!(res= mysql_create_user(thd, lex->users_list)))
      send_ok(thd);
    break;
  }
4035 4036
  case SQLCOM_DROP_USER:
  {
4037
    if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 1, 0) &&
4038
        check_global_access(thd,CREATE_USER_ACL))
4039
      break;
4040 4041
    if (end_active_trans(thd))
      goto error;
4042
    /* Conditionally writes to binlog */
4043
    if (!(res= mysql_drop_user(thd, lex->users_list)))
4044 4045 4046 4047 4048
      send_ok(thd);
    break;
  }
  case SQLCOM_RENAME_USER:
  {
4049
    if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) &&
4050
        check_global_access(thd,CREATE_USER_ACL))
4051
      break;
4052 4053
    if (end_active_trans(thd))
      goto error;
4054
    /* Conditionally writes to binlog */
4055
    if (!(res= mysql_rename_user(thd, lex->users_list)))
4056 4057 4058 4059 4060
      send_ok(thd);
    break;
  }
  case SQLCOM_REVOKE_ALL:
  {
4061
    if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1, 0) &&
4062
        check_global_access(thd,CREATE_USER_ACL))
4063
      break;
4064
    /* Conditionally writes to binlog */
4065 4066 4067 4068
    if (!(res = mysql_revoke_all(thd, lex->users_list)))
      send_ok(thd);
    break;
  }
4069 4070 4071 4072
  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
4073
		     first_table ?  first_table->db : select_lex->db,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4074
		     first_table ? &first_table->grant.privilege : 0,
4075 4076 4077
		     first_table ? 0 : 1, 0,
                     first_table ? (bool) first_table->schema_table :
                     select_lex->db ? is_schema_db(select_lex->db) : 0))
4078 4079
      goto error;

4080
    if (thd->security_ctx->user)              // If not replication
4081
    {
4082
      LEX_USER *user, *tmp_user;
4083

4084
      List_iterator <LEX_USER> user_list(lex->users_list);
4085
      while ((tmp_user= user_list++))
4086
      {
4087 4088
        if (!(user= get_current_user(thd, tmp_user)))
          goto error;
4089 4090 4091 4092 4093 4094 4095 4096
        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);
4097
        if (strcmp(thd->security_ctx->user, user->user.str) ||
4098
            my_strcasecmp(system_charset_info,
4099
                          user->host.str, thd->security_ctx->host_or_ip))
4100 4101
        {
          // TODO: use check_change_password()
4102 4103
          if (is_acl_user(user->host.str, user->user.str) &&
              user->password.str &&
4104
              check_access(thd, UPDATE_ACL,"mysql",0,1,1,0))
4105 4106 4107 4108 4109 4110
          {
            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
4111 4112
      }
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4113
    if (first_table)
4114
    {
4115 4116
      if (lex->type == TYPE_ENUM_PROCEDURE ||
          lex->type == TYPE_ENUM_FUNCTION)
4117 4118 4119 4120 4121
      {
        uint grants= lex->all_privileges 
		   ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL)
		   : lex->grant;
        if (grant_option && 
4122 4123
	    check_grant_routine(thd, grants | GRANT_ACL, all_tables,
                                lex->type == TYPE_ENUM_PROCEDURE, 0))
4124
	  goto error;
4125
        /* Conditionally writes to binlog */
4126 4127 4128 4129
        res= mysql_routine_grant(thd, all_tables,
                                 lex->type == TYPE_ENUM_PROCEDURE, 
                                 lex->users_list, grants,
                                 lex->sql_command == SQLCOM_REVOKE, 0);
4130 4131 4132 4133 4134 4135 4136 4137
      }
      else
      {
	if (grant_option && check_grant(thd,
					(lex->grant | lex->grant_tot_col |
					 GRANT_ACL),
					all_tables, 0, UINT_MAX, 0))
	  goto error;
4138
        /* Conditionally writes to binlog */
4139 4140 4141 4142
        res= mysql_table_grant(thd, all_tables, lex->users_list,
			       lex->columns, lex->grant,
			       lex->sql_command == SQLCOM_REVOKE);
      }
4143 4144 4145
    }
    else
    {
4146
      if (lex->columns.elements || lex->type)
4147
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4148 4149
	my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
                   MYF(0));
4150
        goto error;
4151 4152
      }
      else
4153
	/* Conditionally writes to binlog */
4154 4155 4156 4157
	res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
			  lex->sql_command == SQLCOM_REVOKE);
      if (!res)
      {
4158
	if (lex->sql_command == SQLCOM_GRANT)
4159
	{
4160
	  List_iterator <LEX_USER> str_list(lex->users_list);
4161 4162 4163 4164 4165
	  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
4166
	    reset_mqh(user);
4167
          }
4168
	}
4169 4170 4171 4172
      }
    }
    break;
  }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
4173
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
4174
  case SQLCOM_RESET:
4175 4176 4177
    /*
      RESET commands are never written to the binary log, so we have to
      initialize this variable because RESET shares the same code as FLUSH
4178 4179 4180 4181
    */
    lex->no_write_to_binlog= 1;
  case SQLCOM_FLUSH:
  {
4182
    bool write_to_binlog;
4183
    if (check_global_access(thd,RELOAD_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4184
      goto error;
4185

4186 4187 4188 4189
    /*
      reload_acl_and_cache() will tell us if we are allowed to write to the
      binlog or not.
    */
4190
    if (!reload_acl_and_cache(thd, lex->type, first_table, &write_to_binlog))
4191 4192 4193 4194 4195
    {
      /*
        We WANT to write and we CAN write.
        ! we write after unlocking the table.
      */
4196
      /* Presumably, RESET and binlog writing doesn't require synchronization */
4197 4198 4199 4200
      if (!lex->no_write_to_binlog && write_to_binlog)
      {
        if (mysql_bin_log.is_open())
        {
4201
          Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
4202 4203 4204 4205
          mysql_bin_log.write(&qinfo);
        }
      }
      send_ok(thd);
4206 4207
    } 
    
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4208
    break;
4209
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4210
  case SQLCOM_KILL:
4211 4212 4213
  {
    Item *it= (Item *)lex->value_list.head();

4214
    if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
4215 4216 4217 4218 4219 4220
    {
      my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
		 MYF(0));
      goto error;
    }
    kill_one_thread(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4221
    break;
4222
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
4223
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4224
  case SQLCOM_SHOW_GRANTS:
4225 4226 4227 4228
  {
    LEX_USER *grant_user= get_current_user(thd, lex->grant_user);
    if (!grant_user)
      goto error;
4229
    if ((thd->security_ctx->priv_user &&
4230
	 !strcmp(thd->security_ctx->priv_user, grant_user->user.str)) ||
4231
	!check_access(thd, SELECT_ACL, "mysql",0,1,0,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4232
    {
4233
      res = mysql_show_grants(thd, grant_user);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4234 4235
    }
    break;
4236
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
4237
#endif
4238
  case SQLCOM_HA_OPEN:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4239 4240 4241
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_db_used(thd, all_tables) ||
	check_table_access(thd, SELECT_ACL, all_tables, 0))
4242
      goto error;
monty@mysql.com's avatar
monty@mysql.com committed
4243
    res= mysql_ha_open(thd, first_table, 0);
4244 4245
    break;
  case SQLCOM_HA_CLOSE:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4246 4247
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_db_used(thd, all_tables))
4248
      goto error;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4249
    res= mysql_ha_close(thd, first_table);
4250 4251
    break;
  case SQLCOM_HA_READ:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4252
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
4253 4254 4255 4256 4257
    /*
      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.
    */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4258
    if (check_db_used(thd, all_tables))
4259
      goto error;
4260
    unit->set_limit(select_lex);
4261
    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
4262
                       lex->insert_list, lex->ha_rkey_mode, select_lex->where,
4263
                       unit->select_limit_cnt, unit->offset_limit_cnt);
4264 4265
    break;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4266
  case SQLCOM_BEGIN:
4267 4268 4269 4270 4271 4272
    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;
    }
4273
    if (begin_trans(thd))
4274
      goto error;
serg@serg.mylan's avatar
serg@serg.mylan committed
4275
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4276 4277
    break;
  case SQLCOM_COMMIT:
4278
    if (end_trans(thd, lex->tx_release ? COMMIT_RELEASE :
serg@serg.mylan's avatar
serg@serg.mylan committed
4279
                              lex->tx_chain ? COMMIT_AND_CHAIN : COMMIT))
4280
      goto error;
4281
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4282 4283
    break;
  case SQLCOM_ROLLBACK:
4284
    if (end_trans(thd, lex->tx_release ? ROLLBACK_RELEASE :
serg@serg.mylan's avatar
serg@serg.mylan committed
4285
                              lex->tx_chain ? ROLLBACK_AND_CHAIN : ROLLBACK))
4286
      goto error;
4287
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4288
    break;
serg@serg.mylan's avatar
serg@serg.mylan committed
4289
  case SQLCOM_RELEASE_SAVEPOINT:
serg@serg.mylan's avatar
serg@serg.mylan committed
4290
  {
4291 4292
    SAVEPOINT *sv;
    for (sv=thd->transaction.savepoints; sv; sv=sv->prev)
serg@serg.mylan's avatar
serg@serg.mylan committed
4293 4294 4295
    {
      if (my_strnncoll(system_charset_info,
                       (uchar *)lex->ident.str, lex->ident.length,
4296
                       (uchar *)sv->name, sv->length) == 0)
serg@serg.mylan's avatar
serg@serg.mylan committed
4297 4298
        break;
    }
4299
    if (sv)
serg@serg.mylan's avatar
serg@serg.mylan committed
4300
    {
4301
      if (ha_release_savepoint(thd, sv))
serg@serg.mylan's avatar
serg@serg.mylan committed
4302
        res= TRUE; // cannot happen
serg@serg.mylan's avatar
serg@serg.mylan committed
4303 4304
      else
        send_ok(thd);
4305
      thd->transaction.savepoints=sv->prev;
serg@serg.mylan's avatar
serg@serg.mylan committed
4306
    }
4307
    else
serg@serg.mylan's avatar
serg@serg.mylan committed
4308
      my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4309
    break;
serg@serg.mylan's avatar
serg@serg.mylan committed
4310
  }
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4311
  case SQLCOM_ROLLBACK_TO_SAVEPOINT:
serg@serg.mylan's avatar
serg@serg.mylan committed
4312
  {
4313 4314
    SAVEPOINT *sv;
    for (sv=thd->transaction.savepoints; sv; sv=sv->prev)
4315 4316 4317
    {
      if (my_strnncoll(system_charset_info,
                       (uchar *)lex->ident.str, lex->ident.length,
4318
                       (uchar *)sv->name, sv->length) == 0)
4319 4320
        break;
    }
4321
    if (sv)
4322
    {
4323
      if (ha_rollback_to_savepoint(thd, sv))
4324 4325
        res= TRUE; // cannot happen
      else
serg@serg.mylan's avatar
serg@serg.mylan committed
4326
      {
4327
        if (thd->transaction.all.modified_non_trans_table &&
serg@serg.mylan's avatar
serg@serg.mylan committed
4328 4329 4330 4331 4332 4333
            !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);
      }
4334
      thd->transaction.savepoints=sv;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4335 4336
    }
    else
4337
      my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SAVEPOINT", lex->ident.str);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4338
    break;
4339
  }
4340
  case SQLCOM_SAVEPOINT:
4341 4342
    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
4343
      send_ok(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
4344
    else
4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382
    {
      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);
      }
    }
4383
    break;
4384 4385
  case SQLCOM_CREATE_PROCEDURE:
  case SQLCOM_CREATE_SPFUNCTION:
monty@mysql.com's avatar
monty@mysql.com committed
4386
  {
4387
    uint namelen;
4388
    char *name;
4389
    int sp_result= SP_INTERNAL_ERROR;
4390

4391
    DBUG_ASSERT(lex->sphead != 0);
4392
    DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */
4393 4394 4395 4396 4397 4398 4399
    /*
      Verify that the database name is allowed, optionally
      lowercase it.
    */
    if (check_db_name(lex->sphead->m_db.str))
    {
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->sphead->m_db.str);
4400
      goto create_sp_error;
4401 4402 4403
    }

    /*
4404 4405 4406
      Check that a database directory with this name
      exists. Design note: This won't work on virtual databases
      like information_schema.
4407 4408 4409 4410
    */
    if (check_db_dir_existence(lex->sphead->m_db.str))
    {
      my_error(ER_BAD_DB_ERROR, MYF(0), lex->sphead->m_db.str);
4411
      goto create_sp_error;
4412 4413
    }

4414 4415
    if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, 0, 0, 0,
                     is_schema_db(lex->sphead->m_db.str)))
4416
      goto create_sp_error;
4417

4418 4419
    if (end_active_trans(thd))
      goto create_sp_error;
4420 4421

    name= lex->sphead->name(&namelen);
4422
#ifdef HAVE_DLOPEN
monty@mysql.com's avatar
monty@mysql.com committed
4423 4424 4425
    if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
    {
      udf_func *udf = find_udf(name, namelen);
4426

monty@mysql.com's avatar
monty@mysql.com committed
4427
      if (udf)
4428
      {
4429 4430
        my_error(ER_UDF_EXISTS, MYF(0), name);
        goto create_sp_error;
4431
      }
monty@mysql.com's avatar
monty@mysql.com committed
4432 4433 4434
    }
#endif

4435 4436 4437
    /*
      If the definer is not specified, this means that CREATE-statement missed
      DEFINER-clause. DEFINER-clause can be missed in two cases:
4438

4439 4440 4441 4442 4443 4444 4445 4446
        - 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.
4447

4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461
          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)
    {
4462
      bool local_res= FALSE;
4463 4464 4465 4466
      Query_arena original_arena;
      Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena);

      if (!(lex->definer= create_default_definer(thd)))
4467
        local_res= TRUE;
4468 4469 4470 4471

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

4472
      /* Error has been already reported. */
4473
      if (local_res)
4474
        goto create_sp_error;
4475 4476 4477 4478 4479 4480 4481 4482 4483 4484

      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).
    */
4485

4486 4487 4488 4489 4490 4491 4492 4493
    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");
4494
        goto create_sp_error;
4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512
      }
    }

    /* 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 */

4513
    res= (sp_result= lex->sphead->create(thd));
4514
    switch (sp_result) {
4515
    case SP_OK:
4516
#ifndef NO_EMBEDDED_ACCESS_CHECKS
4517
      /* only add privileges if really neccessary */
4518
      if (sp_automatic_privileges && !opt_noacl &&
4519
          check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS,
4520
                               lex->sphead->m_db.str, name,
4521
                               lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1))
4522
      {
4523
        if (sp_grant_privileges(thd, lex->sphead->m_db.str, name,
4524
                                lex->sql_command == SQLCOM_CREATE_PROCEDURE))
4525 4526 4527 4528
          push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                       ER_PROC_AUTO_GRANT_FAIL,
                       ER(ER_PROC_AUTO_GRANT_FAIL));
        close_thread_tables(thd);
4529
      }
4530
#endif
monty@mysql.com's avatar
monty@mysql.com committed
4531
    break;
4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550
    case SP_WRITE_ROW_FAILED:
      my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name);
    break;
    case SP_BAD_IDENTIFIER:
      my_error(ER_TOO_LONG_IDENT, MYF(0), name);
    break;
    case SP_BODY_TOO_LONG:
      my_error(ER_TOO_LONG_BODY, MYF(0), name);
    break;
    default:
      my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name);
    break;
    } /* end switch */

    /*
      Capture all errors within this CASE and
      clean up the environment.
    */
create_sp_error:
4551
    if (sp_result != SP_OK )
4552 4553 4554 4555
      goto error;
    send_ok(thd);
    break; /* break super switch */
  } /* end case group bracket */
4556 4557 4558 4559
  case SQLCOM_CALL:
    {
      sp_head *sp;

4560 4561 4562 4563 4564 4565 4566 4567 4568
      /*
        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;

      /*
4569 4570
        By this moment all needed SPs should be in cache so no need to look 
        into DB. 
4571
      */
4572 4573
      if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
                                &thd->sp_proc_cache, TRUE)))
4574
      {
4575
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
4576
                 lex->spname->m_qname.str);
4577
	goto error;
4578 4579 4580
      }
      else
      {
pem@mysql.comhem.se's avatar
pem@mysql.comhem.se committed
4581
	ha_rows select_limit;
monty@mysql.com's avatar
monty@mysql.com committed
4582 4583
        /* bits that should be cleared in thd->server_status */
	uint bits_to_be_cleared= 0;
4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595
        /*
          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;
        }
4596

4597
	my_bool save_no_send_ok= thd->net.no_send_ok;
4598
	thd->net.no_send_ok= TRUE;
4599
	if (sp->m_flags & sp_head::MULTI_RESULTS)
4600
	{
4601
	  if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS))
4602
	  {
4603 4604 4605 4606
            /*
              The client does not support multiple result sets being sent
              back
            */
4607
	    my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str);
4608
	    thd->net.no_send_ok= save_no_send_ok;
4609 4610
	    goto error;
	  }
monty@mysql.com's avatar
monty@mysql.com committed
4611 4612 4613 4614 4615 4616 4617
          /*
            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;
4618 4619
	}

4620
#ifndef NO_EMBEDDED_ACCESS_CHECKS
4621
	if (check_routine_access(thd, EXECUTE_ACL,
4622
				 sp->m_db.str, sp->m_name.str, TRUE, FALSE))
4623
	{
4624
	  thd->net.no_send_ok= save_no_send_ok;
4625 4626
	  goto error;
	}
4627
#endif
pem@mysql.comhem.se's avatar
pem@mysql.comhem.se committed
4628 4629
	select_limit= thd->variables.select_limit;
	thd->variables.select_limit= HA_POS_ERROR;
4630

4631
        /* 
4632
          We never write CALL statements into binlog:
4633 4634 4635 4636 4637
           - 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.
4638
        */
4639
	res= sp->execute_procedure(thd, &lex->value_list);
4640 4641 4642
	/*
          If warnings have been cleared, we have to clear total_warn_count
          too, otherwise the clients get confused.
4643 4644 4645 4646
	 */
	if (thd->warn_list.is_empty())
	  thd->total_warn_count= 0;

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

4649
	thd->net.no_send_ok= save_no_send_ok;
monty@mysql.com's avatar
monty@mysql.com committed
4650
        thd->server_status&= ~bits_to_be_cleared;
4651

4652
	if (!res)
monty@mysql.com's avatar
monty@mysql.com committed
4653 4654
	  send_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 :
                                thd->row_count_func));
4655 4656
	else
	  goto error;		// Substatement should already have sent error
4657
      }
4658
      break;
4659 4660
    }
  case SQLCOM_ALTER_PROCEDURE:
4661
  case SQLCOM_ALTER_FUNCTION:
4662
    {
4663
      int sp_result;
4664 4665 4666 4667
      sp_head *sp;
      st_sp_chistics chistics;

      memcpy(&chistics, &lex->sp_chistics, sizeof(chistics));
4668
      if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
4669 4670
        sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
                            &thd->sp_proc_cache, FALSE);
4671
      else
4672 4673
        sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
                            &thd->sp_func_cache, FALSE);
monty@mysql.com's avatar
monty@mysql.com committed
4674
      mysql_reset_errors(thd, 0);
4675
      if (! sp)
4676 4677
      {
	if (lex->spname->m_db.str)
4678
	  sp_result= SP_KEY_NOT_FOUND;
4679 4680 4681 4682 4683 4684
	else
	{
	  my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
	  goto error;
	}
      }
4685 4686
      else
      {
4687 4688 4689
        if (check_routine_access(thd, ALTER_PROC_ACL, sp->m_db.str, 
				 sp->m_name.str,
                                 lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0))
4690
	  goto error;
4691 4692 4693

        if (end_active_trans(thd)) 
          goto error;
4694
	memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics));
4695 4696
        if ((sp->m_type == TYPE_ENUM_FUNCTION) &&
            !trust_function_creators &&  mysql_bin_log.is_open() &&
4697 4698 4699 4700 4701 4702
            !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));
4703
          sp_result= SP_INTERNAL_ERROR;
4704 4705 4706
        }
        else
        {
4707 4708 4709 4710 4711 4712
          /*
            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.
          */
4713
          /* Conditionally writes to binlog */
4714
          if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
4715 4716
            sp_result= sp_update_procedure(thd, lex->spname,
                                           &lex->sp_chistics);
4717
          else
4718
            sp_result= sp_update_function(thd, lex->spname, &lex->sp_chistics);
4719
        }
4720
      }
4721
      switch (sp_result)
4722
      {
4723
      case SP_OK:
4724
	send_ok(thd);
4725 4726
	break;
      case SP_KEY_NOT_FOUND:
4727 4728
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
4729 4730
	goto error;
      default:
4731 4732
	my_error(ER_SP_CANT_ALTER, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
4733
	goto error;
4734
      }
4735
      break;
4736 4737
    }
  case SQLCOM_DROP_PROCEDURE:
4738
  case SQLCOM_DROP_FUNCTION:
4739
    {
4740
      int sp_result;
4741 4742
      int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
                 TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
4743

4744
      sp_result= sp_routine_exists_in_table(thd, type, lex->spname);
monty@mysql.com's avatar
monty@mysql.com committed
4745
      mysql_reset_errors(thd, 0);
4746
      if (sp_result == SP_OK)
4747
      {
4748 4749 4750
        char *db= lex->spname->m_db.str;
	char *name= lex->spname->m_name.str;

4751 4752
	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
4753
          goto error;
4754 4755 4756

        if (end_active_trans(thd)) 
          goto error;
4757
#ifndef NO_EMBEDDED_ACCESS_CHECKS
4758
	if (sp_automatic_privileges && !opt_noacl &&
4759 4760
	    sp_revoke_privileges(thd, db, name, 
                                 lex->sql_command == SQLCOM_DROP_PROCEDURE))
4761 4762 4763 4764 4765
	{
	  push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
		       ER_PROC_AUTO_REVOKE_FAIL,
		       ER(ER_PROC_AUTO_REVOKE_FAIL));
	}
4766
#endif
4767
          /* Conditionally writes to binlog */
4768 4769
	if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
	  sp_result= sp_drop_procedure(thd, lex->spname);
4770
	else
4771
	  sp_result= sp_drop_function(thd, lex->spname);
4772 4773 4774
      }
      else
      {
4775
#ifdef HAVE_DLOPEN
4776 4777 4778 4779 4780 4781
	if (lex->sql_command == SQLCOM_DROP_FUNCTION)
	{
          udf_func *udf = find_udf(lex->spname->m_name.str,
                                   lex->spname->m_name.length);
          if (udf)
          {
4782
	    if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0, 0))
4783
	      goto error;
4784 4785

	    /* Does NOT write to binlog */
4786
	    if (!(res = mysql_drop_function(thd, &lex->spname->m_name)))
4787
	    {
4788 4789
	      send_ok(thd);
	      break;
4790 4791
	    }
	  }
4792
	}
4793
#endif
4794
	if (lex->spname->m_db.str)
4795
	  sp_result= SP_KEY_NOT_FOUND;
4796 4797 4798 4799 4800
	else
	{
	  my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
	  goto error;
	}
4801
      }
4802 4803
      res= sp_result;
      switch (sp_result) {
4804
      case SP_OK:
4805
	send_ok(thd);
4806 4807
	break;
      case SP_KEY_NOT_FOUND:
4808 4809
	if (lex->drop_if_exists)
	{
4810
	  push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
4811
			      ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
4812
			      SP_COM_STRING(lex), lex->spname->m_name.str);
4813
	  res= FALSE;
4814 4815 4816
	  send_ok(thd);
	  break;
	}
4817 4818
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
4819 4820
	goto error;
      default:
4821 4822
	my_error(ER_SP_DROP_FAILED, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
4823
	goto error;
4824
      }
4825
      break;
4826
    }
4827 4828
  case SQLCOM_SHOW_CREATE_PROC:
    {
4829
      if (lex->spname->m_name.length > NAME_LEN)
4830
      {
4831
	my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
4832 4833
	goto error;
      }
4834
      if (sp_show_create_procedure(thd, lex->spname) != SP_OK)
4835
      {			/* We don't distinguish between errors for now */
4836 4837
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_name.str);
4838 4839 4840 4841 4842 4843
	goto error;
      }
      break;
    }
  case SQLCOM_SHOW_CREATE_FUNC:
    {
4844
      if (lex->spname->m_name.length > NAME_LEN)
4845
      {
4846
	my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
4847 4848
	goto error;
      }
4849
      if (sp_show_create_function(thd, lex->spname) != SP_OK)
4850
      {			/* We don't distinguish between errors for now */
4851 4852
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_name.str);
4853 4854 4855 4856 4857 4858
	goto error;
      }
      break;
    }
  case SQLCOM_SHOW_STATUS_PROC:
    {
4859
      res= sp_show_status_procedure(thd, (lex->wild ?
4860 4861 4862 4863 4864
					  lex->wild->ptr() : NullS));
      break;
    }
  case SQLCOM_SHOW_STATUS_FUNC:
    {
4865
      res= sp_show_status_function(thd, (lex->wild ? 
4866 4867 4868
					 lex->wild->ptr() : NullS));
      break;
    }
pem@mysql.com's avatar
pem@mysql.com committed
4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880
#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
4881 4882
        sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
                            &thd->sp_proc_cache, FALSE);
pem@mysql.com's avatar
pem@mysql.com committed
4883
      else
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4884 4885
        sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
                            &thd->sp_func_cache, FALSE);
4886
      if (!sp || sp->show_routine_code(thd))
4887 4888
      {
        /* We don't distinguish between errors for now */
pem@mysql.com's avatar
pem@mysql.com committed
4889 4890 4891 4892 4893 4894 4895
        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
4896 4897
  case SQLCOM_CREATE_VIEW:
    {
4898 4899 4900 4901
      /*
        Note: SQLCOM_CREATE_VIEW also handles 'ALTER VIEW' commands
        as specified through the thd->lex->create_view_mode flag.
      */
4902 4903 4904
      if (end_active_trans(thd))
        goto error;

4905
      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
4906 4907 4908 4909
      break;
    }
  case SQLCOM_DROP_VIEW:
    {
4910 4911 4912
      if (check_table_access(thd, DROP_ACL, all_tables, 0) ||
          end_active_trans(thd))
        goto error;
4913 4914
      /* 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
4915 4916
      break;
    }
4917 4918
  case SQLCOM_CREATE_TRIGGER:
  {
4919 4920 4921
    if (end_active_trans(thd))
      goto error;

4922
    /* Conditionally writes to binlog. */
4923 4924
    res= mysql_create_or_drop_trigger(thd, all_tables, 1);

4925 4926 4927 4928
    break;
  }
  case SQLCOM_DROP_TRIGGER:
  {
4929 4930 4931
    if (end_active_trans(thd))
      goto error;

4932
    /* Conditionally writes to binlog. */
4933 4934 4935
    res= mysql_create_or_drop_trigger(thd, all_tables, 0);
    break;
  }
4936
  case SQLCOM_XA_START:
4937 4938
    if (thd->transaction.xid_state.xa_state == XA_IDLE &&
        thd->lex->xa_opt == XA_RESUME)
4939
    {
4940
      if (! thd->transaction.xid_state.xid.eq(thd->lex->xid))
4941 4942 4943 4944
      {
        my_error(ER_XAER_NOTA, MYF(0));
        break;
      }
4945
      thd->transaction.xid_state.xa_state=XA_ACTIVE;
4946 4947 4948
      send_ok(thd);
      break;
    }
serg@serg.mylan's avatar
serg@serg.mylan committed
4949
    if (thd->lex->xa_opt != XA_NONE)
4950 4951 4952 4953
    { // JOIN is not supported yet. TODO
      my_error(ER_XAER_INVAL, MYF(0));
      break;
    }
4954
    if (thd->transaction.xid_state.xa_state != XA_NOTR)
4955
    {
4956
      my_error(ER_XAER_RMFAIL, MYF(0),
4957
               xa_state_names[thd->transaction.xid_state.xa_state]);
4958 4959 4960 4961 4962 4963 4964
      break;
    }
    if (thd->active_transaction() || thd->locked_tables)
    {
      my_error(ER_XAER_OUTSIDE, MYF(0));
      break;
    }
4965 4966 4967 4968 4969 4970 4971 4972 4973
    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);
4974
    thd->transaction.all.modified_non_trans_table= FALSE;
4975
    thd->options|= (ulong) OPTION_BEGIN;
4976 4977 4978 4979 4980 4981 4982 4983 4984 4985
    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;
    }
4986
    if (thd->transaction.xid_state.xa_state != XA_ACTIVE)
4987
    {
4988
      my_error(ER_XAER_RMFAIL, MYF(0),
4989
               xa_state_names[thd->transaction.xid_state.xa_state]);
4990 4991
      break;
    }
4992
    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
4993 4994 4995 4996
    {
      my_error(ER_XAER_NOTA, MYF(0));
      break;
    }
4997
    thd->transaction.xid_state.xa_state=XA_IDLE;
4998 4999 5000
    send_ok(thd);
    break;
  case SQLCOM_XA_PREPARE:
5001
    if (thd->transaction.xid_state.xa_state != XA_IDLE)
5002
    {
5003
      my_error(ER_XAER_RMFAIL, MYF(0),
5004
               xa_state_names[thd->transaction.xid_state.xa_state]);
5005 5006
      break;
    }
5007
    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
5008 5009 5010 5011 5012 5013 5014
    {
      my_error(ER_XAER_NOTA, MYF(0));
      break;
    }
    if (ha_prepare(thd))
    {
      my_error(ER_XA_RBROLLBACK, MYF(0));
5015 5016
      xid_cache_delete(&thd->transaction.xid_state);
      thd->transaction.xid_state.xa_state=XA_NOTR;
5017 5018
      break;
    }
5019
    thd->transaction.xid_state.xa_state=XA_PREPARED;
5020 5021 5022
    send_ok(thd);
    break;
  case SQLCOM_XA_COMMIT:
5023
    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
5024
    {
5025 5026
      XID_STATE *xs=xid_cache_search(thd->lex->xid);
      if (!xs || xs->in_thd)
5027
        my_error(ER_XAER_NOTA, MYF(0));
serg@serg.mylan's avatar
serg@serg.mylan committed
5028
      else
5029 5030 5031
      {
        ha_commit_or_rollback_by_xid(thd->lex->xid, 1);
        xid_cache_delete(xs);
serg@serg.mylan's avatar
serg@serg.mylan committed
5032
        send_ok(thd);
5033
      }
5034 5035
      break;
    }
5036
    if (thd->transaction.xid_state.xa_state == XA_IDLE &&
monty@mysql.com's avatar
monty@mysql.com committed
5037
        thd->lex->xa_opt == XA_ONE_PHASE)
5038
    {
5039 5040 5041
      int r;
      if ((r= ha_commit(thd)))
        my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
5042 5043 5044
      else
        send_ok(thd);
    }
5045
    else if (thd->transaction.xid_state.xa_state == XA_PREPARED &&
monty@mysql.com's avatar
monty@mysql.com committed
5046
             thd->lex->xa_opt == XA_NONE)
5047
    {
5048 5049 5050
      if (wait_if_global_read_lock(thd, 0, 0))
      {
        ha_rollback(thd);
5051
        my_error(ER_XAER_RMERR, MYF(0));
5052
      }
5053
      else
5054 5055 5056 5057 5058 5059 5060
      {
        if (ha_commit_one_phase(thd, 1))
          my_error(ER_XAER_RMERR, MYF(0));
        else
          send_ok(thd);
        start_waiting_global_read_lock(thd);
      }
5061 5062 5063
    }
    else
    {
5064
      my_error(ER_XAER_RMFAIL, MYF(0),
5065
               xa_state_names[thd->transaction.xid_state.xa_state]);
5066 5067
      break;
    }
5068
    thd->options&= ~(ulong) OPTION_BEGIN;
5069
    thd->transaction.all.modified_non_trans_table= FALSE;
5070
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
5071 5072
    xid_cache_delete(&thd->transaction.xid_state);
    thd->transaction.xid_state.xa_state=XA_NOTR;
5073 5074
    break;
  case SQLCOM_XA_ROLLBACK:
5075
    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
5076
    {
5077 5078
      XID_STATE *xs=xid_cache_search(thd->lex->xid);
      if (!xs || xs->in_thd)
5079
        my_error(ER_XAER_NOTA, MYF(0));
serg@serg.mylan's avatar
serg@serg.mylan committed
5080
      else
5081 5082 5083
      {
        ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
        xid_cache_delete(xs);
serg@serg.mylan's avatar
serg@serg.mylan committed
5084
        send_ok(thd);
5085
      }
5086 5087
      break;
    }
5088 5089
    if (thd->transaction.xid_state.xa_state != XA_IDLE &&
        thd->transaction.xid_state.xa_state != XA_PREPARED)
5090
    {
5091
      my_error(ER_XAER_RMFAIL, MYF(0),
5092
               xa_state_names[thd->transaction.xid_state.xa_state]);
5093 5094 5095 5096 5097 5098
      break;
    }
    if (ha_rollback(thd))
      my_error(ER_XAER_RMERR, MYF(0));
    else
      send_ok(thd);
5099
    thd->options&= ~(ulong) OPTION_BEGIN;
5100
    thd->transaction.all.modified_non_trans_table= FALSE;
5101
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
5102 5103
    xid_cache_delete(&thd->transaction.xid_state);
    thd->transaction.xid_state.xa_state=XA_NOTR;
5104 5105
    break;
  case SQLCOM_XA_RECOVER:
5106
    res= mysql_xa_recover(thd);
5107 5108
    break;
  default:
5109
#ifndef EMBEDDED_LIBRARY
5110
    DBUG_ASSERT(0);                             /* Impossible */
5111
#endif
5112
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5113 5114
    break;
  }
monty@mysql.com's avatar
monty@mysql.com committed
5115
  thd->proc_info="query end";
5116
  /* Two binlog-related cleanups: */
5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129

  /*
    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);

5130
  /*
5131 5132 5133 5134 5135 5136
    The return value for ROW_COUNT() is "implementation dependent" if the
    statement is not DELETE, INSERT or UPDATE, but -1 is what JDBC and ODBC
    wants.

    We do not change the value for a CALL or EXECUTE statement, so the value
    generated by the last called (or executed) statement is preserved.
5137
   */
5138 5139
  if (lex->sql_command != SQLCOM_CALL && lex->sql_command != SQLCOM_EXECUTE &&
      uc_update_queries[lex->sql_command]<2)
5140
    thd->row_count_func= -1;
5141 5142

  goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5143 5144

error:
5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156
  res= TRUE;

end:
  if (need_start_waiting)
  {
    /*
      Release the protection against the global read lock and wake
      everyone, who might want to set a global read lock.
    */
    start_waiting_global_read_lock(thd);
  }
  DBUG_RETURN(res || thd->net.report_error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5157 5158 5159
}


bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5160
/*
5161
  Check grants for commands which work only with one table.
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5162

5163
  SYNOPSIS
5164
    check_single_table_access()
monty@mysql.com's avatar
monty@mysql.com committed
5165
    thd			Thread handler
5166
    privilege		requested privilege
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
5167
    all_tables		global table list of query
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5168 5169 5170

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

5174 5175
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
5176
{
5177 5178 5179 5180 5181 5182
  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;

5183 5184 5185 5186 5187 5188 5189 5190
  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,
5191 5192
		   &all_tables->grant.privilege, 0, 0,
                   test(all_tables->schema_table)))
5193
    goto deny;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5194

5195
  /* Show only 1 table for check_grant */
5196 5197 5198 5199
  if (grant_option &&
      !(all_tables->belong_to_view &&
        (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) &&
      check_grant(thd, privilege, all_tables, 0, 1, 0))
5200 5201 5202
    goto deny;

  thd->security_ctx= backup_ctx;
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
  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
5229

5230
  /* Check rights on tables of subselects and implictly opened tables */
5231
  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
5232
  if ((subselects_tables= all_tables->next_global))
monty@mysql.com's avatar
monty@mysql.com committed
5233
  {
5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245
    /*
      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)))
5246
      return 1;
monty@mysql.com's avatar
monty@mysql.com committed
5247 5248
  }
  return 0;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5249 5250 5251
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
5252
/****************************************************************************
5253
  Get the user (global) and database privileges for all used tables
5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266

  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
5267 5268
                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
5269 5270 5271
****************************************************************************/

bool
5272
check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
5273
	     bool dont_check_global_grants, bool no_errors, bool schema_db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5274
{
5275
  Security_context *sctx= thd->security_ctx;
5276 5277
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  ulong db_access;
5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288
  /*
    GRANT command:
    In case of database level grant the database name may be a pattern,
    in case of table|column level grant the database name can not be a pattern.
    We use 'dont_check_global_grants' as a flag to determine
    if it's database level grant command 
    (see SQLCOM_GRANT case, mysql_execute_command() function) and
    set db_is_pattern according to 'dont_check_global_grants' value.
  */
  bool  db_is_pattern= (test(want_access & GRANT_ACL) &&
                        dont_check_global_grants);
5289 5290
#endif
  ulong dummy;
5291 5292
  DBUG_ENTER("check_access");
  DBUG_PRINT("enter",("db: %s  want_access: %lu  master_access: %lu",
5293
                      db ? db : "", want_access, sctx->master_access));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5294 5295 5296 5297 5298
  if (save_priv)
    *save_priv=0;
  else
    save_priv= &dummy;

5299
  if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5300
  {
5301
    DBUG_PRINT("error",("No database"));
5302
    if (!no_errors)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5303 5304
      my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR),
                 MYF(0));                       /* purecov: tested */
5305
    DBUG_RETURN(TRUE);				/* purecov: tested */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5306 5307
  }

5308 5309
  if (schema_db)
  {
5310 5311
    if (!(sctx->master_access & FILE_ACL) && (want_access & FILE_ACL) ||
        (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
5312 5313
    {
      if (!no_errors)
5314 5315
      {
        const char *db_name= db ? db : thd->db;
5316
        my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
5317 5318
                 sctx->priv_user, sctx->priv_host, db_name);
      }
5319 5320 5321 5322 5323 5324 5325 5326 5327
      DBUG_RETURN(TRUE);
    }
    else
    {
      *save_priv= SELECT_ACL;
      DBUG_RETURN(FALSE);
    }
  }

monty@mysql.com's avatar
monty@mysql.com committed
5328 5329 5330
#ifdef NO_EMBEDDED_ACCESS_CHECKS
  DBUG_RETURN(0);
#else
5331
  if ((sctx->master_access & want_access) == want_access)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5332
  {
5333 5334 5335 5336 5337
    /*
      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
    */
5338 5339
    db_access= sctx->db_access;
    if (!(sctx->master_access & SELECT_ACL) &&
5340
	(db && (!thd->db || db_is_pattern || strcmp(db,thd->db))))
5341 5342 5343
      db_access=acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
                        db_is_pattern);
    *save_priv=sctx->master_access | db_access;
5344
    DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5345
  }
5346
  if (((want_access & ~sctx->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
5347
      ! db && dont_check_global_grants)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5348
  {						// We can never grant this
5349
    DBUG_PRINT("error",("No possible access"));
5350
    if (!no_errors)
5351
      my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
5352 5353
               sctx->priv_user,
               sctx->priv_host,
5354 5355 5356
               (thd->password ?
                ER(ER_YES) :
                ER(ER_NO)));                    /* purecov: tested */
5357
    DBUG_RETURN(TRUE);				/* purecov: tested */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5358 5359 5360
  }

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

5363
  if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
5364 5365
    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
5366
  else
5367
    db_access= sctx->db_access;
5368
  DBUG_PRINT("info",("db_access: %lu", db_access));
monty@mysql.com's avatar
monty@mysql.com committed
5369
  /* Remove SHOW attribute and access rights we already have */
5370
  want_access &= ~(sctx->master_access | EXTRA_ACL);
5371 5372
  DBUG_PRINT("info",("db_access: %lu  want_access: %lu",
                     db_access, want_access));
5373
  db_access= ((*save_priv=(db_access | sctx->master_access)) & want_access);
5374 5375

  /* grant_option is set if there exists a single table or column grant */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5376
  if (db_access == want_access ||
5377
      (grant_option && !dont_check_global_grants &&
5378
       !(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS))))
5379
    DBUG_RETURN(FALSE);				/* Ok */
5380 5381

  DBUG_PRINT("error",("Access denied"));
5382
  if (!no_errors)
5383
    my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
5384
             sctx->priv_user, sctx->priv_host,
5385 5386 5387
             (db ? db : (thd->db ?
                         thd->db :
                         "unknown")));          /* purecov: tested */
5388
  DBUG_RETURN(TRUE);				/* purecov: tested */
monty@mysql.com's avatar
monty@mysql.com committed
5389
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5390 5391 5392
}


5393 5394 5395 5396 5397 5398 5399 5400 5401
/*
  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
5402
    One gets access right if one has ANY of the rights in want_access
5403 5404 5405 5406 5407 5408 5409 5410
    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
*/
5411 5412

bool check_global_access(THD *thd, ulong want_access)
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
5413
{
monty@mysql.com's avatar
monty@mysql.com committed
5414 5415 5416
#ifdef NO_EMBEDDED_ACCESS_CHECKS
  return 0;
#else
5417
  char command[128];
5418
  if ((thd->security_ctx->master_access & want_access))
5419 5420
    return 0;
  get_privilege_desc(command, sizeof(command), want_access);
5421
  my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
5422
  return 1;
monty@mysql.com's avatar
monty@mysql.com committed
5423
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
5424 5425 5426
}


5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503
static bool check_show_access(THD *thd, TABLE_LIST *table)
{
  switch (get_schema_table_idx(table->schema_table))
  {
  case SCH_SCHEMATA:
    return (specialflag & SPECIAL_SKIP_SHOW_DB) &&
           check_global_access(thd, SHOW_DB_ACL);

  case SCH_TABLE_NAMES:
  case SCH_TABLES:
  case SCH_VIEWS:
  case SCH_TRIGGERS:
    {
      const char *dst_db_name= table->schema_select_lex->db;

      DBUG_ASSERT(dst_db_name);

      if (check_access(thd, SELECT_ACL, dst_db_name,
                       &thd->col_access, FALSE, FALSE,
                       is_schema_db(dst_db_name)))
      {
        return TRUE;
      }

      if (!thd->col_access && check_grant_db(thd, dst_db_name))
      {
        my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
                 thd->security_ctx->priv_user,
                 thd->security_ctx->priv_host,
                 dst_db_name);
        return TRUE;
      }

      return FALSE;
    }

  case SCH_COLUMNS:
  case SCH_STATISTICS:
    {
      TABLE_LIST *dst_table=
        (TABLE_LIST *) table->schema_select_lex->table_list.first;

      DBUG_ASSERT(dst_table);

      if (check_access(thd, SELECT_ACL | EXTRA_ACL,
                       dst_table->db,
                       &dst_table->grant.privilege,
                       FALSE, FALSE,
                       test(dst_table->schema_table)))
      {
        return FALSE;
      }

      return grant_option &&
             check_grant(thd, SELECT_ACL, dst_table, 2, UINT_MAX, FALSE);
    }

  case SCH_OPEN_TABLES:
  case SCH_VARIABLES:
  case SCH_STATUS:
  case SCH_PROCEDURES:
  case SCH_CHARSETS:
  case SCH_COLLATIONS:
  case SCH_COLLATION_CHARACTER_SET_APPLICABILITY:
  case SCH_USER_PRIVILEGES:
  case SCH_SCHEMA_PRIVILEGES:
  case SCH_TABLE_PRIVILEGES:
  case SCH_COLUMN_PRIVILEGES:
  case SCH_TABLE_CONSTRAINTS:
  case SCH_KEY_COLUMN_USAGE:
    break;
  }

  return FALSE;
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
5504
/*
5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524
  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
5525 5526
*/

5527
bool
5528
check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
5529
		   bool no_errors)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5530
{
5531 5532
  uint found=0;
  ulong found_access=0;
5533
#ifndef NO_EMBEDDED_ACCESS_CHECKS
5534
  TABLE_LIST *org_tables= tables;
5535
#endif
5536
  TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
5537
  Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx;
5538
  /*
5539 5540 5541
    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.
5542
  */
5543
  for (; tables != first_not_own_table; tables= tables->next_global)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5544
  {
5545 5546 5547 5548 5549
    if (tables->security_ctx)
      sctx= tables->security_ctx;
    else
      sctx= backup_ctx;

5550
    if (tables->schema_table && 
5551
        (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL)))
5552 5553 5554
    {
      if (!no_errors)
        my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
5555
                 sctx->priv_user, sctx->priv_host,
5556
                 INFORMATION_SCHEMA_NAME.str);
5557 5558
      return TRUE;
    }
5559 5560 5561 5562 5563
    /*
       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);
5564 5565 5566 5567 5568 5569 5570 5571 5572 5573

    if (tables->schema_table_reformed)
    {
      if (check_show_access(thd, tables))
        goto deny;

      continue;
    }

    if (tables->derived ||
5574
        (tables->table && (int)tables->table->s->tmp_table) ||
5575 5576
        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
5577
      continue;
5578 5579
    thd->security_ctx= sctx;
    if ((sctx->master_access & want_access) ==
5580
        (want_access & ~EXTRA_ACL) &&
5581
	thd->db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5582
      tables->grant.privilege= want_access;
5583
    else if (tables->db && thd->db && strcmp(tables->db, thd->db) == 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5584 5585 5586 5587 5588
    {
      if (found && !grant_option)		// db already checked
	tables->grant.privilege=found_access;
      else
      {
5589
	if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
5590
			 0, no_errors, test(tables->schema_table)))
5591
	  goto deny;                            // Access denied
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5592
	found_access=tables->grant.privilege;
5593
	found=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5594 5595
      }
    }
5596
    else if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
5597
			  0, no_errors, test(tables->schema_table)))
5598
      goto deny;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5599
  }
5600
  thd->security_ctx= backup_ctx;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5601
  if (grant_option)
5602
    return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5603
		       test(want_access & EXTRA_ACL), UINT_MAX, no_errors);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5604
  return FALSE;
5605 5606 5607
deny:
  thd->security_ctx= backup_ctx;
  return TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5608 5609
}

5610

5611
bool
5612 5613
check_routine_access(THD *thd, ulong want_access,char *db, char *name,
		     bool is_proc, bool no_errors)
5614 5615 5616 5617 5618
{
  TABLE_LIST tables[1];
  
  bzero((char *)tables, sizeof(TABLE_LIST));
  tables->db= db;
5619
  tables->table_name= tables->alias= name;
5620
  
monty@mysql.com's avatar
monty@mysql.com committed
5621 5622 5623 5624 5625 5626 5627
  /*
    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)
5628 5629
    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
5630
			0, no_errors, 0))
5631 5632
    return TRUE;
  
5633
#ifndef NO_EMBEDDED_ACCESS_CHECKS
5634
  if (grant_option)
5635
    return check_grant_routine(thd, want_access, tables, is_proc, no_errors);
5636
#endif
5637 5638 5639 5640

  return FALSE;
}

5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655

/*
  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
*/

5656 5657
bool check_some_routine_access(THD *thd, const char *db, const char *name,
                               bool is_proc)
5658 5659
{
  ulong save_priv;
5660
  if (thd->security_ctx->master_access & SHOW_PROC_ACLS)
5661
    return FALSE;
5662 5663 5664 5665 5666
  /*
    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) ||
5667 5668
      (save_priv & SHOW_PROC_ACLS))
    return FALSE;
5669
  return check_routine_level_acl(thd, db, name, is_proc);
5670 5671 5672
}


5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697
/*
  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,
5698 5699
                        &table->grant.privilege, 0, 1,
                        test(table->schema_table)) &&
5700 5701 5702 5703 5704 5705 5706 5707 5708
          !grant_option || !check_grant(thd, access, table, 0, 1, 1))
        DBUG_RETURN(0);
    }
  }
  DBUG_PRINT("exit",("no matching access rights"));
  DBUG_RETURN(1);
}


5709 5710
bool check_merge_table_access(THD *thd, char *db,
			      TABLE_LIST *table_list)
5711 5712 5713 5714
{
  int error=0;
  if (table_list)
  {
5715
    /* Check that all tables use the current database */
5716
    TABLE_LIST *tmp;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
5717
    for (tmp= table_list; tmp; tmp= tmp->next_local)
5718 5719 5720 5721
    {
      if (!tmp->db || !tmp->db[0])
	tmp->db=db;
    }
5722
    error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
hf@deer.(none)'s avatar
hf@deer.(none) committed
5723
			     table_list,0);
5724 5725 5726 5727
  }
  return error;
}

hf@deer.(none)'s avatar
hf@deer.(none) committed
5728 5729 5730

static bool check_db_used(THD *thd,TABLE_LIST *tables)
{
5731
  char *current_db= NULL;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
5732
  for (; tables; tables= tables->next_global)
hf@deer.(none)'s avatar
hf@deer.(none) committed
5733
  {
5734
    if (tables->db == NULL)
hf@deer.(none)'s avatar
hf@deer.(none) committed
5735
    {
5736 5737 5738 5739 5740 5741 5742 5743 5744
      /*
        This code never works and should be removed in 5.1.  All tables
        that are added to the list of tables should already have its
        database field initialized properly (see st_lex::add_table_to_list).
      */
      DBUG_ASSERT(0);
      if (thd->copy_db_to(&current_db, 0))
        return TRUE;
      tables->db= current_db;
hf@deer.(none)'s avatar
hf@deer.(none) committed
5745 5746 5747 5748
    }
  }
  return FALSE;
}
5749

bk@work.mysql.com's avatar
bk@work.mysql.com committed
5750 5751 5752 5753 5754 5755 5756 5757 5758 5759
/****************************************************************************
	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
5760 5761 5762 5763
#ifndef DBUG_OFF
long max_stack_used;
#endif

5764
#ifndef EMBEDDED_LIBRARY
5765 5766 5767 5768 5769 5770 5771 5772
/*
  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
5773 5774
{
  long stack_used;
5775
  DBUG_ASSERT(thd == current_thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5776
  if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
monty@mysql.com's avatar
monty@mysql.com committed
5777
      (long) (thread_stack - margin))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5778
  {
5779 5780 5781
    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));
5782
    thd->fatal_error();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5783 5784
    return 1;
  }
monty@mysql.com's avatar
monty@mysql.com committed
5785 5786 5787
#ifndef DBUG_OFF
  max_stack_used= max(max_stack_used, stack_used);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5788 5789
  return 0;
}
5790
#endif /* EMBEDDED_LIBRARY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5791 5792 5793 5794

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

5795
bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5796
{
5797
  LEX	*lex= current_thd->lex;
5798
  ulong old_info=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823
  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;
}


5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839
/*
 Reset THD part responsible for command processing state.

 DESCRIPTION
   This needs to be called before execution of every statement
   (prepared or conventional).

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

void mysql_reset_thd_for_next_command(THD *thd)
{
  DBUG_ENTER("mysql_reset_thd_for_next_command");
5840
  thd->free_list= 0;
5841
  thd->select_number= 1;
5842
  thd->query_start_used= thd->insert_id_used=0;
5843
  thd->last_insert_id_used_bin_log= FALSE;
5844
  thd->is_fatal_error= thd->time_zone_used= 0;
monty@mysql.com's avatar
monty@mysql.com committed
5845
  thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | 
monty@mysql.com's avatar
monty@mysql.com committed
5846 5847
                          SERVER_QUERY_NO_INDEX_USED |
                          SERVER_QUERY_NO_GOOD_INDEX_USED);
5848
  DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx);
5849
  thd->tmp_table_used= 0;
5850 5851
  if (!thd->in_sub_stmt)
  {
5852
    if (opt_bin_log)
5853
    {
5854
      reset_dynamic(&thd->user_var_events);
5855 5856
      thd->user_var_events_alloc= thd->mem_root;
    }
5857
    thd->clear_error();
5858 5859 5860 5861
    thd->total_warn_count=0;			// Warnings for this query
    thd->rand_used= 0;
    thd->sent_row_count= thd->examined_row_count= 0;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5862 5863 5864
  DBUG_VOID_RETURN;
}

5865

5866 5867 5868
void
mysql_init_select(LEX *lex)
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5869
  SELECT_LEX *select_lex= lex->current_select;
5870
  select_lex->init_select();
5871
  lex->wild= 0;
5872 5873
  if (select_lex == &lex->select_lex)
  {
5874
    DBUG_ASSERT(lex->result == 0);
5875 5876
    lex->exchange= 0;
  }
5877 5878
}

5879

5880
bool
5881
mysql_new_select(LEX *lex, bool move_down)
5882
{
5883
  SELECT_LEX *select_lex;
5884
  THD *thd= lex->thd;
5885 5886
  DBUG_ENTER("mysql_new_select");

5887
  if (!(select_lex= new (thd->mem_root) SELECT_LEX()))
5888
    DBUG_RETURN(1);
5889
  select_lex->select_number= ++thd->select_number;
5890
  select_lex->parent_lex= lex; /* Used in init_query. */
5891 5892
  select_lex->init_query();
  select_lex->init_select();
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
5893
  lex->nest_level++;
gshchepa/uchum@gleb.loc's avatar
gshchepa/uchum@gleb.loc committed
5894 5895 5896 5897 5898
  if (lex->nest_level > (int) MAX_SELECT_NESTING)
  {
    my_error(ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT,MYF(0),MAX_SELECT_NESTING);
    DBUG_RETURN(1);
  }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
5899
  select_lex->nest_level= lex->nest_level;
5900 5901 5902 5903 5904
  /*
    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
5905
  if (thd->stmt_arena->is_stmt_prepare())
5906
    select_lex->uncacheable|= UNCACHEABLE_PREPARE;
5907 5908
  if (move_down)
  {
5909
    SELECT_LEX_UNIT *unit;
5910
    lex->subqueries= TRUE;
5911
    /* first select_lex of subselect or derived table */
5912
    if (!(unit= new (thd->mem_root) SELECT_LEX_UNIT()))
5913
      DBUG_RETURN(1);
5914

5915 5916
    unit->init_query();
    unit->init_select();
5917
    unit->thd= thd;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5918
    unit->include_down(lex->current_select);
5919 5920
    unit->link_next= 0;
    unit->link_prev= 0;
5921
    unit->return_to= lex->current_select;
5922
    select_lex->include_down(unit);
5923 5924 5925 5926 5927
    /*
      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;
5928 5929
  }
  else
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5930
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
5931 5932
    if (lex->current_select->order_list.first && !lex->current_select->braces)
    {
5933
      my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY");
5934
      DBUG_RETURN(1);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
5935
    }
5936
    select_lex->include_neighbour(lex->current_select);
5937 5938 5939 5940 5941
    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
5942
  }
peter@mysql.com's avatar
peter@mysql.com committed
5943

5944
  select_lex->master_unit()->global_parameters= select_lex;
5945
  select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
5946
  lex->current_select= select_lex;
5947 5948 5949 5950 5951
  /*
    in subquery is SELECT query and we allow resolution of names in SELECT
    list
  */
  select_lex->context.resolve_in_select_list= TRUE;
5952
  DBUG_RETURN(0);
5953
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5954

5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969
/*
  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)
{
5970
  THD *thd;
5971
  LEX *lex;
5972
  LEX_STRING tmp, null_lex_string;
5973 5974
  Item *var;
  char buff[MAX_SYS_VAR_LENGTH*2+4+8], *end;
5975
  DBUG_ENTER("create_select_for_variable");
5976 5977

  thd= current_thd;
pem@mysql.telia.com's avatar
pem@mysql.telia.com committed
5978
  lex= thd->lex;
5979 5980 5981 5982
  mysql_init_select(lex);
  lex->sql_command= SQLCOM_SELECT;
  tmp.str= (char*) var_name;
  tmp.length=strlen(var_name);
5983
  bzero((char*) &null_lex_string.str, sizeof(null_lex_string));
5984 5985 5986 5987
  /*
    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
5988 5989 5990 5991 5992 5993
  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);
  }
5994 5995 5996
  DBUG_VOID_RETURN;
}

5997

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5998 5999
void mysql_init_multi_delete(LEX *lex)
{
6000
  lex->sql_command=  SQLCOM_DELETE_MULTI;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6001
  mysql_init_select(lex);
6002 6003
  lex->select_lex.select_limit= 0;
  lex->unit.select_limit_cnt= HA_POS_ERROR;
6004
  lex->select_lex.table_list.save_and_clear(&lex->auxiliary_table_list);
6005
  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
6006 6007
  lex->query_tables= 0;
  lex->query_tables_last= &lex->query_tables;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6008
}
6009

6010 6011 6012 6013
/*
  When you modify mysql_parse(), you may need to mofify
  mysql_test_parse_for_slave() in this same file.
*/
6014

6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025
/**
  Parse a query.
  @param thd Current thread
  @param inBuf Begining of the query text
  @param length Length of the query text
  @param [out] semicolon For multi queries, position of the character of
  the next query in the query text.
*/

void mysql_parse(THD *thd, const char *inBuf, uint length,
                 const char ** found_semicolon)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6026 6027
{
  DBUG_ENTER("mysql_parse");
6028 6029 6030

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

6031 6032 6033 6034 6035 6036 6037 6038
  /*
    Warning.
    The purpose of query_cache_send_result_to_client() is to lookup the
    query in the query cache first, to avoid parsing and executing it.
    So, the natural implementation would be to:
    - first, call query_cache_send_result_to_client,
    - second, if caching failed, initialise the lexical and syntactic parser.
    The problem is that the query cache depends on a clean initialization
6039 6040
    of (among others) lex->safe_to_cache_query and thd->server_status,
    which are reset respectively in
6041 6042 6043 6044 6045 6046 6047 6048 6049 6050
    - lex_start()
    - mysql_reset_thd_for_next_command()
    So, initializing the lexical analyser *before* using the query cache
    is required for the cache to work properly.
    FIXME: cleanup the dependencies in the code to simplify this.
  */
  lex_start(thd);
  mysql_reset_thd_for_next_command(thd);

  if (query_cache_send_result_to_client(thd, (char*) inBuf, length) <= 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6051
  {
6052
    LEX *lex= thd->lex;
6053

6054 6055
    sp_cache_flush_obsolete(&thd->sp_proc_cache);
    sp_cache_flush_obsolete(&thd->sp_func_cache);
6056

6057 6058 6059
    Lex_input_stream lip(thd, inBuf, length);
    thd->m_lip= &lip;

6060 6061 6062 6063
    int err= MYSQLparse(thd);
    *found_semicolon= lip.found_semicolon;

    if (!err && ! thd->is_fatal_error)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6064
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
6065
#ifndef NO_EMBEDDED_ACCESS_CHECKS
6066
      if (mqh_used && thd->user_connect &&
6067
	  check_mqh(thd, lex->sql_command))
6068 6069 6070 6071
      {
	thd->net.error = 0;
      }
      else
hf@deer.(none)'s avatar
hf@deer.(none) committed
6072
#endif
6073
      {
6074
	if (! thd->net.report_error)
6075
	{
6076 6077 6078 6079 6080 6081 6082 6083 6084 6085
          /*
            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.
          */
6086 6087
          if (lip.found_semicolon &&
              (thd->query_length= (ulong)(lip.found_semicolon - thd->query)))
6088 6089
            thd->query_length--;
          /* Actually execute the query */
6090 6091 6092
          lex->set_trg_event_type_for_tables();
          mysql_execute_command(thd);
          query_cache_end_of_result(thd);
6093
	}
6094
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6095 6096
    }
    else
6097
    {
6098
      DBUG_ASSERT(thd->net.report_error);
6099
      DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
6100
			 thd->is_fatal_error));
6101 6102

      query_cache_abort(&thd->net);
6103
    }
6104 6105 6106 6107 6108 6109
    if (thd->lex->sphead)
    {
      delete thd->lex->sphead;
      thd->lex->sphead= 0;
    }
    lex->unit.cleanup();
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6110
    thd->proc_info="freeing items";
6111
    thd->end_statement();
6112
    thd->cleanup_after_query();
6113
    DBUG_ASSERT(thd->change_list.is_empty());
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6114
  }
6115 6116 6117 6118 6119 6120
  else
  {
    /* There are no multi queries in the cache. */
    *found_semicolon= NULL;
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6121 6122 6123 6124
  DBUG_VOID_RETURN;
}


monty@mysql.com's avatar
monty@mysql.com committed
6125
#ifdef HAVE_REPLICATION
6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136
/*
  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)
{
6137
  LEX *lex= thd->lex;
6138
  bool error= 0;
monty@mysql.com's avatar
monty@mysql.com committed
6139
  DBUG_ENTER("mysql_test_parse_for_slave");
6140

6141 6142 6143 6144 6145 6146 6147
  Lex_input_stream lip(thd, inBuf, length);
  thd->m_lip= &lip;
  lex_start(thd);
  mysql_reset_thd_for_next_command(thd);
  int err= MYSQLparse((void*) thd);

  if (!err && ! thd->is_fatal_error &&
6148
      all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
monty@mysql.com's avatar
monty@mysql.com committed
6149
    error= 1;                  /* Ignore question */
6150
  thd->end_statement();
6151
  thd->cleanup_after_query();
monty@mysql.com's avatar
monty@mysql.com committed
6152
  DBUG_RETURN(error);
6153
}
monty@mysql.com's avatar
monty@mysql.com committed
6154
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6155

6156

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6158 6159 6160 6161 6162
/*****************************************************************************
** Store field definition for create
** Return 0 if ok
******************************************************************************/

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6163
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
6164
		       char *length, char *decimals,
6165
		       uint type_modifier,
6166 6167
		       Item *default_value, Item *on_update_value,
                       LEX_STRING *comment,
6168 6169
		       char *change,
                       List<String> *interval_list, CHARSET_INFO *cs,
6170
		       uint uint_geom_type)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6171 6172
{
  register create_field *new_field;
6173
  LEX  *lex= thd->lex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6174 6175 6176 6177
  DBUG_ENTER("add_field_to_list");

  if (strlen(field_name) > NAME_LEN)
  {
6178
    my_error(ER_TOO_LONG_IDENT, MYF(0), field_name); /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6179 6180 6181 6182 6183
    DBUG_RETURN(1);				/* purecov: inspected */
  }
  if (type_modifier & PRI_KEY_FLAG)
  {
    lex->col_list.push_back(new key_part_spec(field_name,0));
6184 6185 6186
    lex->alter_info.key_list.push_back(new Key(Key::PRIMARY, NullS,
                                               HA_KEY_ALG_UNDEF, 0,
                                               lex->col_list));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6187 6188 6189 6190 6191
    lex->col_list.empty();
  }
  if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
  {
    lex->col_list.push_back(new key_part_spec(field_name,0));
6192 6193 6194
    lex->alter_info.key_list.push_back(new Key(Key::UNIQUE, NullS,
                                               HA_KEY_ALG_UNDEF, 0,
                                               lex->col_list));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6195 6196 6197
    lex->col_list.empty();
  }

6198
  if (default_value)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6199
  {
6200
    /* 
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
6201 6202
      Default value should be literal => basic constants =>
      no need fix_fields()
6203 6204 6205
      
      We allow only one function as part of default value - 
      NOW() as default for TIMESTAMP type.
6206
    */
6207 6208 6209
    if (default_value->type() == Item::FUNC_ITEM && 
        !(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC &&
         type == FIELD_TYPE_TIMESTAMP))
6210
    {
6211
      my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
6212 6213 6214
      DBUG_RETURN(1);
    }
    else if (default_value->type() == Item::NULL_ITEM)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6215
    {
6216
      default_value= 0;
6217 6218 6219
      if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
	  NOT_NULL_FLAG)
      {
6220
	my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
6221 6222 6223 6224 6225
	DBUG_RETURN(1);
      }
    }
    else if (type_modifier & AUTO_INCREMENT_FLAG)
    {
6226
      my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6227 6228 6229
      DBUG_RETURN(1);
    }
  }
6230 6231 6232

  if (on_update_value && type != FIELD_TYPE_TIMESTAMP)
  {
6233
    my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name);
6234 6235
    DBUG_RETURN(1);
  }
6236

6237 6238 6239 6240 6241 6242
  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
6243
    char buf[32];
6244
    my_snprintf(buf, sizeof(buf), "TIMESTAMP(%s)", length);
6245 6246 6247
    push_warning_printf(thd,MYSQL_ERROR::WARN_LEVEL_WARN,
                        ER_WARN_DEPRECATED_SYNTAX,
                        ER(ER_WARN_DEPRECATED_SYNTAX),
msvensson@neptunus.(none)'s avatar
msvensson@neptunus.(none) committed
6248
                        buf, "TIMESTAMP");
6249 6250
  }

6251 6252 6253 6254
  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
6255
    DBUG_RETURN(1);
6256

6257
  lex->alter_info.create_list.push_back(new_field);
6258 6259 6260 6261
  lex->last_field=new_field;
  DBUG_RETURN(0);
}

6262

bk@work.mysql.com's avatar
bk@work.mysql.com committed
6263 6264 6265 6266
/* Store position for column in ALTER TABLE .. ADD column */

void store_position_for_column(const char *name)
{
6267
  current_thd->lex->last_field->after=my_const_cast(char*) (name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6268 6269 6270
}

bool
6271
add_proc_to_list(THD* thd, Item *item)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6272 6273 6274 6275
{
  ORDER *order;
  Item	**item_ptr;

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6276
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6277 6278 6279 6280 6281
    return 1;
  item_ptr = (Item**) (order+1);
  *item_ptr= item;
  order->item=item_ptr;
  order->free_me=0;
6282
  thd->lex->proc_list.link_in_list((byte*) order,(byte**) &order->next);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6283 6284 6285 6286 6287 6288 6289 6290
  return 0;
}


/* Fix escaping of _, % and \ in database and table names (for ODBC) */

static void remove_escape(char *name)
{
6291 6292
  if (!*name)					// For empty DB names
    return;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6293 6294
  char *to;
#ifdef USE_MB
6295
  char *strend=name+(uint) strlen(name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6296 6297 6298 6299 6300
#endif
  for (to=name; *name ; name++)
  {
#ifdef USE_MB
    int l;
6301 6302
    if (use_mb(system_charset_info) &&
        (l = my_ismbchar(system_charset_info, name, strend)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6303 6304 6305 6306 6307 6308 6309 6310
    {
	while (l--)
	    *to++ = *name++;
	name--;
	continue;
    }
#endif
    if (*name == '\\' && name[1])
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6311
      name++;					// Skip '\\'
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6312 6313 6314 6315 6316 6317 6318 6319 6320 6321
    *to++= *name;
  }
  *to=0;
}

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


monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6322
bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6323 6324 6325
{
  ORDER *order;
  DBUG_ENTER("add_to_list");
6326
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6327
    DBUG_RETURN(1);
6328 6329
  order->item_ptr= item;
  order->item= &order->item_ptr;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6330 6331 6332
  order->asc = asc;
  order->free_me=0;
  order->used=0;
6333
  order->counter_used= 0;
6334
  list.link_in_list((byte*) order,(byte**) &order->next);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6335 6336 6337 6338
  DBUG_RETURN(0);
}


monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6339 6340 6341 6342 6343 6344 6345 6346 6347 6348
/*
  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
6349
			TL_OPTION_ALIAS	        an alias in multi table DELETE
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6350 6351 6352 6353 6354 6355 6356 6357 6358
    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
6359 6360
TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
					     Table_ident *table,
6361
					     LEX_STRING *alias,
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6362 6363
					     ulong table_options,
					     thr_lock_type lock_type,
6364 6365
					     List<String> *use_index_arg,
					     List<String> *ignore_index_arg,
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6366
                                             LEX_STRING *option)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6367 6368
{
  register TABLE_LIST *ptr;
6369
  TABLE_LIST *previous_table_ref; /* The table preceding the current one. */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6370
  char *alias_str;
6371
  LEX *lex= thd->lex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6372
  DBUG_ENTER("add_table_to_list");
6373
  LINT_INIT(previous_table_ref);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6374 6375 6376 6377

  if (!table)
    DBUG_RETURN(0);				// End of memory
  alias_str= alias ? alias->str : table->table.str;
6378 6379
  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
6380
  {
6381
    my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6382 6383 6384 6385
    DBUG_RETURN(0);
  }

  if (!alias)					/* Alias is case sensitive */
6386 6387 6388
  {
    if (table->sel)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
6389 6390
      my_message(ER_DERIVED_MUST_HAVE_ALIAS,
                 ER(ER_DERIVED_MUST_HAVE_ALIAS), MYF(0));
6391 6392
      DBUG_RETURN(0);
    }
6393
    if (!(alias_str=thd->memdup(alias_str,table->table.length+1)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6394
      DBUG_RETURN(0);
6395
  }
6396
  if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6397
    DBUG_RETURN(0);				/* purecov: inspected */
peter@mysql.com's avatar
peter@mysql.com committed
6398
  if (table->db.str)
6399
  {
6400 6401 6402 6403 6404
    if (table->is_derived_table() == FALSE && check_db_name(table->db.str))
    {
      my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str);
      DBUG_RETURN(0);
    }
6405 6406 6407
    ptr->db= table->db.str;
    ptr->db_length= table->db.length;
  }
6408
  else if (lex->copy_db_to(&ptr->db, &ptr->db_length))
6409
    DBUG_RETURN(0);
peter@mysql.com's avatar
peter@mysql.com committed
6410

6411
  ptr->alias= alias_str;
6412
  if (lower_case_table_names && table->table.length)
6413
    table->table.length= my_casedn_str(files_charset_info, table->table.str);
6414 6415
  ptr->table_name=table->table.str;
  ptr->table_name_length=table->table.length;
6416
  ptr->lock_type=   lock_type;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6417 6418
  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
6419
  ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
6420
  ptr->derived=	    table->sel;
6421
  if (!ptr->derived && !my_strcasecmp(system_charset_info, ptr->db,
6422
                                      INFORMATION_SCHEMA_NAME.str))
6423
  {
6424
    ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, ptr->table_name);
6425 6426 6427
    if (!schema_table ||
        (schema_table->hidden && 
         lex->orig_sql_command == SQLCOM_END))  // not a 'show' command
6428
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
6429
      my_error(ER_UNKNOWN_TABLE, MYF(0),
6430
               ptr->table_name, INFORMATION_SCHEMA_NAME.str);
6431 6432
      DBUG_RETURN(0);
    }
6433
    ptr->schema_table_name= ptr->table_name;
6434 6435
    ptr->schema_table= schema_table;
  }
6436
  ptr->select_lex=  lex->current_select;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
6437
  ptr->cacheable_table= 1;
6438 6439 6440 6441 6442 6443
  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
6444
  ptr->option= option ? option->str : 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6445
  /* check that used name is unique */
6446
  if (lock_type != TL_IGNORE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6447
  {
timour@mysql.com's avatar
timour@mysql.com committed
6448 6449 6450 6451
    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 ;
6452
	 tables ;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6453
	 tables=tables->next_local)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6454
    {
6455 6456
      if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
	  !strcmp(ptr->db, tables->db))
6457
      {
6458
	my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str); /* purecov: tested */
6459 6460
	DBUG_RETURN(0);				/* purecov: tested */
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6461 6462
    }
  }
6463 6464 6465
  /* Store the table reference preceding the current one. */
  if (table_list.elements > 0)
  {
6466 6467 6468
    /*
      table_list.next points to the last inserted TABLE_LIST->next_local'
      element
6469
      We don't use the offsetof() macro here to avoid warnings from gcc
6470
    */
6471 6472 6473
    previous_table_ref= (TABLE_LIST*) ((char*) table_list.next -
                                       ((char*) &(ptr->next_local) -
                                        (char*) ptr));
6474 6475 6476 6477 6478 6479 6480 6481
    /*
      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;
6482
  }
6483

6484 6485 6486 6487 6488 6489
  /*
    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
6490
  table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local);
6491
  ptr->next_name_resolution_table= NULL;
6492
  /* Link table in global list (all used tables) */
6493
  lex->add_to_query_tables(ptr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6494 6495 6496
  DBUG_RETURN(ptr);
}

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

6498 6499 6500 6501
/*
  Initialize a new table list for a nested join

  SYNOPSIS
6502
    init_nested_join()
6503
    thd         current thread
6504

6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523
  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");
6524

6525 6526
  if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
                                       sizeof(NESTED_JOIN))))
6527
    DBUG_RETURN(1);
6528 6529 6530
  nested_join= ptr->nested_join=
    ((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));

6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550
  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
6551
    moves it one level up, eliminating the nest.
6552 6553 6554 6555 6556 6557 6558 6559 6560

  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;
6561
  NESTED_JOIN *nested_join;
6562
  DBUG_ENTER("end_nested_join");
6563

6564
  DBUG_ASSERT(embedding);
6565 6566 6567
  ptr= embedding;
  join_list= ptr->join_list;
  embedding= ptr->embedding;
6568
  nested_join= ptr->nested_join;
6569 6570 6571 6572 6573 6574 6575 6576 6577
  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;
  }
6578
  else if (nested_join->join_list.elements == 0)
6579 6580
  {
    join_list->pop();
6581
    ptr= 0;                                     // return value
6582
  }
6583 6584 6585 6586 6587 6588 6589 6590
  DBUG_RETURN(ptr);
}


/*
  Nest last join operation

  SYNOPSIS
6591
    nest_last_join()
6592 6593 6594 6595 6596 6597
    thd         current thread

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

  RETURN VALUE
6598 6599 6600
    0  Error
    #  Pointer to TABLE_LIST element created for the new nested join

6601 6602 6603 6604 6605 6606
*/

TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
{
  TABLE_LIST *ptr;
  NESTED_JOIN *nested_join;
6607
  List<TABLE_LIST> *embedded_list;
6608
  DBUG_ENTER("nest_last_join");
6609

6610 6611
  if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
                                       sizeof(NESTED_JOIN))))
6612
    DBUG_RETURN(0);
6613 6614 6615
  nested_join= ptr->nested_join=
    ((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));

6616 6617
  ptr->embedding= embedding;
  ptr->join_list= join_list;
6618
  embedded_list= &nested_join->join_list;
6619
  embedded_list->empty();
6620 6621

  for (uint i=0; i < 2; i++)
6622 6623 6624 6625 6626
  {
    TABLE_LIST *table= join_list->pop();
    table->join_list= embedded_list;
    table->embedding= ptr;
    embedded_list->push_back(table);
6627 6628 6629 6630 6631 6632 6633
    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.
      */
6634 6635
      if (prev_join_using)
        ptr->join_using_fields= prev_join_using;
6636
    }
6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676
  }
  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
6677 6678 6679

  DESCRIPTION
    The function takes the current join list t[0],t[1] ... and
6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702
    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
*/

6703
TABLE_LIST *st_select_lex::convert_right_join()
6704 6705
{
  TABLE_LIST *tab2= join_list->pop();
6706
  TABLE_LIST *tab1= join_list->pop();
6707 6708 6709 6710 6711 6712 6713 6714 6715
  DBUG_ENTER("convert_right_join");

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

  DBUG_RETURN(tab1);
}

6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728
/*
  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
*/

6729
void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
6730 6731 6732 6733 6734 6735
{
  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
6736 6737 6738
  for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first;
       tables;
       tables= tables->next_local)
6739 6740 6741 6742 6743 6744 6745
  {
    tables->lock_type= lock_type;
    tables->updating=  for_update;
  }
  DBUG_VOID_RETURN;
}

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

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770
/*
  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
*/

6771
bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6772 6773 6774 6775
{
  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
6776

6777
  if (!(fake_select_lex= new (thd_arg->mem_root) SELECT_LEX()))
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6778 6779 6780 6781
      DBUG_RETURN(1);
  fake_select_lex->include_standalone(this, 
                                      (SELECT_LEX_NODE**)&fake_select_lex);
  fake_select_lex->select_number= INT_MAX;
6782
  fake_select_lex->parent_lex= thd_arg->lex; /* Used in init_query. */
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6783 6784
  fake_select_lex->make_empty_select();
  fake_select_lex->linkage= GLOBAL_OPTIONS_TYPE;
6785 6786
  fake_select_lex->select_limit= 0;

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6787
  fake_select_lex->context.outer_context=first_sl->context.outer_context;
6788 6789 6790
  /* 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
6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801

  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;
6802
    thd_arg->lex->current_select= fake_select_lex;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6803
  }
6804
  thd_arg->lex->pop_context();
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
6805 6806 6807
  DBUG_RETURN(0);
}

6808

6809
/*
6810 6811
  Push a new name resolution context for a JOIN ... ON clause to the
  context stack of a query block.
6812 6813

  SYNOPSIS
6814
    push_new_name_resolution_context()
6815
    thd       pointer to current thread
timour@mysql.com's avatar
timour@mysql.com committed
6816
    left_op   left  operand of the JOIN
6817 6818 6819 6820
    right_op  rigth operand of the JOIN

  DESCRIPTION
    Create a new name resolution context for a JOIN ... ON clause,
6821 6822 6823
    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.
6824 6825

  RETURN
6826 6827
    FALSE  if all is OK
    TRUE   if a memory allocation error occured
6828 6829
*/

6830 6831 6832
bool
push_new_name_resolution_context(THD *thd,
                                 TABLE_LIST *left_op, TABLE_LIST *right_op)
6833 6834
{
  Name_resolution_context *on_context;
6835
  if (!(on_context= new (thd->mem_root) Name_resolution_context))
6836
    return TRUE;
6837 6838 6839 6840 6841
  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();
6842
  return thd->lex->push_context(on_context);
6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862
}


/*
  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
6863
{
6864
  if (expr)
6865
  {
6866
    if (!b->on_expr)
6867
      b->on_expr= expr;
6868 6869
    else
    {
6870 6871 6872 6873 6874 6875
      /*
        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);
6876 6877
    }
    b->on_expr->top_level_item();
6878
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6879 6880 6881
}


6882
/*
6883 6884
  Mark that there is a NATURAL JOIN or JOIN ... USING between two
  tables.
6885 6886 6887

  SYNOPSIS
    add_join_natural()
6888 6889 6890
    a			Left join argument
    b			Right join argument
    using_fields        Field names from USING clause
6891
    lex                 The current st_select_lex
6892
  
6893
  IMPLEMENTATION
6894 6895 6896 6897 6898 6899 6900 6901 6902 6903
    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
6904 6905 6906
    SELECT * FROM t1 NATURAL LEFT JOIN t2
     <=>
    SELECT * FROM t1 LEFT JOIN t2 ON (t1.i=t2.i and t1.j=t2.j ... )
6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917

    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
6918 6919
*/

6920 6921
void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
                      SELECT_LEX *lex)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6922
{
6923
  b->natural_join= a;
6924
  lex->prev_join_using= using_fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6925 6926
}

6927

6928
/*
6929 6930 6931 6932
  Reload/resets privileges and the different caches.

  SYNOPSIS
    reload_acl_and_cache()
6933
    thd			Thread handler (can be NULL!)
6934 6935 6936 6937 6938
    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
6939 6940 6941
                        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.
6942 6943 6944

  RETURN
    0	 ok
6945
    !=0  error.  thd->killed or thd->net.report_error is set
6946 6947
*/

6948 6949
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
6950 6951 6952
{
  bool result=0;
  select_errors=0;				/* Write if more errors */
6953
  bool tmp_write_to_binlog= 1;
6954

6955
  DBUG_ASSERT(!thd || !thd->in_sub_stmt);
6956

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
6957
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6958 6959
  if (options & REFRESH_GRANT)
  {
6960 6961 6962 6963 6964 6965
    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)))
6966 6967
    {
      thd->thread_stack= (char*) &tmp_thd;
6968
      thd->store_globals();
6969
    }
6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981
    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;
    }
6982
    reset_mqh((LEX_USER *)NULL, TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6983
  }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
6984
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
6985 6986
  if (options & REFRESH_LOG)
  {
6987
    /*
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6988 6989
      Flush the normal query log, the update log, the binary log,
      the slow query log, and the relay log (if it exists).
6990
    */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
6991

6992
    /*
monty@mysql.com's avatar
monty@mysql.com committed
6993 6994 6995 6996
      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)
6997 6998
    */
    tmp_write_to_binlog= 0;
6999 7000
    mysql_log.new_file(1);
    mysql_slow_log.new_file(1);
7001 7002 7003 7004
    if( mysql_bin_log.is_open() )
    {
      mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
    }
7005
#ifdef HAVE_REPLICATION
7006
    pthread_mutex_lock(&LOCK_active_mi);
7007
    rotate_relay_log(active_mi);
7008
    pthread_mutex_unlock(&LOCK_active_mi);
7009
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7010 7011
    if (ha_flush_logs())
      result=1;
7012 7013
    if (flush_error_log())
      result=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7014
  }
7015
#ifdef HAVE_QUERY_CACHE
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
7016 7017
  if (options & REFRESH_QUERY_CACHE_FREE)
  {
7018
    query_cache.pack();				// FLUSH QUERY CACHE
7019
    options &= ~REFRESH_QUERY_CACHE;    // Don't flush cache, just free memory
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
7020 7021 7022
  }
  if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
  {
7023
    query_cache.flush();			// RESET QUERY CACHE
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
7024
  }
7025
#endif /*HAVE_QUERY_CACHE*/
7026 7027 7028 7029 7030
  /*
    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
7031
  {
7032
    if ((options & REFRESH_READ_LOCK) && thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7033
    {
7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044
      /*
        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
7045
        {
7046 7047 7048 7049 7050
          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
7051
        }
7052
      }
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
7053 7054 7055 7056
      /*
	Writing to the binlog could cause deadlocks, as we don't log
	UNLOCK TABLES
      */
7057
      tmp_write_to_binlog= 0;
7058
      if (lock_global_read_lock(thd))
7059
	return 1;                               // Killed
7060 7061
      result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1,
                                 tables);
7062
      if (make_global_read_lock_block_commit(thd)) // Killed
7063 7064 7065 7066 7067
      {
        /* 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
7068
    }
7069 7070
    else
      result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables);
7071
    my_dbopt_cleanup();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7072 7073 7074
  }
  if (options & REFRESH_HOSTS)
    hostname_cache_refresh();
monty@mysql.com's avatar
monty@mysql.com committed
7075
  if (thd && (options & REFRESH_STATUS))
7076
    refresh_status(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7077 7078
  if (options & REFRESH_THREADS)
    flush_thread_cache();
7079
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7080
  if (options & REFRESH_MASTER)
7081
  {
7082
    DBUG_ASSERT(thd);
7083
    tmp_write_to_binlog= 0;
7084
    if (reset_master(thd))
7085
    {
7086
      result=1;
7087 7088
      thd->fatal_error();                       // Ensure client get error
    }
7089
  }
7090
#endif
7091
#ifdef OPENSSL
7092 7093 7094 7095 7096 7097
   if (options & REFRESH_DES_KEY_FILE)
   {
     if (des_key_file)
       result=load_des_key_file(des_key_file);
   }
#endif
7098
#ifdef HAVE_REPLICATION
7099 7100
 if (options & REFRESH_SLAVE)
 {
7101
   tmp_write_to_binlog= 0;
7102
   pthread_mutex_lock(&LOCK_active_mi);
7103
   if (reset_slave(thd, active_mi))
7104
     result=1;
7105
   pthread_mutex_unlock(&LOCK_active_mi);
7106
 }
7107
#endif
7108
 if (options & REFRESH_USER_RESOURCES)
monty@mysql.com's avatar
monty@mysql.com committed
7109
   reset_mqh((LEX_USER *) NULL);
7110
 *write_to_binlog= tmp_write_to_binlog;
7111
 return result;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7112 7113
}

7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125
/*
  kill on thread

  SYNOPSIS
    kill_one_thread()
    thd			Thread class
    id			Thread id

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

hf@genie.(none)'s avatar
SCRUM  
hf@genie.(none) committed
7126
void kill_one_thread(THD *thd, ulong id, bool only_kill_query)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7127 7128 7129
{
  THD *tmp;
  uint error=ER_NO_SUCH_THREAD;
7130 7131
  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
7132 7133 7134 7135
  while ((tmp=it++))
  {
    if (tmp->thread_id == id)
    {
7136 7137
      pthread_mutex_lock(&tmp->LOCK_delete);	// Lock from delete
      break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7138 7139 7140
    }
  }
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
7141 7142
  if (tmp)
  {
7143 7144
    if ((thd->security_ctx->master_access & SUPER_ACL) ||
	!strcmp(thd->security_ctx->user, tmp->security_ctx->user))
7145
    {
hf@genie.(none)'s avatar
SCRUM  
hf@genie.(none) committed
7146
      tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
7147 7148 7149 7150 7151 7152 7153
      error=0;
    }
    else
      error=ER_KILL_DENIED_ERROR;
    pthread_mutex_unlock(&tmp->LOCK_delete);
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
7154
  if (!error)
7155
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7156
  else
7157
    my_error(error, MYF(0), id);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
7158 7159
}

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

7161 7162
	/* If pointer is not a null pointer, append filename to it */

7163 7164
static bool append_file_to_dir(THD *thd, const char **filename_ptr,
			       const char *table_name)
7165
{
7166
  char buff[FN_REFLEN],*ptr, *end;
7167 7168 7169 7170 7171 7172 7173
  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))
  {
7174
    my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr);
7175 7176 7177 7178
    return 1;
  }
  /* Fix is using unix filename format on dos */
  strmov(buff,*filename_ptr);
7179
  end=convert_dirname(buff, *filename_ptr, NullS);
7180
  if (!(ptr=thd->alloc((uint) (end-buff)+(uint) strlen(table_name)+1)))
7181 7182
    return 1;					// End of memory
  *filename_ptr=ptr;
7183
  strxmov(ptr,buff,table_name,NullS);
7184 7185
  return 0;
}
7186

7187

7188 7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201
/*
  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;
7202 7203
  LEX *lex= thd->lex;
  if (lex->current_select != &lex->select_lex)
7204 7205
  {
    char command[80];
7206 7207 7208
    Lex_input_stream *lip= thd->m_lip;
    strmake(command, lip->yylval->symbol.str,
	    min(lip->yylval->symbol.length, sizeof(command)-1));
7209
    my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command);
7210 7211 7212 7213
    return 1;
  }
  return 0;
}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7214

7215

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7216
Comp_creator *comp_eq_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7217
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7218
  return invert?(Comp_creator *)&ne_creator:(Comp_creator *)&eq_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7219 7220
}

7221

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7222
Comp_creator *comp_ge_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7223
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7224
  return invert?(Comp_creator *)&lt_creator:(Comp_creator *)&ge_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7225 7226
}

7227

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7228
Comp_creator *comp_gt_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7229
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7230
  return invert?(Comp_creator *)&le_creator:(Comp_creator *)&gt_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7231 7232
}

7233

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7234
Comp_creator *comp_le_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7235
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7236
  return invert?(Comp_creator *)&gt_creator:(Comp_creator *)&le_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7237 7238
}

7239

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7240
Comp_creator *comp_lt_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7241
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7242
  return invert?(Comp_creator *)&ge_creator:(Comp_creator *)&lt_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7243 7244
}

7245

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7246
Comp_creator *comp_ne_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7247
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7248
  return invert?(Comp_creator *)&eq_creator:(Comp_creator *)&ne_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7249
}
7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262 7263 7264 7265 7266 7267 7268 7269


/*
  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
7270
  if ((cmp == &comp_eq_creator) && !all)       //  = ANY <=> IN
7271
    return new Item_in_subselect(left_expr, select_lex);
serg@serg.mylan's avatar
serg@serg.mylan committed
7272 7273

  if ((cmp == &comp_ne_creator) && all)        // <> ALL <=> NOT IN
7274 7275 7276
    return new Item_func_not(new Item_in_subselect(left_expr, select_lex));

  Item_allany_subselect *it=
7277
    new Item_allany_subselect(left_expr, cmp, select_lex, all);
7278
  if (all)
7279
    return it->upper_item= new Item_func_not_all(it);	/* ALL */
7280

7281
  return it->upper_item= new Item_func_nop_all(it);      /* ANY/SOME */
7282
}
7283 7284 7285 7286 7287 7288 7289


/*
  Multi update query pre-check

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

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7293
  RETURN VALUE
7294 7295
    FALSE OK
    TRUE  Error
7296
*/
7297

7298
bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
7299 7300 7301 7302 7303
{
  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
7304
  DBUG_ENTER("multi_update_precheck");
7305 7306 7307

  if (select_lex->item_list.elements != lex->value_list.elements)
  {
7308
    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
7309
    DBUG_RETURN(TRUE);
7310 7311 7312 7313 7314
  }
  /*
    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
7315
  for (table= tables; table; table= table->next_local)
7316
  {
7317 7318 7319
    if (table->derived)
      table->grant.privilege= SELECT_ACL;
    else if ((check_access(thd, UPDATE_ACL, table->db,
7320 7321
                           &table->grant.privilege, 0, 1,
                           test(table->schema_table)) ||
7322 7323
              grant_option &&
              check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) &&
monty@mysql.com's avatar
monty@mysql.com committed
7324
             (check_access(thd, SELECT_ACL, table->db,
7325 7326
                           &table->grant.privilege, 0, 0,
                           test(table->schema_table)) ||
monty@mysql.com's avatar
monty@mysql.com committed
7327
              grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
7328
      DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7329

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
7330
    table->table_in_first_from_clause= 1;
7331
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7332 7333 7334
  /*
    Is there tables of subqueries?
  */
7335
  if (&lex->select_lex != lex->all_selects_list || lex->time_zone_tables_used)
7336
  {
7337
    DBUG_PRINT("info",("Checking sub query list"));
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
7338
    for (table= tables; table; table= table->next_global)
7339
    {
monty@mysql.com's avatar
monty@mysql.com committed
7340 7341 7342
      if (!my_tz_check_n_skip_implicit_tables(&table,
                                              lex->time_zone_tables_used) &&
          !table->table_in_first_from_clause)
7343 7344
      {
	if (check_access(thd, SELECT_ACL, table->db,
7345 7346
			 &table->grant.privilege, 0, 0,
                         test(table->schema_table)) ||
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7347
	    grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))
7348
	  DBUG_RETURN(TRUE);
7349 7350 7351 7352 7353 7354
      }
    }
  }

  if (select_lex->order_list.elements)
    msg= "ORDER BY";
7355
  else if (select_lex->select_limit)
7356 7357 7358 7359
    msg= "LIMIT";
  if (msg)
  {
    my_error(ER_WRONG_USAGE, MYF(0), "UPDATE", msg);
7360
    DBUG_RETURN(TRUE);
7361
  }
7362
  DBUG_RETURN(FALSE);
7363 7364 7365 7366 7367 7368 7369
}

/*
  Multi delete query pre-check

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

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7373
  RETURN VALUE
7374 7375
    FALSE OK
    TRUE  error
7376
*/
7377

7378
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
7379 7380 7381
{
  SELECT_LEX *select_lex= &thd->lex->select_lex;
  TABLE_LIST *aux_tables=
7382
    (TABLE_LIST *)thd->lex->auxiliary_table_list.first;
7383
  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
7384
  DBUG_ENTER("multi_delete_precheck");
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7385

7386 7387 7388
  /* sql_yacc guarantees that tables and aux_tables are not zero */
  DBUG_ASSERT(aux_tables != 0);
  if (check_db_used(thd, tables) || check_db_used(thd,aux_tables) ||
7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400
      check_table_access(thd, SELECT_ACL, tables, 0))
    DBUG_RETURN(TRUE);

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

7405 7406
  if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7407 7408
    my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
               ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
7409
    DBUG_RETURN(TRUE);
7410
  }
7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435
  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;

7436
  for (target_tbl= (TABLE_LIST *)lex->auxiliary_table_list.first;
7437
       target_tbl; target_tbl= target_tbl->next_local)
7438
  {
7439
    lex->table_count++;
7440 7441
    /* 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
7442
    for (walk= tables; walk; walk= walk->next_local)
7443
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7444 7445 7446
      if (!my_strcasecmp(table_alias_charset,
			 target_tbl->alias, walk->alias) &&
	  !strcmp(walk->db, target_tbl->db))
7447 7448 7449 7450
	break;
    }
    if (!walk)
    {
7451
      my_error(ER_UNKNOWN_TABLE, MYF(0),
7452
               target_tbl->table_name, "MULTI DELETE");
7453
      DBUG_RETURN(TRUE);
7454
    }
serg@serg.mylan's avatar
serg@serg.mylan committed
7455 7456 7457 7458 7459
    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
7460
    walk->updating= target_tbl->updating;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7461
    walk->lock_type= target_tbl->lock_type;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
7462
    target_tbl->correspondent_table= walk;	// Remember corresponding table
7463
  }
7464
  DBUG_RETURN(FALSE);
7465 7466 7467
}


bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7468 7469 7470 7471 7472
/*
  simple UPDATE query pre-check

  SYNOPSIS
    update_precheck()
7473 7474
    thd		Thread handler
    tables	Global table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7475 7476

  RETURN VALUE
7477 7478
    FALSE OK
    TRUE  Error
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7479
*/
7480

7481
bool update_precheck(THD *thd, TABLE_LIST *tables)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7482 7483 7484 7485
{
  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
7486
    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
7487
    DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7488
  }
7489 7490
  DBUG_RETURN(check_db_used(thd, tables) ||
	       check_one_table_access(thd, UPDATE_ACL, tables));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7491 7492 7493 7494 7495 7496 7497 7498
}


/*
  simple DELETE query pre-check

  SYNOPSIS
    delete_precheck()
7499 7500
    thd		Thread handler
    tables	Global table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7501 7502

  RETURN VALUE
7503 7504
    FALSE  OK
    TRUE   error
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7505
*/
7506

7507
bool delete_precheck(THD *thd, TABLE_LIST *tables)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7508 7509 7510
{
  DBUG_ENTER("delete_precheck");
  if (check_one_table_access(thd, DELETE_ACL, tables))
7511
    DBUG_RETURN(TRUE);
7512
  /* Set privilege for the WHERE clause */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7513
  tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
7514
  DBUG_RETURN(FALSE);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7515 7516 7517 7518 7519 7520 7521 7522
}


/*
  simple INSERT query pre-check

  SYNOPSIS
    insert_precheck()
7523 7524
    thd		Thread handler
    tables	Global table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7525 7526

  RETURN VALUE
7527 7528
    FALSE  OK
    TRUE   error
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7529
*/
7530

bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
7531
bool insert_precheck(THD *thd, TABLE_LIST *tables)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7532 7533 7534 7535
{
  LEX *lex= thd->lex;
  DBUG_ENTER("insert_precheck");

7536 7537 7538 7539
  /*
    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
7540 7541 7542
  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
7543 7544

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

7547
  if (lex->update_list.elements != lex->value_list.elements)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7548
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7549
    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
7550
    DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7551
  }
7552 7553
  if (check_db_used(thd, tables))
    DBUG_RETURN(TRUE);
7554
  DBUG_RETURN(FALSE);
7555
}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7556 7557


7558 7559 7560 7561 7562 7563 7564 7565 7566 7567 7568 7569 7570 7571 7572 7573 7574 7575 7576
/**
   @brief  Check privileges for SHOW CREATE TABLE statement.

   @param  thd    Thread context
   @param  table  Target table

   @retval TRUE  Failure
   @retval FALSE Success
*/

static bool check_show_create_table_access(THD *thd, TABLE_LIST *table)
{
  return check_access(thd, SELECT_ACL | EXTRA_ACL, table->db,
                      &table->grant.privilege, 0, 0,
                      test(table->schema_table)) ||
         grant_option && check_grant(thd, SELECT_ACL, table, 2, UINT_MAX, 0);
}


bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7577 7578 7579 7580 7581
/*
  CREATE TABLE query pre-check

  SYNOPSIS
    create_table_precheck()
7582 7583 7584
    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
7585 7586

  RETURN VALUE
7587 7588
    FALSE   OK
    TRUE   Error
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7589
*/
7590

7591 7592
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
7593 7594
{
  LEX *lex= thd->lex;
7595 7596
  SELECT_LEX *select_lex= &lex->select_lex;
  ulong want_priv;
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
7597
  bool error= TRUE;                                 // Error message is given
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7598
  DBUG_ENTER("create_table_precheck");
7599 7600 7601

  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
7602
  if (check_access(thd, want_priv, create_table->db,
7603 7604
		   &create_table->grant.privilege, 0, 0,
                   test(create_table->schema_table)) ||
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7605 7606 7607
      check_merge_table_access(thd, create_table->db,
			       (TABLE_LIST *)
			       lex->create_info.merge_list.first))
7608 7609
    goto err;
  if (grant_option && want_priv != CREATE_TMP_ACL &&
7610
      check_grant(thd, want_priv, create_table, 0, 1, 0))
7611 7612 7613 7614 7615 7616
    goto err;

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

7617 7618
#ifdef NOT_NECESSARY_TO_CHECK_CREATE_TABLE_EXIST_WHEN_PREPARING_STATEMENT
    /* This code throws an ill error for CREATE TABLE t1 SELECT * FROM t1 */
7619
    /*
monty@mysql.com's avatar
monty@mysql.com committed
7620 7621 7622
      Only do the check for PS, becasue we on execute we have to check that
      against the opened tables to ensure we don't use a table that is part
      of the view (which can only be done after the table has been opened).
7623
    */
konstantin@mysql.com's avatar
konstantin@mysql.com committed
7624
    if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute())
7625
    {
monty@mysql.com's avatar
monty@mysql.com committed
7626 7627 7628 7629
      /*
        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
7630
          find_table_in_global_list(tables, create_table->db,
7631
                                    create_table->table_name))
monty@mysql.com's avatar
monty@mysql.com committed
7632
      {
7633
	error= FALSE;
monty@mysql.com's avatar
monty@mysql.com committed
7634 7635 7636
        goto err;
      }
    }
7637
#endif
7638 7639 7640
    if (tables && check_table_access(thd, SELECT_ACL, tables,0))
      goto err;
  }
7641 7642 7643 7644 7645
  else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
  {
    if (check_show_create_table_access(thd, tables))
      goto err;
  }
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
7646
  error= FALSE;
7647 7648 7649

err:
  DBUG_RETURN(error);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7650
}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7651 7652 7653 7654 7655 7656 7657


/*
  negate given expression

  SYNOPSIS
    negate_expression()
7658
    thd  thread handler
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
7659 7660 7661 7662 7663 7664 7665 7666 7667 7668 7669 7670 7671 7672 7673 7674 7675 7676 7677 7678 7679 7680 7681 7682 7683 7684 7685 7686
    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);
}
7687

7688 7689
/*
  Set the specified definer to the default value, which is the current user in
7690
  the thread.
7691 7692 7693 7694 7695 7696 7697
 
  SYNOPSIS
    get_default_definer()
    thd       [in] thread handler
    definer   [out] definer
*/
 
7698
void get_default_definer(THD *thd, LEX_USER *definer)
7699 7700 7701 7702 7703 7704 7705 7706 7707 7708
{
  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);
}

7709

7710
/*
7711
  Create default definer for the specified THD.
7712 7713 7714 7715 7716 7717 7718 7719 7720 7721 7722 7723 7724 7725 7726 7727 7728 7729

  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;

7730
  get_default_definer(thd, definer);
7731 7732 7733 7734 7735

  return definer;
}


7736
/*
7737
  Create definer with the given user and host names.
7738 7739

  SYNOPSIS
7740 7741 7742 7743
    create_definer()
    thd         [in] thread handler
    user_name   [in] user name
    host_name   [in] host name
7744 7745

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

7751
LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
7752
{
7753 7754 7755 7756
  LEX_USER *definer;

  /* Create and initialize. */

7757
  if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
7758 7759 7760 7761 7762 7763
    return 0;

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

  return definer;
7764
}
7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777 7778 7779 7780 7781 7782 7783


/*
  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
7784 7785
    return create_default_definer(thd);

7786 7787
  return user;
}
7788 7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801 7802 7803


/*
  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
*/

7804 7805
bool check_string_length(LEX_STRING *str, const char *err_msg,
                         uint max_length)
7806
{
7807 7808
  if (str->length <= max_length)
    return FALSE;
7809 7810

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

7812 7813
  return TRUE;
}