sql_parse.cc 244 KB
Newer Older
Sergei Golubchik's avatar
Sergei Golubchik committed
1
/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
2
   Copyright (c) 2008, 2013, Monty Program Ab
3

unknown's avatar
unknown committed
4 5
   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
6
   the Free Software Foundation; version 2 of the License.
7

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

unknown's avatar
unknown committed
13 14
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
15
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
unknown's avatar
unknown committed
16

17
#define MYSQL_LEX 1
18
#include "my_global.h"
19 20 21
#include "sql_priv.h"
#include "unireg.h"                    // REQUIRED: for other includes
#include "sql_parse.h"        // sql_kill, *_precheck, *_prepare
22
#include "lock.h"             // try_transactional_lock,
23 24 25 26 27 28 29 30 31 32 33 34
                              // check_transactional_lock,
                              // set_handler_table_locks,
                              // 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
35
#include "sql_delete.h"       // mysql_delete
36 37 38 39 40 41 42 43 44 45 46 47 48 49
#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
50
#include "sql_reload.h"       // reload_acl_and_cache
51
#include "sql_admin.h"        // mysql_assign_to_keycache
Sergei Golubchik's avatar
Sergei Golubchik committed
52 53
#include "sql_connect.h"      // decrease_user_connections,
                              // check_mqh,
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
                              // 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"
78
#include "sql_repl.h"
unknown's avatar
unknown committed
79
#include "rpl_filter.h"
80
#include "repl_failsafe.h"
unknown's avatar
unknown committed
81 82 83
#include <m_ctype.h>
#include <myisam.h>
#include <my_dir.h>
He Zhenxing's avatar
He Zhenxing committed
84
#include "rpl_handler.h"
85
#include "rpl_mi.h"
unknown's avatar
unknown committed
86

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

100 101
#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")

Sergei Golubchik's avatar
Sergei Golubchik committed
102
#ifdef WITH_ARIA_STORAGE_ENGINE
unknown's avatar
unknown committed
103 104 105
#include "../storage/maria/ha_maria.h"
#endif

106 107 108 109 110
/**
  @defgroup Runtime_Environment Runtime Environment
  @{
*/

111 112 113 114 115 116
/* 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 || \
117
   (LP)->sql_command == SQLCOM_SHOW_CREATE_FUNC || \
118 119 120
   (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
   "FUNCTION" : "PROCEDURE")

121
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
Sergei Golubchik's avatar
Sergei Golubchik committed
122 123
static void sql_kill(THD *thd, ulong id, killed_state state);
static void sql_kill_user(THD *thd, LEX_USER *user, killed_state state);
Sergei Golubchik's avatar
Sergei Golubchik committed
124 125
static bool execute_show_status(THD *, TABLE_LIST *);
static bool execute_rename_table(THD *, TABLE_LIST *, TABLE_LIST *);
unknown's avatar
unknown committed
126

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

unknown's avatar
unknown committed
129
const LEX_STRING command_name[]={
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
  { 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
161 162
};

unknown's avatar
unknown committed
163
const char *xa_state_names[]={
164
  "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED", "ROLLBACK ONLY"
unknown's avatar
unknown committed
165 166
};

unknown's avatar
unknown committed
167
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
168 169
/**
  Returns true if all tables should be ignored.
170
*/
171 172
inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables)
{
173 174
  return thd->rpl_filter->is_on() && tables && !thd->spcont &&
         !thd->rpl_filter->tables_ok(thd->db, tables);
175
}
unknown's avatar
unknown committed
176
#endif
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);
184
    if (table->updating && !find_temporary_table(thd, table))
185 186 187 188 189
      return 1;
  }
  return 0;
}

190

Konstantin Osipov's avatar
Konstantin Osipov committed
191 192 193 194 195 196 197
/*
  Implicitly commit a active transaction if statement requires so.

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

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

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

223
  DBUG_RETURN(!skip);
Konstantin Osipov's avatar
Konstantin Osipov committed
224 225 226
}


unknown's avatar
unknown committed
227 228 229 230
/**
  Mark all commands that somehow changes a table.

  This is used to check number of updates / hour.
unknown's avatar
unknown committed
231 232 233

  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
234

235
  See COMMAND_FLAG_xxx for different type of commands
unknown's avatar
unknown committed
236 237
     2  - query that returns meaningful ROW_COUNT() -
          a number of modified rows
238 239
*/

240
uint sql_command_flags[SQLCOM_END+1];
Konstantin Osipov's avatar
Konstantin Osipov committed
241
uint server_command_flags[COM_END+1];
242 243 244

void init_update_queries(void)
{
Konstantin Osipov's avatar
Konstantin Osipov committed
245 246 247 248 249 250 251 252 253 254 255 256
  /* 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));

257 258 259 260 261 262 263 264 265 266
  /*
    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
267
  sql_command_flags[SQLCOM_CREATE_TABLE]=   CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
Sergei Golubchik's avatar
Sergei Golubchik committed
268
                                            CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS |
269
                                            CF_CAN_GENERATE_ROW_EVENTS;
Konstantin Osipov's avatar
Konstantin Osipov committed
270 271
  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 |
272 273
                                            CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS |
                                            CF_INSERTS_DATA;
Konstantin Osipov's avatar
Konstantin Osipov committed
274
  sql_command_flags[SQLCOM_TRUNCATE]=       CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
275
                                            CF_AUTO_COMMIT_TRANS;
Konstantin Osipov's avatar
Konstantin Osipov committed
276
  sql_command_flags[SQLCOM_DROP_TABLE]=     CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
277
  sql_command_flags[SQLCOM_LOAD]=           CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
278 279
                                            CF_CAN_GENERATE_ROW_EVENTS | CF_REPORT_PROGRESS |
                                            CF_INSERTS_DATA;
280 281 282 283
  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;
  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
284
  sql_command_flags[SQLCOM_RENAME_TABLE]=   CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
Sergei Golubchik's avatar
Sergei Golubchik committed
285
  sql_command_flags[SQLCOM_DROP_INDEX]=     CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS;
Konstantin Osipov's avatar
Konstantin Osipov committed
286 287 288
  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;
289 290
  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
291 292 293
  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;
294

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

  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;
323
  sql_command_flags[SQLCOM_SHOW_PLUGINS]=     CF_STATUS_COMMAND;
324 325 326 327 328
  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;
329
  sql_command_flags[SQLCOM_SHOW_BINLOGS]=     CF_STATUS_COMMAND;
330 331 332
  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;
333
  sql_command_flags[SQLCOM_SHOW_AUTHORS]=     CF_STATUS_COMMAND;
334
  sql_command_flags[SQLCOM_SHOW_CONTRIBUTORS]= CF_STATUS_COMMAND;
335 336 337
  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;
338 339 340
  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;
341
  sql_command_flags[SQLCOM_SHOW_EXPLAIN]= CF_STATUS_COMMAND;
342
  sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND;
343 344
  sql_command_flags[SQLCOM_SHOW_GRANTS]=      CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_CREATE_DB]=   CF_STATUS_COMMAND;
345
  sql_command_flags[SQLCOM_SHOW_CREATE]=  CF_STATUS_COMMAND;
346
  sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND;
347
  sql_command_flags[SQLCOM_SHOW_SLAVE_STAT]=  CF_STATUS_COMMAND;
348 349
  sql_command_flags[SQLCOM_SHOW_CREATE_PROC]= CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_CREATE_FUNC]= CF_STATUS_COMMAND;
350
  sql_command_flags[SQLCOM_SHOW_CREATE_TRIGGER]=  CF_STATUS_COMMAND;
351 352 353 354 355 356
  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;
357
  sql_command_flags[SQLCOM_BINLOG_BASE64_EVENT]= CF_STATUS_COMMAND | CF_CAN_GENERATE_ROW_EVENTS;
358 359 360 361
  sql_command_flags[SQLCOM_SHOW_CLIENT_STATS]= CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_USER_STATS]=   CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_TABLE_STATS]=  CF_STATUS_COMMAND;
  sql_command_flags[SQLCOM_SHOW_INDEX_STATS]=  CF_STATUS_COMMAND;
Sergei Golubchik's avatar
Sergei Golubchik committed
362 363
  sql_command_flags[SQLCOM_SHOW_TABLES]=       (CF_STATUS_COMMAND | CF_SHOW_TABLE_COMMAND | CF_REEXECUTION_FRAGILE);
  sql_command_flags[SQLCOM_SHOW_TABLE_STATUS]= (CF_STATUS_COMMAND | CF_SHOW_TABLE_COMMAND | CF_REEXECUTION_FRAGILE);
364

365

366 367 368
  sql_command_flags[SQLCOM_CREATE_USER]=       CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_RENAME_USER]=       CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_DROP_USER]=         CF_CHANGES_DATA;
369 370 371
  sql_command_flags[SQLCOM_GRANT]=             CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_REVOKE]=            CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_OPTIMIZE]=          CF_CHANGES_DATA;
Konstantin Osipov's avatar
Konstantin Osipov committed
372
  sql_command_flags[SQLCOM_CREATE_FUNCTION]=   CF_CHANGES_DATA;
373 374 375 376 377 378
  sql_command_flags[SQLCOM_CREATE_PROCEDURE]=  CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_DROP_PROCEDURE]=    CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_DROP_FUNCTION]=     CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_ALTER_PROCEDURE]=   CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_ALTER_FUNCTION]=    CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
379 380 381
  sql_command_flags[SQLCOM_INSTALL_PLUGIN]=    CF_CHANGES_DATA;
  sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]=  CF_CHANGES_DATA;

382 383 384 385 386 387
  /*
    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.
  */
388 389 390
  sql_command_flags[SQLCOM_CALL]=      CF_REEXECUTION_FRAGILE |
                                       CF_CAN_GENERATE_ROW_EVENTS;
  sql_command_flags[SQLCOM_EXECUTE]=   CF_CAN_GENERATE_ROW_EVENTS;
391

392 393 394 395 396 397 398 399 400 401 402 403 404
  /*
    We don't want to change to statement based replication for these commands
  */
  sql_command_flags[SQLCOM_ROLLBACK]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
  /* We don't want to replicate ALTER TABLE for temp tables in row format */
  sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
  /* We don't want to replicate TRUNCATE for temp tables in row format */
  sql_command_flags[SQLCOM_TRUNCATE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
  /* We don't want to replicate DROP for temp tables in row format */
  sql_command_flags[SQLCOM_DROP_TABLE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
  /* One can change replication mode with SET */
  sql_command_flags[SQLCOM_SET_OPTION]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;

405 406 407 408
  /*
    The following admin table operations are allowed
    on log tables.
  */
Sergei Golubchik's avatar
Sergei Golubchik committed
409 410 411 412 413
  sql_command_flags[SQLCOM_REPAIR]=    CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS;
  sql_command_flags[SQLCOM_OPTIMIZE]|= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS;
  sql_command_flags[SQLCOM_ANALYZE]=   CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS;
  sql_command_flags[SQLCOM_CHECK]=     CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS;
  sql_command_flags[SQLCOM_CHECKSUM]=  CF_REPORT_PROGRESS;
414 415 416 417 418 419 420 421

  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
422 423 424 425
  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;
426
  sql_command_flags[SQLCOM_RESET]=              CF_AUTO_COMMIT_TRANS;
427 428 429
  sql_command_flags[SQLCOM_CREATE_SERVER]=      CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_ALTER_SERVER]=       CF_AUTO_COMMIT_TRANS;
  sql_command_flags[SQLCOM_DROP_SERVER]=        CF_AUTO_COMMIT_TRANS;
430 431
}

432 433 434 435 436 437
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
438 439
bool is_update_query(enum enum_sql_command command)
{
440
  DBUG_ASSERT(command <= SQLCOM_END);
441
  return (sql_command_flags[command] & CF_CHANGES_DATA) != 0;
unknown's avatar
unknown committed
442
}
443

444 445 446 447 448 449 450
/**
  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)
{
451
  DBUG_ASSERT(command <= SQLCOM_END);
452 453
  return (sql_command_flags[command] & CF_WRITE_LOGS_COMMAND) != 0;
}
454

455
void execute_init_command(THD *thd, LEX_STRING *init_command,
Marc Alff's avatar
Marc Alff committed
456
                          mysql_rwlock_t *var_lock)
unknown's avatar
unknown committed
457 458 459 460
{
  Vio* save_vio;
  ulong save_client_capabilities;

Marc Alff's avatar
Marc Alff committed
461
  mysql_rwlock_rdlock(var_lock);
462 463
  if (!init_command->length)
  {
Marc Alff's avatar
Marc Alff committed
464
    mysql_rwlock_unlock(var_lock);
465 466 467 468 469 470 471 472 473 474
    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
475
  mysql_rwlock_unlock(var_lock);
476

477
#if defined(ENABLED_PROFILING)
478
  thd->profiling.start_new_query();
479
  thd->profiling.set_query_source(buf, len);
480 481
#endif

482
  thd_proc_info(thd, "Execution of init_command");
unknown's avatar
unknown committed
483 484
  save_client_capabilities= thd->client_capabilities;
  thd->client_capabilities|= CLIENT_MULTI_QUERIES;
485 486 487 488
  /*
    We don't need return result of execution to client side.
    To forbid this we should set thd->net.vio to 0.
  */
unknown's avatar
unknown committed
489 490
  save_vio= thd->net.vio;
  thd->net.vio= 0;
491
  dispatch_command(COM_QUERY, thd, buf, len);
unknown's avatar
unknown committed
492 493
  thd->client_capabilities= save_client_capabilities;
  thd->net.vio= save_vio;
494

495
#if defined(ENABLED_PROFILING)
496 497
  thd->profiling.finish_current_query();
#endif
unknown's avatar
unknown committed
498 499 500
}


501
static void handle_bootstrap_impl(THD *thd)
unknown's avatar
unknown committed
502
{
Marc Alff's avatar
Marc Alff committed
503
  MYSQL_FILE *file= bootstrap_file;
Sergei Golubchik's avatar
Sergei Golubchik committed
504
  char *buff, *res;
unknown's avatar
unknown committed
505

506 507
  DBUG_ENTER("handle_bootstrap");

unknown's avatar
unknown committed
508
#ifndef EMBEDDED_LIBRARY
509 510
  pthread_detach_this_thread();
  thd->thread_stack= (char*) &thd;
unknown's avatar
unknown committed
511
#endif /* EMBEDDED_LIBRARY */
unknown's avatar
unknown committed
512

513
  thd_proc_info(thd, 0);
514 515
  thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
  thd->security_ctx->priv_user[0]= thd->security_ctx->priv_host[0]=0;
516 517 518 519 520 521
  /*
    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
522

523
  buff= (char*) thd->net.buff;
524
  thd->init_for_queries();
Marc Alff's avatar
Marc Alff committed
525
  while (mysql_file_fgets(buff, thd->net.max_packet, file))
unknown's avatar
unknown committed
526
  {
Marc Alff's avatar
Marc Alff committed
527 528
    char *query;
    /* strlen() can't be deleted because mysql_file_fgets() doesn't return length */
529
    ulong length= (ulong) strlen(buff);
Marc Alff's avatar
Marc Alff committed
530
    while (buff[length-1] != '\n' && !mysql_file_feof(file))
531 532 533 534 535 536 537 538
    {
      /*
        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))
      {
539
        thd->protocol->end_statement();
540
        bootstrap_error= 1;
541 542 543
        break;
      }
      buff= (char*) thd->net.buff;
Sergei Golubchik's avatar
Sergei Golubchik committed
544
      res= mysql_file_fgets(buff + length, thd->net.max_packet - length, file);
545
      if (!res && !mysql_file_feof(file))
546
      {
547
        thd->protocol->end_statement();
548 549 550
        bootstrap_error= 1;
        break;
      }
551 552 553
      length+= (ulong) strlen(buff + length);
      /* purecov: end */
    }
554
    if (bootstrap_error)
555
      break;                                    /* purecov: inspected */
unknown's avatar
unknown committed
556

unknown's avatar
unknown committed
557
    while (length && (my_isspace(thd->charset(), buff[length-1]) ||
558
                      buff[length-1] == ';'))
unknown's avatar
unknown committed
559 560
      length--;
    buff[length]=0;
unknown's avatar
unknown committed
561 562 563 564 565

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

Gleb Shchepa's avatar
Gleb Shchepa committed
566 567
    query= (char *) thd->memdup_w_gap(buff, length + 1,
                                      thd->db_length + 1 +
568
                                      QUERY_CACHE_DB_LENGTH_SIZE +
Gleb Shchepa's avatar
Gleb Shchepa committed
569
                                      QUERY_CACHE_FLAGS_SIZE);
570 571
    size_t db_len= 0;
    memcpy(query + length + 1, (char *) &db_len, sizeof(size_t));
572
    thd->set_query_and_id(query, length, thd->charset(), next_query_id());
573
    int2store(query + length + 1, 0);           // No db in bootstrap
574
    DBUG_PRINT("query",("%-.4096s",thd->query()));
575
#if defined(ENABLED_PROFILING)
576
    thd->profiling.start_new_query();
577
    thd->profiling.set_query_source(thd->query(), length);
578 579
#endif

580 581 582 583
    /*
      We don't need to obtain LOCK_thread_count here because in bootstrap
      mode we have only one thread.
    */
584
    thd->set_time();
585 586 587 588 589 590 591 592
    Parser_state parser_state;
    if (parser_state.init(thd, thd->query(), length))
    {
      thd->protocol->end_statement();
      bootstrap_error= 1;
      break;
    }

593
    mysql_parse(thd, thd->query(), length, &parser_state);
594

595
    bootstrap_error= thd->is_error();
596
    thd->protocol->end_statement();
597

598
#if defined(ENABLED_PROFILING)
599 600 601
    thd->profiling.finish_current_query();
#endif

602
    if (bootstrap_error)
603 604
      break;

unknown's avatar
unknown committed
605
    free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
606
    free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC));
unknown's avatar
unknown committed
607
  }
608

609 610 611 612 613 614 615 616 617 618 619 620 621 622
  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
623 624 625 626 627 628 629 630
  mysql_thread_set_psi_id(thd->thread_id);

  do_handle_bootstrap(thd);
  return 0;
}

void do_handle_bootstrap(THD *thd)
{
631 632 633 634 635
  /* The following must be called before DBUG_ENTER */
  thd->thread_stack= (char*) &thd;
  if (my_thread_init() || thd->store_globals())
  {
#ifndef EMBEDDED_LIBRARY
636
    close_connection(thd, ER_OUT_OF_RESOURCES);
637 638 639 640 641 642 643
#endif
    thd->fatal_error();
    goto end;
  }

  handle_bootstrap_impl(thd);

644
end:
645 646
  delete thd;

unknown's avatar
unknown committed
647
#ifndef EMBEDDED_LIBRARY
Michael Widenius's avatar
Michael Widenius committed
648
  thread_safe_decrement32(&thread_count, &thread_count_lock);
649
  in_bootstrap= FALSE;
Michael Widenius's avatar
Michael Widenius committed
650 651

  mysql_mutex_lock(&LOCK_thread_count);
Marc Alff's avatar
Marc Alff committed
652 653
  mysql_cond_broadcast(&COND_thread_count);
  mysql_mutex_unlock(&LOCK_thread_count);
654 655
  my_thread_end();
  pthread_exit(0);
unknown's avatar
unknown committed
656
#endif
657

Marc Alff's avatar
Marc Alff committed
658
  return;
unknown's avatar
unknown committed
659 660
}

661

unknown's avatar
unknown committed
662 663 664 665 666 667 668 669 670 671 672 673 674 675
/* 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;
}

676 677 678 679
/**
   This works because items are allocated with sql_alloc().
   @note The function also handles null pointers (empty list).
*/
680 681
void cleanup_items(Item *item)
{
unknown's avatar
unknown committed
682
  DBUG_ENTER("cleanup_items");  
683 684
  for (; item ; item=item->next)
    item->cleanup();
unknown's avatar
unknown committed
685
  DBUG_VOID_RETURN;
686 687
}

688
#ifndef EMBEDDED_LIBRARY
689

unknown's avatar
unknown committed
690
/**
unknown's avatar
unknown committed
691
  Read one command from connection and execute it (query or simple command).
692
  This function is called in loop from thread function.
693 694 695

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

unknown's avatar
unknown committed
696
  @retval
697
    0  success
unknown's avatar
unknown committed
698
  @retval
699 700 701
    1  request of thread shutdown (see dispatch_command() description)
*/

unknown's avatar
unknown committed
702 703
bool do_command(THD *thd)
{
704
  bool return_value;
unknown's avatar
unknown committed
705
  char *packet= 0;
unknown's avatar
unknown committed
706
  ulong packet_length;
unknown's avatar
unknown committed
707
  NET *net= &thd->net;
unknown's avatar
unknown committed
708 709 710
  enum enum_server_command command;
  DBUG_ENTER("do_command");

unknown's avatar
unknown committed
711 712 713 714
  /*
    indicator of uninitialized lex => normal flow of errors handling
    (see my_message_sql)
  */
715
  thd->lex->current_select= 0;
unknown's avatar
unknown committed
716

unknown's avatar
unknown committed
717 718 719 720
  /*
    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"
721
    number of seconds has passed.
unknown's avatar
unknown committed
722
  */
723
  if(!thd->skip_wait_timeout)
724 725
    my_net_set_read_timeout(net, thd->variables.net_wait_timeout);

unknown's avatar
unknown committed
726

727 728 729 730
  /*
    XXX: this code is here only to clear possible errors of init_connect. 
    Consider moving to init_connect() instead.
  */
unknown's avatar
unknown committed
731
  thd->clear_error();				// Clear error message
Marc Alff's avatar
Marc Alff committed
732
  thd->stmt_da->reset_diagnostics_area();
unknown's avatar
unknown committed
733 734

  net_new_transaction(net);
735

Sergei Golubchik's avatar
Sergei Golubchik committed
736

737 738
  /* Save for user statistics */
  thd->start_bytes_received= thd->status_var.bytes_received;
Sergei Golubchik's avatar
Sergei Golubchik committed
739

740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
  /*
    Synchronization point for testing of KILL_CONNECTION.
    This sync point can wait here, to simulate slow code execution
    between the last test of thd->killed and blocking in read().

    The goal of this test is to verify that a connection does not
    hang, if it is killed at this point of execution.
    (Bug#37780 - main.kill fails randomly)

    Note that the sync point wait itself will be terminated by a
    kill. In this case it consumes a condition broadcast, but does
    not change anything else. The consumed broadcast should not
    matter here, because the read/recv() below doesn't use it.
  */
  DEBUG_SYNC(thd, "before_do_command_net_read");

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

762
    /* Check if we can continue without closing the connection */
763

764 765
    /* The error must be set. */
    DBUG_ASSERT(thd->is_error());
766
    thd->protocol->end_statement();
767

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

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

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

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

809
  DBUG_ASSERT(packet_length);
810 811 812 813
  return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));

out:
  DBUG_RETURN(return_value);
814
}
815
#endif  /* EMBEDDED_LIBRARY */
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 845 846 847
/**
  @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);

848
  if (!(sql_command_flags[lex->sql_command] & CF_CHANGES_DATA))
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 881 882 883
    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);
}
884

unknown's avatar
unknown committed
885 886
/**
  Perform one connection-level (COM_XXXX) command.
887

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

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

Konstantin Osipov's avatar
Konstantin Osipov committed
914 915 916
#if defined(ENABLED_PROFILING)
  thd->profiling.start_new_query();
#endif
917
  MYSQL_COMMAND_START(thd->thread_id, command,
918
                      &thd->security_ctx->priv_user[0],
919 920
                      (char *) thd->security_ctx->host_or_ip);
  
921 922 923 924
  DBUG_EXECUTE_IF("crash_dispatch_command_before",
                  { DBUG_PRINT("crash_dispatch_command_before", ("now"));
                    DBUG_ABORT(); });

925
  thd->command=command;
unknown's avatar
unknown committed
926
  /*
927 928
    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
929
  */
930
  thd->enable_slow_log= TRUE;
931
  thd->query_plan_flags= QPLAN_INIT;
932
  thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
933 934 935

  DEBUG_SYNC(thd,"dispatch_command_before_set_time");

unknown's avatar
unknown committed
936
  thd->set_time();
Konstantin Osipov's avatar
Konstantin Osipov committed
937
  if (!(server_command_flags[command] & CF_SKIP_QUERY_ID))
Michael Widenius's avatar
Michael Widenius committed
938 939 940 941 942 943 944 945 946
    thd->set_query_id(next_query_id());
  else
  {
    /*
      ping, get statistics or similar stateless command.
      No reason to increase query id here.
    */
    thd->set_query_id(get_query_id());
  }
947
  inc_thread_running();
unknown's avatar
unknown committed
948

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

952 953 954 955 956
  /**
    Clear the set of flags that are expected to be cleared at the
    beginning of each command.
  */
  thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
957
  switch (command) {
unknown's avatar
unknown committed
958
  case COM_INIT_DB:
unknown's avatar
unknown committed
959 960
  {
    LEX_STRING tmp;
961
    status_var_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB]);
unknown's avatar
unknown committed
962
    thd->convert_string(&tmp, system_charset_info,
963
			packet, packet_length, thd->charset());
unknown's avatar
unknown committed
964
    if (!mysql_change_db(thd, &tmp, FALSE))
965
    {
966
      general_log_write(thd, command, thd->db, thd->db_length);
967
      my_ok(thd);
968
    }
unknown's avatar
unknown committed
969 970
    break;
  }
unknown's avatar
unknown committed
971
#ifdef HAVE_REPLICATION
972 973
  case COM_REGISTER_SLAVE:
  {
974
    if (!register_slave(thd, (uchar*)packet, packet_length))
975
      my_ok(thd);
976 977
    break;
  }
978
#endif
unknown's avatar
unknown committed
979 980
  case COM_CHANGE_USER:
  {
981
    bool rc;
982
    status_var_increment(thd->status_var.com_other);
983

unknown's avatar
unknown committed
984
    thd->change_user();
985
    thd->clear_error();                         // if errors from rollback
unknown's avatar
unknown committed
986

987 988
    /* acl_authenticate() takes the data from net->read_pos */
    net->read_pos= (uchar*)packet;
989

990 991 992
    uint save_db_length= thd->db_length;
    char *save_db= thd->db;
    USER_CONN *save_user_connect= thd->user_connect;
993
    Security_context save_security_ctx= *thd->security_ctx;
994 995 996 997 998 999
    CHARSET_INFO *save_character_set_client=
      thd->variables.character_set_client;
    CHARSET_INFO *save_collation_connection=
      thd->variables.collation_connection;
    CHARSET_INFO *save_character_set_results=
      thd->variables.character_set_results;
1000

Michael Widenius's avatar
Michael Widenius committed
1001 1002
    /* Ensure we don't free security_ctx->user in case we have to revert */
    thd->security_ctx->user= 0;
1003
    thd->user_connect= 0;
1004

1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
    /*
      to limit COM_CHANGE_USER ability to brute-force passwords,
      we only allow three unsuccessful COM_CHANGE_USER per connection.
    */
    if (thd->failed_com_change_user >= 3)
    {
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
      rc= 1;
    }
    else
      rc= acl_authenticate(thd, 0, packet_length);

1017
    mysql_audit_notify_connection_change_user(thd);
1018
    if (rc)
1019
    {
Michael Widenius's avatar
Michael Widenius committed
1020
      /* Free user if allocated by acl_authenticate */
Sergei Golubchik's avatar
Sergei Golubchik committed
1021
      my_free(thd->security_ctx->user);
1022
      *thd->security_ctx= save_security_ctx;
1023 1024
      if (thd->user_connect)
	decrease_user_connections(thd->user_connect);
unknown's avatar
unknown committed
1025
      thd->user_connect= save_user_connect;
1026 1027 1028 1029 1030
      thd->reset_db(save_db, save_db_length);
      thd->variables.character_set_client= save_character_set_client;
      thd->variables.collation_connection= save_collation_connection;
      thd->variables.character_set_results= save_character_set_results;
      thd->update_charset();
1031 1032
      thd->failed_com_change_user++;
      my_sleep(1000000);
1033 1034 1035
    }
    else
    {
1036
#ifndef NO_EMBEDDED_ACCESS_CHECKS
1037
      /* we've authenticated new user */
unknown's avatar
unknown committed
1038 1039
      if (save_user_connect)
	decrease_user_connections(save_user_connect);
1040
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
1041 1042
      my_free(save_db);
      my_free(save_security_ctx.user);
1043
    }
unknown's avatar
unknown committed
1044 1045
    break;
  }
1046
  case COM_STMT_EXECUTE:
unknown's avatar
unknown committed
1047
  {
1048
    mysqld_stmt_execute(thd, packet, packet_length);
unknown's avatar
unknown committed
1049 1050
    break;
  }
1051
  case COM_STMT_FETCH:
1052
  {
1053
    mysqld_stmt_fetch(thd, packet, packet_length);
1054 1055
    break;
  }
1056
  case COM_STMT_SEND_LONG_DATA:
unknown's avatar
unknown committed
1057
  {
1058
    mysql_stmt_get_longdata(thd, packet, packet_length);
unknown's avatar
unknown committed
1059 1060
    break;
  }
1061
  case COM_STMT_PREPARE:
unknown's avatar
unknown committed
1062
  {
1063
    mysqld_stmt_prepare(thd, packet, packet_length);
unknown's avatar
unknown committed
1064 1065
    break;
  }
1066
  case COM_STMT_CLOSE:
unknown's avatar
unknown committed
1067
  {
1068
    mysqld_stmt_close(thd, packet);
unknown's avatar
unknown committed
1069 1070
    break;
  }
1071
  case COM_STMT_RESET:
1072
  {
1073
    mysqld_stmt_reset(thd, packet);
1074 1075
    break;
  }
unknown's avatar
unknown committed
1076 1077
  case COM_QUERY:
  {
1078 1079
    if (alloc_query(thd, packet, packet_length))
      break;					// fatal error is set
1080
    MYSQL_QUERY_START(thd->query(), thd->thread_id,
1081
                      (char *) (thd->db ? thd->db : ""),
1082
                      &thd->security_ctx->priv_user[0],
1083
                      (char *) thd->security_ctx->host_or_ip);
1084 1085 1086
    char *packet_end= thd->query() + thd->query_length();
    general_log_write(thd, command, thd->query(), thd->query_length());
    DBUG_PRINT("query",("%-.4096s",thd->query()));
1087
#if defined(ENABLED_PROFILING)
1088
    thd->profiling.set_query_source(thd->query(), thd->query_length());
1089
#endif
1090 1091 1092
    Parser_state parser_state;
    if (parser_state.init(thd, thd->query(), thd->query_length()))
      break;
1093

1094
    mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
1095

1096 1097
    while (!thd->killed && (parser_state.m_lip.found_semicolon != NULL) &&
           ! thd->is_error())
1098
    {
1099
      /*
1100
        Multiple queries exist, execute them individually
1101
      */
1102
      char *beginning_of_next_stmt= (char*) parser_state.m_lip.found_semicolon;
1103

1104 1105 1106 1107
#ifdef WITH_ARIA_STORAGE_ENGINE
    ha_maria::implicit_commit(thd, FALSE);
#endif

1108 1109
      /* Finalize server status flags after executing a statement. */
      thd->update_server_status();
1110 1111
      thd->protocol->end_statement();
      query_cache_end_of_result(thd);
1112 1113 1114 1115 1116

      mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS,
                          thd->stmt_da->is_error() ? thd->stmt_da->sql_errno()
                          : 0, command_name[command].str);

1117
      ulong length= (ulong)(packet_end - beginning_of_next_stmt);
1118

1119
      log_slow_statement(thd);
1120

1121
      /* Remove garbage at start of query */
1122
      while (length > 0 && my_isspace(thd->charset(), *beginning_of_next_stmt))
1123
      {
1124
        beginning_of_next_stmt++;
1125 1126
        length--;
      }
1127

1128 1129 1130 1131 1132
      if (MYSQL_QUERY_DONE_ENABLED())
      {
        MYSQL_QUERY_DONE(thd->is_error());
      }

1133
#if defined(ENABLED_PROFILING)
1134 1135 1136 1137 1138
      thd->profiling.finish_current_query();
      thd->profiling.start_new_query("continuing");
      thd->profiling.set_query_source(beginning_of_next_stmt, length);
#endif

1139
      MYSQL_QUERY_START(beginning_of_next_stmt, thd->thread_id,
1140
                        (char *) (thd->db ? thd->db : ""),
1141
                        &thd->security_ctx->priv_user[0],
1142 1143
                        (char *) thd->security_ctx->host_or_ip);

1144 1145
      thd->set_query_and_id(beginning_of_next_stmt, length,
                            thd->charset(), next_query_id());
1146 1147 1148 1149
      /*
        Count each statement from the client.
      */
      statistic_increment(thd->status_var.questions, &LOCK_status);
1150
      thd->set_time(); /* Reset the query start time. */
1151
      parser_state.reset(beginning_of_next_stmt, length);
1152
      /* TODO: set thd->lex->sql_command to SQLCOM_END here */
1153
      mysql_parse(thd, beginning_of_next_stmt, length, &parser_state);
1154 1155
    }

unknown's avatar
unknown committed
1156 1157 1158
    DBUG_PRINT("info",("query ready"));
    break;
  }
1159
  case COM_FIELD_LIST:				// This isn't actually needed
unknown's avatar
unknown committed
1160
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
1161 1162
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0));	/* purecov: inspected */
unknown's avatar
unknown committed
1163 1164 1165
    break;
#else
  {
1166
    char *fields, *packet_end= packet + packet_length, *arg_end;
1167
    /* Locked closure of all tables */
unknown's avatar
unknown committed
1168
    TABLE_LIST table_list;
1169 1170 1171 1172 1173 1174
    LEX_STRING table_name;
    LEX_STRING db;
    /*
      SHOW statements should not add the used tables to the list of tables
      used in a transaction.
    */
1175
    MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
1176

1177
    status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS]);
1178
    if (thd->copy_db_to(&db.str, &db.length))
unknown's avatar
unknown committed
1179
      break;
1180 1181
    /*
      We have name + wildcard in packet, separated by endzero
Michael Widenius's avatar
Michael Widenius committed
1182
      (The packet is guaranteed to end with an end zero)
1183 1184
    */
    arg_end= strend(packet);
Ramil Kalimullin's avatar
Ramil Kalimullin committed
1185
    uint arg_length= arg_end - packet;
1186

1187
    /* Check given table name length. */
Sergei Golubchik's avatar
Sergei Golubchik committed
1188
    if (packet_length - arg_length > NAME_LEN + 1 || arg_length > SAFE_NAME_LEN)
1189 1190 1191 1192
    {
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
      break;
    }
1193
    thd->convert_string(&table_name, system_charset_info,
1194
			packet, arg_length, thd->charset());
1195
    if (check_table_name(table_name.str, table_name.length, FALSE))
1196 1197
    {
      /* this is OK due to convert_string() null-terminating the string */
1198
      my_error(ER_WRONG_TABLE_NAME, MYF(0), table_name.str);
1199 1200
      break;
    }
1201
    packet= arg_end + 1;
1202
    mysql_reset_thd_for_next_command(thd, opt_userstat_running);
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217
    lex_start(thd);
    /* Must be before we init the table list. */
    if (lower_case_table_names)
      table_name.length= my_casedn_str(files_charset_info, table_name.str);
    table_list.init_one_table(db.str, db.length, table_name.str,
                              table_name.length, table_name.str, TL_READ);
    /*
      Init TABLE_LIST members necessary when the undelrying
      table is view.
    */
    table_list.select_lex= &(thd->lex->select_lex);
    thd->lex->
      select_lex.table_list.link_in_list(&table_list,
                                         &table_list.next_local);
    thd->lex->add_to_query_tables(&table_list);
1218

1219
    if (is_infoschema_db(table_list.db, table_list.db_length))
1220 1221 1222 1223 1224 1225
    {
      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
1226 1227
    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
1228
      break;
1229
    thd->set_query(fields, query_length);
unknown's avatar
unknown committed
1230
    general_log_print(thd, command, "%s %s", table_list.table_name, fields);
unknown's avatar
unknown committed
1231

1232 1233
    if (check_table_access(thd, SELECT_ACL, &table_list,
                           TRUE, UINT_MAX, FALSE))
unknown's avatar
unknown committed
1234
      break;
1235 1236 1237 1238
    /*
      Turn on an optimization relevant if the underlying table
      is a view: do not fill derived tables.
    */
1239
    thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
1240

unknown's avatar
unknown committed
1241
    mysqld_list_fields(thd,&table_list,fields);
1242
    thd->lex->unit.cleanup();
1243 1244 1245 1246 1247
    /* No need to rollback statement transaction, it's not started. */
    DBUG_ASSERT(thd->transaction.stmt.is_empty());
    close_thread_tables(thd);
    thd->mdl_context.rollback_to_savepoint(mdl_savepoint);

1248
    thd->cleanup_after_query();
unknown's avatar
unknown committed
1249 1250 1251 1252
    break;
  }
#endif
  case COM_QUIT:
1253
    /* We don't calculate statistics for this command */
unknown's avatar
unknown committed
1254
    general_log_print(thd, command, NullS);
unknown's avatar
unknown committed
1255
    net->error=0;				// Don't give 'abort' message
Marc Alff's avatar
Marc Alff committed
1256
    thd->stmt_da->disable_status();              // Don't send anything back
unknown's avatar
unknown committed
1257 1258
    error=TRUE;					// End server
    break;
1259
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
1260 1261
  case COM_BINLOG_DUMP:
    {
unknown's avatar
unknown committed
1262 1263 1264 1265
      ulong pos;
      ushort flags;
      uint32 slave_server_id;

1266
      status_var_increment(thd->status_var.com_other);
1267

1268
      thd->enable_slow_log= opt_log_slow_admin_statements;
1269
      thd->query_plan_flags|= QPLAN_ADMIN;
unknown's avatar
unknown committed
1270
      if (check_global_access(thd, REPL_SLAVE_ACL))
unknown's avatar
unknown committed
1271
	break;
unknown's avatar
unknown committed
1272

1273
      /* TODO: The following has to be changed to an 8 byte integer */
1274 1275
      pos = uint4korr(packet);
      flags = uint2korr(packet + 4);
1276
      thd->variables.server_id=0; /* avoid suicide */
unknown's avatar
unknown committed
1277
      if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
unknown's avatar
unknown committed
1278
	kill_zombie_dump_threads(slave_server_id);
1279
      thd->variables.server_id = slave_server_id;
unknown's avatar
unknown committed
1280

unknown's avatar
unknown committed
1281
      general_log_print(thd, command, "Log: '%s'  Pos: %ld", packet+10,
unknown's avatar
unknown committed
1282
                      (long) pos);
1283
      mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
unknown's avatar
unknown committed
1284
      unregister_slave(thd,1,1);
unknown's avatar
unknown committed
1285
      /*  fake COM_QUIT -- if we get here, the thread needs to terminate */
1286
      error = TRUE;
unknown's avatar
unknown committed
1287 1288
      break;
    }
1289
#endif
unknown's avatar
unknown committed
1290
  case COM_REFRESH:
unknown's avatar
unknown committed
1291
  {
1292
    int not_used;
1293 1294 1295 1296 1297 1298 1299 1300

    /*
      Initialize thd->lex since it's used in many base functions, such as
      open_tables(). Otherwise, it remains unitialized and may cause crash
      during execution of COM_REFRESH.
    */
    lex_start(thd);
    
1301
    status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]);
unknown's avatar
unknown committed
1302
    ulong options= (ulong) (uchar) packet[0];
Konstantin Osipov's avatar
Konstantin Osipov committed
1303
    if (trans_commit_implicit(thd))
Konstantin Osipov's avatar
Konstantin Osipov committed
1304
      break;
1305
    thd->mdl_context.release_transactional_locks();
unknown's avatar
unknown committed
1306
    if (check_global_access(thd,RELOAD_ACL))
unknown's avatar
unknown committed
1307
      break;
unknown's avatar
unknown committed
1308
    general_log_print(thd, command, NullS);
1309
#ifndef DBUG_OFF
1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320
    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;
1321
      set_current_thd(0);
1322 1323
      res= reload_acl_and_cache(NULL, options | REFRESH_FAST,
                                NULL, &not_used);
1324
      set_current_thd(thd);
Konstantin Osipov's avatar
Konstantin Osipov committed
1325 1326
      if (res)
        break;
1327
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
1328
    else
1329
#endif
1330 1331 1332 1333 1334 1335
    {
      thd->lex->relay_log_connection_name.str= (char*) "";
      thd->lex->relay_log_connection_name.length= 0;
      if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
        break;
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
1336
    if (trans_commit_implicit(thd))
Konstantin Osipov's avatar
Konstantin Osipov committed
1337
      break;
1338
    close_thread_tables(thd);
1339
    thd->mdl_context.release_transactional_locks();
Konstantin Osipov's avatar
Konstantin Osipov committed
1340
    my_ok(thd);
unknown's avatar
unknown committed
1341 1342
    break;
  }
1343
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
1344
  case COM_SHUTDOWN:
1345
  {
1346
    status_var_increment(thd->status_var.com_other);
unknown's avatar
unknown committed
1347
    if (check_global_access(thd,SHUTDOWN_ACL))
unknown's avatar
unknown committed
1348
      break; /* purecov: inspected */
1349
    /*
1350
      If the client is < 4.1.3, it is going to send us no argument; then
1351
      packet_length is 0, packet[0] is the end 0 of the packet. Note that
1352 1353
      SHUTDOWN_DEFAULT is 0. If client is >= 4.1.3, the shutdown level is in
      packet[0].
1354
    */
1355
    enum mysql_enum_shutdown_level level;
1356
    level= (enum mysql_enum_shutdown_level) (uchar) packet[0];
1357 1358 1359 1360 1361 1362 1363
    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;
    }
1364
    DBUG_PRINT("quit",("Got shutdown command for level %u", level));
unknown's avatar
unknown committed
1365
    general_log_print(thd, command, NullS);
1366
    my_eof(thd);
unknown's avatar
unknown committed
1367 1368 1369
    kill_mysql();
    error=TRUE;
    break;
1370
  }
1371
#endif
unknown's avatar
unknown committed
1372 1373
  case COM_STATISTICS:
  {
1374
    STATUS_VAR *current_global_status_var;      // Big; Don't allocate on stack
1375
    ulong uptime;
Michael Widenius's avatar
Michael Widenius committed
1376
    uint length __attribute__((unused));
1377
    ulonglong queries_per_second1000;
1378 1379
    char buff[250];
    uint buff_len= sizeof(buff);
1380

1381 1382 1383
    if (!(current_global_status_var= (STATUS_VAR*)
          thd->alloc(sizeof(STATUS_VAR))))
      break;
1384
    general_log_print(thd, command, NullS);
1385
    status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_STATUS]);
1386
    calc_sum_of_all_status(current_global_status_var);
1387 1388 1389
    if (!(uptime= (ulong) (thd->start_time - server_start_time)))
      queries_per_second1000= 0;
    else
1390
      queries_per_second1000= thd->query_id * 1000 / uptime;
1391

1392
    length= my_snprintf(buff, buff_len - 1,
1393 1394
                        "Uptime: %lu  Threads: %d  Questions: %lu  "
                        "Slow queries: %lu  Opens: %lu  Flush tables: %lu  "
1395
                        "Open tables: %u  Queries per second avg: %u.%03u",
1396 1397
                        uptime,
                        (int) thread_count, (ulong) thd->query_id,
Sergei Golubchik's avatar
Sergei Golubchik committed
1398 1399
                        current_global_status_var->long_query_count,
                        current_global_status_var->opened_tables,
1400 1401
                        refresh_version,
                        cached_open_tables(),
1402 1403
                        (uint) (queries_per_second1000 / 1000),
                        (uint) (queries_per_second1000 % 1000));
1404 1405
#ifdef EMBEDDED_LIBRARY
    /* Store the buffer in permanent memory */
1406
    my_ok(thd, 0, 0, buff);
1407
#else
Konstantin Osipov's avatar
Konstantin Osipov committed
1408 1409
    (void) my_net_write(net, (uchar*) buff, length);
    (void) net_flush(net);
Marc Alff's avatar
Marc Alff committed
1410
    thd->stmt_da->disable_status();
unknown's avatar
unknown committed
1411
#endif
unknown's avatar
unknown committed
1412 1413 1414
    break;
  }
  case COM_PING:
1415
    status_var_increment(thd->status_var.com_other);
1416
    my_ok(thd);				// Tell client we are alive
unknown's avatar
unknown committed
1417 1418
    break;
  case COM_PROCESS_INFO:
1419
    status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_PROCESSLIST]);
1420 1421
    if (!thd->security_ctx->priv_user[0] &&
        check_global_access(thd, PROCESS_ACL))
unknown's avatar
unknown committed
1422
      break;
unknown's avatar
unknown committed
1423
    general_log_print(thd, command, NullS);
unknown's avatar
unknown committed
1424
    mysqld_list_processes(thd,
1425 1426
			  thd->security_ctx->master_access & PROCESS_ACL ? 
			  NullS : thd->security_ctx->priv_user, 0);
unknown's avatar
unknown committed
1427 1428 1429
    break;
  case COM_PROCESS_KILL:
  {
1430
    status_var_increment(thd->status_var.com_stat[SQLCOM_KILL]);
1431
    ulong id=(ulong) uint4korr(packet);
1432
    sql_kill(thd,id, KILL_CONNECTION_HARD);
unknown's avatar
unknown committed
1433 1434
    break;
  }
1435 1436
  case COM_SET_OPTION:
  {
1437
    status_var_increment(thd->status_var.com_stat[SQLCOM_SET_OPTION]);
unknown's avatar
unknown committed
1438 1439 1440 1441
    uint opt_command= uint2korr(packet);

    switch (opt_command) {
    case (int) MYSQL_OPTION_MULTI_STATEMENTS_ON:
1442
      thd->client_capabilities|= CLIENT_MULTI_STATEMENTS;
1443
      my_eof(thd);
1444
      break;
unknown's avatar
unknown committed
1445
    case (int) MYSQL_OPTION_MULTI_STATEMENTS_OFF:
1446
      thd->client_capabilities&= ~CLIENT_MULTI_STATEMENTS;
1447
      my_eof(thd);
1448 1449
      break;
    default:
unknown's avatar
unknown committed
1450
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
1451 1452 1453 1454
      break;
    }
    break;
  }
unknown's avatar
unknown committed
1455
  case COM_DEBUG:
1456
    status_var_increment(thd->status_var.com_other);
unknown's avatar
unknown committed
1457
    if (check_global_access(thd, SUPER_ACL))
unknown's avatar
unknown committed
1458
      break;					/* purecov: inspected */
1459
    mysql_print_status();
unknown's avatar
unknown committed
1460
    general_log_print(thd, command, NullS);
1461
    my_eof(thd);
unknown's avatar
unknown committed
1462 1463 1464 1465 1466
    break;
  case COM_SLEEP:
  case COM_CONNECT:				// Impossible here
  case COM_TIME:				// Impossible from client
  case COM_DELAYED_INSERT:
1467
  case COM_END:
unknown's avatar
unknown committed
1468
  default:
unknown's avatar
unknown committed
1469
    my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
unknown's avatar
unknown committed
1470 1471
    break;
  }
1472 1473 1474
  DBUG_ASSERT(thd->derived_tables == NULL &&
              (thd->open_tables == NULL ||
               (thd->locked_tables_mode == LTM_LOCK_TABLES)));
1475

1476
  thd_proc_info(thd, "updating status");
1477 1478
  /* Finalize server status flags after executing a command. */
  thd->update_server_status();
1479
  thd->protocol->end_statement();
1480
  query_cache_end_of_result(thd);
unknown's avatar
unknown committed
1481

1482
  if (!thd->is_error() && !thd->killed_errno())
1483
    mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0);
1484

1485 1486 1487 1488
  mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS,
                      thd->stmt_da->is_error() ? thd->stmt_da->sql_errno() : 0,
                      command_name[command].str);

1489 1490
  thd->update_all_stats();

1491
  log_slow_statement(thd);
1492

1493
  thd_proc_info(thd, "cleaning up");
1494
  thd->reset_query();
1495
  thd->examined_row_count= 0;                   // For processlist
1496
  thd->command=COM_SLEEP;
1497
  thd->set_time();
1498
  dec_thread_running();
1499
  thd_proc_info(thd, 0);
1500 1501
  thd->packet.shrink(thd->variables.net_buffer_length);	// Reclaim some memory
  free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
1502

Konstantin Osipov's avatar
Konstantin Osipov committed
1503 1504 1505
#if defined(ENABLED_PROFILING)
  thd->profiling.finish_current_query();
#endif
1506 1507
  if (MYSQL_QUERY_DONE_ENABLED() || MYSQL_COMMAND_DONE_ENABLED())
  {
1508
    int res __attribute__((unused));
1509 1510 1511 1512 1513 1514 1515
    res= (int) thd->is_error();
    if (command == COM_QUERY)
    {
      MYSQL_QUERY_DONE(res);
    }
    MYSQL_COMMAND_DONE(res);
  }
Sergei Golubchik's avatar
Sergei Golubchik committed
1516

1517 1518
  /* Check that some variables are reset properly */
  DBUG_ASSERT(thd->abort_on_warning == 0);
1519 1520 1521 1522
  DBUG_RETURN(error);
}


1523
void log_slow_statement(THD *thd)
1524
{
unknown's avatar
unknown committed
1525
  DBUG_ENTER("log_slow_statement");
1526 1527 1528 1529 1530 1531 1532

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

1535
  /* Follow the slow log filter configuration. */ 
1536
  if (!thd->enable_slow_log ||
1537 1538
      (thd->variables.log_slow_filter
        && !(thd->variables.log_slow_filter & thd->query_plan_flags)))
1539 1540
    DBUG_VOID_RETURN; 
 
1541 1542 1543 1544 1545 1546
  if (((thd->server_status & SERVER_QUERY_WAS_SLOW) ||
       ((thd->server_status &
         (SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
        opt_log_queries_not_using_indexes &&
        !(sql_command_flags[thd->lex->sql_command] & CF_STATUS_COMMAND))) &&
      thd->examined_row_count >= thd->variables.min_examined_row_limit)
unknown's avatar
unknown committed
1547
  {
1548 1549 1550 1551 1552 1553 1554 1555
    thd->status_var.long_query_count++;
    /*
      If rate limiting of slow log writes is enabled, decide whether to log
      this query to the log or not.
    */ 
    if (thd->variables.log_slow_rate_limit > 1 &&
        (global_query_id % thd->variables.log_slow_rate_limit) != 0)
      DBUG_VOID_RETURN;
1556

1557
    thd_proc_info(thd, "logging slow query");
1558 1559 1560
    slow_log_print(thd, thd->query(), thd->query_length(), 
                   thd->utime_after_query);
    thd_proc_info(thd, 0);
unknown's avatar
unknown committed
1561
  }
unknown's avatar
unknown committed
1562
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1563 1564
}

1565

unknown's avatar
unknown committed
1566
/**
unknown's avatar
unknown committed
1567 1568 1569 1570 1571 1572 1573
  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
1574 1575 1576 1577 1578 1579 1580
  @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
1581 1582 1583 1584
    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
1585
  @retval
unknown's avatar
unknown committed
1586
    0                 success
unknown's avatar
unknown committed
1587
  @retval
unknown's avatar
unknown committed
1588 1589 1590 1591
    1                 out of memory or SHOW commands are not allowed
                      in this version of the server.
*/

1592 1593 1594
int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
                         enum enum_schema_tables schema_table_idx)
{
1595
  SELECT_LEX *schema_select_lex= NULL;
1596
  DBUG_ENTER("prepare_schema_table");
1597

1598
  switch (schema_table_idx) {
1599 1600
  case SCH_SCHEMATA:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
unknown's avatar
unknown committed
1601 1602
    my_message(ER_NOT_ALLOWED_COMMAND,
               ER(ER_NOT_ALLOWED_COMMAND), MYF(0));   /* purecov: inspected */
1603 1604 1605 1606
    DBUG_RETURN(1);
#else
    break;
#endif
1607

1608 1609 1610
  case SCH_TABLE_NAMES:
  case SCH_TABLES:
  case SCH_VIEWS:
1611
  case SCH_TRIGGERS:
unknown's avatar
unknown committed
1612
  case SCH_EVENTS:
1613
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
1614 1615
    my_message(ER_NOT_ALLOWED_COMMAND,
               ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
1616 1617 1618
    DBUG_RETURN(1);
#else
    {
1619
      LEX_STRING db;
1620
      size_t dummy;
unknown's avatar
unknown committed
1621
      if (lex->select_lex.db == NULL &&
1622
          lex->copy_db_to(&lex->select_lex.db, &dummy))
1623
      {
unknown's avatar
unknown committed
1624
        DBUG_RETURN(1);
1625
      }
1626 1627 1628
      schema_select_lex= new SELECT_LEX();
      db.str= schema_select_lex->db= lex->select_lex.db;
      schema_select_lex->table_list.first= NULL;
1629
      db.length= strlen(db.str);
unknown's avatar
unknown committed
1630

1631
      if (check_db_name(&db))
1632
      {
1633
        my_error(ER_WRONG_DB_NAME, MYF(0), db.str);
1634 1635 1636 1637 1638 1639 1640
        DBUG_RETURN(1);
      }
      break;
    }
#endif
  case SCH_COLUMNS:
  case SCH_STATISTICS:
unknown's avatar
unknown committed
1641
  {
1642
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
1643 1644
    my_message(ER_NOT_ALLOWED_COMMAND,
               ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */
1645 1646
    DBUG_RETURN(1);
#else
unknown's avatar
unknown committed
1647 1648 1649 1650 1651 1652
    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();
1653 1654
    if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ,
                                              MDL_SHARED_READ))
unknown's avatar
unknown committed
1655 1656 1657 1658
      DBUG_RETURN(1);
    lex->query_tables_last= query_tables_last;
    break;
  }
1659
#endif
1660 1661 1662 1663 1664
  case SCH_PROFILES:
    /* 
      Mark this current profiling record to be discarded.  We don't
      wish to have SHOW commands show up in profiling.
    */
1665
#if defined(ENABLED_PROFILING)
1666
    thd->profiling.discard_current_query();
1667 1668
#endif
    break;
1669 1670
  case SCH_USER_STATS:
  case SCH_CLIENT_STATS:
1671
    if (check_global_access(thd, SUPER_ACL | PROCESS_ACL, true))
1672 1673 1674
      DBUG_RETURN(1);
  case SCH_TABLE_STATS:
  case SCH_INDEX_STATS:
1675 1676 1677
  case SCH_OPEN_TABLES:
  case SCH_VARIABLES:
  case SCH_STATUS:
1678 1679
  case SCH_PROCEDURES:
  case SCH_CHARSETS:
unknown's avatar
unknown committed
1680
  case SCH_ENGINES:
1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697
  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);
  }
1698
  TABLE_LIST *table_list= select_lex->table_list.first;
unknown's avatar
unknown committed
1699
  table_list->schema_select_lex= schema_select_lex;
1700
  table_list->schema_table_reformed= 1;
1701 1702 1703 1704
  DBUG_RETURN(0);
}


unknown's avatar
unknown committed
1705 1706 1707
/**
  Read query from packet and store in thd->query.
  Used in COM_QUERY and COM_STMT_PREPARE.
1708 1709

    Sets the following THD variables:
unknown's avatar
unknown committed
1710 1711
  - query
  - query_length
1712

unknown's avatar
unknown committed
1713
  @retval
unknown's avatar
unknown committed
1714
    FALSE ok
unknown's avatar
unknown committed
1715
  @retval
unknown's avatar
unknown committed
1716
    TRUE  error;  In this case thd->fatal_error is set
1717 1718
*/

1719
bool alloc_query(THD *thd, const char *packet, uint packet_length)
1720
{
1721
  char *query;
1722
  /* Remove garbage at start and end of query */
1723
  while (packet_length > 0 && my_isspace(thd->charset(), packet[0]))
1724 1725 1726 1727
  {
    packet++;
    packet_length--;
  }
1728
  const char *pos= packet + packet_length;     // Point at end null
unknown's avatar
unknown committed
1729
  while (packet_length > 0 &&
unknown's avatar
unknown committed
1730
	 (pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1])))
1731 1732 1733 1734
  {
    pos--;
    packet_length--;
  }
1735 1736 1737 1738 1739 1740 1741 1742 1743 1744
  /* We must allocate some extra memory for query cache 

    The query buffer layout is:
       buffer :==
            <statement>   The input statement(s)
            '\0'          Terminating null char  (1 byte)
            <length>      Length of following current database name (size_t)
            <db_name>     Name of current database
            <flags>       Flags struct
  */
1745 1746 1747
  if (! (query= (char*) thd->memdup_w_gap(packet,
                                          packet_length,
                                          1 + thd->db_length +
1748
                                          QUERY_CACHE_DB_LENGTH_SIZE +
1749 1750 1751
                                          QUERY_CACHE_FLAGS_SIZE)))
      return TRUE;
  query[packet_length]= '\0';
1752 1753 1754 1755 1756
  /*
    Space to hold the name of the current database is allocated.  We
    also store this length, in case current database is changed during
    execution.  We might need to reallocate the 'query' buffer
  */
1757
  int2store(query + packet_length + 1, thd->db_length);
1758
    
1759
  thd->set_query(query, packet_length);
1760 1761 1762 1763

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

unknown's avatar
unknown committed
1765
  return FALSE;
1766 1767
}

1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780
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;
1781
  thd->variables.lc_time_names= &my_locale_en_US;
1782 1783 1784
  thd->one_shot_set= 0;
}

1785

1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831
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);

1832
    if (thd->slave_thread && lex->sphead)
1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845
      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)) &&
1846
        check_global_access(thd, SUPER_ACL, true))
1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870
    {
      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
1871
/**
1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904
  Auxiliary call that opens and locks tables for LOCK TABLES statement
  and initializes the list of locked tables.

  @param thd     Thread context.
  @param tables  List of tables to be locked.

  @return FALSE in case of success, TRUE in case of error.
*/

static bool lock_tables_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
{
  Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;
  uint counter;
  TABLE_LIST *table;

  thd->in_lock_tables= 1;

  if (open_tables(thd, &tables, &counter, 0, &lock_tables_prelocking_strategy))
    goto err;

  /*
    We allow to change temporary tables even if they were locked for read
    by LOCK TABLES. To avoid a discrepancy between lock acquired at LOCK
    TABLES time and by the statement which is later executed under LOCK TABLES
    we ensure that for temporary tables we always request a write lock (such
    discrepancy can cause problems for the storage engine).
    We don't set TABLE_LIST::lock_type in this case as this might result in
    extra warnings from THD::decide_logging_format() even though binary logging
    is totally irrelevant for LOCK TABLES.
  */
  for (table= tables; table; table= table->next_global)
    if (!table->placeholder() && table->table->s->tmp_table)
      table->table->reginfo.lock_type= TL_WRITE;
1905

1906 1907 1908 1909 1910
  if (lock_tables(thd, tables, counter, 0) ||
      thd->locked_tables_list.init_locked_tables(thd))
    goto err;

  thd->in_lock_tables= 0;
1911

1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931
  return FALSE;

err:
  thd->in_lock_tables= 0;

  trans_rollback_stmt(thd);
  /*
    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
  */
  trans_commit_implicit(thd);
  /* Close tables and release metadata locks. */
  close_thread_tables(thd);
  DBUG_ASSERT(!thd->locked_tables_mode);
  thd->mdl_context.release_transactional_locks();
  return TRUE;
}


unknown's avatar
unknown committed
1932 1933
/**
  Execute command saved in thd and lex->sql_command.
1934

unknown's avatar
unknown committed
1935 1936 1937 1938 1939 1940 1941 1942 1943 1944
  @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
1945
    FALSE       OK
unknown's avatar
unknown committed
1946
  @retval
1947 1948
    TRUE        Error
*/
unknown's avatar
unknown committed
1949

1950
int
1951
mysql_execute_command(THD *thd)
unknown's avatar
unknown committed
1952
{
1953
  int res= FALSE;
unknown's avatar
unknown committed
1954
  int  up_result= 0;
1955
  LEX  *lex= thd->lex;
unknown's avatar
unknown committed
1956
  /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
unknown's avatar
unknown committed
1957
  SELECT_LEX *select_lex= &lex->select_lex;
unknown's avatar
VIEW  
unknown committed
1958
  /* first table of first SELECT_LEX */
1959
  TABLE_LIST *first_table= select_lex->table_list.first;
unknown's avatar
VIEW  
unknown committed
1960 1961 1962
  /* list of all tables in query */
  TABLE_LIST *all_tables;
  /* most outer SELECT_LEX_UNIT of query */
1963
  SELECT_LEX_UNIT *unit= &lex->unit;
1964 1965 1966
#ifdef HAVE_REPLICATION
  /* have table map for update for multi-update statement (BUG#37051) */
  bool have_table_map_for_update= FALSE;
1967 1968
  /* */
  Rpl_filter *rpl_filter= thd->rpl_filter;
1969
#endif
unknown's avatar
unknown committed
1970
  DBUG_ENTER("mysql_execute_command");
unknown's avatar
unknown committed
1971 1972 1973
#ifdef WITH_PARTITION_STORAGE_ENGINE
  thd->work_part_info= 0;
#endif
unknown's avatar
unknown committed
1974

1975
  DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt);
unknown's avatar
VIEW  
unknown committed
1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991
  /*
    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();
1992
  /* should be assigned after making first tables same */
unknown's avatar
VIEW  
unknown committed
1993
  all_tables= lex->query_tables;
1994 1995
  /* set context for commands which do not use setup_tables */
  select_lex->
1996
    context.resolve_in_table_list_only(select_lex->
1997
                                       table_list.first);
unknown's avatar
VIEW  
unknown committed
1998

1999 2000 2001 2002 2003 2004
  /*
    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
2005 2006 2007 2008 2009 2010 2011 2012
  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);
  }
2013

unknown's avatar
SCRUM  
unknown committed
2014
#ifdef HAVE_REPLICATION
2015
  if (unlikely(thd->slave_thread))
2016
  {
2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039
    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;
    }
2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081

    /*
      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;
    }
2082
    
unknown's avatar
unknown committed
2083
    /*
unknown's avatar
unknown committed
2084 2085
      Check if statment should be skipped because of slave filtering
      rules
2086 2087

      Exceptions are:
unknown's avatar
unknown committed
2088 2089
      - UPDATE MULTI: For this statement, we want to check the filtering
        rules later in the code
2090
      - SET: we always execute it (Not that many SET commands exists in
unknown's avatar
unknown committed
2091 2092
        the binary log anyway -- only 4.1 masters write SET statements,
	in 5.0 there are no SET statements in the binary log)
2093 2094
      - 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
2095
    */
unknown's avatar
unknown committed
2096 2097
    if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) &&
	!(lex->sql_command == SQLCOM_SET_OPTION) &&
2098
	!(lex->sql_command == SQLCOM_DROP_TABLE &&
2099
          lex->drop_temporary && lex->check_exists) &&
unknown's avatar
Merge  
unknown committed
2100
        all_tables_not_ok(thd, all_tables))
unknown's avatar
unknown committed
2101 2102
    {
      /* we warn the slave SQL thread */
unknown's avatar
unknown committed
2103
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120
      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);
      }
2121
      DBUG_RETURN(0);
unknown's avatar
unknown committed
2122
    }
2123 2124 2125 2126 2127
    /* 
       Execute deferred events first
    */
    if (slave_execute_deferred_events(thd))
      DBUG_RETURN(-1);
2128
  }
2129
  else
