sql_parse.cc 244 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
Mats Kindahl's avatar
Mats Kindahl committed
17
#include "my_global.h"
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
#include "sql_priv.h"
#include "unireg.h"                    // REQUIRED: for other includes
#include "sql_parse.h"        // sql_kill, *_precheck, *_prepare
#include "lock.h"             // wait_if_global_read_lock,
                              // unlock_global_read_lock,
                              // try_transactional_lock,
                              // check_transactional_lock,
                              // set_handler_table_locks,
                              // start_waiting_global_read_lock,
                              // lock_global_read_lock,
                              // make_global_read_lock_block_commit
#include "sql_base.h"         // find_temporary_tablesx
#include "sql_cache.h"        // QUERY_CACHE_FLAGS_SIZE, query_cache_*
#include "sql_show.h"         // mysqld_list_*, mysqld_show_*,
                              // calc_sum_of_all_status
#include "mysqld.h"
#include "sql_locale.h"                         // my_locale_en_US
#include "log.h"                                // flush_error_log
#include "sql_view.h"         // mysql_create_view, mysql_drop_view
#include "sql_delete.h"       // mysql_truncate, mysql_delete
#include "sql_insert.h"       // mysql_insert
#include "sql_update.h"       // mysql_update, mysql_multi_update
#include "sql_partition.h"    // struct partition_info
#include "sql_db.h"           // mysql_change_db, mysql_create_db,
                              // mysql_rm_db, mysql_upgrade_db,
                              // mysql_alter_db,
                              // check_db_dir_existence,
                              // my_dbopt_cleanup
#include "sql_table.h"        // mysql_create_like_table,
                              // mysql_create_table,
                              // mysql_alter_table,
                              // mysql_recreate_table,
                              // mysql_backup_table,
                              // mysql_restore_table
52
#include "sql_truncate.h"     // mysql_truncate_table
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
#include "sql_connect.h"      // check_user,
                              // decrease_user_connections,
                              // thd_init_client_charset, check_mqh,
                              // reset_mqh
#include "sql_rename.h"       // mysql_rename_table
#include "sql_tablespace.h"   // mysql_alter_tablespace
#include "hostname.h"         // hostname_cache_refresh
#include "sql_acl.h"          // *_ACL, check_grant, is_acl_user,
                              // has_any_table_level_privileges,
                              // mysql_drop_user, mysql_rename_user,
                              // check_grant_routine,
                              // mysql_routine_grant,
                              // mysql_show_grants,
                              // sp_grant_privileges, ...
#include "sql_test.h"         // mysql_print_status
#include "sql_select.h"       // handle_select, mysql_select,
                              // mysql_explain_union
#include "sql_load.h"         // mysql_load
#include "sql_servers.h"      // create_servers, alter_servers,
                              // drop_servers, servers_reload
#include "sql_handler.h"      // mysql_ha_open, mysql_ha_close,
                              // mysql_ha_read
#include "sql_binlog.h"       // mysql_client_binlog_statement
#include "sql_do.h"           // mysql_do
#include "sql_help.h"         // mysqld_help
#include "rpl_constants.h"    // Incident, INCIDENT_LOST_EVENTS
#include "log_event.h"
80
#include "sql_repl.h"
unknown's avatar
unknown committed
81
#include "rpl_filter.h"
82
#include "repl_failsafe.h"
unknown's avatar
unknown committed
83 84 85
#include <m_ctype.h>
#include <myisam.h>
#include <my_dir.h>
He Zhenxing's avatar
He Zhenxing committed
86
#include "rpl_handler.h"
unknown's avatar
unknown committed
87

88
#include "sp_head.h"
89
#include "sp.h"
90
#include "sp_cache.h"
91
#include "events.h"
92
#include "sql_trigger.h"
Konstantin Osipov's avatar
Konstantin Osipov committed
93
#include "transaction.h"
Sergey Vojtovich's avatar
Sergey Vojtovich committed
94
#include "sql_audit.h"
95
#include "sql_prepare.h"
96
#include "debug_sync.h"
97
#include "probes_mysql.h"
98
#include "set_var.h"
99

Mats Kindahl's avatar
Mats Kindahl committed
100 101
#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")

102 103 104 105 106
/**
  @defgroup Runtime_Environment Runtime Environment
  @{
*/

107 108 109 110 111 112
/* 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 || \
113
   (LP)->sql_command == SQLCOM_SHOW_CREATE_FUNC || \
114 115 116
   (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
   "FUNCTION" : "PROCEDURE")

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

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

unknown's avatar
unknown committed
121
const LEX_STRING command_name[]={
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
  { 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
153 154
};

unknown's avatar
unknown committed
155
const char *xa_state_names[]={
156
  "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED", "ROLLBACK ONLY"
unknown's avatar
unknown committed
157 158
};

159

unknown's avatar
unknown committed
160
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
161 162
/**
  Returns true if all tables should be ignored.
163
*/
164 165
inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables)
{
unknown's avatar
unknown committed
166 167
  return rpl_filter->is_on() && tables && !thd->spcont &&
         !rpl_filter->tables_ok(thd->db, tables);
168
}
unknown's avatar
unknown committed
169
#endif
170 171


172 173 174 175 176 177 178 179 180 181 182 183
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;
}

184

Konstantin Osipov's avatar
Konstantin Osipov committed
185 186 187 188 189 190 191
/*
  Implicitly commit a active transaction if statement requires so.

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

*/
192
static bool stmt_causes_implicit_commit(THD *thd, uint mask)
Konstantin Osipov's avatar
Konstantin Osipov committed
193 194
{
  LEX *lex= thd->lex;
195 196
  bool skip= FALSE;
  DBUG_ENTER("stmt_causes_implicit_commit");
Konstantin Osipov's avatar
Konstantin Osipov committed
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216

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

217
  DBUG_RETURN(!skip);
Konstantin Osipov's avatar
Konstantin Osipov committed
218 219 220
}


unknown's avatar
unknown committed
221 222 223 224
/**
  Mark all commands that somehow changes a table.

  This is used to check number of updates / hour.
unknown's avatar
unknown committed
225 226 227

  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
228

229
  See COMMAND_FLAG_xxx for different type of commands
unknown's avatar
unknown committed
230 231
     2  - query that returns meaningful ROW_COUNT() -
          a number of modified rows
232 233
*/

234
uint sql_command_flags[SQLCOM_END+1];
Konstantin Osipov's avatar
Konstantin Osipov committed
235
uint server_command_flags[COM_END+1];
236 237 238

void init_update_queries(void)
{
Konstantin Osipov's avatar
Konstantin Osipov committed
239 240 241 242 243 244 245 246 247 248 249 250
  /* 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));

251 252 253 254 255 256 257 258 259 260
  /*
    In general, DDL statements do not generate row events and do not go
    through a cache before being written to the binary log. However, the
    CREATE TABLE...SELECT is an exception because it may generate row
    events. For that reason,  the SQLCOM_CREATE_TABLE  which represents
    a CREATE TABLE, including the CREATE TABLE...SELECT, has the
    CF_CAN_GENERATE_ROW_EVENTS flag. The distinction between a regular
    CREATE TABLE and the CREATE TABLE...SELECT is made in other parts of
    the code, in particular in the Query_log_event's constructor.
  */
Konstantin Osipov's avatar
Konstantin Osipov committed
261
  sql_command_flags[SQLCOM_CREATE_TABLE]=   CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
262 263
                                            CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL |
                                            CF_CAN_GENERATE_ROW_EVENTS;
Konstantin Osipov's avatar
Konstantin Osipov committed
264 265
  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
266
                                            CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
Konstantin Osipov's avatar
Konstantin Osipov committed
267
  sql_command_flags[SQLCOM_TRUNCATE]=       CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
268
                                            CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
Konstantin Osipov's avatar
Konstantin Osipov committed
269
  sql_command_flags[SQLCOM_DROP_TABLE]=     CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
270
  sql_command_flags[SQLCOM_LOAD]=           CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
271 272
                                            CF_PROTECT_AGAINST_GRL |
                                            CF_CAN_GENERATE_ROW_EVENTS;
Konstantin Osipov's avatar
Konstantin Osipov committed
273 274
  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;
275 276
  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
277 278 279 280 281
  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;
282 283
  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
284 285 286 287 288
  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;
289

290
  sql_command_flags[SQLCOM_UPDATE]=	    CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
291 292
                                            CF_PROTECT_AGAINST_GRL |
                                            CF_CAN_GENERATE_ROW_EVENTS;
293
  sql_command_flags[SQLCOM_UPDATE_MULTI]=   CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
294 295
                                            CF_PROTECT_AGAINST_GRL |
                                            CF_CAN_GENERATE_ROW_EVENTS;
296
  sql_command_flags[SQLCOM_INSERT]=	    CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
297 298
                                            CF_PROTECT_AGAINST_GRL |
                                            CF_CAN_GENERATE_ROW_EVENTS;
299
  sql_command_flags[SQLCOM_INSERT_SELECT]=  CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
300 301
                                            CF_PROTECT_AGAINST_GRL |
                                            CF_CAN_GENERATE_ROW_EVENTS;
302
  sql_command_flags[SQLCOM_DELETE]=         CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
303 304
                                            CF_PROTECT_AGAINST_GRL |
                                            CF_CAN_GENERATE_ROW_EVENTS;
305
  sql_command_flags[SQLCOM_DELETE_MULTI]=   CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
306 307 308 309 310 311 312 313
                                            CF_PROTECT_AGAINST_GRL |
                                            CF_CAN_GENERATE_ROW_EVENTS;
  sql_command_flags[SQLCOM_REPLACE]=        CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
                                            CF_CAN_GENERATE_ROW_EVENTS;
  sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
                                            CF_CAN_GENERATE_ROW_EVENTS;
  sql_command_flags[SQLCOM_SELECT]=         CF_REEXECUTION_FRAGILE |
                                            CF_CAN_GENERATE_ROW_EVENTS;
314
  sql_command_flags[SQLCOM_SET_OPTION]=     CF_REEXECUTION_FRAGILE | CF_AUTO_COMMIT_TRANS;
315 316
  sql_command_flags[SQLCOM_DO]=             CF_REEXECUTION_FRAGILE |
                                            CF_CAN_GENERATE_ROW_EVENTS;
317 318 319 320 321 322 323

  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;
324
  sql_command_flags[SQLCOM_SHOW_PLUGINS]=     CF_STATUS_COMMAND;
325 326 327 328 329
  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;
330 331
  sql_command_flags[SQLCOM_SHOW_NEW_MASTER]=  CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_BINLOGS]=     CF_STATUS_COMMAND;
332 333 334
  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;
335
  sql_command_flags[SQLCOM_SHOW_AUTHORS]=     CF_STATUS_COMMAND;
336
  sql_command_flags[SQLCOM_SHOW_CONTRIBUTORS]= CF_STATUS_COMMAND;
337 338 339
  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;
340 341 342 343
  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;
344 345
  sql_command_flags[SQLCOM_SHOW_GRANTS]=      CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_CREATE_DB]=   CF_STATUS_COMMAND;
346
  sql_command_flags[SQLCOM_SHOW_CREATE]=  CF_STATUS_COMMAND;
347
  sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND;
348
  sql_command_flags[SQLCOM_SHOW_SLAVE_STAT]=  CF_STATUS_COMMAND;
349 350
  sql_command_flags[SQLCOM_SHOW_CREATE_PROC]= CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_CREATE_FUNC]= CF_STATUS_COMMAND;
351
  sql_command_flags[SQLCOM_SHOW_CREATE_TRIGGER]=  CF_STATUS_COMMAND;
352 353 354 355 356 357 358
  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;
359 360

   sql_command_flags[SQLCOM_SHOW_TABLES]=       (CF_STATUS_COMMAND |
361 362
                                                 CF_SHOW_TABLE_COMMAND |
                                                 CF_REEXECUTION_FRAGILE);
363
  sql_command_flags[SQLCOM_SHOW_TABLE_STATUS]= (CF_STATUS_COMMAND |
364 365
                                                CF_SHOW_TABLE_COMMAND |
                                                CF_REEXECUTION_FRAGILE);
366

367

Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
368 369 370
  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;
371 372
  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
373
  sql_command_flags[SQLCOM_REVOKE_ALL]=        CF_PROTECT_AGAINST_GRL;
374
  sql_command_flags[SQLCOM_OPTIMIZE]=          CF_CHANGES_DATA;
Konstantin Osipov's avatar
Konstantin Osipov committed
375 376 377 378 379 380 381
  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;
382 383 384
  sql_command_flags[SQLCOM_INSTALL_PLUGIN]=    CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]=  CF_CHANGES_DATA;

385 386 387 388 389 390
  /*
    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.
  */
391 392 393
  sql_command_flags[SQLCOM_CALL]=      CF_REEXECUTION_FRAGILE |
                                       CF_CAN_GENERATE_ROW_EVENTS;
  sql_command_flags[SQLCOM_EXECUTE]=   CF_CAN_GENERATE_ROW_EVENTS;
394 395 396 397 398

  /*
    The following admin table operations are allowed
    on log tables.
  */
399 400 401 402 403 404 405 406 407 408 409
  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
410 411 412 413
  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;
414
  sql_command_flags[SQLCOM_RESET]=              CF_AUTO_COMMIT_TRANS;
Konstantin Osipov's avatar
Konstantin Osipov committed
415
  sql_command_flags[SQLCOM_CHECK]=              CF_AUTO_COMMIT_TRANS;
416 417
}

418 419 420 421 422 423
bool sqlcom_can_generate_row_events(const THD *thd)
{
  return (sql_command_flags[thd->lex->sql_command] &
          CF_CAN_GENERATE_ROW_EVENTS);
}
 
unknown's avatar
unknown committed
424 425
bool is_update_query(enum enum_sql_command command)
{
unknown's avatar
unknown committed
426
  DBUG_ASSERT(command >= 0 && command <= SQLCOM_END);
427
  return (sql_command_flags[command] & CF_CHANGES_DATA) != 0;
unknown's avatar
unknown committed
428
}
429

430 431 432 433 434 435 436 437 438 439
/**
  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;
}
440

441
void execute_init_command(THD *thd, LEX_STRING *init_command,
Marc Alff's avatar
Marc Alff committed
442
                          mysql_rwlock_t *var_lock)
unknown's avatar
SCRUM:  
unknown committed
443 444 445 446
{
  Vio* save_vio;
  ulong save_client_capabilities;

Marc Alff's avatar
Marc Alff committed
447
  mysql_rwlock_rdlock(var_lock);
448 449
  if (!init_command->length)
  {
Marc Alff's avatar
Marc Alff committed
450
    mysql_rwlock_unlock(var_lock);
451 452 453 454 455 456 457 458 459 460
    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
461
  mysql_rwlock_unlock(var_lock);
462

463
#if defined(ENABLED_PROFILING)
464
  thd->profiling.start_new_query();
465
  thd->profiling.set_query_source(buf, len);
466 467
#endif

468
  thd_proc_info(thd, "Execution of init_command");
unknown's avatar
SCRUM:  
unknown committed
469 470
  save_client_capabilities= thd->client_capabilities;
  thd->client_capabilities|= CLIENT_MULTI_QUERIES;
471 472 473 474
  /*
    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
SCRUM:  
unknown committed
475 476
  save_vio= thd->net.vio;
  thd->net.vio= 0;
477
  dispatch_command(COM_QUERY, thd, buf, len);
unknown's avatar
SCRUM:  
unknown committed
478 479
  thd->client_capabilities= save_client_capabilities;
  thd->net.vio= save_vio;
480

481
#if defined(ENABLED_PROFILING)
482 483
  thd->profiling.finish_current_query();
#endif
unknown's avatar
SCRUM:  
unknown committed
484 485 486
}


487
static void handle_bootstrap_impl(THD *thd)
unknown's avatar
unknown committed
488
{
Marc Alff's avatar
Marc Alff committed
489
  MYSQL_FILE *file= bootstrap_file;
490
  char *buff;
unknown's avatar
unknown committed
491

492 493
  DBUG_ENTER("handle_bootstrap");

unknown's avatar
unknown committed
494
#ifndef EMBEDDED_LIBRARY
495 496
  pthread_detach_this_thread();
  thd->thread_stack= (char*) &thd;
unknown's avatar
unknown committed
497
#endif /* EMBEDDED_LIBRARY */
unknown's avatar
unknown committed
498

499
  thd_proc_info(thd, 0);
500 501
  thd->security_ctx->priv_user=
    thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
unknown's avatar
unknown committed
502
  thd->security_ctx->priv_host[0]=0;
503 504 505 506 507 508
  /*
    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
509

510
  buff= (char*) thd->net.buff;
unknown's avatar
unknown committed
511
  thd->init_for_queries();
Marc Alff's avatar
Marc Alff committed
512
  while (mysql_file_fgets(buff, thd->net.max_packet, file))
unknown's avatar
unknown committed
513
  {
Marc Alff's avatar
Marc Alff committed
514 515
    char *query;
    /* strlen() can't be deleted because mysql_file_fgets() doesn't return length */
516
    ulong length= (ulong) strlen(buff);
Marc Alff's avatar
Marc Alff committed
517
    while (buff[length-1] != '\n' && !mysql_file_feof(file))
518 519 520 521 522 523 524 525
    {
      /*
        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))
      {
526
        thd->protocol->end_statement();
527
        bootstrap_error= 1;
528 529 530
        break;
      }
      buff= (char*) thd->net.buff;
Marc Alff's avatar
Marc Alff committed
531
      mysql_file_fgets(buff + length, thd->net.max_packet - length, file);
532 533 534
      length+= (ulong) strlen(buff + length);
      /* purecov: end */
    }
535
    if (bootstrap_error)
536
      break;                                    /* purecov: inspected */
unknown's avatar
unknown committed
537

unknown's avatar
unknown committed
538
    while (length && (my_isspace(thd->charset(), buff[length-1]) ||
539
                      buff[length-1] == ';'))
unknown's avatar
unknown committed
540 541
      length--;
    buff[length]=0;
unknown's avatar
unknown committed
542 543 544 545 546

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

Gleb Shchepa's avatar
Gleb Shchepa committed
547 548 549
    query= (char *) thd->memdup_w_gap(buff, length + 1,
                                      thd->db_length + 1 +
                                      QUERY_CACHE_FLAGS_SIZE);
550
    thd->set_query_and_id(query, length, next_query_id());
551
    DBUG_PRINT("query",("%-.4096s",thd->query()));
552
#if defined(ENABLED_PROFILING)
553
    thd->profiling.start_new_query();
554
    thd->profiling.set_query_source(thd->query(), length);
555 556
#endif

557 558 559 560
    /*
      We don't need to obtain LOCK_thread_count here because in bootstrap
      mode we have only one thread.
    */
561
    thd->set_time();
562 563 564 565 566 567 568 569
    Parser_state parser_state;
    if (parser_state.init(thd, thd->query(), length))
    {
      thd->protocol->end_statement();
      bootstrap_error= 1;
      break;
    }

570
    mysql_parse(thd, thd->query(), length, &parser_state);
unknown's avatar
unknown committed
571
    close_thread_tables(thd);			// Free tables
572

573
    bootstrap_error= thd->is_error();
574
    thd->protocol->end_statement();
575

576
#if defined(ENABLED_PROFILING)
577 578 579
    thd->profiling.finish_current_query();
#endif

580
    if (bootstrap_error)
581 582
      break;

unknown's avatar
unknown committed
583
    free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
584
    free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC));
unknown's avatar
unknown committed
585
  }
586

587 588 589 590 591 592 593 594 595 596 597 598 599 600
  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
601 602 603 604 605 606 607 608
  mysql_thread_set_psi_id(thd->thread_id);

  do_handle_bootstrap(thd);
  return 0;
}

void do_handle_bootstrap(THD *thd)
{
609 610 611 612 613 614 615 616 617 618 619 620 621
  /* 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);

622
end:
unknown's avatar
unknown committed
623 624 625 626
  net_end(&thd->net);
  thd->cleanup();
  delete thd;

unknown's avatar
unknown committed
627
#ifndef EMBEDDED_LIBRARY
Marc Alff's avatar
Marc Alff committed
628
  mysql_mutex_lock(&LOCK_thread_count);
629
  thread_count--;
630
  in_bootstrap= FALSE;
Marc Alff's avatar
Marc Alff committed
631 632
  mysql_cond_broadcast(&COND_thread_count);
  mysql_mutex_unlock(&LOCK_thread_count);
633 634
  my_thread_end();
  pthread_exit(0);
unknown's avatar
unknown committed
635
#endif
636

Marc Alff's avatar
Marc Alff committed
637
  return;
unknown's avatar
unknown committed
638 639 640
}


641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
/**
  @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
658
#ifndef NO_EMBEDDED_ACCESS_CHECKS
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
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,
675
                              table_list, FALSE, UINT_MAX, FALSE);
676 677 678
  }
  return error;
}
unknown's avatar
unknown committed
679
#endif
680

unknown's avatar
unknown committed
681 682 683 684 685 686 687 688 689 690 691 692 693 694
/* 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;
}

695 696 697 698
/**
   This works because items are allocated with sql_alloc().
   @note The function also handles null pointers (empty list).
*/
699 700
void cleanup_items(Item *item)
{
unknown's avatar
unknown committed
701
  DBUG_ENTER("cleanup_items");  
702 703
  for (; item ; item=item->next)
    item->cleanup();
unknown's avatar
unknown committed
704
  DBUG_VOID_RETURN;
705 706
}

707
#ifndef EMBEDDED_LIBRARY
708

unknown's avatar
unknown committed
709
/**
unknown's avatar
unknown committed
710
  Read one command from connection and execute it (query or simple command).
711
  This function is called in loop from thread function.
712 713 714

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

unknown's avatar
unknown committed
715
  @retval
716
    0  success
unknown's avatar
unknown committed
717
  @retval
718 719 720
    1  request of thread shutdown (see dispatch_command() description)
*/

unknown's avatar
unknown committed
721 722
bool do_command(THD *thd)
{
723
  bool return_value;
unknown's avatar
unknown committed
724
  char *packet= 0;
unknown's avatar
unknown committed
725
  ulong packet_length;
unknown's avatar
unknown committed
726
  NET *net= &thd->net;
unknown's avatar
unknown committed
727 728 729
  enum enum_server_command command;
  DBUG_ENTER("do_command");

unknown's avatar
unknown committed
730 731 732 733
  /*
    indicator of uninitialized lex => normal flow of errors handling
    (see my_message_sql)
  */
734
  thd->lex->current_select= 0;
unknown's avatar
unknown committed
735

unknown's avatar
unknown committed
736 737 738 739
  /*
    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"
740
    number of seconds has passed.
unknown's avatar
unknown committed
741
  */
742
  my_net_set_read_timeout(net, thd->variables.net_wait_timeout);
unknown's avatar
unknown committed
743

744 745 746 747
  /*
    XXX: this code is here only to clear possible errors of init_connect. 
    Consider moving to init_connect() instead.
  */
unknown's avatar
unknown committed
748
  thd->clear_error();				// Clear error message
Marc Alff's avatar
Marc Alff committed
749
  thd->stmt_da->reset_diagnostics_area();
unknown's avatar
unknown committed
750 751

  net_new_transaction(net);
752

Konstantin Osipov's avatar
Konstantin Osipov committed
753
  if ((packet_length= my_net_read(net)) == packet_error)
unknown's avatar
unknown committed
754
  {
755 756 757
    DBUG_PRINT("info",("Got error %d reading command from socket %s",
		       net->error,
		       vio_description(net->vio)));
758

759
    /* Check if we can continue without closing the connection */
760

761 762
    /* The error must be set. */
    DBUG_ASSERT(thd->is_error());
763
    thd->protocol->end_statement();
764

765
    if (net->error != 3)
766
    {
767
      return_value= TRUE;                       // We have to close it.
768 769
      goto out;
    }
770

771
    net->error= 0;
772 773
    return_value= FALSE;
    goto out;
unknown's avatar
unknown committed
774
  }
775 776 777 778 779 780 781 782 783 784 785

  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
786
  {
787 788 789
    /* Initialize with COM_SLEEP packet */
    packet[0]= (uchar) COM_SLEEP;
    packet_length= 1;
unknown's avatar
unknown committed
790
  }
791 792 793 794 795 796 797 798 799 800 801
  /* 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
802 803

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

806
  DBUG_ASSERT(packet_length);
807 808 809 810
  return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));

out:
  DBUG_RETURN(return_value);
811
}
812
#endif  /* EMBEDDED_LIBRARY */
813

814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
/**
  @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);

845
  if (!(sql_command_flags[lex->sql_command] & CF_CHANGES_DATA))
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880
    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);
}
881

unknown's avatar
unknown committed
882 883
/**
  Perform one connection-level (COM_XXXX) command.
884

unknown's avatar
unknown committed
885 886 887 888 889 890 891 892
  @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
893 894 895
    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
896 897

  @retval
898
    0   ok
unknown's avatar
unknown committed
899
  @retval
900 901 902
    1   request of thread shutdown, i. e. if command is
        COM_QUIT/COM_SHUTDOWN
*/
903 904 905 906
bool dispatch_command(enum enum_server_command command, THD *thd,
		      char* packet, uint packet_length)
{
  NET *net= &thd->net;
907
  bool error= 0;
908
  DBUG_ENTER("dispatch_command");
909
  DBUG_PRINT("info",("packet: '%*.s'; command: %d", packet_length, packet, command));
910

Konstantin Osipov's avatar
Konstantin Osipov committed
911 912 913
#if defined(ENABLED_PROFILING)
  thd->profiling.start_new_query();
#endif
914 915 916 917
  MYSQL_COMMAND_START(thd->thread_id, command,
                      thd->security_ctx->priv_user,
                      (char *) thd->security_ctx->host_or_ip);
  
918
  thd->command=command;
unknown's avatar
unknown committed
919
  /*
920 921
    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
922
  */
923
  thd->enable_slow_log= TRUE;
924
  thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
unknown's avatar
unknown committed
925
  thd->set_time();
926 927 928 929 930 931 932 933 934 935 936 937
  if (!thd->is_valid_time())
  {
    /*
     If the time has got past 2038 we need to shut this server down
     We do this by making sure every command is a shutdown and we 
     have enough privileges to shut the server down

     TODO: remove this when we have full 64 bit my_time_t support
    */
    thd->security_ctx->master_access|= SHUTDOWN_ACL;
    command= COM_SHUTDOWN;
  }
938
  thd->set_query_id(get_query_id());
Konstantin Osipov's avatar
Konstantin Osipov committed
939
  if (!(server_command_flags[command] & CF_SKIP_QUERY_ID))
940
    next_query_id();
941
  inc_thread_running();
unknown's avatar
unknown committed
942

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

946 947 948 949 950
  /**
    Clear the set of flags that are expected to be cleared at the
    beginning of each command.
  */
  thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
951
  switch (command) {
unknown's avatar
unknown committed
952
  case COM_INIT_DB:
unknown's avatar
unknown committed
953 954
  {
    LEX_STRING tmp;
955
    status_var_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB]);
unknown's avatar
unknown committed
956
    thd->convert_string(&tmp, system_charset_info,
957
			packet, packet_length, thd->charset());
unknown's avatar
unknown committed
958
    if (!mysql_change_db(thd, &tmp, FALSE))
959
    {
960
      general_log_write(thd, command, thd->db, thd->db_length);
961
      my_ok(thd);
962
    }
unknown's avatar
unknown committed
963 964
    break;
  }
unknown's avatar
unknown committed
965
#ifdef HAVE_REPLICATION
966 967
  case COM_REGISTER_SLAVE:
  {
968
    if (!register_slave(thd, (uchar*)packet, packet_length))
969
      my_ok(thd);
970 971
    break;
  }
972
#endif
unknown's avatar
unknown committed
973 974
  case COM_CHANGE_USER:
  {
975
    status_var_increment(thd->status_var.com_other);
976 977
    char *user= (char*) packet, *packet_end= packet + packet_length;
    /* Safe because there is always a trailing \0 at the end of the packet */
978 979
    char *passwd= strend(user)+1;

unknown's avatar
unknown committed
980
    thd->change_user();
981
    thd->clear_error();                         // if errors from rollback
unknown's avatar
unknown committed
982

983
    /*
unknown's avatar
unknown committed
984 985 986
      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).
987 988 989

      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
990
    */
991
    char db_buff[NAME_LEN+1];                 // buffer to store db in utf8
unknown's avatar
unknown committed
992
    char *db= passwd;
993
    char *save_db;
994 995 996 997 998 999 1000 1001 1002
    /*
      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;
    }
1003
    uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
1004
                      (uchar)(*passwd++) : strlen(passwd));
1005 1006
    uint dummy_errors, save_db_length, db_length;
    int res;
1007 1008 1009
    Security_context save_security_ctx= *thd->security_ctx;
    USER_CONN *save_user_connect;

unknown's avatar
unknown committed
1010
    db+= passwd_len + 1;
1011 1012 1013 1014 1015
    /*
      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)
1016
    {
unknown's avatar
unknown committed
1017
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
1018 1019
      break;
    }
1020 1021
    db_length= strlen(db);

1022
    char *ptr= db + db_length + 1;
1023 1024
    uint cs_number= 0;

1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
    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);
    }
1035

1036
    /* Convert database name to utf8 */
unknown's avatar
unknown committed
1037
    db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
1038
                             system_charset_info, db, db_length,
unknown's avatar
unknown committed
1039
                             thd->charset(), &dummy_errors)]= 0;
unknown's avatar
unknown committed
1040
    db= db_buff;
unknown's avatar
unknown committed
1041

1042
    /* Save user and privileges */
1043 1044 1045
    save_db_length= thd->db_length;
    save_db= thd->db;
    save_user_connect= thd->user_connect;
1046 1047

    if (!(thd->security_ctx->user= my_strdup(user, MYF(0))))
1048
    {
1049
      thd->security_ctx->user= save_security_ctx.user;
unknown's avatar
unknown committed
1050
      my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
1051 1052
      break;
    }
unknown's avatar
unknown committed
1053

unknown's avatar
unknown committed
1054 1055
    /* Clear variables that are allocated */
    thd->user_connect= 0;
1056
    thd->security_ctx->priv_user= thd->security_ctx->user;
1057
    res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE);
unknown's avatar
unknown committed
1058

1059 1060
    if (res)
    {
1061 1062
      x_free(thd->security_ctx->user);
      *thd->security_ctx= save_security_ctx;
unknown's avatar
unknown committed
1063
      thd->user_connect= save_user_connect;
1064 1065 1066 1067 1068
      thd->db= save_db;
      thd->db_length= save_db_length;
    }
    else
    {
1069
#ifndef NO_EMBEDDED_ACCESS_CHECKS
1070
      /* we've authenticated new user */
unknown's avatar
unknown committed
1071 1072
      if (save_user_connect)
	decrease_user_connections(save_user_connect);
1073
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
1074 1075
      x_free(save_db);
      x_free(save_security_ctx.user);
1076 1077 1078 1079 1080 1081

      if (cs_number)
      {
        thd_init_client_charset(thd, cs_number);
        thd->update_charset();
      }
1082
    }
unknown's avatar
unknown committed
1083 1084
    break;
  }
1085
  case COM_STMT_EXECUTE:
unknown's avatar
unknown committed
1086
  {
1087
    mysqld_stmt_execute(thd, packet, packet_length);
unknown's avatar
unknown committed
1088 1089
    break;
  }
1090
  case COM_STMT_FETCH:
1091
  {
1092
    mysqld_stmt_fetch(thd, packet, packet_length);
1093 1094
    break;
  }
1095
  case COM_STMT_SEND_LONG_DATA:
unknown's avatar
unknown committed
1096
  {
1097
    mysql_stmt_get_longdata(thd, packet, packet_length);
unknown's avatar
unknown committed
1098 1099
    break;
  }
1100
  case COM_STMT_PREPARE:
unknown's avatar
unknown committed
1101
  {
1102
    mysqld_stmt_prepare(thd, packet, packet_length);
unknown's avatar
unknown committed
1103 1104
    break;
  }
1105
  case COM_STMT_CLOSE:
unknown's avatar
unknown committed
1106
  {
1107
    mysqld_stmt_close(thd, packet);
unknown's avatar
unknown committed
1108 1109
    break;
  }
1110
  case COM_STMT_RESET:
1111
  {
1112
    mysqld_stmt_reset(thd, packet);
1113 1114
    break;
  }
unknown's avatar
unknown committed
1115 1116
  case COM_QUERY:
  {
1117 1118
    if (alloc_query(thd, packet, packet_length))
      break;					// fatal error is set
1119
    MYSQL_QUERY_START(thd->query(), thd->thread_id,
1120 1121 1122
                      (char *) (thd->db ? thd->db : ""),
                      thd->security_ctx->priv_user,
                      (char *) thd->security_ctx->host_or_ip);
1123
    char *packet_end= thd->query() + thd->query_length();
unknown's avatar
unknown committed
1124
    /* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */
1125

1126 1127
    general_log_write(thd, command, thd->query(), thd->query_length());
    DBUG_PRINT("query",("%-.4096s",thd->query()));
1128
#if defined(ENABLED_PROFILING)
1129
    thd->profiling.set_query_source(thd->query(), thd->query_length());
1130
#endif
1131 1132 1133
    Parser_state parser_state;
    if (parser_state.init(thd, thd->query(), thd->query_length()))
      break;
1134

1135
    mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
1136

1137 1138
    while (!thd->killed && (parser_state.m_lip.found_semicolon != NULL) &&
           ! thd->is_error())
1139
    {
1140 1141
      char *beginning_of_next_stmt= (char*)
        parser_state.m_lip.found_semicolon;
1142

1143
      thd->protocol->end_statement();
1144
      query_cache_end_of_result(thd);
1145
      /*
1146 1147
        Multiple queries exits, execute them individually
      */
1148
      close_thread_tables(thd);
1149
      ulong length= (ulong)(packet_end - beginning_of_next_stmt);
1150

1151
      log_slow_statement(thd);
1152

1153
      /* Remove garbage at start of query */
1154
      while (length > 0 && my_isspace(thd->charset(), *beginning_of_next_stmt))
1155
      {
1156
        beginning_of_next_stmt++;
1157 1158
        length--;
      }
1159

1160 1161 1162 1163 1164
      if (MYSQL_QUERY_DONE_ENABLED())
      {
        MYSQL_QUERY_DONE(thd->is_error());
      }

1165
#if defined(ENABLED_PROFILING)
1166 1167 1168 1169 1170
      thd->profiling.finish_current_query();
      thd->profiling.start_new_query("continuing");
      thd->profiling.set_query_source(beginning_of_next_stmt, length);
#endif

1171
      MYSQL_QUERY_START(beginning_of_next_stmt, thd->thread_id,
1172 1173 1174 1175
                        (char *) (thd->db ? thd->db : ""),
                        thd->security_ctx->priv_user,
                        (char *) thd->security_ctx->host_or_ip);

1176
      thd->set_query_and_id(beginning_of_next_stmt, length, next_query_id());
1177 1178 1179 1180
      /*
        Count each statement from the client.
      */
      statistic_increment(thd->status_var.questions, &LOCK_status);
1181
      thd->set_time(); /* Reset the query start time. */
1182
      parser_state.reset(beginning_of_next_stmt, length);
1183
      /* TODO: set thd->lex->sql_command to SQLCOM_END here */
1184
      mysql_parse(thd, beginning_of_next_stmt, length, &parser_state);
1185 1186
    }

unknown's avatar
unknown committed
1187 1188 1189
    DBUG_PRINT("info",("query ready"));
    break;
  }
1190
  case COM_FIELD_LIST:				// This isn't actually needed
unknown's avatar
unknown committed
1191
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
1192 1193
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0));	/* purecov: inspected */
unknown's avatar
unknown committed
1194 1195 1196
    break;
#else
  {
1197
    char *fields, *packet_end= packet + packet_length, *arg_end;
1198
    /* Locked closure of all tables */
unknown's avatar
unknown committed
1199
    TABLE_LIST table_list;
unknown's avatar
unknown committed
1200
    LEX_STRING conv_name;
unknown's avatar
unknown committed
1201

unknown's avatar
unknown committed
1202
    /* used as fields initializator */
1203
    lex_start(thd);
1204

1205
    status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS]);
unknown's avatar
unknown committed
1206
    bzero((char*) &table_list,sizeof(table_list));
1207
    if (thd->copy_db_to(&table_list.db, &table_list.db_length))
unknown's avatar
unknown committed
1208
      break;
1209 1210 1211 1212
    /*
      We have name + wildcard in packet, separated by endzero
    */
    arg_end= strend(packet);
Ramil Kalimullin's avatar
Ramil Kalimullin committed
1213
    uint arg_length= arg_end - packet;
1214 1215 1216 1217 1218 1219 1220
    
    /* Check given table name length. */
    if (arg_length >= packet_length || arg_length > NAME_LEN)
    {
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
      break;
    }
unknown's avatar
unknown committed
1221
    thd->convert_string(&conv_name, system_charset_info,
1222
			packet, arg_length, thd->charset());
1223
    if (check_table_name(conv_name.str, conv_name.length, FALSE))
1224 1225 1226 1227 1228 1229
    {
      /* this is OK due to convert_string() null-terminating the string */
      my_error(ER_WRONG_TABLE_NAME, MYF(0), conv_name.str);
      break;
    }

1230
    table_list.alias= table_list.table_name= conv_name.str;
1231
    packet= arg_end + 1;
1232

1233
    if (is_infoschema_db(table_list.db, table_list.db_length))
1234 1235 1236 1237 1238 1239
    {
      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
1240 1241
    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
1242
      break;
1243
    thd->set_query(fields, query_length);
unknown's avatar
unknown committed
1244
    general_log_print(thd, command, "%s %s", table_list.table_name, fields);
1245
    if (lower_case_table_names)
1246
      my_casedn_str(files_charset_info, table_list.table_name);
unknown's avatar
unknown committed
1247

Marc Alff's avatar
Marc Alff committed
1248 1249 1250 1251
    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
1252
      break;
1253
    if (check_grant(thd, SELECT_ACL, &table_list, TRUE, UINT_MAX, FALSE))
unknown's avatar
unknown committed
1254
      break;
1255 1256
    /* init structures for VIEW processing */
    table_list.select_lex= &(thd->lex->select_lex);
1257 1258 1259 1260

    lex_start(thd);
    mysql_reset_thd_for_next_command(thd);

1261
    thd->lex->
1262 1263
      select_lex.table_list.link_in_list(&table_list,
                                         &table_list.next_local);
unknown's avatar
unknown committed
1264
    thd->lex->add_to_query_tables(&table_list);
Konstantin Osipov's avatar
Konstantin Osipov committed
1265
    init_mdl_requests(&table_list);
1266

1267 1268
    /* switch on VIEW optimisation: do not fill temporary tables */
    thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
unknown's avatar
unknown committed
1269
    mysqld_list_fields(thd,&table_list,fields);
1270
    thd->lex->unit.cleanup();
1271
    thd->cleanup_after_query();
unknown's avatar
unknown committed
1272 1273 1274 1275
    break;
  }
#endif
  case COM_QUIT:
1276
    /* We don't calculate statistics for this command */
unknown's avatar
unknown committed
1277
    general_log_print(thd, command, NullS);
unknown's avatar
unknown committed
1278
    net->error=0;				// Don't give 'abort' message
Marc Alff's avatar
Marc Alff committed
1279
    thd->stmt_da->disable_status();              // Don't send anything back
unknown's avatar
unknown committed
1280 1281
    error=TRUE;					// End server
    break;
1282
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
1283 1284
  case COM_BINLOG_DUMP:
    {
unknown's avatar
unknown committed
1285 1286 1287 1288
      ulong pos;
      ushort flags;
      uint32 slave_server_id;

1289
      status_var_increment(thd->status_var.com_other);
1290
      thd->enable_slow_log= opt_log_slow_admin_statements;
unknown's avatar
unknown committed
1291
      if (check_global_access(thd, REPL_SLAVE_ACL))
unknown's avatar
unknown committed
1292
	break;
unknown's avatar
unknown committed
1293

1294
      /* TODO: The following has to be changed to an 8 byte integer */
1295 1296
      pos = uint4korr(packet);
      flags = uint2korr(packet + 4);
unknown's avatar
unknown committed
1297
      thd->server_id=0; /* avoid suicide */
unknown's avatar
unknown committed
1298
      if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
unknown's avatar
unknown committed
1299
	kill_zombie_dump_threads(slave_server_id);
1300
      thd->server_id = slave_server_id;
unknown's avatar
unknown committed
1301

unknown's avatar
unknown committed
1302
      general_log_print(thd, command, "Log: '%s'  Pos: %ld", packet+10,
unknown's avatar
unknown committed
1303
                      (long) pos);
1304
      mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
unknown's avatar
unknown committed
1305
      unregister_slave(thd,1,1);
unknown's avatar
unknown committed
1306
      /*  fake COM_QUIT -- if we get here, the thread needs to terminate */
1307
      error = TRUE;
unknown's avatar
unknown committed
1308 1309
      break;
    }
1310
#endif
unknown's avatar
unknown committed
1311
  case COM_REFRESH:
unknown's avatar
unknown committed
1312 1313
  {
    bool not_used;
1314
    status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]);
unknown's avatar
unknown committed
1315
    ulong options= (ulong) (uchar) packet[0];
Konstantin Osipov's avatar
Konstantin Osipov committed
1316
    if (trans_commit_implicit(thd))
Konstantin Osipov's avatar
Konstantin Osipov committed
1317
      break;
1318
    close_thread_tables(thd);
1319
    thd->mdl_context.release_transactional_locks();
unknown's avatar
unknown committed
1320
    if (check_global_access(thd,RELOAD_ACL))
unknown's avatar
unknown committed
1321
      break;
unknown's avatar
unknown committed
1322
    general_log_print(thd, command, NullS);
1323
#ifndef DBUG_OFF
1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338
    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
1339 1340
      if (res)
        break;
1341
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
1342
    else
1343
#endif
Konstantin Osipov's avatar
Konstantin Osipov committed
1344 1345
    if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
      break;
Konstantin Osipov's avatar
Konstantin Osipov committed
1346
    if (trans_commit_implicit(thd))
Konstantin Osipov's avatar
Konstantin Osipov committed
1347
      break;
1348
    close_thread_tables(thd);
1349
    thd->mdl_context.release_transactional_locks();
Konstantin Osipov's avatar
Konstantin Osipov committed
1350
    my_ok(thd);
unknown's avatar
unknown committed
1351 1352
    break;
  }
1353
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
1354
  case COM_SHUTDOWN:
1355
  {
1356
    status_var_increment(thd->status_var.com_other);
unknown's avatar
unknown committed
1357
    if (check_global_access(thd,SHUTDOWN_ACL))
unknown's avatar
unknown committed
1358
      break; /* purecov: inspected */
1359
    /*
1360
      If the client is < 4.1.3, it is going to send us no argument; then
1361
      packet_length is 0, packet[0] is the end 0 of the packet. Note that
1362 1363
      SHUTDOWN_DEFAULT is 0. If client is >= 4.1.3, the shutdown level is in
      packet[0].
1364
    */
1365 1366 1367 1368 1369
    enum mysql_enum_shutdown_level level;
    if (!thd->is_valid_time())
      level= SHUTDOWN_DEFAULT;
    else
      level= (enum mysql_enum_shutdown_level) (uchar) packet[0];
1370 1371 1372 1373 1374 1375 1376
    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;
    }
1377
    DBUG_PRINT("quit",("Got shutdown command for level %u", level));
unknown's avatar
unknown committed
1378
    general_log_print(thd, command, NullS);
1379
    my_eof(thd);
unknown's avatar
unknown committed
1380 1381 1382 1383
    close_thread_tables(thd);			// Free before kill
    kill_mysql();
    error=TRUE;
    break;
1384
  }
1385
#endif
unknown's avatar
unknown committed
1386 1387
  case COM_STATISTICS:
  {
1388 1389 1390
    STATUS_VAR current_global_status_var;
    ulong uptime;
    uint length;
1391
    ulonglong queries_per_second1000;
1392 1393
    char buff[250];
    uint buff_len= sizeof(buff);
1394

1395
    general_log_print(thd, command, NullS);
1396
    status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS]);
1397
    calc_sum_of_all_status(&current_global_status_var);
1398 1399 1400 1401 1402
    if (!(uptime= (ulong) (thd->start_time - server_start_time)))
      queries_per_second1000= 0;
    else
      queries_per_second1000= thd->query_id * LL(1000) / uptime;

1403 1404 1405
    length= my_snprintf((char*) buff, buff_len - 1,
                        "Uptime: %lu  Threads: %d  Questions: %lu  "
                        "Slow queries: %lu  Opens: %lu  Flush tables: %lu  "
1406
                        "Open tables: %u  Queries per second avg: %u.%u",
1407 1408 1409 1410 1411 1412
                        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(),
1413 1414
                        (uint) (queries_per_second1000 / 1000),
                        (uint) (queries_per_second1000 % 1000));
1415 1416
#ifdef EMBEDDED_LIBRARY
    /* Store the buffer in permanent memory */
1417
    my_ok(thd, 0, 0, buff);
1418
#endif
unknown's avatar
unknown committed
1419
#ifdef SAFEMALLOC
1420
    if (sf_malloc_cur_memory)				// Using SAFEMALLOC
1421 1422 1423 1424 1425 1426 1427
    {
      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
1428 1429
#endif
#ifndef EMBEDDED_LIBRARY
Konstantin Osipov's avatar
Konstantin Osipov committed
1430 1431
    (void) my_net_write(net, (uchar*) buff, length);
    (void) net_flush(net);
Marc Alff's avatar
Marc Alff committed
1432
    thd->stmt_da->disable_status();
unknown's avatar
unknown committed
1433
#endif
unknown's avatar
unknown committed
1434 1435 1436
    break;
  }
  case COM_PING:
1437
    status_var_increment(thd->status_var.com_other);
1438
    my_ok(thd);				// Tell client we are alive
unknown's avatar
unknown committed
1439 1440
    break;
  case COM_PROCESS_INFO:
1441
    status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST]);
1442 1443
    if (!thd->security_ctx->priv_user[0] &&
        check_global_access(thd, PROCESS_ACL))
unknown's avatar
unknown committed
1444
      break;
unknown's avatar
unknown committed
1445
    general_log_print(thd, command, NullS);
unknown's avatar
SCRUM:  
unknown committed
1446
    mysqld_list_processes(thd,
1447 1448
			  thd->security_ctx->master_access & PROCESS_ACL ? 
			  NullS : thd->security_ctx->priv_user, 0);
unknown's avatar
unknown committed
1449 1450 1451
    break;
  case COM_PROCESS_KILL:
  {
1452
    status_var_increment(thd->status_var.com_stat[SQLCOM_KILL]);
1453
    ulong id=(ulong) uint4korr(packet);
1454
    sql_kill(thd,id,false);
unknown's avatar
unknown committed
1455 1456
    break;
  }
1457 1458
  case COM_SET_OPTION:
  {
1459
    status_var_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION]);
unknown's avatar
unknown committed
1460 1461 1462 1463
    uint opt_command= uint2korr(packet);

    switch (opt_command) {
    case (int) MYSQL_OPTION_MULTI_STATEMENTS_ON:
1464
      thd->client_capabilities|= CLIENT_MULTI_STATEMENTS;
1465
      my_eof(thd);
1466
      break;
unknown's avatar
unknown committed
1467
    case (int) MYSQL_OPTION_MULTI_STATEMENTS_OFF:
1468
      thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS;
1469
      my_eof(thd);
1470 1471
      break;
    default:
unknown's avatar
unknown committed
1472
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
1473 1474 1475 1476
      break;
    }
    break;
  }
unknown's avatar
unknown committed
1477
  case COM_DEBUG:
1478
    status_var_increment(thd->status_var.com_other);
unknown's avatar
unknown committed
1479
    if (check_global_access(thd, SUPER_ACL))
unknown's avatar
unknown committed
1480
      break;					/* purecov: inspected */
1481
    mysql_print_status();
unknown's avatar
unknown committed
1482
    general_log_print(thd, command, NullS);
1483
    my_eof(thd);
unknown's avatar
unknown committed
1484 1485 1486 1487 1488
    break;
  case COM_SLEEP:
  case COM_CONNECT:				// Impossible here
  case COM_TIME:				// Impossible from client
  case COM_DELAYED_INSERT:
1489
  case COM_END:
unknown's avatar
unknown committed
1490
  default:
unknown's avatar
unknown committed
1491
    my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
unknown's avatar
unknown committed
1492 1493
    break;
  }
1494

unknown's avatar
unknown committed
1495
  /* report error issued during command execution */
1496 1497
  if (thd->killed_errno())
  {
Marc Alff's avatar
Marc Alff committed
1498
    if (! thd->stmt_da->is_set())
1499 1500 1501 1502 1503 1504 1505 1506
      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;
  }

1507
  /* If commit fails, we should be able to reset the OK status. */
Marc Alff's avatar
Marc Alff committed
1508
  thd->stmt_da->can_overwrite_status= TRUE;
Konstantin Osipov's avatar
Konstantin Osipov committed
1509
  thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
Marc Alff's avatar
Marc Alff committed
1510
  thd->stmt_da->can_overwrite_status= FALSE;
1511 1512 1513

  thd->transaction.stmt.reset();

1514
  thd->protocol->end_statement();
1515
  query_cache_end_of_result(thd);
unknown's avatar
unknown committed
1516

1517 1518 1519 1520
  thd->proc_info= "closing tables";
  /* Free tables */
  close_thread_tables(thd);

Sergey Vojtovich's avatar
Sergey Vojtovich committed
1521
  if (!thd->is_error() && !thd->killed_errno())
Sergey Vojtovich's avatar
Sergey Vojtovich committed
1522
    mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0);
Sergey Vojtovich's avatar
Sergey Vojtovich committed
1523

1524
  log_slow_statement(thd);
1525

1526
  thd_proc_info(thd, "cleaning up");
1527
  thd->set_query(NULL, 0);
1528
  thd->command=COM_SLEEP;
1529
  dec_thread_running();
1530
  thd_proc_info(thd, 0);
1531 1532
  thd->packet.shrink(thd->variables.net_buffer_length);	// Reclaim some memory
  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
1533

Konstantin Osipov's avatar
Konstantin Osipov committed
1534 1535 1536
#if defined(ENABLED_PROFILING)
  thd->profiling.finish_current_query();
#endif
1537 1538 1539 1540 1541 1542 1543 1544 1545 1546
  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);
  }
1547 1548 1549 1550
  DBUG_RETURN(error);
}


1551
void log_slow_statement(THD *thd)
1552
{
unknown's avatar
unknown committed
1553
  DBUG_ENTER("log_slow_statement");
1554 1555 1556 1557 1558 1559 1560

  /*
    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
1561
    DBUG_VOID_RETURN;                           // Don't set time for sub stmt
1562

1563 1564
  /*
    Do not log administrative statements unless the appropriate option is
1565
    set.
1566
  */
1567
  if (thd->enable_slow_log)
unknown's avatar
unknown committed
1568
  {
1569
    ulonglong end_utime_of_query= thd->current_utime();
1570
    thd_proc_info(thd, "logging slow query");
1571

1572 1573 1574 1575
    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)) &&
1576 1577
          opt_log_queries_not_using_indexes &&
           !(sql_command_flags[thd->lex->sql_command] & CF_STATUS_COMMAND))) &&
1578
        thd->examined_row_count >= thd->variables.min_examined_row_limit)
1579
    {
1580
      thd_proc_info(thd, "logging slow query");
1581
      thd->status_var.long_query_count++;
1582 1583
      slow_log_print(thd, thd->query(), thd->query_length(), 
                     end_utime_of_query);
1584
    }
unknown's avatar
unknown committed
1585
  }
unknown's avatar
unknown committed
1586
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1587 1588
}

1589

unknown's avatar
unknown committed
1590
/**
unknown's avatar
unknown committed
1591 1592 1593 1594 1595 1596 1597
  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
1598 1599 1600 1601 1602 1603 1604
  @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
1605 1606 1607 1608
    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
1609
  @retval
unknown's avatar
unknown committed
1610
    0                 success
unknown's avatar
unknown committed
1611
  @retval
unknown's avatar
unknown committed
1612 1613 1614 1615
    1                 out of memory or SHOW commands are not allowed
                      in this version of the server.
*/

1616 1617 1618
int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
                         enum enum_schema_tables schema_table_idx)
{
1619
  SELECT_LEX *schema_select_lex= NULL;
1620
  DBUG_ENTER("prepare_schema_table");
1621

1622
  switch (schema_table_idx) {
1623 1624
  case SCH_SCHEMATA:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
unknown's avatar
unknown committed
1625 1626
    my_message(ER_NOT_ALLOWED_COMMAND,
               ER(ER_NOT_ALLOWED_COMMAND), MYF(0));   /* purecov: inspected */
1627 1628 1629 1630
    DBUG_RETURN(1);
#else
    break;
#endif
1631

1632 1633 1634
  case SCH_TABLE_NAMES:
  case SCH_TABLES:
  case SCH_VIEWS:
1635
  case SCH_TRIGGERS:
unknown's avatar
unknown committed
1636
  case SCH_EVENTS:
1637
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
1638 1639
    my_message(ER_NOT_ALLOWED_COMMAND,
               ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
1640 1641 1642
    DBUG_RETURN(1);
#else
    {
1643
      LEX_STRING db;
1644
      size_t dummy;
unknown's avatar
unknown committed
1645
      if (lex->select_lex.db == NULL &&
1646
          lex->copy_db_to(&lex->select_lex.db, &dummy))
1647
      {
unknown's avatar
unknown committed
1648
        DBUG_RETURN(1);
1649
      }
1650 1651 1652
      schema_select_lex= new SELECT_LEX();
      db.str= schema_select_lex->db= lex->select_lex.db;
      schema_select_lex->table_list.first= NULL;
1653
      db.length= strlen(db.str);
unknown's avatar
unknown committed
1654

1655
      if (check_db_name(&db))
1656
      {
1657
        my_error(ER_WRONG_DB_NAME, MYF(0), db.str);
1658 1659 1660 1661 1662 1663 1664
        DBUG_RETURN(1);
      }
      break;
    }
#endif
  case SCH_COLUMNS:
  case SCH_STATISTICS:
unknown's avatar
unknown committed
1665
  {
1666
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
1667 1668
    my_message(ER_NOT_ALLOWED_COMMAND,
               ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
1669 1670
    DBUG_RETURN(1);
#else
unknown's avatar
unknown committed
1671 1672 1673 1674 1675 1676
    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();
1677 1678
    if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ,
                                              MDL_SHARED_READ))
unknown's avatar
unknown committed
1679 1680 1681 1682
      DBUG_RETURN(1);
    lex->query_tables_last= query_tables_last;
    break;
  }
1683
#endif
1684 1685 1686 1687 1688
  case SCH_PROFILES:
    /* 
      Mark this current profiling record to be discarded.  We don't
      wish to have SHOW commands show up in profiling.
    */
1689
#if defined(ENABLED_PROFILING)
1690
    thd->profiling.discard_current_query();
1691 1692
#endif
    break;
1693 1694 1695
  case SCH_OPEN_TABLES:
  case SCH_VARIABLES:
  case SCH_STATUS:
1696 1697
  case SCH_PROCEDURES:
  case SCH_CHARSETS:
unknown's avatar
unknown committed
1698
  case SCH_ENGINES:
1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715
  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);
  }
1716
  TABLE_LIST *table_list= select_lex->table_list.first;
unknown's avatar
unknown committed
1717
  table_list->schema_select_lex= schema_select_lex;
1718
  table_list->schema_table_reformed= 1;
1719 1720 1721 1722
  DBUG_RETURN(0);
}


1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766
/**
  Implementation of FLUSH TABLES <table_list> WITH READ LOCK.

  In brief: take exclusive locks, expel tables from the table
  cache, reopen the tables, enter the 'LOCKED TABLES' mode,
  downgrade the locks.

  Required privileges
  -------------------
  Since the statement implicitly enters LOCK TABLES mode,
  it requires LOCK TABLES privilege on every table.
  But since the rest of FLUSH commands require
  the global RELOAD_ACL, it also requires RELOAD_ACL.

  Compatibility with the global read lock
  ---------------------------------------
  We don't wait for the GRL, since neither the
  5.1 combination that this new statement is intended to
  replace (LOCK TABLE <list> WRITE; FLUSH TABLES;),
  nor FLUSH TABLES WITH READ LOCK do.
  @todo: this is not implemented, Dmitry disagrees.
  Currently we wait for GRL in another connection,
  but are compatible with a GRL in our own connection.

  Behaviour under LOCK TABLES
  ---------------------------
  Bail out: i.e. don't perform an implicit UNLOCK TABLES.
  This is not consistent with LOCK TABLES statement, but is
  in line with behaviour of FLUSH TABLES WITH READ LOCK, and we
  try to not introduce any new statements with implicit
  semantics.

  Compatibility with parallel updates
  -----------------------------------
  As a result, we will wait for all open transactions
  against the tables to complete. After the lock downgrade,
  new transactions will be able to read the tables, but not
  write to them.

  Differences from FLUSH TABLES <list>
  -------------------------------------
  - you can't flush WITH READ LOCK a non-existent table
  - you can't flush WITH READ LOCK under LOCK TABLES
  - currently incompatible with the GRL (@todo: fix)
1767 1768 1769 1770 1771 1772 1773 1774

  Effect on views and temporary tables.
  ------------------------------------
  You can only apply this command to existing base tables.
  If a view with such name exists, ER_WRONG_OBJECT is returned.
  If a temporary table with such name exists, it's ignored:
  if there is a base table, it's used, otherwise ER_NO_SUCH_TABLE
  is returned.
1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808
*/

static bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables)
{
  Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;
  TABLE_LIST *table_list;

  /*
    This is called from SQLCOM_FLUSH, the transaction has
    been committed implicitly.
  */

  /* RELOAD_ACL is checked by the caller. Check table-level privileges. */
  if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
                         FALSE, UINT_MAX, FALSE))
    goto error;

  if (thd->locked_tables_mode)
  {
    my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
    goto error;
  }

  /*
    @todo: Since lock_table_names() acquires a global IX
    lock, this actually waits for a GRL in another connection.
    We are thus introducing an incompatibility.
    Do nothing for now, since not taking a global IX violates
    current internal MDL asserts, fix after discussing with
    Dmitry.
  */
  if (lock_table_names(thd, all_tables))
    goto error;

1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823
  for (table_list= all_tables; table_list;
       table_list= table_list->next_global)
  {
    /* Remove the table from cache. */
    mysql_mutex_lock(&LOCK_open);
    tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
                     table_list->db,
                     table_list->table_name);
    mysql_mutex_unlock(&LOCK_open);

    /* Skip views and temporary tables. */
    table_list->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */
    table_list->open_type= OT_BASE_ONLY;      /* Ignore temporary tables. */
  }

1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852
  if  (open_and_lock_tables(thd, all_tables, FALSE,
                            MYSQL_OPEN_HAS_MDL_LOCK,
                            &lock_tables_prelocking_strategy) ||
       thd->locked_tables_list.init_locked_tables(thd))
  {
    close_thread_tables(thd);
    goto error;
  }

  /*
    Downgrade the exclusive locks.
    Use MDL_SHARED_NO_WRITE as the intended
    post effect of this call is identical
    to LOCK TABLES <...> READ, and we didn't use
    thd->in_lock_talbes and thd->sql_command= SQLCOM_LOCK_TABLES
    hacks to enter the LTM.
    @todo: release the global IX lock here!!!
  */
  for (table_list= all_tables; table_list;
       table_list= table_list->next_global)
    table_list->mdl_request.ticket->downgrade_exclusive_lock(MDL_SHARED_NO_WRITE);

  return FALSE;

error:
  return TRUE;
}


unknown's avatar
unknown committed
1853 1854 1855
/**
  Read query from packet and store in thd->query.
  Used in COM_QUERY and COM_STMT_PREPARE.
1856 1857

    Sets the following THD variables:
unknown's avatar
unknown committed
1858 1859
  - query
  - query_length
1860

unknown's avatar
unknown committed
1861
  @retval
unknown's avatar
unknown committed
1862
    FALSE ok
unknown's avatar
unknown committed
1863
  @retval
unknown's avatar
unknown committed
1864
    TRUE  error;  In this case thd->fatal_error is set
1865 1866
*/

1867
bool alloc_query(THD *thd, const char *packet, uint packet_length)
1868
{
1869
  char *query;
1870
  /* Remove garbage at start and end of query */
1871
  while (packet_length > 0 && my_isspace(thd->charset(), packet[0]))
1872 1873 1874 1875
  {
    packet++;
    packet_length--;
  }
1876
  const char *pos= packet + packet_length;     // Point at end null
unknown's avatar
unknown committed
1877
  while (packet_length > 0 &&
unknown's avatar
unknown committed
1878
	 (pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1])))
1879 1880 1881 1882 1883
  {
    pos--;
    packet_length--;
  }
  /* We must allocate some extra memory for query cache */
1884 1885 1886 1887 1888 1889 1890
  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);
1891 1892 1893 1894

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

unknown's avatar
unknown committed
1896
  return FALSE;
1897 1898
}

1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911
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;
1912
  thd->variables.lc_time_names= &my_locale_en_US;
1913 1914 1915
  thd->one_shot_set= 0;
}

1916

unknown's avatar
unknown committed
1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962
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);

1963
    if (thd->slave_thread && lex->sphead)
unknown's avatar
unknown committed
1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001
      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
2002 2003
/**
  Execute command saved in thd and lex->sql_command.
2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014

    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
2015 2016 2017 2018 2019 2020 2021 2022 2023 2024
  @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
2025
    FALSE       OK
unknown's avatar
unknown committed
2026
  @retval
2027 2028
    TRUE        Error
*/
unknown's avatar
unknown committed
2029

