sql_parse.cc 232 KB
Newer Older
Marc Alff's avatar
Marc Alff committed
1
/* Copyright 2000-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
2

unknown's avatar
unknown committed
3 4
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
6

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

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

16
#define MYSQL_LEX 1
unknown's avatar
unknown committed
17
#include "mysql_priv.h"
18
#include "sql_repl.h"
unknown's avatar
unknown committed
19
#include "rpl_filter.h"
20
#include "repl_failsafe.h"
unknown's avatar
unknown committed
21 22 23
#include <m_ctype.h>
#include <myisam.h>
#include <my_dir.h>
He Zhenxing's avatar
He Zhenxing committed
24
#include "rpl_handler.h"
unknown's avatar
unknown committed
25

26
#include "sp_head.h"
27
#include "sp.h"
28
#include "sp_cache.h"
29
#include "events.h"
30
#include "sql_trigger.h"
Konstantin Osipov's avatar
Konstantin Osipov committed
31
#include "transaction.h"
32
#include "sql_audit.h"
33
#include "sql_prepare.h"
34
#include "probes_mysql.h"
35
#include "set_var.h"
36

37 38 39 40 41
/**
  @defgroup Runtime_Environment Runtime Environment
  @{
*/

42 43 44 45 46 47
/* Used in error handling only */
#define SP_TYPE_STRING(LP) \
  ((LP)->sphead->m_type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE")
#define SP_COM_STRING(LP) \
  ((LP)->sql_command == SQLCOM_CREATE_SPFUNCTION || \
   (LP)->sql_command == SQLCOM_ALTER_FUNCTION || \
48
   (LP)->sql_command == SQLCOM_SHOW_CREATE_FUNC || \
49 50 51
   (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
   "FUNCTION" : "PROCEDURE")

52
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
unknown's avatar
unknown committed
53

54
const char *any_db="*any*";	// Special symbol for check_access
unknown's avatar
unknown committed
55

unknown's avatar
unknown committed
56
const LEX_STRING command_name[]={
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
  { C_STRING_WITH_LEN("Sleep") },
  { C_STRING_WITH_LEN("Quit") },
  { C_STRING_WITH_LEN("Init DB") },
  { C_STRING_WITH_LEN("Query") },
  { C_STRING_WITH_LEN("Field List") },
  { C_STRING_WITH_LEN("Create DB") },
  { C_STRING_WITH_LEN("Drop DB") },
  { C_STRING_WITH_LEN("Refresh") },
  { C_STRING_WITH_LEN("Shutdown") },
  { C_STRING_WITH_LEN("Statistics") },
  { C_STRING_WITH_LEN("Processlist") },
  { C_STRING_WITH_LEN("Connect") },
  { C_STRING_WITH_LEN("Kill") },
  { C_STRING_WITH_LEN("Debug") },
  { C_STRING_WITH_LEN("Ping") },
  { C_STRING_WITH_LEN("Time") },
  { C_STRING_WITH_LEN("Delayed insert") },
  { C_STRING_WITH_LEN("Change user") },
  { C_STRING_WITH_LEN("Binlog Dump") },
  { C_STRING_WITH_LEN("Table Dump") },
  { C_STRING_WITH_LEN("Connect Out") },
  { C_STRING_WITH_LEN("Register Slave") },
  { C_STRING_WITH_LEN("Prepare") },
  { C_STRING_WITH_LEN("Execute") },
  { C_STRING_WITH_LEN("Long Data") },
  { C_STRING_WITH_LEN("Close stmt") },
  { C_STRING_WITH_LEN("Reset stmt") },
  { C_STRING_WITH_LEN("Set option") },
  { C_STRING_WITH_LEN("Fetch") },
  { C_STRING_WITH_LEN("Daemon") },
  { C_STRING_WITH_LEN("Error") }  // Last command number
unknown's avatar
unknown committed
88 89
};

unknown's avatar
unknown committed
90
const char *xa_state_names[]={
91
  "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED", "ROLLBACK ONLY"
unknown's avatar
unknown committed
92 93
};

94

unknown's avatar
unknown committed
95
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
96 97
/**
  Returns true if all tables should be ignored.
98
*/
99 100
inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables)
{
unknown's avatar
unknown committed
101 102
  return rpl_filter->is_on() && tables && !thd->spcont &&
         !rpl_filter->tables_ok(thd->db, tables);
103
}
unknown's avatar
unknown committed
104
#endif
105 106


107 108 109 110 111 112 113 114 115 116 117 118
static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables)
{
  for (TABLE_LIST *table= tables; table; table= table->next_global)
  {
    DBUG_ASSERT(table->db && table->table_name);
    if (table->updating &&
        !find_temporary_table(thd, table->db, table->table_name))
      return 1;
  }
  return 0;
}

119

Konstantin Osipov's avatar
Konstantin Osipov committed
120 121 122 123 124 125 126
/*
  Implicitly commit a active transaction if statement requires so.

  @param thd    Thread handle.
  @param mask   Bitmask used for the SQL command match.

*/
127
static bool stmt_causes_implicit_commit(THD *thd, uint mask)
Konstantin Osipov's avatar
Konstantin Osipov committed
128 129
{
  LEX *lex= thd->lex;
130 131
  bool skip= FALSE;
  DBUG_ENTER("stmt_causes_implicit_commit");
Konstantin Osipov's avatar
Konstantin Osipov committed
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151

  if (!(sql_command_flags[lex->sql_command] & mask))
    DBUG_RETURN(FALSE);

  switch (lex->sql_command) {
  case SQLCOM_DROP_TABLE:
    skip= lex->drop_temporary;
    break;
  case SQLCOM_ALTER_TABLE:
  case SQLCOM_CREATE_TABLE:
    /* If CREATE TABLE of non-temporary table, do implicit commit */
    skip= (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE);
    break;
  case SQLCOM_SET_OPTION:
    skip= lex->autocommit ? FALSE : TRUE;
    break;
  default:
    break;
  }

152
  DBUG_RETURN(!skip);
Konstantin Osipov's avatar
Konstantin Osipov committed
153 154 155
}


unknown's avatar
unknown committed
156 157 158 159
/**
  Mark all commands that somehow changes a table.

  This is used to check number of updates / hour.
unknown's avatar
unknown committed
160 161 162

  sql_command is actually set to SQLCOM_END sometimes
  so we need the +1 to include it in the array.
unknown's avatar
unknown committed
163

164
  See COMMAND_FLAG_xxx for different type of commands
unknown's avatar
unknown committed
165 166
     2  - query that returns meaningful ROW_COUNT() -
          a number of modified rows
167 168
*/

169
uint sql_command_flags[SQLCOM_END+1];
Konstantin Osipov's avatar
Konstantin Osipov committed
170
uint server_command_flags[COM_END+1];
171 172 173

void init_update_queries(void)
{
Konstantin Osipov's avatar
Konstantin Osipov committed
174 175 176 177 178 179 180 181 182 183 184 185 186
  /* Initialize the server command flags array. */
  memset(server_command_flags, 0, sizeof(server_command_flags));

  server_command_flags[COM_STATISTICS]= CF_SKIP_QUERY_ID | CF_SKIP_QUESTIONS;
  server_command_flags[COM_PING]=       CF_SKIP_QUERY_ID | CF_SKIP_QUESTIONS;
  server_command_flags[COM_STMT_PREPARE]= CF_SKIP_QUESTIONS;
  server_command_flags[COM_STMT_CLOSE]=   CF_SKIP_QUESTIONS;
  server_command_flags[COM_STMT_RESET]=   CF_SKIP_QUESTIONS;

  /* Initialize the sql command flags array. */
  memset(sql_command_flags, 0, sizeof(sql_command_flags));

  sql_command_flags[SQLCOM_CREATE_TABLE]=   CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
187
                                            CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
Konstantin Osipov's avatar
Konstantin Osipov committed
188 189
  sql_command_flags[SQLCOM_CREATE_INDEX]=   CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_ALTER_TABLE]=    CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
190
                                            CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
Konstantin Osipov's avatar
Konstantin Osipov committed
191 192 193
  sql_command_flags[SQLCOM_TRUNCATE]=       CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
                                            CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_DROP_TABLE]=     CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
194 195
  sql_command_flags[SQLCOM_LOAD]=           CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
                                            CF_PROTECT_AGAINST_GRL;
Konstantin Osipov's avatar
Konstantin Osipov committed
196 197
  sql_command_flags[SQLCOM_CREATE_DB]=      CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_DROP_DB]=        CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
198 199
  sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_ALTER_DB]=       CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
Konstantin Osipov's avatar
Konstantin Osipov committed
200 201 202 203 204
  sql_command_flags[SQLCOM_RENAME_TABLE]=   CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_DROP_INDEX]=     CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_CREATE_VIEW]=    CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
                                            CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_DROP_VIEW]=      CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
205 206
  sql_command_flags[SQLCOM_CREATE_TRIGGER]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_DROP_TRIGGER]=   CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
Konstantin Osipov's avatar
Konstantin Osipov committed
207 208 209 210 211
  sql_command_flags[SQLCOM_CREATE_EVENT]=   CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_ALTER_EVENT]=    CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_DROP_EVENT]=     CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_CREATE_TRIGGER]= CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_DROP_TRIGGER]=   CF_AUTO_COMMIT_TRANS;
212

unknown's avatar
unknown committed
213
  sql_command_flags[SQLCOM_UPDATE]=	    CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
214
                                            CF_REEXECUTION_FRAGILE | CF_PROTECT_AGAINST_GRL;
unknown's avatar
unknown committed
215
  sql_command_flags[SQLCOM_UPDATE_MULTI]=   CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
216
                                            CF_REEXECUTION_FRAGILE | CF_PROTECT_AGAINST_GRL;
unknown's avatar
unknown committed
217
  sql_command_flags[SQLCOM_INSERT]=	    CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
218
                                            CF_REEXECUTION_FRAGILE | CF_PROTECT_AGAINST_GRL;
unknown's avatar
unknown committed
219
  sql_command_flags[SQLCOM_INSERT_SELECT]=  CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
220
                                            CF_REEXECUTION_FRAGILE | CF_PROTECT_AGAINST_GRL;
unknown's avatar
unknown committed
221
  sql_command_flags[SQLCOM_DELETE]=         CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
222
                                            CF_REEXECUTION_FRAGILE | CF_PROTECT_AGAINST_GRL;
unknown's avatar
unknown committed
223
  sql_command_flags[SQLCOM_DELETE_MULTI]=   CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
224
                                            CF_REEXECUTION_FRAGILE | CF_PROTECT_AGAINST_GRL;
unknown's avatar
unknown committed
225 226 227 228 229
  sql_command_flags[SQLCOM_REPLACE]=        CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
                                            CF_REEXECUTION_FRAGILE;
  sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_HAS_ROW_COUNT |
                                            CF_REEXECUTION_FRAGILE;
  sql_command_flags[SQLCOM_SELECT]=         CF_REEXECUTION_FRAGILE;
230
  sql_command_flags[SQLCOM_SET_OPTION]=     CF_REEXECUTION_FRAGILE | CF_AUTO_COMMIT_TRANS;
231 232 233 234 235 236 237 238
  sql_command_flags[SQLCOM_DO]=             CF_REEXECUTION_FRAGILE;

  sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
  sql_command_flags[SQLCOM_SHOW_STATUS]=      CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
  sql_command_flags[SQLCOM_SHOW_DATABASES]=   CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
  sql_command_flags[SQLCOM_SHOW_TRIGGERS]=    CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
  sql_command_flags[SQLCOM_SHOW_EVENTS]=      CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
  sql_command_flags[SQLCOM_SHOW_OPEN_TABLES]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
239
  sql_command_flags[SQLCOM_SHOW_PLUGINS]=     CF_STATUS_COMMAND;
240 241 242 243 244
  sql_command_flags[SQLCOM_SHOW_FIELDS]=      CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
  sql_command_flags[SQLCOM_SHOW_KEYS]=        CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
  sql_command_flags[SQLCOM_SHOW_VARIABLES]=   CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
  sql_command_flags[SQLCOM_SHOW_CHARSETS]=    CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
  sql_command_flags[SQLCOM_SHOW_COLLATIONS]=  CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
245 246
  sql_command_flags[SQLCOM_SHOW_NEW_MASTER]=  CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_BINLOGS]=     CF_STATUS_COMMAND;
247 248 249
  sql_command_flags[SQLCOM_SHOW_SLAVE_HOSTS]= CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_BINLOG_EVENTS]= CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_STORAGE_ENGINES]= CF_STATUS_COMMAND;
250
  sql_command_flags[SQLCOM_SHOW_AUTHORS]=     CF_STATUS_COMMAND;
251
  sql_command_flags[SQLCOM_SHOW_CONTRIBUTORS]= CF_STATUS_COMMAND;
252 253 254
  sql_command_flags[SQLCOM_SHOW_PRIVILEGES]=  CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_WARNS]=       CF_STATUS_COMMAND | CF_DIAGNOSTIC_STMT;
  sql_command_flags[SQLCOM_SHOW_ERRORS]=      CF_STATUS_COMMAND | CF_DIAGNOSTIC_STMT;
255 256 257 258
  sql_command_flags[SQLCOM_SHOW_ENGINE_STATUS]= CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_ENGINE_MUTEX]= CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_ENGINE_LOGS]= CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND;
259 260
  sql_command_flags[SQLCOM_SHOW_GRANTS]=      CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_CREATE_DB]=   CF_STATUS_COMMAND;
261
  sql_command_flags[SQLCOM_SHOW_CREATE]=  CF_STATUS_COMMAND;
262
  sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND;
263
  sql_command_flags[SQLCOM_SHOW_SLAVE_STAT]=  CF_STATUS_COMMAND;
264 265
  sql_command_flags[SQLCOM_SHOW_CREATE_PROC]= CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_CREATE_FUNC]= CF_STATUS_COMMAND;
266
  sql_command_flags[SQLCOM_SHOW_CREATE_TRIGGER]=  CF_STATUS_COMMAND;
267 268 269 270 271 272 273
  sql_command_flags[SQLCOM_SHOW_STATUS_FUNC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
  sql_command_flags[SQLCOM_SHOW_PROC_CODE]=   CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_FUNC_CODE]=   CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_PROFILES]=    CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_PROFILE]=     CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_BINLOG_BASE64_EVENT]= CF_STATUS_COMMAND;
274 275

   sql_command_flags[SQLCOM_SHOW_TABLES]=       (CF_STATUS_COMMAND |
276 277
                                                 CF_SHOW_TABLE_COMMAND |
                                                 CF_REEXECUTION_FRAGILE);
278
  sql_command_flags[SQLCOM_SHOW_TABLE_STATUS]= (CF_STATUS_COMMAND |
279 280
                                                CF_SHOW_TABLE_COMMAND |
                                                CF_REEXECUTION_FRAGILE);
281

282

Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
283 284 285
  sql_command_flags[SQLCOM_CREATE_USER]=       CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL;
  sql_command_flags[SQLCOM_RENAME_USER]=       CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL;
  sql_command_flags[SQLCOM_DROP_USER]=         CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL;
286 287
  sql_command_flags[SQLCOM_GRANT]=             CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_REVOKE]=            CF_CHANGES_DATA;
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
288
  sql_command_flags[SQLCOM_REVOKE_ALL]=        CF_PROTECT_AGAINST_GRL;
289
  sql_command_flags[SQLCOM_OPTIMIZE]=          CF_CHANGES_DATA;
Konstantin Osipov's avatar
Konstantin Osipov committed
290 291 292 293 294 295 296
  sql_command_flags[SQLCOM_CREATE_FUNCTION]=   CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_CREATE_PROCEDURE]=  CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_DROP_PROCEDURE]=    CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_DROP_FUNCTION]=     CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_ALTER_PROCEDURE]=   CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_ALTER_FUNCTION]=    CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
297 298 299
  sql_command_flags[SQLCOM_INSTALL_PLUGIN]=    CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]=  CF_CHANGES_DATA;

300 301 302 303 304 305
  /*
    The following is used to preserver CF_ROW_COUNT during the
    a CALL or EXECUTE statement, so the value generated by the
    last called (or executed) statement is preserved.
    See mysql_execute_command() for how CF_ROW_COUNT is used.
  */
306 307
  sql_command_flags[SQLCOM_CALL]=      CF_HAS_ROW_COUNT | CF_REEXECUTION_FRAGILE;
  sql_command_flags[SQLCOM_EXECUTE]=   CF_HAS_ROW_COUNT;
308 309 310 311 312

  /*
    The following admin table operations are allowed
    on log tables.
  */
313 314 315 316 317 318 319 320 321 322 323
  sql_command_flags[SQLCOM_REPAIR]=    CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_OPTIMIZE]|= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_ANALYZE]=   CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS;

  sql_command_flags[SQLCOM_CREATE_USER]|=       CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_DROP_USER]|=         CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_RENAME_USER]|=       CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_REVOKE_ALL]=         CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_REVOKE]|=            CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_GRANT]|=             CF_AUTO_COMMIT_TRANS;

Konstantin Osipov's avatar
Konstantin Osipov committed
324 325 326 327 328
  sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]= CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_PRELOAD_KEYS]=       CF_AUTO_COMMIT_TRANS;

  sql_command_flags[SQLCOM_FLUSH]=              CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_CHECK]=              CF_AUTO_COMMIT_TRANS;
329 330
}

331

unknown's avatar
unknown committed
332 333
bool is_update_query(enum enum_sql_command command)
{
unknown's avatar
unknown committed
334
  DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
335
  return (sql_command_flags[command] & CF_CHANGES_DATA) != 0;
unknown's avatar
unknown committed
336
}
337

338 339 340 341 342 343 344 345 346 347
/**
  Check if a sql command is allowed to write to log tables.
  @param command The SQL command
  @return true if writing is allowed
*/
bool is_log_table_write_query(enum enum_sql_command command)
{
  DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
  return (sql_command_flags[command] & CF_WRITE_LOGS_COMMAND) != 0;
}
348

349
void execute_init_command(THD *thd, LEX_STRING *init_command,
Marc Alff's avatar
Marc Alff committed
350
                          mysql_rwlock_t *var_lock)
unknown's avatar
unknown committed
351 352 353 354
{
  Vio* save_vio;
  ulong save_client_capabilities;

Marc Alff's avatar
Marc Alff committed
355
  mysql_rwlock_rdlock(var_lock);
356 357
  if (!init_command->length)
  {
Marc Alff's avatar
Marc Alff committed
358
    mysql_rwlock_unlock(var_lock);
359 360 361 362 363 364 365 366 367 368
    return;
  }

  /*
    copy the value under a lock, and release the lock.
    init_command has to be executed without a lock held,
    as it may try to change itself
  */
  size_t len= init_command->length;
  char *buf= thd->strmake(init_command->str, len);
Marc Alff's avatar
Marc Alff committed
369
  mysql_rwlock_unlock(var_lock);
370

371
#if defined(ENABLED_PROFILING)
372
  thd->profiling.start_new_query();
373
  thd->profiling.set_query_source(buf, len);
374 375
#endif

376
  thd_proc_info(thd, "Execution of init_command");
unknown's avatar
unknown committed
377 378
  save_client_capabilities= thd->client_capabilities;
  thd->client_capabilities|= CLIENT_MULTI_QUERIES;
379 380 381 382
  /*
    We don't need return result of execution to client side.
    To forbid this we should set thd->net.vio to 0.
  */
unknown's avatar
unknown committed
383 384
  save_vio= thd->net.vio;
  thd->net.vio= 0;
385
  dispatch_command(COM_QUERY, thd, buf, len);
unknown's avatar
unknown committed
386 387
  thd->client_capabilities= save_client_capabilities;
  thd->net.vio= save_vio;
388

389
#if defined(ENABLED_PROFILING)
390 391
  thd->profiling.finish_current_query();
#endif
unknown's avatar
unknown committed
392 393 394
}


395
static void handle_bootstrap_impl(THD *thd)
unknown's avatar
unknown committed
396
{
Marc Alff's avatar
Marc Alff committed
397
  MYSQL_FILE *file= bootstrap_file;
398
  char *buff;
399
  const char* found_semicolon= NULL;
unknown's avatar
unknown committed
400

401 402
  DBUG_ENTER("handle_bootstrap");

unknown's avatar
unknown committed
403
#ifndef EMBEDDED_LIBRARY
404 405
  pthread_detach_this_thread();
  thd->thread_stack= (char*) &thd;
unknown's avatar
unknown committed
406
#endif /* EMBEDDED_LIBRARY */
unknown's avatar
unknown committed
407

408
  thd_proc_info(thd, 0);
unknown's avatar
unknown committed
409
  thd->version=refresh_version;
410 411
  thd->security_ctx->priv_user=
    thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
unknown's avatar
unknown committed
412
  thd->security_ctx->priv_host[0]=0;
413 414 415 416 417 418
  /*
    Make the "client" handle multiple results. This is necessary
    to enable stored procedures with SELECTs and Dynamic SQL
    in init-file.
  */
  thd->client_capabilities|= CLIENT_MULTI_RESULTS;
unknown's avatar
unknown committed
419

420
  buff= (char*) thd->net.buff;
421
  thd->init_for_queries();
Marc Alff's avatar
Marc Alff committed
422
  while (mysql_file_fgets(buff, thd->net.max_packet, file))
unknown's avatar
unknown committed
423
  {
Marc Alff's avatar
Marc Alff committed
424 425
    char *query;
    /* strlen() can't be deleted because mysql_file_fgets() doesn't return length */
426
    ulong length= (ulong) strlen(buff);
Marc Alff's avatar
Marc Alff committed
427
    while (buff[length-1] != '\n' && !mysql_file_feof(file))
428 429 430 431 432 433 434 435
    {
      /*
        We got only a part of the current string. Will try to increase
        net buffer then read the rest of the current string.
      */
      /* purecov: begin tested */
      if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
      {
436
        thd->protocol->end_statement();
437
        bootstrap_error= 1;
438 439 440
        break;
      }
      buff= (char*) thd->net.buff;
Marc Alff's avatar
Marc Alff committed
441
      mysql_file_fgets(buff + length, thd->net.max_packet - length, file);
442 443 444
      length+= (ulong) strlen(buff + length);
      /* purecov: end */
    }
445
    if (bootstrap_error)
446
      break;                                    /* purecov: inspected */
unknown's avatar
unknown committed
447

unknown's avatar
unknown committed
448
    while (length && (my_isspace(thd->charset(), buff[length-1]) ||
449
                      buff[length-1] == ';'))
unknown's avatar
unknown committed
450 451
      length--;
    buff[length]=0;
unknown's avatar
unknown committed
452 453 454 455 456

    /* Skip lines starting with delimiter */
    if (strncmp(buff, STRING_WITH_LEN("delimiter")) == 0)
      continue;

Gleb Shchepa's avatar
Gleb Shchepa committed
457 458 459
    query= (char *) thd->memdup_w_gap(buff, length + 1,
                                      thd->db_length + 1 +
                                      QUERY_CACHE_FLAGS_SIZE);
460
    thd->set_query_and_id(query, length, next_query_id());
461
    DBUG_PRINT("query",("%-.4096s",thd->query()));
462
#if defined(ENABLED_PROFILING)
463
    thd->profiling.start_new_query();
464
    thd->profiling.set_query_source(thd->query(), length);
465 466
#endif

467 468 469 470
    /*
      We don't need to obtain LOCK_thread_count here because in bootstrap
      mode we have only one thread.
    */
471
    thd->set_time();
472
    mysql_parse(thd, thd->query(), length, & found_semicolon);
unknown's avatar
unknown committed
473
    close_thread_tables(thd);			// Free tables
474

475
    bootstrap_error= thd->is_error();
476
    thd->protocol->end_statement();
477

478
#if defined(ENABLED_PROFILING)
479 480 481
    thd->profiling.finish_current_query();
#endif

482
    if (bootstrap_error)
483 484
      break;

unknown's avatar
unknown committed
485
    free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
486
    free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC));
unknown's avatar
unknown committed
487
  }
488

489 490 491 492 493 494 495 496 497 498 499 500 501 502
  DBUG_VOID_RETURN;
}


/**
  Execute commands from bootstrap_file.

  Used when creating the initial grant tables.
*/

pthread_handler_t handle_bootstrap(void *arg)
{
  THD *thd=(THD*) arg;

Marc Alff's avatar
Marc Alff committed
503 504 505 506 507 508 509 510
  mysql_thread_set_psi_id(thd->thread_id);

  do_handle_bootstrap(thd);
  return 0;
}

void do_handle_bootstrap(THD *thd)
{
511 512 513 514 515 516 517 518 519 520 521 522 523
  /* The following must be called before DBUG_ENTER */
  thd->thread_stack= (char*) &thd;
  if (my_thread_init() || thd->store_globals())
  {
#ifndef EMBEDDED_LIBRARY
    close_connection(thd, ER_OUT_OF_RESOURCES, 1);
#endif
    thd->fatal_error();
    goto end;
  }

  handle_bootstrap_impl(thd);

524
end:
525 526 527 528
  net_end(&thd->net);
  thd->cleanup();
  delete thd;

unknown's avatar
unknown committed
529
#ifndef EMBEDDED_LIBRARY
Marc Alff's avatar
Marc Alff committed
530
  mysql_mutex_lock(&LOCK_thread_count);
531
  thread_count--;
532
  in_bootstrap= FALSE;
Marc Alff's avatar
Marc Alff committed
533 534
  mysql_cond_broadcast(&COND_thread_count);
  mysql_mutex_unlock(&LOCK_thread_count);
535 536
  my_thread_end();
  pthread_exit(0);
unknown's avatar
unknown committed
537
#endif
538

Marc Alff's avatar
Marc Alff committed
539
  return;
unknown's avatar
unknown committed
540 541 542
}


543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
/**
  @brief Check access privs for a MERGE table and fix children lock types.

  @param[in]        thd         thread handle
  @param[in]        db          database name
  @param[in,out]    table_list  list of child tables (merge_list)
                                lock_type and optionally db set per table

  @return           status
    @retval         0           OK
    @retval         != 0        Error

  @detail
    This function is used for write access to MERGE tables only
    (CREATE TABLE, ALTER TABLE ... UNION=(...)). Set TL_WRITE for
    every child. Set 'db' for every child if not present.
*/
unknown's avatar
unknown committed
560
#ifndef NO_EMBEDDED_ACCESS_CHECKS
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
static bool check_merge_table_access(THD *thd, char *db,
                                     TABLE_LIST *table_list)
{
  int error= 0;

  if (table_list)
  {
    /* Check that all tables use the current database */
    TABLE_LIST *tlist;

    for (tlist= table_list; tlist; tlist= tlist->next_local)
    {
      if (!tlist->db || !tlist->db[0])
        tlist->db= db; /* purecov: inspected */
    }
    error= check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
577
                              table_list, FALSE, UINT_MAX, FALSE);
578 579 580
  }
  return error;
}
unknown's avatar
unknown committed
581
#endif
582

unknown's avatar
unknown committed
583 584 585 586 587 588 589 590 591 592 593 594 595 596
/* This works because items are allocated with sql_alloc() */

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

597 598 599 600
/**
   This works because items are allocated with sql_alloc().
   @note The function also handles null pointers (empty list).
*/
601 602
void cleanup_items(Item *item)
{
unknown's avatar
unknown committed
603
  DBUG_ENTER("cleanup_items");  
604 605
  for (; item ; item=item->next)
    item->cleanup();
unknown's avatar
unknown committed
606
  DBUG_VOID_RETURN;
607 608
}

609
#ifndef EMBEDDED_LIBRARY
610

unknown's avatar
unknown committed
611
/**
unknown's avatar
unknown committed
612
  Read one command from connection and execute it (query or simple command).
613
  This function is called in loop from thread function.
614 615 616

  For profiling to work, it must never be called recursively.

unknown's avatar
unknown committed
617
  @retval
618
    0  success
unknown's avatar
unknown committed
619
  @retval
620 621 622
    1  request of thread shutdown (see dispatch_command() description)
*/

unknown's avatar
unknown committed
623 624
bool do_command(THD *thd)
{
625
  bool return_value;
unknown's avatar
unknown committed
626
  char *packet= 0;
unknown's avatar
unknown committed
627
  ulong packet_length;
unknown's avatar
unknown committed
628
  NET *net= &thd->net;
unknown's avatar
unknown committed
629 630 631
  enum enum_server_command command;
  DBUG_ENTER("do_command");

unknown's avatar
unknown committed
632 633 634 635
  /*
    indicator of uninitialized lex => normal flow of errors handling
    (see my_message_sql)
  */
636
  thd->lex->current_select= 0;
unknown's avatar
unknown committed
637

unknown's avatar
unknown committed
638 639 640 641
  /*
    This thread will do a blocking read from the client which
    will be interrupted when the next command is received from
    the client, the connection is closed or "net_wait_timeout"
642
    number of seconds has passed.
unknown's avatar
unknown committed
643
  */
644
  my_net_set_read_timeout(net, thd->variables.net_wait_timeout);
unknown's avatar
unknown committed
645

646 647 648 649
  /*
    XXX: this code is here only to clear possible errors of init_connect. 
    Consider moving to init_connect() instead.
  */
unknown's avatar
unknown committed
650
  thd->clear_error();				// Clear error message
Marc Alff's avatar
Marc Alff committed
651
  thd->stmt_da->reset_diagnostics_area();
unknown's avatar
unknown committed
652 653

  net_new_transaction(net);
654

Konstantin Osipov's avatar
Konstantin Osipov committed
655
  if ((packet_length= my_net_read(net)) == packet_error)
unknown's avatar
unknown committed
656
  {
657 658 659
    DBUG_PRINT("info",("Got error %d reading command from socket %s",
		       net->error,
		       vio_description(net->vio)));
660

661
    /* Check if we can continue without closing the connection */
662

663 664
    /* The error must be set. */
    DBUG_ASSERT(thd->is_error());
665
    thd->protocol->end_statement();
666

667
    if (net->error != 3)
668
    {
669
      return_value= TRUE;                       // We have to close it.
670 671
      goto out;
    }
672

673
    net->error= 0;
674 675
    return_value= FALSE;
    goto out;
unknown's avatar
unknown committed
676
  }
677 678 679 680 681 682 683 684 685 686 687

  packet= (char*) net->read_pos;
  /*
    'packet_length' contains length of data, as it was stored in packet
    header. In case of malformed header, my_net_read returns zero.
    If packet_length is not zero, my_net_read ensures that the returned
    number of bytes was actually read from network.
    There is also an extra safety measure in my_net_read:
    it sets packet[packet_length]= 0, but only for non-zero packets.
  */
  if (packet_length == 0)                       /* safety */
unknown's avatar
unknown committed
688
  {
689 690 691
    /* Initialize with COM_SLEEP packet */
    packet[0]= (uchar) COM_SLEEP;
    packet_length= 1;
unknown's avatar
unknown committed
692
  }
693 694 695 696 697 698 699 700 701 702 703
  /* Do not rely on my_net_read, extra safety against programming errors. */
  packet[packet_length]= '\0';                  /* safety */

  command= (enum enum_server_command) (uchar) packet[0];

  if (command >= COM_END)
    command= COM_END;				// Wrong command

  DBUG_PRINT("info",("Command on %s = %d (%s)",
                     vio_description(net->vio), command,
                     command_name[command].str));
unknown's avatar
unknown committed
704 705

  /* Restore read timeout value */
706
  my_net_set_read_timeout(net, thd->variables.net_read_timeout);
unknown's avatar
unknown committed
707

708
  DBUG_ASSERT(packet_length);
709 710 711 712
  return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));

out:
  DBUG_RETURN(return_value);
713
}
714
#endif  /* EMBEDDED_LIBRARY */
715

716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
/**
  @brief Determine if an attempt to update a non-temporary table while the
    read-only option was enabled has been made.

  This is a helper function to mysql_execute_command.

  @note SQLCOM_MULTI_UPDATE is an exception and delt with elsewhere.

  @see mysql_execute_command
  @returns Status code
    @retval TRUE The statement should be denied.
    @retval FALSE The statement isn't updating any relevant tables.
*/

static my_bool deny_updates_if_read_only_option(THD *thd,
                                                TABLE_LIST *all_tables)
{
  DBUG_ENTER("deny_updates_if_read_only_option");

  if (!opt_readonly)
    DBUG_RETURN(FALSE);

  LEX *lex= thd->lex;

  const my_bool user_is_super=
    ((ulong)(thd->security_ctx->master_access & SUPER_ACL) ==
     (ulong)SUPER_ACL);

  if (user_is_super)
    DBUG_RETURN(FALSE);

747
  if (!(sql_command_flags[lex->sql_command] & CF_CHANGES_DATA))
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
    DBUG_RETURN(FALSE);

  /* Multi update is an exception and is dealt with later. */
  if (lex->sql_command == SQLCOM_UPDATE_MULTI)
    DBUG_RETURN(FALSE);

  const my_bool create_temp_tables= 
    (lex->sql_command == SQLCOM_CREATE_TABLE) &&
    (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE);

  const my_bool drop_temp_tables= 
    (lex->sql_command == SQLCOM_DROP_TABLE) &&
    lex->drop_temporary;

  const my_bool update_real_tables=
    some_non_temp_table_to_be_updated(thd, all_tables) &&
    !(create_temp_tables || drop_temp_tables);


  const my_bool create_or_drop_databases=
    (lex->sql_command == SQLCOM_CREATE_DB) ||
    (lex->sql_command == SQLCOM_DROP_DB);

  if (update_real_tables || create_or_drop_databases)
  {
      /*
        An attempt was made to modify one or more non-temporary tables.
      */
      DBUG_RETURN(TRUE);
  }


  /* Assuming that only temporary tables are modified. */
  DBUG_RETURN(FALSE);
}
783

unknown's avatar
unknown committed
784 785
/**
  Perform one connection-level (COM_XXXX) command.
786

unknown's avatar
unknown committed
787 788 789 790 791 792 793 794
  @param command         type of command to perform
  @param thd             connection handle
  @param packet          data for the command, packet is always null-terminated
  @param packet_length   length of packet + 1 (to show that data is
                         null-terminated) except for COM_SLEEP, where it
                         can be zero.

  @todo
795 796 797
    set thd->lex->sql_command to SQLCOM_END here.
  @todo
    The following has to be changed to an 8 byte integer
unknown's avatar
unknown committed
798 799

  @retval
800
    0   ok
unknown's avatar
unknown committed
801
  @retval
802 803 804
    1   request of thread shutdown, i. e. if command is
        COM_QUIT/COM_SHUTDOWN
*/
805 806 807 808
bool dispatch_command(enum enum_server_command command, THD *thd,
		      char* packet, uint packet_length)
{
  NET *net= &thd->net;
809
  bool error= 0;
810
  DBUG_ENTER("dispatch_command");
811
  DBUG_PRINT("info",("packet: '%*.s'; command: %d", packet_length, packet, command));
812

Konstantin Osipov's avatar
Konstantin Osipov committed
813 814 815
#if defined(ENABLED_PROFILING)
  thd->profiling.start_new_query();
#endif
816 817 818 819
  MYSQL_COMMAND_START(thd->thread_id, command,
                      thd->security_ctx->priv_user,
                      (char *) thd->security_ctx->host_or_ip);
  
820
  thd->command=command;
unknown's avatar
unknown committed
821
  /*
822 823
    Commands which always take a long time are logged into
    the slow log only if opt_log_slow_admin_statements is set.
unknown's avatar
unknown committed
824
  */
825
  thd->enable_slow_log= TRUE;
826
  thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
unknown's avatar
unknown committed
827
  thd->set_time();
828
  thd->set_query_id(get_query_id());
Konstantin Osipov's avatar
Konstantin Osipov committed
829
  if (!(server_command_flags[command] & CF_SKIP_QUERY_ID))
830
    next_query_id();
831
  inc_thread_running();
unknown's avatar
unknown committed
832

Konstantin Osipov's avatar
Konstantin Osipov committed
833 834 835
  if (!(server_command_flags[command] & CF_SKIP_QUESTIONS))
    statistic_increment(thd->status_var.questions, &LOCK_status);

836 837 838 839 840
  /**
    Clear the set of flags that are expected to be cleared at the
    beginning of each command.
  */
  thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
841
  switch (command) {
unknown's avatar
unknown committed
842
  case COM_INIT_DB:
unknown's avatar
unknown committed
843 844
  {
    LEX_STRING tmp;
845
    status_var_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB]);
unknown's avatar
unknown committed
846
    thd->convert_string(&tmp, system_charset_info,
847
			packet, packet_length, thd->charset());
unknown's avatar
unknown committed
848
    if (!mysql_change_db(thd, &tmp, FALSE))
849
    {
850
      general_log_write(thd, command, thd->db, thd->db_length);
851
      my_ok(thd);
852
    }
unknown's avatar
unknown committed
853 854
    break;
  }
unknown's avatar
unknown committed
855
#ifdef HAVE_REPLICATION
856 857
  case COM_REGISTER_SLAVE:
  {
858
    if (!register_slave(thd, (uchar*)packet, packet_length))
859
      my_ok(thd);
860 861
    break;
  }
862
#endif
unknown's avatar
unknown committed
863 864
  case COM_CHANGE_USER:
  {
865
    status_var_increment(thd->status_var.com_other);
866 867
    char *user= (char*) packet, *packet_end= packet + packet_length;
    /* Safe because there is always a trailing \0 at the end of the packet */
868 869
    char *passwd= strend(user)+1;

unknown's avatar
unknown committed
870
    thd->change_user();
871
    thd->clear_error();                         // if errors from rollback
unknown's avatar
unknown committed
872

873
    /*
unknown's avatar
unknown committed
874 875 876
      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).
877 878 879

      Cast *passwd to an unsigned char, so that it doesn't extend the sign
      for *passwd > 127 and become 2**32-127 after casting to uint.
unknown's avatar
unknown committed
880
    */
881
    char db_buff[NAME_LEN+1];                 // buffer to store db in utf8
unknown's avatar
unknown committed
882
    char *db= passwd;
883
    char *save_db;
884 885 886 887 888 889 890 891 892
    /*
      If there is no password supplied, the packet must contain '\0',
      in any type of handshake (4.1 or pre-4.1).
     */
    if (passwd >= packet_end)
    {
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
      break;
    }
893
    uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
894
                      (uchar)(*passwd++) : strlen(passwd));
895 896
    uint dummy_errors, save_db_length, db_length;
    int res;
897 898 899
    Security_context save_security_ctx= *thd->security_ctx;
    USER_CONN *save_user_connect;

unknown's avatar
unknown committed
900
    db+= passwd_len + 1;
901 902 903 904 905
    /*
      Database name is always NUL-terminated, so in case of empty database
      the packet must contain at least the trailing '\0'.
    */
    if (db >= packet_end)
906
    {
unknown's avatar
unknown committed
907
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
908 909
      break;
    }
910 911
    db_length= strlen(db);

912
    char *ptr= db + db_length + 1;
913 914
    uint cs_number= 0;

915 916 917 918 919 920 921 922 923 924
    if (ptr < packet_end)
    {
      if (ptr + 2 > packet_end)
      {
        my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
        break;
      }

      cs_number= uint2korr(ptr);
    }
925

926
    /* Convert database name to utf8 */
927
    db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
928
                             system_charset_info, db, db_length,
929
                             thd->charset(), &dummy_errors)]= 0;