2130
  {
2131
#endif /* HAVE_REPLICATION */
2132 2133 2134 2135
    /*
      When option readonly is set deny operations which change non-temporary
      tables. Except for the replication thread and the 'super' users.
    */
2136
    if (deny_updates_if_read_only_option(thd, all_tables))
2137 2138 2139 2140 2141 2142 2143
    {
      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
2144

2145
  status_var_increment(thd->status_var.com_stat[lex->sql_command]);
2146 2147
  thd->progress.report_to_client= test(sql_command_flags[lex->sql_command] &
                                       CF_REPORT_PROGRESS);
2148

unknown's avatar
unknown committed
2149
  DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE);
Konstantin Osipov's avatar
Konstantin Osipov committed
2150

2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166
  /* store old value of binlog format */
  enum_binlog_format orig_binlog_format,orig_current_stmt_binlog_format;

  thd->get_binlog_format(&orig_binlog_format,
                         &orig_current_stmt_binlog_format);

  /*
    Force statement logging for DDL commands to allow us to update
    privilege, system or statistic tables directly without the updates
    getting logged.
  */
  if (!(sql_command_flags[lex->sql_command] &
        (CF_CAN_GENERATE_ROW_EVENTS | CF_FORCE_ORIGINAL_BINLOG_FORMAT |
         CF_STATUS_COMMAND)))
    thd->set_binlog_format_stmt();

Konstantin Osipov's avatar
Konstantin Osipov committed
2167 2168 2169 2170 2171 2172
  /*
    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.
  */
2173 2174
  if (stmt_causes_implicit_commit(thd, CF_IMPLICT_COMMIT_BEGIN))
  {
2175 2176 2177 2178 2179
    /*
      Note that this should never happen inside of stored functions
      or triggers as all such statements prohibited there.
    */
    DBUG_ASSERT(! thd->in_sub_stmt);
2180 2181 2182 2183 2184
    /* 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;
2185
    /* Release metadata locks acquired in this transaction. */
2186
    thd->mdl_context.release_transactional_locks();
2187
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
2188

2189 2190 2191 2192 2193
#ifndef DBUG_OFF
  if (lex->sql_command != SQLCOM_SET_OPTION)
    DEBUG_SYNC(thd,"before_execute_sql_command");
#endif

unknown's avatar
unknown committed
2194
  switch (lex->sql_command) {
2195

2196
  case SQLCOM_SHOW_EVENTS:
2197 2198 2199 2200
#ifndef HAVE_EVENT_SCHEDULER
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
    break;
#endif
2201 2202
  case SQLCOM_SHOW_STATUS_PROC:
  case SQLCOM_SHOW_STATUS_FUNC:
Konstantin Osipov's avatar
Konstantin Osipov committed
2203
    if ((res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
2204
                                  UINT_MAX, FALSE)))
Konstantin Osipov's avatar
Konstantin Osipov committed
2205 2206
      goto error;
    res= execute_sqlcom_select(thd, all_tables);
2207 2208 2209
    break;
  case SQLCOM_SHOW_STATUS:
  {
2210
    execute_show_status(thd, all_tables);
2211 2212
    break;
  }
2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223
  case SQLCOM_SHOW_EXPLAIN:
  {
    if (!thd->security_ctx->priv_user[0] &&
        check_global_access(thd,PROCESS_ACL))
      break;

    /*
      The select should use only one table, it's the SHOW EXPLAIN pseudo-table
    */
    if (lex->sroutines.records || lex->query_tables->next_global)
    {
2224 2225 2226
      my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
		 MYF(0));
      goto error;
2227 2228
    }

2229
    Item **it= lex->value_list.head_ref();
2230 2231
    if (!(*it)->basic_const_item() ||
        (!(*it)->fixed && (*it)->fix_fields(lex->thd, it)) || 
2232 2233 2234 2235 2236 2237 2238 2239
        (*it)->check_cols(1))
    {
      my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
		 MYF(0));
      goto error;
    }
    /* no break; fall through */
  }
2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250
  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:
2251
  case SQLCOM_SHOW_STORAGE_ENGINES:
2252
  case SQLCOM_SHOW_PROFILE:
2253 2254 2255 2256
  case SQLCOM_SHOW_CLIENT_STATS:
  case SQLCOM_SHOW_USER_STATS:
  case SQLCOM_SHOW_TABLE_STATS:
  case SQLCOM_SHOW_INDEX_STATS:
unknown's avatar
unknown committed
2257
  case SQLCOM_SELECT:
2258
  {
2259
    thd->status_var.last_query_cost= 0.0;
2260 2261 2262 2263 2264 2265 2266 2267

    /*
      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
2268
    if (all_tables)
2269
      res= check_table_access(thd,
2270 2271
                              privileges_requested,
                              all_tables, FALSE, UINT_MAX, FALSE);
unknown's avatar
unknown committed
2272
    else
Marc Alff's avatar
Marc Alff committed
2273
      res= check_access(thd, privileges_requested, any_db, NULL, NULL, 0, 0);
2274

2275 2276 2277 2278
    if (res)
      break;

    res= execute_sqlcom_select(thd, all_tables);
unknown's avatar
unknown committed
2279
    break;
2280 2281
  }
case SQLCOM_PREPARE:
2282
  {
2283
    mysql_sql_stmt_prepare(thd);
unknown's avatar
unknown committed
2284 2285 2286 2287
    break;
  }
  case SQLCOM_EXECUTE:
  {
2288
    mysql_sql_stmt_execute(thd);
unknown's avatar
unknown committed
2289 2290 2291 2292
    break;
  }
  case SQLCOM_DEALLOCATE_PREPARE:
  {
2293
    mysql_sql_stmt_close(thd);
unknown's avatar
unknown committed
2294 2295
    break;
  }
unknown's avatar
unknown committed
2296
  case SQLCOM_DO:
2297
    if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
2298
        || open_and_lock_tables(thd, all_tables, TRUE, 0))
unknown's avatar
unknown committed
2299
      goto error;
unknown's avatar
unknown committed
2300 2301

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

2304
  case SQLCOM_EMPTY_QUERY:
2305
    my_ok(thd);
2306 2307
    break;

unknown's avatar
unknown committed
2308 2309 2310 2311
  case SQLCOM_HELP:
    res= mysqld_help(thd,lex->help_arg);
    break;

2312
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
2313
  case SQLCOM_PURGE:
2314
  {
unknown's avatar
unknown committed
2315
    if (check_global_access(thd, SUPER_ACL))
2316
      goto error;
unknown's avatar
unknown committed
2317
    /* PURGE MASTER LOGS TO 'file' */
2318 2319 2320
    res = purge_master_logs(thd, lex->to_log);
    break;
  }
2321 2322
  case SQLCOM_PURGE_BEFORE:
  {
2323 2324
    Item *it;

2325 2326
    if (check_global_access(thd, SUPER_ACL))
      goto error;
unknown's avatar
unknown committed
2327
    /* PURGE MASTER LOGS BEFORE 'data' */
2328
    it= (Item *)lex->value_list.head();
2329
    if ((!it->fixed && it->fix_fields(lex->thd, &it)) ||
unknown's avatar
unknown committed
2330
        it->check_cols(1))
2331 2332 2333 2334 2335
    {
      my_error(ER_WRONG_ARGUMENTS, MYF(0), "PURGE LOGS BEFORE");
      goto error;
    }
    it= new Item_func_unix_timestamp(it);
2336
    it->fix_fields(thd, &it);
2337
    res = purge_master_logs_before_date(thd, (ulong)it->val_int());
2338 2339
    break;
  }
2340
#endif
unknown's avatar
unknown committed
2341 2342
  case SQLCOM_SHOW_WARNS:
  {
2343 2344
    res= mysqld_show_warnings(thd, (ulong)
			      ((1L << (uint) MYSQL_ERROR::WARN_LEVEL_NOTE) |
2345 2346 2347
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_WARN) |
			       (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)
			       ));
unknown's avatar
unknown committed
2348 2349 2350 2351
    break;
  }
  case SQLCOM_SHOW_ERRORS:
  {
2352 2353
    res= mysqld_show_warnings(thd, (ulong)
			      (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
unknown's avatar
unknown committed
2354 2355
    break;
  }
unknown's avatar
unknown committed
2356 2357
  case SQLCOM_SHOW_PROFILES:
  {
2358
#if defined(ENABLED_PROFILING)
2359
    thd->profiling.discard_current_query();
unknown's avatar
unknown committed
2360
    res= thd->profiling.show_profiles();
2361 2362 2363
    if (res)
      goto error;
#else
2364
    my_error(ER_FEATURE_DISABLED, MYF(0), "SHOW PROFILES", "enable-profiling");
2365 2366
    goto error;
#endif
unknown's avatar
unknown committed
2367 2368
    break;
  }
2369

unknown's avatar
unknown committed
2370
#ifdef HAVE_REPLICATION
2371 2372
  case SQLCOM_SHOW_SLAVE_HOSTS:
  {
unknown's avatar
unknown committed
2373
    if (check_global_access(thd, REPL_SLAVE_ACL))
2374 2375 2376 2377
      goto error;
    res = show_slave_hosts(thd);
    break;
  }
2378
  case SQLCOM_SHOW_RELAYLOG_EVENTS: /* fall through */
unknown's avatar
unknown committed
2379 2380
  case SQLCOM_SHOW_BINLOG_EVENTS:
  {
unknown's avatar
unknown committed
2381
    if (check_global_access(thd, REPL_SLAVE_ACL))
unknown's avatar
unknown committed
2382
      goto error;
2383
    res = mysql_show_binlog_events(thd);
unknown's avatar
unknown committed
2384 2385
    break;
  }
2386 2387
#endif

unknown's avatar
unknown committed
2388 2389
  case SQLCOM_ASSIGN_TO_KEYCACHE:
  {
unknown's avatar
VIEW  
unknown committed
2390
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
2391
    if (check_access(thd, INDEX_ACL, first_table->db,
Marc Alff's avatar
Marc Alff committed
2392 2393 2394
                     &first_table->grant.privilege,
                     &first_table->grant.m_internal,
                     0, 0))
unknown's avatar
unknown committed
2395
      goto error;
2396
    res= mysql_assign_to_keycache(thd, first_table, &lex->ident);
unknown's avatar
unknown committed
2397 2398
    break;
  }
unknown's avatar
unknown committed
2399 2400
  case SQLCOM_PRELOAD_KEYS:
  {
unknown's avatar
VIEW  
unknown committed
2401
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
2402
    if (check_access(thd, INDEX_ACL, first_table->db,
Marc Alff's avatar
Marc Alff committed
2403 2404 2405
                     &first_table->grant.privilege,
                     &first_table->grant.m_internal,
                     0, 0))
2406
      goto error;
unknown's avatar
VIEW  
unknown committed
2407
    res = mysql_preload_keys(thd, first_table);
unknown's avatar
unknown committed
2408 2409
    break;
  }
unknown's avatar
unknown committed
2410
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
2411
  case SQLCOM_CHANGE_MASTER:
2412
  {
2413 2414 2415
    LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
    Master_info *mi;
    bool new_master= 0;
2416
    bool master_info_added;
2417

unknown's avatar
unknown committed
2418
    if (check_global_access(thd, SUPER_ACL))
2419
      goto error;
Marc Alff's avatar
Marc Alff committed
2420
    mysql_mutex_lock(&LOCK_active_mi);
2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438

    mi= master_info_index->get_master_info(&lex_mi->connection_name,
                                           MYSQL_ERROR::WARN_LEVEL_NOTE);

    if (mi == NULL)
    {
      /* New replication created */
      mi= new Master_info(&lex_mi->connection_name, relay_log_recovery); 
      if (!mi || mi->error())
      {
        delete mi;
        res= 1;
        mysql_mutex_unlock(&LOCK_active_mi);
        break;
      }
      new_master= 1;
    }

2439
    res= change_master(thd, mi, &master_info_added);
2440 2441 2442
    if (res && new_master)
    {
      /*
2443 2444 2445 2446
        If the new master was added by change_master(), remove it as it didn't
        work (this will free mi as well).

        If new master was not added, we still need to free mi.
2447
      */
2448 2449 2450 2451
      if (master_info_added)
        master_info_index->remove_master_info(&lex_mi->connection_name);
      else
        delete mi;
2452
    }
2453 2454 2455 2456 2457
    else
    {
      mi->rpl_filter= get_or_create_rpl_filter(lex_mi->connection_name.str,
                                               lex_mi->connection_name.length);
    }
2458

Marc Alff's avatar
Marc Alff committed
2459
    mysql_mutex_unlock(&LOCK_active_mi);
2460 2461
    break;
  }
unknown's avatar
unknown committed
2462
  case SQLCOM_SHOW_SLAVE_STAT:
2463
  {
2464 2465
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2466
      goto error;
Marc Alff's avatar
Marc Alff committed
2467
    mysql_mutex_lock(&LOCK_active_mi);
2468 2469 2470

    if (lex->verbose)
      res= show_all_master_info(thd);
2471 2472
    else
    {
2473 2474 2475 2476 2477 2478 2479 2480
      LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
      Master_info *mi;
      mi= master_info_index->get_master_info(&lex_mi->connection_name,
                                             MYSQL_ERROR::WARN_LEVEL_ERROR);
      if (mi != NULL)
      {
        res= show_master_info(thd, mi, 0);
      }
2481
    }
Marc Alff's avatar
Marc Alff committed
2482
    mysql_mutex_unlock(&LOCK_active_mi);
2483 2484
    break;
  }
unknown's avatar
unknown committed
2485
  case SQLCOM_SHOW_MASTER_STAT:
2486
  {
2487 2488
    /* Accept one of two privileges */
    if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
2489 2490 2491 2492
      goto error;
    res = show_binlog_info(thd);
    break;
  }
unknown's avatar
unknown committed
2493

unknown's avatar
unknown committed
2494
#endif /* HAVE_REPLICATION */
unknown's avatar
unknown committed
2495
  case SQLCOM_SHOW_ENGINE_STATUS:
unknown's avatar
unknown committed
2496
    {
2497
      if (check_global_access(thd, PROCESS_ACL))
unknown's avatar
unknown committed
2498 2499
        goto error;
      res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_STATUS);
unknown's avatar
unknown committed
2500 2501
      break;
    }
unknown's avatar
unknown committed
2502
  case SQLCOM_SHOW_ENGINE_MUTEX:
unknown's avatar
unknown committed
2503
    {
2504
      if (check_global_access(thd, PROCESS_ACL))
unknown's avatar
unknown committed
2505
        goto error;
unknown's avatar
unknown committed
2506
      res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_MUTEX);
unknown's avatar
unknown committed
2507 2508
      break;
    }
unknown's avatar
unknown committed
2509
  case SQLCOM_CREATE_TABLE:
unknown's avatar
unknown committed
2510
  {
unknown's avatar
VIEW  
unknown committed
2511 2512
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    bool link_to_local;
2513 2514 2515
    TABLE_LIST *create_table= first_table;
    TABLE_LIST *select_tables= lex->create_last_non_select_table->next_global;

2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536
    /*
      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
2537

unknown's avatar
VIEW  
unknown committed
2538
    if ((res= create_table_precheck(thd, select_tables, create_table)))
unknown's avatar
unknown committed
2539
      goto end_with_restore_list;
unknown's avatar
unknown committed
2540

2541 2542 2543
    /* Might have been updated in create_table_precheck */
    create_info.alias= create_table->alias;

2544
#ifdef HAVE_READLINK
2545
    /* Fix names if symlinked tables */
2546
    if (append_file_to_dir(thd, &create_info.data_file_name,
2547
			   create_table->table_name) ||
2548
	append_file_to_dir(thd, &create_info.index_file_name,
2549
			   create_table->table_name))
unknown's avatar
unknown committed
2550
      goto end_with_restore_list;
2551
#endif
2552 2553 2554 2555 2556 2557
    /*
      If no engine type was given, work out the default now
      rather than at parse-time.
    */
    if (!(create_info.used_fields & HA_CREATE_USED_ENGINE))
      create_info.db_type= ha_default_handlerton(thd);
2558
    /*
2559
      If we are using SET CHARSET without DEFAULT, add an implicit
2560 2561
      DEFAULT to not confuse old users. (This may change).
    */
2562
    if ((create_info.used_fields &
2563 2564 2565
	 (HA_CREATE_USED_DEFAULT_CHARSET | HA_CREATE_USED_CHARSET)) ==
	HA_CREATE_USED_CHARSET)
    {
2566 2567 2568 2569
      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;
2570
    }
Jon Olav Hauglid's avatar
Jon Olav Hauglid committed
2571

2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582
#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
2583

2584
    /* Close any open handlers for the table. */
2585 2586
    mysql_ha_rm_tables(thd, create_table);

2587
    if (select_lex->item_list.elements)		// With select
unknown's avatar
unknown committed
2588 2589
    {
      select_result *result;
2590

2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603
      /*
        CREATE TABLE...IGNORE/REPLACE SELECT... can be unsafe, unless
        ORDER BY PRIMARY KEY clause is used in SELECT statement. We therefore
        use row based logging if mixed or row based logging is available.
        TODO: Check if the order of the output of the select statement is
        deterministic. Waiting for BUG#42415
      */
      if(lex->ignore)
        lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_CREATE_IGNORE_SELECT);
      
      if(lex->duplicates == DUP_REPLACE)
        lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_CREATE_REPLACE_SELECT);

2604 2605 2606
      /*
        If:
        a) we inside an SP and there was NAME_CONST substitution,
Ramil Kalimullin's avatar
Ramil Kalimullin committed
2607
        b) binlogging is on (STMT mode),
2608 2609 2610 2611 2612 2613
        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
2614
          thd->variables.binlog_format == BINLOG_FORMAT_STMT &&
2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640
          !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.");
      }
      
2641
      select_lex->options|= SELECT_NO_UNLOCK;
2642
      unit->set_limit(select_lex);
2643

2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656
      /*
        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;
      }

2657 2658 2659 2660 2661 2662 2663 2664
      res= open_and_lock_tables(thd, lex->query_tables, TRUE, 0);
      if (res)
      {
        /* Got error or warning. Set res to 1 if error */
        if (!(res= thd->is_error()))
          my_ok(thd);                           // CREATE ... IF NOT EXISTS
      }
      else
2665
      {
2666 2667
        /* The table already exists */
        if (create_table->table)
2668
        {
2669
          if (create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS)
2670
          {
2671 2672 2673 2674 2675
            push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                                ER_TABLE_EXISTS_ERROR,
                                ER(ER_TABLE_EXISTS_ERROR),
                                create_info.alias);
            my_ok(thd);
2676
          }
2677
          else
unknown's avatar
unknown committed
2678
          {
2679 2680
            my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_info.alias);
            res= 1;
unknown's avatar
unknown committed
2681
          }
2682
          goto end_with_restore_list;
unknown's avatar
unknown committed
2683
        }
2684

2685 2686 2687 2688 2689 2690 2691
        /*
          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);

2692 2693 2694 2695
        /* 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
2696
        /*
2697 2698
          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
2699
        */
unknown's avatar
VIEW  
unknown committed
2700
        if ((result= new select_create(create_table,
2701 2702 2703 2704
                                       &create_info,
                                       &alter_info,
                                       select_lex->item_list,
                                       lex->duplicates,
2705
                                       lex->ignore,
2706
                                       select_tables)))
2707 2708 2709 2710 2711
        {
          /*
            CREATE from SELECT give its SELECT_LEX for SELECT,
            and item_list belong to SELECT
          */
2712
          res= handle_select(thd, lex, result, 0);
2713
          delete result;
2714
        }
2715

2716
        lex->link_first_table_back(create_table, link_to_local);
2717 2718
      }
    }
unknown's avatar
unknown committed
2719
    else
unknown's avatar
unknown committed
2720
    {
2721
      /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
2722
      if (create_info.options & HA_LEX_CREATE_TMP_TABLE)
2723
        thd->variables.option_bits|= OPTION_KEEP_LOG;
unknown's avatar
unknown committed
2724
      /* regular create */
2725
      if (create_info.options & HA_LEX_CREATE_TABLE_LIKE)
2726 2727
      {
        /* CREATE TABLE ... LIKE ... */
unknown's avatar
unknown committed
2728
        res= mysql_create_like_table(thd, create_table, select_tables,
2729
                                     &create_info);
2730
      }
unknown's avatar
unknown committed
2731
      else
2732
      {
2733 2734 2735
        /* Regular CREATE TABLE */
        res= mysql_create_table(thd, create_table,
                                &create_info, &alter_info);
2736
      }
unknown's avatar
unknown committed
2737
      if (!res)
2738
        my_ok(thd);
unknown's avatar
unknown committed
2739
    }
2740

unknown's avatar
unknown committed
2741
end_with_restore_list:
unknown's avatar
unknown committed
2742
    break;
unknown's avatar
unknown committed
2743
  }
unknown's avatar
unknown committed
2744
  case SQLCOM_CREATE_INDEX:
2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762
    /* 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
2763 2764
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (check_one_table_access(thd, INDEX_ACL, all_tables))
unknown's avatar
unknown committed
2765
      goto error; /* purecov: inspected */
2766 2767 2768 2769 2770 2771
    /*
      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;
2772
    thd->query_plan_flags|= QPLAN_ADMIN;
2773 2774 2775 2776 2777

    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
2778

2779 2780
    res= mysql_alter_table(thd, first_table->db, first_table->table_name,
                           &create_info, first_table, &alter_info,
2781
                           0, (ORDER*) 0, 0, 0);
2782 2783
    break;
  }
unknown's avatar
unknown committed
2784
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
2785
  case SQLCOM_SLAVE_START:
2786
  {
2787 2788
    LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
    Master_info *mi;
unknown's avatar
unknown committed
2789 2790 2791 2792
    int load_error;

    load_error= rpl_load_gtid_slave_state(thd);

Marc Alff's avatar
Marc Alff committed
2793
    mysql_mutex_lock(&LOCK_active_mi);
2794 2795 2796 2797

    if ((mi= (master_info_index->
              get_master_info(&lex_mi->connection_name,
                              MYSQL_ERROR::WARN_LEVEL_ERROR))))
unknown's avatar
unknown committed
2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813
    {
      if (load_error)
      {
        /*
          We cannot start a slave using GTID if we cannot load the GTID position
          from the mysql.gtid_slave_pos table. But we can allow non-GTID
          replication (useful eg. during upgrade).
        */
        if (mi->using_gtid != Master_info::USE_GTID_NO)
        {
          mysql_mutex_unlock(&LOCK_active_mi);
          break;
        }
        else
          thd->clear_error();
      }
2814 2815
      if (!start_slave(thd, mi, 1 /* net report*/))
        my_ok(thd);
unknown's avatar
unknown committed
2816
    }
Marc Alff's avatar
Marc Alff committed
2817
    mysql_mutex_unlock(&LOCK_active_mi);
unknown's avatar
unknown committed
2818
    break;
2819
  }
unknown's avatar
unknown committed
2820
  case SQLCOM_SLAVE_STOP:
2821 2822 2823 2824 2825 2826 2827 2828 2829 2830
  {
    LEX_MASTER_INFO *lex_mi;
    Master_info *mi;
    /*
      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,
        so it waits for the client thread because t is locked by it.
2831
    - then the client thread does SLAVE STOP.
2832 2833
      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.
2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846
      To prevent that, refuse SLAVE STOP if the
      client thread has locked tables
    */
    if (thd->locked_tables_mode ||
        thd->in_active_multi_stmt_transaction() ||
        thd->global_read_lock.is_acquired())
    {
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
      goto error;
    }

    lex_mi= &thd->lex->mi;
Marc Alff's avatar
Marc Alff committed
2847
    mysql_mutex_lock(&LOCK_active_mi);
2848 2849 2850
    if ((mi= (master_info_index->
              get_master_info(&lex_mi->connection_name,
                              MYSQL_ERROR::WARN_LEVEL_ERROR))))
2851 2852 2853 2854 2855 2856
      if (!stop_slave(thd, mi, 1/* net report*/))
        my_ok(thd);
    mysql_mutex_unlock(&LOCK_active_mi);
    break;
  }
  case SQLCOM_SLAVE_ALL_START:
2857
  {
2858 2859 2860 2861 2862
    mysql_mutex_lock(&LOCK_active_mi);
    if (!master_info_index->start_all_slaves(thd))
      my_ok(thd);
    mysql_mutex_unlock(&LOCK_active_mi);
    break;
2863
  }
2864
  case SQLCOM_SLAVE_ALL_STOP:
2865
  {
2866 2867 2868 2869 2870 2871 2872 2873
    if (thd->locked_tables_mode ||
        thd->in_active_multi_stmt_transaction() ||
        thd->global_read_lock.is_acquired())
    {
      my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
                 ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
      goto error;
    }
Marc Alff's avatar
Marc Alff committed
2874
    mysql_mutex_lock(&LOCK_active_mi);
2875 2876
    if (!master_info_index->stop_all_slaves(thd))
      my_ok(thd);      
Marc Alff's avatar
Marc Alff committed
2877
    mysql_mutex_unlock(&LOCK_active_mi);
unknown's avatar
unknown committed
2878
    break;
2879
  }
unknown's avatar
unknown committed
2880
#endif /* HAVE_REPLICATION */
unknown's avatar
unknown committed
2881
  case SQLCOM_RENAME_TABLE:
unknown's avatar
unknown committed
2882
  {
2883
    if (execute_rename_table(thd, first_table, all_tables))
unknown's avatar
unknown committed
2884
      goto error;
unknown's avatar
unknown committed
2885
    break;
unknown's avatar
unknown committed
2886
  }
2887
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
2888 2889
  case SQLCOM_SHOW_BINLOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
2890 2891
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0)); /* purecov: inspected */
2892
    goto error;
unknown's avatar
unknown committed
2893 2894
#else
    {
2895
      if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
unknown's avatar
unknown committed
2896 2897 2898 2899
	goto error;
      res = show_binlogs(thd);
      break;
    }
unknown's avatar
unknown committed
2900
#endif
2901
#endif /* EMBEDDED_LIBRARY */
unknown's avatar
unknown committed
2902
  case SQLCOM_SHOW_CREATE:
unknown's avatar
VIEW  
unknown committed
2903
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
2904
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
2905 2906
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0)); /* purecov: inspected */
2907
    goto error;
unknown's avatar
unknown committed
2908
#else
unknown's avatar
unknown committed
2909
    {
2910 2911 2912 2913 2914 2915 2916 2917 2918
     /*
        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.
      */

2919 2920 2921
      DBUG_PRINT("debug", ("lex->only_view: %d, table: %s.%s",
                           lex->only_view,
                           first_table->db, first_table->table_name));
2922
      if (lex->only_view)
2923 2924 2925
      {
        if (check_table_access(thd, SELECT_ACL, first_table, FALSE, 1, FALSE))
        {
2926
          DBUG_PRINT("debug", ("check_table_access failed"));
2927 2928 2929 2930 2931
          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;
        }
2932
        DBUG_PRINT("debug", ("check_table_access succeeded"));
2933 2934

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

2937 2938 2939 2940
      }
      else
      {
        /*
Marc Alff's avatar
Marc Alff committed
2941 2942 2943
          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.
2944
        */
Mattias Jonsson's avatar
Mattias Jonsson committed
2945
        DBUG_PRINT("debug", ("first_table->grant.privilege: %lx",
2946
                             first_table->grant.privilege));
Marc Alff's avatar
Marc Alff committed
2947 2948
        if (check_some_access(thd, SHOW_CREATE_TABLE_ACLS, first_table) ||
            (first_table->grant.privilege & SHOW_CREATE_TABLE_ACLS) == 0)
2949 2950 2951 2952 2953 2954 2955 2956
        {
          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;
        }
      }

2957
      /* Access is granted. Execute the command.  */
unknown's avatar
unknown committed
2958
      res= mysqld_show_create(thd, first_table);
unknown's avatar
unknown committed
2959 2960
      break;
    }
unknown's avatar
unknown committed
2961
#endif
2962 2963
  case SQLCOM_CHECKSUM:
  {
unknown's avatar
VIEW  
unknown committed
2964
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
2965 2966
    if (check_table_access(thd, SELECT_ACL, all_tables,
                           FALSE, UINT_MAX, FALSE))
2967
      goto error; /* purecov: inspected */
2968

unknown's avatar
VIEW  
unknown committed
2969
    res = mysql_checksum_table(thd, first_table, &lex->check_opt);
2970 2971
    break;
  }
unknown's avatar
unknown committed
2972
  case SQLCOM_UPDATE:
2973 2974
  {
    ha_rows found= 0, updated= 0;
unknown's avatar
VIEW  
unknown committed
2975 2976
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if (update_precheck(thd, all_tables))
unknown's avatar
unknown committed
2977
      break;
2978 2979 2980 2981 2982 2983 2984 2985 2986 2987

    /*
      UPDATE IGNORE can be unsafe. We therefore use row based
      logging if mixed or row based logging is available.
      TODO: Check if the order of the output of the select statement is
      deterministic. Waiting for BUG#42415
    */
    if (lex->ignore)
      lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_UPDATE_IGNORE);

2988 2989
    DBUG_ASSERT(select_lex->offset_limit == 0);
    unit->set_limit(select_lex);
2990
    MYSQL_UPDATE_START(thd->query());
unknown's avatar
unknown committed
2991 2992 2993 2994 2995
    res= (up_result= mysql_update(thd, all_tables,
                                  select_lex->item_list,
                                  lex->value_list,
                                  select_lex->where,
                                  select_lex->order_list.elements,
2996
                                  select_lex->order_list.first,
unknown's avatar
unknown committed
2997
                                  unit->select_limit_cnt,
2998 2999 3000
                                  lex->duplicates, lex->ignore,
                                  &found, &updated));
    MYSQL_UPDATE_DONE(res, found, updated);
3001
    /* mysql_update return 2 if we need to switch to multi-update */
unknown's avatar
unknown committed
3002
    if (up_result != 2)
3003
      break;
unknown's avatar
unknown committed
3004
    /* Fall through */
3005
  }
3006
  case SQLCOM_UPDATE_MULTI:
unknown's avatar
unknown committed
3007 3008 3009
  {
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    /* if we switched from normal update, rights are checked */
unknown's avatar
unknown committed
3010
    if (up_result != 2)
3011
    {
unknown's avatar
unknown committed
3012 3013 3014 3015 3016
      if ((res= multi_update_precheck(thd, all_tables)))
        break;
    }
    else
      res= 0;
unknown's avatar
unknown committed
3017

3018
    res= mysql_multi_update_prepare(thd);
unknown's avatar
unknown committed
3019

3020
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
3021
    /* Check slave filtering rules */
3022
    if (unlikely(thd->slave_thread && !have_table_map_for_update))
unknown's avatar
unknown committed
3023
    {
3024 3025
      if (all_tables_not_ok(thd, all_tables))
      {
3026 3027 3028 3029 3030
        if (res!= 0)
        {
          res= 0;             /* don't care of prev failure  */
          thd->clear_error(); /* filters are of highest prior */
        }
3031 3032 3033 3034
        /* we warn the slave SQL thread */
        my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
        break;
      }
3035 3036
      if (res)
        break;
unknown's avatar
unknown committed
3037
    }
3038 3039
    else
    {
3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052
#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
3053 3054
    {
      multi_update *result_obj;
3055
      MYSQL_MULTI_UPDATE_START(thd->query());
3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077
      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
3078
    break;
unknown's avatar
unknown committed
3079
  }
unknown's avatar
unknown committed
3080
  case SQLCOM_REPLACE:
3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104
#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);
3105
        (void) mysql_bin_log.write(&ev);        /* error is ignored */
3106
        if (mysql_bin_log.rotate_and_purge(true))
3107 3108 3109 3110
        {
          res= 1;
          break;
        }
3111 3112 3113 3114
      }
      DBUG_PRINT("debug", ("Just after generate_incident()"));
    }
#endif
3115 3116
  case SQLCOM_INSERT:
  {
unknown's avatar
VIEW  
unknown committed
3117
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
3118
    if ((res= insert_precheck(thd, all_tables)))
unknown's avatar
unknown committed
3119
      break;
3120

3121
    MYSQL_INSERT_START(thd->query());
unknown's avatar
VIEW  
unknown committed
3122
    res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
unknown's avatar
unknown committed
3123
		      lex->update_list, lex->value_list,
3124
                      lex->duplicates, lex->ignore);
3125
    MYSQL_INSERT_DONE(res, (ulong) thd->get_row_count_func());
