mirror of
https://github.com/MariaDB/server.git
synced 2026-05-07 07:35:32 +02:00
Merge branch '10.4' into 10.4.29 release
This commit is contained in:
commit
de703a2b21
69 changed files with 1184 additions and 448 deletions
|
|
@ -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[])
|
||||
{
|
||||
Native_func_registry *func;
|
||||
|
||||
DBUG_ENTER("item_create_append");
|
||||
bool Native_functions_hash::append(const Native_func_registry array[])
|
||||
{
|
||||
const Native_func_registry *func;
|
||||
|
||||
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[])
|
||||
{
|
||||
Native_func_registry *func;
|
||||
|
||||
DBUG_ENTER("item_create_remove");
|
||||
bool Native_functions_hash::remove(const Native_func_registry array[])
|
||||
{
|
||||
const Native_func_registry *func;
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -144,16 +144,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
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
10
sql/table.h
10
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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue