sql_parse.cc 153 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 18
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include "mysql_priv.h"
#include "sql_acl.h"
19
#include "sql_repl.h"
20
#include "repl_failsafe.h"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
21 22 23 24
#include <m_ctype.h>
#include <myisam.h>
#include <my_dir.h>

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

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

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

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);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
56 57
static void remove_escape(char *name);
static void refresh_status(void);
58 59
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
60

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

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

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

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

93 94 95 96 97 98 99 100 101 102
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
  }
}

103

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


monty@mysql.com's avatar
monty@mysql.com committed
119
#ifdef HAVE_REPLICATION
120 121 122
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
123
          ((thd->lex->sql_command != SQLCOM_DELETE_MULTI) ||
monty@mysql.com's avatar
monty@mysql.com committed
124 125
           !tables_ok(thd,
		      (TABLE_LIST *)thd->lex->auxilliary_table_list.first)));
126
}
monty@mysql.com's avatar
monty@mysql.com committed
127
#endif
128 129


130 131
static HASH hash_user_connections;

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

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

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

184
}
185 186 187


/*
188
    Check if user exist and password supplied is correct. 
189 190
  SYNOPSIS
    check_user()
191 192 193 194 195 196 197 198 199
    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

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

205 206 207
  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;
208 209
   -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
210 211
*/

hf@deer.(none)'s avatar
hf@deer.(none) committed
212 213 214
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
215
{
216
  DBUG_ENTER("check_user");
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
217
  
monty@mysql.com's avatar
monty@mysql.com committed
218 219
#ifdef NO_EMBEDDED_ACCESS_CHECKS
  thd->master_access= GLOBAL_ACLS;			// Full rights
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
  /* 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
235 236
#else

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

257
  /*
258 259 260 261
    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'.
262
  */
263 264
  thd->db= 0;
  thd->db_length= 0;
265
  
266
  USER_RESOURCES ur;
267
  int res= acl_getroot(thd, &ur, passwd, passwd_len);
hf@deer.(none)'s avatar
hf@deer.(none) committed
268
#ifndef EMBEDDED_LIBRARY
269
  if (res == -1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
270
  {
271 272 273 274 275 276
    /*
      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.
    */
277
    NET *net= &thd->net;
278
    if (opt_secure_auth_local)
279
    {
280 281 282 283 284 285
      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);
    }
286
    if (send_old_password_request(thd) ||
287 288 289 290 291 292 293
        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 */
294
    res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323);
295
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
296
#endif /*EMBEDDED_LIBRARY*/
297 298
  /* here res is always >= 0 */
  if (res == 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
299
  {
300
    if (!(thd->master_access & NO_ACCESS)) // authentification is OK 
301
    {
302 303 304 305 306 307 308 309 310 311
      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)
312
      {
313 314
        VOID(pthread_mutex_lock(&LOCK_thread_count));
        bool count_ok= thread_count < max_connections + delayed_insert_threads
315
                       || (thd->master_access & SUPER_ACL);
316 317 318 319 320 321
        VOID(pthread_mutex_unlock(&LOCK_thread_count));
        if (!count_ok)
        {                                         // too many connections 
          send_error(thd, ER_CON_COUNT_ERROR);
          DBUG_RETURN(-1);
        }
322
      }
peter@mysql.com's avatar
peter@mysql.com committed
323

324 325 326 327 328 329 330 331
      /* 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*) "");

332
      /*
333 334 335
        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.
336
      */
337 338 339
      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
340 341 342 343 344 345 346 347 348
      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);
349 350 351

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

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

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

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

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


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

428 429
#ifndef NO_EMBEDDED_ACCESS_CHECKS

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

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

  end:
455 456
  if (error)
    uc->connections--; // no need for decrease_user_connections() here
457
  (void) pthread_mutex_unlock(&LOCK_user_conn);
458
  DBUG_RETURN(error);
459
}
460
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
461

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

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

494

495 496 497 498 499
void free_max_user_conn(void)
{
  hash_free(&hash_user_connections);
}

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

501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
/*
  Mark all commands that somehow changes a table
  This is used to check number of updates / hour
*/

char  uc_update_queries[SQLCOM_END];

void init_update_queries(void)
{
  uc_update_queries[SQLCOM_CREATE_TABLE]=1;
  uc_update_queries[SQLCOM_CREATE_INDEX]=1;
  uc_update_queries[SQLCOM_ALTER_TABLE]=1;
  uc_update_queries[SQLCOM_UPDATE]=1;
  uc_update_queries[SQLCOM_INSERT]=1;
  uc_update_queries[SQLCOM_INSERT_SELECT]=1;
  uc_update_queries[SQLCOM_DELETE]=1;
  uc_update_queries[SQLCOM_TRUNCATE]=1;
  uc_update_queries[SQLCOM_DROP_TABLE]=1;
  uc_update_queries[SQLCOM_LOAD]=1;
  uc_update_queries[SQLCOM_CREATE_DB]=1;
  uc_update_queries[SQLCOM_DROP_DB]=1;
  uc_update_queries[SQLCOM_REPLACE]=1;
  uc_update_queries[SQLCOM_REPLACE_SELECT]=1;
  uc_update_queries[SQLCOM_RENAME_TABLE]=1;
  uc_update_queries[SQLCOM_BACKUP_TABLE]=1;
  uc_update_queries[SQLCOM_RESTORE_TABLE]=1;
  uc_update_queries[SQLCOM_DELETE_MULTI]=1;
  uc_update_queries[SQLCOM_DROP_INDEX]=1;
529
  uc_update_queries[SQLCOM_UPDATE_MULTI]=1;
530 531
}

532 533 534 535
bool is_update_query(enum enum_sql_command command)
{
  return uc_update_queries[command];
}
536

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
537 538 539
/*
  Check if maximum queries per hour limit has been reached
  returns 0 if OK.
540

541 542 543
  In theory we would need a mutex in the USER_CONN structure for this to
  be 100 % safe, but as the worst scenario is that we would miss counting
  a couple of queries, this isn't critical.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
544 545
*/

546

547
static bool check_mqh(THD *thd, uint check_command)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
548
{
549 550 551
#ifdef NO_EMBEDDED_ACCESS_CHECKS
  return(0);
#else
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
552
  bool error=0;
553
  time_t check_time = thd->start_time ?  thd->start_time : time(NULL);
554
  USER_CONN *uc=thd->user_connect;
555
  DBUG_ENTER("check_mqh");
556
  DBUG_ASSERT(uc != 0);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
557

558
  /* If more than a hour since last check, reset resource checking */
559 560 561 562 563 564 565 566 567
  if (check_time  - uc->intime >= 3600)
  {
    (void) pthread_mutex_lock(&LOCK_user_conn);
    uc->questions=1;
    uc->updates=0;
    uc->conn_per_hour=0;
    uc->intime=check_time;
    (void) pthread_mutex_unlock(&LOCK_user_conn);
  }
568
  /* Check that we have not done too many questions / hour */
569 570 571
  if (uc->user_resources.questions &&
      uc->questions++ >= uc->user_resources.questions)
  {
572
    net_printf(thd, ER_USER_LIMIT_REACHED, uc->user, "max_questions",
573 574 575 576
	       (long) uc->user_resources.questions);
    error=1;
    goto end;
  }
577
  if (check_command < (uint) SQLCOM_END)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
578
  {
579 580 581 582
    /* 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)
    {
583
      net_printf(thd, ER_USER_LIMIT_REACHED, uc->user, "max_updates",
584 585 586 587
		 (long) uc->user_resources.updates);
      error=1;
      goto end;
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
588 589
  }
end:
590
  DBUG_RETURN(error);
monty@mysql.com's avatar
monty@mysql.com committed
591
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
592 593
}

594

595
static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0)
596
{
monty@mysql.com's avatar
monty@mysql.com committed
597
#ifndef NO_EMBEDDED_ACCESS_CHECKS
598
  (void) pthread_mutex_lock(&LOCK_user_conn);
peter@mysql.com's avatar
peter@mysql.com committed
599
  if (lu)  // for GRANT
600
  {
601
    USER_CONN *uc;
602
    uint temp_len=lu->user.length+lu->host.length+2;
603 604
    char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2];

605 606
    memcpy(temp_user,lu->user.str,lu->user.length);
    memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length);
607
    temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0;
608
    if ((uc = (struct  user_conn *) hash_search(&hash_user_connections,
609
						(byte*) temp_user, temp_len)))
610 611
    {
      uc->questions=0;
612
      get_mqh(temp_user,&temp_user[lu->user.length+1],uc);
613 614
      uc->updates=0;
      uc->conn_per_hour=0;
615 616
    }
  }
617
  else // for FLUSH PRIVILEGES and FLUSH USER_RESOURCES
618
  {
619
    for (uint idx=0;idx < hash_user_connections.records; idx++)
620
    {
monty@mysql.com's avatar
monty@mysql.com committed
621 622
      USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections,
						      idx);
623 624 625 626 627
      if (get_them)
	get_mqh(uc->user,uc->host,uc);
      uc->questions=0;
      uc->updates=0;
      uc->conn_per_hour=0;
628 629
    }
  }
630
  (void) pthread_mutex_unlock(&LOCK_user_conn);
monty@mysql.com's avatar
monty@mysql.com committed
631
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
632
}
633

bk@work.mysql.com's avatar
bk@work.mysql.com committed
634
/*
635
    Perform handshake, authorize client and update thd ACL variables.
636
  SYNOPSIS
637
    check_connection()
638
    thd  thread handle
639 640

  RETURN
641
     0  success, OK is sent to user, thd is updated.
642 643
    -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
644 645
*/

hf@deer.(none)'s avatar
hf@deer.(none) committed
646 647
#ifndef EMBEDDED_LIBRARY
static int check_connection(THD *thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
648
{
649
  uint connect_errors= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
650
  NET *net= &thd->net;
651

652 653 654
  DBUG_PRINT("info",
             ("New connection received on %s", vio_description(net->vio)));

bk@work.mysql.com's avatar
bk@work.mysql.com committed
655 656
  if (!thd->host)                           // If TCP/IP connection
  {
657
    char ip[30];
658

659
    if (vio_peer_addr(net->vio, ip, &thd->peer_port))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
660
      return (ER_BAD_HOST_ERROR);
661
    if (!(thd->ip= my_strdup(ip,MYF(0))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
662
      return (ER_OUT_OF_RESOURCES);
663
    thd->host_or_ip= thd->ip;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
664 665 666
#if !defined(HAVE_SYS_UN_H) || defined(HAVE_mit_thread)
    /* Fast local hostname resolve for Win32 */
    if (!strcmp(thd->ip,"127.0.0.1"))
667
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
668 669
      thd->host= (char*) my_localhost;
      thd->host_or_ip= my_localhost;
670
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
671 672 673
    else
#endif
    {
674 675 676 677 678 679
      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
680
	{
681
	  thd->host[min(strlen(thd->host), HOSTNAME_LENGTH)]= 0;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
682 683
	  thd->host_or_ip= thd->host;
	}
684 685 686
	if (connect_errors > max_connect_errors)
	  return(ER_HOST_IS_BLOCKED);
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
687
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
688 689 690
    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
691 692 693
    if (acl_check_host(thd->host,thd->ip))
      return(ER_HOST_NOT_PRIVILEGED);
  }
694
  else /* Hostname given means that the connection was on a socket */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
695
  {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
696
    DBUG_PRINT("info",("Host: %s",thd->host));
697 698
    thd->host_or_ip= thd->host;
    thd->ip= 0;
699
    bzero((char*) &thd->remote, sizeof(struct sockaddr));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
700 701
  }
  vio_keepalive(net->vio, TRUE);
702 703
  ulong pkt_len= 0;
  char *end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
704
  {
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
705
    /* buff[] needs to big enough to hold the server_version variable */
706
    char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
707 708
    ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB |
			  CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION);
709

710 711 712 713 714
    if (opt_using_transactions)
      client_flags|=CLIENT_TRANSACTIONS;
#ifdef HAVE_COMPRESS
    client_flags |= CLIENT_COMPRESS;
#endif /* HAVE_COMPRESS */
715 716 717 718
#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
719

720 721 722 723 724 725 726 727 728 729 730
    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
731
      tail: that's why first part of the scramble is placed here, and second
732 733
      part at the end of packet.
    */
734
    end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1;
735 736 737
   
    int2store(end, client_flags);
    /* write server characteristics: up to 16 bytes allowed */
738
    end[2]=(char) default_charset_info->number;
739 740 741 742 743 744 745 746 747
    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
748
			  (uint) (end-buff)) ||
749
	(pkt_len= my_net_read(net)) == packet_error ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
750 751 752 753 754 755 756 757 758 759 760
	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);
761
  if (thd->packet.alloc(thd->variables.net_buffer_length))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
762 763 764
    return(ER_OUT_OF_RESOURCES);

  thd->client_capabilities=uint2korr(net->read_pos);
765 766 767 768 769 770 771 772 773 774 775
#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
776 777 778 779
  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
780 781 782 783 784 785 786
    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
    */
787
    if (!(thd->variables.character_set_client=
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
788 789 790 791
	  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))
792
    {
793 794
      thd->variables.character_set_client=
	global_system_variables.character_set_client;
795 796
      thd->variables.collation_connection=
	global_system_variables.collation_connection;
797 798
      thd->variables.character_set_results=
	global_system_variables.character_set_results;
799 800 801
    }
    else
    {
802
      thd->variables.character_set_results=
803 804 805
      thd->variables.collation_connection= 
	thd->variables.character_set_client;
    }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
806
    thd->update_charset();
807
    end= (char*) net->read_pos+32;
808 809 810 811 812 813 814
  }
  else
  {
    thd->max_client_packet_length= uint3korr(net->read_pos+2);
    end= (char*) net->read_pos+5;
  }

815
  if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
816
    thd->variables.sql_mode|= MODE_IGNORE_SPACE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
817
#ifdef HAVE_OPENSSL
818
  DBUG_PRINT("info", ("client capabilities: %d", thd->client_capabilities));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
819 820 821
  if (thd->client_capabilities & CLIENT_SSL)
  {
    /* Do the SSL layering. */
822 823 824 825 826
    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
827
    DBUG_PRINT("info", ("IO layer change in progress..."));
828 829 830 831 832
    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
833
      return(ER_HANDSHAKE_ERROR);
834
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
835
    DBUG_PRINT("info", ("Reading user information over SSL layer"));
836
    if ((pkt_len= my_net_read(net)) == packet_error ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
837 838
	pkt_len < NORMAL_HANDSHAKE_SIZE)
    {
839 840
      DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
			   pkt_len));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
841 842 843 844
      inc_host_errors(&thd->remote.sin_addr);
      return(ER_HANDSHAKE_ERROR);
    }
  }
845 846 847
#endif

  if (end >= (char*) net->read_pos+ pkt_len +2)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
848
  {
849 850
    inc_host_errors(&thd->remote.sin_addr);
    return(ER_HANDSHAKE_ERROR);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
851 852 853
  }

  if (thd->client_capabilities & CLIENT_INTERACTIVE)
854
    thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
855
  if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
856 857
      opt_using_transactions)
    net->return_status= &thd->server_status;
858
  net->read_timeout=(uint) thd->variables.net_read_timeout;
peter@mysql.com's avatar
peter@mysql.com committed
859

860 861
  char *user= end;
  char *passwd= strend(user)+1;
862
  char *db= passwd;
863
  char db_buff[NAME_LEN+1];                     // buffer to store db in utf8 
864
  char user_buff[USERNAME_LENGTH+1];		// buffer to store user in utf8
865 866 867 868 869 870 871 872 873
  /* 
    Old clients send null-terminated string as password; new clients send
    the size (1 byte) + string (not null-terminated). Hence in case of empty
    password both send '\0'.
  */
  uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? 
    *passwd++ : strlen(passwd);
  db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
    db + passwd_len + 1 : 0;
peter@mysql.com's avatar
peter@mysql.com committed
874

kostja@oak.local's avatar
kostja@oak.local committed
875 876
  /* Since 4.1 all database names are stored in utf8 */
  if (db)
peter@mysql.com's avatar
peter@mysql.com committed
877
  {
878 879 880 881
    db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
                             system_charset_info,
                             db, strlen(db),
                             thd->charset())]= 0;
882
    db= db_buff;
kostja@oak.local's avatar
kostja@oak.local committed
883
  }
peter@mysql.com's avatar
peter@mysql.com committed
884

885 886 887 888 889 890 891 892
  if (user)
  {
    user_buff[copy_and_convert(user_buff, sizeof(user_buff)-1,
			       system_charset_info, user, strlen(user),
			       thd->charset())]= '\0';
    user= user_buff;
  }

893 894
  if (thd->user)
    x_free(thd->user);
895 896
  if (!(thd->user= my_strdup(user, MYF(0))))
    return (ER_OUT_OF_RESOURCES);
897
  return check_user(thd, COM_CONNECT, passwd, passwd_len, db, true);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
898 899
}

900

901 902
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
903 904 905 906
{
  Vio* save_vio;
  ulong save_client_capabilities;

907 908 909 910 911 912 913 914 915
  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
916 917
  save_client_capabilities= thd->client_capabilities;
  thd->client_capabilities|= CLIENT_MULTI_QUERIES;
918 919 920 921
  /*
    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
922 923 924
  save_vio= thd->net.vio;
  thd->net.vio= 0;
  dispatch_command(COM_QUERY, thd, thd->query, thd->query_length+1);
925
  rw_unlock(var_mutex);
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
926 927 928 929 930
  thd->client_capabilities= save_client_capabilities;
  thd->net.vio= save_vio;
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
931 932 933 934
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
935
    (uint) ((thd->thr_create_time = time(NULL)) - thd->connect_time);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
936 937 938 939 940
  if (launch_time >= slow_launch_time)
    statistic_increment(slow_launch_threads,&LOCK_status );

  pthread_detach_this_thread();

941 942
#if !defined( __WIN__) && !defined(OS2)	// Win32 calls this in pthread_create
  // The following calls needs to be done before we call DBUG_ macros
943
  if (!(test_flags & TEST_NO_THREADS) & my_thread_init())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
944
  {
945
    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
946
    statistic_increment(aborted_connects,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
947 948 949 950 951
    end_thread(thd,0);
    return 0;
  }
#endif

952 953 954 955 956 957 958
  /*
    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
959 960 961 962
  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
963
#if defined(__WIN__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
964
  init_signals();				// IRENA; testing ?
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
965
#elif !defined(OS2) && !defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
966 967 968 969 970 971
  sigset_t set;
  VOID(sigemptyset(&set));			// Get mask in use
  VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
#endif
  if (thd->store_globals())
  {
972
    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
973
    statistic_increment(aborted_connects,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
974 975 976 977 978 979 980 981 982 983
    end_thread(thd,0);
    return 0;
  }

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

984
    if ((error=check_connection(thd)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
985 986
    {						// Wrong permissions
      if (error > 0)
987
	net_printf(thd,error,thd->host_or_ip);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
988 989
#ifdef __NT__
      if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
990
	my_sleep(1000);				/* must wait after eof() */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
991
#endif
992
      statistic_increment(aborted_connects,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
993 994
      goto end_thread;
    }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
995 996 997
#ifdef __NETWARE__
    netware_reg_user(thd->ip, thd->user, "MySQL");
#endif
998
    if (thd->variables.max_join_size == HA_POS_ERROR)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
999 1000 1001 1002
      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
1003
    thd->version= refresh_version;
1004
    if (sys_init_connect.value_length && !(thd->master_access & SUPER_ACL))
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1005
    {
1006 1007 1008
      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
1009 1010 1011
    }

    thd->proc_info=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1012
    thd->set_time();
1013
    thd->init_for_queries();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1014 1015 1016 1017 1018
    while (!net->error && net->vio != 0 && !thd->killed)
    {
      if (do_command(thd))
	break;
    }
1019 1020
    if (thd->user_connect)
      decrease_user_connections(thd->user_connect);
1021
    free_root(&thd->mem_root,MYF(0));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1022
    if (net->error && net->vio != 0 && net->report_error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1023
    {
1024
      if (!thd->killed && thd->variables.log_warnings > 1)
1025 1026 1027 1028 1029 1030
	sql_print_error(ER(ER_NEW_ABORTING_CONNECTION),
			thd->thread_id,(thd->db ? thd->db : "unconnected"),
			thd->user ? thd->user : "unauthenticated",
			thd->host_or_ip,
			(net->last_errno ? ER(net->last_errno) :
			 ER(ER_UNKNOWN_ERROR)));
1031
      send_error(thd,net->last_errno,NullS);
1032
      statistic_increment(aborted_threads,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1033
    }
1034 1035 1036 1037
    else if (thd->killed)
    {
      statistic_increment(aborted_threads,&LOCK_status);
    }
1038
    
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1039
end_thread:
1040
    close_connection(thd, 0, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
    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
1052 1053
#endif /* EMBEDDED_LIBRARY */

1054 1055 1056 1057
/*
  Execute commands from bootstrap_file.
  Used when creating the initial grant tables
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1058

1059
extern "C" pthread_handler_decl(handle_bootstrap,arg)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1060
{
1061 1062 1063
  THD *thd=(THD*) arg;
  FILE *file=bootstrap_file;
  char *buff;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1064

1065
  /* The following must be called before DBUG_ENTER */
1066
  if (my_thread_init() || thd->store_globals())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1067
  {
hf@deer.(none)'s avatar
hf@deer.(none) committed
1068
#ifndef EMBEDDED_LIBRARY
1069
    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1070
#endif
1071
    thd->fatal_error();
1072
    goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1073
  }
1074 1075
  DBUG_ENTER("handle_bootstrap");

hf@deer.(none)'s avatar
hf@deer.(none) committed
1076
#ifndef EMBEDDED_LIBRARY
1077 1078
  pthread_detach_this_thread();
  thd->thread_stack= (char*) &thd;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1079
#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1080
  sigset_t set;
1081 1082
  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
1083
#endif
hf@deer.(none)'s avatar
hf@deer.(none) committed
1084
#endif /* EMBEDDED_LIBRARY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1085

1086
  if (thd->variables.max_join_size == HA_POS_ERROR)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1087 1088 1089 1090
    thd->options |= OPTION_BIG_SELECTS;

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

1093
  buff= (char*) thd->net.buff;
1094
  thd->init_for_queries();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1095 1096
  while (fgets(buff, thd->net.max_packet, file))
  {
1097
    uint length=(uint) strlen(buff);
1098 1099 1100 1101 1102 1103
    if (buff[length-1]!='\n' && !feof(file))
    {
      send_error(thd,ER_NET_PACKET_TOO_LARGE, NullS);
      thd->is_fatal_error= 1;
      break;
    }
1104
    while (length && (my_isspace(thd->charset(), buff[length-1]) ||
1105
           buff[length-1] == ';'))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1106 1107
      length--;
    buff[length]=0;
1108
    thd->query_length=length;
1109 1110
    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
1111
    thd->query_id=query_id++;
1112
    if (mqh_used && thd->user_connect && check_mqh(thd, SQLCOM_END))
1113 1114 1115 1116 1117 1118
    {
      thd->net.error = 0;
      close_thread_tables(thd);			// Free tables
      free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
      break;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1119 1120
    mysql_parse(thd,thd->query,length);
    close_thread_tables(thd);			// Free tables
1121
    if (thd->is_fatal_error)
1122
      break;
1123
    free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
1124
    free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1125
  }
1126 1127 1128

  /* thd->fatal_error should be set in case something went wrong */
end:
hf@deer.(none)'s avatar
hf@deer.(none) committed
1129
#ifndef EMBEDDED_LIBRARY
1130 1131 1132
  (void) pthread_mutex_lock(&LOCK_thread_count);
  thread_count--;
  (void) pthread_mutex_unlock(&LOCK_thread_count);
1133
  (void) pthread_cond_broadcast(&COND_thread_count);
1134 1135
  my_thread_end();
  pthread_exit(0);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1136
#endif
1137
  DBUG_RETURN(0);				// Never reached
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1138 1139
}

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

1142
void free_items(Item *item)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1143
{
1144
  for (; item ; item=item->next)
1145
    item->delete_self();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1146 1147
}

1148 1149 1150 1151 1152 1153 1154 1155
    /* 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
1156 1157 1158 1159 1160 1161 1162
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;
1163
  if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST))))
1164
    DBUG_RETURN(1); // out of memory
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1165
  table_list->db = db;
1166
  table_list->real_name = table_list->alias = tbl_name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1167 1168 1169
  table_list->lock_type = TL_READ_NO_INSERT;
  table_list->next = 0;

1170 1171
  if (!db || check_db_name(db))
  {
1172
    net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
1173 1174
    goto err;
  }
1175
  if (lower_case_table_names)
1176
    my_casedn_str(files_charset_info, tbl_name);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1177
  remove_escape(table_list->real_name);
1178 1179 1180 1181

  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
1182
  if (check_one_table_access(thd, SELECT_ACL, table_list))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1183 1184
    goto err;
  thd->free_list = 0;
1185
  thd->query_length=(uint) strlen(tbl_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1186
  thd->query = tbl_name;
1187 1188
  if ((error = mysqld_dump_create_info(thd, table, -1)))
  {
1189
    my_error(ER_GET_ERRNO, MYF(0), my_errno);
1190 1191
    goto err;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1192
  net_flush(&thd->net);
1193
  if ((error= table->file->dump(thd,fd)))
1194
    my_error(ER_GET_ERRNO, MYF(0), error);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1195

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1196 1197
err:
  close_thread_tables(thd);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1198
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1199 1200 1201
}


1202
#ifndef EMBEDDED_LIBRARY
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213

/*
  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
1214 1215 1216
bool do_command(THD *thd)
{
  char *packet;
1217 1218
  uint old_timeout;
  ulong packet_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1219 1220 1221 1222 1223
  NET *net;
  enum enum_server_command command;
  DBUG_ENTER("do_command");

  net= &thd->net;
1224 1225 1226 1227
  /*
    indicator of uninitialized lex => normal flow of errors handling
    (see my_message_sql)
  */
1228
  thd->lex->current_select= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1229 1230

  packet=0;
1231 1232 1233
  old_timeout=net->read_timeout;
  // Wait max for 8 hours
  net->read_timeout=(uint) thd->variables.net_wait_timeout;
1234
  thd->clear_error();				// Clear error message
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1235 1236 1237 1238

  net_new_transaction(net);
  if ((packet_length=my_net_read(net)) == packet_error)
  {
1239 1240 1241 1242 1243
    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)
1244 1245
    {
      statistic_increment(aborted_threads,&LOCK_status);
1246
      DBUG_RETURN(TRUE);			// We have to close it.
1247
    }
1248
    send_error(thd,net->last_errno,NullS);
1249
    net->error= 0;
1250
    DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1251 1252 1253 1254 1255
  }
  else
  {
    packet=(char*) net->read_pos;
    command = (enum enum_server_command) (uchar) packet[0];
1256 1257
    if (command >= COM_END)
      command= COM_END;				// Wrong command
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1258 1259 1260
    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
1261
  }
1262
  net->read_timeout=old_timeout;		// restore it
1263 1264 1265 1266 1267 1268 1269 1270 1271
  /*
    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.
  */
1272
  DBUG_RETURN(dispatch_command(command,thd, packet+1, (uint) packet_length));
1273
}
1274
#endif  /* EMBEDDED_LIBRARY */
1275

1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290
/*
   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
*/
1291

1292 1293 1294 1295
bool dispatch_command(enum enum_server_command command, THD *thd,
		      char* packet, uint packet_length)
{
  NET *net= &thd->net;
1296
  bool error= 0;
1297 1298 1299
  DBUG_ENTER("dispatch_command");

  thd->command=command;
1300 1301 1302 1303
  /*
    Commands which will always take a long time should be marked with
    this so that they will not get logged to the slow query log
  */
1304
  thd->slow_command=FALSE;
1305
  thd->set_time();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1306 1307 1308 1309 1310
  VOID(pthread_mutex_lock(&LOCK_thread_count));
  thd->query_id=query_id;
  if (command != COM_STATISTICS && command != COM_PING)
    query_id++;
  thread_running++;
1311
  /* TODO: set thd->lex->sql_command to SQLCOM_PARSE here */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1312
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
1313

1314 1315
  thd->server_status&=
           ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
1316
  switch (command) {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1317
  case COM_INIT_DB:
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1318 1319 1320 1321 1322 1323 1324 1325 1326
  {
    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;
  }
1327
#ifdef HAVE_REPLICATION
1328 1329
  case COM_REGISTER_SLAVE:
  {
1330
    if (!register_slave(thd, (uchar*)packet, packet_length))
1331
      send_ok(thd);
1332 1333
    break;
  }
1334
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1335
  case COM_TABLE_DUMP:
1336 1337 1338 1339 1340 1341
  {
    char *db, *tbl_name;
    uint db_len= *(uchar*) packet;
    uint tbl_len= *(uchar*) (packet + db_len + 1);

    statistic_increment(com_other, &LOCK_status);
1342
    thd->slow_command= TRUE;
1343 1344 1345 1346 1347 1348 1349
    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
1350 1351
  case COM_CHANGE_USER:
  {
1352
    thd->change_user();
1353
    thd->clear_error();                         // if errors from rollback
1354

1355 1356
    statistic_increment(com_other, &LOCK_status);
    char *user= (char*) packet;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1357
    char *passwd= strend(user)+1;
1358 1359 1360 1361 1362
    /* 
      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).
    */
1363
    char db_buff[NAME_LEN+1];                 // buffer to store db in utf8 
1364 1365 1366 1367
    char *db= passwd;
    uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? 
      *passwd++ : strlen(passwd);
    db+= passwd_len + 1;
1368
#ifndef EMBEDDED_LIBRARY
peter@mysql.com's avatar
peter@mysql.com committed
1369
    /* Small check for incomming packet */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1370
    if ((uint) ((uchar*) db - net->read_pos) > packet_length)
1371 1372 1373 1374
    {
      send_error(thd, ER_UNKNOWN_COM_ERROR);
      break;
    }
1375
#endif
1376
    /* Convert database name to utf8 */
1377 1378 1379 1380
    db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
                             system_charset_info, db, strlen(db),
                             thd->charset())]= 0;
    db= db_buff;
peter@mysql.com's avatar
peter@mysql.com committed
1381

1382 1383 1384 1385 1386 1387 1388
    /* 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;
1389
    USER_CONN *save_user_connect= thd->user_connect;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1390 1391
    
    if (!(thd->user= my_strdup(user, MYF(0))))
1392 1393 1394 1395 1396
    {
      thd->user= save_user;
      send_error(thd, ER_OUT_OF_RESOURCES);
      break;
    }
peter@mysql.com's avatar
peter@mysql.com committed
1397

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

1402 1403 1404 1405 1406 1407 1408 1409
    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;
1410
      thd->user_connect= save_user_connect;
1411 1412 1413 1414 1415 1416 1417 1418
      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
1419 1420
      if (save_user_connect)
	decrease_user_connections(save_user_connect);
1421 1422 1423
      x_free((gptr) save_db);
      x_free((gptr) save_user);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1424 1425
    break;
  }
1426 1427
  case COM_EXECUTE:
  {
1428
    mysql_stmt_execute(thd, packet, packet_length);
1429 1430 1431 1432
    break;
  }
  case COM_LONG_DATA:
  {
1433
    mysql_stmt_get_longdata(thd, packet, packet_length);
1434 1435 1436 1437
    break;
  }
  case COM_PREPARE:
  {
1438
    mysql_stmt_prepare(thd, packet, packet_length);
1439 1440
    break;
  }
1441 1442 1443 1444 1445
  case COM_CLOSE_STMT:
  {
    mysql_stmt_free(thd, packet);
    break;
  }
1446 1447 1448 1449 1450
  case COM_RESET_STMT:
  {
    mysql_stmt_reset(thd, packet);
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1451 1452
  case COM_QUERY:
  {
1453 1454
    if (alloc_query(thd, packet, packet_length))
      break;					// fatal error is set
1455
    mysql_log.write(thd,command,"%s",thd->query);
1456
    DBUG_PRINT("query",("%-.4096s",thd->query));
1457
    mysql_parse(thd,thd->query, thd->query_length);
1458

1459
    while (!thd->killed && !thd->is_fatal_error && thd->lex->found_colon)
1460
    {
1461
      char *packet= thd->lex->found_colon;
1462
      /*
1463
        Multiple queries exits, execute them individually
1464
	in embedded server - just store them to be executed later 
1465
      */
1466
#ifndef EMBEDDED_LIBRARY
1467
      if (thd->lock || thd->open_tables || thd->derived_tables)
1468
        close_thread_tables(thd);
1469 1470
#endif
      ulong length= thd->query_length-(ulong)(packet-thd->query);
1471

1472
      /* Remove garbage at start of query */
1473
      while (my_isspace(thd->charset(), *packet) && length > 0)
1474 1475 1476 1477
      {
        packet++;
        length--;
      }
monty@mysql.com's avatar
monty@mysql.com committed
1478
      VOID(pthread_mutex_lock(&LOCK_thread_count));
1479
      thd->query_length= length;
1480 1481
      thd->query= packet;
      thd->query_id= query_id++;
1482
      /* TODO: set thd->lex->sql_command to SQLCOM_PARSE here */
1483
      VOID(pthread_mutex_unlock(&LOCK_thread_count));
1484
#ifndef EMBEDDED_LIBRARY
1485
      mysql_parse(thd, packet, length);
1486
#else
hf@deer.(none)'s avatar
hf@deer.(none) committed
1487 1488 1489 1490 1491 1492 1493 1494 1495 1496
      /*
	'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
1497
	thd->query_rest.copy(packet, length, thd->query_rest.charset());
1498 1499
      break;
#endif /*EMBEDDED_LIBRARY*/
1500 1501
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1502 1503 1504 1505 1506
    if (!(specialflag & SPECIAL_NO_PRIOR))
      my_pthread_setprio(pthread_self(),WAIT_PRIOR);
    DBUG_PRINT("info",("query ready"));
    break;
  }
1507
  case COM_FIELD_LIST:				// This isn't actually needed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1508
#ifdef DONT_ALLOW_SHOW_COMMANDS
1509
    send_error(thd,ER_NOT_ALLOWED_COMMAND);	/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1510 1511 1512
    break;
#else
  {
1513
    char *fields, *pend;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1514
    TABLE_LIST table_list;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1515 1516
    LEX_STRING conv_name;

1517
    statistic_increment(com_stat[SQLCOM_SHOW_FIELDS],&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1518 1519 1520
    bzero((char*) &table_list,sizeof(table_list));
    if (!(table_list.db=thd->db))
    {
1521
      send_error(thd,ER_NO_DB_ERROR);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1522 1523 1524
      break;
    }
    thd->free_list=0;
1525
    pend= strend(packet);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1526 1527 1528
    thd->convert_string(&conv_name, system_charset_info,
			packet, (uint) (pend-packet), thd->charset());
    table_list.alias= table_list.real_name= conv_name.str;
1529
    packet= pend+1;
1530
    // command not cachable => no gap for data base name
1531 1532
    if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1)))
      break;
1533
    mysql_log.write(thd,command,"%s %s",table_list.real_name,fields);
1534
    if (lower_case_table_names)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1535
      my_casedn_str(files_charset_info, table_list.real_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1536 1537
    remove_escape(table_list.real_name);	// This can't have wildcards

monty@mysql.com's avatar
monty@mysql.com committed
1538 1539
    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
1540
      break;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1541 1542
    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
1543 1544
      break;
    mysqld_list_fields(thd,&table_list,fields);
1545
    free_items(thd->free_list);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1546 1547 1548 1549
    break;
  }
#endif
  case COM_QUIT:
1550
    /* We don't calculate statistics for this command */
1551
    mysql_log.write(thd,command,NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1552 1553 1554 1555
    net->error=0;				// Don't give 'abort' message
    error=TRUE;					// End server
    break;

1556
  case COM_CREATE_DB:				// QQ: To be removed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1557
    {
1558 1559
      char *db=thd->strdup(packet), *alias;

1560
      statistic_increment(com_stat[SQLCOM_CREATE_DB],&LOCK_status);
1561
      // null test to handle EOM
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1562
      if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
1563
      {
1564
	net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
1565 1566
	break;
      }
hf@deer.(none)'s avatar
hf@deer.(none) committed
1567
      if (check_access(thd,CREATE_ACL,db,0,1,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1568
	break;
1569
      mysql_log.write(thd,command,packet);
1570
      mysql_create_db(thd,(lower_case_table_names == 2 ? alias : db),0,0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1571 1572
      break;
    }
1573
  case COM_DROP_DB:				// QQ: To be removed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1574
    {
1575
      statistic_increment(com_stat[SQLCOM_DROP_DB],&LOCK_status);
1576
      char *db=thd->strdup(packet), *alias;
1577
      // null test to handle EOM
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1578
      if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
1579
      {
1580
	net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
1581 1582
	break;
      }
hf@deer.(none)'s avatar
hf@deer.(none) committed
1583
      if (check_access(thd,DROP_ACL,db,0,1,0))
1584
	break;
1585 1586
      if (thd->locked_tables || thd->active_transaction())
      {
1587
	send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1588
	break;
1589
      }
1590
      mysql_log.write(thd,command,db);
1591
      mysql_rm_db(thd,alias,0,0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1592 1593
      break;
    }
1594
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1595 1596
  case COM_BINLOG_DUMP:
    {
1597
      statistic_increment(com_other,&LOCK_status);
1598
      thd->slow_command = TRUE;
1599
      if (check_global_access(thd, REPL_SLAVE_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1600
	break;
1601
      mysql_log.write(thd,command, 0);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1602

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1603 1604
      ulong pos;
      ushort flags;
1605
      uint32 slave_server_id;
1606
      /* TODO: The following has to be changed to an 8 byte integer */
1607 1608
      pos = uint4korr(packet);
      flags = uint2korr(packet + 4);
1609
      thd->server_id=0; /* avoid suicide */
1610
      if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1611
	kill_zombie_dump_threads(slave_server_id);
1612
      thd->server_id = slave_server_id;
1613
      mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
1614
      unregister_slave(thd,1,1);
1615 1616 1617
      // 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
1618 1619
      break;
    }
1620
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1621 1622
  case COM_REFRESH:
    {
1623
      statistic_increment(com_stat[SQLCOM_FLUSH],&LOCK_status);
1624
      ulong options= (ulong) (uchar) packet[0];
1625
      if (check_global_access(thd,RELOAD_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1626
	break;
1627
      mysql_log.write(thd,command,NullS);
1628 1629 1630 1631
      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
1632 1633
      break;
    }
1634
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1635
  case COM_SHUTDOWN:
1636
  {
1637
    statistic_increment(com_other,&LOCK_status);
1638
    if (check_global_access(thd,SHUTDOWN_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1639
      break; /* purecov: inspected */
1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654
    enum enum_shutdown_level level= (packet_length >= 2) ?
      (enum enum_shutdown_level) (uchar) packet[0] : SHUTDOWN_DEFAULT;
    DBUG_PRINT("quit",("Got shutdown command for level %u", level));
    /*
      Accept old mysql_shutdown (with no argument). For now we do nothing of
      the argument.
    */
    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;
    }
1655
    mysql_log.write(thd,command,NullS);
1656
    send_eof(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1657 1658 1659
#ifdef __WIN__
    sleep(1);					// must wait after eof()
#endif
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1660
#ifndef OS2
1661
    send_eof(thd);				// This is for 'quit request'
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1662
#endif
1663
    close_connection(thd, 0, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1664
    close_thread_tables(thd);			// Free before kill
1665
    free_root(&thd->mem_root,MYF(0));
1666
    free_root(&thd->transaction.mem_root,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1667 1668 1669
    kill_mysql();
    error=TRUE;
    break;
1670
  }
1671
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1672 1673
  case COM_STATISTICS:
  {
1674
    mysql_log.write(thd,command,NullS);
1675
    statistic_increment(com_stat[SQLCOM_SHOW_STATUS],&LOCK_status);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1676
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1677
    char buff[200];
hf@deer.(none)'s avatar
hf@deer.(none) committed
1678 1679 1680
#else
    char *buff= thd->net.last_error;
#endif
1681
    ulong uptime = (ulong) (thd->start_time - start_time);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1682
    sprintf((char*) buff,
1683
	    "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
1684 1685 1686 1687 1688
	    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
1689
    if (sf_malloc_cur_memory)				// Using SAFEMALLOC
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1690
      sprintf(strend(buff), "  Memory in use: %ldK  Max memory used: %ldK",
1691 1692
	      (sf_malloc_cur_memory+1023L)/1024L,
	      (sf_malloc_max_memory+1023L)/1024L);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1693 1694
#endif
#ifndef EMBEDDED_LIBRARY
1695
    VOID(my_net_write(net, buff,(uint) strlen(buff)));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1696
    VOID(net_flush(net));
hf@deer.(none)'s avatar
hf@deer.(none) committed
1697
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1698 1699 1700
    break;
  }
  case COM_PING:
1701
    statistic_increment(com_other,&LOCK_status);
1702
    send_ok(thd);				// Tell client we are alive
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1703 1704
    break;
  case COM_PROCESS_INFO:
1705
    statistic_increment(com_stat[SQLCOM_SHOW_PROCESSLIST],&LOCK_status);
1706
    if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1707
      break;
1708
    mysql_log.write(thd,command,NullS);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1709 1710
    mysqld_list_processes(thd,
			  thd->master_access & PROCESS_ACL ? 
monty@mysql.com's avatar
monty@mysql.com committed
1711
			  NullS : thd->priv_user, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1712 1713 1714
    break;
  case COM_PROCESS_KILL:
  {
1715
    statistic_increment(com_stat[SQLCOM_KILL],&LOCK_status);
1716
    ulong id=(ulong) uint4korr(packet);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1717 1718 1719
    kill_one_thread(thd,id);
    break;
  }
1720 1721 1722 1723 1724 1725 1726
  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;
1727
      send_eof(thd);
1728 1729 1730
      break;
    case MYSQL_OPTION_MULTI_STATEMENTS_OFF:
      thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS;
1731
      send_eof(thd);
1732 1733 1734 1735 1736 1737 1738
      break;
    default:
      send_error(thd, ER_UNKNOWN_COM_ERROR);
      break;
    }
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1739
  case COM_DEBUG:
1740
    statistic_increment(com_other,&LOCK_status);
1741
    if (check_global_access(thd, SUPER_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1742 1743
      break;					/* purecov: inspected */
    mysql_print_status(thd);
1744
    mysql_log.write(thd,command,NullS);
1745
    send_eof(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1746 1747 1748 1749 1750
    break;
  case COM_SLEEP:
  case COM_CONNECT:				// Impossible here
  case COM_TIME:				// Impossible from client
  case COM_DELAYED_INSERT:
1751
  case COM_END:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1752
  default:
1753
    send_error(thd, ER_UNKNOWN_COM_ERROR);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1754 1755
    break;
  }
1756
  if (thd->lock || thd->open_tables || thd->derived_tables)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1757 1758 1759 1760 1761
  {
    thd->proc_info="closing tables";
    close_thread_tables(thd);			/* Free tables */
  }

1762
  if (thd->is_fatal_error)
1763
    send_error(thd,0);				// End of memory ?
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1764 1765

  time_t start_of_query=thd->start_time;
1766
  thd->end_time();				// Set start time
1767

1768
  /* If not reading from backup and if the query took too long */
1769
  if (!thd->slow_command && !thd->user_time) // do not log 'slow_command' queries
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1770
  {
1771 1772
    thd->proc_info="logging slow query";

1773 1774
    if ((ulong) (thd->start_time - thd->time_after_lock) >
	thd->variables.long_query_time ||
1775 1776
	((thd->server_status &
	  (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
1777
	 (specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES)))
1778 1779 1780 1781
    {
      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
1782
  }
1783
  thd->proc_info="cleaning up";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1784 1785 1786 1787
  VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
  thd->proc_info=0;
  thd->command=COM_SLEEP;
  thd->query=0;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1788
  thd->query_length=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1789 1790
  thread_running--;
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
1791
  thd->packet.shrink(thd->variables.net_buffer_length);	// Reclaim some memory
1792
  free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1793 1794 1795
  DBUG_RETURN(error);
}

1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813

/*
  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
1814
  /* Remove garbage at start and end of query */
1815
  while (my_isspace(thd->charset(),packet[0]) && packet_length > 0)
1816 1817 1818 1819 1820
  {
    packet++;
    packet_length--;
  }
  char *pos=packet+packet_length;		// Point at end null
peter@mysql.com's avatar
peter@mysql.com committed
1821
  while (packet_length > 0 &&
1822
	 (pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1])))
1823 1824 1825 1826 1827
  {
    pos--;
    packet_length--;
  }
  /* We must allocate some extra memory for query cache */
monty@mysql.com's avatar
monty@mysql.com committed
1828
  thd->query_length= 0;                        // Extra safety: Avoid races
1829 1830
  if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet),
					      packet_length,
1831 1832
					      thd->db_length+ 1 +
					      QUERY_CACHE_FLAGS_SIZE)))
1833 1834 1835
    return 1;
  thd->query[packet_length]=0;
  thd->query_length= packet_length;
1836 1837 1838 1839

  /* Reclaim some memory */
  thd->packet.shrink(thd->variables.net_buffer_length);
  thd->convert_buffer.shrink(thd->variables.net_buffer_length);
1840 1841 1842 1843 1844 1845

  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
1846 1847 1848 1849 1850 1851
/****************************************************************************
** mysql_execute_command
** Execute command saved in thd and current_lex->sql_command
****************************************************************************/

void
1852
mysql_execute_command(THD *thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1853
{
1854
  int	res= 0;
1855
  LEX	*lex= thd->lex;
1856
  TABLE_LIST *tables= (TABLE_LIST*) lex->select_lex.table_list.first;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1857
  SELECT_LEX *select_lex= &lex->select_lex;
1858
  SELECT_LEX_UNIT *unit= &lex->unit;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1859 1860
  DBUG_ENTER("mysql_execute_command");

1861 1862 1863 1864 1865 1866
  /*
    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.
  */
1867
  if (tables || &lex->select_lex != lex->all_selects_list)
1868 1869
    mysql_reset_errors(thd);

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1870
#ifdef HAVE_REPLICATION
1871 1872
  if (thd->slave_thread)
  {
peter@mysql.com's avatar
peter@mysql.com committed
1873
    /*
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
1874 1875 1876
      Skip if we are in the slave thread, some table rules have been
      given and the table list says the query should not be replicated
    */
1877
    if (all_tables_not_ok(thd,tables))
guilhem@mysql.com's avatar
guilhem@mysql.com committed
1878 1879 1880
    {
      /* we warn the slave SQL thread */
      my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
1881
      DBUG_VOID_RETURN;
guilhem@mysql.com's avatar
guilhem@mysql.com committed
1882
    }
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
1883 1884
#ifndef TO_BE_DELETED
    /*
1885 1886 1887
      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
1888
    */
1889 1890 1891
    if (lex->sql_command == SQLCOM_SELECT)
    {
      lex->sql_command = SQLCOM_DO;
1892
      lex->insert_list = &select_lex->item_list;
1893
    }
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
1894
#endif
1895
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
1896
#endif /* !HAVE_REPLICATION */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1897
  if (&lex->select_lex != lex->all_selects_list &&
1898
      lex->unit.create_total_list(thd, lex, &tables))
1899
    DBUG_VOID_RETURN;
1900

1901 1902 1903 1904 1905
  /*
    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
1906
      !(thd->slave_thread || (thd->master_access & SUPER_ACL)) &&
1907 1908
      (uc_update_queries[lex->sql_command] > 0))
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
1909
    net_printf(thd, ER_OPTION_PREVENTS_STATEMENT, "--read-only");
1910 1911
    DBUG_VOID_RETURN;
  }
1912

1913
  statistic_increment(com_stat[lex->sql_command],&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1914 1915 1916
  switch (lex->sql_command) {
  case SQLCOM_SELECT:
  {
1917 1918 1919 1920 1921 1922 1923
    /* 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;
    }

1924
    select_result *result=lex->result;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1925 1926 1927 1928 1929
    if (tables)
    {
      res=check_table_access(thd,
			     lex->exchange ? SELECT_ACL | FILE_ACL :
			     SELECT_ACL,
hf@deer.(none)'s avatar
hf@deer.(none) committed
1930
			     tables,0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1931 1932 1933
    }
    else
      res=check_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
hf@deer.(none)'s avatar
hf@deer.(none) committed
1934
		       any_db,0,0,0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1935 1936 1937 1938 1939
    if (res)
    {
      res=0;
      break;					// Error message is given
    }
1940 1941 1942 1943
    /* 
       In case of single SELECT unit->global_parameters points on first SELECT
       TODO: move counters to SELECT_LEX
    */
1944 1945 1946
    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
1947
    if (unit->select_limit_cnt <
1948
	(ha_rows) unit->global_parameters->select_limit)
1949
      unit->select_limit_cnt= HA_POS_ERROR;		// no limit
1950
    if (unit->select_limit_cnt == HA_POS_ERROR && !select_lex->next_select())
1951
      select_lex->options&= ~OPTION_FOUND_ROWS;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1952 1953

    if (!(res=open_and_lock_tables(thd,tables)))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1954
    {
1955
      if (lex->describe)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1956
      {
1957 1958 1959 1960 1961 1962 1963
	if (!(result= new select_send()))
	{
	  send_error(thd, ER_OUT_OF_RESOURCES);
	  DBUG_VOID_RETURN;
	}
	else
	  thd->send_explain_fields(result);
1964
	res= mysql_explain_union(thd, &thd->lex->unit, result);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1965 1966
	MYSQL_LOCK *save_lock= thd->lock;
	thd->lock= (MYSQL_LOCK *)0;
1967 1968 1969 1970 1971
	if (lex->describe & DESCRIBE_EXTENDED)
	{
	  char buff[1024];
	  String str(buff,(uint32) sizeof(buff), system_charset_info);
	  str.length(0);
1972
	  thd->lex->unit.print(&str);
1973 1974 1975 1976
	  str.append('\0');
	  push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
		       ER_YES, str.ptr());
	}
1977
	result->send_eof();
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1978 1979 1980 1981
	thd->lock= save_lock;
      }
      else
      {
1982 1983
	if (!result)
	{
1984
	  if (!(result=new select_send()))
1985 1986 1987 1988 1989
	  {
	    res= -1;
	    break;
	  }
	}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1990 1991 1992
	query_cache_store_query(thd, tables);
	res=handle_select(thd, lex, result);
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1993
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1994 1995
    break;
  }
sergefp@mysql.com's avatar
sergefp@mysql.com committed
1996
  case SQLCOM_PREPARE:
1997
  {
1998 1999 2000 2001
    char *query_str;
    uint query_len;
    if (lex->prepared_stmt_code_is_varref)
    {
2002
      /* This is PREPARE stmt FROM @var. */
2003
      String str;
2004
      String *pstr;
2005 2006 2007 2008
      CHARSET_INFO *to_cs= thd->variables.collation_connection;
      bool need_conversion;
      user_var_entry *entry;
      uint32 unused;
2009
      /*
2010 2011 2012
        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.
2013
      */
2014 2015
      if ((entry=
             (user_var_entry*)hash_search(&thd->user_vars,
2016 2017 2018 2019
                                          (byte*)lex->prepared_stmt_code.str,
                                          lex->prepared_stmt_code.length))
          && entry->value)
      {
2020 2021 2022 2023 2024 2025 2026
        String *pstr;
        my_bool is_var_null;
        pstr= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC);
        DBUG_ASSERT(!is_var_null);
        if (!pstr)
          send_error(thd, ER_OUT_OF_RESOURCES);
        DBUG_ASSERT(pstr == &str);
2027 2028
      }
      else
2029 2030 2031 2032 2033 2034
        str.set("NULL", 4, &my_charset_latin1);
      need_conversion=
        String::needs_conversion(str.length(), str.charset(), to_cs, &unused);

      query_len= need_conversion? (str.length() * to_cs->mbmaxlen) :
                                  str.length();
2035 2036
      if (!(query_str= alloc_root(&thd->mem_root, query_len+1)))
        send_error(thd, ER_OUT_OF_RESOURCES);
2037

2038
      if (need_conversion)
2039 2040
        query_len= copy_and_convert(query_str, query_len, to_cs, str.ptr(),
                                    str.length(), str.charset());
2041
      else
2042
        memcpy(query_str, str.ptr(), str.length());
2043
      query_str[query_len]= 0;
2044 2045 2046
    }
    else
    {
2047 2048
      query_str= lex->prepared_stmt_code.str;
      query_len= lex->prepared_stmt_code.length;
2049
      DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n",
2050 2051
                          lex->prepared_stmt_name.length,
                          lex->prepared_stmt_name.str,
2052
                          query_len, query_str));
2053
    }
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2054
    thd->command= COM_PREPARE;
2055 2056
    if (!mysql_stmt_prepare(thd, query_str, query_len + 1,
                            &lex->prepared_stmt_name))
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2057 2058 2059 2060 2061
      send_ok(thd, 0L, 0L, "Statement prepared");
    break;
  }
  case SQLCOM_EXECUTE:
  {
2062
    DBUG_PRINT("info", ("EXECUTE: %.*s\n",
2063 2064 2065
                        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
2066 2067 2068 2069 2070
    lex->prepared_stmt_params.empty();
    break;
  }
  case SQLCOM_DEALLOCATE_PREPARE:
  {
2071 2072 2073 2074 2075
    Statement* stmt;
    DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n", 
                        lex->prepared_stmt_name.length,
                        lex->prepared_stmt_name.str));
    if ((stmt= thd->stmt_map.find_by_name(&lex->prepared_stmt_name)))
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2076
    {
2077 2078
      thd->stmt_map.erase(stmt);
      send_ok(thd);
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2079
    }
2080
    else
2081 2082 2083 2084 2085 2086
    {
      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
2087 2088
    break;
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2089
  case SQLCOM_DO:
hf@deer.(none)'s avatar
hf@deer.(none) committed
2090
    if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
2091 2092 2093 2094 2095 2096
		   (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
2097 2098
    break;

2099
  case SQLCOM_EMPTY_QUERY:
2100
    send_ok(thd);
2101 2102
    break;

2103 2104 2105 2106
  case SQLCOM_HELP:
    res= mysqld_help(thd,lex->help_arg);
    break;

2107
#ifndef EMBEDDED_LIBRARY
2108
  case SQLCOM_PURGE:
2109
  {
2110
    if (check_global_access(thd, SUPER_ACL))
2111
      goto error;
2112
    // PURGE MASTER LOGS TO 'file'
2113 2114 2115
    res = purge_master_logs(thd, lex->to_log);
    break;
  }
2116 2117 2118 2119 2120 2121 2122 2123
  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;
  }
2124
#endif
2125 2126
  case SQLCOM_SHOW_WARNS:
  {
2127 2128
    res= mysqld_show_warnings(thd, (ulong)
			      ((1L << (uint) MYSQL_ERROR::WARN_LEVEL_NOTE) |
2129 2130 2131
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_WARN) |
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)
			       ));
2132 2133 2134 2135
    break;
  }
  case SQLCOM_SHOW_ERRORS:
  {
2136 2137
    res= mysqld_show_warnings(thd, (ulong)
			      (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
2138 2139
    break;
  }
2140 2141
  case SQLCOM_SHOW_NEW_MASTER:
  {
2142
    if (check_global_access(thd, REPL_SLAVE_ACL))
2143
      goto error;
2144
    /* This query don't work now. See comment in repl_failsafe.cc */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2145
#ifndef WORKING_NEW_MASTER
2146
    net_printf(thd, ER_NOT_SUPPORTED_YET, "SHOW NEW MASTER");
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2147 2148
    res= 1;
#else
2149
    res = show_new_master(thd);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2150
#endif
2151 2152
    break;
  }
2153

2154
#ifdef HAVE_REPLICATION
2155 2156
  case SQLCOM_SHOW_SLAVE_HOSTS:
  {
2157
    if (check_global_access(thd, REPL_SLAVE_ACL))
2158 2159 2160 2161
      goto error;
    res = show_slave_hosts(thd);
    break;
  }
2162 2163
  case SQLCOM_SHOW_BINLOG_EVENTS:
  {
2164
    if (check_global_access(thd, REPL_SLAVE_ACL))
2165 2166 2167 2168
      goto error;
    res = show_binlog_events(thd);
    break;
  }
2169 2170
#endif

2171
  case SQLCOM_BACKUP_TABLE:
2172 2173
  {
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2174
	check_table_access(thd,SELECT_ACL, tables,0) ||
2175
	check_global_access(thd, FILE_ACL))
2176
      goto error; /* purecov: inspected */
2177
    thd->slow_command=TRUE;
2178
    res = mysql_backup_table(thd, tables);
2179

2180 2181
    break;
  }
2182
  case SQLCOM_RESTORE_TABLE:
2183 2184
  {
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2185
	check_table_access(thd, INSERT_ACL, tables,0) ||
2186
	check_global_access(thd, FILE_ACL))
2187
      goto error; /* purecov: inspected */
2188
    thd->slow_command=TRUE;
2189 2190 2191
    res = mysql_restore_table(thd, tables);
    break;
  }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2192 2193 2194
  case SQLCOM_ASSIGN_TO_KEYCACHE:
  {
    if (check_db_used(thd, tables) ||
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2195 2196
        check_access(thd, INDEX_ACL, tables->db,
                     &tables->grant.privilege, 0, 0))
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2197
      goto error;
2198
    res= mysql_assign_to_keycache(thd, tables, &lex->name_and_length);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2199 2200
    break;
  }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2201 2202 2203
  case SQLCOM_PRELOAD_KEYS:
  {
    if (check_db_used(thd, tables) ||
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2204 2205
	check_access(thd, INDEX_ACL, tables->db,
                     &tables->grant.privilege, 0, 0))
2206
      goto error;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2207 2208 2209
    res = mysql_preload_keys(thd, tables);
    break;
  }
2210
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2211
  case SQLCOM_CHANGE_MASTER:
2212
  {
2213
    if (check_global_access(thd, SUPER_ACL))
2214
      goto error;
2215
    pthread_mutex_lock(&LOCK_active_mi);
2216
    res = change_master(thd,active_mi);
2217
    pthread_mutex_unlock(&LOCK_active_mi);
2218 2219
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2220
  case SQLCOM_SHOW_SLAVE_STAT:
2221
  {
2222 2223
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2224
      goto error;
2225
    pthread_mutex_lock(&LOCK_active_mi);
2226
    res = show_master_info(thd,active_mi);
2227
    pthread_mutex_unlock(&LOCK_active_mi);
2228 2229
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2230
  case SQLCOM_SHOW_MASTER_STAT:
2231
  {
2232 2233
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2234 2235 2236 2237
      goto error;
    res = show_binlog_info(thd);
    break;
  }
peter@mysql.com's avatar
peter@mysql.com committed
2238

2239
  case SQLCOM_LOAD_MASTER_DATA: // sync with master
2240
    if (check_global_access(thd, SUPER_ACL))
2241
      goto error;
2242 2243 2244 2245
    if (end_active_trans(thd))
      res= -1;
    else
      res = load_master_data(thd);
2246
    break;
2247
#endif /* HAVE_REPLICATION */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2248 2249 2250
#ifdef HAVE_INNOBASE_DB
  case SQLCOM_SHOW_INNODB_STATUS:
    {
2251
      if (check_global_access(thd, SUPER_ACL))
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2252 2253 2254 2255 2256
	goto error;
      res = innodb_show_status(thd);
      break;
    }
#endif
2257
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2258
  case SQLCOM_LOAD_MASTER_TABLE:
2259
  {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2260 2261
    if (!tables->db)
      tables->db=thd->db;
hf@deer.(none)'s avatar
hf@deer.(none) committed
2262
    if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege,0,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2263 2264 2265 2266
      goto error;				/* purecov: inspected */
    if (grant_option)
    {
      /* Check that the first table has CREATE privilege */
2267
      if (check_grant(thd, CREATE_ACL, tables, 0, 1, 0))
2268
	goto error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2269
    }
2270
    if (strlen(tables->real_name) > NAME_LEN)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2271
    {
2272
      net_printf(thd,ER_WRONG_TABLE_NAME, tables->real_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2273 2274
      break;
    }
2275
    pthread_mutex_lock(&LOCK_active_mi);
2276 2277 2278 2279
    /*
      fetch_master_table will send the error to the client on failure.
      Give error if the table already exists.
    */
2280
    if (!fetch_master_table(thd, tables->db, tables->real_name,
2281
			    active_mi, 0, 0))
2282
    {
2283
      send_ok(thd);
2284
    }
2285
    pthread_mutex_unlock(&LOCK_active_mi);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2286
    break;
2287
  }
2288
#endif /* HAVE_REPLICATION */
2289

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2290
  case SQLCOM_CREATE_TABLE:
2291
  {
monty@mysql.com's avatar
monty@mysql.com committed
2292
    /* Skip first table, which is the table we are creating */
2293 2294 2295
    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
2296

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

2300 2301 2302
#ifndef HAVE_READLINK
    lex->create_info.data_file_name=lex->create_info.index_file_name=0;
#else
2303
    /* Fix names if symlinked tables */
2304
    if (append_file_to_dir(thd, &lex->create_info.data_file_name,
monty@mysql.com's avatar
monty@mysql.com committed
2305
			   create_table->real_name) ||
2306
	append_file_to_dir(thd,&lex->create_info.index_file_name,
monty@mysql.com's avatar
monty@mysql.com committed
2307
			   create_table->real_name))
2308 2309
    {
      res=-1;
2310
      goto unsent_create_error;
2311
    }
2312
#endif
2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325
    /*
      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;
    }
2326
    if (select_lex->item_list.elements)		// With select
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2327 2328 2329 2330
    {
      select_result *result;

      if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
monty@mysql.com's avatar
monty@mysql.com committed
2331 2332
	  find_real_table_in_list(tables, create_table->db,
				  create_table->real_name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2333
      {
monty@mysql.com's avatar
monty@mysql.com committed
2334
	net_printf(thd,ER_UPDATE_TABLE_USED, create_table->real_name);
2335
	goto create_error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2336
      }
monty@mysql.com's avatar
monty@mysql.com committed
2337
      if (tables && check_table_access(thd, SELECT_ACL, tables,0))
2338
	goto create_error;			// Error message is given
2339
      select_lex->options|= SELECT_NO_UNLOCK;
2340 2341 2342 2343
      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
2344
	unit->select_limit_cnt= HA_POS_ERROR;	// No limit
2345

monty@mysql.com's avatar
monty@mysql.com committed
2346
      if (!(res=open_and_lock_tables(thd,tables)))
2347
      {
monty@mysql.com's avatar
monty@mysql.com committed
2348 2349 2350 2351
	res= -1;				// If error
        if ((result=new select_create(create_table->db,
                                      create_table->real_name,
				      &lex->create_info,
2352 2353 2354 2355
                                      lex->create_list,
                                      lex->key_list,
                                      select_lex->item_list,lex->duplicates)))
          res=handle_select(thd, lex, result);
2356 2357 2358
	//reset for PS
	lex->create_list.empty();
	lex->key_list.empty();
2359 2360
      }
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2361 2362
    else // regular create
    {
venu@myvenu.com's avatar
venu@myvenu.com committed
2363
      if (lex->name)
monty@mysql.com's avatar
monty@mysql.com committed
2364
        res= mysql_create_like_table(thd, create_table, &lex->create_info, 
venu@myvenu.com's avatar
venu@myvenu.com committed
2365 2366
                                     (Table_ident *)lex->name); 
      else
2367
      {
monty@mysql.com's avatar
monty@mysql.com committed
2368 2369
        res= mysql_create_table(thd,create_table->db,
			         create_table->real_name, &lex->create_info,
venu@myvenu.com's avatar
venu@myvenu.com committed
2370 2371
			         lex->create_list,
			         lex->key_list,0,0,0); // do logging
2372
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2373
      if (!res)
2374
	send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2375
    }
2376

2377
    // put tables back for PS rexecuting
2378 2379
    tables= lex->link_first_table_back(tables, create_table,
				       create_table_local);
2380 2381
    break;

2382
create_error:
2383 2384 2385
    res= 1; //error reported
unsent_create_error:
    // put tables back for PS rexecuting
2386 2387
    tables= lex->link_first_table_back(tables, create_table,
				       create_table_local);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2388
    break;
2389
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2390
  case SQLCOM_CREATE_INDEX:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2391
    if (check_one_table_access(thd, INDEX_ACL, tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2392
      goto error; /* purecov: inspected */
2393
    thd->slow_command=TRUE;
2394 2395 2396 2397
    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
2398 2399
    break;

2400
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2401
  case SQLCOM_SLAVE_START:
2402
  {
2403
    pthread_mutex_lock(&LOCK_active_mi);
2404
    start_slave(thd,active_mi,1 /* net report*/);
2405
    pthread_mutex_unlock(&LOCK_active_mi);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2406
    break;
2407
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2408
  case SQLCOM_SLAVE_STOP:
2409 2410 2411 2412 2413 2414
  /*
    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,
2415
      so it waits for the client thread because t is locked by it.
2416
    - then the client thread does SLAVE STOP.
2417 2418
      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.
2419 2420 2421 2422 2423
    To prevent that, refuse SLAVE STOP if the
    client thread has locked tables
  */
  if (thd->locked_tables || thd->active_transaction())
  {
2424
    send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2425
    break;
2426
  }
2427
  {
2428
    pthread_mutex_lock(&LOCK_active_mi);
2429
    stop_slave(thd,active_mi,1/* net report*/);
2430
    pthread_mutex_unlock(&LOCK_active_mi);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2431
    break;
2432
  }
2433
#endif /* HAVE_REPLICATION */
2434

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2435 2436
  case SQLCOM_ALTER_TABLE:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
2437
    send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2438 2439 2440
    break;
#else
    {
2441
      ulong priv=0;
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
2442
      if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2443
      {
2444
	net_printf(thd, ER_WRONG_TABLE_NAME, lex->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2445 2446 2447
	res=0;
	break;
      }
2448 2449
      if (!select_lex->db)
	select_lex->db=tables->db;
hf@deer.(none)'s avatar
hf@deer.(none) committed
2450 2451
      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
2452
	  check_merge_table_access(thd, tables->db,
2453 2454 2455
				   (TABLE_LIST *)
				   lex->create_info.merge_list.first))
	goto error;				/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2456 2457
      if (grant_option)
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2458
	if (check_grant(thd, ALTER_ACL, tables, 0, UINT_MAX, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2459 2460 2461 2462 2463 2464
	  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;
2465
	  tmp_table.db=select_lex->db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2466
	  tmp_table.grant.privilege=priv;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2467 2468
	  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
2469 2470 2471
	    goto error;
	}
      }
2472 2473
      /* 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
2474
      /* ALTER TABLE ends previous transaction */
2475
      if (end_active_trans(thd))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2476 2477
	res= -1;
      else
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2478
      {
2479
        thd->slow_command=TRUE;
2480
	res= mysql_alter_table(thd, select_lex->db, lex->name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2481 2482
			       &lex->create_info,
			       tables, lex->create_list,
2483
			       lex->key_list,
2484
			       select_lex->order_list.elements,
2485
                               (ORDER *) select_lex->order_list.first,
2486
			       lex->duplicates, &lex->alter_info);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2487
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2488 2489
      break;
    }
hf@deer.(none)'s avatar
hf@deer.(none) committed
2490
#endif /*DONT_ALLOW_SHOW_COMMANDS*/
2491
  case SQLCOM_RENAME_TABLE:
2492 2493 2494
  {
    TABLE_LIST *table;
    if (check_db_used(thd,tables))
2495
      goto error;
2496 2497
    for (table=tables ; table ; table=table->next->next)
    {
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2498
      if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
hf@deer.(none)'s avatar
hf@deer.(none) committed
2499
		       &table->grant.privilege,0,0) ||
2500
	  check_access(thd, INSERT_ACL | CREATE_ACL, table->next->db,
hf@deer.(none)'s avatar
hf@deer.(none) committed
2501
		       &table->next->grant.privilege,0,0))
2502 2503 2504
	goto error;
      if (grant_option)
      {
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2505
	TABLE_LIST old_list,new_list;
2506 2507 2508 2509
	/*
	  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
2510 2511 2512
	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
2513
	if (check_grant(thd, ALTER_ACL, &old_list, 0, UINT_MAX, 0) ||
2514
	    (!test_all_bits(table->next->grant.privilege,
2515
			    INSERT_ACL | CREATE_ACL) &&
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2516 2517
	     check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0,
			 UINT_MAX, 0)))
2518 2519 2520
	  goto error;
      }
    }
2521
    query_cache_invalidate3(thd, tables, 0);
2522 2523 2524
    if (end_active_trans(thd))
      res= -1;
    else if (mysql_rename_tables(thd,tables))
2525 2526
      res= -1;
    break;
2527
  }
2528
#ifndef EMBEDDED_LIBRARY
2529 2530
  case SQLCOM_SHOW_BINLOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
2531
    send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
2532 2533 2534
    DBUG_VOID_RETURN;
#else
    {
2535
      if (check_global_access(thd, SUPER_ACL))
2536 2537 2538 2539
	goto error;
      res = show_binlogs(thd);
      break;
    }
peter@mysql.com's avatar
peter@mysql.com committed
2540
#endif
2541
#endif /* EMBEDDED_LIBRARY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2542
  case SQLCOM_SHOW_CREATE:
2543
#ifdef DONT_ALLOW_SHOW_COMMANDS
2544
    send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
2545 2546
    DBUG_VOID_RETURN;
#else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2547
    {
2548 2549
      if (check_db_used(thd, tables) ||
	  check_access(thd, SELECT_ACL | EXTRA_ACL, tables->db,
hf@deer.(none)'s avatar
hf@deer.(none) committed
2550
		       &tables->grant.privilege,0,0))
2551
	goto error;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2552
      res = mysqld_show_create(thd, tables);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2553 2554
      break;
    }
2555
#endif
2556 2557 2558
  case SQLCOM_CHECKSUM:
  {
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2559
	check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0))
2560
      goto error; /* purecov: inspected */
2561
    res = mysql_checksum_table(thd, tables, &lex->check_opt);
2562 2563
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2564
  case SQLCOM_REPAIR:
2565 2566
  {
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2567
	check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
2568
      goto error; /* purecov: inspected */
2569
    thd->slow_command=TRUE;
2570
    res = mysql_repair_table(thd, tables, &lex->check_opt);
2571 2572 2573 2574 2575 2576 2577 2578 2579 2580
    /* ! 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())
      {
        Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
        mysql_bin_log.write(&qinfo);
      }
    }
2581 2582
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2583
  case SQLCOM_CHECK:
2584 2585
  {
    if (check_db_used(thd,tables) ||
2586
	check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0))
2587
      goto error; /* purecov: inspected */
2588
    thd->slow_command=TRUE;
2589 2590 2591
    res = mysql_check_table(thd, tables, &lex->check_opt);
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2592 2593
  case SQLCOM_ANALYZE:
  {
2594
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2595
	check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2596
      goto error; /* purecov: inspected */
2597
    thd->slow_command=TRUE;
2598
    res = mysql_analyze_table(thd, tables, &lex->check_opt);
2599 2600 2601 2602 2603 2604 2605 2606 2607 2608
    /* ! 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())
      {
        Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
        mysql_bin_log.write(&qinfo);
      }
    }
2609
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2610
  }
2611

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2612 2613 2614
  case SQLCOM_OPTIMIZE:
  {
    HA_CREATE_INFO create_info;
2615
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2616
	check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2617
      goto error; /* purecov: inspected */
2618
    thd->slow_command=TRUE;
2619 2620 2621
    res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
      mysql_recreate_table(thd, tables, 1) :
      mysql_optimize_table(thd, tables, &lex->check_opt);
2622 2623 2624 2625 2626 2627 2628 2629 2630 2631
    /* ! 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())
      {
        Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
        mysql_bin_log.write(&qinfo);
      }
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2632 2633 2634
    break;
  }
  case SQLCOM_UPDATE:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2635 2636
    if (update_precheck(thd, tables))
      break;
2637 2638 2639 2640
    res= mysql_update(thd,tables,
                      select_lex->item_list,
                      lex->value_list,
                      select_lex->where,
2641
		      select_lex->order_list.elements,
2642 2643 2644
                      (ORDER *) select_lex->order_list.first,
                      select_lex->select_limit,
                      lex->duplicates);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2645 2646
    if (thd->net.report_error)
      res= -1;
2647 2648
    break;
  case SQLCOM_UPDATE_MULTI:
monty@mysql.com's avatar
monty@mysql.com committed
2649
  {
2650
    if ((res= multi_update_precheck(thd, tables)))
monty@mysql.com's avatar
monty@mysql.com committed
2651 2652 2653 2654 2655 2656 2657
      break;
    res= mysql_multi_update(thd,tables,
			    &select_lex->item_list,
			    &lex->value_list,
			    select_lex->where,
			    select_lex->options,
			    lex->duplicates, unit, select_lex);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2658
    break;
monty@mysql.com's avatar
monty@mysql.com committed
2659
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2660
  case SQLCOM_REPLACE:
2661 2662
  case SQLCOM_INSERT:
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2663 2664 2665
    my_bool update= (lex->value_list.elements ? UPDATE_ACL : 0);
    if ((res= insert_precheck(thd, tables, update)))
      break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2666
    res = mysql_insert(thd,tables,lex->field_list,lex->many_values,
2667
                       select_lex->item_list, lex->value_list,
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2668
                       (update ? DUP_UPDATE : lex->duplicates));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2669 2670
    if (thd->net.report_error)
      res= -1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2671
    break;
2672
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2673 2674 2675
  case SQLCOM_REPLACE_SELECT:
  case SQLCOM_INSERT_SELECT:
  {
2676 2677 2678
    TABLE_LIST *first_local_table= (TABLE_LIST *) select_lex->table_list.first;
    if ((res= insert_select_precheck(thd, tables)))
      break;
monty@mysql.com's avatar
monty@mysql.com committed
2679

2680 2681
    /* Fix lock for first table */
    if (tables->lock_type == TL_WRITE_DELAYED)
2682
      tables->lock_type= TL_WRITE;
2683

2684 2685
    /* 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
2686 2687

    select_result *result;
2688 2689 2690 2691
    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
2692

2693
    if (find_real_table_in_list(tables->next, tables->db, tables->real_name))
2694
    {
2695 2696
      /* Using same table for INSERT and SELECT */
      select_lex->options |= OPTION_BUFFER_RESULT;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2697
    }
2698

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2699

2700 2701 2702
    if (!(res=open_and_lock_tables(thd, tables)))
    {
      if ((result=new select_insert(tables->table,&lex->field_list,
2703
				    lex->duplicates)))
2704
	/* Skip first table, which is the table we are inserting in */
2705
	lex->select_lex.table_list.first= (byte*) first_local_table->next;
2706
	lex->select_lex.resolve_mode= SELECT_LEX::NOMATTER_MODE;
2707
	res=handle_select(thd,lex,result);
2708
	/* revert changes for SP */
2709
	lex->select_lex.table_list.first= (byte*) first_local_table;
2710
	lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2711 2712
      if (thd->net.report_error)
	res= -1;
2713 2714 2715
    }
    else
      res= -1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2716 2717
    break;
  }
2718
  case SQLCOM_TRUNCATE:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2719
    if (check_one_table_access(thd, DELETE_ACL, tables))
2720
      goto error;
2721 2722 2723 2724 2725 2726
    /*
      Don't allow this within a transaction because we want to use
      re-generate table
    */
    if (thd->locked_tables || thd->active_transaction())
    {
2727
      send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION,NullS);
2728 2729 2730 2731
      goto error;
    }
    res=mysql_truncate(thd,tables);
    break;
2732
  case SQLCOM_DELETE:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2733
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2734 2735
    if ((res= delete_precheck(thd, tables)))
      break;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
2736
    res = mysql_delete(thd,tables, select_lex->where,
2737
                       &select_lex->order_list,
2738
                       select_lex->select_limit, select_lex->options);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2739 2740
    if (thd->net.report_error)
      res= -1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2741 2742
    break;
  }
2743
  case SQLCOM_DELETE_MULTI:
2744
  {
2745 2746
    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
2747 2748
    TABLE_LIST *target_tbl;
    uint table_count;
2749
    multi_delete *result;
2750

2751 2752
    if ((res= multi_delete_precheck(thd, tables, &table_count)))
      break;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2753

2754
    /* condition will be TRUE on SP re-excuting */
2755 2756
    if (select_lex->item_list.elements != 0)
      select_lex->item_list.empty();
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2757
    if (add_item_to_list(thd, new Item_null()))
2758
    {
2759
      res= -1;
2760
      break;
2761
    }
2762

2763 2764 2765 2766
    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
2767 2768 2769
    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
2770
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2771
      target_tbl->table= target_tbl->table_list->table;
2772
      /* 
2773 2774
	 Multi-delete can't be constructed over-union => we always have
	 single SELECT on top and have to check underlaying SELECTs of it
2775 2776 2777 2778
      */
      for (SELECT_LEX_UNIT *un= lex->select_lex.first_inner_unit();
	   un;
	   un= un->next_unit())
2779
      {
2780
	if (un->first_select()->linkage != DERIVED_TABLE_TYPE &&
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2781 2782
	    un->check_updateable(target_tbl->table_list->db,
				 target_tbl->table_list->real_name))
2783
	{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2784 2785
	  my_error(ER_UPDATE_TABLE_USED, MYF(0),
		   target_tbl->table_list->real_name);
2786 2787 2788 2789
	  res= -1;
	  break;
	}
      }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2790
    }
2791

2792 2793
    if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables,
							  table_count)))
2794
    {
2795 2796 2797
      res= mysql_select(thd, &select_lex->ref_pointer_array,
			select_lex->get_table_list(),
			select_lex->with_wild,
2798
			select_lex->item_list,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2799
			select_lex->where,
2800
			0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2801 2802
			(ORDER *)NULL,
			select_lex->options | thd->options |
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
2803
			SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK,
2804
			result, unit, select_lex);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2805 2806
      if (thd->net.report_error)
	res= -1;
2807
      delete result;
2808 2809 2810 2811 2812 2813
    }
    else
      res= -1;					// Error is not sent
    close_thread_tables(thd);
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2814
  case SQLCOM_DROP_TABLE:
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
2815
  {
2816 2817
    if (!lex->drop_temporary)
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
2818
      if (check_table_access(thd,DROP_ACL,tables,0))
2819 2820 2821 2822 2823 2824 2825
	goto error;				/* purecov: inspected */
      if (end_active_trans(thd))
      {
	res= -1;
	break;
      }
    }
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
2826
    else
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
2827 2828 2829 2830 2831 2832
    {
      /*
	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
2833 2834
	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
2835 2836 2837 2838
      */
      if (thd->slave_thread)
	lex->drop_if_exists= 1;
    }
2839
    res= mysql_rm_table(thd,tables,lex->drop_if_exists, lex->drop_temporary);
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
2840 2841
  }
  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2842
  case SQLCOM_DROP_INDEX:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2843
    if (check_one_table_access(thd, INDEX_ACL, tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2844
      goto error;				/* purecov: inspected */
2845 2846 2847
    if (end_active_trans(thd))
      res= -1;
    else
2848
      res = mysql_drop_index(thd, tables, &lex->alter_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2849 2850
    break;
  case SQLCOM_SHOW_DATABASES:
2851
#if defined(DONT_ALLOW_SHOW_COMMANDS)
2852
    send_error(thd,ER_NOT_ALLOWED_COMMAND);   /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2853 2854 2855
    DBUG_VOID_RETURN;
#else
    if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
2856
	check_global_access(thd, SHOW_DB_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2857 2858 2859 2860 2861
      goto error;
    res= mysqld_show_dbs(thd, (lex->wild ? lex->wild->ptr() : NullS));
    break;
#endif
  case SQLCOM_SHOW_PROCESSLIST:
2862
    if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2863
      break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
2864 2865
    mysqld_list_processes(thd,
			  thd->master_access & PROCESS_ACL ? NullS :
monty@mysql.com's avatar
monty@mysql.com committed
2866
			  thd->priv_user,lex->verbose);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2867
    break;
2868 2869
  case SQLCOM_SHOW_STORAGE_ENGINES:
    res= mysqld_show_storage_engines(thd);
2870 2871 2872 2873 2874 2875 2876
    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
2877
  case SQLCOM_SHOW_STATUS:
2878
    res= mysqld_show(thd,(lex->wild ? lex->wild->ptr() : NullS),status_vars,
2879
		     OPT_GLOBAL, &LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2880 2881 2882
    break;
  case SQLCOM_SHOW_VARIABLES:
    res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS),
2883 2884
		     init_vars, lex->option_type,
		     &LOCK_global_system_variables);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2885
    break;
tim@cane.mysql.fi's avatar
tim@cane.mysql.fi committed
2886 2887
  case SQLCOM_SHOW_LOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
2888
    send_error(thd,ER_NOT_ALLOWED_COMMAND);	/* purecov: inspected */
tim@cane.mysql.fi's avatar
tim@cane.mysql.fi committed
2889 2890 2891
    DBUG_VOID_RETURN;
#else
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
2892
      if (grant_option && check_access(thd, FILE_ACL, any_db,0,0,0))
tim@cane.mysql.fi's avatar
tim@cane.mysql.fi committed
2893 2894 2895 2896 2897
	goto error;
      res= mysqld_show_logs(thd);
      break;
    }
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2898
  case SQLCOM_SHOW_TABLES:
2899
    /* FALL THROUGH */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2900
#ifdef DONT_ALLOW_SHOW_COMMANDS
2901
    send_error(thd,ER_NOT_ALLOWED_COMMAND);	/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2902 2903 2904
    DBUG_VOID_RETURN;
#else
    {
2905
      char *db=select_lex->db ? select_lex->db : thd->db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2906 2907
      if (!db)
      {
2908
	send_error(thd,ER_NO_DB_ERROR);	/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2909 2910 2911
	goto error;				/* purecov: inspected */
      }
      remove_escape(db);				// Fix escaped '_'
2912
      if (check_db_name(db))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2913
      {
2914
        net_printf(thd,ER_WRONG_DB_NAME, db);
2915
        goto error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2916
      }
hf@deer.(none)'s avatar
hf@deer.(none) committed
2917
      if (check_access(thd,SELECT_ACL,db,&thd->col_access,0,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2918
	goto error;				/* purecov: inspected */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2919
      if (!thd->col_access && check_grant_db(thd,db))
2920
      {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2921
	net_printf(thd, ER_DBACCESS_DENIED_ERROR,
2922 2923 2924 2925 2926
		   thd->priv_user,
		   thd->priv_host,
		   db);
	goto error;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2927
      /* grant is checked in mysqld_show_tables */
2928
      if (select_lex->options & SELECT_DESCRIBE)
2929
        res= mysqld_extend_show_tables(thd,db,
2930
				       (lex->wild ? lex->wild->ptr() : NullS));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2931 2932 2933 2934 2935 2936
      else
	res= mysqld_show_tables(thd,db,
				(lex->wild ? lex->wild->ptr() : NullS));
      break;
    }
#endif
2937 2938 2939
  case SQLCOM_SHOW_OPEN_TABLES:
    res= mysqld_show_open_tables(thd,(lex->wild ? lex->wild->ptr() : NullS));
    break;
2940 2941
  case SQLCOM_SHOW_CHARSETS:
    res= mysqld_show_charsets(thd,(lex->wild ? lex->wild->ptr() : NullS));
2942
    break;
2943 2944 2945
  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
2946 2947
  case SQLCOM_SHOW_FIELDS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
2948
    send_error(thd,ER_NOT_ALLOWED_COMMAND);	/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2949 2950 2951
    DBUG_VOID_RETURN;
#else
    {
2952
      char *db=tables->db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2953
      remove_escape(db);			// Fix escaped '_'
2954
      remove_escape(tables->real_name);
2955
      if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
monty@mysql.com's avatar
monty@mysql.com committed
2956
		       &tables->grant.privilege, 0, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2957
	goto error;				/* purecov: inspected */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2958
      if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2959 2960
	goto error;
      res= mysqld_show_fields(thd,tables,
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2961 2962
			      (lex->wild ? lex->wild->ptr() : NullS),
			      lex->verbose);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2963 2964 2965 2966 2967
      break;
    }
#endif
  case SQLCOM_SHOW_KEYS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
2968
    send_error(thd,ER_NOT_ALLOWED_COMMAND);	/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2969 2970 2971
    DBUG_VOID_RETURN;
#else
    {
2972
      char *db=tables->db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2973
      remove_escape(db);			// Fix escaped '_'
2974
      remove_escape(tables->real_name);
monty@mysql.com's avatar
monty@mysql.com committed
2975
      if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
monty@mysql.com's avatar
monty@mysql.com committed
2976
		       &tables->grant.privilege, 0, 0))
monty@mysql.com's avatar
monty@mysql.com committed
2977
	goto error;				/* purecov: inspected */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2978
      if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2979 2980 2981 2982 2983 2984
	goto error;
      res= mysqld_show_keys(thd,tables);
      break;
    }
#endif
  case SQLCOM_CHANGE_DB:
2985
    mysql_change_db(thd,select_lex->db);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2986
    break;
2987

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2988 2989 2990
  case SQLCOM_LOAD:
  {
    uint privilege= (lex->duplicates == DUP_REPLACE ?
2991
		     INSERT_ACL | DELETE_ACL : INSERT_ACL);
2992 2993

    if (!lex->local_file)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2994
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
2995
      if (check_access(thd,privilege | FILE_ACL,tables->db,0,0,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2996 2997 2998 2999
	goto error;
    }
    else
    {
3000
      if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) ||
3001
	  ! opt_local_infile)
3002
      {
3003
	send_error(thd,ER_NOT_ALLOWED_COMMAND);
3004 3005
	goto error;
      }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3006
      if (check_one_table_access(thd, privilege, tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3007 3008 3009 3010 3011 3012
	goto error;
    }
    res=mysql_load(thd, lex->exchange, tables, lex->field_list,
		   lex->duplicates, (bool) lex->local_file, lex->lock_option);
    break;
  }
3013

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3014
  case SQLCOM_SET_OPTION:
3015 3016
  {
    List<set_var_base> *lex_var_list= &lex->var_list;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3017
    if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
3018 3019
		   (res= open_and_lock_tables(thd,tables))))
      break;
3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033
    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;
3034
      send_ok(thd);
3035
    }
3036 3037
    if (thd->net.report_error)
      res= -1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3038
    break;
3039
  }
3040

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3041
  case SQLCOM_UNLOCK_TABLES:
3042
    unlock_locked_tables(thd);
3043 3044
    if (thd->options & OPTION_TABLE_LOCK)
    {
3045
      end_active_trans(thd);
3046
      thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3047 3048
    }
    if (thd->global_read_lock)
3049
      unlock_global_read_lock(thd);
3050
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3051 3052
    break;
  case SQLCOM_LOCK_TABLES:
3053
    unlock_locked_tables(thd);
3054
    if (check_db_used(thd,tables) || end_active_trans(thd))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3055
      goto error;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3056
    if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, tables,0))
3057
      goto error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3058
    thd->in_lock_tables=1;
3059
    thd->options|= OPTION_TABLE_LOCK;
3060
    if (!(res= open_and_lock_tables(thd, tables)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3061
    {
3062 3063 3064 3065
#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
3066 3067
      thd->locked_tables=thd->lock;
      thd->lock=0;
3068
      send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3069
    }
3070 3071
    else
      thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3072 3073 3074
    thd->in_lock_tables=0;
    break;
  case SQLCOM_CREATE_DB:
3075
  {
3076
    char *alias;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
3077
    if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3078
    {
3079
      net_printf(thd,ER_WRONG_DB_NAME, lex->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3080 3081
      break;
    }
3082 3083 3084 3085 3086 3087 3088
    /*
      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.
    */
3089
#ifdef HAVE_REPLICATION
3090 3091 3092
    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
3093 3094
    {
      my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
3095
      break;
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3096
    }
3097
#endif
hf@deer.(none)'s avatar
hf@deer.(none) committed
3098
    if (check_access(thd,CREATE_ACL,lex->name,0,1,0))
3099
      break;
monty@mysql.com's avatar
monty@mysql.com committed
3100
    res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name),
monty@mysql.com's avatar
monty@mysql.com committed
3101
			 &lex->create_info, 0);
3102 3103
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3104
  case SQLCOM_DROP_DB:
3105
  {
3106
    char *alias;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
3107
    if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3108
    {
3109
      net_printf(thd, ER_WRONG_DB_NAME, lex->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3110 3111
      break;
    }
3112 3113 3114 3115 3116 3117 3118
    /*
      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.
    */
3119
#ifdef HAVE_REPLICATION
3120 3121 3122
    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
3123 3124
    {
      my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
3125
      break;
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3126
    }
3127
#endif
hf@deer.(none)'s avatar
hf@deer.(none) committed
3128
    if (check_access(thd,DROP_ACL,lex->name,0,1,0))
3129
      break;
3130 3131
    if (thd->locked_tables || thd->active_transaction())
    {
3132
      send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
3133 3134
      goto error;
    }
3135
    res=mysql_rm_db(thd,alias,lex->drop_if_exists,0);
3136 3137
    break;
  }
3138 3139 3140 3141
  case SQLCOM_ALTER_DB:
  {
    if (!strip_sp(lex->name) || check_db_name(lex->name))
    {
3142
      net_printf(thd, ER_WRONG_DB_NAME, lex->name);
3143 3144
      break;
    }
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160
    /*
      If in a slave thread :
      ALTER DATABASE DB may not be preceded by USE DB.
      For that reason, maybe db_ok() in sql/slave.cc did not check the 
      do_db/ignore_db. And as this query involves no tables, tables_ok()
      above was not called. So we have to check rules again here.
    */
#ifdef HAVE_REPLICATION
    if (thd->slave_thread && 
	(!db_ok(lex->name, replicate_do_db, replicate_ignore_db) ||
	 !db_ok_with_wild_table(lex->name)))
    {
      my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
      break;
    }
#endif
hf@deer.(none)'s avatar
hf@deer.(none) committed
3161
    if (check_access(thd,ALTER_ACL,lex->name,0,1,0))
3162 3163 3164
      break;
    if (thd->locked_tables || thd->active_transaction())
    {
3165
      send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
3166 3167
      goto error;
    }
3168
    res=mysql_alter_db(thd,lex->name,&lex->create_info);
3169 3170
    break;
  }
3171 3172 3173 3174
  case SQLCOM_SHOW_CREATE_DB:
  {
    if (!strip_sp(lex->name) || check_db_name(lex->name))
    {
3175
      net_printf(thd,ER_WRONG_DB_NAME, lex->name);
3176 3177
      break;
    }
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
3178
    if (check_access(thd,SELECT_ACL,lex->name,0,1,0))
3179 3180 3181
      break;
    if (thd->locked_tables || thd->active_transaction())
    {
3182
      send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
3183 3184
      goto error;
    }
3185
    res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
3186 3187
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3188
  case SQLCOM_CREATE_FUNCTION:
hf@deer.(none)'s avatar
hf@deer.(none) committed
3189
    if (check_access(thd,INSERT_ACL,"mysql",0,1,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3190 3191 3192
      break;
#ifdef HAVE_DLOPEN
    if (!(res = mysql_create_function(thd,&lex->udf)))
3193
      send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3194 3195 3196 3197 3198
#else
    res= -1;
#endif
    break;
  case SQLCOM_DROP_FUNCTION:
hf@deer.(none)'s avatar
hf@deer.(none) committed
3199
    if (check_access(thd,DELETE_ACL,"mysql",0,1,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3200 3201
      break;
#ifdef HAVE_DLOPEN
3202
    if (!(res = mysql_drop_function(thd,&lex->udf.name)))
3203
      send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3204 3205 3206 3207
#else
    res= -1;
#endif
    break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3208
#ifndef NO_EMBEDDED_ACCESS_CHECKS
3209 3210
  case SQLCOM_DROP_USER:
  {
hf@deer.(none)'s avatar
hf@deer.(none) committed
3211
    if (check_access(thd, GRANT_ACL,"mysql",0,1,0))
3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226
      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())
      {
	Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
	mysql_bin_log.write(&qinfo);
      }
      send_ok(thd);
    }
    break;
  }
  case SQLCOM_REVOKE_ALL:
  {
hf@deer.(none)'s avatar
hf@deer.(none) committed
3227
    if (check_access(thd, GRANT_ACL ,"mysql",0,1,0))
3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240
      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())
      {
	Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
	mysql_bin_log.write(&qinfo);
      }
      send_ok(thd);
    }
    break;
  }
3241 3242 3243 3244 3245 3246
  case SQLCOM_REVOKE:
  case SQLCOM_GRANT:
  {
    if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
		     tables && tables->db ? tables->db : select_lex->db,
		     tables ? &tables->grant.privilege : 0,
hf@deer.(none)'s avatar
hf@deer.(none) committed
3247
		     tables ? 0 : 1,0))
3248 3249
      goto error;

3250 3251 3252 3253
    /*
      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
    */
3254 3255 3256 3257 3258 3259 3260 3261 3262 3263

    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 &&
3264
	     my_strcasecmp(&my_charset_latin1,
3265
                           user->host.str, thd->host_or_ip)))
3266
	{
hf@deer.(none)'s avatar
hf@deer.(none) committed
3267
	  if (check_access(thd, UPDATE_ACL, "mysql",0,1,0))
3268 3269 3270 3271 3272
	    goto error;
	  break;			// We are allowed to do changes
	}
      }
    }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285
    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);
      }
    }
3286 3287 3288 3289 3290
    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
3291
				      tables, 0, UINT_MAX, 0))
3292
	goto error;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3293 3294 3295
      if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns,
				    lex->grant,
				    lex->sql_command == SQLCOM_REVOKE)))
3296
      {
3297
	mysql_update_log.write(thd, thd->query, thd->query_length);
3298 3299
	if (mysql_bin_log.is_open())
	{
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3300
          thd->clear_error();
3301
	  Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
3302 3303 3304 3305 3306 3307 3308 3309
	  mysql_bin_log.write(&qinfo);
	}
      }
    }
    else
    {
      if (lex->columns.elements)
      {
3310
	send_error(thd,ER_ILLEGAL_GRANT_FOR_TABLE);
3311 3312 3313 3314 3315 3316 3317
	res=1;
      }
      else
	res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
			  lex->sql_command == SQLCOM_REVOKE);
      if (!res)
      {
3318
	mysql_update_log.write(thd, thd->query, thd->query_length);
3319 3320
	if (mysql_bin_log.is_open())
	{
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3321
          thd->clear_error();
3322
	  Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
3323 3324
	  mysql_bin_log.write(&qinfo);
	}
3325
	if (mqh_used && lex->sql_command == SQLCOM_GRANT)
3326
	{
3327 3328 3329
	  List_iterator <LEX_USER> str_list(lex->users_list);
	  LEX_USER *user;
	  while ((user=str_list++))
3330
	    reset_mqh(thd,user);
3331
	}
3332 3333 3334 3335
      }
    }
    break;
  }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3336
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3337
  case SQLCOM_RESET:
3338 3339 3340 3341 3342 3343 3344
    /* 
       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:
  {
3345
    if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3346
      goto error;
3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370
    /*
      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())
        {
          Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
          mysql_bin_log.write(&qinfo);
        }
      }
      send_ok(thd);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3371
    break;
3372
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3373 3374 3375
  case SQLCOM_KILL:
    kill_one_thread(thd,lex->thread_id);
    break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3376
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3377 3378
  case SQLCOM_SHOW_GRANTS:
    res=0;
3379 3380
    if ((thd->priv_user &&
	 !strcmp(thd->priv_user,lex->grant_user->user.str)) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
3381
	!check_access(thd, SELECT_ACL, "mysql",0,1,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3382 3383 3384 3385
    {
      res = mysql_show_grants(thd,lex->grant_user);
    }
    break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3386
#endif
3387
  case SQLCOM_HA_OPEN:
3388
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
3389
	check_table_access(thd,SELECT_ACL, tables,0))
3390 3391 3392 3393 3394 3395 3396 3397 3398
      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:
3399 3400 3401 3402 3403
    /*
      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
3404
    if (check_db_used(thd,tables))
3405
      goto error;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3406
    res = mysql_ha_read(thd, tables, lex->ha_read_mode, lex->backup_dir,
3407 3408
			lex->insert_list, lex->ha_rkey_mode, select_lex->where,
			select_lex->select_limit, select_lex->offset_limit);
3409 3410
    break;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3411
  case SQLCOM_BEGIN:
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3412 3413 3414 3415 3416 3417
    if (thd->locked_tables)
    {
      thd->lock=thd->locked_tables;
      thd->locked_tables=0;			// Will be automaticly closed
      close_thread_tables(thd);			// Free tables
    }
3418 3419 3420 3421 3422 3423
    if (end_active_trans(thd))
    {
      res= -1;
    }
    else
    {
3424
      thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
3425 3426
		     OPTION_BEGIN);
      thd->server_status|= SERVER_STATUS_IN_TRANS;
3427
      send_ok(thd);
3428
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3429 3430
    break;
  case SQLCOM_COMMIT:
3431 3432 3433 3434 3435
    /*
      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
3436
  {
3437
    thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3438 3439
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
    if (!ha_commit(thd))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3440
    {
3441
      send_ok(thd);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3442
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3443 3444 3445
    else
      res= -1;
    break;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3446
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3447 3448 3449
  case SQLCOM_ROLLBACK:
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
    if (!ha_rollback(thd))
3450
    {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3451 3452 3453 3454 3455 3456 3457 3458 3459 3460
      /*
        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)
3461
	send_warning(thd,ER_WARNING_NOT_COMPLETE_ROLLBACK,0);
3462
      else
3463
	send_ok(thd);
3464
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3465 3466
    else
      res= -1;
3467
    thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3468
    break;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3469 3470 3471
  case SQLCOM_ROLLBACK_TO_SAVEPOINT:
    if (!ha_rollback_to_savepoint(thd, lex->savepoint_name))
    {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3472
      if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3473
	send_warning(thd, ER_WARNING_NOT_COMPLETE_ROLLBACK, 0);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3474
      else
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3475
	send_ok(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3476 3477 3478 3479
    }
    else
      res= -1;
    break;
3480
  case SQLCOM_SAVEPOINT:
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3481
    if (!ha_savepoint(thd, lex->savepoint_name))
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3482
      send_ok(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3483 3484
    else
      res= -1;
3485
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3486
  default:					/* Impossible */
3487
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3488 3489 3490
    break;
  }
  thd->proc_info="query end";			// QQ
3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513
  if (thd->one_shot_set)
    {
      /*
        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)
      {
        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();
        /* Add timezone stuff here */
        thd->one_shot_set= 0;
      }
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3514
  if (res < 0)
3515
    send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3516 3517 3518 3519 3520 3521

error:
  DBUG_VOID_RETURN;
}


bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3522 3523 3524 3525
/*
  Check grants for commands which work only with one table and all other
  tables belong to subselects.

3526
  SYNOPSIS
monty@mysql.com's avatar
monty@mysql.com committed
3527 3528 3529 3530
    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
3531 3532 3533

  RETURN
    0 - OK
monty@mysql.com's avatar
monty@mysql.com committed
3534
    1 - access denied, error is sent to client
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3535
*/
3536 3537

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
3538
{
monty@mysql.com's avatar
monty@mysql.com committed
3539 3540
  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
3541

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

3546
  /* Check rights on tables of subselect (if exists) */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3547 3548
  TABLE_LIST *subselects_tables;
  if ((subselects_tables= tables->next))
monty@mysql.com's avatar
monty@mysql.com committed
3549
  {
monty@mysql.com's avatar
monty@mysql.com committed
3550
    if ((check_table_access(thd, SELECT_ACL, subselects_tables,0)))
monty@mysql.com's avatar
monty@mysql.com committed
3551 3552 3553
      return 1;
  }
  return 0;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3554 3555 3556
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
3557
/****************************************************************************
3558
  Get the user (global) and database privileges for all used tables
3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571

  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
3572 3573
                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
3574 3575 3576
****************************************************************************/

bool
3577
check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
3578
	     bool dont_check_global_grants, bool no_errors)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3579
{
3580 3581 3582
  DBUG_ENTER("check_access");
  DBUG_PRINT("enter",("want_access: %lu  master_access: %lu", want_access,
		      thd->master_access));
3583 3584 3585 3586
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  ulong db_access;
#endif
  ulong dummy;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3587 3588 3589 3590 3591
  if (save_priv)
    *save_priv=0;
  else
    save_priv= &dummy;

3592
  if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3593
  {
3594
    if (!no_errors)
3595
      send_error(thd,ER_NO_DB_ERROR);	/* purecov: tested */
3596
    DBUG_RETURN(TRUE);				/* purecov: tested */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3597 3598
  }

monty@mysql.com's avatar
monty@mysql.com committed
3599 3600 3601
#ifdef NO_EMBEDDED_ACCESS_CHECKS
  DBUG_RETURN(0);
#else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3602 3603
  if ((thd->master_access & want_access) == want_access)
  {
3604 3605 3606 3607 3608 3609 3610 3611
    /*
      If we don't have a global SELECT privilege, we have to get the database
      specific access rights to be able to handle queries of type
      UPDATE t1 SET a=1 WHERE b > 0
    */
    db_access= thd->db_access;
    if (!(thd->master_access & SELECT_ACL) &&
	(db && (!thd->db || strcmp(db,thd->db))))
3612
      db_access=acl_get(thd->host, thd->ip,
3613
			thd->priv_user, db, test(want_access & GRANT_ACL));
3614
    *save_priv=thd->master_access | db_access;
3615
    DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3616
  }
3617
  if (((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
3618
      ! db && dont_check_global_grants)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3619
  {						// We can never grant this
3620
    if (!no_errors)
3621
      net_printf(thd,ER_ACCESS_DENIED_ERROR,
3622
		 thd->priv_user,
3623
		 thd->priv_host,
3624
		 thd->password ? ER(ER_YES) : ER(ER_NO));/* purecov: tested */
3625
    DBUG_RETURN(TRUE);				/* purecov: tested */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3626 3627 3628
  }

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3631
  if (db && (!thd->db || strcmp(db,thd->db)))
3632
    db_access=acl_get(thd->host, thd->ip,
3633
		      thd->priv_user, db, test(want_access & GRANT_ACL));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3634 3635
  else
    db_access=thd->db_access;
3636 3637
  // Remove SHOW attribute and access rights we already have
  want_access &= ~(thd->master_access | EXTRA_ACL);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3638
  db_access= ((*save_priv=(db_access | thd->master_access)) & want_access);
3639 3640

  /* grant_option is set if there exists a single table or column grant */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3641
  if (db_access == want_access ||
3642
      ((grant_option && !dont_check_global_grants) &&
3643
       !(want_access & ~(db_access | TABLE_ACLS))))
3644
    DBUG_RETURN(FALSE);				/* Ok */
3645
  if (!no_errors)
3646
    net_printf(thd,ER_DBACCESS_DENIED_ERROR,
3647
	       thd->priv_user,
3648
	       thd->priv_host,
3649
	       db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */
3650
  DBUG_RETURN(TRUE);				/* purecov: tested */
monty@mysql.com's avatar
monty@mysql.com committed
3651
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3652 3653 3654
}


3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672
/*
  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
*/
3673 3674

bool check_global_access(THD *thd, ulong want_access)
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3675
{
monty@mysql.com's avatar
monty@mysql.com committed
3676 3677 3678
#ifdef NO_EMBEDDED_ACCESS_CHECKS
  return 0;
#else
3679
  char command[128];
3680
  if ((thd->master_access & want_access))
3681 3682
    return 0;
  get_privilege_desc(command, sizeof(command), want_access);
3683
  net_printf(thd,ER_SPECIFIC_ACCESS_DENIED_ERROR,
3684 3685
	     command);
  return 1;
monty@mysql.com's avatar
monty@mysql.com committed
3686
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3687 3688 3689
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
3690
/*
3691 3692
  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
3693 3694
*/

3695
bool
3696
check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
3697
		   bool no_errors)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3698
{
3699 3700
  uint found=0;
  ulong found_access=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3701 3702 3703
  TABLE_LIST *org_tables=tables;
  for (; tables ; tables=tables->next)
  {
3704
    if (tables->derived || (tables->table && (int)tables->table->tmp_table))
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
3705
      continue;
3706 3707
    if ((thd->master_access & want_access) == (want_access & ~EXTRA_ACL) &&
	thd->db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3708
      tables->grant.privilege= want_access;
3709
    else if (tables->db && tables->db == thd->db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3710 3711 3712 3713 3714
    {
      if (found && !grant_option)		// db already checked
	tables->grant.privilege=found_access;
      else
      {
3715 3716
	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
3717 3718
	  return TRUE;				// Access denied
	found_access=tables->grant.privilege;
3719
	found=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3720 3721
      }
    }
3722
    else if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
3723
			  0, no_errors))
3724
      return TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3725 3726
  }
  if (grant_option)
3727
    return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3728
		       test(want_access & EXTRA_ACL), UINT_MAX, no_errors);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3729 3730 3731
  return FALSE;
}

3732 3733
bool check_merge_table_access(THD *thd, char *db,
			      TABLE_LIST *table_list)
3734 3735 3736 3737
{
  int error=0;
  if (table_list)
  {
3738
    /* Check that all tables use the current database */
3739 3740
    TABLE_LIST *tmp;
    for (tmp=table_list; tmp ; tmp=tmp->next)
3741 3742 3743 3744
    {
      if (!tmp->db || !tmp->db[0])
	tmp->db=db;
    }
3745
    error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
hf@deer.(none)'s avatar
hf@deer.(none) committed
3746
			     table_list,0);
3747 3748 3749 3750
  }
  return error;
}

hf@deer.(none)'s avatar
hf@deer.(none) committed
3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3768 3769 3770 3771 3772 3773 3774 3775 3776 3777
/****************************************************************************
	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
3778 3779 3780 3781
#ifndef DBUG_OFF
long max_stack_used;
#endif

3782
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3783 3784 3785 3786 3787 3788 3789 3790
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));
3791
    thd->fatal_error();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3792 3793
    return 1;
  }
monty@mysql.com's avatar
monty@mysql.com committed
3794 3795 3796
#ifndef DBUG_OFF
  max_stack_used= max(max_stack_used, stack_used);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3797 3798
  return 0;
}
3799
#endif /* EMBEDDED_LIBRARY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833

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

bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, int *yystacksize)
{
  LEX	*lex=current_lex;
  int  old_info=0;
  if ((uint) *yystacksize >= MY_YACC_MAX)
    return 1;
  if (!lex->yacc_yyvs)
    old_info= *yystacksize;
  *yystacksize= set_zone((*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX);
  if (!(lex->yacc_yyvs= (char*)
	my_realloc((gptr) lex->yacc_yyvs,
		   *yystacksize*sizeof(**yyvs),
		   MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) ||
      !(lex->yacc_yyss= (char*)
	my_realloc((gptr) lex->yacc_yyss,
		   *yystacksize*sizeof(**yyss),
		   MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))))
    return 1;
  if (old_info)
  {						// Copy old info from stack
    memcpy(lex->yacc_yyss, (gptr) *yyss, old_info*sizeof(**yyss));
    memcpy(lex->yacc_yyvs, (gptr) *yyvs, old_info*sizeof(**yyvs));
  }
  *yyss=(short*) lex->yacc_yyss;
  *yyvs=(YYSTYPE*) lex->yacc_yyvs;
  return 0;
}


/****************************************************************************
3834
  Initialize global thd variables needed for query
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3835 3836
****************************************************************************/

3837
void
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3838 3839 3840
mysql_init_query(THD *thd)
{
  DBUG_ENTER("mysql_init_query");
3841
  LEX *lex= thd->lex;
3842 3843
  lex->unit.init_query();
  lex->unit.init_select();
3844
  lex->unit.thd= thd;
3845 3846 3847
  lex->select_lex.init_query();
  lex->value_list.empty();
  lex->param_list.empty();
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3848 3849
  lex->unit.next= lex->unit.master= 
    lex->unit.link_next= lex->unit.return_to=0;
3850
  lex->unit.prev= lex->unit.link_prev= 0;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3851
  lex->unit.slave= lex->unit.global_parameters= lex->current_select=
3852
    lex->all_selects_list= &lex->select_lex;
3853 3854
  lex->select_lex.master= &lex->unit;
  lex->select_lex.prev= &lex->unit.slave;
3855
  lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0;
3856
  lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list);
3857
  lex->select_lex.options=0;
3858 3859
  lex->describe= 0;
  lex->derived_tables= FALSE;
3860 3861
  lex->lock_option= TL_READ;
  lex->found_colon= 0;
3862
  lex->safe_to_cache_query= 1;
3863
  thd->select_number= lex->select_lex.select_number= 1;
3864
  thd->free_list= 0;
3865
  thd->total_warn_count=0;			// Warnings for this query
3866 3867
  thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
  thd->sent_row_count= thd->examined_row_count= 0;
3868
  thd->is_fatal_error= thd->rand_used= 0;
monty@mysql.com's avatar
monty@mysql.com committed
3869 3870 3871
  thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | 
			  SERVER_QUERY_NO_INDEX_USED |
			  SERVER_QUERY_NO_GOOD_INDEX_USED);
3872
  thd->tmp_table_used= 0;
3873 3874
  if (opt_bin_log)
    reset_dynamic(&thd->user_var_events);
3875
  thd->clear_error();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3876 3877 3878
  DBUG_VOID_RETURN;
}

3879

3880 3881 3882
void
mysql_init_select(LEX *lex)
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3883
  SELECT_LEX *select_lex= lex->current_select;
3884
  select_lex->init_select();
3885
  select_lex->select_limit= HA_POS_ERROR;
3886 3887 3888 3889 3890 3891
  if (select_lex == &lex->select_lex)
  {
    lex->exchange= 0;
    lex->result= 0;
    lex->proc_list.first= 0;
  }
3892 3893
}

3894

3895
bool
3896
mysql_new_select(LEX *lex, bool move_down)
3897
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3898
  SELECT_LEX *select_lex = new(&lex->thd->mem_root) SELECT_LEX();
3899 3900
  if (!select_lex)
    return 1;
3901
  select_lex->select_number= ++lex->thd->select_number;
3902 3903 3904 3905 3906
  select_lex->init_query();
  select_lex->init_select();
  if (move_down)
  {
    /* first select_lex of subselect or derived table */
3907
    SELECT_LEX_UNIT *unit= new(&lex->thd->mem_root) SELECT_LEX_UNIT();
3908 3909 3910 3911
    if (!unit)
      return 1;
    unit->init_query();
    unit->init_select();
3912
    unit->thd= lex->thd;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3913
    unit->include_down(lex->current_select);
3914 3915
    unit->link_next= 0;
    unit->link_prev= 0;
3916
    unit->return_to= lex->current_select;
3917
    select_lex->include_down(unit);
3918
    // TODO: assign resolve_mode for fake subquery after merging with new tree
3919 3920
  }
  else
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3921
  {
3922
    select_lex->include_neighbour(lex->current_select);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3923 3924 3925 3926 3927 3928 3929 3930
    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
      */
3931
      fake= unit->fake_select_lex= new(&lex->thd->mem_root) SELECT_LEX();
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3932 3933 3934 3935 3936
      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;
3937
      fake->select_limit= HA_POS_ERROR;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3938 3939
    }
  }
