sql_parse.cc 158 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

const char *command_name[]={
  "Sleep", "Quit", "Init DB", "Query", "Field List", "Create DB",
  "Drop DB", "Refresh", "Shutdown", "Statistics", "Processlist",
66
  "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
  "Reset stmt", "Set option",
70
  "Error"					// Last command number
bk@work.mysql.com's avatar
bk@work.mysql.com committed
71 72
};

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

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

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

104

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


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


131 132
static HASH hash_user_connections;

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

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

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

185
}
186 187 188


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

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

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

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

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

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

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

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

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

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

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

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

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


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

429 430
#ifndef NO_EMBEDDED_ACCESS_CHECKS

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

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

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

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

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

495

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

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

502 503 504
/*
  Mark all commands that somehow changes a table
  This is used to check number of updates / hour
kent@mysql.com's avatar
kent@mysql.com committed
505 506 507

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

kent@mysql.com's avatar
kent@mysql.com committed
510
char  uc_update_queries[SQLCOM_END+1];
511 512 513

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

516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
  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;
535
  uc_update_queries[SQLCOM_UPDATE_MULTI]=1;
536 537
}

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

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
544 545 546
/*
  Check if maximum queries per hour limit has been reached
  returns 0 if OK.
547

548 549 550
  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
551 552
*/

553

554
static bool check_mqh(THD *thd, uint check_command)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
555
{
556 557 558
#ifdef NO_EMBEDDED_ACCESS_CHECKS
  return(0);
#else
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
559
  bool error=0;
560
  time_t check_time = thd->start_time ?  thd->start_time : time(NULL);
561
  USER_CONN *uc=thd->user_connect;
562
  DBUG_ENTER("check_mqh");
563
  DBUG_ASSERT(uc != 0);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
564

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

601

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

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
641
/*
642
    Perform handshake, authorize client and update thd ACL variables.
643
  SYNOPSIS
644
    check_connection()
645
    thd  thread handle
646 647

  RETURN
648
     0  success, OK is sent to user, thd is updated.
649 650
    -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
651 652
*/

hf@deer.(none)'s avatar
hf@deer.(none) committed
653 654
#ifndef EMBEDDED_LIBRARY
static int check_connection(THD *thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
655
{
656
  uint connect_errors= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
657
  NET *net= &thd->net;
658

659 660 661
  DBUG_PRINT("info",
             ("New connection received on %s", vio_description(net->vio)));

bk@work.mysql.com's avatar
bk@work.mysql.com committed
662 663
  if (!thd->host)                           // If TCP/IP connection
  {
664
    char ip[30];
665

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

717 718 719 720 721
    if (opt_using_transactions)
      client_flags|=CLIENT_TRANSACTIONS;
#ifdef HAVE_COMPRESS
    client_flags |= CLIENT_COMPRESS;
#endif /* HAVE_COMPRESS */
722 723 724 725
#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
726

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

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

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

  if (end >= (char*) net->read_pos+ pkt_len +2)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
855
  {
856 857
    inc_host_errors(&thd->remote.sin_addr);
    return(ER_HANDSHAKE_ERROR);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
858 859 860
  }

  if (thd->client_capabilities & CLIENT_INTERACTIVE)
861
    thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
862
  if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
863 864
      opt_using_transactions)
    net->return_status= &thd->server_status;
865
  net->read_timeout=(uint) thd->variables.net_read_timeout;
peter@mysql.com's avatar
peter@mysql.com committed
866

867 868
  char *user= end;
  char *passwd= strend(user)+1;
869
  char *db= passwd;
870
  char db_buff[NAME_LEN+1];                     // buffer to store db in utf8 
871
  char user_buff[USERNAME_LENGTH+1];		// buffer to store user in utf8
872 873 874 875 876 877 878 879 880
  /* 
    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
881

kostja@oak.local's avatar
kostja@oak.local committed
882 883
  /* Since 4.1 all database names are stored in utf8 */
  if (db)
peter@mysql.com's avatar
peter@mysql.com committed
884
  {
885
    uint dummy_errors;
886 887 888
    db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
                             system_charset_info,
                             db, strlen(db),
889
                             thd->charset(), &dummy_errors)]= 0;
890
    db= db_buff;
kostja@oak.local's avatar
kostja@oak.local committed
891
  }
peter@mysql.com's avatar
peter@mysql.com committed
892

893 894
  if (user)
  {
895
    uint dummy_errors;
896 897
    user_buff[copy_and_convert(user_buff, sizeof(user_buff)-1,
			       system_charset_info, user, strlen(user),
898
			       thd->charset(), &dummy_errors)]= '\0';
899 900 901
    user= user_buff;
  }

902 903
  if (thd->user)
    x_free(thd->user);
904 905
  if (!(thd->user= my_strdup(user, MYF(0))))
    return (ER_OUT_OF_RESOURCES);
906
  return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
907 908
}

909

910 911
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
912 913 914 915
{
  Vio* save_vio;
  ulong save_client_capabilities;

916 917 918 919 920 921 922 923 924
  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
925 926
  save_client_capabilities= thd->client_capabilities;
  thd->client_capabilities|= CLIENT_MULTI_QUERIES;
927 928 929 930
  /*
    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
931 932 933
  save_vio= thd->net.vio;
  thd->net.vio= 0;
  dispatch_command(COM_QUERY, thd, thd->query, thd->query_length+1);
934
  rw_unlock(var_mutex);
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
935 936 937 938 939
  thd->client_capabilities= save_client_capabilities;
  thd->net.vio= save_vio;
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
940 941 942 943
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
944
    (uint) ((thd->thr_create_time = time(NULL)) - thd->connect_time);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
945 946 947 948 949
  if (launch_time >= slow_launch_time)
    statistic_increment(slow_launch_threads,&LOCK_status );

  pthread_detach_this_thread();

950 951
#if !defined( __WIN__) && !defined(OS2)	// Win32 calls this in pthread_create
  // The following calls needs to be done before we call DBUG_ macros
952
  if (!(test_flags & TEST_NO_THREADS) & my_thread_init())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
953
  {
954
    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
955
    statistic_increment(aborted_connects,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
956 957 958 959 960
    end_thread(thd,0);
    return 0;
  }
#endif

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

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

993
    if ((error=check_connection(thd)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
994 995
    {						// Wrong permissions
      if (error > 0)
996
	net_printf(thd,error,thd->host_or_ip);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
997 998
#ifdef __NT__
      if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
999
	my_sleep(1000);				/* must wait after eof() */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1000
#endif
1001
      statistic_increment(aborted_connects,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1002 1003
      goto end_thread;
    }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1004 1005 1006
#ifdef __NETWARE__
    netware_reg_user(thd->ip, thd->user, "MySQL");
#endif
1007
    if (thd->variables.max_join_size == HA_POS_ERROR)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1008 1009 1010 1011
      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
1012
    thd->version= refresh_version;
1013 1014 1015
    thd->proc_info= 0;
    thd->set_time();
    thd->init_for_queries();
1016
    if (sys_init_connect.value_length && !(thd->master_access & SUPER_ACL))
gluh@gluh.mysql.r18.ru's avatar
gluh@gluh.mysql.r18.ru committed
1017
    {
1018 1019 1020
      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
1021
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1022 1023 1024 1025 1026
    while (!net->error && net->vio != 0 && !thd->killed)
    {
      if (do_command(thd))
	break;
    }
1027 1028
    if (thd->user_connect)
      decrease_user_connections(thd->user_connect);
1029
    free_root(thd->mem_root,MYF(0));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1030
    if (net->error && net->vio != 0 && net->report_error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1031
    {
1032
      if (!thd->killed && thd->variables.log_warnings > 1)
serg@serg.mylan's avatar
serg@serg.mylan committed
1033 1034 1035 1036 1037 1038
        sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION),
                          thd->thread_id,(thd->db ? thd->db : "unconnected"),
                          thd->user ? thd->user : "unauthenticated",
                          thd->host_or_ip,
                          (net->last_errno ? ER(net->last_errno) :
                           ER(ER_UNKNOWN_ERROR)));
1039
      send_error(thd,net->last_errno,NullS);
1040
      statistic_increment(aborted_threads,&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1041
    }
1042 1043 1044 1045
    else if (thd->killed)
    {
      statistic_increment(aborted_threads,&LOCK_status);
    }
1046
    
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1047
end_thread:
1048
    close_connection(thd, 0, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
    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
1060 1061
#endif /* EMBEDDED_LIBRARY */

1062 1063 1064 1065
/*
  Execute commands from bootstrap_file.
  Used when creating the initial grant tables
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1066

1067
extern "C" pthread_handler_decl(handle_bootstrap,arg)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1068
{
1069 1070 1071
  THD *thd=(THD*) arg;
  FILE *file=bootstrap_file;
  char *buff;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1072

1073
  /* The following must be called before DBUG_ENTER */
1074
  if (my_thread_init() || thd->store_globals())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1075
  {
hf@deer.(none)'s avatar
hf@deer.(none) committed
1076
#ifndef EMBEDDED_LIBRARY
1077
    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1078
#endif
1079
    thd->fatal_error();
1080
    goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1081
  }
1082 1083
  DBUG_ENTER("handle_bootstrap");

hf@deer.(none)'s avatar
hf@deer.(none) committed
1084
#ifndef EMBEDDED_LIBRARY
1085 1086
  pthread_detach_this_thread();
  thd->thread_stack= (char*) &thd;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1087
#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1088
  sigset_t set;
1089 1090
  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
1091
#endif
hf@deer.(none)'s avatar
hf@deer.(none) committed
1092
#endif /* EMBEDDED_LIBRARY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1093

1094
  if (thd->variables.max_join_size == HA_POS_ERROR)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1095 1096 1097 1098
    thd->options |= OPTION_BIG_SELECTS;

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

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

  /* thd->fatal_error should be set in case something went wrong */
end:
hf@deer.(none)'s avatar
hf@deer.(none) committed
1137
#ifndef EMBEDDED_LIBRARY
1138 1139 1140
  (void) pthread_mutex_lock(&LOCK_thread_count);
  thread_count--;
  (void) pthread_mutex_unlock(&LOCK_thread_count);
1141
  (void) pthread_cond_broadcast(&COND_thread_count);
1142 1143
  my_thread_end();
  pthread_exit(0);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1144
#endif
1145
  DBUG_RETURN(0);				// Never reached
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1146 1147
}

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

1150
void free_items(Item *item)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1151
{
1152
  for (; item ; item=item->next)
1153
    item->delete_self();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1154 1155
}

1156 1157 1158 1159 1160 1161 1162 1163
    /* 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
1164 1165 1166 1167 1168 1169 1170
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;
1171
  if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST))))
1172
    DBUG_RETURN(1); // out of memory
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1173
  table_list->db = db;
1174
  table_list->real_name = table_list->alias = tbl_name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1175 1176 1177
  table_list->lock_type = TL_READ_NO_INSERT;
  table_list->next = 0;

1178 1179
  if (!db || check_db_name(db))
  {
1180
    net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
1181 1182
    goto err;
  }
1183
  if (lower_case_table_names)
1184
    my_casedn_str(files_charset_info, tbl_name);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1185
  remove_escape(table_list->real_name);
1186 1187 1188 1189

  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
1190
  if (check_one_table_access(thd, SELECT_ACL, table_list))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1191 1192
    goto err;
  thd->free_list = 0;
1193
  thd->query_length=(uint) strlen(tbl_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1194
  thd->query = tbl_name;
1195 1196
  if ((error = mysqld_dump_create_info(thd, table, -1)))
  {
1197
    my_error(ER_GET_ERRNO, MYF(0), my_errno);
1198 1199
    goto err;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1200
  net_flush(&thd->net);
1201
  if ((error= table->file->dump(thd,fd)))
1202
    my_error(ER_GET_ERRNO, MYF(0), error);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1203

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1204 1205
err:
  close_thread_tables(thd);
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1206
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1207 1208 1209
}


1210
#ifndef EMBEDDED_LIBRARY
1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221

/*
  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
1222 1223 1224
bool do_command(THD *thd)
{
  char *packet;
1225 1226
  uint old_timeout;
  ulong packet_length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1227 1228 1229 1230 1231
  NET *net;
  enum enum_server_command command;
  DBUG_ENTER("do_command");

  net= &thd->net;
1232 1233 1234 1235
  /*
    indicator of uninitialized lex => normal flow of errors handling
    (see my_message_sql)
  */
1236
  thd->lex->current_select= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1237 1238

  packet=0;
1239 1240 1241
  old_timeout=net->read_timeout;
  // Wait max for 8 hours
  net->read_timeout=(uint) thd->variables.net_wait_timeout;
1242
  thd->clear_error();				// Clear error message
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1243 1244 1245 1246

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

1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298
/*
   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
*/
1299

1300 1301 1302 1303
bool dispatch_command(enum enum_server_command command, THD *thd,
		      char* packet, uint packet_length)
{
  NET *net= &thd->net;
1304
  bool error= 0;
1305 1306 1307
  DBUG_ENTER("dispatch_command");

  thd->command=command;
1308 1309 1310 1311
  /*
    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
  */
1312
  thd->slow_command=FALSE;
1313
  thd->set_time();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1314 1315 1316 1317 1318
  VOID(pthread_mutex_lock(&LOCK_thread_count));
  thd->query_id=query_id;
  if (command != COM_STATISTICS && command != COM_PING)
    query_id++;
  thread_running++;
1319
  /* TODO: set thd->lex->sql_command to SQLCOM_END here */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1320
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
1321

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

    statistic_increment(com_other, &LOCK_status);
1350
    thd->slow_command= TRUE;
1351 1352 1353 1354 1355 1356 1357
    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
1358 1359
  case COM_CHANGE_USER:
  {
1360
    thd->change_user();
1361
    thd->clear_error();                         // if errors from rollback
1362

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

1391 1392 1393 1394 1395 1396 1397
    /* 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;
1398
    USER_CONN *save_user_connect= thd->user_connect;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1399 1400
    
    if (!(thd->user= my_strdup(user, MYF(0))))
1401 1402 1403 1404 1405
    {
      thd->user= save_user;
      send_error(thd, ER_OUT_OF_RESOURCES);
      break;
    }
peter@mysql.com's avatar
peter@mysql.com committed
1406

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

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

1468
    while (!thd->killed && !thd->is_fatal_error && thd->lex->found_colon)
1469
    {
1470
      char *packet= thd->lex->found_colon;
1471
      /*
1472
        Multiple queries exits, execute them individually
1473
	in embedded server - just store them to be executed later 
1474
      */
1475
#ifndef EMBEDDED_LIBRARY
1476
      if (thd->lock || thd->open_tables || thd->derived_tables)
1477
        close_thread_tables(thd);
1478 1479
#endif
      ulong length= thd->query_length-(ulong)(packet-thd->query);
1480

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1511 1512 1513 1514 1515
    if (!(specialflag & SPECIAL_NO_PRIOR))
      my_pthread_setprio(pthread_self(),WAIT_PRIOR);
    DBUG_PRINT("info",("query ready"));
    break;
  }
1516
  case COM_FIELD_LIST:				// This isn't actually needed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1517
#ifdef DONT_ALLOW_SHOW_COMMANDS
1518
    send_error(thd,ER_NOT_ALLOWED_COMMAND);	/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1519 1520 1521
    break;
#else
  {
1522
    char *fields, *pend;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1523
    TABLE_LIST table_list;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1524 1525
    LEX_STRING conv_name;

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

monty@mysql.com's avatar
monty@mysql.com committed
1547 1548
    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
1549
      break;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1550 1551
    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
1552 1553
      break;
    mysqld_list_fields(thd,&table_list,fields);
1554
    free_items(thd->free_list);
1555
    thd->free_list= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1556 1557 1558 1559
    break;
  }
#endif
  case COM_QUIT:
1560
    /* We don't calculate statistics for this command */
1561
    mysql_log.write(thd,command,NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1562 1563 1564 1565
    net->error=0;				// Don't give 'abort' message
    error=TRUE;					// End server
    break;

1566
  case COM_CREATE_DB:				// QQ: To be removed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1567
    {
1568
      char *db=thd->strdup(packet), *alias;
1569
      HA_CREATE_INFO create_info;
1570

1571
      statistic_increment(com_stat[SQLCOM_CREATE_DB],&LOCK_status);
1572
      // null test to handle EOM
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1573
      if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
1574
      {
1575
	net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
1576 1577
	break;
      }
hf@deer.(none)'s avatar
hf@deer.(none) committed
1578
      if (check_access(thd,CREATE_ACL,db,0,1,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1579
	break;
1580
      mysql_log.write(thd,command,packet);
1581
      bzero(&create_info, sizeof(create_info));
1582
      if (mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db),
1583
                          &create_info, 0) < 0)
monty@mysql.com's avatar
monty@mysql.com committed
1584
        send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1585 1586
      break;
    }
1587
  case COM_DROP_DB:				// QQ: To be removed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1588
    {
1589
      statistic_increment(com_stat[SQLCOM_DROP_DB],&LOCK_status);
1590
      char *db=thd->strdup(packet), *alias;
1591
      // null test to handle EOM
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1592
      if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
1593
      {
1594
	net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
1595 1596
	break;
      }
hf@deer.(none)'s avatar
hf@deer.(none) committed
1597
      if (check_access(thd,DROP_ACL,db,0,1,0))
1598
	break;
1599 1600
      if (thd->locked_tables || thd->active_transaction())
      {
1601
	send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1602
	break;
1603
      }
1604
      mysql_log.write(thd,command,db);
1605 1606
      if (mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : db),
                      0, 0) < 0)
monty@mysql.com's avatar
monty@mysql.com committed
1607
        send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1608 1609
      break;
    }
1610
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1611 1612
  case COM_BINLOG_DUMP:
    {
monty@mysql.com's avatar
monty@mysql.com committed
1613 1614 1615 1616
      ulong pos;
      ushort flags;
      uint32 slave_server_id;

1617
      statistic_increment(com_other,&LOCK_status);
1618
      thd->slow_command = TRUE;
1619
      if (check_global_access(thd, REPL_SLAVE_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1620
	break;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
1621

1622
      /* TODO: The following has to be changed to an 8 byte integer */
1623 1624
      pos = uint4korr(packet);
      flags = uint2korr(packet + 4);
1625
      thd->server_id=0; /* avoid suicide */
1626
      if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
1627
	kill_zombie_dump_threads(slave_server_id);
1628
      thd->server_id = slave_server_id;
monty@mysql.com's avatar
monty@mysql.com committed
1629 1630 1631

      mysql_log.write(thd, command, "Log: '%s'  Pos: %ld", packet+10,
                      (long) pos);
1632
      mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
1633
      unregister_slave(thd,1,1);
1634 1635 1636
      // 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
1637 1638
      break;
    }
1639
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1640 1641
  case COM_REFRESH:
    {
1642
      statistic_increment(com_stat[SQLCOM_FLUSH],&LOCK_status);
1643
      ulong options= (ulong) (uchar) packet[0];
1644
      if (check_global_access(thd,RELOAD_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1645
	break;
1646
      mysql_log.write(thd,command,NullS);
1647 1648 1649 1650
      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
1651 1652
      break;
    }
1653
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1654
  case COM_SHUTDOWN:
1655
  {
1656
    statistic_increment(com_other,&LOCK_status);
1657
    if (check_global_access(thd,SHUTDOWN_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1658
      break; /* purecov: inspected */
1659
    /*
1660 1661 1662 1663
      If the client is < 4.1.3, it is going to send us no argument; then
      packet_length is 1, packet[0] is the end 0 of the packet. Note that
      SHUTDOWN_DEFAULT is 0. If client is >= 4.1.3, the shutdown level is in
      packet[0].
1664
    */
1665 1666
    enum mysql_enum_shutdown_level level=
      (enum mysql_enum_shutdown_level) (uchar) packet[0];
1667
    DBUG_PRINT("quit",("Got shutdown command for level %u", level));
1668 1669 1670 1671 1672 1673 1674 1675
    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;
    }
1676
    DBUG_PRINT("quit",("Got shutdown command for level %u", level));
1677
    mysql_log.write(thd,command,NullS);
1678
    send_eof(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1679 1680 1681
#ifdef __WIN__
    sleep(1);					// must wait after eof()
#endif
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1682
#ifndef OS2
1683
    send_eof(thd);				// This is for 'quit request'
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1684
#endif
1685
    close_connection(thd, 0, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1686
    close_thread_tables(thd);			// Free before kill
1687
    free_root(thd->mem_root,MYF(0));
1688
    free_root(&thd->transaction.mem_root,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1689 1690 1691
    kill_mysql();
    error=TRUE;
    break;
1692
  }
1693
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1694 1695
  case COM_STATISTICS:
  {
1696
    mysql_log.write(thd,command,NullS);
1697
    statistic_increment(com_stat[SQLCOM_SHOW_STATUS],&LOCK_status);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1698
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1699
    char buff[200];
hf@deer.(none)'s avatar
hf@deer.(none) committed
1700 1701 1702
#else
    char *buff= thd->net.last_error;
#endif
1703
    ulong uptime = (ulong) (thd->start_time - start_time);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1704
    sprintf((char*) buff,
1705
	    "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
1706 1707 1708 1709 1710
	    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
1711
    if (sf_malloc_cur_memory)				// Using SAFEMALLOC
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1712
      sprintf(strend(buff), "  Memory in use: %ldK  Max memory used: %ldK",
1713 1714
	      (sf_malloc_cur_memory+1023L)/1024L,
	      (sf_malloc_max_memory+1023L)/1024L);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1715 1716
#endif
#ifndef EMBEDDED_LIBRARY
1717
    VOID(my_net_write(net, buff,(uint) strlen(buff)));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1718
    VOID(net_flush(net));
hf@deer.(none)'s avatar
hf@deer.(none) committed
1719
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1720 1721 1722
    break;
  }
  case COM_PING:
1723
    statistic_increment(com_other,&LOCK_status);
1724
    send_ok(thd);				// Tell client we are alive
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1725 1726
    break;
  case COM_PROCESS_INFO:
1727
    statistic_increment(com_stat[SQLCOM_SHOW_PROCESSLIST],&LOCK_status);
1728
    if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1729
      break;
1730
    mysql_log.write(thd,command,NullS);
hf@deer.(none)'s avatar
hf@deer.(none) committed
1731 1732
    mysqld_list_processes(thd,
			  thd->master_access & PROCESS_ACL ? 
monty@mysql.com's avatar
monty@mysql.com committed
1733
			  NullS : thd->priv_user, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1734 1735 1736
    break;
  case COM_PROCESS_KILL:
  {
1737
    statistic_increment(com_stat[SQLCOM_KILL],&LOCK_status);
1738
    ulong id=(ulong) uint4korr(packet);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1739 1740 1741
    kill_one_thread(thd,id);
    break;
  }
1742 1743 1744 1745 1746 1747 1748
  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;
1749
      send_eof(thd);
1750 1751 1752
      break;
    case MYSQL_OPTION_MULTI_STATEMENTS_OFF:
      thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS;
1753
      send_eof(thd);
1754 1755 1756 1757 1758 1759 1760
      break;
    default:
      send_error(thd, ER_UNKNOWN_COM_ERROR);
      break;
    }
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1761
  case COM_DEBUG:
1762
    statistic_increment(com_other,&LOCK_status);
1763
    if (check_global_access(thd, SUPER_ACL))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1764 1765
      break;					/* purecov: inspected */
    mysql_print_status(thd);
1766
    mysql_log.write(thd,command,NullS);
1767
    send_eof(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1768 1769 1770 1771 1772
    break;
  case COM_SLEEP:
  case COM_CONNECT:				// Impossible here
  case COM_TIME:				// Impossible from client
  case COM_DELAYED_INSERT:
1773
  case COM_END:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1774
  default:
1775
    send_error(thd, ER_UNKNOWN_COM_ERROR);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1776 1777
    break;
  }
1778
  if (thd->lock || thd->open_tables || thd->derived_tables)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1779 1780 1781 1782 1783
  {
    thd->proc_info="closing tables";
    close_thread_tables(thd);			/* Free tables */
  }

1784
  if (thd->is_fatal_error)
1785
    send_error(thd,0);				// End of memory ?
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1786 1787

  time_t start_of_query=thd->start_time;
1788
  thd->end_time();				// Set start time
1789

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

1795 1796
    if ((ulong) (thd->start_time - thd->time_after_lock) >
	thd->variables.long_query_time ||
1797 1798
	((thd->server_status &
	  (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
1799
	 (specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES)))
1800 1801 1802 1803
    {
      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
1804
  }
1805
  thd->proc_info="cleaning up";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1806 1807 1808 1809
  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
1810
  thd->query_length=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1811 1812
  thread_running--;
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
1813
  thd->packet.shrink(thd->variables.net_buffer_length);	// Reclaim some memory
1814
  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1815 1816 1817
  DBUG_RETURN(error);
}

1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835

/*
  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
1836
  /* Remove garbage at start and end of query */
1837
  while (my_isspace(thd->charset(),packet[0]) && packet_length > 0)
1838 1839 1840 1841 1842
  {
    packet++;
    packet_length--;
  }
  char *pos=packet+packet_length;		// Point at end null
peter@mysql.com's avatar
peter@mysql.com committed
1843
  while (packet_length > 0 &&
1844
	 (pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1])))
1845 1846 1847 1848 1849
  {
    pos--;
    packet_length--;
  }
  /* We must allocate some extra memory for query cache */
monty@mysql.com's avatar
monty@mysql.com committed
1850
  thd->query_length= 0;                        // Extra safety: Avoid races
1851 1852
  if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet),
					      packet_length,
1853 1854
					      thd->db_length+ 1 +
					      QUERY_CACHE_FLAGS_SIZE)))
1855 1856 1857
    return 1;
  thd->query[packet_length]=0;
  thd->query_length= packet_length;
1858 1859 1860 1861

  /* Reclaim some memory */
  thd->packet.shrink(thd->variables.net_buffer_length);
  thd->convert_buffer.shrink(thd->variables.net_buffer_length);
1862 1863 1864 1865 1866 1867

  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
1868 1869 1870 1871 1872 1873
/****************************************************************************
** mysql_execute_command
** Execute command saved in thd and current_lex->sql_command
****************************************************************************/

void
1874
mysql_execute_command(THD *thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1875
{
1876
  int	res= 0;
1877
  LEX	*lex= thd->lex;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1878
  SELECT_LEX *select_lex= &lex->select_lex;
1879
  TABLE_LIST *tables= (TABLE_LIST*) select_lex->table_list.first;
1880
  SELECT_LEX_UNIT *unit= &lex->unit;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1881 1882
  DBUG_ENTER("mysql_execute_command");

1883 1884 1885 1886 1887 1888
  /*
    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.
  */
1889
  if (tables || &lex->select_lex != lex->all_selects_list)
1890 1891
    mysql_reset_errors(thd);

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1892
#ifdef HAVE_REPLICATION
1893 1894
  if (thd->slave_thread)
  {
peter@mysql.com's avatar
peter@mysql.com committed
1895
    /*
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
1896 1897 1898
      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
    */
1899
    if (all_tables_not_ok(thd,tables))
guilhem@mysql.com's avatar
guilhem@mysql.com committed
1900 1901 1902
    {
      /* we warn the slave SQL thread */
      my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
1903
      DBUG_VOID_RETURN;
guilhem@mysql.com's avatar
guilhem@mysql.com committed
1904
    }
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
1905 1906
#ifndef TO_BE_DELETED
    /*
1907 1908 1909
      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
1910
    */
1911 1912 1913
    if (lex->sql_command == SQLCOM_SELECT)
    {
      lex->sql_command = SQLCOM_DO;
1914
      lex->insert_list = &select_lex->item_list;
1915
    }
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
1916
#endif
1917
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
1918
#endif /* !HAVE_REPLICATION */
1919 1920
  if ((&lex->select_lex != lex->all_selects_list ||
       lex->time_zone_tables_used) &&
1921
      lex->unit.create_total_list(thd, lex, &tables))
1922
    DBUG_VOID_RETURN;
1923

1924 1925 1926 1927 1928
  /*
    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
1929
      !(thd->slave_thread || (thd->master_access & SUPER_ACL)) &&
1930 1931
      (uc_update_queries[lex->sql_command] > 0))
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
1932
    net_printf(thd, ER_OPTION_PREVENTS_STATEMENT, "--read-only");
1933 1934
    DBUG_VOID_RETURN;
  }
1935

1936
  statistic_increment(com_stat[lex->sql_command],&LOCK_status);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1937 1938 1939
  switch (lex->sql_command) {
  case SQLCOM_SELECT:
  {
1940 1941 1942 1943 1944 1945 1946
    /* 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;
    }

1947
    select_result *result=lex->result;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1948 1949 1950 1951 1952
    if (tables)
    {
      res=check_table_access(thd,
			     lex->exchange ? SELECT_ACL | FILE_ACL :
			     SELECT_ACL,
hf@deer.(none)'s avatar
hf@deer.(none) committed
1953
			     tables,0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1954 1955 1956
    }
    else
      res=check_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
hf@deer.(none)'s avatar
hf@deer.(none) committed
1957
		       any_db,0,0,0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1958 1959 1960 1961 1962
    if (res)
    {
      res=0;
      break;					// Error message is given
    }
1963 1964 1965 1966
    /* 
       In case of single SELECT unit->global_parameters points on first SELECT
       TODO: move counters to SELECT_LEX
    */
1967 1968 1969
    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
1970
    if (unit->select_limit_cnt <
1971
	(ha_rows) unit->global_parameters->select_limit)
1972
      unit->select_limit_cnt= HA_POS_ERROR;		// no limit
1973
    if (unit->select_limit_cnt == HA_POS_ERROR && !select_lex->next_select())
1974
      select_lex->options&= ~OPTION_FOUND_ROWS;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1975 1976

    if (!(res=open_and_lock_tables(thd,tables)))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1977
    {
1978
      if (lex->describe)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1979
      {
1980 1981 1982 1983 1984 1985 1986
	if (!(result= new select_send()))
	{
	  send_error(thd, ER_OUT_OF_RESOURCES);
	  DBUG_VOID_RETURN;
	}
	else
	  thd->send_explain_fields(result);
1987
	res= mysql_explain_union(thd, &thd->lex->unit, result);
1988 1989 1990 1991 1992
	if (lex->describe & DESCRIBE_EXTENDED)
	{
	  char buff[1024];
	  String str(buff,(uint32) sizeof(buff), system_charset_info);
	  str.length(0);
1993
	  thd->lex->unit.print(&str);
1994 1995 1996 1997
	  str.append('\0');
	  push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
		       ER_YES, str.ptr());
	}
1998
	result->send_eof();
1999
        delete result;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2000 2001 2002
      }
      else
      {
2003
	if (!result && !(result= new select_send()))
2004
	{
2005 2006
	  res= -1;
	  break;
2007
	}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2008
	query_cache_store_query(thd, tables);
2009 2010 2011
	res= handle_select(thd, lex, result);
        if (result != lex->result)
          delete result;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2012
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2013
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2014 2015
    break;
  }
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2016
  case SQLCOM_PREPARE:
2017
  {
2018 2019 2020 2021
    char *query_str;
    uint query_len;
    if (lex->prepared_stmt_code_is_varref)
    {
2022
      /* This is PREPARE stmt FROM @var. */
2023 2024 2025 2026
      String str;
      CHARSET_INFO *to_cs= thd->variables.collation_connection;
      bool need_conversion;
      user_var_entry *entry;
2027
      String *pstr= &str;
2028
      uint32 unused;
2029
      /*
2030 2031 2032
        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.
2033
      */
2034 2035
      if ((entry=
             (user_var_entry*)hash_search(&thd->user_vars,
2036 2037 2038 2039
                                          (byte*)lex->prepared_stmt_code.str,
                                          lex->prepared_stmt_code.length))
          && entry->value)
      {
2040 2041
        my_bool is_var_null;
        pstr= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC);
2042 2043 2044 2045
        /*
          NULL value of variable checked early as entry->value so here
          we can't get NULL in normal conditions
        */
2046 2047
        DBUG_ASSERT(!is_var_null);
        if (!pstr)
2048 2049 2050 2051
        {
          res= -1;
          break;      // EOM (error should be reported by allocator)
        }
2052 2053
      }
      else
2054 2055 2056 2057 2058
      {
        /*
          variable absent or equal to NULL, so we need to set variable to
          something reasonable to get readable error message during parsing
        */
2059
        str.set("NULL", 4, &my_charset_latin1);
2060 2061
      }

2062
      need_conversion=
2063 2064
        String::needs_conversion(pstr->length(), pstr->charset(),
                                 to_cs, &unused);
2065

2066 2067
      query_len= need_conversion? (pstr->length() * to_cs->mbmaxlen) :
                                  pstr->length();
2068
      if (!(query_str= alloc_root(thd->mem_root, query_len+1)))
2069 2070 2071 2072
      {
        res= -1;
        break;        // EOM (error should be reported by allocator)
      }
2073

2074
      if (need_conversion)
2075 2076 2077 2078 2079 2080
      {
        uint dummy_errors;
        query_len= copy_and_convert(query_str, query_len, to_cs,
                                    pstr->ptr(), pstr->length(),
                                    pstr->charset(), &dummy_errors);
      }
2081
      else
2082
        memcpy(query_str, pstr->ptr(), pstr->length());
2083
      query_str[query_len]= 0;
2084 2085 2086
    }
    else
    {
2087 2088
      query_str= lex->prepared_stmt_code.str;
      query_len= lex->prepared_stmt_code.length;
2089
      DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n",
2090 2091
                          lex->prepared_stmt_name.length,
                          lex->prepared_stmt_name.str,
2092
                          query_len, query_str));
2093
    }
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2094
    thd->command= COM_PREPARE;
2095 2096
    if (!mysql_stmt_prepare(thd, query_str, query_len + 1,
                            &lex->prepared_stmt_name))
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2097 2098 2099 2100 2101
      send_ok(thd, 0L, 0L, "Statement prepared");
    break;
  }
  case SQLCOM_EXECUTE:
  {
2102
    DBUG_PRINT("info", ("EXECUTE: %.*s\n",
2103 2104 2105
                        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
2106 2107 2108 2109 2110
    lex->prepared_stmt_params.empty();
    break;
  }
  case SQLCOM_DEALLOCATE_PREPARE:
  {
2111 2112 2113 2114 2115
    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
2116
    {
2117 2118
      thd->stmt_map.erase(stmt);
      send_ok(thd);
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2119
    }
2120
    else
2121 2122 2123 2124 2125 2126
    {
      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
2127 2128
    break;
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2129
  case SQLCOM_DO:
hf@deer.(none)'s avatar
hf@deer.(none) committed
2130
    if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
2131 2132 2133 2134 2135 2136
		   (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
2137 2138
    break;

2139
  case SQLCOM_EMPTY_QUERY:
2140
    send_ok(thd);
2141 2142
    break;

2143 2144 2145 2146
  case SQLCOM_HELP:
    res= mysqld_help(thd,lex->help_arg);
    break;

2147
#ifndef EMBEDDED_LIBRARY
2148
  case SQLCOM_PURGE:
2149
  {
2150
    if (check_global_access(thd, SUPER_ACL))
2151
      goto error;
2152
    // PURGE MASTER LOGS TO 'file'
2153 2154 2155
    res = purge_master_logs(thd, lex->to_log);
    break;
  }
2156 2157 2158 2159 2160 2161 2162 2163
  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;
  }
2164
#endif
2165 2166
  case SQLCOM_SHOW_WARNS:
  {
2167 2168
    res= mysqld_show_warnings(thd, (ulong)
			      ((1L << (uint) MYSQL_ERROR::WARN_LEVEL_NOTE) |
2169 2170 2171
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_WARN) |
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)
			       ));
2172 2173 2174 2175
    break;
  }
  case SQLCOM_SHOW_ERRORS:
  {
2176 2177
    res= mysqld_show_warnings(thd, (ulong)
			      (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
2178 2179
    break;
  }
2180 2181
  case SQLCOM_SHOW_NEW_MASTER:
  {
2182
    if (check_global_access(thd, REPL_SLAVE_ACL))
2183
      goto error;
2184
    /* This query don't work now. See comment in repl_failsafe.cc */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2185
#ifndef WORKING_NEW_MASTER
2186
    net_printf(thd, ER_NOT_SUPPORTED_YET, "SHOW NEW MASTER");
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2187 2188
    res= 1;
#else
2189
    res = show_new_master(thd);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2190
#endif
2191 2192
    break;
  }
2193

2194
#ifdef HAVE_REPLICATION
2195 2196
  case SQLCOM_SHOW_SLAVE_HOSTS:
  {
2197
    if (check_global_access(thd, REPL_SLAVE_ACL))
2198 2199 2200 2201
      goto error;
    res = show_slave_hosts(thd);
    break;
  }
2202 2203
  case SQLCOM_SHOW_BINLOG_EVENTS:
  {
2204
    if (check_global_access(thd, REPL_SLAVE_ACL))
2205 2206 2207 2208
      goto error;
    res = show_binlog_events(thd);
    break;
  }
2209 2210
#endif

2211
  case SQLCOM_BACKUP_TABLE:
2212 2213
  {
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2214
	check_table_access(thd,SELECT_ACL, tables,0) ||
2215
	check_global_access(thd, FILE_ACL))
2216
      goto error; /* purecov: inspected */
2217
    thd->slow_command=TRUE;
2218
    res = mysql_backup_table(thd, tables);
2219

2220 2221
    break;
  }
2222
  case SQLCOM_RESTORE_TABLE:
2223 2224
  {
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2225
	check_table_access(thd, INSERT_ACL, tables,0) ||
2226
	check_global_access(thd, FILE_ACL))
2227
      goto error; /* purecov: inspected */
2228
    thd->slow_command=TRUE;
2229 2230 2231
    res = mysql_restore_table(thd, tables);
    break;
  }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2232 2233 2234
  case SQLCOM_ASSIGN_TO_KEYCACHE:
  {
    if (check_db_used(thd, tables) ||
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2235 2236
        check_access(thd, INDEX_ACL, tables->db,
                     &tables->grant.privilege, 0, 0))
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2237
      goto error;
2238
    res= mysql_assign_to_keycache(thd, tables, &lex->name_and_length);
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2239 2240
    break;
  }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2241 2242 2243
  case SQLCOM_PRELOAD_KEYS:
  {
    if (check_db_used(thd, tables) ||
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2244 2245
	check_access(thd, INDEX_ACL, tables->db,
                     &tables->grant.privilege, 0, 0))
2246
      goto error;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2247 2248 2249
    res = mysql_preload_keys(thd, tables);
    break;
  }
2250
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2251
  case SQLCOM_CHANGE_MASTER:
2252
  {
2253
    if (check_global_access(thd, SUPER_ACL))
2254
      goto error;
2255
    pthread_mutex_lock(&LOCK_active_mi);
2256
    res = change_master(thd,active_mi);
2257
    pthread_mutex_unlock(&LOCK_active_mi);
2258 2259
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2260
  case SQLCOM_SHOW_SLAVE_STAT:
2261
  {
2262 2263
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2264
      goto error;
2265
    pthread_mutex_lock(&LOCK_active_mi);
2266
    res = show_master_info(thd,active_mi);
2267
    pthread_mutex_unlock(&LOCK_active_mi);
2268 2269
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2270
  case SQLCOM_SHOW_MASTER_STAT:
2271
  {
2272 2273
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2274 2275 2276 2277
      goto error;
    res = show_binlog_info(thd);
    break;
  }
peter@mysql.com's avatar
peter@mysql.com committed
2278

2279
  case SQLCOM_LOAD_MASTER_DATA: // sync with master
2280
    if (check_global_access(thd, SUPER_ACL))
2281
      goto error;
2282 2283 2284 2285
    if (end_active_trans(thd))
      res= -1;
    else
      res = load_master_data(thd);
2286
    break;
2287
#endif /* HAVE_REPLICATION */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2288 2289 2290
#ifdef HAVE_INNOBASE_DB
  case SQLCOM_SHOW_INNODB_STATUS:
    {
2291
      if (check_global_access(thd, SUPER_ACL))
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
2292 2293 2294 2295 2296
	goto error;
      res = innodb_show_status(thd);
      break;
    }
#endif
2297
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2298
  case SQLCOM_LOAD_MASTER_TABLE:
2299
  {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2300 2301
    if (!tables->db)
      tables->db=thd->db;
hf@deer.(none)'s avatar
hf@deer.(none) committed
2302
    if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege,0,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2303 2304 2305 2306
      goto error;				/* purecov: inspected */
    if (grant_option)
    {
      /* Check that the first table has CREATE privilege */
2307
      if (check_grant(thd, CREATE_ACL, tables, 0, 1, 0))
2308
	goto error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2309
    }
2310
    if (strlen(tables->real_name) > NAME_LEN)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2311
    {
2312
      net_printf(thd,ER_WRONG_TABLE_NAME, tables->real_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2313 2314
      break;
    }
2315
    pthread_mutex_lock(&LOCK_active_mi);
2316 2317 2318 2319
    /*
      fetch_master_table will send the error to the client on failure.
      Give error if the table already exists.
    */
2320
    if (!fetch_master_table(thd, tables->db, tables->real_name,
2321
			    active_mi, 0, 0))
2322
    {
2323
      send_ok(thd);
2324
    }
2325
    pthread_mutex_unlock(&LOCK_active_mi);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2326
    break;
2327
  }
2328
#endif /* HAVE_REPLICATION */
2329

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2330
  case SQLCOM_CREATE_TABLE:
2331
  {
monty@mysql.com's avatar
monty@mysql.com committed
2332
    /* Skip first table, which is the table we are creating */
2333 2334 2335
    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
2336

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

2340 2341 2342
#ifndef HAVE_READLINK
    lex->create_info.data_file_name=lex->create_info.index_file_name=0;
#else
2343
    /* Fix names if symlinked tables */
2344
    if (append_file_to_dir(thd, &lex->create_info.data_file_name,
monty@mysql.com's avatar
monty@mysql.com committed
2345
			   create_table->real_name) ||
2346
	append_file_to_dir(thd,&lex->create_info.index_file_name,
monty@mysql.com's avatar
monty@mysql.com committed
2347
			   create_table->real_name))
2348 2349
    {
      res=-1;
2350
      goto unsent_create_error;
2351
    }
2352
#endif
2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365
    /*
      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;
    }
2366
    if (select_lex->item_list.elements)		// With select
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2367 2368 2369
    {
      select_result *result;

2370
      select_lex->options|= SELECT_NO_UNLOCK;
2371 2372 2373 2374
      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
2375
	unit->select_limit_cnt= HA_POS_ERROR;	// No limit
2376

monty@mysql.com's avatar
monty@mysql.com committed
2377
      if (!(res=open_and_lock_tables(thd,tables)))
2378
      {
monty@mysql.com's avatar
monty@mysql.com committed
2379 2380 2381 2382
	res= -1;				// If error
        if ((result=new select_create(create_table->db,
                                      create_table->real_name,
				      &lex->create_info,
2383 2384 2385
                                      lex->create_list,
                                      lex->key_list,
                                      select_lex->item_list,lex->duplicates)))
2386 2387 2388 2389 2390 2391
        {
          /*
            CREATE from SELECT give its SELECT_LEX for SELECT,
            and item_list belong to SELECT
          */
          select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
2392
          res=handle_select(thd, lex, result);
2393 2394
          select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE;
        }
2395 2396 2397
	//reset for PS
	lex->create_list.empty();
	lex->key_list.empty();
2398 2399
      }
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2400 2401
    else // regular create
    {
venu@myvenu.com's avatar
venu@myvenu.com committed
2402
      if (lex->name)
monty@mysql.com's avatar
monty@mysql.com committed
2403
        res= mysql_create_like_table(thd, create_table, &lex->create_info, 
venu@myvenu.com's avatar
venu@myvenu.com committed
2404 2405
                                     (Table_ident *)lex->name); 
      else
2406
      {
monty@mysql.com's avatar
monty@mysql.com committed
2407 2408
        res= mysql_create_table(thd,create_table->db,
			         create_table->real_name, &lex->create_info,
venu@myvenu.com's avatar
venu@myvenu.com committed
2409
			         lex->create_list,
2410
			         lex->key_list,0,0);
2411
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2412
      if (!res)
2413
	send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2414
    }
2415

2416
    // put tables back for PS rexecuting
2417 2418
    tables= lex->link_first_table_back(tables, create_table,
				       create_table_local);
2419 2420
    break;

2421
create_error:
2422 2423 2424
    res= 1; //error reported
unsent_create_error:
    // put tables back for PS rexecuting
2425 2426
    tables= lex->link_first_table_back(tables, create_table,
				       create_table_local);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2427
    break;
2428
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2429
  case SQLCOM_CREATE_INDEX:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2430
    if (check_one_table_access(thd, INDEX_ACL, tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2431
      goto error; /* purecov: inspected */
2432
    thd->slow_command=TRUE;
2433 2434 2435 2436
    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
2437 2438
    break;

2439
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2440
  case SQLCOM_SLAVE_START:
2441
  {
2442
    pthread_mutex_lock(&LOCK_active_mi);
2443
    start_slave(thd,active_mi,1 /* net report*/);
2444
    pthread_mutex_unlock(&LOCK_active_mi);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2445
    break;
2446
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2447
  case SQLCOM_SLAVE_STOP:
2448 2449 2450 2451 2452 2453
  /*
    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,
2454
      so it waits for the client thread because t is locked by it.
2455
    - then the client thread does SLAVE STOP.
2456 2457
      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.
2458 2459 2460 2461 2462
    To prevent that, refuse SLAVE STOP if the
    client thread has locked tables
  */
  if (thd->locked_tables || thd->active_transaction())
  {
2463
    send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2464
    break;
2465
  }
2466
  {
2467
    pthread_mutex_lock(&LOCK_active_mi);
2468
    stop_slave(thd,active_mi,1/* net report*/);
2469
    pthread_mutex_unlock(&LOCK_active_mi);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2470
    break;
2471
  }
2472
#endif /* HAVE_REPLICATION */
2473

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2474 2475
  case SQLCOM_ALTER_TABLE:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
2476
    send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2477 2478 2479
    break;
#else
    {
2480
      ulong priv=0;
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
2481
      if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2482
      {
2483
	net_printf(thd, ER_WRONG_TABLE_NAME, lex->name);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2484
	res= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2485 2486
	break;
      }
2487 2488
      if (!select_lex->db)
	select_lex->db=tables->db;
hf@deer.(none)'s avatar
hf@deer.(none) committed
2489 2490
      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
2491
	  check_merge_table_access(thd, tables->db,
2492 2493 2494
				   (TABLE_LIST *)
				   lex->create_info.merge_list.first))
	goto error;				/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2495 2496
      if (grant_option)
      {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2497
	if (check_grant(thd, ALTER_ACL, tables, 0, UINT_MAX, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2498 2499 2500 2501 2502 2503
	  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;
2504
	  tmp_table.db=select_lex->db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2505
	  tmp_table.grant.privilege=priv;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2506 2507
	  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
2508 2509 2510
	    goto error;
	}
      }
2511 2512
      /* 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
2513
      /* ALTER TABLE ends previous transaction */
2514
      if (end_active_trans(thd))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2515 2516
	res= -1;
      else
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2517
      {
2518
        thd->slow_command=TRUE;
2519
	res= mysql_alter_table(thd, select_lex->db, lex->name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2520 2521
			       &lex->create_info,
			       tables, lex->create_list,
2522
			       lex->key_list,
2523
			       select_lex->order_list.elements,
2524
                               (ORDER *) select_lex->order_list.first,
2525
			       lex->duplicates, &lex->alter_info);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2526
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2527 2528
      break;
    }
hf@deer.(none)'s avatar
hf@deer.(none) committed
2529
#endif /*DONT_ALLOW_SHOW_COMMANDS*/
2530
  case SQLCOM_RENAME_TABLE:
2531 2532 2533
  {
    TABLE_LIST *table;
    if (check_db_used(thd,tables))
2534
      goto error;
2535 2536
    for (table=tables ; table ; table=table->next->next)
    {
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2537
      if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
hf@deer.(none)'s avatar
hf@deer.(none) committed
2538
		       &table->grant.privilege,0,0) ||
2539
	  check_access(thd, INSERT_ACL | CREATE_ACL, table->next->db,
hf@deer.(none)'s avatar
hf@deer.(none) committed
2540
		       &table->next->grant.privilege,0,0))
2541 2542 2543
	goto error;
      if (grant_option)
      {
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2544
	TABLE_LIST old_list,new_list;
2545 2546 2547 2548
	/*
	  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
2549 2550 2551
	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
2552
	if (check_grant(thd, ALTER_ACL, &old_list, 0, UINT_MAX, 0) ||
2553
	    (!test_all_bits(table->next->grant.privilege,
2554
			    INSERT_ACL | CREATE_ACL) &&
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2555 2556
	     check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0,
			 UINT_MAX, 0)))
2557 2558 2559
	  goto error;
      }
    }
2560
    query_cache_invalidate3(thd, tables, 0);
2561 2562 2563
    if (end_active_trans(thd))
      res= -1;
    else if (mysql_rename_tables(thd,tables))
2564 2565
      res= -1;
    break;
2566
  }
2567
#ifndef EMBEDDED_LIBRARY
2568 2569
  case SQLCOM_SHOW_BINLOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
2570
    send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
2571 2572 2573
    DBUG_VOID_RETURN;
#else
    {
2574
      if (check_global_access(thd, SUPER_ACL))
2575 2576 2577 2578
	goto error;
      res = show_binlogs(thd);
      break;
    }
peter@mysql.com's avatar
peter@mysql.com committed
2579
#endif
2580
#endif /* EMBEDDED_LIBRARY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2581
  case SQLCOM_SHOW_CREATE:
2582
#ifdef DONT_ALLOW_SHOW_COMMANDS
2583
    send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
2584 2585
    DBUG_VOID_RETURN;
#else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2586
    {
2587 2588
      if (check_db_used(thd, tables) ||
	  check_access(thd, SELECT_ACL | EXTRA_ACL, tables->db,
hf@deer.(none)'s avatar
hf@deer.(none) committed
2589
		       &tables->grant.privilege,0,0))
2590
	goto error;
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
2591
      res = mysqld_show_create(thd, tables);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2592 2593
      break;
    }
2594
#endif
2595 2596 2597
  case SQLCOM_CHECKSUM:
  {
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2598
	check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0))
2599
      goto error; /* purecov: inspected */
2600
    res = mysql_checksum_table(thd, tables, &lex->check_opt);
2601 2602
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2603
  case SQLCOM_REPAIR:
2604 2605
  {
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2606
	check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
2607
      goto error; /* purecov: inspected */
2608
    thd->slow_command=TRUE;
2609
    res = mysql_repair_table(thd, tables, &lex->check_opt);
2610 2611 2612 2613 2614 2615
    /* ! 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())
      {
2616
	thd->clear_error(); // No binlog error generated
2617
        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
2618 2619 2620
        mysql_bin_log.write(&qinfo);
      }
    }
2621 2622
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2623
  case SQLCOM_CHECK:
2624 2625
  {
    if (check_db_used(thd,tables) ||
2626
	check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0))
2627
      goto error; /* purecov: inspected */
2628
    thd->slow_command=TRUE;
2629 2630 2631
    res = mysql_check_table(thd, tables, &lex->check_opt);
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2632 2633
  case SQLCOM_ANALYZE:
  {
2634
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2635
	check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2636
      goto error; /* purecov: inspected */
2637
    thd->slow_command=TRUE;
2638
    res = mysql_analyze_table(thd, tables, &lex->check_opt);
2639 2640 2641 2642 2643 2644
    /* ! 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())
      {
2645
	thd->clear_error(); // No binlog error generated
2646
        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
2647 2648 2649
        mysql_bin_log.write(&qinfo);
      }
    }
2650
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2651
  }
2652

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2653 2654
  case SQLCOM_OPTIMIZE:
  {
2655
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2656
	check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2657
      goto error; /* purecov: inspected */
2658
    thd->slow_command=TRUE;
2659 2660 2661
    res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
      mysql_recreate_table(thd, tables, 1) :
      mysql_optimize_table(thd, tables, &lex->check_opt);
2662 2663 2664 2665 2666 2667
    /* ! 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())
      {
2668
	thd->clear_error(); // No binlog error generated
2669
        Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
2670 2671 2672
        mysql_bin_log.write(&qinfo);
      }
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2673 2674 2675
    break;
  }
  case SQLCOM_UPDATE:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2676 2677
    if (update_precheck(thd, tables))
      break;
2678 2679 2680 2681
    res= mysql_update(thd,tables,
                      select_lex->item_list,
                      lex->value_list,
                      select_lex->where,
2682
		      select_lex->order_list.elements,
2683 2684 2685
                      (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
2686 2687
    if (thd->net.report_error)
      res= -1;
2688 2689
    break;
  case SQLCOM_UPDATE_MULTI:
monty@mysql.com's avatar
monty@mysql.com committed
2690
  {
2691
    if ((res= multi_update_precheck(thd, tables)))
monty@mysql.com's avatar
monty@mysql.com committed
2692 2693 2694 2695 2696 2697 2698
      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
2699
    break;
monty@mysql.com's avatar
monty@mysql.com committed
2700
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2701
  case SQLCOM_REPLACE:
2702 2703
  case SQLCOM_INSERT:
  {
2704
    if ((res= insert_precheck(thd, tables)))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2705
      break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2706
    res = mysql_insert(thd,tables,lex->field_list,lex->many_values,
2707
                       select_lex->item_list, lex->value_list,
2708
                       lex->duplicates);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2709 2710
    if (thd->net.report_error)
      res= -1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2711
    break;
2712
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2713 2714 2715
  case SQLCOM_REPLACE_SELECT:
  case SQLCOM_INSERT_SELECT:
  {
2716 2717 2718
    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
2719

2720 2721
    /* Fix lock for first table */
    if (tables->lock_type == TL_WRITE_DELAYED)
2722
      tables->lock_type= TL_WRITE;
2723

2724 2725
    /* 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
2726 2727

    select_result *result;
2728 2729 2730 2731
    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
2732

2733
    if (find_real_table_in_list(tables->next, tables->db, tables->real_name))
2734
    {
2735 2736
      /* Using same table for INSERT and SELECT */
      select_lex->options |= OPTION_BUFFER_RESULT;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2737
    }
2738

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

2740 2741 2742
    if (!(res= open_and_lock_tables(thd, tables)) &&
        (result= new select_insert(tables->table, &lex->field_list,
                                   lex->duplicates)))
2743
    {
2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755
      /* Skip first table, which is the table we are inserting in */
      lex->select_lex.table_list.first= (byte*) first_local_table->next;
      /*
        insert/replace from SELECT give its SELECT_LEX for SELECT,
        and item_list belong to SELECT
      */
      lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
      res= handle_select(thd, lex, result);
      /* revert changes for SP */
      lex->select_lex.table_list.first= (byte*) first_local_table;
      lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
      delete result;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2756
      if (thd->net.report_error)
2757
        res= -1;
2758 2759 2760
    }
    else
      res= -1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2761 2762
    break;
  }
2763
  case SQLCOM_TRUNCATE:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2764
    if (check_one_table_access(thd, DELETE_ACL, tables))
2765
      goto error;
2766 2767 2768 2769 2770 2771
    /*
      Don't allow this within a transaction because we want to use
      re-generate table
    */
    if (thd->locked_tables || thd->active_transaction())
    {
2772
      send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION,NullS);
2773 2774
      goto error;
    }
2775
    res=mysql_truncate(thd, tables, 0);
2776
    break;
2777
  case SQLCOM_DELETE:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2778
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2779 2780
    if ((res= delete_precheck(thd, tables)))
      break;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
2781
    res = mysql_delete(thd,tables, select_lex->where,
2782
                       &select_lex->order_list,
2783
                       select_lex->select_limit, select_lex->options);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2784 2785
    if (thd->net.report_error)
      res= -1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2786 2787
    break;
  }
2788
  case SQLCOM_DELETE_MULTI:
2789
  {
2790 2791
    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
2792 2793
    TABLE_LIST *target_tbl;
    uint table_count;
2794
    multi_delete *result;
2795

2796 2797
    if ((res= multi_delete_precheck(thd, tables, &table_count)))
      break;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2798

2799
    /* condition will be TRUE on SP re-excuting */
2800 2801
    if (select_lex->item_list.elements != 0)
      select_lex->item_list.empty();
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2802
    if (add_item_to_list(thd, new Item_null()))
2803
    {
2804
      res= -1;
2805
      break;
2806
    }
2807

2808 2809 2810 2811
    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
2812 2813 2814
    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
2815
    {
2816 2817 2818
      TABLE_LIST *orig= target_tbl->table_list;
      target_tbl->table= orig->table;
      /*
2819
	 Multi-delete can't be constructed over-union => we always have
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2820
	 single SELECT on top and have to check underlying SELECTs of it
2821
      */
2822 2823
      if (lex->select_lex.check_updateable_in_subqueries(orig->db,
                                                         orig->real_name))
2824
      {
2825 2826 2827 2828
        my_error(ER_UPDATE_TABLE_USED, MYF(0),
                 orig->real_name);
        res= -1;
        break;
2829
      }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2830
    }
2831

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3028 3029 3030
  case SQLCOM_LOAD:
  {
    uint privilege= (lex->duplicates == DUP_REPLACE ?
3031
		     INSERT_ACL | DELETE_ACL : INSERT_ACL);
3032 3033

    if (!lex->local_file)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3034
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
3035
      if (check_access(thd,privilege | FILE_ACL,tables->db,0,0,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3036 3037 3038 3039
	goto error;
    }
    else
    {
3040
      if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) ||
3041
	  ! opt_local_infile)
3042
      {
3043
	send_error(thd,ER_NOT_ALLOWED_COMMAND);
3044 3045
	goto error;
      }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3046
      if (check_one_table_access(thd, privilege, tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3047 3048 3049 3050 3051 3052
	goto error;
    }
    res=mysql_load(thd, lex->exchange, tables, lex->field_list,
		   lex->duplicates, (bool) lex->local_file, lex->lock_option);
    break;
  }
3053

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3054
  case SQLCOM_SET_OPTION:
3055 3056
  {
    List<set_var_base> *lex_var_list= &lex->var_list;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3057
    if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
3058 3059
		   (res= open_and_lock_tables(thd,tables))))
      break;
3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073
    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;
3074
      send_ok(thd);
3075
    }
3076 3077
    if (thd->net.report_error)
      res= -1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3078
    break;
3079
  }
3080

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3081
  case SQLCOM_UNLOCK_TABLES:
3082 3083 3084 3085 3086 3087
    /*
      It is critical for mysqldump --single-transaction --master-data that
      UNLOCK TABLES does not implicitely commit a connection which has only
      done FLUSH TABLES WITH READ LOCK + BEGIN. If this assumption becomes
      false, mysqldump will not work.
    */
3088
    unlock_locked_tables(thd);
3089 3090
    if (thd->options & OPTION_TABLE_LOCK)
    {
3091
      end_active_trans(thd);
3092
      thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3093 3094
    }
    if (thd->global_read_lock)
3095
      unlock_global_read_lock(thd);
3096
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3097 3098
    break;
  case SQLCOM_LOCK_TABLES:
3099
    unlock_locked_tables(thd);
3100
    if (check_db_used(thd,tables) || end_active_trans(thd))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3101
      goto error;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3102
    if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, tables,0))
3103
      goto error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3104
    thd->in_lock_tables=1;
3105
    thd->options|= OPTION_TABLE_LOCK;
3106
    if (!(res= open_and_lock_tables(thd, tables)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3107
    {
3108 3109 3110 3111
#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
3112 3113
      thd->locked_tables=thd->lock;
      thd->lock=0;
3114
      send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3115
    }
3116 3117
    else
      thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3118 3119 3120
    thd->in_lock_tables=0;
    break;
  case SQLCOM_CREATE_DB:
3121
  {
3122
    char *alias;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
3123
    if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3124
    {
3125
      net_printf(thd,ER_WRONG_DB_NAME, lex->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3126 3127
      break;
    }
3128 3129 3130 3131 3132 3133 3134
    /*
      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.
    */
3135
#ifdef HAVE_REPLICATION
3136 3137 3138
    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
3139 3140
    {
      my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
3141
      break;
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3142
    }
3143
#endif
hf@deer.(none)'s avatar
hf@deer.(none) committed
3144
    if (check_access(thd,CREATE_ACL,lex->name,0,1,0))
3145
      break;
monty@mysql.com's avatar
monty@mysql.com committed
3146
    res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name),
monty@mysql.com's avatar
monty@mysql.com committed
3147
			 &lex->create_info, 0);
3148 3149
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3150
  case SQLCOM_DROP_DB:
3151
  {
3152
    char *alias;
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
3153
    if (!(alias=thd->strdup(lex->name)) || check_db_name(lex->name))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3154
    {
3155
      net_printf(thd, ER_WRONG_DB_NAME, lex->name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3156 3157
      break;
    }
3158 3159 3160 3161 3162 3163 3164
    /*
      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.
    */
3165
#ifdef HAVE_REPLICATION
3166 3167 3168
    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
3169 3170
    {
      my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
3171
      break;
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3172
    }
3173
#endif
hf@deer.(none)'s avatar
hf@deer.(none) committed
3174
    if (check_access(thd,DROP_ACL,lex->name,0,1,0))
3175
      break;
3176 3177
    if (thd->locked_tables || thd->active_transaction())
    {
3178
      send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
3179 3180
      goto error;
    }
3181 3182
    res=mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : lex->name),
                    lex->drop_if_exists, 0);
3183 3184
    break;
  }
3185 3186
  case SQLCOM_ALTER_DB:
  {
3187 3188
    char *db= lex->name ? lex->name : thd->db;
    if (!db)
3189
    {
3190 3191 3192 3193 3194 3195
      send_error(thd, ER_NO_DB_ERROR);
      goto error;
    }
    if (!strip_sp(db) || check_db_name(db))
    {
      net_printf(thd, ER_WRONG_DB_NAME, db);
3196 3197
      break;
    }
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3198 3199 3200 3201 3202 3203 3204 3205 3206
    /*
      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 && 
3207 3208
	(!db_ok(db, replicate_do_db, replicate_ignore_db) ||
	 !db_ok_with_wild_table(db)))
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3209 3210 3211 3212 3213
    {
      my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
      break;
    }
#endif
3214
    if (check_access(thd, ALTER_ACL, db, 0, 1, 0))
3215 3216 3217
      break;
    if (thd->locked_tables || thd->active_transaction())
    {
3218
      send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
3219 3220
      goto error;
    }
3221
    res= mysql_alter_db(thd, db, &lex->create_info);
3222 3223
    break;
  }
3224 3225 3226 3227
  case SQLCOM_SHOW_CREATE_DB:
  {
    if (!strip_sp(lex->name) || check_db_name(lex->name))
    {
3228
      net_printf(thd,ER_WRONG_DB_NAME, lex->name);
3229 3230
      break;
    }
vva@eagle.mysql.r18.ru's avatar
vva@eagle.mysql.r18.ru committed
3231
    if (check_access(thd,SELECT_ACL,lex->name,0,1,0))
3232 3233 3234
      break;
    if (thd->locked_tables || thd->active_transaction())
    {
3235
      send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
3236 3237
      goto error;
    }
3238
    res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
3239 3240
    break;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3241
  case SQLCOM_CREATE_FUNCTION:
hf@deer.(none)'s avatar
hf@deer.(none) committed
3242
    if (check_access(thd,INSERT_ACL,"mysql",0,1,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3243 3244 3245
      break;
#ifdef HAVE_DLOPEN
    if (!(res = mysql_create_function(thd,&lex->udf)))
3246
      send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3247 3248 3249 3250 3251
#else
    res= -1;
#endif
    break;
  case SQLCOM_DROP_FUNCTION:
hf@deer.(none)'s avatar
hf@deer.(none) committed
3252
    if (check_access(thd,DELETE_ACL,"mysql",0,1,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3253 3254
      break;
#ifdef HAVE_DLOPEN
3255
    if (!(res = mysql_drop_function(thd,&lex->udf.name)))
3256
      send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3257 3258 3259 3260
#else
    res= -1;
#endif
    break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3261
#ifndef NO_EMBEDDED_ACCESS_CHECKS
3262 3263
  case SQLCOM_DROP_USER:
  {
hf@deer.(none)'s avatar
hf@deer.(none) committed
3264
    if (check_access(thd, GRANT_ACL,"mysql",0,1,0))
3265 3266 3267 3268 3269 3270
      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())
      {
3271
	Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3272 3273 3274 3275 3276 3277 3278 3279
	mysql_bin_log.write(&qinfo);
      }
      send_ok(thd);
    }
    break;
  }
  case SQLCOM_REVOKE_ALL:
  {
hf@deer.(none)'s avatar
hf@deer.(none) committed
3280
    if (check_access(thd, GRANT_ACL ,"mysql",0,1,0))
3281 3282 3283 3284 3285 3286
      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())
      {
3287
	Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3288 3289 3290 3291 3292 3293
	mysql_bin_log.write(&qinfo);
      }
      send_ok(thd);
    }
    break;
  }
3294 3295 3296 3297 3298 3299
  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
3300
		     tables ? 0 : 1,0))
3301 3302
      goto error;

3303 3304 3305 3306
    /*
      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
    */
3307 3308 3309 3310 3311 3312 3313 3314 3315 3316

    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 &&
3317
	     my_strcasecmp(&my_charset_latin1,
3318
                           user->host.str, thd->host_or_ip)))
3319
	{
hf@deer.(none)'s avatar
hf@deer.(none) committed
3320
	  if (check_access(thd, UPDATE_ACL, "mysql",0,1,0))
3321 3322 3323 3324 3325
	    goto error;
	  break;			// We are allowed to do changes
	}
      }
    }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338
    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);
      }
    }
3339 3340 3341 3342 3343
    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
3344
				      tables, 0, UINT_MAX, 0))
3345
	goto error;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3346 3347 3348
      if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns,
				    lex->grant,
				    lex->sql_command == SQLCOM_REVOKE)))
3349
      {
3350
	mysql_update_log.write(thd, thd->query, thd->query_length);
3351 3352
	if (mysql_bin_log.is_open())
	{
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3353
          thd->clear_error();
3354
	  Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3355 3356 3357 3358 3359 3360 3361 3362
	  mysql_bin_log.write(&qinfo);
	}
      }
    }
    else
    {
      if (lex->columns.elements)
      {
3363
	send_error(thd,ER_ILLEGAL_GRANT_FOR_TABLE);
3364 3365 3366 3367 3368 3369 3370
	res=1;
      }
      else
	res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
			  lex->sql_command == SQLCOM_REVOKE);
      if (!res)
      {
3371
	mysql_update_log.write(thd, thd->query, thd->query_length);
3372 3373
	if (mysql_bin_log.is_open())
	{
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3374
          thd->clear_error();
3375
	  Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3376 3377
	  mysql_bin_log.write(&qinfo);
	}
3378
	if (mqh_used && lex->sql_command == SQLCOM_GRANT)
3379
	{
3380 3381 3382
	  List_iterator <LEX_USER> str_list(lex->users_list);
	  LEX_USER *user;
	  while ((user=str_list++))
3383
	    reset_mqh(thd,user);
3384
	}
3385 3386 3387 3388
      }
    }
    break;
  }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3389
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
3390
  case SQLCOM_RESET:
3391 3392 3393 3394 3395 3396 3397
    /* 
       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:
  {
3398
    if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3399
      goto error;
3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417
    /*
      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())
        {
3418
          Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
3419 3420 3421 3422 3423
          mysql_bin_log.write(&qinfo);
        }
      }
      send_ok(thd);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3424
    break;
3425
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3426 3427 3428
  case SQLCOM_KILL:
    kill_one_thread(thd,lex->thread_id);
    break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3429
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3430 3431
  case SQLCOM_SHOW_GRANTS:
    res=0;
3432 3433
    if ((thd->priv_user &&
	 !strcmp(thd->priv_user,lex->grant_user->user.str)) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
3434
	!check_access(thd, SELECT_ACL, "mysql",0,1,0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3435 3436 3437 3438
    {
      res = mysql_show_grants(thd,lex->grant_user);
    }
    break;
hf@deer.(none)'s avatar
hf@deer.(none) committed
3439
#endif
3440
  case SQLCOM_HA_OPEN:
3441
    if (check_db_used(thd,tables) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
3442
	check_table_access(thd,SELECT_ACL, tables,0))
3443 3444 3445 3446 3447 3448 3449 3450 3451
      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:
3452 3453 3454 3455 3456
    /*
      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
3457
    if (check_db_used(thd,tables))
3458
      goto error;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
3459
    res = mysql_ha_read(thd, tables, lex->ha_read_mode, lex->backup_dir,
3460 3461
			lex->insert_list, lex->ha_rkey_mode, select_lex->where,
			select_lex->select_limit, select_lex->offset_limit);
3462 3463
    break;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3464
  case SQLCOM_BEGIN:
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3465 3466 3467 3468 3469 3470
    if (thd->locked_tables)
    {
      thd->lock=thd->locked_tables;
      thd->locked_tables=0;			// Will be automaticly closed
      close_thread_tables(thd);			// Free tables
    }
3471 3472 3473 3474 3475 3476
    if (end_active_trans(thd))
    {
      res= -1;
    }
    else
    {
3477
      thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
3478 3479
		     OPTION_BEGIN);
      thd->server_status|= SERVER_STATUS_IN_TRANS;
3480 3481 3482
      if (!(lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) ||
          !(res= ha_start_consistent_snapshot(thd)))
        send_ok(thd);
3483
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3484 3485
    break;
  case SQLCOM_COMMIT:
3486 3487 3488 3489 3490
    /*
      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
3491
  {
3492
    thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3493 3494
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
    if (!ha_commit(thd))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3495
    {
3496
      send_ok(thd);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3497
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3498 3499 3500
    else
      res= -1;
    break;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3501
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3502 3503 3504
  case SQLCOM_ROLLBACK:
    thd->server_status&= ~SERVER_STATUS_IN_TRANS;
    if (!ha_rollback(thd))
3505
    {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3506 3507 3508 3509 3510 3511 3512 3513 3514 3515
      /*
        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)
3516
	send_warning(thd,ER_WARNING_NOT_COMPLETE_ROLLBACK,0);
3517
      else
3518
	send_ok(thd);
3519
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3520 3521
    else
      res= -1;
3522
    thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3523
    break;
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3524 3525 3526
  case SQLCOM_ROLLBACK_TO_SAVEPOINT:
    if (!ha_rollback_to_savepoint(thd, lex->savepoint_name))
    {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3527
      if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && !thd->slave_thread)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3528
	send_warning(thd, ER_WARNING_NOT_COMPLETE_ROLLBACK, 0);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3529
      else
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3530
	send_ok(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3531 3532 3533 3534
    }
    else
      res= -1;
    break;
3535
  case SQLCOM_SAVEPOINT:
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3536
    if (!ha_savepoint(thd, lex->savepoint_name))
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3537
      send_ok(thd);
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
3538 3539
    else
      res= -1;
3540
    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3541
  default:					/* Impossible */
