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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3 4 5 6
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
7

bk@work.mysql.com's avatar
bk@work.mysql.com committed
8 9 10 11
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
12

bk@work.mysql.com's avatar
bk@work.mysql.com committed
13 14 15 16 17
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

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

46 47 48
#ifdef SOLARIS
extern "C" int gethostname(char *name, int namelen);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
49

50
static void time_out_user_resource_limits(THD *thd, USER_CONN *uc);
51
#ifndef NO_EMBEDDED_ACCESS_CHECKS
52
static int check_for_max_user_connections(THD *thd, USER_CONN *uc);
53
#endif
54
static void decrease_user_connections(USER_CONN *uc);
55
static bool check_db_used(THD *thd,TABLE_LIST *tables);
56
static bool check_multi_update_lock(THD *thd, TABLE_LIST *tables, 
57
				    List<Item> *fields, SELECT_LEX *select_lex);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
58 59
static void remove_escape(char *name);
static void refresh_status(void);
60 61
static bool append_file_to_dir(THD *thd, const char **filename_ptr,
			       const char *table_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
62

63
const char *any_db="*any*";	// Special symbol for check_access
bk@work.mysql.com's avatar
bk@work.mysql.com committed
64 65 66 67

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

75
static char empty_c_string[1]= {0};		// Used for not defined 'db'
bk@work.mysql.com's avatar
bk@work.mysql.com committed
76 77 78 79

#ifdef __WIN__
static void  test_signal(int sig_ptr)
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
80
#if !defined( DBUG_OFF)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
81 82
  MessageBox(NULL,"Test signal","DBUG",MB_OK);
#endif
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
83
#if defined(OS2)
84 85
  fprintf(stderr, "Test signal %d\n", sig_ptr);
  fflush(stderr);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
86
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
87 88 89 90
}
static void init_signals(void)
{
  int signals[7] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGBREAK,SIGABRT } ;
91
  for (int i=0 ; i < 7 ; i++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
92 93 94 95
    signal( signals[i], test_signal) ;
}
#endif

96 97 98 99 100 101 102 103 104 105
static void unlock_locked_tables(THD *thd)
{
  if (thd->locked_tables)
  {
    thd->lock=thd->locked_tables;
    thd->locked_tables=0;			// Will be automaticly closed
    close_thread_tables(thd);			// Free tables
  }
}

106

107
static bool end_active_trans(THD *thd)
108
{
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
109
  int error=0;
110
  if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN |
111
		      OPTION_TABLE_LOCK))
112
  {
113 114
    thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
115
    if (ha_commit(thd))
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
116
      error=1;
117
  }
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
118
  return error;
119 120 121
}


monty@mysql.com's avatar
monty@mysql.com committed
122
#ifdef HAVE_REPLICATION
123 124 125
inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables)
{
  return (table_rules_on && tables && !tables_ok(thd,tables) &&
monty@mysql.com's avatar
monty@mysql.com committed
126
          ((thd->lex->sql_command != SQLCOM_DELETE_MULTI) ||
monty@mysql.com's avatar
monty@mysql.com committed
127 128
           !tables_ok(thd,
		      (TABLE_LIST *)thd->lex->auxilliary_table_list.first)));
129
}
monty@mysql.com's avatar
monty@mysql.com committed
130
#endif
131 132


133 134
static HASH hash_user_connections;

135 136
static int get_or_create_user_conn(THD *thd, const char *user,
				   const char *host,
peter@mysql.com's avatar
peter@mysql.com committed
137
				   USER_RESOURCES *mqh)
138 139
{
  int return_val=0;
140
  uint temp_len, user_len;
141 142 143 144 145 146
  char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2];
  struct  user_conn *uc;

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

147 148
  user_len=strlen(user);
  temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1;
149
  (void) pthread_mutex_lock(&LOCK_user_conn);
150 151
  if (!(uc = (struct  user_conn *) hash_search(&hash_user_connections,
					       (byte*) temp_user, temp_len)))
152
  {
153 154 155
    /* First connection for user; Create a user connection object */
    if (!(uc= ((struct user_conn*)
	       my_malloc(sizeof(struct user_conn) + temp_len+1,
156 157
			 MYF(MY_WME)))))
    {
158
      send_error(thd, 0, NullS);		// Out of memory
159 160
      return_val=1;
      goto end;
161
    }
162 163
    uc->user=(char*) (uc+1);
    memcpy(uc->user,temp_user,temp_len+1);
164 165
    uc->user_len= user_len;
    uc->host=uc->user + uc->user_len +  1;
166
    uc->len = temp_len;
167 168 169
    uc->connections = 1;
    uc->questions=uc->updates=uc->conn_per_hour=0;
    uc->user_resources=*mqh;
peter@mysql.com's avatar
peter@mysql.com committed
170
    if (max_user_connections && mqh->connections > max_user_connections)
171
      uc->user_resources.connections = max_user_connections;
172
    uc->intime=thd->thr_create_time;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
173
    if (my_hash_insert(&hash_user_connections, (byte*) uc))
174 175
    {
      my_free((char*) uc,0);
176
      send_error(thd, 0, NullS);		// Out of memory
177 178 179 180 181
      return_val=1;
      goto end;
    }
  }
  thd->user_connect=uc;
182
  uc->connections++;
183 184 185
end:
  (void) pthread_mutex_unlock(&LOCK_user_conn);
  return return_val;
peter@mysql.com's avatar
peter@mysql.com committed
186

187
}
188 189 190


/*
191
    Check if user exist and password supplied is correct. 
192 193
  SYNOPSIS
    check_user()
194 195 196 197 198 199 200 201 202
    thd          thread handle, thd->{host,user,ip} are used
    command      originator of the check: now check_user is called
                 during connect and change user procedures; used for 
                 logging.
    passwd       scrambled password recieved from client
    passwd_len   length of scrambled password
    db           database name to connect to, may be NULL
    check_count  dont know exactly

203 204
    Note, that host, user and passwd may point to communication buffer.
    Current implementation does not depened on that, but future changes
205 206 207
    should be done with this in mind; 'thd' is INOUT, all other params
    are 'IN'.

208 209 210
  RETURN VALUE
    0  OK; thd->user, thd->master_access, thd->priv_user, thd->db and
       thd->db_access are updated; OK is sent to client;
211 212
   -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
213 214
*/

hf@deer.(none)'s avatar
hf@deer.(none) committed
215 216 217
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
218
{
219
  DBUG_ENTER("check_user");
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
220
  
monty@mysql.com's avatar
monty@mysql.com committed
221 222
#ifdef NO_EMBEDDED_ACCESS_CHECKS
  thd->master_access= GLOBAL_ACLS;			// Full rights
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
  /* Change database if necessary: OK or FAIL is sent in mysql_change_db */
  if (db && db[0])
  {
    thd->db= 0;
    thd->db_length= 0;
    if (mysql_change_db(thd, db))
    {
      if (thd->user_connect)
	decrease_user_connections(thd->user_connect);
      DBUG_RETURN(-1);
    }
  }
  else
    send_ok(thd);
  DBUG_RETURN(0);
monty@mysql.com's avatar
monty@mysql.com committed
238 239
#else

240 241 242 243 244
  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);
  
245
  /*
246 247
    If the server is running in secure auth mode, short scrambles are 
    forbidden.
248
  */
249
  if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
250
  {
251 252 253
    net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE);
    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
254
  }
255 256 257 258
  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
259

260
  /*
261 262 263 264
    Clear thd->db as it points to something, that will be freed when 
    connection is closed. We don't want to accidently free a wrong pointer
    if connect failed. Also in case of 'CHANGE USER' failure, current
    database will be switched to 'no database selected'.
265
  */
266 267
  thd->db= 0;
  thd->db_length= 0;
268
  
269
  USER_RESOURCES ur;
270
  int res= acl_getroot(thd, &ur, passwd, passwd_len);
hf@deer.(none)'s avatar
hf@deer.(none) committed
271
#ifndef EMBEDDED_LIBRARY
272
  if (res == -1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
273
  {
274 275 276 277 278 279
    /*
      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.
    */
280
    NET *net= &thd->net;
281
    if (opt_secure_auth_local)
282
    {
283 284 285 286 287 288
      net_printf(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE,
                 thd->user, thd->host_or_ip);
      mysql_log.write(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
                      thd->user, thd->host_or_ip);
      DBUG_RETURN(-1);
    }
289
    if (send_old_password_request(thd) ||
290 291 292 293 294 295 296
        my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) // We have to read very
    {                                                // specific packet size
      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 */
297
    res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323);
298
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
299
#endif /*EMBEDDED_LIBRARY*/
300 301
  /* here res is always >= 0 */
  if (res == 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
302
  {
303
    if (!(thd->master_access & NO_ACCESS)) // authentification is OK 
304
    {
305 306 307 308 309 310 311 312 313 314
      DBUG_PRINT("info",
                 ("Capabilities: %d  packet_length: %ld  Host: '%s'  "
                  "Login user: '%s' Priv_user: '%s'  Using password: %s "
                  "Access: %u  db: '%s'",
                  thd->client_capabilities, thd->max_client_packet_length,
                  thd->host_or_ip, thd->user, thd->priv_user,
                  passwd_len ? "yes": "no",
                  thd->master_access, thd->db ? thd->db : "*none*"));

      if (check_count)
315
      {
316 317
        VOID(pthread_mutex_lock(&LOCK_thread_count));
        bool count_ok= thread_count < max_connections + delayed_insert_threads
318
                       || (thd->master_access & SUPER_ACL);
319 320 321 322 323 324
        VOID(pthread_mutex_unlock(&LOCK_thread_count));
        if (!count_ok)
        {                                         // too many connections 
          send_error(thd, ER_CON_COUNT_ERROR);
          DBUG_RETURN(-1);
        }
325
      }
peter@mysql.com's avatar
peter@mysql.com committed
326

327 328 329 330 331 332 333 334
      /* Why logging is performed before all checks've passed? */
      mysql_log.write(thd,command,
                      (thd->priv_user == thd->user ?
                       (char*) "%s@%s on %s" :
                       (char*) "%s@%s as anonymous on %s"),
                      thd->user, thd->host_or_ip,
                      db ? db : (char*) "");

335
      /*
336 337 338
        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.
339
      */
340 341 342
      thd->db_access=0;

      /* Don't allow user to connect if he has done too many queries */
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
343 344 345 346 347 348 349 350 351
      if ((ur.questions || ur.updates || ur.connections ||
	   max_user_connections) &&
	  get_or_create_user_conn(thd,thd->user,thd->host_or_ip,&ur))
	DBUG_RETURN(-1);
      if (thd->user_connect &&
	  (thd->user_connect->user_resources.connections ||
	   max_user_connections) &&
	  check_for_max_user_connections(thd, thd->user_connect))
	DBUG_RETURN(-1);
352 353 354

      /* Change database if necessary: OK or FAIL is sent in mysql_change_db */
      if (db && db[0])
355
      {
356 357 358 359 360 361
        if (mysql_change_db(thd, db))
        {
          if (thd->user_connect)
            decrease_user_connections(thd->user_connect);
          DBUG_RETURN(-1);
        }
362 363
      }
      else
monty@mysql.com's avatar
monty@mysql.com committed
364
	send_ok(thd);
365 366 367
      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
368 369
    }
  }
370
  else if (res == 2) // client gave short hash, server has long hash
371
  {
372 373 374
    net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE);
    mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE));
    DBUG_RETURN(-1);
375
  }
376 377 378 379 380 381 382 383 384
  net_printf(thd, ER_ACCESS_DENIED_ERROR,
             thd->user,
             thd->host_or_ip,
             passwd_len ? ER(ER_YES) : ER(ER_NO));
  mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
                  thd->user,
                  thd->host_or_ip,
                  passwd_len ? ER(ER_YES) : ER(ER_NO));
  DBUG_RETURN(-1);
monty@mysql.com's avatar
monty@mysql.com committed
385
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
386 387
}

388
/*
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
389 390
  Check for maximum allowable user connections, if the mysqld server is
  started with corresponding variable that is greater then 0.
391 392
*/

393 394
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
395 396 397 398 399
{
  *length=buff->len;
  return (byte*) buff->user;
}

400
extern "C" void free_user(struct user_conn *uc)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
401 402 403 404
{
  my_free((char*) uc,MYF(0));
}

peter@mysql.com's avatar
peter@mysql.com committed
405
void init_max_user_conn(void)
406
{
407 408
  (void) hash_init(&hash_user_connections,system_charset_info,max_connections,
		   0,0,
409
		   (hash_get_key) get_key_conn, (hash_free_key) free_user,
410
		   0);
411 412 413
}


monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
/*
  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
*/

431 432
#ifndef NO_EMBEDDED_ACCESS_CHECKS

433
static int check_for_max_user_connections(THD *thd, USER_CONN *uc)
434
{
435
  int error=0;
436
  DBUG_ENTER("check_for_max_user_connections");
peter@mysql.com's avatar
peter@mysql.com committed
437

438
  (void) pthread_mutex_lock(&LOCK_user_conn);
439
  if (max_user_connections &&
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
440
      max_user_connections < (uint) uc->connections)
441
  {
442
    net_printf(thd,ER_TOO_MANY_USER_CONNECTIONS, uc->user);
443 444
    error=1;
    goto end;
445
  }
446
  time_out_user_resource_limits(thd, uc);
447
  if (uc->user_resources.connections &&
448
      uc->user_resources.connections <= uc->conn_per_hour)
449
  {
450
    net_printf(thd, ER_USER_LIMIT_REACHED, uc->user,
451
	       "max_connections_per_hour",
452 453 454 455
	       (long) uc->user_resources.connections);
    error=1;
    goto end;
  }
456
  uc->conn_per_hour++;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
457 458

  end:
459 460
  if (error)
    uc->connections--; // no need for decrease_user_connections() here
461
  (void) pthread_mutex_unlock(&LOCK_user_conn);
462
  DBUG_RETURN(error);
463
}
464
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
465

monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
/*
  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.
*/

484
static void decrease_user_connections(USER_CONN *uc)
485
{
486
  DBUG_ENTER("decrease_user_connections");
487 488 489
  (void) pthread_mutex_lock(&LOCK_user_conn);
  DBUG_ASSERT(uc->connections);
  if (!--uc->connections && !mqh_used)
490 491
  {
    /* Last connection for user; Delete it */
492
    (void) hash_delete(&hash_user_connections,(byte*) uc);
493
  }
494
  (void) pthread_mutex_unlock(&LOCK_user_conn);
495
  DBUG_VOID_RETURN;
496 497
}

498

499 500 501 502 503
void free_max_user_conn(void)
{
  hash_free(&hash_user_connections);
}

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

505 506 507
/*
  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
508 509 510

  sql_command is actually set to SQLCOM_END sometimes
  so we need the +1 to include it in the array.
511 512
*/

kent@mysql.com's avatar
kent@mysql.com committed
513
char  uc_update_queries[SQLCOM_END+1];
514 515 516

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

519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
  uc_update_queries[SQLCOM_CREATE_TABLE]=1;
  uc_update_queries[SQLCOM_CREATE_INDEX]=1;
  uc_update_queries[SQLCOM_ALTER_TABLE]=1;
  uc_update_queries[SQLCOM_UPDATE]=1;
  uc_update_queries[SQLCOM_INSERT]=1;
  uc_update_queries[SQLCOM_INSERT_SELECT]=1;
  uc_update_queries[SQLCOM_DELETE]=1;
  uc_update_queries[SQLCOM_TRUNCATE]=1;
  uc_update_queries[SQLCOM_DROP_TABLE]=1;
  uc_update_queries[SQLCOM_LOAD]=1;
  uc_update_queries[SQLCOM_CREATE_DB]=1;
  uc_update_queries[SQLCOM_DROP_DB]=1;
  uc_update_queries[SQLCOM_REPLACE]=1;
  uc_update_queries[SQLCOM_REPLACE_SELECT]=1;
  uc_update_queries[SQLCOM_RENAME_TABLE]=1;
  uc_update_queries[SQLCOM_BACKUP_TABLE]=1;
  uc_update_queries[SQLCOM_RESTORE_TABLE]=1;
  uc_update_queries[SQLCOM_DELETE_MULTI]=1;
  uc_update_queries[SQLCOM_DROP_INDEX]=1;
538
  uc_update_queries[SQLCOM_UPDATE_MULTI]=1;
539 540
}

541 542
bool is_update_query(enum enum_sql_command command)
{
kent@mysql.com's avatar
kent@mysql.com committed
543
  DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
544 545
  return uc_update_queries[command];
}
546

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
547
/*
548 549
  Reset per-hour user resource limits when it has been more than
  an hour since they were last checked
550

551 552 553 554
  SYNOPSIS:
    time_out_user_resource_limits()
    thd			Thread handler
    uc			User connection details
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
555

556 557 558 559
  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.
*/
560

561
static void time_out_user_resource_limits(THD *thd, USER_CONN *uc)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
562
{
563
  time_t check_time = thd->start_time ?  thd->start_time : time(NULL);
564
  DBUG_ENTER("time_out_user_resource_limits");
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
565

566
  /* If more than a hour since last check, reset resource checking */
567 568 569 570 571 572 573
  if (check_time  - uc->intime >= 3600)
  {
    uc->questions=1;
    uc->updates=0;
    uc->conn_per_hour=0;
    uc->intime=check_time;
  }
574 575 576 577 578 579 580 581 582 583 584 585

  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)
{
586 587
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  bool error= 0;
588 589 590 591 592 593 594 595
  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);

596
  /* Check that we have not done too many questions / hour */
597 598 599
  if (uc->user_resources.questions &&
      uc->questions++ >= uc->user_resources.questions)
  {
600
    net_printf(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions",
601 602 603 604
	       (long) uc->user_resources.questions);
    error=1;
    goto end;
  }
605
  if (check_command < (uint) SQLCOM_END)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
606
  {
607 608 609 610
    /* 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)
    {
611
      net_printf(thd, ER_USER_LIMIT_REACHED, uc->user, "max_updates",
612 613 614 615
		 (long) uc->user_resources.updates);
      error=1;
      goto end;
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
616 617
  }
end:
618
  (void) pthread_mutex_unlock(&LOCK_user_conn);
619
  DBUG_RETURN(error);
620 621
#else
  return (0);
monty@mysql.com's avatar
monty@mysql.com committed
622
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
623 624
}

625

626
static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0)
627
{
monty@mysql.com's avatar
monty@mysql.com committed
628
#ifndef NO_EMBEDDED_ACCESS_CHECKS
629
  (void) pthread_mutex_lock(&LOCK_user_conn);
peter@mysql.com's avatar
peter@mysql.com committed
630
  if (lu)  // for GRANT
631
  {
632
    USER_CONN *uc;
633
    uint temp_len=lu->user.length+lu->host.length+2;
634 635
    char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2];

636 637
    memcpy(temp_user,lu->user.str,lu->user.length);
    memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length);
638
    temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0;
639
    if ((uc = (struct  user_conn *) hash_search(&hash_user_connections,
640
						(byte*) temp_user, temp_len)))
641 642
    {
      uc->questions=0;
643
      get_mqh(temp_user,&temp_user[lu->user.length+1],uc);
644 645
      uc->updates=0;
      uc->conn_per_hour=0;
646 647
    }
  }
648
  else // for FLUSH PRIVILEGES and FLUSH USER_RESOURCES
649
  {
650
    for (uint idx=0;idx < hash_user_connections.records; idx++)
651
    {
monty@mysql.com's avatar
monty@mysql.com committed
652 653
      USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections,
						      idx);
654 655 656 657 658
      if (get_them)
	get_mqh(uc->user,uc->host,uc);
      uc->questions=0;
      uc->updates=0;
      uc->conn_per_hour=0;
659 660
    }
  }
661
  (void) pthread_mutex_unlock(&LOCK_user_conn);
monty@mysql.com's avatar
monty@mysql.com committed
662
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
663
}
664

bk@work.mysql.com's avatar
bk@work.mysql.com committed
665
/*
666
    Perform handshake, authorize client and update thd ACL variables.
667
  SYNOPSIS
668
    check_connection()
669
    thd  thread handle
670 671

  RETURN
672
     0  success, OK is sent to user, thd is updated.
673 674
    -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
675 676
*/

hf@deer.(none)'s avatar
hf@deer.(none) committed
677 678
#ifndef EMBEDDED_LIBRARY
static int check_connection(THD *thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
679
{
680
  uint connect_errors= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
681
  NET *net= &thd->net;
monty@mysql.com's avatar
monty@mysql.com committed
682 683
  ulong pkt_len= 0;
  char *end;
684

685 686 687
  DBUG_PRINT("info",
             ("New connection received on %s", vio_description(net->vio)));

bk@work.mysql.com's avatar
bk@work.mysql.com committed
688 689
  if (!thd->host)                           // If TCP/IP connection
  {
690
    char ip[30];
691

692
    if (vio_peer_addr(net->vio, ip, &thd->peer_port))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
693
      return (ER_BAD_HOST_ERROR);
694
    if (!(thd->ip= my_strdup(ip,MYF(0))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
695
      return (ER_OUT_OF_RESOURCES);
696
    thd->host_or_ip= thd->ip;
monty@mysql.com's avatar
monty@mysql.com committed
697
    vio_in_addr(net->vio,&thd->remote.sin_addr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
698 699 700
#if !defined(HAVE_SYS_UN_H) || defined(HAVE_mit_thread)
    /* Fast local hostname resolve for Win32 */
    if (!strcmp(thd->ip,"127.0.0.1"))
701
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
702 703
      thd->host= (char*) my_localhost;
      thd->host_or_ip= my_localhost;
704
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
705 706 707
    else
#endif
    {
708 709 710 711 712 713
      if (!(specialflag & SPECIAL_NO_RESOLVE))
      {
	vio_in_addr(net->vio,&thd->remote.sin_addr);
	thd->host=ip_to_hostname(&thd->remote.sin_addr,&connect_errors);
	/* Cut very long hostnames to avoid possible overflows */
	if (thd->host)
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
714
	{
715
	  thd->host[min(strlen(thd->host), HOSTNAME_LENGTH)]= 0;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
716 717
	  thd->host_or_ip= thd->host;
	}
718 719 720
	if (connect_errors > max_connect_errors)
	  return(ER_HOST_IS_BLOCKED);
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
721
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
722 723 724
    DBUG_PRINT("info",("Host: %s  ip: %s",
		       thd->host ? thd->host : "unknown host",
		       thd->ip ? thd->ip : "unknown ip"));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
725 726 727
    if (acl_check_host(thd->host,thd->ip))
      return(ER_HOST_NOT_PRIVILEGED);
  }
728
  else /* Hostname given means that the connection was on a socket */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
729
  {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
730
    DBUG_PRINT("info",("Host: %s",thd->host));
731 732
    thd->host_or_ip= thd->host;
    thd->ip= 0;
733 734
    /* Reset sin_addr */
    bzero((char*) &thd->remote, sizeof(thd->remote));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
735 736 737
  }
  vio_keepalive(net->vio, TRUE);
  {
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
738
    /* buff[] needs to big enough to hold the server_version variable */
739
    char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
740 741
    ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB |
			  CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION);
742

743 744 745 746 747
    if (opt_using_transactions)
      client_flags|=CLIENT_TRANSACTIONS;
#ifdef HAVE_COMPRESS
    client_flags |= CLIENT_COMPRESS;
#endif /* HAVE_COMPRESS */
748 749 750 751
#ifdef HAVE_OPENSSL
    if (ssl_acceptor_fd)
      client_flags |= CLIENT_SSL;       /* Wow, SSL is avalaible! */
#endif /* HAVE_OPENSSL */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
752

753 754 755 756 757 758 759 760 761 762 763
    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
764
      tail: that's why first part of the scramble is placed here, and second
765 766
      part at the end of packet.
    */
767
    end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1;
768 769 770
   
    int2store(end, client_flags);
    /* write server characteristics: up to 16 bytes allowed */
771
    end[2]=(char) default_charset_info->number;
772 773 774 775 776 777 778 779 780
    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
781
			  (uint) (end-buff)) ||
782
	(pkt_len= my_net_read(net)) == packet_error ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
783 784 785 786 787 788 789 790 791 792 793
	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);
794
  if (thd->packet.alloc(thd->variables.net_buffer_length))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
795 796 797
    return(ER_OUT_OF_RESOURCES);

  thd->client_capabilities=uint2korr(net->read_pos);
798 799 800 801 802 803 804 805 806 807 808
#ifdef TO_BE_REMOVED_IN_4_1_RELEASE
  /*
    This is just a safety check against any client that would use the old
    CLIENT_CHANGE_USER flag
  */
  if ((thd->client_capabilities & CLIENT_PROTOCOL_41) &&
      !(thd->client_capabilities & (CLIENT_RESERVED |
				    CLIENT_SECURE_CONNECTION |
				    CLIENT_MULTI_RESULTS)))
    thd->client_capabilities&= ~CLIENT_PROTOCOL_41;
#endif
809 810 811 812
  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
813 814 815 816 817 818 819
    DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
    /*
      Use server character set and collation if
      - client has not specified a character set
      - client character set is the same as the servers
      - client character set doesn't exists in server
    */
820
    if (!(thd->variables.character_set_client=
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
821 822 823 824
	  get_charset((uint) net->read_pos[8], MYF(0))) ||
	!my_strcasecmp(&my_charset_latin1,
		       global_system_variables.character_set_client->name,
		       thd->variables.character_set_client->name))
825
    {
826 827
      thd->variables.character_set_client=
	global_system_variables.character_set_client;
828 829
      thd->variables.collation_connection=
	global_system_variables.collation_connection;
830 831
      thd->variables.character_set_results=
	global_system_variables.character_set_results;
832 833 834
    }
    else
    {
835
      thd->variables.character_set_results=
836 837 838
      thd->variables.collation_connection= 
	thd->variables.character_set_client;
    }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
839
    thd->update_charset();
840
    end= (char*) net->read_pos+32;
841 842 843 844 845 846 847
  }
  else
  {
    thd->max_client_packet_length= uint3korr(net->read_pos+2);
    end= (char*) net->read_pos+5;
  }

848
  if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
849
    thd->variables.sql_mode|= MODE_IGNORE_SPACE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
850
#ifdef HAVE_OPENSSL
851
  DBUG_PRINT("info", ("client capabilities: %d", thd->client_capabilities));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
852 853 854
  if (thd->client_capabilities & CLIENT_SSL)
  {
    /* Do the SSL layering. */
855 856 857 858 859
    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
860
    DBUG_PRINT("info", ("IO layer change in progress..."));
861 862 863 864 865
    if (sslaccept(ssl_acceptor_fd, net->vio, thd->variables.net_wait_timeout))
    {
      DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
			   pkt_len));
      inc_host_errors(&thd->remote.sin_addr);
peter@mysql.com's avatar
peter@mysql.com committed
866
      return(ER_HANDSHAKE_ERROR);
867
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
868
    DBUG_PRINT("info", ("Reading user information over SSL layer"));
869
    if ((pkt_len= my_net_read(net)) == packet_error ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
870 871
	pkt_len < NORMAL_HANDSHAKE_SIZE)
    {
872 873
      DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
			   pkt_len));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
874 875 876 877
      inc_host_errors(&thd->remote.sin_addr);
      return(ER_HANDSHAKE_ERROR);
    }
  }
878 879 880
#endif

  if (end >= (char*) net->read_pos+ pkt_len +2)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
881
  {
882 883
    inc_host_errors(&thd->remote.sin_addr);
    return(ER_HANDSHAKE_ERROR);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
884 885 886
  }

  if (thd->client_capabilities & CLIENT_INTERACTIVE)
887
    thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
888
  if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
889 890
      opt_using_transactions)
    net->return_status= &thd->server_status;
891
  net->read_timeout=(uint) thd->variables.net_read_timeout;
peter@mysql.com's avatar
peter@mysql.com committed
892

893 894
  char *user= end;
  char *passwd= strend(user)+1;
895
  char *db= passwd;
896
  char db_buff[NAME_LEN+1];                     // buffer to store db in utf8
897
  char user_buff[USERNAME_LENGTH+1];		// buffer to store user in utf8
898 899 900
  uint dummy_errors;

  /*
901 902 903 904
    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'.
  */
905
  uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
906 907 908
    *passwd++ : strlen(passwd);
  db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
    db + passwd_len + 1 : 0;
peter@mysql.com's avatar
peter@mysql.com committed
909

kostja@oak.local's avatar
kostja@oak.local committed
910 911
  /* Since 4.1 all database names are stored in utf8 */
  if (db)
peter@mysql.com's avatar
peter@mysql.com committed
912
  {
913 914 915
    db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
                             system_charset_info,
                             db, strlen(db),
916
                             thd->charset(), &dummy_errors)]= 0;