2030
int
2031
mysql_execute_command(THD *thd)
unknown's avatar
unknown committed
2032
{
2033
  int res= FALSE;
unknown's avatar
unknown committed
2034
  int  up_result= 0;
2035
  LEX  *lex= thd->lex;
unknown's avatar
unknown committed
2036
  /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
unknown's avatar
unknown committed
2037
  SELECT_LEX *select_lex= &lex->select_lex;
unknown's avatar
VIEW  
unknown committed
2038
  /* first table of first SELECT_LEX */
2039
  TABLE_LIST *first_table= select_lex->table_list.first;
unknown's avatar
VIEW  
unknown committed
2040 2041 2042
  /* list of all tables in query */
  TABLE_LIST *all_tables;
  /* most outer SELECT_LEX_UNIT of query */
2043
  SELECT_LEX_UNIT *unit= &lex->unit;
2044 2045 2046 2047
#ifdef HAVE_REPLICATION
  /* have table map for update for multi-update statement (BUG#37051) */
  bool have_table_map_for_update= FALSE;
#endif
2048
  /* Saved variable value */
unknown's avatar
unknown committed
2049
  DBUG_ENTER("mysql_execute_command");
unknown's avatar
unknown committed
2050 2051 2052
#ifdef WITH_PARTITION_STORAGE_ENGINE
  thd->work_part_info= 0;
#endif
unknown's avatar
unknown committed
2053

unknown's avatar
VIEW  
unknown committed
2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069
  /*
    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();
2070
  /* should be assigned after making first tables same */
unknown's avatar
VIEW  
unknown committed
2071
  all_tables= lex->query_tables;
2072 2073
  /* set context for commands which do not use setup_tables */
  select_lex->
2074
    context.resolve_in_table_list_only(select_lex->
2075
                                       table_list.first);
unknown's avatar
VIEW  
unknown committed
2076

2077 2078 2079 2080 2081 2082
  /*
    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
2083 2084 2085 2086 2087 2088 2089 2090
  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);
  }
2091

unknown's avatar
SCRUM  
unknown committed
2092
#ifdef HAVE_REPLICATION
2093
  if (unlikely(thd->slave_thread))
2094
  {
2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117
    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;
    }
2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159

    /*
      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;
    }
2160
    
unknown's avatar
unknown committed
2161
    /*
unknown's avatar
unknown committed
2162 2163
      Check if statment should be skipped because of slave filtering
      rules
2164 2165

      Exceptions are:
unknown's avatar
unknown committed
2166 2167
      - UPDATE MULTI: For this statement, we want to check the filtering
        rules later in the code
2168
      - SET: we always execute it (Not that many SET commands exists in
unknown's avatar
unknown committed
2169 2170
        the binary log anyway -- only 4.1 masters write SET statements,
	in 5.0 there are no SET statements in the binary log)
2171 2172
      - 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
2173
    */
unknown's avatar
unknown committed
2174 2175
    if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) &&
	!(lex->sql_command == SQLCOM_SET_OPTION) &&
2176
	!(lex->sql_command == SQLCOM_DROP_TABLE &&
2177
          lex->drop_temporary && lex->drop_if_exists) &&
unknown's avatar
Merge  
unknown committed
2178
        all_tables_not_ok(thd, all_tables))
unknown's avatar
Fix for  
unknown committed
2179 2180
    {
      /* we warn the slave SQL thread */
unknown's avatar
unknown committed
2181
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198
      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);
      }
2199
      DBUG_RETURN(0);
unknown's avatar
Fix for  
unknown committed
2200
    }
2201
  }
2202
  else
2203
  {
2204
#endif /* HAVE_REPLICATION */
2205 2206 2207 2208
    /*
      When option readonly is set deny operations which change non-temporary
      tables. Except for the replication thread and the 'super' users.
    */
2209
    if (deny_updates_if_read_only_option(thd, all_tables))
2210 2211 2212 2213 2214 2215 2216
    {
      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
2217

2218
  status_var_increment(thd->status_var.com_stat[lex->sql_command]);
2219

unknown's avatar
unknown committed
2220
  DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE);
Konstantin Osipov's avatar
Konstantin Osipov committed
2221 2222 2223 2224 2225 2226 2227

  /*
    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.
  */
2228 2229 2230 2231 2232 2233 2234 2235 2236
  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);
2237
    thd->mdl_context.release_transactional_locks();
2238
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
2239

Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
2240 2241 2242 2243 2244 2245 2246 2247
  /*
    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)
2248
    if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
2249 2250
      goto error;

2251 2252 2253 2254 2255
#ifndef DBUG_OFF
  if (lex->sql_command != SQLCOM_SET_OPTION)
    DEBUG_SYNC(thd,"before_execute_sql_command");
#endif

unknown's avatar
unknown committed
2256
  switch (lex->sql_command) {
2257

2258
  case SQLCOM_SHOW_EVENTS:
2259 2260 2261 2262
#ifndef HAVE_EVENT_SCHEDULER
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
    break;
#endif
2263 2264
  case SQLCOM_SHOW_STATUS_PROC:
  case SQLCOM_SHOW_STATUS_FUNC:
Konstantin Osipov's avatar
Konstantin Osipov committed
2265
    if ((res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
2266
                                  UINT_MAX, FALSE)))
Konstantin Osipov's avatar
Konstantin Osipov committed
2267 2268
      goto error;
    res= execute_sqlcom_select(thd, all_tables);
2269 2270 2271 2272 2273
    break;
  case SQLCOM_SHOW_STATUS:
  {
    system_status_var old_status_var= thd->status_var;
    thd->initial_status_var= &old_status_var;
2274 2275
    if (!(res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
                                  UINT_MAX, FALSE)))
2276
      res= execute_sqlcom_select(thd, all_tables);
2277 2278 2279 2280 2281 2282 2283
    /* 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
2284
    mysql_mutex_lock(&LOCK_status);
2285 2286 2287
    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
2288
    mysql_mutex_unlock(&LOCK_status);
2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301
    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:
2302
  case SQLCOM_SHOW_STORAGE_ENGINES:
2303
  case SQLCOM_SHOW_PROFILE:
unknown's avatar
unknown committed
2304
  case SQLCOM_SELECT:
2305
  {
2306
    thd->status_var.last_query_cost= 0.0;
2307 2308 2309 2310 2311 2312 2313 2314

    /*
      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
2315
    if (all_tables)
2316
      res= check_table_access(thd,
2317 2318
                              privileges_requested,
                              all_tables, FALSE, UINT_MAX, FALSE);
unknown's avatar
unknown committed
2319
    else
Marc Alff's avatar
Marc Alff committed
2320
      res= check_access(thd, privileges_requested, any_db, NULL, NULL, 0, 0);
2321

2322 2323 2324
    if (res)
      break;

Konstantin Osipov's avatar
Konstantin Osipov committed
2325
    if (!thd->locked_tables_mode && lex->protect_against_global_read_lock &&
2326
        thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
2327 2328 2329
      break;

    res= execute_sqlcom_select(thd, all_tables);
unknown's avatar
unknown committed
2330
    break;
2331 2332
  }
case SQLCOM_PREPARE:
2333
  {
2334
    mysql_sql_stmt_prepare(thd);
unknown's avatar
unknown committed
2335 2336 2337 2338
    break;
  }
  case SQLCOM_EXECUTE:
  {
2339
    mysql_sql_stmt_execute(thd);
unknown's avatar
unknown committed
2340 2341 2342 2343
    break;
  }
  case SQLCOM_DEALLOCATE_PREPARE:
  {
2344
    mysql_sql_stmt_close(thd);
unknown's avatar
unknown committed
2345 2346
    break;
  }
unknown's avatar
unknown committed
2347
  case SQLCOM_DO:
2348
    if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
2349
        || open_and_lock_tables(thd, all_tables, TRUE, 0))
unknown's avatar
unknown committed
2350
      goto error;
unknown's avatar
unknown committed
2351 2352

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

2355
  case SQLCOM_EMPTY_QUERY:
2356
    my_ok(thd);
2357 2358
    break;

unknown's avatar
unknown committed
2359 2360 2361 2362
  case SQLCOM_HELP:
    res= mysqld_help(thd,lex->help_arg);
    break;

2363
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
2364
  case SQLCOM_PURGE:
2365
  {
unknown's avatar
unknown committed
2366
    if (check_global_access(thd, SUPER_ACL))
2367
      goto error;
unknown's avatar
unknown committed
2368
    /* PURGE MASTER LOGS TO 'file' */
2369 2370 2371
    res = purge_master_logs(thd, lex->to_log);
    break;
  }
2372 2373
  case SQLCOM_PURGE_BEFORE:
  {
2374 2375
    Item *it;

2376 2377
    if (check_global_access(thd, SUPER_ACL))
      goto error;
unknown's avatar
unknown committed
2378
    /* PURGE MASTER LOGS BEFORE 'data' */
2379
    it= (Item *)lex->value_list.head();
2380
    if ((!it->fixed && it->fix_fields(lex->thd, &it)) ||
unknown's avatar
unknown committed
2381
        it->check_cols(1))
2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392
    {
      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());
2393 2394
    break;
  }
2395
#endif
unknown's avatar
unknown committed
2396 2397
  case SQLCOM_SHOW_WARNS:
  {
2398 2399
    res= mysqld_show_warnings(thd, (ulong)
			      ((1L << (uint) MYSQL_ERROR::WARN_LEVEL_NOTE) |
2400 2401 2402
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_WARN) |
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)
			       ));
unknown's avatar
unknown committed
2403 2404 2405 2406
    break;
  }
  case SQLCOM_SHOW_ERRORS:
  {
2407 2408
    res= mysqld_show_warnings(thd, (ulong)
			      (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
unknown's avatar
unknown committed
2409 2410
    break;
  }
unknown's avatar
unknown committed
2411 2412
  case SQLCOM_SHOW_PROFILES:
  {
2413
#if defined(ENABLED_PROFILING)
2414
    thd->profiling.discard_current_query();
unknown's avatar
unknown committed
2415
    res= thd->profiling.show_profiles();
2416 2417 2418
    if (res)
      goto error;
#else
2419
    my_error(ER_FEATURE_DISABLED, MYF(0), "SHOW PROFILES", "enable-profiling");
2420 2421
    goto error;
#endif
unknown's avatar
unknown committed
2422 2423
    break;
  }
unknown's avatar
unknown committed
2424 2425
  case SQLCOM_SHOW_NEW_MASTER:
  {
unknown's avatar
unknown committed
2426
    if (check_global_access(thd, REPL_SLAVE_ACL))
unknown's avatar
unknown committed
2427
      goto error;
2428
    /* This query don't work now. See comment in repl_failsafe.cc */
unknown's avatar
unknown committed
2429
#ifndef WORKING_NEW_MASTER
unknown's avatar
unknown committed
2430 2431
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "SHOW NEW MASTER");
    goto error;
unknown's avatar
unknown committed
2432
#else
unknown's avatar
unknown committed
2433 2434
    res = show_new_master(thd);
    break;
unknown's avatar
unknown committed
2435
#endif
unknown's avatar
unknown committed
2436
  }
2437

unknown's avatar
unknown committed
2438
#ifdef HAVE_REPLICATION
2439 2440
  case SQLCOM_SHOW_SLAVE_HOSTS:
  {
unknown's avatar
unknown committed
2441
    if (check_global_access(thd, REPL_SLAVE_ACL))
2442 2443 2444 2445
      goto error;
    res = show_slave_hosts(thd);
    break;
  }
2446
  case SQLCOM_SHOW_RELAYLOG_EVENTS: /* fall through */
unknown's avatar
unknown committed
2447 2448
  case SQLCOM_SHOW_BINLOG_EVENTS:
  {
unknown's avatar
unknown committed
2449
    if (check_global_access(thd, REPL_SLAVE_ACL))
unknown's avatar
unknown committed
2450
      goto error;
unknown's avatar
unknown committed
2451
    res = mysql_show_binlog_events(thd);
unknown's avatar
unknown committed
2452 2453
    break;
  }
2454 2455
#endif

unknown's avatar
unknown committed
2456 2457
  case SQLCOM_ASSIGN_TO_KEYCACHE:
  {
unknown's avatar
VIEW  
unknown committed
2458
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
2459
    if (check_access(thd, INDEX_ACL, first_table->db,
Marc Alff's avatar
Marc Alff committed
2460 2461 2462
                     &first_table->grant.privilege,
                     &first_table->grant.m_internal,
                     0, 0))
unknown's avatar
unknown committed
2463
      goto error;
unknown's avatar
unknown committed
2464
    res= mysql_assign_to_keycache(thd, first_table, &lex->ident);
unknown's avatar
unknown committed
2465 2466
    break;
  }
unknown's avatar
unknown committed
2467 2468
  case SQLCOM_PRELOAD_KEYS:
  {
unknown's avatar
VIEW  
unknown committed
2469
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
2470
    if (check_access(thd, INDEX_ACL, first_table->db,
Marc Alff's avatar
Marc Alff committed
2471 2472 2473
                     &first_table->grant.privilege,
                     &first_table->grant.m_internal,
                     0, 0))
2474
      goto error;
unknown's avatar
VIEW  
unknown committed
2475
    res = mysql_preload_keys(thd, first_table);
unknown's avatar
unknown committed
2476 2477
    break;
  }
unknown's avatar
unknown committed
2478
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
2479
  case SQLCOM_CHANGE_MASTER:
2480
  {
unknown's avatar
unknown committed
2481
    if (check_global_access(thd, SUPER_ACL))
2482
      goto error;
Marc Alff's avatar
Marc Alff committed
2483
    mysql_mutex_lock(&LOCK_active_mi);
2484
    res = change_master(thd,active_mi);
Marc Alff's avatar
Marc Alff committed
2485
    mysql_mutex_unlock(&LOCK_active_mi);
2486 2487
    break;
  }
unknown's avatar
unknown committed
2488
  case SQLCOM_SHOW_SLAVE_STAT:
2489
  {
2490 2491
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2492
      goto error;
Marc Alff's avatar
Marc Alff committed
2493
    mysql_mutex_lock(&LOCK_active_mi);
2494 2495 2496 2497 2498 2499
    if (active_mi != NULL)
    {
      res = show_master_info(thd, active_mi);
    }
    else
    {
2500 2501
      push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                   WARN_NO_MASTER_INFO, ER(WARN_NO_MASTER_INFO));
2502
      my_ok(thd);
2503
    }
Marc Alff's avatar
Marc Alff committed
2504
    mysql_mutex_unlock(&LOCK_active_mi);
2505 2506
    break;
  }
unknown's avatar
unknown committed
2507
  case SQLCOM_SHOW_MASTER_STAT:
2508
  {
2509 2510
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2511 2512 2513 2514
      goto error;
    res = show_binlog_info(thd);
    break;
  }
unknown's avatar
unknown committed
2515

unknown's avatar
unknown committed
2516
#endif /* HAVE_REPLICATION */
unknown's avatar
unknown committed
2517
  case SQLCOM_SHOW_ENGINE_STATUS:
unknown's avatar
unknown committed
2518
    {
2519
      if (check_global_access(thd, PROCESS_ACL))
unknown's avatar
unknown committed
2520 2521
        goto error;
      res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_STATUS);
unknown's avatar
unknown committed
2522 2523
      break;
    }
unknown's avatar
unknown committed
2524
  case SQLCOM_SHOW_ENGINE_MUTEX:
unknown's avatar
unknown committed
2525
    {
2526
      if (check_global_access(thd, PROCESS_ACL))
unknown's avatar
unknown committed
2527
        goto error;
unknown's avatar
unknown committed
2528
      res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_MUTEX);
unknown's avatar
unknown committed
2529 2530
      break;
    }
unknown's avatar
unknown committed
2531
  case SQLCOM_CREATE_TABLE:
unknown's avatar
unknown committed
2532
  {
unknown's avatar
VIEW  
unknown committed
2533 2534
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    bool link_to_local;
2535 2536 2537
    TABLE_LIST *create_table= first_table;
    TABLE_LIST *select_tables= lex->create_last_non_select_table->next_global;

2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558
    /*
      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
2559

unknown's avatar
VIEW  
unknown committed
2560
    if ((res= create_table_precheck(thd, select_tables, create_table)))
unknown's avatar
unknown committed
2561
      goto end_with_restore_list;
unknown's avatar
unknown committed
2562

2563 2564 2565
    /* Might have been updated in create_table_precheck */
    create_info.alias= create_table->alias;

2566
#ifdef HAVE_READLINK
unknown's avatar
unknown committed
2567
    /* Fix names if symlinked tables */
2568
    if (append_file_to_dir(thd, &create_info.data_file_name,
2569
			   create_table->table_name) ||
2570
	append_file_to_dir(thd, &create_info.index_file_name,
2571
			   create_table->table_name))
unknown's avatar
unknown committed
2572
      goto end_with_restore_list;
2573
#endif
2574
    /*
2575
      If we are using SET CHARSET without DEFAULT, add an implicit
2576 2577
      DEFAULT to not confuse old users. (This may change).
    */
2578
    if ((create_info.used_fields &
2579 2580 2581
	 (HA_CREATE_USED_DEFAULT_CHARSET | HA_CREATE_USED_CHARSET)) ==
	HA_CREATE_USED_CHARSET)
    {
2582 2583 2584 2585
      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;
2586
    }
2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597
    /*
      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
2598
      created during a global read lock.
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
2599
      Protection against grl is covered by the CF_PROTECT_AGAINST_GRL flag.
2600
    */
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
2601

2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612
#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
2613

2614 2615
    /* Set strategies: reset default or 'prepared' values. */
    create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
2616
    create_table->lock_strategy= TABLE_LIST::OTLS_DOWNGRADE_IF_EXISTS;
2617

2618 2619 2620 2621 2622
    /*
      Close any open handlers for the table
    */
    mysql_ha_rm_tables(thd, create_table);

2623
    if (select_lex->item_list.elements)		// With select
unknown's avatar
unknown committed
2624 2625
    {
      select_result *result;
2626

2627 2628 2629
      /*
        If:
        a) we inside an SP and there was NAME_CONST substitution,
Ramil Kalimullin's avatar
Ramil Kalimullin committed
2630
        b) binlogging is on (STMT mode),
2631 2632 2633 2634 2635 2636
        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
2637
          thd->variables.binlog_format == BINLOG_FORMAT_STMT &&
2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663
          !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.");
      }
      
2664
      select_lex->options|= SELECT_NO_UNLOCK;
2665
      unit->set_limit(select_lex);
2666

2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679
      /*
        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;
      }

2680
      if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
unknown's avatar
unknown committed
2681
      {
2682
        /* Base table and temporary table are not in the same name space. */
2683
        create_table->open_type= OT_BASE_ONLY;
unknown's avatar
unknown committed
2684 2685
      }

2686
      if (!(res= open_and_lock_tables(thd, lex->query_tables, TRUE, 0)))
2687
      {
2688 2689 2690 2691
        /*
          Is table which we are changing used somewhere in other parts
          of query
        */
2692
        if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
2693
        {
2694
          TABLE_LIST *duplicate;
2695
          if ((duplicate= unique_table(thd, create_table, select_tables, 0)))
2696 2697 2698
          {
            update_non_unique_table_error(create_table, "CREATE", duplicate);
            res= 1;
2699
            goto end_with_restore_list;
2700
          }
2701
        }
unknown's avatar
unknown committed
2702
        /* If we create merge table, we have to test tables in merge, too */
2703
        if (create_info.used_fields & HA_CREATE_USED_UNION)
unknown's avatar
unknown committed
2704 2705
        {
          TABLE_LIST *tab;
2706
          for (tab= create_info.merge_list.first;
unknown's avatar
unknown committed
2707 2708 2709
               tab;
               tab= tab->next_local)
          {
2710
            TABLE_LIST *duplicate;
2711
            if ((duplicate= unique_table(thd, tab, select_tables, 0)))
unknown's avatar
unknown committed
2712
            {
2713
              update_non_unique_table_error(tab, "CREATE", duplicate);
unknown's avatar
unknown committed
2714
              res= 1;
2715
              goto end_with_restore_list;
unknown's avatar
unknown committed
2716 2717 2718
            }
          }
        }
2719

2720 2721 2722 2723 2724 2725 2726
        /*
          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);

2727 2728 2729 2730
        /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
        if (create_info.options & HA_LEX_CREATE_TMP_TABLE)
          thd->variables.option_bits|= OPTION_KEEP_LOG;

unknown's avatar
unknown committed
2731
        /*
2732 2733
          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
2734
        */
unknown's avatar
VIEW  
unknown committed
2735
        if ((result= new select_create(create_table,
2736 2737 2738 2739
                                       &create_info,
                                       &alter_info,
                                       select_lex->item_list,
                                       lex->duplicates,
2740
                                       lex->ignore,
2741
                                       select_tables)))
2742 2743 2744 2745 2746
        {
          /*
            CREATE from SELECT give its SELECT_LEX for SELECT,
            and item_list belong to SELECT
          */
2747
          res= handle_select(thd, lex, result, 0);
2748
          delete result;
2749
        }
2750 2751
        
        lex->link_first_table_back(create_table, link_to_local);
2752 2753
      }
    }
unknown's avatar
unknown committed
2754
    else
unknown's avatar
unknown committed
2755
    {
2756
      /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
2757
      if (create_info.options & HA_LEX_CREATE_TMP_TABLE)
2758
        thd->variables.option_bits|= OPTION_KEEP_LOG;
unknown's avatar
unknown committed
2759
      /* regular create */
2760
      if (create_info.options & HA_LEX_CREATE_TABLE_LIKE)
2761 2762
      {
        /* CREATE TABLE ... LIKE ... */
unknown's avatar
unknown committed
2763
        res= mysql_create_like_table(thd, create_table, select_tables,
2764
                                     &create_info);
2765
      }
unknown's avatar
unknown committed
2766
      else
2767
      {
2768 2769 2770
        /* Regular CREATE TABLE */
        res= mysql_create_table(thd, create_table,
                                &create_info, &alter_info);
2771
      }
unknown's avatar
unknown committed
2772
      if (!res)
2773
        my_ok(thd);
unknown's avatar
unknown committed
2774
    }
2775

unknown's avatar
unknown committed
2776
end_with_restore_list:
unknown's avatar
unknown committed
2777
    break;
unknown's avatar
unknown committed
2778
  }
unknown's avatar
unknown committed
2779
  case SQLCOM_CREATE_INDEX:
2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797
    /* 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
2798 2799
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_one_table_access(thd, INDEX_ACL, all_tables))
unknown's avatar
unknown committed
2800
      goto error; /* purecov: inspected */
2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811
    /*
      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
2812

2813 2814 2815 2816 2817
    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
2818
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
2819
  case SQLCOM_SLAVE_START:
2820
  {
Marc Alff's avatar
Marc Alff committed
2821
    mysql_mutex_lock(&LOCK_active_mi);
2822
    start_slave(thd,active_mi,1 /* net report*/);
Marc Alff's avatar
Marc Alff committed
2823
    mysql_mutex_unlock(&LOCK_active_mi);
unknown's avatar
unknown committed
2824
    break;
2825
  }
unknown's avatar
unknown committed
2826
  case SQLCOM_SLAVE_STOP:
2827 2828 2829 2830 2831 2832
  /*
    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,
2833
      so it waits for the client thread because t is locked by it.
2834
    - then the client thread does SLAVE STOP.
2835 2836
      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.
2837 2838 2839
    To prevent that, refuse SLAVE STOP if the
    client thread has locked tables
  */
Konstantin Osipov's avatar
Konstantin Osipov committed
2840
  if (thd->locked_tables_mode ||
2841
      thd->in_active_multi_stmt_transaction() || thd->global_read_lock.is_acquired())
2842
  {
2843 2844
    my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
               ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
2845
    goto error;
2846
  }
2847
  {
Marc Alff's avatar
Marc Alff committed
2848
    mysql_mutex_lock(&LOCK_active_mi);
2849
    stop_slave(thd,active_mi,1/* net report*/);
Marc Alff's avatar
Marc Alff committed
2850
    mysql_mutex_unlock(&LOCK_active_mi);
unknown's avatar
unknown committed
2851
    break;
2852
  }
unknown's avatar
unknown committed
2853
#endif /* HAVE_REPLICATION */
2854

unknown's avatar
unknown committed
2855
  case SQLCOM_ALTER_TABLE:
unknown's avatar
VIEW  
unknown committed
2856
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
2857
    {
unknown's avatar
unknown committed
2858
      ulong priv=0;
unknown's avatar
unknown committed
2859
      ulong priv_needed= ALTER_ACL;
2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870
      /*
        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;
2871 2872 2873 2874
      /*
        We also require DROP priv for ALTER TABLE ... DROP PARTITION, as well
        as for RENAME TO, as being done by SQLCOM_RENAME_TABLE
      */
2875
      if (alter_info.flags & (ALTER_DROP_PARTITION | ALTER_RENAME))
unknown's avatar
unknown committed
2876 2877
        priv_needed|= DROP_ACL;

unknown's avatar
unknown committed
2878 2879
      /* Must be set in the parser */
      DBUG_ASSERT(select_lex->db);
unknown's avatar
unknown committed
2880
      if (check_access(thd, priv_needed, first_table->db,
Marc Alff's avatar
Marc Alff committed
2881 2882 2883 2884 2885 2886 2887
                       &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
2888
	  check_merge_table_access(thd, first_table->db,
2889
				   create_info.merge_list.first))
2890
	goto error;				/* purecov: inspected */
2891
      if (check_grant(thd, priv_needed, all_tables, FALSE, UINT_MAX, FALSE))
2892 2893 2894 2895 2896 2897 2898 2899
        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;
2900 2901
          if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, FALSE,
              UINT_MAX, FALSE))
2902
            goto error;
unknown's avatar
unknown committed
2903
      }
2904

unknown's avatar
unknown committed
2905
      /* Don't yet allow changing of symlinks with ALTER TABLE */
2906
      if (create_info.data_file_name)
2907 2908 2909
        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                            WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
                            "DATA DIRECTORY");
2910
      if (create_info.index_file_name)
2911 2912 2913
        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                            WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
                            "INDEX DIRECTORY");
2914
      create_info.data_file_name= create_info.index_file_name= NULL;
2915 2916

      thd->enable_slow_log= opt_log_slow_admin_statements;
2917
      res= mysql_alter_table(thd, select_lex->db, lex->name.str,
2918 2919 2920
                             &create_info,
                             first_table,
                             &alter_info,
2921
                             select_lex->order_list.elements,
2922
                             select_lex->order_list.first,
2923
                             lex->ignore);
unknown's avatar
unknown committed
2924 2925
      break;
    }
unknown's avatar
unknown committed
2926
  case SQLCOM_RENAME_TABLE:
unknown's avatar
unknown committed
2927
  {
unknown's avatar
VIEW  
unknown committed
2928
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
2929
    TABLE_LIST *table;
unknown's avatar
VIEW  
unknown committed
2930
    for (table= first_table; table; table= table->next_local->next_local)
unknown's avatar
unknown committed
2931
    {
unknown's avatar
unknown committed
2932
      if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
Marc Alff's avatar
Marc Alff committed
2933 2934 2935 2936 2937 2938 2939
                       &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
2940
	goto error;
2941 2942 2943 2944 2945 2946 2947
      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];
2948
      if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, FALSE, 1, FALSE) ||
2949 2950
         (!test_all_bits(table->next_local->grant.privilege,
                         INSERT_ACL | CREATE_ACL) &&
2951 2952
          check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, FALSE, 1,
                      FALSE)))
2953
        goto error;
unknown's avatar
unknown committed
2954
    }
2955

Konstantin Osipov's avatar
Konstantin Osipov committed
2956
    if (mysql_rename_tables(thd, first_table, 0))
unknown's avatar
unknown committed
2957
      goto error;
unknown's avatar
unknown committed
2958
    break;
unknown's avatar
unknown committed
2959
  }
2960
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
2961 2962
  case SQLCOM_SHOW_BINLOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
2963 2964
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0)); /* purecov: inspected */
2965
    goto error;
unknown's avatar
unknown committed
2966 2967
#else
    {
unknown's avatar
unknown committed
2968
      if (check_global_access(thd, SUPER_ACL))
unknown's avatar
unknown committed
2969 2970 2971 2972
	goto error;
      res = show_binlogs(thd);
      break;
    }
unknown's avatar
unknown committed
2973
#endif
2974
#endif /* EMBEDDED_LIBRARY */
unknown's avatar
unknown committed
2975
  case SQLCOM_SHOW_CREATE:
unknown's avatar
VIEW  
unknown committed
2976
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
2977
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
2978 2979
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0)); /* purecov: inspected */
2980
    goto error;
unknown's avatar
unknown committed
2981
#else
unknown's avatar
unknown committed
2982
    {
2983 2984 2985 2986 2987 2988 2989 2990 2991
     /*
        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.
      */

Mats Kindahl's avatar
Mats Kindahl committed
2992 2993 2994
      DBUG_PRINT("debug", ("lex->only_view: %d, table: %s.%s",
                           lex->only_view,
                           first_table->db, first_table->table_name));
2995
      if (lex->only_view)
2996 2997 2998
      {
        if (check_table_access(thd, SELECT_ACL, first_table, FALSE, 1, FALSE))
        {
Mats Kindahl's avatar
Mats Kindahl committed
2999
          DBUG_PRINT("debug", ("check_table_access failed"));
3000 3001 3002 3003 3004
          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;
        }
Mats Kindahl's avatar
Mats Kindahl committed
3005
        DBUG_PRINT("debug", ("check_table_access succeeded"));
3006 3007

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

3010 3011 3012 3013
      }
      else
      {
        /*
Marc Alff's avatar
Marc Alff committed
3014 3015 3016
          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.
3017
        */
Mattias Jonsson's avatar
Mattias Jonsson committed
3018
        DBUG_PRINT("debug", ("first_table->grant.privilege: %lx",
Mats Kindahl's avatar
Mats Kindahl committed
3019
                             first_table->grant.privilege));
Marc Alff's avatar
Marc Alff committed
3020 3021
        if (check_some_access(thd, SHOW_CREATE_TABLE_ACLS, first_table) ||
            (first_table->grant.privilege & SHOW_CREATE_TABLE_ACLS) == 0)
3022 3023 3024 3025 3026 3027 3028 3029
        {
          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;
        }
      }

3030
      /* Access is granted. Execute the command.  */
unknown's avatar
unknown committed
3031
      res= mysqld_show_create(thd, first_table);
unknown's avatar
unknown committed
3032 3033
      break;
    }
unknown's avatar
unknown committed
3034
#endif
3035 3036
  case SQLCOM_CHECKSUM:
  {
unknown's avatar
VIEW  
unknown committed
3037
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3038 3039
    if (check_table_access(thd, SELECT_ACL, all_tables,
                           FALSE, UINT_MAX, FALSE))
3040
      goto error; /* purecov: inspected */
3041

unknown's avatar
VIEW  
unknown committed
3042
    res = mysql_checksum_table(thd, first_table, &lex->check_opt);
3043 3044
    break;
  }
unknown's avatar
unknown committed
3045
  case SQLCOM_REPAIR:
3046
  {
unknown's avatar
VIEW  
unknown committed
3047
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3048
    if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
3049
                           FALSE, UINT_MAX, FALSE))
3050
      goto error; /* purecov: inspected */
3051
    thd->enable_slow_log= opt_log_slow_admin_statements;
unknown's avatar
VIEW  
unknown committed
3052
    res= mysql_repair_table(thd, first_table, &lex->check_opt);
3053 3054 3055
    /* ! we write after unlocking the table */
    if (!res && !lex->no_write_to_binlog)
    {
unknown's avatar
unknown committed
3056 3057 3058
      /*
        Presumably, REPAIR and binlog writing doesn't require synchronization
      */
3059
      res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
3060
    }
3061
    select_lex->table_list.first= first_table;
3062
    lex->query_tables=all_tables;
3063 3064
    break;
  }
unknown's avatar
unknown committed
3065
  case SQLCOM_CHECK:
3066
  {
unknown's avatar
VIEW  
unknown committed
3067
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3068 3069
    if (check_table_access(thd, SELECT_ACL, all_tables,
                           TRUE, UINT_MAX, FALSE))
3070
      goto error; /* purecov: inspected */
3071
    thd->enable_slow_log= opt_log_slow_admin_statements;
unknown's avatar
VIEW  
unknown committed
3072
    res = mysql_check_table(thd, first_table, &lex->check_opt);
3073
    select_lex->table_list.first= first_table;
3074
    lex->query_tables=all_tables;
3075 3076
    break;
  }
unknown's avatar
unknown committed
3077 3078
  case SQLCOM_ANALYZE:
  {
unknown's avatar
VIEW  
unknown committed
3079
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3080
    if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
3081
                           FALSE, UINT_MAX, FALSE))
unknown's avatar
unknown committed
3082
      goto error; /* purecov: inspected */
3083
    thd->enable_slow_log= opt_log_slow_admin_statements;
3084
    res= mysql_analyze_table(thd, first_table, &lex->check_opt);
3085 3086 3087
    /* ! we write after unlocking the table */
    if (!res && !lex->no_write_to_binlog)
    {
unknown's avatar
unknown committed
3088 3089 3090
      /*
        Presumably, ANALYZE and binlog writing doesn't require synchronization
      */
3091
      res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
3092
    }
3093
    select_lex->table_list.first= first_table;
3094
    lex->query_tables=all_tables;
unknown's avatar
unknown committed
3095
    break;
unknown's avatar
unknown committed
3096
  }
3097

unknown's avatar
unknown committed
3098 3099
  case SQLCOM_OPTIMIZE:
  {
unknown's avatar
VIEW  
unknown committed
3100
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3101
    if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables,
3102
                           FALSE, UINT_MAX, FALSE))
unknown's avatar
unknown committed
3103
      goto error; /* purecov: inspected */
3104
    thd->enable_slow_log= opt_log_slow_admin_statements;
3105
    res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
3106
      mysql_recreate_table(thd, first_table) :
unknown's avatar
VIEW  
unknown committed
3107
      mysql_optimize_table(thd, first_table, &lex->check_opt);
3108 3109 3110
    /* ! we write after unlocking the table */
    if (!res && !lex->no_write_to_binlog)
    {
unknown's avatar
unknown committed
3111 3112 3113
      /*
        Presumably, OPTIMIZE and binlog writing doesn't require synchronization
      */
3114
      res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
3115
    }
3116
    select_lex->table_list.first= first_table;
3117
    lex->query_tables=all_tables;
unknown's avatar
unknown committed
3118 3119 3120
    break;
  }
  case SQLCOM_UPDATE:
3121 3122
  {
    ha_rows found= 0, updated= 0;
unknown's avatar
VIEW  
unknown committed
3123 3124
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (update_precheck(thd, all_tables))
unknown's avatar
unknown committed
3125
      break;
3126 3127
    DBUG_ASSERT(select_lex->offset_limit == 0);
    unit->set_limit(select_lex);
3128
    MYSQL_UPDATE_START(thd->query());
unknown's avatar
unknown committed
3129 3130 3131 3132 3133
    res= (up_result= mysql_update(thd, all_tables,
                                  select_lex->item_list,
                                  lex->value_list,
                                  select_lex->where,
                                  select_lex->order_list.elements,
3134
                                  select_lex->order_list.first,
unknown's avatar
unknown committed
3135
                                  unit->select_limit_cnt,
3136 3137 3138
                                  lex->duplicates, lex->ignore,
                                  &found, &updated));
    MYSQL_UPDATE_DONE(res, found, updated);
3139
    /* mysql_update return 2 if we need to switch to multi-update */
unknown's avatar
unknown committed
3140
    if (up_result != 2)
3141
      break;
unknown's avatar
unknown committed
3142
    /* Fall through */
3143
  }
3144
  case SQLCOM_UPDATE_MULTI:
unknown's avatar
unknown committed
3145 3146 3147
  {
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    /* if we switched from normal update, rights are checked */
unknown's avatar
unknown committed
3148
    if (up_result != 2)
3149
    {
unknown's avatar
unknown committed
3150 3151 3152 3153 3154
      if ((res= multi_update_precheck(thd, all_tables)))
        break;
    }
    else
      res= 0;
unknown's avatar
unknown committed
3155

3156
    res= mysql_multi_update_prepare(thd);
unknown's avatar
unknown committed
3157

3158
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
3159
    /* Check slave filtering rules */
3160
    if (unlikely(thd->slave_thread && !have_table_map_for_update))
unknown's avatar
unknown committed
3161
    {
3162 3163
      if (all_tables_not_ok(thd, all_tables))
      {
3164 3165 3166 3167 3168
        if (res!= 0)
        {
          res= 0;             /* don't care of prev failure  */
          thd->clear_error(); /* filters are of highest prior */
        }
3169 3170 3171 3172
        /* we warn the slave SQL thread */
        my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
        break;
      }
3173 3174
      if (res)
        break;
unknown's avatar
unknown committed
3175
    }
3176 3177
    else
    {
3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190
#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
3191 3192
    {
      multi_update *result_obj;
3193
      MYSQL_MULTI_UPDATE_START(thd->query());
3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215
      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
3216
    break;
unknown's avatar
unknown committed
3217
  }
unknown's avatar
unknown committed
3218
  case SQLCOM_REPLACE:
3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242
#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);
3243
        (void) mysql_bin_log.write(&ev);        /* error is ignored */
3244 3245 3246 3247 3248
        mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
      }
      DBUG_PRINT("debug", ("Just after generate_incident()"));
    }
#endif
3249 3250
  case SQLCOM_INSERT:
  {
unknown's avatar
VIEW  
unknown committed
3251
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
3252
    if ((res= insert_precheck(thd, all_tables)))
unknown's avatar
unknown committed
3253
      break;
3254

3255
    MYSQL_INSERT_START(thd->query());
unknown's avatar
VIEW  
unknown committed
3256
    res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
unknown's avatar
unknown committed
3257
		      lex->update_list, lex->value_list,
3258
                      lex->duplicates, lex->ignore);
3259
    MYSQL_INSERT_DONE(res, (ulong) thd->get_row_count_func());
3260 3261 3262 3263 3264 3265
    /*
      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
3266
    if (first_table->view && !first_table->contain_auto_increment)
3267 3268
      thd->first_successful_insert_id_in_cur_stmt=
        thd->first_successful_insert_id_in_prev_stmt;
3269

unknown's avatar
unknown committed
3270
    break;
3271
  }
unknown's avatar
unknown committed
3272 3273 3274
  case SQLCOM_REPLACE_SELECT:
  case SQLCOM_INSERT_SELECT:
  {
unknown's avatar
unknown committed
3275
    select_result *sel_result;
unknown's avatar
VIEW  
unknown committed
3276
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
3277
    if ((res= insert_precheck(thd, all_tables)))
3278
      break;
unknown's avatar
unknown committed
3279

3280
    /* Fix lock for first table */
unknown's avatar
VIEW  
unknown committed
3281 3282
    if (first_table->lock_type == TL_WRITE_DELAYED)
      first_table->lock_type= TL_WRITE;
3283

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

3287
    unit->set_limit(select_lex);
3288

3289
    if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
3290
    {
3291
      MYSQL_INSERT_SELECT_START(thd->query());
3292
      /* Skip first table, which is the table we are inserting in */
unknown's avatar
unknown committed
3293
      TABLE_LIST *second_table= first_table->next_local;
3294
      select_lex->table_list.first= second_table;
3295 3296
      select_lex->context.table_list= 
        select_lex->context.first_name_resolution_table= second_table;
3297
      res= mysql_insert_select_prepare(thd);
unknown's avatar
unknown committed
3298 3299 3300 3301 3302 3303 3304
      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)))
3305
      {
unknown's avatar
unknown committed
3306
	res= handle_select(thd, lex, sel_result, OPTION_SETUP_TABLES_DONE);
3307 3308 3309 3310 3311 3312
        /*
          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.
        */
3313
        if (!res && first_table->lock_type ==  TL_WRITE_CONCURRENT_INSERT &&
3314 3315
            thd->lock)
        {
3316 3317 3318
          /* INSERT ... SELECT should invalidate only the very first table */
          TABLE_LIST *save_table= first_table->next_local;
          first_table->next_local= 0;
3319
          query_cache_invalidate3(thd, first_table, 1);
3320
          first_table->next_local= save_table;
3321
        }
unknown's avatar
unknown committed
3322
        delete sel_result;
3323
      }
3324
      /* revert changes for SP */
3325
      MYSQL_INSERT_SELECT_DONE(res, (ulong) thd->get_row_count_func());
3326
      select_lex->table_list.first= first_table;
3327
    }
3328 3329 3330 3331 3332 3333
    /*
      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
3334
    if (first_table->view && !first_table->contain_auto_increment)
3335 3336
      thd->first_successful_insert_id_in_cur_stmt=
        thd->first_successful_insert_id_in_prev_stmt;
3337

unknown's avatar
unknown committed
3338 3339
    break;
  }
3340
  case SQLCOM_TRUNCATE:
unknown's avatar
VIEW  
unknown committed
3341
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
3342
    if (check_one_table_access(thd, DROP_ACL, all_tables))
unknown's avatar
unknown committed
3343
      goto error;
3344 3345 3346 3347
    /*
      Don't allow this within a transaction because we want to use
      re-generate table
    */
3348
    if (thd->in_active_multi_stmt_transaction())
3349
    {
unknown's avatar
unknown committed
3350 3351
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
3352 3353
      goto error;
    }
3354 3355
    if (! (res= mysql_truncate_table(thd, first_table)))
      my_ok(thd);
3356
    break;
unknown's avatar
unknown committed
3357
  case SQLCOM_DELETE:
unknown's avatar
unknown committed
3358
  {
unknown's avatar
VIEW  
unknown committed
3359 3360
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if ((res= delete_precheck(thd, all_tables)))
unknown's avatar
unknown committed
3361
      break;
3362 3363
    DBUG_ASSERT(select_lex->offset_limit == 0);
    unit->set_limit(select_lex);
3364

3365
    MYSQL_DELETE_START(thd->query());
unknown's avatar
VIEW  
unknown committed
3366
    res = mysql_delete(thd, all_tables, select_lex->where,
3367
                       &select_lex->order_list,
3368
                       unit->select_limit_cnt, select_lex->options);
3369
    MYSQL_DELETE_DONE(res, (ulong) thd->get_row_count_func());
unknown's avatar
unknown committed
3370 3371
    break;
  }
unknown's avatar
unknown committed
3372
  case SQLCOM_DELETE_MULTI:
unknown's avatar
unknown committed
3373
  {
unknown's avatar
VIEW  
unknown committed
3374
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3375
    TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
unknown's avatar
unknown committed
3376
    multi_delete *del_result;
unknown's avatar
unknown committed
3377

3378
    if ((res= multi_delete_precheck(thd, all_tables)))
3379
      break;
unknown's avatar
unknown committed
3380

unknown's avatar
unknown committed
3381
    /* condition will be TRUE on SP re-excuting */
3382 3383
    if (select_lex->item_list.elements != 0)
      select_lex->item_list.empty();
unknown's avatar
unknown committed
3384
    if (add_item_to_list(thd, new Item_null()))
unknown's avatar
unknown committed
3385
      goto error;
3386

3387
    thd_proc_info(thd, "init");
3388
    if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
unknown's avatar
VIEW  
unknown committed
3389 3390
      break;

3391
    MYSQL_MULTI_DELETE_START(thd->query());
unknown's avatar
VIEW  
unknown committed
3392
    if ((res= mysql_multi_delete_prepare(thd)))
3393 3394
    {
      MYSQL_MULTI_DELETE_DONE(1, 0);
unknown's avatar
unknown committed
3395
      goto error;
3396
    }
3397

unknown's avatar
unknown committed
3398 3399
    if (!thd->is_fatal_error &&
        (del_result= new multi_delete(aux_tables, lex->table_count)))
unknown's avatar
unknown committed
3400
    {
3401 3402 3403
      res= mysql_select(thd, &select_lex->ref_pointer_array,
			select_lex->get_table_list(),
			select_lex->with_wild,
unknown's avatar
unknown committed
3404
			select_lex->item_list,
unknown's avatar
unknown committed
3405
			select_lex->where,
3406
			0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
unknown's avatar
unknown committed
3407
			(ORDER *)NULL,
3408
			(select_lex->options | thd->variables.option_bits |
3409
			SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
3410
                        OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT,
unknown's avatar
unknown committed
3411
			del_result, unit, select_lex);
3412
      res|= thd->is_error();
3413
      MYSQL_MULTI_DELETE_DONE(res, del_result->num_deleted());
3414
      if (res)
3415
        del_result->abort();
unknown's avatar
unknown committed
3416
      delete del_result;
unknown's avatar
unknown committed
3417 3418
    }
    else
3419
    {
3420
      res= TRUE;                                // Error
3421 3422
      MYSQL_MULTI_DELETE_DONE(1, 0);
    }
unknown's avatar
unknown committed
3423 3424
    break;
  }
unknown's avatar
unknown committed
3425
  case SQLCOM_DROP_TABLE:
unknown's avatar
unknown committed
3426
  {
unknown's avatar
VIEW  
unknown committed
3427
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3428 3429
    if (!lex->drop_temporary)
    {
3430
      if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE))
3431 3432
	goto error;				/* purecov: inspected */
    }
unknown's avatar
unknown committed
3433
    else
unknown's avatar
unknown committed
3434
    {
unknown's avatar
unknown committed
3435
      /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
3436
      thd->variables.option_bits|= OPTION_KEEP_LOG;
unknown's avatar
unknown committed
3437
    }
3438
    /* DDL and binlog write order protected by LOCK_open */
unknown's avatar
VIEW  
unknown committed
3439 3440
    res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
			lex->drop_temporary);
unknown's avatar
unknown committed
3441 3442
  }
  break;
unknown's avatar
unknown committed
3443
  case SQLCOM_SHOW_PROCESSLIST:
3444 3445
    if (!thd->security_ctx->priv_user[0] &&
        check_global_access(thd,PROCESS_ACL))
unknown's avatar
unknown committed
3446
      break;
unknown's avatar
SCRUM:  
unknown committed
3447
    mysqld_list_processes(thd,
3448 3449 3450 3451
			  (thd->security_ctx->master_access & PROCESS_ACL ?
                           NullS :
                           thd->security_ctx->priv_user),
                          lex->verbose);
unknown's avatar
unknown committed
3452
    break;
unknown's avatar
unknown committed
3453 3454 3455
  case SQLCOM_SHOW_AUTHORS:
    res= mysqld_show_authors(thd);
    break;
3456 3457 3458
  case SQLCOM_SHOW_CONTRIBUTORS:
    res= mysqld_show_contributors(thd);
    break;
unknown's avatar
unknown committed
3459 3460 3461
  case SQLCOM_SHOW_PRIVILEGES:
    res= mysqld_show_privileges(thd);
    break;
unknown's avatar
unknown committed
3462
  case SQLCOM_SHOW_ENGINE_LOGS:
unknown's avatar
unknown committed
3463
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
3464 3465
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0));	/* purecov: inspected */
3466
    goto error;
unknown's avatar
unknown committed
3467 3468
#else
    {
Marc Alff's avatar
Marc Alff committed
3469
      if (check_access(thd, FILE_ACL, any_db, NULL, NULL, 0, 0))
unknown's avatar
unknown committed
3470
	goto error;
unknown's avatar
unknown committed
3471
      res= ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_LOGS);
unknown's avatar
unknown committed
3472 3473
      break;
    }
unknown's avatar
unknown committed
3474 3475
#endif
  case SQLCOM_CHANGE_DB:
3476 3477 3478 3479
  {
    LEX_STRING db_str= { (char *) select_lex->db, strlen(select_lex->db) };

    if (!mysql_change_db(thd, &db_str, FALSE))
3480
      my_ok(thd);
3481

unknown's avatar
unknown committed
3482
    break;
3483
  }
3484

unknown's avatar
unknown committed
3485 3486
  case SQLCOM_LOAD:
  {
unknown's avatar
VIEW  
unknown committed
3487
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
3488
    uint privilege= (lex->duplicates == DUP_REPLACE ?
unknown's avatar
unknown committed
3489 3490
		     INSERT_ACL | DELETE_ACL : INSERT_ACL) |
                    (lex->local_file ? 0 : FILE_ACL);
3491

unknown's avatar
unknown committed
3492
    if (lex->local_file)
unknown's avatar
unknown committed
3493
    {
3494
      if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) ||
3495
          !opt_local_infile)
3496
      {
unknown's avatar
unknown committed
3497
	my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), MYF(0));
3498 3499
	goto error;
      }
unknown's avatar
unknown committed
3500
    }
unknown's avatar
unknown committed
3501 3502 3503 3504

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

unknown's avatar
VIEW  
unknown committed
3505
    res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
unknown's avatar
unknown committed
3506
                    lex->update_list, lex->value_list, lex->duplicates,
3507
                    lex->ignore, (bool) lex->local_file);
unknown's avatar
unknown committed
3508 3509
    break;
  }
3510

unknown's avatar
unknown committed
3511
  case SQLCOM_SET_OPTION:
3512 3513
  {
    List<set_var_base> *lex_var_list= &lex->var_list;
3514

3515
    if ((check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
3516
         || open_and_lock_tables(thd, all_tables, TRUE, 0)))
unknown's avatar
unknown committed
3517
      goto error;
3518 3519 3520 3521 3522 3523 3524
    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;
3525
      my_ok(thd);
3526
    }
3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538
    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
3539
    break;
3540
  }
unknown's avatar
unknown committed
3541

unknown's avatar
unknown committed
3542
  case SQLCOM_UNLOCK_TABLES:
3543 3544 3545 3546 3547 3548
    /*
      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
3549
    thd->locked_tables_list.unlock_locked_tables(thd);
3550
    if (thd->variables.option_bits & OPTION_TABLE_LOCK)
unknown's avatar
unknown committed
3551
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
3552
      trans_commit_implicit(thd);
3553
      thd->mdl_context.release_transactional_locks();
3554
      thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
unknown's avatar
unknown committed
3555
    }
3556 3557
    if (thd->global_read_lock.is_acquired())
      thd->global_read_lock.unlock_global_read_lock(thd);
3558
    my_ok(thd);
unknown's avatar
unknown committed
3559 3560
    break;
  case SQLCOM_LOCK_TABLES:
Konstantin Osipov's avatar
Konstantin Osipov committed
3561
    thd->locked_tables_list.unlock_locked_tables(thd);
3562
    /* we must end the trasaction first, regardless of anything */
Konstantin Osipov's avatar
Konstantin Osipov committed
3563
    if (trans_commit_implicit(thd))
unknown's avatar
unknown committed
3564
      goto error;
3565
    /* release transactional metadata locks. */
3566
    thd->mdl_context.release_transactional_locks();
3567
    if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
3568
                           FALSE, UINT_MAX, FALSE))
3569
      goto error;
3570
    if (lex->protect_against_global_read_lock &&
3571
        thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
3572
      goto error;
Konstantin Osipov's avatar
Konstantin Osipov committed
3573

3574
    thd->variables.option_bits|= OPTION_TABLE_LOCK;
Konstantin Osipov's avatar
Konstantin Osipov committed
3575
    thd->in_lock_tables=1;
unknown's avatar
VIEW  
unknown committed
3576

Konstantin Osipov's avatar
Konstantin Osipov committed
3577 3578 3579
    {
      Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;

3580
      res= (open_and_lock_tables(thd, all_tables, FALSE, 0,
3581
                                 &lock_tables_prelocking_strategy) ||
Konstantin Osipov's avatar
Konstantin Osipov committed
3582 3583
            thd->locked_tables_list.init_locked_tables(thd));
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
3584 3585 3586 3587

    thd->in_lock_tables= 0;

    if (res)
3588
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
3589
      /*
3590 3591 3592 3593
        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
3594 3595
      trans_rollback_stmt(thd);
      trans_commit_implicit(thd);
3596 3597 3598 3599 3600 3601
      /*
        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);
3602
      thd->mdl_context.release_transactional_locks();
3603
      thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
3604
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
3605 3606 3607 3608 3609 3610 3611 3612
    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
3613 3614
    break;
  case SQLCOM_CREATE_DB:
3615
  {
3616 3617 3618 3619 3620 3621
    /*
      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
3622
    char *alias;
3623 3624
    if (!(alias=thd->strmake(lex->name.str, lex->name.length)) ||
        check_db_name(&lex->name))
unknown's avatar
unknown committed
3625
    {
3626
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
unknown's avatar
unknown committed
3627 3628
      break;
    }
3629 3630 3631
    /*
      If in a slave thread :
      CREATE DATABASE DB was certainly not preceded by USE DB.
3632
      For that reason, db_ok() in sql/slave.cc did not check the
3633 3634 3635
      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.
    */
3636
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
3637
    if (thd->slave_thread && 
3638 3639
	(!rpl_filter->db_ok(lex->name.str) ||
	 !rpl_filter->db_ok_with_wild_table(lex->name.str)))
unknown's avatar
Fix for  
unknown committed
3640
    {
unknown's avatar
unknown committed
3641
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
3642
      break;
unknown's avatar
Fix for  
unknown committed
3643
    }
3644
#endif
Marc Alff's avatar
Marc Alff committed
3645
    if (check_access(thd, CREATE_ACL, lex->name.str, NULL, NULL, 1, 0))
3646
      break;
3647 3648 3649 3650 3651 3652
    if (thd->locked_tables_mode)
    {
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
      goto error;
    }
3653
    res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias :
3654
                              lex->name.str), &create_info, 0);
3655 3656
    break;
  }
unknown's avatar
unknown committed
3657
  case SQLCOM_DROP_DB:
3658
  {
3659
    if (check_db_name(&lex->name))
unknown's avatar
unknown committed
3660
    {
3661
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
unknown's avatar
unknown committed
3662 3663
      break;
    }
3664 3665 3666 3667 3668 3669 3670
    /*
      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.
    */
3671
#ifdef HAVE_REPLICATION
3672
    if (thd->slave_thread && 
3673 3674
	(!rpl_filter->db_ok(lex->name.str) ||
	 !rpl_filter->db_ok_with_wild_table(lex->name.str)))
unknown's avatar
Fix for  
unknown committed
3675
    {
unknown's avatar
unknown committed
3676
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
3677
      break;
unknown's avatar
Fix for  
unknown committed
3678
    }
3679
#endif
Marc Alff's avatar
Marc Alff committed
3680
    if (check_access(thd, DROP_ACL, lex->name.str, NULL, NULL, 1, 0))
3681
      break;
3682
    if (thd->locked_tables_mode)
3683
    {
unknown's avatar
unknown committed
3684 3685
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
3686 3687
      goto error;
    }
3688
    res= mysql_rm_db(thd, lex->name.str, lex->drop_if_exists, 0);
3689 3690
    break;
  }
3691
  case SQLCOM_ALTER_DB_UPGRADE:
unknown's avatar
unknown committed
3692
  {
3693
    LEX_STRING *db= & lex->name;
unknown's avatar
unknown committed
3694 3695
#ifdef HAVE_REPLICATION
    if (thd->slave_thread && 
3696 3697
       (!rpl_filter->db_ok(db->str) ||
        !rpl_filter->db_ok_with_wild_table(db->str)))
unknown's avatar
unknown committed
3698 3699 3700 3701 3702 3703
    {
      res= 1;
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
      break;
    }
#endif
3704
    if (check_db_name(db))
3705
    {
3706
      my_error(ER_WRONG_DB_NAME, MYF(0), db->str);
3707 3708
      break;
    }
Marc Alff's avatar
Marc Alff committed
3709 3710 3711
    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
3712 3713 3714 3715
    {
      res= 1;
      break;
    }
3716
    if (thd->locked_tables_mode)
unknown's avatar
unknown committed
3717 3718 3719 3720 3721 3722
    {
      res= 1;
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
      goto error;
    }
3723 3724

    res= mysql_upgrade_db(thd, db);
unknown's avatar
unknown committed
3725
    if (!res)
3726
      my_ok(thd);
unknown's avatar
unknown committed
3727 3728
    break;
  }
3729 3730
  case SQLCOM_ALTER_DB:
  {
3731
    LEX_STRING *db= &lex->name;
3732
    HA_CREATE_INFO create_info(lex->create_info);
3733
    if (check_db_name(db))
3734
    {
3735
      my_error(ER_WRONG_DB_NAME, MYF(0), db->str);
3736 3737
      break;
    }
unknown's avatar
unknown committed
3738 3739 3740
    /*
      If in a slave thread :
      ALTER DATABASE DB may not be preceded by USE DB.
3741
      For that reason, maybe db_ok() in sql/slave.cc did not check the
unknown's avatar
unknown committed
3742 3743 3744 3745
      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
3746
    if (thd->slave_thread &&
3747 3748
	(!rpl_filter->db_ok(db->str) ||
	 !rpl_filter->db_ok_with_wild_table(db->str)))
unknown's avatar
unknown committed
3749
    {
unknown's avatar
unknown committed
3750
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
unknown's avatar
unknown committed
3751 3752 3753
      break;
    }
#endif
Marc Alff's avatar
Marc Alff committed
3754
    if (check_access(thd, ALTER_ACL, db->str, NULL, NULL, 1, 0))
3755
      break;
3756
    if (thd->locked_tables_mode)
3757
    {
unknown's avatar
unknown committed
3758 3759
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
3760 3761
      goto error;
    }
3762
    res= mysql_alter_db(thd, db->str, &create_info);
3763 3764
    break;
  }
unknown's avatar
unknown committed
3765 3766
  case SQLCOM_SHOW_CREATE_DB:
  {
unknown's avatar
unknown committed
3767 3768
    DBUG_EXECUTE_IF("4x_server_emul",
                    my_error(ER_UNKNOWN_ERROR, MYF(0)); goto error;);
3769
    if (check_db_name(&lex->name))
unknown's avatar
unknown committed
3770
    {
3771
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
unknown's avatar
unknown committed
3772 3773
      break;
    }
3774
    res= mysqld_show_create_db(thd, lex->name.str, &lex->create_info);
unknown's avatar
unknown committed
3775 3776
    break;
  }
unknown's avatar
unknown committed
3777 3778
  case SQLCOM_CREATE_EVENT:
  case SQLCOM_ALTER_EVENT:
3779
  #ifdef HAVE_EVENT_SCHEDULER
3780
  do
unknown's avatar
unknown committed
3781
  {
unknown's avatar
unknown committed
3782
    DBUG_ASSERT(lex->event_parse_data);
unknown's avatar
unknown committed
3783 3784 3785 3786 3787 3788
    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;
    }
unknown's avatar
unknown committed
3789 3790 3791 3792 3793

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

unknown's avatar
unknown committed
3794 3795
    switch (lex->sql_command) {
    case SQLCOM_CREATE_EVENT:
3796 3797 3798 3799
    {
      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
3800
      break;
3801
    }
unknown's avatar
unknown committed
3802
    case SQLCOM_ALTER_EVENT:
3803 3804 3805
      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
3806
      break;
3807 3808
    default:
      DBUG_ASSERT(0);
unknown's avatar
unknown committed
3809
    }
3810
    DBUG_PRINT("info",("DDL error code=%d", res));
unknown's avatar
unknown committed
3811
    if (!res)
3812
      my_ok(thd);
unknown's avatar
unknown committed
3813

3814 3815 3816 3817 3818 3819
  } 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
3820
  }
3821 3822
  /* lex->unit.cleanup() is called outside, no need to call it here */
  break;
unknown's avatar
unknown committed
3823
  case SQLCOM_SHOW_CREATE_EVENT:
3824 3825 3826 3827 3828 3829 3830
    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)))
3831
      my_ok(thd);
3832
    break;
3833 3834 3835 3836
#else
    my_error(ER_NOT_SUPPORTED_YET,MYF(0),"embedded server");
    break;
#endif
unknown's avatar
unknown committed
3837
  case SQLCOM_CREATE_FUNCTION:                  // UDF function
unknown's avatar
unknown committed
3838
  {
Marc Alff's avatar
Marc Alff committed
3839
    if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 0))
unknown's avatar
unknown committed
3840
      break;
unknown's avatar
unknown committed
3841
#ifdef HAVE_DLOPEN
3842
    if (!(res = mysql_create_function(thd, &lex->udf)))
3843
      my_ok(thd);
unknown's avatar
unknown committed
3844
#else
unknown's avatar
unknown committed
3845
    my_error(ER_CANT_OPEN_LIBRARY, MYF(0), lex->udf.dl, 0, "feature disabled");
unknown's avatar
unknown committed
3846
    res= TRUE;
unknown's avatar
unknown committed
3847 3848
#endif
    break;
unknown's avatar
unknown committed
3849
  }
unknown's avatar
SCRUM:  
unknown committed
3850
#ifndef NO_EMBEDDED_ACCESS_CHECKS
3851 3852
  case SQLCOM_CREATE_USER:
  {
Marc Alff's avatar
Marc Alff committed
3853
    if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 1) &&
unknown's avatar
unknown committed
3854
        check_global_access(thd,CREATE_USER_ACL))
3855
      break;
3856
    /* Conditionally writes to binlog */
3857
    if (!(res= mysql_create_user(thd, lex->users_list)))
3858
      my_ok(thd);
3859 3860
    break;
  }
3861 3862
  case SQLCOM_DROP_USER:
  {
Marc Alff's avatar
Marc Alff committed
3863
    if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 1) &&
unknown's avatar
unknown committed
3864
        check_global_access(thd,CREATE_USER_ACL))
3865
      break;
3866
    /* Conditionally writes to binlog */
3867
    if (!(res= mysql_drop_user(thd, lex->users_list)))
3868
      my_ok(thd);
3869 3870 3871 3872
    break;
  }
  case SQLCOM_RENAME_USER:
  {
Marc Alff's avatar
Marc Alff committed
3873
    if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1) &&
unknown's avatar
unknown committed
3874
        check_global_access(thd,CREATE_USER_ACL))
3875
      break;
3876
    /* Conditionally writes to binlog */
3877
    if (!(res= mysql_rename_user(thd, lex->users_list)))
3878
      my_ok(thd);
3879 3880 3881 3882
    break;
  }
  case SQLCOM_REVOKE_ALL:
  {
Marc Alff's avatar
Marc Alff committed
3883
    if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1) &&
unknown's avatar
unknown committed
3884
        check_global_access(thd,CREATE_USER_ACL))
3885
      break;
3886
    /* Conditionally writes to binlog */
3887
    if (!(res = mysql_revoke_all(thd, lex->users_list)))
3888
      my_ok(thd);
3889 3890
    break;
  }
3891 3892 3893 3894
  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
3895 3896 3897 3898
                     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))
3899 3900
      goto error;

3901
    if (thd->security_ctx->user)              // If not replication
unknown's avatar
unknown committed
3902
    {
3903
      LEX_USER *user, *tmp_user;
3904

unknown's avatar
unknown committed
3905
      List_iterator <LEX_USER> user_list(lex->users_list);
3906
      while ((tmp_user= user_list++))
unknown's avatar
unknown committed
3907
      {
3908 3909
        if (!(user= get_current_user(thd, tmp_user)))
          goto error;
unknown's avatar
unknown committed
3910 3911 3912 3913 3914 3915 3916 3917
        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);
3918
        if (strcmp(thd->security_ctx->user, user->user.str) ||
unknown's avatar
unknown committed
3919
            my_strcasecmp(system_charset_info,
3920
                          user->host.str, thd->security_ctx->host_or_ip))
unknown's avatar
unknown committed
3921 3922
        {
          // TODO: use check_change_password()
3923 3924
          if (is_acl_user(user->host.str, user->user.str) &&
              user->password.str &&
Marc Alff's avatar
Marc Alff committed
3925
              check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1))
unknown's avatar
unknown committed
3926 3927 3928 3929 3930 3931
          {
            my_message(ER_PASSWORD_NOT_ALLOWED,
                       ER(ER_PASSWORD_NOT_ALLOWED), MYF(0));
            goto error;
          }
        }
unknown's avatar
SCRUM  
unknown committed
3932 3933
      }
    }
unknown's avatar
VIEW  
unknown committed
3934
    if (first_table)