3126 3127 3128 3129 3130 3131
    /*
      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
3132
    if (first_table->view && !first_table->contain_auto_increment)
3133 3134
      thd->first_successful_insert_id_in_cur_stmt=
        thd->first_successful_insert_id_in_prev_stmt;
3135

3136
#ifdef ENABLED_DEBUG_SYNC
3137 3138
    DBUG_EXECUTE_IF("after_mysql_insert",
                    {
3139
                      const char act1[]=
3140 3141
                        "now "
                        "wait_for signal.continue";
3142 3143 3144
                      const char act2[]=
                        "now "
                        "signal signal.continued";
3145
                      DBUG_ASSERT(debug_sync_service);
3146 3147 3148 3149
                      DBUG_ASSERT(!debug_sync_set_action(thd,
                                                         STRING_WITH_LEN(act1)));
                      DBUG_ASSERT(!debug_sync_set_action(thd,
                                                         STRING_WITH_LEN(act2)));
3150
                    };);
3151
    DEBUG_SYNC(thd, "after_mysql_insert");
3152
#endif
unknown's avatar
unknown committed
3153
    break;
3154
  }
unknown's avatar
unknown committed
3155 3156 3157
  case SQLCOM_REPLACE_SELECT:
  case SQLCOM_INSERT_SELECT:
  {
unknown's avatar
unknown committed
3158
    select_result *sel_result;
unknown's avatar
VIEW  
unknown committed
3159
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
3160
    if ((res= insert_precheck(thd, all_tables)))
3161
      break;
3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178
    /*
      INSERT...SELECT...ON DUPLICATE KEY UPDATE/REPLACE SELECT/
      INSERT...IGNORE...SELECT can be unsafe, unless ORDER BY PRIMARY KEY
      clause is used in SELECT statement. We therefore use row based
      logging if mixed or row based logging is available.
      TODO: Check if the order of the output of the select statement is
      deterministic. Waiting for BUG#42415
    */
    if (lex->sql_command == SQLCOM_INSERT_SELECT &&
        lex->duplicates == DUP_UPDATE)
      lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_SELECT_UPDATE);

    if (lex->sql_command == SQLCOM_INSERT_SELECT && lex->ignore)
      lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_IGNORE_SELECT);

    if (lex->sql_command == SQLCOM_REPLACE_SELECT)
      lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_REPLACE_SELECT);
unknown's avatar
unknown committed
3179

3180
    /* Fix lock for first table */
unknown's avatar
VIEW  
unknown committed
3181 3182
    if (first_table->lock_type == TL_WRITE_DELAYED)
      first_table->lock_type= TL_WRITE;
3183

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

3187
    unit->set_limit(select_lex);
3188

3189
    if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
3190
    {
3191
      MYSQL_INSERT_SELECT_START(thd->query());
3192 3193 3194 3195
      /*
        Only the INSERT table should be merged. Other will be handled by
        select.
      */
3196
      /* Skip first table, which is the table we are inserting in */
unknown's avatar
unknown committed
3197
      TABLE_LIST *second_table= first_table->next_local;
3198
      select_lex->table_list.first= second_table;
3199 3200
      select_lex->context.table_list= 
        select_lex->context.first_name_resolution_table= second_table;
3201
      res= mysql_insert_select_prepare(thd);
unknown's avatar
unknown committed
3202 3203 3204 3205 3206 3207 3208
      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)))
3209
      {
unknown's avatar
unknown committed
3210
	res= handle_select(thd, lex, sel_result, OPTION_SETUP_TABLES_DONE);
3211 3212 3213 3214 3215 3216
        /*
          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.
        */
3217
        if (!res && first_table->lock_type ==  TL_WRITE_CONCURRENT_INSERT &&
3218 3219
            thd->lock)
        {
3220 3221 3222
          /* INSERT ... SELECT should invalidate only the very first table */
          TABLE_LIST *save_table= first_table->next_local;
          first_table->next_local= 0;
3223
          query_cache_invalidate3(thd, first_table, 1);
3224
          first_table->next_local= save_table;
3225
        }
unknown's avatar
unknown committed
3226
        delete sel_result;
3227
      }
3228
      /* revert changes for SP */
3229
      MYSQL_INSERT_SELECT_DONE(res, (ulong) thd->get_row_count_func());
3230
      select_lex->table_list.first= first_table;
3231
    }
3232 3233 3234 3235 3236 3237
    /*
      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
3238
    if (first_table->view && !first_table->contain_auto_increment)
3239 3240
      thd->first_successful_insert_id_in_cur_stmt=
        thd->first_successful_insert_id_in_prev_stmt;
3241

unknown's avatar
unknown committed
3242 3243
    break;
  }
unknown's avatar
unknown committed
3244
  case SQLCOM_DELETE:
unknown's avatar
unknown committed
3245
  {
3246
    select_result *sel_result=lex->result;
unknown's avatar
VIEW  
unknown committed
3247 3248
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    if ((res= delete_precheck(thd, all_tables)))
unknown's avatar
unknown committed
3249
      break;
3250 3251
    DBUG_ASSERT(select_lex->offset_limit == 0);
    unit->set_limit(select_lex);
3252

3253
    MYSQL_DELETE_START(thd->query());
3254 3255 3256 3257 3258 3259 3260
    if (!(sel_result= lex->result) && !(sel_result= new select_send()))
      return 1;                       
    res = mysql_delete(thd, all_tables, 
                       select_lex->where, &select_lex->order_list,
                       unit->select_limit_cnt, select_lex->options,
                       sel_result);
    delete sel_result;
3261
    MYSQL_DELETE_DONE(res, (ulong) thd->get_row_count_func());
unknown's avatar
unknown committed
3262 3263
    break;
  }
3264
  case SQLCOM_DELETE_MULTI:
unknown's avatar
unknown committed
3265
  {
unknown's avatar
VIEW  
unknown committed
3266
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3267
    TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
unknown's avatar
unknown committed
3268
    multi_delete *del_result;
unknown's avatar
unknown committed
3269

3270
    if ((res= multi_delete_precheck(thd, all_tables)))
3271
      break;
unknown's avatar
unknown committed
3272

unknown's avatar
unknown committed
3273
    /* condition will be TRUE on SP re-excuting */
3274 3275
    if (select_lex->item_list.elements != 0)
      select_lex->item_list.empty();
unknown's avatar
unknown committed
3276
    if (add_item_to_list(thd, new Item_null()))
unknown's avatar
unknown committed
3277
      goto error;
3278

3279
    thd_proc_info(thd, "init");
3280
    if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
unknown's avatar
VIEW  
unknown committed
3281 3282
      break;

3283
    MYSQL_MULTI_DELETE_START(thd->query());
unknown's avatar
VIEW  
unknown committed
3284
    if ((res= mysql_multi_delete_prepare(thd)))
3285 3286
    {
      MYSQL_MULTI_DELETE_DONE(1, 0);
unknown's avatar
unknown committed
3287
      goto error;
3288
    }
3289

unknown's avatar
unknown committed
3290 3291
    if (!thd->is_fatal_error &&
        (del_result= new multi_delete(aux_tables, lex->table_count)))
unknown's avatar
unknown committed
3292
    {
3293 3294 3295
      res= mysql_select(thd, &select_lex->ref_pointer_array,
			select_lex->get_table_list(),
			select_lex->with_wild,
3296
			select_lex->item_list,
unknown's avatar
unknown committed
3297
			select_lex->where,
3298
			0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
unknown's avatar
unknown committed
3299
			(ORDER *)NULL,
3300
			(select_lex->options | thd->variables.option_bits |
3301
			SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
3302
                        OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT,
unknown's avatar
unknown committed
3303
			del_result, unit, select_lex);
3304
      res|= thd->is_error();
3305
      MYSQL_MULTI_DELETE_DONE(res, del_result->num_deleted());
3306
      if (res)
3307
        del_result->abort_result_set();
unknown's avatar
unknown committed
3308
      delete del_result;
unknown's avatar
unknown committed
3309 3310
    }
    else
3311
    {
3312
      res= TRUE;                                // Error
3313 3314
      MYSQL_MULTI_DELETE_DONE(1, 0);
    }
unknown's avatar
unknown committed
3315 3316
    break;
  }
unknown's avatar
unknown committed
3317
  case SQLCOM_DROP_TABLE:
unknown's avatar
unknown committed
3318
  {
unknown's avatar
VIEW  
unknown committed
3319
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3320 3321
    if (!lex->drop_temporary)
    {
3322
      if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE))
3323 3324
	goto error;				/* purecov: inspected */
    }
unknown's avatar
unknown committed
3325
    else
unknown's avatar
unknown committed
3326
    {
unknown's avatar
unknown committed
3327
      /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */
3328
      thd->variables.option_bits|= OPTION_KEEP_LOG;
unknown's avatar
unknown committed
3329
    }
3330
    /* DDL and binlog write order are protected by metadata locks. */
3331
    res= mysql_rm_table(thd, first_table, lex->check_exists,
unknown's avatar
VIEW  
unknown committed
3332
			lex->drop_temporary);
unknown's avatar
unknown committed
3333 3334
  }
  break;
unknown's avatar
unknown committed
3335
  case SQLCOM_SHOW_PROCESSLIST:
3336 3337
    if (!thd->security_ctx->priv_user[0] &&
        check_global_access(thd,PROCESS_ACL))
unknown's avatar
unknown committed
3338
      break;
unknown's avatar
unknown committed
3339
    mysqld_list_processes(thd,
3340 3341 3342 3343
			  (thd->security_ctx->master_access & PROCESS_ACL ?
                           NullS :
                           thd->security_ctx->priv_user),
                          lex->verbose);
unknown's avatar
unknown committed
3344
    break;
unknown's avatar
unknown committed
3345 3346 3347
  case SQLCOM_SHOW_AUTHORS:
    res= mysqld_show_authors(thd);
    break;
3348 3349 3350
  case SQLCOM_SHOW_CONTRIBUTORS:
    res= mysqld_show_contributors(thd);
    break;
unknown's avatar
unknown committed
3351 3352 3353
  case SQLCOM_SHOW_PRIVILEGES:
    res= mysqld_show_privileges(thd);
    break;
unknown's avatar
unknown committed
3354
  case SQLCOM_SHOW_ENGINE_LOGS:
unknown's avatar
unknown committed
3355
#ifdef DONT_ALLOW_SHOW_COMMANDS
unknown's avatar
unknown committed
3356 3357
    my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND),
               MYF(0));	/* purecov: inspected */
3358
    goto error;
unknown's avatar
unknown committed
3359 3360
#else
    {
Marc Alff's avatar
Marc Alff committed
3361
      if (check_access(thd, FILE_ACL, any_db, NULL, NULL, 0, 0))
unknown's avatar
unknown committed
3362
	goto error;
unknown's avatar
unknown committed
3363
      res= ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_LOGS);
unknown's avatar
unknown committed
3364 3365
      break;
    }
unknown's avatar
unknown committed
3366 3367
#endif
  case SQLCOM_CHANGE_DB:
3368 3369 3370 3371
  {
    LEX_STRING db_str= { (char *) select_lex->db, strlen(select_lex->db) };

    if (!mysql_change_db(thd, &db_str, FALSE))
3372
      my_ok(thd);
3373

unknown's avatar
unknown committed
3374
    break;
3375
  }
3376

unknown's avatar
unknown committed
3377 3378
  case SQLCOM_LOAD:
  {
unknown's avatar
VIEW  
unknown committed
3379
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
unknown's avatar
unknown committed
3380
    uint privilege= (lex->duplicates == DUP_REPLACE ?
unknown's avatar
unknown committed
3381 3382
		     INSERT_ACL | DELETE_ACL : INSERT_ACL) |
                    (lex->local_file ? 0 : FILE_ACL);
3383

unknown's avatar
unknown committed
3384
    if (lex->local_file)
unknown's avatar
unknown committed
3385
    {
3386
      if (!(thd->client_capabilities & CLIENT_LOCAL_FILES) ||
3387
          !opt_local_infile)
3388
      {
unknown's avatar
unknown committed
3389
	my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), MYF(0));
3390 3391
	goto error;
      }
unknown's avatar
unknown committed
3392
    }
unknown's avatar
unknown committed
3393 3394 3395 3396

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

unknown's avatar
VIEW  
unknown committed
3397
    res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
unknown's avatar
unknown committed
3398
                    lex->update_list, lex->value_list, lex->duplicates,
3399
                    lex->ignore, (bool) lex->local_file);
unknown's avatar
unknown committed
3400 3401
    break;
  }
3402

unknown's avatar
unknown committed
3403
  case SQLCOM_SET_OPTION:
3404 3405
  {
    List<set_var_base> *lex_var_list= &lex->var_list;
3406

3407
    if ((check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
3408
         || open_and_lock_tables(thd, all_tables, TRUE, 0)))
unknown's avatar
unknown committed
3409
      goto error;
3410 3411 3412 3413 3414 3415 3416
    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;
3417
      my_ok(thd);
3418
    }
3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430
    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
3431
    break;
3432
  }
unknown's avatar
unknown committed
3433

unknown's avatar
unknown committed
3434
  case SQLCOM_UNLOCK_TABLES:
3435 3436 3437 3438 3439 3440
    /*
      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.
    */
3441
    if (thd->variables.option_bits & OPTION_TABLE_LOCK)
unknown's avatar
unknown committed
3442
    {
3443 3444
      res= trans_commit_implicit(thd);
      thd->locked_tables_list.unlock_locked_tables(thd);
3445
      thd->mdl_context.release_transactional_locks();
3446
      thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
unknown's avatar
unknown committed
3447
    }
3448 3449
    if (thd->global_read_lock.is_acquired())
      thd->global_read_lock.unlock_global_read_lock(thd);
3450 3451
    if (res)
      goto error;
3452
    my_ok(thd);
unknown's avatar
unknown committed
3453 3454
    break;
  case SQLCOM_LOCK_TABLES:
3455 3456
    /* We must end the transaction first, regardless of anything */
    res= trans_commit_implicit(thd);
Konstantin Osipov's avatar
Konstantin Osipov committed
3457
    thd->locked_tables_list.unlock_locked_tables(thd);
3458
    /* Release transactional metadata locks. */
3459
    thd->mdl_context.release_transactional_locks();
3460 3461
    if (res)
      goto error;
3462
    if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
3463
                           FALSE, UINT_MAX, FALSE))
3464
      goto error;
Konstantin Osipov's avatar
Konstantin Osipov committed
3465

3466
    thd->variables.option_bits|= OPTION_TABLE_LOCK;
Konstantin Osipov's avatar
Konstantin Osipov committed
3467

3468
    res= lock_tables_open_and_lock_tables(thd, all_tables);
Konstantin Osipov's avatar
Konstantin Osipov committed
3469 3470

    if (res)
3471
    {
3472
      thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
3473
    }
Konstantin Osipov's avatar
Konstantin Osipov committed
3474 3475 3476 3477
    else
    {
#ifdef HAVE_QUERY_CACHE
      if (thd->variables.query_cache_wlock_invalidate)
3478
	query_cache.invalidate_locked_for_write(thd, first_table);
Konstantin Osipov's avatar
Konstantin Osipov committed
3479 3480 3481
#endif /*HAVE_QUERY_CACHE*/
      my_ok(thd);
    }
unknown's avatar
unknown committed
3482 3483
    break;
  case SQLCOM_CREATE_DB:
3484
  {
3485 3486 3487 3488 3489 3490
    /*
      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
3491
    char *alias;
3492 3493
    if (!(alias=thd->strmake(lex->name.str, lex->name.length)) ||
        check_db_name(&lex->name))
unknown's avatar
unknown committed
3494
    {
3495
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
unknown's avatar
unknown committed
3496 3497
      break;
    }
3498 3499 3500
    /*
      If in a slave thread :
      CREATE DATABASE DB was certainly not preceded by USE DB.
3501
      For that reason, db_ok() in sql/slave.cc did not check the
3502 3503 3504
      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.
    */
3505
#ifdef HAVE_REPLICATION
unknown's avatar
unknown committed
3506
    if (thd->slave_thread && 
3507 3508
	(!rpl_filter->db_ok(lex->name.str) ||
	 !rpl_filter->db_ok_with_wild_table(lex->name.str)))
unknown's avatar
unknown committed
3509
    {
unknown's avatar
unknown committed
3510
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
3511
      break;
unknown's avatar
unknown committed
3512
    }
3513
#endif
Marc Alff's avatar
Marc Alff committed
3514
    if (check_access(thd, CREATE_ACL, lex->name.str, NULL, NULL, 1, 0))
3515
      break;
3516
    res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias :
3517
                              lex->name.str), &create_info, 0);
3518 3519
    break;
  }
unknown's avatar
unknown committed
3520
  case SQLCOM_DROP_DB:
3521
  {
3522
    if (check_db_name(&lex->name))
unknown's avatar
unknown committed
3523
    {
3524
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
unknown's avatar
unknown committed
3525 3526
      break;
    }
3527 3528 3529 3530 3531 3532 3533
    /*
      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.
    */
3534
#ifdef HAVE_REPLICATION
3535
    if (thd->slave_thread && 
3536 3537
	(!rpl_filter->db_ok(lex->name.str) ||
	 !rpl_filter->db_ok_with_wild_table(lex->name.str)))
unknown's avatar
unknown committed
3538
    {
unknown's avatar
unknown committed
3539
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
3540
      break;
unknown's avatar
unknown committed
3541
    }
3542
#endif
Marc Alff's avatar
Marc Alff committed
3543
    if (check_access(thd, DROP_ACL, lex->name.str, NULL, NULL, 1, 0))
3544
      break;
3545
    res= mysql_rm_db(thd, lex->name.str, lex->check_exists, 0);
3546 3547
    break;
  }
3548
  case SQLCOM_ALTER_DB_UPGRADE:
unknown's avatar
unknown committed
3549
  {
3550
    LEX_STRING *db= & lex->name;
unknown's avatar
unknown committed
3551 3552
#ifdef HAVE_REPLICATION
    if (thd->slave_thread && 
3553 3554
       (!rpl_filter->db_ok(db->str) ||
        !rpl_filter->db_ok_with_wild_table(db->str)))
unknown's avatar
unknown committed
3555 3556 3557 3558 3559 3560
    {
      res= 1;
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
      break;
    }
#endif
3561
    if (check_db_name(db))
3562
    {
3563
      my_error(ER_WRONG_DB_NAME, MYF(0), db->str);
3564 3565
      break;
    }
Marc Alff's avatar
Marc Alff committed
3566 3567 3568
    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
3569 3570 3571 3572
    {
      res= 1;
      break;
    }
3573
    res= mysql_upgrade_db(thd, db);
unknown's avatar
unknown committed
3574
    if (!res)
3575
      my_ok(thd);
unknown's avatar
unknown committed
3576 3577
    break;
  }
3578 3579
  case SQLCOM_ALTER_DB:
  {
3580
    LEX_STRING *db= &lex->name;
3581
    HA_CREATE_INFO create_info(lex->create_info);
3582
    if (check_db_name(db))
3583
    {
3584
      my_error(ER_WRONG_DB_NAME, MYF(0), db->str);
3585 3586
      break;
    }
unknown's avatar
unknown committed
3587 3588 3589
    /*
      If in a slave thread :
      ALTER DATABASE DB may not be preceded by USE DB.
3590
      For that reason, maybe db_ok() in sql/slave.cc did not check the
unknown's avatar
unknown committed
3591 3592 3593 3594
      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
3595
    if (thd->slave_thread &&
3596 3597
	(!rpl_filter->db_ok(db->str) ||
	 !rpl_filter->db_ok_with_wild_table(db->str)))
unknown's avatar
unknown committed
3598
    {
unknown's avatar
unknown committed
3599
      my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
unknown's avatar
unknown committed
3600 3601 3602
      break;
    }
#endif
Marc Alff's avatar
Marc Alff committed
3603
    if (check_access(thd, ALTER_ACL, db->str, NULL, NULL, 1, 0))
3604
      break;
3605
    res= mysql_alter_db(thd, db->str, &create_info);
3606 3607
    break;
  }
unknown's avatar
unknown committed
3608 3609
  case SQLCOM_SHOW_CREATE_DB:
  {
unknown's avatar
unknown committed
3610 3611
    DBUG_EXECUTE_IF("4x_server_emul",
                    my_error(ER_UNKNOWN_ERROR, MYF(0)); goto error;);
3612
    if (check_db_name(&lex->name))
unknown's avatar
unknown committed
3613
    {
3614
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
unknown's avatar
unknown committed
3615 3616
      break;
    }
3617
    res= mysqld_show_create_db(thd, lex->name.str, &lex->create_info);
unknown's avatar
unknown committed
3618 3619
    break;
  }
unknown's avatar
unknown committed
3620 3621
  case SQLCOM_CREATE_EVENT:
  case SQLCOM_ALTER_EVENT:
3622
  #ifdef HAVE_EVENT_SCHEDULER
3623
  do
unknown's avatar
unknown committed
3624
  {
unknown's avatar
unknown committed
3625
    DBUG_ASSERT(lex->event_parse_data);
unknown's avatar
unknown committed
3626 3627 3628 3629 3630 3631
    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;
    }
3632 3633 3634 3635 3636

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

unknown's avatar
unknown committed
3637 3638
    switch (lex->sql_command) {
    case SQLCOM_CREATE_EVENT:
3639 3640 3641 3642
    {
      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
3643
      break;
3644
    }
unknown's avatar
unknown committed
3645
    case SQLCOM_ALTER_EVENT:
3646 3647 3648
      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
3649
      break;
3650 3651
    default:
      DBUG_ASSERT(0);
unknown's avatar
unknown committed
3652
    }
3653
    DBUG_PRINT("info",("DDL error code=%d", res));
unknown's avatar
unknown committed
3654
    if (!res)
3655
      my_ok(thd);
unknown's avatar
unknown committed
3656

3657 3658 3659 3660 3661 3662
  } 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
3663
  }
3664 3665
  /* lex->unit.cleanup() is called outside, no need to call it here */
  break;
unknown's avatar
unknown committed
3666
  case SQLCOM_SHOW_CREATE_EVENT:
3667 3668 3669 3670 3671 3672
    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,
3673
                                  lex->check_exists)))
3674
      my_ok(thd);
3675
    break;
3676 3677 3678 3679
#else
    my_error(ER_NOT_SUPPORTED_YET,MYF(0),"embedded server");
    break;
#endif
unknown's avatar
unknown committed
3680
  case SQLCOM_CREATE_FUNCTION:                  // UDF function
unknown's avatar
unknown committed
3681
  {
Marc Alff's avatar
Marc Alff committed
3682
    if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 0))
unknown's avatar
unknown committed
3683
      break;
unknown's avatar
unknown committed
3684
#ifdef HAVE_DLOPEN
3685
    if (!(res = mysql_create_function(thd, &lex->udf)))
3686
      my_ok(thd);
unknown's avatar
unknown committed
3687
#else
unknown's avatar
unknown committed
3688
    my_error(ER_CANT_OPEN_LIBRARY, MYF(0), lex->udf.dl, 0, "feature disabled");
unknown's avatar
unknown committed
3689
    res= TRUE;
unknown's avatar
unknown committed
3690 3691
#endif
    break;
unknown's avatar
unknown committed
3692
  }
unknown's avatar
unknown committed
3693
#ifndef NO_EMBEDDED_ACCESS_CHECKS
3694 3695
  case SQLCOM_CREATE_USER:
  {
Marc Alff's avatar
Marc Alff committed
3696
    if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 1) &&
3697
        check_global_access(thd,CREATE_USER_ACL))
3698
      break;
3699
    /* Conditionally writes to binlog */
3700
    if (!(res= mysql_create_user(thd, lex->users_list)))
3701
      my_ok(thd);
3702 3703
    break;
  }
3704 3705
  case SQLCOM_DROP_USER:
  {
Marc Alff's avatar
Marc Alff committed
3706
    if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 1) &&
3707
        check_global_access(thd,CREATE_USER_ACL))
3708
      break;
3709
    /* Conditionally writes to binlog */
3710
    if (!(res= mysql_drop_user(thd, lex->users_list)))
3711
      my_ok(thd);
3712 3713 3714 3715
    break;
  }
  case SQLCOM_RENAME_USER:
  {
Marc Alff's avatar
Marc Alff committed
3716
    if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1) &&
3717
        check_global_access(thd,CREATE_USER_ACL))
3718
      break;
3719
    /* Conditionally writes to binlog */
3720
    if (!(res= mysql_rename_user(thd, lex->users_list)))
3721
      my_ok(thd);
3722 3723 3724 3725
    break;
  }
  case SQLCOM_REVOKE_ALL:
  {
Marc Alff's avatar
Marc Alff committed
3726
    if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1) &&
3727
        check_global_access(thd,CREATE_USER_ACL))
3728
      break;
3729 3730 3731 3732

    /* Replicate current user as grantor */
    thd->binlog_invoker();

3733
    /* Conditionally writes to binlog */
3734
    if (!(res = mysql_revoke_all(thd, lex->users_list)))
3735
      my_ok(thd);
3736 3737
    break;
  }
3738 3739 3740
  case SQLCOM_REVOKE:
  case SQLCOM_GRANT:
  {
3741 3742
    if (lex->type != TYPE_ENUM_PROXY &&
        check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
Marc Alff's avatar
Marc Alff committed
3743 3744 3745 3746
                     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))
3747 3748
      goto error;

3749 3750 3751
    /* Replicate current user as grantor */
    thd->binlog_invoker();

3752
    if (thd->security_ctx->user)              // If not replication
unknown's avatar
unknown committed
3753
    {
3754
      LEX_USER *user, *tmp_user;
3755
      bool first_user= TRUE;
3756

unknown's avatar
unknown committed
3757
      List_iterator <LEX_USER> user_list(lex->users_list);
3758
      while ((tmp_user= user_list++))
unknown's avatar
unknown committed
3759
      {
3760 3761
        if (!(user= get_current_user(thd, tmp_user)))
          goto error;
3762 3763 3764 3765
        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,
3766
                              ER(ER_WARN_HOSTNAME_WONT_WORK));
3767 3768
        // Are we trying to change a password of another user
        DBUG_ASSERT(user->host.str != 0);
3769 3770 3771 3772 3773

        /*
          GRANT/REVOKE PROXY has the target user as a first entry in the list. 
         */
        if (lex->type == TYPE_ENUM_PROXY && first_user)
3774
        {
3775 3776 3777
          first_user= FALSE;
          if (acl_check_proxy_grant_access (thd, user->host.str, user->user.str,
                                        lex->grant & GRANT_ACL))
3778
            goto error;
3779 3780 3781 3782 3783 3784 3785
        } 
        else if (is_acl_user(user->host.str, user->user.str) &&
                 user->password.str &&
                 check_change_password (thd, user->host.str, user->user.str, 
                                        user->password.str, 
                                        user->password.length))
          goto error;
unknown's avatar
SCRUM  
unknown committed
3786 3787
      }
    }
unknown's avatar
VIEW  
unknown committed
3788
    if (first_table)
3789
    {
3790 3791
      if (lex->type == TYPE_ENUM_PROCEDURE ||
          lex->type == TYPE_ENUM_FUNCTION)
3792 3793 3794 3795
      {
        uint grants= lex->all_privileges 
		   ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL)
		   : lex->grant;
3796
        if (check_grant_routine(thd, grants | GRANT_ACL, all_tables,
3797
                                lex->type == TYPE_ENUM_PROCEDURE, 0))
3798
	  goto error;
3799
        /* Conditionally writes to binlog */
3800 3801 3802
        res= mysql_routine_grant(thd, all_tables,
                                 lex->type == TYPE_ENUM_PROCEDURE, 
                                 lex->users_list, grants,
3803 3804 3805
                                 lex->sql_command == SQLCOM_REVOKE, TRUE);
        if (!res)
          my_ok(thd);
3806 3807 3808
      }
      else
      {
3809
	if (check_grant(thd,(lex->grant | lex->grant_tot_col | GRANT_ACL),
3810
                        all_tables, FALSE, UINT_MAX, FALSE))
3811
	  goto error;
3812
        /* Conditionally writes to binlog */
3813 3814 3815 3816
        res= mysql_table_grant(thd, all_tables, lex->users_list,
			       lex->columns, lex->grant,
			       lex->sql_command == SQLCOM_REVOKE);
      }
3817 3818 3819
    }
    else
    {
3820
      if (lex->columns.elements || (lex->type && lex->type != TYPE_ENUM_PROXY))
3821
      {
unknown's avatar
unknown committed
3822 3823
	my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
                   MYF(0));
unknown's avatar
unknown committed
3824
        goto error;
3825 3826
      }
      else
3827 3828 3829 3830 3831 3832
      {
        /* Conditionally writes to binlog */
        res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
                          lex->sql_command == SQLCOM_REVOKE,
                          lex->type == TYPE_ENUM_PROXY);
      }
3833 3834
      if (!res)
      {
3835
	if (lex->sql_command == SQLCOM_GRANT)
3836
	{
unknown's avatar
unknown committed
3837
	  List_iterator <LEX_USER> str_list(lex->users_list);
3838 3839 3840 3841 3842
	  LEX_USER *user, *tmp_user;
	  while ((tmp_user=str_list++))
          {
            if (!(user= get_current_user(thd, tmp_user)))
              goto error;
3843
	    reset_mqh(user, 0);
3844
          }
3845
	}
3846 3847 3848 3849
      }
    }
    break;
  }
unknown's avatar
SCRUM  
unknown committed
3850
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
unknown's avatar
unknown committed
3851
  case SQLCOM_RESET:
3852 3853 3854
    /*
      RESET commands are never written to the binary log, so we have to
      initialize this variable because RESET shares the same code as FLUSH
3855 3856 3857 3858
    */
    lex->no_write_to_binlog= 1;
  case SQLCOM_FLUSH:
  {
3859
    int write_to_binlog;
unknown's avatar
unknown committed
3860
    if (check_global_access(thd,RELOAD_ACL))
unknown's avatar
unknown committed
3861
      goto error;
3862

3863 3864
    if (first_table && lex->type & REFRESH_READ_LOCK)
    {
3865 3866 3867 3868
      /* Check table-level privileges. */
      if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
                             FALSE, UINT_MAX, FALSE))
        goto error;
3869 3870 3871 3872 3873 3874
      if (flush_tables_with_read_lock(thd, all_tables))
        goto error;
      my_ok(thd);
      break;
    }

3875 3876 3877 3878
    /*
      reload_acl_and_cache() will tell us if we are allowed to write to the
      binlog or not.
    */
unknown's avatar
unknown committed
3879
    if (!reload_acl_and_cache(thd, lex->type, first_table, &write_to_binlog))
3880 3881 3882 3883 3884
    {
      /*
        We WANT to write and we CAN write.
        ! we write after unlocking the table.
      */
3885 3886 3887
      /*
        Presumably, RESET and binlog writing doesn't require synchronization
      */
3888 3889 3890 3891 3892 3893

      if (write_to_binlog > 0)  // we should write
      { 
        if (!lex->no_write_to_binlog)
          res= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
      } else if (write_to_binlog < 0) 
3894
      {
3895 3896 3897 3898 3899 3900 3901 3902 3903
        /* 
           We should not write, but rather report error because 
           reload_acl_and_cache binlog interactions failed 
         */
        res= 1;
      } 

      if (!res)
        my_ok(thd);
3904 3905
    } 
    
unknown's avatar
unknown committed
3906
    break;
3907
  }
unknown's avatar
unknown committed
3908
  case SQLCOM_KILL:
3909
  {
unknown's avatar
unknown committed
3910 3911 3912 3913 3914 3915 3916
    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;
    }

3917
    if (lex->kill_type == KILL_TYPE_ID)
3918
    {
3919 3920 3921 3922 3923 3924 3925 3926
      Item *it= (Item *)lex->value_list.head();
      if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
      {
        my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
                   MYF(0));
        goto error;
      }
      sql_kill(thd, (ulong) it->val_int(), lex->kill_signal);
3927
    }
3928 3929 3930
    else
      sql_kill_user(thd, get_current_user(thd, lex->users_list.head()),
                    lex->kill_signal);
unknown's avatar
unknown committed
3931
    break;
3932
  }
3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943
  case SQLCOM_SHUTDOWN:
#ifndef EMBEDDED_LIBRARY
    if (check_global_access(thd,SHUTDOWN_ACL))
      goto error;
    kill_mysql();
    my_ok(thd);
#else
    my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
#endif
    break;

unknown's avatar
unknown committed
3944
#ifndef NO_EMBEDDED_ACCESS_CHECKS
unknown's avatar
unknown committed
3945
  case SQLCOM_SHOW_GRANTS:
3946 3947 3948 3949
  {
    LEX_USER *grant_user= get_current_user(thd, lex->grant_user);
    if (!grant_user)
      goto error;
3950
    if ((thd->security_ctx->priv_user &&
3951
	 !strcmp(thd->security_ctx->priv_user, grant_user->user.str)) ||
Marc Alff's avatar
Marc Alff committed
3952
        !check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 0))
unknown's avatar
unknown committed
3953
    {
3954
      res = mysql_show_grants(thd, grant_user);
unknown's avatar
unknown committed
3955 3956
    }
    break;
3957
  }