3542
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3543 3544 3545
    break;
  }
  thd->proc_info="query end";			// QQ
3546
  if (thd->one_shot_set)
serg@serg.mylan's avatar
serg@serg.mylan committed
3547 3548 3549 3550 3551 3552 3553 3554
  {
    /*
      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)
3555
    {
serg@serg.mylan's avatar
serg@serg.mylan committed
3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567
      thd->variables.character_set_client=
        global_system_variables.character_set_client;
      thd->variables.collation_connection=
        global_system_variables.collation_connection;
      thd->variables.collation_database=
        global_system_variables.collation_database;
      thd->variables.collation_server=
        global_system_variables.collation_server;
      thd->update_charset();
      thd->variables.time_zone=
        global_system_variables.time_zone;
      thd->one_shot_set= 0;
3568
    }
serg@serg.mylan's avatar
serg@serg.mylan committed
3569
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3570
  if (res < 0)
3571
    send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3572 3573 3574 3575 3576 3577

error:
  DBUG_VOID_RETURN;
}


bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3578 3579 3580 3581
/*
  Check grants for commands which work only with one table and all other
  tables belong to subselects.

3582
  SYNOPSIS
monty@mysql.com's avatar
monty@mysql.com committed
3583 3584 3585 3586
    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
3587 3588 3589

  RETURN
    0 - OK
monty@mysql.com's avatar
monty@mysql.com committed
3590
    1 - access denied, error is sent to client
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3591
*/
3592 3593

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
3594
{
monty@mysql.com's avatar
monty@mysql.com committed
3595 3596
  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
3597

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

3602
  /* Check rights on tables of subselect (if exists) */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3603 3604
  TABLE_LIST *subselects_tables;
  if ((subselects_tables= tables->next))
monty@mysql.com's avatar
monty@mysql.com committed
3605
  {
monty@mysql.com's avatar
monty@mysql.com committed
3606
    if ((check_table_access(thd, SELECT_ACL, subselects_tables,0)))
monty@mysql.com's avatar
monty@mysql.com committed
3607 3608 3609
      return 1;
  }
  return 0;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3610 3611 3612
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
3613
/****************************************************************************
3614
  Get the user (global) and database privileges for all used tables
3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627

  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
3628 3629
                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
3630 3631 3632
****************************************************************************/

bool
3633
check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
3634
	     bool dont_check_global_grants, bool no_errors)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3635
{
3636
  DBUG_ENTER("check_access");
3637 3638
  DBUG_PRINT("enter",("db: '%s'  want_access: %lu  master_access: %lu",
                      db ? db : "", want_access, thd->master_access));
3639 3640 3641 3642
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  ulong db_access;
#endif
  ulong dummy;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3643 3644 3645 3646 3647
  if (save_priv)
    *save_priv=0;
  else
    save_priv= &dummy;

3648
  if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3649
  {
3650
    if (!no_errors)
3651
      send_error(thd,ER_NO_DB_ERROR);	/* purecov: tested */
3652
    DBUG_RETURN(TRUE);				/* purecov: tested */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3653 3654
  }

monty@mysql.com's avatar
monty@mysql.com committed
3655 3656 3657
#ifdef NO_EMBEDDED_ACCESS_CHECKS
  DBUG_RETURN(0);
#else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3658 3659
  if ((thd->master_access & want_access) == want_access)
  {
3660 3661 3662 3663 3664 3665 3666 3667
    /*
      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))))
3668
      db_access=acl_get(thd->host, thd->ip,
3669
			thd->priv_user, db, test(want_access & GRANT_ACL));
3670
    *save_priv=thd->master_access | db_access;
3671
    DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3672
  }
3673
  if (((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
3674
      ! db && dont_check_global_grants)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3675
  {						// We can never grant this
3676
    if (!no_errors)
3677
      net_printf(thd,ER_ACCESS_DENIED_ERROR,
3678
		 thd->priv_user,
3679
		 thd->priv_host,
3680
		 thd->password ? ER(ER_YES) : ER(ER_NO));/* purecov: tested */
3681
    DBUG_RETURN(TRUE);				/* purecov: tested */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3682 3683 3684
  }

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3687
  if (db && (!thd->db || strcmp(db,thd->db)))
3688
    db_access=acl_get(thd->host, thd->ip,
3689
		      thd->priv_user, db, test(want_access & GRANT_ACL));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3690 3691
  else
    db_access=thd->db_access;
3692 3693
  DBUG_PRINT("info",("db_access: %lu", db_access));
  /* Remove SHOW attribute and access rights we already have */
3694
  want_access &= ~(thd->master_access | EXTRA_ACL);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3695
  db_access= ((*save_priv=(db_access | thd->master_access)) & want_access);
3696 3697

  /* grant_option is set if there exists a single table or column grant */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3698
  if (db_access == want_access ||
3699
      ((grant_option && !dont_check_global_grants) &&
3700
       !(want_access & ~(db_access | TABLE_ACLS))))
3701
    DBUG_RETURN(FALSE);				/* Ok */
3702
  if (!no_errors)
3703
    net_printf(thd,ER_DBACCESS_DENIED_ERROR,
3704
	       thd->priv_user,
3705
	       thd->priv_host,
3706
	       db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */
3707
  DBUG_RETURN(TRUE);				/* purecov: tested */
monty@mysql.com's avatar
monty@mysql.com committed
3708
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3709 3710 3711
}


3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729
/*
  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
*/
3730 3731

bool check_global_access(THD *thd, ulong want_access)
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3732
{
monty@mysql.com's avatar
monty@mysql.com committed
3733 3734 3735
#ifdef NO_EMBEDDED_ACCESS_CHECKS
  return 0;
#else
3736
  char command[128];
3737
  if ((thd->master_access & want_access))
3738 3739
    return 0;
  get_privilege_desc(command, sizeof(command), want_access);
3740
  net_printf(thd,ER_SPECIFIC_ACCESS_DENIED_ERROR,
3741 3742
	     command);
  return 1;
monty@mysql.com's avatar
monty@mysql.com committed
3743
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
3744 3745 3746
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
3747
/*
3748 3749
  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
3750 3751
*/

3752
bool
3753
check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
3754
		   bool no_errors)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3755
{
3756 3757
  uint found=0;
  ulong found_access=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3758 3759 3760
  TABLE_LIST *org_tables=tables;
  for (; tables ; tables=tables->next)
  {
3761 3762 3763 3764
    if (tables->derived ||
        (tables->table && (int)tables->table->tmp_table) ||
        my_tz_check_n_skip_implicit_tables(&tables,
                                           thd->lex->time_zone_tables_used))
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
3765
      continue;
3766 3767
    if ((thd->master_access & want_access) == (want_access & ~EXTRA_ACL) &&
	thd->db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3768
      tables->grant.privilege= want_access;
3769
    else if (tables->db && tables->db == thd->db)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3770 3771 3772 3773 3774
    {
      if (found && !grant_option)		// db already checked
	tables->grant.privilege=found_access;
      else
      {
3775 3776
	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
3777 3778
	  return TRUE;				// Access denied
	found_access=tables->grant.privilege;
3779
	found=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3780 3781
      }
    }
3782
    else if (check_access(thd,want_access,tables->db,&tables->grant.privilege,
3783
			  0, no_errors))
3784
      return TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3785 3786
  }
  if (grant_option)
3787
    return check_grant(thd,want_access & ~EXTRA_ACL,org_tables,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3788
		       test(want_access & EXTRA_ACL), UINT_MAX, no_errors);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3789 3790 3791
  return FALSE;
}

3792 3793
bool check_merge_table_access(THD *thd, char *db,
			      TABLE_LIST *table_list)
3794 3795 3796 3797
{
  int error=0;
  if (table_list)
  {
3798
    /* Check that all tables use the current database */
3799 3800
    TABLE_LIST *tmp;
    for (tmp=table_list; tmp ; tmp=tmp->next)
3801 3802 3803 3804
    {
      if (!tmp->db || !tmp->db[0])
	tmp->db=db;
    }
3805
    error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
hf@deer.(none)'s avatar
hf@deer.(none) committed
3806
			     table_list,0);
3807 3808 3809 3810
  }
  return error;
}

hf@deer.(none)'s avatar
hf@deer.(none) committed
3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3828 3829 3830 3831 3832 3833 3834 3835 3836 3837
/****************************************************************************
	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
3838 3839 3840 3841
#ifndef DBUG_OFF
long max_stack_used;
#endif

3842
#ifndef EMBEDDED_LIBRARY
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3843 3844 3845 3846 3847 3848 3849 3850
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));
3851
    thd->fatal_error();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3852 3853
    return 1;
  }
monty@mysql.com's avatar
monty@mysql.com committed
3854 3855 3856
#ifndef DBUG_OFF
  max_stack_used= max(max_stack_used, stack_used);
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3857 3858
  return 0;
}
3859
#endif /* EMBEDDED_LIBRARY */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3860 3861 3862 3863

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

3864
bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3865 3866
{
  LEX	*lex=current_lex;
3867
  ulong old_info=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892
  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;
}

/****************************************************************************
3893
  Initialize global thd variables needed for query
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3894 3895
****************************************************************************/

3896
void
3897
mysql_init_query(THD *thd, uchar *buf, uint length)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3898 3899
{
  DBUG_ENTER("mysql_init_query");
3900
  lex_start(thd, buf, length);
3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921
  mysql_reset_thd_for_next_command(thd);
  DBUG_VOID_RETURN;
}


/*
 Reset THD part responsible for command processing state.

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

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

void mysql_reset_thd_for_next_command(THD *thd)
{
  DBUG_ENTER("mysql_reset_thd_for_next_command");
3922
  thd->free_list= 0;
3923 3924
  thd->select_number= 1;
  thd->total_warn_count= 0;                     // Warnings for this query
3925 3926
  thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
  thd->sent_row_count= thd->examined_row_count= 0;
3927
  thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0;
monty@mysql.com's avatar
monty@mysql.com committed
3928 3929 3930
  thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | 
			  SERVER_QUERY_NO_INDEX_USED |
			  SERVER_QUERY_NO_GOOD_INDEX_USED);
3931
  thd->tmp_table_used= 0;
3932 3933
  if (opt_bin_log)
    reset_dynamic(&thd->user_var_events);
3934
  thd->clear_error();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3935 3936 3937
  DBUG_VOID_RETURN;
}

3938

3939 3940 3941
void
mysql_init_select(LEX *lex)
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3942
  SELECT_LEX *select_lex= lex->current_select;
3943
  select_lex->init_select();
3944
  select_lex->select_limit= HA_POS_ERROR;
3945 3946
  if (select_lex == &lex->select_lex)
  {
3947
    DBUG_ASSERT(lex->result == 0);
3948 3949
    lex->exchange= 0;
  }
3950 3951
}

3952

3953
bool
3954
mysql_new_select(LEX *lex, bool move_down)
3955
{
3956 3957
  SELECT_LEX *select_lex;
  if (!(select_lex= new(lex->thd->mem_root) SELECT_LEX()))
3958
    return 1;
3959
  select_lex->select_number= ++lex->thd->select_number;
3960 3961 3962 3963
  select_lex->init_query();
  select_lex->init_select();
  if (move_down)
  {
3964
    lex->subqueries= TRUE;
3965
    /* first select_lex of subselect or derived table */
3966 3967
    SELECT_LEX_UNIT *unit;
    if (!(unit= new(lex->thd->mem_root) SELECT_LEX_UNIT()))
3968
      return 1;
3969

3970 3971
    unit->init_query();
    unit->init_select();
3972
    unit->thd= lex->thd;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3973
    unit->include_down(lex->current_select);
3974 3975
    unit->link_next= 0;
    unit->link_prev= 0;
3976
    unit->return_to= lex->current_select;
3977
    select_lex->include_down(unit);
3978
    // TODO: assign resolve_mode for fake subquery after merging with new tree
3979 3980
  }
  else
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3981
  {
3982
    select_lex->include_neighbour(lex->current_select);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3983 3984 3985 3986 3987 3988 3989 3990
    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
      */
3991 3992
      if (!(fake= unit->fake_select_lex= new(lex->thd->mem_root) SELECT_LEX()))
        return 1;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3993 3994 3995 3996 3997
      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;
3998
      fake->select_limit= HA_POS_ERROR;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3999 4000
    }
  }