917
    db= db_buff;
kostja@oak.local's avatar
kostja@oak.local committed
918
  }
peter@mysql.com's avatar
peter@mysql.com committed
919

920 921 922 923
  user_buff[copy_and_convert(user_buff, sizeof(user_buff)-1,
                             system_charset_info, user, strlen(user),
                             thd->charset(), &dummy_errors)]= '\0';
  user= user_buff;
924

925 926
  if (thd->user)
    x_free(thd->user);
927 928
  if (!(thd->user= my_strdup(user, MYF(0))))
    return (ER_OUT_OF_RESOURCES);
929
  return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
930 931
}

932

933 934
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
935 936 937 938
{
  Vio* save_vio;
  ulong save_client_capabilities;

939 940 941 942 943 944 945 946 947
  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
948 949
  save_client_capabilities= thd->client_capabilities;
  thd->client_capabilities|= CLIENT_MULTI_QUERIES;
950 951 952 953
  /*
    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
954 955 956
  save_vio= thd->net.vio;
  thd->net.vio= 0;
  dispatch_command(COM_QUERY, thd, thd->query, thd->query_length+1);
957
  rw_unlock(var_mutex);
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
958 959 960 961 962
  thd->client_capabilities= save_client_capabilities;
  thd->net.vio= save_vio;
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
963 964 965 966
pthread_handler_decl(handle_one_connection,arg)
{
  THD *thd=(THD*) arg;
  uint launch_time  =
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
967
    (uint) ((thd->thr_create_time = time(NULL)) - thd->connect_time);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
968 969 970 971 972
  if (launch_time >= slow_launch_time)
    statistic_increment(slow_launch_threads,&LOCK_status );

  pthread_detach_this_thread();

973 974
#if !defined( __WIN__) && !defined(OS2)	// Win32 calls this in pthread_create
  // The following calls needs to be done before we call DBUG_ macros
975
  if (!(test_flags & TEST_NO_THREADS) & my_thread_init())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
976
  {
977
    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
978
    statistic_increment(aborted_connects,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
979 980 981 982 983
    end_thread(thd,0);
    return 0;
  }
#endif

984 985 986 987 988 989 990
  /*
    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.
  */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
991 992 993 994
  DBUG_PRINT("info", ("handle_one_connection called by thread %d\n",
		      thd->thread_id));
  // now that we've called my_thread_init(), it is safe to call DBUG_*

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
995
#if defined(__WIN__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
996
  init_signals();				// IRENA; testing ?
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
997
#elif !defined(OS2) && !defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
998 999 1000 1001 1002 1003
  sigset_t set;
  VOID(sigemptyset(&set));			// Get mask in use
  VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
#endif
  if (thd->store_globals())
  {
1004
    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
1005
    statistic_increment(aborted_connects,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
    end_thread(thd,0);
    return 0;
  }

  do
  {
    int error;
    NET *net= &thd->net;
    thd->thread_stack= (char*) &thd;

1016
    if ((error=check_connection(thd)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1017 1018
    {						// Wrong permissions
      if (error > 0)
1019
	net_printf(thd,error,thd->host_or_ip);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1020 1021
#ifdef __NT__
      if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
1022
	my_sleep(1000);				/* must wait after eof() */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1023
#endif
1024
      statistic_increment(aborted_connects,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1025 1026
      goto end_thread;
    }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1027 1028 1029
#ifdef __NETWARE__
    netware_reg_user(thd->ip, thd->user, "MySQL");
#endif
1030
    if (thd->variables.max_join_size == HA_POS_ERROR)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1031 1032 1033 1034
      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
1035
    thd->version= refresh_version;
1036 1037 1038
    thd->proc_info= 0;
    thd->set_time();
    thd->init_for_queries();
1039
    if (sys_init_connect.value_length && !(thd->master_access & SUPER_ACL))
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1040
    {
1041 1042 1043
      execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect);
      if (thd->query_error)
	thd->killed= 1;
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1044
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1045 1046 1047 1048 1049
    while (!net->error && net->vio != 0 && !thd->killed)
    {
      if (do_command(thd))
	break;
    }
1050 1051
    if (thd->user_connect)
      decrease_user_connections(thd->user_connect);
1052
    free_root(thd->mem_root,MYF(0));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1053
    if (net->error && net->vio != 0 && net->report_error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1054
    {
1055
      if (!thd->killed && thd->variables.log_warnings > 1)
serg@serg.mylan's avatar
serg@serg.mylan committed
1056 1057 1058 1059 1060 1061
        sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
                          thd->thread_id,(thd->db ? thd->db : "unconnected"),
                          thd->user ? thd->user : "unauthenticated",
                          thd->host_or_ip,
                          (net->last_errno ? ER(net->last_errno) :
                           ER(ER_UNKNOWN_ERROR)));
1062
      send_error(thd,net->last_errno,NullS);
1063
      statistic_increment(aborted_threads,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1064
    }
1065 1066 1067 1068
    else if (thd->killed)
    {
      statistic_increment(aborted_threads,&LOCK_status);
    }
1069
    
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1070
end_thread:
1071
    close_connection(thd, 0, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
    end_thread(thd,1);
    /*
      If end_thread returns, we are either running with --one-thread
      or this thread has been schedule to handle the next query
    */
    thd= current_thd;
  } while (!(test_flags & TEST_NO_THREADS));
  /* The following is only executed if we are not using --one-thread */
  return(0);					/* purecov: deadcode */
}

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

1085 1086 1087 1088
/*
  Execute commands from bootstrap_file.
  Used when creating the initial grant tables
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1089

1090
extern "C" pthread_handler_decl(handle_bootstrap,arg)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1091
{
1092 1093 1094
  THD *thd=(THD*) arg;
  FILE *file=bootstrap_file;
  char *buff;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1095

1096
  /* The following must be called before DBUG_ENTER */
1097
  if (my_thread_init() || thd->store_globals())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1098
  {
hf@deer.(none)'s avatar
hf@deer.(none) committed
1099
#ifndef EMBEDDED_LIBRARY
1100
    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1101
#endif
1102
    thd->fatal_error();
1103
    goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1104
  }
1105 1106
  DBUG_ENTER("handle_bootstrap");

hf@deer.(none)'s avatar
hf@deer.(none) committed
1107
#ifndef EMBEDDED_LIBRARY
1108 1109
  pthread_detach_this_thread();
  thd->thread_stack= (char*) &thd;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1110
#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1111
  sigset_t set;
1112 1113
  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
1114
#endif
hf@deer.(none)'s avatar
hf@deer.(none) committed
1115
#endif /* EMBEDDED_LIBRARY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1116

1117
  if (thd->variables.max_join_size == HA_POS_ERROR)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1118 1119 1120 1121
    thd->options |= OPTION_BIG_SELECTS;

  thd->proc_info=0;
  thd->version=refresh_version;
1122
  thd->priv_user=thd->user=(char*) my_strdup("boot", MYF(MY_WME));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1123

1124
  buff= (char*) thd->net.buff;
1125
  thd->init_for_queries();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1126 1127
  while (fgets(buff, thd->net.max_packet, file))
  {
1128 1129
    ulong length= (ulong) strlen(buff);
    while (buff[length-1] != '\n' && !feof(file))
1130
    {
1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143
      /*
        We got only a part of the current string. Will try to increase
        net buffer then read the rest of the current string.
      */
      if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
      {
        send_error(thd, thd->net.last_errno, NullS);
        thd->is_fatal_error= 1;
        break;
      }
      buff= (char*) thd->net.buff;
      fgets(buff + length, thd->net.max_packet - length, file);
      length+= (ulong) strlen(buff + length);
1144
    }
1145 1146
    if (thd->is_fatal_error)
      break;
1147
    while (length && (my_isspace(thd->charset(), buff[length-1]) ||
1148
           buff[length-1] == ';'))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1149 1150
      length--;
    buff[length]=0;
1151
    thd->query_length=length;
1152 1153
    thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1);
    thd->query[length] = '\0';
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1154
    thd->query_id=query_id++;
1155
    if (mqh_used && thd->user_connect && check_mqh(thd, SQLCOM_END))
1156 1157 1158
    {
      thd->net.error = 0;
      close_thread_tables(thd);			// Free tables
1159
      free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
1160 1161
      break;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1162 1163
    mysql_parse(thd,thd->query,length);
    close_thread_tables(thd);			// Free tables
1164
    if (thd->is_fatal_error)
1165
      break;
1166
    free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
1167
    free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1168
  }
1169 1170 1171

  /* thd->fatal_error should be set in case something went wrong */
end:
hf@deer.(none)'s avatar
hf@deer.(none) committed
1172
#ifndef EMBEDDED_LIBRARY
1173 1174 1175
  (void) pthread_mutex_lock(&LOCK_thread_count);
  thread_count--;
  (void) pthread_mutex_unlock(&LOCK_thread_count);
1176
  (void) pthread_cond_broadcast(&COND_thread_count);
1177 1178
  my_thread_end();
  pthread_exit(0);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1179
#endif
1180
  DBUG_RETURN(0);				// Never reached
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1181 1182
}

1183
    /* This works because items are allocated with sql_alloc() */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1184

1185
void free_items(Item *item)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1186
{
1187
  for (; item ; item=item->next)
1188
    item->delete_self();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1189 1190
}

1191 1192 1193 1194 1195 1196 1197 1198
    /* This works because items are allocated with sql_alloc() */

void cleanup_items(Item *item)
{
  for (; item ; item=item->next)
    item->cleanup();
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1199 1200 1201 1202 1203 1204 1205
int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd)
{
  TABLE* table;
  TABLE_LIST* table_list;
  int error = 0;
  DBUG_ENTER("mysql_table_dump");
  db = (db && db[0]) ? db : thd->db;
1206
  if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST))))
1207
    DBUG_RETURN(1); // out of memory
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1208
  table_list->db = db;
1209
  table_list->real_name = table_list->alias = tbl_name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1210 1211 1212
  table_list->lock_type = TL_READ_NO_INSERT;
  table_list->next = 0;

1213 1214
  if (!db || check_db_name(db))
  {
1215
    net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
1216 1217
    goto err;
  }
1218
  if (lower_case_table_names)
1219
    my_casedn_str(files_charset_info, tbl_name);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1220
  remove_escape(table_list->real_name);
1221 1222 1223 1224

  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
1225
  if (check_one_table_access(thd, SELECT_ACL, table_list))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1226 1227
    goto err;
  thd->free_list = 0;
1228
  thd->query_length=(uint) strlen(tbl_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1229
  thd->query = tbl_name;
1230 1231
  if ((error = mysqld_dump_create_info(thd, table, -1)))
  {
1232
    my_error(ER_GET_ERRNO, MYF(0), my_errno);
1233 1234
    goto err;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1235
  net_flush(&thd->net);
1236
  if ((error= table->file->dump(thd,fd)))
1237
    my_error(ER_GET_ERRNO, MYF(0), error);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1238

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1239 1240
err:
  close_thread_tables(thd);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1241
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1242 1243 1244
}


1245
#ifndef EMBEDDED_LIBRARY
1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1257 1258 1259
bool do_command(THD *thd)
{
  char *packet;
1260 1261
  uint old_timeout;
  ulong packet_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1262 1263 1264 1265 1266
  NET *net;
  enum enum_server_command command;
  DBUG_ENTER("do_command");

  net= &thd->net;
1267 1268 1269 1270
  /*
    indicator of uninitialized lex => normal flow of errors handling
    (see my_message_sql)
  */
1271
  thd->lex->current_select= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1272 1273

  packet=0;
1274 1275 1276
  old_timeout=net->read_timeout;
  // Wait max for 8 hours
  net->read_timeout=(uint) thd->variables.net_wait_timeout;
1277
  thd->clear_error();				// Clear error message
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1278 1279 1280 1281

  net_new_transaction(net);
  if ((packet_length=my_net_read(net)) == packet_error)
  {
1282 1283 1284 1285 1286
    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)
1287 1288
    {
      statistic_increment(aborted_threads,&LOCK_status);
1289
      DBUG_RETURN(TRUE);			// We have to close it.
1290
    }
1291
    send_error(thd,net->last_errno,NullS);
1292
    net->error= 0;
1293
    DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1294 1295 1296 1297 1298
  }
  else
  {
    packet=(char*) net->read_pos;
    command = (enum enum_server_command) (uchar) packet[0];
1299 1300
    if (command >= COM_END)
      command= COM_END;				// Wrong command
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1301 1302 1303
    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
1304
  }
1305
  net->read_timeout=old_timeout;		// restore it
1306 1307 1308 1309 1310 1311 1312 1313 1314
  /*
    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.
  */
1315
  DBUG_RETURN(dispatch_command(command,thd, packet+1, (uint) packet_length));
1316
}
1317
#endif  /* EMBEDDED_LIBRARY */
1318

1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333
/*
   Perform one connection-level (COM_XXXX) command.
  SYNOPSIS
    dispatch_command()
    thd             connection handle
    command         type of command to perform 
    packet          data for the command, packet is always null-terminated
    packet_length   length of packet + 1 (to show that data is
                    null-terminated) except for COM_SLEEP, where it
                    can be zero.
  RETURN VALUE
    0   ok
    1   request of thread shutdown, i. e. if command is
        COM_QUIT/COM_SHUTDOWN
*/
1334

1335 1336 1337 1338
bool dispatch_command(enum enum_server_command command, THD *thd,
		      char* packet, uint packet_length)
{
  NET *net= &thd->net;
1339
  bool error= 0;
1340 1341 1342
  DBUG_ENTER("dispatch_command");

  thd->command=command;
1343
  /*
1344 1345
    Commands which always take a long time are logged into
    the slow log only if opt_log_slow_admin_statements is set.
1346
  */
1347
  thd->enable_slow_log= TRUE;
1348
  thd->set_time();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1349 1350 1351 1352 1353
  VOID(pthread_mutex_lock(&LOCK_thread_count));
  thd->query_id=query_id;
  if (command != COM_STATISTICS && command != COM_PING)
    query_id++;
  thread_running++;
1354
  /* TODO: set thd->lex->sql_command to SQLCOM_END here */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1355
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
1356

1357 1358
  thd->server_status&=
           ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
1359
  switch (command) {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1360
  case COM_INIT_DB:
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1361 1362 1363 1364 1365 1366 1367 1368 1369
  {
    LEX_STRING tmp;
    statistic_increment(com_stat[SQLCOM_CHANGE_DB],&LOCK_status);
    thd->convert_string(&tmp, system_charset_info,
			packet, strlen(packet), thd->charset());
    if (!mysql_change_db(thd, tmp.str))
      mysql_log.write(thd,command,"%s",thd->db);
    break;
  }
1370
#ifdef HAVE_REPLICATION
1371 1372
  case COM_REGISTER_SLAVE:
  {
1373
    if (!register_slave(thd, (uchar*)packet, packet_length))
1374
      send_ok(thd);
1375 1376
    break;
  }
1377
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1378
  case COM_TABLE_DUMP:
1379 1380 1381 1382 1383 1384
  {
    char *db, *tbl_name;
    uint db_len= *(uchar*) packet;
    uint tbl_len= *(uchar*) (packet + db_len + 1);

    statistic_increment(com_other, &LOCK_status);
1385
    thd->enable_slow_log= opt_log_slow_admin_statements;
1386 1387 1388 1389 1390 1391 1392
    db= thd->alloc(db_len + tbl_len + 2);
    tbl_name= strmake(db, packet + 1, db_len)+1;
    strmake(tbl_name, packet + db_len + 2, tbl_len);
    if (mysql_table_dump(thd, db, tbl_name, -1))
      send_error(thd); // dump to NET
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1393 1394
  case COM_CHANGE_USER:
  {
1395
    thd->change_user();
1396
    thd->clear_error();                         // if errors from rollback
1397

1398 1399
    statistic_increment(com_other, &LOCK_status);
    char *user= (char*) packet;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1400
    char *passwd= strend(user)+1;
1401 1402 1403 1404 1405
    /* 
      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).
    */
1406
    char db_buff[NAME_LEN+1];                 // buffer to store db in utf8 
1407 1408 1409 1410
    char *db= passwd;
    uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? 
      *passwd++ : strlen(passwd);
    db+= passwd_len + 1;
1411
#ifndef EMBEDDED_LIBRARY
peter@mysql.com's avatar
peter@mysql.com committed
1412
    /* Small check for incomming packet */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1413
    if ((uint) ((uchar*) db - net->read_pos) > packet_length)
1414 1415 1416 1417
    {
      send_error(thd, ER_UNKNOWN_COM_ERROR);
      break;
    }
1418
#endif
1419
    /* Convert database name to utf8 */
1420
    uint dummy_errors;
1421 1422
    db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
                             system_charset_info, db, strlen(db),
1423
                             thd->charset(), &dummy_errors)]= 0;
1424
    db= db_buff;
peter@mysql.com's avatar
peter@mysql.com committed
1425

1426 1427 1428 1429 1430 1431 1432
    /* Save user and privileges */
    uint save_master_access= thd->master_access;
    uint save_db_access= thd->db_access;
    uint save_db_length= thd->db_length;
    char *save_user= thd->user;
    char *save_priv_user= thd->priv_user;
    char *save_db= thd->db;
1433
    USER_CONN *save_user_connect= thd->user_connect;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1434 1435
    
    if (!(thd->user= my_strdup(user, MYF(0))))
1436 1437 1438 1439 1440
    {
      thd->user= save_user;
      send_error(thd, ER_OUT_OF_RESOURCES);
      break;
    }
peter@mysql.com's avatar
peter@mysql.com committed
1441

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

1446 1447 1448 1449 1450 1451 1452 1453
    if (res)
    {
      /* authentification failure, we shall restore old user */
      if (res > 0)
        send_error(thd, ER_UNKNOWN_COM_ERROR);
      x_free(thd->user);
      thd->user= save_user;
      thd->priv_user= save_priv_user;
1454
      thd->user_connect= save_user_connect;
1455 1456 1457 1458 1459 1460 1461 1462
      thd->master_access= save_master_access;
      thd->db_access= save_db_access;
      thd->db= save_db;
      thd->db_length= save_db_length;
    }
    else
    {
      /* we've authenticated new user */
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1463 1464
      if (save_user_connect)
	decrease_user_connections(save_user_connect);
1465 1466 1467
      x_free((gptr) save_db);
      x_free((gptr) save_user);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1468 1469
    break;
  }
1470 1471
  case COM_EXECUTE:
  {
1472
    mysql_stmt_execute(thd, packet, packet_length);
1473 1474 1475 1476
    break;
  }
  case COM_LONG_DATA:
  {
1477
    mysql_stmt_get_longdata(thd, packet, packet_length);
1478 1479 1480 1481
    break;
  }
  case COM_PREPARE:
  {
1482
    mysql_stmt_prepare(thd, packet, packet_length);
1483 1484
    break;
  }
1485 1486 1487 1488 1489
  case COM_CLOSE_STMT:
  {
    mysql_stmt_free(thd, packet);
    break;
  }
1490 1491 1492 1493 1494
  case COM_RESET_STMT:
  {
    mysql_stmt_reset(thd, packet);
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1495 1496
  case COM_QUERY:
  {
1497 1498
    if (alloc_query(thd, packet, packet_length))
      break;					// fatal error is set
1499
    char *packet_end= thd->query + thd->query_length;
1500
    mysql_log.write(thd,command,"%s",thd->query);
1501
    DBUG_PRINT("query",("%-.4096s",thd->query));
1502
    mysql_parse(thd,thd->query, thd->query_length);
1503

1504
    while (!thd->killed && !thd->is_fatal_error && thd->lex->found_colon)
1505
    {
1506
      char *packet= thd->lex->found_colon;
1507
      /*
1508
        Multiple queries exits, execute them individually
1509
	in embedded server - just store them to be executed later 
1510
      */
1511
#ifndef EMBEDDED_LIBRARY
1512
      if (thd->lock || thd->open_tables || thd->derived_tables)
1513
        close_thread_tables(thd);
1514
#endif
1515
      ulong length= (ulong)(packet_end-packet);
1516

1517
      log_slow_statement(thd);
1518

1519
      /* Remove garbage at start of query */
1520
      while (my_isspace(thd->charset(), *packet) && length > 0)
1521 1522 1523 1524
      {
        packet++;
        length--;
      }
monty@mysql.com's avatar
monty@mysql.com committed
1525
      VOID(pthread_mutex_lock(&LOCK_thread_count));
1526
      thd->query_length= length;
1527 1528
      thd->query= packet;
      thd->query_id= query_id++;
1529
      thd->set_time(); /* Reset the query start time. */
1530
      /* TODO: set thd->lex->sql_command to SQLCOM_END here */
1531
      VOID(pthread_mutex_unlock(&LOCK_thread_count));
1532
#ifndef EMBEDDED_LIBRARY
1533
      mysql_parse(thd, packet, length);
1534
#else
hf@deer.(none)'s avatar
hf@deer.(none) committed
1535 1536 1537 1538 1539 1540 1541 1542 1543 1544
      /*
	'packet' can point inside the query_rest's buffer
	so we have to do memmove here
       */
      if (thd->query_rest.length() > length)
      {
	memmove(thd->query_rest.c_ptr(), packet, length);
	thd->query_rest.length(length);
      }
      else
1545
	thd->query_rest.copy(packet, length, thd->query_rest.charset());
1546 1547 1548

      thd->server_status&= ~ (SERVER_QUERY_NO_INDEX_USED |
                              SERVER_QUERY_NO_GOOD_INDEX_USED);
1549 1550
      break;
#endif /*EMBEDDED_LIBRARY*/
1551 1552
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1553 1554 1555 1556 1557
    if (!(specialflag & SPECIAL_NO_PRIOR))
      my_pthread_setprio(pthread_self(),WAIT_PRIOR);
    DBUG_PRINT("info",("query ready"));
    break;
  }
1558
  case COM_FIELD_LIST:				// This isn't actually needed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1559
#ifdef DONT_ALLOW_SHOW_COMMANDS
1560
    send_error(thd,ER_NOT_ALLOWED_COMMAND);	/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1561 1562 1563
    break;
#else
  {
1564
    char *fields, *pend;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1565
    TABLE_LIST table_list;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1566 1567
    LEX_STRING conv_name;

1568
    statistic_increment(com_stat[SQLCOM_SHOW_FIELDS],&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1569 1570 1571
    bzero((char*) &table_list,sizeof(table_list));
    if (!(table_list.db=thd->db))
    {
1572
      send_error(thd,ER_NO_DB_ERROR);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1573 1574 1575
      break;
    }
    thd->free_list=0;
1576
    pend= strend(packet);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1577 1578 1579
    thd->convert_string(&conv_name, system_charset_info,
			packet, (uint) (pend-packet), thd->charset());
    table_list.alias= table_list.real_name= conv_name.str;
1580
    packet= pend+1;
1581
    thd->query_length= strlen(packet);       // for simplicity: don't optimize
1582 1583
    if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1)))
      break;
1584
    mysql_log.write(thd,command,"%s %s",table_list.real_name,fields);
1585
    if (lower_case_table_names)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1586
      my_casedn_str(files_charset_info, table_list.real_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1587 1588
    remove_escape(table_list.real_name);	// This can't have wildcards

monty@mysql.com's avatar
monty@mysql.com committed
1589 1590
    if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege,
		     0, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1591
      break;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1592 1593
    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
1594 1595
      break;
    mysqld_list_fields(thd,&table_list,fields);
1596
    free_items(thd->free_list);
1597
    thd->free_list= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1598 1599 1600 1601
    break;
  }
#endif
  case COM_QUIT:
1602
    /* We don't calculate statistics for this command */
1603
    mysql_log.write(thd,command,NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1604 1605 1606 1607
    net->error=0;				// Don't give 'abort' message
    error=TRUE;					// End server
    break;

1608
  case COM_CREATE_DB:				// QQ: To be removed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1609
    {
1610
      char *db=thd->strdup(packet), *alias;
1611
      HA_CREATE_INFO create_info;
1612

1613
      statistic_increment(com_stat[SQLCOM_CREATE_DB],&LOCK_status);
1614
      // null test to handle EOM
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1615
      if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
1616
      {
1617
	net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
1618 1619
	break;
      }
hf@deer.(none)'s avatar
hf@deer.(none) committed
1620
      if (check_access(thd,CREATE_ACL,db,0,1,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1621
	break;
1622
      mysql_log.write(thd,command,packet);
1623
      bzero(&create_info, sizeof(create_info));
1624
      if (mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db),
1625
                          &create_info, 0) < 0)
monty@mysql.com's avatar
monty@mysql.com committed
1626
        send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1627 1628
      break;
    }
1629
  case COM_DROP_DB:				// QQ: To be removed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1630
    {
1631
      statistic_increment(com_stat[SQLCOM_DROP_DB],&LOCK_status);
1632
      char *db=thd->strdup(packet), *alias;
1633
      // null test to handle EOM
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1634
      if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
1635
      {
1636
	net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
1637 1638
	break;
      }
hf@deer.(none)'s avatar
hf@deer.(none) committed
1639
      if (check_access(thd,DROP_ACL,db,0,1,0))
1640
	break;
1641 1642
      if (thd->locked_tables || thd->active_transaction())
      {
1643
	send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1644
	break;
1645
      }
1646
      mysql_log.write(thd,command,db);
1647 1648
      if (mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : db),
                      0, 0) < 0)
monty@mysql.com's avatar
monty@mysql.com committed
1649
        send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1650 1651
      break;
    }
1652
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1653 1654
  case COM_BINLOG_DUMP:
    {
monty@mysql.com's avatar
monty@mysql.com committed
1655 1656 1657 1658
      ulong pos;
      ushort flags;
      uint32 slave_server_id;

1659
      statistic_increment(com_other,&LOCK_status);
1660
      thd->enable_slow_log= opt_log_slow_admin_statements;
1661
      if (check_global_access(thd, REPL_SLAVE_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1662
	break;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1663

1664
      /* TODO: The following has to be changed to an 8 byte integer */
1665 1666
      pos = uint4korr(packet);
      flags = uint2korr(packet + 4);
1667
      thd->server_id=0; /* avoid suicide */
1668
      if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1669
	kill_zombie_dump_threads(slave_server_id);
1670
      thd->server_id = slave_server_id;
monty@mysql.com's avatar
monty@mysql.com committed
1671 1672 1673

      mysql_log.write(thd, command, "Log: '%s'  Pos: %ld", packet+10,
                      (long) pos);
1674
      mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
1675
      unregister_slave(thd,1,1);
1676 1677 1678
      // fake COM_QUIT -- if we get here, the thread needs to terminate
      error = TRUE;
      net->error = 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1679 1680
      break;
    }
1681
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1682 1683
  case COM_REFRESH:
    {
1684
      statistic_increment(com_stat[SQLCOM_FLUSH],&LOCK_status);
1685
      ulong options= (ulong) (uchar) packet[0];
1686
      if (check_global_access(thd,RELOAD_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1687
	break;
1688
      mysql_log.write(thd,command,NullS);
1689 1690 1691 1692
      if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, NULL))
        send_error(thd, 0);
      else
        send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1693 1694
      break;
    }
1695
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1696
  case COM_SHUTDOWN:
1697
  {
1698
    statistic_increment(com_other,&LOCK_status);
1699
    if (check_global_access(thd,SHUTDOWN_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1700
      break; /* purecov: inspected */
1701
    /*
1702 1703 1704 1705
      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].
1706
    */
1707 1708
    enum mysql_enum_shutdown_level level=
      (enum mysql_enum_shutdown_level) (uchar) packet[0];
1709
    DBUG_PRINT("quit",("Got shutdown command for level %u", level));
1710 1711 1712 1713 1714 1715 1716 1717
    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");
      send_error(thd);
      break;
    }
1718
    DBUG_PRINT("quit",("Got shutdown command for level %u", level));
1719
    mysql_log.write(thd,command,NullS);
1720
    send_eof(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1721 1722 1723
#ifdef __WIN__
    sleep(1);					// must wait after eof()
#endif
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1724
#ifndef OS2
1725
    send_eof(thd);				// This is for 'quit request'
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1726
#endif
1727
    close_connection(thd, 0, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1728
    close_thread_tables(thd);			// Free before kill
1729
    free_root(thd->mem_root,MYF(0));
1730
    free_root(&thd->transaction.mem_root,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1731 1732 1733
    kill_mysql();
    error=TRUE;
    break;
1734
  }
1735
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1736 1737
  case COM_STATISTICS:
  {
1738
    mysql_log.write(thd,command,NullS);
1739
    statistic_increment(com_stat[SQLCOM_SHOW_STATUS],&LOCK_status);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1740
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1741
    char buff[200];
hf@deer.(none)'s avatar
hf@deer.(none) committed
1742 1743 1744
#else
    char *buff= thd->net.last_error;
#endif
1745
    ulong uptime = (ulong) (thd->start_time - start_time);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1746
    sprintf((char*) buff,
1747
	    "Uptime: %ld  Threads: %d  Questions: %lu  Slow queries: %ld  Opens: %ld  Flush tables: %ld  Open tables: %u  Queries per second avg: %.3f",
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1748 1749 1750 1751 1752
	    uptime,
	    (int) thread_count,thd->query_id,long_query_count,
	    opened_tables,refresh_version, cached_tables(),
	    uptime ? (float)thd->query_id/(float)uptime : 0);
#ifdef SAFEMALLOC
1753
    if (sf_malloc_cur_memory)				// Using SAFEMALLOC
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1754
      sprintf(strend(buff), "  Memory in use: %ldK  Max memory used: %ldK",
1755 1756
	      (sf_malloc_cur_memory+1023L)/1024L,
	      (sf_malloc_max_memory+1023L)/1024L);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1757 1758
#endif
#ifndef EMBEDDED_LIBRARY
1759
    VOID(my_net_write(net, buff,(uint) strlen(buff)));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1760
    VOID(net_flush(net));
hf@deer.(none)'s avatar
hf@deer.(none) committed
1761
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1762 1763 1764
    break;
  }
  case COM_PING:
1765
    statistic_increment(com_other,&LOCK_status);
1766
    send_ok(thd);				// Tell client we are alive
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1767 1768
    break;
  case COM_PROCESS_INFO:
1769
    statistic_increment(com_stat[SQLCOM_SHOW_PROCESSLIST],&LOCK_status);
1770
    if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1771
      break;
1772
    mysql_log.write(thd,command,NullS);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1773 1774
    mysqld_list_processes(thd,
			  thd->master_access & PROCESS_ACL ? 
monty@mysql.com's avatar
monty@mysql.com committed
1775
			  NullS : thd->priv_user, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1776 1777 1778
    break;
  case COM_PROCESS_KILL:
  {
1779
    statistic_increment(com_stat[SQLCOM_KILL],&LOCK_status);
1780
    ulong id=(ulong) uint4korr(packet);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1781 1782 1783
    kill_one_thread(thd,id);
    break;
  }
1784 1785 1786 1787 1788 1789 1790
  case COM_SET_OPTION:
  {
    statistic_increment(com_stat[SQLCOM_SET_OPTION], &LOCK_status);
    enum_mysql_set_option command= (enum_mysql_set_option) uint2korr(packet);
    switch (command) {
    case MYSQL_OPTION_MULTI_STATEMENTS_ON:
      thd->client_capabilities|= CLIENT_MULTI_STATEMENTS;
1791
      send_eof(thd);
1792 1793 1794
      break;
    case MYSQL_OPTION_MULTI_STATEMENTS_OFF:
      thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS;
1795
      send_eof(thd);
1796 1797 1798 1799 1800 1801 1802
      break;
    default:
      send_error(thd, ER_UNKNOWN_COM_ERROR);
      break;
    }
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1803
  case COM_DEBUG:
1804
    statistic_increment(com_other,&LOCK_status);
1805
    if (check_global_access(thd, SUPER_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1806 1807
      break;					/* purecov: inspected */
    mysql_print_status(thd);
1808
    mysql_log.write(thd,command,NullS);
1809
    send_eof(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1810 1811 1812 1813 1814
    break;
  case COM_SLEEP:
  case COM_CONNECT:				// Impossible here
  case COM_TIME:				// Impossible from client
  case COM_DELAYED_INSERT:
1815
  case COM_END:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1816
  default:
1817
    send_error(thd, ER_UNKNOWN_COM_ERROR);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1818 1819
    break;
  }
1820
  if (thd->lock || thd->open_tables || thd->derived_tables)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1821 1822 1823 1824 1825
  {
    thd->proc_info="closing tables";
    close_thread_tables(thd);			/* Free tables */
  }

1826
  if (thd->is_fatal_error)
1827
    send_error(thd,0);				// End of memory ?
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1828

1829
  log_slow_statement(thd);
1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844

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


1845
void log_slow_statement(THD *thd)
1846
{
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1847
  time_t start_of_query=thd->start_time;
1848
  thd->end_time();				// Set start time
1849

1850 1851 1852 1853 1854
  /*
    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
1855
  {
1856 1857
    thd->proc_info="logging slow query";

1858 1859
    if ((ulong) (thd->start_time - thd->time_after_lock) >
	thd->variables.long_query_time ||
1860 1861
	((thd->server_status &
	  (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
1862
	 (specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES)))
1863 1864 1865 1866
    {
      long_query_count++;
      mysql_slow_log.write(thd, thd->query, thd->query_length, start_of_query);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1867 1868 1869
  }
}

1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887

/*
  Read query from packet and store in thd->query
  Used in COM_QUERY and COM_PREPARE

  DESCRIPTION
    Sets the following THD variables:
      query
      query_length

  RETURN VALUES
    0	ok
    1	error;  In this case thd->fatal_error is set
*/

bool alloc_query(THD *thd, char *packet, ulong packet_length)
{
  packet_length--;				// Remove end null
1888
  /* Remove garbage at start and end of query */
1889
  while (my_isspace(thd->charset(),packet[0]) && packet_length > 0)
1890 1891 1892 1893 1894
  {
    packet++;
    packet_length--;
  }
  char *pos=packet+packet_length;		// Point at end null
peter@mysql.com's avatar
peter@mysql.com committed
1895
  while (packet_length > 0 &&
1896
	 (pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1])))
1897 1898 1899 1900 1901
  {
    pos--;
    packet_length--;
  }
  /* We must allocate some extra memory for query cache */
monty@mysql.com's avatar
monty@mysql.com committed
1902
  thd->query_length= 0;                        // Extra safety: Avoid races
1903 1904
  if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet),
					      packet_length,
1905 1906
					      thd->db_length+ 1 +
					      QUERY_CACHE_FLAGS_SIZE)))
1907 1908 1909
    return 1;
  thd->query[packet_length]=0;
  thd->query_length= packet_length;
1910 1911 1912 1913

  /* Reclaim some memory */
  thd->packet.shrink(thd->variables.net_buffer_length);
  thd->convert_buffer.shrink(thd->variables.net_buffer_length);
1914 1915 1916 1917 1918 1919

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1920 1921 1922 1923 1924 1925
/****************************************************************************
** mysql_execute_command
** Execute command saved in thd and current_lex->sql_command
****************************************************************************/

void
1926
mysql_execute_command(THD *thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1927
{
1928
  int	res= 0;
1929
  LEX	*lex= thd->lex;
1930 1931
  bool slave_fake_lock= 0;
  MYSQL_LOCK *fake_prev_lock= 0;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1932
  SELECT_LEX *select_lex= &lex->select_lex;
1933
  TABLE_LIST *tables= (TABLE_LIST*) select_lex->table_list.first;
1934
  SELECT_LEX_UNIT *unit= &lex->unit;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1935 1936
  DBUG_ENTER("mysql_execute_command");

1937 1938 1939 1940 1941 1942
  /*
    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.
  */
1943
  if (tables || &lex->select_lex != lex->all_selects_list)
1944 1945
    mysql_reset_errors(thd);

monty@mishka.local's avatar
monty@mishka.local committed
1946 1947 1948 1949
  /*
    When subselects or time_zone info is used in a query
    we create a new TABLE_LIST containing all referenced tables
    and set local variable 'tables' to point to this list.
1950
  */
1951 1952 1953 1954 1955
  if ((&lex->select_lex != lex->all_selects_list ||
       lex->time_zone_tables_used) &&
      lex->unit.create_total_list(thd, lex, &tables))
    DBUG_VOID_RETURN;

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1956
#ifdef HAVE_REPLICATION
1957 1958
  if (thd->slave_thread)
  {
1959
    if (lex->sql_command == SQLCOM_UPDATE_MULTI)
1960 1961 1962
    {
      DBUG_PRINT("info",("need faked locked tables"));
      
1963 1964
      if (check_multi_update_lock(thd, tables, &select_lex->item_list,
				  select_lex))
1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975
        goto error;

      /* Fix for replication, the tables are opened and locked,
         now we pretend that we have performed a LOCK TABLES action */
	 
      fake_prev_lock= thd->locked_tables;
      if (thd->lock)
        thd->locked_tables= thd->lock;
      thd->lock= 0;
      slave_fake_lock= 1;
    }
peter@mysql.com's avatar
peter@mysql.com committed
1976
    /*
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
1977
      Skip if we are in the slave thread, some table rules have been
1978 1979 1980 1981
      given and the table list says the query should not be replicated.
      Exception is 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
1982
    */
1983 1984 1985
    if (!(lex->sql_command == SQLCOM_DROP_TABLE &&
          lex->drop_temporary && lex->drop_if_exists) &&
        all_tables_not_ok(thd,tables))
guilhem@mysql.com's avatar
guilhem@mysql.com committed
1986 1987 1988
    {
      /* we warn the slave SQL thread */
      my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
1989
      DBUG_VOID_RETURN;
guilhem@mysql.com's avatar
guilhem@mysql.com committed
1990
    }
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
1991 1992
#ifndef TO_BE_DELETED
    /*
1993 1994 1995
      This is a workaround to deal with the shortcoming in 3.23.44-3.23.46
      masters in RELEASE_LOCK() logging. We re-write SELECT RELEASE_LOCK()
      as DO RELEASE_LOCK()
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
1996
    */
1997 1998 1999
    if (lex->sql_command == SQLCOM_SELECT)
    {
      lex->sql_command = SQLCOM_DO;
2000
      lex->insert_list = &select_lex->item_list;
2001
    }
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
2002
#endif
2003
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
2004
#endif /* !HAVE_REPLICATION */
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
2005

2006 2007 2008 2009 2010
  /*
    When option readonly is set deny operations which change tables.
    Except for the replication thread and the 'super' users.
  */
  if (opt_readonly &&
monty@mysql.com's avatar
monty@mysql.com committed
2011
      !(thd->slave_thread || (thd->master_access & SUPER_ACL)) &&
2012 2013
      (uc_update_queries[lex->sql_command] > 0))
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
2014
    net_printf(thd, ER_OPTION_PREVENTS_STATEMENT, "--read-only");
2015 2016
    DBUG_VOID_RETURN;
  }
2017

2018
  statistic_increment(com_stat[lex->sql_command],&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2019 2020 2021
  switch (lex->sql_command) {
  case SQLCOM_SELECT:
  {
2022 2023 2024 2025 2026 2027 2028
    /* assign global limit variable if limit is not given */
    {
      SELECT_LEX *param= lex->unit.global_parameters;
      if (!param->explicit_limit)
	param->select_limit= thd->variables.select_limit;
    }

2029
    select_result *result=lex->result;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2030 2031 2032 2033 2034
    if (tables)
    {
      res=check_table_access(thd,
			     lex->exchange ? SELECT_ACL | FILE_ACL :
			     SELECT_ACL,
hf@deer.(none)'s avatar
hf@deer.(none) committed
2035
			     tables,0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2036 2037 2038
    }
    else
      res=check_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
hf@deer.(none)'s avatar
hf@deer.(none) committed
2039
		       any_db,0,0,0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2040 2041 2042 2043 2044
    if (res)
    {
      res=0;
      break;					// Error message is given
    }
2045 2046 2047 2048
    /* 
       In case of single SELECT unit->global_parameters points on first SELECT
       TODO: move counters to SELECT_LEX
    */
2049 2050 2051
    unit->offset_limit_cnt= (ha_rows) unit->global_parameters->offset_limit;
    unit->select_limit_cnt= (ha_rows) (unit->global_parameters->select_limit+
      unit->global_parameters->offset_limit);
peter@mysql.com's avatar
peter@mysql.com committed
2052
    if (unit->select_limit_cnt <
2053
	(ha_rows) unit->global_parameters->select_limit)
2054
      unit->select_limit_cnt= HA_POS_ERROR;		// no limit
2055
    if (unit->select_limit_cnt == HA_POS_ERROR && !select_lex->next_select())
2056
      select_lex->options&= ~OPTION_FOUND_ROWS;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2057 2058

    if (!(res=open_and_lock_tables(thd,tables)))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2059
    {
2060
      if (lex->describe)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2061
      {
2062 2063 2064 2065 2066 2067 2068
	if (!(result= new select_send()))
	{
	  send_error(thd, ER_OUT_OF_RESOURCES);
	  DBUG_VOID_RETURN;
	}
	else
	  thd->send_explain_fields(result);
2069
	res= mysql_explain_union(thd, &thd->lex->unit, result);
2070 2071 2072 2073 2074
	if (lex->describe & DESCRIBE_EXTENDED)
	{
	  char buff[1024];
	  String str(buff,(uint32) sizeof(buff), system_charset_info);
	  str.length(0);
2075
	  thd->lex->unit.print(&str);
2076 2077 2078 2079
	  str.append('\0');
	  push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
		       ER_YES, str.ptr());
	}
2080
	result->send_eof();
2081
        delete result;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2082 2083 2084
      }
      else
      {
2085
	if (!result && !(result= new select_send()))
2086
	{
2087 2088
	  res= -1;
	  break;
2089
	}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2090
	query_cache_store_query(thd, tables);
2091 2092 2093
	res= handle_select(thd, lex, result);
        if (result != lex->result)
          delete result;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2094
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2095
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2096 2097
    break;
  }
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2098
  case SQLCOM_PREPARE:
2099
  {
2100 2101 2102 2103
    char *query_str;
    uint query_len;
    if (lex->prepared_stmt_code_is_varref)
    {
2104
      /* This is PREPARE stmt FROM @var. */
2105 2106 2107 2108
      String str;
      CHARSET_INFO *to_cs= thd->variables.collation_connection;
      bool need_conversion;
      user_var_entry *entry;
2109
      String *pstr= &str;
2110
      uint32 unused;
2111
      /*
2112 2113 2114
        Convert @var contents to string in connection character set. Although
        it is known that int/real/NULL value cannot be a valid query we still
        convert it for error messages to uniform.
2115
      */
2116 2117
      if ((entry=
             (user_var_entry*)hash_search(&thd->user_vars,
2118 2119 2120 2121
                                          (byte*)lex->prepared_stmt_code.str,
                                          lex->prepared_stmt_code.length))
          && entry->value)
      {
2122 2123
        my_bool is_var_null;
        pstr= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC);
2124 2125 2126 2127
        /*
          NULL value of variable checked early as entry->value so here
          we can't get NULL in normal conditions
        */
2128 2129
        DBUG_ASSERT(!is_var_null);
        if (!pstr)
2130 2131 2132 2133
        {
          res= -1;
          break;      // EOM (error should be reported by allocator)
        }
2134 2135
      }
      else
2136 2137 2138 2139 2140
      {
        /*
          variable absent or equal to NULL, so we need to set variable to
          something reasonable to get readable error message during parsing
        */
2141
        str.set("NULL", 4, &my_charset_latin1);
2142 2143
      }

2144
      need_conversion=
2145 2146
        String::needs_conversion(pstr->length(), pstr->charset(),
                                 to_cs, &unused);
2147

2148 2149
      query_len= need_conversion? (pstr->length() * to_cs->mbmaxlen) :
                                  pstr->length();
2150
      if (!(query_str= alloc_root(thd->mem_root, query_len+1)))
2151 2152 2153 2154
      {
        res= -1;
        break;        // EOM (error should be reported by allocator)
      }
2155

2156
      if (need_conversion)
2157 2158 2159 2160 2161 2162
      {
        uint dummy_errors;
        query_len= copy_and_convert(query_str, query_len, to_cs,
                                    pstr->ptr(), pstr->length(),
                                    pstr->charset(), &dummy_errors);
      }
2163
      else
2164
        memcpy(query_str, pstr->ptr(), pstr->length());
2165
      query_str[query_len]= 0;
2166 2167 2168
    }
    else
    {
2169 2170
      query_str= lex->prepared_stmt_code.str;
      query_len= lex->prepared_stmt_code.length;
2171
      DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n",
2172 2173
                          lex->prepared_stmt_name.length,
                          lex->prepared_stmt_name.str,
2174
                          query_len, query_str));
2175
    }
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2176
    thd->command= COM_PREPARE;
2177 2178
    if (!mysql_stmt_prepare(thd, query_str, query_len + 1,
                            &lex->prepared_stmt_name))
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2179 2180 2181 2182 2183
      send_ok(thd, 0L, 0L, "Statement prepared");
    break;
  }
  case SQLCOM_EXECUTE:
  {
2184
    DBUG_PRINT("info", ("EXECUTE: %.*s\n",
2185 2186 2187
                        lex->prepared_stmt_name.length,
                        lex->prepared_stmt_name.str));
    mysql_sql_stmt_execute(thd, &lex->prepared_stmt_name);
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2188 2189 2190 2191 2192
    lex->prepared_stmt_params.empty();
    break;
  }
  case SQLCOM_DEALLOCATE_PREPARE:
  {
2193 2194 2195 2196
    Statement* stmt;
    DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n", 
                        lex->prepared_stmt_name.length,
                        lex->prepared_stmt_name.str));
2197 2198
    /* We account deallocate in the same manner as mysql_stmt_close */
    statistic_increment(com_stmt_close, &LOCK_status);
2199
    if ((stmt= thd->stmt_map.find_by_name(&lex->prepared_stmt_name)))
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2200
    {
2201 2202
      thd->stmt_map.erase(stmt);
      send_ok(thd);
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2203
    }
2204
    else
2205 2206 2207 2208 2209 2210
    {
      res= -1;
      my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0),
               lex->prepared_stmt_name.length, lex->prepared_stmt_name.str,
               "DEALLOCATE PREPARE");
    }
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2211 2212
    break;
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2213
  case SQLCOM_DO:
hf@deer.(none)'s avatar
hf@deer.(none) committed
2214
    if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
2215 2216 2217 2218 2219 2220
		   (res= open_and_lock_tables(thd,tables))))
	break;

    res= mysql_do(thd, *lex->insert_list);
    if (thd->net.report_error)
      res= -1;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2221 2222
    break;

2223
  case SQLCOM_EMPTY_QUERY:
2224
    send_ok(thd);
2225 2226
    break;

2227 2228 2229 2230
  case SQLCOM_HELP:
    res= mysqld_help(thd,lex->help_arg);
    break;

2231
#ifndef EMBEDDED_LIBRARY
2232
  case SQLCOM_PURGE:
2233
  {
2234
    if (check_global_access(thd, SUPER_ACL))
2235
      goto error;
2236
    // PURGE MASTER LOGS TO 'file'
2237 2238 2239
    res = purge_master_logs(thd, lex->to_log);
    break;
  }
2240 2241 2242 2243 2244 2245 2246 2247
  case SQLCOM_PURGE_BEFORE:
  {
    if (check_global_access(thd, SUPER_ACL))
      goto error;
    // PURGE MASTER LOGS BEFORE 'data'
    res = purge_master_logs_before_date(thd, lex->purge_time);
    break;
  }
2248
#endif
2249 2250
  case SQLCOM_SHOW_WARNS:
  {
2251 2252
    res= mysqld_show_warnings(thd, (ulong)
			      ((1L << (uint) MYSQL_ERROR::WARN_LEVEL_NOTE) |
2253 2254 2255
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_WARN) |
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)
			       ));
2256 2257 2258 2259
    break;
  }
  case SQLCOM_SHOW_ERRORS:
  {
2260 2261
    res= mysqld_show_warnings(thd, (ulong)
			      (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
2262 2263
    break;
  }
2264 2265
  case SQLCOM_SHOW_NEW_MASTER:
  {
2266
    if (check_global_access(thd, REPL_SLAVE_ACL))
2267
      goto error;
2268
    /* This query don't work now. See comment in repl_failsafe.cc */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2269
#ifndef WORKING_NEW_MASTER
2270
    net_printf(thd, ER_NOT_SUPPORTED_YET, "SHOW NEW MASTER");
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2271 2272
    res= 1;
#else
2273
    res = show_new_master(thd);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2274
#endif
2275 2276
    break;
  }
2277

2278
#ifdef HAVE_REPLICATION
2279 2280
  case SQLCOM_SHOW_SLAVE_HOSTS:
  {
2281
    if (check_global_access(thd, REPL_SLAVE_ACL))
2282 2283 2284 2285
      goto error;
    res = show_slave_hosts(thd);
    break;
  }
2286 2287
  case SQLCOM_SHOW_BINLOG_EVENTS:
  {
2288
    if (check_global_access(thd, REPL_SLAVE_ACL))
2289 2290 2291 2292
      goto error;
    res = show_binlog_events(thd);
    break;
  }
2293 2294
#endif

2295
  case SQLCOM_BACKUP_TABLE:
2296 2297
  {
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2298
	check_table_access(thd,SELECT_ACL, tables,0) ||
2299
	check_global_access(thd, FILE_ACL))
2300
      goto error; /* purecov: inspected */
2301
    thd->enable_slow_log= opt_log_slow_admin_statements;
2302
    res = mysql_backup_table(thd, tables);
2303

2304 2305
    break;
  }
2306
  case SQLCOM_RESTORE_TABLE:
2307 2308
  {
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2309
	check_table_access(thd, INSERT_ACL, tables,0) ||
2310
	check_global_access(thd, FILE_ACL))
2311
      goto error; /* purecov: inspected */
2312
    thd->enable_slow_log= opt_log_slow_admin_statements;
2313 2314 2315
    res = mysql_restore_table(thd, tables);
    break;
  }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2316 2317 2318
  case SQLCOM_ASSIGN_TO_KEYCACHE:
  {
    if (check_db_used(thd, tables) ||
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2319 2320
        check_access(thd, INDEX_ACL, tables->db,
                     &tables->grant.privilege, 0, 0))
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2321
      goto error;
2322
    res= mysql_assign_to_keycache(thd, tables, &lex->name_and_length);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2323 2324
    break;
  }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2325 2326 2327
  case SQLCOM_PRELOAD_KEYS:
  {
    if (check_db_used(thd, tables) ||
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2328 2329
	check_access(thd, INDEX_ACL, tables->db,
                     &tables->grant.privilege, 0, 0))
2330
      goto error;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2331 2332 2333
    res = mysql_preload_keys(thd, tables);
    break;
  }
2334
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2335
  case SQLCOM_CHANGE_MASTER:
2336
  {
2337
    if (check_global_access(thd, SUPER_ACL))
2338
      goto error;
2339
    pthread_mutex_lock(&LOCK_active_mi);
2340
    res = change_master(thd,active_mi);
2341
    pthread_mutex_unlock(&LOCK_active_mi);
2342 2343
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2344
  case SQLCOM_SHOW_SLAVE_STAT:
2345
  {
2346 2347
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2348
      goto error;
2349
    pthread_mutex_lock(&LOCK_active_mi);
2350
    res = show_master_info(thd,active_mi);
2351
    pthread_mutex_unlock(&LOCK_active_mi);
2352 2353
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2354
  case SQLCOM_SHOW_MASTER_STAT:
2355
  {
2356 2357
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2358 2359 2360 2361
      goto error;
    res = show_binlog_info(thd);
    break;
  }
peter@mysql.com's avatar
peter@mysql.com committed
2362

2363
  case SQLCOM_LOAD_MASTER_DATA: // sync with master
2364
    if (check_global_access(thd, SUPER_ACL))
2365
      goto error;
2366 2367 2368 2369
    if (end_active_trans(thd))
      res= -1;
    else
      res = load_master_data(thd);
2370
    break;
2371
#endif /* HAVE_REPLICATION */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2372 2373 2374
#ifdef HAVE_INNOBASE_DB
  case SQLCOM_SHOW_INNODB_STATUS:
    {
2375
      if (check_global_access(thd, SUPER_ACL))
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2376 2377 2378 2379 2380
	goto error;
      res = innodb_show_status(thd);
      break;
    }
#endif
2381
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2382
  case SQLCOM_LOAD_MASTER_TABLE:
2383
  {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2384 2385
    if (!tables->db)
      tables->db=thd->db;
hf@deer.(none)'s avatar
hf@deer.(none) committed
2386
    if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege,0,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2387 2388 2389 2390
      goto error;				/* purecov: inspected */
    if (grant_option)
    {
      /* Check that the first table has CREATE privilege */
2391
      if (check_grant(thd, CREATE_ACL, tables, 0, 1, 0))
2392
	goto error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2393
    }
2394
    if (strlen(tables->real_name) > NAME_LEN)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2395
    {
2396
      net_printf(thd,ER_WRONG_TABLE_NAME, tables->real_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2397 2398
      break;
    }
2399
    pthread_mutex_lock(&LOCK_active_mi);
2400 2401 2402 2403
    /*
      fetch_master_table will send the error to the client on failure.
      Give error if the table already exists.
    */
2404
    if (!fetch_master_table(thd, tables->db, tables->real_name,
2405
			    active_mi, 0, 0))
2406
    {
2407
      send_ok(thd);
2408
    }
2409
    pthread_mutex_unlock(&LOCK_active_mi);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2410
    break;
2411
  }
2412
#endif /* HAVE_REPLICATION */
2413

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2414
  case SQLCOM_CREATE_TABLE:
2415
  {
2416
    /* If CREATE TABLE of non-temporary table, do implicit commit */
2417 2418 2419 2420 2421 2422 2423 2424 2425 2426
    if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
    {
      if (end_active_trans(thd))
      {
	res= -1;
	break;
      }
    }
    else 
    {
2427 2428
      /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
      thd->options|= OPTION_STATUS_NO_TRANS_UPDATE;
2429
    }
monty@mysql.com's avatar
monty@mysql.com committed
2430
    /* Skip first table, which is the table we are creating */
2431 2432 2433
    TABLE_LIST *create_table, *create_table_local;
    tables= lex->unlink_first_table(tables, &create_table,
				    &create_table_local);
monty@mysql.com's avatar
monty@mysql.com committed
2434

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2435 2436 2437
    if ((res= create_table_precheck(thd, tables, create_table)))
      goto unsent_create_error;

2438 2439 2440
#ifndef HAVE_READLINK
    lex->create_info.data_file_name=lex->create_info.index_file_name=0;
#else
2441
    /* Fix names if symlinked tables */
2442
    if (append_file_to_dir(thd, &lex->create_info.data_file_name,
monty@mysql.com's avatar
monty@mysql.com committed
2443
			   create_table->real_name) ||
2444
	append_file_to_dir(thd,&lex->create_info.index_file_name,
monty@mysql.com's avatar
monty@mysql.com committed
2445
			   create_table->real_name))
2446 2447
    {
      res=-1;
2448
      goto unsent_create_error;
2449
    }
2450
#endif
2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463
    /*
      If we are using SET CHARSET without DEFAULT, add an implicite
      DEFAULT to not confuse old users. (This may change).
    */
    if ((lex->create_info.used_fields & 
	 (HA_CREATE_USED_DEFAULT_CHARSET | HA_CREATE_USED_CHARSET)) ==
	HA_CREATE_USED_CHARSET)
    {
      lex->create_info.used_fields&= ~HA_CREATE_USED_CHARSET;
      lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
      lex->create_info.default_table_charset= lex->create_info.table_charset;
      lex->create_info.table_charset= 0;
    }
2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479
    /*
      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.
    */
    if (wait_if_global_read_lock(thd, 0, 1))
    {
      res= -1;
2480
      goto unsent_create_error;
2481
    }
2482
    if (select_lex->item_list.elements)		// With select
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2483 2484 2485
    {
      select_result *result;

2486
      select_lex->options|= SELECT_NO_UNLOCK;
2487 2488 2489 2490
      unit->offset_limit_cnt= select_lex->offset_limit;
      unit->select_limit_cnt= select_lex->select_limit+
	select_lex->offset_limit;
      if (unit->select_limit_cnt < select_lex->select_limit)
monty@mysql.com's avatar
monty@mysql.com committed
2491
	unit->select_limit_cnt= HA_POS_ERROR;	// No limit
2492

monty@mysql.com's avatar
monty@mysql.com committed
2493
      if (!(res=open_and_lock_tables(thd,tables)))
2494
      {
monty@mysql.com's avatar
monty@mysql.com committed
2495 2496 2497 2498
	res= -1;				// If error
        if ((result=new select_create(create_table->db,
                                      create_table->real_name,
				      &lex->create_info,
2499 2500
                                      lex->create_list,
                                      lex->key_list,
2501 2502
                                      select_lex->item_list, lex->duplicates,
                                      lex->ignore)))
2503 2504 2505 2506 2507 2508
        {
          /*
            CREATE from SELECT give its SELECT_LEX for SELECT,
            and item_list belong to SELECT
          */
          select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
2509
          res=handle_select(thd, lex, result);
2510 2511
          select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE;
        }
2512 2513 2514
	//reset for PS
	lex->create_list.empty();
	lex->key_list.empty();
2515 2516
      }
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2517 2518
    else // regular create
    {
venu@myvenu.com's avatar
venu@myvenu.com committed
2519
      if (lex->name)
monty@mysql.com's avatar
monty@mysql.com committed
2520
        res= mysql_create_like_table(thd, create_table, &lex->create_info, 
venu@myvenu.com's avatar
venu@myvenu.com committed
2521 2522
                                     (Table_ident *)lex->name); 
      else
2523
      {
monty@mysql.com's avatar
monty@mysql.com committed
2524 2525
        res= mysql_create_table(thd,create_table->db,
			         create_table->real_name, &lex->create_info,
venu@myvenu.com's avatar
venu@myvenu.com committed
2526
			         lex->create_list,
2527
			         lex->key_list,0,0);
2528
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2529
      if (!res)
2530
	send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2531
    }
2532 2533 2534 2535 2536
    /*
      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);
2537

2538 2539
unsent_create_error:
    // put tables back for PS rexecuting
2540 2541
    tables= lex->link_first_table_back(tables, create_table,
				       create_table_local);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2542
    break;
2543
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2544
  case SQLCOM_CREATE_INDEX:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2545
    if (check_one_table_access(thd, INDEX_ACL, tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2546
      goto error; /* purecov: inspected */
2547
    thd->enable_slow_log= opt_log_slow_admin_statements;
2548 2549 2550 2551
    if (end_active_trans(thd))
      res= -1;
    else
      res = mysql_create_index(thd, tables, lex->key_list);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2552 2553
    break;

2554
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2555
  case SQLCOM_SLAVE_START:
2556
  {
2557
    pthread_mutex_lock(&LOCK_active_mi);
2558
    start_slave(thd,active_mi,1 /* net report*/);
2559
    pthread_mutex_unlock(&LOCK_active_mi);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2560
    break;
2561
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2562
  case SQLCOM_SLAVE_STOP:
2563 2564 2565 2566 2567 2568
  /*
    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,
2569
      so it waits for the client thread because t is locked by it.
2570
    - then the client thread does SLAVE STOP.
2571 2572
      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.
2573 2574 2575 2576 2577
    To prevent that, refuse SLAVE STOP if the
    client thread has locked tables
  */
  if (thd->locked_tables || thd->active_transaction())
  {
2578
    send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2579
    break;
2580
  }
2581
  {
2582
    pthread_mutex_lock(&LOCK_active_mi);
2583
    stop_slave(thd,active_mi,1/* net report*/);
2584
    pthread_mutex_unlock(&LOCK_active_mi);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2585
    break;
2586
  }
2587
#endif /* HAVE_REPLICATION */
2588

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2589 2590
  case SQLCOM_ALTER_TABLE:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
2591
    send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2592 2593 2594
    break;
#else
    {
2595
      ulong priv=0;
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
2596
      if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2597
      {
2598
	net_printf(thd, ER_WRONG_TABLE_NAME, lex->name);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2599
	res= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2600 2601
	break;
      }
2602 2603
      if (!select_lex->db)
	select_lex->db=tables->db;
hf@deer.(none)'s avatar
hf@deer.(none) committed
2604 2605
      if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege,0,0) ||
	  check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0)||
peter@mysql.com's avatar
peter@mysql.com committed
2606
	  check_merge_table_access(thd, tables->db,
2607 2608 2609
				   (TABLE_LIST *)
				   lex->create_info.merge_list.first))
	goto error;				/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2610 2611
      if (grant_option)
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2612
	if (check_grant(thd, ALTER_ACL, tables, 0, UINT_MAX, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2613 2614 2615 2616 2617 2618
	  goto error;
	if (lex->name && !test_all_bits(priv,INSERT_ACL | CREATE_ACL))
	{					// Rename of table
	  TABLE_LIST tmp_table;
	  bzero((char*) &tmp_table,sizeof(tmp_table));
	  tmp_table.real_name=lex->name;
2619
	  tmp_table.db=select_lex->db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2620
	  tmp_table.grant.privilege=priv;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2621 2622
	  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
2623 2624 2625
	    goto error;
	}
      }
2626 2627
      /* Don't yet allow changing of symlinks with ALTER TABLE */
      lex->create_info.data_file_name=lex->create_info.index_file_name=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2628
      /* ALTER TABLE ends previous transaction */
2629
      if (end_active_trans(thd))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2630 2631
	res= -1;
      else
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2632
      {
2633
        thd->enable_slow_log= opt_log_slow_admin_statements;
2634
	res= mysql_alter_table(thd, select_lex->db, lex->name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2635 2636
			       &lex->create_info,
			       tables, lex->create_list,
2637
			       lex->key_list,
2638
			       select_lex->order_list.elements,
2639
                               (ORDER *) select_lex->order_list.first,
2640
			       lex->duplicates, lex->ignore, &lex->alter_info);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2641
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2642 2643
      break;
    }
hf@deer.(none)'s avatar
hf@deer.(none) committed
2644
#endif /*DONT_ALLOW_SHOW_COMMANDS*/
2645
  case SQLCOM_RENAME_TABLE:
2646 2647 2648
  {
    TABLE_LIST *table;
    if (check_db_used(thd,tables))
2649
      goto error;
2650 2651
    for (table=tables ; table ; table=table->next->next)
    {
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2652
      if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
hf@deer.(none)'s avatar
hf@deer.(none) committed
2653
		       &table->grant.privilege,0,0) ||
2654
	  check_access(thd, INSERT_ACL | CREATE_ACL, table->next->db,
hf@deer.(none)'s avatar
hf@deer.(none) committed
2655
		       &table->next->grant.privilege,0,0))
2656 2657 2658
	goto error;
      if (grant_option)
      {
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2659
	TABLE_LIST old_list,new_list;
2660 2661 2662 2663
	/*
	  we do not need initialize old_list and new_list because we will
	  come table[0] and table->next[0] there
	*/
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2664 2665 2666
	old_list=table[0];
	new_list=table->next[0];
	old_list.next=new_list.next=0;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2667
	if (check_grant(thd, ALTER_ACL, &old_list, 0, UINT_MAX, 0) ||
2668
	    (!test_all_bits(table->next->grant.privilege,
2669
			    INSERT_ACL | CREATE_ACL) &&
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2670 2671
	     check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0,
			 UINT_MAX, 0)))
2672 2673 2674
	  goto error;
      }
    }
2675
    query_cache_invalidate3(thd, tables, 0);
2676 2677 2678
    if (end_active_trans(thd))
      res= -1;
    else if (mysql_rename_tables(thd,tables))
2679 2680
      res= -1;
    break;
2681
  }
2682
#ifndef EMBEDDED_LIBRARY
2683 2684
  case SQLCOM_SHOW_BINLOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
2685
    send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
2686 2687 2688
    DBUG_VOID_RETURN;
#else
    {
2689
      if (check_global_access(thd, SUPER_ACL))
2690 2691 2692 2693
	goto error;
      res = show_binlogs(thd);
      break;
    }
peter@mysql.com's avatar
peter@mysql.com committed
2694
#endif
2695
#endif /* EMBEDDED_LIBRARY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2696
  case SQLCOM_SHOW_CREATE:
2697
#ifdef DONT_ALLOW_SHOW_COMMANDS
2698
    send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
2699 2700
    DBUG_VOID_RETURN;
#else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2701
    {
2702 2703
      if (check_db_used(thd, tables) ||
	  check_access(thd, SELECT_ACL | EXTRA_ACL, tables->db,
hf@deer.(none)'s avatar
hf@deer.(none) committed
2704
		       &tables->grant.privilege,0,0))
2705
	goto error;
2706 2707 2708
      if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
	goto error;
      res= mysqld_show_create(thd, tables);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2709 2710
      break;
    }
2711
#endif
2712 2713 2714
  case SQLCOM_CHECKSUM:
  {
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2715
	check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0))
2716
      goto error; /* purecov: inspected */
2717
    res = mysql_checksum_table(thd, tables, &lex->check_opt);
2718 2719
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2720
  case SQLCOM_REPAIR:
2721 2722
  {
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2723
	check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
2724
      goto error; /* purecov: inspected */
2725
    thd->enable_slow_log= opt_log_slow_admin_statements;
2726
    res = mysql_repair_table(thd, tables, &lex->check_opt);
2727 2728 2729 2730 2731 2732
    /* ! we write after unlocking the table */
    if (!res && !lex->no_write_to_binlog)
    {
      mysql_update_log.write(thd, thd->query, thd->query_length);
      if (mysql_bin_log.is_open())
      {
2733
	thd->clear_error(); // No binlog error generated
2734
        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
2735 2736 2737
        mysql_bin_log.write(&qinfo);
      }
    }
2738 2739
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2740
  case SQLCOM_CHECK:
2741 2742
  {
    if (check_db_used(thd,tables) ||
2743
	check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0))
2744
      goto error; /* purecov: inspected */
2745
    thd->enable_slow_log= opt_log_slow_admin_statements;
2746 2747 2748
    res = mysql_check_table(thd, tables, &lex->check_opt);
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2749 2750
  case SQLCOM_ANALYZE:
  {
2751
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2752
	check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2753
      goto error; /* purecov: inspected */
2754
    thd->enable_slow_log= opt_log_slow_admin_statements;
2755
    res = mysql_analyze_table(thd, tables, &lex->check_opt);
2756 2757 2758 2759 2760 2761
    /* ! we write after unlocking the table */
    if (!res && !lex->no_write_to_binlog)
    {
      mysql_update_log.write(thd, thd->query, thd->query_length);
      if (mysql_bin_log.is_open())
      {
2762
	thd->clear_error(); // No binlog error generated
2763
        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
2764 2765 2766
        mysql_bin_log.write(&qinfo);
      }
    }
2767
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2768
  }
2769

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2770 2771
  case SQLCOM_OPTIMIZE:
  {
2772
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2773
	check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2774
      goto error; /* purecov: inspected */
2775
    thd->enable_slow_log= opt_log_slow_admin_statements;
2776 2777 2778
    res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
      mysql_recreate_table(thd, tables, 1) :
      mysql_optimize_table(thd, tables, &lex->check_opt);
2779 2780 2781 2782 2783 2784
    /* ! we write after unlocking the table */
    if (!res && !lex->no_write_to_binlog)
    {
      mysql_update_log.write(thd, thd->query, thd->query_length);
      if (mysql_bin_log.is_open())
      {
2785
	thd->clear_error(); // No binlog error generated
2786
        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
2787 2788 2789
        mysql_bin_log.write(&qinfo);
      }
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2790 2791 2792
    break;
  }
  case SQLCOM_UPDATE:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2793 2794
    if (update_precheck(thd, tables))
      break;
2795 2796 2797 2798
    res= mysql_update(thd,tables,
                      select_lex->item_list,
                      lex->value_list,
                      select_lex->where,
2799
		      select_lex->order_list.elements,
2800 2801
                      (ORDER *) select_lex->order_list.first,
                      select_lex->select_limit,
2802
                      lex->duplicates, lex->ignore);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2803 2804
    if (thd->net.report_error)
      res= -1;
2805 2806
    break;
  case SQLCOM_UPDATE_MULTI:
monty@mysql.com's avatar
monty@mysql.com committed
2807
  {
2808
    if ((res= multi_update_precheck(thd, tables)))
monty@mysql.com's avatar
monty@mysql.com committed
2809 2810 2811 2812 2813 2814
      break;
    res= mysql_multi_update(thd,tables,
			    &select_lex->item_list,
			    &lex->value_list,
			    select_lex->where,
			    select_lex->options,
2815
			    lex->duplicates, lex->ignore, unit, select_lex);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2816
    break;
monty@mysql.com's avatar
monty@mysql.com committed
2817
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2818
  case SQLCOM_REPLACE:
2819 2820
  case SQLCOM_INSERT:
  {
2821
    if ((res= insert_precheck(thd, tables)))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2822
      break;
2823 2824 2825
    res= mysql_insert(thd,tables,lex->field_list,lex->many_values,
                      lex->update_list, lex->value_list,
                      lex->duplicates, lex->ignore);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2826 2827
    if (thd->net.report_error)
      res= -1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2828
    break;
2829
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2830 2831 2832
  case SQLCOM_REPLACE_SELECT:
  case SQLCOM_INSERT_SELECT:
  {
2833
    TABLE_LIST *first_local_table= (TABLE_LIST *) select_lex->table_list.first;
monty@mishka.local's avatar
monty@mishka.local committed
2834 2835
    TABLE_LIST dup_tables;
    TABLE *insert_table;
2836
    if ((res= insert_precheck(thd, tables)))
2837
      break;
monty@mysql.com's avatar
monty@mysql.com committed
2838

2839 2840
    /* Fix lock for first table */
    if (tables->lock_type == TL_WRITE_DELAYED)
2841
      tables->lock_type= TL_WRITE;
2842

2843 2844
    /* 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
2845 2846

    select_result *result;
2847 2848 2849 2850
    unit->offset_limit_cnt= select_lex->offset_limit;
    unit->select_limit_cnt= select_lex->select_limit+select_lex->offset_limit;
    if (unit->select_limit_cnt < select_lex->select_limit)
      unit->select_limit_cnt= HA_POS_ERROR;		// No limit
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2851

2852
    if (find_real_table_in_list(tables->next, tables->db, tables->real_name))
2853
    {
2854 2855
      /* Using same table for INSERT and SELECT */
      select_lex->options |= OPTION_BUFFER_RESULT;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2856
    }
2857

2858 2859 2860
    if ((res= open_and_lock_tables(thd, tables)))
      break;
      
monty@mishka.local's avatar
monty@mishka.local committed
2861
    insert_table= tables->table;
2862
    /* Skip first table, which is the table we are inserting in */
monty@mysql.com's avatar
monty@mysql.com committed
2863
    select_lex->table_list.first= (byte*) first_local_table->next;
monty@mishka.local's avatar
monty@mishka.local committed
2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878
    tables= (TABLE_LIST *) select_lex->table_list.first;
    dup_tables= *first_local_table;
    first_local_table->next= 0;
    if (select_lex->group_list.elements != 0)
    {
      /*
        When we are using GROUP BY we can't refere to other tables in the
        ON DUPLICATE KEY part
      */         
      dup_tables.next= 0;
    }

    if (!(res= mysql_prepare_insert(thd, tables, first_local_table,
				    &dup_tables, insert_table,
                                    lex->field_list, 0,
2879 2880
				    lex->update_list, lex->value_list,
				    lex->duplicates)) &&
monty@mishka.local's avatar
monty@mishka.local committed
2881
        (result= new select_insert(insert_table, &lex->field_list,
2882
				   &lex->update_list, &lex->value_list,
2883
                                   lex->duplicates, lex->ignore)))
2884
    {
2885 2886 2887 2888 2889 2890 2891 2892 2893
      /*
        insert/replace from SELECT give its SELECT_LEX for SELECT,
        and item_list belong to SELECT
      */
      lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
      res= handle_select(thd, lex, result);
      /* revert changes for SP */
      lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
      delete result;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2894
      if (thd->net.report_error)
2895
        res= -1;
2896 2897 2898
    }
    else
      res= -1;
2899
    insert_table->insert_values= 0;        // Set by mysql_prepare_insert()
monty@mishka.local's avatar
monty@mishka.local committed
2900
    first_local_table->next= tables;
2901
    lex->select_lex.table_list.first= (byte*) first_local_table;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2902 2903
    break;
  }
2904
  case SQLCOM_TRUNCATE:
2905 2906 2907 2908 2909
    if (end_active_trans(thd))
    {
      res= -1;
      break;
    }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2910
    if (check_one_table_access(thd, DELETE_ACL, tables))
2911
      goto error;
2912 2913 2914 2915 2916 2917
    /*
      Don't allow this within a transaction because we want to use
      re-generate table
    */
    if (thd->locked_tables || thd->active_transaction())
    {
2918
      send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION,NullS);
2919 2920
      goto error;
    }
2921
    res=mysql_truncate(thd, tables, 0);
2922
    break;
2923
  case SQLCOM_DELETE:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2924
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2925 2926
    if ((res= delete_precheck(thd, tables)))
      break;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
2927
    res = mysql_delete(thd,tables, select_lex->where,
2928
                       &select_lex->order_list,
2929
                       select_lex->select_limit, select_lex->options);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2930 2931
    if (thd->net.report_error)
      res= -1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2932 2933
    break;
  }
2934
  case SQLCOM_DELETE_MULTI:
2935
  {
2936 2937
    TABLE_LIST *aux_tables=
      (TABLE_LIST *)thd->lex->auxilliary_table_list.first;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2938 2939
    TABLE_LIST *target_tbl;
    uint table_count;
2940
    multi_delete *result;
2941

2942 2943
    if ((res= multi_delete_precheck(thd, tables, &table_count)))
      break;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2944

2945
    /* condition will be TRUE on SP re-excuting */
2946 2947
    if (select_lex->item_list.elements != 0)
      select_lex->item_list.empty();
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2948
    if (add_item_to_list(thd, new Item_null()))
2949
    {
2950
      res= -1;
2951
      break;
2952
    }
2953

2954 2955 2956 2957
    thd->proc_info="init";
    if ((res=open_and_lock_tables(thd,tables)))
      break;
    /* Fix tables-to-be-deleted-from list to point at opened tables */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2958 2959 2960
    for (target_tbl= (TABLE_LIST*) aux_tables;
	 target_tbl;
	 target_tbl= target_tbl->next)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2961
    {
2962 2963 2964
      TABLE_LIST *orig= target_tbl->table_list;
      target_tbl->table= orig->table;
      /*
2965
	 Multi-delete can't be constructed over-union => we always have
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2966
	 single SELECT on top and have to check underlying SELECTs of it
2967
      */
2968 2969
      if (lex->select_lex.check_updateable_in_subqueries(orig->db,
                                                         orig->real_name))
2970
      {
2971 2972 2973 2974
        my_error(ER_UPDATE_TABLE_USED, MYF(0),
                 orig->real_name);
        res= -1;
        break;
2975
      }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2976
    }
2977

2978 2979
    if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables,
							  table_count)))
2980
    {
2981 2982 2983
      res= mysql_select(thd, &select_lex->ref_pointer_array,
			select_lex->get_table_list(),
			select_lex->with_wild,
2984
			select_lex->item_list,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2985
			select_lex->where,
2986
			0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2987 2988
			(ORDER *)NULL,
			select_lex->options | thd->options |
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
2989
			SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK,
2990
			result, unit, select_lex);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2991 2992
      if (thd->net.report_error)
	res= -1;
2993
      delete result;
2994 2995 2996 2997 2998 2999
    }
    else
      res= -1;					// Error is not sent
    close_thread_tables(thd);
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3000
  case SQLCOM_DROP_TABLE:
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3001
  {
3002 3003
    if (!lex->drop_temporary)
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
3004
      if (check_table_access(thd,DROP_ACL,tables,0))
3005 3006 3007 3008 3009 3010 3011
	goto error;				/* purecov: inspected */
      if (end_active_trans(thd))
      {
	res= -1;
	break;
      }
    }
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3012
    else
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3013 3014 3015 3016 3017 3018
    {
      /*
	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
3019 3020
	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
3021 3022 3023
      */
      if (thd->slave_thread)
	lex->drop_if_exists= 1;
3024

3025 3026
      /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
      thd->options|= OPTION_STATUS_NO_TRANS_UPDATE;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3027
    }
3028
    res= mysql_rm_table(thd,tables,lex->drop_if_exists, lex->drop_temporary);
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3029 3030
  }
  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3031
  case SQLCOM_DROP_INDEX:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3032
    if (check_one_table_access(thd, INDEX_ACL, tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3033
      goto error;				/* purecov: inspected */
3034 3035 3036
    if (end_active_trans(thd))
      res= -1;
    else
3037
      res = mysql_drop_index(thd, tables, &lex->alter_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3038 3039
    break;
  case SQLCOM_SHOW_DATABASES:
3040
#if defined(DONT_ALLOW_SHOW_COMMANDS)
3041
    send_error(thd,ER_NOT_ALLOWED_COMMAND);   /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3042 3043 3044
    DBUG_VOID_RETURN;
#else
    if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
3045
	check_global_access(thd, SHOW_DB_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3046 3047 3048 3049 3050
      goto error;
    res= mysqld_show_dbs(thd, (lex->wild ? lex->wild->ptr() : NullS));
    break;
#endif
  case SQLCOM_SHOW_PROCESSLIST:
3051
    if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3052
      break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3053 3054
    mysqld_list_processes(thd,
			  thd->master_access & PROCESS_ACL ? NullS :
monty@mysql.com's avatar
monty@mysql.com committed
3055
			  thd->priv_user,lex->verbose);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3056
    break;
3057 3058
  case SQLCOM_SHOW_STORAGE_ENGINES:
    res= mysqld_show_storage_engines(thd);
3059 3060 3061 3062 3063 3064 3065
    break;
  case SQLCOM_SHOW_PRIVILEGES:
    res= mysqld_show_privileges(thd);
    break;
  case SQLCOM_SHOW_COLUMN_TYPES:
    res= mysqld_show_column_types(thd);
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3066
  case SQLCOM_SHOW_STATUS:
3067
    res= mysqld_show(thd,(lex->wild ? lex->wild->ptr() : NullS),status_vars,
3068
		     OPT_GLOBAL, &LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3069 3070 3071
    break;
  case SQLCOM_SHOW_VARIABLES:
    res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS),
3072 3073
		     init_vars, lex->option_type,
		     &LOCK_global_system_variables);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3074
    break;
tim@cane.mysql.fi's avatar
tim@cane.mysql.fi committed
3075 3076
  case SQLCOM_SHOW_LOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
3077
    send_error(thd,ER_NOT_ALLOWED_COMMAND);	/* purecov: inspected */
tim@cane.mysql.fi's avatar
tim@cane.mysql.fi committed
3078 3079 3080
    DBUG_VOID_RETURN;
#else
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
3081
      if (grant_option && check_access(thd, FILE_ACL, any_db,0,0,0))
tim@cane.mysql.fi's avatar
tim@cane.mysql.fi committed
3082 3083 3084 3085 3086
	goto error;
      res= mysqld_show_logs(thd);
      break;
    }
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3087
  case SQLCOM_SHOW_TABLES:
3088
    /* FALL THROUGH */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3089
#ifdef DONT_ALLOW_SHOW_COMMANDS
3090
    send_error(thd,ER_NOT_ALLOWED_COMMAND);	/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3091 3092 3093
    DBUG_VOID_RETURN;
#else
    {
3094
      char *db=select_lex->db ? select_lex->db : thd->db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3095 3096
      if (!db)
      {
3097
	send_error(thd,ER_NO_DB_ERROR);	/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3098 3099 3100
	goto error;				/* purecov: inspected */
      }
      remove_escape(db);				// Fix escaped '_'
3101
      if (check_db_name(db))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3102
      {
3103
        net_printf(thd,ER_WRONG_DB_NAME, db);
3104
        goto error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3105
      }
hf@deer.(none)'s avatar
hf@deer.(none) committed
3106
      if (check_access(thd,SELECT_ACL,db,&thd->col_access,0,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3107
	goto error;				/* purecov: inspected */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3108
      if (!thd->col_access && check_grant_db(thd,db))
3109
      {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3110
	net_printf(thd, ER_DBACCESS_DENIED_ERROR,
3111 3112 3113 3114 3115
		   thd->priv_user,
		   thd->priv_host,
		   db);
	goto error;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3116
      /* grant is checked in mysqld_show_tables */
3117
      if (lex->describe)
3118
        res= mysqld_extend_show_tables(thd,db,
3119
				       (lex->wild ? lex->wild->ptr() : NullS));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3120 3121 3122 3123 3124 3125
      else
	res= mysqld_show_tables(thd,db,
				(lex->wild ? lex->wild->ptr() : NullS));
      break;
    }
#endif
3126 3127 3128
  case SQLCOM_SHOW_OPEN_TABLES:
    res= mysqld_show_open_tables(thd,(lex->wild ? lex->wild->ptr() : NullS));
    break;
3129 3130
  case SQLCOM_SHOW_CHARSETS:
    res= mysqld_show_charsets(thd,(lex->wild ? lex->wild->ptr() : NullS));
3131
    break;
3132 3133 3134
  case SQLCOM_SHOW_COLLATIONS:
    res= mysqld_show_collations(thd,(lex->wild ? lex->wild->ptr() : NullS));
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3135 3136
  case SQLCOM_SHOW_FIELDS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
3137
    send_error(thd,ER_NOT_ALLOWED_COMMAND);	/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3138 3139 3140
    DBUG_VOID_RETURN;
#else
    {
3141
      char *db=tables->db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3142
      remove_escape(db);			// Fix escaped '_'
3143
      remove_escape(tables->real_name);
3144
      if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
monty@mysql.com's avatar
monty@mysql.com committed
3145
		       &tables->grant.privilege, 0, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3146
	goto error;				/* purecov: inspected */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3147
      if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3148 3149
	goto error;
      res= mysqld_show_fields(thd,tables,
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3150 3151
			      (lex->wild ? lex->wild->ptr() : NullS),
			      lex->verbose);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3152 3153 3154 3155 3156
      break;
    }
#endif
  case SQLCOM_SHOW_KEYS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
3157
    send_error(thd,ER_NOT_ALLOWED_COMMAND);	/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3158 3159 3160
    DBUG_VOID_RETURN;
#else
    {
3161
      char *db=tables->db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3162
      remove_escape(db);			// Fix escaped '_'
3163
      remove_escape(tables->real_name);
monty@mysql.com's avatar
monty@mysql.com committed
3164
      if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
monty@mysql.com's avatar
monty@mysql.com committed
3165
		       &tables->grant.privilege, 0, 0))
monty@mysql.com's avatar
monty@mysql.com committed
3166
	goto error;				/* purecov: inspected */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3167
      if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3168 3169 3170 3171 3172 3173
	goto error;
      res= mysqld_show_keys(thd,tables);
      break;
    }
#endif
  case SQLCOM_CHANGE_DB:
3174
    mysql_change_db(thd,select_lex->db);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3175
    break;
3176

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3177 3178 3179
  case SQLCOM_LOAD:
  {
    uint privilege= (lex->duplicates == DUP_REPLACE ?
3180
		     INSERT_ACL | DELETE_ACL : INSERT_ACL);
3181 3182

    if (!lex->local_file)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3183
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
3184
      if (check_access(thd,privilege | FILE_ACL,tables->db,0,0,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3185 3186 3187 3188
	goto error;
    }
    else
    {
3189
      if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) ||
3190
	  ! opt_local_infile)
3191
      {
3192
	send_error(thd,ER_NOT_ALLOWED_COMMAND);
3193 3194
	goto error;
      }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3195
      if (check_one_table_access(thd, privilege, tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3196 3197 3198
	goto error;
    }
    res=mysql_load(thd, lex->exchange, tables, lex->field_list,
3199
		   lex->duplicates, lex->ignore, (bool) lex->local_file, lex->lock_option);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3200 3201
    break;
  }
3202

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3203
  case SQLCOM_SET_OPTION:
3204 3205
  {
    List<set_var_base> *lex_var_list= &lex->var_list;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3206
    if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
3207 3208
		   (res= open_and_lock_tables(thd,tables))))
      break;
3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222
    if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
    {
      my_printf_error(0, "The SET ONE_SHOT syntax is reserved for \
purposes internal to the MySQL server", MYF(0));
      res= -1;
      break;
    }
    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;
3223
      send_ok(thd);
3224
    }
3225 3226
    if (thd->net.report_error)
      res= -1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3227
    break;
3228
  }
3229

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3230
  case SQLCOM_UNLOCK_TABLES:
3231 3232 3233 3234 3235 3236
    /*
      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.
    */
3237
    unlock_locked_tables(thd);
3238 3239
    if (thd->options & OPTION_TABLE_LOCK)
    {
3240
      end_active_trans(thd);
3241
      thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3242 3243
    }
    if (thd->global_read_lock)
3244
      unlock_global_read_lock(thd);
3245
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3246 3247
    break;
  case SQLCOM_LOCK_TABLES:
3248
    unlock_locked_tables(thd);
3249
    if (check_db_used(thd,tables) || end_active_trans(thd))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3250
      goto error;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3251
    if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, tables,0))
3252
      goto error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3253
    thd->in_lock_tables=1;
3254
    thd->options|= OPTION_TABLE_LOCK;
3255
    if (!(res= open_and_lock_tables(thd, tables)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3256
    {
3257 3258 3259 3260
#ifdef HAVE_QUERY_CACHE
      if (thd->variables.query_cache_wlock_invalidate)
	query_cache.invalidate_locked_for_write(tables);
#endif /*HAVE_QUERY_CACHE*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3261 3262
      thd->locked_tables=thd->lock;
      thd->lock=0;
3263
      send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3264
    }
3265 3266
    else
      thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3267 3268 3269
    thd->in_lock_tables=0;
    break;
  case SQLCOM_CREATE_DB:
3270
  {
3271 3272 3273 3274 3275
    if (end_active_trans(thd))
    {
      res= -1;
      break;
    }
3276
    char *alias;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
3277
    if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3278
    {
3279
      net_printf(thd,ER_WRONG_DB_NAME, lex->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3280 3281
      break;
    }
3282 3283 3284 3285 3286 3287 3288
    /*
      If in a slave thread :
      CREATE DATABASE DB was certainly not preceded by USE DB.
      For that reason, db_ok() in sql/slave.cc did not check the 
      do_db/ignore_db. And as this query involves no tables, tables_ok()
      above was not called. So we have to check rules again here.
    */
3289
#ifdef HAVE_REPLICATION
3290 3291 3292
    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
3293 3294
    {
      my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
3295
      break;
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3296
    }
3297
#endif
hf@deer.(none)'s avatar
hf@deer.(none) committed
3298
    if (check_access(thd,CREATE_ACL,lex->name,0,1,0))
3299
      break;
monty@mysql.com's avatar
monty@mysql.com committed
3300
    res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name),
monty@mysql.com's avatar
monty@mysql.com committed
3301
			 &lex->create_info, 0);
3302 3303
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3304
  case SQLCOM_DROP_DB:
3305
  {
3306 3307 3308 3309 3310
    if (end_active_trans(thd))
    {
      res= -1;
      break;
    }
3311
    char *alias;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
3312
    if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3313
    {
3314
      net_printf(thd, ER_WRONG_DB_NAME, lex->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3315 3316
      break;
    }
3317 3318 3319 3320 3321 3322 3323
    /*
      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.
    */
3324
#ifdef HAVE_REPLICATION
3325 3326 3327
    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
3328 3329
    {
      my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
3330
      break;
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3331
    }
3332
#endif
hf@deer.(none)'s avatar
hf@deer.(none) committed
3333
    if (check_access(thd,DROP_ACL,lex->name,0,1,0))
3334
      break;
3335 3336
    if (thd->locked_tables || thd->active_transaction())
    {
3337
      send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
3338 3339
      goto error;
    }
3340 3341
    res=mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : lex->name),
                    lex->drop_if_exists, 0);
3342 3343
    break;
  }
3344 3345
  case SQLCOM_ALTER_DB:
  {
3346 3347
    char *db= lex->name ? lex->name : thd->db;
    if (!db)
3348
    {
3349 3350 3351 3352 3353 3354
      send_error(thd, ER_NO_DB_ERROR);
      goto error;
    }
    if (!strip_sp(db) || check_db_name(db))
    {
      net_printf(thd, ER_WRONG_DB_NAME, db);
3355 3356
      break;
    }
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3357 3358 3359
    /*
      If in a slave thread :
      ALTER DATABASE DB may not be preceded by USE DB.
3360
      For that reason, maybe db_ok() in sql/slave.cc did not check the
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3361 3362 3363 3364
      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
3365
    if (thd->slave_thread &&
3366 3367
	(!db_ok(db, replicate_do_db, replicate_ignore_db) ||
	 !db_ok_with_wild_table(db)))
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3368 3369 3370 3371 3372
    {
      my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
      break;
    }
#endif
3373
    if (check_access(thd, ALTER_ACL, db, 0, 1, 0))
3374 3375 3376
      break;
    if (thd->locked_tables || thd->active_transaction())
    {
3377
      send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
3378 3379
      goto error;
    }
3380
    res= mysql_alter_db(thd, db, &lex->create_info);
3381 3382
    break;
  }
3383 3384 3385 3386
  case SQLCOM_SHOW_CREATE_DB:
  {
    if (!strip_sp(lex->name) || check_db_name(lex->name))
    {
3387
      net_printf(thd,ER_WRONG_DB_NAME, lex->name);
3388 3389
      break;
    }
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
3390
    if (check_access(thd,SELECT_ACL,lex->name,0,1,0))
3391
      break;
3392
    res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
3393 3394
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3395
  case SQLCOM_CREATE_FUNCTION:
hf@deer.(none)'s avatar
hf@deer.(none) committed
3396
    if (check_access(thd,INSERT_ACL,"mysql",0,1,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3397 3398 3399
      break;
#ifdef HAVE_DLOPEN
    if (!(res = mysql_create_function(thd,&lex->udf)))
3400
      send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3401 3402 3403 3404 3405
#else
    res= -1;
#endif
    break;
  case SQLCOM_DROP_FUNCTION:
hf@deer.(none)'s avatar
hf@deer.(none) committed
3406
    if (check_access(thd,DELETE_ACL,"mysql",0,1,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3407 3408
      break;
#ifdef HAVE_DLOPEN
3409
    if (!(res = mysql_drop_function(thd,&lex->udf.name)))
3410
      send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3411 3412 3413 3414
#else
    res= -1;
#endif
    break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3415
#ifndef NO_EMBEDDED_ACCESS_CHECKS
3416 3417
  case SQLCOM_DROP_USER:
  {
hf@deer.(none)'s avatar
hf@deer.(none) committed
3418
    if (check_access(thd, GRANT_ACL,"mysql",0,1,0))
3419 3420 3421 3422 3423 3424
      break;
    if (!(res= mysql_drop_user(thd, lex->users_list)))
    {
      mysql_update_log.write(thd, thd->query, thd->query_length);
      if (mysql_bin_log.is_open())
      {
3425
	Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3426 3427 3428 3429 3430 3431 3432 3433
	mysql_bin_log.write(&qinfo);
      }
      send_ok(thd);
    }
    break;
  }
  case SQLCOM_REVOKE_ALL:
  {
hf@deer.(none)'s avatar
hf@deer.(none) committed
3434
    if (check_access(thd, GRANT_ACL ,"mysql",0,1,0))
3435 3436 3437 3438 3439 3440
      break;
    if (!(res = mysql_revoke_all(thd, lex->users_list)))
    {
      mysql_update_log.write(thd, thd->query, thd->query_length);
      if (mysql_bin_log.is_open())
      {
3441
	Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3442 3443 3444 3445 3446 3447
	mysql_bin_log.write(&qinfo);
      }
      send_ok(thd);
    }
    break;
  }
3448 3449 3450 3451
  case SQLCOM_REVOKE:
  case SQLCOM_GRANT:
  {
    if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
3452
		     tables ? tables->db : select_lex->db,
3453
		     tables ? &tables->grant.privilege : 0,
3454
		     tables ? 0 : 1, 0))
3455 3456
      goto error;

3457 3458 3459 3460
    /*
      Check that the user isn't trying to change a password for another
      user if he doesn't have UPDATE privilege to the MySQL database
    */
3461 3462 3463 3464 3465 3466 3467 3468 3469 3470

    if (thd->user)				// If not replication
    {
      LEX_USER *user;
      List_iterator <LEX_USER> user_list(lex->users_list);
      while ((user=user_list++))
      {
	if (user->password.str &&
	    (strcmp(thd->user,user->user.str) ||
	     user->host.str &&
3471
	     my_strcasecmp(&my_charset_latin1,
3472
                           user->host.str, thd->host_or_ip)))
3473
	{
3474 3475 3476
	  if (check_access(thd, UPDATE_ACL, "mysql", 0, 1, 1))
	  {
	    send_error(thd, ER_PASSWORD_NOT_ALLOWED);
3477
	    goto error;
3478 3479
	  }
	  break;		  // We are allowed to do global changes
3480 3481 3482
	}
      }
    }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495
    if (specialflag & SPECIAL_NO_RESOLVE)
    {
      LEX_USER *user;
      List_iterator <LEX_USER> user_list(lex->users_list);
      while ((user=user_list++))
      {
	if (hostname_requires_resolving(user->host.str))
	  push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
			      ER_WARN_HOSTNAME_WONT_WORK,
			      ER(ER_WARN_HOSTNAME_WONT_WORK),
			      user->host.str);
      }
    }
3496 3497 3498 3499 3500
    if (tables)
    {
      if (grant_option && check_grant(thd,
				      (lex->grant | lex->grant_tot_col |
				       GRANT_ACL),
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3501
				      tables, 0, UINT_MAX, 0))
3502
	goto error;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3503 3504 3505
      if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns,
				    lex->grant,
				    lex->sql_command == SQLCOM_REVOKE)))
3506
      {
3507
	mysql_update_log.write(thd, thd->query, thd->query_length);
3508 3509
	if (mysql_bin_log.is_open())
	{
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3510
          thd->clear_error();
3511
	  Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3512 3513 3514 3515 3516 3517 3518 3519
	  mysql_bin_log.write(&qinfo);
	}
      }
    }
    else
    {
      if (lex->columns.elements)
      {
3520
	send_error(thd,ER_ILLEGAL_GRANT_FOR_TABLE);
3521 3522 3523 3524 3525 3526 3527
	res=1;
      }
      else
	res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
			  lex->sql_command == SQLCOM_REVOKE);
      if (!res)
      {
3528
	mysql_update_log.write(thd, thd->query, thd->query_length);
3529 3530
	if (mysql_bin_log.is_open())
	{
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3531
          thd->clear_error();
3532
	  Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3533 3534
	  mysql_bin_log.write(&qinfo);
	}
3535
	if (mqh_used && lex->sql_command == SQLCOM_GRANT)
3536
	{
3537 3538 3539
	  List_iterator <LEX_USER> str_list(lex->users_list);
	  LEX_USER *user;
	  while ((user=str_list++))
3540
	    reset_mqh(thd,user);
3541
	}
3542 3543 3544 3545
      }
    }
    break;
  }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3546
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3547
  case SQLCOM_RESET:
3548 3549 3550 3551 3552 3553 3554
    /* 
       RESET commands are never written to the binary log, so we have to
       initialize this variable because RESET shares the same code as FLUSH
    */
    lex->no_write_to_binlog= 1;
  case SQLCOM_FLUSH:
  {
3555
    if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3556
      goto error;
3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574
    /*
      reload_acl_and_cache() will tell us if we are allowed to write to the
      binlog or not.
    */
    bool write_to_binlog;
    if (reload_acl_and_cache(thd, lex->type, tables, &write_to_binlog))
      send_error(thd, 0);
    else
    {
      /*
        We WANT to write and we CAN write.
        ! we write after unlocking the table.
      */
      if (!lex->no_write_to_binlog && write_to_binlog)
      {
        mysql_update_log.write(thd, thd->query, thd->query_length);
        if (mysql_bin_log.is_open())
        {
3575
          Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3576 3577 3578 3579 3580
          mysql_bin_log.write(&qinfo);
        }
      }
      send_ok(thd);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3581
    break;
3582
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3583 3584 3585
  case SQLCOM_KILL:
    kill_one_thread(thd,lex->thread_id);
    break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3586
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3587 3588
  case SQLCOM_SHOW_GRANTS:
    res=0;
3589 3590
    if ((thd->priv_user &&
	 !strcmp(thd->priv_user,lex->grant_user->user.str)) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
3591
	!check_access(thd, SELECT_ACL, "mysql",0,1,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3592 3593 3594 3595
    {
      res = mysql_show_grants(thd,lex->grant_user);
    }
    break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3596
#endif
3597
  case SQLCOM_HA_OPEN:
3598
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
3599
	check_table_access(thd,SELECT_ACL, tables,0))
3600 3601 3602 3603 3604 3605 3606 3607 3608
      goto error;
    res = mysql_ha_open(thd, tables);
    break;
  case SQLCOM_HA_CLOSE:
    if (check_db_used(thd,tables))
      goto error;
    res = mysql_ha_close(thd, tables);
    break;
  case SQLCOM_HA_READ:
3609 3610 3611 3612 3613
    /*
      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.
    */
serg@serg.mylan's avatar
serg@serg.mylan committed
3614
    if (check_db_used(thd,tables))
3615
      goto error;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3616
    res = mysql_ha_read(thd, tables, lex->ha_read_mode, lex->backup_dir,
3617 3618
			lex->insert_list, lex->ha_rkey_mode, select_lex->where,
			select_lex->select_limit, select_lex->offset_limit);
3619 3620
    break;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3621
  case SQLCOM_BEGIN:
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3622 3623 3624 3625 3626 3627
    if (thd->locked_tables)
    {
      thd->lock=thd->locked_tables;
      thd->locked_tables=0;			// Will be automaticly closed
      close_thread_tables(thd);			// Free tables
    }
3628 3629 3630 3631 3632 3633
    if (end_active_trans(thd))
    {
      res= -1;
    }
    else
    {
3634
      thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
3635 3636
		     OPTION_BEGIN);
      thd->server_status|= SERVER_STATUS_IN_TRANS;
3637 3638 3639
      if (!(lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) ||
          !(res= ha_start_consistent_snapshot(thd)))
        send_ok(thd);
3640
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3641 3642
    break;
  case SQLCOM_COMMIT:
3643 3644 3645 3646 3647
    /*
      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...)
    */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3648
  {
3649
    thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3650 3651
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
    if (!ha_commit(thd))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3652
    {
3653
      send_ok(thd);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3654
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3655 3656 3657
    else
      res= -1;
    break;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3658
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3659 3660 3661
  case SQLCOM_ROLLBACK:
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
    if (!ha_rollback(thd))
3662
    {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3663 3664 3665 3666 3667 3668 3669 3670 3671 3672
      /*
        If a non-transactional table was updated, warn; don't warn if this is a
        slave thread (because when a slave thread executes a ROLLBACK, it has
        been read from the binary log, so it's 100% sure and normal to produce
        error ER_WARNING_NOT_COMPLETE_ROLLBACK. If we sent the warning to the
        slave SQL thread, it would not stop the thread but just be printed in
        the error log; but we don't want users to wonder why they have this
        message in the error log, so we don't send it.
      */
      if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread)
3673
	send_warning(thd,ER_WARNING_NOT_COMPLETE_ROLLBACK,0);
3674
      else
3675
	send_ok(thd);
3676
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3677 3678
    else
      res= -1;
3679
    thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3680
    break;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3681 3682 3683
  case SQLCOM_ROLLBACK_TO_SAVEPOINT:
    if (!ha_rollback_to_savepoint(thd, lex->savepoint_name))
    {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3684
      if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3685
	send_warning(thd, ER_WARNING_NOT_COMPLETE_ROLLBACK, 0);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3686
      else
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3687
	send_ok(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3688 3689 3690 3691
    }
    else
      res= -1;
    break;
3692
  case SQLCOM_SAVEPOINT:
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3693
    if (!ha_savepoint(thd, lex->savepoint_name))
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3694
      send_ok(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3695 3696
    else
      res= -1;
3697
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3698
  default:					/* Impossible */
3699
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3700 3701 3702
    break;
  }
  thd->proc_info="query end";			// QQ
3703
  if (thd->one_shot_set)
serg@serg.mylan's avatar
serg@serg.mylan committed
3704 3705 3706 3707 3708 3709 3710 3711
  {
    /*
      If this is a SET, do nothing. This is to allow mysqlbinlog to print
      many SET commands (in this case we want the charset temp setting to
      live until the real query). This is also needed so that SET
      CHARACTER_SET_CLIENT... does not cancel itself immediately.
    */
    if (lex->sql_command != SQLCOM_SET_OPTION)
3712
    {
serg@serg.mylan's avatar
serg@serg.mylan committed
3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724
      thd->variables.character_set_client=
        global_system_variables.character_set_client;
      thd->variables.collation_connection=
        global_system_variables.collation_connection;
      thd->variables.collation_database=
        global_system_variables.collation_database;
      thd->variables.collation_server=
        global_system_variables.collation_server;
      thd->update_charset();
      thd->variables.time_zone=
        global_system_variables.time_zone;
      thd->one_shot_set= 0;
3725
    }
serg@serg.mylan's avatar
serg@serg.mylan committed
3726
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3727
  if (res < 0)
3728
    send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3729 3730

error:
3731 3732 3733 3734 3735 3736 3737 3738
  if (unlikely(slave_fake_lock))
  {
    DBUG_PRINT("info",("undoing faked lock"));
    thd->lock= thd->locked_tables;
    thd->locked_tables= fake_prev_lock;
    if (thd->lock == thd->locked_tables)
      thd->lock= 0;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3739 3740 3741 3742
  DBUG_VOID_RETURN;
}


bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3743 3744
/*
  Check grants for commands which work only with one table and all other
3745
  tables belonging to subselects or implicitly opened tables.
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3746

3747
  SYNOPSIS
monty@mysql.com's avatar
monty@mysql.com committed
3748 3749 3750 3751
    check_one_table_access()
    thd			Thread handler
    privilege		requested privelage
    tables		table list of command
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3752 3753 3754

  RETURN
    0 - OK
monty@mysql.com's avatar
monty@mysql.com committed
3755
    1 - access denied, error is sent to client
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3756
*/
3757 3758

int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3759
{
monty@mysql.com's avatar
monty@mysql.com committed
3760 3761
  if (check_access(thd, privilege, tables->db, &tables->grant.privilege,0,0))
    return 1;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3762

3763
  /* Show only 1 table for check_grant */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3764
  if (grant_option && check_grant(thd, privilege, tables, 0, 1, 0))
monty@mysql.com's avatar
monty@mysql.com committed
3765
    return 1;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3766

3767
  /* Check rights on tables of subselects and implictly opened tables */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3768 3769
  TABLE_LIST *subselects_tables;
  if ((subselects_tables= tables->next))
monty@mysql.com's avatar
monty@mysql.com committed
3770
  {
monty@mysql.com's avatar
monty@mysql.com committed
3771
    if ((check_table_access(thd, SELECT_ACL, subselects_tables,0)))
monty@mysql.com's avatar
monty@mysql.com committed
3772 3773 3774
      return 1;
  }
  return 0;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3775 3776 3777
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
3778
/****************************************************************************
3779
  Get the user (global) and database privileges for all used tables
3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792

  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
3793 3794
                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
3795 3796 3797
****************************************************************************/

bool
3798
check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
3799
	     bool dont_check_global_grants, bool no_errors)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3800
{
3801
  DBUG_ENTER("check_access");
3802 3803
  DBUG_PRINT("enter",("db: '%s'  want_access: %lu  master_access: %lu",
                      db ? db : "", want_access, thd->master_access));
3804 3805
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  ulong db_access;
3806
  bool  db_is_pattern= test(want_access & GRANT_ACL);
3807 3808
#endif
  ulong dummy;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3809 3810 3811 3812 3813
  if (save_priv)
    *save_priv=0;
  else
    save_priv= &dummy;

3814
  if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3815
  {
3816
    if (!no_errors)
3817
      send_error(thd,ER_NO_DB_ERROR);	/* purecov: tested */
3818
    DBUG_RETURN(TRUE);				/* purecov: tested */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3819 3820
  }

monty@mysql.com's avatar
monty@mysql.com committed
3821 3822 3823
#ifdef NO_EMBEDDED_ACCESS_CHECKS
  DBUG_RETURN(0);
#else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3824 3825
  if ((thd->master_access & want_access) == want_access)
  {
3826 3827 3828 3829 3830 3831 3832
    /*
      If we don't have a global SELECT privilege, we have to get the database
      specific access rights to be able to handle queries of type
      UPDATE t1 SET a=1 WHERE b > 0
    */
    db_access= thd->db_access;
    if (!(thd->master_access & SELECT_ACL) &&
3833
	(db && (!thd->db || db_is_pattern || strcmp(db,thd->db))))
serg@serg.mylan's avatar
serg@serg.mylan committed
3834
      db_access=acl_get(thd->host, thd->ip, thd->priv_user, db, db_is_pattern);
3835
    *save_priv=thd->master_access | db_access;
3836
    DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3837
  }
3838
  if (((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
3839
      ! db && dont_check_global_grants)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3840
  {						// We can never grant this
3841
    if (!no_errors)
3842
      net_printf(thd,ER_ACCESS_DENIED_ERROR,
3843
		 thd->priv_user,
3844
		 thd->priv_host,
3845
		 thd->password ? ER(ER_YES) : ER(ER_NO));/* purecov: tested */
3846
    DBUG_RETURN(TRUE);				/* purecov: tested */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3847 3848 3849
  }

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

3852
  if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
serg@serg.mylan's avatar
serg@serg.mylan committed
3853
    db_access=acl_get(thd->host, thd->ip, thd->priv_user, db, db_is_pattern);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3854 3855
  else
    db_access=thd->db_access;
3856 3857
  DBUG_PRINT("info",("db_access: %lu", db_access));
  /* Remove SHOW attribute and access rights we already have */
3858
  want_access &= ~(thd->master_access | EXTRA_ACL);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3859
  db_access= ((*save_priv=(db_access | thd->master_access)) & want_access);
3860 3861

  /* grant_option is set if there exists a single table or column grant */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3862
  if (db_access == want_access ||
3863
      ((grant_option && !dont_check_global_grants) &&
3864
       !(want_access & ~(db_access | TABLE_ACLS))))
3865
    DBUG_RETURN(FALSE);				/* Ok */
3866
  if (!no_errors)
3867
    net_printf(thd,ER_DBACCESS_DENIED_ERROR,
3868
	       thd->priv_user,
3869
	       thd->priv_host,
3870
	       db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */
3871
  DBUG_RETURN(TRUE);				/* purecov: tested */
monty@mysql.com's avatar
monty@mysql.com committed
3872
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3873 3874 3875
}


3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893
/*
  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
    One gets access rigth if one has ANY of the rights in want_access
    This is useful as one in most cases only need one global right,
    but in some case we want to check if the user has SUPER or
    REPL_CLIENT_ACL rights.

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

bool check_global_access(THD *thd, ulong want_access)
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3896
{
monty@mysql.com's avatar
monty@mysql.com committed
3897 3898 3899
#ifdef NO_EMBEDDED_ACCESS_CHECKS
  return 0;
#else
3900
  char command[128];
3901
  if ((thd->master_access & want_access))
3902 3903
    return 0;
  get_privilege_desc(command, sizeof(command), want_access);
3904
  net_printf(thd,ER_SPECIFIC_ACCESS_DENIED_ERROR,
3905 3906
	     command);
  return 1;
monty@mysql.com's avatar
monty@mysql.com committed
3907
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3908 3909 3910
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
3911
/*
3912 3913
  Check the privilege for all used tables.  Table privileges are cached
  in the table list for GRANT checking
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3914 3915
*/

3916
bool
3917
check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
3918
		   bool no_errors)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3919
{
3920 3921
  uint found=0;
  ulong found_access=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3922 3923 3924
  TABLE_LIST *org_tables=tables;
  for (; tables ; tables=tables->next)
  {
3925 3926 3927 3928
    if (tables->derived ||
        (tables->table && (int)tables->table->tmp_table) ||
        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
3929
      continue;
3930 3931
    if ((thd->master_access & want_access) == (want_access & ~EXTRA_ACL) &&
	thd->db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3932
      tables->grant.privilege= want_access;
3933
    else if (tables->db && tables->db == thd->db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3934 3935 3936 3937 3938
    {
      if (found && !grant_option)		// db already checked
	tables->grant.privilege=found_access;
      else
      {
3939 3940
	if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
			 0, no_errors))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3941 3942
	  return TRUE;				// Access denied
	found_access=tables->grant.privilege;
3943
	found=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3944 3945
      }
    }
3946
    else if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
3947
			  0, no_errors))
3948
      return TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3949 3950
  }
  if (grant_option)
3951
    return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3952
		       test(want_access & EXTRA_ACL), UINT_MAX, no_errors);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3953 3954 3955
  return FALSE;
}

3956 3957
bool check_merge_table_access(THD *thd, char *db,
			      TABLE_LIST *table_list)
3958 3959 3960 3961
{
  int error=0;
  if (table_list)
  {
3962
    /* Check that all tables use the current database */
3963 3964
    TABLE_LIST *tmp;
    for (tmp=table_list; tmp ; tmp=tmp->next)
3965 3966 3967 3968
    {
      if (!tmp->db || !tmp->db[0])
	tmp->db=db;
    }
3969
    error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
hf@deer.(none)'s avatar
hf@deer.(none) committed
3970
			     table_list,0);
3971 3972 3973 3974
  }
  return error;
}

hf@deer.(none)'s avatar
hf@deer.(none) committed
3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990

static bool check_db_used(THD *thd,TABLE_LIST *tables)
{
  for (; tables ; tables=tables->next)
  {
    if (!tables->db)
    {
      if (!(tables->db=thd->db))
      {
	send_error(thd,ER_NO_DB_ERROR);	/* purecov: tested */
	return TRUE;				/* purecov: tested */
      }
    }
  }
  return FALSE;
}
3991

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3992 3993 3994 3995 3996 3997 3998 3999 4000 4001
/****************************************************************************
	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
4002 4003 4004 4005
#ifndef DBUG_OFF
long max_stack_used;
#endif

4006
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4007 4008 4009 4010 4011 4012 4013 4014
bool check_stack_overrun(THD *thd,char *buf __attribute__((unused)))
{
  long stack_used;
  if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
      (long) thread_stack_min)
  {
    sprintf(errbuff[0],ER(ER_STACK_OVERRUN),stack_used,thread_stack);
    my_message(ER_STACK_OVERRUN,errbuff[0],MYF(0));
4015
    thd->fatal_error();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4016 4017
    return 1;
  }
monty@mysql.com's avatar
monty@mysql.com committed
4018 4019 4020
#ifndef DBUG_OFF
  max_stack_used= max(max_stack_used, stack_used);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4021 4022
  return 0;
}
4023
#endif /* EMBEDDED_LIBRARY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4024 4025 4026 4027

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

4028
bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4029 4030
{
  LEX	*lex=current_lex;
4031
  ulong old_info=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056
  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;
}

/****************************************************************************
4057
  Initialize global thd variables needed for query
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4058 4059
****************************************************************************/

4060
void
4061
mysql_init_query(THD *thd, uchar *buf, uint length)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4062 4063
{
  DBUG_ENTER("mysql_init_query");
4064
  lex_start(thd, buf, length);
4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085
  mysql_reset_thd_for_next_command(thd);
  DBUG_VOID_RETURN;
}


/*
 Reset THD part responsible for command processing state.

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

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

void mysql_reset_thd_for_next_command(THD *thd)
{
  DBUG_ENTER("mysql_reset_thd_for_next_command");
4086
  thd->free_list= 0;
4087 4088
  thd->select_number= 1;
  thd->total_warn_count= 0;                     // Warnings for this query
4089 4090
  thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
  thd->sent_row_count= thd->examined_row_count= 0;
4091
  thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0;
monty@mysql.com's avatar
monty@mysql.com committed
4092 4093 4094
  thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | 
			  SERVER_QUERY_NO_INDEX_USED |
			  SERVER_QUERY_NO_GOOD_INDEX_USED);
4095
  thd->tmp_table_used= 0;
4096 4097
  if (opt_bin_log)
    reset_dynamic(&thd->user_var_events);
4098
  thd->clear_error();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4099 4100 4101
  DBUG_VOID_RETURN;
}

4102

4103 4104 4105
void
mysql_init_select(LEX *lex)
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4106
  SELECT_LEX *select_lex= lex->current_select;
4107
  select_lex->init_select();
4108
  select_lex->select_limit= HA_POS_ERROR;
4109 4110
  if (select_lex == &lex->select_lex)
  {
4111
    DBUG_ASSERT(lex->result == 0);
4112 4113
    lex->exchange= 0;
  }
4114 4115
}

4116

4117
bool
4118
mysql_new_select(LEX *lex, bool move_down)
4119
{
4120 4121
  SELECT_LEX *select_lex;
  if (!(select_lex= new(lex->thd->mem_root) SELECT_LEX()))
4122
    return 1;
4123
  select_lex->select_number= ++lex->thd->select_number;
4124 4125 4126 4127
  select_lex->init_query();
  select_lex->init_select();
  if (move_down)
  {
4128
    lex->subqueries= TRUE;
4129
    /* first select_lex of subselect or derived table */
4130 4131
    SELECT_LEX_UNIT *unit;
    if (!(unit= new(lex->thd->mem_root) SELECT_LEX_UNIT()))
4132
      return 1;
4133

4134 4135
    unit->init_query();
    unit->init_select();
4136
    unit->thd= lex->thd;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4137
    unit->include_down(lex->current_select);
4138 4139
    unit->link_next= 0;
    unit->link_prev= 0;
4140
    unit->return_to= lex->current_select;
4141
    select_lex->include_down(unit);
4142
    // TODO: assign resolve_mode for fake subquery after merging with new tree
4143 4144
  }
  else
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4145
  {
4146
    select_lex->include_neighbour(lex->current_select);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4147 4148 4149 4150 4151 4152 4153 4154
    SELECT_LEX_UNIT *unit= select_lex->master_unit();
    SELECT_LEX *fake= unit->fake_select_lex;
    if (!fake)
    {
      /*
	as far as we included SELECT_LEX for UNION unit should have
	fake SELECT_LEX for UNION processing
      */
4155 4156
      if (!(fake= unit->fake_select_lex= new(lex->thd->mem_root) SELECT_LEX()))
        return 1;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4157 4158 4159 4160 4161
      fake->include_standalone(unit,
			       (SELECT_LEX_NODE**)&unit->fake_select_lex);
      fake->select_number= INT_MAX;
      fake->make_empty_select();
      fake->linkage= GLOBAL_OPTIONS_TYPE;
4162
      fake->select_limit= HA_POS_ERROR;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4163 4164
    }
  }
peter@mysql.com's avatar
peter@mysql.com committed
4165

4166
  select_lex->master_unit()->global_parameters= select_lex;
4167
  select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
4168
  lex->current_select= select_lex;
4169
  select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
4170
  return 0;
4171
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4172

4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187
/*
  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)
{
4188
  THD *thd;
4189
  LEX *lex;
4190
  LEX_STRING tmp, null_lex_string;
4191
  DBUG_ENTER("create_select_for_variable");
4192 4193

  thd= current_thd;
4194
  lex= thd->lex;
4195 4196 4197 4198
  mysql_init_select(lex);
  lex->sql_command= SQLCOM_SELECT;
  tmp.str= (char*) var_name;
  tmp.length=strlen(var_name);
4199 4200 4201
  bzero((char*) &null_lex_string.str, sizeof(null_lex_string));
  add_item_to_list(thd, get_system_var(thd, OPT_SESSION, tmp,
				       null_lex_string));
4202 4203 4204
  DBUG_VOID_RETURN;
}

4205

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4206 4207
void mysql_init_multi_delete(LEX *lex)
{
4208
  lex->sql_command=  SQLCOM_DELETE_MULTI;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4209
  mysql_init_select(lex);
4210
  lex->select_lex.select_limit= lex->unit.select_limit_cnt=
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4211
    HA_POS_ERROR;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4212
  lex->select_lex.table_list.save_and_clear(&lex->auxilliary_table_list);
4213
  lex->lock_option= using_update_log ? TL_READ_NO_INSERT : TL_READ;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4214
}
4215

4216

4217 4218 4219 4220
/*
  When you modify mysql_parse(), you may need to mofify
  mysql_test_parse_for_slave() in this same file.
*/
4221

4222
void mysql_parse(THD *thd, char *inBuf, uint length)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4223 4224 4225
{
  DBUG_ENTER("mysql_parse");

4226
  mysql_init_query(thd, (uchar*) inBuf, length);
4227
  if (query_cache_send_result_to_client(thd, inBuf, length) <= 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4228
  {
4229
    LEX *lex= thd->lex;
4230
    if (!yyparse((void *)thd) && ! thd->is_fatal_error)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4231
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
4232
#ifndef NO_EMBEDDED_ACCESS_CHECKS
4233
      if (mqh_used && thd->user_connect &&
4234
	  check_mqh(thd, lex->sql_command))
4235 4236 4237 4238
      {
	thd->net.error = 0;
      }
      else
hf@deer.(none)'s avatar
hf@deer.(none) committed
4239
#endif
4240
      {
4241 4242 4243 4244
	if (thd->net.report_error)
	  send_error(thd, 0, NullS);
	else
	{
4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258
          /*
            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.
          */
          if (lex->found_colon &&
              (thd->query_length= (ulong)(lex->found_colon - thd->query)))
            thd->query_length--;
          /* Actually execute the query */
4259
	  mysql_execute_command(thd);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
4260
	  query_cache_end_of_result(thd);
4261
	}
4262
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4263 4264
    }
    else
4265 4266
    {
      DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
4267
			 thd->is_fatal_error));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4268
      query_cache_abort(&thd->net);
4269
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4270
    thd->proc_info="freeing items";
4271
    thd->end_statement();
4272
    DBUG_ASSERT(thd->change_list.is_empty());
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4273
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4274 4275 4276 4277
  DBUG_VOID_RETURN;
}


monty@mysql.com's avatar
monty@mysql.com committed
4278
#ifdef HAVE_REPLICATION
4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289
/*
  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)
{
4290
  LEX *lex= thd->lex;
4291 4292
  bool error= 0;

4293
  mysql_init_query(thd, (uchar*) inBuf, length);
monty@mysql.com's avatar
monty@mysql.com committed
4294
  if (!yyparse((void*) thd) && ! thd->is_fatal_error &&
4295 4296
      all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
    error= 1;                /* Ignore question */
4297
  thd->end_statement();
4298 4299
  return error;
}
monty@mysql.com's avatar
monty@mysql.com committed
4300
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4301

4302

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4303 4304 4305 4306 4307
/*****************************************************************************
** Store field definition for create
** Return 0 if ok
******************************************************************************/

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4308
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
4309
		       char *length, char *decimals,
4310
		       uint type_modifier,
4311 4312
		       Item *default_value, Item *on_update_value,
                       LEX_STRING *comment,
4313 4314
		       char *change,
                       List<String> *interval_list, CHARSET_INFO *cs,
4315
		       uint uint_geom_type)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4316 4317
{
  register create_field *new_field;
4318
  LEX  *lex= thd->lex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4319
  uint allowed_type_modifier=0;
4320
  char warn_buff[MYSQL_ERRMSG_SIZE];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4321 4322 4323 4324
  DBUG_ENTER("add_field_to_list");

  if (strlen(field_name) > NAME_LEN)
  {
4325
    net_printf(thd, ER_TOO_LONG_IDENT, field_name); /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4326 4327 4328 4329 4330
    DBUG_RETURN(1);				/* purecov: inspected */
  }
  if (type_modifier & PRI_KEY_FLAG)
  {
    lex->col_list.push_back(new key_part_spec(field_name,0));
4331
    lex->key_list.push_back(new Key(Key::PRIMARY, NullS, HA_KEY_ALG_UNDEF,
4332
				    0, lex->col_list));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4333 4334 4335 4336 4337
    lex->col_list.empty();
  }
  if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
  {
    lex->col_list.push_back(new key_part_spec(field_name,0));
4338
    lex->key_list.push_back(new Key(Key::UNIQUE, NullS, HA_KEY_ALG_UNDEF, 0,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4339 4340 4341 4342
				    lex->col_list));
    lex->col_list.empty();
  }

4343
  if (default_value)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4344
  {
4345
    /* 
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4346 4347
      Default value should be literal => basic constants =>
      no need fix_fields()
4348 4349 4350
      
      We allow only one function as part of default value - 
      NOW() as default for TIMESTAMP type.
4351
    */
4352 4353 4354 4355 4356 4357 4358 4359
    if (default_value->type() == Item::FUNC_ITEM && 
        !(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC &&
         type == FIELD_TYPE_TIMESTAMP))
    {
      net_printf(thd, ER_INVALID_DEFAULT, field_name);
      DBUG_RETURN(1);
    }
    else if (default_value->type() == Item::NULL_ITEM)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4360
    {
4361
      default_value= 0;
4362 4363 4364
      if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
	  NOT_NULL_FLAG)
      {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4365
	net_printf(thd,ER_INVALID_DEFAULT,field_name);
4366 4367 4368 4369 4370
	DBUG_RETURN(1);
      }
    }
    else if (type_modifier & AUTO_INCREMENT_FLAG)
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4371
      net_printf(thd, ER_INVALID_DEFAULT, field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4372 4373 4374
      DBUG_RETURN(1);
    }
  }
4375 4376 4377 4378 4379 4380 4381

  if (on_update_value && type != FIELD_TYPE_TIMESTAMP)
  {
    net_printf(thd, ER_INVALID_ON_UPDATE, field_name);
    DBUG_RETURN(1);
  }
    
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4382 4383 4384 4385
  if (!(new_field=new create_field()))
    DBUG_RETURN(1);
  new_field->field=0;
  new_field->field_name=field_name;
4386
  new_field->def= default_value;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4387 4388 4389 4390 4391 4392 4393 4394 4395 4396
  new_field->flags= type_modifier;
  new_field->unireg_check= (type_modifier & AUTO_INCREMENT_FLAG ?
			    Field::NEXT_NUMBER : Field::NONE);
  new_field->decimals= decimals ? (uint) set_zone(atoi(decimals),0,
						  NOT_FIXED_DEC-1) : 0;
  new_field->sql_type=type;
  new_field->length=0;
  new_field->change=change;
  new_field->interval=0;
  new_field->pack_length=0;
4397
  new_field->charset=cs;
4398
  new_field->geom_type= (Field::geometry_type) uint_geom_type;
4399

4400 4401 4402 4403 4404 4405 4406 4407
  if (!comment)
  {
    new_field->comment.str=0;
    new_field->comment.length=0;
  }
  else
  {
    /* In this case comment is always of type Item_string */
bar@bar.mysql.r18.ru's avatar
bar@bar.mysql.r18.ru committed
4408 4409
    new_field->comment.str=   (char*) comment->str;
    new_field->comment.length=comment->length;
4410
  }
4411 4412
  if (length && !(new_field->length= (uint) atoi(length)))
    length=0; /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4413 4414 4415
  uint sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1;

  if (new_field->length && new_field->decimals &&
4416
      new_field->length < new_field->decimals+1 &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4417
      new_field->decimals != NOT_FIXED_DEC)
4418
    new_field->length=new_field->decimals+1; /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4419 4420 4421

  switch (type) {
  case FIELD_TYPE_TINY:
4422
    if (!length) new_field->length=MAX_TINYINT_WIDTH+sign_len;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4423 4424 4425
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    break;
  case FIELD_TYPE_SHORT:
4426
    if (!length) new_field->length=MAX_SMALLINT_WIDTH+sign_len;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4427 4428 4429
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    break;
  case FIELD_TYPE_INT24:
4430
    if (!length) new_field->length=MAX_MEDIUMINT_WIDTH+sign_len;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4431 4432 4433
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    break;
  case FIELD_TYPE_LONG:
4434
    if (!length) new_field->length=MAX_INT_WIDTH+sign_len;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4435 4436 4437
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    break;
  case FIELD_TYPE_LONGLONG:
4438
    if (!length) new_field->length=MAX_BIGINT_WIDTH;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4439 4440 4441 4442 4443 4444
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    break;
  case FIELD_TYPE_NULL:
    break;
  case FIELD_TYPE_DECIMAL:
    if (!length)
monty@mysql.com's avatar
monty@mysql.com committed
4445 4446
    {
      if ((new_field->length= new_field->decimals))
4447 4448
        new_field->length++;
      else
monty@mysql.com's avatar
monty@mysql.com committed
4449 4450
        new_field->length= 10;                  // Default length for DECIMAL
    }
4451 4452 4453 4454 4455 4456
    if (new_field->length < MAX_FIELD_WIDTH)	// Skip wrong argument
    {
      new_field->length+=sign_len;
      if (new_field->decimals)
	new_field->length++;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4457
    break;
4458 4459
  case FIELD_TYPE_STRING:
  case FIELD_TYPE_VAR_STRING:
4460
    if (new_field->length <= MAX_FIELD_CHARLENGTH || default_value)
4461 4462 4463 4464
      break;
    /* Convert long CHAR() and VARCHAR columns to TEXT or BLOB */
    new_field->sql_type= FIELD_TYPE_BLOB;
    sprintf(warn_buff, ER(ER_AUTO_CONVERT), field_name, "CHAR",
4465
	    (cs == &my_charset_bin) ? "BLOB" : "TEXT");
4466 4467 4468
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_AUTO_CONVERT,
		 warn_buff);
    /* fall through */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4469 4470 4471 4472
  case FIELD_TYPE_BLOB:
  case FIELD_TYPE_TINY_BLOB:
  case FIELD_TYPE_LONG_BLOB:
  case FIELD_TYPE_MEDIUM_BLOB:
4473
  case FIELD_TYPE_GEOMETRY:
4474 4475 4476 4477 4478
    if (new_field->length)
    {
      /* The user has given a length to the blob column */
      if (new_field->length < 256)
	type= FIELD_TYPE_TINY_BLOB;
4479
      else if (new_field->length < 65536)
4480 4481 4482 4483 4484 4485 4486 4487
	type= FIELD_TYPE_BLOB;
      else if (new_field->length < 256L*256L*256L)
	type= FIELD_TYPE_MEDIUM_BLOB;
      else
	type= FIELD_TYPE_LONG_BLOB;
      new_field->length= 0;
    }
    new_field->sql_type= type;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4488 4489 4490 4491 4492 4493
    if (default_value)				// Allow empty as default value
    {
      String str,*res;
      res=default_value->val_str(&str);
      if (res->length())
      {
4494
	net_printf(thd,ER_BLOB_CANT_HAVE_DEFAULT,field_name); /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513
	DBUG_RETURN(1); /* purecov: inspected */
      }
      new_field->def=0;
    }
    new_field->flags|=BLOB_FLAG;
    break;
  case FIELD_TYPE_YEAR:
    if (!length || new_field->length != 2)
      new_field->length=4;			// Default length
    new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
    break;
  case FIELD_TYPE_FLOAT:
    /* change FLOAT(precision) to FLOAT or DOUBLE */
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    if (length && !decimals)
    {
      uint tmp_length=new_field->length;
      if (tmp_length > PRECISION_FOR_DOUBLE)
      {
4514
	net_printf(thd,ER_WRONG_FIELD_SPEC,field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543
	DBUG_RETURN(1);
      }
      else if (tmp_length > PRECISION_FOR_FLOAT)
      {
	new_field->sql_type=FIELD_TYPE_DOUBLE;
	new_field->length=DBL_DIG+7;			// -[digits].E+###
      }
      else
	new_field->length=FLT_DIG+6;			// -[digits].E+##
      new_field->decimals= NOT_FIXED_DEC;
      break;
    }
    if (!length)
    {
      new_field->length =  FLT_DIG+6;
      new_field->decimals= NOT_FIXED_DEC;
    }
    break;
  case FIELD_TYPE_DOUBLE:
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    if (!length)
    {
      new_field->length = DBL_DIG+7;
      new_field->decimals=NOT_FIXED_DEC;
    }
    break;
  case FIELD_TYPE_TIMESTAMP:
    if (!length)
      new_field->length= 14;			// Full date YYYYMMDDHHMMSS
4544
    else if (new_field->length != 19)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4545
    {
4546 4547 4548 4549
      /*
        We support only even TIMESTAMP lengths less or equal than 14
        and 19 as length of 4.1 compatible representation.
      */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4550 4551 4552
      new_field->length=((new_field->length+1)/2)*2; /* purecov: inspected */
      new_field->length= min(new_field->length,14); /* purecov: inspected */
    }
4553
    new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573
    if (default_value)
    {
      /* Grammar allows only NOW() value for ON UPDATE clause */
      if (default_value->type() == Item::FUNC_ITEM && 
          ((Item_func*)default_value)->functype() == Item_func::NOW_FUNC)
      {
        new_field->unireg_check= (on_update_value?Field::TIMESTAMP_DNUN_FIELD:
                                                  Field::TIMESTAMP_DN_FIELD);
        /*
          We don't need default value any longer moreover it is dangerous.
          Everything handled by unireg_check further.
        */
        new_field->def= 0;
      }
      else
        new_field->unireg_check= (on_update_value?Field::TIMESTAMP_UN_FIELD:
                                                  Field::NONE);
    }
    else
    {
4574 4575 4576 4577 4578 4579 4580 4581
      /*
        If we have default TIMESTAMP NOT NULL column without explicit DEFAULT
        or ON UPDATE values then for the sake of compatiblity we should treat
        this column as having DEFAULT NOW() ON UPDATE NOW() (when we don't
        have another TIMESTAMP column with auto-set option before this one)
        or DEFAULT 0 (in other cases).
        So here we are setting TIMESTAMP_OLD_FIELD only temporary, and will
        replace this value by TIMESTAMP_DNUN_FIELD or NONE later when
4582
        information about all TIMESTAMP fields in table will be availiable.
4583 4584 4585

        If we have TIMESTAMP NULL column without explicit DEFAULT value
        we treat it as having DEFAULT NULL attribute.
4586
      */
4587 4588 4589 4590 4591
      new_field->unireg_check= on_update_value ?
                               Field::TIMESTAMP_UN_FIELD :
                               (new_field->flags & NOT_NULL_FLAG ?
                                Field::TIMESTAMP_OLD_FIELD:
                                Field::NONE);
4592
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608
    break;
  case FIELD_TYPE_DATE:				// Old date type
    if (protocol_version != PROTOCOL_VERSION-1)
      new_field->sql_type=FIELD_TYPE_NEWDATE;
    /* fall trough */
  case FIELD_TYPE_NEWDATE:
    new_field->length=10;
    break;
  case FIELD_TYPE_TIME:
    new_field->length=10;
    break;
  case FIELD_TYPE_DATETIME:
    new_field->length=19;
    break;
  case FIELD_TYPE_SET:
    {
4609
      if (interval_list->elements > sizeof(longlong)*8)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4610
      {
4611 4612
        net_printf(thd,ER_TOO_BIG_SET,field_name); /* purecov: inspected */
        DBUG_RETURN(1);				/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4613
      }
4614
      new_field->pack_length= get_set_pack_length(interval_list->elements);
4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625

      List_iterator<String> it(*interval_list);
      String *tmp;
      while ((tmp= it++))
        new_field->interval_list.push_back(tmp);
      /*
        Set fake length to 1 to pass the below conditions.
        Real length will be set in mysql_prepare_table()
        when we know the character set of the column
      */
      new_field->length= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4626 4627 4628 4629
    }
    break;
  case FIELD_TYPE_ENUM:
    {
4630
      // Should be safe
4631
      new_field->pack_length= get_enum_pack_length(interval_list->elements);
4632 4633 4634 4635 4636 4637

      List_iterator<String> it(*interval_list);
      String *tmp;
      while ((tmp= it++))
        new_field->interval_list.push_back(tmp);
      new_field->length= 1; // See comment for FIELD_TYPE_SET above.
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4638
    }
4639
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4640 4641
  }

4642 4643
  if ((new_field->length > MAX_FIELD_CHARLENGTH && type != FIELD_TYPE_SET && 
       type != FIELD_TYPE_ENUM) ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4644
      (!new_field->length && !(new_field->flags & BLOB_FLAG) &&
4645
       type != FIELD_TYPE_STRING &&
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
4646
       type != FIELD_TYPE_VAR_STRING && type != FIELD_TYPE_GEOMETRY))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4647
  {
4648
    net_printf(thd,ER_TOO_BIG_FIELDLENGTH,field_name,
4649
	       MAX_FIELD_CHARLENGTH);		/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4650 4651 4652 4653 4654
    DBUG_RETURN(1);				/* purecov: inspected */
  }
  type_modifier&= AUTO_INCREMENT_FLAG;
  if ((~allowed_type_modifier) & type_modifier)
  {
4655
    net_printf(thd,ER_WRONG_FIELD_SPEC,field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676
    DBUG_RETURN(1);
  }
  if (!new_field->pack_length)
    new_field->pack_length=calc_pack_length(new_field->sql_type ==
					    FIELD_TYPE_VAR_STRING ?
					    FIELD_TYPE_STRING :
					    new_field->sql_type,
					    new_field->length);
  lex->create_list.push_back(new_field);
  lex->last_field=new_field;
  DBUG_RETURN(0);
}

/* Store position for column in ALTER TABLE .. ADD column */

void store_position_for_column(const char *name)
{
  current_lex->last_field->after=my_const_cast(char*) (name);
}

bool
4677
add_proc_to_list(THD* thd, Item *item)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4678 4679 4680 4681
{
  ORDER *order;
  Item	**item_ptr;

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4682
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4683 4684 4685 4686 4687
    return 1;
  item_ptr = (Item**) (order+1);
  *item_ptr= item;
  order->item=item_ptr;
  order->free_me=0;
4688
  thd->lex->proc_list.link_in_list((byte*) order,(byte**) &order->next);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4689 4690 4691 4692 4693 4694 4695 4696
  return 0;
}


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

static void remove_escape(char *name)
{
4697 4698
  if (!*name)					// For empty DB names
    return;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4699 4700
  char *to;
#ifdef USE_MB
4701
  char *strend=name+(uint) strlen(name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4702 4703 4704 4705 4706 4707
#endif
  for (to=name; *name ; name++)
  {
#ifdef USE_MB
    int l;
/*    if ((l = ismbchar(name, name+MBMAXLEN))) { Wei He: I think it's wrong */
4708 4709
    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
4710 4711 4712 4713 4714 4715 4716 4717
    {
	while (l--)
	    *to++ = *name++;
	name--;
	continue;
    }
#endif
    if (*name == '\\' && name[1])
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4718
      name++;					// Skip '\\'
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4719 4720 4721 4722 4723 4724 4725 4726 4727 4728
    *to++= *name;
  }
  *to=0;
}

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


monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4729
bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4730 4731 4732
{
  ORDER *order;
  DBUG_ENTER("add_to_list");
4733
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4734
    DBUG_RETURN(1);
4735 4736
  order->item_ptr= item;
  order->item= &order->item_ptr;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4737 4738 4739
  order->asc = asc;
  order->free_me=0;
  order->used=0;
4740
  list.link_in_list((byte*) order,(byte**) &order->next);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4741 4742 4743 4744
  DBUG_RETURN(0);
}


monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763
/*
  Add a table to list of used tables

  SYNOPSIS
    add_table_to_list()
    table		Table to add
    alias		alias for table (or null if no alias)
    table_options	A set of the following bits:
			TL_OPTION_UPDATING	Table will be updated
			TL_OPTION_FORCE_INDEX	Force usage of index
    lock_type		How table should be locked
    use_index		List of indexed used in USE INDEX
    ignore_index	List of indexed used in IGNORE INDEX

    RETURN
      0		Error
      #		Pointer to TABLE_LIST element added to the total table list
*/

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4764 4765
TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
					     Table_ident *table,
4766
					     LEX_STRING *alias,
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4767 4768
					     ulong table_options,
					     thr_lock_type lock_type,
4769 4770
					     List<String> *use_index_arg,
					     List<String> *ignore_index_arg,
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4771
                                             LEX_STRING *option)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4772 4773 4774 4775 4776 4777 4778 4779
{
  register TABLE_LIST *ptr;
  char *alias_str;
  DBUG_ENTER("add_table_to_list");

  if (!table)
    DBUG_RETURN(0);				// End of memory
  alias_str= alias ? alias->str : table->table.str;
4780
  if (check_table_name(table->table.str,table->table.length) ||
4781
      table->db.str && check_db_name(table->db.str))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4782
  {
4783
    net_printf(thd, ER_WRONG_TABLE_NAME, table->table.str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4784 4785 4786 4787
    DBUG_RETURN(0);
  }

  if (!alias)					/* Alias is case sensitive */
4788 4789 4790 4791 4792 4793
  {
    if (table->sel)
    {
      net_printf(thd,ER_DERIVED_MUST_HAVE_ALIAS);
      DBUG_RETURN(0);
    }
4794
    if (!(alias_str=thd->memdup(alias_str,table->table.length+1)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4795
      DBUG_RETURN(0);
4796
  }
4797
  if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4798
    DBUG_RETURN(0);				/* purecov: inspected */
peter@mysql.com's avatar
peter@mysql.com committed
4799
  if (table->db.str)
4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810
  {
    ptr->db= table->db.str;
    ptr->db_length= table->db.length;
  }
  else if (thd->db)
  {
    ptr->db= thd->db;
    ptr->db_length= thd->db_length;
  }
  else
  {
4811 4812
    /* The following can't be "" as we may do 'casedn_str()' on it */
    ptr->db= empty_c_string;
4813 4814
    ptr->db_length= 0;
  }
4815 4816
  if (thd->current_arena->is_stmt_prepare())
    ptr->db= thd->strdup(ptr->db);
peter@mysql.com's avatar
peter@mysql.com committed
4817

4818
  ptr->alias= alias_str;
4819 4820
  if (lower_case_table_names && table->table.length)
    my_casedn_str(files_charset_info, table->table.str);
4821
  ptr->real_name=table->table.str;
4822
  ptr->real_name_length=table->table.length;
4823
  ptr->lock_type=   lock_type;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4824 4825
  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
4826
  ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
4827
  ptr->derived=	    table->sel;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4828
  ptr->cacheable_table= 1;
4829 4830 4831 4832 4833 4834
  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
4835
  ptr->option= option ? option->str : 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4836
  /* check that used name is unique */
4837
  if (lock_type != TL_IGNORE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4838
  {
4839
    for (TABLE_LIST *tables=(TABLE_LIST*) table_list.first ;
4840
	 tables ;
4841
	 tables=tables->next)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4842
    {
4843 4844
      if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
	  !strcmp(ptr->db, tables->db))
4845
      {
4846
	net_printf(thd,ER_NONUNIQ_TABLE,alias_str); /* purecov: tested */
4847 4848
	DBUG_RETURN(0);				/* purecov: tested */
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4849 4850
    }
  }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4851
  table_list.link_in_list((byte*) ptr, (byte**) &ptr->next);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4852 4853 4854
  DBUG_RETURN(ptr);
}

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

4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868
/*
  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
*/

4869
void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
4870 4871 4872 4873 4874 4875
{
  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));

4876
  for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first ;
4877 4878 4879 4880 4881 4882 4883 4884 4885
       tables ;
       tables=tables->next)
  {
    tables->lock_type= lock_type;
    tables->updating=  for_update;
  }
  DBUG_VOID_RETURN;
}

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4887 4888
void add_join_on(TABLE_LIST *b,Item *expr)
{
4889
  if (expr)
4890
  {
4891 4892 4893 4894 4895 4896 4897 4898
    if (!b->on_expr)
      b->on_expr=expr;
    else
    {
      // This only happens if you have both a right and left join
      b->on_expr=new Item_cond_and(b->on_expr,expr);
    }
    b->on_expr->top_level_item();
4899
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4900 4901 4902
}


4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920
/*
  Mark that we have a NATURAL JOIN between two tables

  SYNOPSIS
    add_join_natural()
    a			Table to do normal join with
    b			Do normal join with this table
  
  IMPLEMENTATION
    This function just marks that table b should be joined with a.
    The function setup_cond() will create in b->on_expr a list
    of equal condition between all fields of the same name.

    SELECT * FROM t1 NATURAL LEFT JOIN t2
     <=>
    SELECT * FROM t1 LEFT JOIN t2 ON (t1.i=t2.i and t1.j=t2.j ... )
*/

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4921 4922 4923 4924 4925
void add_join_natural(TABLE_LIST *a,TABLE_LIST *b)
{
  b->natural_join=a;
}

4926
/*
4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943
  Reload/resets privileges and the different caches.

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

  RETURN
    0	 ok
    !=0  error
4944 4945
*/

4946 4947
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
4948 4949 4950
{
  bool result=0;
  select_errors=0;				/* Write if more errors */
4951
  bool tmp_write_to_binlog= 1;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
4952
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4953 4954
  if (options & REFRESH_GRANT)
  {
4955
    acl_reload(thd);
4956
    grant_reload(thd);
4957
    if (mqh_used)
4958
      reset_mqh(thd,(LEX_USER *) NULL,TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4959
  }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
4960
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4961 4962
  if (options & REFRESH_LOG)
  {
4963
    /*
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4964 4965
      Flush the normal query log, the update log, the binary log,
      the slow query log, and the relay log (if it exists).
4966
    */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4967

monty@mishka.local's avatar
monty@mishka.local committed
4968 4969 4970 4971 4972
    /*
      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)
4973 4974
    */
    tmp_write_to_binlog= 0;
4975 4976 4977 4978
    mysql_log.new_file(1);
    mysql_update_log.new_file(1);
    mysql_bin_log.new_file(1);
    mysql_slow_log.new_file(1);
4979
#ifdef HAVE_REPLICATION
4980
    if (mysql_bin_log.is_open() && expire_logs_days)
4981 4982 4983
    {
      long purge_time= time(0) - expire_logs_days*24*60*60;
      if (purge_time >= 0)
4984
	mysql_bin_log.purge_logs_before_date(purge_time);
4985
    }
4986
    pthread_mutex_lock(&LOCK_active_mi);
4987
    rotate_relay_log(active_mi);
4988
    pthread_mutex_unlock(&LOCK_active_mi);
4989
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4990 4991
    if (ha_flush_logs())
      result=1;
4992 4993
    if (flush_error_log())
      result=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4994
  }
4995
#ifdef HAVE_QUERY_CACHE
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4996 4997
  if (options & REFRESH_QUERY_CACHE_FREE)
  {
4998
    query_cache.pack();				// FLUSH QUERY CACHE
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4999 5000 5001 5002
    options &= ~REFRESH_QUERY_CACHE; //don't flush all cache, just free memory
  }
  if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
  {
5003
    query_cache.flush();			// RESET QUERY CACHE
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
5004
  }
5005
#endif /*HAVE_QUERY_CACHE*/
5006 5007 5008 5009 5010
  /*
    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
5011
  {
5012
    if ((options & REFRESH_READ_LOCK) && thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5013
    {
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
5014 5015 5016 5017
      /*
	Writing to the binlog could cause deadlocks, as we don't log
	UNLOCK TABLES
      */
5018
      tmp_write_to_binlog= 0;
5019 5020
      if (lock_global_read_lock(thd))
	return 1;
5021 5022 5023
      result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1,
                                 tables);
      make_global_read_lock_block_commit(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5024
    }
5025 5026
    else
      result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables);
5027
    my_dbopt_cleanup();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5028 5029 5030 5031 5032 5033 5034
  }
  if (options & REFRESH_HOSTS)
    hostname_cache_refresh();
  if (options & REFRESH_STATUS)
    refresh_status();
  if (options & REFRESH_THREADS)
    flush_thread_cache();
5035
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5036
  if (options & REFRESH_MASTER)
5037 5038
  {
    tmp_write_to_binlog= 0;
5039 5040
    if (reset_master(thd))
      result=1;
5041
  }
5042
#endif
5043
#ifdef OPENSSL
5044 5045 5046 5047 5048 5049
   if (options & REFRESH_DES_KEY_FILE)
   {
     if (des_key_file)
       result=load_des_key_file(des_key_file);
   }
#endif
5050
#ifdef HAVE_REPLICATION
5051 5052
 if (options & REFRESH_SLAVE)
 {
5053
   tmp_write_to_binlog= 0;
5054
   pthread_mutex_lock(&LOCK_active_mi);
5055
   if (reset_slave(thd, active_mi))
5056
     result=1;
5057
   pthread_mutex_unlock(&LOCK_active_mi);
5058
 }
5059
#endif
5060
 if (options & REFRESH_USER_RESOURCES)
5061
   reset_mqh(thd,(LEX_USER *) NULL);
5062 5063
 if (write_to_binlog)
   *write_to_binlog= tmp_write_to_binlog;
5064
 return result;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5065 5066
}

5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078
/*
  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
*/

5079
void kill_one_thread(THD *thd, ulong id)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5080 5081 5082
{
  THD *tmp;
  uint error=ER_NO_SUCH_THREAD;
5083 5084
  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
5085 5086 5087 5088
  while ((tmp=it++))
  {
    if (tmp->thread_id == id)
    {
5089 5090
      pthread_mutex_lock(&tmp->LOCK_delete);	// Lock from delete
      break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5091 5092 5093
    }
  }
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106
  if (tmp)
  {
    if ((thd->master_access & SUPER_ACL) ||
	!strcmp(thd->user,tmp->user))
    {
      tmp->awake(1 /*prepare to die*/);
      error=0;
    }
    else
      error=ER_KILL_DENIED_ERROR;
    pthread_mutex_unlock(&tmp->LOCK_delete);
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
5107
  if (!error)
5108
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5109
  else
5110
    net_printf(thd,error,id);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5111 5112
}

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
5114 5115 5116 5117 5118 5119 5120 5121
/* Clear most status variables */

static void refresh_status(void)
{
  pthread_mutex_lock(&LOCK_status);
  for (struct show_var_st *ptr=status_vars; ptr->name; ptr++)
  {
    if (ptr->type == SHOW_LONG)
5122
      *(ulong*) ptr->value= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5123
  }
timour@mysql.com's avatar
timour@mysql.com committed
5124 5125
  /* Reset the counters of all key caches (default and named). */
  process_key_caches(reset_key_cache_counters);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5126 5127
  pthread_mutex_unlock(&LOCK_status);
}
5128 5129 5130 5131


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

5132 5133
static bool append_file_to_dir(THD *thd, const char **filename_ptr,
			       const char *table_name)
5134
{
5135
  char buff[FN_REFLEN],*ptr, *end;
5136 5137 5138 5139 5140 5141 5142
  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))
  {
5143
    my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr);
5144 5145 5146 5147
    return 1;
  }
  /* Fix is using unix filename format on dos */
  strmov(buff,*filename_ptr);
5148
  end=convert_dirname(buff, *filename_ptr, NullS);
5149
  if (!(ptr=thd->alloc((uint) (end-buff)+(uint) strlen(table_name)+1)))
5150 5151
    return 1;					// End of memory
  *filename_ptr=ptr;
5152
  strxmov(ptr,buff,table_name,NullS);
5153 5154
  return 0;
}
5155

5156

5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170
/*
  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;
5171
  if (thd->lex->current_select != &thd->lex->select_lex)
5172 5173
  {
    char command[80];
5174 5175
    strmake(command, thd->lex->yylval->symbol.str,
	    min(thd->lex->yylval->symbol.length, sizeof(command)-1));
5176
    net_printf(thd, ER_CANT_USE_OPTION_HERE, command);
5177 5178 5179 5180
    return 1;
  }
  return 0;
}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5181

5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200
/*
  Setup locking for multi-table updates. Used by the replication slave.
  Replication slave SQL thread examines (all_tables_not_ok()) the
  locking state of referenced tables to determine if the query has to
  be executed or ignored. Since in multi-table update, the 
  'default' lock is read-only, this lock is corrected early enough by
  calling this function, before the slave decides to execute/ignore.

  SYNOPSIS
    check_multi_update_lock()
    thd		Current thread
    tables	List of user-supplied tables
    fields	List of fields requiring update

  RETURN VALUES
    0	ok
    1	error
*/
static bool check_multi_update_lock(THD *thd, TABLE_LIST *tables, 
5201
				    List<Item> *fields, SELECT_LEX *select_lex)
5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217
{
  bool res= 1;
  TABLE_LIST *table;
  DBUG_ENTER("check_multi_update_lock");
  
  if (check_db_used(thd, tables))
    goto error;

  /*
    Ensure that we have UPDATE or SELECT privilege for each table
    The exact privilege is checked in mysql_multi_update()
  */
  for (table= tables ; table ; table= table->next)
  {
    TABLE_LIST *save= table->next;
    table->next= 0;
5218 5219 5220
    if ((check_access(thd, UPDATE_ACL, table->db, &table->grant.privilege,0,1) ||
        (grant_option && check_grant(thd, UPDATE_ACL, table,0,1,1))) &&
	check_one_table_access(thd, SELECT_ACL, table))
5221 5222 5223 5224
	goto error;
    table->next= save;
  }
    
5225
  if (mysql_multi_update_lock(thd, tables, fields, select_lex))
5226 5227 5228 5229 5230 5231 5232
    goto error;
  
  res= 0;
  
error:
  DBUG_RETURN(res);
}
5233

5234

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5235
Comp_creator *comp_eq_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5236
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5237
  return invert?(Comp_creator *)&ne_creator:(Comp_creator *)&eq_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5238 5239
}

5240

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5241
Comp_creator *comp_ge_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5242
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5243
  return invert?(Comp_creator *)&lt_creator:(Comp_creator *)&ge_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5244 5245
}

5246

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5247
Comp_creator *comp_gt_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5248
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5249
  return invert?(Comp_creator *)&le_creator:(Comp_creator *)&gt_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5250 5251
}

5252

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5253
Comp_creator *comp_le_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5254
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5255
  return invert?(Comp_creator *)&gt_creator:(Comp_creator *)&le_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5256 5257
}

5258

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5259
Comp_creator *comp_lt_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5260
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5261
  return invert?(Comp_creator *)&ge_creator:(Comp_creator *)&lt_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5262 5263
}

5264

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5265
Comp_creator *comp_ne_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5266
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5267
  return invert?(Comp_creator *)&eq_creator:(Comp_creator *)&ne_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5268
}
5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288


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

  if ((cmp == &comp_ne_creator) && all)        // <> ALL <=> NOT IN
5293 5294 5295
    return new Item_func_not(new Item_in_subselect(left_expr, select_lex));

  Item_allany_subselect *it=
5296
    new Item_allany_subselect(left_expr, (*cmp)(all), select_lex, all);
5297
  if (all)
5298
    return it->upper_item= new Item_func_not_all(it);	/* ALL */
5299

5300
  return it->upper_item= new Item_func_nop_all(it);      /* ANY/SOME */
5301
}
5302 5303


5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318
/*
  CREATE INDEX and DROP INDEX are implemented by calling ALTER TABLE with
  the proper arguments.  This isn't very fast but it should work for most
  cases.

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

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

int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
{
  List<create_field> fields;
5319 5320 5321
  ALTER_INFO alter_info;
  alter_info.flags= ALTER_ADD_INDEX;
  alter_info.is_simple= 0;
5322 5323 5324 5325 5326 5327 5328
  HA_CREATE_INFO create_info;
  DBUG_ENTER("mysql_create_index");
  bzero((char*) &create_info,sizeof(create_info));
  create_info.db_type=DB_TYPE_DEFAULT;
  create_info.default_table_charset= thd->variables.collation_database;
  DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
				&create_info, table_list,
5329
				fields, keys, 0, (ORDER*)0,
5330
				DUP_ERROR, 0, &alter_info));
5331 5332 5333
}


5334
int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info)
5335 5336 5337 5338 5339 5340 5341 5342
{
  List<create_field> fields;
  List<Key> keys;
  HA_CREATE_INFO create_info;
  DBUG_ENTER("mysql_drop_index");
  bzero((char*) &create_info,sizeof(create_info));
  create_info.db_type=DB_TYPE_DEFAULT;
  create_info.default_table_charset= thd->variables.collation_database;
5343 5344 5345
  alter_info->clear();
  alter_info->flags= ALTER_DROP_INDEX;
  alter_info->is_simple= 0;
5346 5347
  DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
				&create_info, table_list,
5348
				fields, keys, 0, (ORDER*)0,
5349
				DUP_ERROR, 0, alter_info));
5350
}
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
5351 5352


5353 5354 5355 5356 5357
/*
  Multi update query pre-check

  SYNOPSIS
    multi_update_precheck()
5358 5359
    thd		Thread handler
    tables	Global table list
5360

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5361
  RETURN VALUE
5362 5363 5364
    0   OK
    1   Error (message is sent to user)
    -1  Error (message is not sent to user)
5365
*/
5366

5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386
int multi_update_precheck(THD *thd, TABLE_LIST *tables)
{
  DBUG_ENTER("multi_update_precheck");
  const char *msg= 0;
  TABLE_LIST *table;
  LEX *lex= thd->lex;
  SELECT_LEX *select_lex= &lex->select_lex;
  TABLE_LIST *update_list= (TABLE_LIST*)select_lex->table_list.first;

  if (select_lex->item_list.elements != lex->value_list.elements)
  {
    my_error(ER_WRONG_VALUE_COUNT, MYF(0));
    DBUG_RETURN(-1);
  }
  /*
    Ensure that we have UPDATE or SELECT privilege for each table
    The exact privilege is checked in mysql_multi_update()
  */
  for (table= update_list; table; table= table->next)
  {
5387 5388 5389 5390 5391 5392
    if (table->derived)
      table->grant.privilege= SELECT_ACL;
    else if ((check_access(thd, UPDATE_ACL, table->db,
                           &table->grant.privilege, 0, 1) ||
              grant_option &&
              check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) &&
5393 5394
	(check_access(thd, SELECT_ACL, table->db,
		      &table->grant.privilege, 0, 0) ||
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5395
	 grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
5396
      DBUG_RETURN(1);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5397 5398 5399 5400 5401

    /*
      We assign following flag only to copy of table, because it will
      be checked only if query contains subqueries i.e. only if copy exists
    */
5402
    if (table->table_list)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5403
      table->table_list->table_in_update_from_clause= 1;
5404
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5405 5406 5407
  /*
    Is there tables of subqueries?
  */
5408 5409
  if (&lex->select_lex != lex->all_selects_list)
  {
5410
    DBUG_PRINT("info",("Checking sub query list"));
5411 5412
    for (table= tables; table; table= table->next)
    {
5413 5414 5415 5416
      if (my_tz_check_n_skip_implicit_tables(&table,
                                             lex->time_zone_tables_used))
        continue;
      else if (table->table_in_update_from_clause)
5417 5418 5419 5420 5421 5422 5423 5424 5425
      {
	/*
	  If we check table by local TABLE_LIST copy then we should copy
	  grants to global table list, because it will be used for table
	  opening.
	*/
	if (table->table_list)
	  table->grant= table->table_list->grant;
      }
5426
      else if (!table->derived)
5427 5428 5429
      {
	if (check_access(thd, SELECT_ACL, table->db,
			 &table->grant.privilege, 0, 0) ||
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5430
	    grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))
5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453
	  DBUG_RETURN(1);
      }
    }
  }

  if (select_lex->order_list.elements)
    msg= "ORDER BY";
  else if (select_lex->select_limit && select_lex->select_limit !=
	   HA_POS_ERROR)
    msg= "LIMIT";
  if (msg)
  {
    my_error(ER_WRONG_USAGE, MYF(0), "UPDATE", msg);
    DBUG_RETURN(-1);
  }
  DBUG_RETURN(0);
}

/*
  Multi delete query pre-check

  SYNOPSIS
    multi_delete_precheck()
5454 5455 5456
    thd			Thread handler
    tables		Global table list
    table_count		Pointer to table counter
5457

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5458
  RETURN VALUE
5459 5460 5461
    0   OK
    1   error (message is sent to user)
    -1  error (message is not sent to user)
5462
*/
monty@mysql.com's avatar
monty@mysql.com committed
5463

5464 5465 5466 5467 5468 5469 5470
int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
{
  DBUG_ENTER("multi_delete_precheck");
  SELECT_LEX *select_lex= &thd->lex->select_lex;
  TABLE_LIST *aux_tables=
    (TABLE_LIST *)thd->lex->auxilliary_table_list.first;
  TABLE_LIST *delete_tables= (TABLE_LIST *)select_lex->table_list.first;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5471 5472 5473
  TABLE_LIST *target_tbl;

  *table_count= 0;
5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485

  /* sql_yacc guarantees that tables and aux_tables are not zero */
  DBUG_ASSERT(aux_tables != 0);
  if (check_db_used(thd, tables) || check_db_used(thd,aux_tables) ||
      check_table_access(thd,SELECT_ACL, tables,0) ||
      check_table_access(thd,DELETE_ACL, aux_tables,0))
    DBUG_RETURN(1);
  if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where)
  {
    my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE, MYF(0));
    DBUG_RETURN(-1);
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5486
  for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next)
5487 5488 5489 5490 5491 5492
  {
    (*table_count)++;
    /* All tables in aux_tables must be found in FROM PART */
    TABLE_LIST *walk;
    for (walk= delete_tables; walk; walk= walk->next)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5493 5494 5495
      if (!my_strcasecmp(table_alias_charset,
			 target_tbl->alias, walk->alias) &&
	  !strcmp(walk->db, target_tbl->db))
5496 5497 5498 5499
	break;
    }
    if (!walk)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5500 5501
      my_error(ER_UNKNOWN_TABLE, MYF(0), target_tbl->real_name,
	       "MULTI DELETE");
5502 5503 5504 5505
      DBUG_RETURN(-1);
    }
    if (walk->derived)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5506 5507
      my_error(ER_NON_UPDATABLE_TABLE, MYF(0), target_tbl->real_name,
	       "DELETE");
5508 5509
      DBUG_RETURN(-1);
    }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5510 5511
    walk->lock_type= target_tbl->lock_type;
    target_tbl->table_list= walk;	// Remember corresponding table
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
5512 5513 5514
    
    /* in case of subselects, we need to set lock_type in
     * corresponding table in list of all tables */
5515 5516 5517 5518 5519
    if (walk->table_list)
    {
      target_tbl->table_list= walk->table_list;
      walk->table_list->lock_type= walk->lock_type;
    }
5520 5521 5522 5523 5524
  }
  DBUG_RETURN(0);
}


bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5525 5526 5527 5528 5529
/*
  simple UPDATE query pre-check

  SYNOPSIS
    update_precheck()
5530 5531
    thd		Thread handler
    tables	Global table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5532 5533

  RETURN VALUE
5534 5535 5536
    0   OK
    1   Error (message is sent to user)
    -1  Error (message is not sent to user)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5537
*/
5538

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556
int update_precheck(THD *thd, TABLE_LIST *tables)
{
  DBUG_ENTER("update_precheck");
  if (thd->lex->select_lex.item_list.elements != thd->lex->value_list.elements)
  {
    my_error(ER_WRONG_VALUE_COUNT, MYF(0));
    DBUG_RETURN(-1);
  }
  DBUG_RETURN((check_db_used(thd, tables) ||
	       check_one_table_access(thd, UPDATE_ACL, tables)) ? 1 : 0);
}


/*
  simple DELETE query pre-check

  SYNOPSIS
    delete_precheck()
5557 5558
    thd		Thread handler
    tables	Global table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5559 5560

  RETURN VALUE
5561 5562 5563
    0   OK
    1   error (message is sent to user)
    -1  error (message is not sent to user)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5564
*/
5565

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5566 5567 5568 5569 5570
int delete_precheck(THD *thd, TABLE_LIST *tables)
{
  DBUG_ENTER("delete_precheck");
  if (check_one_table_access(thd, DELETE_ACL, tables))
    DBUG_RETURN(1);
5571
  /* Set privilege for the WHERE clause */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5572 5573 5574 5575 5576 5577 5578 5579 5580 5581
  tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
  DBUG_RETURN(0);
}


/*
  simple INSERT query pre-check

  SYNOPSIS
    insert_precheck()
5582 5583
    thd		Thread handler
    tables	Global table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5584 5585

  RETURN VALUE
5586 5587 5588
    0   OK
    1   error (message is sent to user)
    -1  error (message is not sent to user)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5589
*/
5590

5591
int insert_precheck(THD *thd, TABLE_LIST *tables)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5592 5593 5594 5595
{
  LEX *lex= thd->lex;
  DBUG_ENTER("insert_precheck");

5596 5597 5598 5599
  /*
    Check that we have modify privileges for the first table and
    select privileges for the rest
  */
5600 5601 5602
  ulong privilege= INSERT_ACL |
                   (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
                   (lex->duplicates == DUP_UPDATE ? UPDATE_ACL : 0);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5603 5604

  if (check_one_table_access(thd, privilege, tables))
5605
    DBUG_RETURN(1);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5606

5607
  if (lex->update_list.elements != lex->value_list.elements)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5608 5609 5610 5611
  {
    my_error(ER_WRONG_VALUE_COUNT, MYF(0));
    DBUG_RETURN(-1);
  }
5612 5613
  DBUG_RETURN(0);
}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5614 5615 5616 5617 5618 5619 5620


/*
  CREATE TABLE query pre-check

  SYNOPSIS
    create_table_precheck()
5621 5622 5623
    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
5624 5625

  RETURN VALUE
5626 5627
    0   OK
    1   Error (message is sent to user)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5628
*/
5629

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5630 5631 5632 5633
int create_table_precheck(THD *thd, TABLE_LIST *tables,
			  TABLE_LIST *create_table)
{
  LEX *lex= thd->lex;
5634 5635 5636
  SELECT_LEX *select_lex= &lex->select_lex;
  ulong want_priv;
  int error= 1;                                 // Error message is given
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5637
  DBUG_ENTER("create_table_precheck");
5638 5639 5640

  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
5641 5642 5643 5644 5645 5646
  lex->create_info.alias= create_table->alias;
  if (check_access(thd, want_priv, create_table->db,
		   &create_table->grant.privilege, 0, 0) ||
      check_merge_table_access(thd, create_table->db,
			       (TABLE_LIST *)
			       lex->create_info.merge_list.first))
5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690
    goto err;
  if (grant_option && want_priv != CREATE_TMP_ACL &&
      check_grant(thd, want_priv, create_table, 0, UINT_MAX, 0))
    goto err;

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

    /*
      For temporary tables or PREPARED STATEMETNS we don't have to check
      if the created table exists
    */
    if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
        ! thd->current_arena->is_stmt_prepare() &&
        find_real_table_in_list(tables, create_table->db,
                                create_table->real_name))
    {
      net_printf(thd,ER_UPDATE_TABLE_USED, create_table->real_name);

      goto err;
    }
    if (lex->create_info.used_fields & HA_CREATE_USED_UNION)
    {
      TABLE_LIST *tab;
      for (tab= tables; tab; tab= tab->next)
      {
        if (find_real_table_in_list((TABLE_LIST*) lex->create_info.
                                    merge_list.first,
                                    tables->db, tab->real_name))
        {
          net_printf(thd, ER_UPDATE_TABLE_USED, tab->real_name);
          goto err;
        }
      }  
    }    

    if (tables && check_table_access(thd, SELECT_ACL, tables,0))
      goto err;
  }
  error= 0;

err:
  DBUG_RETURN(error);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5691
}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727


/*
  negate given expression

  SYNOPSIS
    negate_expression()
    thd  therad handler
    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);
}