3935
    {
3936 3937
      if (lex->type == TYPE_ENUM_PROCEDURE ||
          lex->type == TYPE_ENUM_FUNCTION)
3938 3939 3940 3941
      {
        uint grants= lex->all_privileges 
		   ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL)
		   : lex->grant;
3942
        if (check_grant_routine(thd, grants | GRANT_ACL, all_tables,
3943
                                lex->type == TYPE_ENUM_PROCEDURE, 0))
3944
	  goto error;
3945
        /* Conditionally writes to binlog */
3946 3947 3948
        res= mysql_routine_grant(thd, all_tables,
                                 lex->type == TYPE_ENUM_PROCEDURE, 
                                 lex->users_list, grants,
3949 3950 3951
                                 lex->sql_command == SQLCOM_REVOKE, TRUE);
        if (!res)
          my_ok(thd);
3952 3953 3954
      }
      else
      {
3955
	if (check_grant(thd,(lex->grant | lex->grant_tot_col | GRANT_ACL),
3956
                        all_tables, FALSE, UINT_MAX, FALSE))
3957
	  goto error;
3958
        /* Conditionally writes to binlog */
3959 3960 3961 3962
        res= mysql_table_grant(thd, all_tables, lex->users_list,
			       lex->columns, lex->grant,
			       lex->sql_command == SQLCOM_REVOKE);
      }
3963 3964 3965
    }
    else
    {
3966
      if (lex->columns.elements || lex->type)
3967
      {
unknown's avatar
unknown committed
3968 3969
	my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
                   MYF(0));
unknown's avatar
unknown committed
3970
        goto error;
3971 3972
      }
      else
3973
	/* Conditionally writes to binlog */
3974 3975 3976 3977
	res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
			  lex->sql_command == SQLCOM_REVOKE);
      if (!res)
      {
3978
	if (lex->sql_command == SQLCOM_GRANT)
unknown's avatar
unknown committed
3979
	{
unknown's avatar
unknown committed
3980
	  List_iterator <LEX_USER> str_list(lex->users_list);
3981 3982 3983 3984 3985
	  LEX_USER *user, *tmp_user;
	  while ((tmp_user=str_list++))
          {
            if (!(user= get_current_user(thd, tmp_user)))
              goto error;
unknown's avatar
unknown committed
3986
	    reset_mqh(user, 0);
3987
          }
unknown's avatar
unknown committed
3988
	}
3989 3990 3991 3992
      }
    }
    break;
  }
unknown's avatar
SCRUM  
unknown committed
3993
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
unknown's avatar
unknown committed
3994
  case SQLCOM_RESET:
3995 3996 3997
    /*
      RESET commands are never written to the binary log, so we have to
      initialize this variable because RESET shares the same code as FLUSH
3998 3999 4000 4001
    */
    lex->no_write_to_binlog= 1;
  case SQLCOM_FLUSH:
  {
unknown's avatar
unknown committed
4002
    bool write_to_binlog;
4003

unknown's avatar
unknown committed
4004
    if (check_global_access(thd,RELOAD_ACL))
unknown's avatar
unknown committed
4005
      goto error;
4006

4007 4008 4009 4010 4011 4012 4013 4014
    if (first_table && lex->type & REFRESH_READ_LOCK)
    {
      if (flush_tables_with_read_lock(thd, all_tables))
        goto error;
      my_ok(thd);
      break;
    }

4015 4016 4017 4018
    /*
      reload_acl_and_cache() will tell us if we are allowed to write to the
      binlog or not.
    */
unknown's avatar
unknown committed
4019
    if (!reload_acl_and_cache(thd, lex->type, first_table, &write_to_binlog))
4020 4021 4022 4023 4024
    {
      /*
        We WANT to write and we CAN write.
        ! we write after unlocking the table.
      */
unknown's avatar
unknown committed
4025 4026 4027
      /*
        Presumably, RESET and binlog writing doesn't require synchronization
      */
4028 4029
      if (!lex->no_write_to_binlog && write_to_binlog)
      {
Davi Arnaut's avatar
Davi Arnaut committed
4030
        if ((res= write_bin_log(thd, FALSE, thd->query(), thd->query_length())))
4031
          break;
4032
      }
4033
      my_ok(thd);
4034 4035
    } 
    
unknown's avatar
unknown committed
4036
    break;
4037
  }
unknown's avatar
unknown committed
4038
  case SQLCOM_KILL:
4039 4040 4041
  {
    Item *it= (Item *)lex->value_list.head();

unknown's avatar
unknown committed
4042 4043 4044 4045 4046 4047 4048
    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;
    }

4049
    if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
4050 4051 4052 4053 4054
    {
      my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
		 MYF(0));
      goto error;
    }
4055
    sql_kill(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY);
unknown's avatar
unknown committed
4056
    break;
4057
  }
unknown's avatar
SCRUM:  
unknown committed
4058
#ifndef NO_EMBEDDED_ACCESS_CHECKS
unknown's avatar
unknown committed
4059
  case SQLCOM_SHOW_GRANTS:
4060 4061 4062 4063
  {
    LEX_USER *grant_user= get_current_user(thd, lex->grant_user);
    if (!grant_user)
      goto error;
4064
    if ((thd->security_ctx->priv_user &&
4065
	 !strcmp(thd->security_ctx->priv_user, grant_user->user.str)) ||
Marc Alff's avatar
Marc Alff committed
4066
        !check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 0))
unknown's avatar
unknown committed
4067
    {
4068
      res = mysql_show_grants(thd, grant_user);
unknown's avatar
unknown committed
4069 4070
    }
    break;
4071
  }
unknown's avatar
SCRUM:  
unknown committed
4072
#endif
4073
  case SQLCOM_HA_OPEN:
unknown's avatar
VIEW  
unknown committed
4074
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
4075
    if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE))
4076
      goto error;
unknown's avatar
unknown committed
4077
    res= mysql_ha_open(thd, first_table, 0);
4078 4079
    break;
  case SQLCOM_HA_CLOSE:
unknown's avatar
VIEW  
unknown committed
4080 4081
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    res= mysql_ha_close(thd, first_table);
4082 4083
    break;
  case SQLCOM_HA_READ:
unknown's avatar
VIEW  
unknown committed
4084
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
4085 4086 4087 4088 4089
    /*
      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.
    */
4090
    unit->set_limit(select_lex);
unknown's avatar
unknown committed
4091
    res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->ident.str,
unknown's avatar
VIEW  
unknown committed
4092
                       lex->insert_list, lex->ha_rkey_mode, select_lex->where,
4093
                       unit->select_limit_cnt, unit->offset_limit_cnt);
4094 4095
    break;

unknown's avatar
unknown committed
4096
  case SQLCOM_BEGIN:
Konstantin Osipov's avatar
Konstantin Osipov committed
4097
    if (trans_begin(thd, lex->start_transaction_opt))
unknown's avatar
unknown committed
4098
      goto error;
4099
    my_ok(thd);
unknown's avatar
unknown committed
4100 4101
    break;
  case SQLCOM_COMMIT:
4102
  {
4103 4104
    DBUG_ASSERT(thd->lock == NULL ||
                thd->locked_tables_mode == LTM_LOCK_TABLES);
4105 4106 4107 4108 4109 4110
    bool tx_chain= (lex->tx_chain == TVL_YES ||
                    (thd->variables.completion_type == 1 &&
                     lex->tx_chain != TVL_NO));
    bool tx_release= (lex->tx_release == TVL_YES ||
                      (thd->variables.completion_type == 2 &&
                       lex->tx_release != TVL_NO));
Konstantin Osipov's avatar
Konstantin Osipov committed
4111 4112
    if (trans_commit(thd))
      goto error;
4113
    thd->mdl_context.release_transactional_locks();
Konstantin Osipov's avatar
Konstantin Osipov committed
4114
    /* Begin transaction with the same isolation level. */
4115 4116 4117
    if (tx_chain)
    {
      if (trans_begin(thd))
unknown's avatar
unknown committed
4118
      goto error;
4119 4120 4121 4122 4123 4124
    }
    else
    {
      /* Reset the isolation level if no chaining transaction. */
      thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
4125
    /* Disconnect the current client connection. */
4126
    if (tx_release)
Konstantin Osipov's avatar
Konstantin Osipov committed
4127
      thd->killed= THD::KILL_CONNECTION;
4128
    my_ok(thd);
unknown's avatar
unknown committed
4129
    break;
4130
  }
unknown's avatar
unknown committed
4131
  case SQLCOM_ROLLBACK:
4132
  {
4133 4134
    DBUG_ASSERT(thd->lock == NULL ||
                thd->locked_tables_mode == LTM_LOCK_TABLES);
4135 4136 4137 4138 4139 4140
    bool tx_chain= (lex->tx_chain == TVL_YES ||
                    (thd->variables.completion_type == 1 &&
                     lex->tx_chain != TVL_NO));
    bool tx_release= (lex->tx_release == TVL_YES ||
                      (thd->variables.completion_type == 2 &&
                       lex->tx_release != TVL_NO));
Konstantin Osipov's avatar
Konstantin Osipov committed
4141
    if (trans_rollback(thd))
unknown's avatar
WL#1967  
unknown committed
4142
      goto error;
4143
    thd->mdl_context.release_transactional_locks();
Konstantin Osipov's avatar
Konstantin Osipov committed
4144
    /* Begin transaction with the same isolation level. */
4145 4146 4147 4148 4149 4150 4151 4152 4153 4154
    if (tx_chain)
    {
      if (trans_begin(thd))
        goto error;
    }
    else
    {
      /* Reset the isolation level if no chaining transaction. */
      thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
4155
    /* Disconnect the current client connection. */
4156
    if (tx_release)
Konstantin Osipov's avatar
Konstantin Osipov committed
4157
      thd->killed= THD::KILL_CONNECTION;
4158
    my_ok(thd);
unknown's avatar
unknown committed
4159
    break;
4160
  }
unknown's avatar
unknown committed
4161
  case SQLCOM_RELEASE_SAVEPOINT:
Konstantin Osipov's avatar
Konstantin Osipov committed
4162 4163 4164
    if (trans_release_savepoint(thd, lex->ident))
      goto error;
    my_ok(thd);
unknown's avatar
unknown committed
4165
    break;
unknown's avatar
unknown committed
4166
  case SQLCOM_ROLLBACK_TO_SAVEPOINT:
Konstantin Osipov's avatar
Konstantin Osipov committed
4167 4168 4169
    if (trans_rollback_to_savepoint(thd, lex->ident))
      goto error;
    my_ok(thd);
unknown's avatar
unknown committed
4170
    break;
4171
  case SQLCOM_SAVEPOINT:
Konstantin Osipov's avatar
Konstantin Osipov committed
4172 4173 4174
    if (trans_savepoint(thd, lex->ident))
      goto error;
    my_ok(thd);
unknown's avatar
WL#1967  
unknown committed
4175
    break;
4176 4177
  case SQLCOM_CREATE_PROCEDURE:
  case SQLCOM_CREATE_SPFUNCTION:
unknown's avatar
unknown committed
4178
  {
4179
    uint namelen;
unknown's avatar
unknown committed
4180
    char *name;
unknown's avatar
unknown committed
4181
    int sp_result= SP_INTERNAL_ERROR;
4182

4183
    DBUG_ASSERT(lex->sphead != 0);
unknown's avatar
unknown committed
4184
    DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */
4185 4186 4187 4188
    /*
      Verify that the database name is allowed, optionally
      lowercase it.
    */
unknown's avatar
unknown committed
4189
    if (check_db_name(&lex->sphead->m_db))
4190
    {
4191
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->sphead->m_db.str);
4192
      goto create_sp_error;
4193 4194
    }

4195
    /*
4196 4197 4198
      Check that a database directory with this name
      exists. Design note: This won't work on virtual databases
      like information_schema.
4199 4200
    */
    if (check_db_dir_existence(lex->sphead->m_db.str))
4201
    {
4202
      my_error(ER_BAD_DB_ERROR, MYF(0), lex->sphead->m_db.str);
4203
      goto create_sp_error;
4204
    }
4205

Marc Alff's avatar
Marc Alff committed
4206 4207
    if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str,
                     NULL, NULL, 0, 0))
4208
      goto create_sp_error;
4209 4210

    name= lex->sphead->name(&namelen);
4211
#ifdef HAVE_DLOPEN
unknown's avatar
unknown committed
4212 4213 4214
    if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
    {
      udf_func *udf = find_udf(name, namelen);
4215

unknown's avatar
unknown committed
4216
      if (udf)
4217
      {
4218 4219
        my_error(ER_UDF_EXISTS, MYF(0), name);
        goto create_sp_error;
4220
      }
unknown's avatar
unknown committed
4221 4222 4223
    }
#endif

unknown's avatar
unknown committed
4224 4225
    if (sp_process_definer(thd))
      goto create_sp_error;
4226

Konstantin Osipov's avatar
Konstantin Osipov committed
4227
    res= (sp_result= sp_create_routine(thd, lex->sphead->m_type, lex->sphead));
unknown's avatar
unknown committed
4228
    switch (sp_result) {
4229
    case SP_OK: {
unknown's avatar
unknown committed
4230
#ifndef NO_EMBEDDED_ACCESS_CHECKS
4231
      /* only add privileges if really neccessary */
4232 4233 4234 4235 4236

      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
4237 4238 4239 4240 4241 4242 4243 4244 4245 4246
      /*
        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();
4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264
      /*
        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;
      }

4265
      if (sp_automatic_privileges && !opt_noacl &&
4266
          check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS,
4267
                               lex->sphead->m_db.str, name,
4268
                               lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1))
4269
      {
unknown's avatar
unknown committed
4270
        if (sp_grant_privileges(thd, lex->sphead->m_db.str, name,
4271
                                lex->sql_command == SQLCOM_CREATE_PROCEDURE))
4272 4273 4274
          push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                       ER_PROC_AUTO_GRANT_FAIL,
                       ER(ER_PROC_AUTO_GRANT_FAIL));
4275
      }
4276 4277 4278 4279 4280 4281 4282 4283 4284 4285

      /*
        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
4286
#endif
unknown's avatar
unknown committed
4287
    break;
4288
    }
4289 4290 4291 4292 4293 4294 4295 4296 4297
    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;
4298 4299 4300
    case SP_FLD_STORE_FAILED:
      my_error(ER_CANT_CREATE_SROUTINE, MYF(0), name);
      break;
4301 4302 4303 4304 4305 4306 4307 4308 4309 4310
    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
4311
    if (sp_result != SP_OK )
4312
      goto error;
4313
    my_ok(thd);
4314 4315
    break; /* break super switch */
  } /* end case group bracket */
4316 4317 4318
  case SQLCOM_CALL:
    {
      sp_head *sp;
4319 4320 4321 4322
      /*
        This will cache all SP and SF and open and lock all tables
        required for execution.
      */
4323 4324
      if (check_table_access(thd, SELECT_ACL, all_tables, FALSE,
                             UINT_MAX, FALSE) ||
4325
          open_and_lock_tables(thd, all_tables, TRUE, 0))
4326 4327 4328
       goto error;

      /*
4329 4330
        By this moment all needed SPs should be in cache so no need to look 
        into DB. 
4331
      */
4332 4333
      if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
                                &thd->sp_proc_cache, TRUE)))
4334
      {
4335
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
unknown's avatar
unknown committed
4336
                 lex->spname->m_qname.str);
4337
	goto error;
4338 4339 4340
      }
      else
      {
unknown's avatar
unknown committed
4341
	ha_rows select_limit;
unknown's avatar
unknown committed
4342 4343
        /* bits that should be cleared in thd->server_status */
	uint bits_to_be_cleared= 0;
4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355
        /*
          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;
        }
4356

4357
	if (sp->m_flags & sp_head::MULTI_RESULTS)
4358
	{
4359
	  if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS))
4360
	  {
4361 4362 4363 4364
            /*
              The client does not support multiple result sets being sent
              back
            */
4365
	    my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str);
4366 4367
	    goto error;
	  }
unknown's avatar
unknown committed
4368 4369 4370 4371 4372 4373 4374
          /*
            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;
4375 4376
	}

4377
	if (check_routine_access(thd, EXECUTE_ACL,
4378
				 sp->m_db.str, sp->m_name.str, TRUE, FALSE))
4379 4380 4381
	{
	  goto error;
	}
unknown's avatar
unknown committed
4382 4383
	select_limit= thd->variables.select_limit;
	thd->variables.select_limit= HA_POS_ERROR;
4384

4385
        /* 
4386
          We never write CALL statements into binlog:
4387 4388 4389 4390 4391
           - 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.
4392
        */
4393
	res= sp->execute_procedure(thd, &lex->value_list);
4394

unknown's avatar
unknown committed
4395
	thd->variables.select_limit= select_limit;
4396

unknown's avatar
unknown committed
4397
        thd->server_status&= ~bits_to_be_cleared;
4398

unknown's avatar
unknown committed
4399
	if (!res)
4400 4401 4402
        {
          my_ok(thd, (thd->get_row_count_func() < 0) ? 0 : thd->get_row_count_func());
        }
4403
	else
4404
        {
4405
          DBUG_ASSERT(thd->is_error() || thd->killed);
4406
	  goto error;		// Substatement should already have sent error
4407
        }
4408
      }
4409
      break;
4410 4411
    }
  case SQLCOM_ALTER_PROCEDURE:
4412
  case SQLCOM_ALTER_FUNCTION:
4413
    {
unknown's avatar
unknown committed
4414
      int sp_result;
Konstantin Osipov's avatar
Konstantin Osipov committed
4415 4416
      int type= (lex->sql_command == SQLCOM_ALTER_PROCEDURE ?
                 TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
unknown's avatar
unknown committed
4417

Konstantin Osipov's avatar
Konstantin Osipov committed
4418 4419 4420 4421
      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
4422

Konstantin Osipov's avatar
Konstantin Osipov committed
4423 4424 4425 4426 4427 4428 4429 4430
      /*
        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
4431
      switch (sp_result)
4432
      {
unknown's avatar
unknown committed
4433
      case SP_OK:
4434
	my_ok(thd);
unknown's avatar
unknown committed
4435 4436
	break;
      case SP_KEY_NOT_FOUND:
4437 4438
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
unknown's avatar
unknown committed
4439 4440
	goto error;
      default:
4441 4442
	my_error(ER_SP_CANT_ALTER, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
unknown's avatar
unknown committed
4443
	goto error;
4444
      }
4445
      break;
4446 4447
    }
  case SQLCOM_DROP_PROCEDURE:
4448
  case SQLCOM_DROP_FUNCTION:
4449
    {
4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490
#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
4491
      int sp_result;
4492 4493
      int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
                 TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
4494

Konstantin Osipov's avatar
Konstantin Osipov committed
4495 4496 4497 4498 4499 4500
      /*
        @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
4501
      sp_result= sp_routine_exists_in_table(thd, type, lex->spname);
Marc Alff's avatar
Marc Alff committed
4502
      thd->warning_info->opt_clear_warning_info(thd->query_id);
unknown's avatar
unknown committed
4503
      if (sp_result == SP_OK)
4504
      {
4505 4506 4507
        char *db= lex->spname->m_db.str;
	char *name= lex->spname->m_name.str;

4508 4509
	if (check_routine_access(thd, ALTER_PROC_ACL, db, name,
                                 lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
unknown's avatar
merge  
unknown committed
4510
          goto error;
4511

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

Konstantin Osipov's avatar
Konstantin Osipov committed
4515 4516 4517 4518 4519 4520 4521 4522 4523
#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);
4524
        close_thread_tables(thd);
4525
        thd->mdl_context.release_transactional_locks();
4526

4527 4528
        if (sp_automatic_privileges && !opt_noacl &&
            sp_revoke_privileges(thd, db, name,
4529
                                 lex->sql_command == SQLCOM_DROP_PROCEDURE))
4530 4531 4532 4533 4534 4535 4536
        {
          push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                       ER_PROC_AUTO_REVOKE_FAIL,
                       ER(ER_PROC_AUTO_REVOKE_FAIL));
          /* If this happens, an error should have been reported. */
          goto error;
        }
unknown's avatar
unknown committed
4537
#endif
4538
      }
unknown's avatar
unknown committed
4539 4540
      res= sp_result;
      switch (sp_result) {
4541
      case SP_OK:
4542
	my_ok(thd);
4543 4544
	break;
      case SP_KEY_NOT_FOUND:
4545 4546
	if (lex->drop_if_exists)
	{
4547
          res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
4548
	  push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
4549
			      ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
4550
                              SP_COM_STRING(lex), lex->spname->m_qname.str);
4551 4552
          if (!res)
            my_ok(thd);
4553 4554
	  break;
	}
4555 4556
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
4557 4558
	goto error;
      default:
4559 4560
	my_error(ER_SP_DROP_FAILED, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
4561
	goto error;
4562
      }
4563
      break;
4564
    }
unknown's avatar
unknown committed
4565 4566
  case SQLCOM_SHOW_CREATE_PROC:
    {
unknown's avatar
unknown committed
4567
      if (sp_show_create_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname))
Konstantin Osipov's avatar
Konstantin Osipov committed
4568
        goto error;
unknown's avatar
unknown committed
4569 4570 4571 4572
      break;
    }
  case SQLCOM_SHOW_CREATE_FUNC:
    {
unknown's avatar
unknown committed
4573
      if (sp_show_create_routine(thd, TYPE_ENUM_FUNCTION, lex->spname))
unknown's avatar
unknown committed
4574 4575 4576
	goto error;
      break;
    }
unknown's avatar
unknown committed
4577 4578 4579
  case SQLCOM_SHOW_PROC_CODE:
  case SQLCOM_SHOW_FUNC_CODE:
    {
4580
#ifndef DBUG_OFF
unknown's avatar
unknown committed
4581
      sp_head *sp;
Konstantin Osipov's avatar
Konstantin Osipov committed
4582 4583
      int type= (lex->sql_command == SQLCOM_SHOW_PROC_CODE ?
                 TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
unknown's avatar
unknown committed
4584

Konstantin Osipov's avatar
Konstantin Osipov committed
4585 4586
      if (sp_cache_routine(thd, type, lex->spname, FALSE, &sp))
        goto error;
4587
      if (!sp || sp->show_routine_code(thd))
4588 4589
      {
        /* We don't distinguish between errors for now */
unknown's avatar
unknown committed
4590 4591 4592 4593 4594
        my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_name.str);
        goto error;
      }
      break;
4595 4596 4597 4598
#else
      my_error(ER_FEATURE_DISABLED, MYF(0),
               "SHOW PROCEDURE|FUNCTION CODE", "--with-debug");
      goto error;
unknown's avatar
unknown committed
4599
#endif // ifndef DBUG_OFF
4600
    }
unknown's avatar
unknown committed
4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613
  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
4614 4615
  case SQLCOM_CREATE_VIEW:
    {
4616 4617 4618 4619
      /*
        Note: SQLCOM_CREATE_VIEW also handles 'ALTER VIEW' commands
        as specified through the thd->lex->create_view_mode flag.
      */
4620
      res= mysql_create_view(thd, first_table, thd->lex->create_view_mode);
unknown's avatar
VIEW  
unknown committed
4621 4622 4623 4624
      break;
    }
  case SQLCOM_DROP_VIEW:
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
4625
      if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE))
unknown's avatar
unknown committed
4626
        goto error;
4627 4628
      /* Conditionally writes to binlog. */
      res= mysql_drop_view(thd, first_table, thd->lex->drop_mode);
unknown's avatar
VIEW  
unknown committed
4629 4630
      break;
    }
4631 4632
  case SQLCOM_CREATE_TRIGGER:
  {
4633
    /* Conditionally writes to binlog. */
4634 4635
    res= mysql_create_or_drop_trigger(thd, all_tables, 1);

4636 4637 4638 4639
    break;
  }
  case SQLCOM_DROP_TRIGGER:
  {
4640
    /* Conditionally writes to binlog. */
4641 4642 4643
    res= mysql_create_or_drop_trigger(thd, all_tables, 0);
    break;
  }
unknown's avatar
unknown committed
4644
  case SQLCOM_XA_START:
Konstantin Osipov's avatar
Konstantin Osipov committed
4645 4646
    if (trans_xa_start(thd))
      goto error;
4647
    my_ok(thd);
unknown's avatar
unknown committed
4648 4649
    break;
  case SQLCOM_XA_END:
Konstantin Osipov's avatar
Konstantin Osipov committed
4650 4651
    if (trans_xa_end(thd))
      goto error;
4652
    my_ok(thd);
unknown's avatar
unknown committed
4653 4654
    break;
  case SQLCOM_XA_PREPARE:
Konstantin Osipov's avatar
Konstantin Osipov committed
4655 4656
    if (trans_xa_prepare(thd))
      goto error;
4657
    my_ok(thd);
unknown's avatar
unknown committed
4658 4659
    break;
  case SQLCOM_XA_COMMIT:
Konstantin Osipov's avatar
Konstantin Osipov committed
4660 4661
    if (trans_xa_commit(thd))
      goto error;
4662
    thd->mdl_context.release_transactional_locks();
4663 4664 4665 4666 4667
    /*
      We've just done a commit, reset transaction
      isolation level to the session default.
    */
    thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
Konstantin Osipov's avatar
Konstantin Osipov committed
4668
    my_ok(thd);
unknown's avatar
unknown committed
4669 4670
    break;
  case SQLCOM_XA_ROLLBACK:
Konstantin Osipov's avatar
Konstantin Osipov committed
4671 4672
    if (trans_xa_rollback(thd))
      goto error;
4673
    thd->mdl_context.release_transactional_locks();
4674 4675 4676 4677 4678
    /*
      We've just done a rollback, reset transaction
      isolation level to the session default.
    */
    thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
Konstantin Osipov's avatar
Konstantin Osipov committed
4679
    my_ok(thd);
unknown's avatar
unknown committed
4680 4681
    break;
  case SQLCOM_XA_RECOVER:
4682
    res= mysql_xa_recover(thd);
unknown's avatar
unknown committed
4683
    break;
unknown's avatar
unknown committed
4684
  case SQLCOM_ALTER_TABLESPACE:
4685
    if (check_global_access(thd, CREATE_TABLESPACE_ACL))
unknown's avatar
unknown committed
4686 4687
      break;
    if (!(res= mysql_alter_tablespace(thd, lex->alter_tablespace_info)))
4688
      my_ok(thd);
unknown's avatar
unknown committed
4689 4690 4691 4692
    break;
  case SQLCOM_INSTALL_PLUGIN:
    if (! (res= mysql_install_plugin(thd, &thd->lex->comment,
                                     &thd->lex->ident)))
4693
      my_ok(thd);
unknown's avatar
unknown committed
4694 4695 4696
    break;
  case SQLCOM_UNINSTALL_PLUGIN:
    if (! (res= mysql_uninstall_plugin(thd, &thd->lex->comment)))
4697
      my_ok(thd);
unknown's avatar
unknown committed
4698 4699 4700 4701 4702 4703 4704 4705 4706 4707
    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
4708 4709 4710 4711 4712
  case SQLCOM_CREATE_SERVER:
  {
    int error;
    LEX *lex= thd->lex;
    DBUG_PRINT("info", ("case SQLCOM_CREATE_SERVER"));
unknown's avatar
unknown committed
4713 4714 4715 4716

    if (check_global_access(thd, SUPER_ACL))
      break;

unknown's avatar
unknown committed
4717 4718
    if ((error= create_server(thd, &lex->server_options)))
    {
4719
      DBUG_PRINT("info", ("problem creating server <%s>",
unknown's avatar
unknown committed
4720 4721 4722 4723
                          lex->server_options.server_name));
      my_error(error, MYF(0), lex->server_options.server_name);
      break;
    }
4724
    my_ok(thd, 1);
unknown's avatar
unknown committed
4725 4726 4727 4728 4729 4730 4731
    break;
  }
  case SQLCOM_ALTER_SERVER:
  {
    int error;
    LEX *lex= thd->lex;
    DBUG_PRINT("info", ("case SQLCOM_ALTER_SERVER"));
unknown's avatar
unknown committed
4732 4733 4734 4735

    if (check_global_access(thd, SUPER_ACL))
      break;

unknown's avatar
unknown committed
4736 4737
    if ((error= alter_server(thd, &lex->server_options)))
    {
4738
      DBUG_PRINT("info", ("problem altering server <%s>",
unknown's avatar
unknown committed
4739 4740 4741 4742
                          lex->server_options.server_name));
      my_error(error, MYF(0), lex->server_options.server_name);
      break;
    }
4743
    my_ok(thd, 1);
unknown's avatar
unknown committed
4744 4745 4746 4747 4748 4749 4750
    break;
  }
  case SQLCOM_DROP_SERVER:
  {
    int err_code;
    LEX *lex= thd->lex;
    DBUG_PRINT("info", ("case SQLCOM_DROP_SERVER"));
unknown's avatar
unknown committed
4751 4752 4753 4754

    if (check_global_access(thd, SUPER_ACL))
      break;

unknown's avatar
unknown committed
4755 4756
    if ((err_code= drop_server(thd, &lex->server_options)))
    {
unknown's avatar
unknown committed
4757
      if (! lex->drop_if_exists && err_code == ER_FOREIGN_SERVER_DOESNT_EXIST)
unknown's avatar
unknown committed
4758 4759 4760 4761 4762 4763 4764
      {
        DBUG_PRINT("info", ("problem dropping server %s",
                            lex->server_options.server_name));
        my_error(err_code, MYF(0), lex->server_options.server_name);
      }
      else
      {
4765
        my_ok(thd, 0);
unknown's avatar
unknown committed
4766 4767 4768
      }
      break;
    }
4769
    my_ok(thd, 1);
unknown's avatar
unknown committed
4770 4771
    break;
  }
Marc Alff's avatar
Marc Alff committed
4772 4773 4774 4775 4776
  case SQLCOM_SIGNAL:
  case SQLCOM_RESIGNAL:
    DBUG_ASSERT(lex->m_stmt != NULL);
    res= lex->m_stmt->execute(thd);
    break;
unknown's avatar
unknown committed
4777
  default:
4778
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
4779
    DBUG_ASSERT(0);                             /* Impossible */
4780
#endif
4781
    my_ok(thd);
unknown's avatar
unknown committed
4782 4783
    break;
  }
4784
  thd_proc_info(thd, "query end");
4785 4786

  /*
unknown's avatar
unknown committed
4787
    Binlog-related cleanup:
4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798
    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);

4799
  goto finish;
unknown's avatar
unknown committed
4800 4801

error:
4802 4803
  res= TRUE;

4804
finish:
4805
  if (thd->global_read_lock.has_protection())
4806 4807 4808 4809 4810
  {
    /*
      Release the protection against the global read lock and wake
      everyone, who might want to set a global read lock.
    */
4811
    thd->global_read_lock.start_waiting_global_read_lock(thd);
4812
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
4813

4814 4815 4816
  DBUG_ASSERT(!thd->in_active_multi_stmt_transaction() ||
               thd->in_multi_stmt_transaction_mode());

4817 4818 4819 4820 4821 4822 4823 4824
  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);
4825
    thd->stmt_da->can_overwrite_status= FALSE;
4826 4827
    /* Close tables and release metadata locks. */
    close_thread_tables(thd);
4828
    thd->mdl_context.release_transactional_locks();
4829
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
4830

4831
  DBUG_RETURN(res || thd->is_error());
unknown's avatar
unknown committed
4832 4833 4834
}


4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846
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);
  }
4847
  if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
4848 4849 4850 4851 4852 4853 4854 4855 4856 4857
  {
    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()))
4858
        return 1;                               /* purecov: inspected */
4859 4860 4861 4862 4863 4864 4865
      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);
4866
        thd->lex->unit.print(&str, QT_ORDINARY);
4867 4868 4869 4870
        str.append('\0');
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                     ER_YES, str.ptr());
      }
4871 4872 4873 4874
      if (res)
        result->abort();
      else
        result->send_eof();