930
    db= db_buff;
unknown's avatar
unknown committed
931

932
    /* Save user and privileges */
933 934 935
    save_db_length= thd->db_length;
    save_db= thd->db;
    save_user_connect= thd->user_connect;
936 937

    if (!(thd->security_ctx->user= my_strdup(user, MYF(0))))
938
    {
939
      thd->security_ctx->user= save_security_ctx.user;
unknown's avatar
unknown committed
940
      my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
941 942
      break;
    }
unknown's avatar
unknown committed
943

unknown's avatar
unknown committed
944 945
    /* Clear variables that are allocated */
    thd->user_connect= 0;
946
    thd->security_ctx->priv_user= thd->security_ctx->user;
947
    res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE);
unknown's avatar
unknown committed
948

949 950
    if (res)
    {
951 952
      x_free(thd->security_ctx->user);
      *thd->security_ctx= save_security_ctx;
unknown's avatar
unknown committed
953
      thd->user_connect= save_user_connect;
954 955 956 957 958
      thd->db= save_db;
      thd->db_length= save_db_length;
    }
    else
    {
959
#ifndef NO_EMBEDDED_ACCESS_CHECKS
960
      /* we've authenticated new user */
unknown's avatar
unknown committed
961 962
      if (save_user_connect)
	decrease_user_connections(save_user_connect);
963
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
964 965
      x_free(save_db);
      x_free(save_security_ctx.user);
966 967 968 969 970 971

      if (cs_number)
      {
        thd_init_client_charset(thd, cs_number);
        thd->update_charset();
      }
972
    }
unknown's avatar
unknown committed
973 974
    break;
  }
975
  case COM_STMT_EXECUTE:
unknown's avatar
unknown committed
976
  {
977
    mysqld_stmt_execute(thd, packet, packet_length);
unknown's avatar
unknown committed
978 979
    break;
  }
980
  case COM_STMT_FETCH:
981
  {
982
    mysqld_stmt_fetch(thd, packet, packet_length);
983 984
    break;
  }
985
  case COM_STMT_SEND_LONG_DATA:
unknown's avatar
unknown committed
986
  {
987
    mysql_stmt_get_longdata(thd, packet, packet_length);
unknown's avatar
unknown committed
988 989
    break;
  }
990
  case COM_STMT_PREPARE:
unknown's avatar
unknown committed
991
  {
992
    mysqld_stmt_prepare(thd, packet, packet_length);
unknown's avatar
unknown committed
993 994
    break;
  }
995
  case COM_STMT_CLOSE:
unknown's avatar
unknown committed
996
  {
997
    mysqld_stmt_close(thd, packet);
unknown's avatar
unknown committed
998 999
    break;
  }
1000
  case COM_STMT_RESET:
1001
  {
1002
    mysqld_stmt_reset(thd, packet);
1003 1004
    break;
  }
unknown's avatar
unknown committed
1005 1006
  case COM_QUERY:
  {
1007 1008
    if (alloc_query(thd, packet, packet_length))
      break;					// fatal error is set
1009
    MYSQL_QUERY_START(thd->query(), thd->thread_id,
1010 1011 1012
                      (char *) (thd->db ? thd->db : ""),
                      thd->security_ctx->priv_user,
                      (char *) thd->security_ctx->host_or_ip);
1013
    char *packet_end= thd->query() + thd->query_length();
unknown's avatar
unknown committed
1014
    /* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */
1015
    const char* end_of_stmt= NULL;
1016

1017 1018
    general_log_write(thd, command, thd->query(), thd->query_length());
    DBUG_PRINT("query",("%-.4096s",thd->query()));
1019
#if defined(ENABLED_PROFILING)
1020
    thd->profiling.set_query_source(thd->query(), thd->query_length());
1021
#endif
1022

1023
    mysql_parse(thd, thd->query(), thd->query_length(), &end_of_stmt);
1024

1025
    while (!thd->killed && (end_of_stmt != NULL) && ! thd->is_error())
1026
    {
1027
      char *beginning_of_next_stmt= (char*) end_of_stmt;
1028

1029
      thd->protocol->end_statement();
1030
      query_cache_end_of_result(thd);
1031
      /*
1032 1033
        Multiple queries exits, execute them individually
      */
1034
      close_thread_tables(thd);
1035
      ulong length= (ulong)(packet_end - beginning_of_next_stmt);
1036

1037
      log_slow_statement(thd);
1038

1039
      /* Remove garbage at start of query */
1040
      while (length > 0 && my_isspace(thd->charset(), *beginning_of_next_stmt))
1041
      {
1042
        beginning_of_next_stmt++;
1043 1044
        length--;
      }
1045

1046 1047 1048 1049 1050
      if (MYSQL_QUERY_DONE_ENABLED())
      {
        MYSQL_QUERY_DONE(thd->is_error());
      }

1051
#if defined(ENABLED_PROFILING)
1052 1053 1054 1055 1056
      thd->profiling.finish_current_query();
      thd->profiling.start_new_query("continuing");
      thd->profiling.set_query_source(beginning_of_next_stmt, length);
#endif

1057
      MYSQL_QUERY_START(beginning_of_next_stmt, thd->thread_id,
1058 1059 1060 1061
                        (char *) (thd->db ? thd->db : ""),
                        thd->security_ctx->priv_user,
                        (char *) thd->security_ctx->host_or_ip);

1062
      thd->set_query_and_id(beginning_of_next_stmt, length, next_query_id());
1063 1064 1065 1066
      /*
        Count each statement from the client.
      */
      statistic_increment(thd->status_var.questions, &LOCK_status);
1067
      thd->set_time(); /* Reset the query start time. */
1068
      /* TODO: set thd->lex->sql_command to SQLCOM_END here */
1069
      mysql_parse(thd, beginning_of_next_stmt, length, &end_of_stmt);
1070 1071
    }

unknown's avatar
unknown committed
1072 1073 1074
    DBUG_PRINT("info",("query ready"));
    break;
  }
1075
  case COM_FIELD_LIST:				// This isn't actually needed
unknown's avatar
unknown committed
1076
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
1077 1078
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0));	/* purecov: inspected */
unknown's avatar
unknown committed
1079 1080 1081
    break;
#else
  {
1082
    char *fields, *packet_end= packet + packet_length, *arg_end;
1083
    /* Locked closure of all tables */
unknown's avatar
unknown committed
1084
    TABLE_LIST table_list;
unknown's avatar
unknown committed
1085
    LEX_STRING conv_name;
unknown's avatar
unknown committed
1086

unknown's avatar
unknown committed
1087
    /* used as fields initializator */
1088
    lex_start(thd);
1089

1090
    status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS]);
unknown's avatar
unknown committed
1091
    bzero((char*) &table_list,sizeof(table_list));
1092
    if (thd->copy_db_to(&table_list.db, &table_list.db_length))
unknown's avatar
unknown committed
1093
      break;
1094 1095 1096 1097
    /*
      We have name + wildcard in packet, separated by endzero
    */
    arg_end= strend(packet);
unknown's avatar
unknown committed
1098
    thd->convert_string(&conv_name, system_charset_info,
1099
			packet, (uint) (arg_end - packet), thd->charset());
1100
    table_list.alias= table_list.table_name= conv_name.str;
1101
    packet= arg_end + 1;
1102

1103
    if (is_infoschema_db(table_list.db, table_list.db_length))
1104 1105 1106 1107 1108 1109
    {
      ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, table_list.alias);
      if (schema_table)
        table_list.schema_table= schema_table;
    }

Gleb Shchepa's avatar
Gleb Shchepa committed
1110 1111
    uint query_length= (uint) (packet_end - packet); // Don't count end \0
    if (!(fields= (char *) thd->memdup(packet, query_length + 1)))
unknown's avatar
unknown committed
1112
      break;
1113
    thd->set_query(fields, query_length);
unknown's avatar
unknown committed
1114
    general_log_print(thd, command, "%s %s", table_list.table_name, fields);
1115
    if (lower_case_table_names)
1116
      my_casedn_str(files_charset_info, table_list.table_name);
unknown's avatar
unknown committed
1117

Marc Alff's avatar
Marc Alff committed
1118 1119 1120 1121
    if (check_access(thd, SELECT_ACL, table_list.db,
                     &table_list.grant.privilege,
                     &table_list.grant.m_internal,
                     0, 0))
unknown's avatar
unknown committed
1122
      break;
1123
    if (check_grant(thd, SELECT_ACL, &table_list, TRUE, UINT_MAX, FALSE))
unknown's avatar
unknown committed
1124
      break;
1125 1126
    /* init structures for VIEW processing */
    table_list.select_lex= &(thd->lex->select_lex);
1127 1128 1129 1130

    lex_start(thd);
    mysql_reset_thd_for_next_command(thd);

1131
    thd->lex->
1132 1133
      select_lex.table_list.link_in_list((uchar*) &table_list,
                                         (uchar**) &table_list.next_local);
unknown's avatar
unknown committed
1134
    thd->lex->add_to_query_tables(&table_list);
Konstantin Osipov's avatar
Konstantin Osipov committed
1135
    init_mdl_requests(&table_list);
1136

1137 1138
    /* switch on VIEW optimisation: do not fill temporary tables */
    thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
unknown's avatar
unknown committed
1139
    mysqld_list_fields(thd,&table_list,fields);
1140
    thd->lex->unit.cleanup();
1141
    thd->cleanup_after_query();
unknown's avatar
unknown committed
1142 1143 1144 1145
    break;
  }
#endif
  case COM_QUIT:
1146
    /* We don't calculate statistics for this command */
unknown's avatar
unknown committed
1147
    general_log_print(thd, command, NullS);
unknown's avatar
unknown committed
1148
    net->error=0;				// Don't give 'abort' message
Marc Alff's avatar
Marc Alff committed
1149
    thd->stmt_da->disable_status();              // Don't send anything back
unknown's avatar
unknown committed
1150 1151
    error=TRUE;					// End server
    break;
1152
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
1153 1154
  case COM_BINLOG_DUMP:
    {
unknown's avatar
unknown committed
1155 1156 1157 1158
      ulong pos;
      ushort flags;
      uint32 slave_server_id;

1159
      status_var_increment(thd->status_var.com_other);
1160
      thd->enable_slow_log= opt_log_slow_admin_statements;
unknown's avatar
unknown committed
1161
      if (check_global_access(thd, REPL_SLAVE_ACL))
unknown's avatar
unknown committed
1162
	break;
unknown's avatar
unknown committed
1163

1164
      /* TODO: The following has to be changed to an 8 byte integer */
1165 1166
      pos = uint4korr(packet);
      flags = uint2korr(packet + 4);
unknown's avatar
unknown committed
1167
      thd->server_id=0; /* avoid suicide */
unknown's avatar
unknown committed
1168
      if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
unknown's avatar
unknown committed
1169
	kill_zombie_dump_threads(slave_server_id);
1170
      thd->server_id = slave_server_id;
unknown's avatar
unknown committed
1171

unknown's avatar
unknown committed
1172
      general_log_print(thd, command, "Log: '%s'  Pos: %ld", packet+10,
unknown's avatar
unknown committed
1173
                      (long) pos);
1174
      mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
unknown's avatar
unknown committed
1175
      unregister_slave(thd,1,1);
unknown's avatar
unknown committed
1176
      /*  fake COM_QUIT -- if we get here, the thread needs to terminate */
1177
      error = TRUE;
unknown's avatar
unknown committed
1178 1179
      break;
    }
1180
#endif
unknown's avatar
unknown committed
1181
  case COM_REFRESH:
unknown's avatar
unknown committed
1182 1183
  {
    bool not_used;
1184
    status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]);
unknown's avatar
unknown committed
1185
    ulong options= (ulong) (uchar) packet[0];
Konstantin Osipov's avatar
Konstantin Osipov committed
1186
    if (trans_commit_implicit(thd))
Konstantin Osipov's avatar
Konstantin Osipov committed
1187
      break;
1188
    close_thread_tables(thd);
1189
    thd->mdl_context.release_transactional_locks();
unknown's avatar
unknown committed
1190
    if (check_global_access(thd,RELOAD_ACL))
unknown's avatar
unknown committed
1191
      break;
unknown's avatar
unknown committed
1192
    general_log_print(thd, command, NullS);
1193
#ifndef DBUG_OFF
1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208
    bool debug_simulate= FALSE;
    DBUG_EXECUTE_IF("simulate_detached_thread_refresh", debug_simulate= TRUE;);
    if (debug_simulate)
    {
      /*
        Simulate a reload without a attached thread session.
        Provides a environment similar to that of when the
        server receives a SIGHUP signal and reloads caches
        and flushes tables.
      */
      bool res;
      my_pthread_setspecific_ptr(THR_THD, NULL);
      res= reload_acl_and_cache(NULL, options | REFRESH_FAST,
                                NULL, &not_used);
      my_pthread_setspecific_ptr(THR_THD, thd);
Konstantin Osipov's avatar
Konstantin Osipov committed
1209 1210
      if (res)
        break;
1211
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
1212
    else
1213
#endif
Konstantin Osipov's avatar
Konstantin Osipov committed
1214 1215
    if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
      break;
Konstantin Osipov's avatar
Konstantin Osipov committed
1216
    if (trans_commit_implicit(thd))
Konstantin Osipov's avatar
Konstantin Osipov committed
1217
      break;
1218
    close_thread_tables(thd);
1219
    thd->mdl_context.release_transactional_locks();
Konstantin Osipov's avatar
Konstantin Osipov committed
1220
    my_ok(thd);
unknown's avatar
unknown committed
1221 1222
    break;
  }
1223
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
1224
  case COM_SHUTDOWN:
1225
  {
1226
    status_var_increment(thd->status_var.com_other);
unknown's avatar
unknown committed
1227
    if (check_global_access(thd,SHUTDOWN_ACL))
unknown's avatar
unknown committed
1228
      break; /* purecov: inspected */
1229
    /*
1230
      If the client is < 4.1.3, it is going to send us no argument; then
1231
      packet_length is 0, packet[0] is the end 0 of the packet. Note that
1232 1233
      SHUTDOWN_DEFAULT is 0. If client is >= 4.1.3, the shutdown level is in
      packet[0].
1234
    */
1235 1236
    enum mysql_enum_shutdown_level level=
      (enum mysql_enum_shutdown_level) (uchar) packet[0];
1237 1238 1239 1240 1241 1242 1243
    if (level == SHUTDOWN_DEFAULT)
      level= SHUTDOWN_WAIT_ALL_BUFFERS; // soon default will be configurable
    else if (level != SHUTDOWN_WAIT_ALL_BUFFERS)
    {
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "this shutdown level");
      break;
    }
1244
    DBUG_PRINT("quit",("Got shutdown command for level %u", level));
unknown's avatar
unknown committed
1245
    general_log_print(thd, command, NullS);
1246
    my_eof(thd);
unknown's avatar
unknown committed
1247 1248 1249 1250
    close_thread_tables(thd);			// Free before kill
    kill_mysql();
    error=TRUE;
    break;
1251
  }
1252
#endif
unknown's avatar
unknown committed
1253 1254
  case COM_STATISTICS:
  {
1255 1256 1257
    STATUS_VAR current_global_status_var;
    ulong uptime;
    uint length;
1258
    ulonglong queries_per_second1000;
1259 1260
    char buff[250];
    uint buff_len= sizeof(buff);
1261

1262
    general_log_print(thd, command, NullS);
1263
    status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS]);
1264
    calc_sum_of_all_status(&current_global_status_var);
1265 1266 1267 1268 1269
    if (!(uptime= (ulong) (thd->start_time - server_start_time)))
      queries_per_second1000= 0;
    else
      queries_per_second1000= thd->query_id * LL(1000) / uptime;

1270 1271 1272
    length= my_snprintf((char*) buff, buff_len - 1,
                        "Uptime: %lu  Threads: %d  Questions: %lu  "
                        "Slow queries: %lu  Opens: %lu  Flush tables: %lu  "
1273
                        "Open tables: %u  Queries per second avg: %u.%u",
1274 1275 1276 1277 1278 1279
                        uptime,
                        (int) thread_count, (ulong) thd->query_id,
                        current_global_status_var.long_query_count,
                        current_global_status_var.opened_tables,
                        refresh_version,
                        cached_open_tables(),
1280 1281
                        (uint) (queries_per_second1000 / 1000),
                        (uint) (queries_per_second1000 % 1000));
1282 1283
#ifdef EMBEDDED_LIBRARY
    /* Store the buffer in permanent memory */
1284
    my_ok(thd, 0, 0, buff);
1285
#endif
unknown's avatar
unknown committed
1286
#ifdef SAFEMALLOC
1287
    if (sf_malloc_cur_memory)				// Using SAFEMALLOC
1288 1289 1290 1291 1292 1293 1294
    {
      char *end= buff + length;
      length+= my_snprintf(end, buff_len - length - 1,
                           end,"  Memory in use: %ldK  Max memory used: %ldK",
                           (sf_malloc_cur_memory+1023L)/1024L,
                           (sf_malloc_max_memory+1023L)/1024L);
    }
unknown's avatar
unknown committed
1295 1296
#endif
#ifndef EMBEDDED_LIBRARY
Konstantin Osipov's avatar
Konstantin Osipov committed
1297 1298
    (void) my_net_write(net, (uchar*) buff, length);
    (void) net_flush(net);
Marc Alff's avatar
Marc Alff committed
1299
    thd->stmt_da->disable_status();
unknown's avatar
unknown committed
1300
#endif
unknown's avatar
unknown committed
1301 1302 1303
    break;
  }
  case COM_PING:
1304
    status_var_increment(thd->status_var.com_other);
1305
    my_ok(thd);				// Tell client we are alive
unknown's avatar
unknown committed
1306 1307
    break;
  case COM_PROCESS_INFO:
1308
    status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST]);
1309 1310
    if (!thd->security_ctx->priv_user[0] &&
        check_global_access(thd, PROCESS_ACL))
unknown's avatar
unknown committed
1311
      break;
unknown's avatar
unknown committed
1312
    general_log_print(thd, command, NullS);
unknown's avatar
unknown committed
1313
    mysqld_list_processes(thd,
1314 1315
			  thd->security_ctx->master_access & PROCESS_ACL ? 
			  NullS : thd->security_ctx->priv_user, 0);
unknown's avatar
unknown committed
1316 1317 1318
    break;
  case COM_PROCESS_KILL:
  {
1319
    status_var_increment(thd->status_var.com_stat[SQLCOM_KILL]);
1320
    ulong id=(ulong) uint4korr(packet);
1321
    sql_kill(thd,id,false);
unknown's avatar
unknown committed
1322 1323
    break;
  }
1324 1325
  case COM_SET_OPTION:
  {
1326
    status_var_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION]);
unknown's avatar
unknown committed
1327 1328 1329 1330
    uint opt_command= uint2korr(packet);

    switch (opt_command) {
    case (int) MYSQL_OPTION_MULTI_STATEMENTS_ON:
1331
      thd->client_capabilities|= CLIENT_MULTI_STATEMENTS;
1332
      my_eof(thd);
1333
      break;
unknown's avatar
unknown committed
1334
    case (int) MYSQL_OPTION_MULTI_STATEMENTS_OFF:
1335
      thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS;
1336
      my_eof(thd);
1337 1338
      break;
    default:
unknown's avatar
unknown committed
1339
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
1340 1341 1342 1343
      break;
    }
    break;
  }
unknown's avatar
unknown committed
1344
  case COM_DEBUG:
1345
    status_var_increment(thd->status_var.com_other);
unknown's avatar
unknown committed
1346
    if (check_global_access(thd, SUPER_ACL))
unknown's avatar
unknown committed
1347
      break;					/* purecov: inspected */
1348
    mysql_print_status();
unknown's avatar
unknown committed
1349
    general_log_print(thd, command, NullS);
1350
    my_eof(thd);
unknown's avatar
unknown committed
1351 1352 1353 1354 1355
    break;
  case COM_SLEEP:
  case COM_CONNECT:				// Impossible here
  case COM_TIME:				// Impossible from client
  case COM_DELAYED_INSERT:
1356
  case COM_END:
unknown's avatar
unknown committed
1357
  default:
unknown's avatar
unknown committed
1358
    my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
unknown's avatar
unknown committed
1359 1360
    break;
  }
1361

unknown's avatar
unknown committed
1362
  /* report error issued during command execution */
1363 1364
  if (thd->killed_errno())
  {
Marc Alff's avatar
Marc Alff committed
1365
    if (! thd->stmt_da->is_set())
1366 1367 1368 1369 1370 1371 1372 1373
      thd->send_kill_message();
  }
  if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA)
  {
    thd->killed= THD::NOT_KILLED;
    thd->mysys_var->abort= 0;
  }

1374
  /* If commit fails, we should be able to reset the OK status. */
Marc Alff's avatar
Marc Alff committed
1375
  thd->stmt_da->can_overwrite_status= TRUE;
Konstantin Osipov's avatar
Konstantin Osipov committed
1376
  thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
Marc Alff's avatar
Marc Alff committed
1377
  thd->stmt_da->can_overwrite_status= FALSE;
1378 1379 1380

  thd->transaction.stmt.reset();

1381
  thd->protocol->end_statement();
1382
  query_cache_end_of_result(thd);
unknown's avatar
unknown committed
1383

1384 1385 1386 1387
  thd->proc_info= "closing tables";
  /* Free tables */
  close_thread_tables(thd);

1388
  if (!thd->is_error() && !thd->killed_errno())
1389
    mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0);
1390

1391
  log_slow_statement(thd);
1392

1393
  thd_proc_info(thd, "cleaning up");
1394
  thd->set_query(NULL, 0);
1395
  thd->command=COM_SLEEP;
1396
  dec_thread_running();
1397
  thd_proc_info(thd, 0);
1398 1399
  thd->packet.shrink(thd->variables.net_buffer_length);	// Reclaim some memory
  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
1400

Konstantin Osipov's avatar
Konstantin Osipov committed
1401 1402 1403
#if defined(ENABLED_PROFILING)
  thd->profiling.finish_current_query();
#endif
1404 1405 1406 1407 1408 1409 1410 1411 1412 1413
  if (MYSQL_QUERY_DONE_ENABLED() || MYSQL_COMMAND_DONE_ENABLED())
  {
    int res;
    res= (int) thd->is_error();
    if (command == COM_QUERY)
    {
      MYSQL_QUERY_DONE(res);
    }
    MYSQL_COMMAND_DONE(res);
  }
1414 1415 1416 1417
  DBUG_RETURN(error);
}


1418
void log_slow_statement(THD *thd)
1419
{
unknown's avatar
unknown committed
1420
  DBUG_ENTER("log_slow_statement");
1421 1422 1423 1424 1425 1426 1427

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

1430 1431
  /*
    Do not log administrative statements unless the appropriate option is
1432
    set.
1433
  */
1434
  if (thd->enable_slow_log)
unknown's avatar
unknown committed
1435
  {
1436
    ulonglong end_utime_of_query= thd->current_utime();
1437
    thd_proc_info(thd, "logging slow query");
1438

1439 1440 1441 1442
    if (((end_utime_of_query - thd->utime_after_lock) >
         thd->variables.long_query_time ||
         ((thd->server_status &
           (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
1443 1444
          opt_log_queries_not_using_indexes &&
           !(sql_command_flags[thd->lex->sql_command] & CF_STATUS_COMMAND))) &&
1445
        thd->examined_row_count >= thd->variables.min_examined_row_limit)
1446
    {
1447
      thd_proc_info(thd, "logging slow query");
1448
      thd->status_var.long_query_count++;
1449 1450
      slow_log_print(thd, thd->query(), thd->query_length(), 
                     end_utime_of_query);
1451
    }
unknown's avatar
unknown committed
1452
  }
unknown's avatar
unknown committed
1453
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1454 1455
}

1456

unknown's avatar
unknown committed
1457
/**
unknown's avatar
unknown committed
1458 1459 1460 1461 1462 1463 1464
  Create a TABLE_LIST object for an INFORMATION_SCHEMA table.

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

unknown's avatar
unknown committed
1465 1466 1467 1468 1469 1470 1471
  @param thd              thread handle
  @param lex              current lex
  @param table_ident      table alias if it's used
  @param schema_table_idx the type of the INFORMATION_SCHEMA table to be
                          created

  @note
unknown's avatar
unknown committed
1472 1473 1474 1475
    Due to the way this function works with memory and LEX it cannot
    be used outside the parser (parse tree transformations outside
    the parser break PS and SP).

unknown's avatar
unknown committed
1476
  @retval
unknown's avatar
unknown committed
1477
    0                 success
unknown's avatar
unknown committed
1478
  @retval
unknown's avatar
unknown committed
1479 1480 1481 1482
    1                 out of memory or SHOW commands are not allowed
                      in this version of the server.
*/

1483 1484 1485
int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
                         enum enum_schema_tables schema_table_idx)
{
1486
  SELECT_LEX *schema_select_lex= NULL;
1487
  DBUG_ENTER("prepare_schema_table");
1488

1489
  switch (schema_table_idx) {
1490 1491
  case SCH_SCHEMATA:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
unknown's avatar
unknown committed
1492 1493
    my_message(ER_NOT_ALLOWED_COMMAND,
               ER(ER_NOT_ALLOWED_COMMAND), MYF(0));   /* purecov: inspected */
1494 1495 1496 1497
    DBUG_RETURN(1);
#else
    break;
#endif
1498

1499 1500 1501
  case SCH_TABLE_NAMES:
  case SCH_TABLES:
  case SCH_VIEWS:
1502
  case SCH_TRIGGERS:
unknown's avatar
unknown committed
1503
  case SCH_EVENTS:
1504
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
1505 1506
    my_message(ER_NOT_ALLOWED_COMMAND,
               ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
1507 1508 1509
    DBUG_RETURN(1);
#else
    {
1510
      LEX_STRING db;
1511
      size_t dummy;
unknown's avatar
unknown committed
1512
      if (lex->select_lex.db == NULL &&
1513
          lex->copy_db_to(&lex->select_lex.db, &dummy))
1514
      {
unknown's avatar
unknown committed
1515
        DBUG_RETURN(1);
1516
      }
1517 1518 1519
      schema_select_lex= new SELECT_LEX();
      db.str= schema_select_lex->db= lex->select_lex.db;
      schema_select_lex->table_list.first= NULL;
1520
      db.length= strlen(db.str);
unknown's avatar
unknown committed
1521

1522
      if (check_db_name(&db))
1523
      {
1524
        my_error(ER_WRONG_DB_NAME, MYF(0), db.str);
1525 1526 1527 1528 1529 1530 1531
        DBUG_RETURN(1);
      }
      break;
    }
#endif
  case SCH_COLUMNS:
  case SCH_STATISTICS:
unknown's avatar
unknown committed
1532
  {
1533
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
1534 1535
    my_message(ER_NOT_ALLOWED_COMMAND,
               ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
1536 1537
    DBUG_RETURN(1);
#else
unknown's avatar
unknown committed
1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548
    DBUG_ASSERT(table_ident);
    TABLE_LIST **query_tables_last= lex->query_tables_last;
    schema_select_lex= new SELECT_LEX();
    /* 'parent_lex' is used in init_query() so it must be before it. */
    schema_select_lex->parent_lex= lex;
    schema_select_lex->init_query();
    if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ))
      DBUG_RETURN(1);
    lex->query_tables_last= query_tables_last;
    break;
  }
1549
#endif
1550 1551 1552 1553 1554
  case SCH_PROFILES:
    /* 
      Mark this current profiling record to be discarded.  We don't
      wish to have SHOW commands show up in profiling.
    */
1555
#if defined(ENABLED_PROFILING)
1556
    thd->profiling.discard_current_query();
1557 1558
#endif
    break;
1559 1560 1561
  case SCH_OPEN_TABLES:
  case SCH_VARIABLES:
  case SCH_STATUS:
1562 1563
  case SCH_PROCEDURES:
  case SCH_CHARSETS:
unknown's avatar
unknown committed
1564
  case SCH_ENGINES:
1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582
  case SCH_COLLATIONS:
  case SCH_COLLATION_CHARACTER_SET_APPLICABILITY:
  case SCH_USER_PRIVILEGES:
  case SCH_SCHEMA_PRIVILEGES:
  case SCH_TABLE_PRIVILEGES:
  case SCH_COLUMN_PRIVILEGES:
  case SCH_TABLE_CONSTRAINTS:
  case SCH_KEY_COLUMN_USAGE:
  default:
    break;
  }
  
  SELECT_LEX *select_lex= lex->current_select;
  if (make_schema_select(thd, select_lex, schema_table_idx))
  {
    DBUG_RETURN(1);
  }
  TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first;
unknown's avatar
unknown committed
1583
  table_list->schema_select_lex= schema_select_lex;
1584
  table_list->schema_table_reformed= 1;
1585 1586 1587 1588
  DBUG_RETURN(0);
}


unknown's avatar
unknown committed
1589 1590 1591
/**
  Read query from packet and store in thd->query.
  Used in COM_QUERY and COM_STMT_PREPARE.
1592 1593

    Sets the following THD variables:
unknown's avatar
unknown committed
1594 1595
  - query
  - query_length
1596

unknown's avatar
unknown committed
1597
  @retval
unknown's avatar
unknown committed
1598
    FALSE ok
unknown's avatar
unknown committed
1599
  @retval
unknown's avatar
unknown committed
1600
    TRUE  error;  In this case thd->fatal_error is set
1601 1602
*/

1603
bool alloc_query(THD *thd, const char *packet, uint packet_length)
1604
{
1605
  char *query;
1606
  /* Remove garbage at start and end of query */
1607
  while (packet_length > 0 && my_isspace(thd->charset(), packet[0]))
1608 1609 1610 1611
  {
    packet++;
    packet_length--;
  }
1612
  const char *pos= packet + packet_length;     // Point at end null
unknown's avatar
unknown committed
1613
  while (packet_length > 0 &&
unknown's avatar
unknown committed
1614
	 (pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1])))
1615 1616 1617 1618 1619
  {
    pos--;
    packet_length--;
  }
  /* We must allocate some extra memory for query cache */
1620 1621 1622 1623 1624 1625 1626
  if (! (query= (char*) thd->memdup_w_gap(packet,
                                          packet_length,
                                          1 + thd->db_length +
                                          QUERY_CACHE_FLAGS_SIZE)))
      return TRUE;
  query[packet_length]= '\0';
  thd->set_query(query, packet_length);
1627 1628 1629 1630

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

unknown's avatar
unknown committed
1632
  return FALSE;
1633 1634
}

1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647
static void reset_one_shot_variables(THD *thd) 
{
  thd->variables.character_set_client=
    global_system_variables.character_set_client;
  thd->variables.collation_connection=
    global_system_variables.collation_connection;
  thd->variables.collation_database=
    global_system_variables.collation_database;
  thd->variables.collation_server=
    global_system_variables.collation_server;
  thd->update_charset();
  thd->variables.time_zone=
    global_system_variables.time_zone;
1648
  thd->variables.lc_time_names= &my_locale_en_US;
1649 1650 1651
  thd->one_shot_set= 0;
}

1652

1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698
static
bool sp_process_definer(THD *thd)
{
  DBUG_ENTER("sp_process_definer");

  LEX *lex= thd->lex;

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

      - The user submitted a statement w/o the clause. This is a normal
        case, we should assign CURRENT_USER as definer.

      - Our slave received an updated from the master, that does not
        replicate definer for stored rountines. We should also assign
        CURRENT_USER as definer here, but also we should mark this routine
        as NON-SUID. This is essential for the sake of backward
        compatibility.

        The problem is the slave thread is running under "special" user (@),
        that actually does not exist. In the older versions we do not fail
        execution of a stored routine if its definer does not exist and
        continue the execution under the authorization of the invoker
        (BUG#13198). And now if we try to switch to slave-current-user (@),
        we will fail.

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

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

    lex->definer= create_default_definer(thd);

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

    /* Error has been already reported. */
    if (lex->definer == NULL)
      DBUG_RETURN(TRUE);

1699
    if (thd->slave_thread && lex->sphead)
1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737
      lex->sphead->m_chistics->suid= SP_IS_NOT_SUID;
  }
  else
  {
    /*
      If the specified definer differs from the current user, we
      should check that the current user has SUPER privilege (in order
      to create a stored routine under another user one must have
      SUPER privilege).
    */
    if ((strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
         my_strcasecmp(system_charset_info, lex->definer->host.str,
                       thd->security_ctx->priv_host)) &&
        check_global_access(thd, SUPER_ACL))
    {
      my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
      DBUG_RETURN(TRUE);
    }
  }

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

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

  DBUG_RETURN(FALSE);
}


unknown's avatar
unknown committed
1738 1739
/**
  Execute command saved in thd and lex->sql_command.
1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750

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

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

unknown's avatar
unknown committed
1751 1752 1753 1754 1755 1756 1757 1758 1759 1760
  @param thd                       Thread handle

  @todo
    - Invalidate the table in the query cache if something changed
    after unlocking when changes become visible.
    TODO: this is workaround. right way will be move invalidating in
    the unlock procedure.
    - TODO: use check_change_password()

  @retval
1761
    FALSE       OK
unknown's avatar
unknown committed
1762
  @retval
1763 1764
    TRUE        Error
*/
unknown's avatar
unknown committed
1765

1766
int
1767
mysql_execute_command(THD *thd)
unknown's avatar
unknown committed
1768
{
1769
  int res= FALSE;
unknown's avatar
unknown committed
1770
  int  up_result= 0;
1771
  LEX  *lex= thd->lex;
unknown's avatar
unknown committed
1772
  /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
unknown's avatar
unknown committed
1773
  SELECT_LEX *select_lex= &lex->select_lex;
unknown's avatar
VIEW  
unknown committed
1774
  /* first table of first SELECT_LEX */
unknown's avatar
unknown committed
1775
  TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first;
unknown's avatar
VIEW  
unknown committed
1776 1777 1778
  /* list of all tables in query */
  TABLE_LIST *all_tables;
  /* most outer SELECT_LEX_UNIT of query */
1779
  SELECT_LEX_UNIT *unit= &lex->unit;
1780 1781 1782 1783
#ifdef HAVE_REPLICATION
  /* have table map for update for multi-update statement (BUG#37051) */
  bool have_table_map_for_update= FALSE;
#endif
1784
  /* Saved variable value */
unknown's avatar
unknown committed
1785
  DBUG_ENTER("mysql_execute_command");
unknown's avatar
unknown committed
1786 1787 1788
#ifdef WITH_PARTITION_STORAGE_ENGINE
  thd->work_part_info= 0;
#endif
unknown's avatar
unknown committed
1789

unknown's avatar
VIEW  
unknown committed
1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805
  /*
    In many cases first table of main SELECT_LEX have special meaning =>
    check that it is first table in global list and relink it first in 
    queries_tables list if it is necessary (we need such relinking only
    for queries with subqueries in select list, in this case tables of
    subqueries will go to global list first)

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

    Because of above in place where should be at least one table in most
    outer SELECT_LEX we have following check:
    DBUG_ASSERT(first_table == all_tables);
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
  */
  lex->first_lists_tables_same();
1806
  /* should be assigned after making first tables same */
unknown's avatar
VIEW  
unknown committed
1807
  all_tables= lex->query_tables;
1808 1809 1810 1811
  /* set context for commands which do not use setup_tables */
  select_lex->
    context.resolve_in_table_list_only((TABLE_LIST*)select_lex->
                                       table_list.first);
unknown's avatar
VIEW  
unknown committed
1812

1813 1814 1815 1816 1817 1818
  /*
    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.
  */
Marc Alff's avatar
Marc Alff committed
1819 1820 1821 1822 1823 1824 1825 1826
  if ((sql_command_flags[lex->sql_command] & CF_DIAGNOSTIC_STMT) != 0)
    thd->warning_info->set_read_only(TRUE);
  else
  {
    thd->warning_info->set_read_only(FALSE);
    if (all_tables)
      thd->warning_info->opt_clear_warning_info(thd->query_id);
  }
1827

unknown's avatar
SCRUM  
unknown committed
1828
#ifdef HAVE_REPLICATION
1829
  if (unlikely(thd->slave_thread))
1830
  {
1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853
    if (lex->sql_command == SQLCOM_DROP_TRIGGER)
    {
      /*
        When dropping a trigger, we need to load its table name
        before checking slave filter rules.
      */
      add_table_for_trigger(thd, thd->lex->spname, 1, &all_tables);
      
      if (!all_tables)
      {
        /*
          If table name cannot be loaded,
          it means the trigger does not exists possibly because
          CREATE TRIGGER was previously skipped for this trigger
          according to slave filtering rules.
          Returning success without producing any errors in this case.
        */
        DBUG_RETURN(0);
      }
      
      // force searching in slave.cc:tables_ok() 
      all_tables->updating= 1;
    }
1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895

    /*
      For fix of BUG#37051, the master stores the table map for update
      in the Query_log_event, and the value is assigned to
      thd->variables.table_map_for_update before executing the update
      query.

      If thd->variables.table_map_for_update is set, then we are
      replicating from a new master, we can use this value to apply
      filter rules without opening all the tables. However If
      thd->variables.table_map_for_update is not set, then we are
      replicating from an old master, so we just skip this and
      continue with the old method. And of course, the bug would still
      exist for old masters.
    */
    if (lex->sql_command == SQLCOM_UPDATE_MULTI &&
        thd->table_map_for_update)
    {
      have_table_map_for_update= TRUE;
      table_map table_map_for_update= thd->table_map_for_update;
      uint nr= 0;
      TABLE_LIST *table;
      for (table=all_tables; table; table=table->next_global, nr++)
      {
        if (table_map_for_update & ((table_map)1 << nr))
          table->updating= TRUE;
        else
          table->updating= FALSE;
      }

      if (all_tables_not_ok(thd, all_tables))
      {
        /* we warn the slave SQL thread */
        my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
        if (thd->one_shot_set)
          reset_one_shot_variables(thd);
        DBUG_RETURN(0);
      }
      
      for (table=all_tables; table; table=table->next_global)
        table->updating= TRUE;
    }
1896
    
unknown's avatar
unknown committed
1897
    /*
unknown's avatar
unknown committed
1898 1899
      Check if statment should be skipped because of slave filtering
      rules
1900 1901

      Exceptions are:
unknown's avatar
unknown committed
1902 1903
      - UPDATE MULTI: For this statement, we want to check the filtering
        rules later in the code
1904
      - SET: we always execute it (Not that many SET commands exists in
unknown's avatar
unknown committed
1905 1906
        the binary log anyway -- only 4.1 masters write SET statements,
	in 5.0 there are no SET statements in the binary log)
1907 1908
      - DROP TEMPORARY TABLE IF EXISTS: we always execute it (otherwise we
        have stale files on slave caused by exclusion of one tmp table).
unknown's avatar
merge  
unknown committed
1909
    */
unknown's avatar
unknown committed
1910 1911
    if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) &&
	!(lex->sql_command == SQLCOM_SET_OPTION) &&
1912
	!(lex->sql_command == SQLCOM_DROP_TABLE &&
1913
          lex->drop_temporary && lex->drop_if_exists) &&
unknown's avatar
Merge  
unknown committed
1914
        all_tables_not_ok(thd, all_tables))