peter@mysql.com's avatar
peter@mysql.com committed
3940

3941
  select_lex->master_unit()->global_parameters= select_lex;
3942
  select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
3943
  lex->current_select= select_lex;
3944
  select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
3945
  return 0;
3946
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3947

3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962
/*
  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)
{
3963
  THD *thd;
3964
  LEX *lex;
3965
  LEX_STRING tmp, null_lex_string;
3966
  DBUG_ENTER("create_select_for_variable");
3967 3968

  thd= current_thd;
3969
  lex= thd->lex;
3970 3971 3972 3973
  mysql_init_select(lex);
  lex->sql_command= SQLCOM_SELECT;
  tmp.str= (char*) var_name;
  tmp.length=strlen(var_name);
3974 3975 3976
  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));
3977 3978 3979
  DBUG_VOID_RETURN;
}

3980

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3981 3982
void mysql_init_multi_delete(LEX *lex)
{
3983
  lex->sql_command=  SQLCOM_DELETE_MULTI;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3984
  mysql_init_select(lex);
3985
  lex->select_lex.select_limit= lex->unit.select_limit_cnt=
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3986
    HA_POS_ERROR;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3987
  lex->select_lex.table_list.save_and_clear(&lex->auxilliary_table_list);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3988
}
3989

3990

3991 3992 3993 3994
/*
  When you modify mysql_parse(), you may need to mofify
  mysql_test_parse_for_slave() in this same file.
*/
3995

3996
void mysql_parse(THD *thd, char *inBuf, uint length)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3997 3998 3999 4000
{
  DBUG_ENTER("mysql_parse");

  mysql_init_query(thd);
4001
  if (query_cache_send_result_to_client(thd, inBuf, length) <= 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4002 4003
  {
    LEX *lex=lex_start(thd, (uchar*) inBuf, length);
4004
    if (!yyparse((void *)thd) && ! thd->is_fatal_error)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4005
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
4006
#ifndef NO_EMBEDDED_ACCESS_CHECKS
4007
      if (mqh_used && thd->user_connect &&
4008
	  check_mqh(thd, lex->sql_command))
4009 4010 4011 4012
      {
	thd->net.error = 0;
      }
      else
hf@deer.(none)'s avatar
hf@deer.(none) committed
4013
#endif
4014
      {
4015 4016 4017 4018 4019
	if (thd->net.report_error)
	  send_error(thd, 0, NullS);
	else
	{
	  mysql_execute_command(thd);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
4020
	  query_cache_end_of_result(thd);
4021
	}
4022
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4023 4024
    }
    else
4025 4026
    {
      DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
4027
			 thd->is_fatal_error));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4028
      query_cache_abort(&thd->net);
4029
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4030
    thd->proc_info="freeing items";
4031
    free_items(thd->free_list);  /* Free strings used by items */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4032 4033
    lex_end(lex);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4034 4035 4036 4037
  DBUG_VOID_RETURN;
}


monty@mysql.com's avatar
monty@mysql.com committed
4038
#ifdef HAVE_REPLICATION
4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054
/*
  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)
{
  LEX *lex;
  bool error= 0;

  mysql_init_query(thd);
  lex= lex_start(thd, (uchar*) inBuf, length);
monty@mysql.com's avatar
monty@mysql.com committed
4055
  if (!yyparse((void*) thd) && ! thd->is_fatal_error &&
4056 4057
      all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
    error= 1;                /* Ignore question */
monty@mysql.com's avatar
monty@mysql.com committed
4058
  free_items(thd->free_list);  /* Free strings used by items */
4059 4060 4061 4062
  lex_end(lex);

  return error;
}
monty@mysql.com's avatar
monty@mysql.com committed
4063
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4064 4065 4066 4067 4068 4069

/*****************************************************************************
** Store field definition for create
** Return 0 if ok
******************************************************************************/

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4070
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
4071
		       char *length, char *decimals,
4072
		       uint type_modifier,
4073 4074
		       Item *default_value, Item *on_update_value,
                       LEX_STRING *comment,
4075 4076
		       char *change, TYPELIB *interval, CHARSET_INFO *cs,
		       uint uint_geom_type)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4077 4078
{
  register create_field *new_field;
4079
  LEX  *lex= thd->lex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4080
  uint allowed_type_modifier=0;
4081
  char warn_buff[MYSQL_ERRMSG_SIZE];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4082 4083 4084 4085
  DBUG_ENTER("add_field_to_list");

  if (strlen(field_name) > NAME_LEN)
  {
4086
    net_printf(thd, ER_TOO_LONG_IDENT, field_name); /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4087 4088 4089 4090 4091
    DBUG_RETURN(1);				/* purecov: inspected */
  }
  if (type_modifier & PRI_KEY_FLAG)
  {
    lex->col_list.push_back(new key_part_spec(field_name,0));
4092
    lex->key_list.push_back(new Key(Key::PRIMARY, NullS, HA_KEY_ALG_UNDEF,
4093
				    0, lex->col_list));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4094 4095 4096 4097 4098
    lex->col_list.empty();
  }
  if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
  {
    lex->col_list.push_back(new key_part_spec(field_name,0));
4099
    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
4100 4101 4102 4103
				    lex->col_list));
    lex->col_list.empty();
  }

4104
  if (default_value)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4105
  {
4106
    /* 
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4107 4108
      Default value should be literal => basic constants =>
      no need fix_fields()
4109 4110 4111
      
      We allow only one function as part of default value - 
      NOW() as default for TIMESTAMP type.
4112
    */
4113 4114 4115 4116 4117 4118 4119 4120
    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
4121
    {
4122 4123 4124 4125
      default_value=0;
      if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
	  NOT_NULL_FLAG)
      {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4126
	net_printf(thd,ER_INVALID_DEFAULT,field_name);
4127 4128 4129 4130 4131
	DBUG_RETURN(1);
      }
    }
    else if (type_modifier & AUTO_INCREMENT_FLAG)
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4132
      net_printf(thd, ER_INVALID_DEFAULT, field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4133 4134 4135
      DBUG_RETURN(1);
    }
  }
4136 4137 4138 4139 4140 4141 4142

  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
4143 4144 4145 4146
  if (!(new_field=new create_field()))
    DBUG_RETURN(1);
  new_field->field=0;
  new_field->field_name=field_name;
4147
  new_field->def= default_value;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4148 4149 4150 4151 4152 4153 4154 4155 4156 4157
  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;
4158
  new_field->charset=cs;
4159
  new_field->geom_type= (Field::geometry_type) uint_geom_type;
4160

4161 4162 4163 4164 4165 4166 4167 4168
  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
4169 4170
    new_field->comment.str=   (char*) comment->str;
    new_field->comment.length=comment->length;
4171
  }
4172 4173
  if (length && !(new_field->length= (uint) atoi(length)))
    length=0; /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4174 4175 4176
  uint sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1;

  if (new_field->length && new_field->decimals &&
4177
      new_field->length < new_field->decimals+1 &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4178
      new_field->decimals != NOT_FIXED_DEC)
4179
    new_field->length=new_field->decimals+1; /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205

  switch (type) {
  case FIELD_TYPE_TINY:
    if (!length) new_field->length=3+sign_len;
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    break;
  case FIELD_TYPE_SHORT:
    if (!length) new_field->length=5+sign_len;
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    break;
  case FIELD_TYPE_INT24:
    if (!length) new_field->length=8+sign_len;
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    break;
  case FIELD_TYPE_LONG:
    if (!length) new_field->length=10+sign_len;
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    break;
  case FIELD_TYPE_LONGLONG:
    if (!length) new_field->length=20;
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    break;
  case FIELD_TYPE_NULL:
    break;
  case FIELD_TYPE_DECIMAL:
    if (!length)
4206 4207 4208 4209 4210 4211 4212
      new_field->length= 10;			// Default length for DECIMAL
    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
4213
    break;
4214 4215
  case FIELD_TYPE_STRING:
  case FIELD_TYPE_VAR_STRING:
4216
    if (new_field->length <= MAX_FIELD_CHARLENGTH || default_value)
4217 4218 4219 4220
      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",
4221
	    (cs == &my_charset_bin) ? "BLOB" : "TEXT");
4222 4223 4224
    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
4225 4226 4227 4228
  case FIELD_TYPE_BLOB:
  case FIELD_TYPE_TINY_BLOB:
  case FIELD_TYPE_LONG_BLOB:
  case FIELD_TYPE_MEDIUM_BLOB:
4229
  case FIELD_TYPE_GEOMETRY:
4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243
    if (new_field->length)
    {
      /* The user has given a length to the blob column */
      if (new_field->length < 256)
	type= FIELD_TYPE_TINY_BLOB;
      if (new_field->length < 65536)
	type= FIELD_TYPE_BLOB;
      else if (new_field->length < 256L*256L*256L)
	type= FIELD_TYPE_MEDIUM_BLOB;
      else
	type= FIELD_TYPE_LONG_BLOB;
      new_field->length= 0;
    }
    new_field->sql_type= type;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4244 4245 4246 4247 4248 4249
    if (default_value)				// Allow empty as default value
    {
      String str,*res;
      res=default_value->val_str(&str);
      if (res->length())
      {
4250
	net_printf(thd,ER_BLOB_CANT_HAVE_DEFAULT,field_name); /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269
	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)
      {
4270
	net_printf(thd,ER_WRONG_FIELD_SPEC,field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305
	DBUG_RETURN(1);
      }
      else if (tmp_length > PRECISION_FOR_FLOAT)
      {
	new_field->sql_type=FIELD_TYPE_DOUBLE;
	new_field->length=DBL_DIG+7;			// -[digits].E+###
      }
      else
	new_field->length=FLT_DIG+6;			// -[digits].E+##
      new_field->decimals= NOT_FIXED_DEC;
      break;
    }
    if (!length)
    {
      new_field->length =  FLT_DIG+6;
      new_field->decimals= NOT_FIXED_DEC;
    }
    break;
  case FIELD_TYPE_DOUBLE:
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    if (!length)
    {
      new_field->length = DBL_DIG+7;
      new_field->decimals=NOT_FIXED_DEC;
    }
    break;
  case FIELD_TYPE_TIMESTAMP:
    if (!length)
      new_field->length= 14;			// Full date YYYYMMDDHHMMSS
    else
    {
      new_field->length=((new_field->length+1)/2)*2; /* purecov: inspected */
      new_field->length= min(new_field->length,14); /* purecov: inspected */
    }
    new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG | NOT_NULL_FLAG;
4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333
    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
    {
      /* 
        We are setting TIMESTAMP_OLD_FIELD here only temporary, we will 
        replace this value by TIMESTAMP_DNUN_FIELD or NONE later when 
        information about all TIMESTAMP fields in table will be availiable.
      */
      new_field->unireg_check= on_update_value?Field::TIMESTAMP_UN_FIELD:
                                               Field::TIMESTAMP_OLD_FIELD;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351
    break;
  case FIELD_TYPE_DATE:				// Old date type
    if (protocol_version != PROTOCOL_VERSION-1)
      new_field->sql_type=FIELD_TYPE_NEWDATE;
    /* fall trough */
  case FIELD_TYPE_NEWDATE:
    new_field->length=10;
    break;
  case FIELD_TYPE_TIME:
    new_field->length=10;
    break;
  case FIELD_TYPE_DATETIME:
    new_field->length=19;
    break;
  case FIELD_TYPE_SET:
    {
      if (interval->count > sizeof(longlong)*8)
      {
4352
	net_printf(thd,ER_TOO_BIG_SET,field_name); /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4353 4354 4355 4356 4357 4358 4359 4360
	DBUG_RETURN(1);				/* purecov: inspected */
      }
      new_field->pack_length=(interval->count+7)/8;
      if (new_field->pack_length > 4)
	new_field->pack_length=8;
      new_field->interval=interval;
      new_field->length=0;
      for (const char **pos=interval->type_names; *pos ; pos++)
4361 4362 4363
      {
	new_field->length+=(uint) strip_sp((char*) *pos)+1;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4364 4365 4366 4367
      new_field->length--;
      set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1);
      if (default_value)
      {
4368 4369
	char *not_used;
	uint not_used2;
4370
	bool not_used3;
4371

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4372 4373 4374
	thd->cuted_fields=0;
	String str,*res;
	res=default_value->val_str(&str);
4375
	(void) find_set(interval, res->ptr(), res->length(), &not_used,
4376
			&not_used2, &not_used3);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4377 4378
	if (thd->cuted_fields)
	{
4379
	  net_printf(thd,ER_INVALID_DEFAULT,field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4380 4381 4382 4383 4384 4385 4386 4387 4388
	  DBUG_RETURN(1);
	}
      }
    }
    break;
  case FIELD_TYPE_ENUM:
    {
      new_field->interval=interval;
      new_field->pack_length=interval->count < 256 ? 1 : 2; // Should be safe
4389
      new_field->length=(uint) strip_sp((char*) interval->type_names[0]);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4390 4391
      for (const char **pos=interval->type_names+1; *pos ; pos++)
      {
4392
	uint length=(uint) strip_sp((char*) *pos);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4393 4394 4395 4396 4397 4398 4399
	set_if_bigger(new_field->length,length);
      }
      set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1);
      if (default_value)
      {
	String str,*res;
	res=default_value->val_str(&str);
4400 4401
	res->strip_sp();
	if (!find_type(interval, res->ptr(), res->length(), 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4402
	{
4403
	  net_printf(thd,ER_INVALID_DEFAULT,field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4404 4405 4406 4407 4408 4409 4410
	  DBUG_RETURN(1);
	}
      }
      break;
    }
  }

4411 4412
  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
4413
      (!new_field->length && !(new_field->flags & BLOB_FLAG) &&
4414
       type != FIELD_TYPE_STRING &&
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
4415
       type != FIELD_TYPE_VAR_STRING && type != FIELD_TYPE_GEOMETRY))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4416
  {
4417
    net_printf(thd,ER_TOO_BIG_FIELDLENGTH,field_name,
4418
	       MAX_FIELD_CHARLENGTH);		/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4419 4420 4421 4422 4423
    DBUG_RETURN(1);				/* purecov: inspected */
  }
  type_modifier&= AUTO_INCREMENT_FLAG;
  if ((~allowed_type_modifier) & type_modifier)
  {
4424
    net_printf(thd,ER_WRONG_FIELD_SPEC,field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445
    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
4446
add_proc_to_list(THD* thd, Item *item)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4447 4448 4449 4450
{
  ORDER *order;
  Item	**item_ptr;

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4451
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4452 4453 4454 4455 4456
    return 1;
  item_ptr = (Item**) (order+1);
  *item_ptr= item;
  order->item=item_ptr;
  order->free_me=0;
4457
  thd->lex->proc_list.link_in_list((byte*) order,(byte**) &order->next);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4458 4459 4460 4461 4462 4463 4464 4465
  return 0;
}


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

static void remove_escape(char *name)
{
4466 4467
  if (!*name)					// For empty DB names
    return;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4468 4469
  char *to;
#ifdef USE_MB
4470
  char *strend=name+(uint) strlen(name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4471 4472 4473 4474 4475 4476
#endif
  for (to=name; *name ; name++)
  {
#ifdef USE_MB
    int l;
/*    if ((l = ismbchar(name, name+MBMAXLEN))) { Wei He: I think it's wrong */
4477 4478
    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
4479 4480 4481 4482 4483 4484 4485 4486
    {
	while (l--)
	    *to++ = *name++;
	name--;
	continue;
    }
#endif
    if (*name == '\\' && name[1])
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4487
      name++;					// Skip '\\'
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4488 4489 4490 4491 4492 4493 4494 4495 4496 4497
    *to++= *name;
  }
  *to=0;
}

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


monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4498
bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4499 4500 4501
{
  ORDER *order;
  DBUG_ENTER("add_to_list");
4502
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4503
    DBUG_RETURN(1);
4504 4505
  order->item_ptr= item;
  order->item= &order->item_ptr;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4506 4507 4508
  order->asc = asc;
  order->free_me=0;
  order->used=0;
4509
  list.link_in_list((byte*) order,(byte**) &order->next);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4510 4511 4512 4513
  DBUG_RETURN(0);
}


monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532
/*
  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
4533 4534
TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
					     Table_ident *table,
4535
					     LEX_STRING *alias,
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4536 4537
					     ulong table_options,
					     thr_lock_type lock_type,
4538 4539
					     List<String> *use_index_arg,
					     List<String> *ignore_index_arg,
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4540
                                             LEX_STRING *option)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4541 4542 4543 4544 4545 4546 4547 4548
{
  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;
4549
  if (check_table_name(table->table.str,table->table.length) ||
4550
      table->db.str && check_db_name(table->db.str))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4551
  {
4552
    net_printf(thd, ER_WRONG_TABLE_NAME, table->table.str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4553 4554 4555 4556
    DBUG_RETURN(0);
  }

  if (!alias)					/* Alias is case sensitive */
4557 4558 4559 4560 4561 4562
  {
    if (table->sel)
    {
      net_printf(thd,ER_DERIVED_MUST_HAVE_ALIAS);
      DBUG_RETURN(0);
    }
4563
    if (!(alias_str=thd->memdup(alias_str,table->table.length+1)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4564
      DBUG_RETURN(0);
4565
  }
4566
  if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4567
    DBUG_RETURN(0);				/* purecov: inspected */
peter@mysql.com's avatar
peter@mysql.com committed
4568
  if (table->db.str)
4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579
  {
    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
  {
4580 4581
    /* The following can't be "" as we may do 'casedn_str()' on it */
    ptr->db= empty_c_string;
4582 4583
    ptr->db_length= 0;
  }
peter@mysql.com's avatar
peter@mysql.com committed
4584

4585
  ptr->alias= alias_str;
4586 4587
  if (lower_case_table_names && table->table.length)
    my_casedn_str(files_charset_info, table->table.str);
4588
  ptr->real_name=table->table.str;
4589
  ptr->real_name_length=table->table.length;
4590
  ptr->lock_type=   lock_type;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4591 4592
  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
4593
  ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
4594
  ptr->derived=	    table->sel;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4595
  ptr->cacheable_table= 1;
4596 4597 4598 4599 4600 4601
  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
4602
  ptr->option= option ? option->str : 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4603
  /* check that used name is unique */
4604
  if (lock_type != TL_IGNORE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4605
  {
4606
    for (TABLE_LIST *tables=(TABLE_LIST*) table_list.first ;
4607
	 tables ;
4608
	 tables=tables->next)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4609
    {
4610 4611
      if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
	  !strcmp(ptr->db, tables->db))
4612
      {
4613
	net_printf(thd,ER_NONUNIQ_TABLE,alias_str); /* purecov: tested */
4614 4615
	DBUG_RETURN(0);				/* purecov: tested */
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4616 4617
    }
  }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4618
  table_list.link_in_list((byte*) ptr, (byte**) &ptr->next);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4619 4620 4621
  DBUG_RETURN(ptr);
}

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

4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635
/*
  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
*/

4636
void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
4637 4638 4639 4640 4641 4642
{
  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));

4643
  for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first ;
4644 4645 4646 4647 4648 4649 4650 4651 4652
       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
4653

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4654 4655
void add_join_on(TABLE_LIST *b,Item *expr)
{
4656
  if (expr)
4657
  {
4658 4659 4660 4661 4662 4663 4664 4665
    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();
4666
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4667 4668 4669
}


4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687
/*
  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
4688 4689 4690 4691 4692
void add_join_natural(TABLE_LIST *a,TABLE_LIST *b)
{
  b->natural_join=a;
}

4693
/*
4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710
  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
4711 4712
*/

4713 4714
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
4715 4716 4717
{
  bool result=0;
  select_errors=0;				/* Write if more errors */
4718
  bool tmp_write_to_binlog= 1;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
4719
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4720 4721
  if (options & REFRESH_GRANT)
  {
4722
    acl_reload(thd);
4723
    grant_reload(thd);
4724
    if (mqh_used)
4725
      reset_mqh(thd,(LEX_USER *) NULL,true);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4726
  }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
4727
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4728 4729
  if (options & REFRESH_LOG)
  {
4730
    /*
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4731 4732
      Flush the normal query log, the update log, the binary log,
      the slow query log, and the relay log (if it exists).
4733
    */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4734

4735 4736 4737 4738 4739 4740
    /* 
     Writing this command to the binlog may result in infinite loops when doing
     mysqlbinlog|mysql, and anyway it does not really make sense to log it
     automatically (would cause more trouble to users than it would help them)
    */
    tmp_write_to_binlog= 0;
4741 4742 4743 4744
    mysql_log.new_file(1);
    mysql_update_log.new_file(1);
    mysql_bin_log.new_file(1);
    mysql_slow_log.new_file(1);
4745
#ifdef HAVE_REPLICATION
4746
    if (mysql_bin_log.is_open() && expire_logs_days)
4747 4748 4749
    {
      long purge_time= time(0) - expire_logs_days*24*60*60;
      if (purge_time >= 0)
4750
	mysql_bin_log.purge_logs_before_date(purge_time);
4751
    }
4752
    pthread_mutex_lock(&LOCK_active_mi);
4753
    rotate_relay_log(active_mi);
4754
    pthread_mutex_unlock(&LOCK_active_mi);
4755
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4756 4757
    if (ha_flush_logs())
      result=1;
4758 4759
    if (flush_error_log())
      result=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4760
  }
4761
#ifdef HAVE_QUERY_CACHE
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4762 4763
  if (options & REFRESH_QUERY_CACHE_FREE)
  {
4764
    query_cache.pack();				// FLUSH QUERY CACHE
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4765 4766 4767 4768
    options &= ~REFRESH_QUERY_CACHE; //don't flush all cache, just free memory
  }
  if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
  {
4769
    query_cache.flush();			// RESET QUERY CACHE
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4770
  }
4771
#endif /*HAVE_QUERY_CACHE*/
4772 4773 4774 4775 4776
  /*
    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
4777
  {
4778
    if ((options & REFRESH_READ_LOCK) && thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4779
    {
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
4780 4781 4782 4783
      /*
	Writing to the binlog could cause deadlocks, as we don't log
	UNLOCK TABLES
      */
4784
      tmp_write_to_binlog= 0;
4785 4786
      if (lock_global_read_lock(thd))
	return 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4787
    }
4788
    result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4789 4790 4791 4792 4793 4794 4795
  }
  if (options & REFRESH_HOSTS)
    hostname_cache_refresh();
  if (options & REFRESH_STATUS)
    refresh_status();
  if (options & REFRESH_THREADS)
    flush_thread_cache();
4796
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4797
  if (options & REFRESH_MASTER)
4798 4799
  {
    tmp_write_to_binlog= 0;
4800 4801
    if (reset_master(thd))
      result=1;
4802
  }
4803
#endif
4804
#ifdef OPENSSL
4805 4806 4807 4808 4809 4810
   if (options & REFRESH_DES_KEY_FILE)
   {
     if (des_key_file)
       result=load_des_key_file(des_key_file);
   }
#endif
4811
#ifdef HAVE_REPLICATION
4812 4813
 if (options & REFRESH_SLAVE)
 {
4814
   tmp_write_to_binlog= 0;
4815
   pthread_mutex_lock(&LOCK_active_mi);
4816
   if (reset_slave(thd, active_mi))
4817
     result=1;
4818
   pthread_mutex_unlock(&LOCK_active_mi);
4819
 }
4820
#endif
4821
 if (options & REFRESH_USER_RESOURCES)
4822
   reset_mqh(thd,(LEX_USER *) NULL);
4823 4824
 if (write_to_binlog)
   *write_to_binlog= tmp_write_to_binlog;
4825
 return result;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4826 4827
}

4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839
/*
  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
*/

4840
void kill_one_thread(THD *thd, ulong id)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4841 4842 4843
{
  THD *tmp;
  uint error=ER_NO_SUCH_THREAD;
4844 4845
  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
4846 4847 4848 4849
  while ((tmp=it++))
  {
    if (tmp->thread_id == id)
    {
4850 4851
      pthread_mutex_lock(&tmp->LOCK_delete);	// Lock from delete
      break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4852 4853 4854
    }
  }
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867
  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
4868
  if (!error)
4869
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4870
  else
4871
    net_printf(thd,error,id);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4872 4873 4874 4875 4876 4877 4878 4879 4880 4881
}

/* 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)
4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893
      *(ulong*) ptr->value= 0;
    else if (ptr->type == SHOW_KEY_CACHE_LONG)
    {
      /*
	Reset value in 'default' key cache.
	This needs to be recoded when we have thread specific key values
      */
      char *value= (((char*) sql_key_cache) +
		    (uint) ((char*) (ptr->value) -
			    (char*) &dflt_key_cache_var));
      *(ulong*) value= 0;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4894 4895 4896
  }
  pthread_mutex_unlock(&LOCK_status);
}
4897 4898 4899 4900


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

4901 4902
static bool append_file_to_dir(THD *thd, const char **filename_ptr,
			       const char *table_name)
4903
{
4904
  char buff[FN_REFLEN],*ptr, *end;
4905 4906 4907 4908 4909 4910 4911
  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))
  {
4912
    my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr);
4913 4914 4915 4916
    return 1;
  }
  /* Fix is using unix filename format on dos */
  strmov(buff,*filename_ptr);
4917
  end=convert_dirname(buff, *filename_ptr, NullS);
4918
  if (!(ptr=thd->alloc((uint) (end-buff)+(uint) strlen(table_name)+1)))
4919 4920
    return 1;					// End of memory
  *filename_ptr=ptr;
4921
  strxmov(ptr,buff,table_name,NullS);
4922 4923
  return 0;
}
4924

4925

4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939
/*
  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;
4940
  if (thd->lex->current_select != &thd->lex->select_lex)
4941 4942
  {
    char command[80];
4943 4944
    strmake(command, thd->lex->yylval->symbol.str,
	    min(thd->lex->yylval->symbol.length, sizeof(command)-1));
4945
    net_printf(thd, ER_CANT_USE_OPTION_HERE, command);
4946 4947 4948 4949
    return 1;
  }
  return 0;
}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4950

4951

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4952
Comp_creator *comp_eq_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4953
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4954
  return invert?(Comp_creator *)&ne_creator:(Comp_creator *)&eq_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4955 4956
}

4957

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4958
Comp_creator *comp_ge_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4959
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4960
  return invert?(Comp_creator *)&lt_creator:(Comp_creator *)&ge_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4961 4962
}

4963

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4964
Comp_creator *comp_gt_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4965
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4966
  return invert?(Comp_creator *)&le_creator:(Comp_creator *)&gt_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4967 4968
}

4969

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4970
Comp_creator *comp_le_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4971
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4972
  return invert?(Comp_creator *)&gt_creator:(Comp_creator *)&le_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4973 4974
}

4975

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4976
Comp_creator *comp_lt_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4977
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4978
  return invert?(Comp_creator *)&ge_creator:(Comp_creator *)&lt_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4979 4980
}

4981

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4982
Comp_creator *comp_ne_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4983
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4984
  return invert?(Comp_creator *)&eq_creator:(Comp_creator *)&ne_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4985
}
4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005


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

  if ((cmp == &comp_ne_creator) && all)        // <> ALL <=> NOT IN
5010 5011 5012
    return new Item_func_not(new Item_in_subselect(left_expr, select_lex));

  Item_allany_subselect *it=
5013
    new Item_allany_subselect(left_expr, (*cmp)(all), select_lex, all);
5014 5015 5016 5017 5018
  if (all)
    return it->upper_not= new Item_func_not_all(it);	/* ALL */

  return it;						/* ANY/SOME */
}
5019 5020


5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035
/*
  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;
5036 5037 5038
  ALTER_INFO alter_info;
  alter_info.flags= ALTER_ADD_INDEX;
  alter_info.is_simple= 0;
5039 5040 5041 5042 5043 5044 5045
  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,
5046 5047
				fields, keys, 0, (ORDER*)0,
				DUP_ERROR, &alter_info));
5048 5049 5050
}


5051
int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info)
5052 5053 5054 5055 5056 5057 5058 5059
{
  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;
5060 5061 5062
  alter_info->clear();
  alter_info->flags= ALTER_DROP_INDEX;
  alter_info->is_simple= 0;
5063 5064
  DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
				&create_info, table_list,
5065 5066
				fields, keys, 0, (ORDER*)0,
				DUP_ERROR, alter_info));
5067
}
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
5068 5069


5070 5071 5072 5073 5074
/*
  Multi update query pre-check

  SYNOPSIS
    multi_update_precheck()
5075 5076
    thd		Thread handler
    tables	Global table list
5077

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5078
  RETURN VALUE
5079 5080 5081
    0   OK
    1   Error (message is sent to user)
    -1  Error (message is not sent to user)
5082
*/
5083

5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105
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)
  {
    if ((check_access(thd, UPDATE_ACL, table->db,
		      &table->grant.privilege, 0, 1) ||
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5106
	 grant_option && check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) &&
5107 5108
	(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
5109
	 grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
5110
      DBUG_RETURN(1);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5111 5112 5113 5114 5115

    /*
      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
    */
5116
    if (table->table_list)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5117
      table->table_list->table_in_update_from_clause= 1;
5118
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5119 5120 5121
  /*
    Is there tables of subqueries?
  */
5122 5123 5124 5125
  if (&lex->select_lex != lex->all_selects_list)
  {
    for (table= tables; table; table= table->next)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5126
      if (table->table_in_update_from_clause)
5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139
      {
	/*
	  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;
      }
      else
      {
	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
5140
	    grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))
5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163
	  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()
5164 5165 5166
    thd			Thread handler
    tables		Global table list
    table_count		Pointer to table counter
5167

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5168
  RETURN VALUE
5169 5170 5171
    0   OK
    1   error (message is sent to user)
    -1  error (message is not sent to user)
5172 5173 5174 5175 5176 5177 5178 5179
*/
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
5180 5181 5182
  TABLE_LIST *target_tbl;

  *table_count= 0;
5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194

  /* 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
5195
  for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next)
5196 5197 5198 5199 5200 5201
  {
    (*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
5202 5203 5204
      if (!my_strcasecmp(table_alias_charset,
			 target_tbl->alias, walk->alias) &&
	  !strcmp(walk->db, target_tbl->db))
5205 5206 5207 5208
	break;
    }
    if (!walk)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5209 5210
      my_error(ER_UNKNOWN_TABLE, MYF(0), target_tbl->real_name,
	       "MULTI DELETE");
5211 5212 5213 5214
      DBUG_RETURN(-1);
    }
    if (walk->derived)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5215 5216
      my_error(ER_NON_UPDATABLE_TABLE, MYF(0), target_tbl->real_name,
	       "DELETE");
5217 5218
      DBUG_RETURN(-1);
    }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5219 5220
    walk->lock_type= target_tbl->lock_type;
    target_tbl->table_list= walk;	// Remember corresponding table
5221 5222 5223 5224 5225 5226 5227 5228 5229 5230
  }
  DBUG_RETURN(0);
}


/*
  INSERT ... SELECT query pre-check

  SYNOPSIS
    multi_delete_precheck()
5231 5232
    thd		Thread handler
    tables	Global table list
5233

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5234
  RETURN VALUE
5235 5236 5237
    0   OK
    1   Error (message is sent to user)
    -1  Error (message is not sent to user)
5238
*/
5239

5240 5241 5242 5243 5244 5245 5246 5247 5248
int insert_select_precheck(THD *thd, TABLE_LIST *tables)
{
  DBUG_ENTER("insert_select_precheck");
  /*
    Check that we have modify privileges for the first table and
    select privileges for the rest
  */
  ulong privilege= (thd->lex->duplicates == DUP_REPLACE ?
		    INSERT_ACL | DELETE_ACL : INSERT_ACL);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5249 5250 5251 5252 5253 5254 5255 5256 5257
  DBUG_RETURN(check_one_table_access(thd, privilege, tables) ? 1 : 0);
}


/*
  simple UPDATE query pre-check

  SYNOPSIS
    update_precheck()
5258 5259
    thd		Thread handler
    tables	Global table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5260 5261

  RETURN VALUE
5262 5263 5264
    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
5265
*/
5266

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284
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()
5285 5286
    thd		Thread handler
    tables	Global table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5287 5288

  RETURN VALUE
5289 5290 5291
    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
5292
*/
5293

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5294 5295 5296 5297 5298
int delete_precheck(THD *thd, TABLE_LIST *tables)
{
  DBUG_ENTER("delete_precheck");
  if (check_one_table_access(thd, DELETE_ACL, tables))
    DBUG_RETURN(1);
5299
  /* Set privilege for the WHERE clause */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5300 5301 5302 5303 5304 5305 5306 5307 5308 5309
  tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
  DBUG_RETURN(0);
}


/*
  simple INSERT query pre-check

  SYNOPSIS
    insert_precheck()
5310 5311
    thd		Thread handler
    tables	Global table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5312 5313

  RETURN VALUE
5314 5315 5316
    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
5317
*/
5318

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5319 5320 5321 5322 5323 5324 5325 5326 5327
int insert_precheck(THD *thd, TABLE_LIST *tables, bool update)
{
  LEX *lex= thd->lex;
  DBUG_ENTER("insert_precheck");

  ulong privilege= (lex->duplicates == DUP_REPLACE ?
		    INSERT_ACL | DELETE_ACL : INSERT_ACL | update);

  if (check_one_table_access(thd, privilege, tables))
5328
    DBUG_RETURN(1);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5329 5330 5331 5332 5333 5334

  if (lex->select_lex.item_list.elements != lex->value_list.elements)
  {
    my_error(ER_WRONG_VALUE_COUNT, MYF(0));
    DBUG_RETURN(-1);
  }
5335 5336
  DBUG_RETURN(0);
}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5337 5338 5339 5340 5341 5342 5343


/*
  CREATE TABLE query pre-check

  SYNOPSIS
    create_table_precheck()
5344 5345 5346
    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
5347 5348

  RETURN VALUE
5349 5350 5351
    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
5352
*/
5353

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369
int create_table_precheck(THD *thd, TABLE_LIST *tables,
			  TABLE_LIST *create_table)
{
  LEX *lex= thd->lex;
  DBUG_ENTER("create_table_precheck");
  ulong want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
		    CREATE_TMP_ACL : CREATE_ACL);
  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))
    DBUG_RETURN(1);
  DBUG_RETURN((grant_option && want_priv != CREATE_TMP_ACL &&
	       check_grant(thd, want_priv, create_table, 0, UINT_MAX, 0)) ?
5370
	      1 : 0);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5371
}