4875 4876 4877 4878 4879
      delete result;
    }
    else
    {
      if (!result && !(result= new select_send()))
4880
        return 1;                               /* purecov: inspected */
4881 4882 4883 4884 4885 4886 4887 4888 4889 4890
      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
4891
#ifndef NO_EMBEDDED_ACCESS_CHECKS
unknown's avatar
unknown committed
4892
/**
4893
  Check grants for commands which work only with one table.
unknown's avatar
unknown committed
4894

4895 4896 4897
  @param thd                    Thread handler
  @param privilege              requested privilege
  @param all_tables             global table list of query
unknown's avatar
unknown committed
4898
  @param no_errors              FALSE/TRUE - report/don't report error to
4899
                            the client (using my_error() call).
unknown's avatar
unknown committed
4900 4901 4902 4903 4904

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

4907
bool check_single_table_access(THD *thd, ulong privilege, 
4908
                               TABLE_LIST *all_tables, bool no_errors)
unknown's avatar
unknown committed
4909
{
4910 4911 4912 4913 4914 4915
  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;

4916 4917 4918 4919 4920 4921 4922 4923
  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
4924 4925 4926
                   &all_tables->grant.privilege,
                   &all_tables->grant.m_internal,
                   0, no_errors))
4927
    goto deny;
unknown's avatar
unknown committed
4928

unknown's avatar
unknown committed
4929
  /* Show only 1 table for check_grant */
4930
  if (!(all_tables->belong_to_view &&
4931
        (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) &&
4932
      check_grant(thd, privilege, all_tables, FALSE, 1, no_errors))
4933 4934 4935
    goto deny;

  thd->security_ctx= backup_ctx;
4936 4937 4938 4939 4940 4941 4942
  return 0;

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

unknown's avatar
unknown committed
4943
/**
4944 4945 4946
  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
4947 4948 4949 4950 4951 4952 4953 4954
  @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
4955 4956 4957 4958
*/

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

4962
  /* Check rights on tables of subselects and implictly opened tables */
unknown's avatar
unknown committed
4963
  TABLE_LIST *subselects_tables, *view= all_tables->view ? all_tables : 0;
unknown's avatar
VIEW  
unknown committed
4964
  if ((subselects_tables= all_tables->next_global))
unknown's avatar
unknown committed
4965
  {
unknown's avatar
unknown committed
4966 4967 4968 4969 4970 4971
    /*
      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)
    {
4972
      if (check_single_table_access (thd, privilege, subselects_tables, FALSE))
unknown's avatar
unknown committed
4973 4974 4975 4976
        return 1;
      subselects_tables= subselects_tables->next_global;
    }
    if (subselects_tables &&
4977 4978
        (check_table_access(thd, SELECT_ACL, subselects_tables, FALSE,
                            UINT_MAX, FALSE)))
4979
      return 1;
unknown's avatar
unknown committed
4980 4981
  }
  return 0;
unknown's avatar
unknown committed
4982 4983 4984
}


unknown's avatar
unknown committed
4985
/**
4986 4987 4988 4989 4990 4991
  @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
4992
  @param grant_internal_info A pointer to the internal grant cache.
4993 4994 4995 4996 4997 4998 4999 5000
  @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
5001 5002
  For internal databases (INFORMATION_SCHEMA, PERFORMANCE_SCHEMA),
  additional rules apply, see ACL_internal_schema_access.
5003 5004 5005 5006 5007 5008 5009

  @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
5010
*/
5011

unknown's avatar
unknown committed
5012
bool
unknown's avatar
unknown committed
5013
check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
Marc Alff's avatar
Marc Alff committed
5014 5015
             GRANT_INTERNAL_INFO *grant_internal_info,
             bool dont_check_global_grants, bool no_errors)
unknown's avatar
unknown committed
5016
{
5017
  Security_context *sctx= thd->security_ctx;
unknown's avatar
unknown committed
5018
  ulong db_access;
5019

5020 5021 5022 5023 5024
  /*
    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
5025
    if it's database level grant command
5026 5027 5028
    (see SQLCOM_GRANT case, mysql_execute_command() function) and
    set db_is_pattern according to 'dont_check_global_grants' value.
  */
5029
  bool  db_is_pattern= ((want_access & GRANT_ACL) && dont_check_global_grants);
unknown's avatar
unknown committed
5030
  ulong dummy;
5031 5032
  DBUG_ENTER("check_access");
  DBUG_PRINT("enter",("db: %s  want_access: %lu  master_access: %lu",
5033
                      db ? db : "", want_access, sctx->master_access));
5034

unknown's avatar
unknown committed
5035 5036 5037
  if (save_priv)
    *save_priv=0;
  else
Marc Alff's avatar
Marc Alff committed
5038
  {
unknown's avatar
unknown committed
5039
    save_priv= &dummy;
Marc Alff's avatar
Marc Alff committed
5040 5041
    dummy= 0;
  }
unknown's avatar
unknown committed
5042

5043
  thd_proc_info(thd, "checking permissions");
5044
  if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
unknown's avatar
unknown committed
5045
  {
5046
    DBUG_PRINT("error",("No database"));
5047
    if (!no_errors)
unknown's avatar
unknown committed
5048 5049
      my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR),
                 MYF(0));                       /* purecov: tested */
unknown's avatar
unknown committed
5050
    DBUG_RETURN(TRUE);				/* purecov: tested */
unknown's avatar
unknown committed
5051 5052
  }

Marc Alff's avatar
Marc Alff committed
5053
  if ((db != NULL) && (db != any_db))
5054
  {
Marc Alff's avatar
Marc Alff committed
5055 5056 5057
    const ACL_internal_schema_access *access;
    access= get_cached_schema_access(grant_internal_info, db);
    if (access)
5058
    {
Marc Alff's avatar
Marc Alff committed
5059
      switch (access->check(want_access, save_priv))
5060
      {
Marc Alff's avatar
Marc Alff committed
5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080
      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;
5081
      }
5082 5083 5084
    }
  }

5085
  if ((sctx->master_access & want_access) == want_access)
unknown's avatar
unknown committed
5086
  {
5087
    /*
5088 5089
      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
5090
      UPDATE t1 SET a=1 WHERE b > 0
5091
      2. Change db access if it isn't current db which is being addressed
5092
    */
Marc Alff's avatar
Marc Alff committed
5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111
    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
5112
    DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
5113
  }
5114
  if (((want_access & ~sctx->master_access) & ~DB_ACLS) ||
5115
      (! db && dont_check_global_grants))
unknown's avatar
unknown committed
5116
  {						// We can never grant this
5117
    DBUG_PRINT("error",("No possible access"));
5118
    if (!no_errors)
5119
      my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
5120 5121
               sctx->priv_user,
               sctx->priv_host,
5122 5123 5124
               (thd->password ?
                ER(ER_YES) :
                ER(ER_NO)));                    /* purecov: tested */
unknown's avatar
unknown committed
5125
    DBUG_RETURN(TRUE);				/* purecov: tested */
unknown's avatar
unknown committed
5126 5127 5128
  }

  if (db == any_db)
5129 5130 5131 5132 5133 5134 5135
  {
    /*
      Access granted; Allow select on *any* db.
      [out] *save_privileges= 0
    */
    DBUG_RETURN(FALSE);
  }
unknown's avatar
unknown committed
5136

unknown's avatar
unknown committed
5137
  if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
5138 5139
    db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
                       db_is_pattern);
unknown's avatar
unknown committed
5140
  else
5141
    db_access= sctx->db_access;
5142 5143
  DBUG_PRINT("info",("db_access: %lu  want_access: %lu",
                     db_access, want_access));
5144

5145 5146
  /*
    Save the union of User-table and the intersection between Db-table and
Marc Alff's avatar
Marc Alff committed
5147
    Host-table privileges, with the already saved internal privileges.
5148 5149
  */
  db_access= (db_access | sctx->master_access);
Marc Alff's avatar
Marc Alff committed
5150
  *save_priv|= db_access;
5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165

  /*
    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 ||
5166
      (!dont_check_global_grants &&
5167 5168 5169 5170
       need_table_or_column_check))
  {
    /*
       Ok; but need to check table- and column privileges.
Marc Alff's avatar
Marc Alff committed
5171
       [out] *save_privileges is (User-priv | (Db-priv & Host-priv) | Internal-priv)
5172 5173 5174
    */
    DBUG_RETURN(FALSE);
  }
5175

5176 5177
  /*
    Access is denied;
Marc Alff's avatar
Marc Alff committed
5178
    [out] *save_privileges is (User-priv | (Db-priv & Host-priv) | Internal-priv)
5179
  */
5180
  DBUG_PRINT("error",("Access denied"));
5181
  if (!no_errors)
5182
    my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
5183
             sctx->priv_user, sctx->priv_host,
5184 5185
             (db ? db : (thd->db ?
                         thd->db :
5186 5187 5188
                         "unknown")));
  DBUG_RETURN(TRUE);

unknown's avatar
unknown committed
5189 5190 5191
}


5192 5193
static bool check_show_access(THD *thd, TABLE_LIST *table)
{
Marc Alff's avatar
Marc Alff committed
5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205
  /*
    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
5206
  switch (get_schema_table_idx(table->schema_table)) {
5207 5208
  case SCH_SCHEMATA:
    return (specialflag & SPECIAL_SKIP_SHOW_DB) &&
unknown's avatar
unknown committed
5209
      check_global_access(thd, SHOW_DB_ACL);
5210 5211 5212 5213 5214

  case SCH_TABLE_NAMES:
  case SCH_TABLES:
  case SCH_VIEWS:
  case SCH_TRIGGERS:
unknown's avatar
unknown committed
5215 5216 5217
  case SCH_EVENTS:
  {
    const char *dst_db_name= table->schema_select_lex->db;
5218

unknown's avatar
unknown committed
5219
    DBUG_ASSERT(dst_db_name);
5220

unknown's avatar
unknown committed
5221
    if (check_access(thd, SELECT_ACL, dst_db_name,
Marc Alff's avatar
Marc Alff committed
5222
                     &thd->col_access, NULL, FALSE, FALSE))
unknown's avatar
unknown committed
5223
      return TRUE;
5224

unknown's avatar
unknown committed
5225 5226 5227 5228 5229 5230 5231
    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;
5232 5233
    }

unknown's avatar
unknown committed
5234 5235 5236
    return FALSE;
  }

5237 5238
  case SCH_COLUMNS:
  case SCH_STATISTICS:
unknown's avatar
unknown committed
5239 5240
  {
    TABLE_LIST *dst_table;
5241
    dst_table= table->schema_select_lex->table_list.first;
5242

unknown's avatar
unknown committed
5243
    DBUG_ASSERT(dst_table);
5244

5245
    if (check_access(thd, SELECT_ACL, dst_table->db,
Marc Alff's avatar
Marc Alff committed
5246 5247 5248
                     &dst_table->grant.privilege,
                     &dst_table->grant.m_internal,
                     FALSE, FALSE))
5249 5250 5251 5252 5253 5254 5255 5256
          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 */
5257

5258 5259
    /* Access granted */
    return FALSE;
unknown's avatar
unknown committed
5260 5261
  }
  default:
5262 5263 5264 5265 5266 5267 5268
    break;
  }

  return FALSE;
}


5269

5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297
/**
  @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).
5298

5299 5300 5301 5302
  @return
    @retval FALSE OK
    @retval TRUE  Access denied; But column or routine privileges might need to
      be checked also.
unknown's avatar
unknown committed
5303 5304
*/

5305
bool
5306 5307 5308
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
5309
{
5310 5311
  TABLE_LIST *org_tables= tables;
  TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
5312
  uint i= 0;
5313
  Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx;
5314
  /*
unknown's avatar
unknown committed
5315 5316 5317
    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.
5318
  */
5319
  for (; i < number && tables != first_not_own_table && tables;
5320
       tables= tables->next_global, i++)
unknown's avatar
unknown committed
5321
  {
5322
    ulong want_access= requirements;
5323 5324 5325 5326 5327
    if (tables->security_ctx)
      sctx= tables->security_ctx;
    else
      sctx= backup_ctx;

5328 5329 5330 5331 5332
    /*
       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
5333 5334 5335 5336 5337 5338 5339 5340

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

5341 5342
    DBUG_PRINT("info", ("derived: %d  view: %d", tables->derived != 0,
                        tables->view != 0));
5343
    if (tables->is_anonymous_derived_table() ||
5344 5345
        (tables->table && tables->table->s &&
         (int)tables->table->s->tmp_table))
unknown's avatar
unknown committed
5346
      continue;
5347
    thd->security_ctx= sctx;
Marc Alff's avatar
Marc Alff committed
5348 5349 5350 5351 5352

    if (check_access(thd, want_access, tables->get_db_name(),
                     &tables->grant.privilege,
                     &tables->grant.m_internal,
                     0, no_errors))
5353
      goto deny;
unknown's avatar
unknown committed
5354
  }
5355
  thd->security_ctx= backup_ctx;
5356 5357 5358
  return check_grant(thd,requirements,org_tables,
                     any_combination_of_privileges_will_do,
                     number, no_errors);
5359 5360 5361
deny:
  thd->security_ctx= backup_ctx;
  return TRUE;
unknown's avatar
unknown committed
5362 5363
}

5364

5365
bool
5366 5367
check_routine_access(THD *thd, ulong want_access,char *db, char *name,
		     bool is_proc, bool no_errors)
5368 5369 5370 5371 5372
{
  TABLE_LIST tables[1];
  
  bzero((char *)tables, sizeof(TABLE_LIST));
  tables->db= db;
5373
  tables->table_name= tables->alias= name;
5374
  
unknown's avatar
unknown committed
5375 5376 5377 5378 5379
  /*
    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
5380 5381 5382 5383 5384 5385
    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
5386
  */
Marc Alff's avatar
Marc Alff committed
5387
  DBUG_ASSERT((want_access & CREATE_PROC_ACL) == 0);
unknown's avatar
unknown committed
5388
  if ((thd->security_ctx->master_access & want_access) == want_access)
5389
    tables->grant.privilege= want_access;
Marc Alff's avatar
Marc Alff committed
5390 5391 5392 5393
  else if (check_access(thd, want_access, db,
                        &tables->grant.privilege,
                        &tables->grant.m_internal,
                        0, no_errors))
5394 5395
    return TRUE;
  
5396
  return check_grant_routine(thd, want_access, tables, is_proc, no_errors);
5397 5398
}

5399

unknown's avatar
unknown committed
5400 5401
/**
  Check if the routine has any of the routine privileges.
5402

unknown's avatar
unknown committed
5403 5404 5405
  @param thd	       Thread handler
  @param db           Database name
  @param name         Routine name
5406

unknown's avatar
unknown committed
5407
  @retval
5408
    0            ok
unknown's avatar
unknown committed
5409
  @retval
5410 5411 5412
    1            error
*/

5413 5414
bool check_some_routine_access(THD *thd, const char *db, const char *name,
                               bool is_proc)
5415 5416
{
  ulong save_priv;
5417
  /*
Marc Alff's avatar
Marc Alff committed
5418 5419 5420 5421 5422 5423 5424
    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.
5425
  */
Marc Alff's avatar
Marc Alff committed
5426 5427 5428
  if (thd->security_ctx->master_access & SHOW_PROC_ACLS)
    return FALSE;
  if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, NULL, 0, 1) ||
5429 5430
      (save_priv & SHOW_PROC_ACLS))
    return FALSE;
5431
  return check_routine_level_acl(thd, db, name, is_proc);
5432 5433 5434
}


5435 5436 5437
/*
  Check if the given table has any of the asked privileges

unknown's avatar
unknown committed
5438 5439
  @param thd		 Thread handler
  @param want_access	 Bitmap of possible privileges to check for
5440

unknown's avatar
unknown committed
5441
  @retval
5442
    0  ok
unknown's avatar
unknown committed
5443
  @retval
5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457
    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
5458 5459 5460
                        &table->grant.privilege,
                        &table->grant.m_internal,
                        0, 1) &&
5461
           !check_grant(thd, access, table, FALSE, 1, TRUE))
5462 5463 5464 5465 5466 5467 5468
        DBUG_RETURN(0);
    }
  }
  DBUG_PRINT("exit",("no matching access rights"));
  DBUG_RETURN(1);
}

unknown's avatar
unknown committed
5469
#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
5470

5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503

/**
  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
5504 5505 5506 5507
/****************************************************************************
	Check stack size; Send error if there isn't enough stack to continue
****************************************************************************/

5508

unknown's avatar
unknown committed
5509 5510 5511 5512 5513 5514
#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
5515 5516 5517 5518
#ifndef DBUG_OFF
long max_stack_used;
#endif

unknown's avatar
unknown committed
5519 5520
/**
  @note
5521 5522 5523 5524
  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
5525
*/
5526
bool check_stack_overrun(THD *thd, long margin,
5527
			 uchar *buf __attribute__((unused)))
unknown's avatar
unknown committed
5528 5529
{
  long stack_used;
5530
  DBUG_ASSERT(thd == current_thd);
unknown's avatar
unknown committed
5531
  if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
5532
      (long) (my_thread_stack_size - margin))
unknown's avatar
unknown committed
5533
  {
5534 5535 5536 5537
    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
5538 5539
    return 1;
  }
unknown's avatar
unknown committed
5540 5541 5542
#ifndef DBUG_OFF
  max_stack_used= max(max_stack_used, stack_used);
#endif
unknown's avatar
unknown committed
5543 5544
  return 0;
}
5545

unknown's avatar
unknown committed
5546 5547 5548 5549

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

5550
bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
unknown's avatar
unknown committed
5551
{
5552
  Yacc_state *state= & current_thd->m_parser_state->m_yacc;
5553
  ulong old_info=0;
5554
  DBUG_ASSERT(state);
unknown's avatar
unknown committed
5555 5556
  if ((uint) *yystacksize >= MY_YACC_MAX)
    return 1;
5557
  if (!state->yacc_yyvs)
unknown's avatar
unknown committed
5558 5559
    old_info= *yystacksize;
  *yystacksize= set_zone((*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX);
5560
  if (!(state->yacc_yyvs= (uchar*)
5561
        my_realloc(state->yacc_yyvs,
5562 5563 5564
                   *yystacksize*sizeof(**yyvs),
                   MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) ||
      !(state->yacc_yyss= (uchar*)
5565
        my_realloc(state->yacc_yyss,
5566 5567
                   *yystacksize*sizeof(**yyss),
                   MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))))
unknown's avatar
unknown committed
5568 5569
    return 1;
  if (old_info)
5570 5571 5572 5573 5574 5575 5576 5577
  {
    /*
      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
5578
  }
5579 5580
  *yyss= (short*) state->yacc_yyss;
  *yyvs= (YYSTYPE*) state->yacc_yyvs;
unknown's avatar
unknown committed
5581 5582 5583 5584
  return 0;
}


unknown's avatar
unknown committed
5585
/**
5586 5587
  Reset the part of THD responsible for the state of command
  processing.
5588

5589 5590 5591
  This needs to be called before execution of every statement
  (prepared or conventional).  It is not called by substatements of
  routines.
5592

5593 5594
  @todo Remove mysql_reset_thd_for_next_command and only use the
  member function.
5595

5596 5597
  @todo Call it after we use THD for queries, not before.
*/
5598 5599
void mysql_reset_thd_for_next_command(THD *thd)
{
5600 5601 5602 5603 5604 5605
  thd->reset_for_next_command();
}

void THD::reset_for_next_command()
{
  THD *thd= this;
5606
  DBUG_ENTER("mysql_reset_thd_for_next_command");
5607
  DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */
5608
  DBUG_ASSERT(! thd->in_sub_stmt);
5609
  thd->free_list= 0;
5610
  thd->select_number= 1;
5611 5612 5613 5614
  /*
    Those two lines below are theoretically unneeded as
    THD::cleanup_after_query() should take care of this already.
  */
5615
  thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty();
5616 5617 5618
  thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;

  thd->query_start_used= 0;
5619
  thd->is_fatal_error= thd->time_zone_used= 0;
5620 5621 5622 5623 5624
  /*
    Clear the status flag that are expected to be cleared at the
    beginning of each SQL statement.
  */
  thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
5625 5626 5627 5628 5629
  /*
    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.
  */
5630
  if (!thd->in_multi_stmt_transaction_mode())
unknown's avatar
unknown committed
5631
  {
5632
    thd->variables.option_bits&= ~OPTION_KEEP_LOG;
5633
    thd->transaction.all.modified_non_trans_table= FALSE;
unknown's avatar
unknown committed
5634
  }
5635
  DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx);
5636
  thd->thread_specific_used= FALSE;
5637 5638

  if (opt_bin_log)
5639
  {
5640 5641
    reset_dynamic(&thd->user_var_events);
    thd->user_var_events_alloc= thd->mem_root;
5642
  }
5643
  thd->clear_error();
Marc Alff's avatar
Marc Alff committed
5644 5645
  thd->stmt_da->reset_diagnostics_area();
  thd->warning_info->reset_for_next_command();
5646 5647 5648
  thd->rand_used= 0;
  thd->sent_row_count= thd->examined_row_count= 0;

5649
  thd->reset_current_stmt_binlog_format_row();
5650
  thd->binlog_unsafe_warning_flags= 0;
5651
  thd->stmt_accessed_table_flag= 0;
unknown's avatar
unknown committed
5652

5653
  DBUG_PRINT("debug",
5654
             ("is_current_stmt_binlog_format_row(): %d",
5655
              thd->is_current_stmt_binlog_format_row()));
5656

unknown's avatar
unknown committed
5657 5658 5659
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
5660

5661 5662 5663 5664 5665 5666 5667 5668
/**
  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.
*/

5669 5670 5671
void
mysql_init_select(LEX *lex)
{
unknown's avatar
(SCRUM)  
unknown committed
5672
  SELECT_LEX *select_lex= lex->current_select;
unknown's avatar
unknown committed
5673
  select_lex->init_select();
5674
  lex->wild= 0;
5675 5676
  if (select_lex == &lex->select_lex)
  {
5677
    DBUG_ASSERT(lex->result == 0);
5678 5679
    lex->exchange= 0;
  }
5680 5681
}

5682

5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694
/**
  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
5695
bool
unknown's avatar
unknown committed
5696
mysql_new_select(LEX *lex, bool move_down)
5697
{
unknown's avatar
unknown committed
5698
  SELECT_LEX *select_lex;
5699
  THD *thd= lex->thd;
unknown's avatar
unknown committed
5700 5701
  DBUG_ENTER("mysql_new_select");

5702
  if (!(select_lex= new (thd->mem_root) SELECT_LEX()))
unknown's avatar
unknown committed
5703
    DBUG_RETURN(1);
5704
  select_lex->select_number= ++thd->select_number;
unknown's avatar
unknown committed
5705
  select_lex->parent_lex= lex; /* Used in init_query. */
unknown's avatar
unknown committed
5706 5707
  select_lex->init_query();
  select_lex->init_select();
unknown's avatar
unknown committed
5708
  lex->nest_level++;
unknown's avatar
unknown committed
5709 5710 5711 5712 5713
  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
5714
  select_lex->nest_level= lex->nest_level;
5715 5716 5717
  /*
    Don't evaluate this subquery during statement prepare even if
    it's a constant one. The flag is switched off in the end of
5718
    mysqld_stmt_prepare.
5719
  */
unknown's avatar
Rename:  
unknown committed
5720
  if (thd->stmt_arena->is_stmt_prepare())
5721
    select_lex->uncacheable|= UNCACHEABLE_PREPARE;
unknown's avatar
unknown committed
5722 5723
  if (move_down)
  {
unknown's avatar
unknown committed
5724
    SELECT_LEX_UNIT *unit;
5725
    lex->subqueries= TRUE;
unknown's avatar
unknown committed
5726
    /* first select_lex of subselect or derived table */
5727
    if (!(unit= new (thd->mem_root) SELECT_LEX_UNIT()))
unknown's avatar
unknown committed
5728
      DBUG_RETURN(1);
unknown's avatar
unknown committed
5729

unknown's avatar
unknown committed
5730 5731
    unit->init_query();
    unit->init_select();
5732
    unit->thd= thd;
unknown's avatar
(SCRUM)  
unknown committed
5733
    unit->include_down(lex->current_select);
unknown's avatar
unknown committed
5734 5735
    unit->link_next= 0;
    unit->link_prev= 0;
5736
    unit->return_to= lex->current_select;
unknown's avatar
unknown committed
5737
    select_lex->include_down(unit);
5738 5739 5740 5741 5742
    /*
      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
5743 5744
  }
  else
unknown's avatar
(SCRUM)  
unknown committed
5745
  {
unknown's avatar
VIEW  
unknown committed
5746 5747
    if (lex->current_select->order_list.first && !lex->current_select->braces)
    {
unknown's avatar
unknown committed
5748
      my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY");
unknown's avatar
unknown committed
5749
      DBUG_RETURN(1);
unknown's avatar
VIEW  
unknown committed
5750
    }
5751
    select_lex->include_neighbour(lex->current_select);
5752 5753 5754 5755 5756
    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
(SCRUM)  
unknown committed
5757
  }
unknown's avatar
unknown committed
5758

5759
  select_lex->master_unit()->global_parameters= select_lex;
5760
  select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
5761
  lex->current_select= select_lex;
5762 5763 5764 5765 5766
  /*
    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
5767
  DBUG_RETURN(0);
5768
}
unknown's avatar
unknown committed
5769

unknown's avatar
unknown committed
5770
/**
5771 5772
  Create a select to return the same output as 'SELECT @@var_name'.

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

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

unknown's avatar
unknown committed
5777
  @param var_name		Variable name
5778 5779 5780 5781
*/

void create_select_for_variable(const char *var_name)
{
5782
  THD *thd;
5783
  LEX *lex;
5784
  LEX_STRING tmp, null_lex_string;
5785 5786
  Item *var;
  char buff[MAX_SYS_VAR_LENGTH*2+4+8], *end;
5787
  DBUG_ENTER("create_select_for_variable");
5788 5789

  thd= current_thd;
unknown's avatar
unknown committed
5790
  lex= thd->lex;
5791 5792 5793 5794
  mysql_init_select(lex);
  lex->sql_command= SQLCOM_SELECT;
  tmp.str= (char*) var_name;
  tmp.length=strlen(var_name);
5795
  bzero((char*) &null_lex_string.str, sizeof(null_lex_string));
5796 5797 5798 5799
  /*
    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
5800 5801 5802 5803 5804 5805
  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);
  }
5806 5807 5808
  DBUG_VOID_RETURN;
}

5809

unknown's avatar
unknown committed
5810 5811
void mysql_init_multi_delete(LEX *lex)
{
unknown's avatar
unknown committed
5812
  lex->sql_command=  SQLCOM_DELETE_MULTI;
unknown's avatar
unknown committed
5813
  mysql_init_select(lex);
5814 5815
  lex->select_lex.select_limit= 0;
  lex->unit.select_limit_cnt= HA_POS_ERROR;
unknown's avatar
unknown committed
5816
  lex->select_lex.table_list.save_and_clear(&lex->auxiliary_table_list);
unknown's avatar
VIEW  
unknown committed
5817 5818
  lex->query_tables= 0;
  lex->query_tables_last= &lex->query_tables;
unknown's avatar
unknown committed
5819
}
unknown's avatar
unknown committed
5820

5821

5822 5823 5824 5825
/*
  When you modify mysql_parse(), you may need to mofify
  mysql_test_parse_for_slave() in this same file.
*/
unknown's avatar
unknown committed
5826

5827 5828
/**
  Parse a query.
unknown's avatar
unknown committed
5829 5830 5831 5832 5833 5834

  @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.
5835 5836 5837
*/

void mysql_parse(THD *thd, const char *inBuf, uint length,
5838
                 Parser_state *parser_state)
unknown's avatar
unknown committed
5839
{
5840
  int error;
unknown's avatar
unknown committed
5841
  DBUG_ENTER("mysql_parse");
5842 5843 5844

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

5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862
  /*
    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);
5863

5864
  if (query_cache_send_result_to_client(thd, (char*) inBuf, length) <= 0)
unknown's avatar
unknown committed
5865
  {
unknown's avatar
unknown committed
5866
    LEX *lex= thd->lex;
5867

5868
    bool err= parse_sql(thd, parser_state, NULL);
5869

5870
    if (!err)
unknown's avatar
unknown committed
5871
    {
unknown's avatar
SCRUM:  
unknown committed
5872
#ifndef NO_EMBEDDED_ACCESS_CHECKS
5873
      if (mqh_used && thd->user_connect &&
5874
	  check_mqh(thd, lex->sql_command))
5875 5876 5877 5878
      {
	thd->net.error = 0;
      }
      else
unknown's avatar
SCRUM:  
unknown committed
5879
#endif
5880
      {
5881
	if (! thd->is_error())
unknown's avatar
unknown committed
5882
	{
5883
          const char *found_semicolon= parser_state->m_lip.found_semicolon;
5884 5885 5886 5887 5888 5889 5890 5891 5892 5893
          /*
            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.
          */
5894
          if (found_semicolon && (ulong) (found_semicolon - thd->query()))
5895
            thd->set_query_inner(thd->query(),
5896
                                 (uint32) (found_semicolon -
5897
                                           thd->query() - 1));
5898
          /* Actually execute the query */
5899
          if (found_semicolon)
5900 5901 5902 5903
          {
            lex->safe_to_cache_query= 0;
            thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
          }
5904
          lex->set_trg_event_type_for_tables();
5905
          MYSQL_QUERY_EXEC_START(thd->query(),
5906 5907 5908 5909 5910 5911 5912 5913
                                 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
5914
	}
5915
      }
unknown's avatar
unknown committed
5916 5917
    }
    else
5918
    {
5919
      DBUG_ASSERT(thd->is_error());
5920
      DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
5921
			 thd->is_fatal_error));
5922

5923
      query_cache_abort(&thd->query_cache_tls);
5924
    }
5925 5926 5927 5928 5929 5930
    if (thd->lex->sphead)
    {
      delete thd->lex->sphead;
      thd->lex->sphead= 0;
    }
    lex->unit.cleanup();
5931
    thd_proc_info(thd, "freeing items");
5932
    thd->end_statement();
5933
    thd->cleanup_after_query();
5934
    DBUG_ASSERT(thd->change_list.is_empty());
unknown's avatar
unknown committed
5935
  }
5936

unknown's avatar
unknown committed
5937 5938 5939 5940
  DBUG_VOID_RETURN;
}


unknown's avatar
unknown committed
5941
#ifdef HAVE_REPLICATION
5942 5943 5944 5945
/*
  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
5946
  @retval
5947
    0	cannot be ignored
unknown's avatar
unknown committed
5948
  @retval
5949 5950 5951 5952 5953
    1	can be ignored
*/

bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
{
unknown's avatar
unknown committed
5954
  LEX *lex= thd->lex;
5955
  bool error= 0;
unknown's avatar
unknown committed
5956
  DBUG_ENTER("mysql_test_parse_for_slave");
5957

5958 5959 5960 5961 5962
  Parser_state parser_state;
  if (!(error= parser_state.init(thd, inBuf, length)))
  {
    lex_start(thd);
    mysql_reset_thd_for_next_command(thd);
5963

5964
    if (!parse_sql(thd, & parser_state, NULL) &&
5965
        all_tables_not_ok(thd, lex->select_lex.table_list.first))
5966 5967 5968
      error= 1;                  /* Ignore question */
    thd->end_statement();
  }
5969
  thd->cleanup_after_query();
unknown's avatar
unknown committed
5970
  DBUG_RETURN(error);
5971
}
unknown's avatar
unknown committed
5972
#endif
unknown's avatar
unknown committed
5973

5974

unknown's avatar
unknown committed
5975

unknown's avatar
unknown committed
5976 5977 5978 5979 5980 5981
/**
  Store field definition for create.

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

5983
bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
unknown's avatar
unknown committed
5984
		       char *length, char *decimals,
5985
		       uint type_modifier,
5986 5987
		       Item *default_value, Item *on_update_value,
                       LEX_STRING *comment,
5988 5989
		       char *change,
                       List<String> *interval_list, CHARSET_INFO *cs,
unknown's avatar
unknown committed
5990
		       uint uint_geom_type)
unknown's avatar
unknown committed
5991
{
unknown's avatar
unknown committed
5992
  register Create_field *new_field;
unknown's avatar
unknown committed
5993
  LEX  *lex= thd->lex;
unknown's avatar
unknown committed
5994 5995
  DBUG_ENTER("add_field_to_list");

5996 5997
  if (check_string_char_length(field_name, "", NAME_CHAR_LEN,
                               system_charset_info, 1))
unknown's avatar
unknown committed
5998
  {
5999
    my_error(ER_TOO_LONG_IDENT, MYF(0), field_name->str); /* purecov: inspected */
unknown's avatar
unknown committed
6000 6001 6002 6003
    DBUG_RETURN(1);				/* purecov: inspected */
  }
  if (type_modifier & PRI_KEY_FLAG)
  {
6004
    Key *key;
6005 6006
    lex->col_list.push_back(new Key_part_spec(*field_name, 0));
    key= new Key(Key::PRIMARY, null_lex_str,
6007 6008 6009
                      &default_key_create_info,
                      0, lex->col_list);
    lex->alter_info.key_list.push_back(key);
unknown's avatar
unknown committed
6010 6011 6012 6013
    lex->col_list.empty();
  }
  if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
  {
6014
    Key *key;
6015 6016
    lex->col_list.push_back(new Key_part_spec(*field_name, 0));
    key= new Key(Key::UNIQUE, null_lex_str,
6017 6018 6019
                 &default_key_create_info, 0,
                 lex->col_list);
    lex->alter_info.key_list.push_back(key);
unknown's avatar
unknown committed
6020 6021 6022
    lex->col_list.empty();
  }

6023
  if (default_value)
unknown's avatar
unknown committed
6024
  {
6025
    /* 
unknown's avatar
unknown committed
6026 6027
      Default value should be literal => basic constants =>
      no need fix_fields()
6028 6029 6030
      
      We allow only one function as part of default value - 
      NOW() as default for TIMESTAMP type.
6031
    */
6032 6033
    if (default_value->type() == Item::FUNC_ITEM && 
        !(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC &&
6034
         type == MYSQL_TYPE_TIMESTAMP))
6035
    {
6036
      my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
6037 6038 6039
      DBUG_RETURN(1);
    }
    else if (default_value->type() == Item::NULL_ITEM)
unknown's avatar
unknown committed
6040
    {
6041
      default_value= 0;
6042 6043 6044
      if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
	  NOT_NULL_FLAG)
      {
6045
	my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
6046 6047 6048 6049 6050
	DBUG_RETURN(1);
      }
    }
    else if (type_modifier & AUTO_INCREMENT_FLAG)
    {
6051
      my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
unknown's avatar
unknown committed
6052 6053 6054
      DBUG_RETURN(1);
    }
  }
6055

6056
  if (on_update_value && type != MYSQL_TYPE_TIMESTAMP)
6057
  {
6058
    my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name->str);
6059 6060
    DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
6061

unknown's avatar
unknown committed
6062
  if (!(new_field= new Create_field()) ||
6063
      new_field->init(thd, field_name->str, type, length, decimals, type_modifier,
6064 6065
                      default_value, on_update_value, comment, change,
                      interval_list, cs, uint_geom_type))
unknown's avatar
unknown committed
6066
    DBUG_RETURN(1);
unknown's avatar
unknown committed
6067

6068
  lex->alter_info.create_list.push_back(new_field);
unknown's avatar
unknown committed
6069 6070 6071 6072
  lex->last_field=new_field;
  DBUG_RETURN(0);
}

6073

unknown's avatar
unknown committed
6074
/** Store position for column in ALTER TABLE .. ADD column. */
unknown's avatar
unknown committed
6075 6076 6077

void store_position_for_column(const char *name)
{
6078
  current_thd->lex->last_field->after=my_const_cast(char*) (name);
unknown's avatar
unknown committed
6079 6080 6081
}

bool
unknown's avatar
unknown committed
6082
add_proc_to_list(THD* thd, Item *item)
unknown's avatar
unknown committed
6083 6084 6085 6086
{
  ORDER *order;
  Item	**item_ptr;

unknown's avatar
unknown committed
6087
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*))))
unknown's avatar
unknown committed
6088 6089 6090 6091 6092
    return 1;
  item_ptr = (Item**) (order+1);
  *item_ptr= item;
  order->item=item_ptr;
  order->free_me=0;
6093
  thd->lex->proc_list.link_in_list(order, &order->next);
unknown's avatar
unknown committed
6094 6095 6096 6097
  return 0;
}


unknown's avatar
unknown committed
6098 6099 6100
/**
  save order by and tables in own lists.
*/
unknown's avatar
unknown committed
6101

6102
bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *item,bool asc)
unknown's avatar
unknown committed
6103 6104 6105
{
  ORDER *order;
  DBUG_ENTER("add_to_list");
unknown's avatar
unknown committed
6106
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER))))
unknown's avatar
unknown committed
6107
    DBUG_RETURN(1);
unknown's avatar
unknown committed
6108 6109
  order->item_ptr= item;
  order->item= &order->item_ptr;
unknown's avatar
unknown committed
6110 6111 6112
  order->asc = asc;
  order->free_me=0;
  order->used=0;
6113
  order->counter_used= 0;
6114
  list.link_in_list(order, &order->next);
unknown's avatar
unknown committed
6115 6116 6117 6118
  DBUG_RETURN(0);
}


unknown's avatar
unknown committed
6119 6120 6121 6122 6123 6124 6125 6126 6127 6128
/**
  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
6129
  @param mdl_type       Type of metadata lock to acquire on the table.
unknown's avatar
unknown committed
6130 6131 6132 6133
  @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
6134
      0		Error
unknown's avatar
unknown committed
6135 6136
  @retval
    \#	Pointer to TABLE_LIST element added to the total table list
unknown's avatar
unknown committed
6137 6138
*/

unknown's avatar
unknown committed
6139 6140
TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
					     Table_ident *table,
6141
					     LEX_STRING *alias,
unknown's avatar
unknown committed
6142 6143
					     ulong table_options,
					     thr_lock_type lock_type,
6144
					     enum_mdl_type mdl_type,
unknown's avatar
unknown committed
6145
					     List<Index_hint> *index_hints_arg,
unknown's avatar
unknown committed
6146
                                             LEX_STRING *option)
unknown's avatar
unknown committed
6147 6148
{
  register TABLE_LIST *ptr;
unknown's avatar
unknown committed
6149
  TABLE_LIST *previous_table_ref; /* The table preceding the current one. */
unknown's avatar
unknown committed
6150
  char *alias_str;
6151
  LEX *lex= thd->lex;
unknown's avatar
unknown committed
6152
  DBUG_ENTER("add_table_to_list");
6153
  LINT_INIT(previous_table_ref);
unknown's avatar
unknown committed
6154 6155 6156 6157

  if (!table)
    DBUG_RETURN(0);				// End of memory
  alias_str= alias ? alias->str : table->table.str;
6158
  if (!test(table_options & TL_OPTION_ALIAS) && 
6159
      check_table_name(table->table.str, table->table.length, FALSE))
unknown's avatar
unknown committed
6160
  {
6161
    my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str);
unknown's avatar
unknown committed
6162 6163
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
6164 6165

  if (table->is_derived_table() == FALSE && table->db.str &&
6166
      check_db_name(&table->db))
unknown's avatar
unknown committed
6167 6168 6169 6170
  {
    my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str);
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
6171 6172

  if (!alias)					/* Alias is case sensitive */
6173 6174 6175
  {
    if (table->sel)
    {
unknown's avatar
unknown committed
6176 6177
      my_message(ER_DERIVED_MUST_HAVE_ALIAS,
                 ER(ER_DERIVED_MUST_HAVE_ALIAS), MYF(0));
6178 6179
      DBUG_RETURN(0);
    }
6180
    if (!(alias_str= (char*) thd->memdup(alias_str,table->table.length+1)))
unknown's avatar
unknown committed
6181
      DBUG_RETURN(0);
6182
  }
unknown's avatar
unknown committed
6183
  if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
unknown's avatar
unknown committed
6184
    DBUG_RETURN(0);				/* purecov: inspected */
unknown's avatar
unknown committed
6185
  if (table->db.str)
6186
  {
6187
    ptr->is_fqtn= TRUE;
6188 6189 6190
    ptr->db= table->db.str;
    ptr->db_length= table->db.length;
  }
6191
  else if (lex->copy_db_to(&ptr->db, &ptr->db_length))
unknown's avatar
unknown committed
6192
    DBUG_RETURN(0);
6193 6194
  else
    ptr->is_fqtn= FALSE;
unknown's avatar
unknown committed
6195

6196
  ptr->alias= alias_str;
6197
  ptr->is_alias= alias ? TRUE : FALSE;
6198
  if (lower_case_table_names && table->table.length)
6199
    table->table.length= my_casedn_str(files_charset_info, table->table.str);
6200 6201
  ptr->table_name=table->table.str;
  ptr->table_name_length=table->table.length;
6202
  ptr->lock_type=   lock_type;
unknown's avatar
unknown committed
6203
  ptr->updating=    test(table_options & TL_OPTION_UPDATING);
6204
  /* TODO: remove TL_OPTION_FORCE_INDEX as it looks like it's not used */
unknown's avatar
unknown committed
6205
  ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
unknown's avatar
unknown committed
6206
  ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
6207
  ptr->derived=	    table->sel;
6208
  if (!ptr->derived && is_infoschema_db(ptr->db, ptr->db_length))
6209
  {
6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222
    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);
6223 6224
    if (!schema_table ||
        (schema_table->hidden && 
unknown's avatar
unknown committed
6225
         ((sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0 || 
6226 6227 6228
          /*
            this check is used for show columns|keys from I_S hidden table
          */
unknown's avatar
unknown committed
6229 6230
          lex->sql_command == SQLCOM_SHOW_FIELDS ||
          lex->sql_command == SQLCOM_SHOW_KEYS)))
6231
    {
unknown's avatar
unknown committed
6232
      my_error(ER_UNKNOWN_TABLE, MYF(0),
6233
               ptr->table_name, INFORMATION_SCHEMA_NAME.str);
6234 6235
      DBUG_RETURN(0);
    }
6236
    ptr->schema_table_name= ptr->table_name;
6237 6238
    ptr->schema_table= schema_table;
  }
6239
  ptr->select_lex=  lex->current_select;
unknown's avatar
unknown committed
6240
  ptr->cacheable_table= 1;
6241
  ptr->index_hints= index_hints_arg;
unknown's avatar
unknown committed
6242
  ptr->option= option ? option->str : 0;
unknown's avatar
unknown committed
6243
  /* check that used name is unique */
6244
  if (lock_type != TL_IGNORE)
unknown's avatar
unknown committed
6245
  {
6246
    TABLE_LIST *first_table= table_list.first;
unknown's avatar
unknown committed
6247 6248 6249
    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
6250
	 tables ;
unknown's avatar
VIEW  
unknown committed
6251
	 tables=tables->next_local)
unknown's avatar
unknown committed
6252
    {
6253 6254
      if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
	  !strcmp(ptr->db, tables->db))
unknown's avatar
unknown committed
6255
      {
6256
	my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str); /* purecov: tested */
unknown's avatar
unknown committed
6257 6258
	DBUG_RETURN(0);				/* purecov: tested */
      }
unknown's avatar
unknown committed
6259 6260
    }
  }
unknown's avatar
unknown committed
6261 6262 6263
  /* Store the table reference preceding the current one. */
  if (table_list.elements > 0)
  {
6264 6265 6266
    /*
      table_list.next points to the last inserted TABLE_LIST->next_local'
      element
6267
      We don't use the offsetof() macro here to avoid warnings from gcc
6268
    */
6269 6270 6271
    previous_table_ref= (TABLE_LIST*) ((char*) table_list.next -
                                       ((char*) &(ptr->next_local) -
                                        (char*) ptr));
6272 6273 6274 6275 6276 6277 6278 6279
    /*
      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
6280
  }
6281

unknown's avatar
unknown committed
6282 6283 6284 6285 6286 6287
  /*
    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'.
  */
6288
  table_list.link_in_list(ptr, &ptr->next_local);
unknown's avatar
unknown committed
6289
  ptr->next_name_resolution_table= NULL;
6290
  /* Link table in global list (all used tables) */
6291
  lex->add_to_query_tables(ptr);
6292
  ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type);
unknown's avatar
unknown committed
6293 6294 6295
  DBUG_RETURN(ptr);
}

unknown's avatar
unknown committed
6296

unknown's avatar
unknown committed
6297 6298
/**
  Initialize a new table list for a nested join.
6299

6300 6301 6302 6303 6304 6305 6306 6307
    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
6308 6309 6310 6311 6312 6313
  @param thd         current thread

  @retval
    0   if success
  @retval
    1   otherwise
6314 6315 6316 6317 6318 6319 6320
*/

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

6322 6323
  if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
                                       sizeof(NESTED_JOIN))))
6324
    DBUG_RETURN(1);
6325
  nested_join= ptr->nested_join=
6326
    ((NESTED_JOIN*) ((uchar*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
6327

6328 6329 6330
  join_list->push_front(ptr);
  ptr->embedding= embedding;
  ptr->join_list= join_list;
unknown's avatar
unknown committed
6331
  ptr->alias= (char*) "(nested_join)";
6332 6333 6334 6335 6336 6337 6338
  embedding= ptr;
  join_list= &nested_join->join_list;
  join_list->empty();
  DBUG_RETURN(0);
}


unknown's avatar
unknown committed
6339 6340
/**
  End a nested join table list.
6341 6342 6343

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

unknown's avatar
unknown committed
6346 6347 6348 6349 6350
  @param thd         current thread

  @return
    - Pointer to TABLE_LIST element added to the total table list, if success
    - 0, otherwise
6351 6352 6353 6354 6355
*/

TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
{
  TABLE_LIST *ptr;
6356
  NESTED_JOIN *nested_join;
6357
  DBUG_ENTER("end_nested_join");
6358

unknown's avatar
unknown committed
6359
  DBUG_ASSERT(embedding);
6360 6361 6362
  ptr= embedding;
  join_list= ptr->join_list;
  embedding= ptr->embedding;
6363
  nested_join= ptr->nested_join;
6364 6365 6366 6367 6368 6369 6370 6371 6372
  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;
  }
6373
  else if (nested_join->join_list.elements == 0)
unknown's avatar
unknown committed
6374 6375
  {
    join_list->pop();
6376
    ptr= 0;                                     // return value
unknown's avatar
unknown committed
6377
  }
6378 6379 6380 6381
  DBUG_RETURN(ptr);
}


unknown's avatar
unknown committed
6382 6383
/**
  Nest last join operation.
6384 6385 6386

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

unknown's avatar
unknown committed
6387
  @param thd         current thread
6388

unknown's avatar
unknown committed
6389
  @retval
6390
    0  Error
unknown's avatar
unknown committed
6391 6392
  @retval
    \#  Pointer to TABLE_LIST element created for the new nested join
6393 6394 6395 6396 6397 6398
*/

TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
{
  TABLE_LIST *ptr;
  NESTED_JOIN *nested_join;
6399
  List<TABLE_LIST> *embedded_list;
6400
  DBUG_ENTER("nest_last_join");
6401

6402 6403
  if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
                                       sizeof(NESTED_JOIN))))
6404
    DBUG_RETURN(0);
6405
  nested_join= ptr->nested_join=
6406
    ((NESTED_JOIN*) ((uchar*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
6407

6408 6409
  ptr->embedding= embedding;
  ptr->join_list= join_list;
unknown's avatar
unknown committed
6410
  ptr->alias= (char*) "(nest_last_join)";
6411
  embedded_list= &nested_join->join_list;
6412
  embedded_list->empty();
6413 6414

  for (uint i=0; i < 2; i++)
6415 6416 6417 6418 6419
  {
    TABLE_LIST *table= join_list->pop();
    table->join_list= embedded_list;
    table->embedding= ptr;
    embedded_list->push_back(table);
unknown's avatar
unknown committed
6420 6421 6422 6423 6424 6425 6426
    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
6427 6428
      if (prev_join_using)
        ptr->join_using_fields= prev_join_using;
unknown's avatar
unknown committed
6429
    }
6430 6431 6432 6433 6434 6435 6436
  }
  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
6437 6438
/**
  Add a table to the current join list.
6439 6440 6441 6442 6443 6444

    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
6445 6446 6447
  @param table       the table to add

  @return
6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460
    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
6461 6462
/**
  Convert a right join into equivalent left join.
6463 6464

    The function takes the current join list t[0],t[1] ... and
6465 6466 6467 6468 6469 6470
    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
6471
  @verbatim
6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482
    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
6483
   @endverbatim
6484

unknown's avatar
unknown committed
6485
  @param thd         current thread
6486

unknown's avatar
unknown committed
6487 6488 6489
  @return
    - Pointer to the table representing the inner table, if success
    - 0, otherwise
6490 6491
*/

6492
TABLE_LIST *st_select_lex::convert_right_join()
6493 6494
{
  TABLE_LIST *tab2= join_list->pop();
6495
  TABLE_LIST *tab1= join_list->pop();
6496 6497 6498 6499 6500 6501 6502 6503 6504
  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
6505 6506
/**
  Set lock for all tables in current select level.
unknown's avatar
unknown committed
6507

unknown's avatar
unknown committed
6508
  @param lock_type			Lock to set for tables
unknown's avatar
unknown committed
6509

unknown's avatar
unknown committed
6510
  @note
unknown's avatar
unknown committed
6511 6512 6513 6514 6515
    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
6516
void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
unknown's avatar
unknown committed
6517 6518 6519 6520 6521
{
  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));
6522
  for (TABLE_LIST *tables= table_list.first;
unknown's avatar
VIEW  
unknown committed
6523 6524
       tables;
       tables= tables->next_local)
unknown's avatar
unknown committed
6525 6526 6527
  {
    tables->lock_type= lock_type;
    tables->updating=  for_update;
6528 6529
    tables->mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_WRITE) ?
                                 MDL_SHARED_WRITE : MDL_SHARED_READ);
unknown's avatar
unknown committed
6530 6531 6532 6533
  }
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
6534

unknown's avatar
unknown committed
6535 6536
/**
  Create a fake SELECT_LEX for a unit.
unknown's avatar
unknown committed
6537 6538 6539 6540

    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
6541
    @verbatim
unknown's avatar
unknown committed
6542
    (SELECT ... ORDER BY order_list [LIMIT n]) ORDER BY ... 
unknown's avatar
unknown committed
6543
    @endvarbatim
unknown's avatar
unknown committed
6544
    or of the form
unknown's avatar
unknown committed
6545
    @varbatim
unknown's avatar
unknown committed
6546
    (SELECT ... ORDER BY LIMIT n) ORDER BY ...
unknown's avatar
unknown committed
6547
    @endvarbatim
unknown's avatar
unknown committed
6548
  
unknown's avatar
unknown committed
6549 6550 6551
  @param thd_arg		   thread handle

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

unknown's avatar
unknown committed
6555
  @retval
unknown's avatar
unknown committed
6556
    1     on failure to create the object
unknown's avatar
unknown committed
6557
  @retval
unknown's avatar
unknown committed
6558 6559 6560
    0     on success
*/

unknown's avatar
unknown committed
6561
bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
unknown's avatar
unknown committed
6562 6563 6564 6565
{
  SELECT_LEX *first_sl= first_select();
  DBUG_ENTER("add_fake_select_lex");
  DBUG_ASSERT(!fake_select_lex);
unknown's avatar
unknown committed
6566

unknown's avatar
unknown committed
6567
  if (!(fake_select_lex= new (thd_arg->mem_root) SELECT_LEX()))
unknown's avatar
unknown committed
6568 6569 6570 6571
      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
6572
  fake_select_lex->parent_lex= thd_arg->lex; /* Used in init_query. */
unknown's avatar
unknown committed
6573 6574
  fake_select_lex->make_empty_select();
  fake_select_lex->linkage= GLOBAL_OPTIONS_TYPE;
6575 6576
  fake_select_lex->select_limit= 0;

unknown's avatar
unknown committed
6577
  fake_select_lex->context.outer_context=first_sl->context.outer_context;
6578 6579 6580
  /* 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
6581

unknown's avatar
unknown committed
6582
  if (!is_union())
unknown's avatar
unknown committed
6583 6584 6585 6586 6587 6588 6589 6590 6591
  {
    /* 
      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
6592
    thd_arg->lex->current_select= fake_select_lex;
unknown's avatar
unknown committed
6593
  }
unknown's avatar
unknown committed
6594
  thd_arg->lex->pop_context();
unknown's avatar
unknown committed
6595 6596 6597
  DBUG_RETURN(0);
}

6598

unknown's avatar
unknown committed
6599
/**
6600 6601
  Push a new name resolution context for a JOIN ... ON clause to the
  context stack of a query block.
unknown's avatar
unknown committed
6602 6603

    Create a new name resolution context for a JOIN ... ON clause,
6604 6605 6606
    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
6607

unknown's avatar
unknown committed
6608 6609 6610 6611 6612
  @param thd       pointer to current thread
  @param left_op   left  operand of the JOIN
  @param right_op  rigth operand of the JOIN

  @retval
6613
    FALSE  if all is OK
unknown's avatar
unknown committed
6614
  @retval
6615
    TRUE   if a memory allocation error occured
unknown's avatar
unknown committed
6616 6617
*/

6618 6619 6620
bool
push_new_name_resolution_context(THD *thd,
                                 TABLE_LIST *left_op, TABLE_LIST *right_op)
unknown's avatar
unknown committed
6621 6622
{
  Name_resolution_context *on_context;
6623
  if (!(on_context= new (thd->mem_root) Name_resolution_context))
6624
    return TRUE;
unknown's avatar
unknown committed
6625 6626 6627 6628 6629
  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();
6630
  return thd->lex->push_context(on_context);
unknown's avatar
unknown committed
6631 6632 6633
}


unknown's avatar
unknown committed
6634
/**
unknown's avatar
unknown committed
6635 6636 6637 6638
  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
6639 6640
  @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
6641

unknown's avatar
unknown committed
6642
  @retval
unknown's avatar
unknown committed
6643
    FALSE  if there was some error
unknown's avatar
unknown committed
6644
  @retval
unknown's avatar
unknown committed
6645 6646 6647 6648
    TRUE   if all is OK
*/

void add_join_on(TABLE_LIST *b, Item *expr)
unknown's avatar
unknown committed
6649
{
6650
  if (expr)
6651
  {
6652
    if (!b->on_expr)
unknown's avatar
unknown committed
6653
      b->on_expr= expr;
6654 6655
    else
    {
unknown's avatar
unknown committed
6656 6657 6658 6659 6660 6661
      /*
        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);
6662 6663
    }
    b->on_expr->top_level_item();
6664
  }
unknown's avatar
unknown committed
6665 6666 6667
}


unknown's avatar
unknown committed
6668
/**
unknown's avatar
unknown committed
6669 6670
  Mark that there is a NATURAL JOIN or JOIN ... USING between two
  tables.
6671

unknown's avatar
unknown committed
6672 6673 6674 6675 6676 6677 6678 6679 6680 6681
    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
6682
  @verbatim
6683 6684 6685
    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
6686

unknown's avatar
unknown committed
6687 6688 6689
    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
6690

unknown's avatar
unknown committed
6691 6692 6693
    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
6694 6695 6696 6697 6698
   @endverbatim

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

unknown's avatar
unknown committed
6701 6702
void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
                      SELECT_LEX *lex)
unknown's avatar
unknown committed
6703
{
unknown's avatar
unknown committed
6704
  b->natural_join= a;
unknown's avatar
unknown committed
6705
  lex->prev_join_using= using_fields;
unknown's avatar
unknown committed
6706 6707
}

unknown's avatar
unknown committed
6708

6709
/**
6710 6711
  Reload/resets privileges and the different caches.

6712 6713 6714 6715
  @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.
6716
               
6717 6718 6719 6720 6721 6722 6723 6724
  @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
6725
    @retval !=0  Error; thd->killed is set or thd->is_error() is true
6726 6727
*/

6728 6729
bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
                          bool *write_to_binlog)
unknown's avatar
unknown committed
6730 6731 6732
{
  bool result=0;
  select_errors=0;				/* Write if more errors */
6733
  bool tmp_write_to_binlog= 1;
6734

6735
  DBUG_ASSERT(!thd || !thd->in_sub_stmt);
6736

unknown's avatar
SCRUM  
unknown committed
6737
#ifndef NO_EMBEDDED_ACCESS_CHECKS
unknown's avatar
unknown committed
6738 6739
  if (options & REFRESH_GRANT)
  {
6740 6741 6742 6743 6744 6745
    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)))
6746 6747
    {
      thd->thread_stack= (char*) &tmp_thd;
6748
      thd->store_globals();
6749
    }
6750

6751 6752
    if (thd)
    {
6753 6754
      bool reload_acl_failed= acl_reload(thd);
      bool reload_grants_failed= grant_reload(thd);
Kristofer Pettersson's avatar
Kristofer Pettersson committed
6755
      bool reload_servers_failed= servers_reload(thd);
6756

Kristofer Pettersson's avatar
Kristofer Pettersson committed
6757
      if (reload_acl_failed || reload_grants_failed || reload_servers_failed)
6758
      {
6759
        result= 1;
6760 6761 6762 6763 6764 6765
        /*
          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");
      }
6766
    }
6767

6768 6769 6770 6771 6772 6773 6774
    if (tmp_thd)
    {
      delete tmp_thd;
      /* Remember that we don't have a THD */
      my_pthread_setspecific_ptr(THR_THD,  0);
      thd= 0;
    }
6775
    reset_mqh((LEX_USER *)NULL, TRUE);
unknown's avatar
unknown committed
6776
  }
unknown's avatar
SCRUM  
unknown committed
6777
#endif
unknown's avatar
unknown committed
6778 6779
  if (options & REFRESH_LOG)
  {
6780
    /*
unknown's avatar
unknown committed
6781
      Flush the normal query log, the update log, the binary log,
unknown's avatar
unknown committed
6782 6783
      the slow query log, the relay log (if it exists) and the log
      tables.
6784
    */
unknown's avatar
unknown committed
6785

6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809
    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)
  {
6810
    /*
unknown's avatar
unknown committed
6811 6812 6813 6814
      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)
6815 6816
    */
    tmp_write_to_binlog= 0;
6817
    if (mysql_bin_log.is_open())
6818
      mysql_bin_log.rotate_and_purge(RP_FORCE_ROTATE);
6819 6820 6821
  }
  if (options & REFRESH_RELAY_LOG)
  {
unknown's avatar
unknown committed
6822
#ifdef HAVE_REPLICATION
Marc Alff's avatar
Marc Alff committed
6823
    mysql_mutex_lock(&LOCK_active_mi);
6824
    rotate_relay_log(active_mi);
Marc Alff's avatar
Marc Alff committed
6825
    mysql_mutex_unlock(&LOCK_active_mi);
unknown's avatar
unknown committed
6826
#endif
unknown's avatar
unknown committed
6827
  }
unknown's avatar
unknown committed
6828
#ifdef HAVE_QUERY_CACHE
unknown's avatar
unknown committed
6829 6830
  if (options & REFRESH_QUERY_CACHE_FREE)
  {
unknown's avatar
unknown committed
6831
    query_cache.pack();				// FLUSH QUERY CACHE
6832
    options &= ~REFRESH_QUERY_CACHE;    // Don't flush cache, just free memory
unknown's avatar
unknown committed
6833 6834 6835
  }
  if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
  {
unknown's avatar
unknown committed
6836
    query_cache.flush();			// RESET QUERY CACHE
unknown's avatar
unknown committed
6837
  }
unknown's avatar
unknown committed
6838
#endif /*HAVE_QUERY_CACHE*/
6839

6840 6841
  DBUG_ASSERT(!thd || thd->locked_tables_mode ||
              !thd->mdl_context.has_locks() ||
6842
              thd->handler_tables_hash.records ||
6843
              thd->global_read_lock.is_acquired());
6844

6845 6846 6847 6848 6849
  /*
    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
6850
  {
6851
    if ((options & REFRESH_READ_LOCK) && thd)
unknown's avatar
unknown committed
6852
    {
6853
      /*
6854 6855 6856 6857
        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.
6858
      */
Konstantin Osipov's avatar
Konstantin Osipov committed
6859
      if (thd->locked_tables_mode)
6860
      {
6861 6862
        my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
        return 1;
6863
      }
unknown's avatar
unknown committed
6864 6865 6866 6867
      /*
	Writing to the binlog could cause deadlocks, as we don't log
	UNLOCK TABLES
      */
6868
      tmp_write_to_binlog= 0;
6869
      if (thd->global_read_lock.lock_global_read_lock(thd))
unknown's avatar
unknown committed
6870
	return 1;                               // Killed
Kristofer Pettersson's avatar
Kristofer Pettersson committed
6871
      if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ?
6872
                              FALSE : TRUE))
6873 6874
          result= 1;
      
6875
      if (thd->global_read_lock.make_global_read_lock_block_commit(thd)) // Killed
6876 6877
      {
        /* Don't leave things in a half-locked state */
6878
        thd->global_read_lock.unlock_global_read_lock(thd);
6879 6880
        return 1;
      }
unknown's avatar
unknown committed
6881
    }
6882
    else
6883
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
6884
      if (thd && thd->locked_tables_mode)
6885 6886 6887 6888 6889 6890 6891 6892
      {
        /*
          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)
6893 6894
            if (!find_table_for_mdl_upgrade(thd->open_tables, t->db,
                                            t->table_name, FALSE))
6895 6896 6897 6898 6899 6900
              return 1;
        }
        else
        {
          for (TABLE *tab= thd->open_tables; tab; tab= tab->next)
          {
6901
            if (! tab->mdl_ticket->is_upgradable_or_exclusive())
6902 6903 6904 6905 6906 6907 6908 6909 6910
            {
              my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0),
                       tab->s->table_name.str);
              return 1;
            }
          }
        }
      }

Kristofer Pettersson's avatar
Kristofer Pettersson committed
6911
      if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ?
6912
                              FALSE : TRUE))
6913 6914
        result= 1;
    }
unknown's avatar
unknown committed
6915
    my_dbopt_cleanup();
unknown's avatar
unknown committed
6916 6917 6918
  }
  if (options & REFRESH_HOSTS)
    hostname_cache_refresh();
unknown's avatar
unknown committed
6919
  if (thd && (options & REFRESH_STATUS))
unknown's avatar
unknown committed
6920
    refresh_status(thd);
unknown's avatar
unknown committed
6921 6922
  if (options & REFRESH_THREADS)
    flush_thread_cache();
unknown's avatar
unknown committed
6923
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
6924
  if (options & REFRESH_MASTER)
6925
  {
6926
    DBUG_ASSERT(thd);
6927
    tmp_write_to_binlog= 0;
6928
    if (reset_master(thd))
unknown's avatar
unknown committed
6929
    {
6930
      result=1;
unknown's avatar
unknown committed
6931
    }
6932
  }
6933
#endif
unknown's avatar
unknown committed
6934
#ifdef OPENSSL
6935 6936
   if (options & REFRESH_DES_KEY_FILE)
   {
6937 6938
     if (des_key_file && load_des_key_file(des_key_file))
         result= 1;
6939 6940
   }
#endif
unknown's avatar
unknown committed
6941
#ifdef HAVE_REPLICATION
6942 6943
 if (options & REFRESH_SLAVE)
 {
6944
   tmp_write_to_binlog= 0;
Marc Alff's avatar
Marc Alff committed
6945
   mysql_mutex_lock(&LOCK_active_mi);
6946
   if (reset_slave(thd, active_mi))
6947
     result=1;
Marc Alff's avatar
Marc Alff committed
6948
   mysql_mutex_unlock(&LOCK_active_mi);
6949
 }
6950
#endif
6951
 if (options & REFRESH_USER_RESOURCES)
unknown's avatar
unknown committed
6952
   reset_mqh((LEX_USER *) NULL, 0);             /* purecov: inspected */
unknown's avatar
unknown committed
6953
 *write_to_binlog= tmp_write_to_binlog;
6954 6955 6956
 /*
   If the query was killed then this function must fail.
 */
6957
 return result || (thd ? thd->killed : 0);
unknown's avatar
unknown committed
6958 6959
}

6960

unknown's avatar
unknown committed
6961 6962
/**
  kill on thread.
6963

unknown's avatar
unknown committed
6964 6965 6966
  @param thd			Thread class
  @param id			Thread id
  @param only_kill_query        Should it kill the query or the connection
6967

unknown's avatar
unknown committed
6968
  @note
6969 6970 6971
    This is written such that we have a short lock on LOCK_thread_count
*/

6972
uint kill_one_thread(THD *thd, ulong id, bool only_kill_query)
unknown's avatar
unknown committed
6973 6974 6975
{
  THD *tmp;
  uint error=ER_NO_SUCH_THREAD;
6976 6977
  DBUG_ENTER("kill_one_thread");
  DBUG_PRINT("enter", ("id=%lu only_kill=%d", id, only_kill_query));
Marc Alff's avatar
Marc Alff committed
6978
  mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
6979
  I_List_iterator<THD> it(threads);
unknown's avatar
unknown committed
6980 6981
  while ((tmp=it++))
  {
unknown's avatar
unknown committed
6982 6983
    if (tmp->command == COM_DAEMON)
      continue;
unknown's avatar
unknown committed
6984 6985
    if (tmp->thread_id == id)
    {
Marc Alff's avatar
Marc Alff committed
6986
      mysql_mutex_lock(&tmp->LOCK_thd_data);    // Lock from delete
6987
      break;
unknown's avatar
unknown committed
6988 6989
    }
  }
Marc Alff's avatar
Marc Alff committed
6990
  mysql_mutex_unlock(&LOCK_thread_count);
6991 6992
  if (tmp)
  {
6993 6994 6995 6996 6997

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

6998 6999 7000
      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).
7001

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

7007
      If user of both killer and killee are non-NULL, proceed with
7008 7009 7010
      slayage if both are string-equal.
    */

7011
    if ((thd->security_ctx->master_access & SUPER_ACL) ||
7012
        thd->security_ctx->user_matches(tmp->security_ctx))
7013
    {
unknown's avatar
SCRUM  
unknown committed
7014
      tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
7015 7016 7017 7018
      error=0;
    }
    else
      error=ER_KILL_DENIED_ERROR;
Marc Alff's avatar
Marc Alff committed
7019
    mysql_mutex_unlock(&tmp->LOCK_thd_data);
7020
  }
7021 7022 7023 7024
  DBUG_PRINT("exit", ("%d", error));
  DBUG_RETURN(error);
}

7025

7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039
/*
  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)))
7040
    my_ok(thd);
unknown's avatar
unknown committed
7041
  else
unknown's avatar
unknown committed
7042
    my_error(error, MYF(0), id);
unknown's avatar
unknown committed
7043 7044
}

unknown's avatar
unknown committed
7045

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

unknown's avatar
unknown committed
7048 7049
bool append_file_to_dir(THD *thd, const char **filename_ptr,
                        const char *table_name)
unknown's avatar
unknown committed
7050
{
7051
  char buff[FN_REFLEN],*ptr, *end;
unknown's avatar
unknown committed
7052 7053 7054 7055 7056 7057 7058
  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))
  {
unknown's avatar
unknown committed
7059
    my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr);
unknown's avatar
unknown committed
7060 7061 7062 7063
    return 1;
  }
  /* Fix is using unix filename format on dos */
  strmov(buff,*filename_ptr);
7064
  end=convert_dirname(buff, *filename_ptr, NullS);
7065
  if (!(ptr= (char*) thd->alloc((size_t) (end-buff) + strlen(table_name)+1)))
unknown's avatar
unknown committed
7066 7067
    return 1;					// End of memory
  *filename_ptr=ptr;
7068
  strxmov(ptr,buff,table_name,NullS);
unknown's avatar
unknown committed
7069 7070
  return 0;
}
7071

