diff options
author | Georgi Kodinov <Georgi.Kodinov@Oracle.com> | 2011-05-10 17:20:26 +0300 |
---|---|---|
committer | Georgi Kodinov <Georgi.Kodinov@Oracle.com> | 2011-05-10 17:20:26 +0300 |
commit | c4c808d6066f8754d5504eeca0449d42ac95f8b5 (patch) | |
tree | dedfe71cc0ebd78c6ac5ec49fb9a366ff76a4bf1 /sql | |
parent | 7bc16d29747738c4436e10d5cb27738466079906 (diff) | |
parent | 5ebd07b0f6e3e6a37b8847a0e05753edc5025f59 (diff) | |
download | mariadb-git-c4c808d6066f8754d5504eeca0449d42ac95f8b5.tar.gz |
weave merge of mysql-5.5->mysql-5.5-security
Diffstat (limited to 'sql')
38 files changed, 863 insertions, 341 deletions
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 3a25324db52..75d1c8ef8e8 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -534,6 +534,13 @@ Event_db_repository::fill_schema_events(THD *thd, TABLE_LIST *i_s_table, if (open_system_tables_for_read(thd, &event_table, &open_tables_backup)) DBUG_RETURN(TRUE); + if (table_intact.check(event_table.table, &event_table_def)) + { + close_system_tables(thd, &open_tables_backup); + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + DBUG_RETURN(TRUE); + } + /* 1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order thus we won't order it. OTOH, SHOW EVENTS will be @@ -591,6 +598,14 @@ Event_db_repository::open_event_table(THD *thd, enum thr_lock_type lock_type, *table= tables.table; tables.table->use_all_columns(); + + if (table_intact.check(*table, &event_table_def)) + { + close_thread_tables(thd); + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); } @@ -1035,6 +1050,13 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname, */ if (!(ret= open_system_tables_for_read(thd, &event_table, &open_tables_backup))) { + if (table_intact.check(event_table.table, &event_table_def)) + { + close_system_tables(thd, &open_tables_backup); + my_error(ER_EVENT_OPEN_TABLE_FAILED, MYF(0)); + DBUG_RETURN(TRUE); + } + if ((ret= find_named_event(dbname, name, event_table.table))) my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str); else if ((ret= etn->load_from_row(thd, event_table.table))) diff --git a/sql/field.cc b/sql/field.cc index 01c4459c06c..7ffc399718c 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5382,6 +5382,7 @@ double Field_year::val_real(void) longlong Field_year::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; + DBUG_ASSERT(field_length == 2 || field_length == 4); int tmp= (int) ptr[0]; if (field_length != 4) tmp%=100; // Return last 2 char @@ -5394,6 +5395,7 @@ longlong Field_year::val_int(void) String *Field_year::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { + DBUG_ASSERT(field_length < 5); val_buffer->alloc(5); val_buffer->length(field_length); char *to=(char*) val_buffer->ptr(); diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 59b904aceac..754d5dc99cf 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -162,8 +162,7 @@ const uint ha_partition::NO_CURRENT_PART_ID= 0xFFFFFFFF; */ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share) - :handler(hton, share), m_part_info(NULL), m_create_handler(FALSE), - m_is_sub_partitioned(0) + :handler(hton, share) { DBUG_ENTER("ha_partition::ha_partition(table)"); init_handler_variables(); @@ -183,15 +182,44 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share) */ ha_partition::ha_partition(handlerton *hton, partition_info *part_info) - :handler(hton, NULL), m_part_info(part_info), m_create_handler(TRUE), - m_is_sub_partitioned(m_part_info->is_sub_partitioned()) + :handler(hton, NULL) { DBUG_ENTER("ha_partition::ha_partition(part_info)"); + DBUG_ASSERT(part_info); init_handler_variables(); - DBUG_ASSERT(m_part_info); + m_part_info= part_info; + m_create_handler= TRUE; + m_is_sub_partitioned= m_part_info->is_sub_partitioned(); DBUG_VOID_RETURN; } +/** + ha_partition constructor method used by ha_partition::clone() + + @param hton Handlerton (partition_hton) + @param share Table share object + @param part_info_arg partition_info to use + @param clone_arg ha_partition to clone + @param clme_mem_root_arg MEM_ROOT to use + + @return New partition handler +*/ + +ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share, + partition_info *part_info_arg, + ha_partition *clone_arg, + MEM_ROOT *clone_mem_root_arg) + :handler(hton, share) +{ + DBUG_ENTER("ha_partition::ha_partition(clone)"); + init_handler_variables(); + m_part_info= part_info_arg; + m_create_handler= TRUE; + m_is_sub_partitioned= m_part_info->is_sub_partitioned(); + m_is_clone_of= clone_arg; + m_clone_mem_root= clone_mem_root_arg; + DBUG_VOID_RETURN; +} /* Initialize handler object @@ -243,7 +271,6 @@ void ha_partition::init_handler_variables() m_rec0= 0; m_curr_key_info[0]= NULL; m_curr_key_info[1]= NULL; - is_clone= FALSE, m_part_func_monotonicity_info= NON_MONOTONIC; auto_increment_lock= FALSE; auto_increment_safe_stmt_log_lock= FALSE; @@ -251,6 +278,11 @@ void ha_partition::init_handler_variables() this allows blackhole to work properly */ m_num_locks= 0; + m_part_info= NULL; + m_create_handler= FALSE; + m_is_sub_partitioned= 0; + m_is_clone_of= NULL; + m_clone_mem_root= NULL; #ifdef DONT_HAVE_TO_BE_INITALIZED m_start_key.flag= 0; @@ -358,7 +390,8 @@ bool ha_partition::initialize_partition(MEM_ROOT *mem_root) */ DBUG_RETURN(0); } - else if (get_from_handler_file(table_share->normalized_path.str, mem_root)) + else if (get_from_handler_file(table_share->normalized_path.str, + mem_root, false)) { my_error(ER_FAILED_READ_FROM_PAR_FILE, MYF(0)); DBUG_RETURN(1); @@ -1890,7 +1923,7 @@ uint ha_partition::del_ren_cre_table(const char *from, DBUG_RETURN(TRUE); } - if (get_from_handler_file(from, ha_thd()->mem_root)) + if (get_from_handler_file(from, ha_thd()->mem_root, false)) DBUG_RETURN(TRUE); DBUG_ASSERT(m_file_buffer); DBUG_PRINT("enter", ("from: (%s) to: (%s)", from, to ? to : "(nil)")); @@ -2105,18 +2138,16 @@ static uint name_add(char *dest, const char *first_name, const char *sec_name) } -/* +/** Create the special .par file - SYNOPSIS - create_handler_file() - name Full path of table name + @param name Full path of table name - RETURN VALUE - >0 Error code - 0 Success + @return Operation status + @retval FALSE Error code + @retval TRUE Success - DESCRIPTION + @note Method used to create handler file with names of partitions, their engine types and the number of partitions. */ @@ -2180,19 +2211,22 @@ bool ha_partition::create_handler_file(const char *name) Array of engine types n * 4 bytes where n = (m_tot_parts + 3)/4 Length of name part in bytes 4 bytes + (Names in filename format) Name part m * 4 bytes where m = ((length_name_part + 3)/4)*4 All padding bytes are zeroed */ - tot_partition_words= (tot_parts + 3) / 4; - tot_name_words= (tot_name_len + 3) / 4; + tot_partition_words= (tot_parts + PAR_WORD_SIZE - 1) / PAR_WORD_SIZE; + tot_name_words= (tot_name_len + PAR_WORD_SIZE - 1) / PAR_WORD_SIZE; + /* 4 static words (tot words, checksum, tot partitions, name length) */ tot_len_words= 4 + tot_partition_words + tot_name_words; - tot_len_byte= 4 * tot_len_words; + tot_len_byte= PAR_WORD_SIZE * tot_len_words; if (!(file_buffer= (uchar *) my_malloc(tot_len_byte, MYF(MY_ZEROFILL)))) DBUG_RETURN(TRUE); - engine_array= (file_buffer + 12); - name_buffer_ptr= (char*) (file_buffer + ((4 + tot_partition_words) * 4)); + engine_array= (file_buffer + PAR_ENGINES_OFFSET); + name_buffer_ptr= (char*) (engine_array + tot_partition_words * PAR_WORD_SIZE + + PAR_WORD_SIZE); part_it.rewind(); for (i= 0; i < num_parts; i++) { @@ -2230,13 +2264,15 @@ bool ha_partition::create_handler_file(const char *name) } chksum= 0; int4store(file_buffer, tot_len_words); - int4store(file_buffer + 8, tot_parts); - int4store(file_buffer + 12 + (tot_partition_words * 4), tot_name_len); + int4store(file_buffer + PAR_NUM_PARTS_OFFSET, tot_parts); + int4store(file_buffer + PAR_ENGINES_OFFSET + + (tot_partition_words * PAR_WORD_SIZE), + tot_name_len); for (i= 0; i < tot_len_words; i++) - chksum^= uint4korr(file_buffer + 4 * i); - int4store(file_buffer + 4, chksum); + chksum^= uint4korr(file_buffer + PAR_WORD_SIZE * i); + int4store(file_buffer + PAR_CHECKSUM_OFFSET, chksum); /* - Remove .frm extension and replace with .par + Add .par extension to the file name. Create and write and close file to be used at open, delete_table and rename_table */ @@ -2255,14 +2291,9 @@ bool ha_partition::create_handler_file(const char *name) DBUG_RETURN(result); } -/* - Clear handler variables and free some memory - SYNOPSIS - clear_handler_file() - - RETURN VALUE - NONE +/** + Clear handler variables and free some memory */ void ha_partition::clear_handler_file() @@ -2275,16 +2306,15 @@ void ha_partition::clear_handler_file() m_engine_array= NULL; } -/* + +/** Create underlying handler objects - SYNOPSIS - create_handlers() - mem_root Allocate memory through this + @param mem_root Allocate memory through this - RETURN VALUE - TRUE Error - FALSE Success + @return Operation status + @retval TRUE Error + @retval FALSE Success */ bool ha_partition::create_handlers(MEM_ROOT *mem_root) @@ -2322,6 +2352,7 @@ bool ha_partition::create_handlers(MEM_ROOT *mem_root) DBUG_RETURN(FALSE); } + /* Create underlying handler objects from partition info @@ -2393,101 +2424,165 @@ error_end: } -/* - Get info about partition engines and their names from the .par file +/** + Read the .par file to get the partitions engines and names - SYNOPSIS - get_from_handler_file() - name Full path of table name - mem_root Allocate memory through this + @param name Name of table file (without extention) - RETURN VALUE - TRUE Error - FALSE Success + @return Operation status + @retval true Failure + @retval false Success - DESCRIPTION - Open handler file to get partition names, engine types and number of - partitions. + @note On success, m_file_buffer is allocated and must be + freed by the caller. m_name_buffer_ptr and m_tot_parts is also set. */ -bool ha_partition::get_from_handler_file(const char *name, MEM_ROOT *mem_root) +bool ha_partition::read_par_file(const char *name) { - char buff[FN_REFLEN], *address_tot_name_len; + char buff[FN_REFLEN], *tot_name_len_offset; File file; - char *file_buffer, *name_buffer_ptr; - handlerton **engine_array; + char *file_buffer; uint i, len_bytes, len_words, tot_partition_words, tot_name_words, chksum; - DBUG_ENTER("ha_partition::get_from_handler_file"); + DBUG_ENTER("ha_partition::read_par_file"); DBUG_PRINT("enter", ("table name: '%s'", name)); if (m_file_buffer) - DBUG_RETURN(FALSE); + DBUG_RETURN(false); fn_format(buff, name, "", ha_par_ext, MY_APPEND_EXT); /* Following could be done with mysql_file_stat to read in whole file */ if ((file= mysql_file_open(key_file_partition, buff, O_RDONLY | O_SHARE, MYF(0))) < 0) DBUG_RETURN(TRUE); - if (mysql_file_read(file, (uchar *) &buff[0], 8, MYF(MY_NABP))) + if (mysql_file_read(file, (uchar *) &buff[0], PAR_WORD_SIZE, MYF(MY_NABP))) goto err1; len_words= uint4korr(buff); - len_bytes= 4 * len_words; + len_bytes= PAR_WORD_SIZE * len_words; + if (mysql_file_seek(file, 0, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR) + goto err1; if (!(file_buffer= (char*) my_malloc(len_bytes, MYF(0)))) goto err1; - mysql_file_seek(file, 0, MY_SEEK_SET, MYF(0)); if (mysql_file_read(file, (uchar *) file_buffer, len_bytes, MYF(MY_NABP))) goto err2; chksum= 0; for (i= 0; i < len_words; i++) - chksum ^= uint4korr((file_buffer) + 4 * i); + chksum ^= uint4korr((file_buffer) + PAR_WORD_SIZE * i); if (chksum) goto err2; - m_tot_parts= uint4korr((file_buffer) + 8); + m_tot_parts= uint4korr((file_buffer) + PAR_NUM_PARTS_OFFSET); DBUG_PRINT("info", ("No of parts = %u", m_tot_parts)); - tot_partition_words= (m_tot_parts + 3) / 4; + tot_partition_words= (m_tot_parts + PAR_WORD_SIZE - 1) / PAR_WORD_SIZE; + + tot_name_len_offset= file_buffer + PAR_ENGINES_OFFSET + + PAR_WORD_SIZE * tot_partition_words; + tot_name_words= (uint4korr(tot_name_len_offset) + PAR_WORD_SIZE - 1) / + PAR_WORD_SIZE; + /* + Verify the total length = tot size word, checksum word, num parts word + + engines array + name length word + name array. + */ + if (len_words != (tot_partition_words + tot_name_words + 4)) + goto err2; + (void) mysql_file_close(file, MYF(0)); + m_file_buffer= file_buffer; // Will be freed in clear_handler_file() + m_name_buffer_ptr= tot_name_len_offset + PAR_WORD_SIZE; + + DBUG_RETURN(false); + +err2: + my_free(file_buffer); +err1: + (void) mysql_file_close(file, MYF(0)); + DBUG_RETURN(true); +} + + +/** + Setup m_engine_array + + @param mem_root MEM_ROOT to use for allocating new handlers + + @return Operation status + @retval false Success + @retval true Failure +*/ + +bool ha_partition::setup_engine_array(MEM_ROOT *mem_root) +{ + uint i; + uchar *buff; + handlerton **engine_array; + + DBUG_ASSERT(!m_file); + DBUG_ENTER("ha_partition::setup_engine_array"); engine_array= (handlerton **) my_alloca(m_tot_parts * sizeof(handlerton*)); + if (!engine_array) + DBUG_RETURN(true); + + buff= (uchar *) (m_file_buffer + PAR_ENGINES_OFFSET); for (i= 0; i < m_tot_parts; i++) { engine_array[i]= ha_resolve_by_legacy_type(ha_thd(), (enum legacy_db_type) - *(uchar *) ((file_buffer) + - 12 + i)); + *(buff + i)); if (!engine_array[i]) - goto err3; + goto err; } - address_tot_name_len= file_buffer + 12 + 4 * tot_partition_words; - tot_name_words= (uint4korr(address_tot_name_len) + 3) / 4; - if (len_words != (tot_partition_words + tot_name_words + 4)) - goto err3; - name_buffer_ptr= file_buffer + 16 + 4 * tot_partition_words; - (void) mysql_file_close(file, MYF(0)); - m_file_buffer= file_buffer; // Will be freed in clear_handler_file() - m_name_buffer_ptr= name_buffer_ptr; - if (!(m_engine_array= (plugin_ref*) my_malloc(m_tot_parts * sizeof(plugin_ref), MYF(MY_WME)))) - goto err3; + goto err; for (i= 0; i < m_tot_parts; i++) m_engine_array[i]= ha_lock_engine(NULL, engine_array[i]); my_afree((gptr) engine_array); - if (!m_file && create_handlers(mem_root)) + if (create_handlers(mem_root)) { clear_handler_file(); - DBUG_RETURN(TRUE); + DBUG_RETURN(true); } - DBUG_RETURN(FALSE); -err3: + DBUG_RETURN(false); + +err: my_afree((gptr) engine_array); -err2: - my_free(file_buffer); -err1: - (void) mysql_file_close(file, MYF(0)); - DBUG_RETURN(TRUE); + DBUG_RETURN(true); +} + + +/** + Get info about partition engines and their names from the .par file + + @param name Full path of table name + @param mem_root Allocate memory through this + @param is_clone If it is a clone, don't create new handlers + + @return Operation status + @retval true Error + @retval false Success + + @note Open handler file to get partition names, engine types and number of + partitions. +*/ + +bool ha_partition::get_from_handler_file(const char *name, MEM_ROOT *mem_root, + bool is_clone) +{ + DBUG_ENTER("ha_partition::get_from_handler_file"); + DBUG_PRINT("enter", ("table name: '%s'", name)); + + if (m_file_buffer) + DBUG_RETURN(false); + + if (read_par_file(name)) + DBUG_RETURN(true); + + if (!is_clone && setup_engine_array(mem_root)) + DBUG_RETURN(true); + + DBUG_RETURN(false); } @@ -2533,13 +2628,13 @@ void ha_data_partition_destroy(HA_DATA_PARTITION* ha_part_data) int ha_partition::open(const char *name, int mode, uint test_if_locked) { - char *name_buffer_ptr= m_name_buffer_ptr; - int error; + char *name_buffer_ptr; + int error= HA_ERR_INITIALIZATION; uint alloc_len; handler **file; char name_buff[FN_REFLEN]; bool is_not_tmp_table= (table_share->tmp_table == NO_TMP_TABLE); - ulonglong check_table_flags= 0; + ulonglong check_table_flags; DBUG_ENTER("ha_partition::open"); DBUG_ASSERT(table->s == table_share); @@ -2547,8 +2642,9 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) m_mode= mode; m_open_test_lock= test_if_locked; m_part_field_array= m_part_info->full_part_field_array; - if (get_from_handler_file(name, &table->mem_root)) - DBUG_RETURN(1); + if (get_from_handler_file(name, &table->mem_root, test(m_is_clone_of))) + DBUG_RETURN(error); + name_buffer_ptr= m_name_buffer_ptr; m_start_key.length= 0; m_rec0= table->record[0]; m_rec_length= table_share->reclength; @@ -2558,7 +2654,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) { if (!(m_ordered_rec_buffer= (uchar*)my_malloc(alloc_len, MYF(MY_WME)))) { - DBUG_RETURN(1); + DBUG_RETURN(error); } { /* @@ -2581,48 +2677,84 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) /* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */ if (bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE)) - DBUG_RETURN(1); + DBUG_RETURN(error); bitmap_clear_all(&m_bulk_insert_started); /* Initialize the bitmap we use to determine what partitions are used */ - if (!is_clone) + if (!m_is_clone_of) { + DBUG_ASSERT(!m_clone_mem_root); if (bitmap_init(&(m_part_info->used_partitions), NULL, m_tot_parts, TRUE)) { bitmap_free(&m_bulk_insert_started); - DBUG_RETURN(1); + DBUG_RETURN(error); } bitmap_set_all(&(m_part_info->used_partitions)); } + if (m_is_clone_of) + { + uint i; + DBUG_ASSERT(m_clone_mem_root); + /* Allocate an array of handler pointers for the partitions handlers. */ + alloc_len= (m_tot_parts + 1) * sizeof(handler*); + if (!(m_file= (handler **) alloc_root(m_clone_mem_root, alloc_len))) + goto err_alloc; + memset(m_file, 0, alloc_len); + /* + Populate them by cloning the original partitions. This also opens them. + Note that file->ref is allocated too. + */ + file= m_is_clone_of->m_file; + for (i= 0; i < m_tot_parts; i++) + { + create_partition_name(name_buff, name, name_buffer_ptr, NORMAL_PART_NAME, + FALSE); + if (!(m_file[i]= file[i]->clone(name_buff, m_clone_mem_root))) + { + error= HA_ERR_INITIALIZATION; + file= &m_file[i]; + goto err_handler; + } + name_buffer_ptr+= strlen(name_buffer_ptr) + 1; + } + } + else + { + file= m_file; + do + { + create_partition_name(name_buff, name, name_buffer_ptr, NORMAL_PART_NAME, + FALSE); + if ((error= (*file)->ha_open(table, name_buff, mode, test_if_locked))) + goto err_handler; + m_num_locks+= (*file)->lock_count(); + name_buffer_ptr+= strlen(name_buffer_ptr) + 1; + } while (*(++file)); + } + file= m_file; - do + ref_length= (*file)->ref_length; + check_table_flags= (((*file)->ha_table_flags() & + ~(PARTITION_DISABLED_TABLE_FLAGS)) | + (PARTITION_ENABLED_TABLE_FLAGS)); + while (*(++file)) { - create_partition_name(name_buff, name, name_buffer_ptr, NORMAL_PART_NAME, - FALSE); - if ((error= (*file)->ha_open(table, (const char*) name_buff, mode, - test_if_locked))) - goto err_handler; - m_num_locks+= (*file)->lock_count(); - name_buffer_ptr+= strlen(name_buffer_ptr) + 1; + /* MyISAM can have smaller ref_length for partitions with MAX_ROWS set */ set_if_bigger(ref_length, ((*file)->ref_length)); /* Verify that all partitions have the same set of table flags. Mask all flags that partitioning enables/disables. */ - if (!check_table_flags) - { - check_table_flags= (((*file)->ha_table_flags() & - ~(PARTITION_DISABLED_TABLE_FLAGS)) | - (PARTITION_ENABLED_TABLE_FLAGS)); - } - else if (check_table_flags != (((*file)->ha_table_flags() & - ~(PARTITION_DISABLED_TABLE_FLAGS)) | - (PARTITION_ENABLED_TABLE_FLAGS))) + if (check_table_flags != (((*file)->ha_table_flags() & + ~(PARTITION_DISABLED_TABLE_FLAGS)) | + (PARTITION_ENABLED_TABLE_FLAGS))) { error= HA_ERR_INITIALIZATION; + /* set file to last handler, so all of them is closed */ + file = &m_file[m_tot_parts - 1]; goto err_handler; } - } while (*(++file)); + } key_used_on_scan= m_file[0]->key_used_on_scan; implicit_emptied= m_file[0]->implicit_emptied; /* @@ -2631,6 +2763,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) */ ref_length+= PARTITION_BYTES_IN_POS; m_ref_length= ref_length; + /* Release buffer read from .par file. It will not be reused again after being opened once. @@ -2690,25 +2823,54 @@ err_handler: DEBUG_SYNC(ha_thd(), "partition_open_error"); while (file-- != m_file) (*file)->close(); +err_alloc: bitmap_free(&m_bulk_insert_started); - if (!is_clone) + if (!m_is_clone_of) bitmap_free(&(m_part_info->used_partitions)); DBUG_RETURN(error); } -handler *ha_partition::clone(MEM_ROOT *mem_root) + +/** + Clone the open and locked partitioning handler. + + @param mem_root MEM_ROOT to use. + + @return Pointer to the successfully created clone or NULL + + @details + This function creates a new ha_partition handler as a clone/copy. The + original (this) must already be opened and locked. The clone will use + the originals m_part_info. + It also allocates memory for ref + ref_dup. + In ha_partition::open() it will clone its original handlers partitions + which will allocate then on the correct MEM_ROOT and also open them. +*/ + +handler *ha_partition::clone(const char *name, MEM_ROOT *mem_root) { - handler *new_handler= get_new_handler(table->s, mem_root, - table->s->db_type()); - ((ha_partition*)new_handler)->m_part_info= m_part_info; - ((ha_partition*)new_handler)->is_clone= TRUE; - if (new_handler && !new_handler->ha_open(table, - table->s->normalized_path.str, - table->db_stat, - HA_OPEN_IGNORE_IF_LOCKED)) - return new_handler; - return NULL; + ha_partition *new_handler; + + DBUG_ENTER("ha_partition::clone"); + new_handler= new (mem_root) ha_partition(ht, table_share, m_part_info, + this, mem_root); + /* + Allocate new_handler->ref here because otherwise ha_open will allocate it + on this->table->mem_root and we will not be able to reclaim that memory + when the clone handler object is destroyed. + */ + if (new_handler && + !(new_handler->ref= (uchar*) alloc_root(mem_root, + ALIGN_SIZE(m_ref_length)*2))) + new_handler= NULL; + + if (new_handler && + new_handler->ha_open(table, name, + table->db_stat, HA_OPEN_IGNORE_IF_LOCKED)) + new_handler= NULL; + + DBUG_RETURN((handler*) new_handler); } @@ -2739,7 +2901,7 @@ int ha_partition::close(void) DBUG_ASSERT(table->s == table_share); delete_queue(&m_queue); bitmap_free(&m_bulk_insert_started); - if (!is_clone) + if (!m_is_clone_of) bitmap_free(&(m_part_info->used_partitions)); file= m_file; @@ -3927,19 +4089,16 @@ end_dont_reset_start_part: void ha_partition::position(const uchar *record) { handler *file= m_file[m_last_part]; + uint pad_length; DBUG_ENTER("ha_partition::position"); file->position(record); int2store(ref, m_last_part); - memcpy((ref + PARTITION_BYTES_IN_POS), file->ref, - (ref_length - PARTITION_BYTES_IN_POS)); - -#ifdef SUPPORTING_PARTITION_OVER_DIFFERENT_ENGINES -#ifdef HAVE_purify - bzero(ref + PARTITION_BYTES_IN_POS + ref_length, - max_ref_length-ref_length); -#endif /* HAVE_purify */ -#endif + memcpy((ref + PARTITION_BYTES_IN_POS), file->ref, file->ref_length); + pad_length= m_ref_length - PARTITION_BYTES_IN_POS - file->ref_length; + if (pad_length) + memset((ref + PARTITION_BYTES_IN_POS + file->ref_length), 0, pad_length); + DBUG_VOID_RETURN; } diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 57456ddf8ae..b0b11da823f 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -37,6 +37,16 @@ enum partition_keywords HA_DUPLICATE_POS | \ HA_CAN_SQL_HANDLER | \ HA_CAN_INSERT_DELAYED) + +/* First 4 bytes in the .par file is the number of 32-bit words in the file */ +#define PAR_WORD_SIZE 4 +/* offset to the .par file checksum */ +#define PAR_CHECKSUM_OFFSET 4 +/* offset to the total number of partitions */ +#define PAR_NUM_PARTS_OFFSET 8 +/* offset to the engines array */ +#define PAR_ENGINES_OFFSET 12 + class ha_partition :public handler { private: @@ -53,7 +63,7 @@ private: /* Data for the partition handler */ int m_mode; // Open mode uint m_open_test_lock; // Open test_if_locked - char *m_file_buffer; // Buffer with names + char *m_file_buffer; // Content of the .par file char *m_name_buffer_ptr; // Pointer to first partition name plugin_ref *m_engine_array; // Array of types of the handlers handler **m_file; // Array of references to handler inst. @@ -115,6 +125,13 @@ private: bool m_is_sub_partitioned; // Is subpartitioned bool m_ordered_scan_ongoing; + /* + If set, this object was created with ha_partition::clone and doesn't + "own" the m_part_info structure. + */ + ha_partition *m_is_clone_of; + MEM_ROOT *m_clone_mem_root; + /* We keep track if all underlying handlers are MyISAM since MyISAM has a great number of extra flags not needed by other handlers. @@ -148,11 +165,6 @@ private: */ THR_LOCK_DATA lock; /* MySQL lock */ - /* - TRUE <=> this object was created with ha_partition::clone and doesn't - "own" the m_part_info structure. - */ - bool is_clone; bool auto_increment_lock; /**< lock reading/updating auto_inc */ /** Flag to keep the auto_increment lock through out the statement. @@ -165,7 +177,7 @@ private: /** used for prediction of start_bulk_insert rows */ enum_monotonicity_info m_part_func_monotonicity_info; public: - handler *clone(MEM_ROOT *mem_root); + handler *clone(const char *name, MEM_ROOT *mem_root); virtual void set_part_info(partition_info *part_info) { m_part_info= part_info; @@ -184,6 +196,10 @@ public: */ ha_partition(handlerton *hton, TABLE_SHARE * table); ha_partition(handlerton *hton, partition_info * part_info); + ha_partition(handlerton *hton, TABLE_SHARE *share,
+ partition_info *part_info_arg,
+ ha_partition *clone_arg,
+ MEM_ROOT *clone_mem_root_arg); ~ha_partition(); /* A partition handler has no characteristics in itself. It only inherits @@ -254,7 +270,10 @@ private: And one method to read it in. */ bool create_handler_file(const char *name); - bool get_from_handler_file(const char *name, MEM_ROOT *mem_root); + bool setup_engine_array(MEM_ROOT *mem_root); + bool read_par_file(const char *name); + bool get_from_handler_file(const char *name, MEM_ROOT *mem_root, + bool is_clone); bool new_handlers_from_part_info(MEM_ROOT *mem_root); bool create_handlers(MEM_ROOT *mem_root); void clear_handler_file(); diff --git a/sql/handler.cc b/sql/handler.cc index fefc0553c88..445e1ec5a3c 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2076,22 +2076,29 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, /**************************************************************************** ** General handler functions ****************************************************************************/ -handler *handler::clone(MEM_ROOT *mem_root) +handler *handler::clone(const char *name, MEM_ROOT *mem_root) { - handler *new_handler= get_new_handler(table->s, mem_root, table->s->db_type()); + handler *new_handler= get_new_handler(table->s, mem_root, ht); /* Allocate handler->ref here because otherwise ha_open will allocate it on this->table->mem_root and we will not be able to reclaim that memory when the clone handler object is destroyed. */ - if (!(new_handler->ref= (uchar*) alloc_root(mem_root, ALIGN_SIZE(ref_length)*2))) - return NULL; - if (new_handler && !new_handler->ha_open(table, - table->s->normalized_path.str, - table->db_stat, - HA_OPEN_IGNORE_IF_LOCKED)) - return new_handler; - return NULL; + if (new_handler && + !(new_handler->ref= (uchar*) alloc_root(mem_root, + ALIGN_SIZE(ref_length)*2))) + new_handler= NULL; + /* + TODO: Implement a more efficient way to have more than one index open for + the same table instance. The ha_open call is not cachable for clone. + */ + if (new_handler && new_handler->ha_open(table, + name, + table->db_stat, + HA_OPEN_IGNORE_IF_LOCKED)) + new_handler= NULL; + + return new_handler; } diff --git a/sql/handler.h b/sql/handler.h index 911f7516e98..fbc52170297 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -3,18 +3,20 @@ /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA */ /* Definitions for parameters to do with handler-routines */ @@ -60,7 +62,7 @@ a table with rnd_next() - We will see all rows (including deleted ones) - Row positions are 'table->s->db_record_offset' apart - If this flag is not set, filesort will do a postion() call for each matched + If this flag is not set, filesort will do a position() call for each matched row to be able to find the row later. */ #define HA_REC_NOT_IN_SEQ (1 << 3) @@ -1262,7 +1264,7 @@ public: DBUG_ASSERT(locked == FALSE); DBUG_ASSERT(inited == NONE); } - virtual handler *clone(MEM_ROOT *mem_root); + virtual handler *clone(const char *name, MEM_ROOT *mem_root); /** This is called after create to allow us to set up cached variables */ void init() { diff --git a/sql/item.cc b/sql/item.cc index 639a3a81b78..0aac941740f 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -581,7 +581,7 @@ void Item::rename(char *new_name) Item* Item::transform(Item_transformer transformer, uchar *arg) { - DBUG_ASSERT(!current_thd->is_stmt_prepare()); + DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare()); return (this->*transformer)(arg); } @@ -1781,14 +1781,17 @@ bool agg_item_set_converter(DTCollation &coll, const char *fname, } THD *thd= current_thd; - Query_arena *arena, backup; bool res= FALSE; uint i; + /* In case we're in statement prepare, create conversion item in its memory: it will be reused on each execute. */ - arena= thd->activate_stmt_arena_if_needed(&backup); + Query_arena backup; + Query_arena *arena= thd->stmt_arena->is_stmt_prepare() ? + thd->activate_stmt_arena_if_needed(&backup) : + NULL; for (i= 0, arg= args; i < nargs; i++, arg+= item_sep) { @@ -1845,7 +1848,7 @@ bool agg_item_set_converter(DTCollation &coll, const char *fname, been created in prepare. In this case register the change for rollback. */ - if (thd->is_stmt_prepare()) + if (thd->stmt_arena->is_stmt_prepare()) *arg= conv; else thd->change_item_tree(arg, conv); @@ -6965,7 +6968,7 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) Item *Item_default_value::transform(Item_transformer transformer, uchar *args) { - DBUG_ASSERT(!current_thd->is_stmt_prepare()); + DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare()); /* If the value of arg is NULL, then this object represents a constant, @@ -7131,8 +7134,26 @@ 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)); + if (!item) + return true; + + if (!fixed) + { + if (fix_fields(thd, NULL)) + return true; + } + + // NOTE: field->table->copy_blobs should be false here, but let's + // remember the value at runtime to avoid subtle bugs. + bool copy_blobs_saved= field->table->copy_blobs; + + field->table->copy_blobs= true; + + int err_code= item->save_in_field(field, 0); + + field->table->copy_blobs= copy_blobs_saved; + + return err_code < 0; } diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 68c63285693..e0057d1550b 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -4069,13 +4069,11 @@ void Item_func_in::fix_length_and_dec() uint j=0; for (uint i=1 ; i < arg_count ; i++) { - if (!args[i]->null_value) // Skip NULL values - { - array->set(j,args[i]); - j++; - } - else - have_null= 1; + array->set(j,args[i]); + if (!args[i]->null_value) // Skip NULL values + j++; + else + have_null= 1; } if ((array->used_count= j)) array->sort(); @@ -4347,7 +4345,7 @@ bool Item_cond::walk(Item_processor processor, bool walk_subquery, uchar *arg) Item *Item_cond::transform(Item_transformer transformer, uchar *arg) { - DBUG_ASSERT(!current_thd->is_stmt_prepare()); + DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare()); List_iterator<Item> li(list); Item *item; @@ -5720,7 +5718,7 @@ bool Item_equal::walk(Item_processor processor, bool walk_subquery, uchar *arg) Item *Item_equal::transform(Item_transformer transformer, uchar *arg) { - DBUG_ASSERT(!current_thd->is_stmt_prepare()); + DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare()); List_iterator<Item_field> it(fields); Item *item; diff --git a/sql/item_func.cc b/sql/item_func.cc index 82310365d27..24d0d94c6c5 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -293,7 +293,7 @@ void Item_func::traverse_cond(Cond_traverser traverser, Item *Item_func::transform(Item_transformer transformer, uchar *argument) { - DBUG_ASSERT(!current_thd->is_stmt_prepare()); + DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare()); if (arg_count) { @@ -2407,10 +2407,7 @@ my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value) if (!(null_value= (args[0]->null_value || args[1]->null_value || my_decimal_round(E_DEC_FATAL_ERROR, value, (int) dec, truncate, decimal_value) > 1))) - { - decimal_value->frac= decimals; return decimal_value; - } return 0; } diff --git a/sql/item_row.cc b/sql/item_row.cc index 94515640625..0f5d6f27823 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -170,7 +170,7 @@ bool Item_row::walk(Item_processor processor, bool walk_subquery, uchar *arg) Item *Item_row::transform(Item_transformer transformer, uchar *arg) { - DBUG_ASSERT(!current_thd->is_stmt_prepare()); + DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare()); for (uint i= 0; i < arg_count; i++) { diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index e5c47c110f4..e1a4fcc8def 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2536,7 +2536,7 @@ String *Item_func_make_set::val_str(String *str) Item *Item_func_make_set::transform(Item_transformer transformer, uchar *arg) { - DBUG_ASSERT(!current_thd->is_stmt_prepare()); + DBUG_ASSERT(!current_thd->stmt_arena->is_stmt_prepare()); Item *new_item= item->transform(transformer, arg); if (!new_item) diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 42088616f4a..b0ab436ea4a 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1145,17 +1145,13 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) switch (hybrid_type= item->result_type()) { case INT_RESULT: - max_length= 20; - break; case DECIMAL_RESULT: + case STRING_RESULT: max_length= item->max_length; break; case REAL_RESULT: max_length= float_length(decimals); break; - case STRING_RESULT: - max_length= item->max_length; - break; case ROW_RESULT: default: DBUG_ASSERT(0); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 36b85f2411f..5f30787f015 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -669,7 +669,7 @@ bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time, system_charset_info); break; case 'W': - if (type == MYSQL_TIMESTAMP_TIME) + if (type == MYSQL_TIMESTAMP_TIME || !(l_time->month || l_time->year)) return 1; weekday= calc_weekday(calc_daynr(l_time->year,l_time->month, l_time->day),0); @@ -678,7 +678,7 @@ bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time, system_charset_info); break; case 'a': - if (type == MYSQL_TIMESTAMP_TIME) + if (type == MYSQL_TIMESTAMP_TIME || !(l_time->month || l_time->year)) return 1; weekday=calc_weekday(calc_daynr(l_time->year,l_time->month, l_time->day),0); @@ -837,7 +837,7 @@ bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time, } break; case 'w': - if (type == MYSQL_TIMESTAMP_TIME) + if (type == MYSQL_TIMESTAMP_TIME || !(l_time->month || l_time->year)) return 1; weekday=calc_weekday(calc_daynr(l_time->year,l_time->month, l_time->day),1); diff --git a/sql/mdl.h b/sql/mdl.h index 4a9397fe215..e0934cb732b 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -286,21 +286,6 @@ private: }; - -/** - Hook class which via its methods specifies which members - of T should be used for participating in MDL lists. -*/ - -template <typename T, T* T::*next, T** T::*prev> -struct I_P_List_adapter -{ - static inline T **next_ptr(T *el) { return &(el->*next); } - - static inline T ***prev_ptr(T *el) { return &(el->*prev); } -}; - - /** A pending metadata lock request. diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a31896bd022..b349c5f91b1 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -245,11 +245,7 @@ inline void setup_fpu() #define MYSQL_KILL_SIGNAL SIGTERM -#ifdef HAVE_GLIBC2_STYLE_GETHOSTBYNAME_R -#include <sys/types.h> -#else #include <my_pthread.h> // For thr_setconcurency() -#endif #ifdef SOLARIS extern "C" int gethostname(char *name, int namelen); diff --git a/sql/mysqld.h b/sql/mysqld.h index 13ca52596ce..303ee5bec0f 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -73,7 +73,7 @@ void flush_thread_cache(); void refresh_status(THD *thd); bool is_secure_file_path(char *path); -extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *system_charset_info; +extern "C" MYSQL_PLUGIN_IMPORT CHARSET_INFO *system_charset_info; extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *files_charset_info ; extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *national_charset_info; extern MYSQL_PLUGIN_IMPORT CHARSET_INFO *table_alias_charset; @@ -182,7 +182,8 @@ extern ulong opt_binlog_rows_event_max_size; extern ulong rpl_recovery_rank, thread_cache_size; extern ulong back_log; extern char language[FN_REFLEN]; -extern ulong server_id, concurrency; +extern "C" MYSQL_PLUGIN_IMPORT ulong server_id; +extern ulong concurrency; extern time_t server_start_time, flush_status_time; extern char *opt_mysql_tmpdir, mysql_charsets_dir[]; extern int mysql_unpacked_real_data_home_len; @@ -203,8 +204,8 @@ extern handlerton *heap_hton; extern const char *load_default_groups[]; extern struct my_option my_long_options[]; extern int mysqld_server_started; -extern int orig_argc; -extern char **orig_argv; +extern "C" MYSQL_PLUGIN_IMPORT int orig_argc; +extern "C" MYSQL_PLUGIN_IMPORT char **orig_argv; extern pthread_attr_t connection_attrib; extern MYSQL_FILE *bootstrap_file; extern my_bool old_mode; @@ -310,7 +311,7 @@ extern uint mysql_real_data_home_len; extern const char *mysql_real_data_home_ptr; extern ulong thread_handling; extern MYSQL_PLUGIN_IMPORT char *mysql_data_home; -extern char server_version[SERVER_VERSION_LENGTH]; +extern "C" MYSQL_PLUGIN_IMPORT char server_version[SERVER_VERSION_LENGTH]; extern MYSQL_PLUGIN_IMPORT char mysql_real_data_home[]; extern char mysql_unpacked_real_data_home[]; extern MYSQL_PLUGIN_IMPORT struct system_variables global_system_variables; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 4cdabab31ae..6e106010968 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1368,7 +1368,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) } thd= head->in_use; - if (!(file= head->file->clone(thd->mem_root))) + if (!(file= head->file->clone(head->s->normalized_path.str, thd->mem_root))) { /* Manually set the error flag. Note: there seems to be quite a few diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index a2f8b9c4447..6b8cdfaca60 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -212,6 +212,7 @@ static int get_index_max_value(TABLE *table, TABLE_REF *ref, uint range_fl) /** Substitutes constants for some COUNT(), MIN() and MAX() functions. + @param thd thread handler @param tables list of leaves of join table tree @param all_fields All fields to be returned @param conds WHERE clause @@ -229,9 +230,12 @@ static int get_index_max_value(TABLE *table, TABLE_REF *ref, uint range_fl) HA_ERR_KEY_NOT_FOUND on impossible conditions @retval HA_ERR_... if a deadlock or a lock wait timeout happens, for example + @retval + ER_... e.g. ER_SUBQUERY_NO_1_ROW */ -int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) +int opt_sum_query(THD *thd, + TABLE_LIST *tables, List<Item> &all_fields, COND *conds) { List_iterator_fast<Item> it(all_fields); int const_result= 1; @@ -243,6 +247,8 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) Item *item; int error; + DBUG_ENTER("opt_sum_query"); + if (conds) where_tables= conds->used_tables(); @@ -270,7 +276,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) WHERE t2.field IS NULL; */ if (tl->table->map & where_tables) - return 0; + DBUG_RETURN(0); } else used_tables|= tl->table->map; @@ -297,7 +303,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) if(error) { tl->table->file->print_error(error, MYF(ME_FATALERROR)); - return error; + DBUG_RETURN(error); } count*= tl->table->file->stats.records; } @@ -390,10 +396,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) if (error) { if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE) - return HA_ERR_KEY_NOT_FOUND; // No rows matching WHERE + DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); // No rows matching WHERE /* HA_ERR_LOCK_DEADLOCK or some other error */ table->file->print_error(error, MYF(0)); - return(error); + DBUG_RETURN(error); } removed_tables|= table->map; } @@ -442,6 +448,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) const_result= 0; } } + + if (thd->is_error()) + DBUG_RETURN(thd->stmt_da->sql_errno()); + /* If we have a where clause, we can only ignore searching in the tables if MIN/MAX optimisation replaced all used tables @@ -451,7 +461,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) */ if (removed_tables && used_tables != removed_tables) const_result= 0; // We didn't remove all tables - return const_result; + DBUG_RETURN(const_result); } @@ -737,6 +747,12 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, if (is_null || (is_null_safe_eq && args[1]->is_null())) { + /* + If we have a non-nullable index, we cannot use it, + since set_null will be ignored, and we will compare uninitialized data. + */ + if (!part->field->real_maybe_null()) + DBUG_RETURN(false); part->field->set_null(); *key_ptr= (uchar) 1; } @@ -807,8 +823,9 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, @param[out] prefix_len Length of prefix for the search range @note - This function may set table->key_read to 1, which must be reset after - index is used! (This can only happen when function returns 1) + This function may set field->table->key_read to true, + which must be reset after index is used! + (This can only happen when function returns 1) @retval 0 Index can not be used to optimize MIN(field)/MAX(field) @@ -823,7 +840,9 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref, uint *range_fl, uint *prefix_len) { if (!(field->flags & PART_KEY_FLAG)) - return 0; // Not key field + return false; // Not key field + + DBUG_ENTER("find_key_for_maxmin"); TABLE *table= field->table; uint idx= 0; @@ -848,7 +867,7 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref, part++, jdx++, key_part_to_use= (key_part_to_use << 1) | 1) { if (!(table->file->index_flags(idx, jdx, 0) & HA_READ_ORDER)) - return 0; + DBUG_RETURN(false); /* Check whether the index component is partial */ Field *part_field= table->field[part->fieldnr-1]; @@ -897,12 +916,12 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref, */ if (field->part_of_key.is_set(idx)) table->set_keyread(TRUE); - return 1; + DBUG_RETURN(true); } } } } - return 0; + DBUG_RETURN(false); } diff --git a/sql/rpl_handler.h b/sql/rpl_handler.h index bf207e53e2d..9a181250efc 100644 --- a/sql/rpl_handler.h +++ b/sql/rpl_handler.h @@ -73,7 +73,10 @@ public: while (info && info->observer != observer) info= iter++; if (info) + { iter.remove(); + delete info; + } else ret= TRUE; unlock(); diff --git a/sql/slave.cc b/sql/slave.cc index c676c89b369..223ea21851a 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -113,7 +113,7 @@ static const char *reconnect_messages[SLAVE_RECON_ACT_MAX][SLAVE_RECON_MSG_MAX]= registration on master", "Reconnecting after a failed registration on master", "failed registering on master, reconnecting to try again, \ -log '%s' at postion %s", +log '%s' at position %s", "COM_REGISTER_SLAVE", "Slave I/O thread killed during or after reconnect" }, @@ -121,7 +121,7 @@ log '%s' at postion %s", "Waiting to reconnect after a failed binlog dump request", "Slave I/O thread killed while retrying master dump", "Reconnecting after a failed binlog dump request", - "failed dump request, reconnecting to try again, log '%s' at postion %s", + "failed dump request, reconnecting to try again, log '%s' at position %s", "COM_BINLOG_DUMP", "Slave I/O thread killed during or after reconnect" }, @@ -130,7 +130,7 @@ log '%s' at postion %s", "Slave I/O thread killed while waiting to reconnect after a failed read", "Reconnecting after a failed master event read", "Slave I/O thread: Failed reading log event, reconnecting to retry, \ -log '%s' at postion %s", +log '%s' at position %s", "", "Slave I/O thread killed during or after a reconnect done to recover from \ failed read" diff --git a/sql/sp_head.cc b/sql/sp_head.cc index f568a2d3e68..c8368be953b 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1,4 +1,4 @@ -/* Copyright 2002-2008 MySQL AB, 2008-2010 Sun Microsystems, Inc. +/* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -11,7 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" @@ -1217,7 +1217,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success) String old_packet; Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer; Object_creation_ctx *saved_creation_ctx; - Warning_info *saved_warning_info, warning_info(thd->warning_info->warn_id()); + Warning_info *saved_warning_info; + Warning_info warning_info(thd->warning_info->warn_id(), false); /* Just reporting a stack overrun error diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index dea8d38938c..575991211e6 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -263,7 +263,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, const char *operator_name, thr_lock_type lock_type, bool open_for_modify, - bool no_warnings_for_error, + bool repair_table_use_frm, uint extra_open_options, int (*prepare_func)(THD *, TABLE_LIST *, HA_CHECK_OPT *), @@ -331,18 +331,43 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, lex->query_tables= table; lex->query_tables_last= &table->next_global; lex->query_tables_own_last= 0; - /* - Under locked tables, we know that the table can be opened, - so any errors opening the table are logical errors. - In these cases it makes sense to report them. - */ - if (!thd->locked_tables_mode) - thd->no_warnings_for_error= no_warnings_for_error; + if (view_operator_func == NULL) table->required_type=FRMTYPE_TABLE; - open_error= open_and_lock_tables(thd, table, TRUE, 0); - thd->no_warnings_for_error= 0; + if (!thd->locked_tables_mode && repair_table_use_frm) + { + /* + If we're not under LOCK TABLES and we're executing REPAIR TABLE + USE_FRM, we need to ignore errors from open_and_lock_tables(). + REPAIR TABLE USE_FRM is a heavy weapon used when a table is + critically damaged, so open_and_lock_tables() will most likely + report errors. Those errors are not interesting for the user + because it's already known that the table is badly damaged. + */ + + Warning_info wi(thd->query_id, false); + Warning_info *wi_saved= thd->warning_info; + + thd->warning_info= &wi; + + open_error= open_and_lock_tables(thd, table, TRUE, 0); + + thd->warning_info= wi_saved; + } + else + { + /* + It's assumed that even if it is REPAIR TABLE USE_FRM, the table + can be opened if we're under LOCK TABLES (otherwise LOCK TABLES + would fail). Thus, the only errors we could have from + open_and_lock_tables() are logical ones, like incorrect locking + mode. It does make sense for the user to see such errors. + */ + + open_error= open_and_lock_tables(thd, table, TRUE, 0); + } + table->next_global= save_next_global; table->next_local= save_next_local; thd->open_options&= ~extra_open_options; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index f37f800d091..f9d85b1e024 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4724,6 +4724,14 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, bool has_prelocking_list; DBUG_ENTER("open_tables"); + /* Accessing data in XA_IDLE or XA_PREPARED is not allowed. */ + enum xa_states xa_state= thd->transaction.xid_state.xa_state; + if (*start && (xa_state == XA_IDLE || xa_state == XA_PREPARED)) + { + my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]); + DBUG_RETURN(true); + } + /* temporary mem_root for new .frm parsing. TODO: variables for size diff --git a/sql/sql_class.cc b/sql/sql_class.cc index bc6086eaf19..4af038bb4e0 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -11,8 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /***************************************************************************** ** @@ -522,7 +521,7 @@ THD::THD() #if defined(ENABLED_DEBUG_SYNC) debug_sync_control(0), #endif /* defined(ENABLED_DEBUG_SYNC) */ - main_warning_info(0) + main_warning_info(0, false) { ulong tmp; @@ -581,7 +580,7 @@ THD::THD() client_capabilities= 0; // minimalistic client ull=0; system_thread= NON_SYSTEM_THREAD; - cleanup_done= abort_on_warning= no_warnings_for_error= 0; + cleanup_done= abort_on_warning= 0; peer_port= 0; // For SHOW PROCESSLIST transaction.m_pending_rows_event= 0; transaction.on= 1; @@ -854,10 +853,6 @@ MYSQL_ERROR* THD::raise_condition(uint sql_errno, query_cache_abort(&query_cache_tls); - /* FIXME: broken special case */ - if (no_warnings_for_error && (level == MYSQL_ERROR::WARN_LEVEL_ERROR)) - DBUG_RETURN(NULL); - /* When simulating OOM, skip writing to error log to avoid mtr errors */ DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_RETURN(NULL);); @@ -3675,6 +3670,7 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state) xs->xa_state=xa_state; xs->xid.set(xid); xs->in_thd=0; + xs->rm_error=0; res=my_hash_insert(&xid_cache, (uchar*)xs); } mysql_mutex_unlock(&LOCK_xid_cache); diff --git a/sql/sql_class.h b/sql/sql_class.h index 137a0c48ead..56d85e7cb6d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -655,15 +655,10 @@ public: virtual ~Query_arena() {}; inline bool is_stmt_prepare() const { return state == INITIALIZED; } - inline bool is_first_sp_execute() const - { return state == INITIALIZED_FOR_SP; } inline bool is_stmt_prepare_or_first_sp_execute() const { return (int)state < (int)PREPARED; } inline bool is_stmt_prepare_or_first_stmt_execute() const { return (int)state <= (int)PREPARED; } - inline bool is_first_stmt_execute() const { return state == PREPARED; } - inline bool is_stmt_execute() const - { return state == PREPARED || state == EXECUTED; } inline bool is_conventional() const { return state == CONVENTIONAL_EXECUTION; } @@ -1434,6 +1429,19 @@ extern "C" void my_message_sql(uint error, const char *str, myf MyFlags); class THD :public Statement, public Open_tables_state { +private: + inline bool is_stmt_prepare() const + { DBUG_ASSERT(0); return Statement::is_stmt_prepare(); } + + inline bool is_stmt_prepare_or_first_sp_execute() const + { DBUG_ASSERT(0); return Statement::is_stmt_prepare_or_first_sp_execute(); } + + inline bool is_stmt_prepare_or_first_stmt_execute() const + { DBUG_ASSERT(0); return Statement::is_stmt_prepare_or_first_stmt_execute(); } + + inline bool is_conventional() const + { DBUG_ASSERT(0); return Statement::is_conventional(); } + public: MDL_context mdl_context; @@ -2089,7 +2097,6 @@ public: bool enable_slow_log; /* enable slow log for current statement */ bool abort_on_warning; bool got_warning; /* Set on call to push_warning() */ - bool no_warnings_for_error; /* no warnings on call to my_error() */ /* set during loop of derived table processing */ bool derived_tables_processing; my_bool tablespace_op; /* This is TRUE in DISCARD/IMPORT TABLESPACE */ @@ -2807,6 +2814,7 @@ private: /** The current internal error handler for this thread, or NULL. */ Internal_error_handler *m_internal_handler; + /** The lex to hold the parsed tree of conventional (non-prepared) queries. Whereas for prepared and stored procedure statements we use an own lex diff --git a/sql/sql_error.cc b/sql/sql_error.cc index d0982b879e7..24516f03bee 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -1,5 +1,4 @@ -/* Copyright (C) 1995-2002 MySQL AB, - Copyright (C) 2008-2009 Sun Microsystems, Inc +/* Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -458,10 +457,11 @@ Diagnostics_area::disable_status() m_status= DA_DISABLED; } -Warning_info::Warning_info(ulonglong warn_id_arg) +Warning_info::Warning_info(ulonglong warn_id_arg, bool allow_unlimited_warnings) :m_statement_warn_count(0), m_current_row_for_warning(1), m_warn_id(warn_id_arg), + m_allow_unlimited_warnings(allow_unlimited_warnings), m_read_only(FALSE) { /* Initialize sub structures */ @@ -543,7 +543,8 @@ MYSQL_ERROR *Warning_info::push_warning(THD *thd, if (! m_read_only) { - if (m_warn_list.elements < thd->variables.max_error_count) + if (m_allow_unlimited_warnings || + m_warn_list.elements < thd->variables.max_error_count) { cond= new (& m_warn_root) MYSQL_ERROR(& m_warn_root); if (cond) @@ -559,6 +560,20 @@ MYSQL_ERROR *Warning_info::push_warning(THD *thd, return cond; } +MYSQL_ERROR *Warning_info::push_warning(THD *thd, const MYSQL_ERROR *sql_condition) +{ + MYSQL_ERROR *new_condition= push_warning(thd, + sql_condition->get_sql_errno(), + sql_condition->get_sqlstate(), + sql_condition->get_level(), + sql_condition->get_message_text()); + + if (new_condition) + new_condition->copy_opt_attributes(sql_condition); + + return new_condition; +} + /* Push the warning to error list if there is still room in the list diff --git a/sql/sql_error.h b/sql/sql_error.h index 14dc5e6d12c..955b3767847 100644 --- a/sql/sql_error.h +++ b/sql/sql_error.h @@ -1,5 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB, - Copyright (C) 2008-2009 Sun Microsystems, Inc +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -12,7 +11,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef SQL_ERROR_H #define SQL_ERROR_H @@ -323,10 +322,13 @@ class Warning_info { /** A memory root to allocate warnings and errors */ MEM_ROOT m_warn_root; + /** List of warnings of all severities (levels). */ List <MYSQL_ERROR> m_warn_list; + /** A break down of the number of warnings per severity (level). */ uint m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END]; + /** The number of warnings of the current statement. Warning_info life cycle differs from statement life cycle -- it may span @@ -334,20 +336,25 @@ class Warning_info m_statement_warn_count 0, whereas m_warn_list is not empty. */ uint m_statement_warn_count; + /* Row counter, to print in errors and warnings. Not increased in create_sort_index(); may differ from examined_row_count. */ ulong m_current_row_for_warning; - /** Used to optionally clear warnings only once per statement. */ + + /** Used to optionally clear warnings only once per statement. */ ulonglong m_warn_id; + /** Indicates if push_warning() allows unlimited number of warnings. */ + bool m_allow_unlimited_warnings; + private: Warning_info(const Warning_info &rhs); /* Not implemented */ Warning_info& operator=(const Warning_info &rhs); /* Not implemented */ public: - Warning_info(ulonglong warn_id_arg); + Warning_info(ulonglong warn_id_arg, bool allow_unlimited_warnings); ~Warning_info(); /** @@ -384,19 +391,13 @@ public: void append_warnings(THD *thd, List<MYSQL_ERROR> *src) { MYSQL_ERROR *err; - MYSQL_ERROR *copy; List_iterator_fast<MYSQL_ERROR> it(*src); /* Don't use ::push_warning() to avoid invocation of condition handlers or escalation of warnings to errors. */ while ((err= it++)) - { - copy= Warning_info::push_warning(thd, err->get_sql_errno(), err->get_sqlstate(), - err->get_level(), err->get_message_text()); - if (copy) - copy->copy_opt_attributes(err); - } + Warning_info::push_warning(thd, err); } /** @@ -462,6 +463,9 @@ public: MYSQL_ERROR::enum_warning_level level, const char* msg); + /** Add a new condition to the current list. */ + MYSQL_ERROR *push_warning(THD *thd, const MYSQL_ERROR *sql_condition); + /** Set the read only status for this statement area. This is a privileged operation, reserved for the implementation of diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 0805a1b9f50..f82e0948b54 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -1302,9 +1302,10 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs, String &field_term, String &line_start, String &line_term, String &enclosed_par, int escape, bool get_it_from_net, bool is_fifo) - :file(file_par),escape_char(escape) + :file(file_par), buff_length(tot_length), escape_char(escape), + found_end_of_line(false), eof(false), need_end_io_cache(false), + error(false), line_cuted(false), found_null(false), read_charset(cs) { - read_charset= cs; field_term_ptr=(char*) field_term.ptr(); field_term_length= field_term.length(); line_term_ptr=(char*) line_term.ptr(); @@ -1332,12 +1333,10 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs, (uchar) enclosed_par[0] : INT_MAX; field_term_char= field_term_length ? (uchar) field_term_ptr[0] : INT_MAX; line_term_char= line_term_length ? (uchar) line_term_ptr[0] : INT_MAX; - error=eof=found_end_of_line=found_null=line_cuted=0; - buff_length=tot_length; /* Set of a stack for unget if long terminators */ - uint length=max(field_term_length,line_term_length)+1; + uint length= max(cs->mbmaxlen, max(field_term_length, line_term_length)) + 1; set_if_bigger(length,line_start.length()); stack=stack_pos=(int*) sql_alloc(sizeof(int)*length); @@ -1379,7 +1378,7 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs, READ_INFO::~READ_INFO() { - if (!error && need_end_io_cache) + if (need_end_io_cache) ::end_io_cache(&cache); if (buffer != NULL) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7d0186c0752..24f7fdb8e61 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -7227,10 +7227,20 @@ bool parse_sql(THD *thd, bool mysql_parse_status= MYSQLparse(thd) != 0; - /* Check that if MYSQLparse() failed, thd->is_error() is set. */ + /* + Check that if MYSQLparse() failed either thd->is_error() is set, or an + internal error handler is set. + + The assert will not catch a situation where parsing fails without an + error reported if an error handler exists. The problem is that the + error handler might have intercepted the error, so thd->is_error() is + not set. However, there is no way to be 100% sure here (the error + handler might be for other errors than parsing one). + */ DBUG_ASSERT(!mysql_parse_status || - (mysql_parse_status && thd->is_error())); + (mysql_parse_status && thd->is_error()) || + (mysql_parse_status && thd->get_internal_handler())); /* Reset parser state. */ diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index b9bbec63e7d..52d6757b03a 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -3980,7 +3980,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, part_spec->start_part= 0; part_spec->end_part= num_parts - 1; if ((index < MAX_KEY) && - key_spec->flag == (uint)HA_READ_KEY_EXACT && + key_spec && key_spec->flag == (uint)HA_READ_KEY_EXACT && part_info->some_fields_in_PF.is_set(index)) { key_info= table->key_info+index; diff --git a/sql/sql_plist.h b/sql/sql_plist.h index db85266be15..b71136cd9ab 100644 --- a/sql/sql_plist.h +++ b/sql/sql_plist.h @@ -95,6 +95,7 @@ public: *last= a; *B::prev_ptr(a)= last; I::set_last(B::next_ptr(a)); + C::inc(); } inline void insert_after(T *pos, T *a) { @@ -112,6 +113,7 @@ public: } else I::set_last(B::next_ptr(a)); + C::inc(); } } inline void remove(T *a) @@ -188,6 +190,20 @@ public: /** + Hook class which via its methods specifies which members + of T should be used for participating in a intrusive list. +*/ + +template <typename T, T* T::*next, T** T::*prev> +struct I_P_List_adapter +{ + static inline T **next_ptr(T *el) { return &(el->*next); } + + static inline T ***prev_ptr(T *el) { return &(el->*prev); } +}; + + +/** Element counting policy class for I_P_List to be used in cases when no element counting should be done. */ diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 9085d018dfd..779569b10fb 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -2842,7 +2842,8 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) param= stmt->param_array[param_number]; Diagnostics_area new_stmt_da, *save_stmt_da= thd->stmt_da; - Warning_info new_warnning_info(thd->query_id), *save_warinig_info= thd->warning_info; + Warning_info new_warnning_info(thd->query_id, false); + Warning_info *save_warinig_info= thd->warning_info; thd->stmt_da= &new_stmt_da; thd->warning_info= &new_warnning_info; @@ -3900,7 +3901,7 @@ Ed_result_set::Ed_result_set(List<Ed_row> *rows_arg, */ Ed_connection::Ed_connection(THD *thd) - :m_warning_info(thd->query_id), + :m_warning_info(thd->query_id, false), m_thd(thd), m_rsets(0), m_current_rset(0) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 28981e719b8..7620d7c5f47 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -991,7 +991,7 @@ JOIN::optimize() If all items were resolved by opt_sum_query, there is no need to open any tables. */ - if ((res=opt_sum_query(select_lex->leaf_tables, all_fields, conds))) + if ((res=opt_sum_query(thd, select_lex->leaf_tables, all_fields, conds))) { if (res == HA_ERR_KEY_NOT_FOUND) { @@ -1972,7 +1972,11 @@ JOIN::exec() if (!curr_join->sort_and_group && curr_join->const_tables != curr_join->tables) curr_join->join_tab[curr_join->const_tables].sorted= 0; - if ((tmp_error= do_select(curr_join, (List<Item> *) 0, curr_tmp_table, 0))) + + Procedure *save_proc= curr_join->procedure; + tmp_error= do_select(curr_join, (List<Item> *) 0, curr_tmp_table, 0); + curr_join->procedure= save_proc; + if (tmp_error) { error= tmp_error; DBUG_VOID_RETURN; @@ -2259,7 +2263,7 @@ JOIN::exec() Item* sort_table_cond= make_cond_for_table(curr_join->tmp_having, used_tables, - used_tables); + (table_map) 0); if (sort_table_cond) { if (!curr_table->select) @@ -12620,10 +12624,14 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), } if (join->having && join->having->val_int() == 0) DBUG_RETURN(NESTED_LOOP_OK); // Didn't match having - error=0; if (join->procedure) - error=join->procedure->send_row(join->procedure_fields_list); - else if (join->do_send_rows) + { + if (join->procedure->send_row(join->procedure_fields_list)) + DBUG_RETURN(NESTED_LOOP_ERROR); + DBUG_RETURN(NESTED_LOOP_OK); + } + error=0; + if (join->do_send_rows) error=join->result->send_data(*join->fields); if (error) DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ @@ -13094,6 +13102,42 @@ static bool test_if_ref(Item_field *left_item,Item *right_item) return 0; // keep test } +/** + Extract a condition that can be checked after reading given table + + @param cond Condition to analyze + @param tables Tables for which "current field values" are available + @param used_table Table that we're extracting the condition for (may + also include PSEUDO_TABLE_BITS, and may be zero) + @param exclude_expensive_cond Do not push expensive conditions + + @retval <>NULL Generated condition + @retval =NULL Already checked, OR error + + @details + Extract the condition that can be checked after reading the table + specified in 'used_table', given that current-field values for tables + specified in 'tables' bitmap are available. + If 'used_table' is 0 + - extract conditions for all tables in 'tables'. + - extract conditions are unrelated to any tables + in the same query block/level(i.e. conditions + which have used_tables == 0). + + The function assumes that + - Constant parts of the condition has already been checked. + - Condition that could be checked for tables in 'tables' has already + been checked. + + The function takes into account that some parts of the condition are + guaranteed to be true by employed 'ref' access methods (the code that + does this is located at the end, search down for "EQ_FUNC"). + + @note + Make sure to keep the implementations of make_cond_for_table() and + make_cond_after_sjm() synchronized. + make_cond_for_info_schema() uses similar algorithm as well. +*/ static COND * make_cond_for_table(COND *cond, table_map tables, table_map used_table) diff --git a/sql/sql_select.h b/sql/sql_select.h index bdb9d1a315c..ea4a22527d3 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -619,7 +619,8 @@ bool is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args); /* functions from opt_sum.cc */ bool simple_pred(Item_func *func_item, Item **args, bool *inv_order); -int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds); +int opt_sum_query(THD* thd, + TABLE_LIST *tables, List<Item> &all_fields, COND *conds); /* from sql_delete.cc, used by opt_range.cc */ extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 464b966e4be..9e9de5e3524 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3416,6 +3416,45 @@ end: /** + Trigger_error_handler is intended to intercept and silence SQL conditions + that might happen during trigger loading for SHOW statements. + The potential SQL conditions are: + + - ER_PARSE_ERROR -- this error is thrown if a trigger definition file + is damaged or contains invalid CREATE TRIGGER statement. That should + not happen in normal life. + + - ER_TRG_NO_DEFINER -- this warning is thrown when we're loading a + trigger created/imported in/from the version of MySQL, which does not + support trigger definers. + + - ER_TRG_NO_CREATION_CTX -- this warning is thrown when we're loading a + trigger created/imported in/from the version of MySQL, which does not + support trigger creation contexts. +*/ + +class Trigger_error_handler : public Internal_error_handler +{ +public: + bool handle_condition(THD *thd, + uint sql_errno, + const char* sqlstate, + MYSQL_ERROR::enum_warning_level level, + const char* msg, + MYSQL_ERROR ** cond_hdl) + { + if (sql_errno == ER_PARSE_ERROR || + sql_errno == ER_TRG_NO_DEFINER || + sql_errno == ER_TRG_NO_CREATION_CTX) + return true; + + return false; + } +}; + + + +/** @brief Fill I_S tables whose data are retrieved from frm files and storage engine @@ -3570,7 +3609,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name->str, 0)) #endif { - thd->no_warnings_for_error= 1; List<LEX_STRING> table_names; int res= make_table_name_list(thd, &table_names, lex, &lookup_field_vals, @@ -3619,9 +3657,24 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) if (!(table_open_method & ~OPEN_FRM_ONLY) && !with_i_schema) { - if (!fill_schema_table_from_frm(thd, tables, schema_table, db_name, - table_name, schema_table_idx, - can_deadlock)) + /* + Here we need to filter out warnings, which can happen + during loading of triggers in fill_schema_table_from_frm(), + because we don't need those warnings to pollute output of + SELECT from I_S / SHOW-statements. + */ + + Trigger_error_handler err_handler; + thd->push_internal_handler(&err_handler); + + int res= fill_schema_table_from_frm(thd, tables, schema_table, + db_name, table_name, + schema_table_idx, + can_deadlock); + + thd->pop_internal_handler(); + + if (!res) continue; } @@ -3631,7 +3684,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) Set the parent lex of 'sel' because it is needed by sel.init_query() which is called inside make_table_list. */ - thd->no_warnings_for_error= 1; sel.parent_lex= lex; if (make_table_list(thd, &sel, db_name, table_name)) goto err; @@ -6675,6 +6727,92 @@ int make_schema_select(THD *thd, SELECT_LEX *sel, } +/** + Fill INFORMATION_SCHEMA-table, leave correct Diagnostics_area / + Warning_info state after itself. + + This function is a wrapper around ST_SCHEMA_TABLE::fill_table(), which + may "partially silence" some errors. The thing is that during + fill_table() many errors might be emitted. These errors stem from the + nature of fill_table(). + + For example, SELECT ... FROM INFORMATION_SCHEMA.xxx WHERE TABLE_NAME = 'xxx' + results in a number of 'Table <db name>.xxx does not exist' errors, + because fill_table() tries to open the 'xxx' table in every possible + database. + + Those errors are cleared (the error status is cleared from + Diagnostics_area) inside fill_table(), but they remain in Warning_info + (Warning_info is not cleared because it may contain useful warnings). + + This function is responsible for making sure that Warning_info does not + contain warnings corresponding to the cleared errors. + + @note: THD::no_warnings_for_error used to be set before calling + fill_table(), thus those errors didn't go to Warning_info. This is not + the case now (THD::no_warnings_for_error was eliminated as a hack), so we + need to take care of those warnings here. + + @param thd Thread context. + @param table_list I_S table. + @param join_table JOIN/SELECT table. + + @return Error status. + @retval TRUE Error. + @retval FALSE Success. +*/ +static bool do_fill_table(THD *thd, + TABLE_LIST *table_list, + JOIN_TAB *join_table) +{ + // NOTE: fill_table() may generate many "useless" warnings, which will be + // ignored afterwards. On the other hand, there might be "useful" + // warnings, which should be presented to the user. Warning_info usually + // stores no more than THD::variables.max_error_count warnings. + // The problem is that "useless warnings" may occupy all the slots in the + // Warning_info, so "useful warnings" get rejected. In order to avoid + // that problem we create a Warning_info instance, which is capable of + // storing "unlimited" number of warnings. + Warning_info wi(thd->query_id, true); + Warning_info *wi_saved= thd->warning_info; + + thd->warning_info= &wi; + + bool res= table_list->schema_table->fill_table( + thd, table_list, join_table->select_cond); + + thd->warning_info= wi_saved; + + // Pass an error if any. + + if (thd->stmt_da->is_error()) + { + thd->warning_info->push_warning(thd, + thd->stmt_da->sql_errno(), + thd->stmt_da->get_sqlstate(), + MYSQL_ERROR::WARN_LEVEL_ERROR, + thd->stmt_da->message()); + } + + // Pass warnings (if any). + // + // Filter out warnings with WARN_LEVEL_ERROR level, because they + // correspond to the errors which were filtered out in fill_table(). + + + List_iterator_fast<MYSQL_ERROR> it(wi.warn_list()); + MYSQL_ERROR *err; + + while ((err= it++)) + { + if (err->get_level() != MYSQL_ERROR::WARN_LEVEL_ERROR) + thd->warning_info->push_warning(thd, err); + } + + return res; +} + + /* Fill temporary schema tables before SELECT @@ -6697,7 +6835,6 @@ bool get_schema_tables_result(JOIN *join, bool result= 0; DBUG_ENTER("get_schema_tables_result"); - thd->no_warnings_for_error= 1; for (JOIN_TAB *tab= join->join_tab; tab < tmp_join_tab; tab++) { if (!tab->table || !tab->table->pos_in_table_list) @@ -6748,8 +6885,7 @@ bool get_schema_tables_result(JOIN *join, else table_list->table->file->stats.records= 0; - if (table_list->schema_table->fill_table(thd, table_list, - tab->select_cond)) + if (do_fill_table(thd, table_list, tab)) { result= 1; join->error= 1; @@ -6761,7 +6897,6 @@ bool get_schema_tables_result(JOIN *join, table_list->schema_table_state= executed_place; } } - thd->no_warnings_for_error= 0; DBUG_RETURN(result); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ce4218869ff..f98ac676525 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -6660,15 +6660,15 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, NO need to tamper with MERGE tables. The real open is done later. */ Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN); - TABLE *t_table; + TABLE_LIST temp_table_list; + TABLE_LIST *t_table_list; if (new_name != table_name || new_db != db) { - table_list->alias= new_name; - table_list->table_name= new_name; - table_list->table_name_length= strlen(new_name); - table_list->db= new_db; - table_list->db_length= strlen(new_db); - table_list->mdl_request.ticket= target_mdl_request.ticket; + temp_table_list.init_one_table(new_db, strlen(new_db), + new_name, strlen(new_name), + new_name, TL_READ_NO_INSERT); + temp_table_list.mdl_request.ticket= target_mdl_request.ticket; + t_table_list= &temp_table_list; } else { @@ -6678,20 +6678,21 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, to request the lock. */ table_list->mdl_request.ticket= mdl_ticket; + t_table_list= table_list; } - if (open_table(thd, table_list, thd->mem_root, &ot_ctx)) + if (open_table(thd, t_table_list, thd->mem_root, &ot_ctx)) { goto err_with_mdl; } - t_table= table_list->table; /* Tell the handler that a new frm file is in place. */ - error= t_table->file->ha_create_handler_files(path, NULL, CHF_INDEX_FLAG, - create_info); + error= t_table_list->table->file->ha_create_handler_files(path, NULL, + CHF_INDEX_FLAG, + create_info); - DBUG_ASSERT(thd->open_tables == t_table); + DBUG_ASSERT(thd->open_tables == t_table_list->table); close_thread_table(thd, &thd->open_tables); - table_list->table= 0; + t_table_list->table= NULL; if (error) goto err_with_mdl; diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 4908fce5950..d026714c007 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -1225,13 +1225,12 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, DBUG_RETURN(1); // EOM } - - if (!thd->no_warnings_for_error) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_TRG_NO_CREATION_CTX, - ER(ER_TRG_NO_CREATION_CTX), - (const char*) db, - (const char*) table_name); + + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRG_NO_CREATION_CTX, + ER(ER_TRG_NO_CREATION_CTX), + (const char*) db, + (const char*) table_name); if (!(trg_client_cs_name= alloc_lex_string(&table->mem_root)) || !(trg_connection_cl_name= alloc_lex_string(&table->mem_root)) || @@ -1362,12 +1361,12 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, MySQL, which does not support triggers definers. We should emit warning here. */ - if (!thd->no_warnings_for_error) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_TRG_NO_DEFINER, ER(ER_TRG_NO_DEFINER), - (const char*) db, - (const char*) sp->m_name.str); - + + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TRG_NO_DEFINER, ER(ER_TRG_NO_DEFINER), + (const char*) db, + (const char*) sp->m_name.str); + /* Set definer to the '' to correct displaying in the information schema. diff --git a/sql/transaction.cc b/sql/transaction.cc index 85686810893..94a32200274 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -79,6 +79,33 @@ static bool xa_trans_rolled_back(XID_STATE *xid_state) /** + Rollback the active XA transaction. + + @note Resets rm_error before calling ha_rollback(), so + the thd->transaction.xid structure gets reset + by ha_rollback() / THD::transaction::cleanup(). + + @return TRUE if the rollback failed, FALSE otherwise. +*/ + +static bool xa_trans_force_rollback(THD *thd) +{ + /* + We must reset rm_error before calling ha_rollback(), + so thd->transaction.xid structure gets reset + by ha_rollback()/THD::transaction::cleanup(). + */ + thd->transaction.xid_state.rm_error= 0; + if (ha_rollback_trans(thd, true)) + { + my_error(ER_XAER_RMERR, MYF(0)); + return true; + } + return false; +} + + +/** Begin a new transaction. @note Beginning a transaction implicitly commits any current @@ -362,6 +389,13 @@ bool trans_savepoint(THD *thd, LEX_STRING name) !opt_using_transactions) DBUG_RETURN(FALSE); + enum xa_states xa_state= thd->transaction.xid_state.xa_state; + if (xa_state != XA_NOTR) + { + my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]); + DBUG_RETURN(TRUE); + } + sv= find_savepoint(thd, name); if (*sv) /* old savepoint of the same name exists */ @@ -435,6 +469,13 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name) DBUG_RETURN(TRUE); } + enum xa_states xa_state= thd->transaction.xid_state.xa_state; + if (xa_state != XA_NOTR) + { + my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]); + DBUG_RETURN(TRUE); + } + if (ha_rollback_to_savepoint(thd, sv)) res= TRUE; else if (((thd->variables.option_bits & OPTION_KEEP_LOG) || @@ -635,8 +676,7 @@ bool trans_xa_commit(THD *thd) if (xa_trans_rolled_back(&thd->transaction.xid_state)) { - if (ha_rollback_trans(thd, TRUE)) - my_error(ER_XAER_RMERR, MYF(0)); + xa_trans_force_rollback(thd); res= thd->is_error(); } else if (xa_state == XA_IDLE && thd->lex->xa_opt == XA_ONE_PHASE) @@ -725,15 +765,7 @@ bool trans_xa_rollback(THD *thd) DBUG_RETURN(TRUE); } - /* - Resource Manager error is meaningless at this point, as we perform - explicit rollback request by user. We must reset rm_error before - calling ha_rollback(), so thd->transaction.xid structure gets reset - by ha_rollback()/THD::transaction::cleanup(). - */ - thd->transaction.xid_state.rm_error= 0; - if ((res= test(ha_rollback_trans(thd, TRUE)))) - my_error(ER_XAER_RMERR, MYF(0)); + res= xa_trans_force_rollback(thd); thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG); thd->transaction.all.modified_non_trans_table= FALSE; |