unknown's avatar
unknown committed
1915 1916
    {
      /* we warn the slave SQL thread */
unknown's avatar
unknown committed
1917
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934
      if (thd->one_shot_set)
      {
        /*
          It's ok to check thd->one_shot_set here:

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

          At least there seems to be problems in the rli cache for
          charsets if we are using ONE_SHOT.  Note that this is normally no
          problem because either the >= 5.0 slave reads a 4.1 binlog (with
          ONE_SHOT) *or* or 5.0 binlog (without ONE_SHOT) but never both."
        */
        reset_one_shot_variables(thd);
      }
1935
      DBUG_RETURN(0);
unknown's avatar
unknown committed
1936
    }
1937
  }
1938
  else
1939
  {
1940
#endif /* HAVE_REPLICATION */
1941 1942 1943 1944
    /*
      When option readonly is set deny operations which change non-temporary
      tables. Except for the replication thread and the 'super' users.
    */
1945
    if (deny_updates_if_read_only_option(thd, all_tables))
1946 1947 1948 1949 1950 1951 1952
    {
      my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
      DBUG_RETURN(-1);
    }
#ifdef HAVE_REPLICATION
  } /* endif unlikely slave */
#endif
Konstantin Osipov's avatar
Konstantin Osipov committed
1953

1954
  status_var_increment(thd->status_var.com_stat[lex->sql_command]);
1955

unknown's avatar
unknown committed
1956
  DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE);
Konstantin Osipov's avatar
Konstantin Osipov committed
1957 1958 1959 1960 1961 1962 1963

  /*
    End a active transaction so that this command will have it's
    own transaction and will also sync the binary log. If a DDL is
    not run in it's own transaction it may simply never appear on
    the slave in case the outside transaction rolls back.
  */
1964 1965 1966 1967 1968 1969 1970 1971 1972
  if (stmt_causes_implicit_commit(thd, CF_IMPLICT_COMMIT_BEGIN))
  {
    /* Commit or rollback the statement transaction. */
    thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
    /* Commit the normal transaction if one is active. */
    if (trans_commit_implicit(thd))
      goto error;
    /* Close tables and release metadata locks. */
    close_thread_tables(thd);
1973
    thd->mdl_context.release_transactional_locks();
1974
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
1975

Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
1976 1977 1978 1979 1980 1981 1982 1983
  /*
    Check if this command needs protection against the global read lock
    to avoid deadlock. See CF_PROTECT_AGAINST_GRL.
    start_waiting_global_read_lock() is called at the end of
    mysql_execute_command().
  */
  if (((sql_command_flags[lex->sql_command] & CF_PROTECT_AGAINST_GRL) != 0) &&
      !thd->locked_tables_mode)
1984
    if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
1985 1986
      goto error;

unknown's avatar
unknown committed
1987
  switch (lex->sql_command) {
1988

1989
  case SQLCOM_SHOW_EVENTS:
1990 1991 1992 1993
#ifndef HAVE_EVENT_SCHEDULER
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
    break;
#endif
1994 1995
  case SQLCOM_SHOW_STATUS_PROC:
  case SQLCOM_SHOW_STATUS_FUNC:
Konstantin Osipov's avatar
Konstantin Osipov committed
1996
    if ((res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
1997
                                  UINT_MAX, FALSE)))
Konstantin Osipov's avatar
Konstantin Osipov committed
1998 1999
      goto error;
    res= execute_sqlcom_select(thd, all_tables);
2000 2001 2002 2003 2004
    break;
  case SQLCOM_SHOW_STATUS:
  {
    system_status_var old_status_var= thd->status_var;
    thd->initial_status_var= &old_status_var;
2005 2006
    if (!(res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
                                  UINT_MAX, FALSE)))
2007
      res= execute_sqlcom_select(thd, all_tables);
2008 2009 2010 2011 2012 2013 2014
    /* Don't log SHOW STATUS commands to slow query log */
    thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED |
                           SERVER_QUERY_NO_GOOD_INDEX_USED);
    /*
      restore status variables, as we don't want 'show status' to cause
      changes
    */
Marc Alff's avatar
Marc Alff committed
2015
    mysql_mutex_lock(&LOCK_status);
2016 2017 2018
    add_diff_to_status(&global_status_var, &thd->status_var,
                       &old_status_var);
    thd->status_var= old_status_var;
Marc Alff's avatar
Marc Alff committed
2019
    mysql_mutex_unlock(&LOCK_status);
2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032
    break;
  }
  case SQLCOM_SHOW_DATABASES:
  case SQLCOM_SHOW_TABLES:
  case SQLCOM_SHOW_TRIGGERS:
  case SQLCOM_SHOW_TABLE_STATUS:
  case SQLCOM_SHOW_OPEN_TABLES:
  case SQLCOM_SHOW_PLUGINS:
  case SQLCOM_SHOW_FIELDS:
  case SQLCOM_SHOW_KEYS:
  case SQLCOM_SHOW_VARIABLES:
  case SQLCOM_SHOW_CHARSETS:
  case SQLCOM_SHOW_COLLATIONS:
2033
  case SQLCOM_SHOW_STORAGE_ENGINES:
2034
  case SQLCOM_SHOW_PROFILE:
unknown's avatar
unknown committed
2035
  case SQLCOM_SELECT:
2036
  {
2037
    thd->status_var.last_query_cost= 0.0;
2038 2039 2040 2041 2042 2043 2044 2045

    /*
      lex->exchange != NULL implies SELECT .. INTO OUTFILE and this
      requires FILE_ACL access.
    */
    ulong privileges_requested= lex->exchange ? SELECT_ACL | FILE_ACL :
      SELECT_ACL;

unknown's avatar
VIEW  
unknown committed
2046
    if (all_tables)
2047
      res= check_table_access(thd,
2048 2049
                              privileges_requested,
                              all_tables, FALSE, UINT_MAX, FALSE);
unknown's avatar
unknown committed
2050
    else
Marc Alff's avatar
Marc Alff committed
2051
      res= check_access(thd, privileges_requested, any_db, NULL, NULL, 0, 0);
2052

2053 2054 2055
    if (res)
      break;

Konstantin Osipov's avatar
Konstantin Osipov committed
2056
    if (!thd->locked_tables_mode && lex->protect_against_global_read_lock &&
2057
        thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
2058 2059 2060
      break;

    res= execute_sqlcom_select(thd, all_tables);
unknown's avatar
unknown committed
2061
    break;
2062 2063
  }
case SQLCOM_PREPARE:
2064
  {
2065
    mysql_sql_stmt_prepare(thd);
unknown's avatar
unknown committed
2066 2067 2068 2069
    break;
  }
  case SQLCOM_EXECUTE:
  {
2070
    mysql_sql_stmt_execute(thd);
unknown's avatar
unknown committed
2071 2072 2073 2074
    break;
  }
  case SQLCOM_DEALLOCATE_PREPARE:
  {
2075
    mysql_sql_stmt_close(thd);
unknown's avatar
unknown committed
2076 2077
    break;
  }
unknown's avatar
unknown committed
2078
  case SQLCOM_DO:
2079
    if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
2080
        || open_and_lock_tables(thd, all_tables, TRUE, 0))
unknown's avatar
unknown committed
2081
      goto error;
unknown's avatar
unknown committed
2082 2083

    res= mysql_do(thd, *lex->insert_list);
unknown's avatar
unknown committed
2084 2085
    break;

2086
  case SQLCOM_EMPTY_QUERY:
2087
    my_ok(thd);
2088 2089
    break;

unknown's avatar
unknown committed
2090 2091 2092 2093
  case SQLCOM_HELP:
    res= mysqld_help(thd,lex->help_arg);
    break;

2094
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
2095
  case SQLCOM_PURGE:
2096
  {
unknown's avatar
unknown committed
2097
    if (check_global_access(thd, SUPER_ACL))
2098
      goto error;
unknown's avatar
unknown committed
2099
    /* PURGE MASTER LOGS TO 'file' */
2100 2101 2102
    res = purge_master_logs(thd, lex->to_log);
    break;
  }
2103 2104
  case SQLCOM_PURGE_BEFORE:
  {
2105 2106
    Item *it;

2107 2108
    if (check_global_access(thd, SUPER_ACL))
      goto error;
unknown's avatar
unknown committed
2109
    /* PURGE MASTER LOGS BEFORE 'data' */
2110
    it= (Item *)lex->value_list.head();
2111
    if ((!it->fixed && it->fix_fields(lex->thd, &it)) ||
unknown's avatar
unknown committed
2112
        it->check_cols(1))
2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123
    {
      my_error(ER_WRONG_ARGUMENTS, MYF(0), "PURGE LOGS BEFORE");
      goto error;
    }
    it= new Item_func_unix_timestamp(it);
    /*
      it is OK only emulate fix_fieds, because we need only
      value of constant
    */
    it->quick_fix_field();
    res = purge_master_logs_before_date(thd, (ulong)it->val_int());
2124 2125
    break;
  }
2126
#endif
unknown's avatar
unknown committed
2127 2128
  case SQLCOM_SHOW_WARNS:
  {
2129 2130
    res= mysqld_show_warnings(thd, (ulong)
			      ((1L << (uint) MYSQL_ERROR::WARN_LEVEL_NOTE) |
2131 2132 2133
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_WARN) |
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)
			       ));
unknown's avatar
unknown committed
2134 2135 2136 2137
    break;
  }
  case SQLCOM_SHOW_ERRORS:
  {
2138 2139
    res= mysqld_show_warnings(thd, (ulong)
			      (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
unknown's avatar
unknown committed
2140 2141
    break;
  }
unknown's avatar
unknown committed
2142 2143
  case SQLCOM_SHOW_PROFILES:
  {
2144
#if defined(ENABLED_PROFILING)
2145
    thd->profiling.discard_current_query();
unknown's avatar
unknown committed
2146
    res= thd->profiling.show_profiles();
2147 2148 2149
    if (res)
      goto error;
#else
2150
    my_error(ER_FEATURE_DISABLED, MYF(0), "SHOW PROFILES", "enable-profiling");
2151 2152
    goto error;
#endif
unknown's avatar
unknown committed
2153 2154
    break;
  }
unknown's avatar
unknown committed
2155 2156
  case SQLCOM_SHOW_NEW_MASTER:
  {
unknown's avatar
unknown committed
2157
    if (check_global_access(thd, REPL_SLAVE_ACL))
unknown's avatar
unknown committed
2158
      goto error;
2159
    /* This query don't work now. See comment in repl_failsafe.cc */
unknown's avatar
unknown committed
2160
#ifndef WORKING_NEW_MASTER
unknown's avatar
unknown committed
2161 2162
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "SHOW NEW MASTER");
    goto error;
unknown's avatar
unknown committed
2163
#else
unknown's avatar
unknown committed
2164 2165
    res = show_new_master(thd);
    break;
unknown's avatar
unknown committed
2166
#endif
unknown's avatar
unknown committed
2167
  }
2168

unknown's avatar
unknown committed
2169
#ifdef HAVE_REPLICATION
2170 2171
  case SQLCOM_SHOW_SLAVE_HOSTS:
  {
unknown's avatar
unknown committed
2172
    if (check_global_access(thd, REPL_SLAVE_ACL))
2173 2174 2175 2176
      goto error;
    res = show_slave_hosts(thd);
    break;
  }
2177
  case SQLCOM_SHOW_RELAYLOG_EVENTS: /* fall through */
unknown's avatar
unknown committed
2178 2179
  case SQLCOM_SHOW_BINLOG_EVENTS:
  {
unknown's avatar
unknown committed
2180
    if (check_global_access(thd, REPL_SLAVE_ACL))
unknown's avatar
unknown committed
2181
      goto error;
2182
    res = mysql_show_binlog_events(thd);
unknown's avatar
unknown committed
2183 2184
    break;
  }
2185 2186
#endif

unknown's avatar
unknown committed
2187 2188
  case SQLCOM_ASSIGN_TO_KEYCACHE:
  {
unknown's avatar
VIEW  
unknown committed
2189
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
2190
    if (check_access(thd, INDEX_ACL, first_table->db,
Marc Alff's avatar
Marc Alff committed
2191 2192 2193
                     &first_table->grant.privilege,
                     &first_table->grant.m_internal,
                     0, 0))
unknown's avatar
unknown committed
2194
      goto error;
2195
    res= mysql_assign_to_keycache(thd, first_table, &lex->ident);
unknown's avatar
unknown committed
2196 2197
    break;
  }
unknown's avatar
unknown committed
2198 2199
  case SQLCOM_PRELOAD_KEYS:
  {
unknown's avatar
VIEW  
unknown committed
2200
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
2201
    if (check_access(thd, INDEX_ACL, first_table->db,
Marc Alff's avatar
Marc Alff committed
2202 2203 2204
                     &first_table->grant.privilege,
                     &first_table->grant.m_internal,
                     0, 0))
2205
      goto error;
unknown's avatar
VIEW  
unknown committed
2206
    res = mysql_preload_keys(thd, first_table);
unknown's avatar
unknown committed
2207 2208
    break;
  }
unknown's avatar
unknown committed
2209
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
2210
  case SQLCOM_CHANGE_MASTER:
2211
  {
unknown's avatar
unknown committed
2212
    if (check_global_access(thd, SUPER_ACL))
2213
      goto error;
Marc Alff's avatar
Marc Alff committed
2214
    mysql_mutex_lock(&LOCK_active_mi);
2215
    res = change_master(thd,active_mi);
Marc Alff's avatar
Marc Alff committed
2216
    mysql_mutex_unlock(&LOCK_active_mi);
2217 2218
    break;
  }
unknown's avatar
unknown committed
2219
  case SQLCOM_SHOW_SLAVE_STAT:
2220
  {
2221 2222
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2223
      goto error;
Marc Alff's avatar
Marc Alff committed
2224
    mysql_mutex_lock(&LOCK_active_mi);
2225 2226 2227 2228 2229 2230
    if (active_mi != NULL)
    {
      res = show_master_info(thd, active_mi);
    }
    else
    {
2231 2232
      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                   WARN_NO_MASTER_INFO, ER(WARN_NO_MASTER_INFO));
2233
      my_ok(thd);
2234
    }
Marc Alff's avatar
Marc Alff committed
2235
    mysql_mutex_unlock(&LOCK_active_mi);
2236 2237
    break;
  }
unknown's avatar
unknown committed
2238
  case SQLCOM_SHOW_MASTER_STAT:
2239
  {
2240 2241
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2242 2243 2244 2245
      goto error;
    res = show_binlog_info(thd);
    break;
  }
unknown's avatar
unknown committed
2246

unknown's avatar
unknown committed
2247
#endif /* HAVE_REPLICATION */
unknown's avatar
unknown committed
2248
  case SQLCOM_SHOW_ENGINE_STATUS:
unknown's avatar
unknown committed
2249
    {
2250
      if (check_global_access(thd, PROCESS_ACL))
unknown's avatar
unknown committed
2251 2252
        goto error;
      res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_STATUS);
unknown's avatar
unknown committed
2253 2254
      break;
    }
unknown's avatar
unknown committed
2255
  case SQLCOM_SHOW_ENGINE_MUTEX:
unknown's avatar
unknown committed
2256
    {
2257
      if (check_global_access(thd, PROCESS_ACL))
unknown's avatar
unknown committed
2258
        goto error;
unknown's avatar
unknown committed
2259
      res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_MUTEX);
unknown's avatar
unknown committed
2260 2261
      break;
    }
unknown's avatar
unknown committed
2262
  case SQLCOM_CREATE_TABLE:
unknown's avatar
unknown committed
2263
  {
unknown's avatar
VIEW  
unknown committed
2264 2265
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    bool link_to_local;
2266 2267 2268
    TABLE_LIST *create_table= first_table;
    TABLE_LIST *select_tables= lex->create_last_non_select_table->next_global;

2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289
    /*
      Code below (especially in mysql_create_table() and select_create
      methods) may modify HA_CREATE_INFO structure in LEX, so we have to
      use a copy of this structure to make execution prepared statement-
      safe. A shallow copy is enough as this code won't modify any memory
      referenced from this structure.
    */
    HA_CREATE_INFO create_info(lex->create_info);
    /*
      We need to copy alter_info for the same reasons of re-execution
      safety, only in case of Alter_info we have to do (almost) a deep
      copy.
    */
    Alter_info alter_info(lex->alter_info, thd->mem_root);

    if (thd->is_fatal_error)
    {
      /* If out of memory when creating a copy of alter_info. */
      res= 1;
      goto end_with_restore_list;
    }
unknown's avatar
unknown committed
2290

unknown's avatar
VIEW  
unknown committed
2291
    if ((res= create_table_precheck(thd, select_tables, create_table)))
unknown's avatar
unknown committed
2292
      goto end_with_restore_list;
unknown's avatar
unknown committed
2293

2294 2295 2296
    /* Might have been updated in create_table_precheck */
    create_info.alias= create_table->alias;

2297
#ifdef HAVE_READLINK
2298
    /* Fix names if symlinked tables */
2299
    if (append_file_to_dir(thd, &create_info.data_file_name,
2300
			   create_table->table_name) ||
2301
	append_file_to_dir(thd, &create_info.index_file_name,
2302
			   create_table->table_name))
unknown's avatar
unknown committed
2303
      goto end_with_restore_list;
2304
#endif
2305
    /*
2306
      If we are using SET CHARSET without DEFAULT, add an implicit
2307 2308
      DEFAULT to not confuse old users. (This may change).
    */
2309
    if ((create_info.used_fields &
2310 2311 2312
	 (HA_CREATE_USED_DEFAULT_CHARSET | HA_CREATE_USED_CHARSET)) ==
	HA_CREATE_USED_CHARSET)
    {
2313 2314 2315 2316
      create_info.used_fields&= ~HA_CREATE_USED_CHARSET;
      create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
      create_info.default_table_charset= create_info.table_charset;
      create_info.table_charset= 0;
2317
    }
2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328
    /*
      The create-select command will open and read-lock the select table
      and then create, open and write-lock the new table. If a global
      read lock steps in, we get a deadlock. The write lock waits for
      the global read lock, while the global read lock waits for the
      select table to be closed. So we wait until the global readlock is
      gone before starting both steps. Note that
      wait_if_global_read_lock() sets a protection against a new global
      read lock when it succeeds. This needs to be released by
      start_waiting_global_read_lock(). We protect the normal CREATE
      TABLE in the same way. That way we avoid that a new table is
2329
      created during a global read lock.
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
2330
      Protection against grl is covered by the CF_PROTECT_AGAINST_GRL flag.
2331
    */
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
2332

2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343
#ifdef WITH_PARTITION_STORAGE_ENGINE
    {
      partition_info *part_info= thd->lex->part_info;
      if (part_info && !(part_info= thd->lex->part_info->get_clone()))
      {
        res= -1;
        goto end_with_restore_list;
      }
      thd->work_part_info= part_info;
    }
#endif
2344

2345 2346 2347 2348
    /* Set strategies: reset default or 'prepared' values. */
    create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
    create_table->lock_strategy= TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL;

2349 2350 2351 2352 2353
    /*
      Close any open handlers for the table
    */
    mysql_ha_rm_tables(thd, create_table);

2354
    if (select_lex->item_list.elements)		// With select
unknown's avatar
unknown committed
2355 2356
    {
      select_result *result;
2357

2358 2359 2360
      /*
        If:
        a) we inside an SP and there was NAME_CONST substitution,
Ramil Kalimullin's avatar
Ramil Kalimullin committed
2361
        b) binlogging is on (STMT mode),
2362 2363 2364 2365 2366 2367
        c) we log the SP as separate statements
        raise a warning, as it may cause problems
        (see 'NAME_CONST issues' in 'Binary Logging of Stored Programs')
       */
      if (thd->query_name_consts && 
          mysql_bin_log.is_open() &&
Ramil Kalimullin's avatar
Ramil Kalimullin committed
2368
          thd->variables.binlog_format == BINLOG_FORMAT_STMT &&
2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394
          !mysql_bin_log.is_query_in_union(thd, thd->query_id))
      {
        List_iterator_fast<Item> it(select_lex->item_list);
        Item *item;
        uint splocal_refs= 0;
        /* Count SP local vars in the top-level SELECT list */
        while ((item= it++))
        {
          if (item->is_splocal())
            splocal_refs++;
        }
        /*
          If it differs from number of NAME_CONST substitution applied,
          we may have a SOME_FUNC(NAME_CONST()) in the SELECT list,
          that may cause a problem with binary log (see BUG#35383),
          raise a warning. 
        */
        if (splocal_refs != thd->query_name_consts)
          push_warning(thd, 
                       MYSQL_ERROR::WARN_LEVEL_WARN,
                       ER_UNKNOWN_ERROR,
"Invoked routine ran a statement that may cause problems with "
"binary log, see 'NAME_CONST issues' in 'Binary Logging of Stored Programs' "
"section of the manual.");
      }
      
2395
      select_lex->options|= SELECT_NO_UNLOCK;
2396
      unit->set_limit(select_lex);
2397

2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410
      /*
        Disable non-empty MERGE tables with CREATE...SELECT. Too
        complicated. See Bug #26379. Empty MERGE tables are read-only
        and don't allow CREATE...SELECT anyway.
      */
      if (create_info.used_fields & HA_CREATE_USED_UNION)
      {
        my_error(ER_WRONG_OBJECT, MYF(0), create_table->db,
                 create_table->table_name, "BASE TABLE");
        res= 1;
        goto end_with_restore_list;
      }

2411
      if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
unknown's avatar
unknown committed
2412
      {
2413
        /* Base table and temporary table are not in the same name space. */
2414
        create_table->open_type= OT_BASE_ONLY;
unknown's avatar
unknown committed
2415 2416
      }

2417
      if (!(res= open_and_lock_tables(thd, lex->query_tables, TRUE, 0)))
2418
      {
2419 2420 2421 2422
        /*
          Is table which we are changing used somewhere in other parts
          of query
        */
2423
        if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
2424
        {
2425
          TABLE_LIST *duplicate;
2426
          if ((duplicate= unique_table(thd, create_table, select_tables, 0)))
2427 2428 2429
          {
            update_non_unique_table_error(create_table, "CREATE", duplicate);
            res= 1;
2430
            goto end_with_restore_list;
2431
          }
2432
        }
unknown's avatar
unknown committed
2433
        /* If we create merge table, we have to test tables in merge, too */
2434
        if (create_info.used_fields & HA_CREATE_USED_UNION)
unknown's avatar
unknown committed
2435 2436
        {
          TABLE_LIST *tab;
2437
          for (tab= (TABLE_LIST*) create_info.merge_list.first;
unknown's avatar
unknown committed
2438 2439 2440
               tab;
               tab= tab->next_local)
          {
2441
            TABLE_LIST *duplicate;
2442
            if ((duplicate= unique_table(thd, tab, select_tables, 0)))
unknown's avatar
unknown committed
2443
            {
2444
              update_non_unique_table_error(tab, "CREATE", duplicate);
unknown's avatar
unknown committed
2445
              res= 1;
2446
              goto end_with_restore_list;
unknown's avatar
unknown committed
2447 2448 2449
            }
          }
        }
2450

2451 2452 2453 2454 2455 2456 2457
        /*
          Remove target table from main select and name resolution
          context. This can't be done earlier as it will break view merging in
          statements like "CREATE TABLE IF NOT EXISTS existing_view SELECT".
        */
        lex->unlink_first_table(&link_to_local);

unknown's avatar
unknown committed
2458
        /*
2459 2460
          select_create is currently not re-execution friendly and
          needs to be created for every execution of a PS/SP.
unknown's avatar
unknown committed
2461
        */
unknown's avatar
VIEW  
unknown committed
2462
        if ((result= new select_create(create_table,
2463 2464 2465 2466
                                       &create_info,
                                       &alter_info,
                                       select_lex->item_list,
                                       lex->duplicates,
2467
                                       lex->ignore,
2468
                                       select_tables)))
2469 2470 2471 2472 2473
        {
          /*
            CREATE from SELECT give its SELECT_LEX for SELECT,
            and item_list belong to SELECT
          */
2474
          res= handle_select(thd, lex, result, 0);
2475
          delete result;
2476
        }
2477 2478
        
        lex->link_first_table_back(create_table, link_to_local);
2479 2480
      }
    }
unknown's avatar
unknown committed
2481
    else
unknown's avatar
unknown committed
2482
    {
2483
      /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
2484
      if (create_info.options & HA_LEX_CREATE_TMP_TABLE)
2485
        thd->variables.option_bits|= OPTION_KEEP_LOG;
unknown's avatar
unknown committed
2486
      /* regular create */
2487
      if (create_info.options & HA_LEX_CREATE_TABLE_LIKE)
2488 2489
      {
        /* CREATE TABLE ... LIKE ... */
unknown's avatar
unknown committed
2490
        res= mysql_create_like_table(thd, create_table, select_tables,
2491
                                     &create_info);
2492
      }
unknown's avatar
unknown committed
2493
      else
2494
      {
2495 2496 2497
        /* Regular CREATE TABLE */
        res= mysql_create_table(thd, create_table,
                                &create_info, &alter_info);
2498
      }
unknown's avatar
unknown committed
2499
      if (!res)
2500
        my_ok(thd);
unknown's avatar
unknown committed
2501
    }
2502

unknown's avatar
unknown committed
2503
end_with_restore_list:
unknown's avatar
unknown committed
2504
    break;
unknown's avatar
unknown committed
2505
  }
unknown's avatar
unknown committed
2506
  case SQLCOM_CREATE_INDEX:
2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524
    /* Fall through */
  case SQLCOM_DROP_INDEX:
  /*
    CREATE INDEX and DROP INDEX are implemented by calling ALTER
    TABLE with proper arguments.

    In the future ALTER TABLE will notice that the request is to
    only add indexes and create these one by one for the existing
    table without having to do a full rebuild.
  */
  {
    /* Prepare stack copies to be re-execution safe */
    HA_CREATE_INFO create_info;
    Alter_info alter_info(lex->alter_info, thd->mem_root);

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

unknown's avatar
VIEW  
unknown committed
2525 2526
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_one_table_access(thd, INDEX_ACL, all_tables))
unknown's avatar
unknown committed
2527
      goto error; /* purecov: inspected */
2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538
    /*
      Currently CREATE INDEX or DROP INDEX cause a full table rebuild
      and thus classify as slow administrative statements just like
      ALTER TABLE.
    */
    thd->enable_slow_log= opt_log_slow_admin_statements;

    bzero((char*) &create_info, sizeof(create_info));
    create_info.db_type= 0;
    create_info.row_type= ROW_TYPE_NOT_USED;
    create_info.default_table_charset= thd->variables.collation_database;
unknown's avatar
unknown committed
2539

2540 2541 2542 2543 2544
    res= mysql_alter_table(thd, first_table->db, first_table->table_name,
                           &create_info, first_table, &alter_info,
                           0, (ORDER*) 0, 0);
    break;
  }
unknown's avatar
unknown committed
2545
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
2546
  case SQLCOM_SLAVE_START:
2547
  {
Marc Alff's avatar
Marc Alff committed
2548
    mysql_mutex_lock(&LOCK_active_mi);
2549
    start_slave(thd,active_mi,1 /* net report*/);
Marc Alff's avatar
Marc Alff committed
2550
    mysql_mutex_unlock(&LOCK_active_mi);
unknown's avatar
unknown committed
2551
    break;
2552
  }
unknown's avatar
unknown committed
2553
  case SQLCOM_SLAVE_STOP:
2554 2555 2556 2557 2558 2559
  /*
    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,
2560
      so it waits for the client thread because t is locked by it.
2561
    - then the client thread does SLAVE STOP.
2562 2563
      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.
2564 2565 2566
    To prevent that, refuse SLAVE STOP if the
    client thread has locked tables
  */
Konstantin Osipov's avatar
Konstantin Osipov committed
2567
  if (thd->locked_tables_mode ||
2568
      thd->active_transaction() || thd->global_read_lock.is_acquired())
2569
  {
2570 2571
    my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
               ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
2572
    goto error;
2573
  }
2574
  {
Marc Alff's avatar
Marc Alff committed
2575
    mysql_mutex_lock(&LOCK_active_mi);
2576
    stop_slave(thd,active_mi,1/* net report*/);
Marc Alff's avatar
Marc Alff committed
2577
    mysql_mutex_unlock(&LOCK_active_mi);
unknown's avatar
unknown committed
2578
    break;
2579
  }
unknown's avatar
unknown committed
2580
#endif /* HAVE_REPLICATION */
2581

unknown's avatar
unknown committed
2582
  case SQLCOM_ALTER_TABLE:
unknown's avatar
VIEW  
unknown committed
2583
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
2584
    {
unknown's avatar
unknown committed
2585
      ulong priv=0;
unknown's avatar
unknown committed
2586
      ulong priv_needed= ALTER_ACL;
2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597
      /*
        Code in mysql_alter_table() may modify its HA_CREATE_INFO argument,
        so we have to use a copy of this structure to make execution
        prepared statement- safe. A shallow copy is enough as no memory
        referenced from this structure will be modified.
      */
      HA_CREATE_INFO create_info(lex->create_info);
      Alter_info alter_info(lex->alter_info, thd->mem_root);

      if (thd->is_fatal_error) /* out of memory creating a copy of alter_info */
        goto error;
2598 2599 2600 2601
      /*
        We also require DROP priv for ALTER TABLE ... DROP PARTITION, as well
        as for RENAME TO, as being done by SQLCOM_RENAME_TABLE
      */
2602
      if (alter_info.flags & (ALTER_DROP_PARTITION | ALTER_RENAME))
unknown's avatar
unknown committed
2603 2604
        priv_needed|= DROP_ACL;

unknown's avatar
unknown committed
2605 2606
      /* Must be set in the parser */
      DBUG_ASSERT(select_lex->db);
unknown's avatar
unknown committed
2607
      if (check_access(thd, priv_needed, first_table->db,
Marc Alff's avatar
Marc Alff committed
2608 2609 2610 2611 2612 2613 2614
                       &first_table->grant.privilege,
                       &first_table->grant.m_internal,
                       0, 0) ||
          check_access(thd, INSERT_ACL | CREATE_ACL, select_lex->db,
                       &priv,
                       NULL, /* Do not use first_table->grant with select_lex->db */
                       0, 0) ||
unknown's avatar
VIEW  
unknown committed
2615
	  check_merge_table_access(thd, first_table->db,
2616
				   (TABLE_LIST *)
2617
				   create_info.merge_list.first))
2618
	goto error;				/* purecov: inspected */
2619
      if (check_grant(thd, priv_needed, all_tables, FALSE, UINT_MAX, FALSE))
2620 2621 2622 2623 2624 2625 2626 2627
        goto error;
      if (lex->name.str && !test_all_bits(priv,INSERT_ACL | CREATE_ACL))
      { // Rename of table
          TABLE_LIST tmp_table;
          bzero((char*) &tmp_table,sizeof(tmp_table));
          tmp_table.table_name= lex->name.str;
          tmp_table.db=select_lex->db;
          tmp_table.grant.privilege=priv;
2628 2629
          if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, FALSE,
              UINT_MAX, FALSE))
2630
            goto error;
unknown's avatar
unknown committed
2631
      }
2632

2633
      /* Don't yet allow changing of symlinks with ALTER TABLE */
2634
      if (create_info.data_file_name)
2635 2636 2637
        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                            WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
                            "DATA DIRECTORY");
2638
      if (create_info.index_file_name)
2639 2640 2641
        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                            WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
                            "INDEX DIRECTORY");
2642
      create_info.data_file_name= create_info.index_file_name= NULL;
2643 2644

      thd->enable_slow_log= opt_log_slow_admin_statements;
2645
      res= mysql_alter_table(thd, select_lex->db, lex->name.str,
2646 2647 2648
                             &create_info,
                             first_table,
                             &alter_info,
2649 2650
                             select_lex->order_list.elements,
                             (ORDER *) select_lex->order_list.first,
2651
                             lex->ignore);
unknown's avatar
unknown committed
2652 2653
      break;
    }
unknown's avatar
unknown committed
2654
  case SQLCOM_RENAME_TABLE:
unknown's avatar
unknown committed
2655
  {
unknown's avatar
VIEW  
unknown committed
2656
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
2657
    TABLE_LIST *table;
unknown's avatar
VIEW  
unknown committed
2658
    for (table= first_table; table; table= table->next_local->next_local)
unknown's avatar
unknown committed
2659
    {
unknown's avatar
unknown committed
2660
      if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
Marc Alff's avatar
Marc Alff committed
2661 2662 2663 2664 2665 2666 2667
                       &table->grant.privilege,
                       &table->grant.m_internal,
                       0, 0) ||
          check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db,
                       &table->next_local->grant.privilege,
                       &table->next_local->grant.m_internal,
                       0, 0))
unknown's avatar
unknown committed
2668
	goto error;
2669 2670 2671 2672 2673 2674 2675
      TABLE_LIST old_list, new_list;
      /*
        we do not need initialize old_list and new_list because we will
        come table[0] and table->next[0] there
      */
      old_list= table[0];
      new_list= table->next_local[0];
2676
      if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, FALSE, 1, FALSE) ||
2677 2678
         (!test_all_bits(table->next_local->grant.privilege,
                         INSERT_ACL | CREATE_ACL) &&
2679 2680
          check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, FALSE, 1,
                      FALSE)))
2681
        goto error;
unknown's avatar
unknown committed
2682
    }
2683

Konstantin Osipov's avatar
Konstantin Osipov committed
2684
    if (mysql_rename_tables(thd, first_table, 0))
unknown's avatar
unknown committed
2685
      goto error;
unknown's avatar
unknown committed
2686
    break;
unknown's avatar
unknown committed
2687
  }
2688
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
2689 2690
  case SQLCOM_SHOW_BINLOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
2691 2692
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0)); /* purecov: inspected */
2693
    goto error;
unknown's avatar
unknown committed
2694 2695
#else
    {
unknown's avatar
unknown committed
2696
      if (check_global_access(thd, SUPER_ACL))
unknown's avatar
unknown committed
2697 2698 2699 2700
	goto error;
      res = show_binlogs(thd);
      break;
    }
unknown's avatar
unknown committed
2701
#endif
2702
#endif /* EMBEDDED_LIBRARY */
unknown's avatar
unknown committed
2703
  case SQLCOM_SHOW_CREATE:
unknown's avatar
VIEW  
unknown committed
2704
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
2705
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
2706 2707
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0)); /* purecov: inspected */
2708
    goto error;
unknown's avatar
unknown committed
2709
#else
unknown's avatar
unknown committed
2710
    {
2711 2712 2713 2714 2715 2716 2717 2718 2719
     /*
        Access check:
        SHOW CREATE TABLE require any privileges on the table level (ie
        effecting all columns in the table).
        SHOW CREATE VIEW require the SHOW_VIEW and SELECT ACLs on the table
        level.
        NOTE: SHOW_VIEW ACL is checked when the view is created.
      */

2720
      if (lex->only_view)
2721 2722 2723 2724 2725 2726 2727 2728 2729 2730
      {
        if (check_table_access(thd, SELECT_ACL, first_table, FALSE, 1, FALSE))
        {
          my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
                  "SHOW", thd->security_ctx->priv_user,
                  thd->security_ctx->host_or_ip, first_table->alias);
          goto error;
        }

        /* Ignore temporary tables if this is "SHOW CREATE VIEW" */
2731 2732
        first_table->open_type= OT_BASE_ONLY;

2733 2734 2735 2736
      }
      else
      {
        /*
Marc Alff's avatar
Marc Alff committed
2737 2738 2739
          The fact that check_some_access() returned FALSE does not mean that
          access is granted. We need to check if first_table->grant.privilege
          contains any table-specific privilege.
2740
        */
Marc Alff's avatar
Marc Alff committed
2741 2742
        if (check_some_access(thd, SHOW_CREATE_TABLE_ACLS, first_table) ||
            (first_table->grant.privilege & SHOW_CREATE_TABLE_ACLS) == 0)
2743 2744 2745 2746 2747 2748 2749 2750
        {
          my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
                  "SHOW", thd->security_ctx->priv_user,
                  thd->security_ctx->host_or_ip, first_table->alias);
          goto error;
        }
      }

2751
      /* Access is granted. Execute the command.  */
unknown's avatar
unknown committed
2752
      res= mysqld_show_create(thd, first_table);
unknown's avatar
unknown committed
2753 2754
      break;
    }
unknown's avatar
unknown committed
2755
#endif
2756 2757
  case SQLCOM_CHECKSUM:
  {
unknown's avatar
VIEW  
unknown committed
2758
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
2759 2760
    if (check_table_access(thd, SELECT_ACL, all_tables,
                           FALSE, UINT_MAX, FALSE))
2761
      goto error; /* purecov: inspected */
2762

unknown's avatar
VIEW  
unknown committed
2763
    res = mysql_checksum_table(thd, first_table, &lex->check_opt);
2764 2765
    break;
  }
unknown's avatar
unknown committed
2766
  case SQLCOM_REPAIR:
2767
  {
unknown's avatar
VIEW  
unknown committed
2768
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
2769
    if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
2770
                           FALSE, UINT_MAX, FALSE))
2771
      goto error; /* purecov: inspected */
2772
    thd->enable_slow_log= opt_log_slow_admin_statements;
unknown's avatar
VIEW  
unknown committed
2773
    res= mysql_repair_table(thd, first_table, &lex->check_opt);
2774 2775 2776
    /* ! we write after unlocking the table */
    if (!res && !lex->no_write_to_binlog)
    {
2777 2778 2779
      /*
        Presumably, REPAIR and binlog writing doesn't require synchronization
      */
2780
      res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
2781
    }
2782
    select_lex->table_list.first= (uchar*) first_table;
2783
    lex->query_tables=all_tables;
2784 2785
    break;
  }
unknown's avatar
unknown committed
2786
  case SQLCOM_CHECK:
2787
  {
unknown's avatar
VIEW  
unknown committed
2788
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
2789 2790
    if (check_table_access(thd, SELECT_ACL, all_tables,
                           TRUE, UINT_MAX, FALSE))
2791
      goto error; /* purecov: inspected */
2792
    thd->enable_slow_log= opt_log_slow_admin_statements;
unknown's avatar
VIEW  
unknown committed
2793
    res = mysql_check_table(thd, first_table, &lex->check_opt);
2794
    select_lex->table_list.first= (uchar*) first_table;
2795
    lex->query_tables=all_tables;
2796 2797
    break;
  }
unknown's avatar
unknown committed
2798 2799
  case SQLCOM_ANALYZE:
  {
unknown's avatar
VIEW  
unknown committed
2800
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
2801
    if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
2802
                           FALSE, UINT_MAX, FALSE))
unknown's avatar
unknown committed
2803
      goto error; /* purecov: inspected */
2804
    thd->enable_slow_log= opt_log_slow_admin_statements;
2805
    res= mysql_analyze_table(thd, first_table, &lex->check_opt);
2806 2807 2808
    /* ! we write after unlocking the table */
    if (!res && !lex->no_write_to_binlog)
    {
2809 2810 2811
      /*
        Presumably, ANALYZE and binlog writing doesn't require synchronization
      */
2812
      res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
2813
    }
2814
    select_lex->table_list.first= (uchar*) first_table;
2815
    lex->query_tables=all_tables;
unknown's avatar
unknown committed
2816
    break;
unknown's avatar
unknown committed
2817
  }
2818

unknown's avatar
unknown committed
2819 2820
  case SQLCOM_OPTIMIZE:
  {
unknown's avatar
VIEW  
unknown committed
2821
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
2822
    if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
2823
                           FALSE, UINT_MAX, FALSE))
unknown's avatar
unknown committed
2824
      goto error; /* purecov: inspected */
2825
    thd->enable_slow_log= opt_log_slow_admin_statements;
2826
    res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
2827
      mysql_recreate_table(thd, first_table) :
unknown's avatar
VIEW  
unknown committed
2828
      mysql_optimize_table(thd, first_table, &lex->check_opt);
2829 2830 2831
    /* ! we write after unlocking the table */
    if (!res && !lex->no_write_to_binlog)
    {
2832 2833 2834
      /*
        Presumably, OPTIMIZE and binlog writing doesn't require synchronization
      */
2835
      res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
2836
    }
2837
    select_lex->table_list.first= (uchar*) first_table;
2838
    lex->query_tables=all_tables;
unknown's avatar
unknown committed
2839 2840 2841
    break;
  }
  case SQLCOM_UPDATE:
2842 2843
  {
    ha_rows found= 0, updated= 0;
unknown's avatar
VIEW  
unknown committed
2844 2845
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (update_precheck(thd, all_tables))
unknown's avatar
unknown committed
2846
      break;
2847 2848
    DBUG_ASSERT(select_lex->offset_limit == 0);
    unit->set_limit(select_lex);
2849
    MYSQL_UPDATE_START(thd->query());
unknown's avatar
unknown committed
2850 2851 2852 2853 2854 2855 2856
    res= (up_result= mysql_update(thd, all_tables,
                                  select_lex->item_list,
                                  lex->value_list,
                                  select_lex->where,
                                  select_lex->order_list.elements,
                                  (ORDER *) select_lex->order_list.first,
                                  unit->select_limit_cnt,
2857 2858 2859
                                  lex->duplicates, lex->ignore,
                                  &found, &updated));
    MYSQL_UPDATE_DONE(res, found, updated);
2860
    /* mysql_update return 2 if we need to switch to multi-update */
unknown's avatar
unknown committed
2861
    if (up_result != 2)
2862
      break;
unknown's avatar
unknown committed
2863
    /* Fall through */
2864
  }
2865
  case SQLCOM_UPDATE_MULTI:
unknown's avatar
unknown committed
2866 2867 2868
  {
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    /* if we switched from normal update, rights are checked */
unknown's avatar
unknown committed
2869
    if (up_result != 2)
2870
    {
unknown's avatar
unknown committed
2871 2872 2873 2874 2875
      if ((res= multi_update_precheck(thd, all_tables)))
        break;
    }
    else
      res= 0;
unknown's avatar
unknown committed
2876

2877
    res= mysql_multi_update_prepare(thd);
unknown's avatar
unknown committed
2878

2879
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
2880
    /* Check slave filtering rules */
2881
    if (unlikely(thd->slave_thread && !have_table_map_for_update))
unknown's avatar
unknown committed
2882
    {
2883 2884
      if (all_tables_not_ok(thd, all_tables))
      {
2885 2886 2887 2888 2889
        if (res!= 0)
        {
          res= 0;             /* don't care of prev failure  */
          thd->clear_error(); /* filters are of highest prior */
        }
2890 2891 2892 2893
        /* we warn the slave SQL thread */
        my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
        break;
      }
2894 2895
      if (res)
        break;
unknown's avatar
unknown committed
2896
    }
2897 2898
    else
    {
2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911
#endif /* HAVE_REPLICATION */
      if (res)
        break;
      if (opt_readonly &&
	  !(thd->security_ctx->master_access & SUPER_ACL) &&
	  some_non_temp_table_to_be_updated(thd, all_tables))
      {
	my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
	break;
      }
#ifdef HAVE_REPLICATION
    }  /* unlikely */
#endif
2912 2913
    {
      multi_update *result_obj;
2914
      MYSQL_MULTI_UPDATE_START(thd->query());
2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936
      res= mysql_multi_update(thd, all_tables,
                              &select_lex->item_list,
                              &lex->value_list,
                              select_lex->where,
                              select_lex->options,
                              lex->duplicates,
                              lex->ignore,
                              unit,
                              select_lex,
                              &result_obj);
      if (result_obj)
      {
        MYSQL_MULTI_UPDATE_DONE(res, result_obj->num_found(),
                                result_obj->num_updated());
        res= FALSE; /* Ignore errors here */
        delete result_obj;
      }
      else
      {
        MYSQL_MULTI_UPDATE_DONE(1, 0, 0);
      }
    }
unknown's avatar
unknown committed
2937
    break;
unknown's avatar
unknown committed
2938
  }
unknown's avatar
unknown committed
2939
  case SQLCOM_REPLACE:
2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963
#ifndef DBUG_OFF
    if (mysql_bin_log.is_open())
    {
      /*
        Generate an incident log event before writing the real event
        to the binary log.  We put this event is before the statement
        since that makes it simpler to check that the statement was
        not executed on the slave (since incidents usually stop the
        slave).

        Observe that any row events that are generated will be
        generated before.

        This is only for testing purposes and will not be present in a
        release build.
      */

      Incident incident= INCIDENT_NONE;
      DBUG_PRINT("debug", ("Just before generate_incident()"));
      DBUG_EXECUTE_IF("incident_database_resync_on_replace",
                      incident= INCIDENT_LOST_EVENTS;);
      if (incident)
      {
        Incident_log_event ev(thd, incident);
2964
        (void) mysql_bin_log.write(&ev);        /* error is ignored */
2965 2966 2967 2968 2969
        mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
      }
      DBUG_PRINT("debug", ("Just after generate_incident()"));
    }
#endif
2970 2971
  case SQLCOM_INSERT:
  {
unknown's avatar
VIEW  
unknown committed
2972
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
2973
    if ((res= insert_precheck(thd, all_tables)))
unknown's avatar
unknown committed
2974
      break;
2975

2976
    MYSQL_INSERT_START(thd->query());
unknown's avatar
VIEW  
unknown committed
2977
    res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
unknown's avatar
unknown committed
2978
		      lex->update_list, lex->value_list,
2979
                      lex->duplicates, lex->ignore);
2980
    MYSQL_INSERT_DONE(res, (ulong) thd->row_count_func);
2981 2982 2983 2984 2985 2986
    /*
      If we have inserted into a VIEW, and the base table has
      AUTO_INCREMENT column, but this column is not accessible through
      a view, then we should restore LAST_INSERT_ID to the value it
      had before the statement.
    */
unknown's avatar
VIEW  
unknown committed
2987
    if (first_table->view && !first_table->contain_auto_increment)
2988 2989
      thd->first_successful_insert_id_in_cur_stmt=
        thd->first_successful_insert_id_in_prev_stmt;
2990

unknown's avatar
unknown committed
2991
    break;
2992
  }
unknown's avatar
unknown committed
2993 2994 2995
  case SQLCOM_REPLACE_SELECT:
  case SQLCOM_INSERT_SELECT:
  {
unknown's avatar
unknown committed
2996
    select_result *sel_result;
unknown's avatar
VIEW  
unknown committed
2997
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
2998
    if ((res= insert_precheck(thd, all_tables)))
2999
      break;
unknown's avatar
unknown committed
3000

3001
    /* Fix lock for first table */
unknown's avatar
VIEW  
unknown committed
3002 3003
    if (first_table->lock_type == TL_WRITE_DELAYED)
      first_table->lock_type= TL_WRITE;
3004

3005 3006
    /* Don't unlock tables until command is written to binary log */
    select_lex->options|= SELECT_NO_UNLOCK;
unknown's avatar
unknown committed
3007

3008
    unit->set_limit(select_lex);
3009

3010
    if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
3011
    {
3012
      MYSQL_INSERT_SELECT_START(thd->query());
3013
      /* Skip first table, which is the table we are inserting in */
unknown's avatar
unknown committed
3014
      TABLE_LIST *second_table= first_table->next_local;
3015
      select_lex->table_list.first= (uchar*) second_table;
3016 3017
      select_lex->context.table_list= 
        select_lex->context.first_name_resolution_table= second_table;
3018
      res= mysql_insert_select_prepare(thd);
unknown's avatar
unknown committed
3019 3020 3021 3022 3023 3024 3025
      if (!res && (sel_result= new select_insert(first_table,
                                                 first_table->table,
                                                 &lex->field_list,
                                                 &lex->update_list,
                                                 &lex->value_list,
                                                 lex->duplicates,
                                                 lex->ignore)))
3026
      {
unknown's avatar
unknown committed
3027
	res= handle_select(thd, lex, sel_result, OPTION_SETUP_TABLES_DONE);
3028 3029 3030 3031 3032 3033 3034 3035 3036
        /*
          Invalidate the table in the query cache if something changed
          after unlocking when changes become visible.
          TODO: this is workaround. right way will be move invalidating in
          the unlock procedure.
        */
        if (first_table->lock_type ==  TL_WRITE_CONCURRENT_INSERT &&
            thd->lock)
        {
3037 3038 3039
          /* INSERT ... SELECT should invalidate only the very first table */
          TABLE_LIST *save_table= first_table->next_local;
          first_table->next_local= 0;
3040
          query_cache_invalidate3(thd, first_table, 1);
3041
          first_table->next_local= save_table;
3042
        }
unknown's avatar
unknown committed
3043
        delete sel_result;
3044
      }
3045
      /* revert changes for SP */
3046
      MYSQL_INSERT_SELECT_DONE(res, (ulong) thd->row_count_func);
3047
      select_lex->table_list.first= (uchar*) first_table;
3048
    }
3049 3050 3051 3052 3053 3054
    /*
      If we have inserted into a VIEW, and the base table has
      AUTO_INCREMENT column, but this column is not accessible through
      a view, then we should restore LAST_INSERT_ID to the value it
      had before the statement.
    */
unknown's avatar
VIEW  
unknown committed
3055
    if (first_table->view && !first_table->contain_auto_increment)
3056 3057
      thd->first_successful_insert_id_in_cur_stmt=
        thd->first_successful_insert_id_in_prev_stmt;
3058

unknown's avatar
unknown committed
3059 3060
    break;
  }
3061
  case SQLCOM_TRUNCATE:
unknown's avatar
VIEW  
unknown committed
3062
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
3063
    if (check_one_table_access(thd, DROP_ACL, all_tables))
unknown's avatar
unknown committed
3064
      goto error;
3065 3066 3067 3068
    /*
      Don't allow this within a transaction because we want to use
      re-generate table
    */
Konstantin Osipov's avatar
Konstantin Osipov committed
3069
    if (thd->active_transaction())
3070
    {
unknown's avatar
unknown committed
3071 3072
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
3073 3074
      goto error;
    }
3075
    if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
3076
      goto error;
unknown's avatar
unknown committed
3077
    res= mysql_truncate(thd, first_table, 0);
3078
    break;
unknown's avatar
unknown committed
3079
  case SQLCOM_DELETE:
unknown's avatar
unknown committed
3080
  {
unknown's avatar
VIEW  
unknown committed
3081 3082
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if ((res= delete_precheck(thd, all_tables)))
unknown's avatar
unknown committed
3083
      break;
3084 3085
    DBUG_ASSERT(select_lex->offset_limit == 0);
    unit->set_limit(select_lex);
3086

3087
    MYSQL_DELETE_START(thd->query());
unknown's avatar
VIEW  
unknown committed
3088
    res = mysql_delete(thd, all_tables, select_lex->where,
3089
                       &select_lex->order_list,
unknown's avatar
unknown committed
3090 3091
                       unit->select_limit_cnt, select_lex->options,
                       FALSE);
3092
    MYSQL_DELETE_DONE(res, (ulong) thd->row_count_func);
unknown's avatar
unknown committed
3093 3094
    break;
  }
3095
  case SQLCOM_DELETE_MULTI:
unknown's avatar
unknown committed
3096
  {
unknown's avatar
VIEW  
unknown committed
3097
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3098
    TABLE_LIST *aux_tables=
unknown's avatar
unknown committed
3099
      (TABLE_LIST *)thd->lex->auxiliary_table_list.first;
unknown's avatar
unknown committed
3100
    multi_delete *del_result;
unknown's avatar
unknown committed
3101

3102
    if ((res= multi_delete_precheck(thd, all_tables)))
3103
      break;
unknown's avatar
unknown committed
3104

unknown's avatar
unknown committed
3105
    /* condition will be TRUE on SP re-excuting */
3106 3107
    if (select_lex->item_list.elements != 0)
      select_lex->item_list.empty();
unknown's avatar
unknown committed
3108
    if (add_item_to_list(thd, new Item_null()))
unknown's avatar
unknown committed
3109
      goto error;
3110

3111
    thd_proc_info(thd, "init");
3112
    if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
unknown's avatar
VIEW  
unknown committed
3113 3114
      break;

3115
    MYSQL_MULTI_DELETE_START(thd->query());
unknown's avatar
VIEW  
unknown committed
3116
    if ((res= mysql_multi_delete_prepare(thd)))
3117 3118
    {
      MYSQL_MULTI_DELETE_DONE(1, 0);
unknown's avatar
unknown committed
3119
      goto error;
3120
    }
3121

unknown's avatar
unknown committed
3122 3123
    if (!thd->is_fatal_error &&
        (del_result= new multi_delete(aux_tables, lex->table_count)))
unknown's avatar
unknown committed
3124
    {
3125 3126 3127
      res= mysql_select(thd, &select_lex->ref_pointer_array,
			select_lex->get_table_list(),
			select_lex->with_wild,
3128
			select_lex->item_list,
unknown's avatar
unknown committed
3129
			select_lex->where,
3130
			0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
unknown's avatar
unknown committed
3131
			(ORDER *)NULL,
3132
			(select_lex->options | thd->variables.option_bits |
3133
			SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
3134
                        OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT,
unknown's avatar
unknown committed
3135
			del_result, unit, select_lex);
3136
      res|= thd->is_error();
3137
      MYSQL_MULTI_DELETE_DONE(res, del_result->num_deleted());
3138
      if (res)
3139
        del_result->abort();
unknown's avatar
unknown committed
3140
      delete del_result;
unknown's avatar
unknown committed
3141 3142
    }
    else
3143
    {
3144
      res= TRUE;                                // Error
3145 3146
      MYSQL_MULTI_DELETE_DONE(1, 0);
    }
unknown's avatar
unknown committed
3147 3148
    break;
  }
unknown's avatar
unknown committed
3149
  case SQLCOM_DROP_TABLE:
unknown's avatar
unknown committed
3150
  {
unknown's avatar
VIEW  
unknown committed
3151
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3152 3153
    if (!lex->drop_temporary)
    {
3154
      if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE))
3155 3156
	goto error;				/* purecov: inspected */
    }
unknown's avatar
unknown committed
3157
    else
unknown's avatar
unknown committed
3158
    {
unknown's avatar
unknown committed
3159
      /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
3160
      thd->variables.option_bits|= OPTION_KEEP_LOG;
unknown's avatar
unknown committed
3161
    }
3162
    /* DDL and binlog write order protected by LOCK_open */
unknown's avatar
VIEW  
unknown committed
3163 3164
    res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
			lex->drop_temporary);
unknown's avatar
unknown committed
3165 3166
  }
  break;
unknown's avatar
unknown committed
3167
  case SQLCOM_SHOW_PROCESSLIST:
3168 3169
    if (!thd->security_ctx->priv_user[0] &&
        check_global_access(thd,PROCESS_ACL))
unknown's avatar
unknown committed
3170
      break;
unknown's avatar
unknown committed
3171
    mysqld_list_processes(thd,
3172 3173 3174 3175
			  (thd->security_ctx->master_access & PROCESS_ACL ?
                           NullS :
                           thd->security_ctx->priv_user),
                          lex->verbose);
unknown's avatar
unknown committed
3176
    break;
unknown's avatar
unknown committed
3177 3178 3179
  case SQLCOM_SHOW_AUTHORS:
    res= mysqld_show_authors(thd);
    break;
3180 3181 3182
  case SQLCOM_SHOW_CONTRIBUTORS:
    res= mysqld_show_contributors(thd);
    break;
unknown's avatar
unknown committed
3183 3184 3185
  case SQLCOM_SHOW_PRIVILEGES:
    res= mysqld_show_privileges(thd);
    break;
unknown's avatar
unknown committed
3186
  case SQLCOM_SHOW_ENGINE_LOGS:
unknown's avatar
unknown committed
3187
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
3188 3189
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0));	/* purecov: inspected */
3190
    goto error;
unknown's avatar
unknown committed
3191 3192
#else
    {
Marc Alff's avatar
Marc Alff committed
3193
      if (check_access(thd, FILE_ACL, any_db, NULL, NULL, 0, 0))
unknown's avatar
unknown committed
3194
	goto error;
unknown's avatar
unknown committed
3195
      res= ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_LOGS);
unknown's avatar
unknown committed
3196 3197
      break;
    }
unknown's avatar
unknown committed
3198 3199
#endif
  case SQLCOM_CHANGE_DB:
3200 3201 3202 3203
  {
    LEX_STRING db_str= { (char *) select_lex->db, strlen(select_lex->db) };

    if (!mysql_change_db(thd, &db_str, FALSE))
3204
      my_ok(thd);
3205

unknown's avatar
unknown committed
3206
    break;
3207
  }
3208

unknown's avatar
unknown committed
3209 3210
  case SQLCOM_LOAD:
  {
unknown's avatar
VIEW  
unknown committed
3211
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
3212
    uint privilege= (lex->duplicates == DUP_REPLACE ?
unknown's avatar
unknown committed
3213 3214
		     INSERT_ACL | DELETE_ACL : INSERT_ACL) |
                    (lex->local_file ? 0 : FILE_ACL);
3215

unknown's avatar
unknown committed
3216
    if (lex->local_file)
unknown's avatar
unknown committed
3217
    {
3218
      if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) ||
3219
          !opt_local_infile)
3220
      {
unknown's avatar
unknown committed
3221
	my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), MYF(0));
3222 3223
	goto error;
      }
unknown's avatar
unknown committed
3224
    }
unknown's avatar
unknown committed
3225 3226 3227 3228

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

unknown's avatar
VIEW  
unknown committed
3229
    res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
unknown's avatar
unknown committed
3230
                    lex->update_list, lex->value_list, lex->duplicates,
3231
                    lex->ignore, (bool) lex->local_file);
unknown's avatar
unknown committed
3232 3233
    break;
  }
3234

unknown's avatar
unknown committed
3235
  case SQLCOM_SET_OPTION:
3236 3237
  {
    List<set_var_base> *lex_var_list= &lex->var_list;
3238

3239
    if ((check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
3240
         || open_and_lock_tables(thd, all_tables, TRUE, 0)))
unknown's avatar
unknown committed
3241
      goto error;
3242 3243 3244 3245 3246 3247 3248
    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;
3249
      my_ok(thd);
3250
    }
3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262
    else
    {
      /*
        We encountered some sort of error, but no message was sent.
        Send something semi-generic here since we don't know which
        assignment in the list caused the error.
      */
      if (!thd->is_error())
        my_error(ER_WRONG_ARGUMENTS,MYF(0),"SET");
      goto error;
    }

unknown's avatar
unknown committed
3263
    break;
3264
  }
unknown's avatar
unknown committed
3265

unknown's avatar
unknown committed
3266
  case SQLCOM_UNLOCK_TABLES:
3267 3268 3269 3270 3271 3272
    /*
      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.
    */
Konstantin Osipov's avatar
Konstantin Osipov committed
3273
    thd->locked_tables_list.unlock_locked_tables(thd);
3274
    if (thd->variables.option_bits & OPTION_TABLE_LOCK)
unknown's avatar
unknown committed
3275
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
3276
      trans_commit_implicit(thd);
3277
      thd->mdl_context.release_transactional_locks();
3278
      thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
unknown's avatar
unknown committed
3279
    }
3280 3281
    if (thd->global_read_lock.is_acquired())
      thd->global_read_lock.unlock_global_read_lock(thd);
3282
    my_ok(thd);
unknown's avatar
unknown committed
3283 3284
    break;
  case SQLCOM_LOCK_TABLES:
Konstantin Osipov's avatar
Konstantin Osipov committed
3285
    thd->locked_tables_list.unlock_locked_tables(thd);
3286
    /* we must end the trasaction first, regardless of anything */
Konstantin Osipov's avatar
Konstantin Osipov committed
3287
    if (trans_commit_implicit(thd))
unknown's avatar
unknown committed
3288
      goto error;
3289
    /* release transactional metadata locks. */
3290
    thd->mdl_context.release_transactional_locks();
3291
    if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
3292
                           FALSE, UINT_MAX, FALSE))
3293
      goto error;
3294
    if (lex->protect_against_global_read_lock &&
3295
        thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
3296
      goto error;
Konstantin Osipov's avatar
Konstantin Osipov committed
3297

Konstantin Osipov's avatar
Konstantin Osipov committed
3298
    init_mdl_requests(all_tables);
Konstantin Osipov's avatar
Konstantin Osipov committed
3299

3300
    thd->variables.option_bits|= OPTION_TABLE_LOCK;
Konstantin Osipov's avatar
Konstantin Osipov committed
3301
    thd->in_lock_tables=1;
unknown's avatar
VIEW  
unknown committed
3302

Konstantin Osipov's avatar
Konstantin Osipov committed
3303 3304 3305
    {
      Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;

3306 3307 3308
      res= (open_and_lock_tables(thd, all_tables, FALSE,
                                 MYSQL_OPEN_TAKE_UPGRADABLE_MDL,
                                 &lock_tables_prelocking_strategy) ||
Konstantin Osipov's avatar
Konstantin Osipov committed
3309 3310
            thd->locked_tables_list.init_locked_tables(thd));
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
3311 3312 3313 3314

    thd->in_lock_tables= 0;

    if (res)
3315
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
3316
      /*
3317 3318 3319 3320
        Need to end the current transaction, so the storage engine (InnoDB)
        can free its locks if LOCK TABLES locked some tables before finding
        that it can't lock a table in its list
      */
Konstantin Osipov's avatar
Konstantin Osipov committed
3321 3322
      trans_rollback_stmt(thd);
      trans_commit_implicit(thd);
3323 3324 3325 3326 3327 3328
      /*
        Close tables and release metadata locks otherwise a later call to
        close_thread_tables might not release the locks if autocommit is off.
      */
      close_thread_tables(thd);
      DBUG_ASSERT(!thd->locked_tables_mode);
3329
      thd->mdl_context.release_transactional_locks();
3330
      thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
3331
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
3332 3333 3334 3335 3336 3337 3338 3339
    else
    {
#ifdef HAVE_QUERY_CACHE
      if (thd->variables.query_cache_wlock_invalidate)
        query_cache.invalidate_locked_for_write(first_table);
#endif /*HAVE_QUERY_CACHE*/
      my_ok(thd);
    }
unknown's avatar
unknown committed
3340 3341
    break;
  case SQLCOM_CREATE_DB:
3342
  {
3343 3344 3345 3346 3347 3348
    /*
      As mysql_create_db() may modify HA_CREATE_INFO structure passed to
      it, we need to use a copy of LEX::create_info to make execution
      prepared statement- safe.
    */
    HA_CREATE_INFO create_info(lex->create_info);
unknown's avatar
unknown committed
3349
    char *alias;
3350 3351
    if (!(alias=thd->strmake(lex->name.str, lex->name.length)) ||
        check_db_name(&lex->name))
unknown's avatar
unknown committed
3352
    {
3353
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
unknown's avatar
unknown committed
3354 3355
      break;
    }
3356 3357 3358
    /*
      If in a slave thread :
      CREATE DATABASE DB was certainly not preceded by USE DB.
3359
      For that reason, db_ok() in sql/slave.cc did not check the
3360 3361 3362
      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.
    */
3363
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
3364
    if (thd->slave_thread && 
3365 3366
	(!rpl_filter->db_ok(lex->name.str) ||
	 !rpl_filter->db_ok_with_wild_table(lex->name.str)))
unknown's avatar
unknown committed
3367
    {
unknown's avatar
unknown committed
3368
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
3369
      break;
unknown's avatar
unknown committed
3370
    }
3371
#endif
Marc Alff's avatar
Marc Alff committed
3372
    if (check_access(thd, CREATE_ACL, lex->name.str, NULL, NULL, 1, 0))
3373
      break;
3374 3375 3376 3377 3378 3379
    if (thd->locked_tables_mode)
    {
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
      goto error;
    }
3380
    res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias :
3381
                              lex->name.str), &create_info, 0);
3382 3383
    break;
  }
unknown's avatar
unknown committed
3384
  case SQLCOM_DROP_DB:
3385
  {
3386
    if (check_db_name(&lex->name))
unknown's avatar
unknown committed
3387
    {
3388
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
unknown's avatar
unknown committed
3389 3390
      break;
    }
3391 3392 3393 3394 3395 3396 3397
    /*
      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.
    */
3398
#ifdef HAVE_REPLICATION
3399
    if (thd->slave_thread && 
3400 3401
	(!rpl_filter->db_ok(lex->name.str) ||
	 !rpl_filter->db_ok_with_wild_table(lex->name.str)))
unknown's avatar
unknown committed
3402
    {
unknown's avatar
unknown committed
3403
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
3404
      break;
unknown's avatar
unknown committed
3405
    }
3406
#endif
Marc Alff's avatar
Marc Alff committed
3407
    if (check_access(thd, DROP_ACL, lex->name.str, NULL, NULL, 1, 0))
3408
      break;
3409
    if (thd->locked_tables_mode)
3410
    {
unknown's avatar
unknown committed
3411 3412
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
3413 3414
      goto error;
    }
3415
    res= mysql_rm_db(thd, lex->name.str, lex->drop_if_exists, 0);
3416 3417
    break;
  }
3418
  case SQLCOM_ALTER_DB_UPGRADE:
unknown's avatar
unknown committed
3419
  {
3420
    LEX_STRING *db= & lex->name;
unknown's avatar
unknown committed
3421 3422
#ifdef HAVE_REPLICATION
    if (thd->slave_thread && 
3423 3424
       (!rpl_filter->db_ok(db->str) ||
        !rpl_filter->db_ok_with_wild_table(db->str)))
unknown's avatar
unknown committed
3425 3426 3427 3428 3429 3430
    {
      res= 1;
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
      break;
    }
#endif
3431
    if (check_db_name(db))
3432
    {
3433
      my_error(ER_WRONG_DB_NAME, MYF(0), db->str);
3434 3435
      break;
    }
Marc Alff's avatar
Marc Alff committed
3436 3437 3438
    if (check_access(thd, ALTER_ACL, db->str, NULL, NULL, 1, 0) ||
        check_access(thd, DROP_ACL, db->str, NULL, NULL, 1, 0) ||
        check_access(thd, CREATE_ACL, db->str, NULL, NULL, 1, 0))
unknown's avatar
unknown committed
3439 3440 3441 3442
    {
      res= 1;
      break;
    }
3443
    if (thd->locked_tables_mode)
unknown's avatar
unknown committed
3444 3445 3446 3447 3448 3449
    {
      res= 1;
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
      goto error;
    }
3450 3451

    res= mysql_upgrade_db(thd, db);
unknown's avatar
unknown committed
3452
    if (!res)
3453
      my_ok(thd);
unknown's avatar
unknown committed
3454 3455
    break;
  }
3456 3457
  case SQLCOM_ALTER_DB:
  {
3458
    LEX_STRING *db= &lex->name;
3459
    HA_CREATE_INFO create_info(lex->create_info);
3460
    if (check_db_name(db))
3461
    {
3462
      my_error(ER_WRONG_DB_NAME, MYF(0), db->str);
3463 3464
      break;
    }
unknown's avatar
unknown committed
3465 3466 3467
    /*
      If in a slave thread :
      ALTER DATABASE DB may not be preceded by USE DB.
3468
      For that reason, maybe db_ok() in sql/slave.cc did not check the
unknown's avatar
unknown committed
3469 3470 3471 3472
      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
3473
    if (thd->slave_thread &&
3474 3475
	(!rpl_filter->db_ok(db->str) ||
	 !rpl_filter->db_ok_with_wild_table(db->str)))
unknown's avatar
unknown committed
3476
    {
unknown's avatar
unknown committed
3477
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
unknown's avatar
unknown committed
3478 3479 3480
      break;
    }
#endif
Marc Alff's avatar
Marc Alff committed
3481
    if (check_access(thd, ALTER_ACL, db->str, NULL, NULL, 1, 0))
3482
      break;
3483
    if (thd->locked_tables_mode)
3484
    {
unknown's avatar
unknown committed
3485 3486
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
3487 3488
      goto error;
    }
3489
    res= mysql_alter_db(thd, db->str, &create_info);
3490 3491
    break;
  }
unknown's avatar
unknown committed
3492 3493
  case SQLCOM_SHOW_CREATE_DB:
  {
unknown's avatar
unknown committed
3494 3495
    DBUG_EXECUTE_IF("4x_server_emul",
                    my_error(ER_UNKNOWN_ERROR, MYF(0)); goto error;);
3496
    if (check_db_name(&lex->name))
unknown's avatar
unknown committed
3497
    {
3498
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
unknown's avatar
unknown committed
3499 3500
      break;
    }
3501
    res= mysqld_show_create_db(thd, lex->name.str, &lex->create_info);
unknown's avatar
unknown committed
3502 3503
    break;
  }
unknown's avatar
unknown committed
3504 3505
  case SQLCOM_CREATE_EVENT:
  case SQLCOM_ALTER_EVENT:
3506
  #ifdef HAVE_EVENT_SCHEDULER
3507
  do
unknown's avatar
unknown committed
3508
  {
unknown's avatar
unknown committed
3509
    DBUG_ASSERT(lex->event_parse_data);
unknown's avatar
unknown committed
3510 3511 3512 3513 3514 3515
    if (lex->table_or_sp_used())
    {
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored "
               "function calls as part of this statement");
      break;
    }
3516 3517 3518 3519 3520

    res= sp_process_definer(thd);
    if (res)
      break;

unknown's avatar
unknown committed
3521 3522
    switch (lex->sql_command) {
    case SQLCOM_CREATE_EVENT:
3523 3524 3525 3526
    {
      bool if_not_exists= (lex->create_info.options &
                           HA_LEX_CREATE_IF_NOT_EXISTS);
      res= Events::create_event(thd, lex->event_parse_data, if_not_exists);
unknown's avatar
unknown committed
3527
      break;
3528
    }
unknown's avatar
unknown committed
3529
    case SQLCOM_ALTER_EVENT:
3530 3531 3532
      res= Events::update_event(thd, lex->event_parse_data,
                                lex->spname ? &lex->spname->m_db : NULL,
                                lex->spname ? &lex->spname->m_name : NULL);
unknown's avatar
unknown committed
3533
      break;
3534 3535
    default:
      DBUG_ASSERT(0);
unknown's avatar
unknown committed
3536
    }
3537
    DBUG_PRINT("info",("DDL error code=%d", res));
unknown's avatar
unknown committed
3538
    if (!res)
3539
      my_ok(thd);
unknown's avatar
unknown committed
3540

3541 3542 3543 3544 3545 3546
  } while (0);
  /* Don't do it, if we are inside a SP */
  if (!thd->spcont)
  {
    delete lex->sphead;
    lex->sphead= NULL;
unknown's avatar
unknown committed
3547
  }
3548 3549
  /* lex->unit.cleanup() is called outside, no need to call it here */
  break;
unknown's avatar
unknown committed
3550
  case SQLCOM_SHOW_CREATE_EVENT:
3551 3552 3553 3554 3555 3556 3557
    res= Events::show_create_event(thd, lex->spname->m_db,
                                   lex->spname->m_name);
    break;
  case SQLCOM_DROP_EVENT:
    if (!(res= Events::drop_event(thd,
                                  lex->spname->m_db, lex->spname->m_name,
                                  lex->drop_if_exists)))
3558
      my_ok(thd);
3559
    break;
3560 3561 3562 3563
#else
    my_error(ER_NOT_SUPPORTED_YET,MYF(0),"embedded server");
    break;
#endif
unknown's avatar
unknown committed
3564
  case SQLCOM_CREATE_FUNCTION:                  // UDF function
unknown's avatar
unknown committed
3565
  {
Marc Alff's avatar
Marc Alff committed
3566
    if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 0))
unknown's avatar
unknown committed
3567
      break;
unknown's avatar
unknown committed
3568
#ifdef HAVE_DLOPEN
3569
    if (!(res = mysql_create_function(thd, &lex->udf)))
3570
      my_ok(thd);
unknown's avatar
unknown committed
3571
#else
unknown's avatar
unknown committed
3572
    my_error(ER_CANT_OPEN_LIBRARY, MYF(0), lex->udf.dl, 0, "feature disabled");
unknown's avatar
unknown committed
3573
    res= TRUE;
unknown's avatar
unknown committed
3574 3575
#endif
    break;
unknown's avatar
unknown committed
3576
  }
unknown's avatar
unknown committed
3577
#ifndef NO_EMBEDDED_ACCESS_CHECKS
3578 3579
  case SQLCOM_CREATE_USER:
  {
Marc Alff's avatar
Marc Alff committed
3580
    if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 1) &&
3581
        check_global_access(thd,CREATE_USER_ACL))
3582
      break;
3583
    /* Conditionally writes to binlog */
3584
    if (!(res= mysql_create_user(thd, lex->users_list)))
3585
      my_ok(thd);
3586 3587
    break;
  }
3588 3589
  case SQLCOM_DROP_USER:
  {
Marc Alff's avatar
Marc Alff committed
3590
    if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 1) &&
3591
        check_global_access(thd,CREATE_USER_ACL))
3592
      break;
3593
    /* Conditionally writes to binlog */
3594
    if (!(res= mysql_drop_user(thd, lex->users_list)))
3595
      my_ok(thd);
3596 3597 3598 3599
    break;
  }
  case SQLCOM_RENAME_USER:
  {
Marc Alff's avatar
Marc Alff committed
3600
    if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1) &&
3601
        check_global_access(thd,CREATE_USER_ACL))
3602
      break;
3603
    /* Conditionally writes to binlog */
3604
    if (!(res= mysql_rename_user(thd, lex->users_list)))
3605
      my_ok(thd);
3606 3607 3608 3609
    break;
  }
  case SQLCOM_REVOKE_ALL:
  {
Marc Alff's avatar
Marc Alff committed
3610
    if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1) &&
3611
        check_global_access(thd,CREATE_USER_ACL))
3612
      break;
3613
    /* Conditionally writes to binlog */
3614
    if (!(res = mysql_revoke_all(thd, lex->users_list)))
3615
      my_ok(thd);
3616 3617
    break;
  }
3618 3619 3620 3621
  case SQLCOM_REVOKE:
  case SQLCOM_GRANT:
  {
    if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
Marc Alff's avatar
Marc Alff committed
3622 3623 3624 3625
                     first_table ?  first_table->db : select_lex->db,
                     first_table ? &first_table->grant.privilege : NULL,
                     first_table ? &first_table->grant.m_internal : NULL,
                     first_table ? 0 : 1, 0))
3626 3627
      goto error;

3628
    if (thd->security_ctx->user)              // If not replication
unknown's avatar
unknown committed
3629
    {
3630
      LEX_USER *user, *tmp_user;
3631

unknown's avatar
unknown committed
3632
      List_iterator <LEX_USER> user_list(lex->users_list);
3633
      while ((tmp_user= user_list++))
unknown's avatar
unknown committed
3634
      {
3635 3636
        if (!(user= get_current_user(thd, tmp_user)))
          goto error;
3637 3638 3639 3640 3641 3642 3643 3644
        if (specialflag & SPECIAL_NO_RESOLVE &&
            hostname_requires_resolving(user->host.str))
          push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                              ER_WARN_HOSTNAME_WONT_WORK,
                              ER(ER_WARN_HOSTNAME_WONT_WORK),
                              user->host.str);
        // Are we trying to change a password of another user
        DBUG_ASSERT(user->host.str != 0);
3645
        if (strcmp(thd->security_ctx->user, user->user.str) ||
3646
            my_strcasecmp(system_charset_info,
3647
                          user->host.str, thd->security_ctx->host_or_ip))
3648 3649
        {
          // TODO: use check_change_password()
3650 3651
          if (is_acl_user(user->host.str, user->user.str) &&
              user->password.str &&
Marc Alff's avatar
Marc Alff committed
3652
              check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1))
3653 3654 3655 3656 3657 3658
          {
            my_message(ER_PASSWORD_NOT_ALLOWED,
                       ER(ER_PASSWORD_NOT_ALLOWED), MYF(0));
            goto error;
          }
        }
unknown's avatar
SCRUM  
unknown committed
3659 3660
      }
    }
unknown's avatar
VIEW  
unknown committed
3661
    if (first_table)
3662
    {
3663 3664
      if (lex->type == TYPE_ENUM_PROCEDURE ||
          lex->type == TYPE_ENUM_FUNCTION)
3665 3666 3667 3668
      {
        uint grants= lex->all_privileges 
		   ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL)
		   : lex->grant;
3669
        if (check_grant_routine(thd, grants | GRANT_ACL, all_tables,
3670
                                lex->type == TYPE_ENUM_PROCEDURE, 0))
3671
	  goto error;
3672
        /* Conditionally writes to binlog */
3673 3674 3675
        res= mysql_routine_grant(thd, all_tables,
                                 lex->type == TYPE_ENUM_PROCEDURE, 
                                 lex->users_list, grants,
3676 3677 3678
                                 lex->sql_command == SQLCOM_REVOKE, TRUE);
        if (!res)
          my_ok(thd);
3679 3680 3681
      }
      else
      {
3682
	if (check_grant(thd,(lex->grant | lex->grant_tot_col | GRANT_ACL),
3683
                        all_tables, FALSE, UINT_MAX, FALSE))
3684
	  goto error;
3685
        /* Conditionally writes to binlog */
3686 3687 3688 3689
        res= mysql_table_grant(thd, all_tables, lex->users_list,
			       lex->columns, lex->grant,
			       lex->sql_command == SQLCOM_REVOKE);
      }
3690 3691 3692
    }
    else
    {
3693
      if (lex->columns.elements || lex->type)
3694
      {
unknown's avatar
unknown committed
3695 3696
	my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
                   MYF(0));
unknown's avatar
unknown committed
3697
        goto error;
3698 3699
      }
      else
3700
	/* Conditionally writes to binlog */
3701 3702 3703 3704
	res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
			  lex->sql_command == SQLCOM_REVOKE);
      if (!res)
      {
3705
	if (lex->sql_command == SQLCOM_GRANT)
3706
	{
unknown's avatar
unknown committed
3707
	  List_iterator <LEX_USER> str_list(lex->users_list);
3708 3709 3710 3711 3712
	  LEX_USER *user, *tmp_user;
	  while ((tmp_user=str_list++))
          {
            if (!(user= get_current_user(thd, tmp_user)))
              goto error;
3713
	    reset_mqh(user, 0);
3714
          }
3715
	}
3716 3717 3718 3719
      }
    }
    break;
  }
unknown's avatar
SCRUM  
unknown committed
3720
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
unknown's avatar
unknown committed
3721
  case SQLCOM_RESET:
3722 3723 3724
    /*
      RESET commands are never written to the binary log, so we have to
      initialize this variable because RESET shares the same code as FLUSH
3725 3726 3727 3728
    */
    lex->no_write_to_binlog= 1;
  case SQLCOM_FLUSH:
  {
unknown's avatar
unknown committed
3729
    bool write_to_binlog;
unknown's avatar
unknown committed
3730
    if (check_global_access(thd,RELOAD_ACL))
unknown's avatar
unknown committed
3731
      goto error;
3732

3733 3734 3735 3736
    /*
      reload_acl_and_cache() will tell us if we are allowed to write to the
      binlog or not.
    */
unknown's avatar
unknown committed
3737
    if (!reload_acl_and_cache(thd, lex->type, first_table, &write_to_binlog))
3738 3739 3740 3741 3742
    {
      /*
        We WANT to write and we CAN write.
        ! we write after unlocking the table.
      */
3743 3744 3745
      /*
        Presumably, RESET and binlog writing doesn't require synchronization
      */
3746 3747
      if (!lex->no_write_to_binlog && write_to_binlog)
      {
Davi Arnaut's avatar
Davi Arnaut committed
3748
        if ((res= write_bin_log(thd, FALSE, thd->query(), thd->query_length())))
3749
          break;
3750
      }
3751
      my_ok(thd);
3752 3753
    } 
    
unknown's avatar
unknown committed
3754
    break;
3755
  }
unknown's avatar
unknown committed
3756
  case SQLCOM_KILL:
3757 3758 3759
  {
    Item *it= (Item *)lex->value_list.head();

unknown's avatar
unknown committed
3760 3761 3762 3763 3764 3765 3766
    if (lex->table_or_sp_used())
    {
      my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored "
               "function calls as part of this statement");
      break;
    }

3767
    if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
3768 3769 3770 3771 3772
    {
      my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
		 MYF(0));
      goto error;
    }
3773
    sql_kill(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY);
unknown's avatar
unknown committed
3774
    break;
3775
  }
unknown's avatar
unknown committed
3776
#ifndef NO_EMBEDDED_ACCESS_CHECKS
unknown's avatar
unknown committed
3777
  case SQLCOM_SHOW_GRANTS:
3778 3779 3780 3781
  {
    LEX_USER *grant_user= get_current_user(thd, lex->grant_user);
    if (!grant_user)
      goto error;
3782
    if ((thd->security_ctx->priv_user &&
3783
	 !strcmp(thd->security_ctx->priv_user, grant_user->user.str)) ||
Marc Alff's avatar
Marc Alff committed
3784
        !check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 0))
unknown's avatar
unknown committed
3785
    {
3786
      res = mysql_show_grants(thd, grant_user);
unknown's avatar
unknown committed
3787 3788
    }
    break;
3789
  }
unknown's avatar
unknown committed
3790
#endif
3791
  case SQLCOM_HA_OPEN:
unknown's avatar
VIEW  
unknown committed
3792
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3793
    if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE))
3794
      goto error;
unknown's avatar
unknown committed
3795
    res= mysql_ha_open(thd, first_table, 0);
3796 3797
    break;
  case SQLCOM_HA_CLOSE:
unknown's avatar
VIEW  
unknown committed
3798 3799
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    res= mysql_ha_close(thd, first_table);
3800 3801
    break;
  case SQLCOM_HA_READ:
unknown's avatar
VIEW  
unknown committed
3802
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3803 3804 3805 3806 3807
    /*
      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.
    */
3808
    unit->set_limit(select_lex);
3809
    res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->ident.str,
unknown's avatar
VIEW  
unknown committed
3810
                       lex->insert_list, lex->ha_rkey_mode, select_lex->where,
3811
                       unit->select_limit_cnt, unit->offset_limit_cnt);
3812 3813
    break;

unknown's avatar
unknown committed
3814
  case SQLCOM_BEGIN:
Konstantin Osipov's avatar
Konstantin Osipov committed
3815
    if (trans_begin(thd, lex->start_transaction_opt))
unknown's avatar
unknown committed
3816
      goto error;
3817
    my_ok(thd);
unknown's avatar
unknown committed
3818 3819
    break;
  case SQLCOM_COMMIT:
3820 3821
    DBUG_ASSERT(thd->lock == NULL ||
                thd->locked_tables_mode == LTM_LOCK_TABLES);
Konstantin Osipov's avatar
Konstantin Osipov committed
3822 3823
    if (trans_commit(thd))
      goto error;
3824
    thd->mdl_context.release_transactional_locks();
Konstantin Osipov's avatar
Konstantin Osipov committed
3825 3826
    /* Begin transaction with the same isolation level. */
    if (lex->tx_chain && trans_begin(thd))
unknown's avatar
unknown committed
3827
      goto error;
Konstantin Osipov's avatar
Konstantin Osipov committed
3828 3829 3830
    /* Disconnect the current client connection. */
    if (lex->tx_release)
      thd->killed= THD::KILL_CONNECTION;
3831
    my_ok(thd);
unknown's avatar
unknown committed
3832 3833
    break;
  case SQLCOM_ROLLBACK:
3834 3835
    DBUG_ASSERT(thd->lock == NULL ||
                thd->locked_tables_mode == LTM_LOCK_TABLES);
Konstantin Osipov's avatar
Konstantin Osipov committed
3836
    if (trans_rollback(thd))
unknown's avatar
unknown committed
3837
      goto error;
3838
    thd->mdl_context.release_transactional_locks();
Konstantin Osipov's avatar
Konstantin Osipov committed
3839 3840 3841 3842 3843 3844
    /* Begin transaction with the same isolation level. */
    if (lex->tx_chain && trans_begin(thd))
      goto error;
    /* Disconnect the current client connection. */
    if (lex->tx_release)
      thd->killed= THD::KILL_CONNECTION;
3845
    my_ok(thd);
unknown's avatar
unknown committed
3846
    break;
unknown's avatar
unknown committed
3847
  case SQLCOM_RELEASE_SAVEPOINT:
Konstantin Osipov's avatar
Konstantin Osipov committed
3848 3849 3850
    if (trans_release_savepoint(thd, lex->ident))
      goto error;
    my_ok(thd);
unknown's avatar
unknown committed
3851
    break;
unknown's avatar
unknown committed
3852
  case SQLCOM_ROLLBACK_TO_SAVEPOINT:
Konstantin Osipov's avatar
Konstantin Osipov committed
3853 3854 3855
    if (trans_rollback_to_savepoint(thd, lex->ident))
      goto error;
    my_ok(thd);
unknown's avatar
unknown committed
3856
    break;
3857
  case SQLCOM_SAVEPOINT:
Konstantin Osipov's avatar
Konstantin Osipov committed
3858 3859 3860
    if (trans_savepoint(thd, lex->ident))
      goto error;
    my_ok(thd);
unknown's avatar
unknown committed
3861
    break;
3862 3863
  case SQLCOM_CREATE_PROCEDURE:
  case SQLCOM_CREATE_SPFUNCTION:
unknown's avatar
unknown committed
3864
  {
3865
    uint namelen;
unknown's avatar
unknown committed
3866
    char *name;
unknown's avatar
unknown committed
3867
    int sp_result= SP_INTERNAL_ERROR;
3868

3869
    DBUG_ASSERT(lex->sphead != 0);
unknown's avatar
unknown committed
3870
    DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */
3871 3872 3873 3874
    /*
      Verify that the database name is allowed, optionally
      lowercase it.
    */
3875
    if (check_db_name(&lex->sphead->m_db))
3876
    {
3877
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->sphead->m_db.str);
3878
      goto create_sp_error;
3879 3880
    }

3881
    /*
3882 3883 3884
      Check that a database directory with this name
      exists. Design note: This won't work on virtual databases
      like information_schema.
3885 3886
    */
    if (check_db_dir_existence(lex->sphead->m_db.str))
3887
    {
3888
      my_error(ER_BAD_DB_ERROR, MYF(0), lex->sphead->m_db.str);
3889
      goto create_sp_error;
3890
    }
3891

Marc Alff's avatar
Marc Alff committed
3892 3893
    if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str,
                     NULL, NULL, 0, 0))
3894
      goto create_sp_error;
3895 3896

    name= lex->sphead->name(&namelen);
3897
#ifdef HAVE_DLOPEN
unknown's avatar
unknown committed
3898 3899 3900
    if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
    {
      udf_func *udf = find_udf(name, namelen);
3901

unknown's avatar
unknown committed
3902
      if (udf)
3903
      {
3904 3905
        my_error(ER_UDF_EXISTS, MYF(0), name);
        goto create_sp_error;
3906
      }
unknown's avatar
unknown committed
3907 3908 3909
    }
#endif

3910 3911
    if (sp_process_definer(thd))
      goto create_sp_error;
3912

Konstantin Osipov's avatar
Konstantin Osipov committed
3913
    res= (sp_result= sp_create_routine(thd, lex->sphead->m_type, lex->sphead));
unknown's avatar
unknown committed
3914
    switch (sp_result) {
3915
    case SP_OK: {
unknown's avatar
unknown committed
3916
#ifndef NO_EMBEDDED_ACCESS_CHECKS
3917
      /* only add privileges if really neccessary */
3918 3919 3920 3921 3922

      Security_context security_context;
      bool restore_backup_context= false;
      Security_context *backup= NULL;
      LEX_USER *definer= thd->lex->definer;
Konstantin Osipov's avatar
Konstantin Osipov committed
3923 3924 3925 3926 3927 3928 3929 3930 3931 3932
      /*
        We're going to issue an implicit GRANT statement.
        It takes metadata locks and updates system tables.
        Make sure that sp_create_routine() did not leave any
        locks in the MDL context, so there is no risk to
        deadlock.
      */
      trans_commit_implicit(thd);
      close_thread_tables(thd);
      thd->mdl_context.release_transactional_locks();
3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950
      /*
        Check if the definer exists on slave, 
        then use definer privilege to insert routine privileges to mysql.procs_priv.

        For current user of SQL thread has GLOBAL_ACL privilege, 
        which doesn't any check routine privileges, 
        so no routine privilege record  will insert into mysql.procs_priv.
      */
      if (thd->slave_thread && is_acl_user(definer->host.str, definer->user.str))
      {
        security_context.change_security_context(thd, 
                                                 &thd->lex->definer->user,
                                                 &thd->lex->definer->host,
                                                 &thd->lex->sphead->m_db,
                                                 &backup);
        restore_backup_context= true;
      }

3951
      if (sp_automatic_privileges && !opt_noacl &&
3952
          check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS,
3953
                               lex->sphead->m_db.str, name,
3954
                               lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1))
3955
      {
unknown's avatar
unknown committed
3956
        if (sp_grant_privileges(thd, lex->sphead->m_db.str, name,
3957
                                lex->sql_command == SQLCOM_CREATE_PROCEDURE))
3958 3959 3960
          push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                       ER_PROC_AUTO_GRANT_FAIL,
                       ER(ER_PROC_AUTO_GRANT_FAIL));
3961
      }
3962 3963 3964 3965 3966 3967 3968 3969 3970 3971

      /*
        Restore current user with GLOBAL_ACL privilege of SQL thread
      */ 
      if (restore_backup_context)
      {
        DBUG_ASSERT(thd->slave_thread == 1);
        thd->security_ctx->restore_security_context(thd, backup);
      }

unknown's avatar
unknown committed
3972
#endif
unknown's avatar
unknown committed
3973
    break;
3974
    }
3975 3976 3977 3978 3979 3980 3981 3982 3983
    case SP_WRITE_ROW_FAILED:
      my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name);
    break;
    case SP_BAD_IDENTIFIER:
      my_error(ER_TOO_LONG_IDENT, MYF(0), name);
    break;
    case SP_BODY_TOO_LONG:
      my_error(ER_TOO_LONG_BODY, MYF(0), name);
    break;
3984 3985 3986
    case SP_FLD_STORE_FAILED:
      my_error(ER_CANT_CREATE_SROUTINE, MYF(0), name);
      break;
3987 3988 3989 3990 3991 3992 3993 3994 3995 3996
    default:
      my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name);
    break;
    } /* end switch */

    /*
      Capture all errors within this CASE and
      clean up the environment.
    */
create_sp_error:
unknown's avatar
unknown committed
3997
    if (sp_result != SP_OK )
3998
      goto error;
3999
    my_ok(thd);
4000 4001
    break; /* break super switch */
  } /* end case group bracket */
4002 4003 4004
  case SQLCOM_CALL:
    {
      sp_head *sp;
4005 4006 4007 4008
      /*
        This will cache all SP and SF and open and lock all tables
        required for execution.
      */
4009 4010
      if (check_table_access(thd, SELECT_ACL, all_tables, FALSE,
                             UINT_MAX, FALSE) ||
4011
          open_and_lock_tables(thd, all_tables, TRUE, 0))
4012 4013 4014
       goto error;

      /*
4015 4016
        By this moment all needed SPs should be in cache so no need to look 
        into DB. 
4017
      */
4018 4019
      if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
                                &thd->sp_proc_cache, TRUE)))
4020
      {
4021
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
unknown's avatar
unknown committed
4022
                 lex->spname->m_qname.str);
4023
	goto error;
4024 4025 4026
      }
      else
      {
unknown's avatar
unknown committed
4027
	ha_rows select_limit;
unknown's avatar
unknown committed
4028 4029
        /* bits that should be cleared in thd->server_status */
	uint bits_to_be_cleared= 0;
4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041
        /*
          Check that the stored procedure doesn't contain Dynamic SQL
          and doesn't return result sets: such stored procedures can't
          be called from a function or trigger.
        */
        if (thd->in_sub_stmt)
        {
          const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ?
                              "trigger" : "function");
          if (sp->is_not_allowed_in_function(where))
            goto error;
        }
4042

4043
	if (sp->m_flags & sp_head::MULTI_RESULTS)
4044
	{
4045
	  if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS))
4046
	  {
4047 4048 4049 4050
            /*
              The client does not support multiple result sets being sent
              back
            */
4051
	    my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str);
4052 4053
	    goto error;
	  }
unknown's avatar
unknown committed
4054 4055 4056 4057 4058 4059 4060
          /*
            If SERVER_MORE_RESULTS_EXISTS is not set,
            then remember that it should be cleared
          */
	  bits_to_be_cleared= (~thd->server_status &
                               SERVER_MORE_RESULTS_EXISTS);
	  thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
4061 4062
	}

4063
	if (check_routine_access(thd, EXECUTE_ACL,
4064
				 sp->m_db.str, sp->m_name.str, TRUE, FALSE))
4065 4066 4067
	{
	  goto error;
	}
unknown's avatar
unknown committed
4068 4069
	select_limit= thd->variables.select_limit;
	thd->variables.select_limit= HA_POS_ERROR;
4070

4071
        /* 
4072
          We never write CALL statements into binlog:
4073 4074 4075 4076 4077
           - If the mode is non-prelocked, each statement will be logged
             separately.
           - If the mode is prelocked, the invoking statement will care
             about writing into binlog.
          So just execute the statement.
4078
        */
4079
	res= sp->execute_procedure(thd, &lex->value_list);
4080

unknown's avatar
unknown committed
4081
	thd->variables.select_limit= select_limit;
4082

unknown's avatar
unknown committed
4083
        thd->server_status&= ~bits_to_be_cleared;
4084

unknown's avatar
unknown committed
4085
	if (!res)
4086 4087
          my_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 :
                              thd->row_count_func));
4088
	else
4089
        {
4090
          DBUG_ASSERT(thd->is_error() || thd->killed);
4091
	  goto error;		// Substatement should already have sent error
4092
        }
4093
      }
4094
      break;
4095 4096
    }
  case SQLCOM_ALTER_PROCEDURE:
4097
  case SQLCOM_ALTER_FUNCTION:
4098
    {
unknown's avatar
unknown committed
4099
      int sp_result;
Konstantin Osipov's avatar
Konstantin Osipov committed
4100 4101
      int type= (lex->sql_command == SQLCOM_ALTER_PROCEDURE ?
                 TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
unknown's avatar
unknown committed
4102

Konstantin Osipov's avatar
Konstantin Osipov committed
4103 4104 4105 4106
      if (check_routine_access(thd, ALTER_PROC_ACL, lex->spname->m_db.str,
                               lex->spname->m_name.str,
                               lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0))
        goto error;
unknown's avatar
unknown committed
4107

Konstantin Osipov's avatar
Konstantin Osipov committed
4108 4109 4110 4111 4112 4113 4114 4115
      /*
        Note that if you implement the capability of ALTER FUNCTION to
        alter the body of the function, this command should be made to
        follow the restrictions that log-bin-trust-function-creators=0
        already puts on CREATE FUNCTION.
      */
      /* Conditionally writes to binlog */
      sp_result= sp_update_routine(thd, type, lex->spname, &lex->sp_chistics);
unknown's avatar
unknown committed
4116
      switch (sp_result)
4117
      {
unknown's avatar
unknown committed
4118
      case SP_OK:
4119
	my_ok(thd);
unknown's avatar
unknown committed
4120 4121
	break;
      case SP_KEY_NOT_FOUND:
4122 4123
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
unknown's avatar
unknown committed
4124 4125
	goto error;
      default:
4126 4127
	my_error(ER_SP_CANT_ALTER, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
unknown's avatar
unknown committed
4128
	goto error;
4129
      }
4130
      break;
4131 4132
    }
  case SQLCOM_DROP_PROCEDURE:
4133
  case SQLCOM_DROP_FUNCTION:
4134
    {
4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175
#ifdef HAVE_DLOPEN
      if (lex->sql_command == SQLCOM_DROP_FUNCTION &&
          ! lex->spname->m_explicit_name)
      {
        /* DROP FUNCTION <non qualified name> */
        udf_func *udf = find_udf(lex->spname->m_name.str,
                                 lex->spname->m_name.length);
        if (udf)
        {
          if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 0))
            goto error;

          if (!(res = mysql_drop_function(thd, &lex->spname->m_name)))
          {
            my_ok(thd);
            break;
          }
          my_error(ER_SP_DROP_FAILED, MYF(0),
                   "FUNCTION (UDF)", lex->spname->m_name.str);
          goto error;
        }

        if (lex->spname->m_db.str == NULL)
        {
          if (lex->drop_if_exists)
          {
            push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                                ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
                                "FUNCTION (UDF)", lex->spname->m_name.str);
            res= FALSE;
            my_ok(thd);
            break;
          }
          my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                   "FUNCTION (UDF)", lex->spname->m_name.str);
          goto error;
        }
        /* Fall thought to test for a stored function */
      }
#endif

unknown's avatar
unknown committed
4176
      int sp_result;
4177 4178
      int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
                 TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
4179

Konstantin Osipov's avatar
Konstantin Osipov committed
4180 4181 4182 4183 4184 4185
      /*
        @todo: here we break the metadata locking protocol by
        looking up the information about the routine without
        a metadata lock. Rewrite this piece to make sp_drop_routine
        return whether the routine existed or not.
      */
unknown's avatar
unknown committed
4186
      sp_result= sp_routine_exists_in_table(thd, type, lex->spname);
Marc Alff's avatar
Marc Alff committed
4187
      thd->warning_info->opt_clear_warning_info(thd->query_id);
unknown's avatar
unknown committed
4188
      if (sp_result == SP_OK)
4189
      {
4190 4191 4192
        char *db= lex->spname->m_db.str;
	char *name= lex->spname->m_name.str;

4193 4194
	if (check_routine_access(thd, ALTER_PROC_ACL, db, name,
                                 lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
unknown's avatar
merge  
unknown committed
4195
          goto error;
4196

Konstantin Osipov's avatar
Konstantin Osipov committed
4197 4198
        /* Conditionally writes to binlog */
        sp_result= sp_drop_routine(thd, type, lex->spname);
4199

Konstantin Osipov's avatar
Konstantin Osipov committed
4200 4201 4202 4203 4204 4205 4206 4207 4208
#ifndef NO_EMBEDDED_ACCESS_CHECKS
        /*
          We're going to issue an implicit REVOKE statement.
          It takes metadata locks and updates system tables.
          Make sure that sp_create_routine() did not leave any
          locks in the MDL context, so there is no risk to
          deadlock.
        */
        trans_commit_implicit(thd);
4209
        close_thread_tables(thd);
4210
        thd->mdl_context.release_transactional_locks();
4211

4212
	if (sp_automatic_privileges && !opt_noacl &&
Konstantin Osipov's avatar
Konstantin Osipov committed
4213
	    sp_revoke_privileges(thd, db, name,
4214
                                 lex->sql_command == SQLCOM_DROP_PROCEDURE))
4215
	{
Konstantin Osipov's avatar
Konstantin Osipov committed
4216
	  push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
4217 4218 4219
		       ER_PROC_AUTO_REVOKE_FAIL,
		       ER(ER_PROC_AUTO_REVOKE_FAIL));
	}
unknown's avatar
unknown committed
4220
#endif
4221
      }
unknown's avatar
unknown committed
4222 4223
      res= sp_result;
      switch (sp_result) {
4224
      case SP_OK:
4225
	my_ok(thd);
4226 4227
	break;
      case SP_KEY_NOT_FOUND:
4228 4229
	if (lex->drop_if_exists)
	{
4230
          res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
4231
	  push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
4232
			      ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
4233
                              SP_COM_STRING(lex), lex->spname->m_qname.str);
4234 4235
          if (!res)
            my_ok(thd);
4236 4237
	  break;
	}
4238 4239
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
4240 4241
	goto error;
      default:
4242 4243
	my_error(ER_SP_DROP_FAILED, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
4244
	goto error;
4245
      }
4246
      break;
4247
    }
unknown's avatar
unknown committed
4248 4249
  case SQLCOM_SHOW_CREATE_PROC:
    {
unknown's avatar
unknown committed
4250
      if (sp_show_create_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname))
Konstantin Osipov's avatar
Konstantin Osipov committed
4251
        goto error;
unknown's avatar
unknown committed
4252 4253 4254 4255
      break;
    }
  case SQLCOM_SHOW_CREATE_FUNC:
    {
unknown's avatar
unknown committed
4256
      if (sp_show_create_routine(thd, TYPE_ENUM_FUNCTION, lex->spname))
unknown's avatar
unknown committed
4257 4258 4259
	goto error;
      break;
    }
unknown's avatar
unknown committed
4260 4261 4262
  case SQLCOM_SHOW_PROC_CODE:
  case SQLCOM_SHOW_FUNC_CODE:
    {
4263
#ifndef DBUG_OFF
unknown's avatar
unknown committed
4264
      sp_head *sp;
Konstantin Osipov's avatar
Konstantin Osipov committed
4265 4266
      int type= (lex->sql_command == SQLCOM_SHOW_PROC_CODE ?
                 TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
unknown's avatar
unknown committed
4267

Konstantin Osipov's avatar
Konstantin Osipov committed
4268 4269
      if (sp_cache_routine(thd, type, lex->spname, FALSE, &sp))
        goto error;
4270
      if (!sp || sp->show_routine_code(thd))
4271 4272
      {
        /* We don't distinguish between errors for now */
unknown's avatar
unknown committed
4273 4274 4275 4276 4277
        my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_name.str);
        goto error;
      }
      break;
4278 4279 4280 4281
#else
      my_error(ER_FEATURE_DISABLED, MYF(0),
               "SHOW PROCEDURE|FUNCTION CODE", "--with-debug");
      goto error;
unknown's avatar
unknown committed
4282
#endif // ifndef DBUG_OFF
4283
    }
unknown's avatar
unknown committed
4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296
  case SQLCOM_SHOW_CREATE_TRIGGER:
    {
      if (lex->spname->m_name.length > NAME_LEN)
      {
        my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str);
        goto error;
      }

      if (show_create_trigger(thd, lex->spname))
        goto error; /* Error has been already logged. */

      break;
    }
unknown's avatar
VIEW  
unknown committed
4297 4298
  case SQLCOM_CREATE_VIEW:
    {
4299 4300 4301 4302
      /*
        Note: SQLCOM_CREATE_VIEW also handles 'ALTER VIEW' commands
        as specified through the thd->lex->create_view_mode flag.
      */
4303
      res= mysql_create_view(thd, first_table, thd->lex->create_view_mode);
unknown's avatar
VIEW  
unknown committed
4304 4305 4306 4307
      break;
    }
  case SQLCOM_DROP_VIEW:
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
4308
      if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE))
unknown's avatar
unknown committed
4309
        goto error;
4310 4311
      /* Conditionally writes to binlog. */
      res= mysql_drop_view(thd, first_table, thd->lex->drop_mode);
unknown's avatar
VIEW  
unknown committed
4312 4313
      break;
    }
4314 4315
  case SQLCOM_CREATE_TRIGGER:
  {
4316
    /* Conditionally writes to binlog. */
4317 4318
    res= mysql_create_or_drop_trigger(thd, all_tables, 1);

4319 4320 4321 4322
    break;
  }
  case SQLCOM_DROP_TRIGGER:
  {
4323
    /* Conditionally writes to binlog. */
4324 4325 4326
    res= mysql_create_or_drop_trigger(thd, all_tables, 0);
    break;
  }
4327
  case SQLCOM_XA_START:
Konstantin Osipov's avatar
Konstantin Osipov committed
4328 4329
    if (trans_xa_start(thd))
      goto error;
4330
    my_ok(thd);
4331 4332
    break;
  case SQLCOM_XA_END:
Konstantin Osipov's avatar
Konstantin Osipov committed
4333 4334
    if (trans_xa_end(thd))
      goto error;
4335
    my_ok(thd);
4336 4337
    break;
  case SQLCOM_XA_PREPARE:
Konstantin Osipov's avatar
Konstantin Osipov committed
4338 4339
    if (trans_xa_prepare(thd))
      goto error;
4340
    my_ok(thd);
4341 4342
    break;
  case SQLCOM_XA_COMMIT:
Konstantin Osipov's avatar
Konstantin Osipov committed
4343 4344
    if (trans_xa_commit(thd))
      goto error;
4345
    thd->mdl_context.release_transactional_locks();
Konstantin Osipov's avatar
Konstantin Osipov committed
4346
    my_ok(thd);
4347 4348
    break;
  case SQLCOM_XA_ROLLBACK:
Konstantin Osipov's avatar
Konstantin Osipov committed
4349 4350
    if (trans_xa_rollback(thd))
      goto error;
4351
    thd->mdl_context.release_transactional_locks();
Konstantin Osipov's avatar
Konstantin Osipov committed
4352
    my_ok(thd);
4353 4354
    break;
  case SQLCOM_XA_RECOVER:
4355
    res= mysql_xa_recover(thd);
4356
    break;
unknown's avatar
unknown committed
4357
  case SQLCOM_ALTER_TABLESPACE:
4358
    if (check_global_access(thd, CREATE_TABLESPACE_ACL))
unknown's avatar
unknown committed
4359 4360
      break;
    if (!(res= mysql_alter_tablespace(thd, lex->alter_tablespace_info)))
4361
      my_ok(thd);
unknown's avatar
unknown committed
4362 4363 4364 4365
    break;
  case SQLCOM_INSTALL_PLUGIN:
    if (! (res= mysql_install_plugin(thd, &thd->lex->comment,
                                     &thd->lex->ident)))
4366
      my_ok(thd);
unknown's avatar
unknown committed
4367 4368 4369
    break;
  case SQLCOM_UNINSTALL_PLUGIN:
    if (! (res= mysql_uninstall_plugin(thd, &thd->lex->comment)))
4370
      my_ok(thd);
unknown's avatar
unknown committed
4371 4372 4373 4374 4375 4376 4377 4378 4379 4380
    break;
  case SQLCOM_BINLOG_BASE64_EVENT:
  {
#ifndef EMBEDDED_LIBRARY
    mysql_client_binlog_statement(thd);
#else /* EMBEDDED_LIBRARY */
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "embedded");
#endif /* EMBEDDED_LIBRARY */
    break;
  }
unknown's avatar
unknown committed
4381 4382 4383 4384 4385
  case SQLCOM_CREATE_SERVER:
  {
    int error;
    LEX *lex= thd->lex;
    DBUG_PRINT("info", ("case SQLCOM_CREATE_SERVER"));
unknown's avatar
unknown committed
4386 4387 4388 4389

    if (check_global_access(thd, SUPER_ACL))
      break;

unknown's avatar
unknown committed
4390 4391
    if ((error= create_server(thd, &lex->server_options)))
    {
4392
      DBUG_PRINT("info", ("problem creating server <%s>",
unknown's avatar
unknown committed
4393 4394 4395 4396
                          lex->server_options.server_name));
      my_error(error, MYF(0), lex->server_options.server_name);
      break;
    }
4397
    my_ok(thd, 1);
unknown's avatar
unknown committed
4398 4399 4400 4401 4402 4403 4404
    break;
  }
  case SQLCOM_ALTER_SERVER:
  {
    int error;
    LEX *lex= thd->lex;
    DBUG_PRINT("info", ("case SQLCOM_ALTER_SERVER"));
unknown's avatar
unknown committed
4405 4406 4407 4408

    if (check_global_access(thd, SUPER_ACL))
      break;

unknown's avatar
unknown committed
4409 4410
    if ((error= alter_server(thd, &lex->server_options)))
    {
4411
      DBUG_PRINT("info", ("problem altering server <%s>",
unknown's avatar
unknown committed
4412 4413 4414 4415
                          lex->server_options.server_name));
      my_error(error, MYF(0), lex->server_options.server_name);
      break;
    }
4416
    my_ok(thd, 1);
unknown's avatar
unknown committed
4417 4418 4419 4420 4421 4422 4423
    break;
  }
  case SQLCOM_DROP_SERVER:
  {
    int err_code;
    LEX *lex= thd->lex;
    DBUG_PRINT("info", ("case SQLCOM_DROP_SERVER"));
unknown's avatar
unknown committed
4424 4425 4426 4427

    if (check_global_access(thd, SUPER_ACL))
      break;

unknown's avatar
unknown committed
4428 4429
    if ((err_code= drop_server(thd, &lex->server_options)))
    {
unknown's avatar
unknown committed
4430
      if (! lex->drop_if_exists && err_code == ER_FOREIGN_SERVER_DOESNT_EXIST)
unknown's avatar
unknown committed
4431 4432 4433 4434 4435 4436 4437
      {
        DBUG_PRINT("info", ("problem dropping server %s",
                            lex->server_options.server_name));
        my_error(err_code, MYF(0), lex->server_options.server_name);
      }
      else
      {
4438
        my_ok(thd, 0);
unknown's avatar
unknown committed
4439 4440 4441
      }
      break;
    }
4442
    my_ok(thd, 1);
unknown's avatar
unknown committed
4443 4444
    break;
  }
Marc Alff's avatar
Marc Alff committed
4445 4446 4447 4448 4449
  case SQLCOM_SIGNAL:
  case SQLCOM_RESIGNAL:
    DBUG_ASSERT(lex->m_stmt != NULL);
    res= lex->m_stmt->execute(thd);
    break;
4450
  default:
4451
#ifndef EMBEDDED_LIBRARY
4452
    DBUG_ASSERT(0);                             /* Impossible */
4453
#endif
4454
    my_ok(thd);
unknown's avatar
unknown committed
4455 4456
    break;
  }
4457
  thd_proc_info(thd, "query end");
4458 4459

  /*
unknown's avatar
unknown committed
4460
    Binlog-related cleanup:
4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471
    Reset system variables temporarily modified by SET ONE SHOT.

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

4472
  /*
4473 4474
    The return value for ROW_COUNT() is "implementation dependent" if the
    statement is not DELETE, INSERT or UPDATE, but -1 is what JDBC and ODBC
4475 4476 4477 4478
    wants. We also keep the last value in case of SQLCOM_CALL or
    SQLCOM_EXECUTE.
  */
  if (!(sql_command_flags[lex->sql_command] & CF_HAS_ROW_COUNT))
4479
    thd->row_count_func= -1;
4480

4481
  goto finish;
unknown's avatar
unknown committed
4482 4483

error:
4484 4485
  res= TRUE;

4486
finish:
4487
  if (thd->global_read_lock.has_protection())
4488 4489 4490 4491 4492
  {
    /*
      Release the protection against the global read lock and wake
      everyone, who might want to set a global read lock.
    */
4493
    thd->global_read_lock.start_waiting_global_read_lock(thd);
4494
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
4495

4496 4497 4498 4499 4500 4501 4502 4503
  if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
  {
    /* If commit fails, we should be able to reset the OK status. */
    thd->stmt_da->can_overwrite_status= TRUE;
    /* Commit or rollback the statement transaction. */
    thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
    /* Commit the normal transaction if one is active. */
    trans_commit_implicit(thd);
4504
    thd->stmt_da->can_overwrite_status= FALSE;
4505 4506
    /* Close tables and release metadata locks. */
    close_thread_tables(thd);
4507
    thd->mdl_context.release_transactional_locks();
4508
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
4509

4510
  DBUG_RETURN(res || thd->is_error());
unknown's avatar
unknown committed
4511 4512 4513
}


4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
{
  LEX	*lex= thd->lex;
  select_result *result=lex->result;
  bool res;
  /* assign global limit variable if limit is not given */
  {
    SELECT_LEX *param= lex->unit.global_parameters;
    if (!param->explicit_limit)
      param->select_limit=
        new Item_int((ulonglong) thd->variables.select_limit);
  }
4526
  if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
4527 4528 4529 4530 4531 4532 4533 4534 4535 4536
  {
    if (lex->describe)
    {
      /*
        We always use select_send for EXPLAIN, even if it's an EXPLAIN
        for SELECT ... INTO OUTFILE: a user application should be able
        to prepend EXPLAIN to any query and receive output for it,
        even if the query itself redirects the output.
      */
      if (!(result= new select_send()))
4537
        return 1;                               /* purecov: inspected */
4538 4539 4540 4541 4542 4543 4544
      thd->send_explain_fields(result);
      res= mysql_explain_union(thd, &thd->lex->unit, result);
      if (lex->describe & DESCRIBE_EXTENDED)
      {
        char buff[1024];
        String str(buff,(uint32) sizeof(buff), system_charset_info);
        str.length(0);
4545
        thd->lex->unit.print(&str, QT_ORDINARY);
4546 4547 4548 4549
        str.append('\0');
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                     ER_YES, str.ptr());
      }
4550 4551 4552 4553
      if (res)
        result->abort();
      else
        result->send_eof();
4554 4555 4556 4557 4558
      delete result;
    }
    else
    {
      if (!result && !(result= new select_send()))
4559
        return 1;                               /* purecov: inspected */
4560 4561 4562 4563 4564 4565 4566 4567 4568 4569
      query_cache_store_query(thd, all_tables);
      res= handle_select(thd, lex, result, 0);
      if (result != lex->result)
        delete result;
    }
  }
  return res;
}


unknown's avatar
unknown committed
4570
#ifndef NO_EMBEDDED_ACCESS_CHECKS
unknown's avatar
unknown committed
4571
/**
4572
  Check grants for commands which work only with one table.
unknown's avatar
unknown committed
4573

4574 4575 4576
  @param thd                    Thread handler
  @param privilege              requested privilege
  @param all_tables             global table list of query
unknown's avatar
unknown committed
4577
  @param no_errors              FALSE/TRUE - report/don't report error to
4578
                            the client (using my_error() call).
unknown's avatar
unknown committed
4579 4580 4581 4582 4583

  @retval
    0   OK
  @retval
    1   access denied, error is sent to client
unknown's avatar
unknown committed
4584 4585
*/

4586
bool check_single_table_access(THD *thd, ulong privilege, 
4587
                               TABLE_LIST *all_tables, bool no_errors)
unknown's avatar
unknown committed
4588
{
4589 4590 4591 4592 4593 4594
  Security_context * backup_ctx= thd->security_ctx;

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

4595 4596 4597 4598 4599 4600 4601 4602
  const char *db_name;
  if ((all_tables->view || all_tables->field_translation) &&
      !all_tables->schema_table)
    db_name= all_tables->view_db.str;
  else
    db_name= all_tables->db;

  if (check_access(thd, privilege, db_name,
Marc Alff's avatar
Marc Alff committed
4603 4604 4605
                   &all_tables->grant.privilege,
                   &all_tables->grant.m_internal,
                   0, no_errors))
4606
    goto deny;
unknown's avatar
unknown committed
4607

unknown's avatar
unknown committed
4608
  /* Show only 1 table for check_grant */
4609
  if (!(all_tables->belong_to_view &&
4610
        (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) &&
4611
      check_grant(thd, privilege, all_tables, FALSE, 1, no_errors))
4612 4613 4614
    goto deny;

  thd->security_ctx= backup_ctx;
4615 4616 4617 4618 4619 4620 4621
  return 0;

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

unknown's avatar
unknown committed
4622
/**
4623 4624 4625
  Check grants for commands which work only with one table and all other
  tables belonging to subselects or implicitly opened tables.

unknown's avatar
unknown committed
4626 4627 4628 4629 4630 4631 4632 4633
  @param thd			Thread handler
  @param privilege		requested privilege
  @param all_tables		global table list of query

  @retval
    0   OK
  @retval
    1   access denied, error is sent to client
4634 4635 4636 4637
*/

bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
{
4638
  if (check_single_table_access (thd,privilege,all_tables, FALSE))
4639
    return 1;
unknown's avatar
unknown committed
4640

4641
  /* Check rights on tables of subselects and implictly opened tables */
unknown's avatar
unknown committed
4642
  TABLE_LIST *subselects_tables, *view= all_tables->view ? all_tables : 0;
unknown's avatar
VIEW  
unknown committed
4643
  if ((subselects_tables= all_tables->next_global))
unknown's avatar
unknown committed
4644
  {
unknown's avatar
unknown committed
4645 4646 4647 4648 4649 4650
    /*
      Access rights asked for the first table of a view should be the same
      as for the view
    */
    if (view && subselects_tables->belong_to_view == view)
    {
4651
      if (check_single_table_access (thd, privilege, subselects_tables, FALSE))
unknown's avatar
unknown committed
4652 4653 4654 4655
        return 1;
      subselects_tables= subselects_tables->next_global;
    }
    if (subselects_tables &&
4656 4657
        (check_table_access(thd, SELECT_ACL, subselects_tables, FALSE,
                            UINT_MAX, FALSE)))
4658
      return 1;
unknown's avatar
unknown committed
4659 4660
  }
  return 0;
unknown's avatar
unknown committed
4661 4662 4663
}


unknown's avatar
unknown committed
4664
/**
4665 4666 4667 4668 4669 4670
  @brief Compare requested privileges with the privileges acquired from the
    User- and Db-tables.
  @param thd          Thread handler
  @param want_access  The requested access privileges.
  @param db           A pointer to the Db name.
  @param[out] save_priv A pointer to the granted privileges will be stored.
Marc Alff's avatar
Marc Alff committed
4671
  @param grant_internal_info A pointer to the internal grant cache.
4672 4673 4674 4675 4676 4677 4678 4679
  @param dont_check_global_grants True if no global grants are checked.
  @param no_error     True if no errors should be sent to the client.

  'save_priv' is used to save the User-table (global) and Db-table grants for
  the supplied db name. Note that we don't store db level grants if the global
  grants is enough to satisfy the request AND the global grants contains a
  SELECT grant.

Marc Alff's avatar
Marc Alff committed
4680 4681
  For internal databases (INFORMATION_SCHEMA, PERFORMANCE_SCHEMA),
  additional rules apply, see ACL_internal_schema_access.
4682 4683 4684 4685 4686 4687 4688

  @see check_grant

  @return Status of denial of access by exclusive ACLs.
    @retval FALSE Access can't exclusively be denied by Db- and User-table
      access unless Column- and Table-grants are checked too.
    @retval TRUE Access denied.
unknown's avatar
unknown committed
4689
*/
4690

unknown's avatar
unknown committed
4691
bool
unknown's avatar
unknown committed
4692
check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
Marc Alff's avatar
Marc Alff committed
4693 4694
             GRANT_INTERNAL_INFO *grant_internal_info,
             bool dont_check_global_grants, bool no_errors)
unknown's avatar
unknown committed
4695
{
4696
  Security_context *sctx= thd->security_ctx;
unknown's avatar
unknown committed
4697
  ulong db_access;
4698

4699 4700 4701 4702 4703
  /*
    GRANT command:
    In case of database level grant the database name may be a pattern,
    in case of table|column level grant the database name can not be a pattern.
    We use 'dont_check_global_grants' as a flag to determine
4704
    if it's database level grant command
4705 4706 4707
    (see SQLCOM_GRANT case, mysql_execute_command() function) and
    set db_is_pattern according to 'dont_check_global_grants' value.
  */
4708
  bool  db_is_pattern= ((want_access & GRANT_ACL) && dont_check_global_grants);
unknown's avatar
unknown committed
4709
  ulong dummy;
4710 4711
  DBUG_ENTER("check_access");
  DBUG_PRINT("enter",("db: %s  want_access: %lu  master_access: %lu",
4712
                      db ? db : "", want_access, sctx->master_access));
4713

unknown's avatar
unknown committed
4714 4715 4716
  if (save_priv)
    *save_priv=0;
  else
Marc Alff's avatar
Marc Alff committed
4717
  {
unknown's avatar
unknown committed
4718
    save_priv= &dummy;
Marc Alff's avatar
Marc Alff committed
4719 4720
    dummy= 0;
  }
unknown's avatar
unknown committed
4721

4722
  thd_proc_info(thd, "checking permissions");
4723
  if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
unknown's avatar
unknown committed
4724
  {
4725
    DBUG_PRINT("error",("No database"));
4726
    if (!no_errors)
unknown's avatar
unknown committed
4727 4728
      my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR),
                 MYF(0));                       /* purecov: tested */
unknown's avatar
unknown committed
4729
    DBUG_RETURN(TRUE);				/* purecov: tested */
unknown's avatar
unknown committed
4730 4731
  }

Marc Alff's avatar
Marc Alff committed
4732
  if ((db != NULL) && (db != any_db))
4733
  {
Marc Alff's avatar
Marc Alff committed
4734 4735 4736
    const ACL_internal_schema_access *access;
    access= get_cached_schema_access(grant_internal_info, db);
    if (access)
4737
    {
Marc Alff's avatar
Marc Alff committed
4738
      switch (access->check(want_access, save_priv))
4739
      {
Marc Alff's avatar
Marc Alff committed
4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759
      case ACL_INTERNAL_ACCESS_GRANTED:
        /*
          All the privileges requested have been granted internally.
          [out] *save_privileges= Internal privileges.
        */
        DBUG_RETURN(FALSE);
      case ACL_INTERNAL_ACCESS_DENIED:
        if (! no_errors)
        {
          my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
                   sctx->priv_user, sctx->priv_host, db);
        }
        DBUG_RETURN(TRUE);
      case ACL_INTERNAL_ACCESS_CHECK_GRANT:
        /*
          Only some of the privilege requested have been granted internally,
          proceed with the remaining bits of the request (want_access).
        */
        want_access&= ~(*save_priv);
        break;
4760
      }
4761 4762 4763
    }
  }

4764
  if ((sctx->master_access & want_access) == want_access)
unknown's avatar
unknown committed
4765
  {
4766
    /*
4767 4768
      1. 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
4769
      UPDATE t1 SET a=1 WHERE b > 0
4770
      2. Change db access if it isn't current db which is being addressed
4771
    */
Marc Alff's avatar
Marc Alff committed
4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790
    if (!(sctx->master_access & SELECT_ACL))
    {
      if (db && (!thd->db || db_is_pattern || strcmp(db, thd->db)))
        db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
                           db_is_pattern);
      else
      {
        /* get access for current db */
        db_access= sctx->db_access;
      }
      /*
        The effective privileges are the union of the global privileges
        and the intersection of db- and host-privileges,
        plus the internal privileges.
      */
      *save_priv|= sctx->master_access | db_access;
    }
    else
      *save_priv|= sctx->master_access;
unknown's avatar
unknown committed
4791
    DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
4792
  }
4793
  if (((want_access & ~sctx->master_access) & ~DB_ACLS) ||
4794
      (! db && dont_check_global_grants))
unknown's avatar
unknown committed
4795
  {						// We can never grant this
4796
    DBUG_PRINT("error",("No possible access"));
4797
    if (!no_errors)
4798
      my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
4799 4800
               sctx->priv_user,
               sctx->priv_host,
4801 4802 4803
               (thd->password ?
                ER(ER_YES) :
                ER(ER_NO)));                    /* purecov: tested */
unknown's avatar
unknown committed
4804
    DBUG_RETURN(TRUE);				/* purecov: tested */
unknown's avatar
unknown committed
4805 4806 4807
  }

  if (db == any_db)
4808 4809 4810 4811 4812 4813 4814
  {
    /*
      Access granted; Allow select on *any* db.
      [out] *save_privileges= 0
    */
    DBUG_RETURN(FALSE);
  }
unknown's avatar
unknown committed
4815

unknown's avatar
unknown committed
4816
  if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
4817 4818
    db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
                       db_is_pattern);
unknown's avatar
unknown committed
4819
  else
4820
    db_access= sctx->db_access;
4821 4822
  DBUG_PRINT("info",("db_access: %lu  want_access: %lu",
                     db_access, want_access));
4823

4824 4825
  /*
    Save the union of User-table and the intersection between Db-table and
Marc Alff's avatar
Marc Alff committed
4826
    Host-table privileges, with the already saved internal privileges.
4827 4828
  */
  db_access= (db_access | sctx->master_access);
Marc Alff's avatar
Marc Alff committed
4829
  *save_priv|= db_access;
4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844

  /*
    We need to investigate column- and table access if all requested privileges
    belongs to the bit set of .
  */
  bool need_table_or_column_check=
    (want_access & (TABLE_ACLS | PROC_ACLS | db_access)) == want_access;

  /*
    Grant access if the requested access is in the intersection of
    host- and db-privileges (as retrieved from the acl cache),
    also grant access if all the requested privileges are in the union of
    TABLES_ACLS and PROC_ACLS; see check_grant.
  */
  if ( (db_access & want_access) == want_access ||
4845
      (!dont_check_global_grants &&
4846 4847 4848 4849
       need_table_or_column_check))
  {
    /*
       Ok; but need to check table- and column privileges.
Marc Alff's avatar
Marc Alff committed
4850
       [out] *save_privileges is (User-priv | (Db-priv & Host-priv) | Internal-priv)
4851 4852 4853
    */
    DBUG_RETURN(FALSE);
  }
4854

4855 4856
  /*
    Access is denied;
Marc Alff's avatar
Marc Alff committed
4857
    [out] *save_privileges is (User-priv | (Db-priv & Host-priv) | Internal-priv)
4858
  */
4859
  DBUG_PRINT("error",("Access denied"));
4860
  if (!no_errors)
4861
    my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
4862
             sctx->priv_user, sctx->priv_host,
4863 4864
             (db ? db : (thd->db ?
                         thd->db :
4865 4866 4867
                         "unknown")));
  DBUG_RETURN(TRUE);

unknown's avatar
unknown committed
4868 4869 4870
}


4871 4872
static bool check_show_access(THD *thd, TABLE_LIST *table)
{
Marc Alff's avatar
Marc Alff committed
4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884
  /*
    This is a SHOW command using an INFORMATION_SCHEMA table.
    check_access() has not been called for 'table',
    and SELECT is currently always granted on the I_S, so we automatically
    grant SELECT on table here, to bypass a call to check_access().
    Note that not calling check_access(table) is an optimization,
    which needs to be revisited if the INFORMATION_SCHEMA does
    not always automatically grant SELECT but use the grant tables.
    See Bug#38837 need a way to disable information_schema for security
  */
  table->grant.privilege= SELECT_ACL;

unknown's avatar
unknown committed
4885
  switch (get_schema_table_idx(table->schema_table)) {
4886 4887
  case SCH_SCHEMATA:
    return (specialflag & SPECIAL_SKIP_SHOW_DB) &&
unknown's avatar
unknown committed
4888
      check_global_access(thd, SHOW_DB_ACL);
4889 4890 4891 4892 4893

  case SCH_TABLE_NAMES:
  case SCH_TABLES:
  case SCH_VIEWS:
  case SCH_TRIGGERS:
unknown's avatar
unknown committed
4894 4895 4896
  case SCH_EVENTS:
  {
    const char *dst_db_name= table->schema_select_lex->db;
4897

unknown's avatar
unknown committed
4898
    DBUG_ASSERT(dst_db_name);
4899

unknown's avatar
unknown committed
4900
    if (check_access(thd, SELECT_ACL, dst_db_name,
Marc Alff's avatar
Marc Alff committed
4901
                     &thd->col_access, NULL, FALSE, FALSE))
unknown's avatar
unknown committed
4902
      return TRUE;
4903

unknown's avatar
unknown committed
4904 4905 4906 4907 4908 4909 4910
    if (!thd->col_access && check_grant_db(thd, dst_db_name))
    {
      my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
               thd->security_ctx->priv_user,
               thd->security_ctx->priv_host,
               dst_db_name);
      return TRUE;
4911 4912
    }

unknown's avatar
unknown committed
4913 4914 4915
    return FALSE;
  }

4916 4917
  case SCH_COLUMNS:
  case SCH_STATISTICS:
unknown's avatar
unknown committed
4918 4919 4920
  {
    TABLE_LIST *dst_table;
    dst_table= (TABLE_LIST *) table->schema_select_lex->table_list.first;
4921

unknown's avatar
unknown committed
4922
    DBUG_ASSERT(dst_table);
4923

4924
    if (check_access(thd, SELECT_ACL, dst_table->db,
Marc Alff's avatar
Marc Alff committed
4925 4926 4927
                     &dst_table->grant.privilege,
                     &dst_table->grant.m_internal,
                     FALSE, FALSE))
4928 4929 4930 4931 4932 4933 4934 4935
          return TRUE; /* Access denied */

    /*
      Check_grant will grant access if there is any column privileges on
      all of the tables thanks to the fourth parameter (bool show_table).
    */
    if (check_grant(thd, SELECT_ACL, dst_table, TRUE, UINT_MAX, FALSE))
      return TRUE; /* Access denied */
4936

4937 4938
    /* Access granted */
    return FALSE;
unknown's avatar
unknown committed
4939 4940
  }
  default:
4941 4942 4943 4944 4945 4946 4947
    break;
  }

  return FALSE;
}


4948

4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976
/**
  @brief Check if the requested privileges exists in either User-, Host- or
    Db-tables.
  @param thd          Thread context
  @param want_access  Privileges requested
  @param tables       List of tables to be compared against
  @param no_errors    Don't report error to the client (using my_error() call).
  @param any_combination_of_privileges_will_do TRUE if any privileges on any
    column combination is enough.
  @param number       Only the first 'number' tables in the linked list are
                      relevant.

  The suppled table list contains cached privileges. This functions calls the
  help functions check_access and check_grant to verify the first three steps
  in the privileges check queue:
  1. Global privileges
  2. OR (db privileges AND host privileges)
  3. OR table privileges
  4. OR column privileges (not checked by this function!)
  5. OR routine privileges (not checked by this function!)

  @see check_access
  @see check_grant

  @note This functions assumes that table list used and
  thd->lex->query_tables_own_last value correspond to each other
  (the latter should be either 0 or point to next_global member
  of one of elements of this table list).
4977

4978 4979 4980 4981
  @return
    @retval FALSE OK
    @retval TRUE  Access denied; But column or routine privileges might need to
      be checked also.
unknown's avatar
unknown committed
4982 4983
*/

4984
bool
4985 4986 4987
check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
		   bool any_combination_of_privileges_will_do,
                   uint number, bool no_errors)
unknown's avatar
unknown committed
4988
{
4989 4990
  TABLE_LIST *org_tables= tables;
  TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
4991
  uint i= 0;
4992
  Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx;
4993
  /*
unknown's avatar
unknown committed
4994 4995 4996
    The check that first_not_own_table is not reached is for the case when
    the given table list refers to the list for prelocking (contains tables
    of other queries). For simple queries first_not_own_table is 0.
4997
  */
4998
  for (; i < number && tables != first_not_own_table && tables;
4999
       tables= tables->next_global, i++)
unknown's avatar
unknown committed
5000
  {
5001
    ulong want_access= requirements;
5002 5003 5004 5005 5006
    if (tables->security_ctx)
      sctx= tables->security_ctx;
    else
      sctx= backup_ctx;

5007 5008 5009 5010 5011
    /*
       Register access for view underlying table.
       Remove SHOW_VIEW_ACL, because it will be checked during making view
     */
    tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
unknown's avatar
unknown committed
5012 5013 5014 5015 5016 5017 5018 5019

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

5020 5021
    DBUG_PRINT("info", ("derived: %d  view: %d", tables->derived != 0,
                        tables->view != 0));
5022
    if (tables->is_anonymous_derived_table() ||
5023 5024
        (tables->table && tables->table->s &&
         (int)tables->table->s->tmp_table))
unknown's avatar
unknown committed
5025
      continue;
5026
    thd->security_ctx= sctx;
Marc Alff's avatar
Marc Alff committed
5027 5028 5029 5030 5031

    if (check_access(thd, want_access, tables->get_db_name(),
                     &tables->grant.privilege,
                     &tables->grant.m_internal,
                     0, no_errors))
5032
      goto deny;
unknown's avatar
unknown committed
5033
  }
5034
  thd->security_ctx= backup_ctx;
5035 5036 5037
  return check_grant(thd,requirements,org_tables,
                     any_combination_of_privileges_will_do,
                     number, no_errors);
5038 5039 5040
deny:
  thd->security_ctx= backup_ctx;
  return TRUE;
unknown's avatar
unknown committed
5041 5042
}

5043

5044
bool
5045 5046
check_routine_access(THD *thd, ulong want_access,char *db, char *name,
		     bool is_proc, bool no_errors)
5047 5048 5049 5050 5051
{
  TABLE_LIST tables[1];
  
  bzero((char *)tables, sizeof(TABLE_LIST));
  tables->db= db;
5052
  tables->table_name= tables->alias= name;
5053
  
unknown's avatar
unknown committed
5054 5055 5056 5057 5058
  /*
    The following test is just a shortcut for check_access() (to avoid
    calculating db_access) under the assumption that it's common to
    give persons global right to execute all stored SP (but not
    necessary to create them).
Marc Alff's avatar
Marc Alff committed
5059 5060 5061 5062 5063 5064
    Note that this effectively bypasses the ACL_internal_schema_access checks
    that are implemented for the INFORMATION_SCHEMA and PERFORMANCE_SCHEMA,
    which are located in check_access().
    Since the I_S and P_S do not contain routines, this bypass is ok,
    as long as this code path is not abused to create routines.
    The assert enforce that.
unknown's avatar
unknown committed
5065
  */
Marc Alff's avatar
Marc Alff committed
5066
  DBUG_ASSERT((want_access & CREATE_PROC_ACL) == 0);
unknown's avatar
unknown committed
5067
  if ((thd->security_ctx->master_access & want_access) == want_access)
5068
    tables->grant.privilege= want_access;
Marc Alff's avatar
Marc Alff committed
5069 5070 5071 5072
  else if (check_access(thd, want_access, db,
                        &tables->grant.privilege,
                        &tables->grant.m_internal,
                        0, no_errors))
5073 5074
    return TRUE;
  
5075
  return check_grant_routine(thd, want_access, tables, is_proc, no_errors);
5076 5077
}

5078

unknown's avatar
unknown committed
5079 5080
/**
  Check if the routine has any of the routine privileges.
5081

unknown's avatar
unknown committed
5082 5083 5084
  @param thd	       Thread handler
  @param db           Database name
  @param name         Routine name
5085

unknown's avatar
unknown committed
5086
  @retval
5087
    0            ok
unknown's avatar
unknown committed
5088
  @retval
5089 5090 5091
    1            error
*/

5092 5093
bool check_some_routine_access(THD *thd, const char *db, const char *name,
                               bool is_proc)
5094 5095
{
  ulong save_priv;
5096
  /*
Marc Alff's avatar
Marc Alff committed
5097 5098 5099 5100 5101 5102 5103
    The following test is just a shortcut for check_access() (to avoid
    calculating db_access)
    Note that this effectively bypasses the ACL_internal_schema_access checks
    that are implemented for the INFORMATION_SCHEMA and PERFORMANCE_SCHEMA,
    which are located in check_access().
    Since the I_S and P_S do not contain routines, this bypass is ok,
    as it only opens SHOW_PROC_ACLS.
5104
  */
Marc Alff's avatar
Marc Alff committed
5105 5106 5107
  if (thd->security_ctx->master_access & SHOW_PROC_ACLS)
    return FALSE;
  if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, NULL, 0, 1) ||
5108 5109
      (save_priv & SHOW_PROC_ACLS))
    return FALSE;
5110
  return check_routine_level_acl(thd, db, name, is_proc);
5111 5112 5113
}


5114 5115 5116
/*
  Check if the given table has any of the asked privileges

unknown's avatar
unknown committed
5117 5118
  @param thd		 Thread handler
  @param want_access	 Bitmap of possible privileges to check for
5119

unknown's avatar
unknown committed
5120
  @retval
5121
    0  ok
unknown's avatar
unknown committed
5122
  @retval
5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136
    1  error
*/

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

  /* This loop will work as long as we have less than 32 privileges */
  for (access= 1; access < want_access ; access<<= 1)
  {
    if (access & want_access)
    {
      if (!check_access(thd, access, table->db,
Marc Alff's avatar
Marc Alff committed
5137 5138 5139
                        &table->grant.privilege,
                        &table->grant.m_internal,
                        0, 1) &&
5140
           !check_grant(thd, access, table, FALSE, 1, TRUE))
5141 5142 5143 5144 5145 5146 5147
        DBUG_RETURN(0);
    }
  }
  DBUG_PRINT("exit",("no matching access rights"));
  DBUG_RETURN(1);
}

unknown's avatar
unknown committed
5148
#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
5149

5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182

/**
  check for global access and give descriptive error message if it fails.

  @param thd			Thread handler
  @param want_access		Use should have any of these global rights

  @warning
    One gets access right 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.

  @retval
    0	ok
  @retval
    1	Access denied.  In this case an error is sent to the client
*/

bool check_global_access(THD *thd, ulong want_access)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  char command[128];
  if ((thd->security_ctx->master_access & want_access))
    return 0;
  get_privilege_desc(command, sizeof(command), want_access);
  my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
  return 1;
#else
  return 0;
#endif
}

unknown's avatar
unknown committed
5183 5184 5185 5186
/****************************************************************************
	Check stack size; Send error if there isn't enough stack to continue
****************************************************************************/

5187

unknown's avatar
unknown committed
5188 5189 5190 5191 5192 5193
#if STACK_DIRECTION < 0
#define used_stack(A,B) (long) (A - B)
#else
#define used_stack(A,B) (long) (B - A)
#endif

unknown's avatar
unknown committed
5194 5195 5196 5197
#ifndef DBUG_OFF
long max_stack_used;
#endif

unknown's avatar
unknown committed
5198 5199
/**
  @note
5200 5201 5202 5203
  Note: The 'buf' parameter is necessary, even if it is unused here.
  - fix_fields functions has a "dummy" buffer large enough for the
    corresponding exec. (Thus we only have to check in fix_fields.)
  - Passing to check_stack_overrun() prevents the compiler from removing it.
unknown's avatar
unknown committed
5204
*/
5205
bool check_stack_overrun(THD *thd, long margin,
5206
			 uchar *buf __attribute__((unused)))
unknown's avatar
unknown committed
5207 5208
{
  long stack_used;
5209
  DBUG_ASSERT(thd == current_thd);
unknown's avatar
unknown committed
5210
  if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
5211
      (long) (my_thread_stack_size - margin))
unknown's avatar
unknown committed
5212
  {
5213 5214 5215 5216
    char ebuff[MYSQL_ERRMSG_SIZE];
    my_snprintf(ebuff, sizeof(ebuff), ER(ER_STACK_OVERRUN_NEED_MORE),
                stack_used, my_thread_stack_size, margin);
    my_message(ER_STACK_OVERRUN_NEED_MORE, ebuff, MYF(ME_FATALERROR));
unknown's avatar
unknown committed
5217 5218
    return 1;
  }
unknown's avatar
unknown committed
5219 5220 5221
#ifndef DBUG_OFF
  max_stack_used= max(max_stack_used, stack_used);
#endif
unknown's avatar
unknown committed
5222 5223
  return 0;
}
5224

unknown's avatar
unknown committed
5225 5226 5227 5228

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

5229
bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
unknown's avatar
unknown committed
5230
{
5231
  Yacc_state *state= & current_thd->m_parser_state->m_yacc;
5232
  ulong old_info=0;
5233
  DBUG_ASSERT(state);
unknown's avatar
unknown committed
5234 5235
  if ((uint) *yystacksize >= MY_YACC_MAX)
    return 1;
5236
  if (!state->yacc_yyvs)
unknown's avatar
unknown committed
5237 5238
    old_info= *yystacksize;
  *yystacksize= set_zone((*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX);
5239
  if (!(state->yacc_yyvs= (uchar*)
5240
        my_realloc(state->yacc_yyvs,
5241 5242 5243
                   *yystacksize*sizeof(**yyvs),
                   MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) ||
      !(state->yacc_yyss= (uchar*)
5244
        my_realloc(state->yacc_yyss,
5245 5246
                   *yystacksize*sizeof(**yyss),
                   MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))))
unknown's avatar
unknown committed
5247 5248
    return 1;
  if (old_info)
5249 5250 5251 5252 5253 5254 5255 5256
  {
    /*
      Only copy the old stack on the first call to my_yyoverflow(),
      when replacing a static stack (YYINITDEPTH) by a dynamic stack.
      For subsequent calls, my_realloc already did preserve the old stack.
    */
    memcpy(state->yacc_yyss, *yyss, old_info*sizeof(**yyss));
    memcpy(state->yacc_yyvs, *yyvs, old_info*sizeof(**yyvs));
unknown's avatar
unknown committed
5257
  }
5258 5259
  *yyss= (short*) state->yacc_yyss;
  *yyvs= (YYSTYPE*) state->yacc_yyvs;
unknown's avatar
unknown committed
5260 5261 5262 5263
  return 0;
}


unknown's avatar
unknown committed
5264
/**
5265 5266
  Reset the part of THD responsible for the state of command
  processing.
5267

5268 5269 5270
  This needs to be called before execution of every statement
  (prepared or conventional).  It is not called by substatements of
  routines.
5271

5272 5273
  @todo Remove mysql_reset_thd_for_next_command and only use the
  member function.
5274

5275 5276
  @todo Call it after we use THD for queries, not before.
*/
5277 5278
void mysql_reset_thd_for_next_command(THD *thd)
{
5279 5280 5281 5282 5283 5284
  thd->reset_for_next_command();
}

void THD::reset_for_next_command()
{
  THD *thd= this;
5285
  DBUG_ENTER("mysql_reset_thd_for_next_command");
5286
  DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */
5287
  DBUG_ASSERT(! thd->in_sub_stmt);
5288
  thd->free_list= 0;
5289
  thd->select_number= 1;
5290 5291 5292 5293
  /*
    Those two lines below are theoretically unneeded as
    THD::cleanup_after_query() should take care of this already.
  */
5294
  thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty();
5295 5296 5297
  thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;

  thd->query_start_used= 0;
5298
  thd->is_fatal_error= thd->time_zone_used= 0;
5299 5300 5301 5302 5303
  /*
    Clear the status flag that are expected to be cleared at the
    beginning of each SQL statement.
  */
  thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
5304 5305 5306 5307 5308
  /*
    If in autocommit mode and not in a transaction, reset
    OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG to not get warnings
    in ha_rollback_trans() about some tables couldn't be rolled back.
  */
5309
  if (!thd->in_multi_stmt_transaction())
unknown's avatar
unknown committed
5310
  {
5311
    thd->variables.option_bits&= ~OPTION_KEEP_LOG;
5312
    thd->transaction.all.modified_non_trans_table= FALSE;
unknown's avatar
unknown committed
5313
  }
5314
  DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx);
unknown's avatar
unknown committed
5315
  thd->thread_specific_used= FALSE;
5316 5317

  if (opt_bin_log)
5318
  {
5319 5320
    reset_dynamic(&thd->user_var_events);
    thd->user_var_events_alloc= thd->mem_root;
5321
  }
5322
  thd->clear_error();
Marc Alff's avatar
Marc Alff committed
5323 5324
  thd->stmt_da->reset_diagnostics_area();
  thd->warning_info->reset_for_next_command();
5325 5326 5327
  thd->rand_used= 0;
  thd->sent_row_count= thd->examined_row_count= 0;

5328
  thd->reset_current_stmt_binlog_format_row();
5329
  thd->binlog_unsafe_warning_flags= 0;
unknown's avatar
unknown committed
5330

5331
  DBUG_PRINT("debug",
5332
             ("is_current_stmt_binlog_format_row(): %d",
5333
              thd->is_current_stmt_binlog_format_row()));
5334

unknown's avatar
unknown committed
5335 5336 5337
  DBUG_VOID_RETURN;
}

5338

5339 5340 5341 5342 5343 5344 5345 5346
/**
  Resets the lex->current_select object.
  @note It is assumed that lex->current_select != NULL

  This function is a wrapper around select_lex->init_select() with an added
  check for the special situation when using INTO OUTFILE and LOAD DATA.
*/

5347 5348 5349
void
mysql_init_select(LEX *lex)
{
unknown's avatar
unknown committed
5350
  SELECT_LEX *select_lex= lex->current_select;
unknown's avatar
unknown committed
5351
  select_lex->init_select();
5352
  lex->wild= 0;
5353 5354
  if (select_lex == &lex->select_lex)
  {
5355
    DBUG_ASSERT(lex->result == 0);
5356 5357
    lex->exchange= 0;
  }
5358 5359
}

5360

5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372
/**
  Used to allocate a new SELECT_LEX object on the current thd mem_root and
  link it into the relevant lists.

  This function is always followed by mysql_init_select.

  @see mysql_init_select

  @retval TRUE An error occurred
  @retval FALSE The new SELECT_LEX was successfully allocated.
*/

unknown's avatar
unknown committed
5373
bool
unknown's avatar
unknown committed
5374
mysql_new_select(LEX *lex, bool move_down)
5375
{
unknown's avatar
unknown committed
5376
  SELECT_LEX *select_lex;
5377
  THD *thd= lex->thd;
unknown's avatar
unknown committed
5378 5379
  DBUG_ENTER("mysql_new_select");

5380
  if (!(select_lex= new (thd->mem_root) SELECT_LEX()))
unknown's avatar
unknown committed
5381
    DBUG_RETURN(1);
5382
  select_lex->select_number= ++thd->select_number;
unknown's avatar
unknown committed
5383
  select_lex->parent_lex= lex; /* Used in init_query. */
unknown's avatar
unknown committed
5384 5385
  select_lex->init_query();
  select_lex->init_select();
unknown's avatar
unknown committed
5386
  lex->nest_level++;
unknown's avatar
unknown committed
5387 5388 5389 5390 5391
  if (lex->nest_level > (int) MAX_SELECT_NESTING)
  {
    my_error(ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT,MYF(0),MAX_SELECT_NESTING);
    DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
5392
  select_lex->nest_level= lex->nest_level;
5393 5394 5395
  /*
    Don't evaluate this subquery during statement prepare even if
    it's a constant one. The flag is switched off in the end of
5396
    mysqld_stmt_prepare.
5397
  */
unknown's avatar
unknown committed
5398
  if (thd->stmt_arena->is_stmt_prepare())
5399
    select_lex->uncacheable|= UNCACHEABLE_PREPARE;
unknown's avatar
unknown committed
5400 5401
  if (move_down)
  {
unknown's avatar
unknown committed
5402
    SELECT_LEX_UNIT *unit;
5403
    lex->subqueries= TRUE;
unknown's avatar
unknown committed
5404
    /* first select_lex of subselect or derived table */
5405
    if (!(unit= new (thd->mem_root) SELECT_LEX_UNIT()))
unknown's avatar
unknown committed
5406
      DBUG_RETURN(1);
unknown's avatar
unknown committed
5407

unknown's avatar
unknown committed
5408 5409
    unit->init_query();
    unit->init_select();
5410
    unit->thd= thd;
unknown's avatar
unknown committed
5411
    unit->include_down(lex->current_select);
unknown's avatar
unknown committed
5412 5413
    unit->link_next= 0;
    unit->link_prev= 0;
5414
    unit->return_to= lex->current_select;
unknown's avatar
unknown committed
5415
    select_lex->include_down(unit);
5416 5417 5418 5419 5420
    /*
      By default we assume that it is usual subselect and we have outer name
      resolution context, if no we will assign it to 0 later
    */
    select_lex->context.outer_context= &select_lex->outer_select()->context;
unknown's avatar
unknown committed
5421 5422
  }
  else
unknown's avatar
unknown committed
5423
  {
unknown's avatar
VIEW  
unknown committed
5424 5425
    if (lex->current_select->order_list.first && !lex->current_select->braces)
    {
unknown's avatar
unknown committed
5426
      my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY");
unknown's avatar
unknown committed
5427
      DBUG_RETURN(1);
unknown's avatar
VIEW  
unknown committed
5428
    }
5429
    select_lex->include_neighbour(lex->current_select);
5430 5431 5432 5433 5434
    SELECT_LEX_UNIT *unit= select_lex->master_unit();                              
    if (!unit->fake_select_lex && unit->add_fake_select_lex(lex->thd))
      DBUG_RETURN(1);
    select_lex->context.outer_context= 
                unit->first_select()->context.outer_context;
unknown's avatar
unknown committed
5435
  }
unknown's avatar
unknown committed
5436

5437
  select_lex->master_unit()->global_parameters= select_lex;
5438
  select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
5439
  lex->current_select= select_lex;
5440 5441 5442 5443 5444
  /*
    in subquery is SELECT query and we allow resolution of names in SELECT
    list
  */
  select_lex->context.resolve_in_select_list= TRUE;
unknown's avatar
unknown committed
5445
  DBUG_RETURN(0);
5446
}
unknown's avatar
unknown committed
5447

unknown's avatar
unknown committed
5448
/**
5449 5450
  Create a select to return the same output as 'SELECT @@var_name'.

unknown's avatar
unknown committed
5451
  Used for SHOW COUNT(*) [ WARNINGS | ERROR].
5452

unknown's avatar
unknown committed
5453
  This will crash with a core dump if the variable doesn't exists.
5454

unknown's avatar
unknown committed
5455
  @param var_name		Variable name
5456 5457 5458 5459
*/

void create_select_for_variable(const char *var_name)
{
5460
  THD *thd;
5461
  LEX *lex;
5462
  LEX_STRING tmp, null_lex_string;
5463 5464
  Item *var;
  char buff[MAX_SYS_VAR_LENGTH*2+4+8], *end;
5465
  DBUG_ENTER("create_select_for_variable");
5466 5467

  thd= current_thd;
unknown's avatar
unknown committed
5468
  lex= thd->lex;
5469 5470 5471 5472
  mysql_init_select(lex);
  lex->sql_command= SQLCOM_SELECT;
  tmp.str= (char*) var_name;
  tmp.length=strlen(var_name);
5473
  bzero((char*) &null_lex_string.str, sizeof(null_lex_string));
5474 5475 5476 5477
  /*
    We set the name of Item to @@session.var_name because that then is used
    as the column name in the output.
  */
unknown's avatar
unknown committed
5478 5479 5480 5481 5482 5483
  if ((var= get_system_var(thd, OPT_SESSION, tmp, null_lex_string)))
  {
    end= strxmov(buff, "@@session.", var_name, NullS);
    var->set_name(buff, end-buff, system_charset_info);
    add_item_to_list(thd, var);
  }
5484 5485 5486
  DBUG_VOID_RETURN;
}

5487

unknown's avatar
unknown committed
5488 5489
void mysql_init_multi_delete(LEX *lex)
{
unknown's avatar
unknown committed
5490
  lex->sql_command=  SQLCOM_DELETE_MULTI;
unknown's avatar
unknown committed
5491
  mysql_init_select(lex);
5492 5493
  lex->select_lex.select_limit= 0;
  lex->unit.select_limit_cnt= HA_POS_ERROR;
unknown's avatar
unknown committed
5494
  lex->select_lex.table_list.save_and_clear(&lex->auxiliary_table_list);
5495
  lex->lock_option= TL_READ_DEFAULT;
unknown's avatar
VIEW  
unknown committed
5496 5497
  lex->query_tables= 0;
  lex->query_tables_last= &lex->query_tables;
unknown's avatar
unknown committed
5498
}
unknown's avatar
unknown committed
5499

5500

5501 5502 5503 5504
/*
  When you modify mysql_parse(), you may need to mofify
  mysql_test_parse_for_slave() in this same file.
*/
unknown's avatar
unknown committed
5505

5506 5507
/**
  Parse a query.
unknown's avatar
unknown committed
5508 5509 5510 5511 5512 5513

  @param       thd     Current thread
  @param       inBuf   Begining of the query text
  @param       length  Length of the query text
  @param[out]  found_semicolon For multi queries, position of the character of
                               the next query in the query text.
5514 5515 5516 5517
*/

void mysql_parse(THD *thd, const char *inBuf, uint length,
                 const char ** found_semicolon)
unknown's avatar
unknown committed
5518
{
5519
  int error;
unknown's avatar
unknown committed
5520
  DBUG_ENTER("mysql_parse");
5521 5522 5523

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

5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541
  /*
    Warning.
    The purpose of query_cache_send_result_to_client() is to lookup the
    query in the query cache first, to avoid parsing and executing it.
    So, the natural implementation would be to:
    - first, call query_cache_send_result_to_client,
    - second, if caching failed, initialise the lexical and syntactic parser.
    The problem is that the query cache depends on a clean initialization
    of (among others) lex->safe_to_cache_query and thd->server_status,
    which are reset respectively in
    - lex_start()
    - mysql_reset_thd_for_next_command()
    So, initializing the lexical analyser *before* using the query cache
    is required for the cache to work properly.
    FIXME: cleanup the dependencies in the code to simplify this.
  */
  lex_start(thd);
  mysql_reset_thd_for_next_command(thd);
5542

5543
  if (query_cache_send_result_to_client(thd, (char*) inBuf, length) <= 0)
unknown's avatar
unknown committed
5544
  {
unknown's avatar
unknown committed
5545
    LEX *lex= thd->lex;
5546

5547
    Parser_state parser_state(thd, inBuf, length);
5548

5549
    bool err= parse_sql(thd, & parser_state, NULL);
5550
    *found_semicolon= parser_state.m_lip.found_semicolon;
5551

5552
    if (!err)
unknown's avatar
unknown committed
5553
    {
unknown's avatar
unknown committed
5554
#ifndef NO_EMBEDDED_ACCESS_CHECKS
5555
      if (mqh_used && thd->user_connect &&
5556
	  check_mqh(thd, lex->sql_command))
5557 5558 5559 5560
      {
	thd->net.error = 0;
      }
      else
unknown's avatar
unknown committed
5561
#endif
5562
      {
5563
	if (! thd->is_error())
unknown's avatar
unknown committed
5564
	{
5565 5566 5567 5568 5569 5570 5571 5572 5573 5574
          /*
            Binlog logs a string starting from thd->query and having length
            thd->query_length; so we set thd->query_length correctly (to not
            log several statements in one event, when we executed only first).
            We set it to not see the ';' (otherwise it would get into binlog
            and Query_log_event::print() would give ';;' output).
            This also helps display only the current query in SHOW
            PROCESSLIST.
            Note that we don't need LOCK_thread_count to modify query_length.
          */
5575 5576 5577 5578
          if (*found_semicolon && (ulong) (*found_semicolon - thd->query()))
            thd->set_query_inner(thd->query(),
                                 (uint32) (*found_semicolon -
                                           thd->query() - 1));
5579
          /* Actually execute the query */
5580 5581 5582 5583 5584
          if (*found_semicolon)
          {
            lex->safe_to_cache_query= 0;
            thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
          }
5585
          lex->set_trg_event_type_for_tables();
5586
          MYSQL_QUERY_EXEC_START(thd->query(),
5587 5588 5589 5590 5591 5592 5593 5594
                                 thd->thread_id,
                                 (char *) (thd->db ? thd->db : ""),
                                 thd->security_ctx->priv_user,
                                 (char *) thd->security_ctx->host_or_ip,
                                 0);

          error= mysql_execute_command(thd);
          MYSQL_QUERY_EXEC_DONE(error);
unknown's avatar
unknown committed
5595
	}
5596
      }
unknown's avatar
unknown committed
5597 5598
    }
    else
5599
    {
5600
      DBUG_ASSERT(thd->is_error());
5601
      DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
5602
			 thd->is_fatal_error));
5603

5604
      query_cache_abort(&thd->query_cache_tls);
5605
    }
5606 5607 5608 5609 5610 5611
    if (thd->lex->sphead)
    {
      delete thd->lex->sphead;
      thd->lex->sphead= 0;
    }
    lex->unit.cleanup();
5612
    thd_proc_info(thd, "freeing items");
5613
    thd->end_statement();
5614
    thd->cleanup_after_query();
5615
    DBUG_ASSERT(thd->change_list.is_empty());
unknown's avatar
unknown committed
5616
  }
5617 5618 5619 5620 5621 5622
  else
  {
    /* There are no multi queries in the cache. */
    *found_semicolon= NULL;
  }

unknown's avatar
unknown committed
5623 5624 5625 5626
  DBUG_VOID_RETURN;
}


unknown's avatar
unknown committed
5627
#ifdef HAVE_REPLICATION
5628 5629 5630 5631
/*
  Usable by the replication SQL thread only: just parse a query to know if it
  can be ignored because of replicate-*-table rules.

unknown's avatar
unknown committed
5632
  @retval
5633
    0	cannot be ignored
unknown's avatar
unknown committed
5634
  @retval
5635 5636 5637 5638 5639
    1	can be ignored
*/

bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
{
unknown's avatar
unknown committed
5640
  LEX *lex= thd->lex;
5641
  bool error= 0;
unknown's avatar
unknown committed
5642
  DBUG_ENTER("mysql_test_parse_for_slave");
5643

5644
  Parser_state parser_state(thd, inBuf, length);
5645 5646 5647
  lex_start(thd);
  mysql_reset_thd_for_next_command(thd);

5648
  if (!parse_sql(thd, & parser_state, NULL) &&
5649
      all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
unknown's avatar
unknown committed
5650
    error= 1;                  /* Ignore question */
5651
  thd->end_statement();
5652
  thd->cleanup_after_query();
unknown's avatar
unknown committed
5653
  DBUG_RETURN(error);
5654
}
unknown's avatar
unknown committed
5655
#endif
unknown's avatar
unknown committed
5656

5657

unknown's avatar
unknown committed
5658

unknown's avatar
unknown committed
5659 5660 5661 5662 5663 5664
/**
  Store field definition for create.

  @return
    Return 0 if ok
*/
unknown's avatar
unknown committed
5665

5666
bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
unknown's avatar
unknown committed
5667
		       char *length, char *decimals,
5668
		       uint type_modifier,
5669 5670
		       Item *default_value, Item *on_update_value,
                       LEX_STRING *comment,
5671 5672
		       char *change,
                       List<String> *interval_list, CHARSET_INFO *cs,
unknown's avatar
unknown committed
5673
		       uint uint_geom_type)
