Merge branch '10.4' into 10.4.29 release

This commit is contained in:
Oleksandr Byelkin 2023-05-11 09:07:45 +02:00
commit de703a2b21
69 changed files with 1184 additions and 448 deletions

View file

@ -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)
{

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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)

View file

@ -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);

View file

@ -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);

View file

@ -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))

View file

@ -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);
}

View file

@ -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 &&

View file

@ -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,

View file

@ -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);

View file

@ -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);

View file

@ -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);