7072

unknown's avatar
unknown committed
7073 7074
/**
  Check if the select is a simple select (not an union).
7075

unknown's avatar
unknown committed
7076
  @retval
7077
    0	ok
unknown's avatar
unknown committed
7078
  @retval
7079 7080 7081 7082 7083 7084
    1	error	; In this case the error messege is sent to the client
*/

bool check_simple_select()
{
  THD *thd= current_thd;
7085 7086
  LEX *lex= thd->lex;
  if (lex->current_select != &lex->select_lex)
7087 7088
  {
    char command[80];
7089
    Lex_input_stream *lip= & thd->m_parser_state->m_lip;
7090 7091
    strmake(command, lip->yylval->symbol.str,
	    min(lip->yylval->symbol.length, sizeof(command)-1));
7092
    my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command);
7093 7094 7095 7096
    return 1;
  }
  return 0;
}
unknown's avatar
unknown committed
7097

unknown's avatar
unknown committed
7098

unknown's avatar
unknown committed
7099
Comp_creator *comp_eq_creator(bool invert)
unknown's avatar
unknown committed
7100
{
unknown's avatar
unknown committed
7101
  return invert?(Comp_creator *)&ne_creator:(Comp_creator *)&eq_creator;
unknown's avatar
unknown committed
7102 7103
}

unknown's avatar
unknown committed
7104

unknown's avatar
unknown committed
7105
Comp_creator *comp_ge_creator(bool invert)
unknown's avatar
unknown committed
7106
{
unknown's avatar
unknown committed
7107
  return invert?(Comp_creator *)&lt_creator:(Comp_creator *)&ge_creator;
unknown's avatar
unknown committed
7108 7109
}

unknown's avatar
unknown committed
7110

unknown's avatar
unknown committed
7111
Comp_creator *comp_gt_creator(bool invert)
unknown's avatar
unknown committed
7112
{
unknown's avatar
unknown committed
7113
  return invert?(Comp_creator *)&le_creator:(Comp_creator *)&gt_creator;
unknown's avatar
unknown committed
7114 7115
}

unknown's avatar
unknown committed
7116

unknown's avatar
unknown committed
7117
Comp_creator *comp_le_creator(bool invert)
unknown's avatar
unknown committed
7118
{
unknown's avatar
unknown committed
7119
  return invert?(Comp_creator *)&gt_creator:(Comp_creator *)&le_creator;
unknown's avatar
unknown committed
7120 7121
}

unknown's avatar
unknown committed
7122

unknown's avatar
unknown committed
7123
Comp_creator *comp_lt_creator(bool invert)
unknown's avatar
unknown committed
7124
{
unknown's avatar
unknown committed
7125
  return invert?(Comp_creator *)&ge_creator:(Comp_creator *)&lt_creator;
unknown's avatar
unknown committed
7126 7127
}

unknown's avatar
unknown committed
7128

unknown's avatar
unknown committed
7129
Comp_creator *comp_ne_creator(bool invert)
unknown's avatar
unknown committed
7130
{
unknown's avatar
unknown committed
7131
  return invert?(Comp_creator *)&eq_creator:(Comp_creator *)&ne_creator;
unknown's avatar
unknown committed
7132
}
unknown's avatar
unknown committed
7133 7134


unknown's avatar
unknown committed
7135 7136
/**
  Construct ALL/ANY/SOME subquery Item.
unknown's avatar
unknown committed
7137

unknown's avatar
unknown committed
7138 7139 7140 7141
  @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
7142

unknown's avatar
unknown committed
7143
  @return
unknown's avatar
unknown committed
7144 7145 7146 7147 7148 7149 7150
    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
7151
  if ((cmp == &comp_eq_creator) && !all)       //  = ANY <=> IN
unknown's avatar
unknown committed
7152
    return new Item_in_subselect(left_expr, select_lex);
unknown's avatar
unknown committed
7153 7154

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

  Item_allany_subselect *it=
7158
    new Item_allany_subselect(left_expr, cmp, select_lex, all);
unknown's avatar
unknown committed
7159
  if (all)
7160
    return it->upper_item= new Item_func_not_all(it);	/* ALL */
unknown's avatar
unknown committed
7161

7162
  return it->upper_item= new Item_func_nop_all(it);      /* ANY/SOME */
unknown's avatar
unknown committed
7163
}
7164 7165


unknown's avatar
unknown committed
7166 7167
/**
  Multi update query pre-check.
7168

unknown's avatar
unknown committed
7169 7170
  @param thd		Thread handler
  @param tables	Global/local table list (have to be the same)
7171

unknown's avatar
unknown committed
7172
  @retval
unknown's avatar
unknown committed
7173
    FALSE OK
unknown's avatar
unknown committed
7174
  @retval
unknown's avatar
unknown committed
7175
    TRUE  Error
7176
*/
unknown's avatar
unknown committed
7177

unknown's avatar
unknown committed
7178
bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
7179 7180 7181 7182 7183
{
  const char *msg= 0;
  TABLE_LIST *table;
  LEX *lex= thd->lex;
  SELECT_LEX *select_lex= &lex->select_lex;
unknown's avatar
VIEW  
unknown committed
7184
  DBUG_ENTER("multi_update_precheck");
7185 7186 7187

  if (select_lex->item_list.elements != lex->value_list.elements)
  {
7188
    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
unknown's avatar
unknown committed
7189
    DBUG_RETURN(TRUE);
7190 7191 7192 7193 7194
  }
  /*
    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
7195
  for (table= tables; table; table= table->next_local)
7196
  {
7197 7198 7199
    if (table->derived)
      table->grant.privilege= SELECT_ACL;
    else if ((check_access(thd, UPDATE_ACL, table->db,
Marc Alff's avatar
Marc Alff committed
7200 7201 7202
                           &table->grant.privilege,
                           &table->grant.m_internal,
                           0, 1) ||
7203
              check_grant(thd, UPDATE_ACL, table, FALSE, 1, TRUE)) &&
unknown's avatar
unknown committed
7204
             (check_access(thd, SELECT_ACL, table->db,
Marc Alff's avatar
Marc Alff committed
7205 7206 7207
                           &table->grant.privilege,
                           &table->grant.m_internal,
                           0, 0) ||
7208
              check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE)))
unknown's avatar
unknown committed
7209
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7210

unknown's avatar
VIEW  
unknown committed
7211
    table->table_in_first_from_clause= 1;
7212
  }
unknown's avatar
unknown committed
7213 7214 7215
  /*
    Is there tables of subqueries?
  */
7216
  if (&lex->select_lex != lex->all_selects_list)
7217
  {
7218
    DBUG_PRINT("info",("Checking sub query list"));
unknown's avatar
VIEW  
unknown committed
7219
    for (table= tables; table; table= table->next_global)
7220
    {
7221
      if (!table->table_in_first_from_clause)
7222 7223
      {
	if (check_access(thd, SELECT_ACL, table->db,
Marc Alff's avatar
Marc Alff committed
7224 7225 7226
                         &table->grant.privilege,
                         &table->grant.m_internal,
                         0, 0) ||
7227
	    check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE))
unknown's avatar
unknown committed
7228
	  DBUG_RETURN(TRUE);
7229 7230 7231 7232 7233 7234
      }
    }
  }

  if (select_lex->order_list.elements)
    msg= "ORDER BY";
7235
  else if (select_lex->select_limit)
7236 7237 7238 7239
    msg= "LIMIT";
  if (msg)
  {
    my_error(ER_WRONG_USAGE, MYF(0), "UPDATE", msg);
unknown's avatar
unknown committed
7240
    DBUG_RETURN(TRUE);
7241
  }
unknown's avatar
unknown committed
7242
  DBUG_RETURN(FALSE);
7243 7244
}

unknown's avatar
unknown committed
7245 7246
/**
  Multi delete query pre-check.
7247

unknown's avatar
unknown committed
7248 7249
  @param thd			Thread handler
  @param tables		Global/local table list
7250

unknown's avatar
unknown committed
7251
  @retval
unknown's avatar
unknown committed
7252
    FALSE OK
unknown's avatar
unknown committed
7253
  @retval
unknown's avatar
unknown committed
7254
    TRUE  error
7255
*/
unknown's avatar
unknown committed
7256

7257
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
7258 7259
{
  SELECT_LEX *select_lex= &thd->lex->select_lex;
7260
  TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
7261
  TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
unknown's avatar
VIEW  
unknown committed
7262
  DBUG_ENTER("multi_delete_precheck");
unknown's avatar
unknown committed
7263

7264 7265
  /* sql_yacc guarantees that tables and aux_tables are not zero */
  DBUG_ASSERT(aux_tables != 0);
7266
  if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
7267 7268 7269 7270 7271 7272 7273 7274
    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;
7275
  if (check_table_access(thd, DELETE_ACL, aux_tables, FALSE, UINT_MAX, FALSE))
7276 7277
  {
    thd->lex->query_tables_own_last= save_query_tables_own_last;
unknown's avatar
unknown committed
7278
    DBUG_RETURN(TRUE);
7279 7280 7281
  }
  thd->lex->query_tables_own_last= save_query_tables_own_last;

7282
  if ((thd->variables.option_bits & OPTION_SAFE_UPDATES) && !select_lex->where)
7283
  {
unknown's avatar
unknown committed
7284 7285
    my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
               ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
unknown's avatar
unknown committed
7286
    DBUG_RETURN(TRUE);
7287
  }
7288 7289 7290 7291
  DBUG_RETURN(FALSE);
}


7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 7316 7317 7318 7319 7320 7321 7322 7323 7324 7325 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343 7344 7345 7346 7347 7348
/*
  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
7349
/**
7350 7351 7352
  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
7353
  @param lex   pointer to LEX representing multi-delete
7354

unknown's avatar
unknown committed
7355 7356 7357 7358
  @retval
    FALSE   success
  @retval
    TRUE    error
7359 7360 7361 7362
*/

bool multi_delete_set_locks_and_link_aux_tables(LEX *lex)
{
7363
  TABLE_LIST *tables= lex->select_lex.table_list.first;
7364 7365 7366 7367 7368
  TABLE_LIST *target_tbl;
  DBUG_ENTER("multi_delete_set_locks_and_link_aux_tables");

  lex->table_count= 0;

7369
  for (target_tbl= lex->auxiliary_table_list.first;
7370
       target_tbl; target_tbl= target_tbl->next_local)
7371
  {
7372
    lex->table_count++;
7373
    /* All tables in aux_tables must be found in FROM PART */
7374
    TABLE_LIST *walk= multi_delete_table_match(lex, target_tbl, tables);
7375
    if (!walk)
unknown's avatar
unknown committed
7376
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7377 7378 7379 7380 7381
    if (!walk->derived)
    {
      target_tbl->table_name= walk->table_name;
      target_tbl->table_name_length= walk->table_name_length;
    }
unknown's avatar
merged  
unknown committed
7382
    walk->updating= target_tbl->updating;
unknown's avatar
unknown committed
7383
    walk->lock_type= target_tbl->lock_type;
7384 7385 7386
    /* 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
7387
    target_tbl->correspondent_table= walk;	// Remember corresponding table
7388
  }
unknown's avatar
unknown committed
7389
  DBUG_RETURN(FALSE);
7390 7391 7392
}


unknown's avatar
unknown committed
7393 7394
/**
  simple UPDATE query pre-check.
unknown's avatar
unknown committed
7395

unknown's avatar
unknown committed
7396 7397
  @param thd		Thread handler
  @param tables	Global table list
unknown's avatar
unknown committed
7398

unknown's avatar
unknown committed
7399
  @retval
unknown's avatar
unknown committed
7400
    FALSE OK
unknown's avatar
unknown committed
7401
  @retval
unknown's avatar
unknown committed
7402
    TRUE  Error
unknown's avatar
unknown committed
7403
*/
unknown's avatar
unknown committed
7404

unknown's avatar
unknown committed
7405
bool update_precheck(THD *thd, TABLE_LIST *tables)
unknown's avatar
unknown committed
7406 7407 7408 7409
{
  DBUG_ENTER("update_precheck");
  if (thd->lex->select_lex.item_list.elements != thd->lex->value_list.elements)
  {
unknown's avatar
unknown committed
7410
    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
unknown's avatar
unknown committed
7411
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7412
  }
unknown's avatar
unknown committed
7413
  DBUG_RETURN(check_one_table_access(thd, UPDATE_ACL, tables));
unknown's avatar
unknown committed
7414 7415 7416
}


unknown's avatar
unknown committed
7417 7418
/**
  simple DELETE query pre-check.
unknown's avatar
unknown committed
7419

unknown's avatar
unknown committed
7420 7421
  @param thd		Thread handler
  @param tables	Global table list
unknown's avatar
unknown committed
7422

unknown's avatar
unknown committed
7423
  @retval
unknown's avatar
unknown committed
7424
    FALSE  OK
unknown's avatar
unknown committed
7425
  @retval
unknown's avatar
unknown committed
7426
    TRUE   error
unknown's avatar
unknown committed
7427
*/
unknown's avatar
unknown committed
7428

unknown's avatar
unknown committed
7429
bool delete_precheck(THD *thd, TABLE_LIST *tables)
unknown's avatar
unknown committed
7430 7431 7432
{
  DBUG_ENTER("delete_precheck");
  if (check_one_table_access(thd, DELETE_ACL, tables))
unknown's avatar
unknown committed
7433
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7434
  /* Set privilege for the WHERE clause */
unknown's avatar
unknown committed
7435
  tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
unknown's avatar
unknown committed
7436
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
7437 7438 7439
}


unknown's avatar
unknown committed
7440 7441
/**
  simple INSERT query pre-check.
unknown's avatar
unknown committed
7442

unknown's avatar
unknown committed
7443 7444
  @param thd		Thread handler
  @param tables	Global table list
unknown's avatar
unknown committed
7445

unknown's avatar
unknown committed
7446
  @retval
unknown's avatar
unknown committed
7447
    FALSE  OK
unknown's avatar
unknown committed
7448
  @retval
unknown's avatar
unknown committed
7449
    TRUE   error
unknown's avatar
unknown committed
7450
*/
unknown's avatar
unknown committed
7451

unknown's avatar
merge  
unknown committed
7452
bool insert_precheck(THD *thd, TABLE_LIST *tables)
unknown's avatar
unknown committed
7453 7454 7455 7456
{
  LEX *lex= thd->lex;
  DBUG_ENTER("insert_precheck");

unknown's avatar
unknown committed
7457 7458 7459 7460
  /*
    Check that we have modify privileges for the first table and
    select privileges for the rest
  */
unknown's avatar
unknown committed
7461 7462 7463
  ulong privilege= (INSERT_ACL |
                    (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
                    (lex->value_list.elements ? UPDATE_ACL : 0));
unknown's avatar
unknown committed
7464 7465

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

unknown's avatar
unknown committed
7468
  if (lex->update_list.elements != lex->value_list.elements)
unknown's avatar
unknown committed
7469
  {
unknown's avatar
unknown committed
7470
    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
unknown's avatar
unknown committed
7471
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7472
  }
unknown's avatar
unknown committed
7473
  DBUG_RETURN(FALSE);
7474
}
unknown's avatar
unknown committed
7475 7476


7477 7478 7479 7480 7481 7482 7483 7484 7485 7486 7487 7488 7489 7490 7491 7492 7493 7494 7495 7496 7497 7498 7499 7500 7501 7502 7503 7504
/**
   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
7505 7506
/**
  CREATE TABLE query pre-check.
unknown's avatar
unknown committed
7507

unknown's avatar
unknown committed
7508 7509 7510
  @param thd			Thread handler
  @param tables		Global table list
  @param create_table	        Table which will be created
unknown's avatar
unknown committed
7511

unknown's avatar
unknown committed
7512
  @retval
unknown's avatar
unknown committed
7513
    FALSE   OK
unknown's avatar
unknown committed
7514
  @retval
unknown's avatar
unknown committed
7515
    TRUE   Error
unknown's avatar
unknown committed
7516
*/
unknown's avatar
unknown committed
7517

unknown's avatar
unknown committed
7518 7519
bool create_table_precheck(THD *thd, TABLE_LIST *tables,
                           TABLE_LIST *create_table)
unknown's avatar
unknown committed
7520 7521
{
  LEX *lex= thd->lex;
7522 7523
  SELECT_LEX *select_lex= &lex->select_lex;
  ulong want_priv;
unknown's avatar
merge  
unknown committed
7524
  bool error= TRUE;                                 // Error message is given
unknown's avatar
unknown committed
7525
  DBUG_ENTER("create_table_precheck");
7526

7527 7528 7529 7530 7531
  /*
    Require CREATE [TEMPORARY] privilege on new table; for
    CREATE TABLE ... SELECT, also require INSERT.
  */

7532
  want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
7533 7534 7535
              CREATE_TMP_ACL : CREATE_ACL) |
             (select_lex->item_list.elements ? INSERT_ACL : 0);

unknown's avatar
unknown committed
7536
  if (check_access(thd, want_priv, create_table->db,
Marc Alff's avatar
Marc Alff committed
7537 7538 7539
                   &create_table->grant.privilege,
                   &create_table->grant.m_internal,
                   0, 0) ||
unknown's avatar
unknown committed
7540
      check_merge_table_access(thd, create_table->db,
7541
                               lex->create_info.merge_list.first))
7542
    goto err;
7543
  if (want_priv != CREATE_TMP_ACL &&
7544
      check_grant(thd, want_priv, create_table, FALSE, 1, FALSE))
7545 7546 7547 7548 7549 7550
    goto err;

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

7551 7552
#ifdef NOT_NECESSARY_TO_CHECK_CREATE_TABLE_EXIST_WHEN_PREPARING_STATEMENT
    /* This code throws an ill error for CREATE TABLE t1 SELECT * FROM t1 */
7553
    /*
7554
      Only do the check for PS, because we on execute we have to check that
unknown's avatar
unknown committed
7555 7556
      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).
7557
    */
unknown's avatar
Rename:  
unknown committed
7558
    if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute())
7559
    {
unknown's avatar
unknown committed
7560 7561 7562 7563
      /*
        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
7564
          find_table_in_global_list(tables, create_table->db,
7565
                                    create_table->table_name))
unknown's avatar
unknown committed
7566
      {
7567
	error= FALSE;
unknown's avatar
unknown committed
7568 7569 7570
        goto err;
      }
    }
7571
#endif
7572 7573
    if (tables && check_table_access(thd, SELECT_ACL, tables, FALSE,
                                     UINT_MAX, FALSE))
7574 7575
      goto err;
  }
unknown's avatar
unknown committed
7576 7577
  else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
  {
7578
    if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
unknown's avatar
unknown committed
7579 7580
      goto err;
  }
unknown's avatar
merge  
unknown committed
7581
  error= FALSE;
7582 7583 7584

err:
  DBUG_RETURN(error);
unknown's avatar
unknown committed
7585
}
unknown's avatar
unknown committed
7586 7587


unknown's avatar
unknown committed
7588 7589
/**
  negate given expression.
unknown's avatar
unknown committed
7590

unknown's avatar
unknown committed
7591 7592
  @param thd  thread handler
  @param expr expression for negation
unknown's avatar
unknown committed
7593

unknown's avatar
unknown committed
7594
  @return
unknown's avatar
unknown committed
7595 7596 7597 7598 7599 7600 7601 7602 7603 7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 7619
    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);
}
7620

unknown's avatar
unknown committed
7621 7622 7623
/**
  Set the specified definer to the default value, which is the
  current user in the thread.
7624
 
unknown's avatar
unknown committed
7625 7626
  @param[in]  thd       thread handler
  @param[out] definer   definer
7627 7628
*/
 
7629
void get_default_definer(THD *thd, LEX_USER *definer)
7630 7631 7632 7633 7634 7635 7636 7637
{
  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);
7638 7639 7640

  definer->password.str= NULL;
  definer->password.length= 0;
7641 7642
}

7643

unknown's avatar
unknown committed
7644
/**
7645
  Create default definer for the specified THD.
7646

unknown's avatar
unknown committed
7647
  @param[in] thd         thread handler
7648

unknown's avatar
unknown committed
7649 7650
  @return
    - On success, return a valid pointer to the created and initialized
7651
    LEX_USER, which contains definer information.
unknown's avatar
unknown committed
7652
    - On error, return 0.
7653 7654 7655 7656 7657 7658 7659 7660 7661
*/

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

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

7662
  get_default_definer(thd, definer);
7663 7664 7665 7666 7667

  return definer;
}


unknown's avatar
unknown committed
7668
/**
7669
  Create definer with the given user and host names.
7670

unknown's avatar
unknown committed
7671 7672 7673
  @param[in] thd          thread handler
  @param[in] user_name    user name
  @param[in] host_name    host name
7674

unknown's avatar
unknown committed
7675 7676
  @return
    - On success, return a valid pointer to the created and initialized
7677
    LEX_USER, which contains definer information.
unknown's avatar
unknown committed
7678
    - On error, return 0.
7679 7680
*/

7681
LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
7682
{
7683 7684 7685 7686
  LEX_USER *definer;

  /* Create and initialize. */

unknown's avatar
unknown committed
7687
  if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
7688 7689 7690 7691
    return 0;

  definer->user= *user_name;
  definer->host= *host_name;
7692 7693
  definer->password.str= NULL;
  definer->password.length= 0;
7694 7695

  return definer;
7696
}
7697 7698


unknown's avatar
unknown committed
7699
/**
7700 7701
  Retuns information about user or current user.

unknown's avatar
unknown committed
7702 7703
  @param[in] thd          thread handler
  @param[in] user         user
7704

unknown's avatar
unknown committed
7705 7706
  @return
    - On success, return a valid pointer to initialized
7707
    LEX_USER, which contains user information.
unknown's avatar
unknown committed
7708
    - On error, return 0.
7709 7710 7711 7712 7713
*/

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

7716 7717
  return user;
}
7718 7719


unknown's avatar
unknown committed
7720
/**
7721
  Check that byte length of a string does not exceed some limit.
7722

unknown's avatar
unknown committed
7723 7724 7725
  @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
7726

unknown's avatar
unknown committed
7727
  @retval
7728
    FALSE   the passed string is not longer than max_length
unknown's avatar
unknown committed
7729
  @retval
7730
    TRUE    the passed string is longer than max_length
7731 7732 7733

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

7736 7737
bool check_string_byte_length(LEX_STRING *str, const char *err_msg,
                              uint max_byte_length)
7738
{
7739
  if (str->length <= max_byte_length)
unknown's avatar
unknown committed
7740
    return FALSE;
7741

7742 7743 7744 7745 7746 7747 7748 7749 7750 7751 7752 7753 7754 7755 7756 7757 7758 7759 7760 7761 7762 7763 7764 7765 7766 7767 7768 7769 7770 7771 7772 7773
  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
7774

7775
  if (!no_error)
7776 7777 7778 7779
  {
    ErrConvString err(str->str, str->length, cs);
    my_error(ER_WRONG_STRING_LENGTH, MYF(0), err.ptr(), err_msg, max_char_length);
  }
7780 7781
  return TRUE;
}
7782 7783


7784 7785 7786 7787 7788 7789 7790 7791 7792 7793 7794 7795
/*
  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  
*/
7796
C_MODE_START
7797

7798
int test_if_data_home_dir(const char *dir)
7799
{
7800
  char path[FN_REFLEN];
Alexey Botchkov's avatar
Alexey Botchkov committed
7801
  int dir_len;
7802 7803 7804 7805 7806 7807 7808
  DBUG_ENTER("test_if_data_home_dir");

  if (!dir)
    DBUG_RETURN(0);

  (void) fn_format(path, dir, "", "",
                   (MY_RETURN_REAL_PATH|MY_RESOLVE_SYMLINKS));
7809 7810
  dir_len= strlen(path);
  if (mysql_unpacked_real_data_home_len<= dir_len)
7811
  {
7812 7813 7814 7815
    if (dir_len > mysql_unpacked_real_data_home_len &&
        path[mysql_unpacked_real_data_home_len] != FN_LIBCHAR)
      DBUG_RETURN(0);

7816 7817
    if (lower_case_file_system)
    {
7818 7819
      if (!my_strnncoll(default_charset_info, (const uchar*) path,
                        mysql_unpacked_real_data_home_len,
7820
                        (const uchar*) mysql_unpacked_real_data_home,
7821
                        mysql_unpacked_real_data_home_len))
7822 7823
        DBUG_RETURN(1);
    }
7824 7825
    else if (!memcmp(path, mysql_unpacked_real_data_home,
                     mysql_unpacked_real_data_home_len))
7826 7827 7828 7829 7830
      DBUG_RETURN(1);
  }
  DBUG_RETURN(0);
}

7831 7832
C_MODE_END

7833

7834 7835 7836 7837 7838 7839 7840 7841 7842 7843 7844 7845 7846 7847 7848
/**
  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
7849
  if (check_string_byte_length(str, ER(ER_HOSTNAME), HOSTNAME_LENGTH))
7850 7851 7852 7853 7854 7855 7856 7857 7858 7859 7860 7861 7862 7863 7864
    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
7865 7866


7867 7868 7869 7870 7871 7872 7873 7874
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.
7875
  @param parser_state Parser state.
unknown's avatar
unknown committed
7876
  @param creation_ctx Object creation context.
7877 7878 7879 7880 7881 7882

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

unknown's avatar
unknown committed
7883
bool parse_sql(THD *thd,
7884
               Parser_state *parser_state,
unknown's avatar
unknown committed
7885
               Object_creation_ctx *creation_ctx)
7886
{
7887
  bool ret_value;
7888
  DBUG_ASSERT(thd->m_parser_state == NULL);
7889

7890
  MYSQL_QUERY_PARSE_START(thd->query());
unknown's avatar
unknown committed
7891 7892 7893 7894 7895 7896 7897
  /* Backup creation context. */

  Object_creation_ctx *backup_ctx= NULL;

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

7898
  /* Set parser state. */
unknown's avatar
unknown committed
7899

7900
  thd->m_parser_state= parser_state;
7901

unknown's avatar
unknown committed
7902 7903
  /* Parse the query. */

7904 7905
  bool mysql_parse_status= MYSQLparse(thd) != 0;

7906
  /* Check that if MYSQLparse() failed, thd->is_error() is set. */
7907 7908

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

7911
  /* Reset parser state. */
7912

7913
  thd->m_parser_state= NULL;
7914

unknown's avatar
unknown committed
7915 7916 7917 7918 7919 7920 7921
  /* Restore creation context. */

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

  /* That's it. */

7922 7923 7924
  ret_value= mysql_parse_status || thd->is_fatal_error;
  MYSQL_QUERY_PARSE_DONE(ret_value);
  return ret_value;
7925
}
7926 7927 7928 7929

/**
  @} (end of group Runtime_Environment)
*/
Alexander Barkov's avatar
#  
Alexander Barkov committed
7930 7931 7932 7933 7934 7935 7936 7937 7938 7939 7940 7941 7942 7943 7944 7945 7946 7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 7957 7958 7959 7960 7961 7962 7963



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