peter@mysql.com's avatar
peter@mysql.com committed
4001

4002
  select_lex->master_unit()->global_parameters= select_lex;
4003
  select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
4004
  lex->current_select= select_lex;
4005
  select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
4006
  return 0;
4007
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4008

4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023
/*
  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)
{
4024
  THD *thd;
4025
  LEX *lex;
4026
  LEX_STRING tmp, null_lex_string;
4027
  DBUG_ENTER("create_select_for_variable");
4028 4029

  thd= current_thd;
4030
  lex= thd->lex;
4031 4032 4033 4034
  mysql_init_select(lex);
  lex->sql_command= SQLCOM_SELECT;
  tmp.str= (char*) var_name;
  tmp.length=strlen(var_name);
4035 4036 4037
  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));
4038 4039 4040
  DBUG_VOID_RETURN;
}

4041

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4042 4043
void mysql_init_multi_delete(LEX *lex)
{
4044
  lex->sql_command=  SQLCOM_DELETE_MULTI;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4045
  mysql_init_select(lex);
4046
  lex->select_lex.select_limit= lex->unit.select_limit_cnt=
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4047
    HA_POS_ERROR;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4048
  lex->select_lex.table_list.save_and_clear(&lex->auxilliary_table_list);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4049
}
4050

4051

4052 4053 4054 4055
/*
  When you modify mysql_parse(), you may need to mofify
  mysql_test_parse_for_slave() in this same file.
*/
4056

