summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/item_create.cc78
-rw-r--r--sql/item_create.h57
-rw-r--r--sql/item_func.h2
-rw-r--r--sql/item_subselect.cc24
-rw-r--r--sql/opt_subselect.cc1
-rw-r--r--sql/rpl_parallel.cc4
-rw-r--r--sql/sql_acl.cc2
-rw-r--r--sql/sql_join_cache.cc228
-rw-r--r--sql/sql_join_cache.h15
-rw-r--r--sql/sql_lex.cc2
-rw-r--r--sql/sql_select.cc266
-rw-r--r--sql/sql_select.h6
-rw-r--r--sql/sql_show.cc3
-rw-r--r--sql/sql_yacc.yy2
-rw-r--r--sql/sql_yacc_ora.yy2
-rw-r--r--sql/table.h10
16 files changed, 418 insertions, 284 deletions
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 616ba3a641a..a95d2ae4f9a 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -7249,7 +7249,7 @@ Create_func_year_week::create_native(THD *thd, const LEX_CSTRING *name,
- keep 1 line per entry, it makes grep | sort easier
*/
-Native_func_registry func_array[] =
+const Native_func_registry func_array[] =
{
{ { STRING_WITH_LEN("ABS") }, BUILDER(Create_func_abs)},
{ { STRING_WITH_LEN("ACOS") }, BUILDER(Create_func_acos)},
@@ -7609,9 +7609,10 @@ Native_func_registry func_array[] =
{ {0, 0}, NULL}
};
-size_t func_array_length= sizeof(func_array) / sizeof(Native_func_registry) - 1;
-static HASH native_functions_hash;
+const size_t func_array_length= sizeof(func_array) / sizeof(Native_func_registry) - 1;
+
+Native_functions_hash native_functions_hash;
extern "C" uchar*
get_native_fct_hash_key(const uchar *buff, size_t *length,
@@ -7628,85 +7629,89 @@ get_native_fct_hash_key(const uchar *buff, size_t *length,
startup only (before going multi-threaded)
*/
-int item_create_init()
+bool Native_functions_hash::init(size_t count)
{
- DBUG_ENTER("item_create_init");
+ DBUG_ENTER("Native_functions_hash::init");
- if (my_hash_init(& native_functions_hash,
+ if (my_hash_init(this,
system_charset_info,
- array_elements(func_array),
+ (ulong) count,
0,
0,
(my_hash_get_key) get_native_fct_hash_key,
NULL, /* Nothing to free */
MYF(0)))
- DBUG_RETURN(1);
+ DBUG_RETURN(true);
- DBUG_RETURN(item_create_append(func_array));
+ DBUG_RETURN(false);
}
-int item_create_append(Native_func_registry array[])
+
+bool Native_functions_hash::append(const Native_func_registry array[])
{
- Native_func_registry *func;
+ const Native_func_registry *func;
- DBUG_ENTER("item_create_append");
+ DBUG_ENTER("Native_functions_hash::append");
for (func= array; func->builder != NULL; func++)
{
- if (my_hash_insert(& native_functions_hash, (uchar*) func))
- DBUG_RETURN(1);
+ if (my_hash_insert(this, (uchar*) func))
+ DBUG_RETURN(true);
}
#ifndef DBUG_OFF
- for (uint i=0 ; i < native_functions_hash.records ; i++)
+ for (uint i=0 ; i < records ; i++)
{
- func= (Native_func_registry*) my_hash_element(& native_functions_hash, i);
+ func= (Native_func_registry*) my_hash_element(this, i);
DBUG_PRINT("info", ("native function: %s length: %u",
func->name.str, (uint) func->name.length));
}
#endif
- DBUG_RETURN(0);
+ DBUG_RETURN(false);
}
-int item_create_remove(Native_func_registry array[])
+
+bool Native_functions_hash::remove(const Native_func_registry array[])
{
- Native_func_registry *func;
+ const Native_func_registry *func;
- DBUG_ENTER("item_create_remove");
+ DBUG_ENTER("Native_functions_hash::remove");
for (func= array; func->builder != NULL; func++)
{
- if (my_hash_delete(& native_functions_hash, (uchar*) func))
- DBUG_RETURN(1);
+ if (my_hash_delete(this, (uchar*) func))
+ DBUG_RETURN(true);
}
- DBUG_RETURN(0);
+ DBUG_RETURN(false);
}
+
/*
Empty the hash table for native functions.
Note: this code is not thread safe, and is intended to be used at server
shutdown only (after thread requests have been executed).
*/
-void item_create_cleanup()
+void Native_functions_hash::cleanup()
{
- DBUG_ENTER("item_create_cleanup");
- my_hash_free(& native_functions_hash);
+ DBUG_ENTER("Native_functions_hash::cleanup");
+ my_hash_free(this);
DBUG_VOID_RETURN;
}
+
Create_func *
-find_native_function_builder(THD *thd, const LEX_CSTRING *name)
+Native_functions_hash::find(THD *thd, const LEX_CSTRING &name) const
{
Native_func_registry *func;
Create_func *builder= NULL;
/* Thread safe */
- func= (Native_func_registry*) my_hash_search(&native_functions_hash,
- (uchar*) name->str,
- name->length);
+ func= (Native_func_registry*) my_hash_search(this,
+ (uchar*) name.str,
+ name.length);
if (func)
{
@@ -7716,6 +7721,19 @@ find_native_function_builder(THD *thd, const LEX_CSTRING *name)
return builder;
}
+
+int item_create_init()
+{
+ return native_functions_hash.init(func_array, array_elements(func_array));
+}
+
+
+void item_create_cleanup()
+{
+ native_functions_hash.cleanup();
+}
+
+
Create_qfunc *
find_qualified_function_builder(THD *thd)
{
diff --git a/sql/item_create.h b/sql/item_create.h
index 8456644ff6d..60a757b95d8 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -145,16 +145,6 @@ protected:
/**
- Find the native function builder associated with a given function name.
- @param thd The current thread
- @param name The native function name
- @return The native function builder associated with the name, or NULL
-*/
-extern Create_func *find_native_function_builder(THD *thd,
- const LEX_CSTRING *name);
-
-
-/**
Find the function builder for qualified functions.
@param thd The current thread
@return A function builder for qualified functions
@@ -200,9 +190,52 @@ struct Native_func_registry
Create_func *builder;
};
+
+class Native_functions_hash: public HASH
+{
+public:
+ Native_functions_hash()
+ {
+ bzero(this, sizeof(*this));
+ }
+ ~Native_functions_hash()
+ {
+ /*
+ No automatic free because objects of this type
+ are expected to be declared statically.
+ The code in cleanup() calls my_hash_free() which may not work correctly
+ at the very end of mariadbd shutdown.
+ The the upper level code should call cleanup() explicitly.
+
+ Unfortunatelly, it's not possible to use DBUG_ASSERT(!records) here,
+ because the server terminates using exit() in some cases,
+ e.g. in the test main.named_pipe with the "Create named pipe failed"
+ error.
+ */
+ }
+ bool init(size_t count);
+ bool init(const Native_func_registry array[], size_t count)
+ {
+ return init(count) || append(array);
+ }
+ bool append(const Native_func_registry array[]);
+ bool remove(const Native_func_registry array[]);
+ void cleanup();
+ /**
+ Find the native function builder associated with a given function name.
+ @param thd The current thread
+ @param name The native function name
+ @return The native function builder associated with the name, or NULL
+ */
+ Create_func *find(THD *thd, const LEX_CSTRING &name) const;
+};
+
+extern MYSQL_PLUGIN_IMPORT Native_functions_hash native_functions_hash;
+
+extern const Native_func_registry func_array[];
+extern const size_t func_array_length;
+
int item_create_init();
-int item_create_append(Native_func_registry array[]);
-int item_create_remove(Native_func_registry array[]);
void item_create_cleanup();
Item *create_func_dyncol_create(THD *thd, List<DYNCALL_CREATE_DEF> &list);
diff --git a/sql/item_func.h b/sql/item_func.h
index 41f2a52616d..3afe0d00661 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -381,7 +381,7 @@ public:
{
for (uint i= 0; i < arg_count; i++)
{
- args[i]->no_rows_in_result();
+ args[i]->restore_to_before_no_rows_in_result();
}
}
void convert_const_compared_to_int_field(THD *thd);
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index a4a36d96ccc..ae0ab27ec31 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -4536,16 +4536,11 @@ void subselect_union_engine::print(String *str, enum_query_type query_type)
void subselect_uniquesubquery_engine::print(String *str,
enum_query_type query_type)
{
+ TABLE *table= tab->tab_list ? tab->tab_list->table : tab->table;
str->append(STRING_WITH_LEN("<primary_index_lookup>("));
tab->ref.items[0]->print(str, query_type);
- if (!tab->table)
- {
- // table is not opened so unknown
- str->append(')');
- return;
- }
str->append(STRING_WITH_LEN(" in "));
- if (tab->table->s->table_category == TABLE_CATEGORY_TEMPORARY)
+ if (table->s->table_category == TABLE_CATEGORY_TEMPORARY)
{
/*
Temporary tables' names change across runs, so they can't be used for
@@ -4554,8 +4549,8 @@ void subselect_uniquesubquery_engine::print(String *str,
str->append(STRING_WITH_LEN("<temporary table>"));
}
else
- str->append(&tab->table->s->table_name);
- KEY *key_info= tab->table->key_info+ tab->ref.key;
+ str->append(&table->s->table_name);
+ KEY *key_info= table->key_info+ tab->ref.key;
str->append(STRING_WITH_LEN(" on "));
str->append(&key_info->name);
if (cond)
@@ -4573,12 +4568,13 @@ all other tests pass.
void subselect_uniquesubquery_engine::print(String *str)
{
- KEY *key_info= tab->table->key_info + tab->ref.key;
+ TABLE *table= tab->tab_list ? tab->tab_list->table : tab->table;
+ KEY *key_info= table->key_info + tab->ref.key;
str->append(STRING_WITH_LEN("<primary_index_lookup>("));
for (uint i= 0; i < key_info->user_defined_key_parts; i++)
tab->ref.items[i]->print(str);
str->append(STRING_WITH_LEN(" in "));
- str->append(&tab->table->s->table_name);
+ str->append(&table->s->table_name);
str->append(STRING_WITH_LEN(" on "));
str->append(&key_info->name);
if (cond)
@@ -4593,11 +4589,12 @@ void subselect_uniquesubquery_engine::print(String *str)
void subselect_indexsubquery_engine::print(String *str,
enum_query_type query_type)
{
+ TABLE *table= tab->tab_list ? tab->tab_list->table : tab->table;
str->append(STRING_WITH_LEN("<index_lookup>("));
tab->ref.items[0]->print(str, query_type);
str->append(STRING_WITH_LEN(" in "));
- str->append(tab->table->s->table_name.str, tab->table->s->table_name.length);
- KEY *key_info= tab->table->key_info+ tab->ref.key;
+ str->append(&table->s->table_name);
+ KEY *key_info= table->key_info+ tab->ref.key;
str->append(STRING_WITH_LEN(" on "));
str->append(&key_info->name);
if (check_null)
@@ -5277,6 +5274,7 @@ subselect_hash_sj_engine::make_unique_engine()
DBUG_RETURN(NULL);
tab->table= tmp_table;
+ tab->tab_list= 0;
tab->preread_init_done= FALSE;
tab->ref.tmp_table_index_lookup_init(thd, tmp_key, it, FALSE);
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index 1c03b0bb4a3..db7a4c0fc14 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -4128,6 +4128,7 @@ bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab)
sjm->materialized= FALSE;
sjm_tab->table= sjm->table;
+ sjm_tab->tab_list= emb_sj_nest;
sjm->table->pos_in_table_list= emb_sj_nest;
DBUG_RETURN(FALSE);
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index b550315d69f..6ca582e4f21 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -2317,9 +2317,7 @@ rpl_parallel::find(uint32 domain_id)
mysql_cond_init(key_COND_parallel_entry, &e->COND_parallel_entry, NULL);
if (my_hash_insert(&domain_hash, (uchar *)e))
{
- mysql_cond_destroy(&e->COND_parallel_entry);
- mysql_mutex_destroy(&e->LOCK_parallel_entry);
- my_free(e);
+ free_rpl_parallel_entry(e);
return NULL;
}
}
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 4bb16e3248d..c764d2fe2f7 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -5462,7 +5462,7 @@ table_hash_search(const char *host, const char *ip, const char *db,
const char *user, const char *tname, bool exact)
{
return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db,
- user, tname, exact, FALSE);
+ user, tname, exact, (lower_case_table_names > 0));
}
static bool column_priv_insert(GRANT_TABLE *grant)
diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc
index beca5fc782a..7d61ce31cca 100644
--- a/sql/sql_join_cache.cc
+++ b/sql/sql_join_cache.cc
@@ -634,7 +634,7 @@ void JOIN_CACHE::create_remaining_fields()
/*
- Calculate and set all cache constants
+ Calculate and set all cache constants
SYNOPSIS
set_constants()
@@ -694,16 +694,22 @@ void JOIN_CACHE::set_constants()
(prev_cache ? prev_cache->get_size_of_rec_offset() : 0) +
length + fields*sizeof(uint);
pack_length_with_blob_ptrs= pack_length + blobs*sizeof(uchar *);
- min_buff_size= 0;
min_records= 1;
+ min_buff_size= get_min_join_buffer_size();
buff_size= (size_t)MY_MAX(join->thd->variables.join_buff_size,
- get_min_join_buffer_size());
+ min_buff_size);
size_of_rec_ofs= offset_size(buff_size);
size_of_rec_len= blobs ? size_of_rec_ofs : offset_size(len);
size_of_fld_ofs= size_of_rec_len;
base_prefix_length= (with_length ? size_of_rec_len : 0) +
(prev_cache ? prev_cache->get_size_of_rec_offset() : 0);
- /*
+ /*
+ Call ge_min_join_buffer_size() again as the size may have got smaller
+ if size_of_rec_ofs or some other variable changed since last call.
+ */
+ min_buff_size= 0;
+ min_buff_size= get_min_join_buffer_size();
+ /*
The size of the offsets for referenced fields will be added later.
The values of 'pack_length' and 'pack_length_with_blob_ptrs' are adjusted
every time when the first reference to the referenced field is registered.
@@ -767,30 +773,29 @@ uint JOIN_CACHE::get_record_max_affix_length()
size_t JOIN_CACHE::get_min_join_buffer_size()
{
- if (!min_buff_size)
+ if (min_buff_size)
+ return min_buff_size; // use cached value
+
+ size_t len= 0, len_last= 0, len_addon, min_sz, add_sz= 0;
+
+ for (JOIN_TAB *tab= start_tab; tab != join_tab;
+ tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS))
{
- size_t len= 0;
- size_t len_last= 0;
- for (JOIN_TAB *tab= start_tab; tab != join_tab;
- tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS))
- {
- len+= tab->get_max_used_fieldlength();
- len_last+= tab->get_used_fieldlength();
- }
- size_t len_addon= get_record_max_affix_length() +
- get_max_key_addon_space_per_record();
- len+= len_addon;
- len_last+= len_addon;
- size_t min_sz= len*(min_records-1) + len_last;
- min_sz+= pack_length_with_blob_ptrs;
- size_t add_sz= 0;
- for (uint i=0; i < min_records; i++)
- add_sz+= join_tab_scan->aux_buffer_incr(i+1);
- avg_aux_buffer_incr= add_sz/min_records;
- min_sz+= add_sz;
- set_if_bigger(min_sz, 1);
- min_buff_size= min_sz;
+ len+= tab->get_max_used_fieldlength();
+ len_last+= tab->get_used_fieldlength();
}
+ len_addon= (get_record_max_affix_length() +
+ get_max_key_addon_space_per_record());
+ len+= len_addon;
+ len_last+= len_addon;
+ min_sz= len*(min_records-1) + len_last;
+ min_sz+= pack_length_with_blob_ptrs;
+ for (uint i=0; i < min_records; i++)
+ add_sz+= join_tab_scan->aux_buffer_incr(i+1);
+ avg_aux_buffer_incr= add_sz/min_records;
+ min_sz+= add_sz;
+ set_if_bigger(min_sz, 1);
+ min_buff_size= min_sz;
return min_buff_size;
}
@@ -805,61 +810,67 @@ size_t JOIN_CACHE::get_min_join_buffer_size()
the estimated number of records in the partial join
DESCRIPTION
- At the first its invocation for the cache the function calculates the
- maximum possible size of join buffer for the cache. If the parameter
- optimize_buff_size true then this value does not exceed the size of the
- space needed for the estimated number of records 'max_records' in the
- partial join that joins tables from the first one through join_tab. This
- value is also capped off by the value of join_tab->join_buffer_size_limit,
- if it has been set a to non-zero value, and by the value of the system
- parameter join_buffer_size - otherwise. After the calculation of the
- interesting size the function saves the value in the field 'max_buff_size'
- in order to use it directly at the next invocations of the function.
- NOTES
- Currently the value of join_tab->join_buffer_size_limit is initialized
- to 0 and is never reset.
+ At the first its invocation for the cache the function calculates
+ the maximum possible size of join buffer for the cache. If the
+ parameter optimize_buff_size true then this value does not exceed
+ the size of the space needed for the estimated number of records
+ 'max_records' in the partial join that joins tables from the first
+ one through join_tab. This value is also capped off by the value
+ of the system parameter join_buffer_size. After the calculation of
+ the interesting size the function saves the value in the field
+ 'max_buff_size' in order to use it directly at the next
+ invocations of the function.
+
RETURN VALUE
The maximum possible size of the join buffer of this cache
*/
-size_t JOIN_CACHE::get_max_join_buffer_size(bool optimize_buff_size)
+size_t JOIN_CACHE::get_max_join_buffer_size(bool optimize_buff_size,
+ size_t min_sz)
{
- if (!max_buff_size)
+ if (max_buff_size)
+ return max_buff_size; // use cached value
+
+ size_t limit_sz= (size_t) join->thd->variables.join_buff_size;
+
+ if (!optimize_buff_size)
+ return max_buff_size= limit_sz;
+
+ size_t max_sz;
+ size_t len= 0;
+ double max_records, partial_join_cardinality=
+ (join_tab-1)->get_partial_join_cardinality();
+ /* Expected join buffer space used for one record */
+ size_t space_per_record;
+
+ for (JOIN_TAB *tab= start_tab; tab != join_tab;
+ tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS))
{
- size_t max_sz;
- size_t min_sz= get_min_join_buffer_size();
- size_t len= 0;
- for (JOIN_TAB *tab= start_tab; tab != join_tab;
- tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS))
- {
- len+= tab->get_used_fieldlength();
- }
- len+= get_record_max_affix_length();
- avg_record_length= len;
- len+= get_max_key_addon_space_per_record() + avg_aux_buffer_incr;
- space_per_record= len;
-
- size_t limit_sz= (size_t)join->thd->variables.join_buff_size;
- if (join_tab->join_buffer_size_limit)
- set_if_smaller(limit_sz, join_tab->join_buffer_size_limit);
- if (!optimize_buff_size)
- max_sz= limit_sz;
- else
- {
- if (limit_sz / max_records > space_per_record)
- max_sz= space_per_record * max_records;
- else
- max_sz= limit_sz;
- max_sz+= pack_length_with_blob_ptrs;
- set_if_smaller(max_sz, limit_sz);
- }
- set_if_bigger(max_sz, min_sz);
- max_buff_size= max_sz;
+ len+= tab->get_used_fieldlength();
}
+ len+= get_record_max_affix_length();
+ avg_record_length= len;
+ len+= get_max_key_addon_space_per_record() + avg_aux_buffer_incr;
+ space_per_record= len;
+
+ /* Note that space_per_record can be 0 if no table fields where used */
+ max_records= (double) (limit_sz / MY_MAX(space_per_record, 1));
+ set_if_smaller(max_records, partial_join_cardinality);
+ set_if_bigger(max_records, 10.0);
+
+ if ((size_t) (limit_sz / max_records) > space_per_record)
+ max_sz= space_per_record * (size_t) max_records;
+ else
+ max_sz= limit_sz;
+ max_sz+= pack_length_with_blob_ptrs;
+ set_if_smaller(max_sz, limit_sz);
+
+ set_if_bigger(max_sz, min_sz);
+ max_buff_size= max_sz;
return max_buff_size;
-}
+}
/*
@@ -899,16 +910,8 @@ int JOIN_CACHE::alloc_buffer()
join->thd->variables.join_buff_space_limit;
bool optimize_buff_size=
optimizer_flag(join->thd, OPTIMIZER_SWITCH_OPTIMIZE_JOIN_BUFFER_SIZE);
- double partial_join_cardinality= (join_tab-1)->get_partial_join_cardinality();
buff= NULL;
- min_buff_size= 0;
- max_buff_size= 0;
- min_records= 1;
- max_records= (size_t) (partial_join_cardinality <= join_buff_space_limit ?
- (ulonglong) partial_join_cardinality : join_buff_space_limit);
- set_if_bigger(max_records, 10);
- min_buff_size= get_min_join_buffer_size();
- buff_size= get_max_join_buffer_size(optimize_buff_size);
+ buff_size= get_max_join_buffer_size(optimize_buff_size, min_buff_size);
for (tab= start_tab; tab!= join_tab;
tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS))
@@ -923,13 +926,25 @@ int JOIN_CACHE::alloc_buffer()
curr_min_buff_space_sz+= min_buff_size;
curr_buff_space_sz+= buff_size;
- if (curr_min_buff_space_sz > join_buff_space_limit ||
- (curr_buff_space_sz > join_buff_space_limit &&
- (!optimize_buff_size ||
+ if (optimize_buff_size)
+ {
+ /*
+ optimize_join_buffer_size=on used. We should limit the join
+ buffer space to join_buff_space_limit if possible.
+ */
+ if (curr_min_buff_space_sz > join_buff_space_limit)
+ {
+ /*
+ Increase buffer size to minimum needed, to be able to use the
+ join buffer.
+ */
+ join_buff_space_limit= curr_min_buff_space_sz;
+ }
+ if (curr_buff_space_sz > join_buff_space_limit &&
join->shrink_join_buffers(join_tab, curr_buff_space_sz,
- join_buff_space_limit))))
- goto fail;
-
+ join_buff_space_limit))
+ goto fail; // Fatal error
+ }
if (for_explain_only)
return 0;
@@ -1079,18 +1094,19 @@ int JOIN_CACHE::init(bool for_explain)
/*
- Check the possibility to read the access keys directly from the join buffer
+ Check the possibility to read the access keys directly from the join buffer
SYNOPSIS
check_emb_key_usage()
DESCRIPTION
- The function checks some conditions at which the key values can be read
- directly from the join buffer. This is possible when the key values can be
- composed by concatenation of the record fields stored in the join buffer.
- Sometimes when the access key is multi-component the function has to re-order
- the fields written into the join buffer to make keys embedded. If key
- values for the key access are detected as embedded then 'use_emb_key'
- is set to TRUE.
+ The function checks some conditions at which the key values can be
+ read directly from the join buffer. This is possible when the key
+ values can be composed by concatenation of the record fields
+ stored in the join buffer. Sometimes when the access key is
+ multi-component the function has to re-order the fields written
+ into the join buffer to make keys embedded. If key values for the
+ key access are detected as embedded then 'use_emb_key' is set to
+ TRUE.
EXAMPLE
Let table t2 has an index defined on the columns a,b . Let's assume also
@@ -1243,7 +1259,7 @@ bool JOIN_CACHE::check_emb_key_usage()
trailing spaces
- significant part of fixed length fields that can have trailing spaces
with the prepanded length
- - data of non-blob variable length fields with the prepanded data length
+ - data of non-blob variable length fields with the prepanded data length
- blob data from blob fields with the prepanded data length
(5) record offset values for the data fields that are referred to from
other caches
@@ -1314,7 +1330,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full)
Check whether we won't be able to add any new record into the cache after
this one because the cache will be full. Set last_record to TRUE if it's so.
The assume that the cache will be full after the record has been written
- into it if either the remaining space of the cache is not big enough for the
+ into it if either the remaining space of the cache is not big enough for the
record's blob values or if there is a chance that not all non-blob fields
of the next record can be placed there.
This function is called only in the case when there is enough space left in
@@ -1336,7 +1352,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full)
/*
Put a reference to the fields of the record that are stored in the previous
- cache if there is any. This reference is passed by the 'link' parameter.
+ cache if there is any. This reference is passed by the 'link' parameter.
*/
if (prev_cache)
{
@@ -2766,7 +2782,6 @@ bool JOIN_CACHE_BKAH::save_explain_data(EXPLAIN_BKA_TYPE *explain)
int JOIN_CACHE_HASHED::init(bool for_explain)
{
- int rc= 0;
TABLE_REF *ref= &join_tab->ref;
DBUG_ENTER("JOIN_CACHE_HASHED::init");
@@ -2776,8 +2791,21 @@ int JOIN_CACHE_HASHED::init(bool for_explain)
key_length= ref->key_length;
- if ((rc= JOIN_CACHE::init(for_explain)) || for_explain)
- DBUG_RETURN (rc);
+ if (JOIN_CACHE::init(for_explain))
+ {
+ THD *thd= join->thd;
+ const char *errmsg=
+ "Could not create a join buffer. Please check and "
+ "adjust the value of the variables 'JOIN_BUFFER_SIZE (%llu)' and "
+ "'JOIN_BUFFER_SPACE_LIMIT (%llu)'";
+ my_printf_error(ER_OUTOFMEMORY, errmsg, MYF(0),
+ thd->variables.join_buff_size,
+ thd->variables.join_buff_space_limit);
+ DBUG_RETURN (1);
+ }
+
+ if (for_explain)
+ DBUG_RETURN(0);
if (!(key_buff= (uchar*) join->thd->alloc(key_length)))
DBUG_RETURN(1);
diff --git a/sql/sql_join_cache.h b/sql/sql_join_cache.h
index f75e9fd380f..8bdce1bd592 100644
--- a/sql/sql_join_cache.h
+++ b/sql/sql_join_cache.h
@@ -248,9 +248,6 @@ protected:
/* The expected size of the space per record in the auxiliary buffer */
size_t avg_aux_buffer_incr;
- /* Expected join buffer space used for one record */
- size_t space_per_record;
-
/* Pointer to the beginning of the join buffer */
uchar *buff;
/*
@@ -272,11 +269,6 @@ protected:
the minimal size equal to min_buff_size
*/
size_t min_records;
- /*
- The maximum expected number of records to be put in the join buffer
- at one refill
- */
- size_t max_records;
/*
Pointer to the current position in the join buffer.
@@ -542,6 +534,7 @@ protected:
join_tab= tab;
prev_cache= next_cache= 0;
buff= 0;
+ min_buff_size= max_buff_size= 0; // Caches
}
/*
@@ -557,6 +550,7 @@ protected:
next_cache= 0;
prev_cache= prev;
buff= 0;
+ min_buff_size= max_buff_size= 0; // Caches
if (prev)
prev->next_cache= this;
}
@@ -608,9 +602,10 @@ public:
void set_join_buffer_size(size_t sz) { buff_size= sz; }
/* Get the minimum possible size of the cache join buffer */
- virtual size_t get_min_join_buffer_size();
+ size_t get_min_join_buffer_size();
/* Get the maximum possible size of the cache join buffer */
- virtual size_t get_max_join_buffer_size(bool optimize_buff_size);
+ size_t get_max_join_buffer_size(bool optimize_buff_size,
+ size_t min_buffer_size_arg);
/* Shrink the size if the cache join buffer in a given ratio */
bool shrink_join_buffer_in_ratio(ulonglong n, ulonglong d);
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index f77b98f04f1..019377061a0 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -943,7 +943,7 @@ bool is_lex_native_function(const LEX_CSTRING *name)
bool is_native_function(THD *thd, const LEX_CSTRING *name)
{
- if (find_native_function_builder(thd, name))
+ if (native_functions_hash.find(thd, *name))
return true;
if (is_lex_native_function(name))
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index bfe17bb43e1..34da8639d9a 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -142,10 +142,10 @@ static void update_depend_map_for_order(JOIN *join, ORDER *order);
static ORDER *remove_const(JOIN *join,ORDER *first_order,COND *cond,
bool change_list, bool *simple_order);
static int return_zero_rows(JOIN *join, select_result *res,
- List<TABLE_LIST> &tables,
- List<Item> &fields, bool send_row,
+ List<TABLE_LIST> *tables,
+ List<Item> *fields, bool send_row,
ulonglong select_options, const char *info,
- Item *having, List<Item> &all_fields);
+ Item *having, List<Item> *all_fields);
static COND *build_equal_items(JOIN *join, COND *cond,
COND_EQUAL *inherited,
List<TABLE_LIST> *join_list,
@@ -1157,11 +1157,40 @@ int SELECT_LEX::vers_setup_conds(THD *thd, TABLE_LIST *tables)
DBUG_RETURN(0);
}
+
/*****************************************************************************
Check fields, find best join, do the select and output fields.
mysql_select assumes that all tables are already opened
*****************************************************************************/
+/*
+ Check if we have a field reference. If yes, we have to use
+ mixed_implicit_grouping.
+*/
+
+static bool check_list_for_field(List<Item> *items)
+{
+ List_iterator_fast <Item> select_it(*items);
+ Item *select_el;
+
+ while ((select_el= select_it++))
+ {
+ if (select_el->with_field)
+ return true;
+ }
+ return false;
+}
+
+static bool check_list_for_field(ORDER *order)
+{
+ for (; order; order= order->next)
+ {
+ if (order->item[0]->with_field)
+ return true;
+ }
+ return false;
+}
+
/**
Prepare of whole select (including sub queries in future).
@@ -1241,53 +1270,45 @@ JOIN::prepare(TABLE_LIST *tables_init,
DBUG_RETURN(-1);
/*
- TRUE if the SELECT list mixes elements with and without grouping,
- and there is no GROUP BY clause. Mixing non-aggregated fields with
- aggregate functions in the SELECT list is a MySQL extenstion that
- is allowed only if the ONLY_FULL_GROUP_BY sql mode is not set.
+ mixed_implicit_grouping will be set to TRUE if the SELECT list
+ mixes elements with and without grouping, and there is no GROUP BY
+ clause.
+ Mixing non-aggregated fields with aggregate functions in the
+ SELECT list or HAVING is a MySQL extension that is allowed only if
+ the ONLY_FULL_GROUP_BY sql mode is not set.
*/
mixed_implicit_grouping= false;
if ((~thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) &&
select_lex->with_sum_func && !group_list)
{
- List_iterator_fast <Item> select_it(fields_list);
- Item *select_el; /* Element of the SELECT clause, can be an expression. */
- bool found_field_elem= false;
- bool found_sum_func_elem= false;
-
- while ((select_el= select_it++))
+ if (check_list_for_field(&fields_list) ||
+ check_list_for_field(order))
{
- if (select_el->with_sum_func())
- found_sum_func_elem= true;
- if (select_el->with_field)
- found_field_elem= true;
- if (found_sum_func_elem && found_field_elem)
+ TABLE_LIST *tbl;
+ List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables);
+
+ mixed_implicit_grouping= true; // mark for future
+
+ while ((tbl= li++))
{
- mixed_implicit_grouping= true;
- break;
+ /*
+ If the query uses implicit grouping where the select list
+ contains both aggregate functions and non-aggregate fields,
+ any non-aggregated field may produce a NULL value. Set all
+ fields of each table as nullable before semantic analysis to
+ take into account this change of nullability.
+
+ Note: this loop doesn't touch tables inside merged
+ semi-joins, because subquery-to-semijoin conversion has not
+ been done yet. This is intended.
+ */
+ if (tbl->table)
+ tbl->table->maybe_null= 1;
}
}
}
-
table_count= select_lex->leaf_tables.elements;
- TABLE_LIST *tbl;
- List_iterator_fast<TABLE_LIST> li(select_lex->leaf_tables);
- while ((tbl= li++))
- {
- /*
- If the query uses implicit grouping where the select list contains both
- aggregate functions and non-aggregate fields, any non-aggregated field
- may produce a NULL value. Set all fields of each table as nullable before
- semantic analysis to take into account this change of nullability.
-
- Note: this loop doesn't touch tables inside merged semi-joins, because
- subquery-to-semijoin conversion has not been done yet. This is intended.
- */
- if (mixed_implicit_grouping && tbl->table)
- tbl->table->maybe_null= 1;
- }
-
uint real_og_num= og_num;
if (skip_order_by &&
select_lex != select_lex->master_unit()->global_parameters())
@@ -3838,7 +3859,7 @@ bool JOIN::make_aggr_tables_info()
set_items_ref_array(items0);
if (join_tab)
join_tab[exec_join_tab_cnt() + aggr_tables - 1].next_select=
- setup_end_select_func(this, NULL);
+ setup_end_select_func(this);
group= has_group_by;
DBUG_RETURN(false);
@@ -4220,13 +4241,7 @@ JOIN::reinit()
}
}
- /* Reset of sum functions */
- if (sum_funcs)
- {
- Item_sum *func, **func_ptr= sum_funcs;
- while ((func= *(func_ptr++)))
- func->clear();
- }
+ clear_sum_funcs();
if (no_rows_in_result_called)
{
@@ -4510,12 +4525,12 @@ void JOIN::exec_inner()
}
else
{
- (void) return_zero_rows(this, result, select_lex->leaf_tables,
- *columns_list,
+ (void) return_zero_rows(this, result, &select_lex->leaf_tables,
+ columns_list,
send_row_on_empty_set(),
select_options,
zero_result_cause,
- having ? having : tmp_having, all_fields);
+ having ? having : tmp_having, &all_fields);
DBUG_VOID_RETURN;
}
}
@@ -14642,10 +14657,36 @@ ORDER *simple_remove_const(ORDER *order, COND *where)
}
+/*
+ Set all fields in the table to have a null value
+
+ @param tables Table list
+*/
+
+static void make_tables_null_complemented(List<TABLE_LIST> *tables)
+{
+ List_iterator<TABLE_LIST> ti(*tables);
+ TABLE_LIST *table;
+ while ((table= ti++))
+ {
+ /*
+ Don't touch semi-join materialization tables, as the a join_free()
+ call may have freed them (and HAVING clause can't have references to
+ them anyway).
+ */
+ if (!table->is_jtbm())
+ {
+ TABLE *tbl= table->table;
+ mark_as_null_row(tbl); // Set fields to NULL
+ }
+ }
+}
+
+
static int
-return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables,
- List<Item> &fields, bool send_row, ulonglong select_options,
- const char *info, Item *having, List<Item> &all_fields)
+return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> *tables,
+ List<Item> *fields, bool send_row, ulonglong select_options,
+ const char *info, Item *having, List<Item> *all_fields)
{
DBUG_ENTER("return_zero_rows");
@@ -14661,24 +14702,15 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables,
Set all tables to have NULL row. This is needed as we will be evaluating
HAVING condition.
*/
- List_iterator<TABLE_LIST> ti(tables);
- TABLE_LIST *table;
- while ((table= ti++))
- {
- /*
- Don't touch semi-join materialization tables, as the above join_free()
- call has freed them (and HAVING clause can't have references to them
- anyway).
- */
- if (!table->is_jtbm())
- mark_as_null_row(table->table); // All fields are NULL
- }
- List_iterator_fast<Item> it(all_fields);
+ make_tables_null_complemented(tables);
+
+ List_iterator_fast<Item> it(*all_fields);
Item *item;
/*
Inform all items (especially aggregating) to calculate HAVING correctly,
also we will need it for sending results.
*/
+ join->no_rows_in_result_called= 1;
while ((item= it++))
item->no_rows_in_result();
if (having && having->val_int() == 0)
@@ -14692,12 +14724,12 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables,
join->thd->limit_found_rows= 0;
}
- if (!(result->send_result_set_metadata(fields,
+ if (!(result->send_result_set_metadata(*fields,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)))
{
bool send_error= FALSE;
if (send_row)
- send_error= result->send_data(fields) > 0;
+ send_error= result->send_data(*fields) > 0;
if (likely(!send_error))
result->send_eof(); // Should be safe
}
@@ -14713,49 +14745,42 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables,
}
/**
- used only in JOIN::clear (always) and in do_select()
- (if there where no matching rows)
+ Reset table rows to contain a null-complement row (all fields are null)
+
+ Used only in JOIN::clear() and in do_select() if there where no matching rows.
@param join JOIN
- @param cleared_tables If not null, clear also const tables and mark all
- cleared tables in the map. cleared_tables is only
- set when called from do_select() when there is a
- group function and there where no matching rows.
+ @param cleared_tables Used to mark all cleared tables in the map. Needed for
+ unclear_tables() to know which tables to restore to
+ their original state.
*/
static void clear_tables(JOIN *join, table_map *cleared_tables)
{
- /*
- must clear only the non-const tables as const tables are not re-calculated.
- */
+ DBUG_ASSERT(cleared_tables);
for (uint i= 0 ; i < join->table_count ; i++)
{
TABLE *table= join->table[i];
if (table->null_row)
continue; // Nothing more to do
- if (!(table->map & join->const_table_map) || cleared_tables)
+ (*cleared_tables)|= (((table_map) 1) << i);
+ if (table->s->null_bytes)
{
- if (cleared_tables)
- {
- (*cleared_tables)|= (((table_map) 1) << i);
- if (table->s->null_bytes)
- {
- /*
- Remember null bits for the record so that we can restore the
- original const record in unclear_tables()
- */
- memcpy(table->record[1], table->null_flags, table->s->null_bytes);
- }
- }
- mark_as_null_row(table); // All fields are NULL
+ /*
+ Remember null bits for the record so that we can restore the
+ original const record in unclear_tables()
+ */
+ memcpy(table->record[1], table->null_flags, table->s->null_bytes);
}
+ mark_as_null_row(table); // All fields are NULL
}
}
/**
Reverse null marking for tables and restore null bits.
+ This return the tables to the state of before clear_tables().
We have to do this because the tables may be re-used in a sub query
and the subquery will assume that the const tables contains the original
@@ -20235,9 +20260,9 @@ void set_postjoin_aggr_write_func(JOIN_TAB *tab)
end_select function to use. This function can't fail.
*/
-Next_select_func setup_end_select_func(JOIN *join, JOIN_TAB *tab)
+Next_select_func setup_end_select_func(JOIN *join)
{
- TMP_TABLE_PARAM *tmp_tbl= tab ? tab->tmp_table_param : &join->tmp_table_param;
+ TMP_TABLE_PARAM *tmp_tbl= &join->tmp_table_param;
/*
Choose method for presenting result to user. Use end_send_group
@@ -20308,7 +20333,7 @@ do_select(JOIN *join, Procedure *procedure)
join->duplicate_rows= join->send_records=0;
if (join->only_const_tables() && !join->need_tmp)
{
- Next_select_func end_select= setup_end_select_func(join, NULL);
+ Next_select_func end_select= setup_end_select_func(join);
/*
HAVING will be checked after processing aggregate functions,
@@ -20793,6 +20818,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
}
}
+ /* Restore state if mark_as_null_row() have been called */
if (join_tab->last_inner)
{
JOIN_TAB *last_inner_tab= join_tab->last_inner;
@@ -22155,11 +22181,18 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
{
int idx= -1;
enum_nested_loop_state ok_code= NESTED_LOOP_OK;
+ /*
+ join_tab can be 0 in the case all tables are const tables and we did not
+ need a temporary table to store the result.
+ In this case we use the original given fields, which is stored in
+ join->fields.
+ */
List<Item> *fields= join_tab ? (join_tab-1)->fields : join->fields;
DBUG_ENTER("end_send_group");
if (!join->items3.is_null() && !join->set_group_rpa)
{
+ /* Move ref_pointer_array to points to items3 */
join->set_group_rpa= true;
join->set_items_ref_array(join->items3);
}
@@ -22167,10 +22200,12 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (!join->first_record || end_of_records ||
(idx=test_if_group_changed(join->group_fields)) >= 0)
{
+
if (!join->group_sent &&
(join->first_record ||
(end_of_records && !join->group && !join->group_optimized_away)))
{
+ table_map cleared_tables= (table_map) 0;
if (join->procedure)
join->procedure->end_group();
if (idx < (int) join->send_group_parts)
@@ -22193,11 +22228,13 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
{
if (!join->first_record)
{
- List_iterator_fast<Item> it(*join->fields);
- Item *item;
/* No matching rows for group function */
- join->clear();
+ List_iterator_fast<Item> it(*fields);
+ Item *item;
+ join->no_rows_in_result_called= 1;
+
+ join->clear(&cleared_tables);
while ((item= it++))
item->no_rows_in_result();
}
@@ -22223,7 +22260,14 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (join->rollup_send_data((uint) (idx+1)))
error= 1;
}
- }
+ if (join->no_rows_in_result_called)
+ {
+ /* Restore null tables to original state */
+ join->no_rows_in_result_called= 0;
+ if (cleared_tables)
+ unclear_tables(join, &cleared_tables);
+ }
+ }
if (unlikely(error > 0))
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
if (end_of_records)
@@ -22525,6 +22569,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
{
if (join->first_record || (end_of_records && !join->group))
{
+ table_map cleared_tables= (table_map) 0;
if (join->procedure)
join->procedure->end_group();
int send_group_parts= join->send_group_parts;
@@ -22533,7 +22578,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (!join->first_record)
{
/* No matching rows for group function */
- join->clear();
+ join->clear(&cleared_tables);
}
copy_sum_funcs(join->sum_funcs,
join->sum_funcs_end[send_group_parts]);
@@ -22556,6 +22601,8 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR);
}
}
+ if (cleared_tables)
+ unclear_tables(join, &cleared_tables);
if (end_of_records)
goto end;
}
@@ -26658,11 +26705,8 @@ int JOIN::rollup_write_data(uint idx, TMP_TABLE_PARAM *tmp_table_param_arg, TABL
(end_send_group/end_write_group)
*/
-void JOIN::clear()
+void inline JOIN::clear_sum_funcs()
{
- clear_tables(this, 0);
- copy_fields(&tmp_table_param);
-
if (sum_funcs)
{
Item_sum *func, **func_ptr= sum_funcs;
@@ -26672,6 +26716,22 @@ void JOIN::clear()
}
+/*
+ Prepare for returning 'empty row' when there is no matching row.
+
+ - Mark all tables with mark_as_null_row()
+ - Make a copy of of all simple SELECT items
+ - Reset all sum functions to NULL or 0.
+*/
+
+void JOIN::clear(table_map *cleared_tables)
+{
+ clear_tables(this, cleared_tables);
+ copy_fields(&tmp_table_param);
+ clear_sum_funcs();
+}
+
+
/**
Print an EXPLAIN line with all NULLs and given message in the 'Extra' column
@@ -28138,7 +28198,7 @@ void st_select_lex::print_set_clause(THD *thd, String *str,
else
str->append(',');
- item->print(str, query_type);
+ item->print(str, (enum_query_type) (query_type | QT_NO_DATA_EXPANSION));
str->append(STRING_WITH_LEN(" = "));
val->print(str, query_type);
}
diff --git a/sql/sql_select.h b/sql/sql_select.h
index f4b266292c9..d2d17611e48 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -227,7 +227,7 @@ enum sj_strategy_enum
typedef enum_nested_loop_state
(*Next_select_func)(JOIN *, struct st_join_table *, bool);
-Next_select_func setup_end_select_func(JOIN *join, JOIN_TAB *tab);
+Next_select_func setup_end_select_func(JOIN *join);
int rr_sequential(READ_RECORD *info);
int rr_sequential_and_unpack(READ_RECORD *info);
Item *remove_pushed_top_conjuncts(THD *thd, Item *cond);
@@ -397,7 +397,6 @@ typedef struct st_join_table {
/* TRUE <=> it is prohibited to join this table using join buffer */
bool no_forced_join_cache;
uint used_join_cache_level;
- ulong join_buffer_size_limit;
JOIN_CACHE *cache;
/*
Index condition for BKA access join
@@ -1755,7 +1754,8 @@ public:
void join_free();
/** Cleanup this JOIN, possibly for reuse */
void cleanup(bool full);
- void clear();
+ void clear(table_map *cleared_tables);
+ void inline clear_sum_funcs();
bool send_row_on_empty_set()
{
return (do_send_rows && implicit_grouping && !group_optimized_away &&
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 1ad20ac7593..a621c1de29a 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -76,9 +76,6 @@ extern size_t symbols_length;
extern SYMBOL sql_functions[];
extern size_t sql_functions_length;
-extern Native_func_registry func_array[];
-extern size_t func_array_length;
-
enum enum_i_s_events_fields
{
ISE_EVENT_CATALOG= 0,
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index cf3927ac30c..cdc04d93708 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -11485,7 +11485,7 @@ function_call_generic:
This will be revised with WL#2128 (SQL PATH)
*/
- builder= find_native_function_builder(thd, &$1);
+ builder= native_functions_hash.find(thd, $1);
if (builder)
{
item= builder->create_func(thd, &$1, $4);
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index 87f71d8332b..5b734a27f8c 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -11600,7 +11600,7 @@ function_call_generic:
This will be revised with WL#2128 (SQL PATH)
*/
- builder= find_native_function_builder(thd, &$1);
+ builder= native_functions_hash.find(thd, $1);
if (builder)
{
item= builder->create_func(thd, &$1, $4);
diff --git a/sql/table.h b/sql/table.h
index 6f7f4e63473..1414659b78f 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -3337,10 +3337,16 @@ inline void mark_as_null_row(TABLE *table)
bfill(table->null_flags,table->s->null_bytes,255);
}
+/*
+ Restore table to state before mark_as_null_row() call.
+ This assumes that the caller has restored table->null_flags,
+ as is done in unclear_tables().
+*/
+
inline void unmark_as_null_row(TABLE *table)
{
- table->null_row=0;
- table->status= STATUS_NO_RECORD;
+ table->null_row= 0;
+ table->status&= ~STATUS_NULL_ROW;
}
bool is_simple_order(ORDER *order);