unknown's avatar
unknown committed
3958
#endif
3959
  case SQLCOM_HA_OPEN:
unknown's avatar
VIEW  
unknown committed
3960
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3961
    if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE))
3962
      goto error;
unknown's avatar
unknown committed
3963
    res= mysql_ha_open(thd, first_table, 0);
3964 3965
    break;
  case SQLCOM_HA_CLOSE:
unknown's avatar
VIEW  
unknown committed
3966 3967
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
    res= mysql_ha_close(thd, first_table);
3968 3969
    break;
  case SQLCOM_HA_READ:
unknown's avatar
VIEW  
unknown committed
3970
    DBUG_ASSERT(first_table == all_tables && first_table != 0);
3971 3972 3973 3974 3975
    /*
      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.
    */
3976
    unit->set_limit(select_lex);
3977
    res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->ident.str,
unknown's avatar
VIEW  
unknown committed
3978
                       lex->insert_list, lex->ha_rkey_mode, select_lex->where,
3979
                       unit->select_limit_cnt, unit->offset_limit_cnt);
3980 3981
    break;

unknown's avatar
unknown committed
3982
  case SQLCOM_BEGIN:
Konstantin Osipov's avatar
Konstantin Osipov committed
3983
    if (trans_begin(thd, lex->start_transaction_opt))
unknown's avatar
unknown committed
3984
      goto error;
3985
    my_ok(thd);
unknown's avatar
unknown committed
3986 3987
    break;
  case SQLCOM_COMMIT:
3988
  {
3989 3990
    DBUG_ASSERT(thd->lock == NULL ||
                thd->locked_tables_mode == LTM_LOCK_TABLES);
3991 3992 3993 3994 3995 3996
    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
3997 3998
    if (trans_commit(thd))
      goto error;
3999
    thd->mdl_context.release_transactional_locks();
Konstantin Osipov's avatar
Konstantin Osipov committed
4000
    /* Begin transaction with the same isolation level. */
4001 4002 4003
    if (tx_chain)
    {
      if (trans_begin(thd))
unknown's avatar
unknown committed
4004
      goto error;
4005 4006 4007 4008 4009 4010
    }
    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
4011
    /* Disconnect the current client connection. */
4012
    if (tx_release)
4013
    {
Sergei Golubchik's avatar
Sergei Golubchik committed
4014
      thd->killed= KILL_CONNECTION;
Sergei Golubchik's avatar
Sergei Golubchik committed
4015
      thd->print_aborted_warning(3, "RELEASE");
unknown's avatar
unknown committed
4016
    }
4017
    my_ok(thd);
unknown's avatar
unknown committed
4018
    break;
4019
  }
unknown's avatar
unknown committed
4020
  case SQLCOM_ROLLBACK:
4021
  {
4022 4023
    DBUG_ASSERT(thd->lock == NULL ||
                thd->locked_tables_mode == LTM_LOCK_TABLES);
4024 4025 4026 4027 4028 4029
    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
4030
    if (trans_rollback(thd))
unknown's avatar
unknown committed
4031
      goto error;
4032
    thd->mdl_context.release_transactional_locks();
Konstantin Osipov's avatar
Konstantin Osipov committed
4033
    /* Begin transaction with the same isolation level. */
4034 4035 4036 4037 4038 4039 4040 4041 4042 4043
    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
4044
    /* Disconnect the current client connection. */
4045
    if (tx_release)
Sergei Golubchik's avatar
Sergei Golubchik committed
4046
      thd->killed= KILL_CONNECTION;
4047
    my_ok(thd);
unknown's avatar
unknown committed
4048
    break;
4049
  }
unknown's avatar
unknown committed
4050
  case SQLCOM_RELEASE_SAVEPOINT:
Konstantin Osipov's avatar
Konstantin Osipov committed
4051 4052 4053
    if (trans_release_savepoint(thd, lex->ident))
      goto error;
    my_ok(thd);
unknown's avatar
unknown committed
4054
    break;
unknown's avatar
unknown committed
4055
  case SQLCOM_ROLLBACK_TO_SAVEPOINT:
Konstantin Osipov's avatar
Konstantin Osipov committed
4056 4057 4058
    if (trans_rollback_to_savepoint(thd, lex->ident))
      goto error;
    my_ok(thd);
unknown's avatar
unknown committed
4059
    break;
4060
  case SQLCOM_SAVEPOINT:
Konstantin Osipov's avatar
Konstantin Osipov committed
4061 4062 4063
    if (trans_savepoint(thd, lex->ident))
      goto error;
    my_ok(thd);
unknown's avatar
unknown committed
4064
    break;
4065 4066
  case SQLCOM_CREATE_PROCEDURE:
  case SQLCOM_CREATE_SPFUNCTION:
unknown's avatar
unknown committed
4067
  {
4068
    uint namelen;
unknown's avatar
unknown committed
4069
    char *name;
unknown's avatar
unknown committed
4070
    int sp_result= SP_INTERNAL_ERROR;
4071

4072
    DBUG_ASSERT(lex->sphead != 0);
unknown's avatar
unknown committed
4073
    DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */
4074 4075 4076 4077
    /*
      Verify that the database name is allowed, optionally
      lowercase it.
    */
4078
    if (check_db_name(&lex->sphead->m_db))
4079
    {
4080
      my_error(ER_WRONG_DB_NAME, MYF(0), lex->sphead->m_db.str);
4081
      goto create_sp_error;
4082 4083
    }

4084
    /*
4085 4086 4087
      Check that a database directory with this name
      exists. Design note: This won't work on virtual databases
      like information_schema.
4088 4089
    */
    if (check_db_dir_existence(lex->sphead->m_db.str))
4090
    {
4091
      my_error(ER_BAD_DB_ERROR, MYF(0), lex->sphead->m_db.str);
4092
      goto create_sp_error;
4093
    }
4094

Marc Alff's avatar
Marc Alff committed
4095 4096
    if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str,
                     NULL, NULL, 0, 0))
4097
      goto create_sp_error;
4098 4099

    name= lex->sphead->name(&namelen);
4100
#ifdef HAVE_DLOPEN
unknown's avatar
unknown committed
4101 4102 4103
    if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
    {
      udf_func *udf = find_udf(name, namelen);
4104

unknown's avatar
unknown committed
4105
      if (udf)
4106
      {
4107 4108
        my_error(ER_UDF_EXISTS, MYF(0), name);
        goto create_sp_error;
4109
      }
unknown's avatar
unknown committed
4110 4111 4112
    }
#endif

4113 4114
    if (sp_process_definer(thd))
      goto create_sp_error;
4115

Konstantin Osipov's avatar
Konstantin Osipov committed
4116
    res= (sp_result= sp_create_routine(thd, lex->sphead->m_type, lex->sphead));
unknown's avatar
unknown committed
4117
    switch (sp_result) {
4118
    case SP_OK: {
unknown's avatar
unknown committed
4119
#ifndef NO_EMBEDDED_ACCESS_CHECKS
4120
      /* only add privileges if really neccessary */
4121 4122 4123 4124 4125

      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
4126
      /*
4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139
        We're going to issue an implicit GRANT statement so we close all
        open tables. We have to keep metadata locks as this ensures that
        this statement is atomic against concurent FLUSH TABLES WITH READ
        LOCK. Deadlocks which can arise due to fact that this implicit
        statement takes metadata locks should be detected by a deadlock
        detector in MDL subsystem and reported as errors.

        No need to commit/rollback statement transaction, it's not started.

        TODO: Long-term we should either ensure that implicit GRANT statement
              is written into binary log as a separate statement or make both
              creation of routine and implicit GRANT parts of one fully atomic
              statement.
Konstantin Osipov's avatar
Konstantin Osipov committed
4140
      */
4141 4142
      DBUG_ASSERT(thd->transaction.stmt.is_empty());
      close_thread_tables(thd);
4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160
      /*
        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;
      }

4161
      if (sp_automatic_privileges && !opt_noacl &&
4162
          check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS,
4163
                               lex->sphead->m_db.str, name,
4164
                               lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1))
4165
      {
unknown's avatar
unknown committed
4166
        if (sp_grant_privileges(thd, lex->sphead->m_db.str, name,
4167
                                lex->sql_command == SQLCOM_CREATE_PROCEDURE))
4168
          push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
4169 4170
                       ER_PROC_AUTO_GRANT_FAIL, ER(ER_PROC_AUTO_GRANT_FAIL));
        thd->clear_error();
4171
      }
4172 4173 4174 4175 4176 4177 4178 4179 4180 4181

      /*
        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
4182
#endif
unknown's avatar
unknown committed
4183
    break;
4184
    }
4185 4186 4187 4188 4189 4190 4191 4192 4193
    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;
4194 4195 4196
    case SP_FLD_STORE_FAILED:
      my_error(ER_CANT_CREATE_SROUTINE, MYF(0), name);
      break;
4197 4198 4199 4200 4201 4202 4203 4204 4205 4206
    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
4207
    if (sp_result != SP_OK )
4208
      goto error;
4209
    my_ok(thd);
4210 4211
    break; /* break super switch */
  } /* end case group bracket */
4212 4213 4214
  case SQLCOM_CALL:
    {
      sp_head *sp;
4215 4216 4217 4218
      /*
        This will cache all SP and SF and open and lock all tables
        required for execution.
      */
4219 4220
      if (check_table_access(thd, SELECT_ACL, all_tables, FALSE,
                             UINT_MAX, FALSE) ||
4221
          open_and_lock_tables(thd, all_tables, TRUE, 0))
4222 4223 4224
       goto error;

      /*
4225 4226
        By this moment all needed SPs should be in cache so no need to look 
        into DB. 
4227
      */
4228 4229
      if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
                                &thd->sp_proc_cache, TRUE)))
4230
      {
4231
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
unknown's avatar
unknown committed
4232
                 lex->spname->m_qname.str);
4233
	goto error;
4234 4235 4236
      }
      else
      {
unknown's avatar
unknown committed
4237
	ha_rows select_limit;
unknown's avatar
unknown committed
4238 4239
        /* bits that should be cleared in thd->server_status */
	uint bits_to_be_cleared= 0;
4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251
        /*
          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;
        }
4252

4253
	if (sp->m_flags & sp_head::MULTI_RESULTS)
4254
	{
4255
	  if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS))
4256
	  {
4257 4258 4259 4260
            /*
              The client does not support multiple result sets being sent
              back
            */
4261
	    my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str);
4262 4263
	    goto error;
	  }
unknown's avatar
unknown committed
4264 4265 4266 4267 4268 4269 4270
          /*
            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;
4271 4272
	}

4273
	if (check_routine_access(thd, EXECUTE_ACL,
4274
				 sp->m_db.str, sp->m_name.str, TRUE, FALSE))
4275 4276 4277
	{
	  goto error;
	}
unknown's avatar
unknown committed
4278 4279
	select_limit= thd->variables.select_limit;
	thd->variables.select_limit= HA_POS_ERROR;
4280

4281
        /* 
4282
          We never write CALL statements into binlog:
4283 4284 4285 4286 4287
           - 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.
4288
        */
4289
	res= sp->execute_procedure(thd, &lex->value_list);
4290

unknown's avatar
unknown committed
4291
	thd->variables.select_limit= select_limit;
4292

unknown's avatar
unknown committed
4293
        thd->server_status&= ~bits_to_be_cleared;
4294

unknown's avatar
unknown committed
4295
	if (!res)
4296 4297 4298
        {
          my_ok(thd, (thd->get_row_count_func() < 0) ? 0 : thd->get_row_count_func());
        }
4299
	else
4300
        {
4301
          DBUG_ASSERT(thd->is_error() || thd->killed);
4302
	  goto error;		// Substatement should already have sent error
4303
        }
4304
      }
4305
      break;
4306 4307
    }
  case SQLCOM_ALTER_PROCEDURE:
4308
  case SQLCOM_ALTER_FUNCTION:
4309
    {
unknown's avatar
unknown committed
4310
      int sp_result;
Sergei Golubchik's avatar
Sergei Golubchik committed
4311 4312
      enum stored_procedure_type type;
      type= (lex->sql_command == SQLCOM_ALTER_PROCEDURE ?
Konstantin Osipov's avatar
Konstantin Osipov committed
4313
                 TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
unknown's avatar
unknown committed
4314

Konstantin Osipov's avatar
Konstantin Osipov committed
4315 4316 4317 4318
      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
4319

Konstantin Osipov's avatar
Konstantin Osipov committed
4320 4321 4322 4323 4324 4325 4326 4327
      /*
        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
4328
      switch (sp_result)
4329
      {
unknown's avatar
unknown committed
4330
      case SP_OK:
4331
	my_ok(thd);
unknown's avatar
unknown committed
4332 4333
	break;
      case SP_KEY_NOT_FOUND:
4334 4335
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
unknown's avatar
unknown committed
4336 4337
	goto error;
      default:
4338 4339
	my_error(ER_SP_CANT_ALTER, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
unknown's avatar
unknown committed
4340
	goto error;
4341
      }
4342
      break;
4343 4344
    }
  case SQLCOM_DROP_PROCEDURE:
4345
  case SQLCOM_DROP_FUNCTION:
4346
    {
4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370
#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)
        {
4371
          if (lex->check_exists)
4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387
          {
            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
4388
      int sp_result;
Sergei Golubchik's avatar
Sergei Golubchik committed
4389 4390
      enum stored_procedure_type type;
      type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ?
4391
                 TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
4392 4393
      char *db= lex->spname->m_db.str;
      char *name= lex->spname->m_name.str;
4394

4395 4396 4397
      if (check_routine_access(thd, ALTER_PROC_ACL, db, name,
                               lex->sql_command == SQLCOM_DROP_PROCEDURE, 0))
        goto error;
4398

4399 4400
      /* Conditionally writes to binlog */
      sp_result= sp_drop_routine(thd, type, lex->spname);
4401

Konstantin Osipov's avatar
Konstantin Osipov committed
4402
#ifndef NO_EMBEDDED_ACCESS_CHECKS
4403
      /*
4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416
        We're going to issue an implicit REVOKE statement so we close all
        open tables. We have to keep metadata locks as this ensures that
        this statement is atomic against concurent FLUSH TABLES WITH READ
        LOCK. Deadlocks which can arise due to fact that this implicit
        statement takes metadata locks should be detected by a deadlock
        detector in MDL subsystem and reported as errors.

        No need to commit/rollback statement transaction, it's not started.

        TODO: Long-term we should either ensure that implicit REVOKE statement
              is written into binary log as a separate statement or make both
              dropping of routine and implicit REVOKE parts of one fully atomic
              statement.
4417
      */
4418 4419
      DBUG_ASSERT(thd->transaction.stmt.is_empty());
      close_thread_tables(thd);
4420

4421 4422 4423 4424 4425 4426 4427 4428 4429 4430
      if (sp_result != SP_KEY_NOT_FOUND &&
          sp_automatic_privileges && !opt_noacl &&
          sp_revoke_privileges(thd, db, name,
                               lex->sql_command == SQLCOM_DROP_PROCEDURE))
      {
        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;
4431
      }
4432 4433
#endif

unknown's avatar
unknown committed
4434 4435
      res= sp_result;
      switch (sp_result) {
4436
      case SP_OK:
4437
	my_ok(thd);
4438 4439
	break;
      case SP_KEY_NOT_FOUND:
4440
	if (lex->check_exists)
4441
	{
4442
          res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
4443
	  push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
4444
			      ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
4445
                              SP_COM_STRING(lex), lex->spname->m_qname.str);
4446 4447
          if (!res)
            my_ok(thd);
4448 4449
	  break;
	}
4450 4451
	my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
4452 4453
	goto error;
      default:
4454 4455
	my_error(ER_SP_DROP_FAILED, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_qname.str);
4456
	goto error;
4457
      }
4458
      break;
4459
    }
unknown's avatar
unknown committed
4460 4461
  case SQLCOM_SHOW_CREATE_PROC:
    {
unknown's avatar
unknown committed
4462
      if (sp_show_create_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname))
Konstantin Osipov's avatar
Konstantin Osipov committed
4463
        goto error;
unknown's avatar
unknown committed
4464 4465 4466 4467
      break;
    }
  case SQLCOM_SHOW_CREATE_FUNC:
    {
unknown's avatar
unknown committed
4468
      if (sp_show_create_routine(thd, TYPE_ENUM_FUNCTION, lex->spname))
unknown's avatar
unknown committed
4469 4470 4471
	goto error;
      break;
    }
unknown's avatar
unknown committed
4472 4473 4474
  case SQLCOM_SHOW_PROC_CODE:
  case SQLCOM_SHOW_FUNC_CODE:
    {
4475
#ifndef DBUG_OFF
unknown's avatar
unknown committed
4476
      sp_head *sp;
Sergei Golubchik's avatar
Sergei Golubchik committed
4477
      stored_procedure_type type= (lex->sql_command == SQLCOM_SHOW_PROC_CODE ?
Konstantin Osipov's avatar
Konstantin Osipov committed
4478
                 TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
unknown's avatar
unknown committed
4479

Konstantin Osipov's avatar
Konstantin Osipov committed
4480 4481
      if (sp_cache_routine(thd, type, lex->spname, FALSE, &sp))
        goto error;
4482
      if (!sp || sp->show_routine_code(thd))
4483 4484
      {
        /* We don't distinguish between errors for now */
unknown's avatar
unknown committed
4485 4486 4487 4488 4489
        my_error(ER_SP_DOES_NOT_EXIST, MYF(0),
                 SP_COM_STRING(lex), lex->spname->m_name.str);
        goto error;
      }
      break;
4490 4491 4492 4493
#else
      my_error(ER_FEATURE_DISABLED, MYF(0),
               "SHOW PROCEDURE|FUNCTION CODE", "--with-debug");
      goto error;
unknown's avatar
unknown committed
4494
#endif // ifndef DBUG_OFF
4495
    }
unknown's avatar
unknown committed
4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508
  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
4509 4510
  case SQLCOM_CREATE_VIEW:
    {
4511 4512 4513 4514
      /*
        Note: SQLCOM_CREATE_VIEW also handles 'ALTER VIEW' commands
        as specified through the thd->lex->create_view_mode flag.
      */
4515
      res= mysql_create_view(thd, first_table, thd->lex->create_view_mode);
unknown's avatar
VIEW  
unknown committed
4516 4517 4518 4519
      break;
    }
  case SQLCOM_DROP_VIEW:
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
4520
      if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE))
unknown's avatar
unknown committed
4521
        goto error;
4522 4523
      /* Conditionally writes to binlog. */
      res= mysql_drop_view(thd, first_table, thd->lex->drop_mode);
unknown's avatar
VIEW  
unknown committed
4524 4525
      break;
    }
4526 4527
  case SQLCOM_CREATE_TRIGGER:
  {
4528
    /* Conditionally writes to binlog. */
4529 4530
    res= mysql_create_or_drop_trigger(thd, all_tables, 1);

4531 4532 4533 4534
    break;
  }
  case SQLCOM_DROP_TRIGGER:
  {
4535
    /* Conditionally writes to binlog. */
4536 4537 4538
    res= mysql_create_or_drop_trigger(thd, all_tables, 0);
    break;
  }
4539
  case SQLCOM_XA_START:
Konstantin Osipov's avatar
Konstantin Osipov committed
4540 4541
    if (trans_xa_start(thd))
      goto error;
4542
    my_ok(thd);
4543 4544
    break;
  case SQLCOM_XA_END:
Konstantin Osipov's avatar
Konstantin Osipov committed
4545 4546
    if (trans_xa_end(thd))
      goto error;
4547
    my_ok(thd);
4548 4549
    break;
  case SQLCOM_XA_PREPARE:
Konstantin Osipov's avatar
Konstantin Osipov committed
4550 4551
    if (trans_xa_prepare(thd))
      goto error;
4552
    my_ok(thd);
4553 4554
    break;
  case SQLCOM_XA_COMMIT:
Konstantin Osipov's avatar
Konstantin Osipov committed
4555 4556
    if (trans_xa_commit(thd))
      goto error;
4557
    thd->mdl_context.release_transactional_locks();
4558 4559 4560 4561 4562
    /*
      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
4563
    my_ok(thd);
4564 4565
    break;
  case SQLCOM_XA_ROLLBACK:
Konstantin Osipov's avatar
Konstantin Osipov committed
4566 4567
    if (trans_xa_rollback(thd))
      goto error;
4568
    thd->mdl_context.release_transactional_locks();
4569 4570 4571 4572 4573
    /*
      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
4574
    my_ok(thd);
4575 4576
    break;
  case SQLCOM_XA_RECOVER:
4577
    res= mysql_xa_recover(thd);
4578
    break;
unknown's avatar
unknown committed
4579
  case SQLCOM_ALTER_TABLESPACE:
4580
    if (check_global_access(thd, CREATE_TABLESPACE_ACL))
unknown's avatar
unknown committed
4581 4582
      break;
    if (!(res= mysql_alter_tablespace(thd, lex->alter_tablespace_info)))
4583
      my_ok(thd);
unknown's avatar
unknown committed
4584 4585 4586 4587
    break;
  case SQLCOM_INSTALL_PLUGIN:
    if (! (res= mysql_install_plugin(thd, &thd->lex->comment,
                                     &thd->lex->ident)))
4588
      my_ok(thd);
unknown's avatar
unknown committed
4589 4590
    break;
  case SQLCOM_UNINSTALL_PLUGIN:
4591 4592
    if (! (res= mysql_uninstall_plugin(thd, &thd->lex->comment,
                                       &thd->lex->ident)))
4593
      my_ok(thd);
unknown's avatar
unknown committed
4594 4595 4596 4597 4598 4599 4600 4601 4602 4603
    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
4604 4605 4606 4607 4608
  case SQLCOM_CREATE_SERVER:
  {
    int error;
    LEX *lex= thd->lex;
    DBUG_PRINT("info", ("case SQLCOM_CREATE_SERVER"));
unknown's avatar
unknown committed
4609 4610 4611 4612

    if (check_global_access(thd, SUPER_ACL))
      break;

unknown's avatar
unknown committed
4613 4614
    if ((error= create_server(thd, &lex->server_options)))
    {
4615
      DBUG_PRINT("info", ("problem creating server <%s>",
unknown's avatar
unknown committed
4616 4617 4618 4619
                          lex->server_options.server_name));
      my_error(error, MYF(0), lex->server_options.server_name);
      break;
    }
4620
    my_ok(thd, 1);
unknown's avatar
unknown committed
4621 4622 4623 4624 4625 4626 4627
    break;
  }
  case SQLCOM_ALTER_SERVER:
  {
    int error;
    LEX *lex= thd->lex;
    DBUG_PRINT("info", ("case SQLCOM_ALTER_SERVER"));
unknown's avatar
unknown committed
4628 4629 4630 4631

    if (check_global_access(thd, SUPER_ACL))
      break;

unknown's avatar
unknown committed
4632 4633
    if ((error= alter_server(thd, &lex->server_options)))
    {
4634
      DBUG_PRINT("info", ("problem altering server <%s>",
unknown's avatar
unknown committed
4635 4636 4637 4638
                          lex->server_options.server_name));
      my_error(error, MYF(0), lex->server_options.server_name);
      break;
    }
4639
    my_ok(thd, 1);
unknown's avatar
unknown committed
4640 4641 4642 4643 4644 4645 4646
    break;
  }
  case SQLCOM_DROP_SERVER:
  {
    int err_code;
    LEX *lex= thd->lex;
    DBUG_PRINT("info", ("case SQLCOM_DROP_SERVER"));
unknown's avatar
unknown committed
4647 4648 4649 4650

    if (check_global_access(thd, SUPER_ACL))
      break;

unknown's avatar
unknown committed
4651 4652
    if ((err_code= drop_server(thd, &lex->server_options)))
    {
4653
      if (! lex->check_exists && err_code == ER_FOREIGN_SERVER_DOESNT_EXIST)
unknown's avatar
unknown committed
4654 4655 4656 4657 4658 4659 4660
      {
        DBUG_PRINT("info", ("problem dropping server %s",
                            lex->server_options.server_name));
        my_error(err_code, MYF(0), lex->server_options.server_name);
      }
      else
      {
4661
        my_ok(thd, 0);
unknown's avatar
unknown committed
4662 4663 4664
      }
      break;
    }
4665
    my_ok(thd, 1);
unknown's avatar
unknown committed
4666 4667
    break;
  }
4668 4669 4670 4671 4672 4673
  case SQLCOM_ANALYZE:
  case SQLCOM_CHECK:
  case SQLCOM_OPTIMIZE:
  case SQLCOM_REPAIR:
  case SQLCOM_TRUNCATE:
  case SQLCOM_ALTER_TABLE:
4674
      thd->query_plan_flags|= QPLAN_ADMIN;
4675 4676
      DBUG_ASSERT(first_table == all_tables && first_table != 0);
    /* fall through */
Marc Alff's avatar
Marc Alff committed
4677 4678 4679 4680 4681
  case SQLCOM_SIGNAL:
  case SQLCOM_RESIGNAL:
    DBUG_ASSERT(lex->m_stmt != NULL);
    res= lex->m_stmt->execute(thd);
    break;
4682
  default:
4683
#ifndef EMBEDDED_LIBRARY
4684
    DBUG_ASSERT(0);                             /* Impossible */
4685
#endif
4686
    my_ok(thd);
unknown's avatar
unknown committed
4687 4688
    break;
  }
4689
  thd_proc_info(thd, "query end");
4690
  thd->update_stats();
4691 4692

  /*
unknown's avatar
unknown committed
4693
    Binlog-related cleanup:
4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704
    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);

4705
  goto finish;
unknown's avatar
unknown committed
4706 4707

error:
4708 4709
  res= TRUE;

4710
finish:
Konstantin Osipov's avatar
Konstantin Osipov committed
4711

4712 4713 4714
  DBUG_ASSERT(!thd->in_active_multi_stmt_transaction() ||
               thd->in_multi_stmt_transaction_mode());

4715 4716 4717

  if (! thd->in_sub_stmt)
  {
4718
    if (thd->killed != NOT_KILLED)
4719
    {
4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731
      /* report error issued during command execution */
      if (thd->killed_errno())
      {
        /* If we already sent 'ok', we can ignore any kill query statements */
        if (! thd->stmt_da->is_set())
          thd->send_kill_message();
      }
      if (thd->killed < KILL_CONNECTION)
      {
        thd->reset_killed();
        thd->mysys_var->abort= 0;
      }
4732 4733 4734 4735 4736 4737 4738 4739 4740 4741
    }
    if (thd->is_error() || (thd->variables.option_bits & OPTION_MASTER_SQL_ERROR))
      trans_rollback_stmt(thd);
    else
    {
      /* If commit fails, we should be able to reset the OK status. */
      thd->stmt_da->can_overwrite_status= TRUE;
      trans_commit_stmt(thd);
      thd->stmt_da->can_overwrite_status= FALSE;
    }
4742 4743 4744
#ifdef WITH_ARIA_STORAGE_ENGINE
    ha_maria::implicit_commit(thd, FALSE);
#endif
4745 4746 4747 4748 4749 4750 4751 4752
  }

  lex->unit.cleanup();
  /* Free tables */
  thd_proc_info(thd, "closing tables");
  close_thread_tables(thd);
  thd_proc_info(thd, 0);

4753 4754 4755 4756
#ifndef DBUG_OFF
  if (lex->sql_command != SQLCOM_SET_OPTION && ! thd->in_sub_stmt)
    DEBUG_SYNC(thd, "execute_command_after_close_tables");
#endif
4757 4758 4759 4760 4761
  if (!(sql_command_flags[lex->sql_command] &
        (CF_CAN_GENERATE_ROW_EVENTS | CF_FORCE_ORIGINAL_BINLOG_FORMAT |
         CF_STATUS_COMMAND)))
    thd->set_binlog_format(orig_binlog_format,
                           orig_current_stmt_binlog_format);
4762

4763 4764
  if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
  {
4765 4766
    /* No transaction control allowed in sub-statements. */
    DBUG_ASSERT(! thd->in_sub_stmt);
4767 4768 4769 4770
    /* If commit fails, we should be able to reset the OK status. */
    thd->stmt_da->can_overwrite_status= TRUE;
    /* Commit the normal transaction if one is active. */
    trans_commit_implicit(thd);
4771
    thd->stmt_da->can_overwrite_status= FALSE;
4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785
    thd->mdl_context.release_transactional_locks();
  }
  else if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
  {
    /*
      - If inside a multi-statement transaction,
      defer the release of metadata locks until the current
      transaction is either committed or rolled back. This prevents
      other statements from modifying the table for the entire
      duration of this transaction.  This provides commit ordering
      and guarantees serializability across multiple transactions.
      - If in autocommit mode, or outside a transactional context,
      automatically release metadata locks of the current statement.
    */
4786
    thd->mdl_context.release_transactional_locks();
4787
  }
4788 4789 4790 4791
  else if (! thd->in_sub_stmt)
  {
    thd->mdl_context.release_statement_locks();
  }
Konstantin Osipov's avatar
Konstantin Osipov committed
4792

4793
  DBUG_RETURN(res || thd->is_error());
unknown's avatar
unknown committed
4794 4795 4796
}


4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808
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);
  }
4809
  if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
4810 4811 4812 4813 4814 4815 4816 4817 4818 4819
  {
    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()))
4820
        return 1;                               /* purecov: inspected */
4821 4822
      thd->send_explain_fields(result);
      res= mysql_explain_union(thd, &thd->lex->unit, result);
4823 4824 4825 4826 4827
      /*
        The code which prints the extended description is not robust
        against malformed queries, so skip it if we have an error.
      */
      if (!res && (lex->describe & DESCRIBE_EXTENDED))
4828 4829 4830 4831
      {
        char buff[1024];
        String str(buff,(uint32) sizeof(buff), system_charset_info);
        str.length(0);
4832 4833 4834 4835 4836
        /*
          The warnings system requires input in utf8, @see
          mysqld_show_warnings().
        */
        thd->lex->unit.print(&str, QT_TO_SYSTEM_CHARSET);
4837
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
4838
                     ER_YES, str.c_ptr_safe());
4839
      }
4840
      if (res)
4841
        result->abort_result_set();
4842 4843
      else
        result->send_eof();
4844 4845 4846 4847 4848
      delete result;
    }
    else
    {
      if (!result && !(result= new select_send()))
4849
        return 1;                               /* purecov: inspected */
4850 4851 4852 4853 4854 4855
      query_cache_store_query(thd, all_tables);
      res= handle_select(thd, lex, result, 0);
      if (result != lex->result)
        delete result;
    }
  }
4856 4857 4858 4859
  /* Count number of empty select queries */
  if (!thd->sent_row_count)
    status_var_increment(thd->status_var.empty_queries);
  status_var_add(thd->status_var.rows_sent, thd->sent_row_count);
4860 4861 4862 4863
  return res;
}


4864 4865 4866 4867 4868
static bool execute_show_status(THD *thd, TABLE_LIST *all_tables)
{
  bool res;
  system_status_var old_status_var= thd->status_var;
  thd->initial_status_var= &old_status_var;
Sergei Golubchik's avatar
Sergei Golubchik committed
4869 4870
  if (!(res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
                                UINT_MAX, FALSE)))
4871 4872 4873 4874 4875 4876 4877 4878
    res= execute_sqlcom_select(thd, all_tables);
  /* 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
  */
Sergei Golubchik's avatar
Sergei Golubchik committed
4879
  mysql_mutex_lock(&LOCK_status);
4880 4881
  add_diff_to_status(&global_status_var, &thd->status_var,
                     &old_status_var);
4882 4883
  memcpy(&thd->status_var, &old_status_var,
         offsetof(STATUS_VAR, last_cleared_system_status_var));
Sergei Golubchik's avatar
Sergei Golubchik committed
4884
  mysql_mutex_unlock(&LOCK_status);
4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896
  return res;
}


static bool execute_rename_table(THD *thd, TABLE_LIST *first_table,
                                 TABLE_LIST *all_tables)
{
  DBUG_ASSERT(first_table == all_tables && first_table != 0);
  TABLE_LIST *table;
  for (table= first_table; table; table= table->next_local->next_local)
  {
    if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
Sergei Golubchik's avatar
Sergei Golubchik committed
4897 4898 4899
                     &table->grant.privilege,
                     &table->grant.m_internal,
                     0, 0) ||
4900
        check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db,
Sergei Golubchik's avatar
Sergei Golubchik committed
4901 4902 4903
                     &table->next_local->grant.privilege,
                     &table->next_local->grant.m_internal,
                     0, 0))