4057
void mysql_parse(THD *thd, char *inBuf, uint length)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4058 4059 4060
{
  DBUG_ENTER("mysql_parse");

4061
  mysql_init_query(thd, (uchar*) inBuf, length);
4062
  if (query_cache_send_result_to_client(thd, inBuf, length) <= 0)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4063
  {
4064
    LEX *lex= thd->lex;
4065
    if (!yyparse((void *)thd) && ! thd->is_fatal_error)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4066
    {
hf@deer.(none)'s avatar
hf@deer.(none) committed
4067
#ifndef NO_EMBEDDED_ACCESS_CHECKS
4068
      if (mqh_used && thd->user_connect &&
4069
	  check_mqh(thd, lex->sql_command))
4070 4071 4072 4073
      {
	thd->net.error = 0;
      }
      else
hf@deer.(none)'s avatar
hf@deer.(none) committed
4074
#endif
4075
      {
4076 4077 4078 4079 4080
	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
4081
	  query_cache_end_of_result(thd);
4082
	}
4083
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4084 4085
    }
    else
4086 4087
    {
      DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
4088
			 thd->is_fatal_error));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4089
      query_cache_abort(&thd->net);
4090
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4091
    thd->proc_info="freeing items";
4092
    thd->end_statement();
4093
    DBUG_ASSERT(thd->change_list.is_empty());
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4094
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4095 4096 4097 4098
  DBUG_VOID_RETURN;
}


monty@mysql.com's avatar
monty@mysql.com committed
4099
#ifdef HAVE_REPLICATION
4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110
/*
  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)
{
4111
  LEX *lex= thd->lex;
4112 4113
  bool error= 0;

4114
  mysql_init_query(thd, (uchar*) inBuf, length);
monty@mysql.com's avatar
monty@mysql.com committed
4115
  if (!yyparse((void*) thd) && ! thd->is_fatal_error &&
4116 4117
      all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
    error= 1;                /* Ignore question */
4118
  thd->end_statement();
4119 4120
  return error;
}
monty@mysql.com's avatar
monty@mysql.com committed
4121
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4122

4123

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4124 4125 4126 4127 4128
/*****************************************************************************
** Store field definition for create
** Return 0 if ok
******************************************************************************/

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4129
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
4130
		       char *length, char *decimals,
4131
		       uint type_modifier,
4132 4133
		       Item *default_value, Item *on_update_value,
                       LEX_STRING *comment,
4134 4135
		       char *change,
                       List<String> *interval_list, CHARSET_INFO *cs,
