summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorSergei Golubchik <sergii@pisem.net>2013-01-29 15:10:47 +0100
committerSergei Golubchik <sergii@pisem.net>2013-01-29 15:10:47 +0100
commit0af4b6c6ee2b8a61823478c0a56ebdfa52cae3cc (patch)
tree7b24eb150b9cca718c88edaabbfc6c8bb16fd015 /sql
parentcf20de000bdff07a34a373079991d24837423896 (diff)
parent52fbe44fbbe60ecaba6453884ec1ad32755d7a04 (diff)
downloadmariadb-git-0af4b6c6ee2b8a61823478c0a56ebdfa52cae3cc.tar.gz
5.5 merge
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt4
-rw-r--r--sql/event_db_repository.cc10
-rw-r--r--sql/field.cc15
-rw-r--r--sql/field.h1
-rw-r--r--sql/filesort.cc29
-rw-r--r--sql/ha_ndbcluster_binlog.cc1
-rw-r--r--sql/ha_partition.cc216
-rw-r--r--sql/ha_partition.h4
-rw-r--r--sql/handler.cc66
-rw-r--r--sql/handler.h9
-rw-r--r--sql/hostname.cc56
-rw-r--r--sql/item.cc20
-rw-r--r--sql/item.h43
-rw-r--r--sql/item_cmpfunc.cc24
-rw-r--r--sql/item_cmpfunc.h28
-rw-r--r--sql/item_func.cc43
-rw-r--r--sql/item_func.h73
-rw-r--r--sql/item_geofunc.cc4
-rw-r--r--sql/item_geofunc.h30
-rw-r--r--sql/item_row.cc5
-rw-r--r--sql/item_strfunc.cc38
-rw-r--r--sql/item_strfunc.h29
-rw-r--r--sql/item_subselect.cc31
-rw-r--r--sql/item_sum.cc56
-rw-r--r--sql/item_sum.h2
-rw-r--r--sql/item_timefunc.cc21
-rw-r--r--sql/item_timefunc.h42
-rw-r--r--sql/item_xmlfunc.cc1
-rw-r--r--sql/item_xmlfunc.h8
-rw-r--r--sql/log.cc59
-rw-r--r--sql/log_event.cc88
-rw-r--r--sql/log_event.h6
-rw-r--r--sql/log_event_old.cc7
-rw-r--r--sql/mdl.cc12
-rw-r--r--sql/mysqld.cc6
-rw-r--r--sql/mysqld.h2
-rw-r--r--sql/opt_range.cc110
-rw-r--r--sql/opt_range.h2
-rw-r--r--sql/opt_range_mrr.cc1
-rw-r--r--sql/opt_subselect.cc4
-rw-r--r--sql/records.cc10
-rw-r--r--sql/rpl_mi.h6
-rw-r--r--sql/rpl_rli.cc3
-rw-r--r--sql/sp.cc16
-rw-r--r--sql/sp_head.cc50
-rw-r--r--sql/sp_rcontext.cc13
-rw-r--r--sql/sp_rcontext.h45
-rw-r--r--sql/sql_acl.cc240
-rw-r--r--sql/sql_audit.cc45
-rw-r--r--sql/sql_audit.h2
-rw-r--r--sql/sql_base.cc7
-rw-r--r--sql/sql_class.cc71
-rw-r--r--sql/sql_class.h20
-rw-r--r--sql/sql_connect.cc13
-rw-r--r--sql/sql_const.h4
-rw-r--r--sql/sql_db.cc5
-rw-r--r--sql/sql_handler.cc29
-rw-r--r--sql/sql_help.cc10
-rw-r--r--sql/sql_insert.cc50
-rw-r--r--sql/sql_lex.cc2
-rw-r--r--sql/sql_list.h11
-rw-r--r--sql/sql_load.cc4
-rw-r--r--sql/sql_parse.cc46
-rw-r--r--sql/sql_plugin.cc42
-rw-r--r--sql/sql_plugin_services.h15
-rw-r--r--sql/sql_priv.h35
-rw-r--r--sql/sql_profile.cc29
-rw-r--r--sql/sql_profile.h6
-rw-r--r--sql/sql_repl.cc42
-rw-r--r--sql/sql_repl.h4
-rw-r--r--sql/sql_select.cc221
-rw-r--r--sql/sql_show.cc30
-rw-r--r--sql/sql_signal.cc12
-rw-r--r--sql/sql_statistics.cc2
-rw-r--r--sql/sql_time.cc4
-rw-r--r--sql/sql_truncate.cc3
-rw-r--r--sql/sql_union.cc2
-rw-r--r--sql/sql_update.cc25
-rw-r--r--sql/sql_yacc.yy8
-rw-r--r--sql/table.cc50
-rw-r--r--sql/table.h3
-rw-r--r--sql/tztime.cc32
-rw-r--r--sql/uniques.cc157
83 files changed, 1825 insertions, 805 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 0be452a5f86..4aa7b8f882e 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -298,7 +298,6 @@ IF(WIN32 AND MYSQLD_EXECUTABLE)
COMMAND ${CMAKE_COMMAND}
${CONFIG_PARAM} -P ${CMAKE_CURRENT_BINARY_DIR}/create_initial_db.cmake
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data
- COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/initdb.dep
DEPENDS mysqld
)
ADD_CUSTOM_TARGET(initial_database
@@ -325,11 +324,12 @@ IF(WIN32)
ADD_CUSTOM_COMMAND(OUTPUT
${my_bootstrap_sql}
COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_SOURCE_DIR}/scripts
- cmd /c copy mysql_system_tables.sql+mysql_system_tables_data.sql+fill_help_tables.sql ${native_outfile}
+ cmd /c copy mysql_system_tables.sql+mysql_system_tables_data.sql+fill_help_tables.sql+mysql_performance_tables.sql ${native_outfile}
DEPENDS
${CMAKE_SOURCE_DIR}/scripts/mysql_system_tables.sql
${CMAKE_SOURCE_DIR}/scripts/mysql_system_tables_data.sql
${CMAKE_SOURCE_DIR}/scripts/fill_help_tables.sql
+ ${CMAKE_SOURCE_DIR}/scripts/mysql_performance_tables.sql
)
ADD_CUSTOM_COMMAND(
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
index fb0887e25d1..b7e82d8b4b9 100644
--- a/sql/event_db_repository.cc
+++ b/sql/event_db_repository.cc
@@ -409,7 +409,6 @@ Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
TABLE *event_table,
const char *db)
{
- int ret=0;
CHARSET_INFO *scs= system_charset_info;
KEY *key_info;
uint key_len;
@@ -419,7 +418,14 @@ Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
DBUG_ENTER("Event_db_repository::index_read_for_db_for_i_s");
DBUG_PRINT("info", ("Using prefix scanning on PK"));
- event_table->file->ha_index_init(0, 1);
+
+ int ret= event_table->file->ha_index_init(0, 1);
+ if (ret)
+ {
+ event_table->file->print_error(ret, MYF(0));
+ DBUG_RETURN(true);
+ }
+
key_info= event_table->key_info;
if (key_info->key_parts == 0 ||
diff --git a/sql/field.cc b/sql/field.cc
index cc911dd80d8..d4468ba3c5b 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1114,6 +1114,21 @@ bool Field::type_can_have_key_part(enum enum_field_types type)
}
+void Field::make_sort_key(uchar *buff,uint length)
+{
+ if (maybe_null())
+ {
+ if (is_null())
+ {
+ bzero(buff, length + 1);
+ return;
+ }
+ *buff++= 1;
+ }
+ sort_string(buff, length);
+}
+
+
/**
Numeric fields base class constructor.
*/
diff --git a/sql/field.h b/sql/field.h
index f450e596e03..e832928b114 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -519,6 +519,7 @@ public:
return bytes;
}
+ void make_sort_key(uchar *buff, uint length);
virtual void make_field(Send_field *);
virtual void sort_string(uchar *buff,uint length)=0;
virtual bool optimize_range(uint idx, uint part);
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 3f735e94a11..f31afc226cc 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -61,7 +61,6 @@ static void make_sortkey(Sort_param *param, uchar *to, uchar *ref_pos);
static void register_used_fields(Sort_param *param);
static bool save_index(Sort_param *param, uint count,
Filesort_info *table_sort);
-static void make_sortkey(Sort_param *param, uchar *to, uchar *ref_pos);
static uint suffix_length(ulong string_length);
static uint sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
bool *multi_byte_charset);
@@ -181,6 +180,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
when index_merge select has finished with it.
*/
table->sort.io_cache= NULL;
+ DBUG_ASSERT(table_sort.record_pointers == NULL);
outfile= table_sort.io_cache;
my_b_clear(&tempfile);
@@ -608,6 +608,8 @@ static ha_rows find_all_keys(Sort_param *param, SQL_SELECT *select,
if (!quick_select)
{
next_pos=(uchar*) 0; /* Find records in sequence */
+ DBUG_EXECUTE_IF("bug14365043_1",
+ DBUG_SET("+d,ha_rnd_init_fail"););
if (file->ha_rnd_init_with_error(1))
DBUG_RETURN(HA_POS_ERROR);
file->extra_opt(HA_EXTRA_CACHE,
@@ -865,21 +867,9 @@ static void make_sortkey(register Sort_param *param,
bool maybe_null=0;
if ((field=sort_field->field))
{ // Field
- if (field->maybe_null())
- {
- if (field->is_null())
- {
- if (sort_field->reverse)
- memset(to, 255, sort_field->length+1);
- else
- memset(to, 0, sort_field->length+1);
- to+= sort_field->length+1;
- continue;
- }
- else
- *to++=1;
- }
- field->sort_string(to, sort_field->length);
+ field->make_sort_key(to, sort_field->length);
+ if ((maybe_null = field->maybe_null()))
+ to++;
}
else
{ // Item
@@ -1039,8 +1029,11 @@ static void make_sortkey(register Sort_param *param,
}
if (sort_field->reverse)
{ /* Revers key */
- if (maybe_null)
- to[-1]= ~to[-1];
+ if (maybe_null && (to[-1]= !to[-1]))
+ {
+ to+= sort_field->length; // don't waste the time reversing all 0's
+ continue;
+ }
length=sort_field->length;
while (length--)
{
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index 2b72ffef9bf..1544678de38 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -1,4 +1,5 @@
/* Copyright (c) 2006, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2012, 2013, Monty Proram Ab.
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
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 9a9f0b537f7..ce3f17aeb92 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -2850,6 +2850,17 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
if (bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE))
DBUG_RETURN(error);
bitmap_clear_all(&m_bulk_insert_started);
+ /*
+ Initialize the bitmap we use to keep track of partitions which returned
+ HA_ERR_KEY_NOT_FOUND from index_read_map.
+ */
+ if (bitmap_init(&m_key_not_found_partitions, NULL, m_tot_parts, FALSE))
+ {
+ bitmap_free(&m_bulk_insert_started);
+ DBUG_RETURN(error);
+ }
+ bitmap_clear_all(&m_key_not_found_partitions);
+ m_key_not_found= false;
/* Initialize the bitmap we use to determine what partitions are used */
if (!m_is_clone_of)
{
@@ -2992,6 +3003,7 @@ err_handler:
(*file)->ha_close();
err_alloc:
bitmap_free(&m_bulk_insert_started);
+ bitmap_free(&m_key_not_found_partitions);
if (!m_is_clone_of)
bitmap_free(&(m_part_info->used_partitions));
@@ -3068,6 +3080,7 @@ int ha_partition::close(void)
DBUG_ASSERT(table->s == table_share);
destroy_record_priority_queue();
bitmap_free(&m_bulk_insert_started);
+ bitmap_free(&m_key_not_found_partitions);
if (!m_is_clone_of)
bitmap_free(&(m_part_info->used_partitions));
file= m_file;
@@ -4590,21 +4603,24 @@ int ha_partition::index_read_map(uchar *buf, const uchar *key,
}
-/*
+/**
Common routine for a number of index_read variants
- SYNOPSIS
- ha_partition::common_index_read()
- buf Buffer where the record should be returned
- have_start_key TRUE <=> the left endpoint is available, i.e.
- we're in index_read call or in read_range_first
- call and the range has left endpoint
-
- FALSE <=> there is no left endpoint (we're in
- read_range_first() call and the range has no left
- endpoint)
+ @param buf Buffer where the record should be returned.
+ @param have_start_key TRUE <=> the left endpoint is available, i.e.
+ we're in index_read call or in read_range_first
+ call and the range has left endpoint.
+ FALSE <=> there is no left endpoint (we're in
+ read_range_first() call and the range has no left
+ endpoint).
- DESCRIPTION
+ @return Operation status
+ @retval 0 OK
+ @retval HA_ERR_END_OF_FILE Whole index scanned, without finding the record.
+ @retval HA_ERR_KEY_NOT_FOUND Record not found, but index cursor positioned.
+ @retval other error code.
+
+ @details
Start scanning the range (when invoked from read_range_first()) or doing
an index lookup (when invoked from index_read_XXX):
- If possible, perform partition selection
@@ -4614,10 +4630,6 @@ int ha_partition::index_read_map(uchar *buf, const uchar *key,
handle_unordered_scan_next_partition)
YES: Fill the priority queue and get the record that is the first in
the ordering
-
- RETURN
- 0 OK
- other HA_ERR_END_OF_FILE or other error code.
*/
int ha_partition::common_index_read(uchar *buf, bool have_start_key)
@@ -4627,14 +4639,16 @@ int ha_partition::common_index_read(uchar *buf, bool have_start_key)
bool reverse_order= FALSE;
DBUG_ENTER("ha_partition::common_index_read");
- DBUG_PRINT("info", ("m_ordered: %u have_start_key: %u",
- m_ordered, have_start_key));
+ DBUG_PRINT("info", ("m_ordered %u m_ordered_scan_ong %u",
+ m_ordered, m_ordered_scan_ongoing));
if (have_start_key)
{
m_start_key.length= key_len= calculate_key_len(table, active_index,
m_start_key.key,
m_start_key.keypart_map);
+ DBUG_PRINT("info", ("have_start_key map %lu find_flag %u len %u",
+ m_start_key.keypart_map, m_start_key.flag, key_len));
DBUG_ASSERT(key_len);
}
if ((error= partition_scan_set_up(buf, have_start_key)))
@@ -4652,24 +4666,16 @@ int ha_partition::common_index_read(uchar *buf, bool have_start_key)
}
DBUG_PRINT("info", ("m_ordered %u m_o_scan_ong %u have_start_key %u",
m_ordered, m_ordered_scan_ongoing, have_start_key));
- if (!m_ordered_scan_ongoing ||
- (have_start_key && m_start_key.flag == HA_READ_KEY_EXACT &&
- !m_pkey_is_clustered &&
- key_len >= m_curr_key_info[0]->key_length))
+ if (!m_ordered_scan_ongoing)
{
/*
- We use unordered index scan either when read_range is used and flag
- is set to not use ordered or when an exact key is used and in this
- case all records will be sorted equal and thus the sort order of the
- resulting records doesn't matter.
+ We use unordered index scan when read_range is used and flag
+ is set to not use ordered.
We also use an unordered index scan when the number of partitions to
scan is only one.
The unordered index scan will use the partition set created.
- Need to set unordered scan ongoing since we can come here even when
- it isn't set.
*/
DBUG_PRINT("info", ("doing unordered scan"));
- m_ordered_scan_ongoing= FALSE;
error= handle_unordered_scan_next_partition(buf);
}
else
@@ -4850,6 +4856,8 @@ int ha_partition::index_next(uchar * buf)
TODO(low priority):
If we want partition to work with the HANDLER commands, we
must be able to do index_last() -> index_prev() -> index_next()
+ and if direction changes, we must step back those partitions in
+ the record queue so we don't return a value from the wrong direction.
*/
DBUG_ASSERT(m_index_scan_type != partition_index_last);
if (!m_ordered_scan_ongoing)
@@ -5103,10 +5111,18 @@ int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag)
int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same)
{
- handler *file= m_file[m_part_spec.start_part];
+ handler *file;
int error;
DBUG_ENTER("ha_partition::handle_unordered_next");
+ if (m_part_spec.start_part >= m_tot_parts)
+ {
+ /* Should never happen! */
+ DBUG_ASSERT(0);
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ file= m_file[m_part_spec.start_part];
+
/*
We should consider if this should be split into three functions as
partition_read_range is_next_same are always local constants
@@ -5167,6 +5183,7 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same)
int ha_partition::handle_unordered_scan_next_partition(uchar * buf)
{
uint i;
+ int saved_error= HA_ERR_END_OF_FILE;
DBUG_ENTER("ha_partition::handle_unordered_scan_next_partition");
for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
@@ -5217,26 +5234,33 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf)
}
if ((error != HA_ERR_END_OF_FILE) && (error != HA_ERR_KEY_NOT_FOUND))
DBUG_RETURN(error);
- DBUG_PRINT("info", ("HA_ERR_END_OF_FILE on partition %d", i));
+
+ /*
+ If HA_ERR_KEY_NOT_FOUND, we must return that error instead of
+ HA_ERR_END_OF_FILE, to be able to continue search.
+ */
+ if (saved_error != HA_ERR_KEY_NOT_FOUND)
+ saved_error= error;
+ DBUG_PRINT("info", ("END_OF_FILE/KEY_NOT_FOUND on partition %d", i));
}
- m_part_spec.start_part= NO_CURRENT_PART_ID;
- DBUG_RETURN(HA_ERR_END_OF_FILE);
+ if (saved_error == HA_ERR_END_OF_FILE)
+ m_part_spec.start_part= NO_CURRENT_PART_ID;
+ DBUG_RETURN(saved_error);
}
-/*
- Common routine to start index scan with ordered results
+/**
+ Common routine to start index scan with ordered results.
- SYNOPSIS
- handle_ordered_index_scan()
- out:buf Read row in MySQL Row Format
+ @param[out] buf Read row in MySQL Row Format
- RETURN VALUE
- HA_ERR_END_OF_FILE End of scan
- 0 Success
- other Error code
+ @return Operation status
+ @retval HA_ERR_END_OF_FILE End of scan
+ @retval HA_ERR_KEY_NOT_FOUNE End of scan
+ @retval 0 Success
+ @retval other Error code
- DESCRIPTION
+ @details
This part contains the logic to handle index scans that require ordered
output. This includes all except those started by read_range_first with
the flag ordered set to FALSE. Thus most direct index_read and all
@@ -5258,8 +5282,14 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
uint j= queue_first_element(&m_queue);
bool found= FALSE;
uchar *part_rec_buf_ptr= m_ordered_rec_buffer;
+ int saved_error= HA_ERR_END_OF_FILE;
DBUG_ENTER("ha_partition::handle_ordered_index_scan");
+ if (m_key_not_found)
+ {
+ m_key_not_found= false;
+ bitmap_clear_all(&m_key_not_found_partitions);
+ }
m_top_entry= NO_CURRENT_PART_ID;
queue_remove_all(&m_queue);
@@ -5321,6 +5351,13 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
{
DBUG_RETURN(error);
}
+ else if (error == HA_ERR_KEY_NOT_FOUND)
+ {
+ DBUG_PRINT("info", ("HA_ERR_KEY_NOT_FOUND from partition %u", i));
+ bitmap_set_bit(&m_key_not_found_partitions, i);
+ m_key_not_found= true;
+ saved_error= error;
+ }
part_rec_buf_ptr+= m_rec_length + PARTITION_BYTES_IN_POS;
}
if (found)
@@ -5338,7 +5375,7 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
DBUG_PRINT("info", ("Record returned from partition %d", m_top_entry));
DBUG_RETURN(0);
}
- DBUG_RETURN(HA_ERR_END_OF_FILE);
+ DBUG_RETURN(saved_error);
}
@@ -5366,6 +5403,59 @@ void ha_partition::return_top_record(uchar *buf)
}
+/**
+ Add index_next/prev from partitions without exact match.
+
+ If there where any partitions that returned HA_ERR_KEY_NOT_FOUND when
+ ha_index_read_map was done, those partitions must be included in the
+ following index_next/prev call.
+*/
+
+int ha_partition::handle_ordered_index_scan_key_not_found()
+{
+ int error;
+ uint i;
+ uchar *part_buf= m_ordered_rec_buffer;
+ uchar *curr_rec_buf= NULL;
+ DBUG_ENTER("ha_partition::handle_ordered_index_scan_key_not_found");
+ DBUG_ASSERT(m_key_not_found);
+ /*
+ Loop over all used partitions to get the correct offset
+ into m_ordered_rec_buffer.
+ */
+ for (i= 0; i < m_tot_parts; i++)
+ {
+ if (!bitmap_is_set(&m_part_info->used_partitions, i))
+ continue;
+
+ if (bitmap_is_set(&m_key_not_found_partitions, i))
+ {
+ /*
+ This partition is used and did return HA_ERR_KEY_NOT_FOUND
+ in index_read_map.
+ */
+ curr_rec_buf= part_buf + PARTITION_BYTES_IN_POS;
+ error= m_file[i]->index_next(curr_rec_buf);
+ /* HA_ERR_KEY_NOT_FOUND is not allowed from index_next! */
+ DBUG_ASSERT(error != HA_ERR_KEY_NOT_FOUND);
+ if (!error)
+ queue_insert(&m_queue, part_buf);
+ else if (error != HA_ERR_END_OF_FILE && error != HA_ERR_KEY_NOT_FOUND)
+ DBUG_RETURN(error);
+ }
+ part_buf+= m_rec_length + PARTITION_BYTES_IN_POS;
+ }
+ DBUG_ASSERT(curr_rec_buf);
+ bitmap_clear_all(&m_key_not_found_partitions);
+ m_key_not_found= false;
+
+ /* Update m_top_entry, which may have changed. */
+ uchar *key_buffer= queue_top(&m_queue);
+ m_top_entry= uint2korr(key_buffer);
+ DBUG_RETURN(0);
+}
+
+
/*
Common routine to handle index_next with ordered results
@@ -5385,9 +5475,45 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same)
int error;
uint part_id= m_top_entry;
uchar *rec_buf= queue_top(&m_queue) + PARTITION_BYTES_IN_POS;
- handler *file= m_file[part_id];
+ handler *file;
DBUG_ENTER("ha_partition::handle_ordered_next");
+ if (m_key_not_found)
+ {
+ if (is_next_same)
+ {
+ /* Only rows which match the key. */
+ m_key_not_found= false;
+ bitmap_clear_all(&m_key_not_found_partitions);
+ }
+ else
+ {
+ /* There are partitions not included in the index record queue. */
+ uint old_elements= m_queue.elements;
+ if ((error= handle_ordered_index_scan_key_not_found()))
+ DBUG_RETURN(error);
+ /*
+ If the queue top changed, i.e. one of the partitions that gave
+ HA_ERR_KEY_NOT_FOUND in index_read_map found the next record,
+ return it.
+ Otherwise replace the old with a call to index_next (fall through).
+ */
+ if (old_elements != m_queue.elements && part_id != m_top_entry)
+ {
+ return_top_record(buf);
+ DBUG_RETURN(0);
+ }
+ }
+ }
+ if (part_id >= m_tot_parts)
+ {
+ /* This should never happen! */
+ DBUG_ASSERT(0);
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+
+ file= m_file[part_id];
+
if (m_index_scan_type == partition_read_range)
{
error= file->read_range_next();
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index fff586f52e4..53f0c4f2484 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -182,6 +182,9 @@ private:
static int compare_number_of_records(ha_partition *me,
const uint32 *a,
const uint32 *b);
+ /** partitions that returned HA_ERR_KEY_NOT_FOUND. */
+ MY_BITMAP m_key_not_found_partitions;
+ bool m_key_not_found;
public:
handler *clone(const char *name, MEM_ROOT *mem_root);
virtual void set_part_info(partition_info *part_info)
@@ -516,6 +519,7 @@ private:
int handle_unordered_next(uchar * buf, bool next_same);
int handle_unordered_scan_next_partition(uchar * buf);
int handle_ordered_index_scan(uchar * buf, bool reverse_order);
+ int handle_ordered_index_scan_key_not_found();
int handle_ordered_next(uchar * buf, bool next_same);
int handle_ordered_prev(uchar * buf);
void return_top_record(uchar * buf);
diff --git a/sql/handler.cc b/sql/handler.cc
index 0fac0d756ca..150bbee39a9 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -700,7 +700,6 @@ static my_bool closecon_handlerton(THD *thd, plugin_ref plugin,
return FALSE;
}
-
/**
@note
don't bother to rollback here, it's done already
@@ -710,6 +709,25 @@ void ha_close_connection(THD* thd)
plugin_foreach(thd, closecon_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, 0);
}
+static my_bool kill_handlerton(THD *thd, plugin_ref plugin,
+ void *level)
+{
+ handlerton *hton= plugin_data(plugin, handlerton *);
+
+ if (hton->state == SHOW_OPTION_YES && hton->kill_query &&
+ thd_get_ha_data(thd, hton))
+ hton->kill_query(hton, thd, *(enum thd_kill_levels *) level);
+ return FALSE;
+}
+
+void ha_kill_query(THD* thd, enum thd_kill_levels level)
+{
+ DBUG_ENTER("ha_kill_query");
+ plugin_foreach(thd, kill_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &level);
+ DBUG_VOID_RETURN;
+}
+
+
/* ========================================================================
======================= TRANSACTIONS ===================================*/
@@ -2365,18 +2383,25 @@ int handler::read_first_row(uchar * buf, uint primary_key)
if (stats.deleted < 10 || primary_key >= MAX_KEY ||
!(index_flags(primary_key, 0, 0) & HA_READ_ORDER))
{
- if ((!(error= ha_rnd_init(1))))
+ if (!(error= ha_rnd_init(1)))
{
- while ((error= ha_rnd_next(buf)) == HA_ERR_RECORD_DELETED) ;
- (void) ha_rnd_end();
+ while ((error= ha_rnd_next(buf)) == HA_ERR_RECORD_DELETED)
+ /* skip deleted row */;
+ const int end_error= ha_rnd_end();
+ if (!error)
+ error= end_error;
}
}
else
{
/* Find the first row through the primary key */
- if (!(error = ha_index_init(primary_key, 0)))
+ if (!(error= ha_index_init(primary_key, 0)))
+ {
error= ha_index_first(buf);
- (void) ha_index_end();
+ const int end_error= ha_index_end();
+ if (!error)
+ error= end_error;
+ }
}
DBUG_RETURN(error);
}
@@ -2775,7 +2800,15 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
table->mark_columns_used_by_index_no_reset(table->s->next_number_index,
table->read_set);
column_bitmaps_signal();
- ha_index_init(table->s->next_number_index, 1);
+
+ if (ha_index_init(table->s->next_number_index, 1))
+ {
+ /* This should never happen, assert in debug, and fail in release build */
+ DBUG_ASSERT(0);
+ *first_value= ULONGLONG_MAX;
+ return;
+ }
+
if (table->s->next_number_keypart == 0)
{ // Autoincrement at key-start
error=ha_index_last(table->record[1]);
@@ -2806,13 +2839,25 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
}
if (error)
- nr=1;
+ {
+ if (error == HA_ERR_END_OF_FILE || error == HA_ERR_KEY_NOT_FOUND)
+ {
+ /* No entry found, start with 1. */
+ nr= 1;
+ }
+ else
+ {
+ DBUG_ASSERT(0);
+ nr= ULONGLONG_MAX;
+ }
+ }
else
nr= ((ulonglong) table->next_number_field->
val_int_offset(table->s->rec_buff_length)+1);
ha_index_end();
(void) extra(HA_EXTRA_NO_KEYREAD);
*first_value= nr;
+ return;
}
@@ -4725,7 +4770,9 @@ extern "C" enum icp_result handler_index_cond_check(void* h_arg)
THD *thd= h->table->in_use;
enum icp_result res;
- if (thd_killed(thd))
+ enum thd_kill_levels abort_at= h->has_transactions() ?
+ THD_ABORT_SOFTLY : THD_ABORT_ASAP;
+ if (thd_kill_level(thd) > abort_at)
return ICP_ABORTED_BY_USER;
if (h->end_range && h->compare_key2(h->end_range) > 0)
@@ -5156,6 +5203,7 @@ int handler::ha_write_row(uchar *buf)
int error;
Log_func *log_func= Write_rows_log_event::binlog_row_logging_function;
DBUG_ENTER("handler::ha_write_row");
+ DEBUG_SYNC_C("ha_write_row_start");
MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
diff --git a/sql/handler.h b/sql/handler.h
index f1f2ac6469f..c38dec198b9 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -866,6 +866,10 @@ struct handlerton
*/
int (*close_connection)(handlerton *hton, THD *thd);
/*
+ Tell handler that query has been killed.
+ */
+ void (*kill_query)(handlerton *hton, THD *thd, enum thd_kill_levels level);
+ /*
sv points to an uninitialized storage area of requested size
(see savepoint_offset description)
*/
@@ -1878,6 +1882,7 @@ public:
int ha_open(TABLE *table, const char *name, int mode, uint test_if_locked);
int ha_index_init(uint idx, bool sorted)
{
+ DBUG_EXECUTE_IF("ha_index_init_fail", return HA_ERR_TABLE_DEF_CHANGED;);
int result;
DBUG_ENTER("ha_index_init");
DBUG_ASSERT(inited==NONE);
@@ -1902,6 +1907,7 @@ public:
virtual int prepare_index_scan() { return 0; }
int ha_rnd_init(bool scan) __attribute__ ((warn_unused_result))
{
+ DBUG_EXECUTE_IF("ha_rnd_init_fail", return HA_ERR_TABLE_DEF_CHANGED;);
int result;
DBUG_ENTER("ha_rnd_init");
DBUG_ASSERT(inited==NONE || (inited==RND && scan));
@@ -2783,7 +2789,7 @@ private:
*/
virtual int open(const char *name, int mode, uint test_if_locked)=0;
- /* Note: ha_index_read_idx_map() may buypass index_init() */
+ /* Note: ha_index_read_idx_map() may bypass index_init() */
virtual int index_init(uint idx, bool sorted) { return 0; }
virtual int index_end() { return 0; }
/**
@@ -3016,6 +3022,7 @@ int ha_finalize_handlerton(st_plugin_int *plugin);
TYPELIB *ha_known_exts(void);
int ha_panic(enum ha_panic_function flag);
void ha_close_connection(THD* thd);
+void ha_kill_query(THD* thd, enum thd_kill_levels level);
bool ha_flush_logs(handlerton *db_type);
void ha_drop_database(char* path);
void ha_checkpoint_state(bool disable);
diff --git a/sql/hostname.cc b/sql/hostname.cc
index 763c4647532..ee30d071602 100644
--- a/sql/hostname.cc
+++ b/sql/hostname.cc
@@ -367,6 +367,14 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
err_code= vio_getnameinfo(ip, hostname_buffer, NI_MAXHOST, NULL, 0,
NI_NAMEREQD);
+ /* BEGIN : DEBUG */
+ DBUG_EXECUTE_IF("addr_fake_ipv4",
+ {
+ strcpy(hostname_buffer, "santa.claus.ipv4.example.com");
+ err_code= 0;
+ };);
+ /* END : DEBUG */
+
if (err_code)
{
// NOTE: gai_strerror() returns a string ending by a dot.
@@ -439,6 +447,12 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
DBUG_RETURN(err_status);
}
+ /*
+ To avoid crashing the server in DBUG_EXECUTE_IF,
+ Define a variable which depicts state of addr_info_list.
+ */
+ bool free_addr_info_list= false;
+
/* Get IP-addresses for the resolved host name (FCrDNS technique). */
struct addrinfo hints;
@@ -453,6 +467,42 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
(const char *) hostname_buffer));
err_code= getaddrinfo(hostname_buffer, NULL, &hints, &addr_info_list);
+ if (err_code == 0)
+ free_addr_info_list= true;
+
+ /* BEGIN : DEBUG */
+ DBUG_EXECUTE_IF("addr_fake_ipv4",
+ {
+ if (free_addr_info_list)
+ freeaddrinfo(addr_info_list);
+
+ struct sockaddr_in *debug_addr;
+ static struct sockaddr_in debug_sock_addr[2];
+ static struct addrinfo debug_addr_info[2];
+ /* Simulating ipv4 192.0.2.5 */
+ debug_addr= & debug_sock_addr[0];
+ debug_addr->sin_family= AF_INET;
+ debug_addr->sin_addr.s_addr= inet_addr("192.0.2.5");
+
+ /* Simulating ipv4 192.0.2.4 */
+ debug_addr= & debug_sock_addr[1];
+ debug_addr->sin_family= AF_INET;
+ debug_addr->sin_addr.s_addr= inet_addr("192.0.2.4");
+
+ debug_addr_info[0].ai_addr= (struct sockaddr*) & debug_sock_addr[0];
+ debug_addr_info[0].ai_addrlen= sizeof (struct sockaddr_in);
+ debug_addr_info[0].ai_next= & debug_addr_info[1];
+
+ debug_addr_info[1].ai_addr= (struct sockaddr*) & debug_sock_addr[1];
+ debug_addr_info[1].ai_addrlen= sizeof (struct sockaddr_in);
+ debug_addr_info[1].ai_next= NULL;
+
+ addr_info_list= & debug_addr_info[0];
+ err_code= 0;
+ free_addr_info_list= false;
+ };);
+
+ /* END : DEBUG */
if (err_code == EAI_NONAME)
{
@@ -505,7 +555,8 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
{
DBUG_PRINT("error", ("Out of memory."));
- freeaddrinfo(addr_info_list);
+ if (free_addr_info_list)
+ freeaddrinfo(addr_info_list);
DBUG_RETURN(TRUE);
}
@@ -539,7 +590,8 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
/* Free the result of getaddrinfo(). */
- freeaddrinfo(addr_info_list);
+ if (free_addr_info_list)
+ freeaddrinfo(addr_info_list);
/* Add an entry for the IP to the cache. */
diff --git a/sql/item.cc b/sql/item.cc
index 966b5392b13..96f61c08982 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -5579,7 +5579,7 @@ bool Item::eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs)
/**
Create a field to hold a string value from an item.
- If max_length > CONVERT_IF_BIGGER_TO_BLOB create a blob @n
+ If too_big_for_varchar() create a blob @n
If max_length > 0 create a varchar @n
If max_length == 0 create a CHAR(0)
@@ -5594,7 +5594,7 @@ Field *Item::make_string_field(TABLE *table)
Note: the following check is repeated in
subquery_types_allow_materialization():
*/
- if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB)
+ if (too_big_for_varchar())
field= new Field_blob(max_length, maybe_null, name,
collation.collation, TRUE);
/* Item_type_holder holds the exact type, do not change it */
@@ -5699,7 +5699,7 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length)
DBUG_ASSERT(0);
/* If something goes awfully wrong, it's better to get a string than die */
case MYSQL_TYPE_STRING:
- if (fixed_length && max_length < CONVERT_IF_BIGGER_TO_BLOB)
+ if (fixed_length && !too_big_for_varchar())
{
field= new Field_string(max_length, maybe_null, name,
collation.collation);
@@ -8851,9 +8851,10 @@ int Item_cache_temporal::save_in_field(Field *field, bool no_conversions)
}
-void Item_cache_temporal::store_packed(longlong val_arg)
+void Item_cache_temporal::store_packed(longlong val_arg, Item *example)
{
/* An explicit values is given, save it. */
+ store(example);
value_cached= true;
value= val_arg;
null_value= false;
@@ -9599,11 +9600,18 @@ table_map Item_ref::used_tables() const
void Item_ref::update_used_tables()
-{
+{
if (!get_depended_from())
- (*ref)->update_used_tables();
+ (*ref)->update_used_tables();
+ maybe_null|= (*ref)->maybe_null;
}
+void Item_direct_view_ref::update_used_tables()
+{
+ Item_ref::update_used_tables();
+ if (view->table && view->table->maybe_null)
+ maybe_null= TRUE;
+}
table_map Item_direct_view_ref::used_tables() const
{
diff --git a/sql/item.h b/sql/item.h
index 172932434d2..9b10cdf601b 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -2,7 +2,7 @@
#define SQL_ITEM_INCLUDED
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright (c) 2009-2011 Monty Program Ab
+ Copyright (c) 2009, 2013 Monty Program Ab
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
@@ -611,7 +611,11 @@ public:
@see Query_arena::free_list
*/
Item *next;
- uint32 max_length; /* Maximum length, in bytes */
+ /*
+ The maximum value length in characters multiplied by collation->mbmaxlen.
+ Almost always it's the maximum value length in bytes.
+ */
+ uint32 max_length;
/*
TODO: convert name and name_length fields into LEX_STRING to keep them in
sync (see bug #11829681/60295 etc). Then also remove some strlen(name)
@@ -1424,6 +1428,8 @@ public:
bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs);
uint32 max_char_length() const
{ return max_length / collation.collation->mbmaxlen; }
+ bool too_big_for_varchar() const
+ { return max_char_length() > CONVERT_IF_BIGGER_TO_BLOB; }
void fix_length_and_charset(uint32 max_char_length_arg, CHARSET_INFO *cs)
{
max_length= char_to_byte_length_safe(max_char_length_arg, cs->mbmaxlen);
@@ -1434,24 +1440,11 @@ public:
max_length= char_to_byte_length_safe(max_char_length_arg,
collation.collation->mbmaxlen);
}
- void fix_char_length_ulonglong(ulonglong max_char_length_arg)
- {
- ulonglong max_result_length= max_char_length_arg *
- collation.collation->mbmaxlen;
- if (max_result_length >= MAX_BLOB_WIDTH)
- {
- max_length= MAX_BLOB_WIDTH;
- maybe_null= 1;
- }
- else
- max_length= (uint32) max_result_length;
- }
/*
Return TRUE if the item points to a column of an outer-joined table.
*/
virtual bool is_outer_field() const { DBUG_ASSERT(fixed); return FALSE; }
Item* set_expr_cache(THD *thd);
- virtual Item *get_cached_item() { return NULL; }
virtual Item_equal *get_item_equal() { return NULL; }
virtual void set_item_equal(Item_equal *item_eq) {};
@@ -2057,9 +2050,14 @@ public:
bitmap_fast_test_and_set(tab->read_set, field->field_index);
if (field->vcol_info)
tab->mark_virtual_col(field);
- }
+ }
+ }
+ void update_used_tables()
+ {
+ update_table_bitmaps();
+ if (field && field->table)
+ maybe_null|= field->maybe_null();
}
- void update_used_tables() { update_table_bitmaps(); }
Item *get_tmp_table_item(THD *thd);
bool collect_item_field_processor(uchar * arg);
bool add_field_to_set_processor(uchar * arg);
@@ -3110,7 +3108,11 @@ public:
enum Item_result result_type () const { return orig_item->result_type(); }
enum_field_types field_type() const { return orig_item->field_type(); }
table_map used_tables() const { return orig_item->used_tables(); }
- void update_used_tables() { orig_item->update_used_tables(); }
+ void update_used_tables()
+ {
+ orig_item->update_used_tables();
+ maybe_null|= orig_item->maybe_null;
+ }
bool const_item() const { return orig_item->const_item(); }
table_map not_null_tables() const { return orig_item->not_null_tables(); }
bool walk(Item_processor processor, bool walk_subquery, uchar *arg)
@@ -3202,6 +3204,7 @@ public:
Item *replace_equal_field(uchar *arg);
table_map used_tables() const;
table_map not_null_tables() const;
+ void update_used_tables();
bool walk(Item_processor processor, bool walk_subquery, uchar *arg)
{
return (*ref)->walk(processor, walk_subquery, arg) ||
@@ -4027,7 +4030,7 @@ public:
bool cache_value();
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
int save_in_field(Field *field, bool no_conversions);
- void store_packed(longlong val_arg);
+ void store_packed(longlong val_arg, Item *example);
/*
Having a clone_item method tells optimizer that this object
is a constant and need not be optimized further.
@@ -4036,7 +4039,7 @@ public:
Item *clone_item()
{
Item_cache_temporal *item= new Item_cache_temporal(cached_field_type);
- item->store_packed(value);
+ item->store_packed(value, example);
return item;
}
};
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 4db783242c7..3b09da68927 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2009, 2012 Monty Program Ab
+ Copyright (c) 2009, 2013, Monty Program Ab.
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
@@ -914,7 +914,7 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
if (save_arena)
thd->set_query_arena(save_arena);
- cache->store_packed(value);
+ cache->store_packed(value, item);
*cache_arg= cache;
*item_arg= cache_arg;
}
@@ -1353,7 +1353,7 @@ int Arg_comparator::compare_e_row()
void Item_func_truth::fix_length_and_dec()
{
- maybe_null= 0;
+ set_persist_maybe_null(0);
null_value= 0;
decimals= 0;
max_length= 1;
@@ -1865,7 +1865,8 @@ longlong Item_func_eq::val_int()
void Item_func_equal::fix_length_and_dec()
{
Item_bool_func2::fix_length_and_dec();
- maybe_null=null_value=0;
+ set_persist_maybe_null(0);
+ null_value= 0;
}
longlong Item_func_equal::val_int()
@@ -2004,7 +2005,7 @@ void Item_func_interval::fix_length_and_dec()
}
}
}
- maybe_null= 0;
+ set_persist_maybe_null(0);
max_length= 2;
used_tables_cache|= row->used_tables();
not_null_tables_cache= row->not_null_tables();
@@ -2685,7 +2686,7 @@ void
Item_func_nullif::fix_length_and_dec()
{
Item_bool_func2::fix_length_and_dec();
- maybe_null=1;
+ set_persist_maybe_null(1);
if (args[0]) // Only false if EOM
{
max_length=args[0]->max_length;
@@ -4546,6 +4547,8 @@ void Item_cond::update_used_tables()
item->update_used_tables();
used_tables_cache|= item->used_tables();
const_item_cache&= item->const_item();
+ if (!persistent_maybe_null && item->maybe_null)
+ maybe_null= 1;
}
}
@@ -4720,10 +4723,9 @@ longlong Item_is_not_null_test::val_int()
*/
void Item_is_not_null_test::update_used_tables()
{
+ args[0]->update_used_tables();
if (!args[0]->maybe_null)
used_tables_cache= 0; /* is always true */
- else
- args[0]->update_used_tables();
}
@@ -5004,7 +5006,7 @@ Item_func_regex::fix_fields(THD *thd, Item **ref)
int comp_res= regcomp(TRUE);
if (comp_res == -1)
{ // Will always return NULL
- maybe_null=1;
+ set_persist_maybe_null(1);
fixed= 1;
return FALSE;
}
@@ -5014,7 +5016,7 @@ Item_func_regex::fix_fields(THD *thd, Item **ref)
maybe_null= args[0]->maybe_null;
}
else
- maybe_null=1;
+ set_persist_maybe_null(1);
fixed= 1;
return FALSE;
}
@@ -5828,6 +5830,8 @@ void Item_equal::update_used_tables()
used_tables_cache|= item->used_tables();
/* see commentary at Item_equal::update_const() */
const_item_cache&= item->const_item() && !item->is_outer_field();
+ if (!persistent_maybe_null && item->maybe_null)
+ maybe_null= 1;
}
}
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index f183ee9f8a8..afb7bf005bb 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -767,6 +767,11 @@ public:
my_decimal *decimal_op(my_decimal *);
enum_field_types field_type() const;
void fix_length_and_dec();
+ void update_used_tables()
+ {
+ Item_func_coalesce::update_used_tables();
+ maybe_null|= args[1]->maybe_null;
+ }
const char *func_name() const { return "ifnull"; }
Field *tmp_table_field(TABLE *table);
uint decimal_precision() const;
@@ -789,6 +794,11 @@ public:
enum_field_types field_type() const { return cached_field_type; }
bool fix_fields(THD *, Item **);
void fix_length_and_dec();
+ void update_used_tables()
+ {
+ Item_func::update_used_tables();
+ maybe_null|= args[1]->maybe_null || args[2]->maybe_null;
+ }
uint decimal_precision() const;
const char *func_name() const { return "if"; }
bool eval_not_null_tables(uchar *opt_arg);
@@ -1254,6 +1264,12 @@ public:
my_decimal *val_decimal(my_decimal *);
bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec();
+ void update_used_tables()
+ {
+ Item_func::update_used_tables();
+ if (else_expr_num == -1 || args[else_expr_num]->maybe_null)
+ maybe_null= 1;
+ }
uint decimal_precision() const;
table_map not_null_tables() const { return 0; }
enum Item_result result_type () const { return cached_result_type; }
@@ -1375,13 +1391,14 @@ public:
enum Functype functype() const { return ISNULL_FUNC; }
void fix_length_and_dec()
{
- decimals=0; max_length=1; maybe_null=0;
+ decimals=0; max_length=1; set_persist_maybe_null(0);
update_used_tables();
}
const char *func_name() const { return "isnull"; }
/* Optimize case of not_null_column IS NULL */
virtual void update_used_tables()
{
+ args[0]->update_used_tables();
if (!args[0]->maybe_null)
{
used_tables_cache= 0; /* is always false */
@@ -1389,7 +1406,6 @@ public:
}
else
{
- args[0]->update_used_tables();
used_tables_cache= args[0]->used_tables();
const_item_cache= args[0]->const_item();
}
@@ -1437,7 +1453,7 @@ public:
enum Functype functype() const { return ISNOTNULL_FUNC; }
void fix_length_and_dec()
{
- decimals=0; max_length=1; maybe_null=0;
+ decimals=0; max_length=1; set_persist_maybe_null(0);
}
const char *func_name() const { return "isnotnull"; }
optimize_type select_optimize() const { return OPTIMIZE_NULL; }
@@ -1505,6 +1521,12 @@ public:
void cleanup();
longlong val_int();
bool fix_fields(THD *thd, Item **ref);
+ void update_used_tables()
+ {
+ Item_bool_func::update_used_tables();
+ if (regex_is_const)
+ maybe_null= 1;
+ }
const char *func_name() const { return "regexp"; }
virtual inline void print(String *str, enum_query_type query_type)
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 520b5522d38..aca76eac82a 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright (c) 2008-2011 Monty Program Ab
+ Copyright (c) 2009, 2013, Monty Program Ab.
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
@@ -111,7 +111,7 @@ void Item_func::set_arguments(List<Item> &list)
}
Item_func::Item_func(List<Item> &list)
- :allowed_arg_cols(1)
+ :allowed_arg_cols(1), persistent_maybe_null(0)
{
set_arguments(list);
}
@@ -119,6 +119,7 @@ Item_func::Item_func(List<Item> &list)
Item_func::Item_func(THD *thd, Item_func *item)
:Item_result_field(thd, item),
allowed_arg_cols(item->allowed_arg_cols),
+ persistent_maybe_null(0),
arg_count(item->arg_count),
used_tables_cache(item->used_tables_cache),
not_null_tables_cache(item->not_null_tables_cache),
@@ -446,6 +447,8 @@ void Item_func::update_used_tables()
args[i]->update_used_tables();
used_tables_cache|=args[i]->used_tables();
const_item_cache&=args[i]->const_item();
+ if (!persistent_maybe_null && args[i]->maybe_null)
+ maybe_null= 1;
}
}
@@ -1712,7 +1715,7 @@ void Item_func_div::fix_length_and_dec()
case IMPOSSIBLE_RESULT:
DBUG_ASSERT(0);
}
- maybe_null= 1; // devision by zero
+ set_persist_maybe_null(1); // devision by zero
DBUG_VOID_RETURN;
}
@@ -1796,7 +1799,7 @@ void Item_func_int_div::fix_length_and_dec()
max_length=args[0]->max_length -
(argtype == DECIMAL_RESULT || argtype == INT_RESULT ?
args[0]->decimals : 0);
- maybe_null=1;
+ set_persist_maybe_null(1);
unsigned_flag=args[0]->unsigned_flag | args[1]->unsigned_flag;
}
@@ -1883,7 +1886,7 @@ void Item_func_mod::result_precision()
void Item_func_mod::fix_length_and_dec()
{
Item_num_op::fix_length_and_dec();
- maybe_null= 1;
+ set_persist_maybe_null(1);
unsigned_flag= args[0]->unsigned_flag;
}
@@ -3078,7 +3081,7 @@ longlong Item_func_field::val_int()
void Item_func_field::fix_length_and_dec()
{
- maybe_null=0; max_length=3;
+ set_persist_maybe_null(0); max_length=3;
cmp_type= args[0]->result_type();
for (uint i=1; i < arg_count ; i++)
cmp_type= item_cmp_type(cmp_type, args[i]->result_type());
@@ -4195,7 +4198,8 @@ longlong Item_func_last_insert_id::val_int()
thd->first_successful_insert_id_in_prev_stmt= value;
return value;
}
- return thd->read_first_successful_insert_id_in_prev_stmt();
+ return
+ static_cast<longlong>(thd->read_first_successful_insert_id_in_prev_stmt());
}
@@ -5317,7 +5321,7 @@ void Item_func_get_user_var::fix_length_and_dec()
{
THD *thd=current_thd;
int error;
- maybe_null=1;
+ set_persist_maybe_null(1);
decimals=NOT_FIXED_DEC;
max_length=MAX_BLOB_WIDTH;
@@ -5516,7 +5520,7 @@ void Item_func_get_system_var::update_null_value()
void Item_func_get_system_var::fix_length_and_dec()
{
char *cptr;
- maybe_null= TRUE;
+ set_persist_maybe_null(1);
max_length= 0;
if (var->check_type(var_type))
@@ -6083,7 +6087,7 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref)
status_var_increment(thd->status_var.feature_fulltext);
- maybe_null=1;
+ set_persist_maybe_null(1);
join_key=0;
/*
@@ -6417,7 +6421,7 @@ longlong Item_func_row_count::val_int()
Item_func_sp::Item_func_sp(Name_resolution_context *context_arg, sp_name *name)
:Item_func(), context(context_arg), m_name(name), m_sp(NULL), sp_result_field(NULL)
{
- maybe_null= 1;
+ set_persist_maybe_null(1);
m_name->init_qname(current_thd);
dummy_table= (TABLE*) sql_calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE));
dummy_table->s= (TABLE_SHARE*) (dummy_table+1);
@@ -6428,7 +6432,7 @@ Item_func_sp::Item_func_sp(Name_resolution_context *context_arg,
sp_name *name, List<Item> &list)
:Item_func(list), context(context_arg), m_name(name), m_sp(NULL),sp_result_field(NULL)
{
- maybe_null= 1;
+ set_persist_maybe_null(1);
m_name->init_qname(current_thd);
dummy_table= (TABLE*) sql_calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE));
dummy_table->s= (TABLE_SHARE*) (dummy_table+1);
@@ -6556,6 +6560,19 @@ Item_func_sp::init_result_field(THD *thd)
/**
+ @note
+ Deterministic stored procedures are considered inexpensive.
+ Consequently such procedures may be evaluated during optimization,
+ if they are constant (checked by the optimizer).
+*/
+
+bool Item_func_sp::is_expensive()
+{
+ return !(m_sp->m_chistics->detistic);
+}
+
+
+/**
@brief Initialize local members with values from the Field interface.
@note called from Item::fix_fields.
@@ -6569,7 +6586,7 @@ void Item_func_sp::fix_length_and_dec()
decimals= sp_result_field->decimals();
max_length= sp_result_field->field_length;
collation.set(sp_result_field->charset());
- maybe_null= 1;
+ set_persist_maybe_null(1);
unsigned_flag= test(sp_result_field->flags & UNSIGNED_FLAG);
DBUG_VOID_RETURN;
diff --git a/sql/item_func.h b/sql/item_func.h
index b9b6c865bfd..f562c87fe1c 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -1,7 +1,7 @@
#ifndef ITEM_FUNC_INCLUDED
#define ITEM_FUNC_INCLUDED
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright (c) 2009-2011 Monty Program Ab
+ Copyright (c) 2009, 2013, Monty Program Ab.
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
@@ -39,6 +39,8 @@ protected:
0 means get this number from first argument
*/
uint allowed_arg_cols;
+ /* maybe_null can't be changed by parameters or used table state */
+ bool persistent_maybe_null;
public:
uint arg_count;
table_map used_tables_cache, not_null_tables_cache;
@@ -64,13 +66,13 @@ public:
enum Type type() const { return FUNC_ITEM; }
virtual enum Functype functype() const { return UNKNOWN_FUNC; }
Item_func(void):
- allowed_arg_cols(1), arg_count(0)
+ allowed_arg_cols(1), persistent_maybe_null(0), arg_count(0)
{
with_sum_func= 0;
with_field= 0;
}
Item_func(Item *a):
- allowed_arg_cols(1), arg_count(1)
+ allowed_arg_cols(1), persistent_maybe_null(0), arg_count(1)
{
args= tmp_arg;
args[0]= a;
@@ -78,7 +80,7 @@ public:
with_field= a->with_field;
}
Item_func(Item *a,Item *b):
- allowed_arg_cols(1), arg_count(2)
+ allowed_arg_cols(1), persistent_maybe_null(0), arg_count(2)
{
args= tmp_arg;
args[0]= a; args[1]= b;
@@ -86,7 +88,7 @@ public:
with_field= a->with_field || b->with_field;
}
Item_func(Item *a,Item *b,Item *c):
- allowed_arg_cols(1)
+ allowed_arg_cols(1), persistent_maybe_null(0)
{
arg_count= 0;
if ((args= (Item**) sql_alloc(sizeof(Item*)*3)))
@@ -98,7 +100,7 @@ public:
}
}
Item_func(Item *a,Item *b,Item *c,Item *d):
- allowed_arg_cols(1)
+ allowed_arg_cols(1), persistent_maybe_null(0)
{
arg_count= 0;
if ((args= (Item**) sql_alloc(sizeof(Item*)*4)))
@@ -112,7 +114,7 @@ public:
}
}
Item_func(Item *a,Item *b,Item *c,Item *d,Item* e):
- allowed_arg_cols(1)
+ allowed_arg_cols(1), persistent_maybe_null(0)
{
arg_count= 5;
if ((args= (Item**) sql_alloc(sizeof(Item*)*5)))
@@ -170,6 +172,18 @@ public:
my_decimal *val_decimal(my_decimal *);
+ void fix_char_length_ulonglong(ulonglong max_char_length_arg)
+ {
+ ulonglong max_result_length= max_char_length_arg *
+ collation.collation->mbmaxlen;
+ if (max_result_length >= MAX_BLOB_WIDTH)
+ {
+ max_length= MAX_BLOB_WIDTH;
+ set_persist_maybe_null(1);
+ }
+ else
+ max_length= (uint32) max_result_length;
+ }
bool agg_arg_charsets(DTCollation &c, Item **items, uint nitems,
uint flags, int item_sep)
{
@@ -371,6 +385,11 @@ public:
info.bool_function= &Item::restore_to_before_no_rows_in_result;
walk(&Item::call_bool_func_processor, FALSE, (uchar*) &info);
}
+ inline void set_persist_maybe_null(bool mb_null)
+ {
+ maybe_null= mb_null;
+ persistent_maybe_null= 1;
+ }
};
@@ -586,7 +605,7 @@ public:
}
double val_real();
enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; }
- void fix_length_and_dec() { maybe_null= 1; }
+ void fix_length_and_dec() { set_persist_maybe_null(1); }
const char *func_name() const { return "double_typecast"; }
virtual void print(String *str, enum_query_type query_type);
};
@@ -727,7 +746,7 @@ class Item_dec_func :public Item_real_func
void fix_length_and_dec()
{
decimals=NOT_FIXED_DEC; max_length=float_length(decimals);
- maybe_null=1;
+ set_persist_maybe_null(1);
}
};
@@ -1059,7 +1078,7 @@ public:
Item_func_coercibility(Item *a) :Item_int_func(a) {}
longlong val_int();
const char *func_name() const { return "coercibility"; }
- void fix_length_and_dec() { max_length=10; maybe_null= 0; }
+ void fix_length_and_dec() { max_length=10; set_persist_maybe_null(0); }
table_map not_null_tables() const { return 0; }
};
@@ -1201,6 +1220,7 @@ public:
const char *func_name() const { return "last_insert_id"; }
void fix_length_and_dec()
{
+ unsigned_flag= TRUE;
if (arg_count)
max_length= args[0]->max_length;
unsigned_flag=1;
@@ -1221,7 +1241,7 @@ public:
{}
longlong val_int();
const char *func_name() const { return "benchmark"; }
- void fix_length_and_dec() { max_length=1; maybe_null=0; }
+ void fix_length_and_dec() { max_length=1; set_persist_maybe_null(0); }
virtual void print(String *str, enum_query_type query_type);
bool check_vcol_func_processor(uchar *int_arg)
{
@@ -1474,7 +1494,7 @@ public:
double val_real() { DBUG_ASSERT(fixed == 1); null_value= 1; return 0.0; }
longlong val_int() { DBUG_ASSERT(fixed == 1); null_value=1; return 0; }
enum Item_result result_type () const { return STRING_RESULT; }
- void fix_length_and_dec() { maybe_null=1; max_length=0; }
+ void fix_length_and_dec() { set_persist_maybe_null(1); max_length=0; }
};
#endif /* HAVE_DLOPEN */
@@ -1495,7 +1515,7 @@ class Item_func_get_lock :public Item_int_func
Item_func_get_lock(Item *a,Item *b) :Item_int_func(a,b) {}
longlong val_int();
const char *func_name() const { return "get_lock"; }
- void fix_length_and_dec() { max_length=1; maybe_null=1;}
+ void fix_length_and_dec() { max_length=1; set_persist_maybe_null(1);}
bool check_vcol_func_processor(uchar *int_arg)
{
return trace_unsupported_by_check_vcol_func_processor(func_name());
@@ -1509,7 +1529,7 @@ public:
Item_func_release_lock(Item *a) :Item_int_func(a) {}
longlong val_int();
const char *func_name() const { return "release_lock"; }
- void fix_length_and_dec() { max_length=1; maybe_null=1;}
+ void fix_length_and_dec() { max_length=1; set_persist_maybe_null(1);}
bool check_vcol_func_processor(uchar *int_arg)
{
return trace_unsupported_by_check_vcol_func_processor(func_name());
@@ -1527,7 +1547,7 @@ public:
Item_master_pos_wait(Item *a,Item *b, Item *c, Item *d) :Item_int_func(a,b,c,d) {}
longlong val_int();
const char *func_name() const { return "master_pos_wait"; }
- void fix_length_and_dec() { max_length=21; maybe_null=1;}
+ void fix_length_and_dec() { max_length=21; set_persist_maybe_null(1);}
bool check_vcol_func_processor(uchar *int_arg)
{
return trace_unsupported_by_check_vcol_func_processor(func_name());
@@ -1744,7 +1764,8 @@ public:
Item_func_inet_aton(Item *a) :Item_int_func(a) {}
longlong val_int();
const char *func_name() const { return "inet_aton"; }
- void fix_length_and_dec() { decimals= 0; max_length= 21; maybe_null= 1; unsigned_flag= 1;}
+ void fix_length_and_dec()
+ { decimals= 0; max_length= 21; set_persist_maybe_null(1); unsigned_flag= 1; }
};
@@ -1813,7 +1834,8 @@ public:
Item_func_is_free_lock(Item *a) :Item_int_func(a) {}
longlong val_int();
const char *func_name() const { return "is_free_lock"; }
- void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1;}
+ void fix_length_and_dec()
+ { decimals= 0; max_length= 1; set_persist_maybe_null(1); }
bool check_vcol_func_processor(uchar *int_arg)
{
return trace_unsupported_by_check_vcol_func_processor(func_name());
@@ -1827,7 +1849,8 @@ public:
Item_func_is_used_lock(Item *a) :Item_int_func(a) {}
longlong val_int();
const char *func_name() const { return "is_used_lock"; }
- void fix_length_and_dec() { decimals=0; max_length=10; maybe_null=1;}
+ void fix_length_and_dec()
+ { decimals= 0; max_length= 10; set_persist_maybe_null(1);}
bool check_vcol_func_processor(uchar *int_arg)
{
return trace_unsupported_by_check_vcol_func_processor(func_name());
@@ -1850,7 +1873,7 @@ public:
Item_func_row_count() :Item_int_func() {}
longlong val_int();
const char *func_name() const { return "row_count"; }
- void fix_length_and_dec() { decimals= 0; maybe_null=0; }
+ void fix_length_and_dec() { decimals= 0; set_persist_maybe_null(0); }
bool check_vcol_func_processor(uchar *int_arg)
{
@@ -1887,7 +1910,8 @@ private:
bool init_result_field(THD *thd);
protected:
- bool is_expensive_processor(uchar *arg) { return TRUE; }
+ bool is_expensive_processor(uchar *arg)
+ { return is_expensive(); }
public:
@@ -1966,7 +1990,7 @@ public:
bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec(void);
- bool is_expensive() { return 1; }
+ bool is_expensive();
inline Field *get_sp_result_field()
{
@@ -1990,7 +2014,7 @@ public:
Item_func_found_rows() :Item_int_func() {}
longlong val_int();
const char *func_name() const { return "found_rows"; }
- void fix_length_and_dec() { decimals= 0; maybe_null=0; }
+ void fix_length_and_dec() { decimals= 0; set_persist_maybe_null(0); }
bool check_vcol_func_processor(uchar *int_arg)
{
return trace_unsupported_by_check_vcol_func_processor(func_name());
@@ -2033,6 +2057,11 @@ public:
enum_field_types field_type() const { return last_value->field_type(); }
bool const_item() const { return 0; }
void evaluate_sideeffects();
+ void update_used_tables()
+ {
+ Item_func::update_used_tables();
+ maybe_null= last_value->maybe_null;
+ }
};
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index bc89a6c14b3..e3e80bdf59f 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -53,7 +53,7 @@ void Item_geometry_func::fix_length_and_dec()
collation.set(&my_charset_bin);
decimals=0;
max_length= (uint32) 4294967295U;
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
@@ -147,7 +147,7 @@ void Item_func_as_wkt::fix_length_and_dec()
{
collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
max_length=MAX_BLOB_WIDTH;
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h
index 3638d9f62e8..ee61f921adb 100644
--- a/sql/item_geofunc.h
+++ b/sql/item_geofunc.h
@@ -89,7 +89,7 @@ public:
{
// "GeometryCollection" is the longest
fix_length_and_charset(20, default_charset());
- maybe_null= 1;
+ set_persist_maybe_null(1);
};
};
@@ -224,7 +224,7 @@ public:
{
Item_func::print(str, query_type);
}
- void fix_length_and_dec() { maybe_null= 1; }
+ void fix_length_and_dec() { set_persist_maybe_null(1); }
bool is_null() { (void) val_int(); return null_value; }
};
@@ -251,7 +251,7 @@ public:
Item_func::print(str, query_type);
}
- void fix_length_and_dec() { maybe_null= 1; }
+ void fix_length_and_dec() { set_persist_maybe_null(1); }
bool is_null() { (void) val_int(); return null_value; }
};
@@ -342,7 +342,7 @@ public:
longlong val_int();
optimize_type select_optimize() const { return OPTIMIZE_NONE; }
const char *func_name() const { return "st_isempty"; }
- void fix_length_and_dec() { maybe_null= 1; }
+ void fix_length_and_dec() { set_persist_maybe_null(1); }
};
class Item_func_issimple: public Item_bool_func
@@ -356,7 +356,7 @@ public:
longlong val_int();
optimize_type select_optimize() const { return OPTIMIZE_NONE; }
const char *func_name() const { return "st_issimple"; }
- void fix_length_and_dec() { maybe_null= 1; }
+ void fix_length_and_dec() { set_persist_maybe_null(1); }
};
class Item_func_isclosed: public Item_bool_func
@@ -366,7 +366,7 @@ public:
longlong val_int();
optimize_type select_optimize() const { return OPTIMIZE_NONE; }
const char *func_name() const { return "st_isclosed"; }
- void fix_length_and_dec() { maybe_null= 1; }
+ void fix_length_and_dec() { set_persist_maybe_null(1); }
};
class Item_func_dimension: public Item_int_func
@@ -376,7 +376,7 @@ public:
Item_func_dimension(Item *a): Item_int_func(a) {}
longlong val_int();
const char *func_name() const { return "st_dimension"; }
- void fix_length_and_dec() { max_length= 10; maybe_null= 1; }
+ void fix_length_and_dec() { max_length= 10; set_persist_maybe_null(1); }
};
class Item_func_x: public Item_real_func
@@ -389,7 +389,7 @@ public:
void fix_length_and_dec()
{
Item_real_func::fix_length_and_dec();
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
};
@@ -404,7 +404,7 @@ public:
void fix_length_and_dec()
{
Item_real_func::fix_length_and_dec();
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
};
@@ -416,7 +416,7 @@ public:
Item_func_numgeometries(Item *a): Item_int_func(a) {}
longlong val_int();
const char *func_name() const { return "st_numgeometries"; }
- void fix_length_and_dec() { max_length= 10; maybe_null= 1; }
+ void fix_length_and_dec() { max_length= 10; set_persist_maybe_null(1); }
};
@@ -427,7 +427,7 @@ public:
Item_func_numinteriorring(Item *a): Item_int_func(a) {}
longlong val_int();
const char *func_name() const { return "st_numinteriorrings"; }
- void fix_length_and_dec() { max_length= 10; maybe_null= 1; }
+ void fix_length_and_dec() { max_length= 10; set_persist_maybe_null(1); }
};
@@ -438,7 +438,7 @@ public:
Item_func_numpoints(Item *a): Item_int_func(a) {}
longlong val_int();
const char *func_name() const { return "st_numpoints"; }
- void fix_length_and_dec() { max_length= 10; maybe_null= 1; }
+ void fix_length_and_dec() { max_length= 10; set_persist_maybe_null(1); }
};
@@ -452,7 +452,7 @@ public:
void fix_length_and_dec()
{
Item_real_func::fix_length_and_dec();
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
};
@@ -467,7 +467,7 @@ public:
void fix_length_and_dec()
{
Item_real_func::fix_length_and_dec();
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
};
@@ -479,7 +479,7 @@ public:
Item_func_srid(Item *a): Item_int_func(a) {}
longlong val_int();
const char *func_name() const { return "srid"; }
- void fix_length_and_dec() { max_length= 10; maybe_null= 1; }
+ void fix_length_and_dec() { max_length= 10; set_persist_maybe_null(1); }
};
diff --git a/sql/item_row.cc b/sql/item_row.cc
index 2c4a628075e..ee7bd837553 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -74,7 +74,8 @@ bool Item_row::fix_fields(THD *thd, Item **ref)
Item **arg, **arg_end;
for (arg= items, arg_end= items+arg_count; arg != arg_end ; arg++)
{
- if ((*arg)->fix_fields(thd, arg))
+ if (!(*arg)->fixed &&
+ (*arg)->fix_fields(thd, arg))
return TRUE;
// we can't assign 'item' before, because fix_fields() can change arg
Item *item= *arg;
@@ -145,11 +146,13 @@ void Item_row::update_used_tables()
{
used_tables_cache= 0;
const_item_cache= 1;
+ maybe_null= 0;
for (uint i= 0; i < arg_count; i++)
{
items[i]->update_used_tables();
used_tables_cache|= items[i]->used_tables();
const_item_cache&= items[i]->const_item();
+ maybe_null|= items[i]->maybe_null;
}
}
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index a2bbdd63b40..bcd3b881934 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -1,5 +1,6 @@
/*
Copyright (c) 2000, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2013, Monty Program Ab.
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
@@ -341,7 +342,7 @@ String *Item_func_sha2::val_str_ascii(String *str)
void Item_func_sha2::fix_length_and_dec()
{
- maybe_null = 1;
+ set_persist_maybe_null(1);
max_length = 0;
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
@@ -467,7 +468,7 @@ String *Item_func_aes_decrypt::val_str(String *str)
void Item_func_aes_decrypt::fix_length_and_dec()
{
max_length=args[0]->max_length;
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
@@ -1337,7 +1338,7 @@ void Item_str_func::left_right_max_length()
if (args[1]->const_item())
{
int length= (int) args[1]->val_int();
- if (length <= 0)
+ if (args[1]->null_value || length <= 0)
char_length=0;
else
set_if_smaller(char_length, (uint) length);
@@ -1444,7 +1445,9 @@ void Item_func_substr::fix_length_and_dec()
if (args[1]->const_item())
{
int32 start= (int32) args[1]->val_int();
- if (start < 0)
+ if (args[1]->null_value)
+ max_length= 0;
+ else if (start < 0)
max_length= ((uint)(-start) > max_length) ? 0 : (uint)(-start);
else
max_length-= min((uint)(start - 1), max_length);
@@ -1452,7 +1455,7 @@ void Item_func_substr::fix_length_and_dec()
if (arg_count == 3 && args[2]->const_item())
{
int32 length= (int32) args[2]->val_int();
- if (length <= 0)
+ if (args[2]->null_value || length <= 0)
max_length=0; /* purecov: inspected */
else
set_if_smaller(max_length,(uint) length);
@@ -2411,7 +2414,7 @@ void Item_func_elt::fix_length_and_dec()
set_if_bigger(decimals,args[i]->decimals);
}
fix_char_length(char_length);
- maybe_null=1; // NULL if wrong first arg
+ set_persist_maybe_null(1); // NULL if wrong first arg
}
@@ -2650,7 +2653,9 @@ void Item_func_repeat::fix_length_and_dec()
/* Assumes that the maximum length of a String is < INT_MAX32. */
/* Set here so that rest of code sees out-of-bound value as such. */
- if (count > INT_MAX32)
+ if (args[1]->null_value)
+ count= 0;
+ else if (count > INT_MAX32)
count= INT_MAX32;
ulonglong char_length= (ulonglong) args[0]->max_char_length() * count;
@@ -2659,7 +2664,7 @@ void Item_func_repeat::fix_length_and_dec()
else
{
max_length= MAX_BLOB_WIDTH;
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
}
@@ -2729,14 +2734,16 @@ void Item_func_rpad::fix_length_and_dec()
DBUG_ASSERT(collation.collation->mbmaxlen > 0);
/* Assumes that the maximum length of a String is < INT_MAX32. */
/* Set here so that rest of code sees out-of-bound value as such. */
- if (char_length > INT_MAX32)
+ if (args[1]->null_value)
+ char_length= 0;
+ else if (char_length > INT_MAX32)
char_length= INT_MAX32;
fix_char_length_ulonglong(char_length);
}
else
{
max_length= MAX_BLOB_WIDTH;
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
}
@@ -2833,14 +2840,16 @@ void Item_func_lpad::fix_length_and_dec()
DBUG_ASSERT(collation.collation->mbmaxlen > 0);
/* Assumes that the maximum length of a String is < INT_MAX32. */
/* Set here so that rest of code sees out-of-bound value as such. */
- if (char_length > INT_MAX32)
+ if (args[1]->null_value)
+ char_length= 0;
+ else if (char_length > INT_MAX32)
char_length= INT_MAX32;
fix_char_length_ulonglong(char_length);
}
else
{
max_length= MAX_BLOB_WIDTH;
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
}
@@ -3814,7 +3823,7 @@ bool Item_func_dyncol_create::fix_fields(THD *thd, Item **ref)
void Item_func_dyncol_create::fix_length_and_dec()
{
- maybe_null= TRUE;
+ set_persist_maybe_null(1);
collation.set(&my_charset_bin);
decimals= 0;
}
@@ -4366,8 +4375,7 @@ String *Item_dyncol_get::val_str(String *str_result)
case DYN_COL_DECIMAL:
{
int res;
- int length=
- my_decimal_string_length((const my_decimal*)&val.x.decimal.value);
+ int length= decimal_string_size(&val.x.decimal.value);
if (str_result->alloc(length))
goto null;
if ((res= decimal2string(&val.x.decimal.value, (char*) str_result->ptr(),
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index d21c938f378..a42240f1b35 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -3,6 +3,7 @@
/*
Copyright (c) 2000, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2013, Monty Program Ab.
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
@@ -354,7 +355,7 @@ public:
String *val_str(String *);
void fix_length_and_dec()
{
- maybe_null=1;
+ set_persist_maybe_null(1);
/* 9 = MAX ((8- (arg_len % 8)) + 1) */
max_length = args[0]->max_length + 9;
}
@@ -370,7 +371,7 @@ public:
String *val_str(String *);
void fix_length_and_dec()
{
- maybe_null=1;
+ set_persist_maybe_null(1);
/* 9 = MAX ((8- (arg_len % 8)) + 1) */
max_length= args[0]->max_length;
if (max_length >= 9U)
@@ -398,7 +399,7 @@ public:
constructor_helper();
}
String *val_str(String *);
- void fix_length_and_dec() { maybe_null=1; max_length = 13; }
+ void fix_length_and_dec() { set_persist_maybe_null(1); max_length = 13; }
const char *func_name() const { return "encrypt"; }
bool check_vcol_func_processor(uchar *int_arg)
{
@@ -468,7 +469,7 @@ public:
void fix_length_and_dec()
{
max_length= MAX_FIELD_NAME * system_charset_info->mbmaxlen;
- maybe_null=1;
+ set_persist_maybe_null(1);
}
const char *func_name() const { return "database"; }
const char *fully_qualified_func_name() const { return "database()"; }
@@ -649,7 +650,7 @@ public:
{
collation.set(default_charset());
max_length=64;
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
};
@@ -676,7 +677,7 @@ public:
Item_func_unhex(Item *a) :Item_str_func(a)
{
/* there can be bad hex strings */
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
const char *func_name() const { return "unhex"; }
String *val_str(String *);
@@ -762,7 +763,7 @@ public:
void fix_length_and_dec()
{
collation.set(&my_charset_bin, DERIVATION_COERCIBLE);
- maybe_null=1;
+ set_persist_maybe_null(1);
max_length=MAX_BLOB_WIDTH;
}
bool check_vcol_func_processor(uchar *int_arg)
@@ -795,7 +796,7 @@ public:
{
decimals= 0;
fix_length_and_charset(3 * 8 + 7, default_charset());
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
};
@@ -910,7 +911,7 @@ public:
{
collation.set(system_charset_info);
max_length= 64 * collation.collation->mbmaxlen; // should be enough
- maybe_null= 0;
+ set_persist_maybe_null(0);
};
table_map not_null_tables() const { return 0; }
};
@@ -925,7 +926,7 @@ public:
{
collation.set(system_charset_info);
max_length= 64 * collation.collation->mbmaxlen; // should be enough
- maybe_null= 0;
+ set_persist_maybe_null(0);
};
table_map not_null_tables() const { return 0; }
};
@@ -971,7 +972,8 @@ class Item_func_uncompress: public Item_str_func
String buffer;
public:
Item_func_uncompress(Item *a): Item_str_func(a){}
- void fix_length_and_dec(){ maybe_null= 1; max_length= MAX_BLOB_WIDTH; }
+ void fix_length_and_dec()
+ { set_persist_maybe_null(1); max_length= MAX_BLOB_WIDTH; }
const char *func_name() const{return "uncompress";}
String *val_str(String *) ZLIB_DEPENDED_FUNCTION
};
@@ -1053,7 +1055,7 @@ public:
:Item_str_func(str, num)
{}
void fix_length_and_dec()
- { maybe_null= 1; max_length= MAX_BLOB_WIDTH; }
+ { set_persist_maybe_null(1); max_length= MAX_BLOB_WIDTH; }
/* Mark that collation can change between calls */
bool dynamic_result() { return 1; }
@@ -1072,7 +1074,8 @@ class Item_func_dyncol_list: public Item_str_func
{
public:
Item_func_dyncol_list(Item *str) :Item_str_func(str) {};
- void fix_length_and_dec() { maybe_null= 1; max_length= MAX_BLOB_WIDTH; };
+ void fix_length_and_dec()
+ { set_persist_maybe_null(1); max_length= MAX_BLOB_WIDTH; };
const char *func_name() const{ return "column_list"; }
String *val_str(String *);
};
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 2ee513320ce..8b3ce257642 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -3258,11 +3258,14 @@ int subselect_uniquesubquery_engine::scan_table()
TABLE *table= tab->table;
DBUG_ENTER("subselect_uniquesubquery_engine::scan_table");
- if (table->file->inited)
- table->file->ha_index_end();
-
- if (table->file->ha_rnd_init_with_error(1))
- DBUG_RETURN(1);
+ if ((table->file->inited &&
+ (error= table->file->ha_index_end())) ||
+ (error= table->file->ha_rnd_init(1)))
+ {
+ (void) report_error(table, error);
+ DBUG_RETURN(true);
+ }
+
table->file->extra_opt(HA_EXTRA_CACHE,
current_thd->variables.read_buff_size);
table->null_row= 0;
@@ -3398,8 +3401,13 @@ int subselect_uniquesubquery_engine::exec()
DBUG_RETURN(0);
}
- if (!table->file->inited)
- table->file->ha_index_init(tab->ref.key, 0);
+ if (!table->file->inited &&
+ (error= table->file->ha_index_init(tab->ref.key, 0)))
+ {
+ (void) report_error(table, error);
+ DBUG_RETURN(true);
+ }
+
error= table->file->ha_index_read_map(table->record[0],
tab->ref.key_buff,
make_prev_keypart_map(tab->
@@ -3563,8 +3571,13 @@ int subselect_indexsubquery_engine::exec()
DBUG_RETURN(0);
}
- if (!table->file->inited)
- table->file->ha_index_init(tab->ref.key, 1);
+ if (!table->file->inited &&
+ (error= table->file->ha_index_init(tab->ref.key, 1)))
+ {
+ (void) report_error(table, error);
+ DBUG_RETURN(true);
+ }
+
error= table->file->ha_index_read_map(table->record[0],
tab->ref.key_buff,
make_prev_keypart_map(tab->
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index b1e248032bc..8816e1352a9 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright (c) 2008-2011 Monty Program Ab
+ Copyright (c) 2008, 2013 Monty Program Ab
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
@@ -1097,7 +1097,7 @@ void Aggregator_distinct::endup()
{
/* go over the tree of distinct keys and calculate the aggregate value */
use_distinct_values= TRUE;
- tree->walk(item_sum_distinct_walk, (void*) this);
+ tree->walk(table, item_sum_distinct_walk, (void*) this);
use_distinct_values= FALSE;
}
/* prevent consecutive recalculations */
@@ -2916,13 +2916,12 @@ int group_concat_key_cmp_with_distinct(void* arg, const void* key1,
const void* key2)
{
Item_func_group_concat *item_func= (Item_func_group_concat*)arg;
- TABLE *table= item_func->table;
for (uint i= 0; i < item_func->arg_count_field; i++)
{
Item *item= item_func->args[i];
/*
- If field_item is a const item then either get_tp_table_field returns 0
+ If field_item is a const item then either get_tmp_table_field returns 0
or it is an item over a const table.
*/
if (item->const_item())
@@ -2934,7 +2933,8 @@ int group_concat_key_cmp_with_distinct(void* arg, const void* key1,
*/
Field *field= item->get_tmp_table_field();
int res;
- uint offset= field->offset(field->table->record[0])-table->s->null_bytes;
+ uint offset= (field->offset(field->table->record[0]) -
+ field->table->s->null_bytes);
if((res= field->cmp((uchar*)key1 + offset, (uchar*)key2 + offset)))
return res;
}
@@ -2952,28 +2952,37 @@ int group_concat_key_cmp_with_order(void* arg, const void* key1,
{
Item_func_group_concat* grp_item= (Item_func_group_concat*) arg;
ORDER **order_item, **end;
- TABLE *table= grp_item->table;
for (order_item= grp_item->order, end=order_item+ grp_item->arg_count_order;
order_item < end;
order_item++)
{
Item *item= *(*order_item)->item;
+ /*
+ If field_item is a const item then either get_tmp_table_field returns 0
+ or it is an item over a const table.
+ */
+ if (item->const_item())
+ continue;
/*
We have to use get_tmp_table_field() instead of
real_item()->get_tmp_table_field() because we want the field in
the temporary table, not the original field
+
+ Note that for the case of ROLLUP, field may point to another table
+ tham grp_item->table. This is howver ok as the table definitions are
+ the same.
*/
Field *field= item->get_tmp_table_field();
/*
- If item is a const item then either get_tp_table_field returns 0
+ If item is a const item then either get_tmp_table_field returns 0
or it is an item over a const table.
*/
- if (field && !item->const_item())
+ if (field)
{
int res;
uint offset= (field->offset(field->table->record[0]) -
- table->s->null_bytes);
+ field->table->s->null_bytes);
if ((res= field->cmp((uchar*)key1 + offset, (uchar*)key2 + offset)))
return (*order_item)->asc ? res : -res;
}
@@ -2997,6 +3006,7 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)),
{
Item_func_group_concat *item= (Item_func_group_concat *) item_arg;
TABLE *table= item->table;
+ uint max_length= table->in_use->variables.group_concat_max_len;
String tmp((char *)table->record[1], table->s->reclength,
default_charset_info);
String tmp2;
@@ -3039,7 +3049,7 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)),
item->row_count++;
/* stop if length of result more than max_length */
- if (result->length() > item->max_length)
+ if (result->length() > max_length)
{
int well_formed_error;
CHARSET_INFO *cs= item->collation.collation;
@@ -3052,7 +3062,7 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)),
*/
add_length= cs->cset->well_formed_len(cs,
ptr + old_length,
- ptr + item->max_length,
+ ptr + max_length,
result->length(),
&well_formed_error);
result->length(old_length + add_length);
@@ -3166,12 +3176,13 @@ Item_func_group_concat::Item_func_group_concat(THD *thd,
*/
ORDER *tmp;
if (!(tmp= (ORDER *) thd->alloc(sizeof(ORDER *) * arg_count_order +
- sizeof(ORDER) * arg_count_order)))
+ sizeof(ORDER) * arg_count_order)))
return;
order= (ORDER **)(tmp + arg_count_order);
for (uint i= 0; i < arg_count_order; i++, tmp++)
{
memcpy(tmp, item->order[i], sizeof(ORDER));
+ tmp->next= i == arg_count_order-1 ? 0 : tmp+1;
order[i]= tmp;
}
}
@@ -3216,19 +3227,11 @@ Field *Item_func_group_concat::make_string_field(TABLE *table)
{
Field *field;
DBUG_ASSERT(collation.collation);
- /*
- max_characters is maximum number of characters
- what can fit into max_length size. It's necessary
- to use field size what allows to store group_concat
- result without truncation. For this purpose we use
- max_characters * CS->mbmaxlen.
- */
- const uint32 max_characters= max_length / collation.collation->mbminlen;
- if (max_characters > CONVERT_IF_BIGGER_TO_BLOB)
- field= new Field_blob(max_characters * collation.collation->mbmaxlen,
+ if (too_big_for_varchar())
+ field= new Field_blob(max_length,
maybe_null, name, collation.collation, TRUE);
else
- field= new Field_varstring(max_characters * collation.collation->mbmaxlen,
+ field= new Field_varstring(max_length,
maybe_null, name, table->s, collation.collation);
if (field)
@@ -3343,7 +3346,9 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref)
result.set_charset(collation.collation);
result_field= 0;
null_value= 1;
- max_length= thd->variables.group_concat_max_len;
+ max_length= thd->variables.group_concat_max_len
+ / collation.collation->mbminlen
+ * collation.collation->mbmaxlen;
uint32 offset;
if (separator->needs_conversion(separator->length(), separator->charset(),
@@ -3461,7 +3466,8 @@ bool Item_func_group_concat::setup(THD *thd)
*/
if (!(table= create_tmp_table(thd, tmp_table_param, all_fields,
(ORDER*) 0, 0, TRUE,
- (select_lex->options | thd->variables.option_bits),
+ (select_lex->options |
+ thd->variables.option_bits),
HA_POS_ERROR, (char*) "")))
DBUG_RETURN(TRUE);
table->file->extra(HA_EXTRA_NO_ROWS);
diff --git a/sql/item_sum.h b/sql/item_sum.h
index c8dce60c7d4..40a28d8beae 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -1439,7 +1439,7 @@ public:
virtual Field *make_string_field(TABLE *table);
enum_field_types field_type() const
{
- if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB )
+ if (too_big_for_varchar())
return MYSQL_TYPE_BLOB;
else
return MYSQL_TYPE_VARCHAR;
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 117276e488b..02a7b8511af 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2009, 2011, Monty Program Ab
+ Copyright (c) 2009, 2013, Monty Program Ab
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
@@ -945,7 +945,7 @@ void Item_func_monthname::fix_length_and_dec()
collation.set(cs, DERIVATION_COERCIBLE, repertoire);
decimals=0;
max_length= locale->max_month_name_length * collation.collation->mbmaxlen;
- maybe_null=1;
+ set_persist_maybe_null(1);
}
@@ -1095,7 +1095,7 @@ void Item_func_dayname::fix_length_and_dec()
collation.set(cs, DERIVATION_COERCIBLE, repertoire);
decimals=0;
max_length= locale->max_day_name_length * collation.collation->mbmaxlen;
- maybe_null=1;
+ set_persist_maybe_null(1);
}
@@ -1446,7 +1446,7 @@ void Item_temporal_func::fix_length_and_dec()
{ MAX_DATETIME_WIDTH, MAX_DATETIME_WIDTH, MAX_DATE_WIDTH,
MAX_DATETIME_WIDTH, MIN_TIME_WIDTH };
- maybe_null= true;
+ set_persist_maybe_null(1);
max_length= max_time_type_width[mysql_type_to_time_type(field_type())+2];
if (decimals)
{
@@ -1501,13 +1501,10 @@ bool Item_func_from_days::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
bzero(ltime, sizeof(MYSQL_TIME));
if (get_date_from_daynr((long) value, &ltime->year, &ltime->month,
&ltime->day))
- return (null_value= 1);
-
- if ((fuzzy_date & TIME_NO_ZERO_DATE) && ltime->year == 0)
- return (null_value= 1);
+ return 0;
ltime->time_type= MYSQL_TIMESTAMP_DATE;
- return (null_value= 0);
+ return 0;
}
@@ -1519,7 +1516,7 @@ void Item_func_curdate::fix_length_and_dec()
ltime.hour= ltime.minute= ltime.second= 0;
ltime.time_type= MYSQL_TIMESTAMP_DATE;
Item_datefunc::fix_length_and_dec();
- maybe_null= false;
+ set_persist_maybe_null(0);
}
/**
@@ -1758,7 +1755,7 @@ void Item_func_date_format::fix_length_and_dec()
collation.collation->mbmaxlen;
set_if_smaller(max_length,MAX_BLOB_WIDTH);
}
- maybe_null=1; // If wrong date
+ set_persist_maybe_null(1); // If wrong date
}
@@ -2102,7 +2099,7 @@ void Item_extract::print(String *str, enum_query_type query_type)
void Item_extract::fix_length_and_dec()
{
- maybe_null=1; // If wrong date
+ set_persist_maybe_null(1); // If wrong date
switch (int_type) {
case INTERVAL_YEAR: max_length=4; date_value=1; break;
case INTERVAL_YEAR_MONTH: max_length=6; date_value=1; break;
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index 3af08a8168e..3e3cd698efc 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -82,7 +82,7 @@ public:
{
decimals=0;
max_length=6*MY_CHARSET_BIN_MB_MAXLEN;
- maybe_null=1;
+ set_persist_maybe_null(1);
}
enum_monotonicity_info get_monotonicity_info() const;
longlong val_int_endpoint(bool left_endp, bool *incl_endp);
@@ -105,7 +105,7 @@ public:
{
decimals=0;
max_length=6*MY_CHARSET_BIN_MB_MAXLEN;
- maybe_null=1;
+ set_persist_maybe_null(1);
}
enum_monotonicity_info get_monotonicity_info() const;
longlong val_int_endpoint(bool left_endp, bool *incl_endp);
@@ -138,7 +138,7 @@ public:
{
decimals=0;
max_length=2*MY_CHARSET_BIN_MB_MAXLEN;
- maybe_null=1;
+ set_persist_maybe_null(1);
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
@@ -170,7 +170,7 @@ public:
{
decimals= 0;
fix_char_length(2);
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
@@ -208,7 +208,7 @@ public:
{
decimals= 0;
fix_char_length(3);
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
@@ -229,7 +229,7 @@ public:
{
decimals=0;
max_length=2*MY_CHARSET_BIN_MB_MAXLEN;
- maybe_null=1;
+ set_persist_maybe_null(1);
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
@@ -250,7 +250,7 @@ public:
{
decimals=0;
max_length=2*MY_CHARSET_BIN_MB_MAXLEN;
- maybe_null=1;
+ set_persist_maybe_null(1);
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
@@ -271,7 +271,7 @@ public:
{
decimals=0;
max_length=1*MY_CHARSET_BIN_MB_MAXLEN;
- maybe_null=1;
+ set_persist_maybe_null(1);
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
@@ -292,7 +292,7 @@ public:
{
decimals=0;
max_length=2*MY_CHARSET_BIN_MB_MAXLEN;
- maybe_null=1;
+ set_persist_maybe_null(1);
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
@@ -313,7 +313,7 @@ public:
{
decimals=0;
max_length=2*MY_CHARSET_BIN_MB_MAXLEN;
- maybe_null=1;
+ set_persist_maybe_null(1);
}
};
@@ -327,7 +327,7 @@ public:
{
decimals=0;
max_length=6*MY_CHARSET_BIN_MB_MAXLEN;
- maybe_null=1;
+ set_persist_maybe_null(1);
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
@@ -350,7 +350,7 @@ public:
{
decimals=0;
max_length=4*MY_CHARSET_BIN_MB_MAXLEN;
- maybe_null=1;
+ set_persist_maybe_null(1);
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
@@ -384,7 +384,7 @@ public:
{
decimals= 0;
fix_char_length(1);
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
@@ -419,7 +419,7 @@ public:
decimals= args[0]->decimals;
set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
max_length=17 + (decimals ? decimals + 1 : 0);
- maybe_null= 1;
+ set_persist_maybe_null(1);
}
void find_num_type() { hybrid_type= decimals ? DECIMAL_RESULT : INT_RESULT; }
double real_op() { DBUG_ASSERT(0); return 0; }
@@ -466,7 +466,7 @@ public:
const char *func_name() const { return "time_to_sec"; }
void fix_num_length_and_dec()
{
- maybe_null= true;
+ set_persist_maybe_null(1);
Item_func_seconds_hybrid::fix_num_length_and_dec();
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
@@ -537,7 +537,7 @@ public:
{
store_now_in_TIME(&ltime);
Item_timefunc::fix_length_and_dec();
- maybe_null= false;
+ set_persist_maybe_null(0);
}
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
/*
@@ -619,7 +619,7 @@ public:
{
store_now_in_TIME(&ltime);
Item_temporal_func::fix_length_and_dec();
- maybe_null= false;
+ set_persist_maybe_null(0);
}
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0;
@@ -664,7 +664,7 @@ public:
void update_used_tables()
{
Item_func_now::update_used_tables();
- maybe_null= false;
+ set_persist_maybe_null(0);
used_tables_cache|= RAND_TABLE_BIT;
}
};
@@ -959,7 +959,7 @@ public:
void fix_length_and_dec()
{
decimals=0;
- maybe_null=1;
+ set_persist_maybe_null(1);
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
@@ -981,7 +981,7 @@ public:
void fix_length_and_dec()
{
decimals=0;
- maybe_null=1;
+ set_persist_maybe_null(1);
}
virtual void print(String *str, enum_query_type query_type);
};
@@ -1003,7 +1003,7 @@ public:
const char *func_name() const { return "get_format"; }
void fix_length_and_dec()
{
- maybe_null= 1;
+ set_persist_maybe_null(1);
decimals=0;
fix_length_and_charset(17, default_charset());
}
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
index ae0a74c5ba6..5a824e48b7b 100644
--- a/sql/item_xmlfunc.cc
+++ b/sql/item_xmlfunc.cc
@@ -2604,6 +2604,7 @@ void Item_xml_str_func::fix_length_and_dec()
status_var_increment(current_thd->status_var.feature_xml);
nodeset_func= 0;
+ set_persist_maybe_null(1);
if (agg_arg_charsets_for_comparison(collation, args, arg_count))
return;
diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h
index 800cf6ed760..3356b4ac902 100644
--- a/sql/item_xmlfunc.h
+++ b/sql/item_xmlfunc.h
@@ -34,14 +34,10 @@ protected:
public:
Item_xml_str_func(Item *a, Item *b):
Item_str_func(a,b)
- {
- maybe_null= TRUE;
- }
+ {}
Item_xml_str_func(Item *a, Item *b, Item *c):
Item_str_func(a,b,c)
- {
- maybe_null= TRUE;
- }
+ {}
void fix_length_and_dec();
String *parse_xml(String *raw_xml, String *parsed_xml_buf);
bool check_vcol_func_processor(uchar *int_arg)
diff --git a/sql/log.cc b/sql/log.cc
index 9d476e2a262..0444499c72b 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright (c) 2010-2011 Monty Program Ab
+ Copyright (c) 2009, 2013, Monty Program Ab
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
@@ -1552,10 +1552,7 @@ binlog_trans_log_savepos(THD *thd, my_off_t *pos)
{
DBUG_ENTER("binlog_trans_log_savepos");
DBUG_ASSERT(pos != NULL);
- if (thd_get_ha_data(thd, binlog_hton) == NULL)
- thd->binlog_setup_trx_data();
- binlog_cache_mngr *const cache_mngr=
- (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
+ binlog_cache_mngr *const cache_mngr= thd->binlog_setup_trx_data();
DBUG_ASSERT(mysql_bin_log.is_open());
*pos= cache_mngr->trx_cache.get_byte_position();
DBUG_PRINT("return", ("*pos: %lu", (ulong) *pos));
@@ -2105,15 +2102,17 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv)
binlog_trans_log_savepos(thd, (my_off_t*) sv);
/* Write it to the binary log */
- String log_query;
- if (log_query.append(STRING_WITH_LEN("SAVEPOINT ")) ||
+ char buf[1024];
+ String log_query(buf, sizeof(buf), &my_charset_bin);
+ if (log_query.copy(STRING_WITH_LEN("SAVEPOINT "), &my_charset_bin) ||
append_identifier(thd, &log_query,
thd->lex->ident.str, thd->lex->ident.length))
DBUG_RETURN(1);
int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
Query_log_event qinfo(thd, log_query.ptr(), log_query.length(),
TRUE, FALSE, TRUE, errcode);
- DBUG_RETURN(mysql_bin_log.write(&qinfo));
+ int ret= mysql_bin_log.write(&qinfo);
+ DBUG_RETURN(ret);
}
static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
@@ -2128,8 +2127,9 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
if (unlikely(trans_has_updated_non_trans_table(thd) ||
(thd->variables.option_bits & OPTION_KEEP_LOG)))
{
- String log_query;
- if (log_query.append(STRING_WITH_LEN("ROLLBACK TO ")) ||
+ char buf[1024];
+ String log_query(buf, sizeof(buf), &my_charset_bin);
+ if (log_query.copy(STRING_WITH_LEN("ROLLBACK TO "), &my_charset_bin) ||
append_identifier(thd, &log_query,
thd->lex->ident.str, thd->lex->ident.length))
DBUG_RETURN(1);
@@ -4953,14 +4953,14 @@ bool stmt_has_updated_non_trans_table(const THD* thd)
binlog_hton, which has internal linkage.
*/
-int THD::binlog_setup_trx_data()
+binlog_cache_mngr *THD::binlog_setup_trx_data()
{
DBUG_ENTER("THD::binlog_setup_trx_data");
binlog_cache_mngr *cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
if (cache_mngr)
- DBUG_RETURN(0); // Already set up
+ DBUG_RETURN(cache_mngr); // Already set up
cache_mngr= (binlog_cache_mngr*) my_malloc(sizeof(binlog_cache_mngr), MYF(MY_ZEROFILL));
if (!cache_mngr ||
@@ -4970,18 +4970,18 @@ int THD::binlog_setup_trx_data()
LOG_PREFIX, binlog_cache_size, MYF(MY_WME)))
{
my_free(cache_mngr);
- DBUG_RETURN(1); // Didn't manage to set it up
+ DBUG_RETURN(0); // Didn't manage to set it up
}
thd_set_ha_data(this, binlog_hton, cache_mngr);
- cache_mngr= new (thd_get_ha_data(this, binlog_hton))
+ cache_mngr= new (cache_mngr)
binlog_cache_mngr(max_binlog_stmt_cache_size,
max_binlog_cache_size,
&binlog_stmt_cache_use,
&binlog_stmt_cache_disk_use,
&binlog_cache_use,
&binlog_cache_disk_use);
- DBUG_RETURN(0);
+ DBUG_RETURN(cache_mngr);
}
/*
@@ -5064,9 +5064,7 @@ binlog_start_consistent_snapshot(handlerton *hton, THD *thd)
int err= 0;
DBUG_ENTER("binlog_start_consistent_snapshot");
- thd->binlog_setup_trx_data();
- binlog_cache_mngr *const cache_mngr=
- (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
+ binlog_cache_mngr *const cache_mngr= thd->binlog_setup_trx_data();
/* Server layer calls us with LOCK_commit_ordered locked, so this is safe. */
strmake(cache_mngr->last_commit_pos_file, mysql_bin_log.last_commit_pos_file,
@@ -5178,11 +5176,7 @@ THD::binlog_get_pending_rows_event(bool is_transactional) const
void
THD::binlog_set_pending_rows_event(Rows_log_event* ev, bool is_transactional)
{
- if (thd_get_ha_data(this, binlog_hton) == NULL)
- binlog_setup_trx_data();
-
- binlog_cache_mngr *const cache_mngr=
- (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
+ binlog_cache_mngr *const cache_mngr= binlog_setup_trx_data();
DBUG_ASSERT(cache_mngr);
@@ -5261,12 +5255,18 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd,
/*
Write pending event to the cache.
*/
+ DBUG_EXECUTE_IF("simulate_disk_full_at_flush_pending",
+ {DBUG_SET("+d,simulate_file_write_error");});
if (pending->write(file))
{
set_write_error(thd, is_transactional);
if (check_write_error(thd) && cache_data &&
stmt_has_updated_non_trans_table(thd))
cache_data->set_incident();
+ delete pending;
+ cache_data->set_pending(NULL);
+ DBUG_EXECUTE_IF("simulate_disk_full_at_flush_pending",
+ {DBUG_SET("-d,simulate_file_write_error");});
DBUG_RETURN(1);
}
@@ -5350,12 +5350,10 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
}
else
{
- if (thd->binlog_setup_trx_data())
+ binlog_cache_mngr *const cache_mngr= thd->binlog_setup_trx_data();
+ if (!cache_mngr)
goto err;
- binlog_cache_mngr *const cache_mngr=
- (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
-
is_trans_cache= use_trans_cache(thd, using_trans);
file= cache_mngr->get_binlog_cache_log(is_trans_cache);
cache_data= cache_mngr->get_binlog_cache_data(is_trans_cache);
@@ -7987,8 +7985,9 @@ TC_LOG_BINLOG::log_and_order(THD *thd, my_xid xid, bool all,
int err;
DBUG_ENTER("TC_LOG_BINLOG::log_and_order");
- binlog_cache_mngr *cache_mngr=
- (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
+ binlog_cache_mngr *cache_mngr= thd->binlog_setup_trx_data();
+ if (!cache_mngr)
+ DBUG_RETURN(0);
cache_mngr->using_xa= TRUE;
cache_mngr->xa_xid= xid;
@@ -8536,7 +8535,7 @@ static int show_binlog_vars(THD *thd, SHOW_VAR *var, char *buff)
}
static SHOW_VAR binlog_status_vars_top[]= {
- {"binlog", (char *) &show_binlog_vars, SHOW_FUNC},
+ {"Binlog", (char *) &show_binlog_vars, SHOW_FUNC},
{NullS, NullS, SHOW_LONG}
};
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 87b2f7c4bc8..10f0fe1e931 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2010, 2012, Monty Program Ab
+ Copyright (c) 2009, 2013, Monty Program Ab.
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
@@ -46,7 +46,7 @@
#include "rpl_record.h"
#include "transaction.h"
#include <my_dir.h>
-#include "sql_show.h"
+#include "sql_show.h" // append_identifier
#endif /* MYSQL_CLIENT */
@@ -92,6 +92,23 @@ TYPELIB binlog_checksum_typelib=
*/
#define FMT_G_BUFSIZE(PREC) (3 + (PREC) + 5 + 1)
+/*
+ Explicit instantiation to unsigned int of template available_buffer
+ function.
+*/
+template unsigned int available_buffer<unsigned int>(const char*,
+ const char*,
+ unsigned int);
+
+/*
+ Explicit instantiation to unsigned int of template valid_buffer_range
+ function.
+*/
+template bool valid_buffer_range<unsigned int>(unsigned int,
+ const char*,
+ const char*,
+ unsigned int);
+
/*
replication event checksum is introduced in the following "checksum-home" version.
The checksum-aware servers extract FD's version to decide whether the FD event
@@ -1576,7 +1593,7 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
ev = new Rand_log_event(buf, description_event);
break;
case USER_VAR_EVENT:
- ev = new User_var_log_event(buf, description_event);
+ ev = new User_var_log_event(buf, event_len, description_event);
break;
case FORMAT_DESCRIPTION_EVENT:
ev = new Format_description_log_event(buf, event_len, description_event);
@@ -2301,7 +2318,7 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
for (const uchar *value= m_rows_buf; value < m_rows_end; )
{
size_t length;
- my_b_printf(file, "### %s %s.%s\n",
+ my_b_printf(file, "### %s %`s.%`s\n",
sql_command,
map->get_db_name(), map->get_table_name());
/* Print the first image */
@@ -2467,7 +2484,7 @@ void Query_log_event::pack_info(THD *thd, Protocol *protocol)
{
buf.append(STRING_WITH_LEN("use "));
append_identifier(thd, &buf, db, db_len);
- buf.append("; ");
+ buf.append(STRING_WITH_LEN("; "));
}
if (query && q_len)
buf.append(query, q_len);
@@ -3446,17 +3463,11 @@ void Query_log_event::print_query_header(IO_CACHE* file,
}
else if (db)
{
- /* Room for expand ` to `` + initial/final ` + \0 */
- char buf[FN_REFLEN*2+3];
-
different_db= memcmp(print_event_info->db, db, db_len + 1);
if (different_db)
memcpy(print_event_info->db, db, db_len + 1);
if (db[0] && different_db)
- {
- my_snprintf(buf, sizeof(buf), "%`s", db);
- my_b_printf(file, "use %s%s\n", buf, print_event_info->delimiter);
- }
+ my_b_printf(file, "use %`s%s\n", db, print_event_info->delimiter);
}
end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10);
@@ -3997,7 +4008,7 @@ Default database: '%s'. Query: '%s'",
{
DBUG_PRINT("info",("error ignored"));
clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
- thd->killed= NOT_KILLED;
+ thd->reset_killed();
}
/*
Other cases: mostly we expected no error and get one.
@@ -5290,7 +5301,7 @@ void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info,
}
if (db && db[0] && different_db)
- my_b_printf(&cache, "%suse %s%s\n",
+ my_b_printf(&cache, "%suse %`s%s\n",
commented ? "# " : "",
db, print_event_info->delimiter);
@@ -5342,7 +5353,7 @@ void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info,
{
if (i)
my_b_printf(&cache, ",");
- my_b_printf(&cache, "%s", field);
+ my_b_printf(&cache, "%`s", field);
field += field_lens[i] + 1;
}
@@ -6482,19 +6493,35 @@ void User_var_log_event::pack_info(THD *thd, Protocol* protocol)
User_var_log_event::
-User_var_log_event(const char* buf,
+User_var_log_event(const char* buf, uint event_len,
const Format_description_log_event* description_event)
:Log_event(buf, description_event)
#ifndef MYSQL_CLIENT
, deferred(false)
#endif
{
+ bool error= false;
+ const char* buf_start= buf;
/* The Post-Header is empty. The Variable Data part begins immediately. */
const char *start= buf;
buf+= description_event->common_header_len +
description_event->post_header_len[USER_VAR_EVENT-1];
name_len= uint4korr(buf);
name= (char *) buf + UV_NAME_LEN_SIZE;
+
+ /*
+ We don't know yet is_null value, so we must assume that name_len
+ may have the bigger value possible, is_null= True and there is no
+ payload for val.
+ */
+ if (0 == name_len ||
+ !valid_buffer_range<uint>(name_len, buf_start, name,
+ event_len - UV_VAL_IS_NULL))
+ {
+ error= true;
+ goto err;
+ }
+
buf+= UV_NAME_LEN_SIZE + name_len;
is_null= (bool) *buf;
flags= User_var_log_event::UNDEF_F; // defaults to UNDEF_F
@@ -6507,6 +6534,14 @@ User_var_log_event(const char* buf,
}
else
{
+ if (!valid_buffer_range<uint>(UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE
+ + UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE,
+ buf_start, buf, event_len))
+ {
+ error= true;
+ goto err;
+ }
+
type= (Item_result) buf[UV_VAL_IS_NULL];
charset_number= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE);
val_len= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
@@ -6514,6 +6549,12 @@ User_var_log_event(const char* buf,
val= (char *) (buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE);
+ if (!valid_buffer_range<uint>(val_len, buf_start, val, event_len))
+ {
+ error= true;
+ goto err;
+ }
+
/**
We need to check if this is from an old server
that did not pack information for flags.
@@ -6548,6 +6589,10 @@ User_var_log_event(const char* buf,
val_len);
}
}
+
+err:
+ if (error)
+ name= 0;
}
@@ -6693,8 +6738,9 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
char *hex_str;
CHARSET_INFO *cs;
- if (!(hex_str= (char *)my_alloca(2*val_len+1+2))) // 2 hex digits / byte
- break; // no error, as we are 'void'
+ hex_str= (char *)my_malloc(2*val_len+1+2,MYF(MY_WME)); // 2 hex digits / byte
+ if (!hex_str)
+ return;
str_to_hex(hex_str, val, val_len);
/*
For proper behaviour when mysqlbinlog|mysql, we need to explicitely
@@ -6712,7 +6758,7 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
my_b_printf(&cache, ":=_%s %s COLLATE `%s`%s\n",
cs->csname, hex_str, cs->name,
print_event_info->delimiter);
- my_afree(hex_str);
+ my_free(hex_str);
}
break;
case ROW_RESULT:
@@ -7951,9 +7997,9 @@ void Execute_load_query_log_event::pack_info(THD *thd, Protocol *protocol)
buf.real_alloc(9 + db_len + q_len + 10 + 21);
if (db && db_len)
{
- if (buf.append("use ") ||
+ if (buf.append(STRING_WITH_LEN("use ")) ||
append_identifier(thd, &buf, db, db_len) ||
- buf.append("; "))
+ buf.append(STRING_WITH_LEN("; ")))
return;
}
if (query && q_len && buf.append(query, q_len))
diff --git a/sql/log_event.h b/sql/log_event.h
index dfbefdb359e..ff13cab9cd5 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -2740,7 +2740,7 @@ public:
void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
- User_var_log_event(const char* buf,
+ User_var_log_event(const char* buf, uint event_len,
const Format_description_log_event *description_event);
~User_var_log_event() {}
Log_event_type get_type_code() { return USER_VAR_EVENT;}
@@ -2752,9 +2752,9 @@ public:
and which case the applier adjusts execution path.
*/
bool is_deferred() { return deferred; }
- void set_deferred() { deferred= val; }
+ void set_deferred() { deferred= true; }
#endif
- bool is_valid() const { return 1; }
+ bool is_valid() const { return name != 0; }
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index bd837adc9d9..e9afe474418 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -725,7 +725,10 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
int error;
/* We have a key: search the table using the index */
if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE)))
+ {
+ table->file->print_error(error, MYF(0));
DBUG_RETURN(error);
+ }
/*
Don't print debug messages when running valgrind since they can
@@ -858,7 +861,7 @@ static int find_and_fetch_row(TABLE *table, uchar *key)
default:
table->file->print_error(error, MYF(0));
DBUG_PRINT("info", ("Record not found"));
- table->file->ha_rnd_end();
+ (void) table->file->ha_rnd_end();
DBUG_RETURN(error);
}
}
@@ -2410,7 +2413,7 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli)
continue;
DBUG_PRINT("info",("no record matching the given row found"));
table->file->print_error(error, MYF(0));
- table->file->ha_index_end();
+ (void) table->file->ha_index_end();
DBUG_RETURN(error);
}
}
diff --git a/sql/mdl.cc b/sql/mdl.cc
index 97858646652..7fd522d053a 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -14,7 +14,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "mdl.h"
+#include "sql_class.h"
#include "debug_sync.h"
#include <hash.h>
#include <mysqld_error.h>
@@ -1185,7 +1185,7 @@ MDL_wait::timed_wait(THD *thd, struct timespec *abs_timeout,
wait_state_name);
thd_wait_begin(thd, THD_WAIT_META_DATA_LOCK);
- while (!m_wait_status && !thd_killed(thd) &&
+ while (!m_wait_status && !thd->killed &&
wait_result != ETIMEDOUT && wait_result != ETIME)
{
wait_result= mysql_cond_timedwait(&m_COND_wait_status, &m_LOCK_wait_status,
@@ -1207,7 +1207,7 @@ MDL_wait::timed_wait(THD *thd, struct timespec *abs_timeout,
false, which means that the caller intends to restart the
wait.
*/
- if (thd_killed(thd))
+ if (thd->killed)
m_wait_status= KILLED;
else if (set_status_on_timeout)
m_wait_status= TIMEOUT;
@@ -2078,7 +2078,11 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
*/
m_wait.reset_status();
- if (lock->needs_notification(ticket))
+ /*
+ Don't break conflicting locks if timeout is 0 as 0 is used
+ To check if there is any conflicting locks...
+ */
+ if (lock->needs_notification(ticket) && lock_wait_timeout)
lock->notify_conflicting_locks(this);
mysql_prlock_unlock(&lock->m_rwlock);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 0704a016fed..202cabaec58 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -709,8 +709,8 @@ char *opt_logname, *opt_slow_logname, *opt_bin_logname;
static volatile sig_atomic_t kill_in_progress;
my_bool opt_stack_trace;
-my_bool opt_expect_abort= 0;
-static my_bool opt_bootstrap, opt_myisam_log;
+my_bool opt_expect_abort= 0, opt_bootstrap= 0;
+static my_bool opt_myisam_log;
static int cleanup_done;
static ulong opt_specialflag;
static char *opt_binlog_index_name;
@@ -5445,7 +5445,7 @@ void create_thread_to_handle_connection(THD *thd)
if (cached_thread_count > wake_thread)
{
/* Get thread from cache */
- thread_cache.append(thd);
+ thread_cache.push_back(thd);
wake_thread++;
mysql_cond_signal(&COND_thread_cache);
}
diff --git a/sql/mysqld.h b/sql/mysqld.h
index 520fa03bdbf..6bde25f08fb 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -77,7 +77,7 @@ extern CHARSET_INFO *character_set_filesystem;
extern MY_BITMAP temp_pool;
extern bool opt_large_files, server_id_supplied;
extern bool opt_update_log, opt_bin_log, opt_error_log;
-extern my_bool opt_log, opt_slow_log;
+extern my_bool opt_log, opt_slow_log, opt_bootstrap;
extern my_bool opt_backup_history_log;
extern my_bool opt_backup_progress_log;
extern ulonglong log_output_options;
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index d3c2ec5a132..7a3a0c94a18 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -117,6 +117,7 @@
#include "records.h" // init_read_record, end_read_record
#include <m_ctype.h>
#include "sql_select.h"
+#include "filesort.h" // filesort_free_buffers
#ifndef EXTRA_DEBUG
#define test_rb_tree(A,B) {}
@@ -1790,8 +1791,6 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr,
bzero((char*) &alloc,sizeof(alloc));
file= head->file;
record= head->record[0];
- save_read_set= head->read_set;
- save_write_set= head->write_set;
/* Allocate a bitmap for used columns (Q: why not on MEM_ROOT?) */
if (!(bitmap= (my_bitmap_map*) my_malloc(head->s->column_bitmap_size,
@@ -1861,7 +1860,6 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT()
free_root(&alloc,MYF(0));
my_free(column_bitmap.bitmap);
}
- head->column_bitmaps_set(save_read_set, save_write_set);
my_free(mrr_buf_desc);
DBUG_VOID_RETURN;
}
@@ -1895,7 +1893,8 @@ int QUICK_INDEX_SORT_SELECT::init()
int QUICK_INDEX_SORT_SELECT::reset()
{
DBUG_ENTER("QUICK_INDEX_SORT_SELECT::reset");
- DBUG_RETURN(read_keys_and_merge());
+ const int retval= read_keys_and_merge();
+ DBUG_RETURN(retval);
}
bool
@@ -2006,6 +2005,8 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
handler *save_file= file, *org_file;
my_bool org_key_read;
THD *thd= head->in_use;
+ MY_BITMAP * const save_read_set= head->read_set;
+ MY_BITMAP * const save_write_set= head->write_set;
DBUG_ENTER("QUICK_RANGE_SELECT::init_ror_merged_scan");
in_ror_merged_scan= 1;
@@ -2056,6 +2057,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
last_rowid= file->ref;
end:
+ DBUG_ASSERT(head->read_set == &column_bitmap);
/*
We are only going to read key fields and call position() on 'file'
The following sets head->tmp_set to only use this key and then updates
@@ -2069,7 +2071,8 @@ end:
if (!head->no_keyread)
{
doing_key_read= 1;
- head->mark_columns_used_by_index(index);
+ head->mark_columns_used_by_index_no_reset(index, head->read_set);
+ head->enable_keyread();
}
head->prepare_for_position();
@@ -2091,8 +2094,9 @@ end:
head->file= org_file;
head->key_read= org_key_read;
- bitmap_copy(&column_bitmap, head->read_set);
- head->column_bitmaps_set(&column_bitmap, &column_bitmap);
+
+ /* Restore head->read_set (and write_set) to what they had before the call */
+ head->column_bitmaps_set(save_read_set, save_write_set);
if (reset())
{
@@ -2142,16 +2146,27 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler)
There is no use of this->file. Use it for the first of merged range
selects.
*/
- if (quick->init_ror_merged_scan(TRUE))
- DBUG_RETURN(1);
+ int error= quick->init_ror_merged_scan(TRUE);
+ if (error)
+ DBUG_RETURN(error);
quick->file->extra(HA_EXTRA_KEYREAD_PRESERVE_FIELDS);
}
while ((cur= quick_it++))
{
quick= cur->quick;
+#ifndef DBUG_OFF
+ const MY_BITMAP * const save_read_set= quick->head->read_set;
+ const MY_BITMAP * const save_write_set= quick->head->write_set;
+#endif
if (quick->init_ror_merged_scan(FALSE))
DBUG_RETURN(1);
quick->file->extra(HA_EXTRA_KEYREAD_PRESERVE_FIELDS);
+
+ // Sets are shared by all members of "quick_selects" so must not change
+#ifndef DBUG_OFF
+ DBUG_ASSERT(quick->head->read_set == save_read_set);
+ DBUG_ASSERT(quick->head->write_set == save_write_set);
+#endif
/* All merged scans share the same record buffer in intersection. */
quick->record= head->record[0];
}
@@ -2327,8 +2342,8 @@ int QUICK_ROR_UNION_SELECT::reset()
List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
while ((quick= it++))
{
- if (quick->reset())
- DBUG_RETURN(1);
+ if ((error= quick->reset()))
+ DBUG_RETURN(error);
if ((error= quick->get_next()))
{
if (error == HA_ERR_END_OF_FILE)
@@ -2339,10 +2354,10 @@ int QUICK_ROR_UNION_SELECT::reset()
queue_insert(&queue, (uchar*)quick);
}
- if (head->file->ha_rnd_init_with_error(1))
+ if ((error= head->file->ha_rnd_init(1)))
{
DBUG_PRINT("error", ("ROR index_merge rnd_init call failed"));
- DBUG_RETURN(1);
+ DBUG_RETURN(error);
}
DBUG_RETURN(0);
@@ -10610,7 +10625,10 @@ int read_keys_and_merge_scans(THD *thd,
*unique_ptr= unique;
}
else
+ {
unique->reset();
+ filesort_free_buffers(head, false);
+ }
DBUG_ASSERT(file->ref_length == unique->get_size());
DBUG_ASSERT(thd->variables.sortbuff_size == unique->get_max_in_memory_size());
@@ -10769,6 +10787,13 @@ int QUICK_INDEX_INTERSECT_SELECT::get_next()
If a Clustered PK scan is present, it is used only to check if row
satisfies its condition (and never used for row retrieval).
+ Locking: to ensure that exclusive locks are only set on records that
+ are included in the final result we must release the lock
+ on all rows we read but do not include in the final result. This
+ must be done on each index that reads the record and the lock
+ must be released using the same handler (the same quick object) as
+ used when reading the record.
+
RETURN
0 - Ok
other - Error code if any error occurred.
@@ -10779,6 +10804,12 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
List_iterator_fast<QUICK_SELECT_WITH_RECORD> quick_it(quick_selects);
QUICK_SELECT_WITH_RECORD *qr;
QUICK_RANGE_SELECT* quick;
+
+ /* quick that reads the given rowid first. This is needed in order
+ to be able to unlock the row using the same handler object that locked
+ it */
+ QUICK_RANGE_SELECT* quick_with_last_rowid;
+
int error, cmp;
uint last_rowid_count=0;
DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::get_next");
@@ -10792,7 +10823,10 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
if (cpk_quick)
{
while (!error && !cpk_quick->row_in_ranges())
+ {
+ quick->file->unlock_row(); /* row not in range; unlock */
error= quick->get_next();
+ }
}
if (error)
DBUG_RETURN(error);
@@ -10804,6 +10838,7 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
quick->file->position(quick->record);
memcpy(last_rowid, quick->file->ref, head->file->ref_length);
last_rowid_count= 1;
+ quick_with_last_rowid= quick;
while (last_rowid_count < quick_selects.elements)
{
@@ -10817,9 +10852,17 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
do
{
if ((error= quick->get_next()))
+ {
+ quick_with_last_rowid->file->unlock_row();
DBUG_RETURN(error);
+ }
quick->file->position(quick->record);
cmp= head->file->cmp_ref(quick->file->ref, last_rowid);
+ if (cmp < 0)
+ {
+ /* This row is being skipped. Release lock on it. */
+ quick->file->unlock_row();
+ }
} while (cmp < 0);
key_copy(qr->key_tuple, record, head->key_info + quick->index,
@@ -10833,13 +10876,19 @@ int QUICK_ROR_INTERSECT_SELECT::get_next()
{
while (!cpk_quick->row_in_ranges())
{
+ quick->file->unlock_row(); /* row not in range; unlock */
if ((error= quick->get_next()))
+ {
+ quick_with_last_rowid->file->unlock_row();
DBUG_RETURN(error);
+ }
}
quick->file->position(quick->record);
}
memcpy(last_rowid, quick->file->ref, head->file->ref_length);
+ quick_with_last_rowid->file->unlock_row();
last_rowid_count= 1;
+ quick_with_last_rowid= quick;
//save the fields here
key_copy(qr->key_tuple, record, head->key_info + quick->index,
@@ -10947,9 +10996,15 @@ int QUICK_RANGE_SELECT::reset()
uchar *mrange_buff;
int error;
HANDLER_BUFFER empty_buf;
+ MY_BITMAP * const save_read_set= head->read_set;
+ MY_BITMAP * const save_write_set= head->write_set;
DBUG_ENTER("QUICK_RANGE_SELECT::reset");
last_range= NULL;
cur_range= (QUICK_RANGE**) ranges.buffer;
+ RANGE_SEQ_IF seq_funcs= {NULL, quick_range_seq_init, quick_range_seq_next, 0, 0};
+
+ if (in_ror_merged_scan)
+ head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap);
if (file->inited == handler::RND)
{
@@ -10960,10 +11015,13 @@ int QUICK_RANGE_SELECT::reset()
if (file->inited == handler::NONE)
{
- if (in_ror_merged_scan)
- head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap);
+ DBUG_EXECUTE_IF("bug14365043_2",
+ DBUG_SET("+d,ha_index_init_fail"););
if ((error= file->ha_index_init(index,1)))
- DBUG_RETURN(error);
+ {
+ file->print_error(error, MYF(0));
+ goto err;
+ }
}
/* Allocate buffer if we need one but haven't allocated it yet */
@@ -10998,10 +11056,14 @@ int QUICK_RANGE_SELECT::reset()
if (!mrr_buf_desc)
empty_buf.buffer= empty_buf.buffer_end= empty_buf.end_of_used_area= NULL;
- RANGE_SEQ_IF seq_funcs= {NULL, quick_range_seq_init, quick_range_seq_next, 0, 0};
error= file->multi_range_read_init(&seq_funcs, (void*)this, ranges.elements,
mrr_flags, mrr_buf_desc? mrr_buf_desc:
&empty_buf);
+err:
+ /* Restore bitmaps set on entry */
+ if (in_ror_merged_scan)
+ head->column_bitmaps_set_no_signal(save_read_set, save_write_set);
+
DBUG_RETURN(error);
}
@@ -11024,6 +11086,9 @@ int QUICK_RANGE_SELECT::reset()
int QUICK_RANGE_SELECT::get_next()
{
range_id_t dummy;
+ MY_BITMAP * const save_read_set= head->read_set;
+ MY_BITMAP * const save_write_set= head->write_set;
+
DBUG_ENTER("QUICK_RANGE_SELECT::get_next");
if (in_ror_merged_scan)
{
@@ -13169,7 +13234,10 @@ int QUICK_GROUP_MIN_MAX_SELECT::reset(void)
head->enable_keyread(); /* We need only the key attributes */
}
if ((result= file->ha_index_init(index,1)))
+ {
+ head->file->print_error(result, MYF(0));
DBUG_RETURN(result);
+ }
if (quick_prefix_select && quick_prefix_select->reset())
DBUG_RETURN(1);
result= file->ha_index_last(record);
@@ -13344,9 +13412,10 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min()
*/
if (min_max_arg_part && min_max_arg_part->field->is_null())
{
+ uchar *tmp_key_buff= (uchar*)my_alloca(max_used_key_length);
/* Find the first subsequent record without NULL in the MIN/MAX field. */
- key_copy(tmp_record, record, index_info, max_used_key_length);
- result= file->ha_index_read_map(record, tmp_record,
+ key_copy(tmp_key_buff, record, index_info, max_used_key_length);
+ result= file->ha_index_read_map(record, tmp_key_buff,
make_keypart_map(real_key_parts),
HA_READ_AFTER_KEY);
/*
@@ -13362,10 +13431,11 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min()
if (!result)
{
if (key_cmp(index_info->key_part, group_prefix, real_prefix_len))
- key_restore(record, tmp_record, index_info, 0);
+ key_restore(record, tmp_key_buff, index_info, 0);
}
else if (result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE)
result= 0; /* There is a result in any case. */
+ my_afree(tmp_key_buff);
}
}
diff --git a/sql/opt_range.h b/sql/opt_range.h
index efb45f96345..c59b2a7eb02 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -430,7 +430,7 @@ protected:
/* Members to deal with case when this quick select is a ROR-merged scan */
bool in_ror_merged_scan;
- MY_BITMAP column_bitmap, *save_read_set, *save_write_set;
+ MY_BITMAP column_bitmap;
bool free_file; /* TRUE <=> this->file is "owned" by this quick select */
/* Range pointers to be used when not using MRR interface */
diff --git a/sql/opt_range_mrr.cc b/sql/opt_range_mrr.cc
index a4345059f85..1f4e36178db 100644
--- a/sql/opt_range_mrr.cc
+++ b/sql/opt_range_mrr.cc
@@ -248,6 +248,7 @@ walk_up_n_right:
/* Here minimum contains also function code bits, and maximum is +inf */
range->start_key.key= seq->param->min_key;
range->start_key.length= min_key_length;
+ range->start_key.keypart_map= make_prev_keypart_map(cur->min_key_parts);
range->start_key.flag= (ha_rkey_function) (cur->min_key_flag ^ GEOM_FLAG);
}
else
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index 75566b97d3b..e149de6fe53 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -853,8 +853,7 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs)
create a blob column because item->max_length is too big.
The following check is copied from Item::make_string_field():
*/
- if (inner->max_length / inner->collation.collation->mbmaxlen >
- CONVERT_IF_BIGGER_TO_BLOB)
+ if (inner->too_big_for_varchar())
{
DBUG_RETURN(FALSE);
}
@@ -3868,6 +3867,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
fn_format(path, path, mysql_tmpdir, "", MY_REPLACE_EXT|MY_UNPACK_FILENAME);
/* STEP 2: Figure if we'll be using a key or blob+constraint */
+ /* it always has my_charset_bin, so mbmaxlen==1 */
if (uniq_tuple_length_arg >= CONVERT_IF_BIGGER_TO_BLOB)
using_unique_constraint= TRUE;
diff --git a/sql/records.cc b/sql/records.cc
index c98532c1424..d28799ea9d2 100644
--- a/sql/records.cc
+++ b/sql/records.cc
@@ -68,6 +68,7 @@ static int rr_index_desc(READ_RECORD *info);
void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
bool print_error, uint idx, bool reverse)
{
+ int error;
empty_record(table);
bzero((char*) info,sizeof(*info));
info->thd= thd;
@@ -77,8 +78,13 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
info->unlock_row= rr_unlock_row;
table->status=0; /* And it's always found */
- if (!table->file->inited)
- table->file->ha_index_init(idx, 1);
+ if (!table->file->inited &&
+ (error= table->file->ha_index_init(idx, 1)))
+ {
+ if (print_error)
+ table->file->print_error(error, MYF(0));
+ }
+
/* read_record will be changed to rr_index in rr_index_first */
info->read_record= reverse ? rr_index_last : rr_index_first;
}
diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h
index 48fdd3937c5..d52b2992afd 100644
--- a/sql/rpl_mi.h
+++ b/sql/rpl_mi.h
@@ -71,9 +71,9 @@ class Master_info : public Slave_reporting_capability
/* the variables below are needed because we can change masters on the fly */
char master_log_name[FN_REFLEN+6]; /* Room for multi-*/
- char host[HOSTNAME_LENGTH+1];
- char user[USERNAME_LENGTH+1];
- char password[MAX_PASSWORD_LENGTH+1];
+ char host[HOSTNAME_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
+ char user[USERNAME_LENGTH*+1];
+ char password[MAX_PASSWORD_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
LEX_STRING connection_name; /* User supplied connection name */
LEX_STRING cmp_connection_name; /* Connection name in lower case */
bool ssl; // enables use of SSL connection if true
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 17919d3c1f1..d5cd6a3efed 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -181,7 +181,8 @@ a file name for --relay-log-index option", opt_relaylog_index_name);
ln= rli->relay_log.generate_name(opt_relay_logname, "-relay-bin",
1, buf);
/* We send the warning only at startup, not after every RESET SLAVE */
- if (!opt_relay_logname && !opt_relaylog_index_name && !name_warning_sent)
+ if (!opt_relay_logname && !opt_relaylog_index_name && !name_warning_sent &&
+ !opt_bootstrap)
{
/*
User didn't give us info to name the relay log index file.
diff --git a/sql/sp.cc b/sql/sp.cc
index 0d9bbb37ede..b80fa968483 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -1418,7 +1418,6 @@ bool lock_db_routines(THD *thd, char *db)
{
TABLE *table;
uint key_len;
- int nxtres= 0;
Open_tables_backup open_tables_state_backup;
MDL_request_list mdl_requests;
Lock_db_routines_error_handler err_handler;
@@ -1446,7 +1445,13 @@ bool lock_db_routines(THD *thd, char *db)
table->field[MYSQL_PROC_FIELD_DB]->store(db, strlen(db), system_charset_info);
key_len= table->key_info->key_part[0].store_length;
table->field[MYSQL_PROC_FIELD_DB]->get_key_image(keybuf, key_len, Field::itRAW);
- table->file->ha_index_init(0, 1);
+ int nxtres= table->file->ha_index_init(0, 1);
+ if (nxtres)
+ {
+ table->file->print_error(nxtres, MYF(0));
+ close_system_tables(thd, &open_tables_state_backup);
+ DBUG_RETURN(true);
+ }
if (! table->file->ha_index_read_map(table->record[0], keybuf, (key_part_map)1,
HA_READ_KEY_EXACT))
@@ -1509,7 +1514,11 @@ sp_drop_db_routines(THD *thd, char *db)
table->field[MYSQL_PROC_FIELD_DB]->get_key_image(keybuf, key_len, Field::itRAW);
ret= SP_OK;
- table->file->ha_index_init(0, 1);
+ if (table->file->ha_index_init(0, 1))
+ {
+ ret= SP_KEY_NOT_FOUND;
+ goto err_idx_init;
+ }
if (!table->file->ha_index_read_map(table->record[0], keybuf, (key_part_map)1,
HA_READ_KEY_EXACT))
{
@@ -1535,6 +1544,7 @@ sp_drop_db_routines(THD *thd, char *db)
}
table->file->ha_index_end();
+err_idx_init:
close_thread_tables(thd);
/*
Make sure to only release the MDL lock on mysql.proc, not other
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 0dec9b5f145..cb9c0325845 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -4019,8 +4019,6 @@ typedef struct st_sp_table
Multi-set key:
db_name\0table_name\0alias\0 - for normal tables
db_name\0table_name\0 - for temporary tables
- Note that in both cases we don't take last '\0' into account when
- we count length of key.
*/
LEX_STRING qname;
uint db_length, table_name_length;
@@ -4077,19 +4075,26 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
for (; table ; table= table->next_global)
if (!table->derived && !table->schema_table)
{
- char tname[(SAFE_NAME_LEN + 1) * 3]; // db\0table\0alias\0
- uint tlen, alen;
-
- tlen= table->db_length;
- memcpy(tname, table->db, tlen);
- tname[tlen++]= '\0';
- memcpy(tname+tlen, table->table_name, table->table_name_length);
- tlen+= table->table_name_length;
- tname[tlen++]= '\0';
- alen= strlen(table->alias);
- memcpy(tname+tlen, table->alias, alen);
- tlen+= alen;
- tname[tlen]= '\0';
+ /*
+ Structure of key for the multi-set is "db\0table\0alias\0".
+ Since "alias" part can have arbitrary length we use String
+ object to construct the key. By default String will use
+ buffer allocated on stack with NAME_LEN bytes reserved for
+ alias, since in most cases it is going to be smaller than
+ NAME_LEN bytes.
+ */
+ char tname_buff[(SAFE_NAME_LEN + 1) * 3];
+ String tname(tname_buff, sizeof(tname_buff), &my_charset_bin);
+ uint temp_table_key_length;
+
+ tname.length(0);
+ tname.append(table->db, table->db_length);
+ tname.append('\0');
+ tname.append(table->table_name, table->table_name_length);
+ tname.append('\0');
+ temp_table_key_length= tname.length();
+ tname.append(table->alias);
+ tname.append('\0');
/*
Upgrade the lock type because this table list will be used
@@ -4104,9 +4109,10 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
(and therefore should not be prelocked). Otherwise we will erroneously
treat table with same name but with different alias as non-temporary.
*/
- if ((tab= (SP_TABLE*) my_hash_search(&m_sptabs, (uchar *)tname, tlen)) ||
- ((tab= (SP_TABLE*) my_hash_search(&m_sptabs, (uchar *)tname,
- tlen - alen - 1)) &&
+ if ((tab= (SP_TABLE*) my_hash_search(&m_sptabs, (uchar *)tname.ptr(),
+ tname.length())) ||
+ ((tab= (SP_TABLE*) my_hash_search(&m_sptabs, (uchar *)tname.ptr(),
+ temp_table_key_length)) &&
tab->temp))
{
if (tab->lock_type < table->lock_type)
@@ -4125,11 +4131,11 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
lex_for_tmp_check->create_info.options & HA_LEX_CREATE_TMP_TABLE)
{
tab->temp= TRUE;
- tab->qname.length= tlen - alen - 1;
+ tab->qname.length= temp_table_key_length;
}
else
- tab->qname.length= tlen;
- tab->qname.str= (char*) thd->memdup(tname, tab->qname.length + 1);
+ tab->qname.length= tname.length();
+ tab->qname.str= (char*) thd->memdup(tname.ptr(), tab->qname.length);
if (!tab->qname.str)
return FALSE;
tab->table_name_length= table->table_name_length;
@@ -4198,7 +4204,7 @@ sp_head::add_used_tables_to_table_list(THD *thd,
if (!(tab_buff= (char *)thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST)) *
stab->lock_count)) ||
!(key_buff= (char*)thd->memdup(stab->qname.str,
- stab->qname.length + 1)))
+ stab->qname.length)))
DBUG_RETURN(FALSE);
for (uint j= 0; j < stab->lock_count; j++)
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index 6fe4be989db..30acfebabb2 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -67,19 +67,15 @@ sp_rcontext::~sp_rcontext()
bool sp_rcontext::init(THD *thd)
{
uint handler_count= m_root_parsing_ctx->max_handler_index();
- uint i;
in_sub_stmt= thd->in_sub_stmt;
if (init_var_table(thd) || init_var_items())
return TRUE;
- if (!(m_raised_conditions= new (thd->mem_root) MYSQL_ERROR[handler_count]))
+ if (!(m_raised_conditions= new (thd->mem_root) Sql_condition_info[handler_count]))
return TRUE;
- for (i= 0; i<handler_count; i++)
- m_raised_conditions[i].init(thd->mem_root);
-
return
!(m_handler=
(sp_handler_t*)thd->alloc(handler_count * sizeof(sp_handler_t))) ||
@@ -419,7 +415,7 @@ sp_rcontext::activate_handler(THD *thd,
/* Reset error state. */
thd->clear_error();
- thd->killed= NOT_KILLED; // Some errors set thd->killed
+ thd->reset_killed(); // Some errors set thd->killed
// (e.g. "bad data").
/* Return IP of the activated SQL handler. */
@@ -446,13 +442,12 @@ sp_rcontext::exit_handler()
DBUG_VOID_RETURN;
}
-MYSQL_ERROR*
-sp_rcontext::raised_condition() const
+Sql_condition_info* sp_rcontext::raised_condition() const
{
if (m_ihsp > 0)
{
uint hindex= m_in_handler[m_ihsp - 1].index;
- MYSQL_ERROR *raised= & m_raised_conditions[hindex];
+ Sql_condition_info *raised= & m_raised_conditions[hindex];
return raised;
}
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index 3d976f94381..5008a73d96c 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -58,6 +58,46 @@ typedef struct
uint index;
} sp_active_handler_t;
+
+class Sql_condition_info : public Sql_alloc
+{
+public:
+ /** SQL error code. */
+ uint m_sql_errno;
+
+ /** Error level. */
+ MYSQL_ERROR::enum_warning_level m_level;
+
+ /** SQLSTATE. */
+ char m_sql_state[SQLSTATE_LENGTH + 1];
+
+ /** Text message. */
+ char m_message[MYSQL_ERRMSG_SIZE];
+
+ void set(uint sql_errno, const char* sqlstate,
+ MYSQL_ERROR::enum_warning_level level,
+ const char* msg)
+ {
+ m_sql_errno= sql_errno;
+ m_level= level;
+
+ memcpy(m_sql_state, sqlstate, SQLSTATE_LENGTH);
+ m_sql_state[SQLSTATE_LENGTH]= '\0';
+
+ strncpy(m_message, msg, MYSQL_ERRMSG_SIZE);
+ }
+
+ void clear()
+ {
+ m_sql_errno= 0;
+ m_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
+
+ m_sql_state[0]= '\0';
+ m_message[0]= '\0';
+ }
+};
+
+
/*
This class is a runtime context of a Stored Routine. It is used in an
execution and is intended to contain all dynamic objects (i.e. objects, which
@@ -146,8 +186,7 @@ class sp_rcontext : public Sql_alloc
MYSQL_ERROR::enum_warning_level level,
const char *msg);
- MYSQL_ERROR *
- raised_condition() const;
+ Sql_condition_info *raised_condition() const;
void
push_hstack(uint h);
@@ -232,7 +271,7 @@ private:
SQL conditions caught by each handler.
This is an array indexed by handler index.
*/
- MYSQL_ERROR *m_raised_conditions;
+ Sql_condition_info *m_raised_conditions;
uint m_hcount; // Stack pointer for m_handler
uint *m_hstack; // Return stack for continue handlers
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 2d1493b17de..2f854251ce5 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright (c) 2009-2011, Monty Program Ab
+ Copyright (c) 2009, 2013, Monty Program Ab
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
@@ -50,6 +50,7 @@
#include "sql_connect.h"
#include "hostname.h"
#include "sql_db.h"
+#include "sql_array.h"
bool mysql_user_table_is_in_short_password_format= false;
@@ -560,6 +561,18 @@ static bool update_user_table(THD *thd, TABLE *table, const char *host,
static my_bool acl_load(THD *thd, TABLE_LIST *tables);
static my_bool grant_load(THD *thd, TABLE_LIST *tables);
static inline void get_grantor(THD *thd, char* grantor);
+/*
+ Enumeration of various ACL's and Hashes used in handle_grant_struct()
+*/
+enum enum_acl_lists
+{
+ USER_ACL= 0,
+ DB_ACL,
+ COLUMN_PRIVILEGES_HASH,
+ PROC_PRIVILEGES_HASH,
+ FUNC_PRIVILEGES_HASH,
+ PROXY_USERS_ACL
+};
/*
Convert scrambled password to binary form, according to scramble type,
@@ -767,7 +780,12 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
convert db to lower case and give a warning if the db wasn't
already in lower case
*/
- (void) strmov(tmp_name, host.db);
+ char *end = strnmov(tmp_name, host.db, sizeof(tmp_name));
+ if (end >= tmp_name + sizeof(tmp_name))
+ {
+ sql_print_warning(ER(ER_WRONG_DB_NAME), host.db);
+ continue;
+ }
my_casedn_str(files_charset_info, host.db);
if (strcmp(host.db, tmp_name) != 0)
sql_print_warning("'host' entry '%s|%s' had database in mixed "
@@ -1038,7 +1056,12 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
convert db to lower case and give a warning if the db wasn't
already in lower case
*/
- (void)strmov(tmp_name, db.db);
+ char *end = strnmov(tmp_name, db.db, sizeof(tmp_name));
+ if (end >= tmp_name + sizeof(tmp_name))
+ {
+ sql_print_warning(ER(ER_WRONG_DB_NAME), db.db);
+ continue;
+ }
my_casedn_str(files_charset_info, db.db);
if (strcmp(db.db, tmp_name) != 0)
{
@@ -2698,7 +2721,13 @@ replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
get_grantor(thd, grantor);
- table->file->ha_index_init(0, 1);
+ if ((error= table->file->ha_index_init(0, 1)))
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_PRINT("info", ("ha_index_init error"));
+ DBUG_RETURN(-1);
+ }
+
if (table->file->ha_index_read_map(table->record[0], user_key,
HA_WHOLE_KEY,
HA_READ_KEY_EXACT))
@@ -2940,7 +2969,12 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
col_privs->field[4]->store("",0, &my_charset_latin1);
- col_privs->file->ha_index_init(0, 1);
+ if (col_privs->file->ha_index_init(0, 1))
+ {
+ cols= 0;
+ return;
+ }
+
if (col_privs->file->ha_index_read_map(col_privs->record[0], (uchar*) key,
(key_part_map)15,
HA_READ_KEY_EXACT))
@@ -3004,15 +3038,23 @@ static GRANT_NAME *name_hash_search(HASH *name_hash,
const char *user, const char *tname,
bool exact, bool name_tolower)
{
- char helping [SAFE_NAME_LEN*2+USERNAME_LENGTH+3], *name_ptr;
+ char helping[SAFE_NAME_LEN*2+USERNAME_LENGTH+3];
+ char *hend = helping + sizeof(helping);
uint len;
GRANT_NAME *grant_name,*found=0;
HASH_SEARCH_STATE state;
- name_ptr= strmov(strmov(helping, user) + 1, db) + 1;
- len = (uint) (strmov(name_ptr, tname) - helping) + 1;
+ char *db_ptr= strmov(helping, user) + 1;
+ char *tname_ptr= strnmov(db_ptr, db, hend - db_ptr) + 1;
+ if (tname_ptr > hend)
+ return 0; // invalid name = not found
+ char *end= strnmov(tname_ptr, tname, hend - tname_ptr) + 1;
+ if (end > hend)
+ return 0; // invalid name = not found
+
+ len = (uint) (end - helping);
if (name_tolower)
- my_casedn_str(files_charset_info, name_ptr);
+ my_casedn_str(files_charset_info, tname_ptr);
for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping,
len, &state);
grant_name ;
@@ -3072,7 +3114,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
const char *db, const char *table_name,
ulong rights, bool revoke_grant)
{
- int error=0,result=0;
+ int result=0;
uchar key[MAX_KEY_LENGTH];
uint key_prefix_length;
KEY_PART_INFO *key_part= table->key_info->key_part;
@@ -3099,7 +3141,13 @@ static int replace_column_table(GRANT_TABLE *g_t,
List_iterator <LEX_COLUMN> iter(columns);
class LEX_COLUMN *column;
- table->file->ha_index_init(0, 1);
+ int error= table->file->ha_index_init(0, 1);
+ if (error)
+ {
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(-1);
+ }
+
while ((column= iter++))
{
ulong privileges= column->rights;
@@ -3970,7 +4018,12 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
if (lower_case_table_names && db)
{
- strmov(tmp_db,db);
+ char *end= strnmov(tmp_db,db, sizeof(tmp_db));
+ if (end >= tmp_db + sizeof(tmp_db))
+ {
+ my_error(ER_WRONG_DB_NAME ,MYF(0), db);
+ DBUG_RETURN(TRUE);
+ }
my_casedn_str(files_charset_info, tmp_db);
db=tmp_db;
}
@@ -4155,7 +4208,10 @@ static my_bool grant_load_procs_priv(TABLE *p_table)
(void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
0,0,0, (my_hash_get_key) get_grant_table,
0,0);
- p_table->file->ha_index_init(0, 1);
+
+ if (p_table->file->ha_index_init(0, 1))
+ DBUG_RETURN(TRUE);
+
p_table->use_all_columns();
if (!p_table->file->ha_index_first(p_table->record[0]))
@@ -4256,7 +4312,10 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
t_table = tables[0].table;
c_table = tables[1].table;
- t_table->file->ha_index_init(0, 1);
+
+ if (t_table->file->ha_index_init(0, 1))
+ goto end_index_init;
+
t_table->use_all_columns();
c_table->use_all_columns();
@@ -4301,9 +4360,10 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
return_val=0; // Return ok
end_unlock:
- thd->variables.sql_mode= old_sql_mode;
t_table->file->ha_index_end();
my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
+end_index_init:
+ thd->variables.sql_mode= old_sql_mode;
DBUG_RETURN(return_val);
}
@@ -5990,20 +6050,19 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
Delete from grant structure if drop is true.
Update in grant structure if drop is false and user_to is not NULL.
Search in grant structure if drop is false and user_to is NULL.
- Structures are numbered as follows:
- 0 acl_users
- 1 acl_dbs
- 2 column_priv_hash
- 3 proc_priv_hash
- 4 func_priv_hash
- 5 acl_proxy_users
+ Structures are enumerated as follows:
+ 0 ACL_USER
+ 1 ACL_DB
+ 2 COLUMN_PRIVILEGES_HASH
+ 3 PROC_PRIVILEGES_HASH
+ 4 FUNC_PRIVILEGES_HASH
+ 5 PROXY_USERS_ACL
@retval > 0 At least one element matched.
@retval 0 OK, but no element matched.
- @retval -1 Wrong arguments to function.
*/
-static int handle_grant_struct(uint struct_no, bool drop,
+static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
LEX_USER *user_from, LEX_USER *user_to)
{
int result= 0;
@@ -6027,28 +6086,29 @@ static int handle_grant_struct(uint struct_no, bool drop,
/* Get the number of elements in the in-memory structure. */
switch (struct_no) {
- case 0:
+ case USER_ACL:
elements= acl_users.elements;
break;
- case 1:
+ case DB_ACL:
elements= acl_dbs.elements;
break;
- case 2:
+ case COLUMN_PRIVILEGES_HASH:
grant_name_hash= &column_priv_hash;
elements= grant_name_hash->records;
break;
- case 3:
+ case PROC_PRIVILEGES_HASH:
grant_name_hash= &proc_priv_hash;
elements= grant_name_hash->records;
break;
- case 4:
+ case FUNC_PRIVILEGES_HASH:
grant_name_hash= &func_priv_hash;
elements= grant_name_hash->records;
break;
- case 5:
+ case PROXY_USERS_ACL:
elements= acl_proxy_users.elements;
break;
default:
+ DBUG_ASSERT(0);
return -1;
}
@@ -6063,27 +6123,27 @@ static int handle_grant_struct(uint struct_no, bool drop,
Get a pointer to the element.
*/
switch (struct_no) {
- case 0:
+ case USER_ACL:
acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
user= acl_user->user;
host= acl_user->host.hostname;
break;
- case 1:
+ case DB_ACL:
acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*);
user= acl_db->user;
host= acl_db->host.hostname;
break;
- case 2:
- case 3:
- case 4:
+ case COLUMN_PRIVILEGES_HASH:
+ case PROC_PRIVILEGES_HASH:
+ case FUNC_PRIVILEGES_HASH:
grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
user= grant_name->user;
host= grant_name->host.hostname;
break;
- case 5:
+ case PROXY_USERS_ACL:
acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
user= acl_proxy_user->get_user();
host= acl_proxy_user->get_host();
@@ -6109,21 +6169,21 @@ static int handle_grant_struct(uint struct_no, bool drop,
if ( drop )
{
switch ( struct_no ) {
- case 0:
+ case USER_ACL:
delete_dynamic_element(&acl_users, idx);
break;
- case 1:
+ case DB_ACL:
delete_dynamic_element(&acl_dbs, idx);
break;
- case 2:
- case 3:
- case 4:
+ case COLUMN_PRIVILEGES_HASH:
+ case PROC_PRIVILEGES_HASH:
+ case FUNC_PRIVILEGES_HASH:
my_hash_delete(grant_name_hash, (uchar*) grant_name);
break;
- case 5:
+ case PROXY_USERS_ACL:
delete_dynamic_element(&acl_proxy_users, idx);
break;
@@ -6147,19 +6207,19 @@ static int handle_grant_struct(uint struct_no, bool drop,
else if ( user_to )
{
switch ( struct_no ) {
- case 0:
+ case USER_ACL:
acl_user->user= strdup_root(&mem, user_to->user.str);
acl_user->host.hostname= strdup_root(&mem, user_to->host.str);
break;
- case 1:
+ case DB_ACL:
acl_db->user= strdup_root(&mem, user_to->user.str);
acl_db->host.hostname= strdup_root(&mem, user_to->host.str);
break;
- case 2:
- case 3:
- case 4:
+ case COLUMN_PRIVILEGES_HASH:
+ case PROC_PRIVILEGES_HASH:
+ case FUNC_PRIVILEGES_HASH:
{
/*
Save old hash key and its length to be able properly update
@@ -6195,11 +6255,10 @@ static int handle_grant_struct(uint struct_no, bool drop,
break;
}
- case 5:
+ case PROXY_USERS_ACL:
acl_proxy_user->set_user (&mem, user_to->user.str);
acl_proxy_user->set_host (&mem, user_to->host.str);
break;
-
}
}
else
@@ -6256,7 +6315,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
else
{
/* Handle user array. */
- if ((handle_grant_struct(0, drop, user_from, user_to)) || found)
+ if ((handle_grant_struct(USER_ACL, drop, user_from, user_to)) || found)
{
result= 1; /* At least one record/element found. */
/* If search is requested, we do not need to search further. */
@@ -6274,7 +6333,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
else
{
/* Handle db array. */
- if (((handle_grant_struct(1, drop, user_from, user_to) && ! result) ||
+ if (((handle_grant_struct(DB_ACL, drop, user_from, user_to) && ! result) ||
found) && ! result)
{
result= 1; /* At least one record/element found. */
@@ -6293,7 +6352,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
else
{
/* Handle procs array. */
- if (((handle_grant_struct(3, drop, user_from, user_to) && ! result) ||
+ if (((handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) ||
found) && ! result)
{
result= 1; /* At least one record/element found. */
@@ -6302,7 +6361,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
goto end;
}
/* Handle funcs array. */
- if (((handle_grant_struct(4, drop, user_from, user_to) && ! result) ||
+ if (((handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) ||
found) && ! result)
{
result= 1; /* At least one record/element found. */
@@ -6337,7 +6396,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
else
{
/* Handle columns hash. */
- if (((handle_grant_struct(2, drop, user_from, user_to) && ! result) ||
+ if (((handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) ||
found) && ! result)
result= 1; /* At least one record/element found. */
}
@@ -6354,7 +6413,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
else
{
/* Handle proxies_priv array. */
- if ((handle_grant_struct(5, drop, user_from, user_to) && !result) ||
+ if ((handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) && !result) ||
found)
result= 1; /* At least one record/element found. */
}
@@ -7082,14 +7141,25 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
DBUG_RETURN(FALSE);
}
- /* one can grant proxy to himself to others */
- if (!strcmp(thd->security_ctx->user, user) &&
+ /*
+ one can grant proxy for self to others.
+ Security context in THD contains two pairs of (user,host):
+ 1. (user,host) pair referring to inbound connection.
+ 2. (priv_user,priv_host) pair obtained from mysql.user table after doing
+ authnetication of incoming connection.
+ Privileges should be checked wrt (priv_user, priv_host) tuple, because
+ (user,host) pair obtained from inbound connection may have different
+ values than what is actually stored in mysql.user table and while granting
+ or revoking proxy privilege, user is expected to provide entries mentioned
+ in mysql.user table.
+ */
+ if (!strcmp(thd->security_ctx->priv_user, user) &&
!my_strcasecmp(system_charset_info, host,
- thd->security_ctx->host))
+ thd->security_ctx->priv_host))
{
DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
- thd->security_ctx->user, user,
- host, thd->security_ctx->host));
+ thd->security_ctx->priv_user, user,
+ host, thd->security_ctx->priv_host));
DBUG_RETURN(FALSE);
}
@@ -7755,6 +7825,7 @@ struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
} cached_server_packet;
int packets_read, packets_written; ///< counters for send/received packets
uint connect_errors; ///< if there were connect errors for this host
+ bool make_it_fail;
/** when plugin returns a failure this tells us what really happened */
enum { SUCCESS, FAILURE, RESTART } status;
};
@@ -8020,14 +8091,14 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
/**
Finds acl entry in user database for authentication purposes.
- Finds a user and copies it into mpvio. Reports an authentication
- failure if a user is not found.
+ Finds a user and copies it into mpvio. Creates a fake user
+ if no matching user account is found.
@note find_acl_user is not the same, because it doesn't take into
account the case when user is not empty, but acl_user->user is empty
@retval 0 found
- @retval 1 not found
+ @retval 1 error
*/
static bool find_mpvio_user(MPVIO_EXT *mpvio)
{
@@ -8050,8 +8121,27 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio)
if (!mpvio->acl_user)
{
- login_failed_error(mpvio->thd);
- DBUG_RETURN (1);
+ /*
+ A matching user was not found. Fake it. Take any user, make the
+ authentication fail later.
+ This way we get a realistically looking failure, with occasional
+ "change auth plugin" requests even for nonexistent users. The ratio
+ of "change auth plugin" request will be the same for real and
+ nonexistent users.
+ Note, that we cannot pick any user at random, it must always be
+ the same user account for the incoming sctx->user name.
+ */
+ ulong nr1=1, nr2=4;
+ CHARSET_INFO *cs= &my_charset_latin1;
+ cs->coll->hash_sort(cs, (uchar*) sctx->user, strlen(sctx->user), &nr1, &nr2);
+
+ mysql_mutex_lock(&acl_cache->lock);
+ uint i= nr1 % acl_users.elements;
+ ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
+ mpvio->acl_user= acl_user_tmp->copy(mpvio->thd->mem_root);
+ mysql_mutex_unlock(&acl_cache->lock);
+
+ mpvio->make_it_fail= true;
}
/* user account requires non-default plugin and the client is too old */
@@ -8178,6 +8268,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ thd->password= passwd_len > 0;
if (find_mpvio_user(mpvio))
DBUG_RETURN(1);
@@ -8473,8 +8564,8 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
mpvio->cached_server_packet.pkt_len))
return packet_error;
- passwd_len= my_net_read(&mpvio->thd->net);
- passwd= (char*)mpvio->thd->net.read_pos;
+ passwd_len= my_net_read(&thd->net);
+ passwd= (char*)thd->net.read_pos;
}
*buff= (uchar*) passwd;
@@ -8576,6 +8667,10 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
*buf= (uchar*) mpvio->cached_client_reply.pkt;
mpvio->cached_client_reply.pkt= 0;
mpvio->packets_read++;
+
+ if (mpvio->make_it_fail)
+ goto err;
+
DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len);
}
@@ -8610,6 +8705,9 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
else
*buf= mpvio->thd->net.read_pos;
+ if (mpvio->make_it_fail)
+ goto err;
+
DBUG_RETURN((int)pkt_len);
err:
@@ -8617,7 +8715,12 @@ err:
{
inc_host_errors(mpvio->thd->security_ctx->ip);
if (!mpvio->thd->is_error())
- my_error(ER_HANDSHAKE_ERROR, MYF(0));
+ {
+ if (mpvio->make_it_fail)
+ login_failed_error(mpvio->thd);
+ else
+ my_error(ER_HANDSHAKE_ERROR, MYF(0));
+ }
}
DBUG_RETURN(-1);
}
@@ -8822,6 +8925,7 @@ bool acl_authenticate(THD *thd, uint connect_errors,
mpvio.thd= thd;
mpvio.connect_errors= connect_errors;
mpvio.status= MPVIO_EXT::FAILURE;
+ mpvio.make_it_fail= false;
mpvio.auth_info.host_or_ip= thd->security_ctx->host_or_ip;
mpvio.auth_info.host_or_ip_length=
(unsigned int) strlen(thd->security_ctx->host_or_ip);
diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc
index f1f991242e7..793eead9869 100644
--- a/sql/sql_audit.cc
+++ b/sql/sql_audit.cc
@@ -132,12 +132,9 @@ static const uint audit_handlers_count=
static my_bool acquire_plugins(THD *thd, plugin_ref plugin, void *arg)
{
- uint event_class= *(uint*) arg;
- unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
+ ulong *event_class_mask= (ulong*) arg;
st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *);
- set_audit_mask(event_class_mask, event_class);
-
/* Check if this plugin is interested in the event */
if (check_audit_mask(data->class_mask, event_class_mask))
return 0;
@@ -176,15 +173,13 @@ static my_bool acquire_plugins(THD *thd, plugin_ref plugin, void *arg)
@details Ensure that audit plugins interested in given event
class are locked by current thread.
*/
-void mysql_audit_acquire_plugins(THD *thd, uint event_class)
+void mysql_audit_acquire_plugins(THD *thd, ulong *event_class_mask)
{
- unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
DBUG_ENTER("mysql_audit_acquire_plugins");
- set_audit_mask(event_class_mask, event_class);
if (thd && !check_audit_mask(mysql_global_audit_mask, event_class_mask) &&
check_audit_mask(thd->audit_class_mask, event_class_mask))
{
- plugin_foreach(thd, acquire_plugins, MYSQL_AUDIT_PLUGIN, &event_class);
+ plugin_foreach(thd, acquire_plugins, MYSQL_AUDIT_PLUGIN, event_class_mask);
add_audit_mask(thd->audit_class_mask, event_class_mask);
}
DBUG_VOID_RETURN;
@@ -206,7 +201,9 @@ void mysql_audit_notify(THD *thd, uint event_class, uint event_subtype, ...)
va_list ap;
audit_handler_t *handlers= audit_handlers + event_class;
DBUG_ASSERT(event_class < audit_handlers_count);
- mysql_audit_acquire_plugins(thd, event_class);
+ unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
+ set_audit_mask(event_class_mask, event_class);
+ mysql_audit_acquire_plugins(thd, event_class_mask);
va_start(ap, event_subtype);
(*handlers)(thd, event_subtype, ap);
va_end(ap);
@@ -364,6 +361,34 @@ int initialize_audit_plugin(st_plugin_int *plugin)
add_audit_mask(mysql_global_audit_mask, data->class_mask);
mysql_mutex_unlock(&LOCK_audit_mask);
+ /*
+ Pre-acquire the newly inslalled audit plugin for events that
+ may potentially occur further during INSTALL PLUGIN.
+
+ When audit event is triggered, audit subsystem acquires interested
+ plugins by walking through plugin list. Evidently plugin list
+ iterator protects plugin list by acquiring LOCK_plugin, see
+ plugin_foreach_with_mask().
+
+ On the other hand [UN]INSTALL PLUGIN is acquiring LOCK_plugin
+ rather for a long time.
+
+ When audit event is triggered during [UN]INSTALL PLUGIN, plugin
+ list iterator acquires the same lock (within the same thread)
+ second time.
+
+ This hack should be removed when LOCK_plugin is fixed so it
+ protects only what it supposed to protect.
+
+ See also mysql_install_plugin() and mysql_uninstall_plugin()
+ */
+ THD *thd= current_thd;
+ if (thd)
+ {
+ acquire_plugins(thd, plugin_int_to_ref(plugin), data->class_mask);
+ add_audit_mask(thd->audit_class_mask, data->class_mask);
+ }
+
return 0;
}
@@ -494,7 +519,7 @@ static void event_class_dispatch(THD *thd, unsigned int event_class,
#else /* EMBEDDED_LIBRARY */
-void mysql_audit_acquire_plugins(THD *thd, uint event_class)
+void mysql_audit_acquire_plugins(THD *thd, ulong *event_class_mask)
{
}
diff --git a/sql/sql_audit.h b/sql/sql_audit.h
index b2ce31f1d26..46afe4b7596 100644
--- a/sql/sql_audit.h
+++ b/sql/sql_audit.h
@@ -31,7 +31,7 @@ extern void mysql_audit_finalize();
extern void mysql_audit_init_thd(THD *thd);
extern void mysql_audit_free_thd(THD *thd);
-extern void mysql_audit_acquire_plugins(THD *thd, uint event_class);
+extern void mysql_audit_acquire_plugins(THD *thd, ulong *event_class_mask);
#ifndef EMBEDDED_LIBRARY
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 58b13d4cd8a..b3321bd2771 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -8973,7 +8973,8 @@ fill_record(THD * thd, TABLE *table_arg, List<Item> &fields, List<Item> &values,
ER(ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN),
rfield->field_name, table->s->table_name.str);
}
- if ((value->save_in_field(rfield, 0) < 0) && !ignore_errors)
+ if ((!rfield->vcol_info || rfield->stored_in_db) &&
+ (value->save_in_field(rfield, 0)) < 0 && !ignore_errors)
{
my_message(ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR), MYF(0));
goto err;
@@ -9329,7 +9330,11 @@ bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use,
in_use->killed= KILL_SYSTEM_THREAD;
mysql_mutex_lock(&in_use->mysys_var->mutex);
if (in_use->mysys_var->current_cond)
+ {
+ mysql_mutex_lock(in_use->mysys_var->current_mutex);
mysql_cond_broadcast(in_use->mysys_var->current_cond);
+ mysql_mutex_unlock(in_use->mysys_var->current_mutex);
+ }
mysql_mutex_unlock(&in_use->mysys_var->mutex);
signalled= TRUE;
}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 47c104c2a65..7577056c3c5 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -684,7 +684,7 @@ char *thd_security_context(THD *thd, char *buffer, unsigned int length,
values doesn't have to very accurate and the memory it points to is static,
but we need to attempt a snapshot on the pointer values to avoid using NULL
values. The pointer to thd->query however, doesn't point to static memory
- and has to be protected by LOCK_thread_count or risk pointing to
+ and has to be protected by thd->LOCK_thd_data or risk pointing to
uninitialized memory.
*/
const char *proc_info= thd->proc_info;
@@ -719,20 +719,21 @@ char *thd_security_context(THD *thd, char *buffer, unsigned int length,
str.append(proc_info);
}
- mysql_mutex_lock(&thd->LOCK_thd_data);
-
- if (thd->query())
+ /* Don't wait if LOCK_thd_data is used as this could cause a deadlock */
+ if (!mysql_mutex_trylock(&thd->LOCK_thd_data))
{
- if (max_query_len < 1)
- len= thd->query_length();
- else
- len= min(thd->query_length(), max_query_len);
- str.append('\n');
- str.append(thd->query(), len);
+ if (thd->query())
+ {
+ if (max_query_len < 1)
+ len= thd->query_length();
+ else
+ len= min(thd->query_length(), max_query_len);
+ str.append('\n');
+ str.append(thd->query(), len);
+ }
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
}
- mysql_mutex_unlock(&thd->LOCK_thd_data);
-
if (str.c_ptr_safe() == buffer)
return buffer;
@@ -795,6 +796,7 @@ THD::THD()
stmt_da(&main_da),
thread_id(0),
global_disable_checkpoint(0),
+ failed_com_change_user(0),
is_fatal_error(0),
transaction_rollback_request(0),
is_fatal_sub_stmt_error(0),
@@ -1365,7 +1367,7 @@ void THD::change_user(void)
mysql_mutex_unlock(&LOCK_status);
cleanup();
- killed= NOT_KILLED;
+ reset_killed();
cleanup_done= 0;
init();
stmt_map.reset();
@@ -1636,6 +1638,10 @@ void THD::awake(killed_state state_to_set)
MYSQL_CALLBACK(scheduler, post_kill_notification, (this));
}
+ /* Interrupt target waiting inside a storage engine. */
+ if (state_to_set != NOT_KILLED)
+ ha_kill_query(this, thd_kill_level(this));
+
/* Broadcast a condition to kick the target if it is waiting on it. */
if (mysys_var)
{
@@ -3880,21 +3886,31 @@ void THD::restore_backup_open_tables_state(Open_tables_backup *backup)
DBUG_VOID_RETURN;
}
+#if MARIA_PLUGIN_INTERFACE_VERSION < 0x0200
/**
- Check the killed state of a user thread
- @param thd user thread
- @retval 0 the user thread is active
- @retval 1 the user thread has been killed
-
- This is used to signal a storage engine if it should be killed.
- See also THD::check_killed().
+ This is a backward compatibility method, made obsolete
+ by the thd_kill_statement service. Keep it here to avoid breaking the
+ ABI in case some binary plugins still use it.
*/
-
+#undef thd_killed
extern "C" int thd_killed(const MYSQL_THD thd)
{
+ return thd_kill_level(thd) > THD_ABORT_SOFTLY;
+}
+#else
+#error now thd_killed() function can go away
+#endif
+
+/*
+ return thd->killed status to the client,
+ mapped to the API enum thd_kill_levels values.
+*/
+extern "C" enum thd_kill_levels thd_kill_level(const MYSQL_THD thd)
+{
THD* current= current_thd;
+
if (!thd)
- thd= current_thd;
+ thd= current;
if (thd == current)
{
@@ -3903,11 +3919,11 @@ extern "C" int thd_killed(const MYSQL_THD thd)
apc_target->process_apc_requests();
}
- if (!(thd->killed & KILL_HARD_BIT))
- return 0;
- return thd->killed;
-}
+ if (likely(thd->killed == NOT_KILLED))
+ return THD_IS_NOT_KILLED;
+ return thd->killed & KILL_HARD_BIT ? THD_ABORT_ASAP : THD_ABORT_SOFTLY;
+}
/**
Send an out-of-band progress report to the client
@@ -4949,7 +4965,7 @@ THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
There is no good place to set up the transactional data, so we
have to do it here.
*/
- if (binlog_setup_trx_data())
+ if (binlog_setup_trx_data() == NULL)
DBUG_RETURN(NULL);
Rows_log_event* pending= binlog_get_pending_rows_event(is_transactional);
@@ -5642,4 +5658,3 @@ bool Discrete_intervals_list::append(Discrete_interval *new_interval)
}
#endif /* !defined(MYSQL_CLIENT) */
-
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 9d00d3ff905..87b9783c1ed 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1751,7 +1751,7 @@ public:
bool save_prep_leaf_list;
#ifndef MYSQL_CLIENT
- int binlog_setup_trx_data();
+ binlog_cache_mngr * binlog_setup_trx_data();
/*
Public interface to write RBR events to the binlog
@@ -2234,6 +2234,7 @@ public:
bool no_errors;
uint8 password;
+ uint8 failed_com_change_user;
/**
Set to TRUE if execution of the current compound statement
@@ -2748,6 +2749,19 @@ public:
{
return ::killed_errno(killed);
}
+ inline void reset_killed()
+ {
+ /*
+ Resetting killed has to be done under a mutex to ensure
+ its not done during an awake() call.
+ */
+ if (killed != NOT_KILLED)
+ {
+ mysql_mutex_lock(&LOCK_thd_data);
+ killed= NOT_KILLED;
+ mysql_mutex_unlock(&LOCK_thd_data);
+ }
+ }
inline void send_kill_message() const
{
int err= killed_errno();
@@ -3974,6 +3988,8 @@ class Unique :public Sql_alloc
uint full_size;
uint min_dupl_count; /* always 0 for unions, > 0 for intersections */
+ bool merge(TABLE *table, uchar *buff, bool without_last_merge);
+
public:
ulong elements;
Unique(qsort_cmp2 comp_func, void *comp_func_fixed_arg,
@@ -4014,7 +4030,7 @@ public:
}
void reset();
- bool walk(tree_walk_action action, void *walk_action_arg);
+ bool walk(TABLE *table, tree_walk_action action, void *walk_action_arg);
uint get_size() const { return size; }
ulonglong get_max_in_memory_size() const { return max_in_memory_size; }
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 81a85f10a16..9f6185280cd 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -905,6 +905,19 @@ static int check_connection(THD *thd)
my_error(ER_BAD_HOST_ERROR, MYF(0));
return 1;
}
+ /* BEGIN : DEBUG */
+ DBUG_EXECUTE_IF("addr_fake_ipv4",
+ {
+ struct sockaddr *sa= (sockaddr *) &net->vio->remote;
+ sa->sa_family= AF_INET;
+ struct in_addr *ip4= &((struct sockaddr_in *)sa)->sin_addr;
+ /* See RFC 5737, 192.0.2.0/23 is reserved */
+ const char* fake= "192.0.2.4";
+ ip4->s_addr= inet_addr(fake);
+ strcpy(ip, fake);
+ };);
+ /* END : DEBUG */
+
if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(MY_WME))))
return 1; /* The error is set by my_strdup(). */
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
diff --git a/sql/sql_const.h b/sql/sql_const.h
index 255a0162e93..c6aa52197d5 100644
--- a/sql/sql_const.h
+++ b/sql/sql_const.h
@@ -43,8 +43,8 @@
#define MAX_MBWIDTH 3 /* Max multibyte sequence */
#define MAX_FIELD_CHARLENGTH 255
#define MAX_FIELD_VARCHARLENGTH 65535
-#define MAX_FIELD_BLOBLENGTH UINT_MAX32 /* cf field_blob::get_length() */
-#define CONVERT_IF_BIGGER_TO_BLOB 512 /* Used for CREATE ... SELECT */
+#define MAX_FIELD_BLOBLENGTH UINT_MAX32 /* cf field_blob::get_length() */
+#define CONVERT_IF_BIGGER_TO_BLOB 512 /* Threshold *in characters* */
/* Max column width +1 */
#define MAX_FIELD_WIDTH (MAX_FIELD_CHARLENGTH*MAX_MBWIDTH+1)
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index dc8a2e9f057..87650e643f2 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -1,5 +1,6 @@
/*
Copyright (c) 2000, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2013, Monty Program Ab.
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
@@ -755,7 +756,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
{
ulong deleted_tables= 0;
bool error= true;
- char path[FN_REFLEN+16];
+ char path[FN_REFLEN + 16];
MY_DIR *dirp;
uint length;
bool found_other_files= false;
@@ -928,7 +929,7 @@ update_binlog:
if (!(query= (char*) thd->alloc(MAX_DROP_TABLE_Q_LEN)))
goto exit; /* not much else we can do */
- query_pos= query_data_start= strmov(query,"drop table ");
+ query_pos= query_data_start= strmov(query,"DROP TABLE ");
query_end= query + MAX_DROP_TABLE_Q_LEN;
db_len= strlen(db);
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index c4055a9221f..1c93a59904c 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -846,14 +846,14 @@ retry:
case RFIRST:
if (keyname)
{
- table->file->ha_index_or_rnd_end();
- table->file->ha_index_init(keyno, 1);
- error= table->file->ha_index_first(table->record[0]);
+ if (!(error= table->file->ha_index_or_rnd_end()) &&
+ !(error= table->file->ha_index_init(keyno, 1)))
+ error= table->file->ha_index_first(table->record[0]);
}
else
{
- table->file->ha_index_or_rnd_end();
- if (!(error= table->file->ha_rnd_init(1)))
+ if (!(error= table->file->ha_index_or_rnd_end()) &&
+ !(error= table->file->ha_rnd_init(1)))
error= table->file->ha_rnd_next(table->record[0]);
}
mode= RNEXT;
@@ -872,10 +872,10 @@ retry:
/* else fall through */
case RLAST:
DBUG_ASSERT(keyname != 0);
- table->file->ha_index_or_rnd_end();
- table->file->ha_index_init(keyno, 1);
- error= table->file->ha_index_last(table->record[0]);
- mode= RPREV;
+ if (!(error= table->file->ha_index_or_rnd_end()) &&
+ !(error= table->file->ha_index_init(keyno, 1)))
+ error= table->file->ha_index_last(table->record[0]);
+ mode=RPREV;
break;
case RNEXT_SAME:
/* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...) */
@@ -889,13 +889,14 @@ retry:
if (!(key= (uchar*) thd->calloc(ALIGN_SIZE(handler->key_len))))
goto err;
- table->file->ha_index_or_rnd_end();
- table->file->ha_index_init(keyno, 1);
+ if ((error= table->file->ha_index_or_rnd_end()))
+ break;
key_copy(key, table->record[0], table->key_info + keyno,
handler->key_len);
- error= table->file->ha_index_read_map(table->record[0],
- key, handler->keypart_map,
- ha_rkey_mode);
+ if (!(error= table->file->ha_index_init(keyno, 1)))
+ error= table->file->ha_index_read_map(table->record[0],
+ key, handler->keypart_map,
+ ha_rkey_mode);
mode= rkey_to_rnext[(int)ha_rkey_mode];
break;
}
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index c352272e95c..458904ebe1d 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -303,8 +303,14 @@ int get_topics_for_keyword(THD *thd, TABLE *topics, TABLE *relations,
rtopic_id= find_fields[help_relation_help_topic_id].field;
rkey_id= find_fields[help_relation_help_keyword_id].field;
- topics->file->ha_index_init(iindex_topic,1);
- relations->file->ha_index_init(iindex_relations,1);
+ if (topics->file->ha_index_init(iindex_topic,1) ||
+ relations->file->ha_index_init(iindex_relations,1))
+ {
+ if (topics->file->inited)
+ topics->file->ha_index_end();
+ my_message(ER_CORRUPT_HELP_DB, ER(ER_CORRUPT_HELP_DB), MYF(0));
+ DBUG_RETURN(-1);
+ }
rkey_id->store((longlong) key_id, TRUE);
rkey_id->get_key_image(buff, rkey_id->pack_length(), Field::itRAW);
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index cb887d88f56..833e134bff9 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -2250,11 +2250,8 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
want to send "Server shutdown in progress" in the
INSERT THREAD.
*/
- if (di->thd.stmt_da->sql_errno() == ER_SERVER_SHUTDOWN)
- my_message(ER_QUERY_INTERRUPTED, ER(ER_QUERY_INTERRUPTED), MYF(0));
- else
- my_message(di->thd.stmt_da->sql_errno(), di->thd.stmt_da->message(),
- MYF(0));
+ my_message(di->thd.stmt_da->sql_errno(), di->thd.stmt_da->message(),
+ MYF(0));
}
di->unlock();
goto end_create;
@@ -2303,7 +2300,8 @@ end_create:
TABLE *Delayed_insert::get_local_table(THD* client_thd)
{
my_ptrdiff_t adjust_ptrs;
- Field **field,**org_field, *found_next_number_field, **dfield_ptr= 0;
+ Field **field,**org_field, *found_next_number_field;
+ Field **UNINIT_VAR(vfield), **UNINIT_VAR(dfield_ptr);
TABLE *copy;
TABLE_SHARE *share;
uchar *bitmap;
@@ -2339,7 +2337,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
killed using mysql_notify_thread_having_shared_lock() or
kill_delayed_threads_for_table().
*/
- if (!thd.is_error() || thd.stmt_da->sql_errno() == ER_SERVER_SHUTDOWN)
+ if (!thd.is_error())
my_message(ER_QUERY_INTERRUPTED, ER(ER_QUERY_INTERRUPTED), MYF(0));
else
my_message(thd.stmt_da->sql_errno(), thd.stmt_da->message(), MYF(0));
@@ -2363,6 +2361,13 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
if (!copy_tmp)
goto error;
+ if (share->vfields)
+ {
+ vfield= (Field **) client_thd->alloc((share->vfields+1)*sizeof(Field*));
+ if (!vfield)
+ goto error;
+ }
+
/* Copy the TABLE object. */
copy= new (copy_tmp) TABLE;
*copy= *table;
@@ -2408,6 +2413,28 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
}
}
*field=0;
+
+ if (share->vfields)
+ {
+ copy->vfield= vfield;
+ for (field= copy->field; *field; field++)
+ {
+ if ((*field)->vcol_info)
+ {
+ bool error_reported= FALSE;
+ if (unpack_vcol_info_from_frm(client_thd,
+ client_thd->mem_root,
+ copy,
+ *field,
+ &(*field)->vcol_info->expr_str,
+ &error_reported))
+ goto error;
+ *vfield++= *field;
+ }
+ }
+ *vfield= 0;
+ }
+
if (share->default_fields)
*dfield_ptr= NULL;
@@ -2715,7 +2742,10 @@ pthread_handler_t handle_delayed_insert(void *arg)
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
thd->set_current_time();
threads.append(thd);
- thd->killed=abort_loop ? KILL_CONNECTION : NOT_KILLED;
+ if (abort_loop)
+ thd->killed= KILL_CONNECTION;
+ else
+ thd->reset_killed();
mysql_mutex_unlock(&LOCK_thread_count);
mysql_thread_set_psi_id(thd->thread_id);
@@ -2814,8 +2844,12 @@ pthread_handler_t handle_delayed_insert(void *arg)
set_timespec(abstime, delayed_insert_timeout);
/* Information for pthread_kill */
+ mysql_mutex_unlock(&di->mutex);
+ mysql_mutex_lock(&di->thd.mysys_var->mutex);
di->thd.mysys_var->current_mutex= &di->mutex;
di->thd.mysys_var->current_cond= &di->cond;
+ mysql_mutex_unlock(&di->thd.mysys_var->mutex);
+ mysql_mutex_lock(&di->mutex);
thd_proc_info(&(di->thd), "Waiting for INSERT");
DBUG_PRINT("info",("Waiting for someone to insert rows"));
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 93e3eb51518..b8ce3b6720e 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1,4 +1,5 @@
/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2013, Monty Program Ab.
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
@@ -1876,6 +1877,7 @@ void st_select_lex::init_query()
ref_pointer_array= 0;
select_n_where_fields= 0;
select_n_having_items= 0;
+ n_child_sum_items= 0;
subquery_in_having= explicit_limit= 0;
is_item_list_lookup= 0;
first_execution= 1;
diff --git a/sql/sql_list.h b/sql/sql_list.h
index 2b2d9e59771..b4e0ab84aab 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -1,7 +1,6 @@
#ifndef INCLUDES_MYSQL_SQL_LIST_H
#define INCLUDES_MYSQL_SQL_LIST_H
-/*
- Copyright (c) 2000, 2010, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 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
@@ -169,6 +168,14 @@ protected:
public:
uint elements;
+ bool operator==(const base_list &rhs) const
+ {
+ return
+ elements == rhs.elements &&
+ first == rhs.first &&
+ last == rhs.last;
+ }
+
inline void empty() { elements=0; first= &end_of_list; last=&first;}
inline base_list() { empty(); }
/**
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 7d030329419..6a4712ca5b5 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -728,14 +728,14 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
List_iterator<Item> lu(thd->lex->update_list);
List_iterator<Item> lv(thd->lex->value_list);
- query_str.append(" SET ");
+ query_str.append(STRING_WITH_LEN(" SET "));
n= 0;
while ((item= lu++))
{
val= lv++;
if (n++)
- query_str.append(", ");
+ query_str.append(STRING_WITH_LEN(", "));
append_identifier(thd, &query_str, item->name, strlen(item->name));
query_str.append(val->name);
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 2d3d91ff9d8..b125047cb98 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -991,7 +991,18 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->security_ctx->user= 0;
thd->user_connect= 0;
- rc= acl_authenticate(thd, 0, packet_length);
+ /*
+ to limit COM_CHANGE_USER ability to brute-force passwords,
+ we only allow three unsuccessful COM_CHANGE_USER per connection.
+ */
+ if (thd->failed_com_change_user >= 3)
+ {
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
+ rc= 1;
+ }
+ else
+ rc= acl_authenticate(thd, 0, packet_length);
+
MYSQL_AUDIT_NOTIFY_CONNECTION_CHANGE_USER(thd);
if (rc)
{
@@ -1006,6 +1017,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->variables.collation_connection= save_collation_connection;
thd->variables.character_set_results= save_character_set_results;
thd->update_charset();
+ thd->failed_com_change_user++;
+ my_sleep(1000000);
}
else
{
@@ -4633,16 +4646,20 @@ finish:
if (! thd->in_sub_stmt)
{
- /* report error issued during command execution */
- if (thd->killed_errno())
- {
- if (! thd->stmt_da->is_set())
- thd->send_kill_message();
- }
- if (thd->killed < KILL_CONNECTION)
+ if (thd->killed != NOT_KILLED)
{
- thd->killed= NOT_KILLED;
- thd->mysys_var->abort= 0;
+ /* report error issued during command execution */
+ if (thd->killed_errno())
+ {
+ /* If we already sent 'ok', we can ignore any kill query statements */
+ if (! thd->stmt_da->is_set())
+ thd->send_kill_message();
+ }
+ if (thd->killed < KILL_CONNECTION)
+ {
+ thd->reset_killed();
+ thd->mysys_var->abort= 0;
+ }
}
if (thd->is_error() || (thd->variables.option_bits & OPTION_MASTER_SQL_ERROR))
trans_rollback_stmt(thd);
@@ -6266,8 +6283,13 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->next_name_resolution_table= NULL;
/* Link table in global list (all used tables) */
lex->add_to_query_tables(ptr);
- ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type,
- MDL_TRANSACTION);
+
+ // Pure table aliases do not need to be locked:
+ if (!test(table_options & TL_OPTION_ALIAS))
+ {
+ ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type,
+ MDL_TRANSACTION);
+ }
DBUG_RETURN(ptr);
}
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index d1c12ee4f83..38d8dcf7994 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2005, 2012, Oracle and/or its affiliates.
- Copyright (c) 2010, 2011, Monty Program Ab
+ Copyright (c) 2010, 2013, Monty Program Ab
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
@@ -1875,8 +1875,11 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv,
DBUG_RETURN(FALSE);
error:
mysql_mutex_unlock(&LOCK_plugin);
- sql_print_error("Couldn't load plugin named '%s' with soname '%s'.",
- name.str, dl.str);
+ if (name.str)
+ sql_print_error("Couldn't load plugin '%s' from '%s'.",
+ name.str, dl.str);
+ else
+ sql_print_error("Couldn't load plugins from '%s'.", dl.str);
DBUG_RETURN(TRUE);
}
@@ -2021,7 +2024,7 @@ static bool finalize_install(THD *thd, TABLE *table, const LEX_STRING *name)
struct st_plugin_int *tmp= plugin_find_internal(name, MYSQL_ANY_PLUGIN);
int error;
DBUG_ASSERT(tmp);
- mysql_mutex_assert_owner(&LOCK_plugin);
+ mysql_mutex_assert_owner(&LOCK_plugin); // because of tmp->state
if (tmp->state == PLUGIN_IS_DISABLED)
{
@@ -2108,8 +2111,12 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name,
This hack should be removed when LOCK_plugin is fixed so it
protects only what it supposed to protect.
+
+ See also mysql_uninstall_plugin() and initialize_audit_plugin()
*/
- mysql_audit_acquire_plugins(thd, MYSQL_AUDIT_GENERAL_CLASS);
+ unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] =
+ { MYSQL_AUDIT_GENERAL_CLASSMASK };
+ mysql_audit_acquire_plugins(thd, event_class_mask);
mysql_mutex_lock(&LOCK_plugin);
mysql_rwlock_wrlock(&LOCK_system_variables_hash);
@@ -2252,8 +2259,15 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
When audit event is triggered during [UN]INSTALL PLUGIN, plugin
list iterator acquires the same lock (within the same thread)
second time.
+
+ This hack should be removed when LOCK_plugin is fixed so it
+ protects only what it supposed to protect.
+
+ See also mysql_install_plugin() and initialize_audit_plugin()
*/
- mysql_audit_acquire_plugins(thd, MYSQL_AUDIT_GENERAL_CLASS);
+ unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] =
+ { MYSQL_AUDIT_GENERAL_CLASSMASK };
+ mysql_audit_acquire_plugins(thd, event_class_mask);
mysql_mutex_lock(&LOCK_plugin);
@@ -2263,11 +2277,19 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
{
fix_dl_name(thd->mem_root, &dl);
st_plugin_dl *plugin_dl= plugin_dl_find(&dl);
- struct st_maria_plugin *plugin;
- for (plugin= plugin_dl->plugins; plugin->info; plugin++)
+ if (plugin_dl)
{
- LEX_STRING str= { const_cast<char*>(plugin->name), strlen(plugin->name) };
- error|= do_uninstall(thd, table, &str);
+ for (struct st_maria_plugin *plugin= plugin_dl->plugins;
+ plugin->info; plugin++)
+ {
+ LEX_STRING str= { const_cast<char*>(plugin->name), strlen(plugin->name) };
+ error|= do_uninstall(thd, table, &str);
+ }
+ }
+ else
+ {
+ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "SONAME", dl.str);
+ error= true;
}
}
reap_plugins();
diff --git a/sql/sql_plugin_services.h b/sql/sql_plugin_services.h
index c779547059d..e3ef338eaad 100644
--- a/sql/sql_plugin_services.h
+++ b/sql/sql_plugin_services.h
@@ -54,13 +54,18 @@ static struct progress_report_service_st progress_report_handler= {
set_thd_proc_info
};
+static struct kill_statement_service_st thd_kill_statement_handler= {
+ thd_kill_level
+};
+
static struct st_service_ref list_of_services[]=
{
- { "my_snprintf_service", VERSION_my_snprintf, &my_snprintf_handler },
- { "thd_alloc_service", VERSION_thd_alloc, &thd_alloc_handler },
- { "thd_wait_service", VERSION_thd_wait, &thd_wait_handler },
+ { "my_snprintf_service", VERSION_my_snprintf, &my_snprintf_handler },
+ { "thd_alloc_service", VERSION_thd_alloc, &thd_alloc_handler },
+ { "thd_wait_service", VERSION_thd_wait, &thd_wait_handler },
{ "my_thread_scheduler_service", VERSION_my_thread_scheduler, &my_thread_scheduler_handler },
- { "progress_report_service", VERSION_progress_report, &progress_report_handler },
- { "debug_sync_service", VERSION_debug_sync, 0 } // updated in plugin_init()
+ { "progress_report_service", VERSION_progress_report, &progress_report_handler },
+ { "debug_sync_service", VERSION_debug_sync, 0 }, // updated in plugin_init()
+ { "thd_kill_statement_service", VERSION_kill_statement, &thd_kill_statement_handler }
};
diff --git a/sql/sql_priv.h b/sql/sql_priv.h
index 39976e1a430..345556a0b7f 100644
--- a/sql/sql_priv.h
+++ b/sql/sql_priv.h
@@ -153,6 +153,41 @@
#define OPTION_ALLOW_BATCH (ULL(1) << 36) // THD, intern (slave)
#define OPTION_SKIP_REPLICATION (ULL(1) << 37) // THD, user
+/*
+ Check how many bytes are available on buffer.
+
+ @param buf_start Pointer to buffer start.
+ @param buf_current Pointer to the current position on buffer.
+ @param buf_len Buffer length.
+
+ @return Number of bytes available on event buffer.
+*/
+template <class T> T available_buffer(const char* buf_start,
+ const char* buf_current,
+ T buf_len)
+{
+ return buf_len - (buf_current - buf_start);
+}
+
+/*
+ Check if jump value is within buffer limits.
+
+ @param jump Number of positions we want to advance.
+ @param buf_start Pointer to buffer start
+ @param buf_current Pointer to the current position on buffer.
+ @param buf_len Buffer length.
+
+ @return True If jump value is within buffer limits.
+ False Otherwise.
+*/
+template <class T> bool valid_buffer_range(T jump,
+ const char* buf_start,
+ const char* buf_current,
+ T buf_len)
+{
+ return (jump <= available_buffer(buf_start, buf_current, buf_len));
+}
+
/* The rest of the file is included in the server only */
#ifndef MYSQL_CLIENT
diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc
index 2d00a19870b..feb7810fa28 100644
--- a/sql/sql_profile.cc
+++ b/sql/sql_profile.cc
@@ -46,6 +46,7 @@
#define TIME_I_S_DECIMAL_SIZE (TIME_FLOAT_DIGITS*100)+(TIME_FLOAT_DIGITS-3)
#define MAX_QUERY_LENGTH 300
+#define MAX_QUERY_HISTORY 101
/**
Connects Information_Schema and Profiling.
@@ -264,9 +265,12 @@ void PROF_MEASUREMENT::collect()
QUERY_PROFILE::QUERY_PROFILE(PROFILING *profiling_arg, const char *status_arg)
:profiling(profiling_arg), profiling_query_id(0), query_source(NULL)
{
- profile_start= new PROF_MEASUREMENT(this, status_arg);
- entries.push_back(profile_start);
- profile_end= profile_start;
+ m_seq_counter= 1;
+ PROF_MEASUREMENT *prof= new PROF_MEASUREMENT(this, status_arg);
+ prof->m_seq= m_seq_counter++;
+ m_start_time_usecs= prof->time_usecs;
+ m_end_time_usecs= m_start_time_usecs;
+ entries.push_back(prof);
}
QUERY_PROFILE::~QUERY_PROFILE()
@@ -305,9 +309,14 @@ void QUERY_PROFILE::new_status(const char *status_arg,
else
prof= new PROF_MEASUREMENT(this, status_arg);
- profile_end= prof;
+ prof->m_seq= m_seq_counter++;
+ m_end_time_usecs= prof->time_usecs;
entries.push_back(prof);
+ /* Maintain the query history size. */
+ while (entries.elements > MAX_QUERY_HISTORY)
+ delete entries.pop();
+
DBUG_VOID_RETURN;
}
@@ -467,8 +476,7 @@ bool PROFILING::show_profiles()
String elapsed;
- PROF_MEASUREMENT *ps= prof->profile_start;
- PROF_MEASUREMENT *pe= prof->profile_end;
+ double query_time_usecs= prof->m_end_time_usecs - prof->m_start_time_usecs;
if (++idx <= unit->offset_limit_cnt)
continue;
@@ -477,7 +485,7 @@ bool PROFILING::show_profiles()
protocol->prepare_for_resend();
protocol->store((uint32)(prof->profiling_query_id));
- protocol->store((double)(pe->time_usecs - ps->time_usecs)/(1000.0*1000),
+ protocol->store((double)(query_time_usecs/(1000.0*1000)),
(uint32) TIME_FLOAT_DIGITS-1, &elapsed);
if (prof->query_source != NULL)
protocol->store(prof->query_source, strlen(prof->query_source),
@@ -537,17 +545,18 @@ int PROFILING::fill_statistics_info(THD *thd_arg, TABLE_LIST *tables, Item *cond
us also include a numbering of each state per query. The query_id and
the "seq" together are unique.
*/
- ulonglong seq;
+ ulong seq;
void *entry_iterator;
PROF_MEASUREMENT *entry, *previous= NULL;
/* ...and for each query, go through all its state-change steps. */
- for (seq= 0, entry_iterator= query->entries.new_iterator();
+ for (entry_iterator= query->entries.new_iterator();
entry_iterator != NULL;
entry_iterator= query->entries.iterator_next(entry_iterator),
- seq++, previous=entry, row_number++)
+ previous=entry, row_number++)
{
entry= query->entries.iterator_value(entry_iterator);
+ seq= entry->m_seq;
/* Skip the first. We count spans of fence, not fence-posts. */
if (previous == NULL) continue;
diff --git a/sql/sql_profile.h b/sql/sql_profile.h
index 7705f6ca476..f8970bb162a 100644
--- a/sql/sql_profile.h
+++ b/sql/sql_profile.h
@@ -186,6 +186,7 @@ private:
char *file;
unsigned int line;
+ ulong m_seq;
double time_usecs;
char *allocated_status_memory;
@@ -217,8 +218,9 @@ private:
query_id_t profiling_query_id; /* Session-specific id. */
char *query_source;
- PROF_MEASUREMENT *profile_start;
- PROF_MEASUREMENT *profile_end;
+ double m_start_time_usecs;
+ double m_end_time_usecs;
+ ulong m_seq_counter;
Queue<PROF_MEASUREMENT> entries;
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index e18b0cd04c0..5b2e498cb00 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -1642,6 +1642,31 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
}
}
+/**
+ Get value for a string parameter with error checking
+
+ Note that in case of error the original string should not be updated!
+
+ @ret 0 ok
+ @ret 1 error
+*/
+
+static bool get_string_parameter(char *to, const char *from, size_t length,
+ const char *name)
+{
+ if (from) // Empty paramaters allowed
+ {
+ size_t from_length;
+ if ((from_length= strlen(from)) > length)
+ {
+ my_error(ER_WRONG_STRING_LENGTH, MYF(0), from, name, (int) length);
+ return 1;
+ }
+ memcpy(to, from, from_length+1);
+ }
+ return 0;
+}
+
/**
Execute a CHANGE MASTER statement.
@@ -1770,12 +1795,17 @@ bool change_master(THD* thd, Master_info* mi)
}
DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
- if (lex_mi->host)
- strmake(mi->host, lex_mi->host, sizeof(mi->host)-1);
- if (lex_mi->user)
- strmake(mi->user, lex_mi->user, sizeof(mi->user)-1);
- if (lex_mi->password)
- strmake(mi->password, lex_mi->password, sizeof(mi->password)-1);
+ if (get_string_parameter(mi->host, lex_mi->host, sizeof(mi->host)-1,
+ "MASTER_HOST") ||
+ get_string_parameter(mi->user, lex_mi->user, sizeof(mi->user)-1,
+ "MASTER_USER") ||
+ get_string_parameter(mi->password, lex_mi->password,
+ sizeof(mi->password)-1, "MASTER_PASSWORD"))
+ {
+ ret= TRUE;
+ goto err;
+ }
+
if (lex_mi->port)
mi->port = lex_mi->port;
if (lex_mi->connect_retry)
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index 7dc58c47d52..c5a0b31388e 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -25,9 +25,9 @@ typedef struct st_slave_info
{
uint32 server_id;
uint32 rpl_recovery_rank, master_id;
- char host[HOSTNAME_LENGTH+1];
+ char host[HOSTNAME_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
char user[USERNAME_LENGTH+1];
- char password[MAX_PASSWORD_LENGTH+1];
+ char password[MAX_PASSWORD_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
uint16 port;
THD* thd;
} SLAVE_INFO;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 63718dc9d55..41c0b07bc49 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2012 Oracle and/or its affiliates.
- Copyright (c) 2009, 2012, Monty Program Ab
+ Copyright (c) 2009, 2013 Monty Program Ab.
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
@@ -228,7 +228,7 @@ static int create_sort_index(THD *thd, JOIN *join, ORDER *order,
static int remove_duplicates(JOIN *join,TABLE *entry,List<Item> &fields,
Item *having);
static int remove_dup_with_compare(THD *thd, TABLE *entry, Field **field,
- ulong offset,Item *having);
+ Item *having);
static int remove_dup_with_hash_index(THD *thd,TABLE *table,
uint field_count, Field **first_field,
ulong key_length,Item *having);
@@ -383,7 +383,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
ER(ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT),
thd->accessed_rows_and_keys,
thd->lex->limit_rows_examined->val_uint());
- thd->killed= NOT_KILLED;
+ thd->reset_killed();
}
/* Disable LIMIT ROWS EXAMINED after query execution. */
thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;
@@ -2077,6 +2077,8 @@ bool JOIN::setup_subquery_caches()
*/
void JOIN::restore_tmp()
{
+ DBUG_PRINT("info", ("restore_tmp this %p tmp_join %p", this, tmp_join));
+ DBUG_ASSERT(tmp_join != this);
memcpy(tmp_join, this, (size_t) sizeof(JOIN));
}
@@ -7750,8 +7752,9 @@ get_best_combination(JOIN *join)
if ( !(keyuse= join->best_positions[tablenr].key))
{
j->type=JT_ALL;
- if (tablenr != join->const_tables)
- join->full_join=1;
+ if (join->best_positions[tablenr].use_join_buffer &&
+ tablenr != join->const_tables)
+ join->full_join= 1;
}
/*if (join->best_positions[tablenr].sj_strategy == SJ_OPT_LOOSE_SCAN)
@@ -8744,7 +8747,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
We will use join cache here : prevent sorting of the first
table only and sort at the end.
*/
- if (i != join->const_tables && join->table_count > join->const_tables + 1)
+ if (i != join->const_tables &&
+ join->table_count > join->const_tables + 1 &&
+ join->best_positions[i].use_join_buffer)
join->full_join= 1;
}
@@ -10769,7 +10774,6 @@ void JOIN::cleanup(bool full)
filesort_free_buffers(first_tab->table, full);
}
}
-
if (full)
{
JOIN_TAB *sort_tab= first_linear_tab(this, WITHOUT_CONST_TABLES);
@@ -10804,21 +10808,19 @@ void JOIN::cleanup(bool full)
}
}
}
- /*
- We are not using tables anymore
- Unlock all tables. We may be in an INSERT .... SELECT statement.
- */
if (full)
{
- if (tmp_join)
- tmp_table_param.copy_field= 0;
- group_fields.delete_elements();
/*
- Ensure that the above delete_elements() would not be called
+ Ensure that the following delete_elements() would not be called
twice for the same list.
*/
- if (tmp_join && tmp_join != this)
- tmp_join->group_fields= group_fields;
+ if (tmp_join && tmp_join != this &&
+ tmp_join->group_fields == this->group_fields)
+ tmp_join->group_fields.empty();
+
+ // Run Cached_item DTORs!
+ group_fields.delete_elements();
+
/*
We can't call delete_elements() on copy_funcs as this will cause
problems in free_elements() as some of the elements are then deleted.
@@ -14330,10 +14332,20 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
if (group)
{
+ ORDER **prev= &group;
if (!param->quick_group)
group=0; // Can't use group key
else for (ORDER *tmp=group ; tmp ; tmp=tmp->next)
{
+ /* Exclude found constant from the list */
+ if ((*tmp->item)->const_item())
+ {
+ *prev= tmp->next;
+ param->group_parts--;
+ continue;
+ }
+ else
+ prev= &(tmp->next);
/*
marker == 4 means two things:
- store NULLs in the key, and
@@ -14341,7 +14353,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
can't index BIT fields.
*/
(*tmp->item)->marker=4; // Store null in key
- if ((*tmp->item)->max_length >= CONVERT_IF_BIGGER_TO_BLOB)
+ if ((*tmp->item)->too_big_for_varchar())
using_unique_constraint=1;
}
if (param->group_length >= MAX_BLOB_WIDTH)
@@ -15880,11 +15892,11 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
if (table->group && join->tmp_table_param.sum_func_count &&
table->s->keys && !table->file->inited)
{
- int tmp_error;
- if ((tmp_error= table->file->ha_index_init(0, 0)))
+ rc= table->file->ha_index_init(0, 0);
+ if (rc)
{
- table->file->print_error(tmp_error, MYF(0)); /* purecov: inspected */
- DBUG_RETURN(-1); /* purecov: inspected */
+ table->file->print_error(rc, MYF(0));
+ DBUG_RETURN(-1);
}
}
}
@@ -16918,7 +16930,12 @@ int join_read_key2(THD *thd, JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref)
int error;
if (!table->file->inited)
{
- table->file->ha_index_init(table_ref->key, (tab ? tab->sorted : TRUE));
+ error= table->file->ha_index_init(table_ref->key, tab ? tab->sorted : TRUE);
+ if (error)
+ {
+ (void) report_error(table, error);
+ return 1;
+ }
}
/* TODO: Why don't we do "Late NULLs Filtering" here? */
@@ -17009,8 +17026,8 @@ join_read_always_key(JOIN_TAB *tab)
{
if ((error= table->file->ha_index_init(tab->ref.key, tab->sorted)))
{
- table->file->print_error(error, MYF(0));/* purecov: inspected */
- return(1); /* purecov: inspected */
+ (void) report_error(table, error);
+ return 1;
}
}
@@ -17040,14 +17057,13 @@ join_read_last_key(JOIN_TAB *tab)
int error;
TABLE *table= tab->table;
- if (!table->file->inited)
+ if (!table->file->inited &&
+ (error= table->file->ha_index_init(tab->ref.key, tab->sorted)))
{
- if ((error= table->file->ha_index_init(tab->ref.key, tab->sorted)))
- {
- table->file->print_error(error, MYF(0));/* purecov: inspected */
- return(1); /* purecov: inspected */
- }
+ (void) report_error(table, error);
+ return 1;
}
+
if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref))
return -1;
if ((error= table->file->ha_index_read_map(table->record[0],
@@ -17266,9 +17282,10 @@ join_ft_read_first(JOIN_TAB *tab)
if (!table->file->inited &&
(error= table->file->ha_index_init(tab->ref.key, 1)))
{
- table->file->print_error(error, MYF(0)); /* purecov: inspected */
- return(1); /* purecov: inspected */
+ (void) report_error(table, error);
+ return 1;
}
+
table->file->ft_init();
if ((error= table->file->ha_ft_read(table->record[0])))
@@ -17691,9 +17708,10 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
/* Change method to update rows */
if ((error= table->file->ha_index_init(0, 0)))
{
- table->file->print_error(error, MYF(0));/* purecov: inspected */
- DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(NESTED_LOOP_ERROR);
}
+
join->join_tab[join->top_join_tab_count-1].next_select=end_unique_update;
}
join->send_records++;
@@ -18335,18 +18353,36 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
table->s->primary_key != MAX_KEY &&
table->s->primary_key != idx)
{
+ KEY_PART_INFO *start,*end;
+ uint pk_part_idx= 0;
on_pk_suffix= TRUE;
- key_part= table->key_info[table->s->primary_key].key_part;
- key_part_end=key_part+table->key_info[table->s->primary_key].key_parts;
+ start= key_part= table->key_info[table->s->primary_key].key_part;
const_key_parts=table->const_key_parts[table->s->primary_key];
- for (; const_key_parts & 1 ; const_key_parts>>= 1)
- key_part++;
/*
- The primary and secondary key parts were all const (i.e. there's
- one row). The sorting doesn't matter.
+ Calculate true key_part_end and const_key_parts
+ (we have to stop as first not continous primary key part)
*/
- if (key_part == key_part_end && reverse == 0)
+ for (key_part_end= key_part,
+ end= key_part+table->key_info[table->s->primary_key].key_parts;
+ key_part_end < end; key_part_end++, pk_part_idx++)
+ {
+ /* Found hole in the pk_parts; Abort */
+ if (!(table->key_info[idx].ext_key_part_map &
+ (((key_part_map) 1) << pk_part_idx)))
+ break;
+ }
+ /* Adjust const_key_parts */
+ const_key_parts&= (((key_part_map) 1) << pk_part_idx) -1;
+
+ for (; const_key_parts & 1 ; const_key_parts>>= 1)
+ key_part++;
+ /*
+ Test if the primary key parts were all const (i.e. there's one row).
+ The sorting doesn't matter.
+ */
+ if (key_part == start+table->key_info[table->s->primary_key].key_parts &&
+ reverse == 0)
{
key_parts= 0;
reverse= 1;
@@ -18366,7 +18402,8 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
if (reverse && flag != reverse)
DBUG_RETURN(0);
reverse=flag; // Remember if reverse
- key_part++;
+ if (key_part < key_part_end)
+ key_part++;
}
if (on_pk_suffix)
{
@@ -19269,19 +19306,24 @@ void JOIN::clean_pre_sort_join_tab()
}
-/*****************************************************************************
- Remove duplicates from tmp table
- This should be recoded to add a unique index to the table and remove
- duplicates
- Table is a locked single thread table
- fields is the number of fields to check (from the end)
-*****************************************************************************/
+/**
+ Compare fields from table->record[0] and table->record[1],
+ possibly skipping few first fields.
+
+ @param table
+ @param ptr field to start the comparison from,
+ somewhere in the table->field[] array
+ @retval 1 different
+ @retval 0 identical
+*/
static bool compare_record(TABLE *table, Field **ptr)
{
for (; *ptr ; ptr++)
{
- if ((*ptr)->cmp_offset(table->s->rec_buff_length))
+ Field *f= *ptr;
+ if (f->is_null() != f->is_null(table->s->rec_buff_length) ||
+ (!f->is_null() && f->cmp_offset(table->s->rec_buff_length)))
return 1;
}
return 0;
@@ -19309,16 +19351,16 @@ static void free_blobs(Field **ptr)
static int
-remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
+remove_duplicates(JOIN *join, TABLE *table, List<Item> &fields, Item *having)
{
int error;
- ulong reclength,offset;
+ ulong keylength= 0;
uint field_count;
THD *thd= join->thd;
DBUG_ENTER("remove_duplicates");
- entry->reginfo.lock_type=TL_WRITE;
+ table->reginfo.lock_type=TL_WRITE;
/* Calculate how many saved fields there is in list */
field_count=0;
@@ -19335,11 +19377,10 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
join->unit->select_limit_cnt= 1; // Only send first row
DBUG_RETURN(0);
}
- Field **first_field=entry->field+entry->s->fields - field_count;
- offset= (field_count ?
- entry->field[entry->s->fields - field_count]->
- offset(entry->record[0]) : 0);
- reclength=entry->s->reclength-offset;
+
+ Field **first_field=table->field+table->s->fields - field_count;
+ for (Field **ptr=first_field; *ptr; ptr++)
+ keylength+= (*ptr)->sort_length() + (*ptr)->maybe_null();
/*
Disable LIMIT ROWS EXAMINED in order to avoid interrupting prematurely
@@ -19347,19 +19388,18 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
*/
thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;
if (thd->killed == ABORT_QUERY)
- thd->killed= NOT_KILLED;
- free_io_cache(entry); // Safety
- entry->file->info(HA_STATUS_VARIABLE);
- if (entry->s->db_type() == heap_hton ||
- (!entry->s->blob_fields &&
- ((ALIGN_SIZE(reclength) + HASH_OVERHEAD) * entry->file->stats.records <
+ thd->reset_killed();
+
+ free_io_cache(table); // Safety
+ table->file->info(HA_STATUS_VARIABLE);
+ if (table->s->db_type() == heap_hton ||
+ (!table->s->blob_fields &&
+ ((ALIGN_SIZE(keylength) + HASH_OVERHEAD) * table->file->stats.records <
thd->variables.sortbuff_size)))
- error=remove_dup_with_hash_index(join->thd, entry,
- field_count, first_field,
- reclength, having);
+ error=remove_dup_with_hash_index(join->thd, table, field_count, first_field,
+ keylength, having);
else
- error=remove_dup_with_compare(join->thd, entry, first_field, offset,
- having);
+ error=remove_dup_with_compare(join->thd, table, first_field, having);
if (join->select_lex != join->select_lex->master_unit()->fake_select_lex)
thd->lex->set_limit_rows_examined();
@@ -19369,18 +19409,13 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having)
static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
- ulong offset, Item *having)
+ Item *having)
{
handler *file=table->file;
- char *org_record,*new_record;
- uchar *record;
+ uchar *record=table->record[0];
int error;
- ulong reclength= table->s->reclength-offset;
DBUG_ENTER("remove_dup_with_compare");
- org_record=(char*) (record=table->record[0])+offset;
- new_record=(char*) table->record[1]+offset;
-
if (file->ha_rnd_init_with_error(1))
DBUG_RETURN(1);
@@ -19417,7 +19452,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
error=0;
goto err;
}
- memcpy(new_record,org_record,reclength);
+ store_record(table,record[1]);
/* Read through rest of file and mark duplicated rows deleted */
bool found=0;
@@ -19476,8 +19511,9 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
int error;
handler *file= table->file;
ulong extra_length= ALIGN_SIZE(key_length)-key_length;
- uint *field_lengths,*field_length;
+ uint *field_lengths, *field_length;
HASH hash;
+ Field **ptr;
DBUG_ENTER("remove_dup_with_hash_index");
if (!my_multi_malloc(MYF(MY_WME),
@@ -19489,21 +19525,8 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
NullS))
DBUG_RETURN(1);
- {
- Field **ptr;
- ulong total_length= 0;
- for (ptr= first_field, field_length=field_lengths ; *ptr ; ptr++)
- {
- uint length= (*ptr)->sort_length();
- (*field_length++)= length;
- total_length+= length;
- }
- DBUG_PRINT("info",("field_count: %u key_length: %lu total_length: %lu",
- field_count, key_length, total_length));
- DBUG_ASSERT(total_length <= key_length);
- key_length= total_length;
- extra_length= ALIGN_SIZE(key_length)-key_length;
- }
+ for (ptr= first_field, field_length=field_lengths ; *ptr ; ptr++)
+ (*field_length++)= (*ptr)->sort_length();
if (my_hash_init(&hash, &my_charset_bin, (uint) file->stats.records, 0,
key_length, (my_hash_get_key) 0, 0, 0))
@@ -19543,10 +19566,10 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
/* copy fields to key buffer */
org_key_pos= key_pos;
field_length=field_lengths;
- for (Field **ptr= first_field ; *ptr ; ptr++)
+ for (ptr= first_field ; *ptr ; ptr++)
{
- (*ptr)->sort_string(key_pos,*field_length);
- key_pos+= *field_length++;
+ (*ptr)->make_sort_key(key_pos, *field_length);
+ key_pos+= (*ptr)->maybe_null() + *field_length++;
}
/* Check if it exists before */
if (my_hash_search(&hash, org_key_pos, key_length))
@@ -23168,7 +23191,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
{
int direction;
ha_rows select_limit= select_limit_arg;
- uint used_key_parts;
+ uint used_key_parts= 0;
if (keys.is_set(nr) &&
(direction= test_if_order_by_key(order, table, nr, &used_key_parts)))
@@ -23236,7 +23259,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
to be adjusted accordingly if some components of
the secondary key are included in the primary key.
*/
- for(uint i= 0; i < used_pk_parts; i++)
+ for(uint i= 1; i < used_pk_parts; i++)
{
if (pkinfo->key_part[i].field->key_start.is_set(nr))
{
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 33e31913e41..095ad409a94 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -2810,7 +2810,6 @@ static bool show_status_array(THD *thd, const char *wild,
int len;
LEX_STRING null_lex_str;
SHOW_VAR tmp, *var;
- COND *partial_cond= 0;
enum_check_fields save_count_cuted_fields= thd->count_cuted_fields;
bool res= FALSE;
CHARSET_INFO *charset= system_charset_info;
@@ -2824,7 +2823,6 @@ static bool show_status_array(THD *thd, const char *wild,
if (*prefix)
*prefix_end++= '_';
len=name_buffer + sizeof(name_buffer) - prefix_end;
- partial_cond= make_cond_for_info_schema(cond, table->pos_in_table_list);
for (; variables->name; variables++)
{
@@ -2862,14 +2860,14 @@ static bool show_status_array(THD *thd, const char *wild,
if (show_type == SHOW_ARRAY)
{
show_status_array(thd, wild, (SHOW_VAR *) var->value, value_type,
- status_var, name_buffer, table, ucase_names, partial_cond);
+ status_var, name_buffer, table, ucase_names, cond);
}
else
{
if ((wild_checked ||
(wild && wild[0] && wild_case_compare(system_charset_info,
name_buffer, wild))) &&
- (!partial_cond || partial_cond->val_int()))
+ (!cond || cond->val_int()))
{
char *value=var->value;
const char *pos, *end; // We assign a lot of const's
@@ -5897,7 +5895,13 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
{
DBUG_RETURN(1);
}
- proc_table->file->ha_index_init(0, 1);
+
+ if (proc_table->file->ha_index_init(0, 1))
+ {
+ res= 1;
+ goto err;
+ }
+
if ((res= proc_table->file->ha_index_first(proc_table->record[0])))
{
res= (res == HA_ERR_END_OF_FILE) ? 0 : 1;
@@ -5923,7 +5927,9 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
}
err:
- proc_table->file->ha_index_end();
+ if (proc_table->file->inited)
+ (void) proc_table->file->ha_index_end();
+
close_system_tables(thd, &open_tables_state_backup);
DBUG_RETURN(res);
}
@@ -7122,9 +7128,12 @@ int fill_variables(THD *thd, TABLE_LIST *tables, COND *cond)
schema_table_idx == SCH_GLOBAL_VARIABLES)
option_type= OPT_GLOBAL;
+ COND *partial_cond= make_cond_for_info_schema(cond, tables);
+
mysql_rwlock_rdlock(&LOCK_system_variables_hash);
res= show_status_array(thd, wild, enumerate_sys_vars(thd, sorted_vars, option_type),
- option_type, NULL, "", tables->table, upper_case_names, cond);
+ option_type, NULL, "", tables->table,
+ upper_case_names, partial_cond);
mysql_rwlock_unlock(&LOCK_system_variables_hash);
DBUG_RETURN(res);
}
@@ -7161,13 +7170,18 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond)
tmp1= &thd->status_var;
}
+ COND *partial_cond= make_cond_for_info_schema(cond, tables);
+ // Evaluate and cache const subqueries now, before the mutex.
+ if (partial_cond)
+ partial_cond->val_int();
+
mysql_mutex_lock(&LOCK_status);
if (option_type == OPT_GLOBAL)
calc_sum_of_all_status(&tmp);
res= show_status_array(thd, wild,
(SHOW_VAR *)all_status_vars.buffer,
option_type, tmp1, "", tables->table,
- upper_case_names, cond);
+ upper_case_names, partial_cond);
mysql_mutex_unlock(&LOCK_status);
DBUG_RETURN(res);
}
diff --git a/sql/sql_signal.cc b/sql/sql_signal.cc
index c9f1f42159f..e671ad9526f 100644
--- a/sql/sql_signal.cc
+++ b/sql/sql_signal.cc
@@ -479,7 +479,7 @@ bool Signal_statement::execute(THD *thd)
bool Resignal_statement::execute(THD *thd)
{
- MYSQL_ERROR *signaled;
+ Sql_condition_info *signaled;
int result= TRUE;
DBUG_ENTER("Resignal_statement::execute");
@@ -492,15 +492,21 @@ bool Resignal_statement::execute(THD *thd)
DBUG_RETURN(result);
}
+ MYSQL_ERROR signaled_err(thd->mem_root);
+ signaled_err.set(signaled->m_sql_errno,
+ signaled->m_sql_state,
+ signaled->m_level,
+ signaled->m_message);
+
if (m_cond == NULL)
{
/* RESIGNAL without signal_value */
- result= raise_condition(thd, signaled);
+ result= raise_condition(thd, &signaled_err);
DBUG_RETURN(result);
}
/* RESIGNAL with signal_value */
- result= raise_condition(thd, signaled);
+ result= raise_condition(thd, &signaled_err);
DBUG_RETURN(result);
}
diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
index 687fdf26228..e34b4b21819 100644
--- a/sql/sql_statistics.cc
+++ b/sql/sql_statistics.cc
@@ -1296,7 +1296,7 @@ public:
if (tree->elements == 0)
return (ulonglong) tree->elements_in_tree();
count= 0;
- tree->walk(count_distinct_walk, (void*) &count);
+ tree->walk(table_field->table, count_distinct_walk, (void*) &count);
return count;
}
};
diff --git a/sql/sql_time.cc b/sql/sql_time.cc
index 5c0471fdfaa..57dbd979933 100644
--- a/sql/sql_time.cc
+++ b/sql/sql_time.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2010, Oracle and/or its affiliates.
- Copyright (c) 2009-2011 Monty Program Ab
+ Copyright (c) 2009, 2013 Monty Program Ab.
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
@@ -154,7 +154,7 @@ bool get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month,
uchar *month_pos;
DBUG_ENTER("get_date_from_daynr");
- if (daynr < 365 || daynr > MAX_DAY_NUMBER)
+ if (daynr < 366 || daynr > MAX_DAY_NUMBER)
DBUG_RETURN(1);
year= (uint) (daynr*100 / 36525L);
diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc
index 9d4ca5e1373..4b77344c042 100644
--- a/sql/sql_truncate.cc
+++ b/sql/sql_truncate.cc
@@ -24,7 +24,7 @@
#include "sql_acl.h" // DROP_ACL
#include "sql_parse.h" // check_one_table_access()
#include "sql_truncate.h"
-#include "sql_show.h"
+#include "sql_show.h" //append_identifier()
/**
@@ -263,6 +263,7 @@ static bool recreate_temporary_table(THD *thd, TABLE *table)
DBUG_ENTER("recreate_temporary_table");
memset(&create_info, 0, sizeof(create_info));
+ create_info.options|= HA_LEX_CREATE_TMP_TABLE;
table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 56f413afbd1..eb4454ecab3 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -716,7 +716,7 @@ bool st_select_lex_unit::exec()
ER(ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT),
thd->accessed_rows_and_keys,
thd->lex->limit_rows_examined->val_uint());
- thd->killed= NOT_KILLED;
+ thd->reset_killed();
break;
}
}
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 8a0d95722e8..28b9fe7eacd 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -471,12 +471,15 @@ int mysql_update(THD *thd,
We can't update table directly; We must first search after all
matching rows before updating the table!
*/
+
+ // Verify that table->restore_column_maps_after_mark_index() will work
+ DBUG_ASSERT(table->read_set == &table->def_read_set);
+ DBUG_ASSERT(table->write_set == &table->def_write_set);
+
if (used_index < MAX_KEY && old_covering_keys.is_set(used_index))
table->add_read_columns_used_by_index(used_index);
else
- {
table->use_all_columns();
- }
/* note: We avoid sorting if we sort on the used index */
if (order && (need_sort || used_key_is_modified))
@@ -613,8 +616,11 @@ int mysql_update(THD *thd,
if (error >= 0)
goto err;
}
- if (table->key_read)
- table->restore_column_maps_after_mark_index();
+ /*
+ This restore bitmaps, works for add_read_columns_used_by_index() and
+ use_all_columns():
+ */
+ table->restore_column_maps_after_mark_index();
}
if (ignore)
@@ -2238,11 +2244,16 @@ err:
}
err2:
- (void) table->file->ha_rnd_end();
- (void) tmp_table->file->ha_rnd_end();
+ if (table->file->inited)
+ (void) table->file->ha_rnd_end();
+ if (tmp_table->file->inited)
+ (void) tmp_table->file->ha_rnd_end();
check_opt_it.rewind();
while (TABLE *tbl= check_opt_it++)
- tbl->file->ha_rnd_end();
+ {
+ if (tbl->file->inited)
+ (void) tbl->file->ha_rnd_end();
+ }
if (updated != org_updated)
{
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 1fd6f5150cb..9664aad1e19 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -11587,11 +11587,19 @@ show_param:
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_SHOW_AUTHORS;
+ push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT,
+ ER(ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT),
+ "SHOW AUTHORS");
}
| CONTRIBUTORS_SYM
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_SHOW_CONTRIBUTORS;
+ push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT,
+ ER(ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT),
+ "SHOW CONTRIBUTORS");
}
| PRIVILEGES
{
diff --git a/sql/table.cc b/sql/table.cc
index 117345ad25d..a011fa845a7 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -2163,9 +2163,11 @@ end:
@brief
Unpack the definition of a virtual column from its linear representation
- @parm
+ @param
thd The thread object
@param
+ mem_root The mem_root object where to allocated memory
+ @param
table The table containing the virtual column
@param
field The field for the virtual
@@ -2194,6 +2196,7 @@ end:
TRUE Otherwise
*/
bool unpack_vcol_info_from_frm(THD *thd,
+ MEM_ROOT *mem_root,
TABLE *table,
Field *field,
LEX_STRING *vcol_expr,
@@ -2221,7 +2224,7 @@ bool unpack_vcol_info_from_frm(THD *thd,
"PARSE_VCOL_EXPR (<expr_string_from_frm>)".
*/
- if (!(vcol_expr_str= (char*) alloc_root(&table->mem_root,
+ if (!(vcol_expr_str= (char*) alloc_root(mem_root,
vcol_expr->length +
parse_vcol_keyword.length + 3)))
{
@@ -2255,10 +2258,10 @@ bool unpack_vcol_info_from_frm(THD *thd,
We need to use CONVENTIONAL_EXECUTION here to ensure that
any new items created by fix_fields() are not reverted.
*/
- Query_arena expr_arena(&table->mem_root,
+ Query_arena expr_arena(mem_root,
Query_arena::STMT_CONVENTIONAL_EXECUTION);
- if (!(vcol_arena= (Query_arena *) alloc_root(&table->mem_root,
- sizeof(Query_arena))))
+ if (!(vcol_arena= (Query_arena *) alloc_root(mem_root,
+ sizeof(Query_arena))))
goto err;
*vcol_arena= expr_arena;
table->expr_arena= vcol_arena;
@@ -2525,6 +2528,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
if (share->vfields && (*field_ptr)->vcol_info)
{
if (unpack_vcol_info_from_frm(thd,
+ &outparam->mem_root,
outparam,
*field_ptr,
&(*field_ptr)->vcol_info->expr_str,
@@ -5004,19 +5008,33 @@ TABLE *TABLE_LIST::get_real_join_table()
DBUG_ASSERT(tbl->derived == NULL ||
tbl->derived->first_select()->next_select() == NULL);
- if (tbl->table)
- table= tbl->table;
- tbl= (tbl->view != NULL ?
- tbl->view->select_lex.get_table_list() :
- tbl->derived->first_select()->get_table_list());
-
- /* find left table in outer join on this level */
- while(tbl->outer_join & JOIN_TYPE_RIGHT)
{
- DBUG_ASSERT(tbl->next_local);
- tbl= tbl->next_local;
+ List_iterator_fast<TABLE_LIST> ti;
+ {
+ List_iterator_fast<TABLE_LIST>
+ ti(tbl->view != NULL ?
+ tbl->view->select_lex.top_join_list :
+ tbl->derived->first_select()->top_join_list);
+ for (;;)
+ {
+ tbl= NULL;
+ /*
+ Find left table in outer join on this level
+ (the list is reverted).
+ */
+ for (TABLE_LIST *t= ti++; t; t= ti++)
+ tbl= t;
+ /*
+ It is impossible that the list is empty
+ so tbl can't be NULL after above loop.
+ */
+ if (!tbl->nested_join)
+ break;
+ /* go deeper if we've found nested join */
+ ti= tbl->nested_join->join_list;
+ }
+ }
}
-
}
return tbl->table;
diff --git a/sql/table.h b/sql/table.h
index 0625d791b49..1a567ae75d1 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -2412,6 +2412,9 @@ void init_mdl_requests(TABLE_LIST *table_list);
int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
uint db_stat, uint prgflag, uint ha_open_flags,
TABLE *outparam, bool is_create_table);
+bool unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root,
+ TABLE *table, Field *field,
+ LEX_STRING *vcol_expr, bool *error_reported);
TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
uint key_length);
void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
diff --git a/sql/tztime.cc b/sql/tztime.cc
index ad5deea7b25..b16cc65d6bb 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -1718,14 +1718,11 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
}
table= tz_tables[0].table;
- /*
- It is OK to ignore ha_index_init()/ha_index_end() return values since
- mysql.time_zone* tables are MyISAM and these operations always succeed
- for MyISAM.
- */
- (void)table->file->ha_index_init(0, 1);
- table->use_all_columns();
+ if (table->file->ha_index_init(0, 1))
+ goto end_with_close;
+
+ table->use_all_columns();
tz_leapcnt= 0;
res= table->file->ha_index_first(table->record[0]);
@@ -1913,12 +1910,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
tz_tables= tz_tables->next_local;
table->field[0]->store(tz_name->ptr(), tz_name->length(),
&my_charset_latin1);
- /*
- It is OK to ignore ha_index_init()/ha_index_end() return values since
- mysql.time_zone* tables are MyISAM and these operations always succeed
- for MyISAM.
- */
- (void)table->file->ha_index_init(0, 1);
+ if (table->file->ha_index_init(0, 1))
+ goto end;
if (table->file->ha_index_read_map(table->record[0], table->field[0]->ptr,
HA_WHOLE_KEY, HA_READ_KEY_EXACT))
@@ -1951,7 +1944,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
field->get_key_image(keybuff,
min(field->key_length(), sizeof(keybuff)),
Field::itRAW);
- (void)table->file->ha_index_init(0, 1);
+ if (table->file->ha_index_init(0, 1))
+ goto end;
if (table->file->ha_index_read_map(table->record[0], keybuff,
HA_WHOLE_KEY, HA_READ_KEY_EXACT))
@@ -1983,7 +1977,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
field->get_key_image(keybuff,
min(field->key_length(), sizeof(keybuff)),
Field::itRAW);
- (void)table->file->ha_index_init(0, 1);
+ if (table->file->ha_index_init(0, 1))
+ goto end;
res= table->file->ha_index_read_map(table->record[0], keybuff,
(key_part_map)1, HA_READ_KEY_EXACT);
@@ -2053,7 +2048,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
*/
table= tz_tables->table;
table->field[0]->store((longlong) tzid, TRUE);
- (void)table->file->ha_index_init(0, 1);
+ if (table->file->ha_index_init(0, 1))
+ goto end;
res= table->file->ha_index_read_map(table->record[0], keybuff,
(key_part_map)1, HA_READ_KEY_EXACT);
@@ -2187,8 +2183,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
end:
- if (table)
- (void)table->file->ha_index_end();
+ if (table && table->file->inited)
+ (void) table->file->ha_index_end();
DBUG_RETURN(return_val);
}
diff --git a/sql/uniques.cc b/sql/uniques.cc
index 22f098f8f9a..9fa06311ece 100644
--- a/sql/uniques.cc
+++ b/sql/uniques.cc
@@ -77,7 +77,10 @@ int unique_intersect_write_to_ptrs(uchar* key, element_count count, Unique *uniq
Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg,
uint size_arg, ulonglong max_in_memory_size_arg,
uint min_dupl_count_arg)
- :max_in_memory_size(max_in_memory_size_arg), size(size_arg), elements(0)
+ :max_in_memory_size(max_in_memory_size_arg),
+ record_pointers(NULL),
+ size(size_arg),
+ elements(0)
{
min_dupl_count= min_dupl_count_arg;
full_size= size;
@@ -582,6 +585,7 @@ end:
SYNOPSIS
Unique:walk()
All params are 'IN':
+ table parameter for the call of the merge method
action function-visitor, typed in include/my_tree.h
function is called for each unique element
arg argument for visitor, which is passed to it on each call
@@ -590,71 +594,67 @@ end:
<> 0 error
*/
-bool Unique::walk(tree_walk_action action, void *walk_action_arg)
+bool Unique::walk(TABLE *table, tree_walk_action action, void *walk_action_arg)
{
- int res;
+ int res= 0;
uchar *merge_buffer;
if (elements == 0) /* the whole tree is in memory */
return tree_walk(&tree, action, walk_action_arg, left_root_right);
+ table->sort.found_records=elements+tree.elements_in_tree;
/* flush current tree to the file to have some memory for merge buffer */
if (flush())
return 1;
if (flush_io_cache(&file) || reinit_io_cache(&file, READ_CACHE, 0L, 0, 0))
return 1;
- if (!(merge_buffer= (uchar *) my_malloc((ulong) max_in_memory_size,
- MYF(MY_THREAD_SPECIFIC))))
+ ulong buff_sz= (max_in_memory_size / full_size + 1) * full_size;
+ if (!(merge_buffer= (uchar *) my_malloc(buff_sz, MYF(MY_THREAD_SPECIFIC))))
return 1;
- res= merge_walk(merge_buffer, (ulong) max_in_memory_size, size,
- (BUFFPEK *) file_ptrs.buffer,
- (BUFFPEK *) file_ptrs.buffer + file_ptrs.elements,
- action, walk_action_arg,
- tree.compare, tree.custom_arg, &file);
+ if (buff_sz < full_size * (file_ptrs.elements + 1UL))
+ res= merge(table, merge_buffer, buff_sz >= full_size * MERGEBUFF2) ;
+
+ if (!res)
+ {
+ res= merge_walk(merge_buffer, (ulong) max_in_memory_size, full_size,
+ (BUFFPEK *) file_ptrs.buffer,
+ (BUFFPEK *) file_ptrs.buffer + file_ptrs.elements,
+ action, walk_action_arg,
+ tree.compare, tree.custom_arg, &file);
+ }
my_free(merge_buffer);
return res;
}
+
/*
- Modify the TABLE element so that when one calls init_records()
- the rows will be read in priority order.
-*/
+ DESCRIPTION
+ Perform multi-pass sort merge of the elements accessed through table->sort,
+ using the buffer buff as the merge buffer. The last pass is not performed
+ if without_last_merge is TRUE.
+ SYNOPSIS
+ Unique:merge()
+ All params are 'IN':
+ table the parameter to access sort context
+ buff merge buffer
+ without_last_merge TRUE <=> do not perform the last merge
+ RETURN VALUE
+ 0 OK
+ <> 0 error
+ */
-bool Unique::get(TABLE *table)
+bool Unique::merge(TABLE *table, uchar *buff, bool without_last_merge)
{
- table->sort.found_records=elements+tree.elements_in_tree;
- if (my_b_tell(&file) == 0)
- {
- /* Whole tree is in memory; Don't use disk if you don't need to */
- if ((record_pointers=table->sort.record_pointers= (uchar*)
- my_malloc(size * tree.elements_in_tree, MYF(MY_THREAD_SPECIFIC))))
- {
- tree_walk_action action= min_dupl_count ?
- (tree_walk_action) unique_intersect_write_to_ptrs :
- (tree_walk_action) unique_write_to_ptrs;
- filtered_out_elems= 0;
- (void) tree_walk(&tree, action,
- this, left_root_right);
- table->sort.found_records-= filtered_out_elems;
- return 0;
- }
- }
- /* Not enough memory; Save the result to file && free memory used by tree */
- if (flush())
- return 1;
-
- IO_CACHE *outfile=table->sort.io_cache;
+ IO_CACHE *outfile= table->sort.io_cache;
BUFFPEK *file_ptr= (BUFFPEK*) file_ptrs.buffer;
uint maxbuffer= file_ptrs.elements - 1;
- uchar *sort_buffer;
my_off_t save_pos;
- bool error=1;
-
- /* Open cached file if it isn't open */
- outfile=table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
- MYF(MY_ZEROFILL |
- MY_THREAD_SPECIFIC));
+ bool error= 1;
+ /* Open cached file if it isn't open */
+ if (!outfile)
+ outfile= table->sort.io_cache= (IO_CACHE*) my_malloc(sizeof(IO_CACHE),
+ MYF(MY_THREAD_SPECIFIC|MY_ZEROFILL));
if (!outfile ||
(! my_b_inited(outfile) &&
open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER,
@@ -665,20 +665,16 @@ bool Unique::get(TABLE *table)
Sort_param sort_param;
bzero((char*) &sort_param,sizeof(sort_param));
sort_param.max_rows= elements;
- sort_param.sort_form=table;
+ sort_param.sort_form= table;
sort_param.rec_length= sort_param.sort_length= sort_param.ref_length=
full_size;
sort_param.min_dupl_count= min_dupl_count;
sort_param.res_length= 0;
sort_param.max_keys_per_buffer=
(uint) (max_in_memory_size / sort_param.sort_length);
- sort_param.not_killable=1;
+ sort_param.not_killable= 1;
- if (!(sort_buffer=(uchar*) my_malloc((sort_param.max_keys_per_buffer+1) *
- sort_param.sort_length,
- MYF(MY_THREAD_SPECIFIC))))
- return 1;
- sort_param.unique_buff= sort_buffer+(sort_param.max_keys_per_buffer *
+ sort_param.unique_buff= buff +(sort_param.max_keys_per_buffer *
sort_param.sort_length);
sort_param.compare= (qsort2_cmp) buffpek_compare;
@@ -686,27 +682,74 @@ bool Unique::get(TABLE *table)
sort_param.cmp_context.key_compare_arg= tree.custom_arg;
/* Merge the buffers to one file, removing duplicates */
- if (merge_many_buff(&sort_param,sort_buffer,file_ptr,&maxbuffer,&file))
+ if (merge_many_buff(&sort_param,buff,file_ptr,&maxbuffer,&file))
goto err;
if (flush_io_cache(&file) ||
reinit_io_cache(&file,READ_CACHE,0L,0,0))
goto err;
sort_param.res_length= sort_param.rec_length-
(min_dupl_count ? sizeof(min_dupl_count) : 0);
- if (merge_index(&sort_param, sort_buffer, file_ptr, maxbuffer, &file, outfile))
+ if (without_last_merge)
+ {
+ file_ptrs.elements= maxbuffer+1;
+ return 0;
+ }
+ if (merge_index(&sort_param, buff, file_ptr, maxbuffer, &file, outfile))
goto err;
- error=0;
+ error= 0;
err:
- my_free(sort_buffer);
if (flush_io_cache(outfile))
- error=1;
+ error= 1;
/* Setup io_cache for reading */
- save_pos=outfile->pos_in_file;
+ save_pos= outfile->pos_in_file;
if (reinit_io_cache(outfile,READ_CACHE,0L,0,0))
- error=1;
+ error= 1;
outfile->end_of_file=save_pos;
return error;
}
+/*
+ Modify the TABLE element so that when one calls init_records()
+ the rows will be read in priority order.
+*/
+
+bool Unique::get(TABLE *table)
+{
+ bool rc= 1;
+ uchar *sort_buffer= NULL;
+ table->sort.found_records= elements+tree.elements_in_tree;
+
+ if (my_b_tell(&file) == 0)
+ {
+ /* Whole tree is in memory; Don't use disk if you don't need to */
+ if ((record_pointers=table->sort.record_pointers= (uchar*)
+ my_malloc(size * tree.elements_in_tree, MYF(MY_THREAD_SPECIFIC))))
+ {
+ tree_walk_action action= min_dupl_count ?
+ (tree_walk_action) unique_intersect_write_to_ptrs :
+ (tree_walk_action) unique_write_to_ptrs;
+ filtered_out_elems= 0;
+ (void) tree_walk(&tree, action,
+ this, left_root_right);
+ table->sort.found_records-= filtered_out_elems;
+ return 0;
+ }
+ }
+ /* Not enough memory; Save the result to file && free memory used by tree */
+ if (flush())
+ return 1;
+
+ ulong buff_sz= (max_in_memory_size / full_size + 1) * full_size;
+ if (!(sort_buffer= (uchar*) my_malloc(buff_sz, MYF(MY_THREAD_SPECIFIC))))
+ return 1;
+
+ if (merge(table, sort_buffer, FALSE))
+ goto err;
+ rc= 0;
+
+err:
+ my_free(sort_buffer);
+ return rc;
+}