unknown's avatar
unknown committed
5674
{
unknown's avatar
unknown committed
5675
  register Create_field *new_field;
unknown's avatar
unknown committed
5676
  LEX  *lex= thd->lex;
unknown's avatar
unknown committed
5677 5678
  DBUG_ENTER("add_field_to_list");

5679 5680
  if (check_string_char_length(field_name, "", NAME_CHAR_LEN,
                               system_charset_info, 1))
unknown's avatar
unknown committed
5681
  {
5682
    my_error(ER_TOO_LONG_IDENT, MYF(0), field_name->str); /* purecov: inspected */
unknown's avatar
unknown committed
5683 5684 5685 5686
    DBUG_RETURN(1);				/* purecov: inspected */
  }
  if (type_modifier & PRI_KEY_FLAG)
  {
5687
    Key *key;
5688 5689
    lex->col_list.push_back(new Key_part_spec(*field_name, 0));
    key= new Key(Key::PRIMARY, null_lex_str,
5690 5691 5692
                      &default_key_create_info,
                      0, lex->col_list);
    lex->alter_info.key_list.push_back(key);
unknown's avatar
unknown committed
5693 5694 5695 5696
    lex->col_list.empty();
  }
  if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
  {
5697
    Key *key;
5698 5699
    lex->col_list.push_back(new Key_part_spec(*field_name, 0));
    key= new Key(Key::UNIQUE, null_lex_str,
5700 5701 5702
                 &default_key_create_info, 0,
                 lex->col_list);
    lex->alter_info.key_list.push_back(key);
unknown's avatar
unknown committed
5703 5704 5705
    lex->col_list.empty();
  }

5706
  if (default_value)
unknown's avatar
unknown committed
5707
  {
5708
    /* 
unknown's avatar
unknown committed
5709 5710
      Default value should be literal => basic constants =>
      no need fix_fields()
5711 5712 5713
      
      We allow only one function as part of default value - 
      NOW() as default for TIMESTAMP type.
5714
    */
5715 5716
    if (default_value->type() == Item::FUNC_ITEM && 
        !(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC &&
5717
         type == MYSQL_TYPE_TIMESTAMP))
5718
    {
5719
      my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
5720 5721 5722
      DBUG_RETURN(1);
    }
    else if (default_value->type() == Item::NULL_ITEM)
unknown's avatar
unknown committed
5723
    {
5724
      default_value= 0;
5725 5726 5727
      if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
	  NOT_NULL_FLAG)
      {
5728
	my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
5729 5730 5731 5732 5733
	DBUG_RETURN(1);
      }
    }
    else if (type_modifier & AUTO_INCREMENT_FLAG)
    {
5734
      my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
unknown's avatar
unknown committed
5735 5736 5737
      DBUG_RETURN(1);
    }
  }
5738

5739
  if (on_update_value && type != MYSQL_TYPE_TIMESTAMP)
5740
  {
5741
    my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name->str);
5742 5743
    DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
5744

unknown's avatar
unknown committed
5745
  if (!(new_field= new Create_field()) ||
5746
      new_field->init(thd, field_name->str, type, length, decimals, type_modifier,
5747 5748
                      default_value, on_update_value, comment, change,
                      interval_list, cs, uint_geom_type))
unknown's avatar
unknown committed
5749
    DBUG_RETURN(1);
unknown's avatar
unknown committed
5750

5751
  lex->alter_info.create_list.push_back(new_field);
unknown's avatar
unknown committed
5752 5753 5754 5755
  lex->last_field=new_field;
  DBUG_RETURN(0);
}

5756

unknown's avatar
unknown committed
5757
/** Store position for column in ALTER TABLE .. ADD column. */
unknown's avatar
unknown committed
5758 5759 5760

void store_position_for_column(const char *name)
{
5761
  current_thd->lex->last_field->after=my_const_cast(char*) (name);
unknown's avatar
unknown committed
5762 5763 5764
}

bool
unknown's avatar
unknown committed
5765
add_proc_to_list(THD* thd, Item *item)
unknown's avatar
unknown committed
5766 5767 5768 5769
{
  ORDER *order;
  Item	**item_ptr;

unknown's avatar
unknown committed
5770
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*))))
unknown's avatar
unknown committed
5771 5772 5773 5774 5775
    return 1;
  item_ptr = (Item**) (order+1);
  *item_ptr= item;
  order->item=item_ptr;
  order->free_me=0;
5776
  thd->lex->proc_list.link_in_list((uchar*) order,(uchar**) &order->next);
unknown's avatar
unknown committed
5777 5778 5779 5780
  return 0;
}


unknown's avatar
unknown committed
5781 5782 5783
/**
  save order by and tables in own lists.
*/
unknown's avatar
unknown committed
5784

unknown's avatar
unknown committed
5785
bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc)
unknown's avatar
unknown committed
5786 5787 5788
{
  ORDER *order;
  DBUG_ENTER("add_to_list");
unknown's avatar
unknown committed
5789
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER))))
unknown's avatar
unknown committed
5790
    DBUG_RETURN(1);
unknown's avatar
unknown committed
5791 5792
  order->item_ptr= item;
  order->item= &order->item_ptr;
unknown's avatar
unknown committed
5793 5794 5795
  order->asc = asc;
  order->free_me=0;
  order->used=0;
5796
  order->counter_used= 0;
5797
  list.link_in_list((uchar*) order,(uchar**) &order->next);
unknown's avatar
unknown committed
5798 5799 5800 5801
  DBUG_RETURN(0);
}


unknown's avatar
unknown committed
5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815
/**
  Add a table to list of used tables.

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

  @retval
unknown's avatar
unknown committed
5816
      0		Error
unknown's avatar
unknown committed
5817 5818
  @retval
    \#	Pointer to TABLE_LIST element added to the total table list
unknown's avatar
unknown committed
5819 5820
*/

unknown's avatar
unknown committed
5821 5822
TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
					     Table_ident *table,
5823
					     LEX_STRING *alias,
unknown's avatar
unknown committed
5824 5825
					     ulong table_options,
					     thr_lock_type lock_type,
unknown's avatar
unknown committed
5826
					     List<Index_hint> *index_hints_arg,
unknown's avatar
unknown committed
5827
                                             LEX_STRING *option)
unknown's avatar
unknown committed
5828 5829
{
  register TABLE_LIST *ptr;
unknown's avatar
unknown committed
5830
  TABLE_LIST *previous_table_ref; /* The table preceding the current one. */
unknown's avatar
unknown committed
5831
  char *alias_str;
5832
  LEX *lex= thd->lex;
unknown's avatar
unknown committed
5833
  DBUG_ENTER("add_table_to_list");
5834
  LINT_INIT(previous_table_ref);
unknown's avatar
unknown committed
5835 5836 5837 5838

  if (!table)
    DBUG_RETURN(0);				// End of memory
  alias_str= alias ? alias->str : table->table.str;
5839 5840
  if (!test(table_options & TL_OPTION_ALIAS) && 
      check_table_name(table->table.str, table->table.length))
unknown's avatar
unknown committed
5841
  {
5842
    my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str);
unknown's avatar
unknown committed
5843 5844
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
5845 5846

  if (table->is_derived_table() == FALSE && table->db.str &&
5847
      check_db_name(&table->db))
unknown's avatar
unknown committed
5848 5849 5850 5851
  {
    my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str);
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
5852 5853

  if (!alias)					/* Alias is case sensitive */
5854 5855 5856
  {
    if (table->sel)
    {
unknown's avatar
unknown committed
5857 5858
      my_message(ER_DERIVED_MUST_HAVE_ALIAS,
                 ER(ER_DERIVED_MUST_HAVE_ALIAS), MYF(0));
5859 5860
      DBUG_RETURN(0);
    }
5861
    if (!(alias_str= (char*) thd->memdup(alias_str,table->table.length+1)))
unknown's avatar
unknown committed
5862
      DBUG_RETURN(0);
5863
  }
unknown's avatar
unknown committed
5864
  if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
unknown's avatar
unknown committed
5865
    DBUG_RETURN(0);				/* purecov: inspected */
unknown's avatar
unknown committed
5866
  if (table->db.str)
5867
  {
5868
    ptr->is_fqtn= TRUE;
5869 5870 5871
    ptr->db= table->db.str;
    ptr->db_length= table->db.length;
  }
5872
  else if (lex->copy_db_to(&ptr->db, &ptr->db_length))
unknown's avatar
unknown committed
5873
    DBUG_RETURN(0);
5874 5875
  else
    ptr->is_fqtn= FALSE;
unknown's avatar
unknown committed
5876

5877
  ptr->alias= alias_str;
5878
  ptr->is_alias= alias ? TRUE : FALSE;
5879
  if (lower_case_table_names && table->table.length)
5880
    table->table.length= my_casedn_str(files_charset_info, table->table.str);
5881 5882
  ptr->table_name=table->table.str;
  ptr->table_name_length=table->table.length;
5883
  ptr->lock_type=   lock_type;
unknown's avatar
unknown committed
5884
  ptr->updating=    test(table_options & TL_OPTION_UPDATING);
5885
  /* TODO: remove TL_OPTION_FORCE_INDEX as it looks like it's not used */
unknown's avatar
unknown committed
5886
  ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
unknown's avatar
unknown committed
5887
  ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
5888
  ptr->derived=	    table->sel;
5889
  if (!ptr->derived && is_infoschema_db(ptr->db, ptr->db_length))
5890
  {
5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903
    ST_SCHEMA_TABLE *schema_table;
    if (ptr->updating &&
        /* Special cases which are processed by commands itself */
        lex->sql_command != SQLCOM_CHECK &&
        lex->sql_command != SQLCOM_CHECKSUM)
    {
      my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
               thd->security_ctx->priv_user,
               thd->security_ctx->priv_host,
               INFORMATION_SCHEMA_NAME.str);
      DBUG_RETURN(0);
    }
    schema_table= find_schema_table(thd, ptr->table_name);
5904 5905
    if (!schema_table ||
        (schema_table->hidden && 
unknown's avatar
unknown committed
5906
         ((sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0 || 
5907 5908 5909
          /*
            this check is used for show columns|keys from I_S hidden table
          */
unknown's avatar
unknown committed
5910 5911
          lex->sql_command == SQLCOM_SHOW_FIELDS ||
          lex->sql_command == SQLCOM_SHOW_KEYS)))
5912
    {
unknown's avatar
unknown committed
5913
      my_error(ER_UNKNOWN_TABLE, MYF(0),
5914
               ptr->table_name, INFORMATION_SCHEMA_NAME.str);
5915 5916
      DBUG_RETURN(0);
    }
5917
    ptr->schema_table_name= ptr->table_name;
5918 5919
    ptr->schema_table= schema_table;
  }
5920
  ptr->select_lex=  lex->current_select;
unknown's avatar
unknown committed
5921
  ptr->cacheable_table= 1;
5922
  ptr->index_hints= index_hints_arg;
unknown's avatar
unknown committed
5923
  ptr->option= option ? option->str : 0;
unknown's avatar
unknown committed
5924
  /* check that used name is unique */
5925
  if (lock_type != TL_IGNORE)
unknown's avatar
unknown committed
5926
  {
unknown's avatar
unknown committed
5927 5928 5929 5930
    TABLE_LIST *first_table= (TABLE_LIST*) table_list.first;
    if (lex->sql_command == SQLCOM_CREATE_VIEW)
      first_table= first_table ? first_table->next_local : NULL;
    for (TABLE_LIST *tables= first_table ;
unknown's avatar
unknown committed
5931
	 tables ;
unknown's avatar
VIEW  
unknown committed
5932
	 tables=tables->next_local)
unknown's avatar
unknown committed
5933
    {
5934 5935
      if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
	  !strcmp(ptr->db, tables->db))
unknown's avatar
unknown committed
5936
      {
5937
	my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str); /* purecov: tested */
unknown's avatar
unknown committed
5938 5939
	DBUG_RETURN(0);				/* purecov: tested */
      }
unknown's avatar
unknown committed
5940 5941
    }
  }
unknown's avatar
unknown committed
5942 5943 5944
  /* Store the table reference preceding the current one. */
  if (table_list.elements > 0)
  {
5945 5946 5947
    /*
      table_list.next points to the last inserted TABLE_LIST->next_local'
      element
5948
      We don't use the offsetof() macro here to avoid warnings from gcc
5949
    */
5950 5951 5952
    previous_table_ref= (TABLE_LIST*) ((char*) table_list.next -
                                       ((char*) &(ptr->next_local) -
                                        (char*) ptr));
5953 5954 5955 5956 5957 5958 5959 5960
    /*
      Set next_name_resolution_table of the previous table reference to point
      to the current table reference. In effect the list
      TABLE_LIST::next_name_resolution_table coincides with
      TABLE_LIST::next_local. Later this may be changed in
      store_top_level_join_columns() for NATURAL/USING joins.
    */
    previous_table_ref->next_name_resolution_table= ptr;
unknown's avatar
unknown committed
5961
  }
5962

unknown's avatar
unknown committed
5963 5964 5965 5966 5967 5968
  /*
    Link the current table reference in a local list (list for current select).
    Notice that as a side effect here we set the next_local field of the
    previous table reference to 'ptr'. Here we also add one element to the
    list 'table_list'.
  */
5969
  table_list.link_in_list((uchar*) ptr, (uchar**) &ptr->next_local);
unknown's avatar
unknown committed
5970
  ptr->next_name_resolution_table= NULL;
5971
  /* Link table in global list (all used tables) */
5972
  lex->add_to_query_tables(ptr);
5973 5974 5975
  ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name,
                        (ptr->lock_type >= TL_WRITE_ALLOW_WRITE) ?
                        MDL_SHARED_WRITE : MDL_SHARED_READ);
unknown's avatar
unknown committed
5976 5977 5978
  DBUG_RETURN(ptr);
}

unknown's avatar
unknown committed
5979

unknown's avatar
unknown committed
5980 5981
/**
  Initialize a new table list for a nested join.
5982

5983 5984 5985 5986 5987 5988 5989 5990
    The function initializes a structure of the TABLE_LIST type
    for a nested join. It sets up its nested join list as empty.
    The created structure is added to the front of the current
    join list in the st_select_lex object. Then the function
    changes the current nest level for joins to refer to the newly
    created empty list after having saved the info on the old level
    in the initialized structure.

unknown's avatar
unknown committed
5991 5992 5993 5994 5995 5996
  @param thd         current thread

  @retval
    0   if success
  @retval
    1   otherwise
5997 5998 5999 6000 6001 6002 6003
*/

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

6005 6006
  if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
                                       sizeof(NESTED_JOIN))))
6007
    DBUG_RETURN(1);
6008
  nested_join= ptr->nested_join=
6009
    ((NESTED_JOIN*) ((uchar*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
6010

6011 6012 6013
  join_list->push_front(ptr);
  ptr->embedding= embedding;
  ptr->join_list= join_list;
6014
  ptr->alias= (char*) "(nested_join)";
6015 6016 6017 6018 6019 6020 6021
  embedding= ptr;
  join_list= &nested_join->join_list;
  join_list->empty();
  DBUG_RETURN(0);
}


unknown's avatar
unknown committed
6022 6023
/**
  End a nested join table list.
6024 6025 6026

    The function returns to the previous join nest level.
    If the current level contains only one member, the function
6027
    moves it one level up, eliminating the nest.
6028

unknown's avatar
unknown committed
6029 6030 6031 6032 6033
  @param thd         current thread

  @return
    - Pointer to TABLE_LIST element added to the total table list, if success
    - 0, otherwise
6034 6035 6036 6037 6038
*/

TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
{
  TABLE_LIST *ptr;
6039
  NESTED_JOIN *nested_join;
6040
  DBUG_ENTER("end_nested_join");
6041

unknown's avatar
unknown committed
6042
  DBUG_ASSERT(embedding);
6043 6044 6045
  ptr= embedding;
  join_list= ptr->join_list;
  embedding= ptr->embedding;
6046
  nested_join= ptr->nested_join;
6047 6048 6049 6050 6051 6052 6053 6054 6055
  if (nested_join->join_list.elements == 1)
  {
    TABLE_LIST *embedded= nested_join->join_list.head();
    join_list->pop();
    embedded->join_list= join_list;
    embedded->embedding= embedding;
    join_list->push_front(embedded);
    ptr= embedded;
  }
6056
  else if (nested_join->join_list.elements == 0)
unknown's avatar
unknown committed
6057 6058
  {
    join_list->pop();
6059
    ptr= 0;                                     // return value
unknown's avatar
unknown committed
6060
  }
6061 6062 6063 6064
  DBUG_RETURN(ptr);
}


unknown's avatar
unknown committed
6065 6066
/**
  Nest last join operation.
6067 6068 6069

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

unknown's avatar
unknown committed
6070
  @param thd         current thread
6071

unknown's avatar
unknown committed
6072
  @retval
6073
    0  Error
unknown's avatar
unknown committed
6074 6075
  @retval
    \#  Pointer to TABLE_LIST element created for the new nested join
6076 6077 6078 6079 6080 6081
*/

TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
{
  TABLE_LIST *ptr;
  NESTED_JOIN *nested_join;
6082
  List<TABLE_LIST> *embedded_list;
6083
  DBUG_ENTER("nest_last_join");
6084

6085 6086
  if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
                                       sizeof(NESTED_JOIN))))
6087
    DBUG_RETURN(0);
6088
  nested_join= ptr->nested_join=
6089
    ((NESTED_JOIN*) ((uchar*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
6090

6091 6092
  ptr->embedding= embedding;
  ptr->join_list= join_list;
6093
  ptr->alias= (char*) "(nest_last_join)";
6094
  embedded_list= &nested_join->join_list;
6095
  embedded_list->empty();
6096 6097

  for (uint i=0; i < 2; i++)
6098 6099 6100 6101 6102
  {
    TABLE_LIST *table= join_list->pop();
    table->join_list= embedded_list;
    table->embedding= ptr;
    embedded_list->push_back(table);
unknown's avatar
unknown committed
6103 6104 6105 6106 6107 6108 6109
    if (table->natural_join)
    {
      ptr->is_natural_join= TRUE;
      /*
        If this is a JOIN ... USING, move the list of joined fields to the
        table reference that describes the join.
      */
unknown's avatar
unknown committed
6110 6111
      if (prev_join_using)
        ptr->join_using_fields= prev_join_using;
unknown's avatar
unknown committed
6112
    }
6113 6114 6115 6116 6117 6118 6119
  }
  join_list->push_front(ptr);
  nested_join->used_tables= nested_join->not_null_tables= (table_map) 0;
  DBUG_RETURN(ptr);
}


unknown's avatar
unknown committed
6120 6121
/**
  Add a table to the current join list.
6122 6123 6124 6125 6126 6127

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

unknown's avatar
unknown committed
6128 6129 6130
  @param table       the table to add

  @return
6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143
    None
*/

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


unknown's avatar
unknown committed
6144 6145
/**
  Convert a right join into equivalent left join.
6146 6147

    The function takes the current join list t[0],t[1] ... and
6148 6149 6150 6151 6152 6153
    effectively converts it into the list t[1],t[0] ...
    Although the outer_join flag for the new nested table contains
    JOIN_TYPE_RIGHT, it will be handled as the inner table of a left join
    operation.

  EXAMPLES
unknown's avatar
unknown committed
6154
  @verbatim
6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165
    SELECT * FROM t1 RIGHT JOIN t2 ON on_expr =>
      SELECT * FROM t2 LEFT JOIN t1 ON on_expr

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

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

    SELECT * FROM t1 LEFT JOIN t2 ON on_expr1 RIGHT JOIN t3  ON on_expr2 =>
      SELECT * FROM t3 LEFT JOIN (t1 LEFT JOIN t2 ON on_expr2) ON on_expr1
unknown's avatar
unknown committed
6166
   @endverbatim
6167

unknown's avatar
unknown committed
6168
  @param thd         current thread
6169

unknown's avatar
unknown committed
6170 6171 6172
  @return
    - Pointer to the table representing the inner table, if success
    - 0, otherwise
6173 6174
*/

6175
TABLE_LIST *st_select_lex::convert_right_join()
6176 6177
{
  TABLE_LIST *tab2= join_list->pop();
6178
  TABLE_LIST *tab1= join_list->pop();
6179 6180 6181 6182 6183 6184 6185 6186 6187
  DBUG_ENTER("convert_right_join");

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

  DBUG_RETURN(tab1);
}

unknown's avatar
unknown committed
6188 6189
/**
  Set lock for all tables in current select level.
unknown's avatar
unknown committed
6190

unknown's avatar
unknown committed
6191
  @param lock_type			Lock to set for tables
unknown's avatar
unknown committed
6192

unknown's avatar
unknown committed
6193
  @note
unknown's avatar
unknown committed
6194 6195 6196 6197 6198
    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
*/

unknown's avatar
unknown committed
6199
void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
unknown's avatar
unknown committed
6200 6201 6202 6203 6204
{
  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));
unknown's avatar
VIEW  
unknown committed
6205 6206 6207
  for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first;
       tables;
       tables= tables->next_local)
unknown's avatar
unknown committed
6208 6209 6210
  {
    tables->lock_type= lock_type;
    tables->updating=  for_update;
6211 6212
    tables->mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_WRITE) ?
                                 MDL_SHARED_WRITE : MDL_SHARED_READ);
unknown's avatar
unknown committed
6213 6214 6215 6216
  }
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
6217

unknown's avatar
unknown committed
6218 6219
/**
  Create a fake SELECT_LEX for a unit.
unknown's avatar
unknown committed
6220 6221 6222 6223

    The method create a fake SELECT_LEX object for a unit.
    This object is created for any union construct containing a union
    operation and also for any single select union construct of the form
unknown's avatar
unknown committed
6224
    @verbatim
unknown's avatar
unknown committed
6225
    (SELECT ... ORDER BY order_list [LIMIT n]) ORDER BY ... 
unknown's avatar
unknown committed
6226
    @endvarbatim
unknown's avatar
unknown committed
6227
    or of the form
unknown's avatar
unknown committed
6228
    @varbatim
unknown's avatar
unknown committed
6229
    (SELECT ... ORDER BY LIMIT n) ORDER BY ...
unknown's avatar
unknown committed
6230
    @endvarbatim
unknown's avatar
unknown committed
6231
  
unknown's avatar
unknown committed
6232 6233 6234
  @param thd_arg		   thread handle

  @note
unknown's avatar
unknown committed
6235 6236 6237
    The object is used to retrieve rows from the temporary table
    where the result on the union is obtained.

unknown's avatar
unknown committed
6238
  @retval
unknown's avatar
unknown committed
6239
    1     on failure to create the object
unknown's avatar
unknown committed
6240
  @retval
unknown's avatar
unknown committed
6241 6242 6243
    0     on success
*/

unknown's avatar
unknown committed
6244
bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
unknown's avatar
unknown committed
6245 6246 6247 6248
{
  SELECT_LEX *first_sl= first_select();
  DBUG_ENTER("add_fake_select_lex");
  DBUG_ASSERT(!fake_select_lex);
unknown's avatar
unknown committed
6249

unknown's avatar
unknown committed
6250
  if (!(fake_select_lex= new (thd_arg->mem_root) SELECT_LEX()))
unknown's avatar
unknown committed
6251 6252 6253 6254
      DBUG_RETURN(1);
  fake_select_lex->include_standalone(this, 
                                      (SELECT_LEX_NODE**)&fake_select_lex);
  fake_select_lex->select_number= INT_MAX;
unknown's avatar
unknown committed
6255
  fake_select_lex->parent_lex= thd_arg->lex; /* Used in init_query. */
unknown's avatar
unknown committed
6256 6257
  fake_select_lex->make_empty_select();
  fake_select_lex->linkage= GLOBAL_OPTIONS_TYPE;
6258 6259
  fake_select_lex->select_limit= 0;

unknown's avatar
unknown committed
6260
  fake_select_lex->context.outer_context=first_sl->context.outer_context;
6261 6262 6263
  /* allow item list resolving in fake select for ORDER BY */
  fake_select_lex->context.resolve_in_select_list= TRUE;
  fake_select_lex->context.select_lex= fake_select_lex;
unknown's avatar
unknown committed
6264

6265
  if (!is_union())
unknown's avatar
unknown committed
6266 6267 6268 6269 6270 6271 6272 6273 6274
  {
    /* 
      This works only for 
      (SELECT ... ORDER BY list [LIMIT n]) ORDER BY order_list [LIMIT m],
      (SELECT ... LIMIT n) ORDER BY order_list [LIMIT m]
      just before the parser starts processing order_list
    */ 
    global_parameters= fake_select_lex;
    fake_select_lex->no_table_names_allowed= 1;
unknown's avatar
unknown committed
6275
    thd_arg->lex->current_select= fake_select_lex;
unknown's avatar
unknown committed
6276
  }
unknown's avatar
unknown committed
6277
  thd_arg->lex->pop_context();
unknown's avatar
unknown committed
6278 6279 6280
  DBUG_RETURN(0);
}

6281

unknown's avatar
unknown committed
6282
/**
6283 6284
  Push a new name resolution context for a JOIN ... ON clause to the
  context stack of a query block.
unknown's avatar
unknown committed
6285 6286

    Create a new name resolution context for a JOIN ... ON clause,
6287 6288 6289
    set the first and last leaves of the list of table references
    to be used for name resolution, and push the newly created
    context to the stack of contexts of the query.
unknown's avatar
unknown committed
6290

unknown's avatar
unknown committed
6291 6292 6293 6294 6295
  @param thd       pointer to current thread
  @param left_op   left  operand of the JOIN
  @param right_op  rigth operand of the JOIN

  @retval
6296
    FALSE  if all is OK
unknown's avatar
unknown committed
6297
  @retval
6298
    TRUE   if a memory allocation error occured
unknown's avatar
unknown committed
6299 6300
*/

6301 6302 6303
bool
push_new_name_resolution_context(THD *thd,
                                 TABLE_LIST *left_op, TABLE_LIST *right_op)
unknown's avatar
unknown committed
6304 6305
{
  Name_resolution_context *on_context;
6306
  if (!(on_context= new (thd->mem_root) Name_resolution_context))
6307
    return TRUE;
unknown's avatar
unknown committed
6308 6309 6310 6311 6312
  on_context->init();
  on_context->first_name_resolution_table=
    left_op->first_leaf_for_name_resolution();
  on_context->last_name_resolution_table=
    right_op->last_leaf_for_name_resolution();
6313
  return thd->lex->push_context(on_context);
unknown's avatar
unknown committed
6314 6315 6316
}


unknown's avatar
unknown committed
6317
/**
unknown's avatar
unknown committed
6318 6319 6320 6321
  Add an ON condition to the second operand of a JOIN ... ON.

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

unknown's avatar
unknown committed
6322 6323
  @param b     the second operand of a JOIN ... ON
  @param expr  the condition to be added to the ON clause
unknown's avatar
unknown committed
6324

unknown's avatar
unknown committed
6325
  @retval
unknown's avatar
unknown committed
6326
    FALSE  if there was some error
unknown's avatar
unknown committed
6327
  @retval
unknown's avatar
unknown committed
6328 6329 6330 6331
    TRUE   if all is OK
*/

void add_join_on(TABLE_LIST *b, Item *expr)
unknown's avatar
unknown committed
6332
{
6333
  if (expr)
6334
  {
6335
    if (!b->on_expr)
unknown's avatar
unknown committed
6336
      b->on_expr= expr;
6337 6338
    else
    {
unknown's avatar
unknown committed
6339 6340 6341 6342 6343 6344
      /*
        If called from the parser, this happens if you have both a
        right and left join. If called later, it happens if we add more
        than one condition to the ON clause.
      */
      b->on_expr= new Item_cond_and(b->on_expr,expr);
6345 6346
    }
    b->on_expr->top_level_item();
6347
  }
unknown's avatar
unknown committed
6348 6349 6350
}


unknown's avatar
unknown committed
6351
/**
unknown's avatar
unknown committed
6352 6353
  Mark that there is a NATURAL JOIN or JOIN ... USING between two
  tables.
6354

unknown's avatar
unknown committed
6355 6356 6357 6358 6359 6360 6361 6362 6363 6364
    This function marks that table b should be joined with a either via
    a NATURAL JOIN or via JOIN ... USING. Both join types are special
    cases of each other, so we treat them together. The function
    setup_conds() creates a list of equal condition between all fields
    of the same name for NATURAL JOIN or the fields in 'using_fields'
    for JOIN ... USING. The list of equality conditions is stored
    either in b->on_expr, or in JOIN::conds, depending on whether there
    was an outer join.

  EXAMPLE
unknown's avatar
unknown committed
6365
  @verbatim
6366 6367 6368
    SELECT * FROM t1 NATURAL LEFT JOIN t2
     <=>
    SELECT * FROM t1 LEFT JOIN t2 ON (t1.i=t2.i and t1.j=t2.j ... )
unknown's avatar
unknown committed
6369

unknown's avatar
unknown committed
6370 6371 6372
    SELECT * FROM t1 NATURAL JOIN t2 WHERE <some_cond>
     <=>
    SELECT * FROM t1, t2 WHERE (t1.i=t2.i and t1.j=t2.j and <some_cond>)
unknown's avatar
unknown committed
6373

unknown's avatar
unknown committed
6374 6375 6376
    SELECT * FROM t1 JOIN t2 USING(j) WHERE <some_cond>
     <=>
    SELECT * FROM t1, t2 WHERE (t1.j=t2.j and <some_cond>)
unknown's avatar
unknown committed
6377 6378 6379 6380 6381
   @endverbatim

  @param a		  Left join argument
  @param b		  Right join argument
  @param using_fields    Field names from USING clause
6382 6383
*/

unknown's avatar
unknown committed
6384 6385
void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
                      SELECT_LEX *lex)
unknown's avatar
unknown committed
6386
{
unknown's avatar
unknown committed
6387
  b->natural_join= a;
unknown's avatar
unknown committed
6388
  lex->prev_join_using= using_fields;
unknown's avatar
unknown committed
6389 6390
}

unknown's avatar
unknown committed
6391

6392
/**
6393 6394
  Reload/resets privileges and the different caches.

6395 6396 6397 6398
  @param thd Thread handler (can be NULL!)
  @param options What should be reset/reloaded (tables, privileges, slave...)
  @param tables Tables to flush (if any)
  @param write_to_binlog True if we can write to the binlog.
6399
               
6400 6401 6402 6403 6404 6405 6406 6407
  @note Depending on 'options', it may be very bad to write the
    query to the binlog (e.g. FLUSH SLAVE); this is a
    pointer where reload_acl_and_cache() will put 0 if
    it thinks we really should not write to the binlog.
    Otherwise it will put 1.

  @return Error status code
    @retval 0 Ok
6408
    @retval !=0  Error; thd->killed is set or thd->is_error() is true
6409 6410
*/

6411 6412
bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
                          bool *write_to_binlog)
unknown's avatar
unknown committed
6413 6414 6415
{
  bool result=0;
  select_errors=0;				/* Write if more errors */
6416
  bool tmp_write_to_binlog= 1;
6417

6418
  DBUG_ASSERT(!thd || !thd->in_sub_stmt);
6419

unknown's avatar
SCRUM  
unknown committed
6420
#ifndef NO_EMBEDDED_ACCESS_CHECKS
unknown's avatar
unknown committed
6421 6422
  if (options & REFRESH_GRANT)
  {
6423 6424 6425 6426 6427 6428
    THD *tmp_thd= 0;
    /*
      If reload_acl_and_cache() is called from SIGHUP handler we have to
      allocate temporary THD for execution of acl_reload()/grant_reload().
    */
    if (!thd && (thd= (tmp_thd= new THD)))
6429 6430
    {
      thd->thread_stack= (char*) &tmp_thd;
6431
      thd->store_globals();
6432
    }
6433

6434 6435
    if (thd)
    {
6436 6437
      bool reload_acl_failed= acl_reload(thd);
      bool reload_grants_failed= grant_reload(thd);
Kristofer Pettersson's avatar
Kristofer Pettersson committed
6438
      bool reload_servers_failed= servers_reload(thd);
6439

Kristofer Pettersson's avatar
Kristofer Pettersson committed
6440
      if (reload_acl_failed || reload_grants_failed || reload_servers_failed)
6441
      {
6442
        result= 1;
6443 6444 6445 6446 6447 6448
        /*
          When an error is returned, my_message may have not been called and
          the client will hang waiting for a response.
        */
        my_error(ER_UNKNOWN_ERROR, MYF(0), "FLUSH PRIVILEGES failed");
      }
6449
    }
6450

6451 6452 6453 6454 6455 6456 6457
    if (tmp_thd)
    {
      delete tmp_thd;
      /* Remember that we don't have a THD */
      my_pthread_setspecific_ptr(THR_THD,  0);
      thd= 0;
    }
6458
    reset_mqh((LEX_USER *)NULL, TRUE);
unknown's avatar
unknown committed
6459
  }
unknown's avatar
SCRUM  
unknown committed
6460
#endif
unknown's avatar
unknown committed
6461 6462
  if (options & REFRESH_LOG)
  {
6463
    /*
unknown's avatar
unknown committed
6464
      Flush the normal query log, the update log, the binary log,
unknown's avatar
unknown committed
6465 6466
      the slow query log, the relay log (if it exists) and the log
      tables.
6467
    */
unknown's avatar
unknown committed
6468

6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492
    options|= REFRESH_BINARY_LOG;
    options|= REFRESH_RELAY_LOG;
    options|= REFRESH_SLOW_LOG;
    options|= REFRESH_GENERAL_LOG;
    options|= REFRESH_ENGINE_LOG;
    options|= REFRESH_ERROR_LOG;
  }

  if (options & REFRESH_ERROR_LOG)
    if (flush_error_log())
      result= 1;

  if ((options & REFRESH_SLOW_LOG) && opt_slow_log)
    logger.flush_slow_log();

  if ((options & REFRESH_GENERAL_LOG) && opt_log)
    logger.flush_general_log();

  if (options & REFRESH_ENGINE_LOG)
    if (ha_flush_logs(NULL))
      result= 1;

  if (options & REFRESH_BINARY_LOG)
  {
6493
    /*
unknown's avatar
unknown committed
6494 6495 6496 6497
      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)
6498 6499
    */
    tmp_write_to_binlog= 0;
6500
    if (mysql_bin_log.is_open())
6501
      mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
6502 6503 6504
  }
  if (options & REFRESH_RELAY_LOG)
  {
unknown's avatar
unknown committed
6505
#ifdef HAVE_REPLICATION
Marc Alff's avatar
Marc Alff committed
6506
    mysql_mutex_lock(&LOCK_active_mi);
6507
    rotate_relay_log(active_mi);
Marc Alff's avatar
Marc Alff committed
6508
    mysql_mutex_unlock(&LOCK_active_mi);
unknown's avatar
unknown committed
6509
#endif
unknown's avatar
unknown committed
6510
  }
unknown's avatar
unknown committed
6511
#ifdef HAVE_QUERY_CACHE
unknown's avatar
unknown committed
6512 6513
  if (options & REFRESH_QUERY_CACHE_FREE)
  {
unknown's avatar
unknown committed
6514
    query_cache.pack();				// FLUSH QUERY CACHE
6515
    options &= ~REFRESH_QUERY_CACHE;    // Don't flush cache, just free memory
unknown's avatar
unknown committed
6516 6517 6518
  }
  if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
  {
unknown's avatar
unknown committed
6519
    query_cache.flush();			// RESET QUERY CACHE
unknown's avatar
unknown committed
6520
  }
unknown's avatar
unknown committed
6521
#endif /*HAVE_QUERY_CACHE*/
6522

6523 6524
  DBUG_ASSERT(!thd || thd->locked_tables_mode ||
              !thd->mdl_context.has_locks() ||
6525
              thd->handler_tables_hash.records ||
6526
              thd->global_read_lock.is_acquired());
6527

6528 6529 6530 6531 6532
  /*
    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)) 
unknown's avatar
unknown committed
6533
  {
6534
    if ((options & REFRESH_READ_LOCK) && thd)
unknown's avatar
unknown committed
6535
    {
6536
      /*
6537 6538 6539 6540
        On the first hand we need write lock on the tables to be flushed,
        on the other hand we must not try to aspire a global read lock
        if we have a write locked table as this would lead to a deadlock
        when trying to reopen (and re-lock) the table after the flush.
6541
      */
Konstantin Osipov's avatar
Konstantin Osipov committed
6542
      if (thd->locked_tables_mode)
6543
      {
6544 6545
        my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
        return 1;
6546
      }
unknown's avatar
unknown committed
6547 6548 6549 6550
      /*
	Writing to the binlog could cause deadlocks, as we don't log
	UNLOCK TABLES
      */
6551
      tmp_write_to_binlog= 0;
6552
      if (thd->global_read_lock.lock_global_read_lock(thd))
unknown's avatar
unknown committed
6553
	return 1;                               // Killed
Kristofer Pettersson's avatar
Kristofer Pettersson committed
6554
      if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ?
6555
                              FALSE : TRUE))
6556 6557
          result= 1;
      
6558
      if (thd->global_read_lock.make_global_read_lock_block_commit(thd)) // Killed
6559 6560
      {
        /* Don't leave things in a half-locked state */
6561
        thd->global_read_lock.unlock_global_read_lock(thd);
6562 6563
        return 1;
      }
unknown's avatar
unknown committed
6564
    }
6565
    else
6566
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
6567
      if (thd && thd->locked_tables_mode)
6568 6569 6570 6571 6572 6573 6574 6575
      {
        /*
          If we are under LOCK TABLES we should have a write
          lock on tables which we are going to flush.
        */
        if (tables)
        {
          for (TABLE_LIST *t= tables; t; t= t->next_local)
6576 6577
            if (!find_table_for_mdl_upgrade(thd->open_tables, t->db,
                                            t->table_name, FALSE))
6578 6579 6580 6581 6582 6583
              return 1;
        }
        else
        {
          for (TABLE *tab= thd->open_tables; tab; tab= tab->next)
          {
6584
            if (! tab->mdl_ticket->is_upgradable_or_exclusive())
6585 6586 6587 6588 6589 6590 6591 6592 6593
            {
              my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),
                       tab->s->table_name.str);
              return 1;
            }
          }
        }
      }

Kristofer Pettersson's avatar
Kristofer Pettersson committed
6594
      if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ?
6595
                              FALSE : TRUE))
6596 6597
        result= 1;
    }
unknown's avatar
unknown committed
6598
    my_dbopt_cleanup();
unknown's avatar
unknown committed
6599 6600 6601
  }
  if (options & REFRESH_HOSTS)
    hostname_cache_refresh();
unknown's avatar
unknown committed
6602
  if (thd && (options & REFRESH_STATUS))
unknown's avatar
unknown committed
6603
    refresh_status(thd);
unknown's avatar
unknown committed
6604 6605
  if (options & REFRESH_THREADS)
    flush_thread_cache();
unknown's avatar
unknown committed
6606
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
6607
  if (options & REFRESH_MASTER)
6608
  {
6609
    DBUG_ASSERT(thd);
6610
    tmp_write_to_binlog= 0;
6611
    if (reset_master(thd))
unknown's avatar
unknown committed
6612
    {
6613
      result=1;
unknown's avatar
unknown committed
6614
    }
6615
  }
6616
#endif
unknown's avatar
unknown committed
6617
#ifdef OPENSSL
6618 6619
   if (options & REFRESH_DES_KEY_FILE)
   {
6620 6621
     if (des_key_file && load_des_key_file(des_key_file))
         result= 1;
6622 6623
   }
#endif
unknown's avatar
unknown committed
6624
#ifdef HAVE_REPLICATION
6625 6626
 if (options & REFRESH_SLAVE)
 {
6627
   tmp_write_to_binlog= 0;
Marc Alff's avatar
Marc Alff committed
6628
   mysql_mutex_lock(&LOCK_active_mi);
6629
   if (reset_slave(thd, active_mi))
6630
     result=1;
Marc Alff's avatar
Marc Alff committed
6631
   mysql_mutex_unlock(&LOCK_active_mi);
6632
 }
6633
#endif
6634
 if (options & REFRESH_USER_RESOURCES)
6635
   reset_mqh((LEX_USER *) NULL, 0);             /* purecov: inspected */
unknown's avatar
unknown committed
6636
 *write_to_binlog= tmp_write_to_binlog;
6637 6638 6639
 /*
   If the query was killed then this function must fail.
 */
6640
 return result || (thd ? thd->killed : 0);
unknown's avatar
unknown committed
6641 6642
}

6643

unknown's avatar
unknown committed
6644 6645
/**
  kill on thread.
6646

unknown's avatar
unknown committed
6647 6648 6649
  @param thd			Thread class
  @param id			Thread id
  @param only_kill_query        Should it kill the query or the connection
6650

unknown's avatar
unknown committed
6651
  @note
6652 6653 6654
    This is written such that we have a short lock on LOCK_thread_count
*/

6655
uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
unknown's avatar
unknown committed
6656 6657 6658
{
  THD *tmp;
  uint error=ER_NO_SUCH_THREAD;
6659 6660
  DBUG_ENTER("kill_one_thread");
  DBUG_PRINT("enter", ("id=%lu only_kill=%d", id, only_kill_query));
Marc Alff's avatar
Marc Alff committed
6661
  mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
6662
  I_List_iterator<THD> it(threads);
unknown's avatar
unknown committed
6663 6664
  while ((tmp=it++))
  {
unknown's avatar
unknown committed
6665 6666
    if (tmp->command == COM_DAEMON)
      continue;
unknown's avatar
unknown committed
6667 6668
    if (tmp->thread_id == id)
    {
Marc Alff's avatar
Marc Alff committed
6669
      mysql_mutex_lock(&tmp->LOCK_thd_data);    // Lock from delete
6670
      break;
unknown's avatar
unknown committed
6671 6672
    }
  }
Marc Alff's avatar
Marc Alff committed
6673
  mysql_mutex_unlock(&LOCK_thread_count);
6674 6675
  if (tmp)
  {
6676 6677 6678 6679 6680

    /*
      If we're SUPER, we can KILL anything, including system-threads.
      No further checks.

6681 6682 6683
      KILLer: thd->security_ctx->user could in theory be NULL while
      we're still in "unauthenticated" state. This is a theoretical
      case (the code suggests this could happen, so we play it safe).
6684

6685
      KILLee: tmp->security_ctx->user will be NULL for system threads.
6686
      We need to check so Jane Random User doesn't crash the server
6687 6688
      when trying to kill a) system threads or b) unauthenticated users'
      threads (Bug#43748).
6689

6690
      If user of both killer and killee are non-NULL, proceed with
6691 6692 6693
      slayage if both are string-equal.
    */

6694
    if ((thd->security_ctx->master_access & SUPER_ACL) ||
6695
        thd->security_ctx->user_matches(tmp->security_ctx))
6696
    {
unknown's avatar
SCRUM  
unknown committed
6697
      tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
6698 6699 6700 6701
      error=0;
    }
    else
      error=ER_KILL_DENIED_ERROR;
Marc Alff's avatar
Marc Alff committed
6702
    mysql_mutex_unlock(&tmp->LOCK_thd_data);
6703
  }
6704 6705 6706 6707
  DBUG_PRINT("exit", ("%d", error));
  DBUG_RETURN(error);
}

6708

6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722
/*
  kills a thread and sends response

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

void sql_kill(THD *thd, ulong id, bool only_kill_query)
{
  uint error;
  if (!(error= kill_one_thread(thd, id, only_kill_query)))
6723
    my_ok(thd);
unknown's avatar
unknown committed
6724
  else
unknown's avatar
unknown committed
6725
    my_error(error, MYF(0), id);
unknown's avatar
unknown committed
6726 6727
}

unknown's avatar
unknown committed
6728

unknown's avatar
unknown committed
6729
/** If pointer is not a null pointer, append filename to it. */
6730

unknown's avatar
unknown committed
6731 6732
bool append_file_to_dir(THD *thd, const char **filename_ptr,
                        const char *table_name)
6733
{
6734
  char buff[FN_REFLEN],*ptr, *end;
6735 6736 6737 6738 6739 6740 6741
  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))
  {
6742
    my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr);
6743 6744 6745 6746
    return 1;
  }
  /* Fix is using unix filename format on dos */
  strmov(buff,*filename_ptr);
6747
  end=convert_dirname(buff, *filename_ptr, NullS);
6748
  if (!(ptr= (char*) thd->alloc((size_t) (end-buff) + strlen(table_name)+1)))
6749 6750
    return 1;					// End of memory
  *filename_ptr=ptr;
6751
  strxmov(ptr,buff,table_name,NullS);
6752 6753
  return 0;
}
6754

6755

unknown's avatar
unknown committed
6756 6757
/**
  Check if the select is a simple select (not an union).
6758

unknown's avatar
unknown committed
6759
  @retval
6760
    0	ok
unknown's avatar
unknown committed
6761
  @retval
6762 6763 6764 6765 6766 6767
    1	error	; In this case the error messege is sent to the client
*/

bool check_simple_select()
{
  THD *thd= current_thd;
6768 6769
  LEX *lex= thd->lex;
  if (lex->current_select != &lex->select_lex)
6770 6771
  {
    char command[80];
6772
    Lex_input_stream *lip= & thd->m_parser_state->m_lip;
6773 6774
    strmake(command, lip->yylval->symbol.str,
	    min(lip->yylval->symbol.length, sizeof(command)-1));
6775
    my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command);
6776 6777 6778 6779
    return 1;
  }
  return 0;
}
unknown's avatar
unknown committed
6780

unknown's avatar
unknown committed
6781

unknown's avatar
unknown committed
6782
Comp_creator *comp_eq_creator(bool invert)
unknown's avatar
unknown committed
6783
{
unknown's avatar
unknown committed
6784
  return invert?(Comp_creator *)&ne_creator:(Comp_creator *)&eq_creator;
unknown's avatar
unknown committed
6785 6786
}

unknown's avatar
unknown committed
6787

unknown's avatar
unknown committed
6788
Comp_creator *comp_ge_creator(bool invert)
unknown's avatar
unknown committed
6789
{
unknown's avatar
unknown committed
6790
  return invert?(Comp_creator *)&lt_creator:(Comp_creator *)&ge_creator;
unknown's avatar
unknown committed
6791 6792
}

unknown's avatar
unknown committed
6793

unknown's avatar
unknown committed
6794
Comp_creator *comp_gt_creator(bool invert)
unknown's avatar
unknown committed
6795
{
unknown's avatar
unknown committed
6796
  return invert?(Comp_creator *)&le_creator:(Comp_creator *)&gt_creator;
unknown's avatar
unknown committed
6797 6798
}

unknown's avatar
unknown committed
6799

unknown's avatar
unknown committed
6800
Comp_creator *comp_le_creator(bool invert)
unknown's avatar
unknown committed
6801
{
unknown's avatar
unknown committed
6802
  return invert?(Comp_creator *)&gt_creator:(Comp_creator *)&le_creator;
unknown's avatar
unknown committed
6803 6804
}

unknown's avatar
unknown committed
6805

unknown's avatar
unknown committed
6806
Comp_creator *comp_lt_creator(bool invert)
unknown's avatar
unknown committed
6807
{
unknown's avatar
unknown committed
6808
  return invert?(Comp_creator *)&ge_creator:(Comp_creator *)&lt_creator;
unknown's avatar
unknown committed
6809 6810
}

unknown's avatar
unknown committed
6811

unknown's avatar
unknown committed
6812
Comp_creator *comp_ne_creator(bool invert)
unknown's avatar
unknown committed
6813
{
unknown's avatar
unknown committed
6814
  return invert?(Comp_creator *)&eq_creator:(Comp_creator *)&ne_creator;
unknown's avatar
unknown committed
6815
}
unknown's avatar
unknown committed
6816 6817


unknown's avatar
unknown committed
6818 6819
/**
  Construct ALL/ANY/SOME subquery Item.
unknown's avatar
unknown committed
6820

unknown's avatar
unknown committed
6821 6822 6823 6824
  @param left_expr   pointer to left expression
  @param cmp         compare function creator
  @param all         true if we create ALL subquery
  @param select_lex  pointer on parsed subquery structure
unknown's avatar
unknown committed
6825

unknown's avatar
unknown committed
6826
  @return
unknown's avatar
unknown committed
6827 6828 6829 6830 6831 6832 6833
    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)
{
unknown's avatar
unknown committed
6834
  if ((cmp == &comp_eq_creator) && !all)       //  = ANY <=> IN
unknown's avatar
unknown committed
6835
    return new Item_in_subselect(left_expr, select_lex);
unknown's avatar
unknown committed
6836 6837

  if ((cmp == &comp_ne_creator) && all)        // <> ALL <=> NOT IN
unknown's avatar
unknown committed
6838 6839 6840
    return new Item_func_not(new Item_in_subselect(left_expr, select_lex));

  Item_allany_subselect *it=
6841
    new Item_allany_subselect(left_expr, cmp, select_lex, all);
unknown's avatar
unknown committed
6842
  if (all)
6843
    return it->upper_item= new Item_func_not_all(it);	/* ALL */
unknown's avatar
unknown committed
6844

6845
  return it->upper_item= new Item_func_nop_all(it);      /* ANY/SOME */
unknown's avatar
unknown committed
6846
}
6847 6848


unknown's avatar
unknown committed
6849 6850
/**
  Multi update query pre-check.
6851

unknown's avatar
unknown committed
6852 6853
  @param thd		Thread handler
  @param tables	Global/local table list (have to be the same)
6854

unknown's avatar
unknown committed
6855
  @retval
unknown's avatar
unknown committed
6856
    FALSE OK
unknown's avatar
unknown committed
6857
  @retval
unknown's avatar
unknown committed
6858
    TRUE  Error
6859
*/
unknown's avatar
unknown committed
6860

unknown's avatar
unknown committed
6861
bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
6862 6863 6864 6865 6866
{
  const char *msg= 0;
  TABLE_LIST *table;
  LEX *lex= thd->lex;
  SELECT_LEX *select_lex= &lex->select_lex;
unknown's avatar
VIEW  
unknown committed
6867
  DBUG_ENTER("multi_update_precheck");
6868 6869 6870

  if (select_lex->item_list.elements != lex->value_list.elements)
  {
6871
    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
unknown's avatar
unknown committed
6872
    DBUG_RETURN(TRUE);
6873 6874 6875 6876 6877
  }
  /*
    Ensure that we have UPDATE or SELECT privilege for each table
    The exact privilege is checked in mysql_multi_update()
  */
unknown's avatar
VIEW  
unknown committed
6878
  for (table= tables; table; table= table->next_local)
6879
  {
6880 6881 6882
    if (table->derived)
      table->grant.privilege= SELECT_ACL;
    else if ((check_access(thd, UPDATE_ACL, table->db,
Marc Alff's avatar
Marc Alff committed
6883 6884 6885
                           &table->grant.privilege,
                           &table->grant.m_internal,
                           0, 1) ||
6886
              check_grant(thd, UPDATE_ACL, table, FALSE, 1, TRUE)) &&
unknown's avatar
unknown committed
6887
             (check_access(thd, SELECT_ACL, table->db,
Marc Alff's avatar
Marc Alff committed
6888 6889 6890
                           &table->grant.privilege,
                           &table->grant.m_internal,
                           0, 0) ||
6891
              check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE)))
unknown's avatar
unknown committed
6892
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
6893

unknown's avatar
VIEW  
unknown committed
6894
    table->table_in_first_from_clause= 1;
6895
  }
unknown's avatar
unknown committed
6896 6897 6898
  /*
    Is there tables of subqueries?
  */
6899
  if (&lex->select_lex != lex->all_selects_list)
6900
  {
6901
    DBUG_PRINT("info",("Checking sub query list"));
unknown's avatar
VIEW  
unknown committed
6902
    for (table= tables; table; table= table->next_global)
6903
    {
6904
      if (!table->table_in_first_from_clause)
6905 6906
      {
	if (check_access(thd, SELECT_ACL, table->db,
Marc Alff's avatar
Marc Alff committed
6907 6908 6909
                         &table->grant.privilege,
                         &table->grant.m_internal,
                         0, 0) ||
6910
	    check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE))
unknown's avatar
unknown committed
6911
	  DBUG_RETURN(TRUE);
6912 6913 6914 6915 6916 6917
      }
    }
  }

  if (select_lex->order_list.elements)
    msg= "ORDER BY";
6918
  else if (select_lex->select_limit)
6919 6920 6921 6922
    msg= "LIMIT";
  if (msg)
  {
    my_error(ER_WRONG_USAGE, MYF(0), "UPDATE", msg);
unknown's avatar
unknown committed
6923
    DBUG_RETURN(TRUE);
6924
  }
unknown's avatar
unknown committed
6925
  DBUG_RETURN(FALSE);
6926 6927
}

unknown's avatar
unknown committed
6928 6929
/**
  Multi delete query pre-check.
6930

unknown's avatar
unknown committed
6931 6932
  @param thd			Thread handler
  @param tables		Global/local table list
6933

unknown's avatar
unknown committed
6934
  @retval
unknown's avatar
unknown committed
6935
    FALSE OK
unknown's avatar
unknown committed
6936
  @retval
unknown's avatar
unknown committed
6937
    TRUE  error
6938
*/
unknown's avatar
unknown committed
6939

6940
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
6941 6942 6943
{
  SELECT_LEX *select_lex= &thd->lex->select_lex;
  TABLE_LIST *aux_tables=
unknown's avatar
unknown committed
6944
    (TABLE_LIST *)thd->lex->auxiliary_table_list.first;
6945
  TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
unknown's avatar
VIEW  
unknown committed
6946
  DBUG_ENTER("multi_delete_precheck");
unknown's avatar
unknown committed
6947

6948 6949
  /* sql_yacc guarantees that tables and aux_tables are not zero */
  DBUG_ASSERT(aux_tables != 0);
6950
  if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
6951 6952 6953 6954 6955 6956 6957 6958
    DBUG_RETURN(TRUE);

  /*
    Since aux_tables list is not part of LEX::query_tables list we
    have to juggle with LEX::query_tables_own_last value to be able
    call check_table_access() safely.
  */
  thd->lex->query_tables_own_last= 0;
6959
  if (check_table_access(thd, DELETE_ACL, aux_tables, FALSE, UINT_MAX, FALSE))
6960 6961
  {
    thd->lex->query_tables_own_last= save_query_tables_own_last;
unknown's avatar
unknown committed
6962
    DBUG_RETURN(TRUE);
6963 6964 6965
  }
  thd->lex->query_tables_own_last= save_query_tables_own_last;

6966
  if ((thd->variables.option_bits & OPTION_SAFE_UPDATES) && !select_lex->where)
6967
  {
unknown's avatar
unknown committed
6968 6969
    my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
               ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
unknown's avatar
unknown committed
6970
    DBUG_RETURN(TRUE);
6971
  }
6972 6973 6974 6975
  DBUG_RETURN(FALSE);
}


6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032
/*
  Given a table in the source list, find a correspondent table in the
  table references list.

  @param lex Pointer to LEX representing multi-delete.
  @param src Source table to match.
  @param ref Table references list.

  @remark The source table list (tables listed before the FROM clause
  or tables listed in the FROM clause before the USING clause) may
  contain table names or aliases that must match unambiguously one,
  and only one, table in the target table list (table references list,
  after FROM/USING clause).

  @return Matching table, NULL otherwise.
*/

static TABLE_LIST *multi_delete_table_match(LEX *lex, TABLE_LIST *tbl,
                                            TABLE_LIST *tables)
{
  TABLE_LIST *match= NULL;
  DBUG_ENTER("multi_delete_table_match");

  for (TABLE_LIST *elem= tables; elem; elem= elem->next_local)
  {
    int cmp;

    if (tbl->is_fqtn && elem->is_alias)
      continue; /* no match */
    if (tbl->is_fqtn && elem->is_fqtn)
      cmp= my_strcasecmp(table_alias_charset, tbl->table_name, elem->table_name) ||
           strcmp(tbl->db, elem->db);
    else if (elem->is_alias)
      cmp= my_strcasecmp(table_alias_charset, tbl->alias, elem->alias);
    else
      cmp= my_strcasecmp(table_alias_charset, tbl->table_name, elem->table_name) ||
           strcmp(tbl->db, elem->db);

    if (cmp)
      continue;

    if (match)
    {
      my_error(ER_NONUNIQ_TABLE, MYF(0), elem->alias);
      DBUG_RETURN(NULL);
    }

    match= elem;
  }

  if (!match)
    my_error(ER_UNKNOWN_TABLE, MYF(0), tbl->table_name, "MULTI DELETE");

  DBUG_RETURN(match);
}


unknown's avatar
unknown committed
7033
/**
7034 7035 7036
  Link tables in auxilary table list of multi-delete with corresponding
  elements in main table list, and set proper locks for them.

unknown's avatar
unknown committed
7037
  @param lex   pointer to LEX representing multi-delete
7038

unknown's avatar
unknown committed
7039 7040 7041 7042
  @retval
    FALSE   success
  @retval
    TRUE    error
7043 7044 7045 7046 7047 7048 7049 7050 7051 7052
*/

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

  lex->table_count= 0;

unknown's avatar
unknown committed
7053
  for (target_tbl= (TABLE_LIST *)lex->auxiliary_table_list.first;
7054
       target_tbl; target_tbl= target_tbl->next_local)
7055
  {
7056
    lex->table_count++;
7057
    /* All tables in aux_tables must be found in FROM PART */
7058
    TABLE_LIST *walk= multi_delete_table_match(lex, target_tbl, tables);
7059
    if (!walk)
unknown's avatar
unknown committed
7060
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7061 7062 7063 7064 7065
    if (!walk->derived)
    {
      target_tbl->table_name= walk->table_name;
      target_tbl->table_name_length= walk->table_name_length;
    }
unknown's avatar
unknown committed
7066
    walk->updating= target_tbl->updating;
unknown's avatar
unknown committed
7067
    walk->lock_type= target_tbl->lock_type;
7068 7069 7070
    /* We can assume that tables to be deleted from are locked for write. */
    DBUG_ASSERT(walk->lock_type >= TL_WRITE_ALLOW_WRITE);
    walk->mdl_request.set_type(MDL_SHARED_WRITE);
unknown's avatar
VIEW  
unknown committed
7071
    target_tbl->correspondent_table= walk;	// Remember corresponding table
7072
  }
unknown's avatar
unknown committed
7073
  DBUG_RETURN(FALSE);
7074 7075 7076
}


unknown's avatar
unknown committed
7077 7078
/**
  simple UPDATE query pre-check.
unknown's avatar
unknown committed
7079

unknown's avatar
unknown committed
7080 7081
  @param thd		Thread handler
  @param tables	Global table list
unknown's avatar
unknown committed
7082

unknown's avatar
unknown committed
7083
  @retval
unknown's avatar
unknown committed
7084
    FALSE OK
unknown's avatar
unknown committed
7085
  @retval
unknown's avatar
unknown committed
7086
    TRUE  Error
unknown's avatar
unknown committed
7087
*/
unknown's avatar
unknown committed
7088

unknown's avatar
unknown committed
7089
bool update_precheck(THD *thd, TABLE_LIST *tables)
unknown's avatar
unknown committed
7090 7091 7092 7093
{
  DBUG_ENTER("update_precheck");
  if (thd->lex->select_lex.item_list.elements != thd->lex->value_list.elements)
  {
unknown's avatar
unknown committed
7094
    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
unknown's avatar
unknown committed
7095
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7096
  }
unknown's avatar
unknown committed
7097
  DBUG_RETURN(check_one_table_access(thd, UPDATE_ACL, tables));
unknown's avatar
unknown committed
7098 7099 7100
}


unknown's avatar
unknown committed
7101 7102
/**
  simple DELETE query pre-check.
unknown's avatar
unknown committed
7103

unknown's avatar
unknown committed
7104 7105
  @param thd		Thread handler
  @param tables	Global table list
unknown's avatar
unknown committed
7106

unknown's avatar
unknown committed
7107
  @retval
unknown's avatar
unknown committed
7108
    FALSE  OK
unknown's avatar
unknown committed
7109
  @retval
unknown's avatar
unknown committed
7110
    TRUE   error
unknown's avatar
unknown committed
7111
*/
unknown's avatar
unknown committed
7112

unknown's avatar
unknown committed
7113
bool delete_precheck(THD *thd, TABLE_LIST *tables)
unknown's avatar
unknown committed
7114 7115 7116
{
  DBUG_ENTER("delete_precheck");
  if (check_one_table_access(thd, DELETE_ACL, tables))
unknown's avatar
unknown committed
7117
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7118
  /* Set privilege for the WHERE clause */
unknown's avatar
unknown committed
7119
  tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
unknown's avatar
unknown committed
7120
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
7121 7122 7123
}


unknown's avatar
unknown committed
7124 7125
/**
  simple INSERT query pre-check.
unknown's avatar
unknown committed
7126

unknown's avatar
unknown committed
7127 7128
  @param thd		Thread handler
  @param tables	Global table list
unknown's avatar
unknown committed
7129

unknown's avatar
unknown committed
7130
  @retval
unknown's avatar
unknown committed
7131
    FALSE  OK
unknown's avatar
unknown committed
7132
  @retval
unknown's avatar
unknown committed
7133
    TRUE   error
unknown's avatar
unknown committed
7134
*/
unknown's avatar
unknown committed
7135

unknown's avatar
merge  
unknown committed
7136
bool insert_precheck(THD *thd, TABLE_LIST *tables)
unknown's avatar
unknown committed
7137 7138 7139 7140
{
  LEX *lex= thd->lex;
  DBUG_ENTER("insert_precheck");

unknown's avatar
unknown committed
7141 7142 7143 7144
  /*
    Check that we have modify privileges for the first table and
    select privileges for the rest
  */
unknown's avatar
unknown committed
7145 7146 7147
  ulong privilege= (INSERT_ACL |
                    (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
                    (lex->value_list.elements ? UPDATE_ACL : 0));
unknown's avatar
unknown committed
7148 7149

  if (check_one_table_access(thd, privilege, tables))
unknown's avatar
unknown committed
7150
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7151

unknown's avatar
unknown committed
7152
  if (lex->update_list.elements != lex->value_list.elements)
unknown's avatar
unknown committed
7153
  {
unknown's avatar
unknown committed
7154
    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
unknown's avatar
unknown committed
7155
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7156
  }
unknown's avatar
unknown committed
7157
  DBUG_RETURN(FALSE);
7158
}
unknown's avatar
unknown committed
7159 7160


7161 7162 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188
/**
   Set proper open mode and table type for element representing target table
   of CREATE TABLE statement, also adjust statement table list if necessary.
*/

void create_table_set_open_action_and_adjust_tables(LEX *lex)
{
  TABLE_LIST *create_table= lex->query_tables;

  if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)
    create_table->open_type= OT_TEMPORARY_ONLY;
  else if (!lex->select_lex.item_list.elements)
    create_table->open_type= OT_BASE_ONLY;

  if (!lex->select_lex.item_list.elements)
  {
    /*
      Avoid opening and locking target table for ordinary CREATE TABLE
      or CREATE TABLE LIKE for write (unlike in CREATE ... SELECT we
      won't do any insertions in it anyway). Not doing this causes
      problems when running CREATE TABLE IF NOT EXISTS for already
      existing log table.
    */
    create_table->lock_type= TL_READ;
  }
}


unknown's avatar
unknown committed
7189 7190
/**
  CREATE TABLE query pre-check.
unknown's avatar
unknown committed
7191

unknown's avatar
unknown committed
7192 7193 7194
  @param thd			Thread handler
  @param tables		Global table list
  @param create_table	        Table which will be created
unknown's avatar
unknown committed
7195

unknown's avatar
unknown committed
7196
  @retval
unknown's avatar
unknown committed
7197
    FALSE   OK
unknown's avatar
unknown committed
7198
  @retval
unknown's avatar
unknown committed
7199
    TRUE   Error
unknown's avatar
unknown committed
7200
*/
unknown's avatar
unknown committed
7201

unknown's avatar
unknown committed
7202 7203
bool create_table_precheck(THD *thd, TABLE_LIST *tables,
                           TABLE_LIST *create_table)
unknown's avatar
unknown committed
7204 7205
{
  LEX *lex= thd->lex;
7206 7207
  SELECT_LEX *select_lex= &lex->select_lex;
  ulong want_priv;
unknown's avatar
merge  
unknown committed
7208
  bool error= TRUE;                                 // Error message is given
unknown's avatar
unknown committed
7209
  DBUG_ENTER("create_table_precheck");
7210

7211 7212 7213 7214 7215
  /*
    Require CREATE [TEMPORARY] privilege on new table; for
    CREATE TABLE ... SELECT, also require INSERT.
  */

7216
  want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
7217 7218 7219
              CREATE_TMP_ACL : CREATE_ACL) |
             (select_lex->item_list.elements ? INSERT_ACL : 0);

unknown's avatar
unknown committed
7220
  if (check_access(thd, want_priv, create_table->db,
Marc Alff's avatar
Marc Alff committed
7221 7222 7223
                   &create_table->grant.privilege,
                   &create_table->grant.m_internal,
                   0, 0) ||
unknown's avatar
unknown committed
7224 7225 7226
      check_merge_table_access(thd, create_table->db,
			       (TABLE_LIST *)
			       lex->create_info.merge_list.first))
7227
    goto err;
7228
  if (want_priv != CREATE_TMP_ACL &&
7229
      check_grant(thd, want_priv, create_table, FALSE, 1, FALSE))
7230 7231 7232 7233 7234 7235
    goto err;

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

7236 7237
#ifdef NOT_NECESSARY_TO_CHECK_CREATE_TABLE_EXIST_WHEN_PREPARING_STATEMENT
    /* This code throws an ill error for CREATE TABLE t1 SELECT * FROM t1 */
7238
    /*
7239
      Only do the check for PS, because we on execute we have to check that
unknown's avatar
unknown committed
7240 7241
      against the opened tables to ensure we don't use a table that is part
      of the view (which can only be done after the table has been opened).
7242
    */
unknown's avatar
unknown committed
7243
    if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute())
7244
    {
unknown's avatar
unknown committed
7245 7246 7247 7248
      /*
        For temporary tables we don't have to check if the created table exists
      */
      if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
unknown's avatar
unknown committed
7249
          find_table_in_global_list(tables, create_table->db,
7250
                                    create_table->table_name))
unknown's avatar
unknown committed
7251
      {
7252
	error= FALSE;
unknown's avatar
unknown committed
7253 7254 7255
        goto err;
      }
    }
7256
#endif
7257 7258
    if (tables && check_table_access(thd, SELECT_ACL, tables, FALSE,
                                     UINT_MAX, FALSE))
7259 7260
      goto err;
  }
unknown's avatar
unknown committed
7261 7262
  else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
  {
7263
    if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
unknown's avatar
unknown committed
7264 7265
      goto err;
  }
unknown's avatar
merge  
unknown committed
7266
  error= FALSE;
7267 7268 7269

err:
  DBUG_RETURN(error);
unknown's avatar
unknown committed
7270
}
unknown's avatar
unknown committed
7271 7272


unknown's avatar
unknown committed
7273 7274
/**
  negate given expression.
unknown's avatar
unknown committed
7275

unknown's avatar
unknown committed
7276 7277
  @param thd  thread handler
  @param expr expression for negation
unknown's avatar
unknown committed
7278

unknown's avatar
unknown committed
7279
  @return
unknown's avatar
unknown committed
7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304
    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);
}
7305

unknown's avatar
unknown committed
7306 7307 7308
/**
  Set the specified definer to the default value, which is the
  current user in the thread.
7309
 
unknown's avatar
unknown committed
7310 7311
  @param[in]  thd       thread handler
  @param[out] definer   definer
7312 7313
*/
 
7314
void get_default_definer(THD *thd, LEX_USER *definer)
7315 7316 7317 7318 7319 7320 7321 7322
{
  const Security_context *sctx= thd->security_ctx;

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

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

  definer->password.str= NULL;
  definer->password.length= 0;
7326 7327
}

7328

unknown's avatar
unknown committed
7329
/**
7330
  Create default definer for the specified THD.
7331

unknown's avatar
unknown committed
7332
  @param[in] thd         thread handler
7333

unknown's avatar
unknown committed
7334 7335
  @return
    - On success, return a valid pointer to the created and initialized
7336
    LEX_USER, which contains definer information.
unknown's avatar
unknown committed
7337
    - On error, return 0.
7338 7339 7340 7341 7342 7343 7344 7345 7346
*/

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

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

7347
  get_default_definer(thd, definer);
7348 7349 7350 7351 7352

  return definer;
}


unknown's avatar
unknown committed
7353
/**
7354
  Create definer with the given user and host names.
7355

unknown's avatar
unknown committed
7356 7357 7358
  @param[in] thd          thread handler
  @param[in] user_name    user name
  @param[in] host_name    host name
7359

unknown's avatar
unknown committed
7360 7361
  @return
    - On success, return a valid pointer to the created and initialized
7362
    LEX_USER, which contains definer information.
unknown's avatar
unknown committed
7363
    - On error, return 0.
7364 7365
*/

7366
LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
7367
{
7368 7369 7370 7371
  LEX_USER *definer;

  /* Create and initialize. */

unknown's avatar
unknown committed
7372
  if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
7373 7374 7375 7376
    return 0;

  definer->user= *user_name;
  definer->host= *host_name;
7377 7378
  definer->password.str= NULL;
  definer->password.length= 0;
7379 7380

  return definer;
7381
}
7382 7383


unknown's avatar
unknown committed
7384
/**
7385 7386
  Retuns information about user or current user.

unknown's avatar
unknown committed
7387 7388
  @param[in] thd          thread handler
  @param[in] user         user
7389

unknown's avatar
unknown committed
7390 7391
  @return
    - On success, return a valid pointer to initialized
7392
    LEX_USER, which contains user information.
unknown's avatar
unknown committed
7393
    - On error, return 0.
7394 7395 7396 7397 7398
*/

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

7401 7402
  return user;
}
7403 7404


unknown's avatar
unknown committed
7405
/**
7406
  Check that byte length of a string does not exceed some limit.
7407

unknown's avatar
unknown committed
7408 7409 7410
  @param str         string to be checked
  @param err_msg     error message to be displayed if the string is too long
  @param max_length  max length
7411

unknown's avatar
unknown committed
7412
  @retval
7413
    FALSE   the passed string is not longer than max_length
unknown's avatar
unknown committed
7414
  @retval
7415
    TRUE    the passed string is longer than max_length
7416 7417 7418

  NOTE
    The function is not used in existing code but can be useful later?
7419 7420
*/

7421 7422
bool check_string_byte_length(LEX_STRING *str, const char *err_msg,
                              uint max_byte_length)
7423
{
7424
  if (str->length <= max_byte_length)
unknown's avatar
unknown committed
7425
    return FALSE;
7426

7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458
  my_error(ER_WRONG_STRING_LENGTH, MYF(0), str->str, err_msg, max_byte_length);

  return TRUE;
}


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

  SYNOPSIS
  check_string_char_length()
      str              string to be checked
      err_msg          error message to be displayed if the string is too long
      max_char_length  max length in symbols
      cs               string charset

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


bool check_string_char_length(LEX_STRING *str, const char *err_msg,
                              uint max_char_length, CHARSET_INFO *cs,
                              bool no_error)
{
  int well_formed_error;
  uint res= cs->cset->well_formed_len(cs, str->str, str->str + str->length,
                                      max_char_length, &well_formed_error);

  if (!well_formed_error &&  str->length == res)
    return FALSE;
unknown's avatar
unknown committed
7459

7460
  if (!no_error)
7461 7462 7463 7464
  {
    ErrConvString err(str->str, str->length, cs);
    my_error(ER_WRONG_STRING_LENGTH, MYF(0), err.ptr(), err_msg, max_char_length);
  }
7465 7466
  return TRUE;
}
7467 7468


7469 7470 7471 7472 7473 7474 7475 7476 7477 7478 7479 7480
/*
  Check if path does not contain mysql data home directory
  SYNOPSIS
    test_if_data_home_dir()
    dir                     directory
    conv_home_dir           converted data home directory
    home_dir_len            converted data home directory length

  RETURN VALUES
    0	ok
    1	error  
*/
7481
C_MODE_START
7482

7483
int test_if_data_home_dir(const char *dir)
7484
{
7485
  char path[FN_REFLEN];
Alexey Botchkov's avatar
Alexey Botchkov committed
7486
  int dir_len;
7487 7488 7489 7490 7491 7492 7493
  DBUG_ENTER("test_if_data_home_dir");

  if (!dir)
    DBUG_RETURN(0);

  (void) fn_format(path, dir, "", "",
                   (MY_RETURN_REAL_PATH|MY_RESOLVE_SYMLINKS));
7494 7495
  dir_len= strlen(path);
  if (mysql_unpacked_real_data_home_len<= dir_len)
7496
  {
7497 7498 7499 7500
    if (dir_len > mysql_unpacked_real_data_home_len &&
        path[mysql_unpacked_real_data_home_len] != FN_LIBCHAR)
      DBUG_RETURN(0);

7501 7502
    if (lower_case_file_system)
    {
7503 7504
      if (!my_strnncoll(default_charset_info, (const uchar*) path,
                        mysql_unpacked_real_data_home_len,
7505
                        (const uchar*) mysql_unpacked_real_data_home,
7506
                        mysql_unpacked_real_data_home_len))
7507 7508
        DBUG_RETURN(1);
    }
7509 7510
    else if (!memcmp(path, mysql_unpacked_real_data_home,
                     mysql_unpacked_real_data_home_len))
7511 7512 7513 7514 7515
      DBUG_RETURN(1);
  }
  DBUG_RETURN(0);
}

7516 7517
C_MODE_END

7518

7519 7520 7521 7522 7523 7524 7525 7526 7527 7528 7529 7530 7531 7532 7533
/**
  Check that host name string is valid.

  @param[in] str string to be checked

  @return             Operation status
    @retval  FALSE    host name is ok
    @retval  TRUE     host name string is longer than max_length or
                      has invalid symbols
*/

bool check_host_name(LEX_STRING *str)
{
  const char *name= str->str;
  const char *end= str->str + str->length;
Sergey Glukhov's avatar
Sergey Glukhov committed
7534
  if (check_string_byte_length(str, ER(ER_HOSTNAME), HOSTNAME_LENGTH))
7535 7536 7537 7538 7539 7540 7541 7542 7543 7544 7545 7546 7547 7548 7549
    return TRUE;

  while (name != end)
  {
    if (*name == '@')
    {
      my_printf_error(ER_UNKNOWN_ERROR, 
                      "Malformed hostname (illegal symbol: '%c')", MYF(0),
                      *name);
      return TRUE;
    }
    name++;
  }
  return FALSE;
}
Sergey Glukhov's avatar
Sergey Glukhov committed
7550 7551


7552 7553 7554 7555 7556 7557 7558 7559
extern int MYSQLparse(void *thd); // from sql_yacc.cc


/**
  This is a wrapper of MYSQLparse(). All the code should call parse_sql()
  instead of MYSQLparse().

  @param thd Thread context.
7560
  @param parser_state Parser state.
unknown's avatar
unknown committed
7561
  @param creation_ctx Object creation context.
7562 7563 7564 7565 7566 7567

  @return Error status.
    @retval FALSE on success.
    @retval TRUE on parsing error.
*/

unknown's avatar
unknown committed
7568
bool parse_sql(THD *thd,
7569
               Parser_state *parser_state,
unknown's avatar
unknown committed
7570
               Object_creation_ctx *creation_ctx)
7571
{
7572
  bool ret_value;
7573
  DBUG_ASSERT(thd->m_parser_state == NULL);
7574

7575
  MYSQL_QUERY_PARSE_START(thd->query());
unknown's avatar
unknown committed
7576 7577 7578 7579 7580 7581 7582
  /* Backup creation context. */

  Object_creation_ctx *backup_ctx= NULL;

  if (creation_ctx)
    backup_ctx= creation_ctx->set_n_backup(thd);

7583
  /* Set parser state. */
unknown's avatar
unknown committed
7584

7585
  thd->m_parser_state= parser_state;
7586

unknown's avatar
unknown committed
7587 7588
  /* Parse the query. */

7589 7590
  bool mysql_parse_status= MYSQLparse(thd) != 0;

7591
  /* Check that if MYSQLparse() failed, thd->is_error() is set. */
7592 7593

  DBUG_ASSERT(!mysql_parse_status ||
Staale Smedseng's avatar
Staale Smedseng committed
7594
              (mysql_parse_status && thd->is_error()));
unknown's avatar
unknown committed
7595

7596
  /* Reset parser state. */
7597

7598
  thd->m_parser_state= NULL;
7599

unknown's avatar
unknown committed
7600 7601 7602 7603 7604 7605 7606
  /* Restore creation context. */

  if (creation_ctx)
    creation_ctx->restore_env(thd, backup_ctx);

  /* That's it. */

7607 7608 7609
  ret_value= mysql_parse_status || thd->is_fatal_error;
  MYSQL_QUERY_PARSE_DONE(ret_value);
  return ret_value;
7610
}
7611 7612 7613 7614

/**
  @} (end of group Runtime_Environment)
*/
Alexander Barkov's avatar
#  
Alexander Barkov committed
7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 7638 7639 7640 7641 7642 7643 7644 7645 7646 7647 7648



/**
  Check and merge "CHARACTER SET cs [ COLLATE cl ]" clause

  @param cs character set pointer.
  @param cl collation pointer.

  Check if collation "cl" is applicable to character set "cs".

  If "cl" is NULL (e.g. when COLLATE clause is not specified),
  then simply "cs" is returned.
  
  @return Error status.
    @retval NULL, if "cl" is not applicable to "cs".
    @retval pointer to merged CHARSET_INFO on success.
*/


CHARSET_INFO*
merge_charset_and_collation(CHARSET_INFO *cs, CHARSET_INFO *cl)
{
  if (cl)
  {
    if (!my_charset_same(cs, cl))
    {
      my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), cl->name, cs->csname);
      return NULL;
    }
    return cl;
  }
  return cs;
}