4904 4905 4906 4907 4908 4909 4910 4911
      return 1;
    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];
Sergei Golubchik's avatar
Sergei Golubchik committed
4912 4913 4914 4915 4916
    if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, FALSE, 1, FALSE) ||
       (!test_all_bits(table->next_local->grant.privilege,
                       INSERT_ACL | CREATE_ACL) &&
        check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, FALSE, 1,
                    FALSE)))
4917 4918 4919
      return 1;
  }

Sergei Golubchik's avatar
Sergei Golubchik committed
4920
  return mysql_rename_tables(thd, first_table, 0);
4921 4922 4923
}


unknown's avatar
unknown committed
4924
#ifndef NO_EMBEDDED_ACCESS_CHECKS
unknown's avatar
unknown committed
4925
/**
4926
  Check grants for commands which work only with one table.
unknown's avatar
unknown committed
4927

4928 4929 4930
  @param thd                    Thread handler
  @param privilege              requested privilege
  @param all_tables             global table list of query
unknown's avatar
unknown committed
4931
  @param no_errors              FALSE/TRUE - report/don't report error to
4932
                            the client (using my_error() call).
unknown's avatar
unknown committed
4933 4934 4935 4936 4937

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

4940
bool check_single_table_access(THD *thd, ulong privilege, 
4941
                               TABLE_LIST *all_tables, bool no_errors)
unknown's avatar
unknown committed
4942
{
4943 4944 4945 4946 4947 4948
  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;

4949 4950 4951 4952 4953 4954 4955 4956
  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
4957 4958 4959
                   &all_tables->grant.privilege,
                   &all_tables->grant.m_internal,
                   0, no_errors))
4960
    goto deny;
unknown's avatar
unknown committed
4961

unknown's avatar
unknown committed
4962
  /* Show only 1 table for check_grant */
4963
  if (!(all_tables->belong_to_view &&
4964
        (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) &&
4965
      check_grant(thd, privilege, all_tables, FALSE, 1, no_errors))
4966 4967 4968
    goto deny;

  thd->security_ctx= backup_ctx;
4969 4970 4971 4972 4973 4974 4975
  return 0;

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

unknown's avatar
unknown committed
4976
/**
4977 4978 4979
  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
4980 4981 4982 4983 4984 4985 4986 4987
  @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
4988 4989 4990 4991
*/

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

4995
  /* Check rights on tables of subselects and implictly opened tables */
unknown's avatar
unknown committed
4996
  TABLE_LIST *subselects_tables, *view= all_tables->view ? all_tables : 0;
unknown's avatar
VIEW  
unknown committed
4997
  if ((subselects_tables= all_tables->next_global))
unknown's avatar
unknown committed
4998
  {
unknown's avatar
unknown committed
4999 5000 5001 5002 5003 5004
    /*
      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)
    {
5005
      if (check_single_table_access (thd, privilege, subselects_tables, FALSE))
unknown's avatar
unknown committed
5006 5007 5008 5009
        return 1;
      subselects_tables= subselects_tables->next_global;
    }
    if (subselects_tables &&
5010 5011
        (check_table_access(thd, SELECT_ACL, subselects_tables, FALSE,
                            UINT_MAX, FALSE)))
5012
      return 1;
unknown's avatar
unknown committed
5013 5014
  }
  return 0;
unknown's avatar
unknown committed
5015 5016 5017
}


unknown's avatar
unknown committed
5018
/**
5019 5020 5021 5022 5023 5024
  @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
5025
  @param grant_internal_info A pointer to the internal grant cache.
5026 5027 5028 5029 5030 5031 5032 5033
  @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
5034 5035
  For internal databases (INFORMATION_SCHEMA, PERFORMANCE_SCHEMA),
  additional rules apply, see ACL_internal_schema_access.
5036 5037 5038 5039 5040 5041 5042

  @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
5043
*/
5044

unknown's avatar
unknown committed
5045
bool
unknown's avatar
unknown committed
5046
check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
Marc Alff's avatar
Marc Alff committed
5047 5048
             GRANT_INTERNAL_INFO *grant_internal_info,
             bool dont_check_global_grants, bool no_errors)
unknown's avatar
unknown committed
5049
{
5050
  Security_context *sctx= thd->security_ctx;
unknown's avatar
unknown committed
5051
  ulong db_access;
5052

5053 5054 5055 5056 5057
  /*
    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
5058
    if it's database level grant command
5059 5060 5061
    (see SQLCOM_GRANT case, mysql_execute_command() function) and
    set db_is_pattern according to 'dont_check_global_grants' value.
  */
5062
  bool  db_is_pattern= ((want_access & GRANT_ACL) && dont_check_global_grants);
unknown's avatar
unknown committed
5063
  ulong dummy;
5064 5065
  DBUG_ENTER("check_access");
  DBUG_PRINT("enter",("db: %s  want_access: %lu  master_access: %lu",
5066
                      db ? db : "", want_access, sctx->master_access));
5067

unknown's avatar
unknown committed
5068 5069 5070
  if (save_priv)
    *save_priv=0;
  else
Marc Alff's avatar
Marc Alff committed
5071
  {
unknown's avatar
unknown committed
5072
    save_priv= &dummy;
Marc Alff's avatar
Marc Alff committed
5073 5074
    dummy= 0;
  }
unknown's avatar
unknown committed
5075

5076
  thd_proc_info(thd, "checking permissions");
5077
  if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
unknown's avatar
unknown committed
5078
  {
5079
    DBUG_PRINT("error",("No database"));
5080
    if (!no_errors)
unknown's avatar
unknown committed
5081 5082
      my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR),
                 MYF(0));                       /* purecov: tested */
unknown's avatar
unknown committed
5083
    DBUG_RETURN(TRUE);				/* purecov: tested */
unknown's avatar
unknown committed
5084 5085
  }

Marc Alff's avatar
Marc Alff committed
5086
  if ((db != NULL) && (db != any_db))
5087
  {
Michael Widenius's avatar
Michael Widenius committed
5088 5089 5090 5091
    /*
      Check if this is reserved database, like information schema or
      performance schema
    */
Marc Alff's avatar
Marc Alff committed
5092 5093 5094
    const ACL_internal_schema_access *access;
    access= get_cached_schema_access(grant_internal_info, db);
    if (access)
5095
    {
Marc Alff's avatar
Marc Alff committed
5096
      switch (access->check(want_access, save_priv))
5097
      {
Marc Alff's avatar
Marc Alff committed
5098 5099 5100 5101 5102 5103 5104 5105 5106
      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)
        {
Sergei Golubchik's avatar
Sergei Golubchik committed
5107
          status_var_increment(thd->status_var.access_denied_errors);
Marc Alff's avatar
Marc Alff committed
5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118
          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;
5119
      }
5120 5121 5122
    }
  }

5123
  if ((sctx->master_access & want_access) == want_access)
unknown's avatar
unknown committed
5124
  {
5125
    /*
5126 5127
      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
5128
      UPDATE t1 SET a=1 WHERE b > 0
5129
      2. Change db access if it isn't current db which is being addressed
5130
    */
Marc Alff's avatar
Marc Alff committed
5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149
    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
5150
    DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
5151
  }
5152
  if (((want_access & ~sctx->master_access) & ~DB_ACLS) ||
5153
      (! db && dont_check_global_grants))
unknown's avatar
unknown committed
5154
  {						// We can never grant this
5155
    DBUG_PRINT("error",("No possible access"));
5156
    if (!no_errors)
5157 5158
    {
      status_var_increment(thd->status_var.access_denied_errors);
Sergei Golubchik's avatar
Sergei Golubchik committed
5159
      my_error(access_denied_error_code(thd->password), MYF(0),
5160 5161
               sctx->priv_user,
               sctx->priv_host,
5162 5163 5164
               (thd->password ?
                ER(ER_YES) :
                ER(ER_NO)));                    /* purecov: tested */
5165
    }
unknown's avatar
unknown committed
5166
    DBUG_RETURN(TRUE);				/* purecov: tested */
unknown's avatar
unknown committed
5167 5168 5169
  }

  if (db == any_db)
5170 5171 5172 5173 5174 5175 5176
  {
    /*
      Access granted; Allow select on *any* db.
      [out] *save_privileges= 0
    */
    DBUG_RETURN(FALSE);
  }
unknown's avatar
unknown committed
5177

unknown's avatar
unknown committed
5178
  if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
5179 5180
    db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
                       db_is_pattern);
unknown's avatar
unknown committed
5181
  else
5182
    db_access= sctx->db_access;
5183 5184
  DBUG_PRINT("info",("db_access: %lu  want_access: %lu",
                     db_access, want_access));
5185

5186 5187
  /*
    Save the union of User-table and the intersection between Db-table and
Marc Alff's avatar
Marc Alff committed
5188
    Host-table privileges, with the already saved internal privileges.
5189 5190
  */
  db_access= (db_access | sctx->master_access);
Marc Alff's avatar
Marc Alff committed
5191
  *save_priv|= db_access;
5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206

  /*
    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 ||
5207
      (!dont_check_global_grants &&
5208 5209 5210 5211
       need_table_or_column_check))
  {
    /*
       Ok; but need to check table- and column privileges.
Marc Alff's avatar
Marc Alff committed
5212
       [out] *save_privileges is (User-priv | (Db-priv & Host-priv) | Internal-priv)
5213 5214 5215
    */
    DBUG_RETURN(FALSE);
  }
5216

5217 5218
  /*
    Access is denied;
Marc Alff's avatar
Marc Alff committed
5219
    [out] *save_privileges is (User-priv | (Db-priv & Host-priv) | Internal-priv)
5220
  */
5221
  DBUG_PRINT("error",("Access denied"));
5222
  if (!no_errors)
5223 5224
  {
    status_var_increment(thd->status_var.access_denied_errors);
5225
    my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
5226
             sctx->priv_user, sctx->priv_host,
5227 5228
             (db ? db : (thd->db ?
                         thd->db :
5229
                         "unknown")));
5230
  }
5231
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
5232 5233 5234
}


5235 5236
static bool check_show_access(THD *thd, TABLE_LIST *table)
{
Marc Alff's avatar
Marc Alff committed
5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248
  /*
    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
5249
  switch (get_schema_table_idx(table->schema_table)) {
5250 5251
  case SCH_SCHEMATA:
    return (specialflag & SPECIAL_SKIP_SHOW_DB) &&
unknown's avatar
unknown committed
5252
      check_global_access(thd, SHOW_DB_ACL);
5253 5254 5255 5256 5257

  case SCH_TABLE_NAMES:
  case SCH_TABLES:
  case SCH_VIEWS:
  case SCH_TRIGGERS:
unknown's avatar
unknown committed
5258 5259 5260
  case SCH_EVENTS:
  {
    const char *dst_db_name= table->schema_select_lex->db;
5261

unknown's avatar
unknown committed
5262
    DBUG_ASSERT(dst_db_name);
5263

unknown's avatar
unknown committed
5264
    if (check_access(thd, SELECT_ACL, dst_db_name,
Marc Alff's avatar
Marc Alff committed
5265
                     &thd->col_access, NULL, FALSE, FALSE))
unknown's avatar
unknown committed
5266
      return TRUE;
5267

unknown's avatar
unknown committed
5268 5269
    if (!thd->col_access && check_grant_db(thd, dst_db_name))
    {
5270
      status_var_increment(thd->status_var.access_denied_errors);
unknown's avatar
unknown committed
5271 5272 5273 5274 5275
      my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
               thd->security_ctx->priv_user,
               thd->security_ctx->priv_host,
               dst_db_name);
      return TRUE;
5276 5277
    }

unknown's avatar
unknown committed
5278 5279 5280
    return FALSE;
  }

5281 5282
  case SCH_COLUMNS:
  case SCH_STATISTICS:
unknown's avatar
unknown committed
5283 5284
  {
    TABLE_LIST *dst_table;
5285
    dst_table= table->schema_select_lex->table_list.first;
5286

unknown's avatar
unknown committed
5287
    DBUG_ASSERT(dst_table);
5288

5289
    if (check_access(thd, SELECT_ACL, dst_table->db,
Marc Alff's avatar
Marc Alff committed
5290 5291 5292
                     &dst_table->grant.privilege,
                     &dst_table->grant.m_internal,
                     FALSE, FALSE))
5293 5294 5295 5296 5297 5298 5299 5300
          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 */
5301

5302 5303
    /* Access granted */
    return FALSE;
unknown's avatar
unknown committed
5304 5305
  }
  default:
5306 5307 5308 5309 5310 5311 5312
    break;
  }

  return FALSE;
}


5313

5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341
/**
  @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).
5342

5343 5344 5345 5346
  @return
    @retval FALSE OK
    @retval TRUE  Access denied; But column or routine privileges might need to
      be checked also.
unknown's avatar
unknown committed
5347 5348
*/

5349
bool
5350 5351 5352
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
5353
{
5354 5355
  TABLE_LIST *org_tables= tables;
  TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
5356
  Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx;
Sergei Golubchik's avatar
Sergei Golubchik committed
5357
  uint i= 0;
5358
  /*
unknown's avatar
unknown committed
5359 5360 5361
    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.
5362
  */
5363
  for (; i < number && tables != first_not_own_table && tables;
5364
       tables= tables->next_global, i++)
unknown's avatar
unknown committed
5365
  {
5366
    ulong want_access= requirements;
5367 5368 5369 5370 5371
    if (tables->security_ctx)
      sctx= tables->security_ctx;
    else
      sctx= backup_ctx;

5372 5373 5374 5375 5376
    /*
       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
5377 5378 5379 5380 5381 5382 5383 5384

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

5385 5386
    DBUG_PRINT("info", ("derived: %d  view: %d", tables->derived != 0,
                        tables->view != 0));
5387
    if (tables->is_anonymous_derived_table() ||
5388 5389
        (tables->table && tables->table->s &&
         (int)tables->table->s->tmp_table))
unknown's avatar
unknown committed
5390
      continue;
5391
    thd->security_ctx= sctx;
Marc Alff's avatar
Marc Alff committed
5392 5393 5394 5395 5396

    if (check_access(thd, want_access, tables->get_db_name(),
                     &tables->grant.privilege,
                     &tables->grant.m_internal,
                     0, no_errors))
5397
      goto deny;
unknown's avatar
unknown committed
5398
  }
5399
  thd->security_ctx= backup_ctx;
5400 5401 5402
  return check_grant(thd,requirements,org_tables,
                     any_combination_of_privileges_will_do,
                     number, no_errors);
5403 5404 5405
deny:
  thd->security_ctx= backup_ctx;
  return TRUE;
unknown's avatar
unknown committed
5406 5407
}

5408

5409
bool
5410 5411
check_routine_access(THD *thd, ulong want_access,char *db, char *name,
		     bool is_proc, bool no_errors)
5412 5413 5414 5415 5416
{
  TABLE_LIST tables[1];
  
  bzero((char *)tables, sizeof(TABLE_LIST));
  tables->db= db;
5417
  tables->table_name= tables->alias= name;
5418
  
unknown's avatar
unknown committed
5419 5420 5421 5422 5423
  /*
    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
5424 5425 5426 5427 5428 5429
    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
5430
  */
Marc Alff's avatar
Marc Alff committed
5431
  DBUG_ASSERT((want_access & CREATE_PROC_ACL) == 0);
unknown's avatar
unknown committed
5432
  if ((thd->security_ctx->master_access & want_access) == want_access)
5433
    tables->grant.privilege= want_access;
Marc Alff's avatar
Marc Alff committed
5434 5435 5436 5437
  else if (check_access(thd, want_access, db,
                        &tables->grant.privilege,
                        &tables->grant.m_internal,
                        0, no_errors))
5438 5439
    return TRUE;
  
5440
  return check_grant_routine(thd, want_access, tables, is_proc, no_errors);
5441 5442
}

5443

unknown's avatar
unknown committed
5444 5445
/**
  Check if the routine has any of the routine privileges.
5446

unknown's avatar
unknown committed
5447 5448 5449
  @param thd	       Thread handler
  @param db           Database name
  @param name         Routine name
5450

unknown's avatar
unknown committed
5451
  @retval
5452
    0            ok
unknown's avatar
unknown committed
5453
  @retval
5454 5455 5456
    1            error
*/

5457 5458
bool check_some_routine_access(THD *thd, const char *db, const char *name,
                               bool is_proc)
5459 5460
{
  ulong save_priv;
5461
  /*
Marc Alff's avatar
Marc Alff committed
5462 5463 5464 5465 5466 5467 5468
    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.
5469
  */
Marc Alff's avatar
Marc Alff committed
5470 5471 5472
  if (thd->security_ctx->master_access & SHOW_PROC_ACLS)
    return FALSE;
  if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, NULL, 0, 1) ||
5473 5474
      (save_priv & SHOW_PROC_ACLS))
    return FALSE;
5475
  return check_routine_level_acl(thd, db, name, is_proc);
5476 5477 5478
}


5479 5480 5481
/*
  Check if the given table has any of the asked privileges

unknown's avatar
unknown committed
5482 5483
  @param thd		 Thread handler
  @param want_access	 Bitmap of possible privileges to check for
5484

unknown's avatar
unknown committed
5485
  @retval
5486
    0  ok
unknown's avatar
unknown committed
5487
  @retval
5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501
    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
5502 5503 5504
                        &table->grant.privilege,
                        &table->grant.m_internal,
                        0, 1) &&
5505
           !check_grant(thd, access, table, FALSE, 1, TRUE))
5506 5507 5508 5509 5510 5511 5512
        DBUG_RETURN(0);
    }
  }
  DBUG_PRINT("exit",("no matching access rights"));
  DBUG_RETURN(1);
}

unknown's avatar
unknown committed
5513
#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
5514

5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533

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

5534
bool check_global_access(THD *thd, ulong want_access, bool no_errors)
5535 5536 5537 5538 5539
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  char command[128];
  if ((thd->security_ctx->master_access & want_access))
    return 0;
5540 5541 5542 5543 5544
  if (!no_errors)
  {
    get_privilege_desc(command, sizeof(command), want_access);
    my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
  }
5545
  status_var_increment(thd->status_var.access_denied_errors);
5546 5547 5548 5549 5550 5551
  return 1;
#else
  return 0;
#endif
}

unknown's avatar
unknown committed
5552 5553 5554 5555
/****************************************************************************
	Check stack size; Send error if there isn't enough stack to continue
****************************************************************************/

5556

unknown's avatar
unknown committed
5557 5558 5559 5560 5561 5562
#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
5563 5564 5565 5566
#ifndef DBUG_OFF
long max_stack_used;
#endif

unknown's avatar
unknown committed
5567 5568
/**
  @note
5569 5570 5571 5572
  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
5573
*/
5574
bool check_stack_overrun(THD *thd, long margin,
5575
			 uchar *buf __attribute__((unused)))
unknown's avatar
unknown committed
5576 5577
{
  long stack_used;
5578
  DBUG_ASSERT(thd == current_thd);
unknown's avatar
unknown committed
5579
  if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
5580
      (long) (my_thread_stack_size - margin))
unknown's avatar
unknown committed
5581
  {
5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592
    /*
      Do not use stack for the message buffer to ensure correct
      behaviour in cases we have close to no stack left.
    */
    char* ebuff= new char[MYSQL_ERRMSG_SIZE];
    if (ebuff) {
      my_snprintf(ebuff, MYSQL_ERRMSG_SIZE, ER(ER_STACK_OVERRUN_NEED_MORE),
                  stack_used, my_thread_stack_size, margin);
      my_message(ER_STACK_OVERRUN_NEED_MORE, ebuff, MYF(ME_FATALERROR));
      delete [] ebuff;
    }
unknown's avatar
unknown committed
5593 5594
    return 1;
  }
unknown's avatar
unknown committed
5595 5596 5597
#ifndef DBUG_OFF
  max_stack_used= max(max_stack_used, stack_used);
#endif
unknown's avatar
unknown committed
5598 5599
  return 0;
}
5600

unknown's avatar
unknown committed
5601 5602 5603 5604

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

5605
bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
unknown's avatar
unknown committed
5606
{
5607
  Yacc_state *state= & current_thd->m_parser_state->m_yacc;
5608
  ulong old_info=0;
5609
  DBUG_ASSERT(state);
unknown's avatar
unknown committed
5610 5611
  if ((uint) *yystacksize >= MY_YACC_MAX)
    return 1;
5612
  if (!state->yacc_yyvs)
unknown's avatar
unknown committed
5613 5614
    old_info= *yystacksize;
  *yystacksize= set_zone((*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX);
5615
  if (!(state->yacc_yyvs= (uchar*)
5616
        my_realloc(state->yacc_yyvs,
5617 5618 5619
                   *yystacksize*sizeof(**yyvs),
                   MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) ||
      !(state->yacc_yyss= (uchar*)
5620
        my_realloc(state->yacc_yyss,
5621 5622
                   *yystacksize*sizeof(**yyss),
                   MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))))
unknown's avatar
unknown committed
5623 5624
    return 1;
  if (old_info)
5625 5626 5627 5628 5629 5630 5631 5632
  {
    /*
      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
5633
  }
5634 5635
  *yyss= (short*) state->yacc_yyss;
  *yyvs= (YYSTYPE*) state->yacc_yyvs;
unknown's avatar
unknown committed
5636 5637 5638 5639
  return 0;
}


unknown's avatar
unknown committed
5640
/**
5641 5642
  Reset the part of THD responsible for the state of command
  processing.
5643

5644 5645 5646
  This needs to be called before execution of every statement
  (prepared or conventional).  It is not called by substatements of
  routines.
5647

5648 5649
  @todo Remove mysql_reset_thd_for_next_command and only use the
  member function.
5650

5651 5652
  @todo Call it after we use THD for queries, not before.
*/
5653
void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat)
5654
{
Sergei Golubchik's avatar
Sergei Golubchik committed
5655
  thd->reset_for_next_command(calculate_userstat);
5656 5657
}

Sergei Golubchik's avatar
Sergei Golubchik committed
5658
void THD::reset_for_next_command(bool calculate_userstat)
5659 5660
{
  THD *thd= this;
5661
  DBUG_ENTER("mysql_reset_thd_for_next_command");
5662
  DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */
5663
  DBUG_ASSERT(! thd->in_sub_stmt);
5664
  DBUG_ASSERT(thd->transaction.on);
5665
  thd->free_list= 0;
5666
  thd->select_number= 1;
5667 5668 5669 5670
  /*
    Those two lines below are theoretically unneeded as
    THD::cleanup_after_query() should take care of this already.
  */
5671
  thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty();
5672 5673 5674
  thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;

  thd->query_start_used= 0;
5675
  thd->query_start_sec_part_used= 0;
5676
  thd->is_fatal_error= thd->time_zone_used= 0;
5677 5678 5679 5680 5681
  /*
    Clear the status flag that are expected to be cleared at the
    beginning of each SQL statement.
  */
  thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
5682 5683 5684 5685 5686
  /*
    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.
  */
5687
  if (!thd->in_multi_stmt_transaction_mode())
unknown's avatar
unknown committed
5688
  {
5689
    thd->variables.option_bits&= ~OPTION_KEEP_LOG;
5690
    thd->transaction.all.modified_non_trans_table= FALSE;
unknown's avatar
unknown committed
5691
  }
5692
  DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx);
5693
  thd->thread_specific_used= FALSE;
5694 5695

  if (opt_bin_log)
5696
  {
5697 5698
    reset_dynamic(&thd->user_var_events);
    thd->user_var_events_alloc= thd->mem_root;
5699
  }
5700
  thd->clear_error();
Marc Alff's avatar
Marc Alff committed
5701 5702
  thd->stmt_da->reset_diagnostics_area();
  thd->warning_info->reset_for_next_command();
5703 5704
  thd->rand_used= 0;
  thd->sent_row_count= thd->examined_row_count= 0;
5705
  thd->accessed_rows_and_keys= 0;
5706

5707 5708 5709 5710 5711 5712 5713 5714
  /* Copy data for user stats */
  if ((thd->userstat_running= calculate_userstat))
  {
    thd->start_cpu_time= my_getcputime();
    memcpy(&thd->org_status_var, &thd->status_var, sizeof(thd->status_var));
    thd->select_commands= thd->update_commands= thd->other_commands= 0;
  }

5715 5716
  thd->query_plan_flags= QPLAN_INIT;
  thd->query_plan_fsort_passes= 0;
5717

5718
  thd->reset_current_stmt_binlog_format_row();
5719
  thd->binlog_unsafe_warning_flags= 0;
unknown's avatar
unknown committed
5720

5721
  DBUG_PRINT("debug",
5722
             ("is_current_stmt_binlog_format_row(): %d",
5723
              thd->is_current_stmt_binlog_format_row()));
5724

unknown's avatar
unknown committed
5725 5726 5727
  DBUG_VOID_RETURN;
}

5728

5729 5730 5731 5732 5733 5734 5735 5736
/**
  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.
*/

5737 5738 5739
void
mysql_init_select(LEX *lex)
{
unknown's avatar
unknown committed
5740
  SELECT_LEX *select_lex= lex->current_select;
unknown's avatar
unknown committed
5741
  select_lex->init_select();
5742
  lex->wild= 0;
5743 5744
  if (select_lex == &lex->select_lex)
  {
5745
    DBUG_ASSERT(lex->result == 0);
5746 5747
    lex->exchange= 0;
  }
5748 5749
}

5750

5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762
/**
  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
5763
bool
unknown's avatar
unknown committed
5764
mysql_new_select(LEX *lex, bool move_down)
5765
{
unknown's avatar
unknown committed
5766
  SELECT_LEX *select_lex;
5767
  THD *thd= lex->thd;
unknown's avatar
unknown committed
5768 5769
  DBUG_ENTER("mysql_new_select");

5770
  if (!(select_lex= new (thd->mem_root) SELECT_LEX()))
unknown's avatar
unknown committed
5771
    DBUG_RETURN(1);
5772
  select_lex->select_number= ++thd->select_number;
unknown's avatar
unknown committed
5773
  select_lex->parent_lex= lex; /* Used in init_query. */
unknown's avatar
unknown committed
5774 5775
  select_lex->init_query();
  select_lex->init_select();
unknown's avatar
unknown committed
5776
  lex->nest_level++;
unknown's avatar
unknown committed
5777 5778
  if (lex->nest_level > (int) MAX_SELECT_NESTING)
  {
5779
    my_error(ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT, MYF(0));
unknown's avatar
unknown committed
5780 5781
    DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
5782
  select_lex->nest_level= lex->nest_level;
unknown's avatar
unknown committed
5783
  select_lex->nest_level_base= &thd->lex->unit;
unknown's avatar
unknown committed
5784 5785
  if (move_down)
  {
unknown's avatar
unknown committed
5786
    SELECT_LEX_UNIT *unit;
5787
    lex->subqueries= TRUE;
unknown's avatar
unknown committed
5788
    /* first select_lex of subselect or derived table */
5789
    if (!(unit= new (thd->mem_root) SELECT_LEX_UNIT()))
unknown's avatar
unknown committed
5790
      DBUG_RETURN(1);
unknown's avatar
unknown committed
5791

unknown's avatar
unknown committed
5792 5793
    unit->init_query();
    unit->init_select();
5794
    unit->thd= thd;
unknown's avatar
unknown committed
5795
    unit->include_down(lex->current_select);
unknown's avatar
unknown committed
5796 5797
    unit->link_next= 0;
    unit->link_prev= 0;
5798
    unit->return_to= lex->current_select;
unknown's avatar
unknown committed
5799
    select_lex->include_down(unit);
5800 5801 5802 5803 5804
    /*
      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
5805 5806
  }
  else
unknown's avatar
unknown committed
5807
  {
unknown's avatar
VIEW  
unknown committed
5808 5809
    if (lex->current_select->order_list.first && !lex->current_select->braces)
    {
unknown's avatar
unknown committed
5810
      my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY");
unknown's avatar
unknown committed
5811
      DBUG_RETURN(1);
unknown's avatar
VIEW  
unknown committed
5812
    }
5813
    select_lex->include_neighbour(lex->current_select);
5814 5815 5816 5817 5818
    SELECT_LEX_UNIT *unit= select_lex->master_unit();                              
    if (!unit->fake_select_lex && unit->add_fake_select_lex(lex->thd))
      DBUG_RETURN(1);
    select_lex->context.outer_context= 
                unit->first_select()->context.outer_context;
unknown's avatar
unknown committed
5819
  }
unknown's avatar
unknown committed
5820

5821
  select_lex->master_unit()->global_parameters= select_lex;
5822
  select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
5823
  lex->current_select= select_lex;
5824 5825 5826 5827 5828
  /*
    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
5829
  DBUG_RETURN(0);
5830
}
unknown's avatar
unknown committed
5831

unknown's avatar
unknown committed
5832
/**
5833 5834
  Create a select to return the same output as 'SELECT @@var_name'.

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

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

unknown's avatar
unknown committed
5839
  @param var_name		Variable name
5840 5841 5842 5843
*/

void create_select_for_variable(const char *var_name)
{
5844
  THD *thd;
5845
  LEX *lex;
5846
  LEX_STRING tmp, null_lex_string;
5847 5848
  Item *var;
  char buff[MAX_SYS_VAR_LENGTH*2+4+8], *end;
5849
  DBUG_ENTER("create_select_for_variable");
5850 5851

  thd= current_thd;
unknown's avatar
unknown committed
5852
  lex= thd->lex;
5853 5854 5855 5856
  mysql_init_select(lex);
  lex->sql_command= SQLCOM_SELECT;
  tmp.str= (char*) var_name;
  tmp.length=strlen(var_name);
5857
  bzero((char*) &null_lex_string.str, sizeof(null_lex_string));
5858 5859 5860 5861
  /*
    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
5862 5863 5864 5865 5866 5867
  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);
  }
5868 5869 5870
  DBUG_VOID_RETURN;
}

5871

unknown's avatar
unknown committed
5872 5873
void mysql_init_multi_delete(LEX *lex)
{
unknown's avatar
unknown committed
5874
  lex->sql_command=  SQLCOM_DELETE_MULTI;
unknown's avatar
unknown committed
5875
  mysql_init_select(lex);
5876 5877
  lex->select_lex.select_limit= 0;
  lex->unit.select_limit_cnt= HA_POS_ERROR;
unknown's avatar
unknown committed
5878
  lex->select_lex.table_list.save_and_clear(&lex->auxiliary_table_list);
unknown's avatar
VIEW  
unknown committed
5879 5880
  lex->query_tables= 0;
  lex->query_tables_last= &lex->query_tables;
unknown's avatar
unknown committed
5881
}
unknown's avatar
unknown committed
5882

5883

5884 5885 5886 5887
/*
  When you modify mysql_parse(), you may need to mofify
  mysql_test_parse_for_slave() in this same file.
*/
unknown's avatar
unknown committed
5888

5889 5890
/**
  Parse a query.
unknown's avatar
unknown committed
5891 5892

  @param       thd     Current thread
5893
  @param       rawbuf  Begining of the query text
unknown's avatar
unknown committed
5894 5895 5896
  @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.
5897 5898
*/

5899
void mysql_parse(THD *thd, char *rawbuf, uint length,
5900
                 Parser_state *parser_state)
unknown's avatar
unknown committed
5901
{
5902
  int error __attribute__((unused));
unknown's avatar
unknown committed
5903
  DBUG_ENTER("mysql_parse");
5904 5905
  DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on(););

5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922
  /*
    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);
5923
  mysql_reset_thd_for_next_command(thd, opt_userstat_running);
5924

5925
  if (query_cache_send_result_to_client(thd, rawbuf, length) <= 0)
unknown's avatar
unknown committed
5926
  {
unknown's avatar
unknown committed
5927
    LEX *lex= thd->lex;
5928

5929
    bool err= parse_sql(thd, parser_state, NULL);
5930

5931
    if (!err)
unknown's avatar
unknown committed
5932
    {
unknown's avatar
unknown committed
5933
#ifndef NO_EMBEDDED_ACCESS_CHECKS
5934
      if (mqh_used && thd->user_connect &&
5935
	  check_mqh(thd, lex->sql_command))
5936 5937 5938 5939
      {
	thd->net.error = 0;
      }
      else
unknown's avatar
unknown committed
5940
#endif
5941
      {
5942
	if (! thd->is_error())
unknown's avatar
unknown committed
5943
	{
5944
          const char *found_semicolon= parser_state->m_lip.found_semicolon;
5945 5946 5947 5948 5949 5950 5951 5952 5953 5954
          /*
            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.
          */
5955
          if (found_semicolon && (ulong) (found_semicolon - thd->query()))
5956
            thd->set_query_inner(thd->query(),
5957
                                 (uint32) (found_semicolon -
5958 5959
                                           thd->query() - 1),
                                 thd->charset());
5960
          /* Actually execute the query */
5961
          if (found_semicolon)
5962 5963 5964 5965
          {
            lex->safe_to_cache_query= 0;
            thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
          }
5966
          lex->set_trg_event_type_for_tables();
5967
          MYSQL_QUERY_EXEC_START(thd->query(),
5968 5969
                                 thd->thread_id,
                                 (char *) (thd->db ? thd->db : ""),
5970
                                 &thd->security_ctx->priv_user[0],
5971 5972 5973 5974 5975
                                 (char *) thd->security_ctx->host_or_ip,
                                 0);

          error= mysql_execute_command(thd);
          MYSQL_QUERY_EXEC_DONE(error);
unknown's avatar
unknown committed
5976
	}
5977
      }
unknown's avatar
unknown committed
5978 5979
    }
    else
5980
    {
5981
      DBUG_ASSERT(thd->is_error());
5982
      DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
5983
			 thd->is_fatal_error));
