diff options
Diffstat (limited to 'sql')
48 files changed, 1468 insertions, 958 deletions
diff --git a/sql/cmakelists.txt b/sql/CMakeLists.txt index 2b44fbdcc79..2b44fbdcc79 100644 --- a/sql/cmakelists.txt +++ b/sql/CMakeLists.txt diff --git a/sql/Makefile.am b/sql/Makefile.am index 8f051b61e74..eec7209bf50 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -122,7 +122,7 @@ DEFS = -DMYSQL_SERVER \ BUILT_SOURCES = sql_yacc.cc sql_yacc.h lex_hash.h EXTRA_DIST = udf_example.cc $(BUILT_SOURCES) \ - nt_servc.cc nt_servc.h message.mc cmakelists.txt + nt_servc.cc nt_servc.h message.mc CMakeLists.txt CLEANFILES = lex_hash.h sql_yacc.cc sql_yacc.h AM_YFLAGS = -d diff --git a/sql/field_conv.cc b/sql/field_conv.cc index f718a3d778c..3eab782d167 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -641,7 +641,8 @@ void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*) void field_conv(Field *to,Field *from) { - if (to->real_type() == from->real_type()) + if (to->real_type() == from->real_type() && + !(to->type() == FIELD_TYPE_BLOB && to->table->copy_blobs)) { if (to->pack_length() == from->pack_length() && !(to->flags & UNSIGNED_FLAG && !(from->flags & UNSIGNED_FLAG)) && diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 786d45a4966..40081c975c8 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -1786,7 +1786,8 @@ bool ha_myisam::check_if_incompatible_data(HA_CREATE_INFO *info, if (info->auto_increment_value != auto_increment_value || info->data_file_name != data_file_name || info->index_file_name != index_file_name || - table_changes == IS_EQUAL_NO) + table_changes == IS_EQUAL_NO || + table_changes & IS_EQUAL_PACK_LENGTH) // Not implemented yet return COMPATIBLE_DATA_NO; if ((options & (HA_OPTION_PACK_RECORD | HA_OPTION_CHECKSUM | diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index 1cde37644bc..f4a052cea8a 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -74,7 +74,7 @@ handlerton myisammrg_hton= { NULL, /* Alter table flags */ NULL, /* Alter Tablespace */ NULL, /* Fill Files Table */ - HTON_CAN_RECREATE, + HTON_CAN_RECREATE | HTON_ALTER_CANNOT_CREATE, NULL, /* binlog_func */ NULL, /* binlog_log_query */ NULL /* release_temporary_latches */ diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 5a1d4f48c9b..f46a5eccabf 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -182,6 +182,8 @@ static const char * ndb_connected_host= 0; static long ndb_connected_port= 0; static long ndb_number_of_replicas= 0; long ndb_number_of_storage_nodes= 0; +long ndb_number_of_ready_storage_nodes= 0; +long ndb_connect_count= 0; static int update_status_variables(Ndb_cluster_connection *c) { @@ -190,6 +192,8 @@ static int update_status_variables(Ndb_cluster_connection *c) ndb_connected_host= c->get_connected_host(); ndb_number_of_replicas= 0; ndb_number_of_storage_nodes= c->no_db_nodes(); + ndb_number_of_ready_storage_nodes= c->get_no_ready(); + ndb_connect_count= c->get_connect_count(); return 0; } @@ -7128,10 +7132,6 @@ void ndbcluster_real_free_share(NDB_SHARE **share) #ifndef DBUG_OFF bzero((gptr)(*share)->table_share, sizeof(*(*share)->table_share)); bzero((gptr)(*share)->table, sizeof(*(*share)->table)); -#endif - my_free((gptr) (*share)->table_share, MYF(0)); - my_free((gptr) (*share)->table, MYF(0)); -#ifndef DBUG_OFF (*share)->table_share= 0; (*share)->table= 0; #endif @@ -9361,11 +9361,15 @@ ndbcluster_show_status(THD* thd, stat_print_fn *stat_print, "cluster_node_id=%u, " "connected_host=%s, " "connected_port=%u, " - "number_of_storage_nodes=%u", + "number_of_storage_nodes=%u, " + "number_of_ready_storage_nodes=%u, " + "connect_count=%u", ndb_cluster_node_id, ndb_connected_host, ndb_connected_port, - ndb_number_of_storage_nodes); + ndb_number_of_storage_nodes, + ndb_number_of_ready_storage_nodes, + ndb_connect_count); if (stat_print(thd, ndbcluster_hton.name, strlen(ndbcluster_hton.name), "connection", strlen("connection"), buf, buflen)) diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index f407cb0090f..78c9ec765bb 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -113,6 +113,7 @@ typedef struct st_ndbcluster_share { char *old_names; // for rename table TABLE_SHARE *table_share; TABLE *table; + byte *record[2]; // pointer to allocated records for receiving data NdbValue *ndb_value[2]; MY_BITMAP *subscriber_bitmap; #endif diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index e358c87b378..144c073d565 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -25,6 +25,7 @@ #include "slave.h" #include "ha_ndbcluster_binlog.h" #include "NdbDictionary.hpp" +#include <util/NdbAutoPtr.hpp> #ifdef ndb_dynamite #undef assert @@ -265,7 +266,8 @@ ndbcluster_binlog_close_table(THD *thd, NDB_SHARE *share) static int ndbcluster_binlog_open_table(THD *thd, NDB_SHARE *share, - TABLE_SHARE *table_share, TABLE *table) + TABLE_SHARE *table_share, TABLE *table, + int reopen) { int error; DBUG_ENTER("ndbcluster_binlog_open_table"); @@ -278,27 +280,34 @@ ndbcluster_binlog_open_table(THD *thd, NDB_SHARE *share, share->key, error); DBUG_PRINT("error", ("open_table_def failed %d", error)); free_table_share(table_share); - my_free((gptr) table_share, MYF(0)); - my_free((gptr) table, MYF(0)); DBUG_RETURN(error); } - if ((error= open_table_from_share(thd, table_share, "", 0, + if ((error= open_table_from_share(thd, table_share, "", 0 /* fon't allocate buffers */, (uint) READ_ALL, 0, table, FALSE))) { sql_print_error("Unable to open table for %s, error=%d(%d)", share->key, error, my_errno); DBUG_PRINT("error", ("open_table_from_share failed %d", error)); free_table_share(table_share); - my_free((gptr) table_share, MYF(0)); - my_free((gptr) table, MYF(0)); DBUG_RETURN(error); } assign_new_table_id(table_share); - if (!table->record[1] || table->record[1] == table->record[0]) + + if (!reopen) + { + // allocate memory on ndb share so it can be reused after online alter table + share->record[0]= (byte*) alloc_root(&share->mem_root, table->s->rec_buff_length); + share->record[1]= (byte*) alloc_root(&share->mem_root, table->s->rec_buff_length); + } { - table->record[1]= alloc_root(&table->mem_root, - table->s->rec_buff_length); + my_ptrdiff_t row_offset= share->record[0] - table->record[0]; + Field **p_field; + for (p_field= table->field; *p_field; p_field++) + (*p_field)->move_field_offset(row_offset); + table->record[0]= share->record[0]; + table->record[1]= share->record[1]; } + table->in_use= injector_thd; table->s->db.str= share->db; @@ -366,10 +375,9 @@ void ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table) while (1) { int error; - TABLE_SHARE *table_share= - (TABLE_SHARE *) my_malloc(sizeof(*table_share), MYF(MY_WME)); - TABLE *table= (TABLE*) my_malloc(sizeof(*table), MYF(MY_WME)); - if ((error= ndbcluster_binlog_open_table(thd, share, table_share, table))) + TABLE_SHARE *table_share= (TABLE_SHARE *) alloc_root(mem_root, sizeof(*table_share)); + TABLE *table= (TABLE*) alloc_root(mem_root, sizeof(*table)); + if ((error= ndbcluster_binlog_open_table(thd, share, table_share, table, 0))) break; /* ! do not touch the contents of the table @@ -1535,6 +1543,10 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp, sql_print_information("NDB: Failed write frm for %s.%s, error %d", dbname, tabname, error); } + + // copy names as memory will be freed + NdbAutoPtr<char> a1((char *)(dbname= strdup(dbname))); + NdbAutoPtr<char> a2((char *)(tabname= strdup(tabname))); ndbcluster_binlog_close_table(thd, share); TABLE_LIST table_list; @@ -1543,10 +1555,16 @@ ndb_handle_schema_change(THD *thd, Ndb *ndb, NdbEventOperation *pOp, table_list.alias= table_list.table_name= (char *)tabname; close_cached_tables(thd, 0, &table_list, TRUE); - if ((error= ndbcluster_binlog_open_table(thd, share, - table_share, table))) + if ((error= ndbcluster_binlog_open_table(thd, share, + table_share, table, 1))) sql_print_information("NDB: Failed to re-open table %s.%s", dbname, tabname); + + table= share->table; + table_share= share->table_share; + dbname= table_share->db.str; + tabname= table_share->table_name.str; + pthread_mutex_unlock(&LOCK_open); } my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR)); @@ -1775,6 +1793,9 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, // skip break; case NDBEVENT::TE_CLUSTER_FAILURE: + if (ndb_extra_logging) + sql_print_information("NDB Binlog: cluster failure for %s at epoch %u.", + schema_share->key, (unsigned) pOp->getGCI()); // fall through case NDBEVENT::TE_DROP: if (ndb_extra_logging && @@ -1783,7 +1804,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, "read only on reconnect."); free_share(&schema_share); schema_share= 0; - ndb_binlog_tables_inited= FALSE; + close_cached_tables((THD*) 0, 0, (TABLE_LIST*) 0, FALSE); // fall through case NDBEVENT::TE_ALTER: ndb_handle_schema_change(thd, ndb, pOp, tmp_share); @@ -2101,7 +2122,14 @@ add_binlog_index_err: Functions for start, stop, wait for ndbcluster binlog thread *********************************************************************/ -static int do_ndbcluster_binlog_close_connection= 0; +enum Binlog_thread_state +{ + BCCC_running= 0, + BCCC_exit= 1, + BCCC_restart= 2 +}; + +static enum Binlog_thread_state do_ndbcluster_binlog_close_connection= BCCC_restart; int ndbcluster_binlog_start() { @@ -2139,7 +2167,7 @@ static void ndbcluster_binlog_close_connection(THD *thd) DBUG_ENTER("ndbcluster_binlog_close_connection"); const char *save_info= thd->proc_info; thd->proc_info= "ndbcluster_binlog_close_connection"; - do_ndbcluster_binlog_close_connection= 1; + do_ndbcluster_binlog_close_connection= BCCC_exit; while (ndb_binlog_thread_running > 0) sleep(1); thd->proc_info= save_info; @@ -2819,7 +2847,8 @@ ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb, { case NDBEVENT::TE_CLUSTER_FAILURE: if (ndb_extra_logging) - sql_print_information("NDB Binlog: cluster failure for %s.", share->key); + sql_print_information("NDB Binlog: cluster failure for %s at epoch %u.", + share->key, (unsigned) pOp->getGCI()); if (apply_status_share == share) { if (ndb_extra_logging && @@ -2828,7 +2857,6 @@ ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb, "read only on reconnect."); free_share(&apply_status_share); apply_status_share= 0; - ndb_binlog_tables_inited= FALSE; } DBUG_PRINT("info", ("CLUSTER FAILURE EVENT: " "%s received share: 0x%lx op: %lx share op: %lx " @@ -2844,7 +2872,6 @@ ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb, "read only on reconnect."); free_share(&apply_status_share); apply_status_share= 0; - ndb_binlog_tables_inited= FALSE; } /* ToDo: remove printout */ if (ndb_extra_logging) @@ -3257,37 +3284,73 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) pthread_mutex_unlock(&injector_mutex); pthread_cond_signal(&injector_cond); - thd->proc_info= "Waiting for ndbcluster to start"; - - pthread_mutex_lock(&injector_mutex); - while (!schema_share || - (ndb_binlog_running && !apply_status_share)) +restart: + /* + Main NDB Injector loop + */ { - /* ndb not connected yet */ - struct timespec abstime; - set_timespec(abstime, 1); - pthread_cond_timedwait(&injector_cond, &injector_mutex, &abstime); - if (abort_loop) + thd->proc_info= "Waiting for ndbcluster to start"; + + pthread_mutex_lock(&injector_mutex); + while (!schema_share || + (ndb_binlog_running && !apply_status_share)) { - pthread_mutex_unlock(&injector_mutex); - goto err; + /* ndb not connected yet */ + struct timespec abstime; + set_timespec(abstime, 1); + pthread_cond_timedwait(&injector_cond, &injector_mutex, &abstime); + if (abort_loop) + { + pthread_mutex_unlock(&injector_mutex); + goto err; + } } - } - pthread_mutex_unlock(&injector_mutex); + pthread_mutex_unlock(&injector_mutex); - /* - Main NDB Injector loop - */ + if (thd_ndb == NULL) + { + DBUG_ASSERT(ndbcluster_hton.slot != ~(uint)0); + if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb())) + { + sql_print_error("Could not allocate Thd_ndb object"); + goto err; + } + set_thd_ndb(thd, thd_ndb); + thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP; + thd->query_id= 0; // to keep valgrind quiet + } + } - DBUG_ASSERT(ndbcluster_hton.slot != ~(uint)0); - if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb())) { - sql_print_error("Could not allocate Thd_ndb object"); - goto err; + // wait for the first event + thd->proc_info= "Waiting for first event from ndbcluster"; + DBUG_PRINT("info", ("Waiting for the first event")); + int schema_res= 0; + Uint64 schema_gci= 0; + while (schema_res == 0 && !abort_loop) + { + schema_res= s_ndb->pollEvents(100, &schema_gci); + } + // now check that we have epochs consistant with what we had before the restart + DBUG_PRINT("info", ("schema_res: %d schema_gci: %d", schema_res, schema_gci)); + if (schema_res > 0) + { + i_ndb->pollEvents(0); + i_ndb->flushIncompleteEvents(schema_gci); + s_ndb->flushIncompleteEvents(schema_gci); + if (schema_gci < ndb_latest_handled_binlog_epoch) + { + sql_print_error("NDB Binlog: cluster has been restarted --initial or with older filesystem. " + "ndb_latest_handled_binlog_epoch: %u, while current epoch: %u. " + "RESET MASTER should be issued. Resetting ndb_latest_handled_binlog_epoch.", + (unsigned) ndb_latest_handled_binlog_epoch, (unsigned) schema_gci); + g_latest_trans_gci= 0; + ndb_latest_handled_binlog_epoch= 0; + ndb_latest_applied_binlog_epoch= 0; + ndb_latest_received_binlog_epoch= 0; + } + } } - set_thd_ndb(thd, thd_ndb); - thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP; - thd->query_id= 0; // to keep valgrind quiet { static char db[]= ""; thd->db= db; @@ -3295,11 +3358,20 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) open_binlog_index(thd, &binlog_tables, &binlog_index); thd->db= db; } - + do_ndbcluster_binlog_close_connection= BCCC_running; for ( ; !((abort_loop || do_ndbcluster_binlog_close_connection) && - ndb_latest_handled_binlog_epoch >= g_latest_trans_gci); ) + ndb_latest_handled_binlog_epoch >= g_latest_trans_gci) && + do_ndbcluster_binlog_close_connection != BCCC_restart; ) { - +#ifndef DBUG_OFF + if (do_ndbcluster_binlog_close_connection) + { + DBUG_PRINT("info", ("do_ndbcluster_binlog_close_connection: %d, " + "ndb_latest_handled_binlog_epoch: %llu, " + "g_latest_trans_gci: %llu", do_ndbcluster_binlog_close_connection, + ndb_latest_handled_binlog_epoch, g_latest_trans_gci)); + } +#endif #ifdef RUN_NDB_BINLOG_TIMER main_timer.stop(); sql_print_information("main_timer %ld ms", main_timer.elapsed_ms()); @@ -3324,7 +3396,13 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) ndb_latest_received_binlog_epoch= gci; while (gci > schema_gci && schema_res >= 0) + { + static char buf[64]; + thd->proc_info= "Waiting for schema epoch"; + my_snprintf(buf, sizeof(buf), "%s %u(%u)", thd->proc_info, (unsigned) schema_gci, (unsigned) gci); + thd->proc_info= buf; schema_res= s_ndb->pollEvents(10, &schema_gci); + } if ((abort_loop || do_ndbcluster_binlog_close_connection) && (ndb_latest_handled_binlog_epoch >= g_latest_trans_gci || @@ -3360,10 +3438,31 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) while (pOp != NULL) { if (!pOp->hasError()) + { ndb_binlog_thread_handle_schema_event(thd, s_ndb, pOp, &post_epoch_log_list, &post_epoch_unlock_list, &mem_root); + DBUG_PRINT("info", ("s_ndb first: %s", s_ndb->getEventOperation() ? + s_ndb->getEventOperation()->getEvent()->getTable()->getName() : + "<empty>")); + DBUG_PRINT("info", ("i_ndb first: %s", i_ndb->getEventOperation() ? + i_ndb->getEventOperation()->getEvent()->getTable()->getName() : + "<empty>")); + if (i_ndb->getEventOperation() == NULL && + s_ndb->getEventOperation() == NULL && + do_ndbcluster_binlog_close_connection == BCCC_running) + { + DBUG_PRINT("info", ("do_ndbcluster_binlog_close_connection= BCCC_restart")); + do_ndbcluster_binlog_close_connection= BCCC_restart; + if (ndb_latest_received_binlog_epoch < g_latest_trans_gci && ndb_binlog_running) + { + sql_print_error("NDB Binlog: latest transaction in epoch %lld not in binlog " + "as latest received epoch is %lld", + g_latest_trans_gci, ndb_latest_received_binlog_epoch); + } + } + } else sql_print_error("NDB: error %lu (%s) on handling " "binlog schema event", @@ -3532,6 +3631,25 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) ndb_binlog_thread_handle_non_data_event(thd, i_ndb, pOp, row); // reset to catch errors i_ndb->setDatabaseName(""); + DBUG_PRINT("info", ("s_ndb first: %s", s_ndb->getEventOperation() ? + s_ndb->getEventOperation()->getEvent()->getTable()->getName() : + "<empty>")); + DBUG_PRINT("info", ("i_ndb first: %s", i_ndb->getEventOperation() ? + i_ndb->getEventOperation()->getEvent()->getTable()->getName() : + "<empty>")); + if (i_ndb->getEventOperation() == NULL && + s_ndb->getEventOperation() == NULL && + do_ndbcluster_binlog_close_connection == BCCC_running) + { + DBUG_PRINT("info", ("do_ndbcluster_binlog_close_connection= BCCC_restart")); + do_ndbcluster_binlog_close_connection= BCCC_restart; + if (ndb_latest_received_binlog_epoch < g_latest_trans_gci && ndb_binlog_running) + { + sql_print_error("NDB Binlog: latest transaction in epoch %lld not in binlog " + "as latest received epoch is %lld", + g_latest_trans_gci, ndb_latest_received_binlog_epoch); + } + } } pOp= i_ndb->nextEvent(); @@ -3587,6 +3705,13 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) *root_ptr= old_root; ndb_latest_handled_binlog_epoch= ndb_latest_received_binlog_epoch; } + if (do_ndbcluster_binlog_close_connection == BCCC_restart) + { + ndb_binlog_tables_inited= FALSE; + close_thread_tables(thd); + binlog_index= 0; + goto restart; + } err: DBUG_PRINT("info",("Shutting down cluster binlog thread")); thd->proc_info= "Shutting down"; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index b1a5a447b6f..7b9dd00ed56 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4175,6 +4175,8 @@ void ha_partition::info(uint flag) index_file_length: Length of index file, in principle bytes in indexes in the table We report sum + delete_length: Length of free space easily used by new records in table + We report sum mean_record_length:Mean record length in the table We calculate this check_time: Time of last check (only applicable to MyISAM) @@ -4184,6 +4186,7 @@ void ha_partition::info(uint flag) deleted= 0; data_file_length= 0; index_file_length= 0; + delete_length= 0; check_time= 0; file_array= m_file; do @@ -4196,6 +4199,7 @@ void ha_partition::info(uint flag) deleted+= file->deleted; data_file_length+= file->data_file_length; index_file_length+= file->index_file_length; + delete_length+= file->delete_length; if (file->check_time > check_time) check_time= file->check_time; } @@ -5325,6 +5329,82 @@ void ha_partition::init_table_handle_for_HANDLER() /**************************************************************************** + MODULE enable/disable indexes +****************************************************************************/ + +/* + Disable indexes for a while + SYNOPSIS + disable_indexes() + mode Mode + RETURN VALUES + 0 Success + != 0 Error +*/ + +int ha_partition::disable_indexes(uint mode) +{ + handler **file; + int error= 0; + + for (file= m_file; *file; file++) + { + if ((error= (*file)->disable_indexes(mode))) + break; + } + return error; +} + + +/* + Enable indexes again + SYNOPSIS + enable_indexes() + mode Mode + RETURN VALUES + 0 Success + != 0 Error +*/ + +int ha_partition::enable_indexes(uint mode) +{ + handler **file; + int error= 0; + + for (file= m_file; *file; file++) + { + if ((error= (*file)->enable_indexes(mode))) + break; + } + return error; +} + + +/* + Check if indexes are disabled + SYNOPSIS + indexes_are_disabled() + + RETURN VALUES + 0 Indexes are enabled + != 0 Indexes are disabled +*/ + +int ha_partition::indexes_are_disabled(void) +{ + handler **file; + int error= 0; + + for (file= m_file; *file; file++) + { + if ((error= (*file)->indexes_are_disabled())) + break; + } + return error; +} + + +/**************************************************************************** MODULE Partition Share ****************************************************************************/ /* diff --git a/sql/ha_partition.h b/sql/ha_partition.h index b31b9af28a3..1443a20133c 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -938,17 +938,18 @@ public: virtual uint checksum() const; virtual bool is_crashed() const; virtual bool auto_repair() const; + */ + /* ------------------------------------------------------------------------- MODULE enable/disable indexes ------------------------------------------------------------------------- - Enable/Disable Indexes are not supported currently (Heap, MyISAM) - This means that the following methods are not implemented: + Enable/Disable Indexes are only supported by HEAP and MyISAM. ------------------------------------------------------------------------- + */ virtual int disable_indexes(uint mode); virtual int enable_indexes(uint mode); virtual int indexes_are_disabled(void); - */ /* ------------------------------------------------------------------------- diff --git a/sql/handler.h b/sql/handler.h index d988e46b236..afee6bb9f8d 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -603,6 +603,7 @@ struct show_table_alias_st { #define HTON_FLUSH_AFTER_RENAME (1 << 4) #define HTON_NOT_USER_SELECTABLE (1 << 5) #define HTON_TEMPORARY_NOT_SUPPORTED (1 << 6) //Having temporary tables not supported +#define HTON_ALTER_CANNOT_CREATE (1 << 7) //Cannot use alter to create typedef struct st_thd_trans { diff --git a/sql/item.cc b/sql/item.cc index f778f0cb38e..d596699dd30 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -304,6 +304,7 @@ Item::Item(): marker= 0; maybe_null=null_value=with_sum_func=unsigned_flag=0; decimals= 0; max_length= 0; + with_subselect= 0; /* Put item in free list so that we can free all items at end */ THD *thd= current_thd; @@ -961,6 +962,12 @@ void Item_splocal::print(String *str) } +bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item **it) +{ + return ctx->set_variable(thd, get_var_idx(), it); +} + + /***************************************************************************** Item_case_expr methods *****************************************************************************/ @@ -1890,7 +1897,6 @@ Item_decimal::Item_decimal(const char *str_arg, uint length, name= (char*) str_arg; decimals= (uint8) decimal_value.frac; fixed= 1; - unsigned_flag= !decimal_value.sign(); max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, decimals, unsigned_flag); } @@ -1900,7 +1906,6 @@ Item_decimal::Item_decimal(longlong val, bool unsig) int2my_decimal(E_DEC_FATAL_ERROR, val, unsig, &decimal_value); decimals= (uint8) decimal_value.frac; fixed= 1; - unsigned_flag= !decimal_value.sign(); max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, decimals, unsigned_flag); } @@ -1911,7 +1916,6 @@ Item_decimal::Item_decimal(double val, int precision, int scale) double2my_decimal(E_DEC_FATAL_ERROR, val, &decimal_value); decimals= (uint8) decimal_value.frac; fixed= 1; - unsigned_flag= !decimal_value.sign(); max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, decimals, unsigned_flag); } @@ -1924,7 +1928,6 @@ Item_decimal::Item_decimal(const char *str, const my_decimal *val_arg, name= (char*) str; decimals= (uint8) decimal_par; max_length= length; - unsigned_flag= !decimal_value.sign(); fixed= 1; } @@ -1934,7 +1937,6 @@ Item_decimal::Item_decimal(my_decimal *value_par) my_decimal2decimal(value_par, &decimal_value); decimals= (uint8) decimal_value.frac; fixed= 1; - unsigned_flag= !decimal_value.sign(); max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, decimals, unsigned_flag); } @@ -1946,7 +1948,6 @@ Item_decimal::Item_decimal(const char *bin, int precision, int scale) &decimal_value, precision, scale); decimals= (uint8) decimal_value.frac; fixed= 1; - unsigned_flag= !decimal_value.sign(); max_length= my_decimal_precision_to_length(precision, decimals, unsigned_flag); } @@ -4881,7 +4882,16 @@ void Item_ref::cleanup() void Item_ref::print(String *str) { if (ref) - (*ref)->print(str); + { + if ((*ref)->type() != Item::CACHE_ITEM && ref_type() != VIEW_REF && + name && alias_name_used) + { + THD *thd= current_thd; + append_identifier(thd, str, name, (uint) strlen(name)); + } + else + (*ref)->print(str); + } else Item_ident::print(str); } @@ -5413,6 +5423,25 @@ bool Item_trigger_field::eq(const Item *item, bool binary_cmp) const } +void Item_trigger_field::set_required_privilege(bool rw) +{ + /* + Require SELECT and UPDATE privilege if this field will be read and + set, and only UPDATE privilege for setting the field. + */ + want_privilege= (rw ? SELECT_ACL | UPDATE_ACL : UPDATE_ACL); +} + + +bool Item_trigger_field::set_value(THD *thd, sp_rcontext */*ctx*/, Item **it) +{ + Item *item= sp_prepare_func_item(thd, it); + + return (!item || (!fixed && fix_fields(thd, 0)) || + (item->save_in_field(field, 0) < 0)); +} + + bool Item_trigger_field::fix_fields(THD *thd, Item **items) { /* @@ -5435,8 +5464,7 @@ bool Item_trigger_field::fix_fields(THD *thd, Item **items) if (table_grants) { - table_grants->want_privilege= - access_type == AT_READ ? SELECT_ACL : UPDATE_ACL; + table_grants->want_privilege= want_privilege; if (check_grant_column(thd, table_grants, triggers->table->s->db.str, triggers->table->s->table_name.str, field_name, @@ -5468,6 +5496,7 @@ void Item_trigger_field::print(String *str) void Item_trigger_field::cleanup() { + want_privilege= original_privilege; /* Since special nature of Item_trigger_field we should not do most of things from Item_field::cleanup() or Item_ident::cleanup() here. diff --git a/sql/item.h b/sql/item.h index f73017563dd..2f99034130a 100644 --- a/sql/item.h +++ b/sql/item.h @@ -401,6 +401,42 @@ typedef enum monotonicity_info /*************************************************************************/ +class sp_rcontext; + + +class Settable_routine_parameter +{ +public: + /* + Set required privileges for accessing the parameter. + + SYNOPSIS + set_required_privilege() + rw if 'rw' is true then we are going to read and set the + parameter, so SELECT and UPDATE privileges might be + required, otherwise we only reading it and SELECT + privilege might be required. + */ + virtual void set_required_privilege(bool rw) {}; + + /* + Set parameter value. + + SYNOPSIS + set_value() + thd thread handle + ctx context to which parameter belongs (if it is local + variable). + it item which represents new value + + RETURN + FALSE if parameter value has been set, + TRUE if error has occured. + */ + virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it)= 0; +}; + + typedef bool (Item::*Item_processor)(byte *arg); typedef Item* (Item::*Item_transformer) (byte *arg); typedef void (*Cond_traverser) (const Item *item, void *arg); @@ -454,6 +490,9 @@ public: my_bool is_autogenerated_name; /* indicate was name of this Item autogenerated or set by user */ DTCollation collation; + my_bool with_subselect; /* If this item is a subselect or some + of its arguments is or contains a + subselect */ // alloc & destruct is done as start of select using sql_alloc Item(); @@ -784,6 +823,15 @@ public: } virtual bool is_splocal() { return 0; } /* Needed for error checking */ + + /* + Return Settable_routine_parameter interface of the Item. Return 0 + if this Item is not Settable_routine_parameter. + */ + virtual Settable_routine_parameter *get_settable_routine_parameter() + { + return 0; + } }; @@ -882,7 +930,8 @@ inline bool Item_sp_variable::send(Protocol *protocol, String *str) runtime. *****************************************************************************/ -class Item_splocal :public Item_sp_variable +class Item_splocal :public Item_sp_variable, + private Settable_routine_parameter { uint m_var_idx; @@ -920,6 +969,15 @@ public: inline enum Type type() const; inline Item_result result_type() const; + +private: + bool set_value(THD *thd, sp_rcontext *ctx, Item **it); + +public: + Settable_routine_parameter *get_settable_routine_parameter() + { + return this; + } }; /***************************************************************************** @@ -2146,14 +2204,13 @@ class Table_triggers_list; two Field instances representing either OLD or NEW version of this field. */ -class Item_trigger_field : public Item_field +class Item_trigger_field : public Item_field, + private Settable_routine_parameter { public: /* Is this item represents row from NEW or OLD row ? */ enum row_version_type {OLD_ROW, NEW_ROW}; row_version_type row_version; - /* Is this item used for reading or updating the value? */ - enum access_types { AT_READ = 0x1, AT_UPDATE = 0x2 }; /* Next in list of all Item_trigger_field's in trigger */ Item_trigger_field *next_trg_field; /* Index of the field in the TABLE::field array */ @@ -2164,11 +2221,11 @@ public: Item_trigger_field(Name_resolution_context *context_arg, row_version_type row_ver_arg, const char *field_name_arg, - access_types access_type_arg) + ulong priv, const bool ro) :Item_field(context_arg, (const char *)NULL, (const char *)NULL, field_name_arg), - row_version(row_ver_arg), field_idx((uint)-1), - access_type(access_type_arg), table_grants(NULL) + row_version(row_ver_arg), field_idx((uint)-1), original_privilege(priv), + want_privilege(priv), table_grants(NULL), read_only (ro) {} void setup_field(THD *thd, TABLE *table, GRANT_INFO *table_grant_info); enum Type type() const { return TRIGGER_FIELD_ITEM; } @@ -2179,8 +2236,39 @@ public: void cleanup(); private: - access_types access_type; + void set_required_privilege(bool rw); + bool set_value(THD *thd, sp_rcontext *ctx, Item **it); + +public: + Settable_routine_parameter *get_settable_routine_parameter() + { + return (read_only ? 0 : this); + } + + bool set_value(THD *thd, Item **it) + { + return set_value(thd, NULL, it); + } + +private: + /* + 'want_privilege' holds privileges required to perform operation on + this trigger field (SELECT_ACL if we are going to read it and + UPDATE_ACL if we are going to update it). It is initialized at + parse time but can be updated later if this trigger field is used + as OUT or INOUT parameter of stored routine (in this case + set_required_privilege() is called to appropriately update + want_privilege and cleanup() is responsible for restoring of + original want_privilege once parameter's value is updated). + */ + ulong original_privilege; + ulong want_privilege; GRANT_INFO *table_grants; + /* + Trigger field is read-only unless it belongs to the NEW row in a + BEFORE INSERT of BEFORE UPDATE trigger. + */ + bool read_only; }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index eb26f7ff960..0552e8b6336 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -204,10 +204,28 @@ longlong Item_func_nop_all::val_int() /* - Convert a constant expression or string to an integer. - This is done when comparing DATE's of different formats and - also when comparing bigint to strings (in which case the string - is converted once to a bigint). + Convert a constant item to an int and replace the original item + + SYNOPSIS + convert_constant_item() + thd thread handle + field item will be converted using the type of this field + item [in/out] reference to the item to convert + + DESCRIPTION + The function converts a constant expression or string to an integer. + On successful conversion the original item is substituted for the + result of the item evaluation. + This is done when comparing DATE/TIME of different formats and + also when comparing bigint to strings (in which case strings + are converted to bigints). + + NOTES + This function is called only at prepare stage. + As all derived tables are filled only after all derived tables + are prepared we do not evaluate items with subselects here because + they can contain derived tables and thus we may attempt to use a + table that has not been populated yet. RESULT VALUES 0 Can't convert item @@ -216,7 +234,7 @@ longlong Item_func_nop_all::val_int() static bool convert_constant_item(THD *thd, Field *field, Item **item) { - if ((*item)->const_item()) + if (!(*item)->with_subselect && (*item)->const_item()) { /* For comparison purposes allow invalid dates like 2000-01-32 */ ulong orig_sql_mode= field->table->in_use->variables.sql_mode; @@ -2570,7 +2588,9 @@ Item_cond::fix_fields(THD *thd, Item **ref) (item= *li.ref())->check_cols(1)) return TRUE; /* purecov: inspected */ used_tables_cache|= item->used_tables(); - if (!item->const_item()) + if (item->const_item()) + and_tables_cache= (table_map) 0; + else { tmp_table_map= item->not_null_tables(); not_null_tables_cache|= tmp_table_map; @@ -2578,6 +2598,7 @@ Item_cond::fix_fields(THD *thd, Item **ref) const_item_cache= FALSE; } with_sum_func= with_sum_func || item->with_sum_func; + with_subselect|= item->with_subselect; if (item->maybe_null) maybe_null=1; } diff --git a/sql/item_func.cc b/sql/item_func.cc index 9281a8a1ddf..acdaa4b246d 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -184,6 +184,7 @@ Item_func::fix_fields(THD *thd, Item **ref) used_tables_cache|= item->used_tables(); not_null_tables_cache|= item->not_null_tables(); const_item_cache&= item->const_item(); + with_subselect|= item->with_subselect; } } fix_length_and_dec(); @@ -2736,9 +2737,10 @@ String *udf_handler::val_str(String *str,String *save_str) { uchar is_null_tmp=0; ulong res_length; + DBUG_ENTER("udf_handler::val_str"); if (get_arguments()) - return 0; + DBUG_RETURN(0); char * (*func)(UDF_INIT *, UDF_ARGS *, char *, ulong *, uchar *, uchar *)= (char* (*)(UDF_INIT *, UDF_ARGS *, char *, ulong *, uchar *, uchar *)) u_d->func; @@ -2748,22 +2750,26 @@ String *udf_handler::val_str(String *str,String *save_str) if (str->alloc(MAX_FIELD_WIDTH)) { error=1; - return 0; + DBUG_RETURN(0); } } char *res=func(&initid, &f_args, (char*) str->ptr(), &res_length, &is_null_tmp, &error); + DBUG_PRINT("info", ("udf func returned, res_length: %lu", res_length)); if (is_null_tmp || !res || error) // The !res is for safety { - return 0; + DBUG_PRINT("info", ("Null or error")); + DBUG_RETURN(0); } if (res == str->ptr()) { str->length(res_length); - return str; + DBUG_PRINT("exit", ("str: %s", str->ptr())); + DBUG_RETURN(str); } save_str->set(res, res_length, str->charset()); - return save_str; + DBUG_PRINT("exit", ("save_str: %s", save_str->ptr())); + DBUG_RETURN(save_str); } @@ -3013,6 +3019,7 @@ void item_user_lock_free(void) void item_user_lock_release(User_level_lock *ull) { ull->locked=0; + ull->thread_id= 0; if (--ull->count) pthread_cond_signal(&ull->cond); else @@ -3220,6 +3227,7 @@ longlong Item_func_get_lock::val_int() { ull->locked=1; ull->thread=thd->real_id; + ull->thread_id= thd->thread_id; thd->ull=ull; error=0; } @@ -3946,14 +3954,24 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command, sql_set_variables(), we could instead manually call check() and update(); this would save memory and time; but calling sql_set_variables() makes one unique place to maintain (sql_set_variables()). + + Manipulation with lex is necessary since free_underlaid_joins + is going to release memory belonging to the main query. */ List<set_var_base> tmp_var_list; + LEX *sav_lex= thd->lex, lex_tmp; + thd->lex= &lex_tmp; + lex_start(thd, NULL, 0); tmp_var_list.push_back(new set_var_user(new Item_func_set_user_var(name, new Item_null()))); /* Create the variable */ if (sql_set_variables(thd, &tmp_var_list)) + { + thd->lex= sav_lex; goto err; + } + thd->lex= sav_lex; if (!(var_entry= get_variable(&thd->user_vars, name, 0))) goto err; } @@ -4105,6 +4123,18 @@ bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const } +bool Item_func_get_user_var::set_value(THD *thd, + sp_rcontext */*ctx*/, Item **it) +{ + Item_func_set_user_var *suv= new Item_func_set_user_var(get_name(), *it); + /* + Item_func_set_user_var is not fixed after construction, call + fix_fields(). + */ + return (!suv || suv->fix_fields(thd, it) || suv->check() || suv->update()); +} + + bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); @@ -4737,6 +4767,7 @@ Item_func_sp::sp_result_field(void) const dummy_table->alias= empty_name; dummy_table->maybe_null= maybe_null; dummy_table->in_use= current_thd; + dummy_table->copy_blobs= TRUE; dummy_table->s->table_cache_key.str = empty_name; dummy_table->s->table_name.str= empty_name; dummy_table->s->db.str= empty_name; diff --git a/sql/item_func.h b/sql/item_func.h index a91d93be8c6..1d8a1bd5e22 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1179,7 +1179,8 @@ public: }; -class Item_func_get_user_var :public Item_func +class Item_func_get_user_var :public Item_func, + private Settable_routine_parameter { user_var_entry *var_entry; @@ -1206,6 +1207,15 @@ public: table_map used_tables() const { return const_item() ? 0 : RAND_TABLE_BIT; } bool eq(const Item *item, bool binary_cmp) const; + +private: + bool set_value(THD *thd, sp_rcontext *ctx, Item **it); + +public: + Settable_routine_parameter *get_settable_routine_parameter() + { + return this; + } }; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 1e483f32b02..eb1fbf4855d 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2579,7 +2579,7 @@ String *Item_load_file::val_str(String *str) (void) fn_format(path, file_name->c_ptr(), mysql_real_data_home, "", MY_RELATIVE_PATH | MY_UNPACK_FILENAME); - if (!my_stat(path, &stat_info, MYF(MY_WME))) + if (!my_stat(path, &stat_info, MYF(0))) goto err; if (!(stat_info.st_mode & S_IROTH)) diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 7d7b62df0dc..90d421a2c68 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -542,7 +542,7 @@ public: void fix_length_and_dec() { collation.set(default_charset()); - decimals=0; max_length=64; + max_length= 64; } }; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 6c2ff19825f..5280dbf6813 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -39,6 +39,7 @@ Item_subselect::Item_subselect(): engine(0), old_engine(0), used_tables_cache(0), have_to_be_excluded(0), const_item_cache(1), engine_changed(0), changed(0) { + with_subselect= 1; reset(); /* item value is NULL if select_subselect not changed this value @@ -1354,6 +1355,17 @@ void Item_in_subselect::print(String *str) } +bool Item_in_subselect::fix_fields(THD *thd, Item **ref) +{ + bool result = 0; + + if(thd->lex->view_prepare_mode && left_expr && !left_expr->fixed) + result = left_expr->fix_fields(thd, &left_expr); + + return result || Item_subselect::fix_fields(thd, ref); +} + + Item_subselect::trans_res Item_allany_subselect::select_transformer(JOIN *join) { diff --git a/sql/item_subselect.h b/sql/item_subselect.h index a4dac5bda87..293408dc09e 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -258,6 +258,7 @@ public: void top_level_item() { abort_on_null=1; } bool test_limit(st_select_lex_unit *unit); void print(String *str); + bool fix_fields(THD *thd, Item **ref); friend class Item_ref_null_helper; friend class Item_is_not_null_test; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 34e8b585dcc..9d98e446c84 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -631,7 +631,8 @@ bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time, case 'r': length= my_sprintf(intbuff, (intbuff, - (l_time->hour < 12) ? "%02d:%02d:%02d AM" : "%02d:%02d:%02d PM", + ((l_time->hour % 24) < 12) ? + "%02d:%02d:%02d AM" : "%02d:%02d:%02d PM", (l_time->hour+11)%12+1, l_time->minute, l_time->second)); @@ -869,9 +870,9 @@ String* Item_func_monthname::val_str(String* str) { DBUG_ASSERT(fixed == 1); const char *month_name; - uint month=(uint) Item_func_month::val_int(); + uint month= (uint) val_int(); - if (!month) // This is also true for NULL + if (null_value || !month) { null_value=1; return (String*) 0; @@ -1962,6 +1963,9 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date) if (date_sub_interval) interval.neg = !interval.neg; + if (ltime->year < YY_MAGIC_BELOW) + return (null_value=1); + return (null_value= date_add_interval(ltime, int_type, interval)); } @@ -2020,8 +2024,13 @@ bool Item_date_add_interval::eq(const Item *item, bool binary_cmp) const Item_date_add_interval *other= (Item_date_add_interval*) item; if ((int_type != other->int_type) || - (!args[0]->eq(other->args[0], binary_cmp)) || - (get_interval_value(args[1], int_type, &val, &interval))) + (!args[0]->eq(other->args[0], binary_cmp))) + return FALSE; + + if (!args[1]->const_item() || !other->args[1]->const_item()) + return (args[1]->eq(other->args[1], binary_cmp)); + + if (get_interval_value(args[1], int_type, &val, &interval)) return FALSE; val= other->value; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 4da098f4b48..e4fd4c0da05 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -862,13 +862,6 @@ bool mysql_create_table(THD *thd,const char *db, const char *table_name, List<create_field> &fields, List<Key> &keys, bool tmp_table, uint select_field_count); -TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, - TABLE_LIST *create_table, - List<create_field> *extra_fields, - List<Key> *keys, - List<Item> *items, - MYSQL_LOCK **lock, - TABLEOP_HOOKS *hooks); bool mysql_alter_table(THD *thd, char *new_db, char *new_name, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, @@ -1509,6 +1502,7 @@ extern my_bool locked_in_memory; extern bool opt_using_transactions, mysqld_embedded; extern bool using_update_log, opt_large_files, server_id_supplied; extern bool opt_log, opt_update_log, opt_bin_log, opt_slow_log, opt_error_log; +extern my_bool opt_log_queries_not_using_indexes; extern bool opt_disable_networking, opt_skip_show_db; extern my_bool opt_character_set_client_handshake; extern bool volatile abort_loop, shutdown_in_progress, grant_option; @@ -1648,15 +1642,16 @@ extern pthread_t signal_thread; #endif #ifdef HAVE_OPENSSL -extern struct st_VioSSLAcceptorFd * ssl_acceptor_fd; +extern struct st_VioSSLFd * ssl_acceptor_fd; #endif /* HAVE_OPENSSL */ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **table, uint count, uint flags, bool *need_reopen); -/* mysql_lock_tables() flags bits */ +/* mysql_lock_tables() and open_table() flags bits */ #define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK 0x0001 #define MYSQL_LOCK_IGNORE_FLUSH 0x0002 #define MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN 0x0004 +#define MYSQL_OPEN_IGNORE_LOCKED_TABLES 0x0008 void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); @@ -1946,7 +1941,6 @@ inline int hexchar_to_int(char c) return -1; } - /* Some functions that are different in the embedded library and the normal server diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 0584bcc5ba1..42d87d6dc2f 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -318,7 +318,6 @@ static bool volatile ready_to_exit; static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0; static my_bool opt_bdb, opt_isam, opt_ndbcluster; static my_bool opt_short_log_format= 0; -static my_bool opt_log_queries_not_using_indexes= 0; static uint kill_cached_threads, wake_thread; static ulong killed_threads, thread_created; static ulong max_used_connections; @@ -344,6 +343,7 @@ static my_bool opt_sync_bdb_logs; /* Global variables */ bool opt_log, opt_update_log, opt_bin_log, opt_slow_log; +my_bool opt_log_queries_not_using_indexes= 0; bool opt_error_log= IF_WIN(1,0); bool opt_disable_networking=0, opt_skip_show_db=0; my_bool opt_character_set_client_handshake= 1; @@ -693,6 +693,7 @@ my_bool opt_enable_shared_memory; HANDLE smem_event_connect_request= 0; #endif +#define SSL_VARS_NOT_STATIC #include "sslopt-vars.h" #ifdef HAVE_OPENSSL #include <openssl/crypto.h> @@ -710,7 +711,7 @@ static void openssl_lock(int, openssl_lock_t *, const char *, int); static unsigned long openssl_id_function(); #endif char *des_key_file; -struct st_VioSSLAcceptorFd *ssl_acceptor_fd; +struct st_VioSSLFd *ssl_acceptor_fd; #endif /* HAVE_OPENSSL */ @@ -1044,7 +1045,8 @@ static void __cdecl kill_server(int sig_ptr) RETURN_FROM_KILL_SERVER; kill_in_progress=TRUE; abort_loop=1; // This should be set - my_sigset(sig,SIG_IGN); + if (sig != 0) // 0 is not a valid signal number + my_sigset(sig,SIG_IGN); if (sig == MYSQL_KILL_SIGNAL || sig == 0) sql_print_information(ER(ER_NORMAL_SHUTDOWN),my_progname); else @@ -1076,6 +1078,10 @@ static void __cdecl kill_server(int sig_ptr) pthread_join(select_thread, NULL); // wait for main thread #endif /* __NETWARE__ */ +#if defined(__NETWARE__) || (defined(USE_ONE_SIGNAL_HAND) && !defined(__WIN__) && !defined(OS2)) + my_thread_end(); +#endif + pthread_exit(0); /* purecov: deadcode */ #endif /* EMBEDDED_LIBRARY */ @@ -1220,6 +1226,7 @@ void clean_up(bool print_message) delete binlog_filter; delete rpl_filter; end_ssl(); + vio_end(); #ifdef USE_REGEX my_regex_end(); #endif diff --git a/sql/opt_range.cc b/sql/opt_range.cc index e4eb6e8ab3f..ffaf3fad6c8 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -450,6 +450,8 @@ public: /* TRUE if last checked tree->key can be used for ROR-scan */ bool is_ror_scan; + /* Number of ranges in the last checked tree->key */ + uint n_ranges; }; class TABLE_READ_PLAN; @@ -4306,7 +4308,8 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, DBUG_EXECUTE("info", print_ror_scans_arr(param->table, "building covering ROR-I", ror_scan_mark, ror_scans_end);); - do { + do + { /* Update changed sorting info: #covered fields, @@ -4695,17 +4698,46 @@ static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func, if (inv) { - /* - We get here for conditions like "t.keypart NOT IN (....)". - - If the IN-list contains only constants (and func->array is an ordered - array of them), we construct the appropriate SEL_ARG tree manually, - because constructing it using the range analyzer (as - AND_i( t.keypart != c_i)) will cause lots of memory to be consumed - (see BUG#15872). - */ if (func->array && func->cmp_type != ROW_RESULT) { + /* + We get here for conditions in form "t.key NOT IN (c1, c2, ...)" + (where c{i} are constants). + Our goal is to produce a SEL_ARG graph that represents intervals: + + ($MIN<t.key<c1) OR (c1<t.key<c2) OR (c2<t.key<c3) OR ... (*) + + where $MIN is either "-inf" or NULL. + + The most straightforward way to handle NOT IN would be to convert + it to "(t.key != c1) AND (t.key != c2) AND ..." and let the range + optimizer to build SEL_ARG graph from that. However that will cause + the range optimizer to use O(N^2) memory (it's a bug, not filed), + and people do use big NOT IN lists (see BUG#15872). Also, for big + NOT IN lists constructing/using graph (*) does not make the query + faster. + + So, we will handle NOT IN manually in the following way: + * if the number of entries in the NOT IN list is less then + NOT_IN_IGNORE_THRESHOLD, we will construct SEL_ARG graph (*) + manually. + * Otherwise, we will construct a smaller graph: for + "t.key NOT IN (c1,...cN)" we construct a graph representing + ($MIN < t.key) OR (cN < t.key) // here sequence of c_i is + // ordered. + + A note about partially-covering indexes: for those (e.g. for + "a CHAR(10), KEY(a(5))") the handling is correct (albeit not very + efficient): + Instead of "t.key < c1" we get "t.key <= prefix-val(c1)". + Combining the intervals in (*) together, we get: + (-inf<=t.key<=c1) OR (c1<=t.key<=c2) OR (c2<=t.key<=c3) OR ... + i.e. actually we get intervals combined into one interval: + (-inf<=t.key<=+inf). This doesn't make much sense but it doesn't + cause any problems. + */ + MEM_ROOT *tmp_root= param->mem_root; + param->thd->mem_root= param->old_root; /* Create one Item_type constant object. We'll need it as get_mm_parts only accepts constant values wrapped in Item_Type @@ -4714,25 +4746,35 @@ static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func, per-statement mem_root (while thd->mem_root is currently pointing to mem_root local to range optimizer). */ - MEM_ROOT *tmp_root= param->mem_root; - param->thd->mem_root= param->old_root; Item *value_item= func->array->create_item(); param->thd->mem_root= tmp_root; if (!value_item) break; - /* Get a SEL_TREE for "-inf < X < c_0" interval */ - func->array->value_to_item(0, value_item); - tree= get_mm_parts(param, cond_func, field, Item_func::LT_FUNC, - value_item, cmp_type); - if (!tree) + /* Get a SEL_TREE for "(-inf|NULL) < X < c_0" interval. */ + uint i=0; + do + { + func->array->value_to_item(i, value_item); + tree= get_mm_parts(param, cond_func, field, Item_func::LT_FUNC, + value_item, cmp_type); + if (!tree) + break; + i++; + } while (i < func->array->count && tree->type == SEL_TREE::IMPOSSIBLE); + + if (!tree || tree->type == SEL_TREE::IMPOSSIBLE) + { + /* We get here in cases like "t.unsigned NOT IN (-1,-2,-3) */ + tree= NULL; break; + } #define NOT_IN_IGNORE_THRESHOLD 1000 SEL_TREE *tree2; if (func->array->count < NOT_IN_IGNORE_THRESHOLD) { - for (uint i=1; i < func->array->count; i++) + for (; i < func->array->count; i++) { if (func->array->compare_elems(i, i-1)) { @@ -4740,32 +4782,44 @@ static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func, func->array->value_to_item(i, value_item); tree2= get_mm_parts(param, cond_func, field, Item_func::LT_FUNC, value_item, cmp_type); - + if (!tree2) + { + tree= NULL; + break; + } + /* Change all intervals to be "c_{i-1} < X < c_i" */ for (uint idx= 0; idx < param->keys; idx++) { - SEL_ARG *new_interval; - if ((new_interval= tree2->keys[idx])) + SEL_ARG *new_interval, *last_val; + if (((new_interval= tree2->keys[idx])) && + ((last_val= tree->keys[idx]->last()))) { - SEL_ARG *last_val= tree->keys[idx]->last(); new_interval->min_value= last_val->max_value; new_interval->min_flag= NEAR_MIN; } } + /* + The following doesn't try to allocate memory so no need to + check for NULL. + */ tree= tree_or(param, tree, tree2); } } } else func->array->value_to_item(func->array->count - 1, value_item); - - /* - Get the SEL_TREE for the last "c_last < X < +inf" interval - (value_item cotains c_last already) - */ - tree2= get_mm_parts(param, cond_func, field, Item_func::GT_FUNC, - value_item, cmp_type); - tree= tree_or(param, tree, tree2); + + if (tree && tree->type != SEL_TREE::IMPOSSIBLE) + { + /* + Get the SEL_TREE for the last "c_last < X < +inf" interval + (value_item cotains c_last already) + */ + tree2= get_mm_parts(param, cond_func, field, Item_func::GT_FUNC, + value_item, cmp_type); + tree= tree_or(param, tree, tree2); + } } else { @@ -6581,6 +6635,7 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree) param->table->file->primary_key_is_clustered()); param->is_ror_scan= !cpk_scan; } + param->n_ranges= 0; records=check_quick_keys(param,idx,tree,param->min_key,0,param->max_key,0); if (records != HA_POS_ERROR) @@ -6588,7 +6643,7 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree) param->table->quick_keys.set_bit(key); param->table->quick_rows[key]=records; param->table->quick_key_parts[key]=param->max_key_part+1; - + param->table->quick_n_ranges[key]= param->n_ranges; if (cpk_scan) param->is_ror_scan= TRUE; } @@ -6724,7 +6779,10 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree, HA_NOSAME && min_key_length == max_key_length && !memcmp(param->min_key,param->max_key,min_key_length)) + { tmp=1; // Max one record + param->n_ranges++; + } else { if (param->is_ror_scan) @@ -6744,6 +6802,7 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree, is_key_scan_ror(param, keynr, key_tree->part + 1))) param->is_ror_scan= FALSE; } + param->n_ranges++; if (tmp_min_flag & GEOM_FLAG) { @@ -7387,64 +7446,69 @@ int QUICK_ROR_INTERSECT_SELECT::get_next() uint last_rowid_count=0; DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::get_next"); - /* Get a rowid for first quick and save it as a 'candidate' */ - quick= quick_it++; - if (cpk_quick) + do { - do { + /* Get a rowid for first quick and save it as a 'candidate' */ + quick= quick_it++; + if (cpk_quick) + { + do + { + error= quick->get_next(); + }while (!error && !cpk_quick->row_in_ranges()); + } + else error= quick->get_next(); - }while (!error && !cpk_quick->row_in_ranges()); - } - else - error= quick->get_next(); - if (error) - DBUG_RETURN(error); + if (error) + DBUG_RETURN(error); - quick->file->position(quick->record); - memcpy(last_rowid, quick->file->ref, head->file->ref_length); - last_rowid_count= 1; + quick->file->position(quick->record); + memcpy(last_rowid, quick->file->ref, head->file->ref_length); + last_rowid_count= 1; - while (last_rowid_count < quick_selects.elements) - { - if (!(quick= quick_it++)) + while (last_rowid_count < quick_selects.elements) { - quick_it.rewind(); - quick= quick_it++; - } - - do { - if ((error= quick->get_next())) - DBUG_RETURN(error); - quick->file->position(quick->record); - cmp= head->file->cmp_ref(quick->file->ref, last_rowid); - } while (cmp < 0); + if (!(quick= quick_it++)) + { + quick_it.rewind(); + quick= quick_it++; + } - /* Ok, current select 'caught up' and returned ref >= cur_ref */ - if (cmp > 0) - { - /* Found a row with ref > cur_ref. Make it a new 'candidate' */ - if (cpk_quick) + do { - while (!cpk_quick->row_in_ranges()) + if ((error= quick->get_next())) + DBUG_RETURN(error); + quick->file->position(quick->record); + cmp= head->file->cmp_ref(quick->file->ref, last_rowid); + } while (cmp < 0); + + /* Ok, current select 'caught up' and returned ref >= cur_ref */ + if (cmp > 0) + { + /* Found a row with ref > cur_ref. Make it a new 'candidate' */ + if (cpk_quick) { - if ((error= quick->get_next())) - DBUG_RETURN(error); + while (!cpk_quick->row_in_ranges()) + { + if ((error= quick->get_next())) + DBUG_RETURN(error); + } } + memcpy(last_rowid, quick->file->ref, head->file->ref_length); + last_rowid_count= 1; + } + else + { + /* current 'candidate' row confirmed by this select */ + last_rowid_count++; } - memcpy(last_rowid, quick->file->ref, head->file->ref_length); - last_rowid_count= 1; - } - else - { - /* current 'candidate' row confirmed by this select */ - last_rowid_count++; } - } - /* We get here iff we got the same row ref in all scans. */ - if (need_to_fetch_row) - error= head->file->rnd_pos(head->record[0], last_rowid); + /* We get here iff we got the same row ref in all scans. */ + if (need_to_fetch_row) + error= head->file->rnd_pos(head->record[0], last_rowid); + } while (error == HA_ERR_RECORD_DELETED); DBUG_RETURN(error); } @@ -7473,41 +7537,44 @@ int QUICK_ROR_UNION_SELECT::get_next() do { - if (!queue.elements) - DBUG_RETURN(HA_ERR_END_OF_FILE); - /* Ok, we have a queue with >= 1 scans */ + do + { + if (!queue.elements) + DBUG_RETURN(HA_ERR_END_OF_FILE); + /* Ok, we have a queue with >= 1 scans */ - quick= (QUICK_SELECT_I*)queue_top(&queue); - memcpy(cur_rowid, quick->last_rowid, rowid_length); + quick= (QUICK_SELECT_I*)queue_top(&queue); + memcpy(cur_rowid, quick->last_rowid, rowid_length); - /* put into queue rowid from the same stream as top element */ - if ((error= quick->get_next())) - { - if (error != HA_ERR_END_OF_FILE) - DBUG_RETURN(error); - queue_remove(&queue, 0); - } - else - { - quick->save_last_pos(); - queue_replaced(&queue); - } + /* put into queue rowid from the same stream as top element */ + if ((error= quick->get_next())) + { + if (error != HA_ERR_END_OF_FILE) + DBUG_RETURN(error); + queue_remove(&queue, 0); + } + else + { + quick->save_last_pos(); + queue_replaced(&queue); + } - if (!have_prev_rowid) - { - /* No rows have been returned yet */ - dup_row= FALSE; - have_prev_rowid= TRUE; - } - else - dup_row= !head->file->cmp_ref(cur_rowid, prev_rowid); - }while (dup_row); + if (!have_prev_rowid) + { + /* No rows have been returned yet */ + dup_row= FALSE; + have_prev_rowid= TRUE; + } + else + dup_row= !head->file->cmp_ref(cur_rowid, prev_rowid); + } while (dup_row); - tmp= cur_rowid; - cur_rowid= prev_rowid; - prev_rowid= tmp; + tmp= cur_rowid; + cur_rowid= prev_rowid; + prev_rowid= tmp; - error= head->file->rnd_pos(quick->record, prev_rowid); + error= head->file->rnd_pos(quick->record, prev_rowid); + } while (error == HA_ERR_RECORD_DELETED); DBUG_RETURN(error); } diff --git a/sql/partition_info.h b/sql/partition_info.h index 3a1e6be4050..af43f7b7933 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -184,6 +184,7 @@ public: bool list_of_subpart_fields; bool linear_hash_ind; bool fixed; + bool is_auto_partitioned; bool from_openfrm; bool has_null_value; uint has_null_part_id; @@ -219,6 +220,7 @@ public: list_of_part_fields(FALSE), list_of_subpart_fields(FALSE), linear_hash_ind(FALSE), fixed(FALSE), + is_auto_partitioned(FALSE), from_openfrm(FALSE), has_null_value(FALSE), has_null_part_id(0) diff --git a/sql/set_var.cc b/sql/set_var.cc index 9b06e0f833f..c2925256d3b 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -163,6 +163,7 @@ void fix_sql_mode_var(THD *thd, enum_var_type type); static byte *get_error_count(THD *thd); static byte *get_warning_count(THD *thd); static byte *get_prepared_stmt_count(THD *thd); +static byte *get_tmpdir(THD *thd); /* Variable definition list @@ -185,6 +186,7 @@ sys_var_thd_ulong sys_auto_increment_offset("auto_increment_offset", sys_var_bool_ptr sys_automatic_sp_privileges("automatic_sp_privileges", &sp_automatic_privileges); +sys_var_const_str sys_basedir("basedir", mysql_home); sys_var_long_ptr sys_binlog_cache_size("binlog_cache_size", &binlog_cache_size); sys_var_thd_binlog_format sys_binlog_format("binlog_format", @@ -210,6 +212,7 @@ sys_var_long_ptr sys_concurrent_insert("concurrent_insert", &myisam_concurrent_insert); sys_var_long_ptr sys_connect_timeout("connect_timeout", &connect_timeout); +sys_var_const_str sys_datadir("datadir", mysql_real_data_home); #ifndef DBUG_OFF sys_var_thd_dbug sys_dbug("debug"); #endif @@ -262,6 +265,9 @@ sys_trust_routine_creators("log_bin_trust_routine_creators", sys_var_bool_ptr sys_trust_function_creators("log_bin_trust_function_creators", &trust_function_creators); +sys_var_bool_ptr + sys_log_queries_not_using_indexes("log_queries_not_using_indexes", + &opt_log_queries_not_using_indexes); sys_var_thd_ulong sys_log_warnings("log_warnings", &SV::log_warnings); sys_var_thd_ulong sys_long_query_time("long_query_time", &SV::long_query_time); @@ -389,6 +395,7 @@ sys_var_thd_ulong sys_query_alloc_block_size("query_alloc_block_size", sys_var_thd_ulong sys_query_prealloc_size("query_prealloc_size", &SV::query_prealloc_size, 0, fix_thd_mem_root); +sys_var_readonly sys_tmpdir("tmpdir", OPT_GLOBAL, SHOW_CHAR, get_tmpdir); sys_var_thd_ulong sys_trans_alloc_block_size("transaction_alloc_block_size", &SV::trans_alloc_block_size, 0, fix_trans_mem_root); @@ -425,6 +432,21 @@ sys_var_thd_ulong sys_sort_buffer("sort_buffer_size", &SV::sortbuff_size); sys_var_thd_sql_mode sys_sql_mode("sql_mode", &SV::sql_mode); +#ifdef HAVE_OPENSSL +extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher, + *opt_ssl_key; +sys_var_const_str_ptr sys_ssl_ca("ssl_ca", &opt_ssl_ca); +sys_var_const_str_ptr sys_ssl_capath("ssl_capath", &opt_ssl_capath); +sys_var_const_str_ptr sys_ssl_cert("ssl_cert", &opt_ssl_cert); +sys_var_const_str_ptr sys_ssl_cipher("ssl_cipher", &opt_ssl_cipher); +sys_var_const_str_ptr sys_ssl_key("ssl_key", &opt_ssl_key); +#else +sys_var_const_str sys_ssl_ca("ssl_ca", NULL); +sys_var_const_str sys_ssl_capath("ssl_capath", NULL); +sys_var_const_str sys_ssl_cert("ssl_cert", NULL); +sys_var_const_str sys_ssl_cipher("ssl_cipher", NULL); +sys_var_const_str sys_ssl_key("ssl_key", NULL); +#endif sys_var_thd_enum sys_updatable_views_with_limit("updatable_views_with_limit", &SV::updatable_views_with_limit, @@ -696,7 +718,6 @@ static int show_slave_skip_errors(THD *thd, SHOW_VAR *var, char *buff) } #endif /* HAVE_REPLICATION */ - /* Variables shown by SHOW variables in alphabetical order */ @@ -706,7 +727,7 @@ SHOW_VAR init_vars[]= { {"auto_increment_offset", (char*) &sys_auto_increment_offset, SHOW_SYS}, {sys_automatic_sp_privileges.name,(char*) &sys_automatic_sp_privileges, SHOW_SYS}, {"back_log", (char*) &back_log, SHOW_LONG}, - {"basedir", mysql_home, SHOW_CHAR}, + {sys_basedir.name, (char*) &sys_basedir, SHOW_SYS}, {"bdb_cache_parts", (char*) &berkeley_cache_parts, SHOW_LONG}, {"bdb_cache_size", (char*) &berkeley_cache_size, SHOW_LONGLONG}, {"bdb_home", (char*) &berkeley_home, SHOW_CHAR_PTR}, @@ -733,7 +754,7 @@ SHOW_VAR init_vars[]= { {sys_completion_type.name, (char*) &sys_completion_type, SHOW_SYS}, {sys_concurrent_insert.name,(char*) &sys_concurrent_insert, SHOW_SYS}, {sys_connect_timeout.name, (char*) &sys_connect_timeout, SHOW_SYS}, - {"datadir", mysql_real_data_home, SHOW_CHAR}, + {sys_datadir.name, (char*) &sys_datadir, SHOW_SYS}, {sys_date_format.name, (char*) &sys_date_format, SHOW_SYS}, {sys_datetime_format.name, (char*) &sys_datetime_format, SHOW_SYS}, #ifndef DBUG_OFF @@ -833,6 +854,8 @@ SHOW_VAR init_vars[]= { {"log_bin", (char*) &opt_bin_log, SHOW_BOOL}, {sys_trust_function_creators.name,(char*) &sys_trust_function_creators, SHOW_SYS}, {"log_error", (char*) log_error_file, SHOW_CHAR}, + {sys_log_queries_not_using_indexes.name, + (char*) &sys_log_queries_not_using_indexes, SHOW_SYS}, #ifdef HAVE_REPLICATION {"log_slave_updates", (char*) &opt_log_slave_updates, SHOW_MY_BOOL}, #endif @@ -962,6 +985,11 @@ SHOW_VAR init_vars[]= { {sys_sql_mode.name, (char*) &sys_sql_mode, SHOW_SYS}, {"sql_notes", (char*) &sys_sql_notes, SHOW_SYS}, {"sql_warnings", (char*) &sys_sql_warnings, SHOW_SYS}, + {sys_ssl_ca.name, (char*) &sys_ssl_ca, SHOW_SYS}, + {sys_ssl_capath.name, (char*) &sys_ssl_capath, SHOW_SYS}, + {sys_ssl_cert.name, (char*) &sys_ssl_cert, SHOW_SYS}, + {sys_ssl_cipher.name, (char*) &sys_ssl_cipher, SHOW_SYS}, + {sys_ssl_key.name, (char*) &sys_ssl_key, SHOW_SYS}, {sys_storage_engine.name, (char*) &sys_storage_engine, SHOW_SYS}, #ifdef HAVE_REPLICATION {sys_sync_binlog_period.name,(char*) &sys_sync_binlog_period, SHOW_SYS}, @@ -983,7 +1011,7 @@ SHOW_VAR init_vars[]= { {"time_zone", (char*) &sys_time_zone, SHOW_SYS}, {sys_timed_mutexes.name, (char*) &sys_timed_mutexes, SHOW_SYS}, {sys_tmp_table_size.name, (char*) &sys_tmp_table_size, SHOW_SYS}, - {"tmpdir", (char*) &opt_mysql_tmpdir, SHOW_CHAR_PTR}, + {sys_tmpdir.name, (char*) &sys_tmpdir, SHOW_SYS}, {sys_trans_alloc_block_size.name, (char*) &sys_trans_alloc_block_size, SHOW_SYS}, {sys_trans_prealloc_size.name, (char*) &sys_trans_prealloc_size, SHOW_SYS}, @@ -2855,6 +2883,31 @@ static byte *get_prepared_stmt_count(THD *thd) return (byte*) &thd->sys_var_tmp.ulong_value; } + +/* + Get the tmpdir that was specified or chosen by default + + SYNOPSIS + get_tmpdir() + thd thread handle + + DESCRIPTION + This is necessary because if the user does not specify a temporary + directory via the command line, one is chosen based on the environment + or system defaults. But we can't just always use mysql_tmpdir, because + that is actually a call to my_tmpdir() which cycles among possible + temporary directories. + + RETURN VALUES + ptr pointer to NUL-terminated string + */ +static byte *get_tmpdir(THD *thd) +{ + if (opt_mysql_tmpdir) + return (byte *)opt_mysql_tmpdir; + return (byte*)mysql_tmpdir; +} + /**************************************************************************** Main handling of variables: - Initialisation @@ -3417,9 +3470,9 @@ ulong fix_sql_mode(ulong sql_mode) MODE_NO_KEY_OPTIONS | MODE_NO_TABLE_OPTIONS | MODE_NO_FIELD_OPTIONS | MODE_NO_AUTO_CREATE_USER); if (sql_mode & MODE_MYSQL40) - sql_mode|= MODE_NO_FIELD_OPTIONS | MODE_HIGH_NOT_PRECEDENCE; + sql_mode|= MODE_HIGH_NOT_PRECEDENCE; if (sql_mode & MODE_MYSQL323) - sql_mode|= MODE_NO_FIELD_OPTIONS | MODE_HIGH_NOT_PRECEDENCE; + sql_mode|= MODE_HIGH_NOT_PRECEDENCE; if (sql_mode & MODE_TRADITIONAL) sql_mode|= (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES | MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | diff --git a/sql/set_var.h b/sql/set_var.h index 0ae88144c0f..1049b154d47 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -231,6 +231,35 @@ public: }; +class sys_var_const_str_ptr :public sys_var +{ +public: + char **value; // Pointer to const value + sys_var_const_str_ptr(const char *name_arg, char **value_arg) + :sys_var(name_arg),value(value_arg) + {} + bool check(THD *thd, set_var *var) + { + return 1; + } + bool update(THD *thd, set_var *var) + { + return 1; + } + SHOW_TYPE type() { return SHOW_CHAR; } + byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) + { + return (byte*) *value; + } + bool check_update_type(Item_result type) + { + return 1; + } + bool check_default(enum_var_type type) { return 1; } + bool is_readonly() const { return 1; } +}; + + class sys_var_enum :public sys_var { uint *value; diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index f34002ab0cf..aa9cbaef36d 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5472,7 +5472,7 @@ ER_SP_DUP_HANDLER 42000 eng "Duplicate handler declared in the same block" ger "Doppelter Handler im selben Block deklariert" ER_SP_NOT_VAR_ARG 42000 - eng "OUT or INOUT argument %d for routine %s is not a variable" + eng "OUT or INOUT argument %d for routine %s is not a variable or NEW pseudo-variable in BEFORE trigger" ger "OUT- oder INOUT-Argument %d für Routine %s ist keine Variable" ER_SP_NO_RETSET 0A000 eng "Not allowed to return a result set from a %s" diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 8a64799e5f9..de56e261bd3 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -315,14 +315,16 @@ sp_prepare_func_item(THD* thd, Item **it_addr) */ bool -sp_eval_expr(THD *thd, Field *result_field, Item *expr_item) +sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr) { + Item *expr_item; + DBUG_ENTER("sp_eval_expr"); - if (!expr_item) + if (!*expr_item_ptr) DBUG_RETURN(TRUE); - if (!(expr_item= sp_prepare_func_item(thd, &expr_item))) + if (!(expr_item= sp_prepare_func_item(thd, expr_item_ptr))) DBUG_RETURN(TRUE); bool err_status= FALSE; @@ -951,6 +953,7 @@ sp_head::execute(THD *thd) bool err_status= FALSE; uint ip= 0; ulong save_sql_mode; + bool save_abort_on_warning; Query_arena *old_arena; /* per-instruction arena */ MEM_ROOT execute_mem_root; @@ -1011,6 +1014,10 @@ sp_head::execute(THD *thd) thd->derived_tables= 0; save_sql_mode= thd->variables.sql_mode; thd->variables.sql_mode= m_sql_mode; + save_abort_on_warning= thd->abort_on_warning; + thd->abort_on_warning= + (m_sql_mode & (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)); + /* It is also more efficient to save/restore current thd->lex once when do it in each instruction @@ -1143,6 +1150,7 @@ sp_head::execute(THD *thd) DBUG_ASSERT(!thd->derived_tables); thd->derived_tables= old_derived_tables; thd->variables.sql_mode= save_sql_mode; + thd->abort_on_warning= save_abort_on_warning; thd->stmt_arena= old_arena; state= EXECUTED; @@ -1219,18 +1227,22 @@ bool sp_head::execute_function(THD *thd, Item **argp, uint argcount, Field *return_value_fld) { - Item_cache **param_values; ulonglong binlog_save_options; bool need_binlog_call; - uint params; + uint arg_no; sp_rcontext *octx = thd->spcont; sp_rcontext *nctx = NULL; + char buf[STRING_BUFFER_USUAL_SIZE]; + String binlog_buf(buf, sizeof(buf), &my_charset_bin); bool err_status= FALSE; + MEM_ROOT call_mem_root; + Query_arena call_arena(&call_mem_root, Query_arena::INITIALIZED_FOR_SP); + Query_arena backup_arena; + DBUG_ENTER("sp_head::execute_function"); DBUG_PRINT("info", ("function %s", m_name.str)); LINT_INIT(binlog_save_options); - params= m_pcont->context_var_count(); /* Check that the function is called with all specified arguments. @@ -1238,74 +1250,97 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, If it is not, use my_error() to report an error, or it will not terminate the invoking query properly. */ - - if (argcount != params) + if (argcount != m_pcont->context_var_count()) { /* Need to use my_error here, or it will not terminate the invoking query properly. */ my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), - "FUNCTION", m_qname.str, params, argcount); + "FUNCTION", m_qname.str, m_pcont->context_var_count(), argcount); DBUG_RETURN(TRUE); } - - /* Allocate param_values to be used for dumping the call into binlog. */ - - if (!(param_values= (Item_cache**)thd->alloc(sizeof(Item_cache*)*argcount))) - DBUG_RETURN(TRUE); - - // QQ Should have some error checking here? (types, etc...) + /* + Prepare arena and memroot for objects which lifetime is whole + duration of function call (sp_rcontext, it's tables and items, + sp_cursor and Item_cache holders for case expressions). + We can't use caller's arena/memroot for those objects because + in this case some fixed amount of memory will be consumed for + each function/trigger invocation and so statements which involve + lot of them will hog memory. + TODO: we should create sp_rcontext once per command and reuse + it on subsequent executions of a function/trigger. + */ + init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0); + thd->set_n_backup_active_arena(&call_arena, &backup_arena); if (!(nctx= new sp_rcontext(m_pcont, return_value_fld, octx)) || nctx->init(thd)) { - delete nctx; /* Delete nctx if it was init() that failed. */ - DBUG_RETURN(TRUE); + thd->restore_active_arena(&call_arena, &backup_arena); + err_status= TRUE; + goto err_with_cleanup; } + /* + We have to switch temporarily back to callers arena/memroot. + Function arguments belong to the caller and so the may reference + memory which they will allocate during calculation long after + this function call will be finished (e.g. in Item::cleanup()). + */ + thd->restore_active_arena(&call_arena, &backup_arena); + #ifndef DBUG_OFF nctx->sp= this; #endif /* Pass arguments. */ - + for (arg_no= 0; arg_no < argcount; arg_no++) { - uint i; - - for (i= 0 ; i < argcount ; i++) - { - if (!argp[i]->fixed && argp[i]->fix_fields(thd, &argp[i])) - { - err_status= TRUE; - break; - } - - param_values[i]= Item_cache::get_cache(argp[i]->result_type()); - param_values[i]->store(argp[i]); - - if (nctx->set_variable(thd, i, param_values[i])) - { - err_status= TRUE; - break; - } - } - } + /* Arguments must be fixed in Item_func_sp::fix_fields */ + DBUG_ASSERT(argp[arg_no]->fixed); - if (err_status) - { - delete nctx; - DBUG_RETURN(TRUE); + if ((err_status= nctx->set_variable(thd, arg_no, &(argp[arg_no])))) + goto err_with_cleanup; } - thd->spcont= nctx; - /* If row-based binlogging, we don't need to binlog the function's call, let each substatement be binlogged its way. */ need_binlog_call= mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG) && !thd->current_stmt_binlog_row_based; + + /* + Remember the original arguments for unrolled replication of functions + before they are changed by execution. + */ + if (need_binlog_call) + { + binlog_buf.length(0); + binlog_buf.append(STRING_WITH_LEN("SELECT ")); + append_identifier(thd, &binlog_buf, m_name.str, m_name.length); + binlog_buf.append('('); + for (arg_no= 0; arg_no < argcount; arg_no++) + { + String str_value_holder; + String *str_value; + + if (arg_no) + binlog_buf.append(','); + + str_value= sp_get_item_value(nctx->get_item(arg_no), + &str_value_holder); + + if (str_value) + binlog_buf.append(*str_value); + else + binlog_buf.append(STRING_WITH_LEN("NULL")); + } + binlog_buf.append(')'); + } + thd->spcont= nctx; + if (need_binlog_call) { reset_dynamic(&thd->user_var_events); @@ -1314,38 +1349,27 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, thd->options&= ~OPTION_BIN_LOG; } + /* + Switch to call arena/mem_root so objects like sp_cursor or + Item_cache holders for case expressions can be allocated on it. + + TODO: In future we should associate call arena/mem_root with + sp_rcontext and allocate all these objects (and sp_rcontext + itself) on it directly rather than juggle with arenas. + */ + thd->set_n_backup_active_arena(&call_arena, &backup_arena); + err_status= execute(thd); + thd->restore_active_arena(&call_arena, &backup_arena); + if (need_binlog_call) { mysql_bin_log.stop_union_events(thd); thd->options= binlog_save_options; if (thd->binlog_evt_union.unioned_events) { - char buf[256]; - String bufstr(buf, sizeof(buf), &my_charset_bin); - bufstr.length(0); - bufstr.append(STRING_WITH_LEN("SELECT ")); - append_identifier(thd, &bufstr, m_name.str, m_name.length); - bufstr.append('('); - for (uint i=0; i < argcount; i++) - { - String str_value_holder; - String *str_value; - - if (i) - bufstr.append(','); - - str_value= sp_get_item_value(param_values[i], &str_value_holder); - - if (str_value) - bufstr.append(*str_value); - else - bufstr.append(STRING_WITH_LEN("NULL")); - } - bufstr.append(')'); - - Query_log_event qinfo(thd, bufstr.ptr(), bufstr.length(), + Query_log_event qinfo(thd, binlog_buf.ptr(), binlog_buf.length(), thd->binlog_evt_union.unioned_events_trans, FALSE); if (mysql_bin_log.write(&qinfo) && thd->binlog_evt_union.unioned_events_trans) @@ -1369,27 +1393,19 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, } } + nctx->pop_all_cursors(); // To avoid memory leaks after an error + +err_with_cleanup: delete nctx; + call_arena.free_items(); + free_root(&call_mem_root, MYF(0)); thd->spcont= octx; DBUG_RETURN(err_status); } -static Item_func_get_user_var *item_is_user_var(Item *it) -{ - if (it->type() == Item::FUNC_ITEM) - { - Item_func *fi= static_cast<Item_func*>(it); - - if (fi->functype() == Item_func::GUSERVAR_FUNC) - return static_cast<Item_func_get_user_var*>(fi); - } - return NULL; -} - - /* Execute a procedure. SYNOPSIS @@ -1467,22 +1483,28 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) for (uint i= 0 ; i < params ; i++) { Item *arg_item= it_args++; - sp_variable_t *spvar= m_pcont->find_variable(i); if (!arg_item) break; + sp_variable_t *spvar= m_pcont->find_variable(i); + if (!spvar) continue; if (spvar->mode != sp_param_in) { - if (!arg_item->is_splocal() && !item_is_user_var(arg_item)) + Settable_routine_parameter *srp= + arg_item->get_settable_routine_parameter(); + + if (!srp) { my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, m_qname.str); err_status= TRUE; break; } + + srp->set_required_privilege(spvar->mode == sp_param_inout); } if (spvar->mode == sp_param_out) @@ -1490,7 +1512,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) Item_null *null_item= new Item_null(); if (!null_item || - nctx->set_variable(thd, i, null_item)) + nctx->set_variable(thd, i, (struct Item **)&null_item)) { err_status= TRUE; break; @@ -1498,7 +1520,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) } else { - if (nctx->set_variable(thd, i, *it_args.ref())) + if (nctx->set_variable(thd, i, it_args.ref())) { err_status= TRUE; break; @@ -1566,36 +1588,16 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) if (spvar->mode == sp_param_in) continue; - if (arg_item->is_splocal()) - { - if (octx->set_variable(thd, - ((Item_splocal*) arg_item)->get_var_idx(), - nctx->get_item(i))) - { - err_status= TRUE; - break; - } - } - else + Settable_routine_parameter *srp= + arg_item->get_settable_routine_parameter(); + + DBUG_ASSERT(srp); + + if (srp->set_value(thd, octx, nctx->get_item_addr(i))) { - Item_func_get_user_var *guv= item_is_user_var(arg_item); - - if (guv) - { - Item *item= nctx->get_item(i); - Item_func_set_user_var *suv; - - suv= new Item_func_set_user_var(guv->get_name(), item); - /* - Item_func_set_user_var is not fixed after construction, - call fix_fields(). - */ - if ((err_status= test(!suv || suv->fix_fields(thd, &item) || - suv->check() || suv->update()))) - break; - } + err_status= TRUE; + break; } - } } @@ -2372,7 +2374,7 @@ sp_instr_set::execute(THD *thd, uint *nextp) int sp_instr_set::exec_core(THD *thd, uint *nextp) { - int res= thd->spcont->set_variable(thd, m_offset, m_value); + int res= thd->spcont->set_variable(thd, m_offset, &m_value); if (res && thd->spcont->found_handler_here()) { @@ -2437,12 +2439,7 @@ sp_instr_set_trigger_field::execute(THD *thd, uint *nextp) int sp_instr_set_trigger_field::exec_core(THD *thd, uint *nextp) { - int res= 0; - Item *it= sp_prepare_func_item(thd, &value); - if (!it || - !trigger_field->fixed && trigger_field->fix_fields(thd, 0) || - (it->save_in_field(trigger_field->field, 0) < 0)) - res= -1; + const int res= (trigger_field->set_value(thd, &value) ? -1 : 0); *nextp = m_ip+1; return res; } @@ -2647,7 +2644,7 @@ sp_instr_freturn::exec_core(THD *thd, uint *nextp) do it in scope of execution the current context/block. */ - return thd->spcont->set_return_value(thd, m_value); + return thd->spcont->set_return_value(thd, &m_value); } void @@ -3091,7 +3088,7 @@ sp_instr_set_case_expr::execute(THD *thd, uint *nextp) int sp_instr_set_case_expr::exec_core(THD *thd, uint *nextp) { - int res= thd->spcont->set_case_expr(thd, m_case_expr_id, m_case_expr); + int res= thd->spcont->set_case_expr(thd, m_case_expr_id, &m_case_expr); if (res && !thd->spcont->get_case_expr(m_case_expr_id) && @@ -3105,7 +3102,7 @@ sp_instr_set_case_expr::exec_core(THD *thd, uint *nextp) Item *null_item= new Item_null(); if (!null_item || - thd->spcont->set_case_expr(thd, m_case_expr_id, null_item)) + thd->spcont->set_case_expr(thd, m_case_expr_id, &null_item)) { /* If this also failed, we have to abort. */ diff --git a/sql/sp_head.h b/sql/sp_head.h index fbc277b4de8..791343f0061 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -1170,6 +1170,6 @@ Item * sp_prepare_func_item(THD* thd, Item **it_addr); bool -sp_eval_expr(THD *thd, Field *result_field, Item *expr_item); +sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr); #endif /* _SP_HEAD_H_ */ diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 38b6de0e75a..3bc27a029d0 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -150,7 +150,7 @@ sp_rcontext::init_var_items() bool -sp_rcontext::set_return_value(THD *thd, Item *return_value_item) +sp_rcontext::set_return_value(THD *thd, Item **return_value_item) { DBUG_ASSERT(m_return_value_fld); @@ -279,14 +279,14 @@ sp_rcontext::pop_cursors(uint count) int -sp_rcontext::set_variable(THD *thd, uint var_idx, Item *value) +sp_rcontext::set_variable(THD *thd, uint var_idx, Item **value) { return set_variable(thd, m_var_table->field[var_idx], value); } int -sp_rcontext::set_variable(THD *thd, Field *field, Item *value) +sp_rcontext::set_variable(THD *thd, Field *field, Item **value) { if (!value) { @@ -478,9 +478,10 @@ sp_rcontext::create_case_expr_holder(THD *thd, Item_result result_type) */ int -sp_rcontext::set_case_expr(THD *thd, int case_expr_id, Item *case_expr_item) +sp_rcontext::set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr) { - if (!(case_expr_item= sp_prepare_func_item(thd, &case_expr_item))) + Item *case_expr_item= sp_prepare_func_item(thd, case_expr_item_ptr); + if (!case_expr_item) return TRUE; if (!m_case_expr_holders[case_expr_id] || @@ -542,7 +543,7 @@ bool Select_fetch_into_spvars::send_data(List<Item> &items) */ for (; spvar= spvar_iter++, item= item_iter++; ) { - if (thd->spcont->set_variable(thd, spvar->offset, item)) + if (thd->spcont->set_variable(thd, spvar->offset, &item)) return TRUE; } return FALSE; diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 20aaea3b7c1..30521f6da84 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -91,7 +91,7 @@ class sp_rcontext : public Sql_alloc ~sp_rcontext(); int - set_variable(THD *thd, uint var_idx, Item *value); + set_variable(THD *thd, uint var_idx, Item **value); Item * get_item(uint var_idx); @@ -100,7 +100,7 @@ class sp_rcontext : public Sql_alloc get_item_addr(uint var_idx); bool - set_return_value(THD *thd, Item *return_value_item); + set_return_value(THD *thd, Item **return_value_item); inline bool is_return_value_set() const @@ -200,7 +200,7 @@ class sp_rcontext : public Sql_alloc */ int - set_case_expr(THD *thd, int case_expr_id, Item *case_expr_item); + set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr); Item * get_case_expr(int case_expr_id); @@ -254,7 +254,7 @@ private: Item_cache *create_case_expr_holder(THD *thd, Item_result result_type); - int set_variable(THD *thd, Field *field, Item *value); + int set_variable(THD *thd, Field *field, Item **value); }; // class sp_rcontext : public Sql_alloc diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 09b684c8706..d2501d08c4d 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -987,8 +987,8 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, if (acl_user->x509_issuer) { DBUG_PRINT("info",("checkpoint 3")); - char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); - DBUG_PRINT("info",("comparing issuers: '%s' and '%s'", + char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); + DBUG_PRINT("info",("comparing issuers: '%s' and '%s'", acl_user->x509_issuer, ptr)); if (strcmp(acl_user->x509_issuer, ptr)) { @@ -1156,6 +1156,8 @@ static void acl_update_user(const char *user, const char *host, USER_RESOURCES *mqh, ulong privileges) { + safe_mutex_assert_owner(&acl_cache->lock); + for (uint i=0 ; i < acl_users.elements ; i++) { ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); @@ -1206,6 +1208,9 @@ static void acl_insert_user(const char *user, const char *host, ulong privileges) { ACL_USER acl_user; + + safe_mutex_assert_owner(&acl_cache->lock); + acl_user.user=*user ? strdup_root(&mem,user) : 0; update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0); acl_user.access=privileges; @@ -1235,6 +1240,8 @@ static void acl_insert_user(const char *user, const char *host, static void acl_update_db(const char *user, const char *host, const char *db, ulong privileges) { + safe_mutex_assert_owner(&acl_cache->lock); + for (uint i=0 ; i < acl_dbs.elements ; i++) { ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*); @@ -1660,6 +1667,9 @@ find_acl_user(const char *host, const char *user, my_bool exact) { DBUG_ENTER("find_acl_user"); DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user)); + + safe_mutex_assert_owner(&acl_cache->lock); + for (uint i=0 ; i < acl_users.elements ; i++) { ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); @@ -1672,7 +1682,7 @@ find_acl_user(const char *host, const char *user, my_bool exact) if (!acl_user->user && !user[0] || acl_user->user && !strcmp(user,acl_user->user)) { - if (exact ? !my_strcasecmp(&my_charset_latin1, host, + if (exact ? !my_strcasecmp(system_charset_info, host, acl_user->host.hostname ? acl_user->host.hostname : "") : compare_hostname(&acl_user->host,host,host)) @@ -2977,7 +2987,6 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list, some kind of updates to the mysql.% tables. */ if (thd->slave_thread && rpl_filter->is_on()) - if (thd->slave_thread && rpl_filter->is_on()) { /* The tables must be marked "updating" so that tables_ok() takes them into @@ -2999,6 +3008,7 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list, create_new_users= test_if_create_new_users(thd); bool result= FALSE; rw_wrlock(&LOCK_grant); + pthread_mutex_lock(&acl_cache->lock); MEM_ROOT *old_root= thd->mem_root; thd->mem_root= &memex; grant_version++; @@ -3016,12 +3026,10 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list, continue; } /* Create user if needed */ - pthread_mutex_lock(&acl_cache->lock); error=replace_user_table(thd, tables[0].table, *Str, 0, revoke_grant, create_new_users, test(thd->variables.sql_mode & MODE_NO_AUTO_CREATE_USER)); - pthread_mutex_unlock(&acl_cache->lock); if (error) { result= TRUE; // Remember error @@ -3113,6 +3121,7 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list, } grant_option=TRUE; thd->mem_root= old_root; + pthread_mutex_unlock(&acl_cache->lock); rw_unlock(&LOCK_grant); if (!result) send_ok(thd); @@ -3205,6 +3214,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, if (!revoke_grant) create_new_users= test_if_create_new_users(thd); rw_wrlock(&LOCK_grant); + pthread_mutex_lock(&acl_cache->lock); MEM_ROOT *old_root= thd->mem_root; thd->mem_root= &memex; @@ -3224,12 +3234,10 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, continue; } /* Create user if needed */ - pthread_mutex_lock(&acl_cache->lock); error=replace_user_table(thd, tables[0].table, *Str, 0, revoke_grant, create_new_users, test(thd->variables.sql_mode & MODE_NO_AUTO_CREATE_USER)); - pthread_mutex_unlock(&acl_cache->lock); if (error) { result= TRUE; // Remember error @@ -3271,6 +3279,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, } grant_option=TRUE; thd->mem_root= old_root; + pthread_mutex_unlock(&acl_cache->lock); rw_unlock(&LOCK_grant); if (!result && !no_error) send_ok(thd); @@ -4247,20 +4256,15 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) DBUG_RETURN(TRUE); } - for (counter=0 ; counter < acl_users.elements ; counter++) - { - const char *user,*host; - acl_user=dynamic_element(&acl_users,counter,ACL_USER*); - if (!(user=acl_user->user)) - user= ""; - if (!(host=acl_user->host.hostname)) - host= ""; - if (!strcmp(lex_user->user.str,user) && - !my_strcasecmp(system_charset_info, lex_user->host.str, host)) - break; - } - if (counter == acl_users.elements) + rw_rdlock(&LOCK_grant); + VOID(pthread_mutex_lock(&acl_cache->lock)); + + acl_user= find_acl_user(lex_user->host.str, lex_user->user.str, TRUE); + if (!acl_user) { + VOID(pthread_mutex_unlock(&acl_cache->lock)); + rw_unlock(&LOCK_grant); + my_error(ER_NONEXISTING_GRANT, MYF(0), lex_user->user.str, lex_user->host.str); DBUG_RETURN(TRUE); @@ -4275,10 +4279,12 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) field_list.push_back(field); if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) - DBUG_RETURN(TRUE); + { + VOID(pthread_mutex_unlock(&acl_cache->lock)); + rw_unlock(&LOCK_grant); - rw_wrlock(&LOCK_grant); - VOID(pthread_mutex_lock(&acl_cache->lock)); + DBUG_RETURN(TRUE); + } /* Add first global access grants */ { @@ -4686,10 +4692,15 @@ void get_privilege_desc(char *to, uint max_length, ulong access) void get_mqh(const char *user, const char *host, USER_CONN *uc) { ACL_USER *acl_user; + + pthread_mutex_lock(&acl_cache->lock); + if (initialized && (acl_user= find_acl_user(host,user, FALSE))) uc->user_resources= acl_user->user_resource; else bzero((char*) &uc->user_resources, sizeof(uc->user_resources)); + + pthread_mutex_unlock(&acl_cache->lock); } /* @@ -4769,31 +4780,6 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) DBUG_RETURN(0); } -ACL_USER *check_acl_user(LEX_USER *user_name, - uint *acl_acl_userdx) -{ - ACL_USER *acl_user= 0; - uint counter; - - for (counter= 0 ; counter < acl_users.elements ; counter++) - { - const char *user,*host; - acl_user= dynamic_element(&acl_users, counter, ACL_USER*); - if (!(user=acl_user->user)) - user= ""; - if (!(host=acl_user->host.hostname)) - host= ""; - if (!strcmp(user_name->user.str,user) && - !my_strcasecmp(system_charset_info, user_name->host.str, host)) - break; - } - if (counter == acl_users.elements) - return 0; - - *acl_acl_userdx= counter; - return acl_user; -} - /* Modify a privilege table. @@ -5040,6 +5026,8 @@ static int handle_grant_struct(uint struct_no, bool drop, LINT_INIT(user); LINT_INIT(host); + safe_mutex_assert_owner(&acl_cache->lock); + /* Get the number of elements in the in-memory structure. */ switch (struct_no) { case 0: @@ -5502,7 +5490,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) List_iterator <LEX_USER> user_list(list); while ((lex_user=user_list++)) { - if (!check_acl_user(lex_user, &counter)) + if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE)) { sql_print_error("REVOKE ALL PRIVILEGES, GRANT: User '%s'@'%s' does not " "exists", lex_user->user.str, lex_user->host.str); @@ -5738,6 +5726,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, combo->user.str= sctx->user; + VOID(pthread_mutex_lock(&acl_cache->lock)); if (!find_acl_user(combo->host.str=(char*)sctx->host_or_ip, combo->user.str, FALSE) && !find_acl_user(combo->host.str=(char*)sctx->host, combo->user.str, @@ -5745,7 +5734,11 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, !find_acl_user(combo->host.str=(char*)sctx->ip, combo->user.str, FALSE) && !find_acl_user(combo->host.str=(char*)"%", combo->user.str, FALSE)) + { + VOID(pthread_mutex_unlock(&acl_cache->lock)); DBUG_RETURN(TRUE); + } + VOID(pthread_mutex_unlock(&acl_cache->lock)); bzero((char*)tables, sizeof(TABLE_LIST)); user_list.empty(); @@ -5863,6 +5856,8 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond) char *curr_host= thd->security_ctx->priv_host_name(); DBUG_ENTER("fill_schema_user_privileges"); + pthread_mutex_lock(&acl_cache->lock); + for (counter=0 ; counter < acl_users.elements ; counter++) { const char *user,*host, *is_grantable="YES"; @@ -5898,6 +5893,9 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond) } } } + + pthread_mutex_unlock(&acl_cache->lock); + DBUG_RETURN(0); #else return(0); @@ -5917,6 +5915,8 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond) char *curr_host= thd->security_ctx->priv_host_name(); DBUG_ENTER("fill_schema_schema_privileges"); + pthread_mutex_lock(&acl_cache->lock); + for (counter=0 ; counter < acl_dbs.elements ; counter++) { const char *user, *host, *is_grantable="YES"; @@ -5955,6 +5955,9 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond) } } } + + pthread_mutex_unlock(&acl_cache->lock); + DBUG_RETURN(0); #else return (0); @@ -5972,6 +5975,8 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond) char *curr_host= thd->security_ctx->priv_host_name(); DBUG_ENTER("fill_schema_table_privileges"); + rw_rdlock(&LOCK_grant); + for (index=0 ; index < column_priv_hash.records ; index++) { const char *user, *is_grantable= "YES"; @@ -6017,6 +6022,9 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond) } } } + + rw_unlock(&LOCK_grant); + DBUG_RETURN(0); #else return (0); @@ -6034,6 +6042,8 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond) char *curr_host= thd->security_ctx->priv_host_name(); DBUG_ENTER("fill_schema_table_privileges"); + rw_rdlock(&LOCK_grant); + for (index=0 ; index < column_priv_hash.records ; index++) { const char *user, *is_grantable= "YES"; @@ -6085,6 +6095,9 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond) } } } + + rw_unlock(&LOCK_grant); + DBUG_RETURN(0); #else return (0); @@ -6141,20 +6154,21 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, } /* table privileges */ + rw_rdlock(&LOCK_grant); if (grant->version != grant_version) { - rw_rdlock(&LOCK_grant); grant->grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user, table, 0); /* purecov: inspected */ grant->version= grant_version; /* purecov: inspected */ - rw_unlock(&LOCK_grant); } if (grant->grant_table != 0) { grant->privilege|= grant->grant_table->privs; } + rw_unlock(&LOCK_grant); + DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege)); DBUG_VOID_RETURN; } diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 8c64adbbece..e1153522ed5 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -236,7 +236,6 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list, bool mysql_routine_grant(THD *thd, TABLE_LIST *table, bool is_proc, List <LEX_USER> &user_list, ulong rights, bool revoke, bool no_error); -ACL_USER *check_acl_user(LEX_USER *user_name, uint *acl_acl_userdx); my_bool grant_init(); void grant_free(void); my_bool grant_reload(THD *thd); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8ea53747b40..58c04224ac9 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1289,7 +1289,7 @@ void close_temporary_tables(THD *thd) table= next) { end_cur= strxmov(end_cur, "`", table->s->db.str, "`.`", - table->s->table_name.str, "`,", NullS); + table->s->table_name.str, "`,", NullS); next= table->next; close_temporary(table, 1, 1); } @@ -1317,7 +1317,6 @@ void close_temporary_tables(THD *thd) thd->temporary_tables=0; } - /* Find table in list. @@ -1793,6 +1792,8 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) MYSQL_LOCK_IGNORE_FLUSH - Open table even if someone has done a flush or namelock on it. No version number checking is done. + MYSQL_OPEN_IGNORE_LOCKED_TABLES - Open table + ignoring set of locked tables and prelocked mode. IMPLEMENTATION Uses a cache of open tables to find a table not in use. @@ -1852,7 +1853,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, } } - if (thd->locked_tables || thd->prelocked_mode) + if (!(flags & MYSQL_OPEN_IGNORE_LOCKED_TABLES) && + (thd->locked_tables || thd->prelocked_mode)) { // Using table locks TABLE *best_table= 0; int best_distance= INT_MIN; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index a2ccaf092ee..9b2ad209e84 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1934,7 +1934,7 @@ bool select_dumpvar::send_data(List<Item> &items) if ((yy=var_li++)) { if (thd->spcont->set_variable(current_thd, yy->get_var_idx(), - *it.ref())) + it.ref())) DBUG_RETURN(1); } } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 946c0536897..675fb256b32 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2518,6 +2518,162 @@ bool select_insert::send_eof() CREATE TABLE (SELECT) ... ***************************************************************************/ +/* + Create table from lists of fields and items (or open existing table + with same name). + + SYNOPSIS + create_table_from_items() + thd in Thread object + create_info in Create information (like MAX_ROWS, ENGINE or + temporary table flag) + create_table in Pointer to TABLE_LIST object providing database + and name for table to be created or to be open + extra_fields in/out Initial list of fields for table to be created + keys in List of keys for table to be created + items in List of items which should be used to produce rest + of fields for the table (corresponding fields will + be added to the end of 'extra_fields' list) + lock out Pointer to the MYSQL_LOCK object for table created + (open) will be returned in this parameter. Since + this table is not included in THD::lock caller is + responsible for explicitly unlocking this table. + hooks + + NOTES + If 'create_info->options' bitmask has HA_LEX_CREATE_IF_NOT_EXISTS + flag and table with name provided already exists then this function will + simply open existing table. + Also note that create, open and lock sequence in this function is not + atomic and thus contains gap for deadlock and can cause other troubles. + Since this function contains some logic specific to CREATE TABLE ... SELECT + it should be changed before it can be used in other contexts. + + RETURN VALUES + non-zero Pointer to TABLE object for table created or opened + 0 Error +*/ + +static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, + TABLE_LIST *create_table, + List<create_field> *extra_fields, + List<Key> *keys, + List<Item> *items, + MYSQL_LOCK **lock, + TABLEOP_HOOKS *hooks) +{ + TABLE tmp_table; // Used during 'create_field()' + TABLE_SHARE share; + TABLE *table= 0; + uint select_field_count= items->elements; + /* Add selected items to field list */ + List_iterator_fast<Item> it(*items); + Item *item; + Field *tmp_field; + bool not_used; + DBUG_ENTER("create_table_from_items"); + + tmp_table.alias= 0; + tmp_table.timestamp_field= 0; + tmp_table.s= &share; + init_tmp_table_share(&share, "", 0, "", ""); + + tmp_table.s->db_create_options=0; + tmp_table.s->blob_ptr_size= portable_sizeof_char_ptr; + tmp_table.s->db_low_byte_first= + test(create_info->db_type == &myisam_hton || + create_info->db_type == &heap_hton); + tmp_table.null_row=tmp_table.maybe_null=0; + + while ((item=it++)) + { + create_field *cr_field; + Field *field; + if (item->type() == Item::FUNC_ITEM) + field=item->tmp_table_field(&tmp_table); + else + field=create_tmp_field(thd, &tmp_table, item, item->type(), + (Item ***) 0, &tmp_field, 0, 0, 0, 0, 0); + if (!field || + !(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ? + ((Item_field *)item)->field : + (Field*) 0)))) + DBUG_RETURN(0); + if (item->maybe_null) + cr_field->flags &= ~NOT_NULL_FLAG; + extra_fields->push_back(cr_field); + } + /* + create and lock table + + We don't log the statement, it will be logged later. + + If this is a HEAP table, the automatic DELETE FROM which is written to the + binlog when a HEAP table is opened for the first time since startup, must + not be written: 1) it would be wrong (imagine we're in CREATE SELECT: we + don't want to delete from it) 2) it would be written before the CREATE + TABLE, which is a wrong order. So we keep binary logging disabled when we + open_table(). + NOTE: By locking table which we just have created (or for which we just have + have found that it already exists) separately from other tables used by the + statement we create potential window for deadlock. + TODO: create and open should be done atomic ! + */ + { + tmp_disable_binlog(thd); + if (!mysql_create_table(thd, create_table->db, create_table->table_name, + create_info, *extra_fields, *keys, 0, + select_field_count)) + { + /* + If we are here in prelocked mode we either create temporary table + or prelocked mode is caused by the SELECT part of this statement. + */ + DBUG_ASSERT(!thd->prelocked_mode || + create_info->options & HA_LEX_CREATE_TMP_TABLE || + thd->lex->requires_prelocking()); + + /* + NOTE: We don't want to ignore set of locked tables here if we are + under explicit LOCK TABLES since it will open gap for deadlock + too wide (and also is not backward compatible). + */ + + if (! (table= open_table(thd, create_table, thd->mem_root, (bool*) 0, + (MYSQL_LOCK_IGNORE_FLUSH | + ((thd->prelocked_mode == PRELOCKED) ? + MYSQL_OPEN_IGNORE_LOCKED_TABLES:0))))) + quick_rm_table(create_info->db_type, create_table->db, + table_case_name(create_info, create_table->table_name)); + } + reenable_binlog(thd); + if (!table) // open failed + DBUG_RETURN(0); + } + + /* + FIXME: What happens if trigger manages to be created while we are + obtaining this lock ? May be it is sensible just to disable + trigger execution in this case ? Or will MYSQL_LOCK_IGNORE_FLUSH + save us from that ? + */ + table->reginfo.lock_type=TL_WRITE; + hooks->prelock(&table, 1); // Call prelock hooks + if (! ((*lock)= mysql_lock_tables(thd, &table, 1, + MYSQL_LOCK_IGNORE_FLUSH, ¬_used))) + { + VOID(pthread_mutex_lock(&LOCK_open)); + hash_delete(&open_cache,(byte*) table); + VOID(pthread_mutex_unlock(&LOCK_open)); + quick_rm_table(create_info->db_type, create_table->db, + table_case_name(create_info, create_table->table_name)); + DBUG_RETURN(0); + } + table->file->extra(HA_EXTRA_WRITE_CACHE); + DBUG_RETURN(table); +} + + int select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 826f09af425..af6ac7d862a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2857,6 +2857,17 @@ mysql_execute_command(THD *thd) res= 1; goto end_with_restore_list; } +#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 if (select_lex->item_list.elements) // With select { select_result *result; @@ -2926,15 +2937,6 @@ mysql_execute_command(THD *thd) lex->like_name); else { -#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 res= mysql_create_table(thd, create_table->db, create_table->table_name, &lex->create_info, lex->create_list, @@ -3994,7 +3996,6 @@ end_with_restore_list: if (thd->security_ctx->user) // If not replication { LEX_USER *user; - uint counter; List_iterator <LEX_USER> user_list(lex->users_list); while ((user= user_list++)) @@ -4012,7 +4013,8 @@ end_with_restore_list: user->host.str, thd->security_ctx->host_or_ip)) { // TODO: use check_change_password() - if (check_acl_user(user, &counter) && user->password.str && + if (is_acl_user(user->host.str, user->user.str) && + user->password.str && check_access(thd, UPDATE_ACL,"mysql",0,1,1,0)) { my_message(ER_PASSWORD_NOT_ALLOWED, @@ -5902,6 +5904,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) } else { + DBUG_ASSERT(thd->net.report_error); DBUG_PRINT("info",("Command aborted. Fatal_error: %d", thd->is_fatal_error)); query_cache_abort(&thd->net); @@ -7455,7 +7458,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, lex->create_info.merge_list.first)) goto err; if (grant_option && want_priv != CREATE_TMP_ACL && - check_grant(thd, want_priv, create_table, 0, UINT_MAX, 0)) + check_grant(thd, want_priv, create_table, 0, 1, 0)) goto err; if (select_lex->item_list.elements) diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 71b8e9b1d95..e946e972968 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -4076,6 +4076,7 @@ that are reorganised. tab_part_info->use_default_partitions= FALSE; } tab_part_info->use_default_no_partitions= FALSE; + tab_part_info->is_auto_partitioned= FALSE; } } else if (alter_info->flags == ALTER_DROP_PARTITION) @@ -4091,6 +4092,8 @@ that are reorganised. uint no_parts_dropped= alter_info->partition_names.elements; uint no_parts_found= 0; List_iterator<partition_element> part_it(tab_part_info->partitions); + + tab_part_info->is_auto_partitioned= FALSE; if (!(tab_part_info->part_type == RANGE_PARTITION || tab_part_info->part_type == LIST_PARTITION)) { @@ -4275,7 +4278,10 @@ state of p1. tab_part_info->no_parts= no_parts_remain; } if (!(alter_info->flags & ALTER_TABLE_REORG)) + { tab_part_info->use_default_no_partitions= FALSE; + tab_part_info->is_auto_partitioned= FALSE; + } } else if (alter_info->flags == ALTER_REORGANIZE_PARTITION) { @@ -4294,6 +4300,8 @@ state of p1. uint no_parts_new= thd->work_part_info->partitions.elements; partition_info *alt_part_info= thd->work_part_info; uint check_total_partitions; + + tab_part_info->is_auto_partitioned= FALSE; if (no_parts_reorged > tab_part_info->no_parts) { my_error(ER_REORG_PARTITION_NOT_EXIST, MYF(0)); @@ -4510,7 +4518,7 @@ the generated partition syntax in a correct manner. if (alter_info->flags & ALTER_REMOVE_PARTITIONING) { DBUG_PRINT("info", ("Remove partitioning")); - if (!(thd->lex->create_info.used_fields & HA_CREATE_USED_ENGINE)) + if (!(create_info->used_fields & HA_CREATE_USED_ENGINE)) { DBUG_PRINT("info", ("No explicit engine used")); create_info->db_type= table->part_info->default_engine_type; @@ -4527,14 +4535,29 @@ the generated partition syntax in a correct manner. beneath. */ thd->work_part_info= table->part_info; - if (thd->lex->create_info.used_fields & HA_CREATE_USED_ENGINE && + if (create_info->used_fields & HA_CREATE_USED_ENGINE && create_info->db_type != table->part_info->default_engine_type) { /* Make sure change of engine happens to all partitions. */ DBUG_PRINT("info", ("partition changed")); - set_engine_all_partitions(thd->work_part_info, create_info->db_type); + if (table->part_info->is_auto_partitioned) + { + /* + If the user originally didn't specify partitioning to be + used we can remove it now. + */ + thd->work_part_info= NULL; + } + else + { + /* + Ensure that all partitions have the proper engine set-up + */ + set_engine_all_partitions(thd->work_part_info, + create_info->db_type); + } *partition_changed= TRUE; } } diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 6d0a0f4799c..14bcb437337 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2087,19 +2087,20 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) sl->exclude_from_table_unique_test= FALSE; /* - Copy WHERE, HAVING clause pointers to avoid damaging them by optimisation + Copy WHERE, HAVING clause pointers to avoid damaging them + by optimisation */ - if (sl->prep_where) - { - sl->where= sl->prep_where->copy_andor_structure(thd); - sl->where->cleanup(); - } - if (sl->prep_having) - { - sl->having= sl->prep_having->copy_andor_structure(thd); - sl->having->cleanup(); - } - DBUG_ASSERT(sl->join == 0); + if (sl->prep_where) + { + sl->where= sl->prep_where->copy_andor_structure(thd); + sl->where->cleanup(); + } + if (sl->prep_having) + { + sl->having= sl->prep_having->copy_andor_structure(thd); + sl->having->cleanup(); + } + DBUG_ASSERT(sl->join == 0); ORDER *order; /* Fix GROUP list */ for (order= (ORDER *)sl->group_list.first; order; order= order->next) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5f8c4dd2e1a..810b7789f3d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2419,7 +2419,19 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, { if (old->field == new_fields->field) { - if (new_fields->val->used_tables()) + /* + NOTE: below const_item() call really works as "!used_tables()", i.e. + it can return FALSE where it is feasible to make it return TRUE. + + The cause is as follows: Some of the tables are already known to be + const tables (the detection code is in make_join_statistics(), + above the update_ref_and_keys() call), but we didn't propagate + information about this: TABLE::const_table is not set to TRUE, and + Item::update_used_tables() hasn't been called for each item. + The result of this is that we're missing some 'ref' accesses. + TODO: OptimizerTeam: Fix this + */ + if (!new_fields->val->const_item()) { /* If the value matches, we can use the key reference. @@ -2449,7 +2461,8 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, new_fields->null_rejecting); } else if (old->eq_func && new_fields->eq_func && - (old->val->is_null() || new_fields->val->is_null())) + ((old->val->const_item() && old->val->is_null()) || + new_fields->val->is_null())) { /* field = expression OR field IS NULL */ old->level= and_level; @@ -3364,7 +3377,10 @@ best_access_path(JOIN *join, uint key= keyuse->key; KEY *keyinfo= table->key_info+key; bool ft_key= (keyuse->keypart == FT_KEYPART); - uint found_ref_or_null= 0; + /* Bitmap of keyparts where the ref access is over 'keypart=const': */ + key_part_map const_part= 0; + /* The or-null keypart in ref-or-null access: */ + key_part_map ref_or_null_part= 0; /* Calculate how many key segments of the current key we can use */ start_key= keyuse; @@ -3376,12 +3392,14 @@ best_access_path(JOIN *join, do { if (!(remaining_tables & keyuse->used_tables) && - !(found_ref_or_null & keyuse->optimize)) + !(ref_or_null_part && (keyuse->optimize & + KEY_OPTIMIZE_REF_OR_NULL))) { found_part|= keyuse->keypart_map; - double tmp= prev_record_reads(join, - (found_ref | - keyuse->used_tables)); + if (!(keyuse->used_tables & ~join->const_table_map)) + const_part|= keyuse->keypart_map; + double tmp= prev_record_reads(join, (found_ref | + keyuse->used_tables)); if (tmp < best_prev_record_reads) { best_part_found_ref= keyuse->used_tables; @@ -3393,8 +3411,8 @@ best_access_path(JOIN *join, If there is one 'key_column IS NULL' expression, we can use this ref_or_null optimisation of this field */ - found_ref_or_null|= (keyuse->optimize & - KEY_OPTIMIZE_REF_OR_NULL); + if (keyuse->optimize & KEY_OPTIMIZE_REF_OR_NULL) + ref_or_null_part |= keyuse->keypart_map; } keyuse++; } while (keyuse->table == table && keyuse->key == key && @@ -3430,7 +3448,7 @@ best_access_path(JOIN *join, Check if we found full key */ if (found_part == PREV_BITS(uint,keyinfo->key_parts) && - !found_ref_or_null) + !ref_or_null_part) { /* use eq key */ max_key_part= (uint) ~0; if ((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME) @@ -3442,6 +3460,23 @@ best_access_path(JOIN *join, { if (!found_ref) { /* We found a const key */ + /* + ReuseRangeEstimateForRef-1: + We get here if we've found a ref(const) (c_i are constants): + "(keypart1=c1) AND ... AND (keypartN=cN)" [ref_const_cond] + + If range optimizer was able to construct a "range" + access on this index, then its condition "quick_cond" was + eqivalent to ref_const_cond (*), and we can re-use E(#rows) + from the range optimizer. + + Proof of (*): By properties of range and ref optimizers + quick_cond will be equal or tighther than ref_const_cond. + ref_const_cond already covers "smallest" possible interval - + a singlepoint interval over all keyparts. Therefore, + quick_cond is equivalent to ref_const_cond (if it was an + empty interval we wouldn't have got here). + */ if (table->quick_keys.is_set(key)) records= (double) table->quick_rows[key]; else @@ -3462,6 +3497,23 @@ best_access_path(JOIN *join, if (records < 2.0) records=2.0; /* Can't be as good as a unique */ } + /* + ReuseRangeEstimateForRef-2: We get here if we could not reuse + E(#rows) from range optimizer. Make another try: + + If range optimizer produced E(#rows) for a prefix of the ref + access we're considering, and that E(#rows) is lower then our + current estimate, make an adjustment. The criteria of when we + can make an adjustment is a special case of the criteria used + in ReuseRangeEstimateForRef-3. + */ + if (table->quick_keys.is_set(key) && + const_part & (1 << table->quick_key_parts[key]) && + table->quick_n_ranges[key] == 1 && + records > (double) table->quick_rows[key]) + { + records= (double) table->quick_rows[key]; + } } /* Limit the number of matched rows */ tmp= records; @@ -3490,12 +3542,50 @@ best_access_path(JOIN *join, { max_key_part= max_part_bit(found_part); /* - Check if quick_range could determinate how many rows we - will match + ReuseRangeEstimateForRef-3: + We're now considering a ref[or_null] access via + (t.keypart1=e1 AND ... AND t.keypartK=eK) [ OR + (same-as-above but with one cond replaced + with "t.keypart_i IS NULL")] (**) + + Try re-using E(#rows) from "range" optimizer: + We can do so if "range" optimizer used the same intervals as + in (**). The intervals used by range optimizer may be not + available at this point (as "range" access might have choosen to + create quick select over another index), so we can't compare + them to (**). We'll make indirect judgements instead. + The sufficient conditions for re-use are: + (C1) All e_i in (**) are constants, i.e. found_ref==FALSE. (if + this is not satisfied we have no way to know which ranges + will be actually scanned by 'ref' until we execute the + join) + (C2) max #key parts in 'range' access == K == max_key_part (this + is apparently a necessary requirement) + + We also have a property that "range optimizer produces equal or + tighter set of scan intervals than ref(const) optimizer". Each + of the intervals in (**) are "tightest possible" intervals when + one limits itself to using keyparts 1..K (which we do in #2). + From here it follows that range access used either one, or + both of the (I1) and (I2) intervals: + + (t.keypart1=c1 AND ... AND t.keypartK=eK) (I1) + (same-as-above but with one cond replaced + with "t.keypart_i IS NULL") (I2) + + The remaining part is to exclude the situation where range + optimizer used one interval while we're considering + ref-or-null and looking for estimate for two intervals. This + is done by last limitation: + + (C3) "range optimizer used (have ref_or_null?2:1) intervals" */ - if (table->quick_keys.is_set(key) && - table->quick_key_parts[key] == max_key_part) + if (table->quick_keys.is_set(key) && !found_ref && //(C1) + table->quick_key_parts[key] == max_key_part && //(C2) + table->quick_n_ranges[key] == 1+test(ref_or_null_part)) //(C3) + { tmp= records= (double) table->quick_rows[key]; + } else { /* Check if we have statistic about the distribution */ @@ -3539,21 +3629,37 @@ best_access_path(JOIN *join, } records = (ulong) tmp; } + + if (ref_or_null_part) + { + /* We need to do two key searches to find key */ + tmp *= 2.0; + records *= 2.0; + } + /* - If quick_select was used on a part of this key, we know - the maximum number of rows that the key can match. + ReuseRangeEstimateForRef-4: We get here if we could not reuse + E(#rows) from range optimizer. Make another try: + + If range optimizer produced E(#rows) for a prefix of the ref + access we're considering, and that E(#rows) is lower then our + current estimate, make the adjustment. + + The decision whether we can re-use the estimate from the range + optimizer is the same as in ReuseRangeEstimateForRef-3, + applied to first table->quick_key_parts[key] key parts. */ if (table->quick_keys.is_set(key) && table->quick_key_parts[key] <= max_key_part && + const_part & (1 << table->quick_key_parts[key]) && + table->quick_n_ranges[key] == 1 + test(ref_or_null_part & + const_part) && records > (double) table->quick_rows[key]) - tmp= records= (double) table->quick_rows[key]; - else if (found_ref_or_null) { - /* We need to do two key searches to find key */ - tmp *= 2.0; - records *= 2.0; + tmp= records= (double) table->quick_rows[key]; } } + /* Limit the number of matched rows */ set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key); if (table->used_keys.is_set(key)) @@ -4369,344 +4475,11 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, if ((rest_tables & real_table_bit) && !(rest_tables & s->dependent) && (!idx|| !check_interleaving_with_nj(join->positions[idx-1].table, s))) { - double best,best_time,records; - best=best_time=records=DBL_MAX; - KEYUSE *best_key=0; - uint best_max_key_part=0; - my_bool found_constraint= 0; - - if (s->keyuse) - { /* Use key if possible */ - TABLE *table=s->table; - KEYUSE *keyuse,*start_key=0; - double best_records=DBL_MAX; - uint max_key_part=0; - - /* Test how we can use keys */ - rec= s->records/MATCHING_ROWS_IN_OTHER_TABLE; // Assumed records/key - for (keyuse=s->keyuse ; keyuse->table == table ;) - { - key_part_map found_part=0; - table_map found_ref=0; - uint key=keyuse->key; - KEY *keyinfo=table->key_info+key; - bool ft_key=(keyuse->keypart == FT_KEYPART); - uint found_ref_or_null= 0; - - /* Calculate how many key segments of the current key we can use */ - start_key=keyuse; - do - { - uint keypart=keyuse->keypart; - table_map best_part_found_ref= 0; - double best_prev_record_reads= DBL_MAX; - do - { - if (!(rest_tables & keyuse->used_tables) && - !(found_ref_or_null & keyuse->optimize)) - { - found_part|=keyuse->keypart_map; - double tmp= prev_record_reads(join, - (found_ref | - keyuse->used_tables)); - if (tmp < best_prev_record_reads) - { - best_part_found_ref= keyuse->used_tables; - best_prev_record_reads= tmp; - } - if (rec > keyuse->ref_table_rows) - rec= keyuse->ref_table_rows; - /* - If there is one 'key_column IS NULL' expression, we can - use this ref_or_null optimisation of this field - */ - found_ref_or_null|= (keyuse->optimize & - KEY_OPTIMIZE_REF_OR_NULL); - } - keyuse++; - } while (keyuse->table == table && keyuse->key == key && - keyuse->keypart == keypart); - found_ref|= best_part_found_ref; - } while (keyuse->table == table && keyuse->key == key); - - /* - Assume that that each key matches a proportional part of table. - */ - if (!found_part && !ft_key) - continue; // Nothing usable found - if (rec < MATCHING_ROWS_IN_OTHER_TABLE) - rec= MATCHING_ROWS_IN_OTHER_TABLE; // Fix for small tables - - /* - ft-keys require special treatment - */ - if (ft_key) - { - /* - Really, there should be records=0.0 (yes!) - but 1.0 would be probably safer - */ - tmp=prev_record_reads(join,found_ref); - records=1.0; - } - else - { - found_constraint= 1; - /* - Check if we found full key - */ - if (found_part == PREV_BITS(uint,keyinfo->key_parts) && - !found_ref_or_null) - { /* use eq key */ - max_key_part= (uint) ~0; - if ((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY | - HA_END_SPACE_KEY)) == HA_NOSAME) - { - tmp=prev_record_reads(join,found_ref); - records=1.0; - } - else - { - if (!found_ref) - { // We found a const key - if (table->quick_keys.is_set(key)) - records= (double) table->quick_rows[key]; - else - { - /* quick_range couldn't use key! */ - records= (double) s->records/rec; - } - } - else - { - if (!(records=keyinfo->rec_per_key[keyinfo->key_parts-1])) - { // Prefere longer keys - records= - ((double) s->records / (double) rec * - (1.0 + - ((double) (table->s->max_key_length-keyinfo->key_length) / - (double) table->s->max_key_length))); - if (records < 2.0) - records=2.0; // Can't be as good as a unique - } - } - /* Limit the number of matched rows */ - tmp= records; - set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key); - if (table->used_keys.is_set(key)) - { - /* we can use only index tree */ - uint keys_per_block= table->file->block_size/2/ - (keyinfo->key_length+table->file->ref_length)+1; - tmp=record_count*(tmp+keys_per_block-1)/keys_per_block; - } - else - tmp=record_count*min(tmp,s->worst_seeks); - } - } - else - { - /* - Use as much key-parts as possible and a uniq key is better - than a not unique key - Set tmp to (previous record count) * (records / combination) - */ - if ((found_part & 1) && - (!(table->file->index_flags(key,0,0) & HA_ONLY_WHOLE_INDEX) || - found_part == PREV_BITS(uint,keyinfo->key_parts))) - { - max_key_part=max_part_bit(found_part); - /* - Check if quick_range could determinate how many rows we - will match - */ - if (table->quick_keys.is_set(key) && - table->quick_key_parts[key] == max_key_part) - tmp=records= (double) table->quick_rows[key]; - else - { - /* Check if we have statistic about the distribution */ - if ((records=keyinfo->rec_per_key[max_key_part-1])) - tmp=records; - else - { - /* - Assume that the first key part matches 1% of the file - and that the whole key matches 10 (duplicates) or 1 - (unique) records. - Assume also that more key matches proportionally more - records - This gives the formula: - records= (x * (b-a) + a*c-b)/(c-1) - - b = records matched by whole key - a = records matched by first key part (10% of all records?) - c = number of key parts in key - x = used key parts (1 <= x <= c) - */ - double rec_per_key; - rec_per_key= keyinfo->rec_per_key[keyinfo->key_parts-1] ? - (double) keyinfo->rec_per_key[keyinfo->key_parts-1] : - (double) s->records/rec+1; - if (!s->records) - tmp=0; - else if (rec_per_key/(double) s->records >= 0.01) - tmp=rec_per_key; - else - { - double a=s->records*0.01; - tmp=(max_key_part * (rec_per_key - a) + - a*keyinfo->key_parts - rec_per_key)/ - (keyinfo->key_parts-1); - set_if_bigger(tmp,1.0); - } - records=(ulong) tmp; - } - /* - If quick_select was used on a part of this key, we know - the maximum number of rows that the key can match. - */ - if (table->quick_keys.is_set(key) && - table->quick_key_parts[key] <= max_key_part && - records > (double) table->quick_rows[key]) - tmp= records= (double) table->quick_rows[key]; - else if (found_ref_or_null) - { - /* We need to do two key searches to find key */ - tmp*= 2.0; - records*= 2.0; - } - } - /* Limit the number of matched rows */ - set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key); - if (table->used_keys.is_set(key)) - { - /* we can use only index tree */ - uint keys_per_block= table->file->block_size/2/ - (keyinfo->key_length+table->file->ref_length)+1; - tmp=record_count*(tmp+keys_per_block-1)/keys_per_block; - } - else - tmp=record_count*min(tmp,s->worst_seeks); - } - else - tmp=best_time; // Do nothing - } - } /* not ft_key */ - if (tmp < best_time - records/(double) TIME_FOR_COMPARE) - { - best_time=tmp + records/(double) TIME_FOR_COMPARE; - best=tmp; - best_records=records; - best_key=start_key; - best_max_key_part=max_key_part; - } - } - records=best_records; - } - - /* - Don't test table scan if it can't be better. - Prefer key lookup if we would use the same key for scanning. - - Don't do a table scan on InnoDB tables, if we can read the used - parts of the row from any of the used index. - This is because table scans uses index and we would not win - anything by using a table scan. - (see comment in best_access_path() for more details on the below - condition) - */ - if ((records >= s->found_records || best > s->read_time) && - !(s->quick && best_key && s->quick->index == best_key->key && - best_max_key_part >= s->table->quick_key_parts[best_key->key]) && - !((s->table->file->table_flags() & HA_TABLE_SCAN_ON_INDEX) && - ! s->table->used_keys.is_clear_all() && best_key) && - !(s->table->force_index && best_key && !s->quick)) - { // Check full join - ha_rows rnd_records= s->found_records; - /* - If there is a restriction on the table, assume that 25% of the - rows can be skipped on next part. - This is to force tables that this table depends on before this - table - */ - if (found_constraint) - rnd_records-= rnd_records/4; - - /* - Range optimizer never proposes a RANGE if it isn't better - than FULL: so if RANGE is present, it's always preferred to FULL. - Here we estimate its cost. - */ - if (s->quick) - { - /* - For each record we: - - read record range through 'quick' - - skip rows which does not satisfy WHERE constraints - */ - tmp= record_count * - (s->quick->read_time + - (s->found_records - rnd_records)/(double) TIME_FOR_COMPARE); - } - else - { - /* Estimate cost of reading table. */ - tmp= s->table->file->scan_time(); - if (s->table->map & join->outer_join) // Can't use join cache - { - /* - For each record we have to: - - read the whole table record - - skip rows which does not satisfy join condition - */ - tmp= record_count * - (tmp + - (s->records - rnd_records)/(double) TIME_FOR_COMPARE); - } - else - { - /* We read the table as many times as join buffer becomes full. */ - tmp*= (1.0 + floor((double) cache_record_length(join,idx) * - record_count / - (double) thd->variables.join_buff_size)); - /* - We don't make full cartesian product between rows in the scanned - table and existing records because we skip all rows from the - scanned table, which does not satisfy join condition when - we read the table (see flush_cached_records for details). Here we - take into account cost to read and skip these records. - */ - tmp+= (s->records - rnd_records)/(double) TIME_FOR_COMPARE; - } - } - - /* - We estimate the cost of evaluating WHERE clause for found records - as record_count * rnd_records / TIME_FOR_COMPARE. This cost plus - tmp give us total cost of using TABLE SCAN - */ - if (best == DBL_MAX || - (tmp + record_count/(double) TIME_FOR_COMPARE*rnd_records < - best + record_count/(double) TIME_FOR_COMPARE*records)) - { - /* - If the table has a range (s->quick is set) make_join_select() - will ensure that this will be used - */ - best=tmp; - records= rows2double(rnd_records); - best_key=0; - } - } - join->positions[idx].records_read= records; - join->positions[idx].key=best_key; - join->positions[idx].table= s; - if (!best_key && idx == join->const_tables && - s->table == join->sort_by_table && - join->unit->select_limit_cnt >= records) - join->sort_by_table= (TABLE*) 1; // Must use temporary table - + double records, best; + best_access_path(join, s, thd, rest_tables, idx, record_count, + read_time); + records= join->positions[idx].records_read; + best= join->positions[idx].read_time; /* Go to the next level only if there hasn't been a better key on this level! This will cut down the search for a lot simple cases! @@ -12803,6 +12576,17 @@ create_distinct_group(THD *thd, Item **ref_pointer_array, { if (!item->const_item() && !item->with_sum_func && !item->marker) { + /* + Don't put duplicate columns from the SELECT list into the + GROUP BY list. + */ + ORDER *ord_iter; + for (ord_iter= group; ord_iter; ord_iter= ord_iter->next) + if ((*ord_iter->item)->eq(item, 1)) + break; + if (ord_iter) + continue; + ORDER *ord=(ORDER*) thd->calloc(sizeof(ORDER)); if (!ord) return 0; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 7c8499f2a2c..f94ddc57c80 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -938,6 +938,9 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, MODE_DB2 | MODE_MAXDB | MODE_ANSI)) != 0; + bool limited_mysql_mode= (thd->variables.sql_mode & (MODE_NO_FIELD_OPTIONS | + MODE_MYSQL323 | + MODE_MYSQL40)) != 0; DBUG_ENTER("store_create_info"); DBUG_PRINT("enter",("table: %s", table->s->table_name.str)); @@ -1022,8 +1025,8 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, has_default= (field->type() != FIELD_TYPE_BLOB && !(field->flags & NO_DEFAULT_VALUE_FLAG) && field->unireg_check != Field::NEXT_NUMBER && - !((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) && - has_now_default)); + !((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) + && has_now_default)); if (has_default) { @@ -1052,8 +1055,7 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(tmp); } - if (!(thd->variables.sql_mode & MODE_NO_FIELD_OPTIONS) && - table->timestamp_field == field && + if (!limited_mysql_mode && table->timestamp_field == field && field->unireg_check != Field::TIMESTAMP_DN_FIELD) packet->append(STRING_WITH_LEN(" ON UPDATE CURRENT_TIMESTAMP")); @@ -1100,12 +1102,6 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, if (!found_primary) append_identifier(thd, packet, key_info->name, strlen(key_info->name)); -#if MYSQL_VERSION_ID < 50300 - /* Key options moved to after key parts in 5.3.0 */ - if (!thd->variables.new_mode) - store_key_options(thd, packet, table, key_info); -#endif - packet->append(STRING_WITH_LEN(" (")); for (uint j=0 ; j < key_info->key_parts ; j++,key_part++) @@ -1130,10 +1126,7 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, } } packet->append(')'); -#if MYSQL_VERSION_ID < 50300 - if (thd->variables.new_mode) -#endif - store_key_options(thd, packet, table, key_info); + store_key_options(thd, packet, table, key_info); if (key_info->parser) { packet->append(" WITH PARSER ", 13); @@ -1190,6 +1183,25 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(file->table_type()); #endif } + + /* + Add AUTO_INCREMENT=... if there is an AUTO_INCREMENT column, + and NEXT_ID > 1 (the default). We must not print the clause + for engines that do not support this as it would break the + import of dumps, but as of this writing, the test for whether + AUTO_INCREMENT columns are allowed and wether AUTO_INCREMENT=... + is supported is identical, !(file->table_flags() & HA_NO_AUTO_INCREMENT)) + Because of that, we do not explicitly test for the feature, + but may extrapolate its existence from that of an AUTO_INCREMENT column. + */ + + if(create_info.auto_increment_value > 1) + { + packet->append(" AUTO_INCREMENT=", 16); + end= longlong10_to_str(create_info.auto_increment_value, buff,10); + packet->append(buff, (uint) (end - buff)); + } + if (share->table_charset && !(thd->variables.sql_mode & MODE_MYSQL323) && @@ -1274,6 +1286,7 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, uint part_syntax_len; char *part_syntax; if (table->part_info && + (!table->part_info->is_auto_partitioned) && ((part_syntax= generate_partition_syntax(table->part_info, &part_syntax_len, FALSE,FALSE)))) @@ -2919,7 +2932,7 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, field->real_type() == MYSQL_TYPE_STRING) // For binary type { uint32 octet_max_length= field->max_length(); - if (octet_max_length != (uint32) 4294967295U) + if (is_blob && octet_max_length != (uint32) 4294967295U) octet_max_length /= field->charset()->mbmaxlen; longlong char_max_len= is_blob ? (longlong) octet_max_length / field->charset()->mbminlen : @@ -3156,17 +3169,18 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, const char *wild, bool full_access, const char *sp_user) { String tmp_string; + String sp_db, sp_name, definer; TIME time; LEX *lex= thd->lex; CHARSET_INFO *cs= system_charset_info; - const char *sp_db, *sp_name, *definer; - sp_db= get_field(thd->mem_root, proc_table->field[0]); - sp_name= get_field(thd->mem_root, proc_table->field[1]); - definer= get_field(thd->mem_root, proc_table->field[11]); + get_field(thd->mem_root, proc_table->field[0], &sp_db); + get_field(thd->mem_root, proc_table->field[1], &sp_name); + get_field(thd->mem_root, proc_table->field[11], &definer); if (!full_access) - full_access= !strcmp(sp_user, definer); - if (!full_access && check_some_routine_access(thd, sp_db, sp_name, - proc_table->field[2]->val_int() == TYPE_ENUM_PROCEDURE)) + full_access= !strcmp(sp_user, definer.ptr()); + if (!full_access && check_some_routine_access(thd, sp_db.ptr(), sp_name.ptr(), + proc_table->field[2]->val_int() == + TYPE_ENUM_PROCEDURE)) return 0; if (lex->orig_sql_command == SQLCOM_SHOW_STATUS_PROC && @@ -3176,13 +3190,13 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, lex->orig_sql_command == SQLCOM_END) { restore_record(table, s->default_values); - if (!wild || !wild[0] || !wild_compare(sp_name, wild, 0)) + if (!wild || !wild[0] || !wild_compare(sp_name.ptr(), wild, 0)) { int enum_idx= proc_table->field[5]->val_int(); - table->field[3]->store(sp_name, strlen(sp_name), cs); + table->field[3]->store(sp_name.ptr(), sp_name.length(), cs); get_field(thd->mem_root, proc_table->field[3], &tmp_string); table->field[0]->store(tmp_string.ptr(), tmp_string.length(), cs); - table->field[2]->store(sp_db, strlen(sp_db), cs); + table->field[2]->store(sp_db.ptr(), sp_db.length(), cs); get_field(thd->mem_root, proc_table->field[2], &tmp_string); table->field[4]->store(tmp_string.ptr(), tmp_string.length(), cs); if (proc_table->field[2]->val_int() == TYPE_ENUM_FUNCTION) @@ -3214,7 +3228,7 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, table->field[17]->store(tmp_string.ptr(), tmp_string.length(), cs); get_field(thd->mem_root, proc_table->field[15], &tmp_string); table->field[18]->store(tmp_string.ptr(), tmp_string.length(), cs); - table->field[19]->store(definer, strlen(definer), cs); + table->field[19]->store(definer.ptr(), definer.length(), cs); return schema_table_store_record(thd, table); } } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index f890f504952..f530af33e48 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3078,6 +3078,7 @@ bool mysql_create_table_internal(THD *thd, } file->set_auto_partitions(part_info); part_info->default_engine_type= create_info->db_type; + part_info->is_auto_partitioned= TRUE; } if (part_info) { @@ -3138,8 +3139,8 @@ bool mysql_create_table_internal(THD *thd, } DBUG_PRINT("info", ("db_type = %d", ha_legacy_type(part_info->default_engine_type))); - if (part_info->check_partition_info( &engine_type, file, - create_info->max_rows)) + if (part_info->check_partition_info(&engine_type, file, + create_info->max_rows)) goto err; part_info->default_engine_type= engine_type; @@ -3198,6 +3199,12 @@ bool mysql_create_table_internal(THD *thd, } else if (create_info->db_type != engine_type) { + /* + We come here when we don't use a partitioned handler. + Since we use a partitioned table it must be "native partitioned". + We have switched engine from defaults, most likely only specified + engines in partition clauses. + */ delete file; if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, engine_type))) { @@ -3419,111 +3426,6 @@ make_unique_key_name(const char *field_name,KEY *start,KEY *end) /**************************************************************************** -** Create table from a list of fields and items -****************************************************************************/ - -TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, - TABLE_LIST *create_table, - List<create_field> *extra_fields, - List<Key> *keys, - List<Item> *items, - MYSQL_LOCK **lock, - TABLEOP_HOOKS *hooks) -{ - TABLE tmp_table; // Used during 'create_field()' - TABLE_SHARE share; - TABLE *table= 0; - uint select_field_count= items->elements; - /* Add selected items to field list */ - List_iterator_fast<Item> it(*items); - Item *item; - Field *tmp_field; - bool not_used; - DBUG_ENTER("create_table_from_items"); - - tmp_table.alias= 0; - tmp_table.timestamp_field= 0; - tmp_table.s= &share; - init_tmp_table_share(&share, "", 0, "", ""); - - tmp_table.s->db_create_options=0; - tmp_table.s->blob_ptr_size= portable_sizeof_char_ptr; - tmp_table.s->db_low_byte_first= - test(create_info->db_type == &myisam_hton || - create_info->db_type == &heap_hton); - tmp_table.null_row=tmp_table.maybe_null=0; - - while ((item=it++)) - { - create_field *cr_field; - Field *field; - if (item->type() == Item::FUNC_ITEM) - field=item->tmp_table_field(&tmp_table); - else - field=create_tmp_field(thd, &tmp_table, item, item->type(), - (Item ***) 0, &tmp_field, 0, 0, 0, 0, 0); - if (!field || - !(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ? - ((Item_field *)item)->field : - (Field*) 0)))) - DBUG_RETURN(0); - if (item->maybe_null) - cr_field->flags &= ~NOT_NULL_FLAG; - extra_fields->push_back(cr_field); - } - /* - create and lock table - - We don't log the statement, it will be logged later. - - If this is a HEAP table, the automatic DELETE FROM which is written to the - binlog when a HEAP table is opened for the first time since startup, must - not be written: 1) it would be wrong (imagine we're in CREATE SELECT: we - don't want to delete from it) 2) it would be written before the CREATE - TABLE, which is a wrong order. So we keep binary logging disabled when we - open_table(). - TODO: create and open should be done atomic ! - */ - { - tmp_disable_binlog(thd); - if (!mysql_create_table(thd, create_table->db, create_table->table_name, - create_info, *extra_fields, *keys, 0, - select_field_count)) - { - if (! (table= open_table(thd, create_table, thd->mem_root, (bool*) 0, - MYSQL_LOCK_IGNORE_FLUSH))) - quick_rm_table(create_info->db_type, create_table->db, - table_case_name(create_info, create_table->table_name)); - } - reenable_binlog(thd); - if (!table) // open failed - DBUG_RETURN(0); - } - - /* - FIXME: What happens if trigger manages to be created while we are - obtaining this lock ? May be it is sensible just to disable - trigger execution in this case ? Or will MYSQL_LOCK_IGNORE_FLUSH - save us from that ? - */ - table->reginfo.lock_type=TL_WRITE; - hooks->prelock(&table, 1); // Call prelock hooks - if (! ((*lock)= mysql_lock_tables(thd, &table, 1, - MYSQL_LOCK_IGNORE_FLUSH, ¬_used))) - { - VOID(pthread_mutex_lock(&LOCK_open)); - hash_delete(&open_cache,(byte*) table); - VOID(pthread_mutex_unlock(&LOCK_open)); - quick_rm_table(create_info->db_type, create_table->db, - table_case_name(create_info, create_table->table_name)); - DBUG_RETURN(0); - } - table->file->extra(HA_EXTRA_WRITE_CACHE); - DBUG_RETURN(table); -} - - -/**************************************************************************** ** Alter a table definition ****************************************************************************/ @@ -5098,7 +5000,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ha_resolve_storage_engine_name(old_db_type), ha_resolve_storage_engine_name(new_db_type))); if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) || - ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED)) + ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED) || + (old_db_type != new_db_type && + ha_check_storage_engine_flag(new_db_type, HTON_ALTER_CANNOT_CREATE))) { DBUG_PRINT("info", ("doesn't support alter")); my_error(ER_ILLEGAL_HA, MYF(0), table_name); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index aa42cd901d8..4398e0039e8 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -475,6 +475,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NUMERIC_SYM %token NVARCHAR_SYM %token OFFSET_SYM +%token OJ_SYM %token OLD_PASSWORD %token ON %token ONE_SHOT_SYM @@ -797,7 +798,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); key_type opt_unique_or_fulltext constraint_key_type %type <key_alg> - opt_btree_or_rtree + btree_or_rtree %type <string_list> key_usage_list using_list @@ -888,7 +889,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); view_suid view_tail view_list_opt view_list view_select view_check_option trigger_tail sp_tail install uninstall partition_entry binlog_base64_event - init_key_options key_options key_opts key_opt + init_key_options key_options key_opts key_opt key_using_alg END_OF_INPUT %type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt @@ -4529,7 +4530,7 @@ init_key_options: key_alg: /* empty */ init_key_options - | init_key_options key_opts + | init_key_options key_using_alg ; key_options: @@ -4541,10 +4542,14 @@ key_opts: key_opt | key_opts key_opt ; - + +key_using_alg: + USING btree_or_rtree { Lex->key_create_info.algorithm= $2; } + | TYPE_SYM btree_or_rtree { Lex->key_create_info.algorithm= $2; } + ; + key_opt: - USING opt_btree_or_rtree { Lex->key_create_info.algorithm= $2; } - | TYPE_SYM opt_btree_or_rtree { Lex->key_create_info.algorithm= $2; } + key_using_alg | KEY_BLOCK_SIZE opt_equal ulong_num { Lex->key_create_info.block_size= $3; } | WITH PARSER_SYM IDENT_sys @@ -4560,7 +4565,7 @@ key_opt: ; -opt_btree_or_rtree: +btree_or_rtree: BTREE_SYM { $$= HA_KEY_ALG_BTREE; } | RTREE_SYM { @@ -4574,7 +4579,7 @@ key_list: key_part: ident { $$=new key_part_spec($1.str); } - | ident '(' NUM ')' + | ident '(' NUM ')' { int key_part_len= atoi($3.str); if (!key_part_len) @@ -6940,11 +6945,14 @@ table_factor: } expr '}' { + LEX *lex= Lex; YYERROR_UNLESS($3 && $7); add_join_on($7,$10); Lex->pop_context(); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; + if (!($$= lex->current_select->nest_last_join(lex->thd))) + YYABORT; } | select_derived_init get_select_lex select_derived2 { @@ -7489,7 +7497,11 @@ select_var_ident: if (lex->result) ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($2,0,0,(enum_field_types)0)); else - YYABORT; + /* + The parser won't create select_result instance only + if it's an EXPLAIN. + */ + DBUG_ASSERT(lex->describe); } | ident_or_text { @@ -7501,10 +7513,8 @@ select_var_ident: my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str); YYABORT; } - if (! lex->result) - YYABORT; - else - { + if (lex->result) + { my_var *var; ((select_dumpvar *)lex->result)-> var_list.push_back(var= new my_var($1,1,t->offset,t->type)); @@ -7512,6 +7522,14 @@ select_var_ident: if (var) var->sp= lex->sphead; #endif + } + else + { + /* + The parser won't create select_result instance only + if it's an EXPLAIN. + */ + DBUG_ASSERT(lex->describe); } } ; @@ -9016,12 +9034,18 @@ simple_ident_q: YYABORT; } + DBUG_ASSERT(!new_row || + (lex->trg_chistics.event == TRG_EVENT_INSERT || + lex->trg_chistics.event == TRG_EVENT_UPDATE)); + const bool read_only= + !(new_row && lex->trg_chistics.action_time == TRG_ACTION_BEFORE); if (!(trg_fld= new Item_trigger_field(Lex->current_context(), new_row ? Item_trigger_field::NEW_ROW: Item_trigger_field::OLD_ROW, $3.str, - Item_trigger_field::AT_READ))) + SELECT_ACL, + read_only))) YYABORT; /* @@ -9703,11 +9727,13 @@ sys_option_value: it= new Item_null(); } + DBUG_ASSERT(lex->trg_chistics.action_time == TRG_ACTION_BEFORE && + (lex->trg_chistics.event == TRG_EVENT_INSERT || + lex->trg_chistics.event == TRG_EVENT_UPDATE)); if (!(trg_fld= new Item_trigger_field(Lex->current_context(), Item_trigger_field::NEW_ROW, $2.base_name.str, - Item_trigger_field::AT_UPDATE) - ) || + UPDATE_ACL, FALSE)) || !(sp_fld= new sp_instr_set_trigger_field(lex->sphead-> instructions(), lex->spcont, diff --git a/sql/table.cc b/sql/table.cc index fb9733c9180..d0caba7fe9e 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -656,7 +656,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, my_free(buff, MYF(0)); goto err; } - next_chunk++; } #else if (partition_info_len) @@ -680,7 +679,15 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, */ next_chunk+= 4; } + else if (share->mysql_version >= 50110) #endif + { + /* New auto_partitioned indicator introduced in 5.1.11 */ +#ifdef WITH_PARTITION_STORAGE_ENGINE + share->auto_partitioned= *next_chunk; +#endif + next_chunk++; + } keyinfo= share->key_info; for (i= 0; i < keys; i++, keyinfo++) { @@ -1471,6 +1478,8 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, Fix the partition functions and ensure they are not constant functions */ + outparam->part_info->is_auto_partitioned= share->auto_partitioned; + DBUG_PRINT("info", ("autopartitioned = %u", share->auto_partitioned)); if (fix_partition_func(thd, share->normalized_path.str, outparam, is_create_table)) goto err; diff --git a/sql/table.h b/sql/table.h index 5fd9cd28585..ffecc60b19c 100644 --- a/sql/table.h +++ b/sql/table.h @@ -214,6 +214,7 @@ typedef struct st_table_share */ bool log_table; #ifdef WITH_PARTITION_STORAGE_ENGINE + bool auto_partitioned; const uchar *partition_info; uint partition_info_len; const uchar *part_state; @@ -263,6 +264,7 @@ struct st_table { ha_rows quick_rows[MAX_KEY]; key_part_map const_key_parts[MAX_KEY]; uint quick_key_parts[MAX_KEY]; + uint quick_n_ranges[MAX_KEY]; /* If this table has TIMESTAMP field with auto-set property (pointed by diff --git a/sql/udf_example.cc b/sql/udf_example.cc index f4f936f34ef..6ad066eacc2 100644 --- a/sql/udf_example.cc +++ b/sql/udf_example.cc @@ -344,7 +344,7 @@ char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result, KSflag = 0; /* state flag for KS translation */ for (metaph_end = result + MAXMETAPH, n_start = n; - n <= n_end && result < metaph_end; n++ ) + n < n_end && result < metaph_end; n++ ) { if ( KSflag ) diff --git a/sql/unireg.cc b/sql/unireg.cc index eb38e6c0592..11aa73bb502 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -130,8 +130,14 @@ bool mysql_create_frm(THD *thd, const char *file_name, /* str_db_type */ create_info->extra_size= (2 + str_db_type.length + 2 + create_info->connect_string.length); - /* Partition */ - create_info->extra_size+= 9; + /* + Partition: + Length of partition info = 4 byte + Potential NULL byte at end of partition info string = 1 byte + Indicator if auto-partitioned table = 1 byte + => Total 6 byte + */ + create_info->extra_size+= 6; #ifdef WITH_PARTITION_STORAGE_ENGINE if (part_info) { @@ -203,17 +209,19 @@ bool mysql_create_frm(THD *thd, const char *file_name, #ifdef WITH_PARTITION_STORAGE_ENGINE if (part_info) { + char auto_partitioned= part_info->is_auto_partitioned ? 1 : 0; int4store(buff, part_info->part_info_len); if (my_write(file, (const byte*)buff, 4, MYF_RW) || my_write(file, (const byte*)part_info->part_info_string, - part_info->part_info_len + 1, MYF_RW)) + part_info->part_info_len + 1, MYF_RW) || + my_write(file, (const byte*)&auto_partitioned, 1, MYF_RW)) goto err; } else #endif { - bzero(buff, 9); - if (my_write(file, (byte*) buff, 9, MYF_RW)) + bzero(buff, 6); + if (my_write(file, (byte*) buff, 6, MYF_RW)) goto err; } for (i= 0; i < keys; i++) |