4136
		       uint uint_geom_type)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4137 4138
{
  register create_field *new_field;
4139
  LEX  *lex= thd->lex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4140
  uint allowed_type_modifier=0;
4141
  char warn_buff[MYSQL_ERRMSG_SIZE];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4142 4143 4144 4145
  DBUG_ENTER("add_field_to_list");

  if (strlen(field_name) > NAME_LEN)
  {
4146
    net_printf(thd, ER_TOO_LONG_IDENT, field_name); /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4147 4148 4149 4150 4151
    DBUG_RETURN(1);				/* purecov: inspected */
  }
  if (type_modifier & PRI_KEY_FLAG)
  {
    lex->col_list.push_back(new key_part_spec(field_name,0));
4152
    lex->key_list.push_back(new Key(Key::PRIMARY, NullS, HA_KEY_ALG_UNDEF,
4153
				    0, lex->col_list));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4154 4155 4156 4157 4158
    lex->col_list.empty();
  }
  if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
  {
    lex->col_list.push_back(new key_part_spec(field_name,0));
4159
    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
4160 4161 4162 4163
				    lex->col_list));
    lex->col_list.empty();
  }

4164
  if (default_value)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4165
  {
4166
    /* 
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4167 4168
      Default value should be literal => basic constants =>
      no need fix_fields()
4169 4170 4171
      
      We allow only one function as part of default value - 
      NOW() as default for TIMESTAMP type.
4172
    */
4173 4174 4175 4176 4177 4178 4179 4180
    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
4181
    {
4182
      default_value= 0;
4183 4184 4185
      if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
	  NOT_NULL_FLAG)
      {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4186
	net_printf(thd,ER_INVALID_DEFAULT,field_name);
4187 4188 4189 4190 4191
	DBUG_RETURN(1);
      }
    }
    else if (type_modifier & AUTO_INCREMENT_FLAG)
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4192
      net_printf(thd, ER_INVALID_DEFAULT, field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4193 4194 4195
      DBUG_RETURN(1);
    }
  }
4196 4197 4198 4199 4200 4201 4202

  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
4203 4204 4205 4206
  if (!(new_field=new create_field()))
    DBUG_RETURN(1);
  new_field->field=0;
  new_field->field_name=field_name;
4207
  new_field->def= default_value;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4208 4209 4210 4211 4212 4213 4214 4215 4216 4217
  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;
4218
  new_field->charset=cs;
4219
  new_field->geom_type= (Field::geometry_type) uint_geom_type;
4220

4221 4222 4223 4224 4225 4226 4227 4228
  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
4229 4230
    new_field->comment.str=   (char*) comment->str;
    new_field->comment.length=comment->length;
4231
  }
4232 4233
  if (length && !(new_field->length= (uint) atoi(length)))
    length=0; /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4234 4235 4236
  uint sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1;

  if (new_field->length && new_field->decimals &&
4237
      new_field->length < new_field->decimals+1 &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4238
      new_field->decimals != NOT_FIXED_DEC)
4239
    new_field->length=new_field->decimals+1; /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4240 4241 4242

  switch (type) {
  case FIELD_TYPE_TINY:
4243
    if (!length) new_field->length=MAX_TINYINT_WIDTH+sign_len;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4244 4245 4246
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    break;
  case FIELD_TYPE_SHORT:
4247
    if (!length) new_field->length=MAX_SMALLINT_WIDTH+sign_len;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4248 4249 4250
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    break;
  case FIELD_TYPE_INT24:
4251
    if (!length) new_field->length=MAX_MEDIUMINT_WIDTH+sign_len;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4252 4253 4254
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    break;
  case FIELD_TYPE_LONG:
4255
    if (!length) new_field->length=MAX_INT_WIDTH+sign_len;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4256 4257 4258
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    break;
  case FIELD_TYPE_LONGLONG:
4259
    if (!length) new_field->length=MAX_BIGINT_WIDTH;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4260 4261 4262 4263 4264 4265
    allowed_type_modifier= AUTO_INCREMENT_FLAG;
    break;
  case FIELD_TYPE_NULL:
    break;
  case FIELD_TYPE_DECIMAL:
    if (!length)
monty@mysql.com's avatar
monty@mysql.com committed
4266 4267
    {
      if ((new_field->length= new_field->decimals))
4268 4269
        new_field->length++;
      else
monty@mysql.com's avatar
monty@mysql.com committed
4270 4271
        new_field->length= 10;                  // Default length for DECIMAL
    }
4272 4273 4274 4275 4276 4277
    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
4278
    break;
4279 4280
  case FIELD_TYPE_STRING:
  case FIELD_TYPE_VAR_STRING:
4281
    if (new_field->length <= MAX_FIELD_CHARLENGTH || default_value)
4282 4283 4284 4285
      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",
4286
	    (cs == &my_charset_bin) ? "BLOB" : "TEXT");
4287 4288 4289
    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
4290 4291 4292 4293
  case FIELD_TYPE_BLOB:
  case FIELD_TYPE_TINY_BLOB:
  case FIELD_TYPE_LONG_BLOB:
  case FIELD_TYPE_MEDIUM_BLOB:
4294
  case FIELD_TYPE_GEOMETRY:
4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308
    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
