diff options
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r-- | sql/sql_parse.cc | 949 |
1 files changed, 767 insertions, 182 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index adead650029..461c4a419be 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -23,7 +23,10 @@ // set_handler_table_locks, // lock_global_read_lock, // make_global_read_lock_block_commit -#include "sql_base.h" // find_temporary_table +#include "sql_base.h" // open_tables, open_and_lock_tables, + // lock_tables, unique_table, + // close_thread_tables, is_temporary_table + // table_cache.h #include "sql_cache.h" // QUERY_CACHE_FLAGS_SIZE, query_cache_* #include "sql_show.h" // mysqld_list_*, mysqld_show_*, // calc_sum_of_all_status @@ -92,6 +95,7 @@ #include "transaction.h" #include "sql_audit.h" #include "sql_prepare.h" +#include "sql_cte.h" #include "debug_sync.h" #include "probes_mysql.h" #include "set_var.h" @@ -110,7 +114,9 @@ #include "wsrep_thd.h" static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, - Parser_state *parser_state); + Parser_state *parser_state, + bool is_com_multi, + bool is_next_command); /** @defgroup Runtime_Environment Runtime Environment @@ -134,38 +140,263 @@ static bool check_rename_table(THD *, TABLE_LIST *, TABLE_LIST *); const char *any_db="*any*"; // Special symbol for check_access -const LEX_STRING command_name[]={ - { 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 +const LEX_STRING command_name[257]={ + { C_STRING_WITH_LEN("Sleep") }, //0 + { C_STRING_WITH_LEN("Quit") }, //1 + { C_STRING_WITH_LEN("Init DB") }, //2 + { C_STRING_WITH_LEN("Query") }, //3 + { C_STRING_WITH_LEN("Field List") }, //4 + { C_STRING_WITH_LEN("Create DB") }, //5 + { C_STRING_WITH_LEN("Drop DB") }, //6 + { C_STRING_WITH_LEN("Refresh") }, //7 + { C_STRING_WITH_LEN("Shutdown") }, //8 + { C_STRING_WITH_LEN("Statistics") }, //9 + { C_STRING_WITH_LEN("Processlist") }, //10 + { C_STRING_WITH_LEN("Connect") }, //11 + { C_STRING_WITH_LEN("Kill") }, //12 + { C_STRING_WITH_LEN("Debug") }, //13 + { C_STRING_WITH_LEN("Ping") }, //14 + { C_STRING_WITH_LEN("Time") }, //15 + { C_STRING_WITH_LEN("Delayed insert") }, //16 + { C_STRING_WITH_LEN("Change user") }, //17 + { C_STRING_WITH_LEN("Binlog Dump") }, //18 + { C_STRING_WITH_LEN("Table Dump") }, //19 + { C_STRING_WITH_LEN("Connect Out") }, //20 + { C_STRING_WITH_LEN("Register Slave") }, //21 + { C_STRING_WITH_LEN("Prepare") }, //22 + { C_STRING_WITH_LEN("Execute") }, //23 + { C_STRING_WITH_LEN("Long Data") }, //24 + { C_STRING_WITH_LEN("Close stmt") }, //25 + { C_STRING_WITH_LEN("Reset stmt") }, //26 + { C_STRING_WITH_LEN("Set option") }, //27 + { C_STRING_WITH_LEN("Fetch") }, //28 + { C_STRING_WITH_LEN("Daemon") }, //29 + { C_STRING_WITH_LEN("Unimpl get tid") }, //30 + { C_STRING_WITH_LEN("Reset connection") },//31 + { 0, 0 }, //32 + { 0, 0 }, //33 + { 0, 0 }, //34 + { 0, 0 }, //35 + { 0, 0 }, //36 + { 0, 0 }, //37 + { 0, 0 }, //38 + { 0, 0 }, //39 + { 0, 0 }, //40 + { 0, 0 }, //41 + { 0, 0 }, //42 + { 0, 0 }, //43 + { 0, 0 }, //44 + { 0, 0 }, //45 + { 0, 0 }, //46 + { 0, 0 }, //47 + { 0, 0 }, //48 + { 0, 0 }, //49 + { 0, 0 }, //50 + { 0, 0 }, //51 + { 0, 0 }, //52 + { 0, 0 }, //53 + { 0, 0 }, //54 + { 0, 0 }, //55 + { 0, 0 }, //56 + { 0, 0 }, //57 + { 0, 0 }, //58 + { 0, 0 }, //59 + { 0, 0 }, //60 + { 0, 0 }, //61 + { 0, 0 }, //62 + { 0, 0 }, //63 + { 0, 0 }, //64 + { 0, 0 }, //65 + { 0, 0 }, //66 + { 0, 0 }, //67 + { 0, 0 }, //68 + { 0, 0 }, //69 + { 0, 0 }, //70 + { 0, 0 }, //71 + { 0, 0 }, //72 + { 0, 0 }, //73 + { 0, 0 }, //74 + { 0, 0 }, //75 + { 0, 0 }, //76 + { 0, 0 }, //77 + { 0, 0 }, //78 + { 0, 0 }, //79 + { 0, 0 }, //80 + { 0, 0 }, //81 + { 0, 0 }, //82 + { 0, 0 }, //83 + { 0, 0 }, //84 + { 0, 0 }, //85 + { 0, 0 }, //86 + { 0, 0 }, //87 + { 0, 0 }, //88 + { 0, 0 }, //89 + { 0, 0 }, //90 + { 0, 0 }, //91 + { 0, 0 }, //92 + { 0, 0 }, //93 + { 0, 0 }, //94 + { 0, 0 }, //95 + { 0, 0 }, //96 + { 0, 0 }, //97 + { 0, 0 }, //98 + { 0, 0 }, //99 + { 0, 0 }, //100 + { 0, 0 }, //101 + { 0, 0 }, //102 + { 0, 0 }, //103 + { 0, 0 }, //104 + { 0, 0 }, //105 + { 0, 0 }, //106 + { 0, 0 }, //107 + { 0, 0 }, //108 + { 0, 0 }, //109 + { 0, 0 }, //110 + { 0, 0 }, //111 + { 0, 0 }, //112 + { 0, 0 }, //113 + { 0, 0 }, //114 + { 0, 0 }, //115 + { 0, 0 }, //116 + { 0, 0 }, //117 + { 0, 0 }, //118 + { 0, 0 }, //119 + { 0, 0 }, //120 + { 0, 0 }, //121 + { 0, 0 }, //122 + { 0, 0 }, //123 + { 0, 0 }, //124 + { 0, 0 }, //125 + { 0, 0 }, //126 + { 0, 0 }, //127 + { 0, 0 }, //128 + { 0, 0 }, //129 + { 0, 0 }, //130 + { 0, 0 }, //131 + { 0, 0 }, //132 + { 0, 0 }, //133 + { 0, 0 }, //134 + { 0, 0 }, //135 + { 0, 0 }, //136 + { 0, 0 }, //137 + { 0, 0 }, //138 + { 0, 0 }, //139 + { 0, 0 }, //140 + { 0, 0 }, //141 + { 0, 0 }, //142 + { 0, 0 }, //143 + { 0, 0 }, //144 + { 0, 0 }, //145 + { 0, 0 }, //146 + { 0, 0 }, //147 + { 0, 0 }, //148 + { 0, 0 }, //149 + { 0, 0 }, //150 + { 0, 0 }, //151 + { 0, 0 }, //152 + { 0, 0 }, //153 + { 0, 0 }, //154 + { 0, 0 }, //155 + { 0, 0 }, //156 + { 0, 0 }, //157 + { 0, 0 }, //158 + { 0, 0 }, //159 + { 0, 0 }, //160 + { 0, 0 }, //161 + { 0, 0 }, //162 + { 0, 0 }, //163 + { 0, 0 }, //164 + { 0, 0 }, //165 + { 0, 0 }, //166 + { 0, 0 }, //167 + { 0, 0 }, //168 + { 0, 0 }, //169 + { 0, 0 }, //170 + { 0, 0 }, //171 + { 0, 0 }, //172 + { 0, 0 }, //173 + { 0, 0 }, //174 + { 0, 0 }, //175 + { 0, 0 }, //176 + { 0, 0 }, //177 + { 0, 0 }, //178 + { 0, 0 }, //179 + { 0, 0 }, //180 + { 0, 0 }, //181 + { 0, 0 }, //182 + { 0, 0 }, //183 + { 0, 0 }, //184 + { 0, 0 }, //185 + { 0, 0 }, //186 + { 0, 0 }, //187 + { 0, 0 }, //188 + { 0, 0 }, //189 + { 0, 0 }, //190 + { 0, 0 }, //191 + { 0, 0 }, //192 + { 0, 0 }, //193 + { 0, 0 }, //194 + { 0, 0 }, //195 + { 0, 0 }, //196 + { 0, 0 }, //197 + { 0, 0 }, //198 + { 0, 0 }, //199 + { 0, 0 }, //200 + { 0, 0 }, //201 + { 0, 0 }, //202 + { 0, 0 }, //203 + { 0, 0 }, //204 + { 0, 0 }, //205 + { 0, 0 }, //206 + { 0, 0 }, //207 + { 0, 0 }, //208 + { 0, 0 }, //209 + { 0, 0 }, //210 + { 0, 0 }, //211 + { 0, 0 }, //212 + { 0, 0 }, //213 + { 0, 0 }, //214 + { 0, 0 }, //215 + { 0, 0 }, //216 + { 0, 0 }, //217 + { 0, 0 }, //218 + { 0, 0 }, //219 + { 0, 0 }, //220 + { 0, 0 }, //221 + { 0, 0 }, //222 + { 0, 0 }, //223 + { 0, 0 }, //224 + { 0, 0 }, //225 + { 0, 0 }, //226 + { 0, 0 }, //227 + { 0, 0 }, //228 + { 0, 0 }, //229 + { 0, 0 }, //230 + { 0, 0 }, //231 + { 0, 0 }, //232 + { 0, 0 }, //233 + { 0, 0 }, //234 + { 0, 0 }, //235 + { 0, 0 }, //236 + { 0, 0 }, //237 + { 0, 0 }, //238 + { 0, 0 }, //239 + { 0, 0 }, //240 + { 0, 0 }, //241 + { 0, 0 }, //242 + { 0, 0 }, //243 + { 0, 0 }, //244 + { 0, 0 }, //245 + { 0, 0 }, //246 + { 0, 0 }, //247 + { 0, 0 }, //248 + { 0, 0 }, //249 + { C_STRING_WITH_LEN("Bulk_execute") }, //250 + { C_STRING_WITH_LEN("Slave_worker") }, //251 + { C_STRING_WITH_LEN("Slave_IO") }, //252 + { C_STRING_WITH_LEN("Slave_SQL") }, //253 + { C_STRING_WITH_LEN("Com_multi") }, //254 + { C_STRING_WITH_LEN("Error") } // Last command number 255 }; const char *xa_state_names[]={ @@ -190,7 +421,7 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables) for (TABLE_LIST *table= tables; table; table= table->next_global) { DBUG_ASSERT(table->db && table->table_name); - if (table->updating && !find_temporary_table(thd, table)) + if (table->updating && !thd->find_tmp_table_share(table)) return 1; } return 0; @@ -267,7 +498,7 @@ void init_update_queries(void) memset(server_command_flags, 0, sizeof(server_command_flags)); server_command_flags[COM_STATISTICS]= CF_SKIP_QUERY_ID | CF_SKIP_QUESTIONS | CF_SKIP_WSREP_CHECK; - server_command_flags[COM_PING]= CF_SKIP_QUERY_ID | CF_SKIP_QUESTIONS | CF_SKIP_WSREP_CHECK; + server_command_flags[COM_PING]= CF_SKIP_QUERY_ID | CF_SKIP_QUESTIONS | CF_SKIP_WSREP_CHECK | CF_NO_COM_MULTI; server_command_flags[COM_QUIT]= CF_SKIP_WSREP_CHECK; server_command_flags[COM_PROCESS_INFO]= CF_SKIP_WSREP_CHECK; @@ -277,6 +508,10 @@ void init_update_queries(void) server_command_flags[COM_TIME]= CF_SKIP_WSREP_CHECK; server_command_flags[COM_INIT_DB]= CF_SKIP_WSREP_CHECK; server_command_flags[COM_END]= CF_SKIP_WSREP_CHECK; + for (uint i= COM_MDB_GAP_BEG; i <= COM_MDB_GAP_END; i++) + { + server_command_flags[i]= CF_SKIP_WSREP_CHECK; + } /* COM_QUERY, COM_SET_OPTION and COM_STMT_XXX are allowed to pass the early @@ -292,6 +527,8 @@ void init_update_queries(void) server_command_flags[COM_STMT_EXECUTE]= CF_SKIP_WSREP_CHECK; server_command_flags[COM_STMT_SEND_LONG_DATA]= CF_SKIP_WSREP_CHECK; server_command_flags[COM_REGISTER_SLAVE]= CF_SKIP_WSREP_CHECK; + server_command_flags[COM_MULTI]= CF_SKIP_WSREP_CHECK | CF_NO_COM_MULTI; + server_command_flags[CF_NO_COM_MULTI]= CF_NO_COM_MULTI; /* Initialize the sql command flags array. */ memset(sql_command_flags, 0, sizeof(sql_command_flags)); @@ -341,17 +578,19 @@ void init_update_queries(void) CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | CF_CAN_BE_EXPLAINED | - CF_UPDATES_DATA; + CF_UPDATES_DATA | CF_SP_BULK_SAFE; sql_command_flags[SQLCOM_UPDATE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | CF_CAN_BE_EXPLAINED | - CF_UPDATES_DATA; + CF_UPDATES_DATA | CF_SP_BULK_SAFE; sql_command_flags[SQLCOM_INSERT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | CF_CAN_BE_EXPLAINED | - CF_INSERTS_DATA; + CF_INSERTS_DATA | + CF_SP_BULK_SAFE | + CF_SP_BULK_OPTIMIZED; sql_command_flags[SQLCOM_INSERT_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | @@ -360,7 +599,8 @@ void init_update_queries(void) sql_command_flags[SQLCOM_DELETE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | - CF_CAN_BE_EXPLAINED; + CF_CAN_BE_EXPLAINED | + CF_SP_BULK_SAFE; sql_command_flags[SQLCOM_DELETE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | @@ -369,7 +609,8 @@ void init_update_queries(void) CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | CF_CAN_BE_EXPLAINED | - CF_INSERTS_DATA;; + CF_INSERTS_DATA | CF_SP_BULK_SAFE | + CF_SP_BULK_OPTIMIZED; sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE | @@ -422,6 +663,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_SHOW_EXPLAIN]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CREATE_USER]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CREATE]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND; @@ -443,6 +685,7 @@ void init_update_queries(void) 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; + sql_command_flags[SQLCOM_ALTER_USER]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_CREATE_ROLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_GRANT_ROLE]= CF_CHANGES_DATA; @@ -473,6 +716,7 @@ void init_update_queries(void) CF_CAN_GENERATE_ROW_EVENTS | CF_OPTIMIZER_TRACE; // (1) sql_command_flags[SQLCOM_EXECUTE]= CF_CAN_GENERATE_ROW_EVENTS; + sql_command_flags[SQLCOM_EXECUTE_IMMEDIATE]= CF_CAN_GENERATE_ROW_EVENTS; sql_command_flags[SQLCOM_COMPOUND]= CF_CAN_GENERATE_ROW_EVENTS; /* @@ -506,6 +750,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_CHECKSUM]= CF_REPORT_PROGRESS; sql_command_flags[SQLCOM_CREATE_USER]|= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_ALTER_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_CREATE_ROLE]|= CF_AUTO_COMMIT_TRANS; @@ -534,7 +779,6 @@ void init_update_queries(void) Note that SQLCOM_RENAME_TABLE should not be in this list! */ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_PREOPEN_TMP_TABLES; - sql_command_flags[SQLCOM_DROP_TABLE]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_TRUNCATE]|= CF_PREOPEN_TMP_TABLES; @@ -601,6 +845,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_ALTER_EVENT]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_DROP_EVENT]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_CREATE_USER]|= CF_DISALLOW_IN_RO_TRANS; + sql_command_flags[SQLCOM_ALTER_USER]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_RENAME_USER]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_DROP_USER]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_CREATE_SERVER]|= CF_DISALLOW_IN_RO_TRANS; @@ -651,7 +896,7 @@ void execute_init_command(THD *thd, LEX_STRING *init_command, mysql_rwlock_t *var_lock) { Vio* save_vio; - ulong save_client_capabilities; + ulonglong save_client_capabilities; mysql_rwlock_rdlock(var_lock); if (!init_command->length) @@ -684,7 +929,7 @@ void execute_init_command(THD *thd, LEX_STRING *init_command, save_vio= thd->net.vio; thd->net.vio= 0; thd->clear_error(1); - dispatch_command(COM_QUERY, thd, buf, len); + dispatch_command(COM_QUERY, thd, buf, len, FALSE, FALSE); thd->client_capabilities= save_client_capabilities; thd->net.vio= save_vio; @@ -802,7 +1047,7 @@ static void handle_bootstrap_impl(THD *thd) break; } - mysql_parse(thd, thd->query(), length, &parser_state); + mysql_parse(thd, thd->query(), length, &parser_state, FALSE, FALSE); bootstrap_error= thd->is_error(); thd->protocol->end_statement(); @@ -859,13 +1104,12 @@ void do_handle_bootstrap(THD *thd) end: delete thd; -#ifndef EMBEDDED_LIBRARY - thread_safe_decrement32(&thread_count); - in_bootstrap= FALSE; - mysql_mutex_lock(&LOCK_thread_count); + in_bootstrap = FALSE; mysql_cond_broadcast(&COND_thread_count); mysql_mutex_unlock(&LOCK_thread_count); + +#ifndef EMBEDDED_LIBRARY my_thread_end(); pthread_exit(0); #endif @@ -874,7 +1118,7 @@ end: } -/* This works because items are allocated with sql_alloc() */ +/* This works because items are allocated on THD::mem_root */ void free_items(Item *item) { @@ -889,7 +1133,7 @@ void free_items(Item *item) } /** - This works because items are allocated with sql_alloc(). + This works because items are allocated on THD::mem_root. @note The function also handles null pointers (empty list). */ void cleanup_items(Item *item) @@ -900,6 +1144,22 @@ void cleanup_items(Item *item) DBUG_VOID_RETURN; } +static enum enum_server_command fetch_command(THD *thd, char *packet) +{ + enum enum_server_command + command= (enum enum_server_command) (uchar) packet[0]; + DBUG_ENTER("fetch_command"); + + if (command >= COM_END || + (command >= COM_MDB_GAP_BEG && command <= COM_MDB_GAP_END)) + command= COM_END; // Wrong command + + DBUG_PRINT("info",("Command on %s = %d (%s)", + vio_description(thd->net.vio), command, + command_name[command].str)); + DBUG_RETURN(command); +} + #ifdef WITH_WSREP static bool wsrep_tables_accessible_when_detached(const TABLE_LIST *tables) @@ -1089,14 +1349,8 @@ bool do_command(THD *thd) /* 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)); + command= fetch_command(thd, packet); #ifdef WITH_WSREP if (WSREP(thd)) @@ -1128,7 +1382,8 @@ bool do_command(THD *thd) DBUG_ASSERT(packet_length); DBUG_ASSERT(!thd->apc_target.is_enabled()); - return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1)); + return_value= dispatch_command(command, thd, packet+1, + (uint) (packet_length-1), FALSE, FALSE); #ifdef WITH_WSREP if (WSREP(thd)) { @@ -1147,7 +1402,7 @@ bool do_command(THD *thd) } thd->clear_error(); return_value= dispatch_command(command, thd, thd->wsrep_retry_query, - thd->wsrep_retry_query_len); + thd->wsrep_retry_query_len, FALSE, FALSE); thd->variables.character_set_client = current_charset; } @@ -1229,6 +1484,47 @@ static bool deny_updates_if_read_only_option(THD *thd, TABLE_LIST *all_tables) /** + check COM_MULTI packet + + @param thd thread handle + @param packet pointer on the packet of commands + @param packet_length length of this packet + + @retval 0 - Error + @retval # - Number of commands in the batch +*/ + +uint maria_multi_check(THD *thd, char *packet, uint packet_length) +{ + uint counter= 0; + DBUG_ENTER("maria_multi_check"); + while (packet_length) + { + char *packet_start= packet; + size_t subpacket_length= net_field_length((uchar **)&packet_start); + size_t length_length= packet_start - packet; + // length of command + 3 bytes where that length was stored + DBUG_PRINT("info", ("sub-packet length: %zu + %zu command: %x", + subpacket_length, length_length, + packet_start[3])); + + if (subpacket_length == 0 || + (subpacket_length + length_length) > packet_length) + { + my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR), + MYF(0)); + DBUG_RETURN(0); + } + + counter++; + packet= packet_start + subpacket_length; + packet_length-= (subpacket_length + length_length); + } + DBUG_RETURN(counter); +} + + +/** Perform one connection-level (COM_XXXX) command. @param command type of command to perform @@ -1237,6 +1533,8 @@ static bool deny_updates_if_read_only_option(THD *thd, TABLE_LIST *all_tables) @param packet_length length of packet + 1 (to show that data is null-terminated) except for COM_SLEEP, where it can be zero. + @param is_com_multi recursive call from COM_MULTI + @param is_next_command there will be more command in the COM_MULTI batch @todo set thd->lex->sql_command to SQLCOM_END here. @@ -1250,15 +1548,24 @@ static bool deny_updates_if_read_only_option(THD *thd, TABLE_LIST *all_tables) COM_QUIT/COM_SHUTDOWN */ bool dispatch_command(enum enum_server_command command, THD *thd, - char* packet, uint packet_length) + char* packet, uint packet_length, bool is_com_multi, + bool is_next_command) { NET *net= &thd->net; bool error= 0; bool do_end_of_statement= true; DBUG_ENTER("dispatch_command"); - DBUG_PRINT("info", ("command: %d", command)); + DBUG_PRINT("info", ("command: %d %s", command, + (command_name[command].str != 0 ? + command_name[command].str : + "<?>"))); + bool drop_more_results= 0; - inc_thread_running(); + if (!is_com_multi) + inc_thread_running(); + + /* keep it withing 1 byte */ + compile_time_assert(COM_END == 255); #ifdef WITH_WSREP if (WSREP(thd)) @@ -1283,7 +1590,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, command != COM_STMT_CLOSE && command != COM_QUIT) { mysql_mutex_unlock(&thd->LOCK_thd_data); - my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction"); + my_message(ER_LOCK_DEADLOCK, "Deadlock: wsrep aborted transaction", + MYF(0)); WSREP_DEBUG("Deadlock error for: %s", thd->query()); thd->reset_killed(); thd->mysys_var->abort = 0; @@ -1346,6 +1654,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd, beginning of each command. */ thd->server_status&= ~SERVER_STATUS_CLEAR_SET; + if (is_next_command) + { + drop_more_results= !MY_TEST(thd->server_status & + SERVER_MORE_RESULTS_EXISTS); + thd->server_status|= SERVER_MORE_RESULTS_EXISTS; + if (is_com_multi) + thd->get_stmt_da()->set_skip_flush(); + } + switch (command) { case COM_INIT_DB: { @@ -1370,6 +1687,17 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } #endif + case COM_RESET_CONNECTION: + { + thd->status_var.com_other++; + thd->change_user(); + thd->clear_error(); // if errors from rollback + /* Restore original charset from client authentication packet.*/ + if(thd->org_charset) + thd->update_charset(thd->org_charset,thd->org_charset,thd->org_charset); + my_ok(thd, 0, 0, 0); + break; + } case COM_CHANGE_USER: { int auth_rc; @@ -1419,10 +1747,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, decrease_user_connections(thd->user_connect); thd->user_connect= save_user_connect; 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(); + thd->update_charset(save_character_set_client, save_collation_connection, + save_character_set_results); thd->failed_com_change_user++; my_sleep(1000000); } @@ -1438,6 +1764,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } break; } + case COM_STMT_BULK_EXECUTE: + { + mysqld_stmt_bulk_execute(thd, packet, packet_length); + break; + } case COM_STMT_EXECUTE: { mysqld_stmt_execute(thd, packet, packet_length); @@ -1494,13 +1825,16 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; if (WSREP_ON) - wsrep_mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); + wsrep_mysql_parse(thd, thd->query(), thd->query_length(), &parser_state, + is_com_multi, is_next_command); else - mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); + mysql_parse(thd, thd->query(), thd->query_length(), &parser_state, + is_com_multi, is_next_command); while (!thd->killed && (parser_state.m_lip.found_semicolon != NULL) && ! thd->is_error()) { + thd->get_stmt_da()->set_skip_flush(); /* Multiple queries exist, execute them individually */ @@ -1581,9 +1915,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* TODO: set thd->lex->sql_command to SQLCOM_END here */ if (WSREP_ON) - wsrep_mysql_parse(thd, beginning_of_next_stmt, length, &parser_state); + wsrep_mysql_parse(thd, beginning_of_next_stmt, length, &parser_state, + is_com_multi, is_next_command); else - mysql_parse(thd, beginning_of_next_stmt, length, &parser_state); + mysql_parse(thd, beginning_of_next_stmt, length, &parser_state, + is_com_multi, is_next_command); } @@ -1616,7 +1952,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, (The packet is guaranteed to end with an end zero) */ arg_end= strend(packet); - uint arg_length= arg_end - packet; + uint arg_length= (uint)(arg_end - packet); /* Check given table name length. */ if (packet_length - arg_length > NAME_LEN + 1 || arg_length > SAFE_NAME_LEN) @@ -1635,6 +1971,14 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } packet= arg_end + 1; thd->reset_for_next_command(0); // Don't clear errors + // thd->reset_for_next_command reset state => restore it + if (is_next_command) + { + thd->server_status|= SERVER_MORE_RESULTS_EXISTS; + if (is_com_multi) + thd->get_stmt_da()->set_skip_flush(); + } + lex_start(thd); /* Must be before we init the table list. */ if (lower_case_table_names) @@ -1667,7 +2011,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->set_query(fields, query_length); general_log_print(thd, command, "%s %s", table_list.table_name, fields); - if (open_temporary_tables(thd, &table_list)) + if (thd->open_temporary_tables(&table_list)) break; if (check_table_access(thd, SELECT_ACL, &table_list, @@ -1821,7 +2165,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, DBUG_PRINT("quit",("Got shutdown command for level %u", level)); general_log_print(thd, command, NullS); my_eof(thd); - kill_mysql(); + kill_mysql(thd); error=TRUE; break; } @@ -1848,7 +2192,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, length= my_snprintf(buff, buff_len - 1, "Uptime: %lu Threads: %d Questions: %lu " - "Slow queries: %lu Opens: %lu Flush tables: %lu " + "Slow queries: %lu Opens: %lu Flush tables: %lld " "Open tables: %u Queries per second avg: %u.%03u", uptime, (int) thread_count, (ulong) thd->query_id, @@ -1918,11 +2262,90 @@ bool dispatch_command(enum enum_server_command command, THD *thd, general_log_print(thd, command, NullS); my_eof(thd); break; + case COM_MULTI: + { + uint counter; + uint current_com= 0; + DBUG_ASSERT(!is_com_multi); + if (!(thd->client_capabilities & CLIENT_MULTI_RESULTS)) + { + /* The client does not support multiple result sets being sent back */ + my_error(ER_COMMULTI_BADCONTEXT, MYF(0)); + break; + } + + if (!(counter= maria_multi_check(thd, packet, packet_length))) + break; + + { + char *packet_start= packet; + /* We have to store next length because it will be destroyed by '\0' */ + size_t next_subpacket_length= net_field_length((uchar **)&packet_start); + size_t next_length_length= packet_start - packet; + unsigned char *readbuff= net->buff; + + if (net_allocate_new_packet(net, thd, MYF(0))) + break; + + PSI_statement_locker *save_locker= thd->m_statement_psi; + sql_digest_state *save_digest= thd->m_digest; + thd->m_statement_psi= NULL; + thd->m_digest= NULL; + + while (packet_length) + { + current_com++; + size_t subpacket_length= next_subpacket_length + next_length_length; + size_t length_length= next_length_length; + if (subpacket_length < packet_length) + { + packet_start= packet + subpacket_length; + next_subpacket_length= net_field_length((uchar**)&packet_start); + next_length_length= packet_start - (packet + subpacket_length); + } + /* safety like in do_command() */ + packet[subpacket_length]= '\0'; + + enum enum_server_command subcommand= + fetch_command(thd, (packet + length_length)); + + if (server_command_flags[subcommand] & CF_NO_COM_MULTI) + { + my_error(ER_BAD_COMMAND_IN_MULTI, MYF(0), + command_name[subcommand].str); + goto com_multi_end; + } + + if (dispatch_command(subcommand, thd, packet + (1 + length_length), + subpacket_length - (1 + length_length), TRUE, + (current_com != counter))) + { + DBUG_ASSERT(thd->is_error()); + goto com_multi_end; + } + + DBUG_ASSERT(subpacket_length <= packet_length); + packet+= subpacket_length; + packet_length-= subpacket_length; + } + +com_multi_end: + thd->m_statement_psi= save_locker; + thd->m_digest= save_digest; + + /* release old buffer */ + net_flush(net); + DBUG_ASSERT(net->buff == net->write_pos); // nothing to send + my_free(readbuff); + } + break; + } case COM_SLEEP: case COM_CONNECT: // Impossible here case COM_TIME: // Impossible from client case COM_DELAYED_INSERT: case COM_END: + case COM_UNIMPLEMENTED: default: my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR), MYF(0)); @@ -1961,9 +2384,14 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd_proc_info(thd, "updating status"); /* Finalize server status flags after executing a command. */ thd->update_server_status(); - thd->protocol->end_statement(); - query_cache_end_of_result(thd); + if (command != COM_MULTI) + { + thd->protocol->end_statement(); + query_cache_end_of_result(thd); + } } + if (drop_more_results) + thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS; if (!thd->is_error() && !thd->killed_errno()) mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0); @@ -1988,8 +2416,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->m_statement_psi= NULL; thd->m_digest= NULL; - dec_thread_running(); - thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory + if (!is_com_multi) + { + dec_thread_running(); + thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory + } thd->reset_kill_query(); /* Ensure that killed_errmsg is released */ /* LEX::m_sql_cmd can point to Sql_cmd allocated on thd->mem_root. @@ -2161,7 +2592,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, { DBUG_RETURN(1); } - schema_select_lex= new SELECT_LEX(); + schema_select_lex= new (thd->mem_root) SELECT_LEX(); db.str= schema_select_lex->db= lex->select_lex.db; schema_select_lex->table_list.first= NULL; db.length= strlen(db.str); @@ -2184,7 +2615,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, { DBUG_ASSERT(table_ident); TABLE_LIST **query_tables_last= lex->query_tables_last; - schema_select_lex= new SELECT_LEX(); + schema_select_lex= new (thd->mem_root) 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(); @@ -2382,27 +2813,80 @@ bool sp_process_definer(THD *thd) static bool lock_tables_open_and_lock_tables(THD *thd, TABLE_LIST *tables) { Lock_tables_prelocking_strategy lock_tables_prelocking_strategy; + MDL_deadlock_and_lock_abort_error_handler deadlock_handler; + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); uint counter; TABLE_LIST *table; thd->in_lock_tables= 1; +retry: + 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; + { + if (!table->placeholder()) + { + if (table->table->s->tmp_table) + { + /* + 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. + */ + table->table->reginfo.lock_type= TL_WRITE; + } + else if (table->mdl_request.type == MDL_SHARED_READ && + ! table->prelocking_placeholder && + table->table->file->lock_count() == 0) + { + enum enum_mdl_type lock_type; + /* + In case when LOCK TABLE ... READ LOCAL was issued for table with + storage engine which doesn't support READ LOCAL option and doesn't + use THR_LOCK locks we need to upgrade weak SR metadata lock acquired + in open_tables() to stronger SRO metadata lock. + This is not needed for tables used through stored routines or + triggers as we always acquire SRO (or even stronger SNRW) metadata + lock for them. + */ + deadlock_handler.init(); + thd->push_internal_handler(&deadlock_handler); + + lock_type= table->table->mdl_ticket->get_type() == MDL_SHARED_WRITE ? + MDL_SHARED_NO_READ_WRITE : MDL_SHARED_READ_ONLY; + + bool result= thd->mdl_context.upgrade_shared_lock( + table->table->mdl_ticket, + lock_type, + thd->variables.lock_wait_timeout); + + thd->pop_internal_handler(); + + if (deadlock_handler.need_reopen()) + { + /* + Deadlock occurred during upgrade of metadata lock. + Let us restart acquring and opening tables for LOCK TABLES. + */ + close_tables_for_reopen(thd, &tables, mdl_savepoint); + if (thd->open_temporary_tables(tables)) + goto err; + goto retry; + } + + if (result) + goto err; + } + } + } if (lock_tables(thd, tables, counter, 0) || thd->locked_tables_list.init_locked_tables(thd)) @@ -2454,6 +2938,13 @@ static bool do_execute_sp(THD *thd, sp_head *sp) thd->variables.select_limit= HA_POS_ERROR; /* + Reset current_select as it may point to random data as a + result of previous parsing. + */ + thd->lex->current_select= NULL; + thd->lex->in_sum_func= 0; // For Item_field::fix_fields() + + /* We never write CALL statements into binlog: - If the mode is non-prelocked, each statement will be logged separately. @@ -2563,6 +3054,9 @@ mysql_execute_command(THD *thd) thd->get_stmt_da()->opt_clear_warning_info(thd->query_id); } + if (check_dependencies_in_with_clauses(thd->lex->with_clauses_list)) + DBUG_RETURN(1); + #ifdef HAVE_REPLICATION if (unlikely(thd->slave_thread)) { @@ -2890,7 +3384,8 @@ mysql_execute_command(THD *thd) thd->mdl_context.release_transactional_locks(); if (commit_failed) { - WSREP_DEBUG("implicit commit failed, MDL released: %lu", thd->thread_id); + WSREP_DEBUG("implicit commit failed, MDL released: %lld", + (longlong) thd->thread_id); goto error; } } @@ -2936,7 +3431,7 @@ mysql_execute_command(THD *thd) */ if (sql_command_flags[lex->sql_command] & CF_PREOPEN_TMP_TABLES) { - if (open_temporary_tables(thd, all_tables)) + if (thd->open_temporary_tables(all_tables)) goto error; } @@ -3039,6 +3534,11 @@ mysql_execute_command(THD *thd) break; } + case SQLCOM_EXECUTE_IMMEDIATE: + { + mysql_sql_stmt_execute_immediate(thd); + break; + } case SQLCOM_PREPARE: { mysql_sql_stmt_prepare(thd); @@ -3504,7 +4004,7 @@ mysql_execute_command(THD *thd) DBUG_ASSERT(select_lex->offset_limit == 0); unit->set_limit(select_lex); MYSQL_UPDATE_START(thd->query()); - res= (up_result= mysql_update(thd, all_tables, + res= up_result= mysql_update(thd, all_tables, select_lex->item_list, lex->value_list, select_lex->where, @@ -3512,7 +4012,7 @@ mysql_execute_command(THD *thd) select_lex->order_list.first, unit->select_limit_cnt, lex->duplicates, lex->ignore, - &found, &updated)); + &found, &updated); MYSQL_UPDATE_DONE(res, found, updated); /* mysql_update return 2 if we need to switch to multi-update */ if (up_result != 2) @@ -3656,7 +4156,8 @@ mysql_execute_command(THD *thd) */ if (first_table->lock_type != TL_WRITE_DELAYED) { - if ((res= open_temporary_tables(thd, all_tables))) + res= (thd->open_temporary_tables(all_tables)) ? TRUE : FALSE; + if (res) break; } @@ -3745,7 +4246,7 @@ mysql_execute_command(THD *thd) unit->set_limit(select_lex); - if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0))) + if (!(res=open_and_lock_tables(thd, all_tables, TRUE, 0))) { MYSQL_INSERT_SELECT_START(thd->query()); /* @@ -3924,7 +4425,7 @@ mysql_execute_command(THD *thd) lex->table_count); if (result) { - res= mysql_select(thd, &select_lex->ref_pointer_array, + res= mysql_select(thd, select_lex->get_table_list(), select_lex->with_wild, select_lex->item_list, @@ -3957,7 +4458,14 @@ mysql_execute_command(THD *thd) } case SQLCOM_DROP_TABLE: { + int result; DBUG_ASSERT(first_table == all_tables && first_table != 0); + + thd->open_options|= HA_OPEN_FOR_REPAIR; + result= thd->open_temporary_tables(all_tables); + thd->open_options&= ~HA_OPEN_FOR_REPAIR; + if (result) + goto error; if (!lex->tmp_table()) { if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE)) @@ -3987,7 +4495,7 @@ mysql_execute_command(THD *thd) { if (!lex->tmp_table() && (!thd->is_current_stmt_binlog_format_row() || - !find_temporary_table(thd, table))) + !thd->find_temporary_table(table))) { WSREP_TO_ISOLATION_BEGIN(NULL, NULL, all_tables); break; @@ -3997,6 +4505,13 @@ mysql_execute_command(THD *thd) /* DDL and binlog write order are protected by metadata locks. */ res= mysql_rm_table(thd, first_table, lex->if_exists(), lex->tmp_table()); + + /* when dropping temporary tables if @@session_track_state_change is ON then + send the boolean tracker in the OK packet */ + if(!res && (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) + { + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); + } break; } case SQLCOM_SHOW_PROCESSLIST: @@ -4076,7 +4591,8 @@ mysql_execute_command(THD *thd) goto error; if (!(res= sql_set_variables(thd, lex_var_list, true))) { - my_ok(thd); + if (!thd->is_error()) + my_ok(thd); } else { @@ -4131,7 +4647,7 @@ mysql_execute_command(THD *thd) CF_PREOPEN_TMP_TABLES was set and the tables would be pre-opened in a usual way, they would have been closed. */ - if (open_temporary_tables(thd, all_tables)) + if (thd->open_temporary_tables(all_tables)) goto error; if (lock_tables_precheck(thd, all_tables)) @@ -4412,6 +4928,7 @@ mysql_execute_command(THD *thd) my_ok(thd); break; } + case SQLCOM_ALTER_USER: case SQLCOM_RENAME_USER: { if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1) && @@ -4419,7 +4936,11 @@ mysql_execute_command(THD *thd) break; /* Conditionally writes to binlog */ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) - if (!(res= mysql_rename_user(thd, lex->users_list))) + if (lex->sql_command == SQLCOM_ALTER_USER) + res= mysql_alter_user(thd, lex->users_list); + else + res= mysql_rename_user(thd, lex->users_list); + if (!res) my_ok(thd); break; } @@ -4708,7 +5229,7 @@ mysql_execute_command(THD *thd) DBUG_EXECUTE_IF("crash_shutdown", DBUG_SUICIDE();); if (check_global_access(thd,SHUTDOWN_ACL)) goto error; - kill_mysql(); + kill_mysql(thd); my_ok(thd); #else my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server"); @@ -4716,6 +5237,15 @@ mysql_execute_command(THD *thd) break; #ifndef NO_EMBEDDED_ACCESS_CHECKS + case SQLCOM_SHOW_CREATE_USER: + { + LEX_USER *grant_user= lex->grant_user; + if (!grant_user) + goto error; + + res = mysql_show_create_user(thd, grant_user); + break; + } case SQLCOM_SHOW_GRANTS: { LEX_USER *grant_user= lex->grant_user; @@ -4759,7 +5289,8 @@ mysql_execute_command(THD *thd) if (trans_begin(thd, lex->start_transaction_opt)) { thd->mdl_context.release_transactional_locks(); - WSREP_DEBUG("BEGIN failed, MDL released: %lu", thd->thread_id); + WSREP_DEBUG("BEGIN failed, MDL released: %lld", + (longlong) thd->thread_id); goto error; } my_ok(thd); @@ -4778,7 +5309,8 @@ mysql_execute_command(THD *thd) thd->mdl_context.release_transactional_locks(); if (commit_failed) { - WSREP_DEBUG("COMMIT failed, MDL released: %lu", thd->thread_id); + WSREP_DEBUG("COMMIT failed, MDL released: %lld", + (longlong) thd->thread_id); goto error; } /* Begin transaction with the same isolation level. */ @@ -4790,8 +5322,7 @@ mysql_execute_command(THD *thd) else { /* Reset the isolation level and access mode if no chaining transaction.*/ - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); } /* Disconnect the current client connection. */ if (tx_release) @@ -4830,7 +5361,8 @@ mysql_execute_command(THD *thd) if (rollback_failed) { - WSREP_DEBUG("rollback failed, MDL released: %lu", thd->thread_id); + WSREP_DEBUG("rollback failed, MDL released: %lld", + (longlong) thd->thread_id); goto error; } /* Begin transaction with the same isolation level. */ @@ -4842,8 +5374,7 @@ mysql_execute_command(THD *thd) else { /* Reset the isolation level and access mode if no chaining transaction.*/ - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); } /* Disconnect the current client connection. */ if (tx_release) @@ -4903,7 +5434,7 @@ mysql_execute_command(THD *thd) { if (check_routine_access(thd, ALTER_PROC_ACL, lex->spname->m_db.str, lex->spname->m_name.str, - lex->sql_command == SQLCOM_DROP_PROCEDURE, 0)) + lex->sql_command == SQLCOM_CREATE_PROCEDURE, 0)) goto error; } @@ -5309,15 +5840,15 @@ mysql_execute_command(THD *thd) thd->mdl_context.release_transactional_locks(); if (commit_failed) { - WSREP_DEBUG("XA commit failed, MDL released: %lu", thd->thread_id); + WSREP_DEBUG("XA commit failed, MDL released: %lld", + (longlong) thd->thread_id); goto error; } /* We've just done a commit, reset transaction isolation level and access mode to the session default. */ - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); my_ok(thd); break; } @@ -5327,15 +5858,15 @@ mysql_execute_command(THD *thd) thd->mdl_context.release_transactional_locks(); if (rollback_failed) { - WSREP_DEBUG("XA rollback failed, MDL released: %lu", thd->thread_id); + WSREP_DEBUG("XA rollback failed, MDL released: %lld", + (longlong) thd->thread_id); goto error; } /* We've just done a rollback, reset transaction isolation level and access mode to the session default. */ - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); my_ok(thd); break; } @@ -5575,6 +6106,9 @@ finish: { thd->mdl_context.release_statement_locks(); } + + TRANSACT_TRACKER(add_trx_state_from_thd(thd)); + WSREP_TO_ISOLATION_END; #ifdef WITH_WSREP @@ -5586,8 +6120,8 @@ finish: ! thd->in_active_multi_stmt_transaction() && thd->mdl_context.has_transactional_locks()) { - WSREP_DEBUG("Forcing release of transactional locks for thd %lu", - thd->thread_id); + WSREP_DEBUG("Forcing release of transactional locks for thd: %lld", + (longlong) thd->thread_id); thd->mdl_context.release_transactional_locks(); } #endif /* WITH_WSREP */ @@ -5609,6 +6143,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) new (thd->mem_root) Item_int(thd, (ulonglong) thd->variables.select_limit); } + if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0))) { if (lex->describe) @@ -5705,7 +6240,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) } } /* Count number of empty select queries */ - if (!thd->get_sent_row_count()) + if (!thd->get_sent_row_count() && !res) status_var_increment(thd->status_var.empty_queries); else status_var_add(thd->status_var.rows_sent, thd->get_sent_row_count()); @@ -5789,7 +6324,7 @@ static TABLE *find_temporary_table_for_rename(THD *thd, } } if (!found) - res= find_temporary_table(thd, table); + res= thd->find_temporary_table(table, THD::TMP_TABLE_ANY); DBUG_RETURN(res); } @@ -5901,11 +6436,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, THD_STAGE_INFO(thd, stage_checking_permissions); if ((!db || !db[0]) && !thd->db && !dont_check_global_grants) { - DBUG_PRINT("error",("No database")); - if (!no_errors) - my_message(ER_NO_DB_ERROR, ER_THD(thd, ER_NO_DB_ERROR), - MYF(0)); /* purecov: tested */ - DBUG_RETURN(TRUE); /* purecov: tested */ + DBUG_RETURN(FALSE); // CTE reference or an error later } if ((db != NULL) && (db != any_db)) @@ -6219,7 +6750,7 @@ static bool check_show_access(THD *thd, TABLE_LIST *table) /* Open temporary tables to be able to detect them during privilege check. */ - if (open_temporary_tables(thd, dst_table)) + if (thd->open_temporary_tables(dst_table)) return TRUE; if (check_access(thd, SELECT_ACL, dst_table->db, @@ -6784,10 +7315,9 @@ void THD::reset_for_next_command(bool do_clear_error) thd->thread_specific_used= FALSE; if (opt_bin_log) - { reset_dynamic(&thd->user_var_events); - thd->user_var_events_alloc= thd->mem_root; - } + DBUG_ASSERT(thd->user_var_events_alloc == &thd->main_mem_root); + thd->get_stmt_da()->reset_for_next_command(); thd->rand_used= 0; thd->m_sent_row_count= thd->m_examined_row_count= 0; @@ -6894,22 +7424,30 @@ mysql_new_select(LEX *lex, bool move_down) my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO"); DBUG_RETURN(TRUE); } + + /* + This type of query is not possible in the grammar: + SELECT 1 FROM t1 PROCEDURE ANALYSE() UNION ... ; + + But this type of query is still possible: + (SELECT 1 FROM t1 PROCEDURE ANALYSE()) UNION ... ; + and it's not easy to disallow this grammatically, + because there can be any parenthesis nest level: + (((SELECT 1 FROM t1 PROCEDURE ANALYSE()))) UNION ... ; + */ if (lex->proc_list.elements!=0) { my_error(ER_WRONG_USAGE, MYF(0), "UNION", "SELECT ... PROCEDURE ANALYSE()"); DBUG_RETURN(TRUE); } - if (lex->current_select->order_list.first && !lex->current_select->braces) - { - my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY"); - DBUG_RETURN(1); - } - if (lex->current_select->explicit_limit && !lex->current_select->braces) - { - my_error(ER_WRONG_USAGE, MYF(0), "UNION", "LIMIT"); - DBUG_RETURN(1); - } + // SELECT 1 FROM t1 ORDER BY 1 UNION SELECT 1 FROM t1 -- not possible + DBUG_ASSERT(!lex->current_select->order_list.first || + lex->current_select->braces); + // SELECT 1 FROM t1 LIMIT 1 UNION SELECT 1 FROM t1; -- not possible + DBUG_ASSERT(!lex->current_select->explicit_limit || + lex->current_select->braces); + select_lex->include_neighbour(lex->current_select); SELECT_LEX_UNIT *unit= select_lex->master_unit(); if (!unit->fake_select_lex && unit->add_fake_select_lex(lex->thd)) @@ -6960,7 +7498,7 @@ void create_select_for_variable(const char *var_name) if ((var= get_system_var(thd, OPT_SESSION, tmp, null_lex_str))) { end= strxmov(buff, "@@session.", var_name, NullS); - var->set_name(buff, end-buff, system_charset_info); + var->set_name(thd, buff, (uint)(end-buff), system_charset_info); add_item_to_list(thd, var); } DBUG_VOID_RETURN; @@ -6979,7 +7517,9 @@ void mysql_init_multi_delete(LEX *lex) } static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, - Parser_state *parser_state) + Parser_state *parser_state, + bool is_com_multi, + bool is_next_command) { #ifdef WITH_WSREP bool is_autocommit= @@ -7009,7 +7549,8 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, WSREP_DEBUG("Retry autocommit query: %s", thd->query()); } - mysql_parse(thd, rawbuf, length, parser_state); + mysql_parse(thd, rawbuf, length, parser_state, is_com_multi, + is_next_command); if (WSREP(thd)) { /* wsrep BF abort in query exec phase */ @@ -7063,12 +7604,14 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, { mysql_mutex_unlock(&thd->LOCK_thd_data); // This does dirty read to wsrep variables but it is only a debug code - WSREP_DEBUG("%s, thd: %lu is_AC: %d, retry: %lu - %lu SQL: %s", + WSREP_DEBUG("%s, thd: %lld is_AC: %d, retry: %lu - %lu SQL: %s", (thd->wsrep_conflict_state == ABORTED) ? "BF Aborted" : "cert failure", - thd->thread_id, is_autocommit, thd->wsrep_retry_counter, + (longlong) thd->thread_id, is_autocommit, + thd->wsrep_retry_counter, thd->variables.wsrep_retry_autocommit, thd->query()); - my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction"); + my_message(ER_LOCK_DEADLOCK, "Deadlock: wsrep aborted transaction", + MYF(0)); mysql_mutex_lock(&thd->LOCK_thd_data); thd->wsrep_conflict_state= NO_CONFLICT; @@ -7126,10 +7669,13 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, @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. + @param is_next_command there will be more command in the COM_MULTI batch */ void mysql_parse(THD *thd, char *rawbuf, uint length, - Parser_state *parser_state) + Parser_state *parser_state, + bool is_com_multi, + bool is_next_command) { int error __attribute__((unused)); DBUG_ENTER("mysql_parse"); @@ -7153,6 +7699,12 @@ void mysql_parse(THD *thd, char *rawbuf, uint length, */ lex_start(thd); thd->reset_for_next_command(); + if (is_next_command) + { + thd->server_status|= SERVER_MORE_RESULTS_EXISTS; + if (is_com_multi) + thd->get_stmt_da()->set_skip_flush(); + } if (query_cache_send_result_to_client(thd, rawbuf, length) <= 0) { @@ -7186,7 +7738,6 @@ void mysql_parse(THD *thd, char *rawbuf, uint length, 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. */ if (found_semicolon && (ulong) (found_semicolon - thd->query())) thd->set_query(thd->query(), @@ -7228,7 +7779,7 @@ void mysql_parse(THD *thd, char *rawbuf, uint length, sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size); thd->end_statement(); thd->cleanup_after_query(); - DBUG_ASSERT(thd->change_list.is_empty()); + DBUG_ASSERT(thd->Item_change_list::is_empty()); } else { @@ -7284,13 +7835,6 @@ bool mysql_test_parse_for_slave(THD *thd, char *rawbuf, uint length) #endif -/** Store position for column in ALTER TABLE .. ADD column. */ - -void store_position_for_column(const char *name) -{ - current_thd->lex->last_field->after=(char*) (name); -} - bool add_proc_to_list(THD* thd, Item *item) { @@ -7302,7 +7846,6 @@ add_proc_to_list(THD* thd, Item *item) item_ptr = (Item**) (order+1); *item_ptr= item; order->item=item_ptr; - order->free_me=0; thd->lex->proc_list.link_in_list(order, &order->next); return 0; } @@ -7320,8 +7863,7 @@ bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *item,bool asc) DBUG_RETURN(1); order->item_ptr= item; order->item= &order->item_ptr; - order->asc = asc; - order->free_me=0; + order->direction= (asc ? ORDER::ORDER_ASC : ORDER::ORDER_DESC); order->used=0; order->counter_used= 0; order->fast_field_copier_setup= 0; @@ -7427,7 +7969,6 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->derived= table->sel; if (!ptr->derived && is_infoschema_db(ptr->db, ptr->db_length)) { - ST_SCHEMA_TABLE *schema_table; if (ptr->updating && /* Special cases which are processed by commands itself */ lex->sql_command != SQLCOM_CHECK && @@ -7439,20 +7980,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, INFORMATION_SCHEMA_NAME.str); DBUG_RETURN(0); } + ST_SCHEMA_TABLE *schema_table; schema_table= find_schema_table(thd, ptr->table_name); - if (!schema_table || - (schema_table->hidden && - ((sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0 || - /* - this check is used for show columns|keys from I_S hidden table - */ - lex->sql_command == SQLCOM_SHOW_FIELDS || - lex->sql_command == SQLCOM_SHOW_KEYS))) - { - my_error(ER_UNKNOWN_TABLE, MYF(0), - ptr->table_name, INFORMATION_SCHEMA_NAME.str); - DBUG_RETURN(0); - } ptr->schema_table_name= ptr->table_name; ptr->schema_table= schema_table; } @@ -7518,7 +8047,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, lex->add_to_query_tables(ptr); // Pure table aliases do not need to be locked: - if (!MY_TEST(table_options & TL_OPTION_ALIAS)) + if (ptr->db && !(table_options & TL_OPTION_ALIAS)) { ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type, MDL_TRANSACTION); @@ -7973,6 +8502,65 @@ TABLE_LIST *st_select_lex::convert_right_join() DBUG_RETURN(tab1); } + +void st_select_lex::prepare_add_window_spec(THD *thd) +{ + LEX *lex= thd->lex; + lex->save_group_list= group_list; + lex->save_order_list= order_list; + lex->win_ref= NULL; + lex->win_frame= NULL; + lex->frame_top_bound= NULL; + lex->frame_bottom_bound= NULL; + group_list.empty(); + order_list.empty(); +} + +bool st_select_lex::add_window_def(THD *thd, + LEX_STRING *win_name, + LEX_STRING *win_ref, + SQL_I_List<ORDER> win_partition_list, + SQL_I_List<ORDER> win_order_list, + Window_frame *win_frame) +{ + SQL_I_List<ORDER> *win_part_list_ptr= + new (thd->mem_root) SQL_I_List<ORDER> (win_partition_list); + SQL_I_List<ORDER> *win_order_list_ptr= + new (thd->mem_root) SQL_I_List<ORDER> (win_order_list); + if (!(win_part_list_ptr && win_order_list_ptr)) + return true; + Window_def *win_def= new (thd->mem_root) Window_def(win_name, + win_ref, + win_part_list_ptr, + win_order_list_ptr, + win_frame); + group_list= thd->lex->save_group_list; + order_list= thd->lex->save_order_list; + return (win_def == NULL || window_specs.push_back(win_def)); +} + +bool st_select_lex::add_window_spec(THD *thd, + LEX_STRING *win_ref, + SQL_I_List<ORDER> win_partition_list, + SQL_I_List<ORDER> win_order_list, + Window_frame *win_frame) +{ + SQL_I_List<ORDER> *win_part_list_ptr= + new (thd->mem_root) SQL_I_List<ORDER> (win_partition_list); + SQL_I_List<ORDER> *win_order_list_ptr= + new (thd->mem_root) SQL_I_List<ORDER> (win_order_list); + if (!(win_part_list_ptr && win_order_list_ptr)) + return true; + Window_spec *win_spec= new (thd->mem_root) Window_spec(win_ref, + win_part_list_ptr, + win_order_list_ptr, + win_frame); + group_list= thd->lex->save_group_list; + order_list= thd->lex->save_order_list; + thd->lex->win_spec= win_spec; + return (win_spec == NULL || window_specs.push_back(win_spec)); +} + /** Set lock for all tables in current select level. @@ -9204,11 +9792,8 @@ bool check_string_char_length(LEX_STRING *str, uint 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) + Well_formed_prefix prefix(cs, str->str, str->length, max_char_length); + if (!prefix.well_formed_error_pos() && str->length == prefix.length()) return FALSE; if (!no_error) |