5984

5985
      query_cache_abort(&thd->query_cache_tls);
5986
    }
5987
    thd_proc_info(thd, "freeing items");
5988 5989
    sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
    sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
5990
    thd->end_statement();
5991
    thd->cleanup_after_query();
5992
    DBUG_ASSERT(thd->change_list.is_empty());
unknown's avatar
unknown committed
5993
  }
5994 5995
  else
  {
5996 5997
    /* Update statistics for getting the query from the cache */
    thd->lex->sql_command= SQLCOM_SELECT;
5998
  }
unknown's avatar
unknown committed
5999 6000 6001 6002
  DBUG_VOID_RETURN;
}


unknown's avatar
unknown committed
6003
#ifdef HAVE_REPLICATION
6004 6005 6006 6007
/*
  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
6008
  @retval
6009
    0	cannot be ignored
unknown's avatar
unknown committed
6010
  @retval
6011 6012 6013
    1	can be ignored
*/

6014
bool mysql_test_parse_for_slave(THD *thd, char *rawbuf, uint length)
6015
{
unknown's avatar
unknown committed
6016
  LEX *lex= thd->lex;
6017
  bool error= 0;
unknown's avatar
unknown committed
6018
  DBUG_ENTER("mysql_test_parse_for_slave");
6019

6020
  Parser_state parser_state;
6021
  if (!(error= parser_state.init(thd, rawbuf, length)))
6022 6023
  {
    lex_start(thd);
Michael Widenius's avatar
Michael Widenius committed
6024
    mysql_reset_thd_for_next_command(thd, opt_userstat_running);
6025

6026
    if (!parse_sql(thd, & parser_state, NULL) &&
6027
        all_tables_not_ok(thd, lex->select_lex.table_list.first))
6028 6029 6030
      error= 1;                  /* Ignore question */
    thd->end_statement();
  }
6031
  thd->cleanup_after_query();
unknown's avatar
unknown committed
6032
  DBUG_RETURN(error);
6033
}
unknown's avatar
unknown committed
6034
#endif
unknown's avatar
unknown committed
6035

6036

unknown's avatar
unknown committed
6037

unknown's avatar
unknown committed
6038 6039 6040 6041 6042 6043
/**
  Store field definition for create.

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

6045
bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
unknown's avatar
unknown committed
6046
		       char *length, char *decimals,
6047
		       uint type_modifier,
6048 6049
		       Item *default_value, Item *on_update_value,
                       LEX_STRING *comment,
6050 6051
		       char *change,
                       List<String> *interval_list, CHARSET_INFO *cs,
6052
		       uint uint_geom_type,
6053 6054
		       Virtual_column_info *vcol_info,
                       engine_option_value *create_options)
unknown's avatar
unknown committed
6055
{
unknown's avatar
unknown committed
6056
  register Create_field *new_field;
unknown's avatar
unknown committed
6057
  LEX  *lex= thd->lex;
6058
  uint8 datetime_precision= length ? atoi(length) : 0;
unknown's avatar
unknown committed
6059 6060
  DBUG_ENTER("add_field_to_list");

6061 6062
  if (check_string_char_length(field_name, "", NAME_CHAR_LEN,
                               system_charset_info, 1))
unknown's avatar
unknown committed
6063
  {
6064
    my_error(ER_TOO_LONG_IDENT, MYF(0), field_name->str); /* purecov: inspected */
unknown's avatar
unknown committed
6065 6066 6067 6068
    DBUG_RETURN(1);				/* purecov: inspected */
  }
  if (type_modifier & PRI_KEY_FLAG)
  {
6069
    Key *key;
6070 6071
    lex->col_list.push_back(new Key_part_spec(*field_name, 0));
    key= new Key(Key::PRIMARY, null_lex_str,
6072
                      &default_key_create_info,
6073
                      0, lex->col_list, NULL, lex->check_exists);
6074
    lex->alter_info.key_list.push_back(key);
unknown's avatar
unknown committed
6075 6076 6077 6078
    lex->col_list.empty();
  }
  if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
  {
6079
    Key *key;
6080 6081
    lex->col_list.push_back(new Key_part_spec(*field_name, 0));
    key= new Key(Key::UNIQUE, null_lex_str,
6082
                 &default_key_create_info, 0,
6083
                 lex->col_list, NULL, lex->check_exists);
6084
    lex->alter_info.key_list.push_back(key);
unknown's avatar
unknown committed
6085 6086 6087
    lex->col_list.empty();
  }

6088
  if (default_value)
unknown's avatar
unknown committed
6089
  {
6090
    /* 
unknown's avatar
unknown committed
6091 6092
      Default value should be literal => basic constants =>
      no need fix_fields()
6093 6094
      
      We allow only one function as part of default value - 
6095
      NOW() as default for TIMESTAMP and DATETIME type.
6096
    */
6097
    if (default_value->type() == Item::FUNC_ITEM && 
6098 6099 6100 6101
        (static_cast<Item_func*>(default_value)->functype() !=
         Item_func::NOW_FUNC ||
         (mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_DATETIME) ||
         default_value->decimals < datetime_precision))
6102
    {
6103
      my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
6104 6105 6106
      DBUG_RETURN(1);
    }
    else if (default_value->type() == Item::NULL_ITEM)
unknown's avatar
unknown committed
6107
    {
6108
      default_value= 0;
6109 6110 6111
      if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
	  NOT_NULL_FLAG)
      {
6112
	my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
6113 6114 6115 6116 6117
	DBUG_RETURN(1);
      }
    }
    else if (type_modifier & AUTO_INCREMENT_FLAG)
    {
6118
      my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
unknown's avatar
unknown committed
6119 6120 6121
      DBUG_RETURN(1);
    }
  }
6122

6123
  if (on_update_value &&
6124 6125
      (mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_DATETIME ||
       on_update_value->decimals < datetime_precision))
6126
  {
6127
    my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name->str);
6128 6129
    DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
6130

unknown's avatar
unknown committed
6131
  if (!(new_field= new Create_field()) ||
6132
      new_field->init(thd, field_name->str, type, length, decimals, type_modifier,
6133
                      default_value, on_update_value, comment, change,
6134
                      interval_list, cs, uint_geom_type, vcol_info,
6135
                      create_options, lex->check_exists))
unknown's avatar
unknown committed
6136
    DBUG_RETURN(1);
unknown's avatar
unknown committed
6137

6138
  lex->alter_info.create_list.push_back(new_field);
unknown's avatar
unknown committed
6139 6140 6141 6142
  lex->last_field=new_field;
  DBUG_RETURN(0);
}

6143

unknown's avatar
unknown committed
6144
/** Store position for column in ALTER TABLE .. ADD column. */
unknown's avatar
unknown committed
6145 6146 6147

void store_position_for_column(const char *name)
{
6148
  current_thd->lex->last_field->after=(char*) (name);
unknown's avatar
unknown committed
6149 6150 6151
}

bool
unknown's avatar
unknown committed
6152
add_proc_to_list(THD* thd, Item *item)
unknown's avatar
unknown committed
6153 6154 6155 6156
{
  ORDER *order;
  Item	**item_ptr;

unknown's avatar
unknown committed
6157
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER)+sizeof(Item*))))
unknown's avatar
unknown committed
6158 6159 6160 6161 6162
    return 1;
  item_ptr = (Item**) (order+1);
  *item_ptr= item;
  order->item=item_ptr;
  order->free_me=0;
6163
  thd->lex->proc_list.link_in_list(order, &order->next);
unknown's avatar
unknown committed
6164 6165 6166 6167
  return 0;
}


unknown's avatar
unknown committed
6168 6169 6170
/**
  save order by and tables in own lists.
*/
unknown's avatar
unknown committed
6171

6172
bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *item,bool asc)
unknown's avatar
unknown committed
6173 6174 6175
{
  ORDER *order;
  DBUG_ENTER("add_to_list");
unknown's avatar
unknown committed
6176
  if (!(order = (ORDER *) thd->alloc(sizeof(ORDER))))
unknown's avatar
unknown committed
6177
    DBUG_RETURN(1);
unknown's avatar
unknown committed
6178 6179
  order->item_ptr= item;
  order->item= &order->item_ptr;
unknown's avatar
unknown committed
6180 6181 6182
  order->asc = asc;
  order->free_me=0;
  order->used=0;
6183
  order->counter_used= 0;
6184
  list.link_in_list(order, &order->next);
unknown's avatar
unknown committed
6185 6186 6187 6188
  DBUG_RETURN(0);
}


unknown's avatar
unknown committed
6189 6190 6191 6192 6193 6194 6195 6196 6197 6198
/**
  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
6199
  @param mdl_type       Type of metadata lock to acquire on the table.
unknown's avatar
unknown committed
6200 6201 6202 6203
  @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
6204
      0		Error
unknown's avatar
unknown committed
6205 6206
  @retval
    \#	Pointer to TABLE_LIST element added to the total table list
unknown's avatar
unknown committed
6207 6208
*/

unknown's avatar
unknown committed
6209 6210
TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
					     Table_ident *table,
6211
					     LEX_STRING *alias,
unknown's avatar
unknown committed
6212 6213
					     ulong table_options,
					     thr_lock_type lock_type,
6214
					     enum_mdl_type mdl_type,
unknown's avatar
unknown committed
6215
					     List<Index_hint> *index_hints_arg,
unknown's avatar
unknown committed
6216
                                             LEX_STRING *option)
unknown's avatar
unknown committed
6217 6218
{
  register TABLE_LIST *ptr;
unknown's avatar
unknown committed
6219
  TABLE_LIST *previous_table_ref; /* The table preceding the current one. */
unknown's avatar
unknown committed
6220
  char *alias_str;
6221
  LEX *lex= thd->lex;
unknown's avatar
unknown committed
6222
  DBUG_ENTER("add_table_to_list");
6223
  LINT_INIT(previous_table_ref);
unknown's avatar
unknown committed
6224 6225 6226 6227

  if (!table)
    DBUG_RETURN(0);				// End of memory
  alias_str= alias ? alias->str : table->table.str;
6228
  if (!test(table_options & TL_OPTION_ALIAS) && 
6229
      check_table_name(table->table.str, table->table.length, FALSE))
unknown's avatar
unknown committed
6230
  {
6231
    my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str);
unknown's avatar
unknown committed
6232 6233
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
6234 6235

  if (table->is_derived_table() == FALSE && table->db.str &&
6236
      check_db_name(&table->db))
unknown's avatar
unknown committed
6237 6238 6239 6240
  {
    my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str);
    DBUG_RETURN(0);
  }
unknown's avatar
unknown committed
6241 6242

  if (!alias)					/* Alias is case sensitive */
6243 6244 6245
  {
    if (table->sel)
    {
unknown's avatar
unknown committed
6246 6247
      my_message(ER_DERIVED_MUST_HAVE_ALIAS,
                 ER(ER_DERIVED_MUST_HAVE_ALIAS), MYF(0));
6248 6249
      DBUG_RETURN(0);
    }
6250
    if (!(alias_str= (char*) thd->memdup(alias_str,table->table.length+1)))
unknown's avatar
unknown committed
6251
      DBUG_RETURN(0);
6252
  }
unknown's avatar
unknown committed
6253
  if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))))
unknown's avatar
unknown committed
6254
    DBUG_RETURN(0);				/* purecov: inspected */
unknown's avatar
unknown committed
6255
  if (table->db.str)
6256
  {
6257
    ptr->is_fqtn= TRUE;
6258 6259 6260
    ptr->db= table->db.str;
    ptr->db_length= table->db.length;
  }
6261
  else if (lex->copy_db_to(&ptr->db, &ptr->db_length))
unknown's avatar
unknown committed
6262
    DBUG_RETURN(0);
6263 6264
  else
    ptr->is_fqtn= FALSE;
unknown's avatar
unknown committed
6265

6266
  ptr->alias= alias_str;
6267
  ptr->is_alias= alias ? TRUE : FALSE;
6268
  if (lower_case_table_names && table->table.length)
6269
    table->table.length= my_casedn_str(files_charset_info, table->table.str);
6270 6271
  ptr->table_name=table->table.str;
  ptr->table_name_length=table->table.length;
6272
  ptr->lock_type=   lock_type;
unknown's avatar
unknown committed
6273
  ptr->updating=    test(table_options & TL_OPTION_UPDATING);
6274
  /* TODO: remove TL_OPTION_FORCE_INDEX as it looks like it's not used */
unknown's avatar
unknown committed
6275
  ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
unknown's avatar
unknown committed
6276
  ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
6277
  ptr->derived=	    table->sel;
6278
  if (!ptr->derived && is_infoschema_db(ptr->db, ptr->db_length))
6279
  {
6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292
    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);
6293 6294
    if (!schema_table ||
        (schema_table->hidden && 
unknown's avatar
unknown committed
6295
         ((sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0 || 
6296 6297 6298
          /*
            this check is used for show columns|keys from I_S hidden table
          */
unknown's avatar
unknown committed
6299 6300
          lex->sql_command == SQLCOM_SHOW_FIELDS ||
          lex->sql_command == SQLCOM_SHOW_KEYS)))
6301
    {
unknown's avatar
unknown committed
6302
      my_error(ER_UNKNOWN_TABLE, MYF(0),
6303
               ptr->table_name, INFORMATION_SCHEMA_NAME.str);
6304 6305
      DBUG_RETURN(0);
    }
6306
    ptr->schema_table_name= ptr->table_name;
6307 6308
    ptr->schema_table= schema_table;
  }
6309
  ptr->select_lex=  lex->current_select;
unknown's avatar
unknown committed
6310
  ptr->cacheable_table= 1;
6311
  ptr->index_hints= index_hints_arg;
unknown's avatar
unknown committed
6312
  ptr->option= option ? option->str : 0;
unknown's avatar
unknown committed
6313
  /* check that used name is unique */
6314
  if (lock_type != TL_IGNORE)
unknown's avatar
unknown committed
6315
  {
6316
    TABLE_LIST *first_table= table_list.first;
unknown's avatar
unknown committed
6317 6318 6319
    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
6320
	 tables ;
unknown's avatar
VIEW  
unknown committed
6321
	 tables=tables->next_local)
unknown's avatar
unknown committed
6322
    {
6323 6324
      if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
	  !strcmp(ptr->db, tables->db))
unknown's avatar
unknown committed
6325
      {
6326
	my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str); /* purecov: tested */
unknown's avatar
unknown committed
6327 6328
	DBUG_RETURN(0);				/* purecov: tested */
      }
unknown's avatar
unknown committed
6329 6330
    }
  }
unknown's avatar
unknown committed
6331 6332 6333
  /* Store the table reference preceding the current one. */
  if (table_list.elements > 0)
  {
6334 6335 6336
    /*
      table_list.next points to the last inserted TABLE_LIST->next_local'
      element
6337
      We don't use the offsetof() macro here to avoid warnings from gcc
6338
    */
6339 6340 6341
    previous_table_ref= (TABLE_LIST*) ((char*) table_list.next -
                                       ((char*) &(ptr->next_local) -
                                        (char*) ptr));
6342 6343 6344 6345 6346 6347 6348 6349
    /*
      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
6350
  }
6351

unknown's avatar
unknown committed
6352 6353 6354 6355 6356 6357
  /*
    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'.
  */
6358
  table_list.link_in_list(ptr, &ptr->next_local);
unknown's avatar
unknown committed
6359
  ptr->next_name_resolution_table= NULL;
6360
  /* Link table in global list (all used tables) */
6361
  lex->add_to_query_tables(ptr);
6362 6363 6364 6365 6366 6367 6368

  // Pure table aliases do not need to be locked:
  if (!test(table_options & TL_OPTION_ALIAS))
  {
    ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type,
                          MDL_TRANSACTION);
  }
unknown's avatar
unknown committed
6369 6370 6371
  DBUG_RETURN(ptr);
}

unknown's avatar
unknown committed
6372

unknown's avatar
unknown committed
6373 6374
/**
  Initialize a new table list for a nested join.
6375

6376 6377 6378 6379 6380 6381 6382 6383
    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
6384 6385 6386 6387 6388 6389
  @param thd         current thread

  @retval
    0   if success
  @retval
    1   otherwise
6390 6391 6392 6393 6394 6395 6396
*/

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

6398 6399
  if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
                                       sizeof(NESTED_JOIN))))
6400
    DBUG_RETURN(1);
6401
  nested_join= ptr->nested_join=
6402
    ((NESTED_JOIN*) ((uchar*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
6403

6404 6405 6406
  join_list->push_front(ptr);
  ptr->embedding= embedding;
  ptr->join_list= join_list;
6407
  ptr->alias= (char*) "(nested_join)";
6408 6409 6410 6411 6412 6413 6414
  embedding= ptr;
  join_list= &nested_join->join_list;
  join_list->empty();
  DBUG_RETURN(0);
}


unknown's avatar
unknown committed
6415 6416
/**
  End a nested join table list.
6417 6418 6419

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

unknown's avatar
unknown committed
6422 6423 6424 6425 6426
  @param thd         current thread

  @return
    - Pointer to TABLE_LIST element added to the total table list, if success
    - 0, otherwise
6427 6428 6429 6430 6431
*/

TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
{
  TABLE_LIST *ptr;
6432
  NESTED_JOIN *nested_join;
6433
  DBUG_ENTER("end_nested_join");
6434

unknown's avatar
unknown committed
6435
  DBUG_ASSERT(embedding);
6436 6437 6438
  ptr= embedding;
  join_list= ptr->join_list;
  embedding= ptr->embedding;
6439
  nested_join= ptr->nested_join;
6440 6441 6442 6443 6444 6445 6446 6447
  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;
Igor Babaev's avatar
Igor Babaev committed
6448
    embedded->lifted= 1;
6449
  }
6450
  else if (nested_join->join_list.elements == 0)
unknown's avatar
unknown committed
6451 6452
  {
    join_list->pop();
6453
    ptr= 0;                                     // return value
unknown's avatar
unknown committed
6454
  }
6455 6456 6457 6458
  DBUG_RETURN(ptr);
}


unknown's avatar
unknown committed
6459 6460
/**
  Nest last join operation.
6461 6462 6463

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

unknown's avatar
unknown committed
6464
  @param thd         current thread
6465

unknown's avatar
unknown committed
6466
  @retval
6467
    0  Error
unknown's avatar
unknown committed
6468 6469
  @retval
    \#  Pointer to TABLE_LIST element created for the new nested join
6470 6471 6472 6473 6474 6475
*/

TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
{
  TABLE_LIST *ptr;
  NESTED_JOIN *nested_join;
6476
  List<TABLE_LIST> *embedded_list;
6477
  DBUG_ENTER("nest_last_join");
6478

6479 6480
  if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
                                       sizeof(NESTED_JOIN))))
6481
    DBUG_RETURN(0);
6482
  nested_join= ptr->nested_join=
6483
    ((NESTED_JOIN*) ((uchar*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
6484

6485 6486
  ptr->embedding= embedding;
  ptr->join_list= join_list;
6487
  ptr->alias= (char*) "(nest_last_join)";
6488
  embedded_list= &nested_join->join_list;
6489
  embedded_list->empty();
6490 6491

  for (uint i=0; i < 2; i++)
6492 6493
  {
    TABLE_LIST *table= join_list->pop();
6494 6495
    if (!table)
      DBUG_RETURN(NULL);
6496 6497 6498
    table->join_list= embedded_list;
    table->embedding= ptr;
    embedded_list->push_back(table);
unknown's avatar
unknown committed
6499 6500 6501 6502 6503 6504 6505
    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
6506 6507
      if (prev_join_using)
        ptr->join_using_fields= prev_join_using;
unknown's avatar
unknown committed
6508
    }
6509 6510 6511 6512 6513 6514 6515
  }
  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
6516 6517
/**
  Add a table to the current join list.
6518 6519 6520 6521 6522 6523

    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
6524 6525 6526
  @param table       the table to add

  @return
6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539
    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
6540 6541
/**
  Convert a right join into equivalent left join.
6542 6543

    The function takes the current join list t[0],t[1] ... and
6544 6545 6546 6547 6548 6549
    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
6550
  @verbatim
6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561
    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
6562
   @endverbatim
6563

unknown's avatar
unknown committed
6564
  @param thd         current thread
6565

unknown's avatar
unknown committed
6566 6567 6568
  @return
    - Pointer to the table representing the inner table, if success
    - 0, otherwise
6569 6570
*/

6571
TABLE_LIST *st_select_lex::convert_right_join()
6572 6573
{
  TABLE_LIST *tab2= join_list->pop();
6574
  TABLE_LIST *tab1= join_list->pop();
6575 6576 6577 6578 6579 6580 6581 6582 6583
  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
6584 6585
/**
  Set lock for all tables in current select level.
unknown's avatar
unknown committed
6586

unknown's avatar
unknown committed
6587
  @param lock_type			Lock to set for tables
unknown's avatar
unknown committed
6588

unknown's avatar
unknown committed
6589
  @note
unknown's avatar
unknown committed
6590 6591 6592 6593 6594
    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
6595
void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
unknown's avatar
unknown committed
6596 6597 6598 6599 6600
{
  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));
6601
  for (TABLE_LIST *tables= table_list.first;
unknown's avatar
VIEW  
unknown committed
6602 6603
       tables;
       tables= tables->next_local)
unknown's avatar
unknown committed
6604 6605 6606
  {
    tables->lock_type= lock_type;
    tables->updating=  for_update;
6607 6608
    tables->mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_WRITE) ?
                                 MDL_SHARED_WRITE : MDL_SHARED_READ);
unknown's avatar
unknown committed
6609 6610 6611 6612
  }
  DBUG_VOID_RETURN;
}

unknown's avatar
unknown committed
6613

unknown's avatar
unknown committed
6614 6615
/**
  Create a fake SELECT_LEX for a unit.
unknown's avatar
unknown committed
6616 6617 6618 6619

    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
6620
    @verbatim
unknown's avatar
unknown committed
6621
    (SELECT ... ORDER BY order_list [LIMIT n]) ORDER BY ... 
unknown's avatar
unknown committed
6622
    @endvarbatim
unknown's avatar
unknown committed
6623
    or of the form
unknown's avatar
unknown committed
6624
    @varbatim
unknown's avatar
unknown committed
6625
    (SELECT ... ORDER BY LIMIT n) ORDER BY ...
unknown's avatar
unknown committed
6626
    @endvarbatim
unknown's avatar
unknown committed
6627
  
unknown's avatar
unknown committed
6628 6629 6630
  @param thd_arg		   thread handle

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

unknown's avatar
unknown committed
6634
  @retval
unknown's avatar
unknown committed
6635
    1     on failure to create the object
unknown's avatar
unknown committed
6636
  @retval
unknown's avatar
unknown committed
6637 6638 6639
    0     on success
*/

unknown's avatar
unknown committed
6640
bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg)
unknown's avatar
unknown committed
6641 6642 6643 6644
{
  SELECT_LEX *first_sl= first_select();
  DBUG_ENTER("add_fake_select_lex");
  DBUG_ASSERT(!fake_select_lex);
unknown's avatar
unknown committed
6645

unknown's avatar
unknown committed
6646
  if (!(fake_select_lex= new (thd_arg->mem_root) SELECT_LEX()))
unknown's avatar
unknown committed
6647 6648 6649 6650
      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
6651
  fake_select_lex->parent_lex= thd_arg->lex; /* Used in init_query. */
unknown's avatar
unknown committed
6652 6653
  fake_select_lex->make_empty_select();
  fake_select_lex->linkage= GLOBAL_OPTIONS_TYPE;
6654 6655
  fake_select_lex->select_limit= 0;

unknown's avatar
unknown committed
6656
  fake_select_lex->context.outer_context=first_sl->context.outer_context;
6657 6658 6659
  /* 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
6660

6661
  if (!is_union())
unknown's avatar
unknown committed
6662 6663 6664 6665 6666 6667 6668 6669 6670
  {
    /* 
      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
6671
    thd_arg->lex->current_select= fake_select_lex;
unknown's avatar
unknown committed
6672
  }
unknown's avatar
unknown committed
6673
  thd_arg->lex->pop_context();
unknown's avatar
unknown committed
6674 6675 6676
  DBUG_RETURN(0);
}

6677

unknown's avatar
unknown committed
6678
/**
6679 6680
  Push a new name resolution context for a JOIN ... ON clause to the
  context stack of a query block.
unknown's avatar
unknown committed
6681 6682

    Create a new name resolution context for a JOIN ... ON clause,
6683 6684 6685
    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
6686

unknown's avatar
unknown committed
6687 6688 6689 6690 6691
  @param thd       pointer to current thread
  @param left_op   left  operand of the JOIN
  @param right_op  rigth operand of the JOIN

  @retval
6692
    FALSE  if all is OK
unknown's avatar
unknown committed
6693
  @retval
6694
    TRUE   if a memory allocation error occured
unknown's avatar
unknown committed
6695 6696
*/

6697 6698 6699
bool
push_new_name_resolution_context(THD *thd,
                                 TABLE_LIST *left_op, TABLE_LIST *right_op)
unknown's avatar
unknown committed
6700 6701
{
  Name_resolution_context *on_context;
6702
  if (!(on_context= new (thd->mem_root) Name_resolution_context))
6703
    return TRUE;
unknown's avatar
unknown committed
6704 6705 6706 6707 6708
  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();
6709
  return thd->lex->push_context(on_context);
unknown's avatar
unknown committed
6710 6711 6712
}


unknown's avatar
unknown committed
6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734
/**
  Fix condition which contains only field (f turns to  f <> 0 )

  @param cond            The condition to fix

  @return fixed condition
*/

Item *normalize_cond(Item *cond)
{
  if (cond)
  {
    Item::Type type= cond->type();
    if (type == Item::FIELD_ITEM || type == Item::REF_ITEM)
    {
      cond= new Item_func_ne(cond, new Item_int(0));
    }
  }
  return cond;
}


unknown's avatar
unknown committed
6735
/**
unknown's avatar
unknown committed
6736 6737 6738 6739
  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
6740 6741
  @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
6742

unknown's avatar
unknown committed
6743
  @retval
unknown's avatar
unknown committed
6744
    FALSE  if there was some error
unknown's avatar
unknown committed
6745
  @retval
unknown's avatar
unknown committed
6746 6747 6748 6749
    TRUE   if all is OK
*/

void add_join_on(TABLE_LIST *b, Item *expr)
unknown's avatar
unknown committed
6750
{
6751
  if (expr)
6752
  {
unknown's avatar
unknown committed
6753
    expr= normalize_cond(expr);
6754
    if (!b->on_expr)
unknown's avatar
unknown committed
6755
      b->on_expr= expr;
6756 6757
    else
    {
unknown's avatar
unknown committed
6758 6759 6760 6761 6762 6763
      /*
        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);
6764 6765
    }
    b->on_expr->top_level_item();
6766
  }
unknown's avatar
unknown committed
6767 6768 6769
}


unknown's avatar
unknown committed
6770
/**
unknown's avatar
unknown committed
6771 6772
  Mark that there is a NATURAL JOIN or JOIN ... USING between two
  tables.
6773

unknown's avatar
unknown committed
6774 6775 6776 6777 6778 6779 6780 6781 6782 6783
    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
6784
  @verbatim
6785 6786 6787
    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
6788

unknown's avatar
unknown committed
6789 6790 6791
    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
6792

unknown's avatar
unknown committed
6793 6794 6795
    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
6796 6797 6798 6799 6800
   @endverbatim

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

unknown's avatar
unknown committed
6803 6804
void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
                      SELECT_LEX *lex)
unknown's avatar
unknown committed
6805
{
unknown's avatar
unknown committed
6806
  b->natural_join= a;
unknown's avatar
unknown committed
6807
  lex->prev_join_using= using_fields;
unknown's avatar
unknown committed
6808 6809
}

unknown's avatar
unknown committed
6810

unknown's avatar
unknown committed
6811
/**
6812
  Find a thread by id and return it, locking it LOCK_thd_data
6813

6814
  @param id  Identifier of the thread we're looking for
6815

6816 6817
  @return NULL    - not found
          pointer - thread found, and its LOCK_thd_data is locked.
6818 6819
*/

6820
THD *find_thread_by_id(ulong id)
unknown's avatar
unknown committed
6821 6822
{
  THD *tmp;
Marc Alff's avatar
Marc Alff committed
6823
  mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
6824
  I_List_iterator<THD> it(threads);
unknown's avatar
unknown committed
6825 6826
  while ((tmp=it++))
  {
unknown's avatar
unknown committed
6827 6828
    if (tmp->command == COM_DAEMON)
      continue;
unknown's avatar
unknown committed
6829 6830
    if (tmp->thread_id == id)
    {
Marc Alff's avatar
Marc Alff committed
6831
      mysql_mutex_lock(&tmp->LOCK_thd_data);    // Lock from delete
6832
      break;
unknown's avatar
unknown committed
6833 6834
    }
  }
Marc Alff's avatar
Marc Alff committed
6835
  mysql_mutex_unlock(&LOCK_thread_count);
6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858
  return tmp;
}


/**
  kill on thread.

  @param thd			Thread class
  @param id			Thread id
  @param only_kill_query        Should it kill the query or the connection

  @note
    This is written such that we have a short lock on LOCK_thread_count
*/

uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal)
{
  THD *tmp;
  uint error=ER_NO_SUCH_THREAD;
  DBUG_ENTER("kill_one_thread");
  DBUG_PRINT("enter", ("id: %lu  signal: %u", id, (uint) kill_signal));

  if ((tmp= find_thread_by_id(id)))
6859
  {
6860 6861 6862 6863
    /*
      If we're SUPER, we can KILL anything, including system-threads.
      No further checks.

6864 6865 6866
      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).
6867

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

6873
      If user of both killer and killee are non-NULL, proceed with
6874
      slayage if both are string-equal.
6875 6876 6877 6878

      It's ok to also kill DELAYED threads with KILL_CONNECTION instead of
      KILL_SYSTEM_THREAD; The difference is that KILL_CONNECTION may be
      faster and do a harder kill than KILL_SYSTEM_THREAD;
6879 6880
    */

6881
    if ((thd->security_ctx->master_access & SUPER_ACL) ||
6882
        thd->security_ctx->user_matches(tmp->security_ctx))
6883
    {
6884
      tmp->awake(kill_signal);
6885 6886 6887 6888
      error=0;
    }
    else
      error=ER_KILL_DENIED_ERROR;
Marc Alff's avatar
Marc Alff committed
6889
    mysql_mutex_unlock(&tmp->LOCK_thd_data);
6890
  }
6891 6892 6893 6894
  DBUG_PRINT("exit", ("%d", error));
  DBUG_RETURN(error);
}

6895

6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924
/**
  kill all threads from one user

  @param thd			Thread class
  @param user_name		User name for threads we should kill
  @param only_kill_query        Should it kill the query or the connection

  @note
    This is written such that we have a short lock on LOCK_thread_count

    If we can't kill all threads because of security issues, no threads
    are killed.
*/

static uint kill_threads_for_user(THD *thd, LEX_USER *user,
                                  killed_state kill_signal, ha_rows *rows)
{
  THD *tmp;
  List<THD> threads_to_kill;
  DBUG_ENTER("kill_threads_for_user");

  *rows= 0;

  if (thd->is_fatal_error)                       // If we run out of memory
    DBUG_RETURN(ER_OUT_OF_RESOURCES);

  DBUG_PRINT("enter", ("user: %s  signal: %u", user->user.str,
                       (uint) kill_signal));

Sergei Golubchik's avatar
Sergei Golubchik committed
6925
  mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942
  I_List_iterator<THD> it(threads);
  while ((tmp=it++))
  {
    if (tmp->command == COM_DAEMON)
      continue;
    /*
      Check that hostname (if given) and user name matches.

      host.str[0] == '%' means that host name was not given. See sql_yacc.yy
    */
    if (((user->host.str[0] == '%' && !user->host.str[1]) ||
         !strcmp(tmp->security_ctx->host, user->host.str)) &&
        !strcmp(tmp->security_ctx->user, user->user.str))
    {
      if (!(thd->security_ctx->master_access & SUPER_ACL) &&
          !thd->security_ctx->user_matches(tmp->security_ctx))
      {
Sergei Golubchik's avatar
Sergei Golubchik committed
6943
        mysql_mutex_unlock(&LOCK_thread_count);
6944 6945
        DBUG_RETURN(ER_KILL_DENIED_ERROR);
      }
6946
      if (!threads_to_kill.push_back(tmp, thd->mem_root))
Sergei Golubchik's avatar
Sergei Golubchik committed
6947
        mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
6948 6949
    }
  }
Sergei Golubchik's avatar
Sergei Golubchik committed
6950
  mysql_mutex_unlock(&LOCK_thread_count);
6951 6952 6953
  if (!threads_to_kill.is_empty())
  {
    List_iterator_fast<THD> it(threads_to_kill);
6954 6955 6956
    THD *next_ptr;
    THD *ptr= it++;
    do
6957 6958
    {
      ptr->awake(kill_signal);
6959 6960 6961 6962 6963 6964 6965 6966 6967
      /*
        Careful here: The list nodes are allocated on the memroots of the
        THDs to be awakened.
        But those THDs may be terminated and deleted as soon as we release
        LOCK_thd_data, which will make the list nodes invalid.
        Since the operation "it++" dereferences the "next" pointer of the
        previous list node, we need to do this while holding LOCK_thd_data.
      */
      next_ptr= it++;
Sergei Golubchik's avatar
Sergei Golubchik committed
6968
      mysql_mutex_unlock(&ptr->LOCK_thd_data);
6969
      (*rows)++;
6970
    } while ((ptr= next_ptr));
6971 6972 6973 6974 6975
  }
  DBUG_RETURN(0);
}


6976 6977 6978 6979 6980 6981 6982 6983 6984 6985
/*
  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
*/

6986
static
6987
void sql_kill(THD *thd, ulong id, killed_state state)
6988 6989
{
  uint error;
6990
  if (!(error= kill_one_thread(thd, id, state)))
6991
  {
6992
    if ((!thd->killed))
6993
      my_ok(thd);
6994 6995
    else
      my_error(killed_errno(thd->killed), MYF(0), id);
6996
  }
unknown's avatar
unknown committed
6997
  else
unknown's avatar
unknown committed
6998
    my_error(error, MYF(0), id);
unknown's avatar
unknown committed
6999 7000
}

unknown's avatar
unknown committed
7001

Sergei Golubchik's avatar
Sergei Golubchik committed
7002
static
7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019
void sql_kill_user(THD *thd, LEX_USER *user, killed_state state)
{
  uint error;
  ha_rows rows;
  if (!(error= kill_threads_for_user(thd, user, state, &rows)))
    my_ok(thd, rows);
  else
  {
    /*
      This is probably ER_OUT_OF_RESOURCES, but in the future we may
      want to write the name of the user we tried to kill
    */
    my_error(error, MYF(0), user->host.str, user->user.str);
  }
}


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

unknown's avatar
unknown committed
7022 7023
bool append_file_to_dir(THD *thd, const char **filename_ptr,
                        const char *table_name)
7024
{
7025
  char buff[FN_REFLEN],*ptr, *end;
7026 7027 7028 7029 7030 7031 7032
  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))
  {
7033
    my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr);
7034 7035 7036 7037
    return 1;
  }
  /* Fix is using unix filename format on dos */
  strmov(buff,*filename_ptr);
7038
  end=convert_dirname(buff, *filename_ptr, NullS);
7039
  if (!(ptr= (char*) thd->alloc((size_t) (end-buff) + strlen(table_name)+1)))
7040 7041
    return 1;					// End of memory
  *filename_ptr=ptr;
7042
  strxmov(ptr,buff,table_name,NullS);
7043 7044
  return 0;
}
7045

7046

unknown's avatar
unknown committed
7047 7048
/**
  Check if the select is a simple select (not an union).
7049

unknown's avatar
unknown committed
7050
  @retval
7051
    0	ok
unknown's avatar
unknown committed
7052
  @retval
7053 7054 7055 7056 7057 7058
    1	error	; In this case the error messege is sent to the client
*/

bool check_simple_select()
{
  THD *thd= current_thd;
7059 7060
  LEX *lex= thd->lex;
  if (lex->current_select != &lex->select_lex)
7061 7062
  {
    char command[80];
7063
    Lex_input_stream *lip= & thd->m_parser_state->m_lip;
7064 7065
    strmake(command, lip->yylval->symbol.str,
	    min(lip->yylval->symbol.length, sizeof(command)-1));
7066
    my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command);
7067 7068 7069 7070
    return 1;
  }
  return 0;
}
unknown's avatar
unknown committed
7071

unknown's avatar
unknown committed
7072

unknown's avatar
unknown committed
7073
Comp_creator *comp_eq_creator(bool invert)
unknown's avatar
unknown committed
7074
{
unknown's avatar
unknown committed
7075
  return invert?(Comp_creator *)&ne_creator:(Comp_creator *)&eq_creator;
unknown's avatar
unknown committed
7076 7077
}

unknown's avatar
unknown committed
7078

unknown's avatar
unknown committed
7079
Comp_creator *comp_ge_creator(bool invert)
unknown's avatar
unknown committed
7080
{
unknown's avatar
unknown committed
7081
  return invert?(Comp_creator *)&lt_creator:(Comp_creator *)&ge_creator;
unknown's avatar
unknown committed
7082 7083
}

unknown's avatar
unknown committed
7084

unknown's avatar
unknown committed
7085
Comp_creator *comp_gt_creator(bool invert)
unknown's avatar
unknown committed
7086
{
unknown's avatar
unknown committed
7087
  return invert?(Comp_creator *)&le_creator:(Comp_creator *)&gt_creator;
unknown's avatar
unknown committed
7088 7089
}

unknown's avatar
unknown committed
7090

unknown's avatar
unknown committed
7091
Comp_creator *comp_le_creator(bool invert)
unknown's avatar
unknown committed
7092
{
unknown's avatar
unknown committed
7093
  return invert?(Comp_creator *)&gt_creator:(Comp_creator *)&le_creator;
unknown's avatar
unknown committed
7094 7095
}

unknown's avatar
unknown committed
7096

unknown's avatar
unknown committed
7097
Comp_creator *comp_lt_creator(bool invert)
unknown's avatar
unknown committed
7098
{
unknown's avatar
unknown committed
7099
  return invert?(Comp_creator *)&ge_creator:(Comp_creator *)&lt_creator;
unknown's avatar
unknown committed
7100 7101
}

unknown's avatar
unknown committed
7102

unknown's avatar
unknown committed
7103
Comp_creator *comp_ne_creator(bool invert)
unknown's avatar
unknown committed
7104
{
unknown's avatar
unknown committed
7105
  return invert?(Comp_creator *)&eq_creator:(Comp_creator *)&ne_creator;
unknown's avatar
unknown committed
7106
}
unknown's avatar
unknown committed
7107 7108


unknown's avatar
unknown committed
7109 7110
/**
  Construct ALL/ANY/SOME subquery Item.
unknown's avatar
unknown committed
7111

unknown's avatar
unknown committed
7112 7113 7114 7115
  @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
7116

unknown's avatar
unknown committed
7117
  @return
unknown's avatar
unknown committed
7118 7119 7120 7121 7122 7123 7124
    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
7125
  if ((cmp == &comp_eq_creator) && !all)       //  = ANY <=> IN
unknown's avatar
unknown committed
7126
    return new Item_in_subselect(left_expr, select_lex);
unknown's avatar
unknown committed
7127 7128

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

  Item_allany_subselect *it=
7132
    new Item_allany_subselect(left_expr, cmp, select_lex, all);
unknown's avatar
unknown committed
7133
  if (all)
7134
    return it->upper_item= new Item_func_not_all(it);	/* ALL */
unknown's avatar
unknown committed
7135

7136
  return it->upper_item= new Item_func_nop_all(it);      /* ANY/SOME */
unknown's avatar
unknown committed
7137
}
7138 7139