4309 4310 4311 4312 4313 4314
    if (default_value)				// Allow empty as default value
    {
      String str,*res;
      res=default_value->val_str(&str);
      if (res->length())
      {
4315
	net_printf(thd,ER_BLOB_CANT_HAVE_DEFAULT,field_name); /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334
	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)
      {
4335
	net_printf(thd,ER_WRONG_FIELD_SPEC,field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364
	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
4365
    else if (new_field->length != 19)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4366
    {
4367 4368 4369 4370
      /*
        We support only even TIMESTAMP lengths less or equal than 14
        and 19 as length of 4.1 compatible representation.
      */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4371 4372 4373
      new_field->length=((new_field->length+1)/2)*2; /* purecov: inspected */
      new_field->length= min(new_field->length,14); /* purecov: inspected */
    }
4374
    new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394
    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
    {
4395 4396 4397 4398 4399 4400 4401 4402
      /*
        If we have default TIMESTAMP NOT NULL column without explicit DEFAULT
        or ON UPDATE values then for the sake of compatiblity we should treat
        this column as having DEFAULT NOW() ON UPDATE NOW() (when we don't
        have another TIMESTAMP column with auto-set option before this one)
        or DEFAULT 0 (in other cases).
        So here we are setting TIMESTAMP_OLD_FIELD only temporary, and will
        replace this value by TIMESTAMP_DNUN_FIELD or NONE later when
4403
        information about all TIMESTAMP fields in table will be availiable.
4404 4405 4406

        If we have TIMESTAMP NULL column without explicit DEFAULT value
        we treat it as having DEFAULT NULL attribute.
4407
      */
4408 4409 4410 4411 4412
      new_field->unireg_check= on_update_value ?
                               Field::TIMESTAMP_UN_FIELD :
                               (new_field->flags & NOT_NULL_FLAG ?
                                Field::TIMESTAMP_OLD_FIELD:
                                Field::NONE);
4413
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429
    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:
    {
4430
      if (interval_list->elements > sizeof(longlong)*8)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4431
      {
4432 4433
        net_printf(thd,ER_TOO_BIG_SET,field_name); /* purecov: inspected */
        DBUG_RETURN(1);				/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4434
      }
4435
      new_field->pack_length= (interval_list->elements + 7) / 8;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4436
      if (new_field->pack_length > 4)
4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448
        new_field->pack_length=8;

      List_iterator<String> it(*interval_list);
      String *tmp;
      while ((tmp= it++))
        new_field->interval_list.push_back(tmp);
      /*
        Set fake length to 1 to pass the below conditions.
        Real length will be set in mysql_prepare_table()
        when we know the character set of the column
      */
      new_field->length= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4449 4450 4451 4452
    }
    break;
  case FIELD_TYPE_ENUM:
    {
4453 4454 4455 4456 4457 4458 4459 4460
      // Should be safe
      new_field->pack_length= interval_list->elements < 256 ? 1 : 2; 

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

4465 4466
  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
4467
      (!new_field->length && !(new_field->flags & BLOB_FLAG) &&
4468
       type != FIELD_TYPE_STRING &&
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
4469
       type != FIELD_TYPE_VAR_STRING && type != FIELD_TYPE_GEOMETRY))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4470
  {
4471
    net_printf(thd,ER_TOO_BIG_FIELDLENGTH,field_name,
4472
	       MAX_FIELD_CHARLENGTH);		/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4473 4474 4475 4476 4477
    DBUG_RETURN(1);				/* purecov: inspected */
  }
  type_modifier&= AUTO_INCREMENT_FLAG;
  if ((~allowed_type_modifier) & type_modifier)
  {
4478
    net_printf(thd,ER_WRONG_FIELD_SPEC,field_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499
    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
4500
add_proc_to_list(THD* thd, Item *item)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4501 4502 4503 4504
{
  ORDER *order;
  Item	**item_ptr;

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4505
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4506 4507 4508 4509 4510
    return 1;
  item_ptr = (Item**) (order+1);
  *item_ptr= item;
  order->item=item_ptr;
  order->free_me=0;
4511
  thd->lex->proc_list.link_in_list((byte*) order,(byte**) &order->next);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4512 4513 4514 4515 4516 4517 4518 4519
  return 0;
}


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

static void remove_escape(char *name)
{
4520 4521
  if (!*name)					// For empty DB names
    return;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4522 4523
  char *to;
#ifdef USE_MB
4524
  char *strend=name+(uint) strlen(name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4525 4526 4527 4528 4529 4530
#endif
  for (to=name; *name ; name++)
  {
#ifdef USE_MB
    int l;
/*    if ((l = ismbchar(name, name+MBMAXLEN))) { Wei He: I think it's wrong */
4531 4532
    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
4533 4534 4535 4536 4537 4538 4539 4540
    {
	while (l--)
	    *to++ = *name++;
	name--;
	continue;
    }
#endif
    if (*name == '\\' && name[1])
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4541
      name++;					// Skip '\\'
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4542 4543 4544 4545 4546 4547 4548 4549 4550 4551
    *to++= *name;
  }
  *to=0;
}

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


monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4552
bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4553 4554 4555
{
  ORDER *order;
  DBUG_ENTER("add_to_list");
4556
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4557
    DBUG_RETURN(1);
4558 4559
  order->item_ptr= item;
  order->item= &order->item_ptr;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4560 4561 4562
  order->asc = asc;
  order->free_me=0;
  order->used=0;
4563
  list.link_in_list((byte*) order,(byte**) &order->next);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4564 4565 4566 4567
  DBUG_RETURN(0);
}


monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586
/*
  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
4587 4588
TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
					     Table_ident *table,
4589
					     LEX_STRING *alias,
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4590 4591
					     ulong table_options,
					     thr_lock_type lock_type,
4592 4593
					     List<String> *use_index_arg,
					     List<String> *ignore_index_arg,
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
4594
                                             LEX_STRING *option)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4595 4596 4597 4598 4599 4600 4601 4602
{
  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;
4603
  if (check_table_name(table->table.str,table->table.length) ||
4604
      table->db.str && check_db_name(table->db.str))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4605
  {
4606
    net_printf(thd, ER_WRONG_TABLE_NAME, table->table.str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4607 4608 4609 4610
    DBUG_RETURN(0);
  }

  if (!alias)					/* Alias is case sensitive */
4611 4612 4613 4614 4615 4616
  {
    if (table->sel)
    {
      net_printf(thd,ER_DERIVED_MUST_HAVE_ALIAS);
      DBUG_RETURN(0);
    }
4617
    if (!(alias_str=thd->memdup(alias_str,table->table.length+1)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4618
      DBUG_RETURN(0);
4619
  }
4620
  if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4621
    DBUG_RETURN(0);				/* purecov: inspected */
peter@mysql.com's avatar
peter@mysql.com committed
4622
  if (table->db.str)
4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633
  {
    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
  {
4634 4635
    /* The following can't be "" as we may do 'casedn_str()' on it */
    ptr->db= empty_c_string;
4636 4637
    ptr->db_length= 0;
  }
4638 4639
  if (thd->current_arena->is_stmt_prepare())
    ptr->db= thd->strdup(ptr->db);
peter@mysql.com's avatar
peter@mysql.com committed
4640

4641
  ptr->alias= alias_str;
4642 4643
  if (lower_case_table_names && table->table.length)
    my_casedn_str(files_charset_info, table->table.str);
4644
  ptr->real_name=table->table.str;
4645
  ptr->real_name_length=table->table.length;
4646
  ptr->lock_type=   lock_type;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4647 4648
  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
4649
  ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
4650
  ptr->derived=	    table->sel;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4651
  ptr->cacheable_table= 1;
4652 4653 4654 4655 4656 4657
  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
4658
  ptr->option= option ? option->str : 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4659
  /* check that used name is unique */
4660
  if (lock_type != TL_IGNORE)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4661
  {
4662
    for (TABLE_LIST *tables=(TABLE_LIST*) table_list.first ;
4663
	 tables ;
4664
	 tables=tables->next)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4665
    {
4666 4667
      if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
	  !strcmp(ptr->db, tables->db))
4668
      {
4669
	net_printf(thd,ER_NONUNIQ_TABLE,alias_str); /* purecov: tested */
4670 4671
	DBUG_RETURN(0);				/* purecov: tested */
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4672 4673
    }
  }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4674
  table_list.link_in_list((byte*) ptr, (byte**) &ptr->next);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4675 4676 4677
  DBUG_RETURN(ptr);
}

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

4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691
/*
  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
*/

4692
void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
4693 4694 4695 4696 4697 4698
{
  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));

4699
  for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first ;
4700 4701 4702 4703 4704 4705 4706 4707 4708
       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
4709

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4710 4711
void add_join_on(TABLE_LIST *b,Item *expr)
{
4712
  if (expr)
4713
  {
4714 4715 4716 4717 4718 4719 4720 4721
    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();
4722
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4723 4724 4725
}


4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743
/*
  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
4744 4745 4746 4747 4748
void add_join_natural(TABLE_LIST *a,TABLE_LIST *b)
{
  b->natural_join=a;
}

4749
/*
4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766
  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
4767 4768
*/

4769 4770
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
4771 4772 4773
{
  bool result=0;
  select_errors=0;				/* Write if more errors */
4774
  bool tmp_write_to_binlog= 1;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
4775
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4776 4777
  if (options & REFRESH_GRANT)
  {
4778
    acl_reload(thd);
4779
    grant_reload(thd);
4780
    if (mqh_used)
4781
      reset_mqh(thd,(LEX_USER *) NULL,TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4782
  }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
4783
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4784 4785
  if (options & REFRESH_LOG)
  {
4786
    /*
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4787 4788
      Flush the normal query log, the update log, the binary log,
      the slow query log, and the relay log (if it exists).
4789
    */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4790

4791 4792 4793 4794 4795 4796
    /* 
     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;
4797 4798 4799 4800
    mysql_log.new_file(1);
    mysql_update_log.new_file(1);
    mysql_bin_log.new_file(1);
    mysql_slow_log.new_file(1);
4801
#ifdef HAVE_REPLICATION
4802
    if (mysql_bin_log.is_open() && expire_logs_days)
4803 4804 4805
    {
      long purge_time= time(0) - expire_logs_days*24*60*60;
      if (purge_time >= 0)
4806
	mysql_bin_log.purge_logs_before_date(purge_time);
4807
    }
4808
    pthread_mutex_lock(&LOCK_active_mi);
4809
    rotate_relay_log(active_mi);
4810
    pthread_mutex_unlock(&LOCK_active_mi);
4811
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4812 4813
    if (ha_flush_logs())
      result=1;
4814 4815
    if (flush_error_log())
      result=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4816
  }
4817
#ifdef HAVE_QUERY_CACHE
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4818 4819
  if (options & REFRESH_QUERY_CACHE_FREE)
  {
4820
    query_cache.pack();				// FLUSH QUERY CACHE
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4821 4822 4823 4824
    options &= ~REFRESH_QUERY_CACHE; //don't flush all cache, just free memory
  }
  if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
  {
4825
    query_cache.flush();			// RESET QUERY CACHE
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
4826
  }
4827
#endif /*HAVE_QUERY_CACHE*/
4828 4829 4830 4831 4832
  /*
    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
4833
  {
4834
    if ((options & REFRESH_READ_LOCK) && thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4835
    {
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
4836 4837 4838 4839
      /*
	Writing to the binlog could cause deadlocks, as we don't log
	UNLOCK TABLES
      */
4840
      tmp_write_to_binlog= 0;
4841 4842
      if (lock_global_read_lock(thd))
	return 1;
4843 4844 4845
      result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1,
                                 tables);
      make_global_read_lock_block_commit(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4846
    }
4847 4848
    else
      result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables);
4849
    my_dbopt_cleanup();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4850 4851 4852 4853 4854 4855 4856
  }
  if (options & REFRESH_HOSTS)
    hostname_cache_refresh();
  if (options & REFRESH_STATUS)
    refresh_status();
  if (options & REFRESH_THREADS)
    flush_thread_cache();
4857
#ifdef HAVE_REPLICATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4858
  if (options & REFRESH_MASTER)
4859 4860
  {
    tmp_write_to_binlog= 0;
4861 4862
    if (reset_master(thd))
      result=1;
4863
  }
4864
#endif
4865
#ifdef OPENSSL
4866 4867 4868 4869 4870 4871
   if (options & REFRESH_DES_KEY_FILE)
   {
     if (des_key_file)
       result=load_des_key_file(des_key_file);
   }
#endif
4872
#ifdef HAVE_REPLICATION
4873 4874
 if (options & REFRESH_SLAVE)
 {
4875
   tmp_write_to_binlog= 0;
4876
   pthread_mutex_lock(&LOCK_active_mi);
4877
   if (reset_slave(thd, active_mi))
4878
     result=1;
4879
   pthread_mutex_unlock(&LOCK_active_mi);
4880
 }
4881
#endif
4882
 if (options & REFRESH_USER_RESOURCES)
4883
   reset_mqh(thd,(LEX_USER *) NULL);
4884 4885
 if (write_to_binlog)
   *write_to_binlog= tmp_write_to_binlog;
4886
 return result;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4887 4888
}

4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900
/*
  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
*/

4901
void kill_one_thread(THD *thd, ulong id)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4902 4903 4904
{
  THD *tmp;
  uint error=ER_NO_SUCH_THREAD;
4905 4906
  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
4907 4908 4909 4910
  while ((tmp=it++))
  {
    if (tmp->thread_id == id)
    {
4911 4912
      pthread_mutex_lock(&tmp->LOCK_delete);	// Lock from delete
      break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4913 4914 4915
    }
  }
  VOID(pthread_mutex_unlock(&LOCK_thread_count));
4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928
  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
4929
  if (!error)
4930
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4931
  else
4932
    net_printf(thd,error,id);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4933 4934 4935 4936 4937 4938 4939 4940 4941 4942
}

/* 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)
4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954
      *(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
4955 4956 4957
  }
  pthread_mutex_unlock(&LOCK_status);
}
4958 4959 4960 4961


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

4962 4963
static bool append_file_to_dir(THD *thd, const char **filename_ptr,
			       const char *table_name)
4964
{
4965
  char buff[FN_REFLEN],*ptr, *end;
4966 4967 4968 4969 4970 4971 4972
  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))
  {
4973
    my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr);
4974 4975 4976 4977
    return 1;
  }
  /* Fix is using unix filename format on dos */
  strmov(buff,*filename_ptr);
4978
  end=convert_dirname(buff, *filename_ptr, NullS);
4979
  if (!(ptr=thd->alloc((uint) (end-buff)+(uint) strlen(table_name)+1)))
4980 4981
    return 1;					// End of memory
  *filename_ptr=ptr;
4982
  strxmov(ptr,buff,table_name,NullS);
4983 4984
  return 0;
}
4985

4986

4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000
/*
  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;
5001
  if (thd->lex->current_select != &thd->lex->select_lex)
5002 5003
  {
    char command[80];
5004 5005
    strmake(command, thd->lex->yylval->symbol.str,
	    min(thd->lex->yylval->symbol.length, sizeof(command)-1));
5006
    net_printf(thd, ER_CANT_USE_OPTION_HERE, command);
5007 5008 5009 5010
    return 1;
  }
  return 0;
}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5011

5012

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5013
Comp_creator *comp_eq_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5014
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5015
  return invert?(Comp_creator *)&ne_creator:(Comp_creator *)&eq_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5016 5017
}

5018

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5019
Comp_creator *comp_ge_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5020
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5021
  return invert?(Comp_creator *)&lt_creator:(Comp_creator *)&ge_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5022 5023
}

5024

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5025
Comp_creator *comp_gt_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5026
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5027
  return invert?(Comp_creator *)&le_creator:(Comp_creator *)&gt_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5028 5029
}

5030

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5031
Comp_creator *comp_le_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5032
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5033
  return invert?(Comp_creator *)&gt_creator:(Comp_creator *)&le_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5034 5035
}

5036

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5037
Comp_creator *comp_lt_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5038
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5039
  return invert?(Comp_creator *)&ge_creator:(Comp_creator *)&lt_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5040 5041
}

5042

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5043
Comp_creator *comp_ne_creator(bool invert)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5044
{
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5045
  return invert?(Comp_creator *)&eq_creator:(Comp_creator *)&ne_creator;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5046
}
5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066


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

  if ((cmp == &comp_ne_creator) && all)        // <> ALL <=> NOT IN
5071 5072 5073
    return new Item_func_not(new Item_in_subselect(left_expr, select_lex));

  Item_allany_subselect *it=
5074
    new Item_allany_subselect(left_expr, (*cmp)(all), select_lex, all);
5075 5076 5077 5078 5079
  if (all)
    return it->upper_not= new Item_func_not_all(it);	/* ALL */

  return it;						/* ANY/SOME */
}
5080 5081


5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096
/*
  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;
5097 5098 5099
  ALTER_INFO alter_info;
  alter_info.flags= ALTER_ADD_INDEX;
  alter_info.is_simple= 0;
5100 5101 5102 5103 5104 5105 5106
  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,
5107 5108
				fields, keys, 0, (ORDER*)0,
				DUP_ERROR, &alter_info));
5109 5110 5111
}


5112
int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info)
5113 5114 5115 5116 5117 5118 5119 5120
{
  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;
5121 5122 5123
  alter_info->clear();
  alter_info->flags= ALTER_DROP_INDEX;
  alter_info->is_simple= 0;
5124 5125
  DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
				&create_info, table_list,
5126 5127
				fields, keys, 0, (ORDER*)0,
				DUP_ERROR, alter_info));
5128
}
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
5129 5130


5131 5132 5133 5134 5135
/*
  Multi update query pre-check

  SYNOPSIS
    multi_update_precheck()
5136 5137
    thd		Thread handler
    tables	Global table list
5138

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5139
  RETURN VALUE
5140 5141 5142
    0   OK
    1   Error (message is sent to user)
    -1  Error (message is not sent to user)
5143
*/
5144

5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164
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)
  {
5165 5166 5167 5168 5169 5170
    if (table->derived)
      table->grant.privilege= SELECT_ACL;
    else if ((check_access(thd, UPDATE_ACL, table->db,
                           &table->grant.privilege, 0, 1) ||
              grant_option &&
              check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) &&
5171 5172
	(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
5173
	 grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
5174
      DBUG_RETURN(1);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5175 5176 5177 5178 5179

    /*
      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
    */
5180
    if (table->table_list)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5181
      table->table_list->table_in_update_from_clause= 1;
5182
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5183 5184 5185
  /*
    Is there tables of subqueries?
  */
5186 5187
  if (&lex->select_lex != lex->all_selects_list)
  {
5188
    DBUG_PRINT("info",("Checking sub query list"));
5189 5190
    for (table= tables; table; table= table->next)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5191
      if (table->table_in_update_from_clause)
5192 5193 5194 5195 5196 5197 5198 5199 5200
      {
	/*
	  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;
      }
5201
      else if (!table->derived)
5202 5203 5204
      {
	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
5205
	    grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0))
5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228
	  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()
5229 5230 5231
    thd			Thread handler
    tables		Global table list
    table_count		Pointer to table counter
5232

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5233
  RETURN VALUE
5234 5235 5236
    0   OK
    1   error (message is sent to user)
    -1  error (message is not sent to user)
5237 5238 5239 5240 5241 5242 5243 5244
*/
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
5245 5246 5247
  TABLE_LIST *target_tbl;

  *table_count= 0;
5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259

  /* 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
5260
  for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next)
5261 5262 5263 5264 5265 5266
  {
    (*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
5267 5268 5269
      if (!my_strcasecmp(table_alias_charset,
			 target_tbl->alias, walk->alias) &&
	  !strcmp(walk->db, target_tbl->db))
5270 5271 5272 5273
	break;
    }
    if (!walk)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5274 5275
      my_error(ER_UNKNOWN_TABLE, MYF(0), target_tbl->real_name,
	       "MULTI DELETE");
5276 5277 5278 5279
      DBUG_RETURN(-1);
    }
    if (walk->derived)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5280 5281
      my_error(ER_NON_UPDATABLE_TABLE, MYF(0), target_tbl->real_name,
	       "DELETE");
5282 5283
      DBUG_RETURN(-1);
    }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5284 5285
    walk->lock_type= target_tbl->lock_type;
    target_tbl->table_list= walk;	// Remember corresponding table
5286 5287 5288 5289 5290 5291 5292 5293 5294
  }
  DBUG_RETURN(0);
}


/*
  INSERT ... SELECT query pre-check

  SYNOPSIS
5295
    insert_delete_precheck()
5296 5297
    thd		Thread handler
    tables	Global table list
5298

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5299
  RETURN VALUE
5300 5301 5302
    0   OK
    1   Error (message is sent to user)
    -1  Error (message is not sent to user)
5303
*/
5304

5305 5306 5307 5308 5309 5310 5311 5312 5313
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
5314 5315 5316 5317 5318 5319 5320 5321 5322
  DBUG_RETURN(check_one_table_access(thd, privilege, tables) ? 1 : 0);
}


/*
  simple UPDATE query pre-check

  SYNOPSIS
    update_precheck()
5323 5324
    thd		Thread handler
    tables	Global table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5325 5326

  RETURN VALUE
5327 5328 5329
    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
5330
*/
5331

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349
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()
5350 5351
    thd		Thread handler
    tables	Global table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5352 5353

  RETURN VALUE
5354 5355 5356
    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
5357
*/
5358

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5359 5360 5361 5362 5363
int delete_precheck(THD *thd, TABLE_LIST *tables)
{
  DBUG_ENTER("delete_precheck");
  if (check_one_table_access(thd, DELETE_ACL, tables))
    DBUG_RETURN(1);
5364
  /* Set privilege for the WHERE clause */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5365 5366 5367 5368 5369 5370 5371 5372 5373 5374
  tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
  DBUG_RETURN(0);
}


/*
  simple INSERT query pre-check

  SYNOPSIS
    insert_precheck()
5375 5376
    thd		Thread handler
    tables	Global table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5377 5378

  RETURN VALUE
5379 5380 5381
    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
5382
*/
5383

5384
int insert_precheck(THD *thd, TABLE_LIST *tables)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5385 5386 5387 5388
{
  LEX *lex= thd->lex;
  DBUG_ENTER("insert_precheck");

5389 5390 5391
  ulong privilege= INSERT_ACL |
                   (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
                   (lex->duplicates == DUP_UPDATE ? UPDATE_ACL : 0);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5392 5393

  if (check_one_table_access(thd, privilege, tables))
5394
    DBUG_RETURN(1);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5395 5396 5397 5398 5399 5400

  if (lex->select_lex.item_list.elements != lex->value_list.elements)
  {
    my_error(ER_WRONG_VALUE_COUNT, MYF(0));
    DBUG_RETURN(-1);
  }
5401 5402
  DBUG_RETURN(0);
}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5403 5404 5405 5406 5407 5408 5409


/*
  CREATE TABLE query pre-check

  SYNOPSIS
    create_table_precheck()
5410 5411 5412
    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
5413 5414

  RETURN VALUE
5415 5416
    0   OK
    1   Error (message is sent to user)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5417
*/
5418

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5419 5420 5421 5422
int create_table_precheck(THD *thd, TABLE_LIST *tables,
			  TABLE_LIST *create_table)
{
  LEX *lex= thd->lex;
5423 5424 5425
  SELECT_LEX *select_lex= &lex->select_lex;
  ulong want_priv;
  int error= 1;                                 // Error message is given
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5426
  DBUG_ENTER("create_table_precheck");
5427 5428 5429

  want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
              CREATE_TMP_ACL : CREATE_ACL);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5430 5431 5432 5433 5434 5435
  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))
5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479
    goto err;
  if (grant_option && want_priv != CREATE_TMP_ACL &&
      check_grant(thd, want_priv, create_table, 0, UINT_MAX, 0))
    goto err;

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

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

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

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

err:
  DBUG_RETURN(error);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5480
}
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516


/*
  negate given expression

  SYNOPSIS
    negate_expression()
    thd  therad handler
    expr expression for negation

  RETURN
    negated expression
*/

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

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