summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorSergei Golubchik <sergii@pisem.net>2012-04-10 08:28:13 +0200
committerSergei Golubchik <sergii@pisem.net>2012-04-10 08:28:13 +0200
commit16c5c53fc21b162844c38a87ac48205448ca1d2f (patch)
tree7a50437d5f9da772ec8aaacab322e5f6c13e7305 /sql
parentf860b2aad41cd1b5ed0438ea211dcd78eec82b94 (diff)
parented418461614d912fbb114053508b3fb09b1fc2f0 (diff)
downloadmariadb-git-16c5c53fc21b162844c38a87ac48205448ca1d2f.tar.gz
mysql 5.5.23 merge
Diffstat (limited to 'sql')
-rw-r--r--sql/field.cc3
-rw-r--r--sql/filesort.cc2
-rw-r--r--sql/ha_partition.cc267
-rw-r--r--sql/ha_partition.h40
-rw-r--r--sql/item.cc7
-rw-r--r--sql/item.h2
-rw-r--r--sql/item_cmpfunc.h2
-rw-r--r--sql/log.cc2
-rw-r--r--sql/log_event.cc25
-rw-r--r--sql/log_event.h2
-rw-r--r--sql/mem_root_array.h175
-rw-r--r--sql/mysqld.cc11
-rw-r--r--sql/rpl_rli.h7
-rw-r--r--sql/share/errmsg-utf8.txt5
-rw-r--r--sql/signal_handler.cc1
-rw-r--r--sql/slave.cc92
-rw-r--r--sql/sp_head.cc2
-rw-r--r--sql/sql_lex.cc23
-rw-r--r--sql/sql_lex.h16
-rw-r--r--sql/sql_prepare.cc14
-rw-r--r--sql/sql_show.cc5
-rw-r--r--sql/sql_view.cc7
-rw-r--r--sql/sys_vars.cc2
-rw-r--r--sql/transaction.cc2
24 files changed, 569 insertions, 145 deletions
diff --git a/sql/field.cc b/sql/field.cc
index f5b43c69f63..d913e395b1a 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -7860,8 +7860,7 @@ String *Field_set::val_str(String *val_buffer,
ulonglong tmp=(ulonglong) Field_enum::val_int();
uint bitnr=0;
- val_buffer->length(0);
- val_buffer->set_charset(field_charset);
+ val_buffer->set("", 0, field_charset);
while (tmp && bitnr < (uint) typelib->count)
{
if (tmp & 1)
diff --git a/sql/filesort.cc b/sql/filesort.cc
index bec43d98e9e..03379f2738a 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -219,7 +219,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
param.keys= table_sort.keys;
if (memory_available < min_sort_memory)
{
- my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR+ME_WAITTANG));
+ my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR + ME_FATALERROR));
goto err;
}
if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX,
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 9b94de11cb7..b4181fc6d7f 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2005, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2005, 2012, Oracle and/or its affiliates.
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
@@ -289,6 +289,7 @@ void ha_partition::init_handler_variables()
m_is_sub_partitioned= 0;
m_is_clone_of= NULL;
m_clone_mem_root= NULL;
+ m_part_ids_sorted_by_num_of_records= NULL;
#ifdef DONT_HAVE_TO_BE_INITALIZED
m_start_key.flag= 0;
@@ -325,6 +326,7 @@ ha_partition::~ha_partition()
}
my_free(m_ordered_rec_buffer);
m_ordered_rec_buffer= NULL;
+ my_free(m_part_ids_sorted_by_num_of_records);
clear_handler_file();
@@ -2741,6 +2743,16 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
m_start_key.key= (const uchar*)ptr;
}
}
+ if (!m_part_ids_sorted_by_num_of_records)
+ {
+ if (!(m_part_ids_sorted_by_num_of_records=
+ (uint32*) my_malloc(m_tot_parts * sizeof(uint32), MYF(MY_WME))))
+ DBUG_RETURN(error);
+ uint32 i;
+ /* Initialize it with all partition ids. */
+ for (i= 0; i < m_tot_parts; i++)
+ m_part_ids_sorted_by_num_of_records[i]= i;
+ }
/* 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))
@@ -5314,6 +5326,24 @@ int ha_partition::handle_ordered_prev(uchar *buf)
and read_time calls
*/
+/**
+ Helper function for sorting according to number of rows in descending order.
+*/
+
+int ha_partition::compare_number_of_records(ha_partition *me,
+ const uint32 *a,
+ const uint32 *b)
+{
+ handler **file= me->m_file;
+ /* Note: sorting in descending order! */
+ if (file[*a]->stats.records > file[*b]->stats.records)
+ return -1;
+ if (file[*a]->stats.records < file[*b]->stats.records)
+ return 1;
+ return 0;
+}
+
+
/*
General method to gather info from handler
@@ -5558,6 +5588,15 @@ int ha_partition::info(uint flag)
}
i++;
} while (*(++file_array));
+ /*
+ Sort the array of part_ids by number of records in
+ in descending order.
+ */
+ my_qsort2((void*) m_part_ids_sorted_by_num_of_records,
+ m_tot_parts,
+ sizeof(uint32),
+ (qsort2_cmp) compare_number_of_records,
+ this);
file= m_file[handler_instance];
file->info(HA_STATUS_CONST | no_lock_flag);
@@ -6313,21 +6352,73 @@ const key_map *ha_partition::keys_to_use_for_scanning()
DBUG_RETURN(m_file[0]->keys_to_use_for_scanning());
}
-#define MAX_PARTS_FOR_OPTIMIZER_CALLS 10
-/*
- Prepare start variables for estimating optimizer costs.
- @param[out] num_used_parts Number of partitions after pruning.
- @param[out] check_min_num Number of partitions to call.
- @param[out] first first used partition.
+/**
+ Minimum number of rows to base optimizer estimate on.
*/
-void ha_partition::partitions_optimizer_call_preparations(uint *first,
- uint *num_used_parts,
- uint *check_min_num)
+
+ha_rows ha_partition::min_rows_for_estimate()
{
- *first= bitmap_get_first_set(&(m_part_info->used_partitions));
- *num_used_parts= bitmap_bits_set(&(m_part_info->used_partitions));
- *check_min_num= min(MAX_PARTS_FOR_OPTIMIZER_CALLS, *num_used_parts);
+ uint i, max_used_partitions, tot_used_partitions;
+ DBUG_ENTER("ha_partition::min_rows_for_estimate");
+
+ tot_used_partitions= bitmap_bits_set(&m_part_info->used_partitions);
+ DBUG_ASSERT(tot_used_partitions);
+
+ /*
+ Allow O(log2(tot_partitions)) increase in number of used partitions.
+ This gives O(tot_rows/log2(tot_partitions)) rows to base the estimate on.
+ I.e when the total number of partitions doubles, allow one more
+ partition to be checked.
+ */
+ i= 2;
+ max_used_partitions= 1;
+ while (i < m_tot_parts)
+ {
+ max_used_partitions++;
+ i= i << 1;
+ }
+ if (max_used_partitions > tot_used_partitions)
+ max_used_partitions= tot_used_partitions;
+
+ /* stats.records is already updated by the info(HA_STATUS_VARIABLE) call. */
+ DBUG_PRINT("info", ("max_used_partitions: %u tot_rows: %lu",
+ max_used_partitions,
+ (ulong) stats.records));
+ DBUG_PRINT("info", ("tot_used_partitions: %u min_rows_to_check: %lu",
+ tot_used_partitions,
+ (ulong) stats.records * max_used_partitions
+ / tot_used_partitions));
+ DBUG_RETURN(stats.records * max_used_partitions / tot_used_partitions);
+}
+
+
+/**
+ Get the biggest used partition.
+
+ Starting at the N:th biggest partition and skips all non used
+ partitions, returning the biggest used partition found
+
+ @param[in,out] part_index Skip the *part_index biggest partitions
+
+ @return The biggest used partition with index not lower than *part_index.
+ @retval NO_CURRENT_PART_ID No more partition used.
+ @retval != NO_CURRENT_PART_ID partition id of biggest used partition with
+ index >= *part_index supplied. Note that
+ *part_index will be updated to the next
+ partition index to use.
+*/
+
+uint ha_partition::get_biggest_used_partition(uint *part_index)
+{
+ uint part_id;
+ while ((*part_index) < m_tot_parts)
+ {
+ part_id= m_part_ids_sorted_by_num_of_records[(*part_index)++];
+ if (bitmap_is_set(&m_part_info->used_partitions, part_id))
+ return part_id;
+ }
+ return NO_CURRENT_PART_ID;
}
@@ -6343,115 +6434,107 @@ void ha_partition::partitions_optimizer_call_preparations(uint *first,
double ha_partition::scan_time()
{
- double scan_time= 0.0;
- uint first, part_id, num_used_parts, check_min_num, partitions_called= 0;
+ double scan_time= 0;
+ handler **file;
DBUG_ENTER("ha_partition::scan_time");
- partitions_optimizer_call_preparations(&first, &num_used_parts, &check_min_num);
- for (part_id= first; partitions_called < num_used_parts ; part_id++)
- {
- if (!bitmap_is_set(&(m_part_info->used_partitions), part_id))
- continue;
- scan_time+= m_file[part_id]->scan_time();
- partitions_called++;
- if (partitions_called >= check_min_num && scan_time != 0.0)
- {
- DBUG_RETURN(scan_time *
- (double) num_used_parts / (double) partitions_called);
- }
- }
+ for (file= m_file; *file; file++)
+ if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
+ scan_time+= (*file)->scan_time();
DBUG_RETURN(scan_time);
}
-/*
- Estimate rows for records_in_range or estimate_rows_upper_bound.
+/**
+ Find number of records in a range.
+ @param inx Index number
+ @param min_key Start of range
+ @param max_key End of range
- @param is_records_in_range call records_in_range instead of
- estimate_rows_upper_bound.
- @param inx (only for records_in_range) index to use.
- @param min_key (only for records_in_range) start of range.
- @param max_key (only for records_in_range) end of range.
+ @return Number of rows in range.
- @return Number of rows or HA_POS_ERROR.
+ Given a starting key, and an ending key estimate the number of rows that
+ will exist between the two. max_key may be empty which in case determine
+ if start_key matches any rows.
*/
-ha_rows ha_partition::estimate_rows(bool is_records_in_range, uint inx,
- key_range *min_key, key_range *max_key)
+
+ha_rows ha_partition::records_in_range(uint inx, key_range *min_key,
+ key_range *max_key)
{
- ha_rows rows, estimated_rows= 0;
- uint first, part_id, num_used_parts, check_min_num, partitions_called= 0;
+ ha_rows min_rows_to_check, rows, estimated_rows=0, checked_rows= 0;
+ uint partition_index= 0, part_id;
DBUG_ENTER("ha_partition::records_in_range");
- partitions_optimizer_call_preparations(&first, &num_used_parts, &check_min_num);
- for (part_id= first; partitions_called < num_used_parts ; part_id++)
+ min_rows_to_check= min_rows_for_estimate();
+
+ while ((part_id= get_biggest_used_partition(&partition_index))
+ != NO_CURRENT_PART_ID)
{
- if (!bitmap_is_set(&(m_part_info->used_partitions), part_id))
- continue;
- if (is_records_in_range)
- rows= m_file[part_id]->records_in_range(inx, min_key, max_key);
- else
- rows= m_file[part_id]->estimate_rows_upper_bound();
+ rows= m_file[part_id]->records_in_range(inx, min_key, max_key);
+
+ DBUG_PRINT("info", ("part %u match %lu rows of %lu", part_id, (ulong) rows,
+ (ulong) m_file[part_id]->stats.records));
+
if (rows == HA_POS_ERROR)
DBUG_RETURN(HA_POS_ERROR);
estimated_rows+= rows;
- partitions_called++;
- if (partitions_called >= check_min_num && estimated_rows)
+ checked_rows+= m_file[part_id]->stats.records;
+ /*
+ Returning 0 means no rows can be found, so we must continue
+ this loop as long as we have estimated_rows == 0.
+ Also many engines return 1 to indicate that there may exist
+ a matching row, we do not normalize this by dividing by number of
+ used partitions, but leave it to be returned as a sum, which will
+ reflect that we will need to scan each partition's index.
+
+ Note that this statistics may not always be correct, so we must
+ continue even if the current partition has 0 rows, since we might have
+ deleted rows from the current partition, or inserted to the next
+ partition.
+ */
+ if (estimated_rows && checked_rows &&
+ checked_rows >= min_rows_to_check)
{
- DBUG_RETURN(estimated_rows * num_used_parts / partitions_called);
+ DBUG_PRINT("info",
+ ("records_in_range(inx %u): %lu (%lu * %lu / %lu)",
+ inx,
+ (ulong) (estimated_rows * stats.records / checked_rows),
+ (ulong) estimated_rows,
+ (ulong) stats.records,
+ (ulong) checked_rows));
+ DBUG_RETURN(estimated_rows * stats.records / checked_rows);
}
}
+ DBUG_PRINT("info", ("records_in_range(inx %u): %lu",
+ inx,
+ (ulong) estimated_rows));
DBUG_RETURN(estimated_rows);
}
-/*
- Find number of records in a range
-
- SYNOPSIS
- records_in_range()
- inx Index number
- min_key Start of range
- max_key End of range
-
- RETURN VALUE
- Number of rows in range
-
- DESCRIPTION
- Given a starting key, and an ending key estimate the number of rows that
- will exist between the two. end_key may be empty which in case determine
- if start_key matches any rows.
-
- Called from opt_range.cc by check_quick_keys().
-
- monty: MUST be called for each range and added.
- Note that MySQL will assume that if this returns 0 there is no
- matching rows for the range!
-*/
-
-ha_rows ha_partition::records_in_range(uint inx, key_range *min_key,
- key_range *max_key)
-{
- DBUG_ENTER("ha_partition::records_in_range");
-
- DBUG_RETURN(estimate_rows(TRUE, inx, min_key, max_key));
-}
-
-
-/*
- Estimate upper bound of number of rows
-
- SYNOPSIS
- estimate_rows_upper_bound()
+/**
+ Estimate upper bound of number of rows.
- RETURN VALUE
- Number of rows
+ @return Number of rows.
*/
ha_rows ha_partition::estimate_rows_upper_bound()
{
+ ha_rows rows, tot_rows= 0;
+ handler **file= m_file;
DBUG_ENTER("ha_partition::estimate_rows_upper_bound");
- DBUG_RETURN(estimate_rows(FALSE, 0, NULL, NULL));
+ do
+ {
+ if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
+ {
+ rows= (*file)->estimate_rows_upper_bound();
+ if (rows == HA_POS_ERROR)
+ DBUG_RETURN(HA_POS_ERROR);
+ tot_rows+= rows;
+ }
+ } while (*(++file));
+ DBUG_RETURN(tot_rows);
}
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index d58bc578180..aa9179f9f69 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -2,7 +2,7 @@
#define HA_PARTITION_INCLUDED
/*
- Copyright (c) 2005, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2005, 2012, Oracle and/or its affiliates.
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
@@ -176,6 +176,12 @@ private:
ha_rows m_bulk_inserted_rows;
/** used for prediction of start_bulk_insert rows */
enum_monotonicity_info m_part_func_monotonicity_info;
+ /** Sorted array of partition ids in descending order of number of rows. */
+ uint32 *m_part_ids_sorted_by_num_of_records;
+ /* Compare function for my_qsort2, for reversed order. */
+ static int compare_number_of_records(ha_partition *me,
+ const uint32 *a,
+ const uint32 *b);
public:
handler *clone(const char *name, MEM_ROOT *mem_root);
virtual void set_part_info(partition_info *part_info)
@@ -196,9 +202,9 @@ 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,
+ ha_partition(handlerton *hton, TABLE_SHARE *share,
+ partition_info *part_info_arg,
+ ha_partition *clone_arg,
MEM_ROOT *clone_mem_root_arg);
~ha_partition();
/*
@@ -538,6 +544,20 @@ public:
virtual int extra(enum ha_extra_function operation);
virtual int extra_opt(enum ha_extra_function operation, ulong cachesize);
virtual int reset(void);
+ /*
+ Do not allow caching of partitioned tables, since we cannot return
+ a callback or engine_data that would work for a generic engine.
+ */
+ virtual my_bool register_query_cache_table(THD *thd, char *table_key,
+ uint key_length,
+ qc_engine_callback
+ *engine_callback,
+ ulonglong *engine_data)
+ {
+ *engine_callback= NULL;
+ *engine_data= 0;
+ return FALSE;
+ }
private:
static const uint NO_CURRENT_PART_ID;
@@ -567,15 +587,9 @@ public:
*/
private:
- /*
- Helper function to get the minimum number of partitions to use for
- the optimizer hints/cost calls.
- */
- void partitions_optimizer_call_preparations(uint *num_used_parts,
- uint *check_min_num,
- uint *first);
- ha_rows estimate_rows(bool is_records_in_range, uint inx,
- key_range *min_key, key_range *max_key);
+ /* Helper functions for optimizer hints. */
+ ha_rows min_rows_for_estimate();
+ uint get_biggest_used_partition(uint *part_index);
public:
/*
diff --git a/sql/item.cc b/sql/item.cc
index 4fdec8eaca0..f9032c79c12 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -3575,7 +3575,7 @@ String *Item_param::val_str(String* str)
that binary log contains wrong statement
*/
-const String *Item_param::query_val_str(String* str) const
+const String *Item_param::query_val_str(THD *thd, String* str) const
{
switch (state) {
case INT_VALUE:
@@ -3613,7 +3613,8 @@ const String *Item_param::query_val_str(String* str) const
case LONG_DATA_VALUE:
{
str->length(0);
- append_query_string(value.cs_info.character_set_client, &str_value, str);
+ append_query_string(thd, value.cs_info.character_set_client, &str_value,
+ str);
break;
}
case NULL_VALUE:
@@ -3746,7 +3747,7 @@ void Item_param::print(String *str, enum_query_type query_type)
char buffer[STRING_BUFFER_USUAL_SIZE];
String tmp(buffer, sizeof(buffer), &my_charset_bin);
const String *res;
- res= query_val_str(&tmp);
+ res= query_val_str(current_thd, &tmp);
str->append(*res);
}
}
diff --git a/sql/item.h b/sql/item.h
index a28cc402d2d..b3801e6c488 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -2260,7 +2260,7 @@ public:
*/
void (*set_param_func)(Item_param *param, uchar **pos, ulong len);
- const String *query_val_str(String *str) const;
+ const String *query_val_str(THD *thd, String *str) const;
bool convert_str_value(THD *thd);
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 38dbc2901c6..5558b77d67d 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -249,7 +249,7 @@ public:
Item_in_optimizer(Item *a, Item_in_subselect *b):
Item_bool_func(a, reinterpret_cast<Item *>(b)), cache(0), expr_cache(0),
save_cache(0), result_for_null_param(UNKNOWN)
- {}
+ { with_subselect= true; }
bool fix_fields(THD *, Item **);
bool fix_left(THD *thd, Item **ref);
bool is_null();
diff --git a/sql/log.cc b/sql/log.cc
index 577297fa1a4..e80a2aaec51 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -3709,8 +3709,6 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
mysql_mutex_lock(&rli->log_space_lock);
rli->relay_log.purge_logs(to_purge_if_included, included,
0, 0, &rli->log_space_total);
- // Tell the I/O thread to take the relay_log_space_limit into account
- rli->ignore_log_space_limit= 0;
mysql_mutex_unlock(&rli->log_space_lock);
/*
diff --git a/sql/log_event.cc b/sql/log_event.cc
index cec0785a088..f8fdbae6214 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -629,7 +629,7 @@ char *str_to_hex(char *to, const char *from, uint len)
*/
int
-append_query_string(CHARSET_INFO *csinfo,
+append_query_string(THD *thd, CHARSET_INFO *csinfo,
String const *from, String *to)
{
char *beg, *ptr;
@@ -644,9 +644,26 @@ append_query_string(CHARSET_INFO *csinfo,
else
{
*ptr++= '\'';
- ptr+= escape_string_for_mysql(csinfo, ptr, 0,
- from->ptr(), from->length());
- *ptr++='\'';
+ if (!(thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES))
+ {
+ ptr+= escape_string_for_mysql(csinfo, ptr, 0,
+ from->ptr(), from->length());
+ }
+ else
+ {
+ const char *frm_str= from->ptr();
+
+ for (; frm_str < (from->ptr() + from->length()); frm_str++)
+ {
+ /* Using '' way to represent "'" */
+ if (*frm_str == '\'')
+ *ptr++= *frm_str;
+
+ *ptr++= *frm_str;
+ }
+ }
+
+ *ptr++= '\'';
}
to->length(orig_len + ptr - beg);
return 0;
diff --git a/sql/log_event.h b/sql/log_event.h
index 7a153a2d08e..78dccb3cac1 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -4260,7 +4260,7 @@ private:
};
#endif
-int append_query_string(CHARSET_INFO *csinfo,
+int append_query_string(THD *thd, CHARSET_INFO *csinfo,
String const *from, String *to);
bool rpl_get_position_info(const char **log_file_name, ulonglong *log_pos,
diff --git a/sql/mem_root_array.h b/sql/mem_root_array.h
new file mode 100644
index 00000000000..5ce4dcb584d
--- /dev/null
+++ b/sql/mem_root_array.h
@@ -0,0 +1,175 @@
+/* Copyright (c) 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 distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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 */
+
+
+#ifndef MEM_ROOT_ARRAY_INCLUDED
+#define MEM_ROOT_ARRAY_INCLUDED
+
+#include <my_alloc.h>
+
+/**
+ A typesafe replacement for DYNAMIC_ARRAY.
+ We use MEM_ROOT for allocating storage, rather than the C++ heap.
+ The interface is chosen to be similar to std::vector.
+
+ @remark
+ Unlike DYNAMIC_ARRAY, elements are properly copied
+ (rather than memcpy()d) if the underlying array needs to be expanded.
+
+ @remark
+ Depending on has_trivial_destructor, we destroy objects which are
+ removed from the array (including when the array object itself is destroyed).
+
+ @remark
+ Note that MEM_ROOT has no facility for reusing free space,
+ so don't use this if multiple re-expansions are likely to happen.
+
+ @param Element_type The type of the elements of the container.
+ Elements must be copyable.
+ @param has_trivial_destructor If true, we don't destroy elements.
+ We could have used type traits to determine this.
+ __has_trivial_destructor is supported by some (but not all)
+ compilers we use.
+*/
+template<typename Element_type, bool has_trivial_destructor>
+class Mem_root_array
+{
+public:
+ Mem_root_array(MEM_ROOT *root)
+ : m_root(root), m_array(NULL), m_size(0), m_capacity(0)
+ {
+ DBUG_ASSERT(m_root != NULL);
+ }
+
+ ~Mem_root_array()
+ {
+ clear();
+ }
+
+ Element_type &at(size_t n)
+ {
+ DBUG_ASSERT(n < size());
+ return m_array[n];
+ }
+
+ const Element_type &at(size_t n) const
+ {
+ DBUG_ASSERT(n < size());
+ return m_array[n];
+ }
+
+ // Returns a pointer to the first element in the array.
+ Element_type *begin() { return &m_array[0]; }
+
+ // Returns a pointer to the past-the-end element in the array.
+ Element_type *end() { return &m_array[size()]; }
+
+ // Erases all of the elements.
+ void clear()
+ {
+ if (!empty())
+ chop(0);
+ }
+
+ /*
+ Chops the tail off the array, erasing all tail elements.
+ @param pos Index of first element to erase.
+ */
+ void chop(const size_t pos)
+ {
+ DBUG_ASSERT(pos < m_size);
+ if (!has_trivial_destructor)
+ {
+ for (size_t ix= pos; ix < m_size; ++ix)
+ {
+ Element_type *p= &m_array[ix];
+ p->~Element_type(); // Destroy discarded element.
+ }
+ }
+ m_size= pos;
+ }
+
+ /*
+ Reserves space for array elements.
+ Copies over existing elements, in case we are re-expanding the array.
+
+ @param n number of elements.
+ @retval true if out-of-memory, false otherwise.
+ */
+ bool reserve(size_t n)
+ {
+ if (n <= m_capacity)
+ return false;
+
+ void *mem= alloc_root(m_root, n * element_size());
+ if (!mem)
+ return true;
+ Element_type *array= static_cast<Element_type*>(mem);
+
+ // Copy all the existing elements into the new array.
+ for (size_t ix= 0; ix < m_size; ++ix)
+ {
+ Element_type *new_p= &array[ix];
+ Element_type *old_p= &m_array[ix];
+ new (new_p) Element_type(*old_p); // Copy into new location.
+ if (!has_trivial_destructor)
+ old_p->~Element_type(); // Destroy the old element.
+ }
+
+ // Forget the old array.
+ m_array= array;
+ m_capacity= n;
+ return false;
+ }
+
+ /*
+ Adds a new element at the end of the array, after its current last
+ element. The content of this new element is initialized to a copy of
+ the input argument.
+
+ @param element Object to copy.
+ @retval true if out-of-memory, false otherwise.
+ */
+ bool push_back(const Element_type &element)
+ {
+ const size_t min_capacity= 20;
+ const size_t expansion_factor= 2;
+ if (0 == m_capacity && reserve(min_capacity))
+ return true;
+ if (m_size == m_capacity && reserve(m_capacity * expansion_factor))
+ return true;
+ Element_type *p= &m_array[m_size++];
+ new (p) Element_type(element);
+ return false;
+ }
+
+ size_t capacity() const { return m_capacity; }
+ size_t element_size() const { return sizeof(Element_type); }
+ bool empty() const { return size() == 0; }
+ size_t size() const { return m_size; }
+
+private:
+ MEM_ROOT *const m_root;
+ Element_type *m_array;
+ size_t m_size;
+ size_t m_capacity;
+
+ // Not (yet) implemented.
+ Mem_root_array(const Mem_root_array&);
+ Mem_root_array &operator=(const Mem_root_array&);
+};
+
+
+#endif // MEM_ROOT_ARRAY_INCLUDED
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 3233ffdc5d3..d6db9a4e230 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -676,7 +676,7 @@ int mysqld_server_started= 0;
File_parser_dummy_hook file_parser_dummy_hook;
/* replication parameters, if master_host is not NULL, we are a slave */
-uint report_port= MYSQL_PORT;
+uint report_port= 0;
ulong master_retry_count=0;
char *master_info_file;
char *relay_log_info_file, *report_user, *report_password, *report_host;
@@ -2229,6 +2229,11 @@ static void network_init(void)
set_ports();
+ if (report_port == 0)
+ {
+ report_port= mysqld_port;
+ }
+ DBUG_ASSERT(report_port != 0);
if (!opt_disable_networking && !opt_bootstrap)
{
if (mysqld_port)
@@ -2746,10 +2751,6 @@ static void check_data_home(const char *path)
#endif /* __WIN__ */
-#ifdef HAVE_LINUXTHREADS
-#define UNSAFE_DEFAULT_LINUX_THREADS 200
-#endif
-
#if BACKTRACE_DEMANGLE
#include <cxxabi.h>
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index 9c9bee805eb..958002561bc 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -203,6 +203,13 @@ public:
bool ignore_log_space_limit;
/*
+ Used by the SQL thread to instructs the IO thread to rotate
+ the logs when the SQL thread needs to purge to release some
+ disk space.
+ */
+ bool sql_force_rotate_relay;
+
+ /*
When it commits, InnoDB internally stores the master log position it has
processed so far; the position to store is the one of the end of the
committing event (the COMMIT query event, or the event if in autocommit
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 468f89dff06..8510eb1f1ac 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -6493,6 +6493,10 @@ ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC
eng "CREATE TABLE... SELECT... on a table with an auto-increment column is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are inserted. This order cannot be predicted and may differ on master and the slave."
#
+# End of 5.5 error messages.
+#
+
+#
# MariaDB error messages section starts here
#
@@ -6569,4 +6573,3 @@ ER_STORED_FUNCTION_PREVENTS_SWITCH_SKIP_REPLICATION
eng "Cannot modify @@session.skip_replication inside a stored function or trigger"
ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT
eng "Query execution was interrupted. The query examined at least %llu rows, which exceeds LIMIT ROWS EXAMINED (%llu). The query result may be incomplete."
-
diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc
index 61b67c9d229..3e194805dbc 100644
--- a/sql/signal_handler.cc
+++ b/sql/signal_handler.cc
@@ -139,6 +139,7 @@ extern "C" sig_handler handle_fatal_signal(int sig)
"Hope that's ok; if not, decrease some variables in the equation.\n\n");
#if defined(HAVE_LINUXTHREADS)
+#define UNSAFE_DEFAULT_LINUX_THREADS 200
if (sizeof(char*) == 4 && thread_count > UNSAFE_DEFAULT_LINUX_THREADS)
{
my_safe_printf_stderr(
diff --git a/sql/slave.cc b/sql/slave.cc
index ac7c9cd8c9e..9f593b3075e 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -1788,6 +1788,54 @@ Waiting for the slave SQL thread to free enough relay log space");
!(slave_killed=io_slave_killed(thd,mi)) &&
!rli->ignore_log_space_limit)
mysql_cond_wait(&rli->log_space_cond, &rli->log_space_lock);
+
+ /*
+ Makes the IO thread read only one event at a time
+ until the SQL thread is able to purge the relay
+ logs, freeing some space.
+
+ Therefore, once the SQL thread processes this next
+ event, it goes to sleep (no more events in the queue),
+ sets ignore_log_space_limit=true and wakes the IO thread.
+ However, this event may have been enough already for
+ the SQL thread to purge some log files, freeing
+ rli->log_space_total .
+
+ This guarantees that the SQL and IO thread move
+ forward only one event at a time (to avoid deadlocks),
+ when the relay space limit is reached. It also
+ guarantees that when the SQL thread is prepared to
+ rotate (to be able to purge some logs), the IO thread
+ will know about it and will rotate.
+
+ NOTE: The ignore_log_space_limit is only set when the SQL
+ thread sleeps waiting for events.
+
+ */
+ if (rli->ignore_log_space_limit)
+ {
+#ifndef DBUG_OFF
+ {
+ char llbuf1[22], llbuf2[22];
+ DBUG_PRINT("info", ("log_space_limit=%s "
+ "log_space_total=%s "
+ "ignore_log_space_limit=%d "
+ "sql_force_rotate_relay=%d",
+ llstr(rli->log_space_limit,llbuf1),
+ llstr(rli->log_space_total,llbuf2),
+ (int) rli->ignore_log_space_limit,
+ (int) rli->sql_force_rotate_relay));
+ }
+#endif
+ if (rli->sql_force_rotate_relay)
+ {
+ rotate_relay_log(rli->mi);
+ rli->sql_force_rotate_relay= false;
+ }
+
+ rli->ignore_log_space_limit= false;
+ }
+
thd->exit_cond(save_proc_info);
DBUG_RETURN(slave_killed);
}
@@ -5058,19 +5106,45 @@ static Log_event* next_event(Relay_log_info* rli)
constraint, because we do not want the I/O thread to block because of
space (it's ok if it blocks for any other reason (e.g. because the
master does not send anything). Then the I/O thread stops waiting
- and reads more events.
- The SQL thread decides when the I/O thread should take log_space_limit
- into account again : ignore_log_space_limit is reset to 0
- in purge_first_log (when the SQL thread purges the just-read relay
- log), and also when the SQL thread starts. We should also reset
- ignore_log_space_limit to 0 when the user does RESET SLAVE, but in
- fact, no need as RESET SLAVE requires that the slave
+ and reads one more event and starts honoring log_space_limit again.
+
+ If the SQL thread needs more events to be able to rotate the log (it
+ might need to finish the current group first), then it can ask for one
+ more at a time. Thus we don't outgrow the relay log indefinitely,
+ but rather in a controlled manner, until the next rotate.
+
+ When the SQL thread starts it sets ignore_log_space_limit to false.
+ We should also reset ignore_log_space_limit to 0 when the user does
+ RESET SLAVE, but in fact, no need as RESET SLAVE requires that the slave
be stopped, and the SQL thread sets ignore_log_space_limit to 0 when
it stops.
*/
mysql_mutex_lock(&rli->log_space_lock);
- // prevent the I/O thread from blocking next times
- rli->ignore_log_space_limit= 1;
+
+ /*
+ If we have reached the limit of the relay space and we
+ are going to sleep, waiting for more events:
+
+ 1. If outside a group, SQL thread asks the IO thread
+ to force a rotation so that the SQL thread purges
+ logs next time it processes an event (thus space is
+ freed).
+
+ 2. If in a group, SQL thread asks the IO thread to
+ ignore the limit and queues yet one more event
+ so that the SQL thread finishes the group and
+ is are able to rotate and purge sometime soon.
+ */
+ if (rli->log_space_limit &&
+ rli->log_space_limit < rli->log_space_total)
+ {
+ /* force rotation if not in an unfinished group */
+ rli->sql_force_rotate_relay= !rli->is_in_group();
+
+ /* ask for one more event */
+ rli->ignore_log_space_limit= true;
+ }
+
/*
If the I/O thread is blocked, unblock it. Ok to broadcast
after unlock, because the mutex is only destroyed in
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 237ad589e4a..9a356cb3321 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -158,7 +158,7 @@ sp_get_item_value(THD *thd, Item *item, String *str)
buf.append(result->charset()->csname);
if (cs->escape_with_backslash_is_dangerous)
buf.append(' ');
- append_query_string(cs, result, &buf);
+ append_query_string(thd, cs, result, &buf);
buf.append(" COLLATE '");
buf.append(item->collation.collation->name);
buf.append('\'');
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index ae0832deb84..246f03a7754 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -471,6 +471,8 @@ void lex_start(THD *thd)
lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
lex->select_lex.init_order();
lex->select_lex.group_list.empty();
+ if (lex->select_lex.group_list_ptrs)
+ lex->select_lex.group_list_ptrs->clear();
lex->describe= 0;
lex->subqueries= FALSE;
lex->context_analysis_only= 0;
@@ -1881,6 +1883,8 @@ void st_select_lex::init_select()
sj_nests.empty();
sj_subselects.empty();
group_list.empty();
+ if (group_list_ptrs)
+ group_list_ptrs->clear();
type= db= 0;
having= 0;
table_join_options= 0;
@@ -3288,6 +3292,8 @@ static void fix_prepare_info_in_table_list(THD *thd, TABLE_LIST *tbl)
The passed WHERE and HAVING are to be saved for the future executions.
This function saves it, and returns a copy which can be thrashed during
this execution of the statement. By saving/thrashing here we mean only
+ We also save the chain of ORDER::next in group_list, in case
+ the list is modified by remove_const().
AND/OR trees.
The function also calls fix_prepare_info_in_table_list that saves all
ON expressions.
@@ -3299,6 +3305,19 @@ void st_select_lex::fix_prepare_information(THD *thd, Item **conds,
if (!thd->stmt_arena->is_conventional() && first_execution)
{
first_execution= 0;
+ if (group_list.first)
+ {
+ if (!group_list_ptrs)
+ {
+ void *mem= thd->stmt_arena->alloc(sizeof(Group_list_ptrs));
+ group_list_ptrs= new (mem) Group_list_ptrs(thd->stmt_arena->mem_root);
+ }
+ group_list_ptrs->reserve(group_list.elements);
+ for (ORDER *order= group_list.first; order; order= order->next)
+ {
+ group_list_ptrs->push_back(order);
+ }
+ }
if (*conds)
{
thd->check_and_register_item_tree(&prep_where, conds);
@@ -4187,3 +4206,7 @@ void binlog_unsafe_map_init()
BINLOG_DIRECT_OFF & TRX_CACHE_NOT_EMPTY);
}
#endif
+
+#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
+template class Mem_root_array<ORDER*, true>;
+#endif
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 7e4d76b4240..f1ee6cf22ec 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -24,6 +24,7 @@
#include "sql_trigger.h"
#include "item.h" /* From item_subselect.h: subselect_union_engine */
#include "thr_lock.h" /* thr_lock_type, TL_UNLOCK */
+#include "mem_root_array.h"
/* YACC and LEX Definitions */
@@ -261,6 +262,7 @@ enum enum_drop_mode
#define TL_OPTION_ALIAS 8
typedef List<Item> List_item;
+typedef Mem_root_array<ORDER*, true> Group_list_ptrs;
/* SERVERS CACHE CHANGES */
typedef struct st_lex_server_options
@@ -735,7 +737,16 @@ public:
enum olap_type olap;
/* FROM clause - points to the beginning of the TABLE_LIST::next_local list. */
SQL_I_List<TABLE_LIST> table_list;
- SQL_I_List<ORDER> group_list; /* GROUP BY clause. */
+
+ /*
+ GROUP BY clause.
+ This list may be mutated during optimization (by remove_const()),
+ so for prepared statements, we keep a copy of the ORDER.next pointers in
+ group_list_ptrs, and re-establish the original list before each execution.
+ */
+ SQL_I_List<ORDER> group_list;
+ Group_list_ptrs *group_list_ptrs;
+
List<Item> item_list; /* list of fields & expressions */
List<String> interval_list;
bool is_item_list_lookup;
@@ -941,7 +952,8 @@ public:
bool test_limit();
friend void lex_start(THD *thd);
- st_select_lex() : n_sum_items(0), n_child_sum_items(0) {}
+ st_select_lex() : group_list_ptrs(NULL), n_sum_items(0), n_child_sum_items(0)
+ {}
void make_empty_select()
{
init_query();
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 27bd25680c4..ed437c498e6 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -888,7 +888,7 @@ static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array,
*/
else if (! is_param_long_data_type(param))
DBUG_RETURN(1);
- res= param->query_val_str(&str);
+ res= param->query_val_str(thd, &str);
if (param->convert_str_value(thd))
DBUG_RETURN(1); /* out of memory */
@@ -1062,7 +1062,7 @@ static bool emb_insert_params_with_log(Prepared_statement *stmt,
DBUG_RETURN(1);
}
}
- res= param->query_val_str(&str);
+ res= param->query_val_str(thd, &str);
if (param->convert_str_value(thd))
DBUG_RETURN(1); /* out of memory */
@@ -1208,7 +1208,7 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
setup_one_conversion_function(thd, param, param->param_type);
if (param->set_from_user_var(thd, entry))
DBUG_RETURN(1);
- val= param->query_val_str(&buf);
+ val= param->query_val_str(thd, &buf);
if (param->convert_str_value(thd))
DBUG_RETURN(1); /* out of memory */
@@ -2512,6 +2512,14 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
DBUG_ASSERT(sl->join == 0);
ORDER *order;
/* Fix GROUP list */
+ if (sl->group_list_ptrs && sl->group_list_ptrs->size() > 0)
+ {
+ for (uint ix= 0; ix < sl->group_list_ptrs->size() - 1; ++ix)
+ {
+ order= sl->group_list_ptrs->at(ix);
+ order->next= sl->group_list_ptrs->at(ix+1);
+ }
+ }
for (order= sl->group_list.first; order; order= order->next)
order->item= &order->item_ptr;
/* Fix ORDER list */
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index f780a3b7c18..b5ed327c193 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -5254,7 +5254,8 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table,
(sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0)
{
restore_record(table, s->default_values);
- if (!wild || !wild[0] || !wild_compare(sp_name.c_ptr_safe(), wild, 0))
+ if (!wild || !wild[0] || !wild_case_compare(system_charset_info,
+ sp_name.c_ptr_safe(), wild))
{
int enum_idx= (int) proc_table->field[MYSQL_PROC_FIELD_ACCESS]->val_int();
table->field[3]->store(sp_name.ptr(), sp_name.length(), cs);
@@ -6414,7 +6415,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
DBUG_RETURN(1);
}
- if (!(!wild || !wild[0] || !wild_compare(et.name.str, wild, 0)))
+ if (!(!wild || !wild[0] || !wild_case_compare(scs, et.name.str, wild)))
DBUG_RETURN(0);
/*
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 0db69af094f..b6a7cf010ed 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -445,6 +445,13 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
view= lex->unlink_first_table(&link_to_local);
+ if (check_db_dir_existence(view->db))
+ {
+ my_error(ER_BAD_DB_ERROR, MYF(0), view->db);
+ res= TRUE;
+ goto err;
+ }
+
if (mode == VIEW_ALTER && fill_defined_view_parts(thd, view))
{
res= TRUE;
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 01afb477942..ddce87b4a42 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -2954,7 +2954,7 @@ static Sys_var_uint Sys_repl_report_port(
"port or if you have a special tunnel from the master or other clients "
"to the slave. If not sure, leave this option unset",
READ_ONLY GLOBAL_VAR(report_port), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(0, UINT_MAX), DEFAULT(MYSQL_PORT), BLOCK_SIZE(1));
+ VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1));
#endif
static Sys_var_mybool Sys_keep_files_on_create(
diff --git a/sql/transaction.cc b/sql/transaction.cc
index 94a32200274..3359decbcd5 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -390,7 +390,7 @@ bool trans_savepoint(THD *thd, LEX_STRING name)
DBUG_RETURN(FALSE);
enum xa_states xa_state= thd->transaction.xid_state.xa_state;
- if (xa_state != XA_NOTR)
+ if (xa_state != XA_NOTR && xa_state != XA_ACTIVE)
{
my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
DBUG_RETURN(TRUE);