unknown's avatar
unknown committed
7140 7141
/**
  Multi update query pre-check.
7142

unknown's avatar
unknown committed
7143 7144
  @param thd		Thread handler
  @param tables	Global/local table list (have to be the same)
7145

unknown's avatar
unknown committed
7146
  @retval
unknown's avatar
unknown committed
7147
    FALSE OK
unknown's avatar
unknown committed
7148
  @retval
unknown's avatar
unknown committed
7149
    TRUE  Error
7150
*/
unknown's avatar
unknown committed
7151

unknown's avatar
unknown committed
7152
bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
7153 7154 7155 7156 7157
{
  const char *msg= 0;
  TABLE_LIST *table;
  LEX *lex= thd->lex;
  SELECT_LEX *select_lex= &lex->select_lex;
unknown's avatar
VIEW  
unknown committed
7158
  DBUG_ENTER("multi_update_precheck");
7159 7160 7161

  if (select_lex->item_list.elements != lex->value_list.elements)
  {
7162
    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
unknown's avatar
unknown committed
7163
    DBUG_RETURN(TRUE);
7164 7165 7166 7167 7168
  }
  /*
    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
7169
  for (table= tables; table; table= table->next_local)
7170
  {
7171 7172 7173
    if (table->derived)
      table->grant.privilege= SELECT_ACL;
    else if ((check_access(thd, UPDATE_ACL, table->db,
Marc Alff's avatar
Marc Alff committed
7174 7175 7176
                           &table->grant.privilege,
                           &table->grant.m_internal,
                           0, 1) ||
7177
              check_grant(thd, UPDATE_ACL, table, FALSE, 1, TRUE)) &&
unknown's avatar
unknown committed
7178
             (check_access(thd, SELECT_ACL, table->db,
Marc Alff's avatar
Marc Alff committed
7179 7180 7181
                           &table->grant.privilege,
                           &table->grant.m_internal,
                           0, 0) ||
7182
              check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE)))
unknown's avatar
unknown committed
7183
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7184

unknown's avatar
VIEW  
unknown committed
7185
    table->table_in_first_from_clause= 1;
7186
  }
unknown's avatar
unknown committed
7187 7188 7189
  /*
    Is there tables of subqueries?
  */
7190
  if (&lex->select_lex != lex->all_selects_list)
7191
  {
7192
    DBUG_PRINT("info",("Checking sub query list"));
unknown's avatar
VIEW  
unknown committed
7193
    for (table= tables; table; table= table->next_global)
7194
    {
7195
      if (!table->table_in_first_from_clause)
7196 7197
      {
	if (check_access(thd, SELECT_ACL, table->db,
Marc Alff's avatar
Marc Alff committed
7198 7199 7200
                         &table->grant.privilege,
                         &table->grant.m_internal,
                         0, 0) ||
7201
	    check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE))
unknown's avatar
unknown committed
7202
	  DBUG_RETURN(TRUE);
7203 7204 7205 7206 7207 7208
      }
    }
  }

  if (select_lex->order_list.elements)
    msg= "ORDER BY";
7209
  else if (select_lex->select_limit)
7210 7211 7212 7213
    msg= "LIMIT";
  if (msg)
  {
    my_error(ER_WRONG_USAGE, MYF(0), "UPDATE", msg);
unknown's avatar
unknown committed
7214
    DBUG_RETURN(TRUE);
7215
  }
unknown's avatar
unknown committed
7216
  DBUG_RETURN(FALSE);
7217 7218
}

unknown's avatar
unknown committed
7219 7220
/**
  Multi delete query pre-check.
7221

unknown's avatar
unknown committed
7222 7223
  @param thd			Thread handler
  @param tables		Global/local table list
7224

unknown's avatar
unknown committed
7225
  @retval
unknown's avatar
unknown committed
7226
    FALSE OK
unknown's avatar
unknown committed
7227
  @retval
unknown's avatar
unknown committed
7228
    TRUE  error
7229
*/
unknown's avatar
unknown committed
7230

7231
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
7232 7233
{
  SELECT_LEX *select_lex= &thd->lex->select_lex;
7234
  TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
7235
  TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
unknown's avatar
VIEW  
unknown committed
7236
  DBUG_ENTER("multi_delete_precheck");
unknown's avatar
unknown committed
7237

7238 7239
  /* sql_yacc guarantees that tables and aux_tables are not zero */
  DBUG_ASSERT(aux_tables != 0);
7240
  if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
7241 7242 7243 7244 7245 7246 7247 7248
    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;
7249
  if (check_table_access(thd, DELETE_ACL, aux_tables, FALSE, UINT_MAX, FALSE))
7250 7251
  {
    thd->lex->query_tables_own_last= save_query_tables_own_last;
unknown's avatar
unknown committed
7252
    DBUG_RETURN(TRUE);
7253 7254 7255
  }
  thd->lex->query_tables_own_last= save_query_tables_own_last;

7256
  if ((thd->variables.option_bits & OPTION_SAFE_UPDATES) && !select_lex->where)
7257
  {
unknown's avatar
unknown committed
7258 7259
    my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
               ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
unknown's avatar
unknown committed
7260
    DBUG_RETURN(TRUE);
7261
  }
7262 7263 7264 7265
  DBUG_RETURN(FALSE);
}


7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 7316 7317 7318 7319 7320 7321 7322
/*
  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
7323
/**
7324 7325 7326
  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
7327
  @param lex   pointer to LEX representing multi-delete
7328

unknown's avatar
unknown committed
7329 7330 7331 7332
  @retval
    FALSE   success
  @retval
    TRUE    error
7333 7334 7335 7336
*/

bool multi_delete_set_locks_and_link_aux_tables(LEX *lex)
{
7337
  TABLE_LIST *tables= lex->select_lex.table_list.first;
7338 7339 7340 7341 7342
  TABLE_LIST *target_tbl;
  DBUG_ENTER("multi_delete_set_locks_and_link_aux_tables");

  lex->table_count= 0;

7343
  for (target_tbl= lex->auxiliary_table_list.first;
7344
       target_tbl; target_tbl= target_tbl->next_local)
7345
  {
7346
    lex->table_count++;
7347
    /* All tables in aux_tables must be found in FROM PART */
7348
    TABLE_LIST *walk= multi_delete_table_match(lex, target_tbl, tables);
7349
    if (!walk)
unknown's avatar
unknown committed
7350
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7351 7352 7353 7354 7355
    if (!walk->derived)
    {
      target_tbl->table_name= walk->table_name;
      target_tbl->table_name_length= walk->table_name_length;
    }
unknown's avatar
unknown committed
7356
    walk->updating= target_tbl->updating;
unknown's avatar
unknown committed
7357
    walk->lock_type= target_tbl->lock_type;
7358 7359 7360
    /* 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
7361
    target_tbl->correspondent_table= walk;	// Remember corresponding table
7362
  }
unknown's avatar
unknown committed
7363
  DBUG_RETURN(FALSE);
7364 7365 7366
}


unknown's avatar
unknown committed
7367 7368
/**
  simple UPDATE query pre-check.
unknown's avatar
unknown committed
7369

unknown's avatar
unknown committed
7370 7371
  @param thd		Thread handler
  @param tables	Global table list
unknown's avatar
unknown committed
7372

unknown's avatar
unknown committed
7373
  @retval
unknown's avatar
unknown committed
7374
    FALSE OK
unknown's avatar
unknown committed
7375
  @retval
unknown's avatar
unknown committed
7376
    TRUE  Error
unknown's avatar
unknown committed
7377
*/
unknown's avatar
unknown committed
7378

unknown's avatar
unknown committed
7379
bool update_precheck(THD *thd, TABLE_LIST *tables)
unknown's avatar
unknown committed
7380 7381 7382 7383
{
  DBUG_ENTER("update_precheck");
  if (thd->lex->select_lex.item_list.elements != thd->lex->value_list.elements)
  {
unknown's avatar
unknown committed
7384
    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
unknown's avatar
unknown committed
7385
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7386
  }
unknown's avatar
unknown committed
7387
  DBUG_RETURN(check_one_table_access(thd, UPDATE_ACL, tables));
unknown's avatar
unknown committed
7388 7389 7390
}


unknown's avatar
unknown committed
7391 7392
/**
  simple DELETE query pre-check.
unknown's avatar
unknown committed
7393

unknown's avatar
unknown committed
7394 7395
  @param thd		Thread handler
  @param tables	Global table list
unknown's avatar
unknown committed
7396

unknown's avatar
unknown committed
7397
  @retval
unknown's avatar
unknown committed
7398
    FALSE  OK
unknown's avatar
unknown committed
7399
  @retval
unknown's avatar
unknown committed
7400
    TRUE   error
unknown's avatar
unknown committed
7401
*/
unknown's avatar
unknown committed
7402

unknown's avatar
unknown committed
7403
bool delete_precheck(THD *thd, TABLE_LIST *tables)
unknown's avatar
unknown committed
7404 7405 7406
{
  DBUG_ENTER("delete_precheck");
  if (check_one_table_access(thd, DELETE_ACL, tables))
unknown's avatar
unknown committed
7407
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7408
  /* Set privilege for the WHERE clause */
unknown's avatar
unknown committed
7409
  tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
unknown's avatar
unknown committed
7410
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
7411 7412 7413
}


unknown's avatar
unknown committed
7414 7415
/**
  simple INSERT query pre-check.
unknown's avatar
unknown committed
7416

unknown's avatar
unknown committed
7417 7418
  @param thd		Thread handler
  @param tables	Global table list
unknown's avatar
unknown committed
7419

unknown's avatar
unknown committed
7420
  @retval
unknown's avatar
unknown committed
7421
    FALSE  OK
unknown's avatar
unknown committed
7422
  @retval
unknown's avatar
unknown committed
7423
    TRUE   error
unknown's avatar
unknown committed
7424
*/
unknown's avatar
unknown committed
7425

unknown's avatar
merge  
unknown committed
7426
bool insert_precheck(THD *thd, TABLE_LIST *tables)
unknown's avatar
unknown committed
7427 7428 7429 7430
{
  LEX *lex= thd->lex;
  DBUG_ENTER("insert_precheck");

unknown's avatar
unknown committed
7431 7432 7433 7434
  /*
    Check that we have modify privileges for the first table and
    select privileges for the rest
  */
unknown's avatar
unknown committed
7435 7436 7437
  ulong privilege= (INSERT_ACL |
                    (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
                    (lex->value_list.elements ? UPDATE_ACL : 0));
unknown's avatar
unknown committed
7438 7439

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

unknown's avatar
unknown committed
7442
  if (lex->update_list.elements != lex->value_list.elements)
unknown's avatar
unknown committed
7443
  {
unknown's avatar
unknown committed
7444
    my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
unknown's avatar
unknown committed
7445
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
7446
  }
unknown's avatar
unknown committed
7447
  DBUG_RETURN(FALSE);
7448
}
unknown's avatar
unknown committed
7449 7450


7451 7452 7453 7454 7455 7456 7457 7458 7459 7460 7461
/**
   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;
7462
  else
7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476 7477 7478
    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
7479 7480
/**
  CREATE TABLE query pre-check.
unknown's avatar
unknown committed
7481

unknown's avatar
unknown committed
7482 7483 7484
  @param thd			Thread handler
  @param tables		Global table list
  @param create_table	        Table which will be created
unknown's avatar
unknown committed
7485

unknown's avatar
unknown committed
7486
  @retval
unknown's avatar
unknown committed
7487
    FALSE   OK
unknown's avatar
unknown committed
7488
  @retval
unknown's avatar
unknown committed
7489
    TRUE   Error
unknown's avatar
unknown committed
7490
*/
unknown's avatar
unknown committed
7491

unknown's avatar
unknown committed
7492 7493
bool create_table_precheck(THD *thd, TABLE_LIST *tables,
                           TABLE_LIST *create_table)
unknown's avatar
unknown committed
7494 7495
{
  LEX *lex= thd->lex;
7496 7497
  SELECT_LEX *select_lex= &lex->select_lex;
  ulong want_priv;
unknown's avatar
merge  
unknown committed
7498
  bool error= TRUE;                                 // Error message is given
unknown's avatar
unknown committed
7499
  DBUG_ENTER("create_table_precheck");
7500

7501 7502 7503 7504 7505
  /*
    Require CREATE [TEMPORARY] privilege on new table; for
    CREATE TABLE ... SELECT, also require INSERT.
  */

7506
  want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
7507 7508 7509
              CREATE_TMP_ACL : CREATE_ACL) |
             (select_lex->item_list.elements ? INSERT_ACL : 0);

unknown's avatar
unknown committed
7510
  if (check_access(thd, want_priv, create_table->db,
Marc Alff's avatar
Marc Alff committed
7511 7512
                   &create_table->grant.privilege,
                   &create_table->grant.m_internal,
7513
                   0, 0))
7514
    goto err;
7515 7516 7517 7518 7519 7520

  /* If it is a merge table, check privileges for merge children. */
  if (lex->create_info.merge_list.first &&
      check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
                         lex->create_info.merge_list.first,
                         FALSE, UINT_MAX, FALSE))
7521
    goto err;
7522

7523
  if (want_priv != CREATE_TMP_ACL &&
7524
      check_grant(thd, want_priv, create_table, FALSE, 1, FALSE))
7525 7526 7527 7528 7529
    goto err;

  if (select_lex->item_list.elements)
  {
    /* Check permissions for used tables in CREATE TABLE ... SELECT */
7530 7531
    if (tables && check_table_access(thd, SELECT_ACL, tables, FALSE,
                                     UINT_MAX, FALSE))
7532 7533
      goto err;
  }
unknown's avatar
unknown committed
7534 7535
  else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
  {
7536
    if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
unknown's avatar
unknown committed
7537 7538
      goto err;
  }
unknown's avatar
merge  
unknown committed
7539
  error= FALSE;
7540 7541 7542

err:
  DBUG_RETURN(error);
unknown's avatar
unknown committed
7543
}
unknown's avatar
unknown committed
7544 7545


unknown's avatar
unknown committed
7546 7547
/**
  negate given expression.
unknown's avatar
unknown committed
7548

unknown's avatar
unknown committed
7549 7550
  @param thd  thread handler
  @param expr expression for negation
unknown's avatar
unknown committed
7551

unknown's avatar
unknown committed
7552
  @return
unknown's avatar
unknown committed
7553 7554 7555 7556 7557 7558 7559 7560 7561 7562 7563 7564 7565 7566 7567 7568 7569 7570 7571 7572 7573 7574 7575 7576 7577
    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);
}
7578

unknown's avatar
unknown committed
7579 7580 7581
/**
  Set the specified definer to the default value, which is the
  current user in the thread.
7582
 
unknown's avatar
unknown committed
7583 7584
  @param[in]  thd       thread handler
  @param[out] definer   definer
7585 7586
*/
 
7587
void get_default_definer(THD *thd, LEX_USER *definer)
7588 7589 7590 7591 7592 7593 7594 7595
{
  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);
7596

7597 7598 7599
  definer->password= null_lex_str;
  definer->plugin= empty_lex_str;
  definer->auth= empty_lex_str;
7600 7601
}

7602

unknown's avatar
unknown committed
7603
/**
7604
  Create default definer for the specified THD.
7605

unknown's avatar
unknown committed
7606
  @param[in] thd         thread handler
7607

unknown's avatar
unknown committed
7608 7609
  @return
    - On success, return a valid pointer to the created and initialized
7610
    LEX_USER, which contains definer information.
unknown's avatar
unknown committed
7611
    - On error, return 0.
7612 7613 7614 7615 7616 7617 7618 7619 7620
*/

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

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

7621
  thd->get_definer(definer);
7622 7623 7624 7625 7626

  return definer;
}


unknown's avatar
unknown committed
7627
/**
7628
  Create definer with the given user and host names.
7629

unknown's avatar
unknown committed
7630 7631 7632
  @param[in] thd          thread handler
  @param[in] user_name    user name
  @param[in] host_name    host name
7633

unknown's avatar
unknown committed
7634 7635
  @return
    - On success, return a valid pointer to the created and initialized
7636
    LEX_USER, which contains definer information.
unknown's avatar
unknown committed
7637
    - On error, return 0.
7638 7639
*/

7640
LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
7641
{
7642 7643 7644 7645
  LEX_USER *definer;

  /* Create and initialize. */

unknown's avatar
unknown committed
7646
  if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
7647 7648 7649 7650
    return 0;

  definer->user= *user_name;
  definer->host= *host_name;
7651 7652
  definer->password.str= NULL;
  definer->password.length= 0;
7653 7654

  return definer;
7655
}
7656 7657


unknown's avatar
unknown committed
7658
/**
7659 7660
  Retuns information about user or current user.

unknown's avatar
unknown committed
7661 7662
  @param[in] thd          thread handler
  @param[in] user         user
7663

unknown's avatar
unknown committed
7664 7665
  @return
    - On success, return a valid pointer to initialized
7666
    LEX_USER, which contains user information.
unknown's avatar
unknown committed
7667
    - On error, return 0.
7668 7669 7670 7671 7672
*/

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

7675 7676
  return user;
}
7677 7678


unknown's avatar
unknown committed
7679
/**
7680
  Check that byte length of a string does not exceed some limit.
7681

unknown's avatar
unknown committed
7682 7683 7684
  @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
7685

unknown's avatar
unknown committed
7686
  @retval
7687
    FALSE   the passed string is not longer than max_length
unknown's avatar
unknown committed
7688
  @retval
7689
    TRUE    the passed string is longer than max_length
7690 7691 7692

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

7695 7696
bool check_string_byte_length(LEX_STRING *str, const char *err_msg,
                              uint max_byte_length)
7697
{
7698
  if (str->length <= max_byte_length)
unknown's avatar
unknown committed
7699
    return FALSE;
7700

7701 7702 7703 7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 7718 7719 7720 7721 7722 7723 7724 7725 7726 7727 7728 7729 7730 7731 7732
  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
7733

7734
  if (!no_error)
7735 7736 7737 7738
  {
    ErrConvString err(str->str, str->length, cs);
    my_error(ER_WRONG_STRING_LENGTH, MYF(0), err.ptr(), err_msg, max_char_length);
  }
7739 7740
  return TRUE;
}
7741

Sergei Golubchik's avatar
Sergei Golubchik committed
7742
C_MODE_START
7743

7744 7745
/*
  Check if path does not contain mysql data home directory
7746

7747 7748 7749 7750 7751 7752
  SYNOPSIS
    test_if_data_home_dir()
    dir                     directory

  RETURN VALUES
    0	ok
7753
    1	error ;  Given path contains data directory
7754 7755
*/

7756
int test_if_data_home_dir(const char *dir)
7757
{
7758
  char path[FN_REFLEN];
Alexey Botchkov's avatar
Alexey Botchkov committed
7759
  int dir_len;
7760 7761 7762 7763 7764
  DBUG_ENTER("test_if_data_home_dir");

  if (!dir)
    DBUG_RETURN(0);

Sergei Golubchik's avatar
Sergei Golubchik committed
7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777 7778 7779 7780
  /*
    data_file_name and index_file_name include the table name without
    extension. Mostly this does not refer to an existing file. When
    comparing data_file_name or index_file_name against the data
    directory, we try to resolve all symbolic links. On some systems,
    we use realpath(3) for the resolution. This returns ENOENT if the
    resolved path does not refer to an existing file. my_realpath()
    does then copy the requested path verbatim, without symlink
    resolution. Thereafter the comparison can fail even if the
    requested path is within the data directory. E.g. if symlinks to
    another file system are used. To make realpath(3) return the
    resolved path, we strip the table name and compare the directory
    path only. If the directory doesn't exist either, table creation
    will fail anyway.
  */

7781 7782
  (void) fn_format(path, dir, "", "",
                   (MY_RETURN_REAL_PATH|MY_RESOLVE_SYMLINKS));
7783 7784
  dir_len= strlen(path);
  if (mysql_unpacked_real_data_home_len<= dir_len)
7785
  {
7786 7787 7788 7789
    if (dir_len > mysql_unpacked_real_data_home_len &&
        path[mysql_unpacked_real_data_home_len] != FN_LIBCHAR)
      DBUG_RETURN(0);

7790 7791
    if (lower_case_file_system)
    {
7792 7793
      if (!my_strnncoll(default_charset_info, (const uchar*) path,
                        mysql_unpacked_real_data_home_len,
7794
                        (const uchar*) mysql_unpacked_real_data_home,
7795
                        mysql_unpacked_real_data_home_len))
7796 7797
      {
        DBUG_PRINT("error", ("Path is part of mysql_real_data_home"));
7798
        DBUG_RETURN(1);
7799
      }
7800
    }
7801 7802
    else if (!memcmp(path, mysql_unpacked_real_data_home,
                     mysql_unpacked_real_data_home_len))
7803 7804
    {
      DBUG_PRINT("error", ("Path is part of mysql_real_data_home"));
7805
      DBUG_RETURN(1);
7806
    }
7807 7808 7809 7810
  }
  DBUG_RETURN(0);
}

7811 7812
C_MODE_END

7813

Sergei Golubchik's avatar
Sergei Golubchik committed
7814 7815 7816 7817 7818 7819 7820 7821 7822 7823 7824 7825 7826 7827 7828 7829
int error_if_data_home_dir(const char *path, const char *what)
{
  size_t dirlen;
  char   dirpath[FN_REFLEN];
  if (path)
  {
    dirname_part(dirpath, path, &dirlen);
    if (test_if_data_home_dir(dirpath))
    {
      my_error(ER_WRONG_ARGUMENTS, MYF(0), what);
      return 1;
    }
  }
  return 0;
}

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


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

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

unknown's avatar
unknown committed
7879
bool parse_sql(THD *thd,
7880
               Parser_state *parser_state,
unknown's avatar
unknown committed
7881
               Object_creation_ctx *creation_ctx)
7882
{
7883
  bool ret_value;
7884
  DBUG_ENTER("parse_sql");
7885
  DBUG_ASSERT(thd->m_parser_state == NULL);
7886
  DBUG_ASSERT(thd->lex->m_stmt == NULL);
7887

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

  Object_creation_ctx *backup_ctx= NULL;

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

7896
  /* Set parser state. */
unknown's avatar
unknown committed
7897

7898
  thd->m_parser_state= parser_state;
7899

unknown's avatar
unknown committed
7900 7901
  /* Parse the query. */

7902 7903
  bool mysql_parse_status= MYSQLparse(thd) != 0;

7904 7905 7906 7907 7908 7909 7910 7911 7912 7913
  /*
    Check that if MYSQLparse() failed either thd->is_error() is set, or an
    internal error handler is set.

    The assert will not catch a situation where parsing fails without an
    error reported if an error handler exists. The problem is that the
    error handler might have intercepted the error, so thd->is_error() is
    not set. However, there is no way to be 100% sure here (the error
    handler might be for other errors than parsing one).
  */
7914 7915

  DBUG_ASSERT(!mysql_parse_status ||
7916 7917
              thd->is_error() ||
              thd->get_internal_handler());
unknown's avatar
unknown committed
7918

7919
  /* Reset parser state. */
7920

7921
  thd->m_parser_state= NULL;
7922

unknown's avatar
unknown committed
7923 7924 7925 7926 7927 7928 7929
  /* Restore creation context. */

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

  /* That's it. */

7930 7931
  ret_value= mysql_parse_status || thd->is_fatal_error;
  MYSQL_QUERY_PARSE_DONE(ret_value);
7932
  DBUG_RETURN(ret_value);
7933
}
7934 7935 7936 7937

/**
  @} (end of group Runtime_Environment)
*/
Alexander Barkov's avatar
#  
Alexander Barkov committed
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 7964 7965 7966 7967 7968 7969 7970 7971



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